1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.net.ipsec.ike.crypto;
18 
19 import com.android.internal.util.HexDump;
20 
21 import java.nio.ByteBuffer;
22 import java.security.GeneralSecurityException;
23 import java.security.InvalidAlgorithmParameterException;
24 import java.security.InvalidKeyException;
25 import java.util.Arrays;
26 
27 import javax.crypto.BadPaddingException;
28 import javax.crypto.Cipher;
29 import javax.crypto.IllegalBlockSizeException;
30 import javax.crypto.ShortBufferException;
31 import javax.crypto.spec.IvParameterSpec;
32 import javax.crypto.spec.SecretKeySpec;
33 
34 public class AesXCbcImpl {
35     // Use AES CBC as per NIST Special Publication 800-38B 6.2
36     private static final String AES_CBC = "AES/CBC/NoPadding";
37 
38     private static final int AES_CBC_IV_LEN = 16;
39 
40     private static final int AES_CBC_BLOCK_LEN = 16;
41 
42     private static final int AES_XCBC_96_MAC_LEN = 12;
43 
44     private final Cipher mCipher;
45 
46     private static final String KEY1_SEED_HEX_STRING = "01010101010101010101010101010101";
47     private static final String KEY2_SEED_HEX_STRING = "02020202020202020202020202020202";
48     private static final String KEY3_SEED_HEX_STRING = "03030303030303030303030303030303";
49 
50     private static final byte[] E_INITIAL = new byte[AES_CBC_BLOCK_LEN];
51 
AesXCbcImpl()52     public AesXCbcImpl() throws GeneralSecurityException {
53         mCipher = Cipher.getInstance(AES_CBC);
54     }
55 
56     /** Calculate the MAC */
mac(byte[] keyBytes, byte[] dataToSign, boolean needTruncation)57     public byte[] mac(byte[] keyBytes, byte[] dataToSign, boolean needTruncation) {
58         // See RFC 3566#section-4 for the algorithm
59         int blockSize = mCipher.getBlockSize();
60         boolean isPaddingNeeded = dataToSign.length % blockSize != 0;
61 
62         byte[] paddedData = dataToSign;
63         if (isPaddingNeeded) {
64             paddedData = padData(dataToSign, blockSize);
65         }
66 
67         // (1)  Derive 3 128-bit keys (K1, K2 and K3) from the 128-bit secret key K, as follows:
68         // K1 = 0x01010101010101010101010101010101 encrypted with Key K
69         // K2 = 0x02020202020202020202020202020202 encrypted with Key K
70         // K3 = 0x03030303030303030303030303030303 encrypted with Key K
71         byte[] key1 = encryptAesBlock(keyBytes, HexDump.hexStringToByteArray(KEY1_SEED_HEX_STRING));
72         byte[] key2 = encryptAesBlock(keyBytes, HexDump.hexStringToByteArray(KEY2_SEED_HEX_STRING));
73         byte[] key3 = encryptAesBlock(keyBytes, HexDump.hexStringToByteArray(KEY3_SEED_HEX_STRING));
74 
75         // (2)  Define E[0] = 0x00000000000000000000000000000000
76         byte[] e = E_INITIAL;
77 
78         int numMessageBlocks = paddedData.length / blockSize;
79 
80         // (3)  For each block M[i], where i = 1 ... n-1:
81         // XOR M[i] with E[i-1], then encrypt the result with Key K1, yielding E[i].
82         byte[] message;
83         for (int i = 0; i < numMessageBlocks - 1; ++i) {
84             message = Arrays.copyOfRange(paddedData, i * blockSize, i * blockSize + blockSize);
85             message = xorByteArrays(message, e);
86             e = encryptAesBlock(key1, message);
87         }
88 
89         // (4)  For block M[n]:
90         // a)  If the blocksize of M[n] is 128 bits:
91         // XOR M[n] with E[n-1] and Key K2, then encrypt the result with Key K1, yielding E[n].
92         //
93         // b)  If the blocksize of M[n] is less than 128 bits:
94         //
95         // i)  Pad M[n] with a single "1" bit, followed by the number of "0" bits (possibly none)
96         // required to increase M[n]'s blocksize to 128 bits.
97         //
98         // ii) XOR M[n] with E[n-1] and Key K3, then encrypt the result with Key K1, yielding E[n].
99         message = Arrays.copyOfRange(paddedData, paddedData.length - blockSize, paddedData.length);
100         message = xorByteArrays(message, e);
101         if (isPaddingNeeded) {
102             message = xorByteArrays(message, key3);
103         } else {
104             message = xorByteArrays(message, key2);
105         }
106 
107         byte[] encryptedMessage = encryptAesBlock(key1, message);
108 
109         // AES-XCBC-MAC-96 algorithm requires the output to be truncated to 96-bits
110         if (needTruncation) {
111             encryptedMessage = Arrays.copyOfRange(encryptedMessage, 0, AES_XCBC_96_MAC_LEN);
112         }
113 
114         return encryptedMessage;
115     }
116 
xorByteArrays(byte[] message, byte[] e)117     private static byte[] xorByteArrays(byte[] message, byte[] e) {
118         byte[] output = new byte[message.length];
119 
120         for (int i = 0; i < output.length; ++i) {
121             output[i] = (byte) (message[i] ^ e[i]);
122         }
123 
124         return output;
125     }
126 
padData(byte[] dataToSign, int blockSize)127     private static byte[] padData(byte[] dataToSign, int blockSize) {
128         int dataLen = dataToSign.length;
129         int padLen = (dataLen + blockSize - 1) / blockSize * blockSize - dataLen;
130         ByteBuffer paddedData = ByteBuffer.allocate(dataLen + padLen);
131         byte[] padding = new byte[padLen];
132 
133         // Obligatory 10* Padding as per RFC 3566#section-4
134         padding[0] = (byte) 128;
135         paddedData.put(dataToSign).put(padding);
136         return paddedData.array();
137     }
138 
encryptAesBlock(byte[] keyBytes, byte[] dataToEncrypt)139     private byte[] encryptAesBlock(byte[] keyBytes, byte[] dataToEncrypt) {
140         // IV is zero as per the recommendation in NIST Special Publication 800-38B
141         IvParameterSpec iv = new IvParameterSpec(new byte[AES_CBC_IV_LEN]);
142 
143         ByteBuffer inputBuffer = ByteBuffer.wrap(dataToEncrypt);
144         ByteBuffer outputBuffer = ByteBuffer.allocate(dataToEncrypt.length);
145         try {
146             mCipher.init(
147                     Cipher.ENCRYPT_MODE, new SecretKeySpec(keyBytes, mCipher.getAlgorithm()), iv);
148             mCipher.doFinal(inputBuffer, outputBuffer);
149             return outputBuffer.array();
150         } catch (InvalidAlgorithmParameterException
151                 | InvalidKeyException
152                 | BadPaddingException
153                 | IllegalBlockSizeException
154                 | ShortBufferException e) {
155             throw new IllegalArgumentException("Failed to sign data: ", e);
156         }
157     }
158 }
159