1 package org.metricshub.ipmi.core.coding.security;
2
3 /*-
4 * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5 * IPMI Java Client
6 * ჻჻჻჻჻჻
7 * Copyright 2023 Verax Systems, MetricsHub
8 * ჻჻჻჻჻჻
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation, either version 3 of the
12 * License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Lesser Public License for more details.
18 *
19 * You should have received a copy of the GNU General Lesser Public
20 * License along with this program. If not, see
21 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22 * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
23 */
24
25 import org.metricshub.ipmi.core.coding.commands.session.Rakp1;
26 import org.metricshub.ipmi.core.common.TypeConverter;
27
28 import java.security.InvalidKeyException;
29 import java.util.Arrays;
30
31 import javax.crypto.Mac;
32 import javax.crypto.spec.SecretKeySpec;
33
34 /**
35 * Interface for Integrity Algorithms. All classes extending this one must
36 * implement constructor(byte[]).
37 */
38 public abstract class IntegrityAlgorithm {
39
40 protected static final byte[] CONST1 = new byte[20];
41 static {
42 Arrays.fill(CONST1, (byte) 1);
43 }
44
45 protected byte[] sik;
46 private final Mac mac;
47
48 /**
49 * Constructs an integrity algorithm.
50 */
51 protected IntegrityAlgorithm(String algorithmName) {
52 this(CipherSuite.newMacInstance(algorithmName));
53 }
54
55 /**
56 * Constructs an integrity algorithm with the provided MAC.
57 *
58 * @param mac the MAC instance to use
59 */
60 private IntegrityAlgorithm(Mac mac) {
61 this.mac = mac;
62 }
63
64 /**
65 * Initializes Integrity Algorithm
66 *
67 * @param sik - Session Integrity Key calculated during the opening of the
68 * session or user password if 'one-key' logins are enabled.
69 */
70 public void initialize(byte[] sik) throws InvalidKeyException {
71 this.sik = sik;
72 final String algorithmName = getAlgorithmName();
73
74 SecretKeySpec k1 = new SecretKeySpec(sik, algorithmName);
75
76 mac.init(k1);
77 k1 = new SecretKeySpec(mac.doFinal(CONST1), algorithmName);
78
79 mac.init(k1);
80 }
81
82 /**
83 * Returns the algorithm's ID.
84 */
85 public abstract byte getCode();
86
87 /**
88 * Creates AuthCode field for message.
89 *
90 * @param base - data starting with the AuthType/Format field up to and
91 * including the field that immediately precedes the AuthCode field
92 * @return AuthCode field. Might be null if empty AuthCOde field is generated.
93 *
94 * @see Rakp1#calculateSik(org.metricshub.ipmi.core.coding.commands.session.Rakp1ResponseData)
95 */
96 public byte[] generateAuthCode(final byte[] base) {
97
98 if (sik == null) {
99 throw new NullPointerException("Algorithm not initialized.");
100 }
101
102 final int authCodeLength = getAuthCodeLength();
103 final byte[] result = new byte[authCodeLength];
104 byte[] updatedBase;
105
106 if (base[base.length - 2] == 0) { // No padding
107 updatedBase = injectIntegrityPad(base, authCodeLength);
108 } else {
109 updatedBase = base;
110 }
111
112 System.arraycopy(mac.doFinal(updatedBase), 0, result, 0, authCodeLength);
113
114 return result;
115 }
116
117 /**
118 * Modifies the algorithm base since with null Auth Code during encoding
119 * Integrity Pad isn't calculated.
120 *
121 * @param base - integrity algorithm base without Integrity Pad.
122 * @param authCodeLength - expected length of the Auth Code field.
123 * @return - integrity algorithm base with Integrity Pad and updated Pad Length
124 * field.
125 */
126 protected byte[] injectIntegrityPad(byte[] base, int authCodeLength) {
127 int pad = 0;
128 if ((base.length + authCodeLength) % 4 != 0) {
129 pad = 4 - (base.length + authCodeLength) % 4;
130 }
131
132 if (pad != 0) {
133 byte[] newBase = new byte[base.length + pad];
134
135 System.arraycopy(base, 0, newBase, 0, base.length - 2);
136
137 for (int i = base.length - 2; i < base.length - 2 + pad; ++i) {
138 newBase[i] = TypeConverter.intToByte(0xff);
139 }
140
141 newBase[newBase.length - 2] = TypeConverter.intToByte(pad);
142
143 newBase[newBase.length - 1] = base[base.length - 1];
144
145 return newBase;
146 } else {
147 return base;
148 }
149 }
150
151 /**
152 * Returns the name of the algorithm.
153 *
154 * @return The algorithm name as a {@code String}.
155 */
156 public abstract String getAlgorithmName();
157
158 /**
159 * Returns the length of the authentication code
160 *
161 * @return The length of the authentication code in bytes.
162 */
163 public abstract int getAuthCodeLength();
164
165 }