View Javadoc
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 }