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