blob: 7fc18ed2014c84051bb1e11a3a1025c0d3b65e65 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.net.ipsec.ike.crypto;
import android.net.IpSecAlgorithm;
import android.net.ipsec.ike.SaProposal;
import java.nio.ByteBuffer;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* IkeCipher represents a negotiated normal mode encryption algorithm.
*
* @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296, Internet Key Exchange
* Protocol Version 2 (IKEv2)</a>
*/
public final class IkeNormalModeCipher extends IkeCipher {
// Block counter field should be 32 bits and starts from value one.
static final byte[] AES_CTR_INITIAL_COUNTER = new byte[] {0x00, 0x00, 0x00, 0x01};
/** Package private */
IkeNormalModeCipher(int algorithmId, int keyLength, int ivLength, String algorithmName) {
this(algorithmId, keyLength, ivLength, algorithmName, SALT_LEN_NOT_INCLUDED);
}
/** Package private */
IkeNormalModeCipher(
int algorithmId, int keyLength, int ivLength, String algorithmName, int saltLen) {
super(
algorithmId,
keyLength,
ivLength,
algorithmName,
false /*isAead*/,
saltLen,
BLOCK_SIZE_NOT_SPECIFIED);
}
private byte[] doCipherAction(byte[] data, byte[] keyBytes, byte[] ivBytes, int opmode)
throws IllegalBlockSizeException {
if (getKeyLength() != keyBytes.length) {
throw new IllegalArgumentException(
"Expected key length: "
+ getKeyLength()
+ " Received key length: "
+ keyBytes.length);
}
try {
byte[] secretKeyBytes = Arrays.copyOfRange(keyBytes, 0, keyBytes.length - mSaltLen);
byte[] salt = Arrays.copyOfRange(keyBytes, secretKeyBytes.length, keyBytes.length);
byte[] nonce = concatenateByteArray(salt, ivBytes);
if (getAlgorithmId() == SaProposal.ENCRYPTION_ALGORITHM_AES_CTR) {
nonce = concatenateByteArray(nonce, AES_CTR_INITIAL_COUNTER);
}
SecretKeySpec key = new SecretKeySpec(secretKeyBytes, getAlgorithmName());
IvParameterSpec iv = new IvParameterSpec(nonce);
mCipher.init(opmode, key, iv);
ByteBuffer inputBuffer = ByteBuffer.wrap(data);
ByteBuffer outputBuffer = ByteBuffer.allocate(data.length);
mCipher.doFinal(inputBuffer, outputBuffer);
return outputBuffer.array();
} catch (InvalidKeyException
| InvalidAlgorithmParameterException
| BadPaddingException
| ShortBufferException e) {
String errorMessage =
Cipher.ENCRYPT_MODE == opmode
? "Failed to encrypt data: "
: "Failed to decrypt data: ";
throw new IllegalArgumentException(errorMessage, e);
}
}
/**
* Encrypt padded data.
*
* @param paddedData the padded data to encrypt.
* @param keyBytes the encryption key.
* @param ivBytes the initialization vector (IV).
* @return the encrypted and padded data.
*/
public byte[] encrypt(byte[] paddedData, byte[] keyBytes, byte[] ivBytes) {
try {
return doCipherAction(paddedData, keyBytes, ivBytes, Cipher.ENCRYPT_MODE);
} catch (IllegalBlockSizeException e) {
throw new IllegalArgumentException("Failed to encrypt data: ", e);
}
}
/**
* Decrypt the encrypted and padded data.
*
* @param encryptedData the encrypted and padded data.
* @param keyBytes the decryption key.
* @param ivBytes the initialization vector (IV).
* @return the decrypted and padded data.
* @throws IllegalBlockSizeException if the total encryptedData length is not a multiple of
* block size.
*/
public byte[] decrypt(byte[] encryptedData, byte[] keyBytes, byte[] ivBytes)
throws IllegalBlockSizeException {
return doCipherAction(encryptedData, keyBytes, ivBytes, Cipher.DECRYPT_MODE);
}
@Override
protected IpSecAlgorithm buildIpSecAlgorithmWithKeyImpl(byte[] key) {
return new IpSecAlgorithm(getIpSecAlgorithmName(getAlgorithmId()), key);
}
private static byte[] concatenateByteArray(byte[] left, byte[] right) {
byte[] result = new byte[left.length + right.length];
System.arraycopy(left, 0, result, 0, left.length);
System.arraycopy(right, 0, result, left.length, right.length);
return result;
}
}