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 java.security.InvalidKeyException;
26 import java.security.NoSuchAlgorithmException;
27 import java.util.Arrays;
28
29 import javax.crypto.Mac;
30 import javax.crypto.spec.SecretKeySpec;
31
32 import org.metricshub.ipmi.core.coding.commands.session.Rakp1;
33
34 /**
35 * Interface for authentication algorithms. All classes extending this one must
36 * have a parameterless constructor.
37 */
38 public abstract class AuthenticationAlgorithm {
39
40 private final Mac mac;
41
42 /**
43 * Constructs an authentication algorithm.
44 */
45 protected AuthenticationAlgorithm(String algorithmName) {
46 this(CipherSuite.newMacInstance(algorithmName));
47 }
48
49 /**
50 * Constructs an authentication algorithm with the provided MAC.
51 *
52 * @param mac the MAC instance to use
53 */
54 private AuthenticationAlgorithm(Mac mac) {
55 this.mac = mac;
56 }
57
58 /**
59 * @return algorithm-specific code
60 */
61 public abstract byte getCode();
62
63 /**
64 * @return length of the key for the RAKP2 message
65 */
66 public abstract int getKeyLength();
67
68 /**
69 * @return length of the integrity check base for RAKP4 message
70 */
71 public abstract int getIntegrityCheckBaseLength();
72
73 /**
74 * Checks value of the Key Exchange Authentication Code in RAKP messages
75 *
76 * @param data - The base for authentication algorithm. Depends on RAKP
77 * Message.
78 * @param key - the Key Exchange Authentication Code to check.
79 * @param password - password of the user establishing a session
80 * @return True if authentication check was successful, false otherwise.
81 * @throws NoSuchAlgorithmException when initiation of the algorithm fails
82 * @throws InvalidKeyException when creating of the algorithm key fails
83 */
84 public boolean checkKeyExchangeAuthenticationCode(byte[] data, byte[] key, String password)
85 throws NoSuchAlgorithmException, InvalidKeyException {
86 byte[] check = getKeyExchangeAuthenticationCode(data, password);
87 return Arrays.equals(check, key);
88 }
89
90 /**
91 * Calculates value of the Key Exchange Authentication Code in RAKP messages
92 *
93 * @param data - The base for authentication algorithm. Depends on RAKP
94 * Message.
95 * @param password - password of the user establishing a session
96 * @throws NoSuchAlgorithmException when initiation of the algorithm fails
97 * @throws InvalidKeyException when creating of the algorithm key fails
98 */
99 public byte[] getKeyExchangeAuthenticationCode(byte[] data, String password)
100 throws NoSuchAlgorithmException, InvalidKeyException {
101
102 final byte[] key = password.getBytes();
103
104 SecretKeySpec sKey = new SecretKeySpec(key, getAlgorithmName());
105 mac.init(sKey);
106
107 return mac.doFinal(data);
108 }
109
110 /**
111 * Validates Integrity Check Value in RAKP Message 4.
112 *
113 * @param data - The base for authentication algorithm.
114 * @param reference - The Integrity Check Value to validate.
115 * @param sik - The Session Integrity Key generated on base of RAKP
116 * Messages 1 and 2.
117 * @see Rakp1#calculateSik(org.metricshub.ipmi.core.coding.commands.session.Rakp1ResponseData)
118 * @return True if integrity check was successful, false otherwise.
119 * @throws NoSuchAlgorithmException when initiation of the algorithm fails
120 * @throws InvalidKeyException when creating of the algorithm key fails
121 */
122 public boolean doIntegrityCheck(byte[] data, byte[] reference, byte[] sik)
123 throws InvalidKeyException, NoSuchAlgorithmException {
124
125 SecretKeySpec sKey = new SecretKeySpec(sik, getAlgorithmName());
126 mac.init(sKey);
127
128 final int integrityCheckLength = getIntegrityCheckBaseLength();
129 final byte[] result = new byte[integrityCheckLength];
130
131 System.arraycopy(mac.doFinal(data), 0, result, 0, integrityCheckLength);
132
133 return Arrays.equals(result, reference);
134 }
135
136 /**
137 * @return the name of the algorithm as a {@code String}.
138 */
139 public abstract String getAlgorithmName();
140 }