diff --git a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationAlgorithm.java b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationAlgorithm.java index 68d2298..d74d6fa 100644 --- a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationAlgorithm.java +++ b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationAlgorithm.java @@ -24,6 +24,10 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; import org.sentrysoftware.ipmi.core.coding.commands.session.Rakp1; @@ -33,75 +37,104 @@ */ public abstract class AuthenticationAlgorithm { - /** - * @return algorithm-specific code - */ - public abstract byte getCode(); - - /** - * @return length of the key for the RAKP2 message - */ - public abstract int getKeyLength(); - - /** - * @return length of the integrity check base for RAKP4 message - */ - public abstract int getIntegrityCheckBaseLength(); - - /** - * Checks value of the Key Exchange Authentication Code in RAKP messages - * - * @param data - * - The base for authentication algorithm. Depends on RAKP - * Message. - * @param key - * - the Key Exchange Authentication Code to check. - * @param password - * - password of the user establishing a session - * @return True if authentication check was successful, false otherwise. - * @throws NoSuchAlgorithmException - * when initiation of the algorithm fails - * @throws InvalidKeyException - * when creating of the algorithm key failsS - */ - public abstract boolean checkKeyExchangeAuthenticationCode(byte[] data, - byte[] key, String password) throws NoSuchAlgorithmException, - InvalidKeyException; - - /** - * Calculates value of the Key Exchange Authentication Code in RAKP messages - * - * @param data - * - The base for authentication algorithm. Depends on RAKP - * Message. - * @param password - * - password of the user establishing a session - * @throws NoSuchAlgorithmException - * when initiation of the algorithm fails - * @throws InvalidKeyException - * when creating of the algorithm key fails - */ - public abstract byte[] getKeyExchangeAuthenticationCode(byte[] data, - String password) throws NoSuchAlgorithmException, - InvalidKeyException; - - /** - * Validates Integrity Check Value in RAKP Message 4. - * - * @param data - * - The base for authentication algorithm. - * @param reference - * - The Integrity Check Value to validate. - * @param sik - * - The Session Integrity Key generated on base of RAKP Messages - * 1 and 2. - * @see Rakp1#calculateSik(org.sentrysoftware.ipmi.core.coding.commands.session.Rakp1ResponseData) - * @return True if integrity check was successful, false otherwise. - * @throws NoSuchAlgorithmException - * when initiation of the algorithm fails - * @throws InvalidKeyException - * when creating of the algorithm key fails - */ - public abstract boolean doIntegrityCheck(byte[] data, byte[] reference, - byte[] sik) throws InvalidKeyException, NoSuchAlgorithmException; + private final Mac mac; + + /** + * Constructs an authentication algorithm. + */ + protected AuthenticationAlgorithm(String algorithmName) { + this(CipherSuite.newMacInstance(algorithmName)); + } + + /** + * Constructs an authentication algorithm with the provided MAC. + * + * @param mac the MAC instance to use + */ + private AuthenticationAlgorithm(Mac mac) { + this.mac = mac; + } + + /** + * @return algorithm-specific code + */ + public abstract byte getCode(); + + /** + * @return length of the key for the RAKP2 message + */ + public abstract int getKeyLength(); + + /** + * @return length of the integrity check base for RAKP4 message + */ + public abstract int getIntegrityCheckBaseLength(); + + /** + * Checks value of the Key Exchange Authentication Code in RAKP messages + * + * @param data - The base for authentication algorithm. Depends on RAKP + * Message. + * @param key - the Key Exchange Authentication Code to check. + * @param password - password of the user establishing a session + * @return True if authentication check was successful, false otherwise. + * @throws NoSuchAlgorithmException when initiation of the algorithm fails + * @throws InvalidKeyException when creating of the algorithm key fails + */ + public boolean checkKeyExchangeAuthenticationCode(byte[] data, byte[] key, String password) + throws NoSuchAlgorithmException, InvalidKeyException { + byte[] check = getKeyExchangeAuthenticationCode(data, password); + return Arrays.equals(check, key); + } + + /** + * Calculates value of the Key Exchange Authentication Code in RAKP messages + * + * @param data - The base for authentication algorithm. Depends on RAKP + * Message. + * @param password - password of the user establishing a session + * @throws NoSuchAlgorithmException when initiation of the algorithm fails + * @throws InvalidKeyException when creating of the algorithm key fails + */ + public byte[] getKeyExchangeAuthenticationCode(byte[] data, String password) + throws NoSuchAlgorithmException, InvalidKeyException { + + final byte[] key = password.getBytes(); + + SecretKeySpec sKey = new SecretKeySpec(key, getAlgorithmName()); + mac.init(sKey); + + return mac.doFinal(data); + } + + /** + * Validates Integrity Check Value in RAKP Message 4. + * + * @param data - The base for authentication algorithm. + * @param reference - The Integrity Check Value to validate. + * @param sik - The Session Integrity Key generated on base of RAKP + * Messages 1 and 2. + * @see Rakp1#calculateSik(org.sentrysoftware.ipmi.core.coding.commands.session.Rakp1ResponseData) + * @return True if integrity check was successful, false otherwise. + * @throws NoSuchAlgorithmException when initiation of the algorithm fails + * @throws InvalidKeyException when creating of the algorithm key fails + */ + public boolean doIntegrityCheck(byte[] data, byte[] reference, byte[] sik) + throws InvalidKeyException, NoSuchAlgorithmException { + + SecretKeySpec sKey = new SecretKeySpec(sik, getAlgorithmName()); + mac.init(sKey); + + final int integrityCheckLength = getIntegrityCheckBaseLength(); + final byte[] result = new byte[integrityCheckLength]; + + System.arraycopy(mac.doFinal(data), 0, result, 0, integrityCheckLength); + + return Arrays.equals(result, reference); + } + + /** + * @return the name of the algorithm as a {@code String}. + */ + public abstract String getAlgorithmName(); } diff --git a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationRakpHmacMd5.java b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationRakpHmacMd5.java new file mode 100644 index 0000000..1cfe947 --- /dev/null +++ b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationRakpHmacMd5.java @@ -0,0 +1,59 @@ +package org.sentrysoftware.ipmi.core.coding.security; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * IPMI Java Client + * ჻჻჻჻჻჻ + * Copyright 2023 Verax Systems, Sentry Software + * ჻჻჻჻჻჻ + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +/** + * RAKP-HMAC-MD5 authentication algorithm. + */ +public class AuthenticationRakpHmacMd5 extends AuthenticationAlgorithm { + + private static final String ALGORITHM_NAME = "HmacMD5"; + + /** + * Initiates RAKP-HMAC-MD5 authentication algorithm. + */ + public AuthenticationRakpHmacMd5() { + super(ALGORITHM_NAME); + } + + @Override + public byte getCode() { + return SecurityConstants.AA_RAKP_HMAC_MD5; + } + + @Override + public int getKeyLength() { + return 16; + } + + @Override + public int getIntegrityCheckBaseLength() { + return 12; + } + + @Override + public String getAlgorithmName() { + return ALGORITHM_NAME; + } + +} diff --git a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationRakpHmacSha1.java b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationRakpHmacSha1.java index f3e025a..1016ca6 100644 --- a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationRakpHmacSha1.java +++ b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationRakpHmacSha1.java @@ -22,78 +22,38 @@ * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ */ -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - /** * RAKP-HMAC-SHA1 authentication algorithm. */ public class AuthenticationRakpHmacSha1 extends AuthenticationAlgorithm { - - private static final String ALGORITHM_NAME = "HmacSHA1"; - - private Mac mac; - - /** - * Initiates RAKP-HMAC-SHA1 authentication algorithm. - * @throws NoSuchAlgorithmException - * - when initiation of the algorithm fails - */ - public AuthenticationRakpHmacSha1() throws NoSuchAlgorithmException { - mac = Mac.getInstance(ALGORITHM_NAME); - } - - @Override - public byte getCode() { - return SecurityConstants.AA_RAKP_HMAC_SHA1; - } - - @Override - public boolean checkKeyExchangeAuthenticationCode(byte[] data, byte[] key, - String password) throws NoSuchAlgorithmException, - InvalidKeyException { - byte[] check = getKeyExchangeAuthenticationCode(data, password); - return Arrays.equals(check, key); - } - - @Override - public byte[] getKeyExchangeAuthenticationCode(byte[] data, - String password) - throws NoSuchAlgorithmException, InvalidKeyException { - - byte[] key = password.getBytes(); - - SecretKeySpec sKey = new SecretKeySpec(key, ALGORITHM_NAME); - mac.init(sKey); - - return mac.doFinal(data); - } - - @Override - public boolean doIntegrityCheck(byte[] data, byte[] reference, byte[] sik) throws InvalidKeyException, NoSuchAlgorithmException { - - SecretKeySpec sKey = new SecretKeySpec(sik, ALGORITHM_NAME); - mac.init(sKey); - - byte[] result = new byte[getIntegrityCheckBaseLength()]; - - System.arraycopy(mac.doFinal(data), 0, result, 0, getIntegrityCheckBaseLength()); - - return Arrays.equals(result, reference); - } - - @Override - public int getKeyLength() { - return 20; - } - - @Override - public int getIntegrityCheckBaseLength() { - return 12; - } + + private static final String ALGORITHM_NAME = "HmacSHA1"; + + /** + * Initiates RAKP-HMAC-SHA1 authentication algorithm. + */ + public AuthenticationRakpHmacSha1() { + super(ALGORITHM_NAME); + } + + @Override + public byte getCode() { + return SecurityConstants.AA_RAKP_HMAC_SHA1; + } + + @Override + public int getKeyLength() { + return 20; + } + + @Override + public int getIntegrityCheckBaseLength() { + return 12; + } + + @Override + public String getAlgorithmName() { + return ALGORITHM_NAME; + } } diff --git a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationRakpHmacSha256.java b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationRakpHmacSha256.java new file mode 100644 index 0000000..dc53b07 --- /dev/null +++ b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationRakpHmacSha256.java @@ -0,0 +1,59 @@ +package org.sentrysoftware.ipmi.core.coding.security; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * IPMI Java Client + * ჻჻჻჻჻჻ + * Copyright 2023 Verax Systems, Sentry Software + * ჻჻჻჻჻჻ + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +/** + * RAKP-HMAC-SHA256 authentication algorithm. + */ +public class AuthenticationRakpHmacSha256 extends AuthenticationAlgorithm { + + private static final String ALGORITHM_NAME = "HmacSHA256"; + + /** + * Initiates RAKP-HMAC-SHA256 authentication algorithm. + */ + public AuthenticationRakpHmacSha256() { + super(ALGORITHM_NAME); + } + + @Override + public byte getCode() { + return SecurityConstants.AA_RAKP_HMAC_SHA256; + } + + @Override + public int getKeyLength() { + return 32; + } + + @Override + public int getIntegrityCheckBaseLength() { + return 16; + } + + @Override + public String getAlgorithmName() { + return ALGORITHM_NAME; + } + +} diff --git a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationRakpNone.java b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationRakpNone.java index 5630ac3..5035348 100644 --- a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationRakpNone.java +++ b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/AuthenticationRakpNone.java @@ -27,47 +27,56 @@ */ public class AuthenticationRakpNone extends AuthenticationAlgorithm { - @Override - public byte getCode() { - return SecurityConstants.AA_RAKP_NONE; - } - - /** - * Checks value of the Key Exchange Authentication Code in RAKP messages - * using the RAKP-None algorithm. + /** + * Constructs an instance of the RAKP-None authentication algorithm. */ - @Override - public boolean checkKeyExchangeAuthenticationCode(byte[] data, byte[] key, String password) { - return true; - } + public AuthenticationRakpNone() { + super(""); + } - /** - * Calculates value of the Key Exchange Authentication Code in RAKP messages - * using the RAKP-None algorithm. - */ - @Override - public byte[] getKeyExchangeAuthenticationCode(byte[] data, - String password) { - return new byte[0]; - } + @Override + public byte getCode() { + return SecurityConstants.AA_RAKP_NONE; + } - /** - * Performs Integrity Check in RAKP 4 message - * using the RAKP-None algorithm. - */ - @Override - public boolean doIntegrityCheck(byte[] data, byte[] reference, byte[] sik) { - return true; - } + /** + * Checks value of the Key Exchange Authentication Code in RAKP messages using + * the RAKP-None algorithm. + */ + @Override + public boolean checkKeyExchangeAuthenticationCode(byte[] data, byte[] key, String password) { + return true; + } + + /** + * Calculates value of the Key Exchange Authentication Code in RAKP messages + * using the RAKP-None algorithm. + */ + @Override + public byte[] getKeyExchangeAuthenticationCode(byte[] data, String password) { + return new byte[0]; + } + + /** + * Performs Integrity Check in RAKP 4 message using the RAKP-None algorithm. + */ + @Override + public boolean doIntegrityCheck(byte[] data, byte[] reference, byte[] sik) { + return true; + } - @Override - public int getKeyLength() { - return 0; - } + @Override + public int getKeyLength() { + return 0; + } - @Override - public int getIntegrityCheckBaseLength() { - return 0; - } + @Override + public int getIntegrityCheckBaseLength() { + return 0; + } + @Override + public String getAlgorithmName() { + return ""; + } } diff --git a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/CipherSuite.java b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/CipherSuite.java index d615abd..61f457c 100644 --- a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/CipherSuite.java +++ b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/CipherSuite.java @@ -26,11 +26,13 @@ import org.sentrysoftware.ipmi.core.coding.commands.session.GetChannelCipherSuitesResponseData; import org.sentrysoftware.ipmi.core.common.TypeConverter; +import javax.crypto.Mac; import javax.crypto.NoSuchPaddingException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; +import java.util.function.Supplier; /** * Provides cipher suite (authentication, confidentiality and integrity @@ -78,90 +80,57 @@ public CipherSuite(byte id, byte authenticationAlgorithm, */ public void initializeAlgorithms(byte[] sik) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException { getIntegrityAlgorithm().initialize(sik); - getConfidentialityAlgorithm().initialize(sik); + getConfidentialityAlgorithm().initialize(sik, getAuthenticationAlgorithm()); } - /** - * Returns instance of AuthenticationAlgorithm class. - * - * @throws IllegalArgumentException - * when authentication algorithm code is incorrect. - */ - public AuthenticationAlgorithm getAuthenticationAlgorithm() { - if (aa != null && aa.getCode() != authenticationAlgorithm) { - throw new IllegalArgumentException( - "Invalid authentication algorithm code"); - } - switch (authenticationAlgorithm) { - case SecurityConstants.AA_RAKP_NONE: - if (aa == null) { - aa = new AuthenticationRakpNone(); - } - return aa; - case SecurityConstants.AA_RAKP_HMAC_SHA1: - if (aa == null) { - try { - aa = new AuthenticationRakpHmacSha1(); - } catch (NoSuchAlgorithmException e) { - throw new IllegalArgumentException( - "Initiation of the algorithm failed", e); - } - } - return aa; - case SecurityConstants.AA_RAKP_HMAC_MD5: - // TODO: RAKP HMAC MD5 - throw new IllegalArgumentException(NOT_YET_IMPLEMENTED_MESSAGE); - case SecurityConstants.AA_RAKP_HMAC_SHA256: - // TODO: RAKP HMAC Sha256 - throw new IllegalArgumentException(NOT_YET_IMPLEMENTED_MESSAGE); - default: - throw new IllegalArgumentException( - "Invalid authentication algorithm."); - - } - } - - /** - * Returns instance of IntegrityAlgorithm class. - * - * @throws IllegalArgumentException - * when integrity algorithm code is incorrect. - */ - public IntegrityAlgorithm getIntegrityAlgorithm() { - if (ia != null && ia.getCode() != integrityAlgorithm) { - throw new IllegalArgumentException( - "Invalid integrity algorithm code"); - } - switch (integrityAlgorithm) { - case SecurityConstants.IA_NONE: - if (ia == null) { - ia = new IntegrityNone(); - } - return ia; - case SecurityConstants.IA_HMAC_SHA1_96: - if (ia == null) { - try { - ia = new IntegrityHmacSha1_96(); - } catch (NoSuchAlgorithmException e) { - throw new IllegalArgumentException( - "Initiation of the algorithm failed", e); - } - } - return ia; - case SecurityConstants.IA_HMAC_SHA256_128: - // TODO: HMAC SHA256-128 - throw new IllegalArgumentException(NOT_YET_IMPLEMENTED_MESSAGE); - case SecurityConstants.IA_MD5_128: - // TODO: MD5-128 - throw new IllegalArgumentException(NOT_YET_IMPLEMENTED_MESSAGE); - case SecurityConstants.IA_HMAC_MD5_128: - // TODO: HMAC MD5-128 - throw new IllegalArgumentException(NOT_YET_IMPLEMENTED_MESSAGE); - default: - throw new IllegalArgumentException("Invalid integrity algorithm."); + /** + * Returns instance of AuthenticationAlgorithm class. + * + * @throws IllegalArgumentException when authentication algorithm code is + * incorrect. + */ + public AuthenticationAlgorithm getAuthenticationAlgorithm() { + if (aa != null && aa.getCode() != authenticationAlgorithm) { + throw new IllegalArgumentException("Invalid authentication algorithm code"); + } + switch (authenticationAlgorithm) { + case SecurityConstants.AA_RAKP_NONE: + return instantiateAuthenticationAlgorithm(AuthenticationRakpNone::new); + case SecurityConstants.AA_RAKP_HMAC_SHA1: + return instantiateAuthenticationAlgorithm(AuthenticationRakpHmacSha1::new); + case SecurityConstants.AA_RAKP_HMAC_MD5: + return instantiateAuthenticationAlgorithm(AuthenticationRakpHmacMd5::new); + case SecurityConstants.AA_RAKP_HMAC_SHA256: + return instantiateAuthenticationAlgorithm(AuthenticationRakpHmacSha256::new); + default: + throw new IllegalArgumentException("Invalid authentication algorithm."); + } + } - } - } + /** + * Returns instance of IntegrityAlgorithm class. + * + * @throws IllegalArgumentException when integrity algorithm code is incorrect. + */ + public IntegrityAlgorithm getIntegrityAlgorithm() { + if (ia != null && ia.getCode() != integrityAlgorithm) { + throw new IllegalArgumentException("Invalid integrity algorithm code"); + } + switch (integrityAlgorithm) { + case SecurityConstants.IA_NONE: + return instantiateIntegrityAlgorithm(IntegrityNone::new); + case SecurityConstants.IA_HMAC_SHA1_96: + return instantiateIntegrityAlgorithm(IntegrityHmacSha1_96::new); + case SecurityConstants.IA_MD5_128: + // TODO: MD5-128 + case SecurityConstants.IA_HMAC_MD5_128: + return instantiateIntegrityAlgorithm(IntegrityHmacMd5_128::new); + case SecurityConstants.IA_HMAC_SHA256_128: + return instantiateIntegrityAlgorithm(IntegrityHmacSha256_128::new); + default: + throw new IllegalArgumentException("Invalid integrity algorithm."); + } + } /** * Returns instance of ConfidentialityAlgorithm class. @@ -252,4 +221,46 @@ public static List getCipherSuites(byte[] bytes) { public static CipherSuite getEmpty() { return new CipherSuite((byte) 0, (byte) 0, (byte) 0, (byte) 0); } + + /** + * Creates an instance of AuthenticationAlgorithm. + * + * @param supplier constructor of the algorithm + */ + private AuthenticationAlgorithm instantiateAuthenticationAlgorithm( + final Supplier constructor) { + if (aa == null) { + aa = constructor.get(); + } + return aa; + } + + /** + * Creates an instance of IntegrityAlgorithm. + * + * @param supplier constructor of the algorithm + */ + private IntegrityAlgorithm instantiateIntegrityAlgorithm(final Supplier constructor) { + if (ia == null) { + ia = constructor.get(); + } + return ia; + } + + /** + * Constructs a Mac object that implements the given MAC algorithm. + * + * @param algorithmName the name of the algorithm to use + * @return The Mac object that implements the specified MAC algorithm. + */ + public static Mac newMacInstance(final String algorithmName) { + if (algorithmName == null || algorithmName.trim().isEmpty()) { + return null; + } + try { + return Mac.getInstance(algorithmName); + } catch (NoSuchAlgorithmException e) { + throw new IllegalArgumentException("Algorithm " + algorithmName + " is not available", e); + } + } } diff --git a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/ConfidentialityAesCbc128.java b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/ConfidentialityAesCbc128.java index b8021ed..f3b324b 100644 --- a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/ConfidentialityAesCbc128.java +++ b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/ConfidentialityAesCbc128.java @@ -24,6 +24,7 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.util.Arrays; import javax.crypto.Cipher; import javax.crypto.Mac; @@ -38,8 +39,10 @@ */ public class ConfidentialityAesCbc128 extends ConfidentialityAlgorithm { - private static final byte[] CONST2 = new byte[] { 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; + protected static final byte[] CONST2 = new byte[20]; + static { + Arrays.fill(CONST2, (byte) 2); + } private Cipher cipher; @@ -51,13 +54,14 @@ public byte getCode() { } @Override - public void initialize(byte[] sik) throws InvalidKeyException, + public void initialize(byte[] sik, AuthenticationAlgorithm authenticationAlgorithm) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException { - super.initialize(sik); + super.initialize(sik, authenticationAlgorithm); - SecretKeySpec k2 = new SecretKeySpec(sik, "HmacSHA1"); + final String algorithmName = authenticationAlgorithm.getAlgorithmName(); + SecretKeySpec k2 = new SecretKeySpec(sik, algorithmName); - Mac mac = Mac.getInstance("HmacSHA1"); + Mac mac = Mac.getInstance(algorithmName); mac.init(k2); byte[] ckey = mac.doFinal(CONST2); diff --git a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/ConfidentialityAlgorithm.java b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/ConfidentialityAlgorithm.java index 2bd4be1..e5a943e 100644 --- a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/ConfidentialityAlgorithm.java +++ b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/ConfidentialityAlgorithm.java @@ -39,6 +39,8 @@ public abstract class ConfidentialityAlgorithm { * @param sik * - Session Integrity Key calculated during the opening of the * session or user password if 'one-key' logins are enabled. + * @param authenticationAlgorithm + * - Algorithm used for authentication. * @throws InvalidKeyException * - when initiation of the algorithm fails * @throws NoSuchAlgorithmException @@ -46,7 +48,7 @@ public abstract class ConfidentialityAlgorithm { * @throws NoSuchPaddingException * - when initiation of the algorithm fails */ - public void initialize(byte[] sik) throws InvalidKeyException, + public void initialize(byte[] sik, AuthenticationAlgorithm authenticationAlgorithm) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException { this.sik = sik; } diff --git a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityAlgorithm.java b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityAlgorithm.java index 6fd7fb1..886a278 100644 --- a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityAlgorithm.java +++ b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityAlgorithm.java @@ -26,6 +26,11 @@ import org.sentrysoftware.ipmi.core.common.TypeConverter; import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; /** * Interface for Integrity Algorithms. All classes extending this one must @@ -33,75 +38,129 @@ */ public abstract class IntegrityAlgorithm { - protected byte[] sik; - - public IntegrityAlgorithm() { - - } - - /** - * Initializes Integrity Algorithm - * - * @param sik - * - Session Integrity Key calculated during the opening of the - * session or user password if 'one-key' logins are enabled. - */ - public void initialize(byte[] sik) throws InvalidKeyException { - this.sik = sik; - } - - /** - * Returns the algorithm's ID. - */ - public abstract byte getCode(); - - /** - * Creates AuthCode field for message. - * - * @param base - * - data starting with the AuthType/Format field up to and - * including the field that immediately precedes the AuthCode - * field - * @return AuthCode field. Might be null if empty AuthCOde field is - * generated. - * - * @see Rakp1#calculateSik(org.sentrysoftware.ipmi.core.coding.commands.session.Rakp1ResponseData) - */ - public abstract byte[] generateAuthCode(byte[] base); - - /** - * Modifies the algorithm base since with null Auth Code during encoding - * Integrity Pad isn't calculated. - * - * @param base - * - integrity algorithm base without Integrity Pad. - * @param authCodeLength - * - expected length of the Auth Code field. - * @return - integrity algorithm base with Integrity Pad and updated Pad - * Length field. - */ - protected byte[] injectIntegrityPad(byte[] base, int authCodeLength) { - int pad = 0; - if ((base.length + authCodeLength) % 4 != 0) { - pad = 4 - (base.length + authCodeLength) % 4; - } - - if (pad != 0) { - byte[] newBase = new byte[base.length + pad]; - - System.arraycopy(base, 0, newBase, 0, base.length - 2); - - for (int i = base.length - 2; i < base.length - 2 + pad; ++i) { - newBase[i] = TypeConverter.intToByte(0xff); - } - - newBase[newBase.length - 2] = TypeConverter.intToByte(pad); - - newBase[newBase.length - 1] = base[base.length - 1]; - - return newBase; - } else { - return base; - } - } + protected static final byte[] CONST1 = new byte[20]; + static { + Arrays.fill(CONST1, (byte) 1); + } + + protected byte[] sik; + private final Mac mac; + + /** + * Constructs an integrity algorithm. + */ + protected IntegrityAlgorithm(String algorithmName) { + this(CipherSuite.newMacInstance(algorithmName)); + } + + /** + * Constructs an integrity algorithm with the provided MAC. + * + * @param mac the MAC instance to use + */ + private IntegrityAlgorithm(Mac mac) { + this.mac = mac; + } + + /** + * Initializes Integrity Algorithm + * + * @param sik - Session Integrity Key calculated during the opening of the + * session or user password if 'one-key' logins are enabled. + */ + public void initialize(byte[] sik) throws InvalidKeyException { + this.sik = sik; + final String algorithmName = getAlgorithmName(); + + SecretKeySpec k1 = new SecretKeySpec(sik, algorithmName); + + mac.init(k1); + k1 = new SecretKeySpec(mac.doFinal(CONST1), algorithmName); + + mac.init(k1); + } + + /** + * Returns the algorithm's ID. + */ + public abstract byte getCode(); + + /** + * Creates AuthCode field for message. + * + * @param base - data starting with the AuthType/Format field up to and + * including the field that immediately precedes the AuthCode field + * @return AuthCode field. Might be null if empty AuthCOde field is generated. + * + * @see Rakp1#calculateSik(org.sentrysoftware.ipmi.core.coding.commands.session.Rakp1ResponseData) + */ + public byte[] generateAuthCode(final byte[] base) { + + if (sik == null) { + throw new NullPointerException("Algorithm not initialized."); + } + + final int authCodeLength = getAuthCodeLength(); + final byte[] result = new byte[authCodeLength]; + byte[] updatedBase; + + if (base[base.length - 2] == 0) { // pas de padding + updatedBase = injectIntegrityPad(base, authCodeLength); + } else { + updatedBase = base; + } + + System.arraycopy(mac.doFinal(updatedBase), 0, result, 0, authCodeLength); + + return result; + } + + /** + * Modifies the algorithm base since with null Auth Code during encoding + * Integrity Pad isn't calculated. + * + * @param base - integrity algorithm base without Integrity Pad. + * @param authCodeLength - expected length of the Auth Code field. + * @return - integrity algorithm base with Integrity Pad and updated Pad Length + * field. + */ + protected byte[] injectIntegrityPad(byte[] base, int authCodeLength) { + int pad = 0; + if ((base.length + authCodeLength) % 4 != 0) { + pad = 4 - (base.length + authCodeLength) % 4; + } + + if (pad != 0) { + byte[] newBase = new byte[base.length + pad]; + + System.arraycopy(base, 0, newBase, 0, base.length - 2); + + for (int i = base.length - 2; i < base.length - 2 + pad; ++i) { + newBase[i] = TypeConverter.intToByte(0xff); + } + + newBase[newBase.length - 2] = TypeConverter.intToByte(pad); + + newBase[newBase.length - 1] = base[base.length - 1]; + + return newBase; + } else { + return base; + } + } + + /** + * Returns the name of the algorithm. + * + * @return The algorithm name as a {@code String}. + */ + public abstract String getAlgorithmName(); + + /** + * Returns the length of the authentication code + * + * @return The length of the authentication code in bytes. + */ + public abstract int getAuthCodeLength(); + } diff --git a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityHmacMd5_128.java b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityHmacMd5_128.java new file mode 100644 index 0000000..3518904 --- /dev/null +++ b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityHmacMd5_128.java @@ -0,0 +1,53 @@ +package org.sentrysoftware.ipmi.core.coding.security; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * IPMI Java Client + * ჻჻჻჻჻჻ + * Copyright 2023 Verax Systems, Sentry Software + * ჻჻჻჻჻჻ + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +/** + * HMAC-MD5-128 integrity algorithm. + */ +public class IntegrityHmacMd5_128 extends IntegrityAlgorithm { + + public static final String ALGORITHM_NAME = "HmacMD5"; + + /** + * Initiates HMAC-MD5-128 integrity algorithm. + */ + public IntegrityHmacMd5_128() { + super(ALGORITHM_NAME); + } + + @Override + public byte getCode() { + return SecurityConstants.IA_HMAC_MD5_128; + } + + @Override + public String getAlgorithmName() { + return ALGORITHM_NAME; + } + + @Override + public int getAuthCodeLength() { + return 16; + } +} \ No newline at end of file diff --git a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityHmacSha1_96.java b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityHmacSha1_96.java index 0fe7284..f971f55 100644 --- a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityHmacSha1_96.java +++ b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityHmacSha1_96.java @@ -22,43 +22,18 @@ * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ */ -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; - /** * HMAC-SHA1-96 integrity algorithm. */ public class IntegrityHmacSha1_96 extends IntegrityAlgorithm { public static final String ALGORITHM_NAME = "HmacSHA1"; - private Mac mac; - - private static final byte[] CONST1 = new byte[] { 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; /** * Initiates HMAC-SHA1-96 integrity algorithm. - * - * @throws NoSuchAlgorithmException - * - when initiation of the algorithm fails */ - public IntegrityHmacSha1_96() throws NoSuchAlgorithmException { - mac = Mac.getInstance(ALGORITHM_NAME); - } - - @Override - public void initialize(byte[] sik) throws InvalidKeyException { - super.initialize(sik); - - SecretKeySpec k1 = new SecretKeySpec(sik, ALGORITHM_NAME); - - mac.init(k1); - - k1 = new SecretKeySpec(mac.doFinal(CONST1), ALGORITHM_NAME); - - mac.init(k1); + public IntegrityHmacSha1_96() { + super(ALGORITHM_NAME); } @Override @@ -67,23 +42,12 @@ public byte getCode() { } @Override - public byte[] generateAuthCode(final byte[] base) { - if (sik == null) { - throw new NullPointerException("Algorithm not initialized."); - } - - byte[] result = new byte[12]; - byte[] updatedBase; - - if(base[base.length - 2] == 0 /*there are no integrity pad bytes*/) { - updatedBase = injectIntegrityPad(base,12); - } else { - updatedBase = base; - } - - System.arraycopy(mac.doFinal(updatedBase), 0, result, 0, 12); - - return result; + public String getAlgorithmName() { + return ALGORITHM_NAME; } - + + @Override + public int getAuthCodeLength() { + return 12; + } } diff --git a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityHmacSha256_128.java b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityHmacSha256_128.java new file mode 100644 index 0000000..5e2f35a --- /dev/null +++ b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityHmacSha256_128.java @@ -0,0 +1,54 @@ +package org.sentrysoftware.ipmi.core.coding.security; + +/*- + * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ + * IPMI Java Client + * ჻჻჻჻჻჻ + * Copyright 2023 Verax Systems, Sentry Software + * ჻჻჻჻჻჻ + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ + */ + +/** + * HMAC-SHA256-128 integrity algorithm. + */ +public class IntegrityHmacSha256_128 extends IntegrityAlgorithm { + + private static final String ALGORITHM_NAME = "HmacSHA256"; + + /** + * Initiates HMAC-SHA1-96 integrity algorithm. + */ + public IntegrityHmacSha256_128() { + super(ALGORITHM_NAME); + } + + @Override + public byte getCode() { + return SecurityConstants.IA_HMAC_SHA256_128; + } + + @Override + public String getAlgorithmName() { + return ALGORITHM_NAME; + } + + @Override + public int getAuthCodeLength() { + return 16; + } + +} diff --git a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityNone.java b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityNone.java index f3088ab..77cd196 100644 --- a/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityNone.java +++ b/src/main/java/org/sentrysoftware/ipmi/core/coding/security/IntegrityNone.java @@ -22,23 +22,43 @@ * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ */ +import java.security.InvalidKeyException; + /** * Class representing RAKP-None integrity algorithm */ public class IntegrityNone extends IntegrityAlgorithm { - public IntegrityNone() { - super(); - } + /** + * Initiates the IntegrityNone algorithm + */ + public IntegrityNone() { + super(""); + } + + @Override + public void initialize(byte[] sik) throws InvalidKeyException { + this.sik = sik; + } + + @Override + public byte getCode() { + return SecurityConstants.IA_NONE; + } + + @Override + public byte[] generateAuthCode(byte[] base) { + return null; + } - @Override - public byte getCode() { - return SecurityConstants.IA_NONE; - } + @Override + public String getAlgorithmName() { + return ""; + } - @Override - public byte[] generateAuthCode(byte[] base) { - return null; - } + @Override + public int getAuthCodeLength() { + return 0; + } } diff --git a/src/main/java/org/sentrysoftware/ipmi/core/connection/Connection.java b/src/main/java/org/sentrysoftware/ipmi/core/connection/Connection.java index 7c8fbb5..c200bc9 100644 --- a/src/main/java/org/sentrysoftware/ipmi/core/connection/Connection.java +++ b/src/main/java/org/sentrysoftware/ipmi/core/connection/Connection.java @@ -665,17 +665,14 @@ public int getNextSessionSequenceNumber() { return result; } - /** - * @return Default cipher suite (3) - */ - public static CipherSuite getDefaultCipherSuite() { - try { - return new CipherSuite((byte) DEFAULT_CIPHER_SUITE, new AuthenticationRakpHmacSha1().getCode(), - new ConfidentialityAesCbc128().getCode(), new IntegrityHmacSha1_96().getCode()); - } catch (NoSuchAlgorithmException e) { - logger.error("Wrong algorithm in default Cipher suite - SHOULD NOT HAPPEN!!!", e); - return null; - } - } + /** + * @return Default cipher suite (3) + */ + public static CipherSuite getDefaultCipherSuite() { + + return new CipherSuite((byte) DEFAULT_CIPHER_SUITE, new AuthenticationRakpHmacSha1().getCode(), + new ConfidentialityAesCbc128().getCode(), new IntegrityHmacSha1_96().getCode()); + + } }