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 java.security.InvalidKeyException;
26  import java.security.NoSuchAlgorithmException;
27  import java.util.Arrays;
28  
29  import javax.crypto.Cipher;
30  import javax.crypto.Mac;
31  import javax.crypto.NoSuchPaddingException;
32  import javax.crypto.spec.IvParameterSpec;
33  import javax.crypto.spec.SecretKeySpec;
34  
35  import org.metricshub.ipmi.core.common.TypeConverter;
36  
37  /**
38   * AES-CBC-128 confidentiality algorithm
39   */
40  public class ConfidentialityAesCbc128 extends ConfidentialityAlgorithm {
41  
42  	private static final byte[] CONST2 = new byte[20];
43  	static {
44  		Arrays.fill(CONST2, (byte) 2);
45  	}
46  
47  	// The sending and receiving threads share this instance: encrypt() and decrypt() each init the cipher
48  	private Cipher cipher;
49  
50  	private SecretKeySpec cipherKey;
51  
52  	@Override
53  	public byte getCode() {
54  		return SecurityConstants.CA_AES_CBC128;
55  	}
56  
57  	@Override
58  	public synchronized void initialize(byte[] sik, AuthenticationAlgorithm authenticationAlgorithm)
59  			throws InvalidKeyException,
60  			NoSuchAlgorithmException,
61  			NoSuchPaddingException {
62  		super.initialize(sik, authenticationAlgorithm);
63  
64  		final String algorithmName = authenticationAlgorithm.getAlgorithmName();
65  		SecretKeySpec k2 = new SecretKeySpec(sik, algorithmName);
66  
67  		Mac mac = Mac.getInstance(algorithmName);
68  		mac.init(k2);
69  
70  		byte[] ckey = mac.doFinal(CONST2);
71  
72  		byte[] ciphKey = new byte[16];
73  
74  		System.arraycopy(ckey, 0, ciphKey, 0, 16);
75  
76  		cipherKey = new SecretKeySpec(ciphKey, "AES");
77  
78  		cipher = Cipher.getInstance("AES/CBC/NoPadding");
79  	}
80  
81  	@Override
82  	public synchronized byte[] encrypt(byte[] data) throws InvalidKeyException {
83  		int length = data.length + 17;
84  		int pad = 0;
85  		if (length % 16 != 0) {
86  			pad = 16 - length % 16;
87  		}
88  		length += pad;
89  
90  		byte[] result = new byte[length - 16];
91  
92  		cipher.init(Cipher.ENCRYPT_MODE, cipherKey);
93  
94  		System.arraycopy(data, 0, result, 0, data.length);
95  
96  		for (int i = 0; i < pad; ++i) {
97  			result[i + data.length] = TypeConverter.intToByte(i + 1);
98  		}
99  
100 		result[length - 17] = TypeConverter.intToByte(pad);
101 
102 		try {
103 			byte[] encrypted = cipher.doFinal(result);
104 
105 			result = new byte[encrypted.length + 16];
106 
107 			System.arraycopy(encrypted, 0, result, 16, encrypted.length); // encrypted
108 																																		// payload
109 			System.arraycopy(cipher.getIV(), 0, result, 0, 16); // Initialization
110 																													// vector
111 
112 			return result;
113 		} catch (Exception e) {
114 			throw new IllegalArgumentException(e.getMessage(), e);
115 		}
116 	}
117 
118 	@Override
119 	public synchronized byte[] decrypt(byte[] data) {
120 
121 		byte[] decrypted = null;
122 		try {
123 			byte[] iv = new byte[16];
124 
125 			System.arraycopy(data, 0, iv, 0, 16);
126 
127 			byte[] encrypted = new byte[data.length - 16];
128 
129 			System.arraycopy(data, 16, encrypted, 0, encrypted.length);
130 
131 			cipher.init(Cipher.DECRYPT_MODE, cipherKey, new IvParameterSpec(iv));
132 			decrypted = cipher.doFinal(encrypted);
133 		} catch (Exception e) {
134 			throw new IllegalArgumentException("Decryption failed", e);
135 		}
136 
137 		// Confidentiality Pad: 0 to 15 bytes 01h, 02h, 03h... then the Pad Length (IPMI 2.0 section 13.29)
138 		int pad = decrypted.length == 0 ? -1 : TypeConverter.byteToInt(decrypted[decrypted.length - 1]);
139 		if (pad < 0 || pad > 15 || pad >= decrypted.length) {
140 			throw new IllegalArgumentException("Invalid confidentiality pad length");
141 		}
142 		int length = decrypted.length - pad - 1;
143 		for (int i = 0; i < pad; ++i) {
144 			if (TypeConverter.byteToInt(decrypted[length + i]) != i + 1) {
145 				throw new IllegalArgumentException("Invalid confidentiality pad");
146 			}
147 		}
148 
149 		return Arrays.copyOf(decrypted, length);
150 	}
151 
152 	@Override
153 	public int getConfidentialityOverheadSize(int payloadSize) {
154 		int size = 17;
155 		if ((size + payloadSize) % 16 != 0) {
156 			size += 16 - (size + payloadSize) % 16;
157 		}
158 		return size;
159 	}
160 
161 }