aboutsummaryrefslogtreecommitdiff
path: root/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java
diff options
context:
space:
mode:
Diffstat (limited to 'ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java')
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java415
1 files changed, 415 insertions, 0 deletions
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java
new file mode 100644
index 0000000..8059e44
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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.javacard.seprovider;
+
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+import javacard.security.CryptoException;
+import javacard.security.Key;
+import javacard.security.KeyAgreement;
+import javacard.security.PrivateKey;
+import javacard.security.Signature;
+import javacardx.crypto.AEADCipher;
+import javacardx.crypto.Cipher;
+
+/**
+ * This class contains the actual implementation of all the crypto operations. It internally uses
+ * the Javacard crypto library to perform the operations.
+ */
+public class KMOperationImpl implements KMOperation {
+
+ private static final byte ALG_TYPE_OFFSET = 0x00;
+ private static final byte PADDING_OFFSET = 0x01;
+ private static final byte PURPOSE_OFFSET = 0x02;
+ private static final byte BLOCK_MODE_OFFSET = 0x03;
+ private static final byte MAC_LENGTH_OFFSET = 0x04;
+ private final byte[] EMPTY = {};
+ // This will hold the length of the buffer stored inside the
+ // Java Card after the GCM update operation.
+ private static final byte AES_GCM_UPDATE_LEN_OFFSET = 0x05;
+ private static final byte PARAMETERS_LENGTH = 6;
+ private short[] parameters;
+ // Either one of Cipher/Signature instance is stored.
+ private Object[] operationInst;
+
+ public KMOperationImpl() {
+ parameters = JCSystem.makeTransientShortArray(PARAMETERS_LENGTH, JCSystem.CLEAR_ON_RESET);
+ operationInst = JCSystem.makeTransientObjectArray((short) 2, JCSystem.CLEAR_ON_RESET);
+ reset();
+ }
+
+ public short getPurpose() {
+ return parameters[PURPOSE_OFFSET];
+ }
+
+ public void setPurpose(short mode) {
+ parameters[PURPOSE_OFFSET] = mode;
+ }
+
+ public short getMacLength() {
+ return parameters[MAC_LENGTH_OFFSET];
+ }
+
+ public void setMacLength(short macLength) {
+ parameters[MAC_LENGTH_OFFSET] = macLength;
+ }
+
+ public short getPaddingAlgorithm() {
+ return parameters[PADDING_OFFSET];
+ }
+
+ public void setPaddingAlgorithm(short alg) {
+ parameters[PADDING_OFFSET] = alg;
+ }
+
+ public void setBlockMode(short mode) {
+ parameters[BLOCK_MODE_OFFSET] = mode;
+ }
+
+ public short getBlockMode() {
+ return parameters[BLOCK_MODE_OFFSET];
+ }
+
+ public short getAlgorithmType() {
+ return parameters[ALG_TYPE_OFFSET];
+ }
+
+ public void setAlgorithmType(short cipherAlg) {
+ parameters[ALG_TYPE_OFFSET] = cipherAlg;
+ }
+
+ public void setCipher(Cipher cipher) {
+ operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO] = cipher;
+ }
+
+ public void setSignature(Signature signer) {
+ operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO] = signer;
+ }
+
+ public void setKeyAgreement(KeyAgreement keyAgreement) {
+ operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO] = keyAgreement;
+ }
+
+ public boolean isResourceMatches(Object object, byte resourceType) {
+ return operationInst[resourceType] == object;
+ }
+
+ public void setKeyObject(KMKeyObject keyObject) {
+ operationInst[KMPoolManager.RESOURCE_TYPE_KEY] = keyObject;
+ }
+
+ public KMKeyObject getKeyObject() {
+ return (KMKeyObject) operationInst[KMPoolManager.RESOURCE_TYPE_KEY];
+ }
+
+ private void reset() {
+ operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO] = null;
+ operationInst[KMPoolManager.RESOURCE_TYPE_KEY] = null;
+ parameters[MAC_LENGTH_OFFSET] = KMType.INVALID_VALUE;
+ parameters[AES_GCM_UPDATE_LEN_OFFSET] = 0;
+ parameters[BLOCK_MODE_OFFSET] = KMType.INVALID_VALUE;
+ parameters[PURPOSE_OFFSET] = KMType.INVALID_VALUE;
+ parameters[ALG_TYPE_OFFSET] = KMType.INVALID_VALUE;
+ parameters[PADDING_OFFSET] = KMType.INVALID_VALUE;
+ }
+
+ private byte mapPurpose(short purpose) {
+ switch (purpose) {
+ case KMType.ENCRYPT:
+ return Cipher.MODE_ENCRYPT;
+ case KMType.DECRYPT:
+ return Cipher.MODE_DECRYPT;
+ case KMType.SIGN:
+ return Signature.MODE_SIGN;
+ case KMType.VERIFY:
+ return Signature.MODE_VERIFY;
+ }
+ return -1;
+ }
+
+ private void initSymmetricCipher(Key key, byte[] ivBuffer, short ivStart, short ivLength) {
+ Cipher symmCipher = (Cipher) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO];
+ byte cipherAlg = symmCipher.getAlgorithm();
+ switch (cipherAlg) {
+ case Cipher.ALG_AES_BLOCK_128_CBC_NOPAD:
+ case Cipher.ALG_AES_CTR:
+ symmCipher.init(key, mapPurpose(getPurpose()), ivBuffer, ivStart, ivLength);
+ break;
+ case Cipher.ALG_AES_BLOCK_128_ECB_NOPAD:
+ case Cipher.ALG_DES_ECB_NOPAD:
+ symmCipher.init(key, mapPurpose(getPurpose()));
+ break;
+ case Cipher.ALG_DES_CBC_NOPAD:
+ // Consume only 8 bytes of iv. the random number for iv is of 16 bytes.
+ // While sending back the iv, send only 8 bytes.
+ symmCipher.init(key, mapPurpose(getPurpose()), ivBuffer, ivStart, (short) 8);
+ break;
+ case AEADCipher.ALG_AES_GCM:
+ ((AEADCipher) symmCipher).init(key, mapPurpose(getPurpose()), ivBuffer, ivStart, ivLength);
+ break;
+ default: // This should never happen
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ break;
+ }
+ }
+
+ private void initRsa(Key key, short digest) {
+ if (KMType.SIGN == getPurpose()) {
+ byte mode;
+ if (getPaddingAlgorithm() == KMType.PADDING_NONE
+ || (getPaddingAlgorithm() == KMType.RSA_PKCS1_1_5_SIGN && digest == KMType.DIGEST_NONE)) {
+ mode = Cipher.MODE_DECRYPT;
+ } else {
+ mode = Signature.MODE_SIGN;
+ }
+ ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]).init((PrivateKey) key, mode);
+ } else { // RSA Cipher
+ ((Cipher) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+ .init((PrivateKey) key, mapPurpose(getPurpose()));
+ }
+ }
+
+ private void initEc(Key key) {
+ if (KMType.AGREE_KEY == getPurpose()) {
+ ((KeyAgreement) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]).init((PrivateKey) key);
+ } else {
+ ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+ .init((PrivateKey) key, mapPurpose(getPurpose()));
+ }
+ }
+
+ public void init(Key key, short digest, byte[] buf, short start, short length) {
+ switch (getAlgorithmType()) {
+ case KMType.AES:
+ case KMType.DES:
+ initSymmetricCipher(key, buf, start, length);
+ break;
+ case KMType.HMAC:
+ ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+ .init(key, mapPurpose(getPurpose()));
+ break;
+ case KMType.RSA:
+ initRsa(key, digest);
+ break;
+ case KMType.EC:
+ initEc(key);
+ break;
+ default: // This should never happen
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ break;
+ }
+ }
+
+ @Override
+ public short update(
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart) {
+ short len =
+ ((Cipher) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+ .update(inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart);
+ if (parameters[ALG_TYPE_OFFSET] == KMType.AES && parameters[BLOCK_MODE_OFFSET] == KMType.GCM) {
+ // Every time Block size data is stored as intermediate result.
+ parameters[AES_GCM_UPDATE_LEN_OFFSET] += (short) (inputDataLength - len);
+ }
+ return len;
+ }
+
+ @Override
+ public short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength) {
+ ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+ .update(inputDataBuf, inputDataStart, inputDataLength);
+ return 0;
+ }
+
+ private short finishKeyAgreement(
+ byte[] publicKey, short start, short len, byte[] output, short outputStart) {
+ return ((KeyAgreement) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+ .generateSecret(publicKey, start, len, output, outputStart);
+ }
+
+ private short finishCipher(
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLen,
+ byte[] outputDataBuf,
+ short outputDataStart) {
+ short len = 0;
+ try {
+ byte[] tmpArray = KMAndroidSEProvider.getInstance().tmpArray;
+ Cipher cipher = (Cipher) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO];
+ short cipherAlg = parameters[ALG_TYPE_OFFSET];
+ short blockMode = parameters[BLOCK_MODE_OFFSET];
+ short mode = parameters[PURPOSE_OFFSET];
+ short macLength = parameters[MAC_LENGTH_OFFSET];
+ short padding = parameters[PADDING_OFFSET];
+
+ if (cipherAlg == KMType.AES && blockMode == KMType.GCM) {
+ if (mode == KMType.DECRYPT) {
+ inputDataLen = (short) (inputDataLen - macLength);
+ }
+ } else if ((cipherAlg == KMType.DES || cipherAlg == KMType.AES)
+ && padding == KMType.PKCS7
+ && mode == KMType.ENCRYPT) {
+ byte blkSize = 16;
+ byte paddingBytes;
+ short inputlen = inputDataLen;
+ if (cipherAlg == KMType.DES) {
+ blkSize = 8;
+ }
+ // padding bytes
+ if (inputlen % blkSize == 0) {
+ paddingBytes = blkSize;
+ } else {
+ paddingBytes = (byte) (blkSize - (inputlen % blkSize));
+ }
+ // final len with padding
+ inputlen = (short) (inputlen + paddingBytes);
+ // intermediate buffer to copy input data+padding
+ // fill in the padding
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, inputlen, paddingBytes);
+ // copy the input data
+ Util.arrayCopyNonAtomic(inputDataBuf, inputDataStart, tmpArray, (short) 0, inputDataLen);
+ inputDataBuf = tmpArray;
+ inputDataLen = inputlen;
+ inputDataStart = 0;
+ }
+ len =
+ cipher.doFinal(
+ inputDataBuf, inputDataStart, inputDataLen, outputDataBuf, outputDataStart);
+ if ((cipherAlg == KMType.AES || cipherAlg == KMType.DES)
+ && padding == KMType.PKCS7
+ && mode == KMType.DECRYPT) {
+ byte blkSize = 16;
+ if (cipherAlg == KMType.DES) {
+ blkSize = 8;
+ }
+ if (len > 0) {
+ // verify if padding is corrupted.
+ byte paddingByte = outputDataBuf[(short) (outputDataStart + len - 1)];
+ // padding byte always should be <= block size
+ if ((short) paddingByte > blkSize || (short) paddingByte <= 0) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+
+ for (short j = 1; j <= paddingByte; ++j) {
+ if (outputDataBuf[(short) (outputDataStart + len - j)] != paddingByte) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ }
+ len = (short) (len - (short) paddingByte); // remove the padding bytes
+ }
+ } else if (cipherAlg == KMType.AES && blockMode == KMType.GCM) {
+ if (mode == KMType.ENCRYPT) {
+ len +=
+ ((AEADCipher) cipher)
+ .retrieveTag(outputDataBuf, (short) (outputDataStart + len), macLength);
+ } else {
+ boolean verified =
+ ((AEADCipher) cipher)
+ .verifyTag(
+ inputDataBuf, (short) (inputDataStart + inputDataLen), macLength, macLength);
+ if (!verified) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+ }
+ }
+ } finally {
+ KMAndroidSEProvider.getInstance().clean();
+ }
+ return len;
+ }
+
+ @Override
+ public short finish(
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLen,
+ byte[] outputDataBuf,
+ short outputDataStart) {
+ if (parameters[PURPOSE_OFFSET] == KMType.AGREE_KEY) {
+ return finishKeyAgreement(
+ inputDataBuf, inputDataStart, inputDataLen, outputDataBuf, outputDataStart);
+ } else {
+ return finishCipher(
+ inputDataBuf, inputDataStart, inputDataLen, outputDataBuf, outputDataStart);
+ }
+ }
+
+ @Override
+ public short sign(
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] signBuf,
+ short signStart) {
+ return ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+ .sign(inputDataBuf, inputDataStart, inputDataLength, signBuf, signStart);
+ }
+
+ @Override
+ public boolean verify(
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] signBuf,
+ short signStart,
+ short signLength) {
+ return ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+ .verify(inputDataBuf, inputDataStart, inputDataLength, signBuf, signStart, signLength);
+ }
+
+ @Override
+ public void abort() {
+ // Few simulators does not reset the Hmac signer instance on init so as
+ // a workaround to reset the hmac signer instance in case of abort/failure of the operation
+ // the corresponding sign / verify function is called.
+ if (operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO] != null) {
+ if ((parameters[PURPOSE_OFFSET] == KMType.SIGN || parameters[PURPOSE_OFFSET] == KMType.VERIFY)
+ && (((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]).getAlgorithm()
+ == Signature.ALG_HMAC_SHA_256)) {
+ Signature signer = (Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO];
+ try {
+ if (parameters[PURPOSE_OFFSET] == KMType.SIGN) {
+ signer.sign(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0);
+ } else {
+ signer.verify(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0, (short) 0);
+ }
+ } catch (Exception e) {
+ // Ignore.
+ }
+ }
+ }
+ reset();
+ }
+
+ @Override
+ public void updateAAD(byte[] dataBuf, short dataStart, short dataLength) {
+ ((AEADCipher) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+ .updateAAD(dataBuf, dataStart, dataLength);
+ }
+
+ @Override
+ public short getAESGCMOutputSize(short dataSize, short macLength) {
+ if (parameters[PURPOSE_OFFSET] == KMType.ENCRYPT) {
+ return (short) (parameters[AES_GCM_UPDATE_LEN_OFFSET] + dataSize + macLength);
+ } else {
+ return (short) (parameters[AES_GCM_UPDATE_LEN_OFFSET] + dataSize - macLength);
+ }
+ }
+}