aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 05:12:16 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 05:12:16 +0000
commit0b125613bce94daff1d77b0748a3088160b01843 (patch)
tree4ab55fda2f6ca57c7f124f95b46330ca4c7b4626
parent6fe42ca21f3dee3acd60a9ab62c06a107038e8dc (diff)
parent7d5f689cd2c1ea2dc579933678e7c622dc21ba1e (diff)
downloadlibese-android14-mainline-uwb-release.tar.gz
Change-Id: I0e040345afee15cf8ce675d6559ed702452485b0
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java619
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java1055
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java31
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java440
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java53
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java1578
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java198
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java17
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKeyPair.java55
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java138
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java32
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java46
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java53
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKey.java10
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKeyObject.java10
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java75
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java415
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java665
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java140
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java289
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java782
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java79
-rw-r--r--ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java30
-rw-r--r--ready_se/google/keymint/KM200/Applet/README.md16
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMArray.java165
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMAsn1Parser.java471
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMBignumTag.java110
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMBoolTag.java115
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMByteBlob.java142
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMByteTag.java145
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCose.java602
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java136
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java203
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseKey.java255
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseMap.java171
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java137
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java89
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java92
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java92
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java76
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java248
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java91
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMDecoder.java781
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEncoder.java777
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnum.java166
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java305
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumTag.java152
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMError.java134
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java171
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java110
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMInteger.java215
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java163
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java218
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java124
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java472
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java5146
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java1057
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMMap.java209
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMNInteger.java134
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMOperationState.java354
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMRemotelyProvisionedComponentDevice.java1758
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMRepository.java130
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMSemanticTag.java80
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMSimpleValue.java71
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMTag.java102
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMTextString.java80
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMType.java407
-rw-r--r--ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java129
-rw-r--r--ready_se/google/keymint/KM200/HAL/.clang-format10
-rw-r--r--ready_se/google/keymint/KM200/HAL/Android.bp140
-rw-r--r--ready_se/google/keymint/KM200/HAL/CborConverter.cpp512
-rw-r--r--ready_se/google/keymint/KM200/HAL/CborConverter.h139
-rw-r--r--ready_se/google/keymint/KM200/HAL/ITransport.h55
-rw-r--r--ready_se/google/keymint/KM200/HAL/JavacardKeyMintDevice.cpp454
-rw-r--r--ready_se/google/keymint/KM200/HAL/JavacardKeyMintDevice.h124
-rw-r--r--ready_se/google/keymint/KM200/HAL/JavacardKeyMintOperation.cpp300
-rw-r--r--ready_se/google/keymint/KM200/HAL/JavacardKeyMintOperation.h136
-rw-r--r--ready_se/google/keymint/KM200/HAL/JavacardRemotelyProvisionedComponentDevice.cpp284
-rw-r--r--ready_se/google/keymint/KM200/HAL/JavacardRemotelyProvisionedComponentDevice.h80
-rw-r--r--ready_se/google/keymint/KM200/HAL/JavacardSecureElement.cpp157
-rw-r--r--ready_se/google/keymint/KM200/HAL/JavacardSecureElement.h114
-rw-r--r--ready_se/google/keymint/KM200/HAL/JavacardSharedSecret.cpp61
-rw-r--r--ready_se/google/keymint/KM200/HAL/JavacardSharedSecret.h34
-rw-r--r--ready_se/google/keymint/KM200/HAL/LICENSE202
-rw-r--r--ready_se/google/keymint/KM200/HAL/METADATA3
-rw-r--r--ready_se/google/keymint/KM200/HAL/OWNERS3
-rw-r--r--ready_se/google/keymint/KM200/HAL/OmapiTransport.cpp276
-rw-r--r--ready_se/google/keymint/KM200/HAL/OmapiTransport.h65
-rw-r--r--ready_se/google/keymint/KM200/HAL/SocketTransport.cpp144
-rw-r--r--ready_se/google/keymint/KM200/HAL/SocketTransport.h55
-rw-r--r--ready_se/google/keymint/KM200/HAL/android.hardware.hardware_keystore.jc-strongbox-keymint.xml17
-rw-r--r--ready_se/google/keymint/KM200/HAL/android.hardware.security.keymint-service.strongbox.rc3
-rw-r--r--ready_se/google/keymint/KM200/HAL/android.hardware.security.keymint-service.strongbox.xml10
-rw-r--r--ready_se/google/keymint/KM200/HAL/android.hardware.security.sharedsecret-service.strongbox.xml6
-rw-r--r--ready_se/google/keymint/KM200/HAL/keymint_utils.cpp130
-rw-r--r--ready_se/google/keymint/KM200/HAL/keymint_utils.h47
-rw-r--r--ready_se/google/keymint/KM200/HAL/service.cpp95
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java622
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java1056
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java32
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java440
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java53
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java1572
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java198
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java17
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKeyPair.java55
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java138
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java32
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java46
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java53
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKey.java10
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKeyObject.java10
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java75
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java415
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java657
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java140
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java289
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java780
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java79
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java30
-rw-r--r--ready_se/google/keymint/KM300/Applet/README.md16
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMArray.java165
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMAsn1Parser.java471
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMBignumTag.java110
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMBoolTag.java115
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMByteBlob.java142
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMByteTag.java146
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCose.java513
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java136
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java203
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCoseKey.java255
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCoseMap.java171
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java137
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java89
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java92
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java92
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java76
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java248
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java91
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMDecoder.java781
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEncoder.java785
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEnum.java166
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java305
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEnumTag.java152
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMError.java134
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java171
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java110
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMInteger.java215
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java163
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java218
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java124
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java472
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java5172
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java1075
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMMap.java209
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMNInteger.java134
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMOperationState.java354
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMRemotelyProvisionedComponentDevice.java1396
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMRepository.java130
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMSemanticTag.java80
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMSimpleValue.java71
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMTag.java102
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMTextString.java80
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMType.java409
-rw-r--r--ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java129
-rw-r--r--ready_se/google/keymint/KM300/HAL/.clang-format10
-rw-r--r--ready_se/google/keymint/KM300/HAL/Android.bp117
-rw-r--r--ready_se/google/keymint/KM300/HAL/CborConverter.cpp521
-rw-r--r--ready_se/google/keymint/KM300/HAL/CborConverter.h142
-rw-r--r--ready_se/google/keymint/KM300/HAL/JavacardKeyMintDevice.cpp454
-rw-r--r--ready_se/google/keymint/KM300/HAL/JavacardKeyMintDevice.h124
-rw-r--r--ready_se/google/keymint/KM300/HAL/JavacardKeyMintOperation.cpp300
-rw-r--r--ready_se/google/keymint/KM300/HAL/JavacardKeyMintOperation.h136
-rw-r--r--ready_se/google/keymint/KM300/HAL/JavacardRemotelyProvisionedComponentDevice.cpp320
-rw-r--r--ready_se/google/keymint/KM300/HAL/JavacardRemotelyProvisionedComponentDevice.h80
-rw-r--r--ready_se/google/keymint/KM300/HAL/JavacardSecureElement.cpp157
-rw-r--r--ready_se/google/keymint/KM300/HAL/JavacardSecureElement.h116
-rw-r--r--ready_se/google/keymint/KM300/HAL/JavacardSharedSecret.cpp61
-rw-r--r--ready_se/google/keymint/KM300/HAL/JavacardSharedSecret.h34
-rw-r--r--ready_se/google/keymint/KM300/HAL/LICENSE202
-rw-r--r--ready_se/google/keymint/KM300/HAL/METADATA3
-rw-r--r--ready_se/google/keymint/KM300/HAL/OWNERS3
-rw-r--r--ready_se/google/keymint/KM300/HAL/android.hardware.hardware_keystore.jc-strongbox-keymint3.xml17
-rw-r--r--ready_se/google/keymint/KM300/HAL/android.hardware.security.keymint3-service.strongbox.rc3
-rw-r--r--ready_se/google/keymint/KM300/HAL/android.hardware.security.keymint3-service.strongbox.xml12
-rw-r--r--ready_se/google/keymint/KM300/HAL/android.hardware.security.sharedsecret3-service.strongbox.xml6
-rw-r--r--ready_se/google/keymint/KM300/HAL/keymint_utils.cpp130
-rw-r--r--ready_se/google/keymint/KM300/HAL/keymint_utils.h47
-rw-r--r--ready_se/google/keymint/KM300/HAL/service.cpp95
-rw-r--r--tools/ese_relay/ese_relay_fake.c3
190 files changed, 53563 insertions, 1 deletions
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java
new file mode 100644
index 0000000..7d84099
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java
@@ -0,0 +1,619 @@
+/*
+ * 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.keymaster;
+
+import com.android.javacard.seprovider.KMAndroidSEProvider;
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.APDU;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+import javacard.security.CryptoException;
+import org.globalplatform.upgrade.Element;
+import org.globalplatform.upgrade.OnUpgradeListener;
+import org.globalplatform.upgrade.UpgradeManager;
+
+/**
+ * This class extends from KMKeymasterApplet which is main entry point to receive apdu commands. All
+ * the provision commands are processed here and later the data is handed over to the KMDataStore
+ * class which stores the data in the flash memory.
+ */
+public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener {
+ // Magic number version stored along with provisioned data. This is used to differentiate
+ // between data before and after the magic number is used.
+ private static final byte KM_MAGIC_NUMBER = (byte) 0x82;
+ // MSB byte is for Major version and LSB byte is for Minor version.
+ public static final short KM_APPLET_PACKAGE_VERSION = 0x0301;
+ // This flag is used to know if card reset happened.
+ private static final short POWER_RESET_MASK_FLAG = (short) 0x4000;
+
+ // Provider specific Commands
+ private static final byte INS_KEYMINT_PROVIDER_APDU_START = 0x00;
+ private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 3;
+ // Commands 4, 5 and 6 are reserved for vendor usage.
+ private static final byte INS_GET_PROVISION_STATUS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 7;
+ // 0x08 was reserved for INS_INIT_STRONGBOX_CMD
+ // 0x09 was reserved for INS_SET_BOOT_ENDED_CMD earlier. it is unused now.
+ private static final byte INS_SE_FACTORY_PROVISIONING_LOCK_CMD =
+ INS_KEYMINT_PROVIDER_APDU_START + 10;
+ private static final byte INS_PROVISION_OEM_ROOT_PUBLIC_KEY_CMD =
+ INS_KEYMINT_PROVIDER_APDU_START + 11;
+ private static final byte INS_OEM_UNLOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 12;
+ private static final byte INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD =
+ INS_KEYMINT_PROVIDER_APDU_START + 13;
+ private static final byte INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD =
+ INS_KEYMINT_PROVIDER_APDU_START + 14;
+ private static final byte INS_PROVISION_PRESHARED_SECRET_CMD =
+ INS_KEYMINT_PROVIDER_APDU_START + 15;
+ private static final byte INS_SET_BOOT_PARAMS_CMD =
+ INS_KEYMINT_PROVIDER_APDU_START + 16; // Unused
+ private static final byte INS_OEM_LOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 17;
+ private static final byte INS_PROVISION_SECURE_BOOT_MODE_CMD =
+ INS_KEYMINT_PROVIDER_APDU_START + 18;
+
+ private static final byte INS_KEYMINT_PROVIDER_APDU_END = 0x1F;
+ // The length of the provisioned pre shared key.
+ public static final byte SHARED_SECRET_KEY_SIZE = 32;
+
+ // Version of the database which is used to differentiate between different version of the
+ // database.
+ protected short packageVersion;
+
+ KMAndroidSEApplet() {
+ super(new KMAndroidSEProvider());
+ packageVersion = KM_APPLET_PACKAGE_VERSION;
+ }
+
+ /**
+ * Installs this applet.
+ *
+ * @param bArray the array containing installation parameters
+ * @param bOffset the starting offset in bArray
+ * @param bLength the length in bytes of the parameter data in bArray
+ */
+ public static void install(byte[] bArray, short bOffset, byte bLength) {
+ new KMAndroidSEApplet().register(bArray, (short) (bOffset + 1), bArray[bOffset]);
+ }
+
+ public void handleDeviceBooted() {
+ if (seProvider.isBootSignalEventSupported() && seProvider.isDeviceRebooted()) {
+ kmDataStore.clearDeviceBootStatus();
+ super.reboot();
+ seProvider.clearDeviceBooted(true);
+ }
+ }
+
+ @Override
+ public void updateApduStatusFlags(short apduIns) {
+ apduStatusFlags[APDU_INCOMING_AND_RECEIVE_STATUS_INDEX] = 0;
+ apduStatusFlags[APDU_CASE4_COMMAND_STATUS_INDEX] = 1;
+ switch (apduIns) {
+ case INS_GET_PROVISION_STATUS_CMD:
+ case INS_SE_FACTORY_PROVISIONING_LOCK_CMD:
+ apduStatusFlags[APDU_CASE4_COMMAND_STATUS_INDEX] = 0;
+ break;
+ default:
+ super.updateApduStatusFlags(apduIns);
+ }
+ }
+
+ @Override
+ public void process(APDU apdu) {
+ try {
+ handleDeviceBooted();
+ // If this is select applet apdu which is selecting this applet then return
+ if (apdu.isISOInterindustryCLA()) {
+ if (selectingApplet()) {
+ return;
+ }
+ }
+ short apduIns = validateApdu(apdu);
+ if (apduIns == KMType.INVALID_VALUE) {
+ return;
+ }
+ updateApduStatusFlags(apduIns);
+ if (((KMAndroidSEProvider) seProvider).isPowerReset()) {
+ super.powerReset();
+ }
+
+ if (isCommandAllowed(apduIns)) {
+ switch (apduIns) {
+ case INS_PROVISION_ATTEST_IDS_CMD:
+ processProvisionAttestIdsCmd(apdu);
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_ATTEST_IDS);
+ sendResponse(apdu, KMError.OK);
+ break;
+
+ case INS_PROVISION_PRESHARED_SECRET_CMD:
+ processProvisionPreSharedSecretCmd(apdu);
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_PRESHARED_SECRET);
+ sendResponse(apdu, KMError.OK);
+ break;
+
+ case INS_GET_PROVISION_STATUS_CMD:
+ processGetProvisionStatusCmd(apdu);
+ break;
+
+ case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD:
+ processProvisionRkpDeviceUniqueKeyPair(apdu);
+ break;
+
+ case INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD:
+ processProvisionRkpAdditionalCertChain(apdu);
+ break;
+
+ case INS_SE_FACTORY_PROVISIONING_LOCK_CMD:
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_SE_LOCKED);
+ sendResponse(apdu, KMError.OK);
+ break;
+
+ case INS_PROVISION_OEM_ROOT_PUBLIC_KEY_CMD:
+ processProvisionOEMRootPublicKeyCmd(apdu);
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_OEM_PUBLIC_KEY);
+ sendResponse(apdu, KMError.OK);
+ break;
+
+ case INS_OEM_LOCK_PROVISIONING_CMD:
+ processOEMLockProvisionCmd(apdu);
+ break;
+
+ case INS_OEM_UNLOCK_PROVISIONING_CMD:
+ processOEMUnlockProvisionCmd(apdu);
+ break;
+
+ case INS_PROVISION_SECURE_BOOT_MODE_CMD:
+ processSecureBootCmd(apdu);
+ break;
+
+ default:
+ super.process(apdu);
+ break;
+ }
+ } else {
+ ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED);
+ }
+ } catch (KMException exception) {
+ sendResponse(apdu, KMException.reason());
+ } catch (ISOException exp) {
+ sendResponse(apdu, mapISOErrorToKMError(exp.getReason()));
+ } catch (CryptoException e) {
+ sendResponse(apdu, mapCryptoErrorToKMError(e.getReason()));
+ } catch (Exception e) {
+ sendResponse(apdu, KMError.GENERIC_UNKNOWN_ERROR);
+ } finally {
+ repository.clean();
+ }
+ }
+
+ private boolean isCommandAllowed(short apduIns) {
+ boolean result = true;
+ switch (apduIns) {
+ case INS_PROVISION_ATTEST_IDS_CMD:
+ case INS_PROVISION_PRESHARED_SECRET_CMD:
+ case INS_PROVISION_SECURE_BOOT_MODE_CMD:
+ case INS_PROVISION_OEM_ROOT_PUBLIC_KEY_CMD:
+ if (kmDataStore.isProvisionLocked()) {
+ result = false;
+ }
+ break;
+
+ case INS_OEM_UNLOCK_PROVISIONING_CMD:
+ if (!kmDataStore.isProvisionLocked()) {
+ result = false;
+ }
+ break;
+
+ case INS_SE_FACTORY_PROVISIONING_LOCK_CMD:
+ if (isSeFactoryProvisioningLocked() || !isSeFactoryProvisioningComplete()) {
+ result = false;
+ }
+ break;
+
+ case INS_OEM_LOCK_PROVISIONING_CMD:
+ // Allow lock only when
+ // 1. All the necessary provisioning commands are succcessfully executed
+ // 2. SE provision is locked
+ // 3. OEM Root Public is provisioned.
+ if (kmDataStore.isProvisionLocked()
+ || !(isProvisioningComplete() && isSeFactoryProvisioningLocked())) {
+ result = false;
+ }
+ break;
+
+ case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD:
+ case INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD:
+ if (isSeFactoryProvisioningLocked()) {
+ result = false;
+ }
+ break;
+
+ case INS_GET_PROVISION_STATUS_CMD:
+ break;
+
+ default:
+ // Allow other commands only if provision is completed.
+ if (!isProvisioningComplete()) {
+ result = false;
+ }
+ }
+ return result;
+ }
+
+ private boolean isSeFactoryProvisioningLocked() {
+ short pStatus = kmDataStore.getProvisionStatus();
+ boolean result = false;
+ if ((0 != (pStatus & PROVISION_STATUS_SE_LOCKED))) {
+ result = true;
+ }
+ return result;
+ }
+
+ private boolean isSeFactoryProvisioningComplete() {
+ short pStatus = kmDataStore.getProvisionStatus();
+ if (PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR
+ == (pStatus & PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR)) {
+ return true;
+ }
+ return false;
+ }
+
+ private void processSecureBootCmd(APDU apdu) {
+ short argsProto = KMArray.instance((short) 1);
+ KMArray.cast(argsProto).add((short) 0, KMInteger.exp());
+ short args = receiveIncoming(apdu, argsProto);
+ short val = KMInteger.cast(KMArray.cast(args).get((short) 0)).getShort();
+ if (val != 1 && val != 0) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ // Store secure boot mode value.
+ JCSystem.beginTransaction();
+ kmDataStore.secureBootMode = (byte) val;
+ JCSystem.commitTransaction();
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_SECURE_BOOT_MODE);
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private void processOEMUnlockProvisionCmd(APDU apdu) {
+ authenticateOEM(OEM_UNLOCK_PROVISION_VERIFICATION_LABEL, apdu);
+ kmDataStore.unlockProvision();
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private void processOEMLockProvisionCmd(APDU apdu) {
+ authenticateOEM(OEM_LOCK_PROVISION_VERIFICATION_LABEL, apdu);
+ // Enable the lock bit in provision status.
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_PROVISIONING_LOCKED);
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private void authenticateOEM(byte[] plainMsg, APDU apdu) {
+
+ tmpVariables[0] = KMArray.instance((short) 1);
+ KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp());
+ short args = receiveIncoming(apdu, tmpVariables[0]);
+ // Get the signature input.
+ short signature = KMArray.cast(args).get((short) 0);
+ byte[] oemPublicKey = kmDataStore.getOEMRootPublicKey();
+
+ if (!seProvider.ecVerify256(
+ oemPublicKey,
+ (short) 0,
+ (short) oemPublicKey.length,
+ plainMsg,
+ (short) 0,
+ (short) plainMsg.length,
+ KMByteBlob.cast(signature).getBuffer(),
+ KMByteBlob.cast(signature).getStartOff(),
+ KMByteBlob.cast(signature).length())) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+ }
+
+ private void processProvisionOEMRootPublicKeyCmd(APDU apdu) {
+ // Arguments
+ short keyparams = KMKeyParameters.exp();
+ short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT);
+ short blob = KMByteBlob.exp();
+ short argsProto = KMArray.instance((short) 3);
+ KMArray.cast(argsProto).add((short) 0, keyparams);
+ KMArray.cast(argsProto).add((short) 1, keyFormatPtr);
+ KMArray.cast(argsProto).add((short) 2, blob);
+ short args = receiveIncoming(apdu, argsProto);
+
+ // key params should have os patch, os version and verified root of trust
+ data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 0);
+ tmpVariables[0] = KMArray.cast(args).get((short) 1);
+ // Key format must be RAW format
+ byte keyFormat = KMEnum.cast(tmpVariables[0]).getVal();
+ if (keyFormat != KMType.RAW) {
+ KMException.throwIt(KMError.UNIMPLEMENTED);
+ }
+
+ // get algorithm - only EC keys expected
+ tmpVariables[0] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]);
+ if (tmpVariables[0] != KMType.EC) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ // get digest - only SHA256 supported
+ tmpVariables[0] =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]);
+ if (tmpVariables[0] != KMType.INVALID_VALUE) {
+ if (KMEnumArrayTag.cast(tmpVariables[0]).length() != 1) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ tmpVariables[0] = KMEnumArrayTag.cast(tmpVariables[0]).get((short) 0);
+ if (tmpVariables[0] != KMType.SHA2_256) {
+ KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+ }
+ } else {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ // Purpose should be VERIFY
+ tmpVariables[0] =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, data[KEY_PARAMETERS]);
+ if (tmpVariables[0] != KMType.INVALID_VALUE) {
+ if (KMEnumArrayTag.cast(tmpVariables[0]).length() != 1) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ tmpVariables[0] = KMEnumArrayTag.cast(tmpVariables[0]).get((short) 0);
+ if (tmpVariables[0] != KMType.VERIFY) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+ }
+ } else {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+
+ tmpVariables[0] = KMArray.cast(args).get((short) 2);
+ // persist OEM Root Public Key.
+ kmDataStore.persistOEMRootPublicKey(
+ KMByteBlob.cast(tmpVariables[0]).getBuffer(),
+ KMByteBlob.cast(tmpVariables[0]).getStartOff(),
+ KMByteBlob.cast(tmpVariables[0]).length());
+ }
+
+ private static void processProvisionRkpDeviceUniqueKeyPair(APDU apdu) {
+ // Re-purpose the apdu buffer as scratch pad.
+ byte[] scratchPad = apdu.getBuffer();
+ short arr = KMArray.instance((short) 1);
+ short coseKeyExp = KMCoseKey.exp();
+ KMArray.cast(arr).add((short) 0, coseKeyExp); // [ CoseKey ]
+ arr = receiveIncoming(apdu, arr);
+ // Get cose key.
+ short coseKey = KMArray.cast(arr).get((short) 0);
+ short pubKeyLen = KMCoseKey.cast(coseKey).getEcdsa256PublicKey(scratchPad, (short) 0);
+ short privKeyLen = KMCoseKey.cast(coseKey).getPrivateKey(scratchPad, pubKeyLen);
+ // Store the Device unique Key.
+ kmDataStore.createRkpDeviceUniqueKeyPair(
+ scratchPad, (short) 0, pubKeyLen, scratchPad, pubKeyLen, privKeyLen);
+ short bcc = generateBcc(false, scratchPad);
+ short len = KMKeymasterApplet.encodeToApduBuffer(bcc, scratchPad, (short) 0, MAX_COSE_BUF_SIZE);
+ kmDataStore.persistBootCertificateChain(scratchPad, (short) 0, len);
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR);
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private void processProvisionRkpAdditionalCertChain(APDU apdu) {
+ // X509 certificate chain is received as shown below:
+ /**
+ * x509CertChain = bstr .cbor UdsCerts
+ *
+ * <p>AdditionalDKSignatures = { * SignerName => DKCertChain } ; SignerName is a string
+ * identifier that indicates both the signing authority as ; well as the format of the
+ * DKCertChain SignerName = tstr
+ *
+ * <p>DKCertChain = [ 2* X509Certificate ; Root -> ... -> Leaf. "Root" is the vendor self-signed
+ * ; cert, "Leaf" contains DK_pub. There may also be ; intermediate certificates between Root
+ * and Leaf. ] ; A bstr containing a DER-encoded X.509 certificate (RSA, NIST P-curve, or edDSA)
+ * X509Certificate = bstr
+ */
+ // Store the cbor encoded UdsCerts as it is in the persistent memory so cbor decoding is
+ // required here.
+ byte[] srcBuffer = apdu.getBuffer();
+ short recvLen = apdu.setIncomingAndReceive();
+ apduStatusFlags[APDU_INCOMING_AND_RECEIVE_STATUS_INDEX] = 1;
+ short srcOffset = apdu.getOffsetCdata();
+ short bufferLength = apdu.getIncomingLength();
+ short bufferStartOffset = repository.allocReclaimableMemory(bufferLength);
+ short index = bufferStartOffset;
+ byte[] buffer = repository.getHeap();
+ while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) {
+ Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen);
+ index += recvLen;
+ recvLen = apdu.receiveBytes(srcOffset);
+ }
+ short byteHeaderLen =
+ decoder.readCertificateChainHeaderLen(buffer, bufferStartOffset, bufferLength);
+ kmDataStore.persistAdditionalCertChain(
+ buffer,
+ (short) (bufferStartOffset + byteHeaderLen),
+ (short) (bufferLength - byteHeaderLen));
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_ADDITIONAL_CERT_CHAIN);
+ // reclaim memory
+ repository.reclaimMemory(bufferLength);
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private void processProvisionAttestIdsCmd(APDU apdu) {
+ short keyparams = KMKeyParameters.exp();
+ short cmd = KMArray.instance((short) 1);
+ KMArray.cast(cmd).add((short) 0, keyparams);
+ short args = receiveIncoming(apdu, cmd);
+
+ short attData = KMArray.cast(args).get((short) 0);
+ // persist attestation Ids - if any is missing then exception occurs
+ setAttestationIds(attData);
+ }
+
+ public void setAttestationIds(short attIdVals) {
+ KMKeyParameters instParam = KMKeyParameters.cast(attIdVals);
+ KMArray vals = KMArray.cast(instParam.getVals());
+ short index = 0;
+ short length = vals.length();
+ short key;
+ short type;
+ short obj;
+ while (index < length) {
+ obj = vals.get(index);
+ key = KMTag.getKey(obj);
+ type = KMTag.getTagType(obj);
+
+ if (KMType.BYTES_TAG != type) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ obj = KMByteTag.cast(obj).getValue();
+ if (KMByteBlob.cast(obj).length() > KMConfigurations.MAX_ATTESTATION_IDS_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ kmDataStore.setAttestationId(
+ key,
+ KMByteBlob.cast(obj).getBuffer(),
+ KMByteBlob.cast(obj).getStartOff(),
+ KMByteBlob.cast(obj).length());
+ index++;
+ }
+ }
+
+ private void processProvisionPreSharedSecretCmd(APDU apdu) {
+ short blob = KMByteBlob.exp();
+ short argsProto = KMArray.instance((short) 1);
+ KMArray.cast(argsProto).add((short) 0, blob);
+ short args = receiveIncoming(apdu, argsProto);
+
+ short val = KMArray.cast(args).get((short) 0);
+
+ if (val != KMType.INVALID_VALUE && KMByteBlob.cast(val).length() != SHARED_SECRET_KEY_SIZE) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ // Persist shared Hmac.
+ kmDataStore.createPresharedKey(
+ KMByteBlob.cast(val).getBuffer(),
+ KMByteBlob.cast(val).getStartOff(),
+ KMByteBlob.cast(val).length());
+ }
+
+ // This function masks the error code with POWER_RESET_MASK_FLAG
+ // in case if card reset event occurred. The clients of the Applet
+ // has to extract the power reset status from the error code and
+ // process accordingly.
+ private static short buildErrorStatus(short err) {
+ short int32Ptr = KMInteger.instance((short) 4);
+ short powerResetStatus = 0;
+ if (((KMAndroidSEProvider) seProvider).isPowerReset()) {
+ powerResetStatus = POWER_RESET_MASK_FLAG;
+ }
+
+ Util.setShort(
+ KMInteger.cast(int32Ptr).getBuffer(),
+ KMInteger.cast(int32Ptr).getStartOff(),
+ powerResetStatus);
+
+ Util.setShort(
+ KMInteger.cast(int32Ptr).getBuffer(),
+ (short) (KMInteger.cast(int32Ptr).getStartOff() + 2),
+ err);
+ // reset power reset status flag to its default value.
+ // repository.restorePowerResetStatus(); //TODO
+ return int32Ptr;
+ }
+
+ private void processGetProvisionStatusCmd(APDU apdu) {
+ byte[] scratchpad = apdu.getBuffer();
+ short pStatus = kmDataStore.getProvisionStatus();
+ Util.setShort(scratchpad, (short) 0, pStatus);
+ short resp = KMArray.instance((short) 2);
+ KMArray.cast(resp).add((short) 0, buildErrorStatus(KMError.OK));
+ KMArray.cast(resp).add((short) 1, KMInteger.instance(scratchpad, (short) 0, (short) 2));
+ sendOutgoing(apdu, resp);
+ }
+
+ private boolean isProvisioningComplete() {
+ short pStatus = kmDataStore.getProvisionStatus();
+ short pCompleteStatus =
+ PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR
+ | PROVISION_STATUS_PRESHARED_SECRET
+ | PROVISION_STATUS_ATTEST_IDS
+ | PROVISION_STATUS_OEM_PUBLIC_KEY
+ | PROVISION_STATUS_SECURE_BOOT_MODE;
+ if (kmDataStore.isProvisionLocked() || (pCompleteStatus == (pStatus & pCompleteStatus))) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onCleanup() {}
+
+ @Override
+ public void onConsolidate() {}
+
+ private boolean isUpgradeAllowed(short oldVersion) {
+ boolean upgradeAllowed = false;
+ // Downgrade of the Applet is not allowed.
+ if (KM_APPLET_PACKAGE_VERSION >= oldVersion) {
+ upgradeAllowed = true;
+ }
+ return upgradeAllowed;
+ }
+
+ @Override
+ public void onRestore(Element element) {
+ element.initRead();
+ byte magicNumber = element.readByte();
+ if (magicNumber != KM_MAGIC_NUMBER) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short oldPackageVersion = element.readShort();
+ // Validate version.
+ if (!isUpgradeAllowed(oldPackageVersion)) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ kmDataStore.onRestore(element, oldPackageVersion, KM_APPLET_PACKAGE_VERSION);
+ }
+
+ @Override
+ public Element onSave() {
+ short primitiveCount = 3;
+ primitiveCount += kmDataStore.getBackupPrimitiveByteCount();
+ short objectCount = kmDataStore.getBackupObjectCount();
+ // Create element.
+ Element element =
+ UpgradeManager.createElement(Element.TYPE_SIMPLE, primitiveCount, objectCount);
+
+ element.write(KM_MAGIC_NUMBER);
+ element.write(packageVersion);
+ kmDataStore.onSave(element);
+ return element;
+ }
+
+ private short validateApdu(APDU apdu) {
+ // Read the apdu header and buffer.
+ byte[] apduBuffer = apdu.getBuffer();
+ short P1P2 = Util.getShort(apduBuffer, ISO7816.OFFSET_P1);
+
+ // Validate CLA
+ if (!apdu.isValidCLA()) {
+ ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
+ }
+
+ // Validate P1P2.
+ if (P1P2 != KMKeymasterApplet.KM_HAL_VERSION) {
+ sendResponse(apdu, KMError.INVALID_P1P2);
+ return KMType.INVALID_VALUE;
+ }
+ return apduBuffer[ISO7816.OFFSET_INS];
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java
new file mode 100644
index 0000000..e7d8252
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java
@@ -0,0 +1,1055 @@
+/*
+ * 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.keymaster;
+
+import com.android.javacard.seprovider.KMAESKey;
+import com.android.javacard.seprovider.KMAttestationCert;
+import com.android.javacard.seprovider.KMException;
+import com.android.javacard.seprovider.KMKey;
+import com.android.javacard.seprovider.KMSEProvider;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * The class encodes strongbox generated and signed attestation certificates. It only encodes the
+ * required fields of the certificates. This class is not meant to be a generic X509 cert encoder.
+ * Any fields that are fixed are added as byte arrays. Extensions are encoded as per the values. The
+ * certificate is assembled with leafs first and then the sequences.
+ */
+public class KMAttestationCertImpl implements KMAttestationCert {
+
+ // The maximum size of the either software or hardware parameters.
+ private static final byte MAX_PARAMS = 30;
+ // DER encoded object identifiers required by the cert.
+ // rsaEncryption - 1.2.840.113549.1.1.1
+ private static final byte[] rsaEncryption = {
+ 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01
+ };
+ // ecPublicKey - 1.2.840.10045.2.1
+ private static final byte[] eccPubKey = {
+ 0x06, 0x07, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x02, 0x01
+ };
+ // prime256v1 curve - 1.2.840.10045.3.1.7
+ private static final byte[] prime256v1 = {
+ 0x06, 0x08, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x03, 0x01, 0x07
+ };
+ // Key Usage Extn - 2.5.29.15
+ private static final byte[] keyUsageExtn = {0x06, 0x03, 0x55, 0x1D, 0x0F};
+ // Android Extn - 1.3.6.1.4.1.11129.2.1.17
+ private static final byte[] androidExtn = {
+ 0x06, 0x0A, 0X2B, 0X06, 0X01, 0X04, 0X01, (byte) 0XD6, 0X79, 0X02, 0X01, 0X11
+ };
+ // The length of the RSA signature.
+ private static final short RSA_SIG_LEN = 256;
+ // The maximum length of the ECDSA signature.
+ private static final byte ECDSA_MAX_SIG_LEN = 72;
+ // Signature algorithm identifier - ecdsaWithSha256 - 1.2.840.10045.4.3.2
+ // SEQUENCE of alg OBJ ID and parameters = NULL.
+ private static final byte[] X509EcdsaSignAlgIdentifier = {
+ 0x30, 0x0A, 0x06, 0x08, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, (byte) 0x3D, 0x04, 0x03, 0x02
+ };
+ // Signature algorithm identifier - sha256WithRSAEncryption - 1.2.840.113549.1.1.11
+ // SEQUENCE of alg OBJ ID and parameters = NULL.
+ private static final byte[] X509RsaSignAlgIdentifier = {
+ 0x30,
+ 0x0D,
+ 0x06,
+ 0x09,
+ 0x2A,
+ (byte) 0x86,
+ 0x48,
+ (byte) 0x86,
+ (byte) 0xF7,
+ 0x0D,
+ 0x01,
+ 0x01,
+ 0x0B,
+ 0x05,
+ 0x00
+ };
+
+ // Below are the allowed softwareEnforced Authorization tags inside the attestation certificate's
+ // extension.
+ private static final short[] swTagIds = {
+ KMType.ATTESTATION_APPLICATION_ID,
+ KMType.CREATION_DATETIME,
+ KMType.ALLOW_WHILE_ON_BODY,
+ KMType.USAGE_COUNT_LIMIT,
+ KMType.USAGE_EXPIRE_DATETIME,
+ KMType.ORIGINATION_EXPIRE_DATETIME,
+ KMType.ACTIVE_DATETIME,
+ };
+
+ // Below are the allowed hardwareEnforced Authorization tags inside the attestation certificate's
+ // extension.
+ private static final short[] hwTagIds = {
+ KMType.BOOT_PATCH_LEVEL,
+ KMType.VENDOR_PATCH_LEVEL,
+ KMType.ATTESTATION_ID_MODEL,
+ KMType.ATTESTATION_ID_MANUFACTURER,
+ KMType.ATTESTATION_ID_MEID,
+ KMType.ATTESTATION_ID_IMEI,
+ KMType.ATTESTATION_ID_SERIAL,
+ KMType.ATTESTATION_ID_PRODUCT,
+ KMType.ATTESTATION_ID_DEVICE,
+ KMType.ATTESTATION_ID_BRAND,
+ KMType.OS_PATCH_LEVEL,
+ KMType.OS_VERSION,
+ KMType.ROOT_OF_TRUST,
+ KMType.ORIGIN,
+ KMType.UNLOCKED_DEVICE_REQUIRED,
+ KMType.TRUSTED_CONFIRMATION_REQUIRED,
+ KMType.AUTH_TIMEOUT,
+ KMType.USER_AUTH_TYPE,
+ KMType.NO_AUTH_REQUIRED,
+ KMType.EARLY_BOOT_ONLY,
+ KMType.ROLLBACK_RESISTANCE,
+ KMType.RSA_OAEP_MGF_DIGEST,
+ KMType.RSA_PUBLIC_EXPONENT,
+ KMType.ECCURVE,
+ KMType.PADDING,
+ KMType.DIGEST,
+ KMType.KEYSIZE,
+ KMType.ALGORITHM,
+ KMType.PURPOSE
+ };
+ // Below are the constants for the key usage extension.
+ private static final byte keyUsageSign = (byte) 0x80; // 0 bit
+ private static final byte keyUsageKeyEncipher = (byte) 0x20; // 2nd- bit
+ private static final byte keyUsageDataEncipher = (byte) 0x10; // 3rd- bit
+ private static final byte keyUsageKeyAgreement = (byte) 0x08; // 4th- bit
+ private static final byte keyUsageCertSign = (byte) 0x04; // 5th- bit
+ // KeyMint HAL Version constant.
+ private static final short KEYMINT_VERSION = 200;
+ // Attestation version constant.
+ private static final short ATTESTATION_VERSION = 200;
+ // The X.509 version as per rfc5280#section-4.1.2.1
+ private static final byte X509_VERSION = (byte) 0x02;
+
+ // Buffer indexes in transient array
+ private static final byte NUM_INDEX_ENTRIES = 21;
+ private static final byte CERT_START = (byte) 0;
+ private static final byte CERT_LENGTH = (byte) 1;
+ private static final byte TBS_START = (byte) 2;
+ private static final byte TBS_LENGTH = (byte) 3;
+ private static final byte BUF_START = (byte) 4;
+ private static final byte BUF_LENGTH = (byte) 5;
+ private static final byte SW_PARAM_INDEX = (byte) 6;
+ private static final byte HW_PARAM_INDEX = (byte) 7;
+ // Data indexes in transient array
+ private static final byte STACK_PTR = (byte) 8;
+ private static final byte UNIQUE_ID = (byte) 9;
+ private static final byte ATT_CHALLENGE = (byte) 10;
+ private static final byte NOT_BEFORE = (byte) 11;
+ private static final byte NOT_AFTER = (byte) 12;
+ private static final byte PUB_KEY = (byte) 13;
+ private static final byte VERIFIED_BOOT_KEY = (byte) 14;
+ private static final byte VERIFIED_HASH = (byte) 15;
+ private static final byte ISSUER = (byte) 16;
+ private static final byte SUBJECT_NAME = (byte) 17;
+ private static final byte SERIAL_NUMBER = (byte) 18;
+ private static final byte CERT_ATT_KEY_SECRET = (byte) 19;
+ private static final byte CERT_ATT_KEY_RSA_PUB_MOD = (byte) 20;
+ // State indexes in transient array
+ private static final byte NUM_STATE_ENTRIES = 7;
+ private static final byte KEY_USAGE = (byte) 0;
+ private static final byte UNUSED_BITS = (byte) 1;
+ private static final byte DEVICE_LOCKED = (byte) 2;
+ private static final byte VERIFIED_STATE = (byte) 3;
+ private static final byte CERT_MODE = (byte) 4;
+ private static final byte RSA_CERT = (byte) 5;
+ private static final byte CERT_RSA_SIGN = (byte) 6;
+
+ private static KMAttestationCert inst;
+ private static KMSEProvider seProvider;
+
+ private static short[] indexes;
+ private static byte[] states;
+
+ private static byte[] stack;
+ private static short[] swParams;
+ private static short[] hwParams;
+ // The maximum size of the serial number.
+ private static final byte SERIAL_NUM_MAX_LEN = 20;
+
+ private KMAttestationCertImpl() {}
+
+ public static KMAttestationCert instance(boolean rsaCert, KMSEProvider provider) {
+ if (inst == null) {
+ inst = new KMAttestationCertImpl();
+ seProvider = provider;
+
+ // Allocate transient memory
+ indexes = JCSystem.makeTransientShortArray(NUM_INDEX_ENTRIES, JCSystem.CLEAR_ON_RESET);
+ states = JCSystem.makeTransientByteArray(NUM_STATE_ENTRIES, JCSystem.CLEAR_ON_RESET);
+ swParams = JCSystem.makeTransientShortArray(MAX_PARAMS, JCSystem.CLEAR_ON_RESET);
+ hwParams = JCSystem.makeTransientShortArray(MAX_PARAMS, JCSystem.CLEAR_ON_RESET);
+ }
+ init(rsaCert);
+ return inst;
+ }
+
+ private static void init(boolean rsaCert) {
+ for (short i = 0; i < NUM_INDEX_ENTRIES; i++) {
+ indexes[i] = 0;
+ }
+ Util.arrayFillNonAtomic(states, (short) 0, NUM_STATE_ENTRIES, (byte) 0);
+ stack = null;
+ states[CERT_MODE] = KMType.NO_CERT;
+ states[UNUSED_BITS] = 8;
+ states[RSA_CERT] = rsaCert ? (byte) 1 : (byte) 0;
+ states[CERT_RSA_SIGN] = 1;
+ indexes[CERT_ATT_KEY_SECRET] = KMType.INVALID_VALUE;
+ indexes[CERT_ATT_KEY_RSA_PUB_MOD] = KMType.INVALID_VALUE;
+ indexes[ISSUER] = KMType.INVALID_VALUE;
+ indexes[SUBJECT_NAME] = KMType.INVALID_VALUE;
+ indexes[SERIAL_NUMBER] = KMType.INVALID_VALUE;
+ }
+
+ @Override
+ public KMAttestationCert verifiedBootHash(short obj) {
+ indexes[VERIFIED_HASH] = obj;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert verifiedBootKey(short obj) {
+ indexes[VERIFIED_BOOT_KEY] = obj;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert verifiedBootState(byte val) {
+ states[VERIFIED_STATE] = val;
+ return this;
+ }
+
+ private KMAttestationCert uniqueId(short obj) {
+ indexes[UNIQUE_ID] = obj;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert notBefore(short obj, boolean derEncoded, byte[] scratchpad) {
+ if (!derEncoded) {
+ // convert milliseconds to UTC date
+ indexes[NOT_BEFORE] = KMUtils.convertToDate(obj, scratchpad, true);
+ } else {
+ indexes[NOT_BEFORE] =
+ KMByteBlob.instance(
+ KMByteBlob.cast(obj).getBuffer(),
+ KMByteBlob.cast(obj).getStartOff(),
+ KMByteBlob.cast(obj).length());
+ }
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert notAfter(
+ short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad) {
+ if (!derEncoded) {
+ if (usageExpiryTimeObj != KMType.INVALID_VALUE) {
+ // compare if the expiry time is greater then 2050 then use generalized
+ // time format else use utc time format.
+ short tmpVar = KMInteger.uint_64(KMUtils.firstJan2050, (short) 0);
+ if (KMInteger.compare(usageExpiryTimeObj, tmpVar) >= 0) {
+ usageExpiryTimeObj = KMUtils.convertToDate(usageExpiryTimeObj, scratchPad, false);
+ } else {
+ usageExpiryTimeObj = KMUtils.convertToDate(usageExpiryTimeObj, scratchPad, true);
+ }
+ indexes[NOT_AFTER] = usageExpiryTimeObj;
+ } else {
+ // notAfter = certExpirtyTimeObj;
+ }
+ } else {
+ indexes[NOT_AFTER] = usageExpiryTimeObj;
+ }
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert deviceLocked(boolean val) {
+ if (val) {
+ states[DEVICE_LOCKED] = (byte) 0xFF;
+ } else {
+ states[DEVICE_LOCKED] = 0;
+ }
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert publicKey(short obj) {
+ indexes[PUB_KEY] = obj;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert attestationChallenge(short obj) {
+ indexes[ATT_CHALLENGE] = obj;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert extensionTag(short tag, boolean hwEnforced) {
+ if (hwEnforced) {
+ hwParams[indexes[HW_PARAM_INDEX]] = tag;
+ indexes[HW_PARAM_INDEX]++;
+ } else {
+ swParams[indexes[SW_PARAM_INDEX]] = tag;
+ indexes[SW_PARAM_INDEX]++;
+ }
+ if (KMTag.getKey(tag) == KMType.PURPOSE) {
+ createKeyUsage(tag);
+ }
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert issuer(short obj) {
+ indexes[ISSUER] = obj;
+ return this;
+ }
+
+ private void createKeyUsage(short tag) {
+ short len = KMEnumArrayTag.cast(tag).length();
+ byte index = 0;
+ while (index < len) {
+ if (KMEnumArrayTag.cast(tag).get(index) == KMType.SIGN) {
+ states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageSign);
+ } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.WRAP_KEY) {
+ states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageKeyEncipher);
+ } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.DECRYPT) {
+ states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageDataEncipher);
+ } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.AGREE_KEY) {
+ states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageKeyAgreement);
+ } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.ATTEST_KEY) {
+ states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageCertSign);
+ }
+ index++;
+ }
+ index = states[KEY_USAGE];
+ while (index != 0) {
+ index = (byte) (index << 1);
+ states[UNUSED_BITS]--;
+ }
+ }
+
+ private static void pushTbsCert(boolean rsaCert, boolean rsa) {
+ short last = indexes[STACK_PTR];
+ pushExtensions();
+ // subject public key info
+ if (rsaCert) {
+ pushRsaSubjectKeyInfo();
+ } else {
+ pushEccSubjectKeyInfo();
+ }
+ // subject
+ pushBytes(
+ KMByteBlob.cast(indexes[SUBJECT_NAME]).getBuffer(),
+ KMByteBlob.cast(indexes[SUBJECT_NAME]).getStartOff(),
+ KMByteBlob.cast(indexes[SUBJECT_NAME]).length());
+ pushValidity();
+ // issuer - der encoded
+ pushBytes(
+ KMByteBlob.cast(indexes[ISSUER]).getBuffer(),
+ KMByteBlob.cast(indexes[ISSUER]).getStartOff(),
+ KMByteBlob.cast(indexes[ISSUER]).length());
+ // Algorithm Id
+ if (rsa) {
+ pushAlgorithmId(X509RsaSignAlgIdentifier);
+ } else {
+ pushAlgorithmId(X509EcdsaSignAlgIdentifier);
+ }
+ // Serial Number
+ pushBytes(
+ KMByteBlob.cast(indexes[SERIAL_NUMBER]).getBuffer(),
+ KMByteBlob.cast(indexes[SERIAL_NUMBER]).getStartOff(),
+ KMByteBlob.cast(indexes[SERIAL_NUMBER]).length());
+ pushIntegerHeader(KMByteBlob.cast(indexes[SERIAL_NUMBER]).length());
+ // Version
+ pushByte(X509_VERSION);
+ pushIntegerHeader((short) 1);
+ pushByte((byte) 0x03);
+ pushByte((byte) 0xA0);
+ // Finally sequence header.
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushExtensions() {
+ short last = indexes[STACK_PTR];
+ // Push KeyUsage extension
+ if (states[KEY_USAGE] != 0) {
+ pushKeyUsage(states[KEY_USAGE], states[UNUSED_BITS]);
+ }
+ if (states[CERT_MODE] == KMType.ATTESTATION_CERT) {
+ pushKeyDescription();
+ }
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ // Extensions have explicit tag of [3]
+ pushLength((short) (last - indexes[STACK_PTR]));
+ pushByte((byte) 0xA3);
+ }
+
+ // Time SEQUENCE{UTCTime, UTC or Generalized Time)
+ private static void pushValidity() {
+ short last = indexes[STACK_PTR];
+ if (indexes[NOT_AFTER] != 0) {
+ pushBytes(
+ KMByteBlob.cast(indexes[NOT_AFTER]).getBuffer(),
+ KMByteBlob.cast(indexes[NOT_AFTER]).getStartOff(),
+ KMByteBlob.cast(indexes[NOT_AFTER]).length());
+ } else {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ pushTimeHeader(KMByteBlob.cast(indexes[NOT_AFTER]).length());
+ pushBytes(
+ KMByteBlob.cast(indexes[NOT_BEFORE]).getBuffer(),
+ KMByteBlob.cast(indexes[NOT_BEFORE]).getStartOff(),
+ KMByteBlob.cast(indexes[NOT_BEFORE]).length());
+ pushTimeHeader(KMByteBlob.cast(indexes[NOT_BEFORE]).length());
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushTimeHeader(short len) {
+ if (len == 13) { // UTC Time
+ pushLength((short) 0x0D);
+ pushByte((byte) 0x17);
+ } else if (len == 15) { // Generalized Time
+ pushLength((short) 0x0F);
+ pushByte((byte) 0x18);
+ } else {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ }
+
+ // SEQUENCE{SEQUENCE{algId, NULL}, bitString{SEQUENCE{ modulus as positive integer, public
+ // exponent
+ // as positive integer}
+ private static void pushRsaSubjectKeyInfo() {
+ short last = indexes[STACK_PTR];
+ pushBytes(KMKeymasterApplet.F4, (short) 0, (short) KMKeymasterApplet.F4.length);
+ pushIntegerHeader((short) KMKeymasterApplet.F4.length);
+ pushBytes(
+ KMByteBlob.cast(indexes[PUB_KEY]).getBuffer(),
+ KMByteBlob.cast(indexes[PUB_KEY]).getStartOff(),
+ KMByteBlob.cast(indexes[PUB_KEY]).length());
+
+ // encode modulus as positive if the MSB is 1.
+ if (KMByteBlob.cast(indexes[PUB_KEY]).get((short) 0) < 0) {
+ pushByte((byte) 0x00);
+ pushIntegerHeader((short) (KMByteBlob.cast(indexes[PUB_KEY]).length() + 1));
+ } else {
+ pushIntegerHeader(KMByteBlob.cast(indexes[PUB_KEY]).length());
+ }
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ pushBitStringHeader((byte) 0x00, (short) (last - indexes[STACK_PTR]));
+ pushRsaEncryption();
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ // SEQUENCE{SEQUENCE{ecPubKey, prime256v1}, bitString{pubKey}}
+ private static void pushEccSubjectKeyInfo() {
+ short last = indexes[STACK_PTR];
+ pushBytes(
+ KMByteBlob.cast(indexes[PUB_KEY]).getBuffer(),
+ KMByteBlob.cast(indexes[PUB_KEY]).getStartOff(),
+ KMByteBlob.cast(indexes[PUB_KEY]).length());
+ pushBitStringHeader((byte) 0x00, KMByteBlob.cast(indexes[PUB_KEY]).length());
+ pushEcDsa();
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushEcDsa() {
+ short last = indexes[STACK_PTR];
+ pushBytes(prime256v1, (short) 0, (short) prime256v1.length);
+ pushBytes(eccPubKey, (short) 0, (short) eccPubKey.length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushRsaEncryption() {
+ short last = indexes[STACK_PTR];
+ pushNullHeader();
+ pushBytes(rsaEncryption, (short) 0, (short) rsaEncryption.length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ // KeyDescription ::= SEQUENCE {
+ // attestationVersion INTEGER, # Value 200
+ // attestationSecurityLevel SecurityLevel, # See below
+ // keymasterVersion INTEGER, # Value 200
+ // keymasterSecurityLevel SecurityLevel, # See below
+ // attestationChallenge OCTET_STRING, # Tag::ATTESTATION_CHALLENGE from attestParams
+ // uniqueId OCTET_STRING, # Empty unless key has Tag::INCLUDE_UNIQUE_ID
+ // softwareEnforced AuthorizationList, # See below
+ // hardwareEnforced AuthorizationList, # See below
+ // }
+ private static void pushKeyDescription() {
+ short last = indexes[STACK_PTR];
+ pushHWParams();
+ pushSWParams();
+ if (indexes[UNIQUE_ID] != 0) {
+ pushOctetString(
+ KMByteBlob.cast(indexes[UNIQUE_ID]).getBuffer(),
+ KMByteBlob.cast(indexes[UNIQUE_ID]).getStartOff(),
+ KMByteBlob.cast(indexes[UNIQUE_ID]).length());
+ } else {
+ pushOctetStringHeader((short) 0);
+ }
+ pushOctetString(
+ KMByteBlob.cast(indexes[ATT_CHALLENGE]).getBuffer(),
+ KMByteBlob.cast(indexes[ATT_CHALLENGE]).getStartOff(),
+ KMByteBlob.cast(indexes[ATT_CHALLENGE]).length());
+ pushEnumerated(KMType.STRONGBOX);
+ pushShort(KEYMINT_VERSION);
+ pushIntegerHeader((short) 2);
+ pushEnumerated(KMType.STRONGBOX);
+ pushShort(ATTESTATION_VERSION);
+ pushIntegerHeader((short) 2);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ pushOctetStringHeader((short) (last - indexes[STACK_PTR]));
+ pushBytes(androidExtn, (short) 0, (short) androidExtn.length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushSWParams() {
+ short last = indexes[STACK_PTR];
+ byte index = 0;
+ short length = (short) swTagIds.length;
+ do {
+ pushParams(swParams, indexes[SW_PARAM_INDEX], swTagIds[index]);
+ } while (++index < length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushHWParams() {
+ short last = indexes[STACK_PTR];
+ byte index = 0;
+ short length = (short) hwTagIds.length;
+ do {
+ if (hwTagIds[index] == KMType.ROOT_OF_TRUST) {
+ pushRoT();
+ continue;
+ }
+ if (pushParams(hwParams, indexes[HW_PARAM_INDEX], hwTagIds[index])) {
+ continue;
+ }
+ } while (++index < length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static boolean pushParams(short[] params, short len, short tagId) {
+ short index = 0;
+ while (index < len) {
+ if (tagId == KMTag.getKey(params[index])) {
+ pushTag(params[index]);
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ private static void pushTag(short tag) {
+ short type = KMTag.getTagType(tag);
+ short tagId = KMTag.getKey(tag);
+ short val;
+ switch (type) {
+ case KMType.BYTES_TAG:
+ val = KMByteTag.cast(tag).getValue();
+ pushBytesTag(
+ tagId,
+ KMByteBlob.cast(val).getBuffer(),
+ KMByteBlob.cast(val).getStartOff(),
+ KMByteBlob.cast(val).length());
+ break;
+ case KMType.ENUM_TAG:
+ val = KMEnumTag.cast(tag).getValue();
+ pushEnumTag(tagId, (byte) val);
+ break;
+ case KMType.ENUM_ARRAY_TAG:
+ val = KMEnumArrayTag.cast(tag).getValues();
+ pushEnumArrayTag(
+ tagId,
+ KMByteBlob.cast(val).getBuffer(),
+ KMByteBlob.cast(val).getStartOff(),
+ KMByteBlob.cast(val).length());
+ break;
+ case KMType.UINT_TAG:
+ case KMType.ULONG_TAG:
+ case KMType.DATE_TAG:
+ val = KMIntegerTag.cast(tag).getValue();
+ pushIntegerTag(
+ tagId,
+ KMInteger.cast(val).getBuffer(),
+ KMInteger.cast(val).getStartOff(),
+ KMInteger.cast(val).length());
+ break;
+ case KMType.UINT_ARRAY_TAG:
+ case KMType.ULONG_ARRAY_TAG:
+ // According to KeyMint hal only one user secure id is used but this conflicts with
+ // tag type which is ULONG-REP. Currently this is encoded as SET OF INTEGERS
+ val = KMIntegerArrayTag.cast(tag).getValues();
+ pushIntegerArrayTag(tagId, val);
+ break;
+ case KMType.BOOL_TAG:
+ val = KMBoolTag.cast(tag).getVal();
+ pushBoolTag(tagId);
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_TAG);
+ break;
+ }
+ }
+
+ // RootOfTrust ::= SEQUENCE {
+ // verifiedBootKey OCTET_STRING,
+ // deviceLocked BOOLEAN,
+ // verifiedBootState VerifiedBootState,
+ // verifiedBootHash OCTET_STRING,
+ // }
+ // VerifiedBootState ::= ENUMERATED {
+ // Verified (0),
+ // SelfSigned (1),
+ // Unverified (2),
+ // Failed (3),
+ // }
+ private static void pushRoT() {
+ short last = indexes[STACK_PTR];
+ // verified boot hash
+ pushOctetString(
+ KMByteBlob.cast(indexes[VERIFIED_HASH]).getBuffer(),
+ KMByteBlob.cast(indexes[VERIFIED_HASH]).getStartOff(),
+ KMByteBlob.cast(indexes[VERIFIED_HASH]).length());
+
+ pushEnumerated(states[VERIFIED_STATE]);
+
+ pushBoolean(states[DEVICE_LOCKED]);
+ // verified boot Key
+ pushOctetString(
+ KMByteBlob.cast(indexes[VERIFIED_BOOT_KEY]).getBuffer(),
+ KMByteBlob.cast(indexes[VERIFIED_BOOT_KEY]).getStartOff(),
+ KMByteBlob.cast(indexes[VERIFIED_BOOT_KEY]).length());
+
+ // Finally sequence header
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ // ... and tag Id
+ pushTagIdHeader(KMType.ROOT_OF_TRUST, (short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushOctetString(byte[] buf, short start, short len) {
+ pushBytes(buf, start, len);
+ pushOctetStringHeader(len);
+ }
+
+ private static void pushBoolean(byte val) {
+ pushByte(val);
+ pushBooleanHeader((short) 1);
+ }
+
+ private static void pushBooleanHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x01);
+ }
+
+ // Only SET of INTEGERS supported are padding, digest, purpose and blockmode
+ // All of these are enum array tags i.e. byte long values
+ private static void pushEnumArrayTag(short tagId, byte[] buf, short start, short len) {
+ short last = indexes[STACK_PTR];
+ short index = 0;
+ while (index < len) {
+ pushByte(buf[(short) (start + index)]);
+ pushIntegerHeader((short) 1);
+ index++;
+ }
+ pushSetHeader((short) (last - indexes[STACK_PTR]));
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ // Only SET of INTEGERS supported are padding, digest, purpose and blockmode
+ // All of these are enum array tags i.e. byte long values
+ private static void pushIntegerArrayTag(short tagId, short arr) {
+ short last = indexes[STACK_PTR];
+ short index = 0;
+ short len = KMArray.cast(arr).length();
+ short ptr;
+ while (index < len) {
+ ptr = KMArray.cast(arr).get(index);
+ pushInteger(
+ KMInteger.cast(ptr).getBuffer(),
+ KMInteger.cast(ptr).getStartOff(),
+ KMInteger.cast(ptr).length());
+ index++;
+ }
+ pushSetHeader((short) (last - indexes[STACK_PTR]));
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushSetHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x31);
+ }
+
+ private static void pushEnumerated(byte val) {
+ short last = indexes[STACK_PTR];
+ pushByte(val);
+ pushEnumeratedHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushEnumeratedHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x0A);
+ }
+
+ private static void pushBoolTag(short tagId) {
+ short last = indexes[STACK_PTR];
+ pushNullHeader();
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushNullHeader() {
+ pushByte((byte) 0);
+ pushByte((byte) 0x05);
+ }
+
+ private static void pushEnumTag(short tagId, byte val) {
+ short last = indexes[STACK_PTR];
+ pushByte(val);
+ pushIntegerHeader((short) (last - indexes[STACK_PTR]));
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushIntegerTag(short tagId, byte[] buf, short start, short len) {
+ short last = indexes[STACK_PTR];
+ pushInteger(buf, start, len);
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ // Ignore leading zeros. Only Unsigned Integers are required hence if MSB is set then add 0x00
+ // as most significant byte.
+ private static void pushInteger(byte[] buf, short start, short len) {
+ short last = indexes[STACK_PTR];
+ byte index = 0;
+ while (index < (byte) len) {
+ if (buf[(short) (start + index)] != 0) {
+ break;
+ }
+ index++;
+ }
+ if (index == (byte) len) {
+ pushByte((byte) 0x00);
+ } else {
+ pushBytes(buf, (short) (start + index), (short) (len - index));
+ if (buf[(short) (start + index)] < 0) { // MSB is 1
+ pushByte((byte) 0x00); // always unsigned int
+ }
+ }
+ pushIntegerHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ // Bytes Tag is a octet string and tag id is added explicitly
+ private static void pushBytesTag(short tagId, byte[] buf, short start, short len) {
+ short last = indexes[STACK_PTR];
+ pushBytes(buf, start, len);
+ pushOctetStringHeader((short) (last - indexes[STACK_PTR]));
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ // tag id <= 30 ---> 0xA0 | {tagId}
+ // 30 < tagId < 128 ---> 0xBF 0x{tagId}
+ // tagId >= 128 ---> 0xBF 0x80+(tagId/128) 0x{tagId - (128*(tagId/128))}
+ private static void pushTagIdHeader(short tagId, short len) {
+ pushLength(len);
+ short count = (short) (tagId / 128);
+ if (count > 0) {
+ pushByte((byte) (tagId - (128 * count)));
+ pushByte((byte) (0x80 + count));
+ pushByte((byte) 0xBF);
+ } else if (tagId > 30) {
+ pushByte((byte) tagId);
+ pushByte((byte) 0xBF);
+ } else {
+ pushByte((byte) (0xA0 | (byte) tagId));
+ }
+ }
+
+ // SEQUENCE {ObjId, OCTET STRING{BIT STRING{keyUsage}}}
+ private static void pushKeyUsage(byte keyUsage, byte unusedBits) {
+ short last = indexes[STACK_PTR];
+ pushByte(keyUsage);
+ pushBitStringHeader(unusedBits, (short) (last - indexes[STACK_PTR]));
+ pushOctetStringHeader((short) (last - indexes[STACK_PTR]));
+ pushBytes(keyUsageExtn, (short) 0, (short) keyUsageExtn.length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushAlgorithmId(byte[] algId) {
+ pushBytes(algId, (short) 0, (short) algId.length);
+ }
+
+ private static void pushIntegerHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x02);
+ }
+
+ private static void pushOctetStringHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x04);
+ }
+
+ private static void pushSequenceHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x30);
+ }
+
+ private static void pushBitStringHeader(byte unusedBits, short len) {
+ pushByte(unusedBits);
+ pushLength((short) (len + 1)); // 1 extra byte for unused bits byte
+ pushByte((byte) 0x03);
+ }
+
+ private static void pushLength(short len) {
+ if (len < 128) {
+ pushByte((byte) len);
+ } else if (len < 256) {
+ pushByte((byte) len);
+ pushByte((byte) 0x81);
+ } else {
+ pushShort(len);
+ pushByte((byte) 0x82);
+ }
+ }
+
+ private static void pushShort(short val) {
+ decrementStackPtr((short) 2);
+ Util.setShort(stack, indexes[STACK_PTR], val);
+ }
+
+ private static void pushByte(byte val) {
+ decrementStackPtr((short) 1);
+ stack[indexes[STACK_PTR]] = val;
+ }
+
+ private static void pushBytes(byte[] buf, short start, short len) {
+ decrementStackPtr(len);
+ if (buf != null) {
+ Util.arrayCopyNonAtomic(buf, start, stack, indexes[STACK_PTR], len);
+ }
+ }
+
+ private static void decrementStackPtr(short cnt) {
+ indexes[STACK_PTR] = (short) (indexes[STACK_PTR] - cnt);
+ if (indexes[BUF_START] > indexes[STACK_PTR]) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ }
+
+ @Override
+ public KMAttestationCert buffer(byte[] buf, short start, short maxLen) {
+ stack = buf;
+ indexes[BUF_START] = start;
+ indexes[BUF_LENGTH] = maxLen;
+ indexes[STACK_PTR] = (short) (indexes[BUF_START] + indexes[BUF_LENGTH]);
+ return this;
+ }
+
+ @Override
+ public short getCertStart() {
+ return indexes[CERT_START];
+ }
+
+ @Override
+ public short getCertLength() {
+ return indexes[CERT_LENGTH];
+ }
+
+ public void build(short attSecret, short attMod, boolean rsaSign, boolean fakeCert) {
+ indexes[STACK_PTR] = (short) (indexes[BUF_START] + indexes[BUF_LENGTH]);
+ short last = indexes[STACK_PTR];
+ short sigLen = 0;
+ if (fakeCert) {
+ rsaSign = true;
+ pushByte((byte) 0);
+ sigLen = 1;
+ }
+ // Push placeholder signature Bit string header
+ // This will potentially change at the end
+ else if (rsaSign) {
+ decrementStackPtr(RSA_SIG_LEN);
+ } else {
+ decrementStackPtr(ECDSA_MAX_SIG_LEN);
+ }
+ short signatureOffset = indexes[STACK_PTR];
+ pushBitStringHeader((byte) 0, (short) (last - indexes[STACK_PTR]));
+ if (rsaSign) {
+ pushAlgorithmId(X509RsaSignAlgIdentifier);
+ } else {
+ pushAlgorithmId(X509EcdsaSignAlgIdentifier);
+ }
+ indexes[TBS_LENGTH] = indexes[STACK_PTR];
+ pushTbsCert((states[RSA_CERT] == 0 ? false : true), rsaSign);
+ indexes[TBS_START] = indexes[STACK_PTR];
+ indexes[TBS_LENGTH] = (short) (indexes[TBS_LENGTH] - indexes[TBS_START]);
+ if (attSecret != KMType.INVALID_VALUE) {
+ // Sign with the attestation key
+ // The pubKey is the modulus.
+ if (rsaSign) {
+ sigLen =
+ seProvider.rsaSign256Pkcs1(
+ KMByteBlob.cast(attSecret).getBuffer(),
+ KMByteBlob.cast(attSecret).getStartOff(),
+ KMByteBlob.cast(attSecret).length(),
+ KMByteBlob.cast(attMod).getBuffer(),
+ KMByteBlob.cast(attMod).getStartOff(),
+ KMByteBlob.cast(attMod).length(),
+ stack,
+ indexes[TBS_START],
+ indexes[TBS_LENGTH],
+ stack,
+ signatureOffset);
+ if (sigLen > RSA_SIG_LEN) KMException.throwIt(KMError.UNKNOWN_ERROR);
+ } else {
+ sigLen =
+ seProvider.ecSign256(
+ KMByteBlob.cast(attSecret).getBuffer(),
+ KMByteBlob.cast(attSecret).getStartOff(),
+ KMByteBlob.cast(attSecret).length(),
+ stack,
+ indexes[TBS_START],
+ indexes[TBS_LENGTH],
+ stack,
+ signatureOffset);
+ if (sigLen > ECDSA_MAX_SIG_LEN) KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ // Adjust signature length
+ indexes[STACK_PTR] = signatureOffset;
+ pushBitStringHeader((byte) 0, sigLen);
+ } else if (!fakeCert) { // No attestation key provisioned in the factory
+ KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED);
+ }
+ last = (short) (signatureOffset + sigLen);
+ // Add certificate sequence header
+ indexes[STACK_PTR] = indexes[TBS_START];
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ indexes[CERT_START] = indexes[STACK_PTR];
+ indexes[CERT_LENGTH] = (short) (last - indexes[CERT_START]);
+ }
+
+ @Override
+ public void build() {
+ if (states[CERT_MODE] == KMType.FAKE_CERT) {
+ build(KMType.INVALID_VALUE, KMType.INVALID_VALUE, true, true);
+ } else {
+ build(
+ indexes[CERT_ATT_KEY_SECRET],
+ indexes[CERT_ATT_KEY_RSA_PUB_MOD],
+ (states[CERT_RSA_SIGN] == 0 ? false : true),
+ false);
+ }
+ }
+
+ @Override
+ public KMAttestationCert makeUniqueId(
+ byte[] scratchPad,
+ short scratchPadOff,
+ byte[] creationTime,
+ short timeOffset,
+ short creationTimeLen,
+ byte[] attestAppId,
+ short appIdOff,
+ short attestAppIdLen,
+ byte resetSinceIdRotation,
+ KMKey masterKey) {
+ // Concatenate T||C||R
+ // temporal count T
+ short temp =
+ KMUtils.countTemporalCount(
+ creationTime, timeOffset, creationTimeLen, scratchPad, scratchPadOff);
+ Util.setShort(scratchPad, (short) scratchPadOff, temp);
+ temp = scratchPadOff;
+ scratchPadOff += 2;
+
+ // Application Id C
+ Util.arrayCopyNonAtomic(attestAppId, appIdOff, scratchPad, scratchPadOff, attestAppIdLen);
+ scratchPadOff += attestAppIdLen;
+
+ // Reset After Rotation R
+ scratchPad[scratchPadOff] = resetSinceIdRotation;
+ scratchPadOff++;
+
+ // Get the key data from the master key
+ KMAESKey aesKey = (KMAESKey) masterKey;
+ short mKeyData = KMByteBlob.instance((short) (aesKey.aesKey.getSize() / 8));
+ aesKey.aesKey.getKey(
+ KMByteBlob.cast(mKeyData).getBuffer(), /* Key */
+ KMByteBlob.cast(mKeyData).getStartOff()); /* Key start*/
+ timeOffset = KMByteBlob.instance((short) 32);
+ appIdOff =
+ seProvider.hmacSign(
+ KMByteBlob.cast(mKeyData).getBuffer(), /* Key */
+ KMByteBlob.cast(mKeyData).getStartOff(), /* Key start*/
+ KMByteBlob.cast(mKeyData).length(), /* Key length*/
+ scratchPad, /* data */
+ temp, /* data start */
+ scratchPadOff, /* data length */
+ KMByteBlob.cast(timeOffset).getBuffer(), /* signature buffer */
+ KMByteBlob.cast(timeOffset).getStartOff()); /* signature start */
+ if (appIdOff != 32) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ return uniqueId(timeOffset);
+ }
+
+ @Override
+ public boolean serialNumber(short number) {
+ // https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.2
+ short length = KMByteBlob.cast(number).length();
+ if (length > SERIAL_NUM_MAX_LEN) {
+ return false;
+ }
+ // The serial number Must be a positive integer.
+ byte msb = KMByteBlob.cast(number).get((short) 0);
+ if (msb < 0 && length > (SERIAL_NUM_MAX_LEN - 1)) {
+ return false;
+ }
+ indexes[SERIAL_NUMBER] = number;
+ return true;
+ }
+
+ @Override
+ public boolean subjectName(short sub) {
+ if (sub == KMType.INVALID_VALUE || KMByteBlob.cast(sub).length() == 0) return false;
+ indexes[SUBJECT_NAME] = sub;
+ return true;
+ }
+
+ @Override
+ public KMAttestationCert ecAttestKey(short attestKey, byte mode) {
+ states[CERT_MODE] = mode;
+ indexes[CERT_ATT_KEY_SECRET] = attestKey;
+ indexes[CERT_ATT_KEY_RSA_PUB_MOD] = KMType.INVALID_VALUE;
+ states[CERT_RSA_SIGN] = 0;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte mode) {
+ states[CERT_MODE] = mode;
+ indexes[CERT_ATT_KEY_SECRET] = attestPrivExp;
+ indexes[CERT_ATT_KEY_RSA_PUB_MOD] = attestMod;
+ states[CERT_RSA_SIGN] = 1;
+ return this;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java
new file mode 100644
index 0000000..738326b
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java
@@ -0,0 +1,31 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+/**
+ * This class contains all the configuration values. Vendors can modify these values accordingly
+ * based on their environment.
+ */
+public class KMConfigurations {
+ // Machine types
+ public static final byte LITTLE_ENDIAN = 0x00;
+ public static final byte BIG_ENDIAN = 0x01;
+ public static final byte TEE_MACHINE_TYPE = LITTLE_ENDIAN;
+ // If the size of the attestation ids is known and lesser than 64
+ // then reduce the size here. It reduces the heap memory usage.
+ public static final byte MAX_ATTESTATION_IDS_SIZE = 64;
+ public static final short MAX_SUBJECT_DER_LEN = 1095;
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java
new file mode 100644
index 0000000..95ee67f
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java
@@ -0,0 +1,440 @@
+/*
+ * 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.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.Util;
+
+/**
+ * This is a utility class which helps in converting date to UTC format and doing some arithmetic
+ * Operations.
+ */
+public class KMUtils {
+
+ // 64 bit unsigned calculations for time
+ public static final byte[] oneSecMsec = {0, 0, 0, 0, 0, 0, 0x03, (byte) 0xE8}; // 1000 msec
+ public static final byte[] oneMinMsec = {0, 0, 0, 0, 0, 0, (byte) 0xEA, 0x60}; // 60000 msec
+ public static final byte[] oneHourMsec = {
+ 0, 0, 0, 0, 0, 0x36, (byte) 0xEE, (byte) 0x80
+ }; // 3600000 msec
+ public static final byte[] oneDayMsec = {0, 0, 0, 0, 0x05, 0x26, 0x5C, 0x00}; // 86400000 msec
+ public static final byte[] oneMonthMsec = {
+ 0, 0, 0, 0, (byte) 0x9C, (byte) 0xBE, (byte) 0xBD, 0x50
+ }; // 2629746000 msec
+ public static final byte[] leapYearMsec = {
+ 0, 0, 0, 0x07, (byte) 0x5C, (byte) 0xD7, (byte) 0x88, 0x00
+ }; // 31622400000;
+ public static final byte[] yearMsec = {
+ 0, 0, 0, 0x07, 0x57, (byte) 0xB1, 0x2C, 0x00
+ }; // 31536000000
+ // Leap year(366) + 3 * 365
+ public static final byte[] fourYrsMsec = {
+ 0, 0, 0, 0x1D, 0x63, (byte) 0xEB, 0x0C, 0x00
+ }; // 126230400000
+ public static final byte[] firstJan2020 = {
+ 0, 0, 0x01, 0x6F, 0x5E, 0x66, (byte) 0xE8, 0x00
+ }; // 1577836800000 msec
+ public static final byte[] firstJan2050 = {
+ 0, 0, 0x02, 0x4b, (byte) 0xCE, 0x5C, (byte) 0xF0, 0x00
+ }; // 2524608000000
+ // msec
+ public static final byte[] febMonthLeapMSec = {
+ 0, 0, 0, 0, (byte) 0x95, 0x58, 0x6C, 0x00
+ }; // 2505600000
+ public static final byte[] febMonthMsec = {
+ 0, 0, 0, 0, (byte) 0x90, 0x32, 0x10, 0x00
+ }; // 2419200000
+ public static final byte[] ThirtyOneDaysMonthMsec = {
+ 0, 0, 0, 0, (byte) 0x9F, (byte) 0xA5, 0x24, 0x00
+ }; // 2678400000
+ public static final byte[] ThirtDaysMonthMsec = {
+ 0, 0, 0, 0, (byte) 0x9A, 0x7E, (byte) 0xC8, 0x00
+ }; // 2592000000
+ public static final short year2051 = 2051;
+ public static final short year2020 = 2020;
+ // Convert to milliseconds constants
+ public static final byte[] SEC_TO_MILLIS_SHIFT_POS = {9, 8, 7, 6, 5, 3};
+
+ // --------------------------------------
+ public static short convertToDate(short time, byte[] scratchPad, boolean utcFlag) {
+
+ short yrsCount = 0;
+ short monthCount = 1;
+ short dayCount = 1;
+ short hhCount = 0;
+ short mmCount = 0;
+ short ssCount = 0;
+ byte Z = 0x5A;
+ boolean from2020 = true;
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+ Util.arrayCopyNonAtomic(
+ KMInteger.cast(time).getBuffer(),
+ KMInteger.cast(time).getStartOff(),
+ scratchPad,
+ (short) (8 - KMInteger.cast(time).length()),
+ KMInteger.cast(time).length());
+ // If the time is less then 1 Jan 2020 then it is an error
+ if (KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, firstJan2020, (short) 0, (short) 8)
+ < 0) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ if (utcFlag
+ && KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, firstJan2050, (short) 0, (short) 8)
+ >= 0) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+
+ if (KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, firstJan2050, (short) 0, (short) 8)
+ < 0) {
+ Util.arrayCopyNonAtomic(firstJan2020, (short) 0, scratchPad, (short) 8, (short) 8);
+ subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8);
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ } else {
+ from2020 = false;
+ Util.arrayCopyNonAtomic(firstJan2050, (short) 0, scratchPad, (short) 8, (short) 8);
+ subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8);
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ }
+ // divide the given time with four yrs msec count
+ if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, fourYrsMsec, (short) 0, (short) 8)
+ >= 0) {
+ Util.arrayCopyNonAtomic(fourYrsMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ // quotient is multiple of 4
+ yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16);
+ yrsCount = (short) (yrsCount * 4); // number of yrs.
+ // copy reminder as new dividend
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ }
+
+ // Get the leap year index starting from the (base Year + yrsCount) Year.
+ short leapYrIdx = getLeapYrIndex(from2020, yrsCount);
+
+ // if leap year index is 0, then the number of days for the 1st year will be 366 days.
+ // if leap year index is not 0, then the number of days for the 1st year will be 365 days.
+ if (((leapYrIdx == 0)
+ && (KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, leapYearMsec, (short) 0, (short) 8)
+ >= 0))
+ || ((leapYrIdx != 0)
+ && (KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, yearMsec, (short) 0, (short) 8)
+ >= 0))) {
+ for (short i = 0; i < 4; i++) {
+ yrsCount++;
+ if (i == leapYrIdx) {
+ Util.arrayCopyNonAtomic(leapYearMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ } else {
+ Util.arrayCopyNonAtomic(yearMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ }
+ subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8);
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ if (((short) (i + 1) == leapYrIdx)) {
+ if (KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, leapYearMsec, (short) 0, (short) 8)
+ < 0) {
+ break;
+ }
+ } else {
+ if (KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, yearMsec, (short) 0, (short) 8)
+ < 0) {
+ break;
+ }
+ }
+ }
+ }
+
+ // total yrs from 1970
+ if (from2020) {
+ yrsCount = (short) (year2020 + yrsCount);
+ } else {
+ yrsCount = (short) (year2051 + yrsCount);
+ }
+
+ // divide the given time with one month msec count
+ if (KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, oneMonthMsec, (short) 0, (short) 8)
+ >= 0) {
+ for (short i = 0; i < 12; i++) {
+ if (i == 1) {
+ // Feb month
+ if (isLeapYear(yrsCount)) {
+ // Leap year 29 days
+ Util.arrayCopyNonAtomic(febMonthLeapMSec, (short) 0, scratchPad, (short) 8, (short) 8);
+ } else {
+ // 28 days
+ Util.arrayCopyNonAtomic(febMonthMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ }
+ } else if (((i <= 6) && ((i % 2 == 0))) || ((i > 6) && ((i % 2 == 1)))) {
+ Util.arrayCopyNonAtomic(
+ ThirtyOneDaysMonthMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ } else {
+ // 30 Days
+ Util.arrayCopyNonAtomic(ThirtDaysMonthMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ }
+
+ if (KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, scratchPad, (short) 8, (short) 8)
+ >= 0) {
+ subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8);
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ } else {
+ break;
+ }
+ monthCount++;
+ }
+ }
+
+ // divide the given time with one day msec count
+ if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneDayMsec, (short) 0, (short) 8)
+ >= 0) {
+ Util.arrayCopyNonAtomic(oneDayMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ dayCount = divide(scratchPad, (short) 0, (short) 8, (short) 16);
+ dayCount++;
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ }
+
+ // divide the given time with one hour msec count
+ if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneHourMsec, (short) 0, (short) 8)
+ >= 0) {
+ Util.arrayCopyNonAtomic(oneHourMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ hhCount = divide(scratchPad, (short) 0, (short) 8, (short) 16);
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ }
+
+ // divide the given time with one minute msec count
+ if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneMinMsec, (short) 0, (short) 8)
+ >= 0) {
+ Util.arrayCopyNonAtomic(oneMinMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ mmCount = divide(scratchPad, (short) 0, (short) 8, (short) 16);
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ }
+
+ // divide the given time with one second msec count
+ if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneSecMsec, (short) 0, (short) 8)
+ >= 0) {
+ Util.arrayCopyNonAtomic(oneSecMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ ssCount = divide(scratchPad, (short) 0, (short) 8, (short) 16);
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ }
+
+ // Now convert to ascii string YYMMDDhhmmssZ or YYYYMMDDhhmmssZ
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+ short len = numberToString(yrsCount, scratchPad, (short) 0); // returns YYYY
+ len += numberToString(monthCount, scratchPad, len);
+ len += numberToString(dayCount, scratchPad, len);
+ len += numberToString(hhCount, scratchPad, len);
+ len += numberToString(mmCount, scratchPad, len);
+ len += numberToString(ssCount, scratchPad, len);
+ scratchPad[len] = Z;
+ len++;
+ if (utcFlag) {
+ return KMByteBlob.instance(scratchPad, (short) 2, (short) (len - 2)); // YY
+ } else {
+ return KMByteBlob.instance(scratchPad, (short) 0, len); // YYYY
+ }
+ }
+
+ public static short numberToString(short number, byte[] scratchPad, short offset) {
+ byte zero = 0x30;
+ byte len = 2;
+ byte digit;
+ if (number > 999) {
+ len = 4;
+ }
+ byte index = len;
+ while (index > 0) {
+ digit = (byte) (number % 10);
+ number = (short) (number / 10);
+ scratchPad[(short) (offset + index - 1)] = (byte) (digit + zero);
+ index--;
+ }
+ return len;
+ }
+
+ // Use Euclid's formula: dividend = quotient*divisor + remainder
+ // i.e. dividend - quotient*divisor = remainder where remainder < divisor.
+ // so this is division by subtraction until remainder remains.
+ public static short divide(byte[] buf, short dividend, short divisor, short remainder) {
+ short expCnt = 1;
+ short q = 0;
+ // first increase divisor so that it becomes greater then dividend.
+ while (compare(buf, divisor, dividend) < 0) {
+ shiftLeft(buf, divisor);
+ expCnt = (short) (expCnt << 1);
+ }
+ // Now subtract divisor from dividend if dividend is greater then divisor.
+ // Copy remainder in the dividend and repeat.
+ while (expCnt != 0) {
+ if (compare(buf, dividend, divisor) >= 0) {
+ subtract(buf, dividend, divisor, remainder, (byte) 8);
+ copy(buf, remainder, dividend);
+ q = (short) (q + expCnt);
+ }
+ expCnt = (short) (expCnt >> 1);
+ shiftRight(buf, divisor);
+ }
+ return q;
+ }
+
+ public static void copy(byte[] buf, short from, short to) {
+ Util.arrayCopyNonAtomic(buf, from, buf, to, (short) 8);
+ }
+
+ public static byte compare(byte[] buf, short lhs, short rhs) {
+ return KMInteger.unsignedByteArrayCompare(buf, lhs, buf, rhs, (short) 8);
+ }
+
+ public static void shiftLeft(byte[] buf, short start, short count) {
+ short index = 0;
+ while (index < count) {
+ shiftLeft(buf, start);
+ index++;
+ }
+ }
+
+ public static void shiftLeft(byte[] buf, short start) {
+ byte index = 7;
+ byte carry = 0;
+ byte tmp;
+ while (index >= 0) {
+ tmp = buf[(short) (start + index)];
+ buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] << 1);
+ buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] + carry);
+ if (tmp < 0) {
+ carry = 1;
+ } else {
+ carry = 0;
+ }
+ index--;
+ }
+ }
+
+ public static void shiftRight(byte[] buf, short start) {
+ byte index = 0;
+ byte carry = 0;
+ byte tmp;
+ while (index < 8) {
+ tmp = (byte) (buf[(short) (start + index)] & 0x01);
+ buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] >> 1);
+ buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] & 0x7F);
+ buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] | carry);
+ if (tmp == 1) {
+ carry = (byte) 0x80;
+ } else {
+ carry = 0;
+ }
+ index++;
+ }
+ }
+
+ public static void add(byte[] buf, short op1, short op2, short result) {
+ byte index = 7;
+ byte carry = 0;
+ short tmp;
+ short val1 = 0;
+ short val2 = 0;
+ while (index >= 0) {
+ val1 = (short) (buf[(short) (op1 + index)] & 0x00FF);
+ val2 = (short) (buf[(short) (op2 + index)] & 0x00FF);
+ tmp = (short) (val1 + val2 + carry);
+ carry = 0;
+ if (tmp > 255) {
+ carry = 1; // max unsigned byte value is 255
+ }
+ buf[(short) (result + index)] = (byte) (tmp & (byte) 0xFF);
+ index--;
+ }
+ }
+
+ // subtraction by borrowing.
+ public static void subtract(byte[] buf, short op1, short op2, short result, byte sizeBytes) {
+ byte borrow = 0;
+ byte index = (byte) (sizeBytes - 1);
+ short r;
+ short x;
+ short y;
+ while (index >= 0) {
+ x = (short) (buf[(short) (op1 + index)] & 0xFF);
+ y = (short) (buf[(short) (op2 + index)] & 0xFF);
+ r = (short) (x - y - borrow);
+ borrow = 0;
+ if (r < 0) {
+ borrow = 1;
+ r = (short) (r + 256); // max unsigned byte value is 255
+ }
+ buf[(short) (result + index)] = (byte) (r & 0xFF);
+ index--;
+ }
+ }
+
+ public static short countTemporalCount(
+ byte[] bufTime, short timeOff, short timeLen, byte[] scratchPad, short offset) {
+ Util.arrayFillNonAtomic(scratchPad, (short) offset, (short) 24, (byte) 0);
+ Util.arrayCopyNonAtomic(bufTime, timeOff, scratchPad, (short) (offset + 8 - timeLen), timeLen);
+ Util.arrayCopyNonAtomic(
+ ThirtDaysMonthMsec, (short) 0, scratchPad, (short) (offset + 8), (short) 8);
+ return divide(scratchPad, (short) 0, (short) 8, (short) 16);
+ }
+
+ public static boolean isLeapYear(short year) {
+ if ((short) (year % 4) == (short) 0) {
+ if (((short) (year % 100) == (short) 0) && ((short) (year % 400)) != (short) 0) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public static short getLeapYrIndex(boolean from2020, short yrsCount) {
+ short newBaseYr = (short) (from2020 ? (year2020 + yrsCount) : (year2051 + yrsCount));
+ for (short i = 0; i < 4; i++) {
+ if (isLeapYear((short) (newBaseYr + i))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public static void computeOnesCompliment(byte[] buf, short offset, short len) {
+ short index = offset;
+ // Compute 1s compliment
+ while (index < (short) (len + offset)) {
+ buf[index] = (byte) ~buf[index];
+ index++;
+ }
+ }
+
+ // i * 1000 = (i << 9) + (i << 8) + (i << 7) + (i << 6) + (i << 5) + ( i << 3)
+ public static void convertToMilliseconds(
+ byte[] buf, short inputOff, short outputOff, short scratchPadOff) {
+ short index = 0;
+ short length = (short) SEC_TO_MILLIS_SHIFT_POS.length;
+ while (index < length) {
+ Util.arrayCopyNonAtomic(buf, inputOff, buf, scratchPadOff, (short) 8);
+ shiftLeft(buf, scratchPadOff, SEC_TO_MILLIS_SHIFT_POS[index]);
+ Util.arrayCopyNonAtomic(buf, outputOff, buf, (short) (scratchPadOff + 8), (short) 8);
+ add(buf, scratchPadOff, (short) (8 + scratchPadOff), (short) (16 + scratchPadOff));
+ Util.arrayCopyNonAtomic(buf, (short) (scratchPadOff + 16), buf, outputOff, (short) 8);
+ Util.arrayFillNonAtomic(buf, scratchPadOff, (short) 24, (byte) 0);
+ index++;
+ }
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java
new file mode 100644
index 0000000..41059bb
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java
@@ -0,0 +1,53 @@
+/*
+ * 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.security.AESKey;
+import org.globalplatform.upgrade.Element;
+
+/** This is a wrapper class for AESKey. */
+public class KMAESKey implements KMKey {
+
+ public AESKey aesKey;
+
+ public KMAESKey(AESKey key) {
+ aesKey = key;
+ }
+
+ public static void onSave(Element element, KMAESKey kmKey) {
+ element.write(kmKey.aesKey);
+ }
+
+ public static KMAESKey onRestore(AESKey aesKey) {
+ if (aesKey == null) {
+ return null;
+ }
+ return new KMAESKey(aesKey);
+ }
+
+ public static short getBackupPrimitiveByteCount() {
+ return (short) 0;
+ }
+
+ public static short getBackupObjectCount() {
+ return (short) 1;
+ }
+
+ @Override
+ public short getPublicKey(byte[] buf, short offset) {
+ return (short) 0;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java
new file mode 100644
index 0000000..cb33272
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java
@@ -0,0 +1,1578 @@
+/*
+ * 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" 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.javacard.seprovider;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+import javacard.security.AESKey;
+import javacard.security.CryptoException;
+import javacard.security.DESKey;
+import javacard.security.ECPrivateKey;
+import javacard.security.ECPublicKey;
+import javacard.security.HMACKey;
+import javacard.security.Key;
+import javacard.security.KeyAgreement;
+import javacard.security.KeyBuilder;
+import javacard.security.KeyPair;
+import javacard.security.MessageDigest;
+import javacard.security.RSAPrivateKey;
+import javacard.security.RandomData;
+import javacard.security.Signature;
+import javacardx.crypto.AEADCipher;
+import javacardx.crypto.Cipher;
+import org.globalplatform.upgrade.Element;
+import org.globalplatform.upgrade.UpgradeManager;
+
+/**
+ * This class implements KMSEProvider and provides all the necessary crypto operations required to
+ * support the KeyMint specification. This class supports AES, 3DES, HMAC, RSA, ECDSA, ECDH
+ * algorithms additionally it also supports ECDSA_NO_DIGEST, RSA_NO_DIGEST and RSA_OAEP_MGF1_SHA1
+ * and RSA_OAEP_MGF1_SHA256 algorithms. This class follows the pattern of Init-Update-Final for the
+ * crypto operations.
+ */
+public class KMAndroidSEProvider implements KMSEProvider {
+
+ // The tag length for AES GCM algorithm.
+ public static final byte AES_GCM_TAG_LENGTH = 16;
+ // The nonce length for AES GCM algorithm.
+ public static final byte AES_GCM_NONCE_LENGTH = 12;
+ // AES keysize offsets in aesKeys[] for 128 and 256 sizes respectively.
+ public static final byte KEYSIZE_128_OFFSET = 0x00;
+ public static final byte KEYSIZE_256_OFFSET = 0x01;
+ // The size of the temporary buffer.
+ public static final short TMP_ARRAY_SIZE = 300;
+ // The length of the rsa key in bytes.
+ private static final short RSA_KEY_SIZE = 256;
+ // Below are the flag to denote device reset events
+ public static final byte POWER_RESET_FALSE = (byte) 0xAA;
+ public static final byte POWER_RESET_TRUE = (byte) 0x00;
+ // The computed HMAC key size.
+ private static final byte COMPUTED_HMAC_KEY_SIZE = 32;
+ // The constant 'L' as defiend in
+ // https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf, page 12.
+ private static byte[] CMAC_KDF_CONSTANT_L;
+ // Constant to represent 0.
+ private static byte[] CMAC_KDF_CONSTANT_ZERO;
+ // KeyAgreement instance.
+ private static KeyAgreement keyAgreement;
+
+ // AESKey
+ private AESKey aesKeys[];
+ // DES3Key
+ private DESKey triDesKey;
+ // HMACKey
+ private HMACKey hmacKey;
+ // RSA Key Pair
+ private KeyPair rsaKeyPair;
+ // EC Key Pair.
+ private KeyPair ecKeyPair;
+ // Temporary array.
+ public byte[] tmpArray;
+ // This is used for internal encryption/decryption operations.
+ private static AEADCipher aesGcmCipher;
+ // Instance of Signature algorithm used in KDF.
+ private Signature kdf;
+ // Flag used to denote the power reset event.
+ public static byte[] resetFlag;
+ // Instance of HMAC Signature algorithm.
+ private Signature hmacSignature;
+ // For ImportwrappedKey operations.
+ private KMRsaOAEPEncoding rsaOaepDecipher;
+ // Instance of pool manager.
+ private KMPoolManager poolMgr;
+ // Instance of KMOperationImpl used only to encrypt/decrypt the KeyBlobs.
+ private KMOperationImpl globalOperation;
+ // Entropy
+ private RandomData rng;
+ // Singleton instance.
+ private static KMAndroidSEProvider androidSEProvider = null;
+
+ public static KMAndroidSEProvider getInstance() {
+ return androidSEProvider;
+ }
+
+ public KMAndroidSEProvider() {
+ initStatics();
+ // Re-usable AES,DES and HMAC keys in persisted memory.
+ aesKeys = new AESKey[2];
+ aesKeys[KEYSIZE_128_OFFSET] =
+ (AESKey)
+ KeyBuilder.buildKey(
+ KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_128, false);
+ aesKeys[KEYSIZE_256_OFFSET] =
+ (AESKey)
+ KeyBuilder.buildKey(
+ KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_256, false);
+ triDesKey =
+ (DESKey)
+ KeyBuilder.buildKey(
+ KeyBuilder.TYPE_DES_TRANSIENT_RESET, KeyBuilder.LENGTH_DES3_3KEY, false);
+ hmacKey =
+ (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_RESET, (short) 512, false);
+ rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048);
+ ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256);
+ keyAgreement = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false);
+ poolMgr = KMPoolManager.getInstance();
+ poolMgr.initECKey(ecKeyPair);
+ // RsaOAEP Decipher
+ rsaOaepDecipher = new KMRsaOAEPEncoding(KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1);
+
+ kdf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false);
+ hmacSignature = Signature.getInstance(Signature.ALG_HMAC_SHA_256, false);
+
+ globalOperation = new KMOperationImpl();
+
+ // Temporary transient array created to use locally inside functions.
+ tmpArray = JCSystem.makeTransientByteArray(TMP_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT);
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0);
+ // Random number generator initialisation.
+ rng = RandomData.getInstance(RandomData.ALG_KEYGENERATION);
+ androidSEProvider = this;
+ resetFlag = JCSystem.makeTransientByteArray((short) 1, JCSystem.CLEAR_ON_RESET);
+ resetFlag[0] = (byte) POWER_RESET_FALSE;
+ }
+
+ void initStatics() {
+ CMAC_KDF_CONSTANT_L = new byte[] {0x00, 0x00, 0x01, 0x00};
+ CMAC_KDF_CONSTANT_ZERO = new byte[] {0x00};
+ }
+
+ public void clean() {
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0);
+ }
+
+ public AESKey createAESKey(short keysize) {
+ try {
+ if (keysize > TMP_ARRAY_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ newRandomNumber(tmpArray, (short) 0, (short) (keysize / 8));
+ return createAESKey(tmpArray, (short) 0, (short) (keysize / 8));
+ } finally {
+ clean();
+ }
+ }
+
+ public AESKey createAESKey(byte[] buf, short startOff, short length) {
+ AESKey key = null;
+ short keysize = (short) (length * 8);
+ if (keysize == 128) {
+ key = (AESKey) aesKeys[KEYSIZE_128_OFFSET];
+ key.setKey(buf, (short) startOff);
+ } else if (keysize == 256) {
+ key = (AESKey) aesKeys[KEYSIZE_256_OFFSET];
+ key.setKey(buf, (short) startOff);
+ } else {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ return key;
+ }
+
+ public DESKey createTDESKey() {
+ try {
+ newRandomNumber(tmpArray, (short) 0, (short) (KeyBuilder.LENGTH_DES3_3KEY / 8));
+ return createTDESKey(tmpArray, (short) 0, (short) (KeyBuilder.LENGTH_DES3_3KEY / 8));
+ } finally {
+ clean();
+ }
+ }
+
+ public DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength) {
+ triDesKey.setKey(secretBuffer, secretOff);
+ return triDesKey;
+ }
+
+ public HMACKey createHMACKey(short keysize) {
+ // As per the KeyMint2.0 specification
+ // The minimum supported HMAC key size is 64 bits
+ // The maximum supported HMAC key size is 512 bits
+ // The keysize should be a multiple of 8.
+ if ((keysize % 8 != 0) || !(keysize >= 64 && keysize <= 512)) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ try {
+ newRandomNumber(tmpArray, (short) 0, (short) (keysize / 8));
+ return createHMACKey(tmpArray, (short) 0, (short) (keysize / 8));
+ } finally {
+ clean();
+ }
+ }
+
+ public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength) {
+ hmacKey.setKey(secretBuffer, secretOff, secretLength);
+ return hmacKey;
+ }
+
+ public KeyPair createRsaKeyPair() {
+ rsaKeyPair.genKeyPair();
+ return rsaKeyPair;
+ }
+
+ public RSAPrivateKey createRsaKey(
+ byte[] modBuffer,
+ short modOff,
+ short modLength,
+ byte[] privBuffer,
+ short privOff,
+ short privLength) {
+ RSAPrivateKey privKey = (RSAPrivateKey) rsaKeyPair.getPrivate();
+ privKey.setExponent(privBuffer, privOff, privLength);
+ privKey.setModulus(modBuffer, modOff, modLength);
+ return privKey;
+ }
+
+ public KeyPair createECKeyPair() {
+ ecKeyPair.genKeyPair();
+ return ecKeyPair;
+ }
+
+ public ECPrivateKey createEcKey(byte[] privBuffer, short privOff, short privLength) {
+ ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate();
+ privKey.setS(privBuffer, privOff, privLength);
+ return privKey;
+ }
+
+ @Override
+ public short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff) {
+ switch (alg) {
+ case KMType.AES:
+ AESKey aesKey = createAESKey(keysize);
+ return aesKey.getKey(buf, startOff);
+ case KMType.DES:
+ DESKey desKey = createTDESKey();
+ return desKey.getKey(buf, startOff);
+ case KMType.HMAC:
+ HMACKey hmacKey = createHMACKey(keysize);
+ return hmacKey.getKey(buf, startOff);
+ default:
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ break;
+ }
+ return 0;
+ }
+
+ @Override
+ public void createAsymmetricKey(
+ byte alg,
+ byte[] privKeyBuf,
+ short privKeyStart,
+ short privKeyLength,
+ byte[] pubModBuf,
+ short pubModStart,
+ short pubModLength,
+ short[] lengths) {
+ switch (alg) {
+ case KMType.RSA:
+ if (RSA_KEY_SIZE != privKeyLength || RSA_KEY_SIZE != pubModLength) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ KeyPair rsaKey = createRsaKeyPair();
+ RSAPrivateKey privKey = (RSAPrivateKey) rsaKey.getPrivate();
+ // Copy exponent.
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, RSA_KEY_SIZE, (byte) 0);
+ lengths[0] = privKey.getExponent(tmpArray, (short) 0);
+ if (lengths[0] > privKeyLength) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ Util.arrayFillNonAtomic(privKeyBuf, privKeyStart, privKeyLength, (byte) 0);
+ Util.arrayCopyNonAtomic(
+ tmpArray,
+ (short) 0,
+ privKeyBuf,
+ (short) (privKeyStart + privKeyLength - lengths[0]),
+ lengths[0]);
+ // Copy modulus
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, RSA_KEY_SIZE, (byte) 0);
+ lengths[1] = privKey.getModulus(tmpArray, (short) 0);
+ if (lengths[1] > pubModLength) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ Util.arrayFillNonAtomic(pubModBuf, pubModStart, pubModLength, (byte) 0);
+ Util.arrayCopyNonAtomic(
+ tmpArray,
+ (short) 0,
+ pubModBuf,
+ (short) (pubModStart + pubModLength - lengths[1]),
+ lengths[1]);
+ break;
+ case KMType.EC:
+ KeyPair ecKey = createECKeyPair();
+ ECPublicKey ecPubKey = (ECPublicKey) ecKey.getPublic();
+ ECPrivateKey ecPrivKey = (ECPrivateKey) ecKey.getPrivate();
+ lengths[0] = ecPrivKey.getS(privKeyBuf, privKeyStart);
+ lengths[1] = ecPubKey.getW(pubModBuf, pubModStart);
+ if (lengths[0] > privKeyLength || lengths[1] > pubModLength) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ break;
+ default:
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ break;
+ }
+ }
+
+ @Override
+ public boolean importSymmetricKey(
+ byte alg, short keysize, byte[] buf, short startOff, short length) {
+ switch (alg) {
+ case KMType.AES:
+ createAESKey(buf, startOff, length);
+ break;
+ case KMType.DES:
+ createTDESKey(buf, startOff, length);
+ break;
+ case KMType.HMAC:
+ createHMACKey(buf, startOff, length);
+ break;
+ default:
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean importAsymmetricKey(
+ byte alg,
+ byte[] privKeyBuf,
+ short privKeyStart,
+ short privKeyLength,
+ byte[] pubModBuf,
+ short pubModStart,
+ short pubModLength) {
+ switch (alg) {
+ case KMType.RSA:
+ createRsaKey(pubModBuf, pubModStart, pubModLength, privKeyBuf, privKeyStart, privKeyLength);
+ break;
+ case KMType.EC:
+ createEcKey(privKeyBuf, privKeyStart, privKeyLength);
+ break;
+ default:
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ public void getTrueRandomNumber(byte[] buf, short start, short length) {
+ newRandomNumber(buf, start, length);
+ }
+
+ @Override
+ public void newRandomNumber(byte[] num, short startOff, short length) {
+ rng.nextBytes(num, startOff, length);
+ }
+
+ @Override
+ public void addRngEntropy(byte[] num, short offset, short length) {
+ rng.setSeed(num, offset, length);
+ }
+
+ public short aesGCMEncrypt(
+ AESKey key,
+ byte[] secret,
+ short secretStart,
+ short secretLen,
+ byte[] encSecret,
+ short encSecretStart,
+ byte[] nonce,
+ short nonceStart,
+ short nonceLen,
+ byte[] authData,
+ short authDataStart,
+ short authDataLen,
+ byte[] authTag,
+ short authTagStart,
+ short authTagLen) {
+ if (authTagLen != AES_GCM_TAG_LENGTH) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ if (nonceLen != AES_GCM_NONCE_LENGTH) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ if (aesGcmCipher == null) {
+ aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false);
+ }
+ aesGcmCipher.init(key, Cipher.MODE_ENCRYPT, nonce, nonceStart, nonceLen);
+ if (authDataLen != 0) {
+ aesGcmCipher.updateAAD(authData, authDataStart, authDataLen);
+ }
+ short ciphLen = aesGcmCipher.doFinal(secret, secretStart, secretLen, encSecret, encSecretStart);
+ aesGcmCipher.retrieveTag(authTag, authTagStart, authTagLen);
+ return ciphLen;
+ }
+
+ @Override
+ public short aesGCMEncrypt(
+ byte[] aesKey,
+ short aesKeyStart,
+ short aesKeyLen,
+ byte[] secret,
+ short secretStart,
+ short secretLen,
+ byte[] encSecret,
+ short encSecretStart,
+ byte[] nonce,
+ short nonceStart,
+ short nonceLen,
+ byte[] authData,
+ short authDataStart,
+ short authDataLen,
+ byte[] authTag,
+ short authTagStart,
+ short authTagLen) {
+
+ AESKey key = createAESKey(aesKey, aesKeyStart, aesKeyLen);
+ return aesGCMEncrypt(
+ key,
+ secret,
+ secretStart,
+ secretLen,
+ encSecret,
+ encSecretStart,
+ nonce,
+ nonceStart,
+ nonceLen,
+ authData,
+ authDataStart,
+ authDataLen,
+ authTag,
+ authTagStart,
+ authTagLen);
+ }
+
+ @Override
+ public boolean aesGCMDecrypt(
+ byte[] aesKey,
+ short aesKeyStart,
+ short aesKeyLen,
+ byte[] encSecret,
+ short encSecretStart,
+ short encSecretLen,
+ byte[] secret,
+ short secretStart,
+ byte[] nonce,
+ short nonceStart,
+ short nonceLen,
+ byte[] authData,
+ short authDataStart,
+ short authDataLen,
+ byte[] authTag,
+ short authTagStart,
+ short authTagLen) {
+ if (aesGcmCipher == null) {
+ aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false);
+ }
+ boolean verification = false;
+ AESKey key = createAESKey(aesKey, aesKeyStart, aesKeyLen);
+ aesGcmCipher.init(key, Cipher.MODE_DECRYPT, nonce, nonceStart, nonceLen);
+ if (authDataLen != 0) {
+ aesGcmCipher.updateAAD(authData, authDataStart, authDataLen);
+ }
+ // encrypt the secret
+ aesGcmCipher.doFinal(encSecret, encSecretStart, encSecretLen, secret, secretStart);
+ verification =
+ aesGcmCipher.verifyTag(
+ authTag, authTagStart, (short) authTagLen, (short) AES_GCM_TAG_LENGTH);
+ return verification;
+ }
+
+ public HMACKey cmacKdf(
+ KMKey preSharedKey,
+ byte[] label,
+ short labelStart,
+ short labelLen,
+ byte[] context,
+ short contextStart,
+ short contextLength) {
+ // Note: the variables i and L correspond to i and L in the standard. See page 12 of
+ // http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf.
+ try {
+ // This is hardcoded to requirement - 32 byte output with two concatenated
+ // 16 bytes K1 and K2.
+ final byte n = 2; // hardcoded
+
+ // [i] counter - 32 bits
+ short iBufLen = 4;
+ short keyOutLen = n * 16;
+ // Convert Hmackey to AES Key as the algorithm is ALG_AES_CMAC_128.
+ KMHmacKey hmacKey = ((KMHmacKey) preSharedKey);
+ hmacKey.hmacKey.getKey(tmpArray, (short) 0);
+ aesKeys[KEYSIZE_256_OFFSET].setKey(tmpArray, (short) 0);
+ // Initialize the key derivation function.
+ kdf.init(aesKeys[KEYSIZE_256_OFFSET], Signature.MODE_SIGN);
+ // Clear the tmpArray buffer.
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0);
+
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, iBufLen, (byte) 0);
+ Util.arrayFillNonAtomic(tmpArray, (short) iBufLen, keyOutLen, (byte) 0);
+
+ byte i = 1;
+ short pos = 0;
+ while (i <= n) {
+ tmpArray[3] = i;
+ // 4 bytes of iBuf with counter in it
+ kdf.update(tmpArray, (short) 0, (short) iBufLen);
+ kdf.update(label, labelStart, (short) labelLen); // label
+ kdf.update(
+ CMAC_KDF_CONSTANT_ZERO,
+ (short) 0,
+ (short) CMAC_KDF_CONSTANT_ZERO.length); // 1 byte of 0x00
+ kdf.update(context, contextStart, contextLength); // context
+ // 4 bytes of L - signature of 16 bytes
+ pos =
+ kdf.sign(
+ CMAC_KDF_CONSTANT_L,
+ (short) 0,
+ (short) CMAC_KDF_CONSTANT_L.length,
+ tmpArray,
+ (short) (iBufLen + pos));
+ i++;
+ }
+ return createHMACKey(tmpArray, (short) iBufLen, (short) keyOutLen);
+ } finally {
+ clean();
+ }
+ }
+
+ public short hmacSign(
+ HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) {
+ hmacSignature.init(key, Signature.MODE_SIGN);
+ return hmacSignature.sign(data, dataStart, dataLength, mac, macStart);
+ }
+
+ @Override
+ public short hmacSign(
+ byte[] keyBuf,
+ short keyStart,
+ short keyLength,
+ byte[] data,
+ short dataStart,
+ short dataLength,
+ byte[] mac,
+ short macStart) {
+ HMACKey key = createHMACKey(keyBuf, keyStart, keyLength);
+ return hmacSign(key, data, dataStart, dataLength, mac, macStart);
+ }
+
+ @Override
+ public short hmacSign(
+ Object key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) {
+ if (!(key instanceof KMHmacKey)) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ KMHmacKey hmacKey = (KMHmacKey) key;
+ return hmacSign(hmacKey.hmacKey, data, dataStart, dataLength, mac, macStart);
+ }
+
+ @Override
+ public short hmacKDF(
+ KMKey masterkey,
+ byte[] data,
+ short dataStart,
+ short dataLength,
+ byte[] signature,
+ short signatureStart) {
+ try {
+ KMAESKey aesKey = (KMAESKey) masterkey;
+ short keyLen = (short) (aesKey.aesKey.getSize() / 8);
+ aesKey.aesKey.getKey(tmpArray, (short) 0);
+ return hmacSign(
+ tmpArray, (short) 0, keyLen, data, dataStart, dataLength, signature, signatureStart);
+ } finally {
+ clean();
+ }
+ }
+
+ @Override
+ public boolean hmacVerify(
+ KMKey key,
+ byte[] data,
+ short dataStart,
+ short dataLength,
+ byte[] mac,
+ short macStart,
+ short macLength) {
+ KMHmacKey hmacKey = (KMHmacKey) key;
+ hmacSignature.init(hmacKey.hmacKey, Signature.MODE_VERIFY);
+ return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, macLength);
+ }
+
+ @Override
+ public short rsaDecipherOAEP256(
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ byte[] modBuffer,
+ short modOff,
+ short modLength,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart) {
+ RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate();
+ key.setExponent(secret, (short) secretStart, (short) secretLength);
+ key.setModulus(modBuffer, (short) modOff, (short) modLength);
+ rsaOaepDecipher.init(key, Cipher.MODE_DECRYPT);
+ return rsaOaepDecipher.doFinal(
+ inputDataBuf,
+ (short) inputDataStart,
+ (short) inputDataLength,
+ outputDataBuf,
+ (short) outputDataStart);
+ }
+
+ private byte mapSignature256Alg(byte alg, byte padding, byte digest) {
+ switch (alg) {
+ case KMType.RSA:
+ switch (padding) {
+ case KMType.RSA_PKCS1_1_5_SIGN:
+ {
+ if (digest == KMType.DIGEST_NONE) {
+ return KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST;
+ } else {
+ return Signature.ALG_RSA_SHA_256_PKCS1;
+ }
+ }
+ case KMType.RSA_PSS:
+ return Signature.ALG_RSA_SHA_256_PKCS1_PSS;
+ case KMType.PADDING_NONE:
+ return KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD;
+ }
+ break;
+ case KMType.EC:
+ if (digest == KMType.DIGEST_NONE) {
+ return KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST;
+ } else {
+ return Signature.ALG_ECDSA_SHA_256;
+ }
+ case KMType.HMAC:
+ return Signature.ALG_HMAC_SHA_256;
+ }
+ return -1;
+ }
+
+ private byte mapCipherAlg(byte alg, byte padding, byte blockmode, byte digest) {
+ switch (alg) {
+ case KMType.AES:
+ switch (blockmode) {
+ case KMType.ECB:
+ return Cipher.ALG_AES_BLOCK_128_ECB_NOPAD;
+ case KMType.CBC:
+ return Cipher.ALG_AES_BLOCK_128_CBC_NOPAD;
+ case KMType.CTR:
+ return Cipher.ALG_AES_CTR;
+ case KMType.GCM:
+ return AEADCipher.ALG_AES_GCM;
+ }
+ break;
+ case KMType.DES:
+ switch (blockmode) {
+ case KMType.ECB:
+ return Cipher.ALG_DES_ECB_NOPAD;
+ case KMType.CBC:
+ return Cipher.ALG_DES_CBC_NOPAD;
+ }
+ break;
+ case KMType.RSA:
+ switch (padding) {
+ case KMType.PADDING_NONE:
+ return Cipher.ALG_RSA_NOPAD;
+ case KMType.RSA_PKCS1_1_5_ENCRYPT:
+ return Cipher.ALG_RSA_PKCS1;
+ case KMType.RSA_OAEP:
+ {
+ if (digest == KMType.SHA1) {
+ /* MGF Digest is SHA1 */
+ return KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1;
+ } else if (digest == KMType.SHA2_256) {
+ /* MGF Digest is SHA256 */
+ return KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256;
+ } else {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ }
+ }
+ break;
+ }
+ return -1;
+ }
+
+ public KMOperation createSymmetricCipher(
+ short alg,
+ short purpose,
+ short macLength,
+ short blockMode,
+ short padding,
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ byte[] ivBuffer,
+ short ivStart,
+ short ivLength,
+ boolean isRkp) {
+
+ short cipherAlg = mapCipherAlg((byte) alg, (byte) padding, (byte) blockMode, (byte) 0);
+ KMOperation operation = null;
+ if (isRkp) {
+ operation = poolMgr.getRKpOperation(purpose, cipherAlg, alg, padding, blockMode, macLength);
+ } else {
+ operation =
+ poolMgr.getOperationImpl(
+ purpose, cipherAlg, alg, padding, blockMode, macLength, secretLength, false);
+ }
+ // Get the KeyObject from the operation and update the key with the secret key material.
+ KMKeyObject keyObj = operation.getKeyObject();
+ Key key = (Key) keyObj.keyObjectInst;
+ switch (secretLength) {
+ case 32:
+ case 16:
+ ((AESKey) key).setKey(secret, secretStart);
+ break;
+ case 24:
+ ((DESKey) key).setKey(secret, secretStart);
+ break;
+ default:
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ break;
+ }
+ ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, ivBuffer, ivStart, ivLength);
+ return operation;
+ }
+
+ public KMOperation createHmacSignerVerifier(
+ short purpose,
+ short digest,
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ boolean isRkp) {
+ KMOperation operation = null;
+ if (digest != KMType.SHA2_256) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ if (isRkp) {
+ operation =
+ poolMgr.getRKpOperation(
+ purpose,
+ Signature.ALG_HMAC_SHA_256,
+ KMType.HMAC,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE);
+ } else {
+ operation =
+ poolMgr.getOperationImpl(
+ purpose,
+ Signature.ALG_HMAC_SHA_256,
+ KMType.HMAC,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ (short) 0,
+ false);
+ }
+ // Get the KeyObject from the operation and update the key with the secret key material.
+ KMKeyObject keyObj = operation.getKeyObject();
+ HMACKey key = (HMACKey) keyObj.keyObjectInst;
+ key.setKey(secret, secretStart, secretLength);
+ ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0);
+ return operation;
+ }
+
+ private KMOperation createHmacSignerVerifier(
+ short purpose, short digest, HMACKey hmacKey, boolean isTrustedConf) {
+ if (digest != KMType.SHA2_256) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ KMOperation operation =
+ poolMgr.getOperationImpl(
+ purpose,
+ Signature.ALG_HMAC_SHA_256,
+ KMType.HMAC,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ (short) 0,
+ isTrustedConf);
+ // Get the KeyObject from the operation and update the key with the secret key material.
+ KMKeyObject keyObj = operation.getKeyObject();
+ HMACKey key = (HMACKey) keyObj.keyObjectInst;
+ short len = hmacKey.getKey(tmpArray, (short) 0);
+ key.setKey(tmpArray, (short) 0, len);
+ ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0);
+ return operation;
+ }
+
+ @Override
+ public KMOperation getRkpOperation(
+ byte purpose,
+ byte alg,
+ byte digest,
+ byte padding,
+ byte blockMode,
+ byte[] keyBuf,
+ short keyStart,
+ short keyLength,
+ byte[] ivBuf,
+ short ivStart,
+ short ivLength,
+ short macLength) {
+ KMOperation opr = null;
+ switch (alg) {
+ case KMType.AES:
+ // Convert macLength to bytes
+ macLength = (short) (macLength / 8);
+ opr =
+ createSymmetricCipher(
+ alg,
+ purpose,
+ macLength,
+ blockMode,
+ padding,
+ keyBuf,
+ keyStart,
+ keyLength,
+ ivBuf,
+ ivStart,
+ ivLength,
+ true /* isRKP */);
+ break;
+ case KMType.HMAC:
+ opr =
+ createHmacSignerVerifier(
+ purpose, digest, keyBuf, keyStart, keyLength, true /* isRKP */);
+ break;
+ default:
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ break;
+ }
+ return opr;
+ }
+
+ @Override
+ public KMOperation initSymmetricOperation(
+ byte purpose,
+ byte alg,
+ byte digest,
+ byte padding,
+ byte blockMode,
+ byte[] keyBuf,
+ short keyStart,
+ short keyLength,
+ byte[] ivBuf,
+ short ivStart,
+ short ivLength,
+ short macLength) {
+ KMOperation opr = null;
+ switch (alg) {
+ case KMType.AES:
+ case KMType.DES:
+ // Convert macLength to bytes
+ macLength = (short) (macLength / 8);
+ opr =
+ createSymmetricCipher(
+ alg,
+ purpose,
+ macLength,
+ blockMode,
+ padding,
+ keyBuf,
+ keyStart,
+ keyLength,
+ ivBuf,
+ ivStart,
+ ivLength,
+ false /* isRKP */);
+ break;
+ case KMType.HMAC:
+ opr =
+ createHmacSignerVerifier(
+ purpose, digest, keyBuf, keyStart, keyLength, false /* isRKP */);
+ break;
+ default:
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ break;
+ }
+ return opr;
+ }
+
+ @Override
+ public KMOperation initSymmetricOperation(
+ byte purpose,
+ byte alg,
+ byte digest,
+ byte padding,
+ byte blockMode,
+ Object key,
+ byte interfaceType,
+ byte[] ivBuf,
+ short ivStart,
+ short ivLength,
+ short macLength,
+ boolean oneShot) {
+ short keyLen = 0;
+ globalOperation.setPurpose(purpose);
+ globalOperation.setAlgorithmType(alg);
+ globalOperation.setPaddingAlgorithm(padding);
+ globalOperation.setBlockMode(blockMode);
+ try {
+ switch (interfaceType) {
+ case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY:
+ KMAESKey aesKey = (KMAESKey) key;
+ keyLen = (short) (aesKey.aesKey.getSize() / 8);
+ aesKey.aesKey.getKey(tmpArray, (short) 0);
+ break;
+
+ default:
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+
+ switch (alg) {
+ case KMType.HMAC:
+ HMACKey hmackey = createHMACKey(tmpArray, (short) 0, keyLen);
+ globalOperation.setSignature(hmacSignature);
+ globalOperation.init(hmackey, digest, null, (short) 0, (short) 0);
+ break;
+
+ default:
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ } finally {
+ clean();
+ }
+ return globalOperation;
+ }
+
+ @Override
+ public KMOperation initTrustedConfirmationSymmetricOperation(KMKey computedHmacKey) {
+ KMHmacKey key = (KMHmacKey) computedHmacKey;
+ return createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.hmacKey, true);
+ }
+
+ public KMOperation createRsaSigner(
+ short digest,
+ short padding,
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ byte[] modBuffer,
+ short modOff,
+ short modLength) {
+ byte alg = mapSignature256Alg(KMType.RSA, (byte) padding, (byte) digest);
+ KMOperation operation =
+ poolMgr.getOperationImpl(
+ KMType.SIGN,
+ alg,
+ KMType.RSA,
+ padding,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ secretLength,
+ false);
+ // Get the KeyObject from the operation and update the key with the secret key material.
+ KMKeyObject keyObj = operation.getKeyObject();
+ RSAPrivateKey key = (RSAPrivateKey) ((KeyPair) (keyObj.keyObjectInst)).getPrivate();
+ key.setExponent(secret, secretStart, secretLength);
+ key.setModulus(modBuffer, modOff, modLength);
+ ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0);
+ return operation;
+ }
+
+ public KMOperation createRsaDecipher(
+ short padding,
+ short mgfDigest,
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ byte[] modBuffer,
+ short modOff,
+ short modLength) {
+ byte cipherAlg = mapCipherAlg(KMType.RSA, (byte) padding, (byte) 0, (byte) mgfDigest);
+ KMOperation operation =
+ poolMgr.getOperationImpl(
+ KMType.DECRYPT,
+ cipherAlg,
+ KMType.RSA,
+ padding,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ secretLength,
+ false);
+ // Get the KeyObject from the operation and update the key with the secret key material.
+ KMKeyObject keyObj = operation.getKeyObject();
+ RSAPrivateKey key = (RSAPrivateKey) ((KeyPair) (keyObj.keyObjectInst)).getPrivate();
+ key.setExponent(secret, secretStart, secretLength);
+ key.setModulus(modBuffer, modOff, modLength);
+ ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, null, (short) 0, (short) 0);
+ return operation;
+ }
+
+ public KMOperation createEcSigner(
+ short digest, byte[] secret, short secretStart, short secretLength) {
+ byte alg = mapSignature256Alg(KMType.EC, (byte) 0, (byte) digest);
+ KMOperation operation =
+ poolMgr.getOperationImpl(
+ KMType.SIGN,
+ alg,
+ KMType.EC,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ secretLength,
+ false);
+ KMKeyObject keyObj = operation.getKeyObject();
+ ECPrivateKey key = (ECPrivateKey) ((KeyPair) (keyObj.keyObjectInst)).getPrivate();
+ key.setS(secret, secretStart, secretLength);
+ ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0);
+ return operation;
+ }
+
+ public KMOperation createKeyAgreement(byte[] secret, short secretStart, short secretLength) {
+ KMOperation operation =
+ poolMgr.getOperationImpl(
+ KMType.AGREE_KEY,
+ KeyAgreement.ALG_EC_SVDP_DH_PLAIN,
+ KMType.EC,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ (short) 0,
+ false);
+ KMKeyObject keyObj = operation.getKeyObject();
+ ECPrivateKey key = (ECPrivateKey) ((KeyPair) (keyObj.keyObjectInst)).getPrivate();
+ key.setS(secret, secretStart, secretLength);
+ ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, null, (short) 0, (short) 0);
+ return operation;
+ }
+
+ @Override
+ public KMOperation initAsymmetricOperation(
+ byte purpose,
+ byte alg,
+ byte padding,
+ byte digest,
+ byte mgfDigest,
+ byte[] privKeyBuf,
+ short privKeyStart,
+ short privKeyLength,
+ byte[] pubModBuf,
+ short pubModStart,
+ short pubModLength) {
+ KMOperation opr = null;
+ if (alg == KMType.RSA) {
+ switch (purpose) {
+ case KMType.SIGN:
+ opr =
+ createRsaSigner(
+ digest,
+ padding,
+ privKeyBuf,
+ privKeyStart,
+ privKeyLength,
+ pubModBuf,
+ pubModStart,
+ pubModLength);
+ break;
+ case KMType.DECRYPT:
+ opr =
+ createRsaDecipher(
+ padding,
+ mgfDigest,
+ privKeyBuf,
+ privKeyStart,
+ privKeyLength,
+ pubModBuf,
+ pubModStart,
+ pubModLength);
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ break;
+ }
+ } else if (alg == KMType.EC) {
+ switch (purpose) {
+ case KMType.SIGN:
+ opr = createEcSigner(digest, privKeyBuf, privKeyStart, privKeyLength);
+ break;
+
+ case KMType.AGREE_KEY:
+ opr = createKeyAgreement(privKeyBuf, privKeyStart, privKeyLength);
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ break;
+ }
+ } else {
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ }
+ return opr;
+ }
+
+ @Override
+ public short cmacKDF(
+ KMKey pSharedKey,
+ byte[] label,
+ short labelStart,
+ short labelLen,
+ byte[] context,
+ short contextStart,
+ short contextLength,
+ byte[] keyBuf,
+ short keyStart) {
+ HMACKey key =
+ cmacKdf(pSharedKey, label, labelStart, labelLen, context, contextStart, contextLength);
+ return key.getKey(keyBuf, keyStart);
+ }
+
+ @Override
+ public boolean isUpgrading() {
+ return UpgradeManager.isUpgrading();
+ }
+
+ @Override
+ public KMKey createMasterKey(KMKey masterKey, short keySizeBits) {
+ try {
+ if (masterKey == null) {
+ AESKey key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, keySizeBits, false);
+ masterKey = new KMAESKey(key);
+ }
+ short keyLen = (short) (keySizeBits / 8);
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, keyLen, (byte) 0);
+ getTrueRandomNumber(tmpArray, (short) 0, keyLen);
+ ((KMAESKey) masterKey).aesKey.setKey(tmpArray, (short) 0);
+ return (KMKey) masterKey;
+ } finally {
+ clean();
+ }
+ }
+
+ @Override
+ public KMKey createPreSharedKey(KMKey preSharedKey, byte[] keyData, short offset, short length) {
+ short lengthInBits = (short) (length * 8);
+ if ((lengthInBits % 8 != 0) || !(lengthInBits >= 64 && lengthInBits <= 512)) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ if (preSharedKey == null) {
+ HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, lengthInBits, false);
+ preSharedKey = new KMHmacKey(key);
+ }
+ ((KMHmacKey) preSharedKey).hmacKey.setKey(keyData, offset, length);
+ return (KMKey) preSharedKey;
+ }
+
+ @Override
+ public KMKey createComputedHmacKey(
+ KMKey computedHmacKey, byte[] keyData, short offset, short length) {
+ if (length != COMPUTED_HMAC_KEY_SIZE) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ if (computedHmacKey == null) {
+ HMACKey key =
+ (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), false);
+ computedHmacKey = new KMHmacKey(key);
+ }
+ ((KMHmacKey) computedHmacKey).hmacKey.setKey(keyData, offset, length);
+ return (KMKey) computedHmacKey;
+ }
+
+ @Override
+ public short ecSign256(
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart) {
+
+ ECPrivateKey key = (ECPrivateKey) ecKeyPair.getPrivate();
+ key.setS(secret, secretStart, secretLength);
+
+ Signature.OneShot signer = null;
+ try {
+
+ signer =
+ Signature.OneShot.open(
+ MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL);
+ signer.init(key, Signature.MODE_SIGN);
+ return signer.sign(
+ inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart);
+ } finally {
+ if (signer != null) {
+ signer.close();
+ }
+ }
+ }
+
+ @Override
+ public short rsaSign256Pkcs1(
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ byte[] modBuf,
+ short modStart,
+ short modLength,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart) {
+
+ Signature.OneShot signer = null;
+ try {
+
+ signer =
+ Signature.OneShot.open(
+ MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_RSA, Cipher.PAD_PKCS1);
+
+ RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate();
+ ;
+ key.setExponent(secret, secretStart, secretLength);
+ key.setModulus(modBuf, modStart, modLength);
+
+ signer.init(key, Signature.MODE_SIGN);
+ return signer.sign(
+ inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart);
+ } finally {
+ if (signer != null) {
+ signer.close();
+ }
+ }
+ }
+
+ @Override
+ public boolean isAttestationKeyProvisioned() {
+ return false;
+ }
+
+ @Override
+ public short getAttestationKeyAlgorithm() {
+ return KMType.INVALID_VALUE;
+ }
+
+ @Override
+ public short hkdf(
+ byte[] ikm,
+ short ikmOff,
+ short ikmLen,
+ byte[] salt,
+ short saltOff,
+ short saltLen,
+ byte[] info,
+ short infoOff,
+ short infoLen,
+ byte[] out,
+ short outOff,
+ short outLen) {
+ // HMAC_extract
+ hkdfExtract(ikm, ikmOff, ikmLen, salt, saltOff, saltLen, tmpArray, (short) 0);
+ // HMAC_expand
+ return hkdfExpand(tmpArray, (short) 0, (short) 32, info, infoOff, infoLen, out, outOff, outLen);
+ }
+
+ private short hkdfExtract(
+ byte[] ikm,
+ short ikmOff,
+ short ikmLen,
+ byte[] salt,
+ short saltOff,
+ short saltLen,
+ byte[] out,
+ short off) {
+ // https://tools.ietf.org/html/rfc5869#section-2.2
+ HMACKey hmacKey = createHMACKey(salt, saltOff, saltLen);
+ hmacSignature.init(hmacKey, Signature.MODE_SIGN);
+ return hmacSignature.sign(ikm, ikmOff, ikmLen, out, off);
+ }
+
+ private short hkdfExpand(
+ byte[] prk,
+ short prkOff,
+ short prkLen,
+ byte[] info,
+ short infoOff,
+ short infoLen,
+ byte[] out,
+ short outOff,
+ short outLen) {
+ // https://tools.ietf.org/html/rfc5869#section-2.3
+ short digestLen = (short) 32; // SHA256 digest length.
+ // Calculate no of iterations N.
+ short n = (short) ((short) (outLen + digestLen - 1) / digestLen);
+ if (n > 255) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ HMACKey hmacKey = createHMACKey(prk, prkOff, prkLen);
+ Util.arrayFill(tmpArray, (short) 0, (short) 33, (byte) 0);
+ short bytesCopied = 0;
+ short len = 0;
+ for (short i = 0; i < n; i++) {
+ tmpArray[0]++;
+ hmacSignature.init(hmacKey, Signature.MODE_SIGN);
+ if (i != 0) {
+ hmacSignature.update(tmpArray, (short) 1, (short) 32);
+ }
+ hmacSignature.update(info, infoOff, infoLen);
+ len = hmacSignature.sign(tmpArray, (short) 0, (short) 1, tmpArray, (short) 1);
+ if ((short) (bytesCopied + len) > outLen) {
+ len = (short) (outLen - bytesCopied);
+ }
+ Util.arrayCopyNonAtomic(tmpArray, (short) 1, out, (short) (outOff + bytesCopied), len);
+ bytesCopied += len;
+ }
+ return outLen;
+ }
+
+ @Override
+ public short ecdhKeyAgreement(
+ byte[] privKey,
+ short privKeyOff,
+ short privKeyLen,
+ byte[] publicKey,
+ short publicKeyOff,
+ short publicKeyLen,
+ byte[] secret,
+ short secretOff) {
+ keyAgreement.init(createEcKey(privKey, privKeyOff, privKeyLen));
+ return keyAgreement.generateSecret(publicKey, publicKeyOff, publicKeyLen, secret, secretOff);
+ }
+
+ @Override
+ public boolean ecVerify256(
+ byte[] pubKey,
+ short pubKeyOffset,
+ short pubKeyLen,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] signatureDataBuf,
+ short signatureDataStart,
+ short signatureDataLen) {
+ Signature.OneShot signer = null;
+ try {
+ signer =
+ Signature.OneShot.open(
+ MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL);
+ ECPublicKey key = (ECPublicKey) ecKeyPair.getPublic();
+ key.setW(pubKey, pubKeyOffset, pubKeyLen);
+ signer.init(key, Signature.MODE_VERIFY);
+ return signer.verify(
+ inputDataBuf,
+ inputDataStart,
+ inputDataLength,
+ signatureDataBuf,
+ signatureDataStart,
+ (short) (signatureDataBuf[(short) (signatureDataStart + 1)] + 2));
+ } finally {
+ if (signer != null) {
+ signer.close();
+ }
+ }
+ }
+
+ @Override
+ public short signWithDeviceUniqueKey(
+ KMKey ecPrivKey,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart) {
+ Signature.OneShot signer = null;
+ try {
+ signer =
+ Signature.OneShot.open(
+ MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL);
+ signer.init(
+ ((KMECDeviceUniqueKeyPair) ecPrivKey).ecKeyPair.getPrivate(), Signature.MODE_SIGN);
+ return signer.sign(
+ inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart);
+ } finally {
+ if (signer != null) {
+ signer.close();
+ }
+ }
+ }
+
+ @Override
+ public KMKey createRkpDeviceUniqueKeyPair(
+ KMKey key,
+ byte[] pubKey,
+ short pubKeyOff,
+ short pubKeyLen,
+ byte[] privKey,
+ short privKeyOff,
+ short privKeyLen) {
+ if (key == null) {
+ KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256);
+ poolMgr.initECKey(ecKeyPair);
+ key = new KMECDeviceUniqueKeyPair(ecKeyPair);
+ }
+ ECPrivateKey ecKeyPair = (ECPrivateKey) ((KMECDeviceUniqueKeyPair) key).ecKeyPair.getPrivate();
+ ECPublicKey ecPublicKey = (ECPublicKey) ((KMECDeviceUniqueKeyPair) key).ecKeyPair.getPublic();
+ ecKeyPair.setS(privKey, privKeyOff, privKeyLen);
+ ecPublicKey.setW(pubKey, pubKeyOff, pubKeyLen);
+ return (KMKey) key;
+ }
+
+ @Override
+ public KMKey createRkpMacKey(KMKey rkpMacKey, byte[] keyData, short offset, short length) {
+ if (rkpMacKey == null) {
+ HMACKey key =
+ (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), false);
+ rkpMacKey = new KMHmacKey(key);
+ }
+ ((KMHmacKey) rkpMacKey).hmacKey.setKey(keyData, offset, length);
+ return rkpMacKey;
+ }
+
+ @Override
+ public short messageDigest256(
+ byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset) {
+ MessageDigest.OneShot mDigest = null;
+ short len = 0;
+ try {
+ mDigest = MessageDigest.OneShot.open(MessageDigest.ALG_SHA_256);
+ len = mDigest.doFinal(inBuff, inOffset, inLength, outBuff, outOffset);
+ } finally {
+ if (mDigest != null) {
+ mDigest.close();
+ mDigest = null;
+ }
+ }
+ return len;
+ }
+
+ public boolean isPowerReset() {
+ boolean flag = false;
+ if (resetFlag[0] == POWER_RESET_TRUE) {
+ resetFlag[0] = POWER_RESET_FALSE;
+ flag = true;
+ if (poolMgr != null) {
+ poolMgr.powerReset();
+ }
+ }
+ return flag;
+ }
+
+ @Override
+ public void onSave(Element element, byte interfaceType, Object object) {
+ element.write(interfaceType);
+ if (object == null) {
+ element.write(null);
+ return;
+ }
+ switch (interfaceType) {
+ case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY:
+ KMAESKey.onSave(element, (KMAESKey) object);
+ break;
+ case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY:
+ KMHmacKey.onSave(element, (KMHmacKey) object);
+ break;
+ case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR:
+ KMECDeviceUniqueKeyPair.onSave(element, (KMECDeviceUniqueKeyPair) object);
+ break;
+ case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY:
+ KMHmacKey.onSave(element, (KMHmacKey) object);
+ break;
+ default:
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ }
+
+ @Override
+ public Object onRestore(Element element) {
+ if (element == null) {
+ return null;
+ }
+ byte interfaceType = element.readByte();
+ switch (interfaceType) {
+ case KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY:
+ return KMHmacKey.onRestore((HMACKey) element.readObject());
+ case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY:
+ return KMAESKey.onRestore((AESKey) element.readObject());
+ case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY:
+ return KMHmacKey.onRestore((HMACKey) element.readObject());
+ case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR:
+ return KMECDeviceUniqueKeyPair.onRestore((KeyPair) element.readObject());
+ case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY:
+ return KMHmacKey.onRestore((HMACKey) element.readObject());
+ default:
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return null;
+ }
+
+ @Override
+ public short getBackupPrimitiveByteCount(byte interfaceType) {
+ short primitiveCount = 1; // interface type
+ switch (interfaceType) {
+ case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY:
+ primitiveCount += KMAESKey.getBackupPrimitiveByteCount();
+ break;
+ case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY:
+ primitiveCount += KMHmacKey.getBackupPrimitiveByteCount();
+ break;
+ case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR:
+ primitiveCount += KMECDeviceUniqueKeyPair.getBackupPrimitiveByteCount();
+ break;
+ case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY:
+ primitiveCount += KMHmacKey.getBackupPrimitiveByteCount();
+ break;
+ default:
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return primitiveCount;
+ }
+
+ @Override
+ public short getBackupObjectCount(byte interfaceType) {
+ switch (interfaceType) {
+ case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY:
+ return KMAESKey.getBackupObjectCount();
+ case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY:
+ return KMHmacKey.getBackupObjectCount();
+ case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR:
+ return KMECDeviceUniqueKeyPair.getBackupObjectCount();
+ case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY:
+ return KMHmacKey.getBackupObjectCount();
+ default:
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean isBootSignalEventSupported() {
+ return false;
+ }
+
+ @Override
+ public boolean isDeviceRebooted() {
+ return false;
+ }
+
+ @Override
+ public void clearDeviceBooted(boolean resetBootFlag) {
+ // To be filled
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java
new file mode 100644
index 0000000..05801b0
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java
@@ -0,0 +1,198 @@
+/*
+ * 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" 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.javacard.seprovider;
+
+/**
+ * The KMAttestationCert interface represents a X509 compliant attestation certificate required to
+ * support keymaster's attestKey function. This cert will be created according to the specifications
+ * given in android keymaster hal documentation. KMSeProvider has to provide the instance of this
+ * certificate. This interface is designed based on builder pattern and hence each method returns
+ * instance of cert.
+ */
+public interface KMAttestationCert {
+
+ /**
+ * Set verified boot hash.
+ *
+ * @param obj This is a KMByteBlob containing hash
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert verifiedBootHash(short obj);
+
+ /**
+ * Set verified boot key received during booting up.
+ *
+ * @param obj This is a KMByteBlob containing verified boot key.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert verifiedBootKey(short obj);
+
+ /**
+ * Set verified boot state received during booting up.
+ *
+ * @param val This is a byte containing verified boot state value.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert verifiedBootState(byte val);
+
+ /**
+ * Set uniqueId received from CA certificate during provisioning.
+ *
+ * @param scratchpad Buffer to store intermediate results.
+ * @param scratchPadOff Start offset of the scratchpad buffer.
+ * @param creationTime This buffer contains the CREATION_TIME value.
+ * @param creationTimeOff Start offset of creattionTime buffer.
+ * @param creationTimeLen Length of the creationTime buffer.
+ * @param attestAppId This buffer contains the ATTESTATION_APPLICATION_ID value.
+ * @param attestAppIdOff Start offset of the attestAppId buffer.
+ * @param attestAppIdLen Length of the attestAppId buffer.
+ * @param resetSinceIdRotation This holds the information of RESET_SINCE_ID_ROTATION.
+ * @param masterKey
+ * @return instance of KMAttestationCert.
+ */
+ KMAttestationCert makeUniqueId(
+ byte[] scratchpad,
+ short scratchPadOff,
+ byte[] creationTime,
+ short creationTimeOff,
+ short creationTimeLen,
+ byte[] attestAppId,
+ short attestAppIdOff,
+ short attestAppIdLen,
+ byte resetSinceIdRotation,
+ KMKey masterKey);
+
+ /**
+ * Set start time received from creation/activation time tag. Used for certificate's valid period.
+ *
+ * @param obj This is a KMByteBlob object containing start time.
+ * @param scratchpad Buffer to store intermediate results.
+ * @return instance of KMAttestationCert.
+ */
+ KMAttestationCert notBefore(short obj, boolean derEncoded, byte[] scratchpad);
+
+ /**
+ * Set expiry time received from expiry time tag or ca certificates expiry time. Used for
+ * certificate's valid period.
+ *
+ * @param usageExpiryTimeObj This is a KMByteBlob containing expiry time. certificate.
+ * @param scratchPad Buffer to store intermediate results.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert notAfter(short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad);
+
+ /**
+ * Set device lock status received during booting time or due to device lock command.
+ *
+ * @param val This is true if device is locked.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert deviceLocked(boolean val);
+
+ /**
+ * Set public key to be attested received from attestKey command.
+ *
+ * @param obj This is KMByteBlob containing the public key.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert publicKey(short obj);
+
+ /**
+ * Set attestation challenge received from attestKey command.
+ *
+ * @param obj This is KMByteBlob containing the attestation challenge.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert attestationChallenge(short obj);
+
+ /**
+ * Set extension tag received from key characteristics which needs to be added to android
+ * extension. This method will called once for each tag.
+ *
+ * @param tag is the KMByteBlob containing KMTag.
+ * @param hwEnforced is true if the tag has to be added to hw enforced list or else added to sw
+ * enforced list.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert extensionTag(short tag, boolean hwEnforced);
+
+ /**
+ * Set ASN.1 encoded X509 issuer field received from attestation key CA cert.
+ *
+ * @param obj This is KMByteBlob containing the issuer.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert issuer(short obj);
+
+ /**
+ * Set byte buffer to be used to generate certificate.
+ *
+ * @param buf This is byte[] buffer.
+ * @param bufStart This is short start offset.
+ * @param maxLen This is short length of the buffer.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen);
+
+ /**
+ * Get the start of the certificate
+ *
+ * @return start of the attestation cert.
+ */
+ short getCertStart();
+
+ /**
+ * Get the length of the certificate
+ *
+ * @return length of the attestation cert.
+ */
+ short getCertLength();
+
+ /**
+ * Build a fake signed certificate. After this method executes the certificate is ready with the
+ * signature equal to 1 byte which is 0 and with rsa signature algorithm.
+ */
+ void build();
+
+ /**
+ * Set the Serial number in the certificate. If no serial number is set then serial number is 1.
+ *
+ * @param serialNumber
+ */
+ boolean serialNumber(short serialNumber);
+
+ /**
+ * Set the Subject Name in the certificate.
+ *
+ * @param subject
+ */
+ boolean subjectName(short subject);
+
+ /**
+ * Set attestation key and mode.
+ *
+ * @param attestKey KMByteBlob of the key
+ * @param mode
+ */
+ KMAttestationCert ecAttestKey(short attestKey, byte mode);
+ /**
+ * Set attestation key and mode.
+ *
+ * @param attestKey KMByteBlob of the key
+ * @param mode
+ */
+ KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte mode);
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java
new file mode 100644
index 0000000..61ddb36
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java
@@ -0,0 +1,17 @@
+package com.android.javacard.seprovider;
+
+/**
+ * This class holds different interface type constants to differentiate between the instances of
+ * Computed Hmac key, device unique key pair, RKP Mac key, and master key when passed as generic
+ * objects. These constants are used in upgrade flow to retrieve the size of the object and
+ * primitive types saved and restored for respective key types.
+ */
+public class KMDataStoreConstants {
+ // INTERFACE Types
+ public static final byte INTERFACE_TYPE_COMPUTED_HMAC_KEY = 0x01;
+ // 0x02 reserved for INTERFACE_TYPE_ATTESTATION_KEY
+ public static final byte INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR = 0x03;
+ public static final byte INTERFACE_TYPE_MASTER_KEY = 0x04;
+ public static final byte INTERFACE_TYPE_PRE_SHARED_KEY = 0x05;
+ public static final byte INTERFACE_TYPE_RKP_MAC_KEY = 0x06;
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKeyPair.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKeyPair.java
new file mode 100644
index 0000000..0e430a3
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKeyPair.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright(C) 2021 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.security.ECPublicKey;
+import javacard.security.KeyPair;
+import org.globalplatform.upgrade.Element;
+
+/** This is a wrapper class for KeyPair. */
+public class KMECDeviceUniqueKeyPair implements KMKey {
+
+ public KeyPair ecKeyPair;
+
+ @Override
+ public short getPublicKey(byte[] buf, short offset) {
+ ECPublicKey publicKey = (ECPublicKey) ecKeyPair.getPublic();
+ return publicKey.getW(buf, offset);
+ }
+
+ public KMECDeviceUniqueKeyPair(KeyPair ecPair) {
+ ecKeyPair = ecPair;
+ }
+
+ public static void onSave(Element element, KMECDeviceUniqueKeyPair kmKey) {
+ element.write(kmKey.ecKeyPair);
+ }
+
+ public static KMECDeviceUniqueKeyPair onRestore(KeyPair ecKey) {
+ if (ecKey == null) {
+ return null;
+ }
+ return new KMECDeviceUniqueKeyPair(ecKey);
+ }
+
+ public static short getBackupPrimitiveByteCount() {
+ return (short) 0;
+ }
+
+ public static short getBackupObjectCount() {
+ return (short) 1;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java
new file mode 100644
index 0000000..83774ab
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java
@@ -0,0 +1,138 @@
+/*
+ * 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.Util;
+import javacard.security.CryptoException;
+import javacard.security.Key;
+import javacard.security.MessageDigest;
+import javacard.security.Signature;
+import javacardx.crypto.Cipher;
+
+/**
+ * This class provides support for ECDSA_NO_DIGEST signature algorithm. Added this because javacard
+ * 3.0.5 does not support this
+ */
+public class KMEcdsa256NoDigestSignature extends Signature {
+
+ public static final byte ALG_ECDSA_NODIGEST = (byte) 0x67;
+ public static final byte MAX_NO_DIGEST_MSG_LEN = 32;
+ private byte algorithm;
+ private Signature inst;
+
+ public KMEcdsa256NoDigestSignature(byte alg) {
+ algorithm = alg;
+ // There is no constant for no digest so ALG_ECDSA_SHA_256 is used. However,
+ // signPreComputedHash is used for signing which is equivalent to no digest sign.
+ inst = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false);
+ }
+
+ @Override
+ public void init(Key key, byte b) throws CryptoException {
+ inst.init(key, b);
+ }
+
+ @Override
+ public void init(Key key, byte b, byte[] bytes, short i, short i1) throws CryptoException {
+ inst.init(key, b, bytes, i, i1);
+ }
+
+ @Override
+ public void setInitialDigest(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3)
+ throws CryptoException {}
+
+ @Override
+ public byte getAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public byte getMessageDigestAlgorithm() {
+ return MessageDigest.ALG_NULL;
+ }
+
+ @Override
+ public byte getCipherAlgorithm() {
+ return 0;
+ }
+
+ @Override
+ public byte getPaddingAlgorithm() {
+ return Cipher.PAD_NULL;
+ }
+
+ @Override
+ public short getLength() throws CryptoException {
+ return inst.getLength();
+ }
+
+ @Override
+ public void update(byte[] message, short msgStart, short messageLength) throws CryptoException {
+ // HAL accumulates the data and send it at finish operation.
+ }
+
+ @Override
+ public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2)
+ throws CryptoException {
+ try {
+ if (i1 > MAX_NO_DIGEST_MSG_LEN) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_USE);
+ }
+ // add zeros to the left
+ if (i1 < MAX_NO_DIGEST_MSG_LEN) {
+ Util.arrayFillNonAtomic(
+ KMAndroidSEProvider.getInstance().tmpArray,
+ (short) 0,
+ (short) MAX_NO_DIGEST_MSG_LEN,
+ (byte) 0);
+ }
+ Util.arrayCopyNonAtomic(
+ bytes,
+ i,
+ KMAndroidSEProvider.getInstance().tmpArray,
+ (short) (MAX_NO_DIGEST_MSG_LEN - i1),
+ i1);
+ return inst.signPreComputedHash(
+ KMAndroidSEProvider.getInstance().tmpArray,
+ (short) 0,
+ (short) MAX_NO_DIGEST_MSG_LEN,
+ bytes1,
+ i2);
+ } finally {
+ KMAndroidSEProvider.getInstance().clean();
+ }
+ }
+
+ @Override
+ public short signPreComputedHash(byte[] bytes, short i, short i1, byte[] bytes1, short i2)
+ throws CryptoException {
+ return inst.sign(bytes, i, i1, bytes1, i2);
+ }
+
+ @Override
+ public boolean verify(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3)
+ throws CryptoException {
+ // Verification is handled inside HAL
+ return false;
+ }
+
+ @Override
+ public boolean verifyPreComputedHash(
+ byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) throws CryptoException {
+ // Verification is handled inside HAL
+ return false;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java
new file mode 100644
index 0000000..69cb069
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java
@@ -0,0 +1,32 @@
+/*
+ * 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" 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.javacard.seprovider;
+
+/**
+ * KMError includes all the error codes from android keymaster hal specifications. The values are
+ * positive unlike negative values in keymaster hal.
+ */
+public class KMError {
+
+ public static final short OK = 0;
+ public static final short UNSUPPORTED_PURPOSE = 2;
+ public static final short UNSUPPORTED_ALGORITHM = 4;
+ public static final short INVALID_INPUT_LENGTH = 21;
+ public static final short VERIFICATION_FAILED = 30;
+ public static final short TOO_MANY_OPERATIONS = 31;
+ public static final short INVALID_ARGUMENT = 38;
+ public static final short UNKNOWN_ERROR = 1000;
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java
new file mode 100644
index 0000000..79983a2
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java
@@ -0,0 +1,46 @@
+/*
+ * 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" 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.javacard.seprovider;
+
+import javacard.framework.JCSystem;
+
+/**
+ * KMException is shared instance of exception used for all exceptions in the applet. It is used to
+ * throw EMError errors.
+ */
+public class KMException extends RuntimeException {
+
+ private static short[] reason;
+ private static KMException exception;
+
+ private KMException() {}
+
+ public static short reason() {
+ return reason[0];
+ }
+
+ public static void throwIt(short e) {
+ if (reason == null) {
+ reason = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_DESELECT);
+ }
+ if (exception == null) {
+ exception = new KMException();
+ }
+ reason[0] = e;
+ throw exception;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java
new file mode 100644
index 0000000..e938a2b
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java
@@ -0,0 +1,53 @@
+/*
+ * 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.security.HMACKey;
+import org.globalplatform.upgrade.Element;
+
+/** This is a wrapper class for HMACKey. */
+public class KMHmacKey implements KMKey {
+
+ public HMACKey hmacKey;
+
+ public KMHmacKey(HMACKey key) {
+ hmacKey = key;
+ }
+
+ public static void onSave(Element element, KMHmacKey kmKey) {
+ element.write(kmKey.hmacKey);
+ }
+
+ public static KMHmacKey onRestore(HMACKey hmacKey) {
+ if (hmacKey == null) {
+ return null;
+ }
+ return new KMHmacKey(hmacKey);
+ }
+
+ public static short getBackupPrimitiveByteCount() {
+ return (short) 0;
+ }
+
+ public static short getBackupObjectCount() {
+ return (short) 1;
+ }
+
+ @Override
+ public short getPublicKey(byte[] buf, short offset) {
+ return (short) 0;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKey.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKey.java
new file mode 100644
index 0000000..9894382
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKey.java
@@ -0,0 +1,10 @@
+package com.android.javacard.seprovider;
+
+/**
+ * This interface helps to decouple Javacard internal key objects from the keymaster package. Using
+ * Javacard key objects provides security by providing protection against side channel attacks.
+ * KMAESKey, KMECDeviceUniqueKey and KMHmacKey implements this interface.
+ */
+public interface KMKey {
+ short getPublicKey(byte[] buf, short offset);
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKeyObject.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKeyObject.java
new file mode 100644
index 0000000..a37da08
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKeyObject.java
@@ -0,0 +1,10 @@
+package com.android.javacard.seprovider;
+
+/**
+ * This class holds the KeyObject and its associated algorithm value. Each KMKeyObject is tied to
+ * one of the crypto operations.
+ */
+public class KMKeyObject {
+ public byte algorithm;
+ public Object keyObjectInst;
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java
new file mode 100644
index 0000000..12e691e
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java
@@ -0,0 +1,75 @@
+/*
+ * 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" 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.javacard.seprovider;
+
+/**
+ * KMOperation represents a persistent operation started by keymaster hal's beginOperation function.
+ * This operation is persistent i.e. it will be stored in non volatile memory of se card. It will be
+ * returned back to KMSEProvider for the reuse when the operation is finished.
+ */
+public interface KMOperation {
+
+ // Used for cipher operations
+ short update(
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart);
+
+ // Used for signature operations
+ short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength);
+
+ // Used for finishing cipher operations or ecdh keyAgreement.
+ short finish(
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart);
+
+ // Used for finishing signing operations.
+ short sign(
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] signBuf,
+ short signStart);
+
+ // Used for finishing verifying operations.
+ boolean verify(
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] signBuf,
+ short signStart,
+ short signLength);
+
+ // Used for aborting the ongoing operations.
+ void abort();
+
+ // Used for AES GCM cipher operation.
+ void updateAAD(byte[] dataBuf, short dataStart, short dataLength);
+
+ // Used for getting output size before finishing a AES GCM cipher operation. For encryption this
+ // will
+ // include the auth tag which is appended at the end of the encrypted data. For decryption this
+ // will be
+ // size of the decrypted data only.
+ short getAESGCMOutputSize(short dataSize, short macLength);
+
+ KMKeyObject getKeyObject();
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java
new file mode 100644
index 0000000..8059e44
--- /dev/null
+++ b/ready_se/google/keymint/KM200/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);
+ }
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java
new file mode 100644
index 0000000..8ca2664
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java
@@ -0,0 +1,665 @@
+/*
+ * Copyright(C) 2021 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.security.AESKey;
+import javacard.security.CryptoException;
+import javacard.security.DESKey;
+import javacard.security.ECPrivateKey;
+import javacard.security.ECPublicKey;
+import javacard.security.HMACKey;
+import javacard.security.KeyAgreement;
+import javacard.security.KeyBuilder;
+import javacard.security.KeyPair;
+import javacard.security.Signature;
+import javacardx.crypto.AEADCipher;
+import javacardx.crypto.Cipher;
+
+/**
+ * This class creates and manages all the cipher, signer, key agreement, operation and trusted
+ * confirmation pool instances. Each cipher or signer pool can hold a maximum of 4 instances per
+ * algorithm; however, only one instance of each algorithm is created initially and if required more
+ * instances are created dynamically. A maximum of four operations can be performed simultaneously.
+ * Upon reaching the maximum limit of 4, further operations or crypto instances will throw a
+ * TOO_MANY_OPERATIONS error. TrustedConfirmation pool is to support any operation which has the
+ * TRUSTED_CONFIRMATION tag in its key parameters.
+ */
+public class KMPoolManager {
+
+ public static final byte MAX_OPERATION_INSTANCES = 4;
+ private static final byte HMAC_MAX_OPERATION_INSTANCES = 8;
+ public static final byte AES_128 = 0x04;
+ public static final byte AES_256 = 0x05;
+ // Resource type constants
+ public static final byte RESOURCE_TYPE_CRYPTO = 0x00;
+ public static final byte RESOURCE_TYPE_KEY = 0x01;
+ // static final variables
+ // --------------------------------------------------------------
+ // P-256 Curve Parameters
+ static byte[] secp256r1_P;
+ static byte[] secp256r1_A;
+
+ static byte[] secp256r1_B;
+ static byte[] secp256r1_S;
+
+ // Uncompressed form
+ static byte[] secp256r1_UCG;
+ static byte[] secp256r1_N;
+ static final short secp256r1_H = 1;
+ // --------------------------------------------------------------
+
+ // Cipher pool
+ private Object[] cipherPool;
+ // Signature pool
+ private Object[] signerPool;
+ // Keyagreement pool
+ private Object[] keyAgreementPool;
+ // KMOperationImpl pool
+ private Object[] operationPool;
+ // Hmac signer pool which is used to support TRUSTED_CONFIRMATION_REQUIRED tag.
+ private Object[] hmacSignOperationPool;
+
+ private Object[] keysPool;
+ // RKP uses AESGCM and HMAC in generateCSR flow.
+ KMOperation rkpOPeration;
+ Cipher rkpAesGcm;
+ Signature rkpHmac;
+ KMKeyObject rkpHmacKey;
+ KMKeyObject rkpAesKey;
+
+ final byte[] KEY_ALGS = {
+ AES_128, AES_256, KMType.DES, KMType.RSA, KMType.EC, KMType.HMAC,
+ };
+
+ final byte[] CIPHER_ALGS = {
+ Cipher.ALG_AES_BLOCK_128_CBC_NOPAD,
+ Cipher.ALG_AES_BLOCK_128_ECB_NOPAD,
+ Cipher.ALG_DES_CBC_NOPAD,
+ Cipher.ALG_DES_ECB_NOPAD,
+ Cipher.ALG_AES_CTR,
+ Cipher.ALG_RSA_PKCS1,
+ KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1,
+ KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256,
+ Cipher.ALG_RSA_NOPAD,
+ AEADCipher.ALG_AES_GCM
+ };
+
+ final byte[] SIG_ALGS = {
+ Signature.ALG_RSA_SHA_256_PKCS1,
+ Signature.ALG_RSA_SHA_256_PKCS1_PSS,
+ Signature.ALG_ECDSA_SHA_256,
+ Signature.ALG_HMAC_SHA_256,
+ KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD,
+ KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST,
+ KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST
+ };
+
+ final byte[] KEY_AGREE_ALGS = {KeyAgreement.ALG_EC_SVDP_DH_PLAIN};
+
+ private static KMPoolManager poolManager;
+
+ public static KMPoolManager getInstance() {
+ if (poolManager == null) {
+ poolManager = new KMPoolManager();
+ }
+ return poolManager;
+ }
+
+ public static void initStatics() {
+ secp256r1_P =
+ new byte[] {
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF
+ };
+
+ secp256r1_A =
+ new byte[] {
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFC
+ };
+
+ secp256r1_B =
+ new byte[] {
+ (byte) 0x5A, (byte) 0xC6, (byte) 0x35, (byte) 0xD8, (byte) 0xAA, (byte) 0x3A,
+ (byte) 0x93, (byte) 0xE7, (byte) 0xB3, (byte) 0xEB, (byte) 0xBD, (byte) 0x55,
+ (byte) 0x76, (byte) 0x98, (byte) 0x86, (byte) 0xBC, (byte) 0x65, (byte) 0x1D,
+ (byte) 0x06, (byte) 0xB0, (byte) 0xCC, (byte) 0x53, (byte) 0xB0, (byte) 0xF6,
+ (byte) 0x3B, (byte) 0xCE, (byte) 0x3C, (byte) 0x3E, (byte) 0x27, (byte) 0xD2,
+ (byte) 0x60, (byte) 0x4B
+ };
+
+ secp256r1_S =
+ new byte[] {
+ (byte) 0xC4, (byte) 0x9D, (byte) 0x36, (byte) 0x08, (byte) 0x86, (byte) 0xE7,
+ (byte) 0x04, (byte) 0x93, (byte) 0x6A, (byte) 0x66, (byte) 0x78, (byte) 0xE1,
+ (byte) 0x13, (byte) 0x9D, (byte) 0x26, (byte) 0xB7, (byte) 0x81, (byte) 0x9F,
+ (byte) 0x7E, (byte) 0x90
+ };
+
+ // Uncompressed form
+ secp256r1_UCG =
+ new byte[] {
+ (byte) 0x04, (byte) 0x6B, (byte) 0x17, (byte) 0xD1, (byte) 0xF2, (byte) 0xE1,
+ (byte) 0x2C, (byte) 0x42, (byte) 0x47, (byte) 0xF8, (byte) 0xBC, (byte) 0xE6,
+ (byte) 0xE5, (byte) 0x63, (byte) 0xA4, (byte) 0x40, (byte) 0xF2, (byte) 0x77,
+ (byte) 0x03, (byte) 0x7D, (byte) 0x81, (byte) 0x2D, (byte) 0xEB, (byte) 0x33,
+ (byte) 0xA0, (byte) 0xF4, (byte) 0xA1, (byte) 0x39, (byte) 0x45, (byte) 0xD8,
+ (byte) 0x98, (byte) 0xC2, (byte) 0x96, (byte) 0x4F, (byte) 0xE3, (byte) 0x42,
+ (byte) 0xE2, (byte) 0xFE, (byte) 0x1A, (byte) 0x7F, (byte) 0x9B, (byte) 0x8E,
+ (byte) 0xE7, (byte) 0xEB, (byte) 0x4A, (byte) 0x7C, (byte) 0x0F, (byte) 0x9E,
+ (byte) 0x16, (byte) 0x2B, (byte) 0xCE, (byte) 0x33, (byte) 0x57, (byte) 0x6B,
+ (byte) 0x31, (byte) 0x5E, (byte) 0xCE, (byte) 0xCB, (byte) 0xB6, (byte) 0x40,
+ (byte) 0x68, (byte) 0x37, (byte) 0xBF, (byte) 0x51, (byte) 0xF5
+ };
+
+ secp256r1_N =
+ new byte[] {
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xBC, (byte) 0xE6,
+ (byte) 0xFA, (byte) 0xAD, (byte) 0xA7, (byte) 0x17, (byte) 0x9E, (byte) 0x84,
+ (byte) 0xF3, (byte) 0xB9, (byte) 0xCA, (byte) 0xC2, (byte) 0xFC, (byte) 0x63,
+ (byte) 0x25, (byte) 0x51
+ };
+ }
+
+ private KMPoolManager() {
+ initStatics();
+ cipherPool = new Object[(short) (CIPHER_ALGS.length * MAX_OPERATION_INSTANCES)];
+ // Extra 4 algorithms are used to support TRUSTED_CONFIRMATION_REQUIRED feature.
+ signerPool =
+ new Object[(short) ((SIG_ALGS.length * MAX_OPERATION_INSTANCES) + MAX_OPERATION_INSTANCES)];
+ keyAgreementPool = new Object[(short) (KEY_AGREE_ALGS.length * MAX_OPERATION_INSTANCES)];
+
+ keysPool =
+ new Object[(short) ((KEY_ALGS.length * MAX_OPERATION_INSTANCES) + MAX_OPERATION_INSTANCES)];
+ operationPool = new Object[MAX_OPERATION_INSTANCES];
+ hmacSignOperationPool = new Object[MAX_OPERATION_INSTANCES];
+ /* Initialize pools */
+ initializeOperationPool();
+ initializeHmacSignOperationPool();
+ initializeSignerPool();
+ initializeCipherPool();
+ initializeKeyAgreementPool();
+ initializeKeysPool();
+ // Initialize the Crypto and Key objects required for RKP flow.
+ initializeRKpObjects();
+ }
+
+ private void initializeRKpObjects() {
+ rkpOPeration = new KMOperationImpl();
+ rkpAesGcm = Cipher.getInstance(AEADCipher.ALG_AES_GCM, false);
+ rkpHmac = Signature.getInstance(Signature.ALG_HMAC_SHA_256, false);
+ rkpAesKey = createKeyObjectInstance(AES_256);
+ rkpHmacKey = createKeyObjectInstance(KMType.HMAC);
+ }
+
+ private void initializeKeysPool() {
+ for (short index = 0; index < KEY_ALGS.length; index++) {
+ keysPool[index] = createKeyObjectInstance(KEY_ALGS[index]);
+ }
+ }
+
+ private void initializeOperationPool() {
+ for (short index = 0; index < MAX_OPERATION_INSTANCES; index++) {
+ operationPool[index] = new KMOperationImpl();
+ }
+ }
+
+ private void initializeHmacSignOperationPool() {
+ for (short index = 0; index < MAX_OPERATION_INSTANCES; index++) {
+ hmacSignOperationPool[index] = new KMOperationImpl();
+ }
+ }
+
+ // Create a signature instance of each algorithm once.
+ private void initializeSignerPool() {
+ short index;
+ for (index = 0; index < SIG_ALGS.length; index++) {
+ signerPool[index] = getSignatureInstance(SIG_ALGS[index]);
+ }
+
+ // Allocate extra 4 HMAC signer instances required for trusted confirmation
+ for (short len = (short) (index + 4); index < len; index++) {
+ signerPool[index] = getSignatureInstance(Signature.ALG_HMAC_SHA_256);
+ }
+ }
+
+ // Create a cipher instance of each algorithm once.
+ private void initializeCipherPool() {
+ for (short index = 0; index < CIPHER_ALGS.length; index++) {
+ cipherPool[index] = getCipherInstance(CIPHER_ALGS[index]);
+ }
+ }
+
+ private void initializeKeyAgreementPool() {
+ for (short index = 0; index < KEY_AGREE_ALGS.length; index++) {
+ keyAgreementPool[index] = getKeyAgreementInstance(KEY_AGREE_ALGS[index]);
+ }
+ }
+
+ private Object[] getCryptoPoolInstance(short purpose) {
+ switch (purpose) {
+ case KMType.AGREE_KEY:
+ return keyAgreementPool;
+
+ case KMType.ENCRYPT:
+ case KMType.DECRYPT:
+ return cipherPool;
+
+ case KMType.SIGN:
+ case KMType.VERIFY:
+ return signerPool;
+
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ return null;
+ }
+
+ private Object createInstance(short purpose, short alg) {
+ switch (purpose) {
+ case KMType.AGREE_KEY:
+ return getKeyAgreementInstance((byte) alg);
+
+ case KMType.ENCRYPT:
+ case KMType.DECRYPT:
+ return getCipherInstance((byte) alg);
+
+ case KMType.SIGN:
+ case KMType.VERIFY:
+ return getSignatureInstance((byte) alg);
+
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ return null;
+ }
+
+ private KeyAgreement getKeyAgreementInstance(byte alg) {
+ return KeyAgreement.getInstance(alg, false);
+ }
+
+ private Signature getSignatureInstance(byte alg) {
+ if (KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD == alg
+ || KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST == alg) {
+ return new KMRsa2048NoDigestSignature(alg);
+ } else if (KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST == alg) {
+ return new KMEcdsa256NoDigestSignature(alg);
+ } else {
+ return Signature.getInstance(alg, false);
+ }
+ }
+
+ private KMKeyObject createKeyObjectInstance(byte alg) {
+ Object keyObject = null;
+ switch (alg) {
+ case AES_128:
+ keyObject =
+ (AESKey)
+ KeyBuilder.buildKey(
+ KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_128, false);
+ break;
+ case AES_256:
+ keyObject =
+ (AESKey)
+ KeyBuilder.buildKey(
+ KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_256, false);
+ break;
+ case KMType.DES:
+ keyObject =
+ (DESKey)
+ KeyBuilder.buildKey(
+ KeyBuilder.TYPE_DES_TRANSIENT_RESET, KeyBuilder.LENGTH_DES3_3KEY, false);
+ break;
+ case KMType.RSA:
+ keyObject = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048);
+ break;
+ case KMType.EC:
+ keyObject = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256);
+ initECKey((KeyPair) keyObject);
+ break;
+ case KMType.HMAC:
+ keyObject =
+ (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_RESET, (short) 512, false);
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ KMKeyObject ptr = new KMKeyObject();
+ ptr.algorithm = alg;
+ ptr.keyObjectInst = keyObject;
+ return ptr;
+ }
+
+ private Cipher getCipherInstance(byte alg) {
+ if ((KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1 == alg)
+ || (KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256 == alg)) {
+ return new KMRsaOAEPEncoding(alg);
+ } else {
+ return Cipher.getInstance(alg, false);
+ }
+ }
+
+ /**
+ * Returns the first available resource from operation pool.
+ *
+ * @return instance of the available resource or null if no resource is available.
+ */
+ public KMOperation getResourceFromOperationPool(boolean isTrustedConfOpr) {
+ short index = 0;
+ KMOperationImpl impl;
+ Object[] oprPool;
+ if (isTrustedConfOpr) {
+ oprPool = hmacSignOperationPool;
+ } else {
+ oprPool = operationPool;
+ }
+ while (index < oprPool.length) {
+ impl = (KMOperationImpl) oprPool[index];
+ // Mode is always set. so compare using mode value.
+ if (impl.getPurpose() == KMType.INVALID_VALUE) {
+ return impl;
+ }
+ index++;
+ }
+ return null;
+ }
+
+ private byte getAlgorithm(short purpose, Object object) {
+ switch (purpose) {
+ case KMType.AGREE_KEY:
+ return ((KeyAgreement) object).getAlgorithm();
+
+ case KMType.ENCRYPT:
+ case KMType.DECRYPT:
+ return ((Cipher) object).getAlgorithm();
+
+ case KMType.SIGN:
+ case KMType.VERIFY:
+ return ((Signature) object).getAlgorithm();
+
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ return 0;
+ }
+
+ private boolean isResourceBusy(Object obj, byte resourceType) {
+ short index = 0;
+ while (index < MAX_OPERATION_INSTANCES) {
+ if (((KMOperationImpl) operationPool[index]).isResourceMatches(obj, resourceType)
+ || ((KMOperationImpl) hmacSignOperationPool[index])
+ .isResourceMatches(obj, resourceType)) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ private void setObject(short purpose, KMOperation operation, Object obj) {
+ switch (purpose) {
+ case KMType.AGREE_KEY:
+ ((KMOperationImpl) operation).setKeyAgreement((KeyAgreement) obj);
+ break;
+ case KMType.ENCRYPT:
+ case KMType.DECRYPT:
+ ((KMOperationImpl) operation).setCipher((Cipher) obj);
+ break;
+ case KMType.SIGN:
+ case KMType.VERIFY:
+ ((KMOperationImpl) operation).setSignature((Signature) obj);
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ }
+
+ private void reserveOperation(
+ KMOperation operation,
+ short purpose,
+ short strongboxAlgType,
+ short padding,
+ short blockMode,
+ short macLength,
+ Object obj,
+ KMKeyObject keyObject) {
+ ((KMOperationImpl) operation).setPurpose(purpose);
+ ((KMOperationImpl) operation).setAlgorithmType(strongboxAlgType);
+ ((KMOperationImpl) operation).setPaddingAlgorithm(padding);
+ ((KMOperationImpl) operation).setBlockMode(blockMode);
+ ((KMOperationImpl) operation).setMacLength(macLength);
+ ((KMOperationImpl) operation).setKeyObject(keyObject);
+ setObject(purpose, operation, obj);
+ }
+
+ public KMOperation getRKpOperation(
+ short purpose,
+ short alg,
+ short strongboxAlgType,
+ short padding,
+ short blockMode,
+ short macLength) {
+ if (((KMOperationImpl) rkpOPeration).getPurpose() != KMType.INVALID_VALUE) {
+ // Should not come here.
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ Object cryptoObj = null;
+ KMKeyObject keyObject = null;
+
+ switch (alg) {
+ case AEADCipher.ALG_AES_GCM:
+ cryptoObj = rkpAesGcm;
+ keyObject = rkpAesKey;
+ break;
+ case Signature.ALG_HMAC_SHA_256:
+ cryptoObj = rkpHmac;
+ keyObject = rkpHmacKey;
+ break;
+ default:
+ // Should not come here.
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ break;
+ }
+ reserveOperation(
+ rkpOPeration,
+ purpose,
+ strongboxAlgType,
+ padding,
+ blockMode,
+ macLength,
+ cryptoObj,
+ keyObject);
+ return rkpOPeration;
+ }
+
+ public KMOperation getOperationImpl(
+ short purpose,
+ short alg,
+ short strongboxAlgType,
+ short padding,
+ short blockMode,
+ short macLength,
+ short secretLength,
+ boolean isTrustedConfOpr) {
+ KMOperation operation;
+ // Throw exception if no resource from operation pool is available.
+ if (null == (operation = getResourceFromOperationPool(isTrustedConfOpr))) {
+ KMException.throwIt(KMError.TOO_MANY_OPERATIONS);
+ }
+ // Get one of the pool instances (cipher / signer / keyAgreement) based on purpose.
+ Object[] pool = getCryptoPoolInstance(purpose);
+ short index = 0;
+ short usageCount = 0;
+ short maxOperations = MAX_OPERATION_INSTANCES;
+ if (Signature.ALG_HMAC_SHA_256 == alg) {
+ maxOperations = HMAC_MAX_OPERATION_INSTANCES;
+ }
+
+ KMKeyObject keyObject = getKeyObjectFromPool(alg, secretLength, maxOperations);
+ while (index < pool.length) {
+ if (usageCount >= maxOperations) {
+ KMException.throwIt(KMError.TOO_MANY_OPERATIONS);
+ }
+ if (pool[index] == null) {
+ // Create one of the instance (Cipher / Signer / KeyAgreement] based on purpose.
+ Object cipherObject = createInstance(purpose, alg);
+ JCSystem.beginTransaction();
+ pool[index] = cipherObject;
+ JCSystem.commitTransaction();
+ reserveOperation(
+ operation,
+ purpose,
+ strongboxAlgType,
+ padding,
+ blockMode,
+ macLength,
+ pool[index],
+ keyObject);
+ break;
+ }
+ if (alg == getAlgorithm(purpose, pool[index])) {
+ // Check if the crypto instance is not busy and free to use.
+ if (!isResourceBusy(pool[index], RESOURCE_TYPE_CRYPTO)) {
+ reserveOperation(
+ operation,
+ purpose,
+ strongboxAlgType,
+ padding,
+ blockMode,
+ macLength,
+ pool[index],
+ keyObject);
+ break;
+ }
+ usageCount++;
+ }
+ index++;
+ }
+ return operation;
+ }
+
+ public KMKeyObject getKeyObjectFromPool(short alg, short secretLength, short maxOperations) {
+ KMKeyObject keyObject = null;
+ byte algo = mapAlgorithm(alg, secretLength);
+ short index = 0;
+ short usageCount = 0;
+ while (index < keysPool.length) {
+ if (usageCount >= maxOperations) {
+ KMException.throwIt(KMError.TOO_MANY_OPERATIONS);
+ }
+ if (keysPool[index] == null) {
+ keyObject = createKeyObjectInstance(algo);
+ JCSystem.beginTransaction();
+ keysPool[index] = keyObject;
+ JCSystem.commitTransaction();
+ break;
+ }
+ keyObject = (KMKeyObject) keysPool[index];
+ if (algo == keyObject.algorithm) {
+ // Check if the Object instance is not busy and free to use.
+ if (!isResourceBusy(keyObject, RESOURCE_TYPE_KEY)) {
+ break;
+ }
+ usageCount++;
+ }
+ index++;
+ }
+ return keyObject;
+ }
+
+ private byte mapAlgorithm(short alg, short secretLength) {
+ byte algo = 0;
+ switch (alg) {
+ case Cipher.ALG_AES_BLOCK_128_CBC_NOPAD:
+ case Cipher.ALG_AES_BLOCK_128_ECB_NOPAD:
+ case Cipher.ALG_AES_CTR:
+ case AEADCipher.ALG_AES_GCM:
+ if (secretLength == 16) {
+ algo = AES_128;
+ } else if (secretLength == 32) {
+ algo = AES_256;
+ } else {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ break;
+ case Cipher.ALG_DES_CBC_NOPAD:
+ case Cipher.ALG_DES_ECB_NOPAD:
+ algo = KMType.DES;
+ break;
+ case Cipher.ALG_RSA_PKCS1:
+ case KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1:
+ case KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256:
+ case Cipher.ALG_RSA_NOPAD:
+ case Signature.ALG_RSA_SHA_256_PKCS1:
+ case Signature.ALG_RSA_SHA_256_PKCS1_PSS:
+ case KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD:
+ case KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST:
+ algo = KMType.RSA;
+ break;
+ case Signature.ALG_ECDSA_SHA_256:
+ case KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST:
+ case KeyAgreement.ALG_EC_SVDP_DH_PLAIN:
+ algo = KMType.EC;
+ break;
+ case Signature.ALG_HMAC_SHA_256:
+ algo = KMType.HMAC;
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ return algo;
+ }
+
+ public void initECKey(KeyPair ecKeyPair) {
+ ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate();
+ ECPublicKey pubkey = (ECPublicKey) ecKeyPair.getPublic();
+ pubkey.setFieldFP(secp256r1_P, (short) 0, (short) secp256r1_P.length);
+ pubkey.setA(secp256r1_A, (short) 0, (short) secp256r1_A.length);
+ pubkey.setB(secp256r1_B, (short) 0, (short) secp256r1_B.length);
+ pubkey.setG(secp256r1_UCG, (short) 0, (short) secp256r1_UCG.length);
+ pubkey.setK(secp256r1_H);
+ pubkey.setR(secp256r1_N, (short) 0, (short) secp256r1_N.length);
+
+ privKey.setFieldFP(secp256r1_P, (short) 0, (short) secp256r1_P.length);
+ privKey.setA(secp256r1_A, (short) 0, (short) secp256r1_A.length);
+ privKey.setB(secp256r1_B, (short) 0, (short) secp256r1_B.length);
+ privKey.setG(secp256r1_UCG, (short) 0, (short) secp256r1_UCG.length);
+ privKey.setK(secp256r1_H);
+ privKey.setR(secp256r1_N, (short) 0, (short) secp256r1_N.length);
+ }
+
+ public void powerReset() {
+ short index = 0;
+ while (index < operationPool.length) {
+ ((KMOperationImpl) operationPool[index]).abort();
+ ((KMOperationImpl) hmacSignOperationPool[index]).abort();
+ index++;
+ }
+ // release rkp operation
+ rkpOPeration.abort();
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java
new file mode 100644
index 0000000..4890ce2
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java
@@ -0,0 +1,140 @@
+/*
+ * 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.Util;
+import javacard.security.CryptoException;
+import javacard.security.Key;
+import javacard.security.MessageDigest;
+import javacard.security.Signature;
+import javacardx.crypto.Cipher;
+
+/** This class provides support for RSA_NO_DIGEST signature algorithm. */
+public class KMRsa2048NoDigestSignature extends Signature {
+
+ public static final byte ALG_RSA_SIGN_NOPAD = (byte) 0x65;
+ public static final byte ALG_RSA_PKCS1_NODIGEST = (byte) 0x66;
+ private byte algorithm;
+ private Cipher inst;
+
+ public KMRsa2048NoDigestSignature(byte alg) {
+ algorithm = alg;
+ inst = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false);
+ }
+
+ @Override
+ public void init(Key key, byte b) throws CryptoException {
+ inst.init(key, b);
+ }
+
+ @Override
+ public void init(Key key, byte b, byte[] bytes, short i, short i1) throws CryptoException {
+ inst.init(key, b, bytes, i, i1);
+ }
+
+ @Override
+ public void setInitialDigest(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3)
+ throws CryptoException {}
+
+ @Override
+ public byte getAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public byte getMessageDigestAlgorithm() {
+ return MessageDigest.ALG_NULL;
+ }
+
+ @Override
+ public byte getCipherAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public byte getPaddingAlgorithm() {
+ return Cipher.PAD_NULL;
+ }
+
+ @Override
+ public short getLength() throws CryptoException {
+ return 0;
+ }
+
+ @Override
+ public void update(byte[] bytes, short i, short i1) throws CryptoException {
+ // HAL accumulates the data and send it at finish operation.
+ }
+
+ @Override
+ public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2)
+ throws CryptoException {
+ padData(bytes, i, i1, KMAndroidSEProvider.getInstance().tmpArray, (short) 0);
+ return inst.doFinal(
+ KMAndroidSEProvider.getInstance().tmpArray, (short) 0, (short) 256, bytes1, i2);
+ }
+
+ @Override
+ public short signPreComputedHash(byte[] bytes, short i, short i1, byte[] bytes1, short i2)
+ throws CryptoException {
+ return 0;
+ }
+
+ @Override
+ public boolean verify(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3)
+ throws CryptoException {
+ // Verification is handled inside HAL
+ return false;
+ }
+
+ @Override
+ public boolean verifyPreComputedHash(
+ byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) throws CryptoException {
+ // Verification is handled inside HAL
+ return false;
+ }
+
+ private void padData(byte[] buf, short start, short len, byte[] outBuf, short outBufStart) {
+ if (!isValidData(buf, start, len)) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ Util.arrayFillNonAtomic(outBuf, (short) outBufStart, (short) 256, (byte) 0x00);
+ if (algorithm == ALG_RSA_SIGN_NOPAD) { // add zero to right
+ } else if (algorithm == ALG_RSA_PKCS1_NODIGEST) { // 0x00||0x01||PS||0x00
+ outBuf[0] = 0x00;
+ outBuf[1] = 0x01;
+ Util.arrayFillNonAtomic(outBuf, (short) 2, (short) (256 - len - 3), (byte) 0xFF);
+ outBuf[(short) (256 - len - 1)] = 0x00;
+ } else {
+ CryptoException.throwIt(CryptoException.ILLEGAL_USE);
+ }
+ Util.arrayCopyNonAtomic(buf, start, outBuf, (short) (256 - len), len);
+ }
+
+ private boolean isValidData(byte[] buf, short start, short len) {
+ if (algorithm == ALG_RSA_SIGN_NOPAD) {
+ if (len > 256) {
+ return false;
+ }
+ } else { // ALG_RSA_PKCS1_NODIGEST
+ if (len > 245) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java
new file mode 100644
index 0000000..bf34ea1
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java
@@ -0,0 +1,289 @@
+/*
+ * 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.MessageDigest;
+import javacardx.crypto.Cipher;
+
+/** This class has the implementation for RSA_OAEP decoding algorithm. */
+public class KMRsaOAEPEncoding extends Cipher {
+
+ public static final byte ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1 = (byte) 0x1E;
+ public static final byte ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256 = (byte) 0x1F;
+
+ final short MGF1_BUF_SIZE = 256;
+ static byte[] mgf1Buf;
+ private Cipher cipher;
+ private byte hash;
+ private byte mgf1Hash;
+ private byte algorithm;
+
+ public KMRsaOAEPEncoding(byte alg) {
+ setDigests(alg);
+ cipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false);
+ algorithm = alg;
+ if (null == mgf1Buf) {
+ mgf1Buf =
+ JCSystem.makeTransientByteArray(MGF1_BUF_SIZE, JCSystem.MEMORY_TYPE_TRANSIENT_DESELECT);
+ }
+ }
+
+ private void setDigests(byte alg) {
+ switch (alg) {
+ case ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1:
+ hash = MessageDigest.ALG_SHA_256;
+ mgf1Hash = MessageDigest.ALG_SHA;
+ break;
+ case ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256:
+ hash = MessageDigest.ALG_SHA_256;
+ mgf1Hash = MessageDigest.ALG_SHA_256;
+ break;
+ default:
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ }
+ }
+
+ private short getDigestLength() {
+ switch (hash) {
+ case MessageDigest.ALG_SHA:
+ return MessageDigest.LENGTH_SHA;
+ case MessageDigest.ALG_SHA_224:
+ return MessageDigest.LENGTH_SHA_224;
+ case MessageDigest.ALG_SHA_256:
+ return MessageDigest.LENGTH_SHA_256;
+ case MessageDigest.ALG_SHA_384:
+ return MessageDigest.LENGTH_SHA_384;
+ case MessageDigest.ALG_SHA_512:
+ return MessageDigest.LENGTH_SHA_512;
+ default:
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ }
+ return 0;
+ }
+
+ @Override
+ public void init(Key theKey, byte theMode) throws CryptoException {
+ cipher.init(theKey, theMode);
+ }
+
+ @Override
+ public void init(Key theKey, byte theMode, byte[] bArray, short bOff, short bLen)
+ throws CryptoException {
+ cipher.init(theKey, theMode, bArray, bOff, bLen);
+ }
+
+ @Override
+ public byte getAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public byte getCipherAlgorithm() {
+ return 0;
+ }
+
+ @Override
+ public byte getPaddingAlgorithm() {
+ return 0;
+ }
+
+ @Override
+ public short doFinal(
+ byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset)
+ throws CryptoException {
+ short len = cipher.doFinal(inBuff, inOffset, inLength, outBuff, outOffset);
+
+ // https://tools.ietf.org/html/rfc8017#section-7.1
+ // https://www.inf.pucrs.br/~calazans/graduate/TPVLSI_I/RSA-oaep_spec.pdf
+ // RSA OAEP Encoding and Decoding Mechanism for a 2048 bit RSA Key.
+ // Msg -> RSA-OAEP-ENCODE -> RSAEncryption -> RSADecryption ->
+ // RSA-OAEP-DECODE -> Msg
+ // RSA-OAEP-ENCODE generates an output length of 255, but RSAEncryption
+ // requires and input of length 256 so we pad 0 to the left of the input
+ // message and make the length equal to 256 and pass to RSAEncryption.
+ // RSADecryption takes input length equal to 256 and generates an
+ // output of length 256. After decryption the first byte of the output
+ // should be 0(left padding we did in encryption).
+ // RSA-OAEP-DECODE takes input of length 255 so remove the left padding of 1
+ // byte.
+ if (len != 256 || outBuff[0] != 0) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ Util.arrayCopyNonAtomic(
+ outBuff, (short) (outOffset + 1), outBuff, (short) 0, (short) (len - 1));
+ return rsaOAEPDecode(outBuff, (short) 0, (short) (len - 1));
+ }
+
+ @Override
+ public short update(
+ byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset)
+ throws CryptoException {
+ return cipher.update(inBuff, inOffset, inLength, outBuff, outOffset);
+ }
+
+ private void maskGenerationFunction1(
+ byte[] input,
+ short inputOffset,
+ short inputLen,
+ short expectedOutLen,
+ byte[] outBuf,
+ short outOffset) {
+ short counter = 0;
+ MessageDigest.OneShot md = null;
+ try {
+ md = MessageDigest.OneShot.open(mgf1Hash);
+ short digestLen = md.getLength();
+
+ Util.arrayCopyNonAtomic(input, inputOffset, mgf1Buf, (short) 0, inputLen);
+ while (counter < (short) (expectedOutLen / digestLen)) {
+ I2OS(counter, mgf1Buf, (short) inputLen);
+ md.doFinal(
+ mgf1Buf,
+ (short) 0,
+ (short) (4 + inputLen),
+ outBuf,
+ (short) (outOffset + (counter * digestLen)));
+ counter++;
+ }
+
+ if ((short) (counter * digestLen) < expectedOutLen) {
+ I2OS(counter, mgf1Buf, (short) inputLen);
+ md.doFinal(
+ mgf1Buf,
+ (short) 0,
+ (short) (4 + inputLen),
+ outBuf,
+ (short) (outOffset + (counter * digestLen)));
+ }
+
+ } finally {
+ if (md != null) {
+ md.close();
+ }
+ Util.arrayFillNonAtomic(mgf1Buf, (short) 0, (short) MGF1_BUF_SIZE, (byte) 0);
+ }
+ }
+
+ // Integer to Octet String conversion.
+ private void I2OS(short i, byte[] out, short offset) {
+ Util.arrayFillNonAtomic(out, (short) offset, (short) 4, (byte) 0);
+ out[(short) (offset + 3)] = (byte) (i >>> 0);
+ out[(short) (offset + 2)] = (byte) (i >>> 8);
+ }
+
+ private short rsaOAEPDecode(byte[] encodedMsg, short encodedMsgOff, short encodedMsgLen) {
+ MessageDigest.OneShot md = null;
+ byte[] tmpArray = KMAndroidSEProvider.getInstance().tmpArray;
+
+ try {
+ short hLen = getDigestLength();
+
+ if (encodedMsgLen < (short) (2 * hLen + 1)) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ // encodedMsg will be in the format of maskedSeed||maskedDB.
+ // maskedSeed length is hLen and maskedDB length is (encodedMsgLen - hLen)
+ // Now retrieve the seedMask by calling MGF(maskedDB, hLen). The length
+ // of the seedMask is hLen.
+ // seedMask = MGF(maskedDB, hLen)
+ maskGenerationFunction1(
+ encodedMsg,
+ (short) (encodedMsgOff + hLen),
+ (short) (encodedMsgLen - hLen),
+ hLen,
+ tmpArray,
+ (short) 0);
+
+ // Get the seed by doing XOR of (maskedSeed ^ seedMask).
+ // seed = (maskedSeed ^ seedMask)
+ for (short i = 0; i < hLen; i++) {
+ // Store the seed in encodeMsg itself.
+ encodedMsg[(short) (encodedMsgOff + i)] ^= tmpArray[i];
+ }
+
+ // Now get the dbMask by calling MGF(seed , (emLen-hLen)).
+ // dbMask = MGF(seed , (emLen-hLen)).
+ maskGenerationFunction1(
+ encodedMsg,
+ (short) encodedMsgOff,
+ hLen,
+ (short) (encodedMsgLen - hLen),
+ tmpArray,
+ (short) 0);
+
+ // Get the DB value. DB = (maskedDB ^ dbMask)
+ // DB = Hash(P)||00||01||Msg, where P is encoding parameters. (P = NULL)
+ for (short i = 0; i < (short) (encodedMsgLen - hLen); i++) {
+ // Store the DB inside encodeMsg itself.
+ encodedMsg[(short) (encodedMsgOff + i + hLen)] ^= tmpArray[i];
+ }
+
+ // Verify Hash.
+ md = MessageDigest.OneShot.open(hash);
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0);
+ md.doFinal(tmpArray, (short) 0, (short) 0, tmpArray, (short) 0);
+ if (0
+ != Util.arrayCompare(
+ encodedMsg, (short) (encodedMsgOff + hLen), tmpArray, (short) 0, hLen)) {
+ // Verification failed.
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+
+ // Find the Message block in DB.
+ // DB = Hash(P)||00||01||Msg, where P is encoding parameters. (P = NULL)
+ // The message will be located at the end of the Data block (DB).
+ // The DB block is first constructed by keeping the message at the end and
+ // to the message 0x01 byte is prepended. The hash of the
+ // encoding parameters is calculated and then copied from the
+ // starting of the block and a variable length of 0's are
+ // appended to the end of the hash till the 0x01 byte.
+ short start = (short) (encodedMsgOff + encodedMsgLen);
+ for (short i = (short) (encodedMsgOff + 2 * hLen);
+ i < (short) (encodedMsgOff + encodedMsgLen);
+ i++) {
+ if ((encodedMsg[i] != 0)) {
+ start = i;
+ break;
+ }
+ }
+ if ((start >= (short) (encodedMsgOff + encodedMsgLen)) || (encodedMsg[start] != 0x01)) {
+ // Bad Padding.
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ start++; // Message starting pos.
+ if (start < (short) (encodedMsgOff + encodedMsgLen)) {
+ // Copy the message
+ Util.arrayCopyNonAtomic(
+ encodedMsg,
+ start,
+ encodedMsg,
+ encodedMsgOff,
+ (short) (encodedMsgLen - (start - encodedMsgOff)));
+ }
+ return (short) (encodedMsgLen - (start - encodedMsgOff));
+
+ } finally {
+ if (md != null) {
+ md.close();
+ }
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, KMAndroidSEProvider.TMP_ARRAY_SIZE, (byte) 0);
+ }
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java
new file mode 100644
index 0000000..9313c04
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java
@@ -0,0 +1,782 @@
+/*
+ * 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 org.globalplatform.upgrade.Element;
+
+/**
+ * KMSEProvider is facade to use SE specific methods. The main intention of this interface is to
+ * abstract the cipher, signature and backup and restore related functions. The instance of this
+ * interface is created by the singleton KMSEProviderImpl class for each provider. At a time there
+ * can be only one provider in the applet package.
+ */
+public interface KMSEProvider {
+
+ /**
+ * This function tells if boot signal event is supported or not.
+ *
+ * @return true if supported, false otherwise.
+ */
+ boolean isBootSignalEventSupported();
+
+ /**
+ * This function tells if the device is booted or not.
+ *
+ * @return true if device booted, false otherwise.
+ */
+ boolean isDeviceRebooted();
+
+ /**
+ * This function is supposed to be used to reset the device booted stated after set boot param is
+ * handled
+ *
+ * @param resetBootFlag is false if event has been handled
+ */
+ void clearDeviceBooted(boolean resetBootFlag);
+
+ /**
+ * Create a symmetric key instance. If the algorithm and/or keysize are not supported then it
+ * should throw a CryptoException.
+ *
+ * @param alg will be KMType.AES, KMType.DES or KMType.HMAC.
+ * @param keysize will be 128 or 256 for AES or DES. It can be 64 to 512 (multiple of 8) for HMAC.
+ * @param buf is the buffer in which key has to be returned
+ * @param startOff is the start offset.
+ * @return length of the data in the buf. This should match the keysize (in bytes).
+ */
+ short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff);
+
+ /**
+ * Create a asymmetric key pair. If the algorithms are not supported then it should throw a
+ * CryptoException. For RSA the public key exponent must always be 0x010001. The key size of RSA
+ * key pair must be 2048 bits and key size of EC key pair must be for p256 curve.
+ *
+ * @param alg will be KMType.RSA or KMType.EC.
+ * @param privKeyBuf is the buffer to return the private key exponent in case of RSA or private
+ * key in case of EC.
+ * @param privKeyStart is the start offset.
+ * @param privKeyMaxLength is the maximum length of this private key buffer.
+ * @param pubModBuf is the buffer to return the modulus in case of RSA or public key in case of
+ * EC.
+ * @param pubModStart is the start of offset.
+ * @param pubModMaxLength is the maximum length of this public key buffer.
+ * @param lengths is the actual length of the key pair - lengths[0] should be private key and
+ * lengths[1] should be public key.
+ */
+ void createAsymmetricKey(
+ byte alg,
+ byte[] privKeyBuf,
+ short privKeyStart,
+ short privKeyMaxLength,
+ byte[] pubModBuf,
+ short pubModStart,
+ short pubModMaxLength,
+ short[] lengths);
+
+ /**
+ * Initializes the trusted confirmation operation.
+ *
+ * @param computedHmacKey Instance of the computed Hmac key.
+ * @return instance of KMOperation.
+ */
+ KMOperation initTrustedConfirmationSymmetricOperation(KMKey computedHmacKey);
+
+ /**
+ * Verify that the imported key is valid. If the algorithm and/or keysize are not supported then
+ * it should throw a CryptoException.
+ *
+ * @param alg will be KMType.AES, KMType.DES or KMType.HMAC.
+ * @param keysize will be 128 or 256 for AES or DES. It can be 64 to 512 (multiple of 8) for HMAC.
+ * @param buf is the buffer that contains the symmetric key.
+ * @param startOff is the start offset.
+ * @param length of the data in the buf. This should match the keysize (in bytes).
+ * @return true if the symmetric key is supported and valid.
+ */
+ boolean importSymmetricKey(byte alg, short keysize, byte[] buf, short startOff, short length);
+
+ /**
+ * Validate that the imported asymmetric key pair is valid. For RSA the public key exponent must
+ * always be 0x010001. The key size of RSA key pair must be 2048 bits and key size of EC key pair
+ * must be for p256 curve. If the algorithms are not supported then it should throw a
+ * CryptoException.
+ *
+ * @param alg will be KMType.RSA or KMType.EC.
+ * @param privKeyBuf is the buffer that contains the private key exponent in case of RSA or
+ * private key in case of EC.
+ * @param privKeyStart is the start offset.
+ * @param privKeyLength is the length of this private key buffer.
+ * @param pubModBuf is the buffer that contains the modulus in case of RSA or public key in case
+ * of EC.
+ * @param pubModStart is the start of offset.
+ * @param pubModLength is the length of this public key buffer.
+ * @return true if the key pair is supported and valid.
+ */
+ boolean importAsymmetricKey(
+ byte alg,
+ byte[] privKeyBuf,
+ short privKeyStart,
+ short privKeyLength,
+ byte[] pubModBuf,
+ short pubModStart,
+ short pubModLength);
+
+ /**
+ * This is a oneshot operation that generates random number of desired length.
+ *
+ * @param num is the buffer in which random number is returned to the applet.
+ * @param offset is start of the buffer.
+ * @param length indicates the size of buffer and desired length of random number in bytes.
+ */
+ void newRandomNumber(byte[] num, short offset, short length);
+
+ /**
+ * This is a oneshot operation that adds the entropy to the entropy pool. This operation
+ * corresponds to addRndEntropy command. This method may ignore the added entropy value if the SE
+ * provider does not support it.
+ *
+ * @param num is the buffer in which entropy value is given.
+ * @param offset is start of the buffer.
+ * @param length length of the buffer.
+ */
+ void addRngEntropy(byte[] num, short offset, short length);
+
+ /**
+ * This is a oneshot operation that generates and returns back a true random number.
+ *
+ * @param num is the buffer in which entropy value is returned.
+ * @param offset is start of the buffer.
+ * @param length length of the buffer.
+ */
+ void getTrueRandomNumber(byte[] num, short offset, short length);
+
+ /**
+ * This is a oneshot operation that performs encryption operation using AES GCM algorithm. It
+ * throws CryptoException if algorithm is not supported or if tag length is not equal to 16 or
+ * nonce length is not equal to 12.
+ *
+ * @param aesKey is the buffer that contains 128 bit or 256 bit aes key used to encrypt.
+ * @param aesKeyStart is the start in aes key buffer.
+ * @param aesKeyLen is the length of aes key buffer in bytes (16 or 32 bytes).
+ * @param data is the buffer that contains data to encrypt.
+ * @param dataStart is the start of the data buffer.
+ * @param dataLen is the length of the data buffer.
+ * @param encData is the buffer of the output encrypted data.
+ * @param encDataStart is the start of the encrypted data buffer.
+ * @param nonce is the buffer of nonce.
+ * @param nonceStart is the start of the nonce buffer.
+ * @param nonceLen is the length of the nonce buffer.
+ * @param authData is the authentication data buffer.
+ * @param authDataStart is the start of the authentication buffer.
+ * @param authDataLen is the length of the authentication buffer.
+ * @param authTag is the buffer to output authentication tag.
+ * @param authTagStart is the start of the buffer.
+ * @param authTagLen is the length of the buffer.
+ * @return length of the encrypted data.
+ */
+ short aesGCMEncrypt(
+ byte[] aesKey,
+ short aesKeyStart,
+ short aesKeyLen,
+ byte[] data,
+ short dataStart,
+ short dataLen,
+ byte[] encData,
+ short encDataStart,
+ byte[] nonce,
+ short nonceStart,
+ short nonceLen,
+ byte[] authData,
+ short authDataStart,
+ short authDataLen,
+ byte[] authTag,
+ short authTagStart,
+ short authTagLen);
+
+ /**
+ * This is a oneshot operation that performs decryption operation using AES GCM algorithm. It
+ * throws CryptoException if algorithm is not supported.
+ *
+ * @param aesKey is the buffer that contains 128 bit or 256 bit aes key used to encrypt.
+ * @param aesKeyStart is the start in aes key buffer.
+ * @param aesKeyLen is the length of aes key buffer in bytes (16 or 32 bytes).
+ * @param encData is the buffer of the input encrypted data.
+ * @param encDataStart is the start of the encrypted data buffer.
+ * @param encDataLen is the length of the data buffer.
+ * @param data is the buffer that contains output decrypted data.
+ * @param dataStart is the start of the data buffer.
+ * @param nonce is the buffer of nonce.
+ * @param nonceStart is the start of the nonce buffer.
+ * @param nonceLen is the length of the nonce buffer.
+ * @param authData is the authentication data buffer.
+ * @param authDataStart is the start of the authentication buffer.
+ * @param authDataLen is the length of the authentication buffer.
+ * @param authTag is the buffer to output authentication tag.
+ * @param authTagStart is the start of the buffer.
+ * @param authTagLen is the length of the buffer.
+ * @return true if the authentication is valid.
+ */
+ boolean aesGCMDecrypt(
+ byte[] aesKey,
+ short aesKeyStart,
+ short aesKeyLen,
+ byte[] encData,
+ short encDataStart,
+ short encDataLen,
+ byte[] data,
+ short dataStart,
+ byte[] nonce,
+ short nonceStart,
+ short nonceLen,
+ byte[] authData,
+ short authDataStart,
+ short authDataLen,
+ byte[] authTag,
+ short authTagStart,
+ short authTagLen);
+
+ /**
+ * This is a oneshot operation that performs key derivation function using cmac kdf (CKDF) as
+ * defined in android keymaster hal definition.
+ *
+ * @param hmacKey of pre-shared key.
+ * @param label is the label to be used for ckdf.
+ * @param labelStart is the start of label.
+ * @param labelLen is the length of the label.
+ * @param context is the context to be used for ckdf.
+ * @param contextStart is the start of the context
+ * @param contextLength is the length of the context
+ * @param key is the output buffer to return the derived key
+ * @param keyStart is the start of the output buffer.
+ * @return length of the derived key buffer in bytes.
+ */
+ short cmacKDF(
+ KMKey hmacKey,
+ byte[] label,
+ short labelStart,
+ short labelLen,
+ byte[] context,
+ short contextStart,
+ short contextLength,
+ byte[] key,
+ short keyStart);
+
+ /**
+ * This is a oneshot operation that signs the data using hmac algorithm.
+ *
+ * @param keyBuf is the buffer with hmac key.
+ * @param keyStart is the start of the buffer.
+ * @param keyLength is the length of the buffer which will be in bytes from 8 to 64.
+ * @param data is the buffer containing data to be signed.
+ * @param dataStart is the start of the data.
+ * @param dataLength is the length of the data.
+ * @param signature is the output signature buffer
+ * @param signatureStart is the start of the signature
+ * @return length of the signature buffer in bytes.
+ */
+ short hmacSign(
+ byte[] keyBuf,
+ short keyStart,
+ short keyLength,
+ byte[] data,
+ short dataStart,
+ short dataLength,
+ byte[] signature,
+ short signatureStart);
+
+ /**
+ * This is a oneshot operation that signs the data using hmac algorithm.
+ *
+ * @param hmacKey is the KMHmacKey.
+ * @param data is the buffer containing data to be signed.
+ * @param dataStart is the start of the data.
+ * @param dataLength is the length of the data.
+ * @param signature is the output signature buffer
+ * @param signatureStart is the start of the signature
+ * @return length of the signature buffer in bytes.
+ */
+ short hmacSign(
+ Object hmacKey,
+ byte[] data,
+ short dataStart,
+ short dataLength,
+ byte[] signature,
+ short signatureStart);
+
+ /**
+ * This is a oneshot operation that signs the data using hmac algorithm. This is used to derive
+ * the key, which is used to encrypt the keyblob.
+ *
+ * @param masterkey of masterkey.
+ * @param data is the buffer containing data to be signed.
+ * @param dataStart is the start of the data.
+ * @param dataLength is the length of the data.
+ * @param signature is the output signature buffer
+ * @param signatureStart is the start of the signature
+ * @return length of the signature buffer in bytes.
+ */
+ short hmacKDF(
+ KMKey masterkey,
+ byte[] data,
+ short dataStart,
+ short dataLength,
+ byte[] signature,
+ short signatureStart);
+
+ /**
+ * This is a oneshot operation that verifies the signature using hmac algorithm.
+ *
+ * @param keyBuf is the buffer with hmac key.
+ * @param keyStart is the start of the buffer.
+ * @param keyLength is the length of the buffer which will be in bytes from 8 to 64.
+ * @param data is the buffer containing data.
+ * @param dataStart is the start of the data.
+ * @param dataLength is the length of the data.
+ * @param signature is the signature buffer.
+ * @param signatureStart is the start of the signature buffer.
+ * @param signatureLen is the length of the signature buffer in bytes.
+ * @return true if the signature matches.
+ */
+ boolean hmacVerify(
+ KMKey hmacKey,
+ byte[] data,
+ short dataStart,
+ short dataLength,
+ byte[] signature,
+ short signatureStart,
+ short signatureLen);
+
+ /**
+ * This is a oneshot operation that decrypts the data using RSA algorithm with oaep256 padding.
+ * The public exponent is always 0x010001. It throws CryptoException if OAEP encoding validation
+ * fails.
+ *
+ * @param privExp is the private exponent (2048 bit) buffer.
+ * @param privExpStart is the start of the private exponent buffer.
+ * @param privExpLength is the length of the private exponent buffer in bytes.
+ * @param modBuffer is the modulus (2048 bit) buffer.
+ * @param modOff is the start of the modulus buffer.
+ * @param modLength is the length of the modulus buffer in bytes.
+ * @param inputDataBuf is the buffer of the input data.
+ * @param inputDataStart is the start of the input data buffer.
+ * @param inputDataLength is the length of the input data buffer in bytes.
+ * @param outputDataBuf is the output buffer that contains the decrypted data.
+ * @param outputDataStart is the start of the output data buffer.
+ * @return length of the decrypted data.
+ */
+ short rsaDecipherOAEP256(
+ byte[] privExp,
+ short privExpStart,
+ short privExpLength,
+ byte[] modBuffer,
+ short modOff,
+ short modLength,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart);
+
+ /**
+ * Implementation of HKDF as per RFC5869 https://datatracker.ietf.org/doc/html/rfc5869#section-2
+ *
+ * @param ikm is the buffer containing input key material.
+ * @param ikmOff is the start of the input key.
+ * @param ikmLen is the length of the input key.
+ * @param salt is the buffer containing the salt.
+ * @param saltOff is the start of the salt buffer.
+ * @param saltLen is the length of the salt buffer.
+ * @param info is the buffer containing the application specific information
+ * @param infoOff is the start of the info buffer.
+ * @param infoLen is the length of the info buffer.
+ * @param out is the output buffer.
+ * @param outOff is the start of the output buffer.
+ * @param outLen is the length of the expected out buffer.
+ * @return Length of the out buffer which is outLen.
+ */
+ short hkdf(
+ byte[] ikm,
+ short ikmOff,
+ short ikmLen,
+ byte[] salt,
+ short saltOff,
+ short saltLen,
+ byte[] info,
+ short infoOff,
+ short infoLen,
+ byte[] out,
+ short outOff,
+ short outLen);
+
+ /**
+ * This function performs ECDH key agreement and generates a secret.
+ *
+ * @param privKey is the buffer containing the private key from first party.
+ * @param privKeyOff is the offset of the private key buffer.
+ * @param privKeyLen is the length of the private key buffer.
+ * @param publicKey is the buffer containing the public key from second party.
+ * @param publicKeyOff is the offset of the public key buffer.
+ * @param publicKeyLen is the length of the public key buffer.
+ * @param secret is the output buffer.
+ * @param secretOff is the offset of the output buffer.
+ * @return The length of the secret.
+ */
+ short ecdhKeyAgreement(
+ byte[] privKey,
+ short privKeyOff,
+ short privKeyLen,
+ byte[] publicKey,
+ short publicKeyOff,
+ short publicKeyLen,
+ byte[] secret,
+ short secretOff);
+
+ /**
+ * This is a oneshort operation that verifies the data using EC public key
+ *
+ * @param pubKey is the public key buffer.
+ * @param pubKeyOffset is the start of the public key buffer.
+ * @param pubKeyLen is the length of the public key.
+ * @param inputDataBuf is the buffer of the input data.
+ * @param inputDataStart is the start of the input data buffer.
+ * @param inputDataLength is the length of the input data buffer in bytes.
+ * @param signatureDataBuf is the buffer the signature input data.
+ * @param signatureDataStart is the start of the signature input data.
+ * @param signatureDataLen is the length of the signature input data.
+ * @return true if verification is successful, otherwise false.
+ */
+ boolean ecVerify256(
+ byte[] pubKey,
+ short pubKeyOffset,
+ short pubKeyLen,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] signatureDataBuf,
+ short signatureDataStart,
+ short signatureDataLen);
+
+ /**
+ * This is a oneshot operation that signs the data using device unique key.
+ *
+ * @param ecPrivKey instance of KMECDeviceUniqueKey to sign the input data.
+ * @param inputDataBuf is the buffer of the input data.
+ * @param inputDataStart is the start of the input data buffer.
+ * @param inputDataLength is the length of the input data buffer in bytes.
+ * @param outputDataBuf is the output buffer that contains the signature.
+ * @param outputDataStart is the start of the output data buffer.
+ * @return length of the decrypted data.
+ */
+ short signWithDeviceUniqueKey(
+ KMKey deviceUniqueKey,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart);
+
+ short ecSign256(
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart);
+
+ short rsaSign256Pkcs1(
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ byte[] modBuf,
+ short modStart,
+ short modLength,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart);
+
+ /**
+ * This creates a persistent operation for signing, verify, encryption and decryption using HMAC,
+ * AES and DES algorithms when keymaster hal's beginOperation function is executed. The
+ * KMOperation instance can be reclaimed by the seProvider when KMOperation is finished or
+ * aborted. It throws CryptoException if algorithm is not supported.
+ *
+ * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be
+ * KMType.SIGN and KMType.VERIFY for HMAC algorithm
+ * @param alg is KMType.HMAC, KMType.AES or KMType.DES.
+ * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE.
+ * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES).
+ * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is
+ * 0.
+ * @param keyBuf is aes, des or hmac key buffer.
+ * @param keyStart is the start of the key buffer.
+ * @param keyLength is the length of the key buffer.
+ * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode)
+ * @param ivStart is the start of the iv buffer.
+ * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES
+ * with ECB mode.
+ * @param macLength is the mac length in case of signing operation for hmac algorithm.
+ * @return KMOperation instance.
+ */
+ KMOperation initSymmetricOperation(
+ byte purpose,
+ byte alg,
+ byte digest,
+ byte padding,
+ byte blockMode,
+ byte[] keyBuf,
+ short keyStart,
+ short keyLength,
+ byte[] ivBuf,
+ short ivStart,
+ short ivLength,
+ short macLength);
+
+ /**
+ * This creates a persistent operation for signing, verify, encryption and decryption using HMAC,
+ * AES and DES algorithms when keymaster hal's beginOperation function is executed. The
+ * KMOperation instance can be reclaimed by the seProvider when KMOperation is finished or
+ * aborted. It throws CryptoException if algorithm is not supported.
+ *
+ * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be
+ * KMType.SIGN and KMType.VERIFY for HMAC algorithm
+ * @param alg is KMType.HMAC, KMType.AES or KMType.DES.
+ * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE.
+ * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES).
+ * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is
+ * 0.
+ * @param key is a key object.
+ * @param interfaceType defines the type of key in the key object.
+ * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode)
+ * @param ivStart is the start of the iv buffer.
+ * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES
+ * with ECB mode.
+ * @param macLength is the mac length in case of signing operation for hmac algorithm.
+ * @param oneShot if true, creates oneshot operation.
+ * @return KMOperation instance.
+ */
+ KMOperation initSymmetricOperation(
+ byte purpose,
+ byte alg,
+ byte digest,
+ byte padding,
+ byte blockMode,
+ Object key,
+ byte interfaceType,
+ byte[] ivBuf,
+ short ivStart,
+ short ivLength,
+ short macLength,
+ boolean oneShot);
+
+ /**
+ * This function creates an Operation instance only for RKP module.
+ *
+ * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be
+ * KMType.SIGN and KMType.VERIFY for HMAC algorithm
+ * @param alg is KMType.HMAC, KMType.AES or KMType.DES.
+ * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE.
+ * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES).
+ * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is
+ * 0.
+ * @param keyBuf is aes, des or hmac key buffer.
+ * @param keyStart is the start of the key buffer.
+ * @param keyLength is the length of the key buffer.
+ * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode)
+ * @param ivStart is the start of the iv buffer.
+ * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES
+ * with ECB mode.
+ * @param macLength is the mac length in case of signing operation for hmac algorithm.
+ * @return KMOperation instance.
+ */
+ KMOperation getRkpOperation(
+ byte purpose,
+ byte alg,
+ byte digest,
+ byte padding,
+ byte blockMode,
+ byte[] keyBuf,
+ short keyStart,
+ short keyLength,
+ byte[] ivBuf,
+ short ivStart,
+ short ivLength,
+ short macLength);
+
+ /**
+ * This creates a persistent operation for signing, verify, encryption and decryption using RSA
+ * and EC algorithms when keymaster hal's beginOperation function is executed. For RSA the public
+ * exponent is always 0x0100101. For EC the curve is always p256. The KMOperation instance can be
+ * reclaimed by the seProvider when KMOperation is finished or aborted. It throws CryptoException
+ * if algorithm is not supported.
+ *
+ * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for RSA. It will be * KMType.SIGN and
+ * KMType.VERIFY for RSA and EC algorithms.
+ * @param alg is KMType.RSA or KMType.EC algorithms.
+ * @param padding is KMType.PADDING_NONE or KMType.RSA_OAEP, KMType.RSA_PKCS1_1_5_ENCRYPT,
+ * KMType.RSA_PKCS1_1_5_SIGN or KMType.RSA_PSS.
+ * @param digest is KMType.DIGEST_NONE or KMType.SHA2_256.
+ * @param mgfDigest is the MGF digest.
+ * @param privKeyBuf is the private key in case of EC or private key exponent is case of RSA.
+ * @param privKeyStart is the start of the private key.
+ * @param privKeyLength is the length of the private key.
+ * @param pubModBuf is the modulus (in case of RSA) or public key (in case of EC).
+ * @param pubModStart is the start of the modulus.
+ * @param pubModLength is the length of the modulus.
+ * @return KMOperation instance that can be executed.
+ */
+ KMOperation initAsymmetricOperation(
+ byte purpose,
+ byte alg,
+ byte padding,
+ byte digest,
+ byte mgfDigest,
+ byte[] privKeyBuf,
+ short privKeyStart,
+ short privKeyLength,
+ byte[] pubModBuf,
+ short pubModStart,
+ short pubModLength);
+
+ /**
+ * This function tells if applet is upgrading or not.
+ *
+ * @return true if upgrading, otherwise false.
+ */
+ boolean isUpgrading();
+
+ /**
+ * This function generates an AES Key of keySizeBits, which is used as an master key. This
+ * generated key is maintained by the SEProvider. This function should be called only once at the
+ * time of installation.
+ *
+ * @param instance of the masterkey.
+ * @param keySizeBits key size in bits.
+ * @return An instance of KMMasterKey.
+ */
+ KMKey createMasterKey(KMKey masterKey, short keySizeBits);
+
+ /**
+ * This function creates an HMACKey and initializes the key with the provided input key data.
+ *
+ * @param keyData buffer containing the key data.
+ * @param offset start of the buffer.
+ * @param length length of the buffer.
+ * @return An instance of the KMComputedHmacKey.
+ */
+ KMKey createComputedHmacKey(KMKey computedHmacKey, byte[] keyData, short offset, short length);
+
+ /** Returns true if factory provisioned attestation key is supported. */
+ boolean isAttestationKeyProvisioned();
+
+ /**
+ * Returns algorithm type of the attestation key. It can be KMType.EC or KMType.RSA if the
+ * attestation key is provisioned in the factory.
+ */
+ short getAttestationKeyAlgorithm();
+
+ /**
+ * Creates an ECKey instance and sets the public and private keys to it.
+ *
+ * @param testMode to indicate if current execution is for test or production.
+ * @param pubKey buffer containing the public key.
+ * @param pubKeyOff public key buffer start offset.
+ * @param pubKeyLen public key buffer length.
+ * @param privKey buffer containing the private key.
+ * @param privKeyOff private key buffer start offset.
+ * @param privKeyLen private key buffer length.
+ * @return instance of KMDeviceUniqueKey.
+ */
+ KMKey createRkpDeviceUniqueKeyPair(
+ KMKey key,
+ byte[] pubKey,
+ short pubKeyOff,
+ short pubKeyLen,
+ byte[] privKey,
+ short privKeyOff,
+ short privKeyLen);
+
+ /**
+ * This is a one-shot operation the does digest of the input mesage.
+ *
+ * @param inBuff input buffer to be digested.
+ * @param inOffset start offset of the input buffer.
+ * @param inLength length of the input buffer.
+ * @param outBuff is the output buffer that contains the digested data.
+ * @param outOffset start offset of the digested output buffer.
+ * @return length of the digested data.
+ */
+ short messageDigest256(
+ byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset);
+
+ /**
+ * This function generates a HMAC key from the provided key buffers.
+ *
+ * @param presharedKey instance of the presharedkey.
+ * @param key buffer containing the key data.
+ * @param offset start offset of the buffer.
+ * @param length is the length of the key.
+ * @return instance of KMPresharedKey.
+ */
+ KMKey createPreSharedKey(KMKey presharedKey, byte[] key, short offset, short length);
+
+ /**
+ * This function saves the key objects while upgrade.
+ *
+ * @param element instance of the Element class where the objects to be stored.
+ * @param interfaceType the type interface of the parent object.
+ * @param object instance of the object to be saved.
+ */
+ void onSave(Element element, byte interfaceType, Object object);
+
+ /**
+ * This function restores the the object from element instance.
+ *
+ * @param element instance of the Element class.
+ * @return restored object.
+ */
+ Object onRestore(Element element);
+
+ /**
+ * This function returns the count of the primitive bytes required to be stored by the
+ * implementation of the interface type.
+ *
+ * @param interfaceType type interface of the parent object.
+ * @return count of the primitive bytes.
+ */
+ short getBackupPrimitiveByteCount(byte interfaceType);
+
+ /**
+ * This function returns the object count required to be stored by the implementation of the
+ * interface type.
+ *
+ * @param interfaceType type interface of the parent object.
+ * @return count of the objects.
+ */
+ short getBackupObjectCount(byte interfaceType);
+
+ /**
+ * This function creates an HMACKey and initializes the key with the provided input key data.
+ *
+ * @param keyData buffer containing the key data.
+ * @param offset start of the buffer.
+ * @param length length of the buffer.
+ * @return An instance of the KMRkpMacKey.
+ */
+ KMKey createRkpMacKey(KMKey createComputedHmacKey, byte[] keyData, short offset, short length);
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java
new file mode 100644
index 0000000..82cbe26
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java
@@ -0,0 +1,79 @@
+/*
+ * 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" 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.javacard.seprovider;
+
+/**
+ * This class declares all types, tag types, and tag keys. It also establishes basic structure of
+ * any KMType i.e. struct{byte type, short length, value} where value can any of the KMType. Also,
+ * KMType refers to transient memory heap in the repository. Finally KMType's subtypes are singleton
+ * prototype objects which just cast the structure over contiguous memory buffer.
+ */
+public abstract class KMType {
+
+ public static final short INVALID_VALUE = (short) 0x8000;
+
+ // Algorithm Enum Tag key and values
+ public static final short ALGORITHM = 0x0002;
+ public static final byte RSA = 0x01;
+ public static final byte DES = 0x21;
+ public static final byte EC = 0x03;
+ public static final byte AES = 0x20;
+ public static final byte HMAC = (byte) 0x80;
+
+ // EcCurve Enum Tag key and values.
+ public static final short ECCURVE = 0x000A;
+ public static final byte P_224 = 0x00;
+ public static final byte P_256 = 0x01;
+ public static final byte P_384 = 0x02;
+ public static final byte P_521 = 0x03;
+
+ // Purpose
+ public static final short PURPOSE = 0x0001;
+ public static final byte ENCRYPT = 0x00;
+ public static final byte DECRYPT = 0x01;
+ public static final byte SIGN = 0x02;
+ public static final byte VERIFY = 0x03;
+ public static final byte DERIVE_KEY = 0x04;
+ public static final byte WRAP_KEY = 0x05;
+ public static final byte AGREE_KEY = 0x06;
+ public static final byte ATTEST_KEY = (byte) 0x07;
+ // Block mode
+ public static final short BLOCK_MODE = 0x0004;
+ public static final byte ECB = 0x01;
+ public static final byte CBC = 0x02;
+ public static final byte CTR = 0x03;
+ public static final byte GCM = 0x20;
+
+ // Digest
+ public static final short DIGEST = 0x0005;
+ public static final byte DIGEST_NONE = 0x00;
+ public static final byte MD5 = 0x01;
+ public static final byte SHA1 = 0x02;
+ public static final byte SHA2_224 = 0x03;
+ public static final byte SHA2_256 = 0x04;
+ public static final byte SHA2_384 = 0x05;
+ public static final byte SHA2_512 = 0x06;
+
+ // Padding mode
+ public static final short PADDING = 0x0006;
+ public static final byte PADDING_NONE = 0x01;
+ public static final byte RSA_OAEP = 0x02;
+ public static final byte RSA_PSS = 0x03;
+ public static final byte RSA_PKCS1_1_5_ENCRYPT = 0x04;
+ public static final byte RSA_PKCS1_1_5_SIGN = 0x05;
+ public static final byte PKCS7 = 0x40;
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java
new file mode 100644
index 0000000..420b2c7
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java
@@ -0,0 +1,30 @@
+/*
+ * 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 org.globalplatform.upgrade.Element;
+
+/** This interface helps in storing and restoring the applet data during the applet upgrades. */
+public interface KMUpgradable {
+
+ void onSave(Element ele);
+
+ void onRestore(Element ele, short oldVersion, short currentVersion);
+
+ short getBackupPrimitiveByteCount();
+
+ short getBackupObjectCount();
+}
diff --git a/ready_se/google/keymint/KM200/Applet/README.md b/ready_se/google/keymint/KM200/Applet/README.md
new file mode 100644
index 0000000..c14a369
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/README.md
@@ -0,0 +1,16 @@
+# JavaCardKeymaster Applet
+
+This directory contains the implementation of the KeyMint 2.0
+interface, in the form of a JavaCard 3.0.5 applet which runs in a secure
+element. It must be deployed in conjunction with the associated HAL,
+which mediates between Android Keystore and this applet.
+
+# Supported Features!
+
+ - KeyMint 2.0 supported functions for required VTS compliance.
+ - SharedSecret 1.0 supported functions for required VTS compliance.
+ - RemotelyProvisionedComponent 2.0 supported functions for required VTS compliance.
+
+# Not supported features
+ - Factory provisioned attestation key will not be supported in this applet.
+ - Limited usage keys will not be supported in this applet.
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMArray.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMArray.java
new file mode 100644
index 0000000..aa54d54
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMArray.java
@@ -0,0 +1,165 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMArray represents an array of KMTypes. Array is the sequence of elements of one or more sub
+ * types of KMType. It also acts as a vector of one subtype of KMTypes on the lines of class KMArray
+ * <subType>, where subType is subclass of KMType. Vector is the sequence of elements of one sub
+ * type e.g. KMType.BYTE_BLOB_TYPE. The KMArray instance maps to the CBOR type array. KMArray is a
+ * KMType and it further extends the value field in TLV_HEADER as ARRAY_HEADER struct{short subType;
+ * short length;} followed by sequence of short pointers to KMType instances. The subType can be 0
+ * if this is an array or subType is short KMType value e.g. KMType.BYTE_BLOB_TYPE if this is a
+ * vector of that sub type.
+ */
+public class KMArray extends KMType {
+
+ public static final short ANY_ARRAY_LENGTH = 0x1000;
+ private static final byte ARRAY_HEADER_SIZE = 4;
+ private static KMArray prototype;
+
+ private KMArray() {}
+
+ private static KMArray proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMArray();
+ }
+ KMType.instanceTable[KM_ARRAY_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp() {
+ short ptr = instance(ARRAY_TYPE, (short) ARRAY_HEADER_SIZE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.INVALID_VALUE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), ANY_ARRAY_LENGTH);
+ return ptr;
+ }
+
+ public static short exp(short type) {
+ short ptr = instance(ARRAY_TYPE, (short) ARRAY_HEADER_SIZE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), type);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), ANY_ARRAY_LENGTH);
+ return ptr;
+ }
+
+ public static short instance(short length) {
+ short ptr = KMType.instance(ARRAY_TYPE, (short) (ARRAY_HEADER_SIZE + (length * 2)));
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.INVALID_VALUE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), length);
+ return ptr;
+ }
+
+ public static short instance(short length, byte type) {
+ short ptr = instance(length);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), type);
+ return ptr;
+ }
+
+ public static KMArray cast(short ptr) {
+ if (heap[ptr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public void add(short index, short objPtr) {
+ short len = length();
+ if (index >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ Util.setShort(heap, (short) (getStartOff() + (short) (index * 2)), objPtr);
+ }
+
+ public short get(short index) {
+ short len = length();
+ if (index >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ return Util.getShort(heap, (short) (getStartOff() + (short) (index * 2)));
+ }
+
+ public void swap(short index1, short index2) {
+ short len = length();
+ if (index1 >= len || index2 >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ short indexPtr1 =
+ Util.getShort(
+ heap,
+ (short)
+ (instanceTable[KM_ARRAY_OFFSET]
+ + TLV_HEADER_SIZE
+ + ARRAY_HEADER_SIZE
+ + (short) (index1 * 2)));
+ short indexPtr2 =
+ Util.getShort(
+ heap,
+ (short)
+ (instanceTable[KM_ARRAY_OFFSET]
+ + TLV_HEADER_SIZE
+ + ARRAY_HEADER_SIZE
+ + (short) (index2 * 2)));
+ Util.setShort(
+ heap,
+ (short)
+ (instanceTable[KM_ARRAY_OFFSET]
+ + TLV_HEADER_SIZE
+ + ARRAY_HEADER_SIZE
+ + (short) (index1 * 2)),
+ indexPtr2);
+ Util.setShort(
+ heap,
+ (short)
+ (instanceTable[KM_ARRAY_OFFSET]
+ + TLV_HEADER_SIZE
+ + ARRAY_HEADER_SIZE
+ + (short) (index2 * 2)),
+ indexPtr1);
+ }
+
+ public short containedType() {
+ return Util.getShort(heap, (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short getStartOff() {
+ return (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE);
+ }
+
+ public short length() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short setLength(short len) {
+ return Util.setShort(
+ heap, (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2), len);
+ }
+
+ public byte[] getBuffer() {
+ return heap;
+ }
+
+ public void deleteLastEntry() {
+ short len = length();
+ Util.setShort(
+ heap, (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2), (short) (len - 1));
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMAsn1Parser.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMAsn1Parser.java
new file mode 100644
index 0000000..22a16a3
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMAsn1Parser.java
@@ -0,0 +1,471 @@
+package com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * This is a utility class that helps in parsing the PKCS8 encoded RSA and EC keys, certificate
+ * subject, subjectPublicKey info and ECDSA signatures.
+ */
+public class KMAsn1Parser {
+
+ // Below are the ASN.1 tag types
+ public static final byte ASN1_OCTET_STRING = 0x04;
+ public static final byte ASN1_SEQUENCE = 0x30;
+ public static final byte ASN1_SET = 0x31;
+ public static final byte ASN1_INTEGER = 0x02;
+ public static final byte OBJECT_IDENTIFIER = 0x06;
+ public static final byte ASN1_A0_TAG = (byte) 0xA0;
+ public static final byte ASN1_A1_TAG = (byte) 0xA1;
+ public static final byte ASN1_BIT_STRING = 0x03;
+
+ public static final byte ASN1_UTF8_STRING = 0x0C;
+ public static final byte ASN1_TELETEX_STRING = 0x14;
+ public static final byte ASN1_PRINTABLE_STRING = 0x13;
+ public static final byte ASN1_UNIVERSAL_STRING = 0x1C;
+ public static final byte ASN1_BMP_STRING = 0x1E;
+ public static final byte IA5_STRING = 0x16;
+ // OID of the EC P256 curve 1.2.840.10045.3.1.7
+ public static final byte[] EC_CURVE = {
+ 0x06, 0x08, 0x2a, (byte) 0x86, 0x48, (byte) 0xce, 0x3d, 0x03, 0x01, 0x07
+ };
+ // Constant for rsaEncryption pkcs#1 (1.2.840.113549.1.1.1) and NULL
+ public static final byte[] RSA_ALGORITHM = {
+ 0x06,
+ 0x09,
+ 0x2A,
+ (byte) 0x86,
+ 0x48,
+ (byte) 0x86,
+ (byte) 0xF7,
+ 0x0D,
+ 0x01,
+ 0x01,
+ 0x01,
+ 0x05,
+ 0x00
+ };
+ // Constant for ecPublicKey (1.2.840.10045.2.1) and prime256v1 (1.2.840.10045.3.1.7)
+ public static final byte[] EC_ALGORITHM = {
+ 0x06,
+ 0x07,
+ 0x2a,
+ (byte) 0x86,
+ 0x48,
+ (byte) 0xce,
+ 0x3d,
+ 0x02,
+ 0x01,
+ 0x06,
+ 0x08,
+ 0x2a,
+ (byte) 0x86,
+ 0x48,
+ (byte) 0xce,
+ 0x3d,
+ 0x03,
+ 0x01,
+ 0x07
+ };
+ // The maximum length of email id attribute.
+ public static final short MAX_EMAIL_ADD_LEN = 255;
+ // Datatable offsets.
+ private static final byte DATA_START_OFFSET = 0;
+ private static final byte DATA_LENGTH_OFFSET = 1;
+ private static final byte DATA_CURSOR_OFFSET = 2;
+ // This array contains the last byte of OID for each oid type.
+ // The first 4 bytes are common as shown above in COMMON_OID
+ private static final byte[] attributeOIds = {
+ 0x03, /* commonName COMMON_OID.3 */ 0x04, /* surName COMMON_OID.4*/
+ 0x05, /* serialNumber COMMON_OID.5 */ 0x06, /* countryName COMMON_OID.6 */
+ 0x07, /* locality COMMON_OID.7 */ 0x08, /* stateOrProviince COMMON_OID.8 */
+ 0x0A, /* organizationName COMMON_OID.10 */ 0x0B, /* organizationalUnitName COMMON_OID.11 */
+ 0x0C, /* title COMMON_OID.10 */ 0x29, /* name COMMON_OID.41 */
+ 0x2A, /* givenName COMMON_OID.42 */ 0x2B, /* initials COMMON_OID.43 */
+ 0x2C, /* generationQualifier COMMON_OID.44 */ 0x2E, /* dnQualifer COMMON_OID.46 */
+ 0x41, /* pseudonym COMMON_OID.65 */
+ };
+ // https://datatracker.ietf.org/doc/html/rfc5280, RFC 5280, Page 124
+ // TODO Specification does not mention about the DN_QUALIFIER_OID max length.
+ // So the max limit is set at 64.
+ // For name the RFC 5280 supports up to 32768, as Javacard doesn't support
+ // that much length, the max limit for name is set to 128.
+ private static final byte[] attributeValueMaxLen = {
+ 0x40, /* 1-64 commonName */
+ 0x28, /* 1-40 surname */
+ 0x40, /* 1-64 serial */
+ 0x02, /* 1-2 country */
+ (byte) 0x80, /* 1-128 locality */
+ (byte) 0x80, /* 1-128 state */
+ 0x40, /* 1-64 organization */
+ 0x40, /* 1-64 organization unit*/
+ 0x40, /* 1-64 title */
+ 0x29, /* 1-128 name */
+ 0x10, /* 1-16 givenName */
+ 0x05, /* 1-5 initials */
+ 0x03, /* 1-3 gen qualifier */
+ 0x40, /* 1-64 dn-qualifier */
+ (byte) 0x80 /* 1-128 pseudonym */
+ };
+ private static KMAsn1Parser inst;
+ // https://datatracker.ietf.org/doc/html/rfc5280, RFC 5280, Page 21
+ // 2.5.4
+ public byte[] COMMON_OID = new byte[] {0x06, 0x03, 0x55, 0x04};
+ public byte[] EMAIL_ADDRESS_OID =
+ new byte[] {
+ 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x09, 0x01
+ };
+ private byte[] data;
+ private short[] dataInfo;
+
+ private KMAsn1Parser() {
+ dataInfo = JCSystem.makeTransientShortArray((short) 3, JCSystem.CLEAR_ON_RESET);
+ dataInfo[DATA_START_OFFSET] = 0;
+ dataInfo[DATA_LENGTH_OFFSET] = 0;
+ dataInfo[DATA_CURSOR_OFFSET] = 0;
+ }
+
+ public static KMAsn1Parser instance() {
+ if (inst == null) {
+ inst = new KMAsn1Parser();
+ }
+ return inst;
+ }
+
+ public short decodeRsa(short blob) {
+ init(blob);
+ decodeCommon((short) 0, RSA_ALGORITHM);
+ return decodeRsaPrivateKey((short) 0);
+ }
+
+ public short decodeEc(short blob) {
+ init(blob);
+ decodeCommon((short) 0, EC_ALGORITHM);
+ return decodeEcPrivateKey((short) 1);
+ }
+
+ /*
+ Name ::= CHOICE { -- only one possibility for now --
+ rdnSequence RDNSequence }
+ RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+ RelativeDistinguishedName ::=
+ SET SIZE (1..MAX) OF AttributeTypeAndValue
+ AttributeTypeAndValue ::= SEQUENCE {
+ type AttributeType,
+ value AttributeValue }
+ AttributeType ::= OBJECT IDENTIFIER
+ AttributeValue ::= ANY -- DEFINED BY AttributeType
+ */
+ public void validateDerSubject(short blob) {
+ init(blob);
+ header(ASN1_SEQUENCE);
+ while (dataInfo[DATA_CURSOR_OFFSET]
+ < ((short) (dataInfo[DATA_START_OFFSET] + dataInfo[DATA_LENGTH_OFFSET]))) {
+ header(ASN1_SET);
+ header(ASN1_SEQUENCE);
+ // Parse and validate OBJECT-IDENTIFIER and Value fields
+ // Cursor is incremented in validateAttributeTypeAndValue.
+ validateAttributeTypeAndValue();
+ }
+ }
+
+ public short decodeEcSubjectPublicKeyInfo(short blob) {
+ init(blob);
+ header(ASN1_SEQUENCE);
+ short len = header(ASN1_SEQUENCE);
+ short ecPublicInfo = KMByteBlob.instance(len);
+ getBytes(ecPublicInfo);
+ if (Util.arrayCompare(
+ KMByteBlob.cast(ecPublicInfo).getBuffer(),
+ KMByteBlob.cast(ecPublicInfo).getStartOff(),
+ EC_ALGORITHM,
+ (short) 0,
+ KMByteBlob.cast(ecPublicInfo).length())
+ != 0) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ len = header(ASN1_BIT_STRING);
+ if (len < 1) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ // TODO need to handle if unused bits are not zero
+ byte unusedBits = getByte();
+ if (unusedBits != 0) {
+ KMException.throwIt(KMError.UNIMPLEMENTED);
+ }
+ short pubKey = KMByteBlob.instance((short) (len - 1));
+ getBytes(pubKey);
+ return pubKey;
+ }
+
+ // Seq[Int,Int,Int,Int,<ignore rest>]
+ public short decodeRsaPrivateKey(short version) {
+ short resp = KMArray.instance((short) 3);
+ header(ASN1_OCTET_STRING);
+ header(ASN1_SEQUENCE);
+ short len = header(ASN1_INTEGER);
+ if (len != 1) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ short ver = getByte();
+ if (ver != version) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ len = header(ASN1_INTEGER);
+ short modulus = KMByteBlob.instance(len);
+ getBytes(modulus);
+ updateRsaKeyBuffer(modulus);
+ len = header(ASN1_INTEGER);
+ short pubKey = KMByteBlob.instance(len);
+ getBytes(pubKey);
+ len = header(ASN1_INTEGER);
+ short privKey = KMByteBlob.instance(len);
+ getBytes(privKey);
+ updateRsaKeyBuffer(privKey);
+ KMArray.cast(resp).add((short) 0, modulus);
+ KMArray.cast(resp).add((short) 1, pubKey);
+ KMArray.cast(resp).add((short) 2, privKey);
+ return resp;
+ }
+
+ private void updateRsaKeyBuffer(short blob) {
+ byte[] buffer = KMByteBlob.cast(blob).getBuffer();
+ short startOff = KMByteBlob.cast(blob).getStartOff();
+ short len = KMByteBlob.cast(blob).length();
+ if (0 == buffer[startOff] && len > 256) {
+ KMByteBlob.cast(blob).setStartOff(++startOff);
+ KMByteBlob.cast(blob).setLength(--len);
+ }
+ }
+
+ private short readEcdsa256SigIntegerHeader() {
+ short len = header(ASN1_INTEGER);
+ if (len == 33) {
+ if (0 != getByte()) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ len--;
+ } else if (len > 33) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return len;
+ }
+
+ // Seq [Int, Int]
+ public short decodeEcdsa256Signature(short blob, byte[] scratchPad, short scratchPadOff) {
+ init(blob);
+ short len = header(ASN1_SEQUENCE);
+ len = readEcdsa256SigIntegerHeader();
+ // concatenate r and s in the buffer (r||s)
+ Util.arrayFillNonAtomic(scratchPad, scratchPadOff, (short) 64, (byte) 0);
+ // read r
+ getBytes(scratchPad, (short) (scratchPadOff + 32 - len), len);
+ len = readEcdsa256SigIntegerHeader();
+ // read s
+ getBytes(scratchPad, (short) (scratchPadOff + 64 - len), len);
+ return (short) 64;
+ }
+
+ // Seq [Int, Blob]
+ public void decodeCommon(short version, byte[] alg) {
+ short len = header(ASN1_SEQUENCE);
+ len = header(ASN1_INTEGER);
+ if (len != 1) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ short ver = getByte();
+ if (ver != version) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ len = header(ASN1_SEQUENCE);
+ short blob = KMByteBlob.instance(len);
+ getBytes(blob);
+ if (Util.arrayCompare(
+ KMByteBlob.cast(blob).getBuffer(),
+ KMByteBlob.cast(blob).getStartOff(),
+ alg,
+ (short) 0,
+ KMByteBlob.cast(blob).length())
+ != 0) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ }
+
+ // Seq[Int,blob,blob]
+ public short decodeEcPrivateKey(short version) {
+ short resp = KMArray.instance((short) 2);
+ header(ASN1_OCTET_STRING);
+ header(ASN1_SEQUENCE);
+ short len = header(ASN1_INTEGER);
+ if (len != 1) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ short ver = getByte();
+ if (ver != version) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ len = header(ASN1_OCTET_STRING);
+ short privKey = KMByteBlob.instance(len);
+ getBytes(privKey);
+ validateTag0IfPresent();
+ header(ASN1_A1_TAG);
+ len = header(ASN1_BIT_STRING);
+ if (len < 1) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ // TODO need to handle if unused bits are not zero
+ byte unusedBits = getByte();
+ if (unusedBits != 0) {
+ KMException.throwIt(KMError.UNIMPLEMENTED);
+ }
+ short pubKey = KMByteBlob.instance((short) (len - 1));
+ getBytes(pubKey);
+ KMArray.cast(resp).add((short) 0, pubKey);
+ KMArray.cast(resp).add((short) 1, privKey);
+ return resp;
+ }
+
+ private void validateTag0IfPresent() {
+ if (data[dataInfo[DATA_CURSOR_OFFSET]] != ASN1_A0_TAG) {
+ return;
+ }
+ ;
+ short len = header(ASN1_A0_TAG);
+ if (len != EC_CURVE.length) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ if (Util.arrayCompare(data, dataInfo[DATA_CURSOR_OFFSET], EC_CURVE, (short) 0, len) != 0) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ incrementCursor(len);
+ }
+
+ private void validateAttributeTypeAndValue() {
+ // First byte should be OBJECT_IDENTIFIER, otherwise it is not well-formed DER Subject.
+ if (data[dataInfo[DATA_CURSOR_OFFSET]] != OBJECT_IDENTIFIER) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ // Check if the OID matches the email address
+ if ((Util.arrayCompare(
+ data,
+ dataInfo[DATA_CURSOR_OFFSET],
+ EMAIL_ADDRESS_OID,
+ (short) 0,
+ (short) EMAIL_ADDRESS_OID.length)
+ == 0)) {
+ incrementCursor((short) EMAIL_ADDRESS_OID.length);
+ // Validate the length of the attribute value.
+ if (getByte() != IA5_STRING) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ short emailLength = getLength();
+ if (emailLength <= 0 && emailLength > MAX_EMAIL_ADD_LEN) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ incrementCursor(emailLength);
+ return;
+ }
+ // Check other OIDs.
+ for (short i = 0; i < (short) attributeOIds.length; i++) {
+ if ((Util.arrayCompare(
+ data,
+ dataInfo[DATA_CURSOR_OFFSET],
+ COMMON_OID,
+ (short) 0,
+ (short) COMMON_OID.length)
+ == 0)
+ && (attributeOIds[i]
+ == data[(short) (dataInfo[DATA_CURSOR_OFFSET] + COMMON_OID.length)])) {
+ incrementCursor((short) (COMMON_OID.length + 1));
+ // Validate the length of the attribute value.
+ short tag = getByte();
+ if (tag != ASN1_UTF8_STRING
+ && tag != ASN1_TELETEX_STRING
+ && tag != ASN1_PRINTABLE_STRING
+ && tag != ASN1_UNIVERSAL_STRING
+ && tag != ASN1_BMP_STRING) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ short attrValueLength = getLength();
+ if (attrValueLength <= 0 && attrValueLength > attributeValueMaxLen[i]) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ incrementCursor(attrValueLength);
+ return;
+ }
+ }
+ // If no match is found above then move the cursor to next element.
+ getByte(); // Move Cursor by one byte (OID)
+ incrementCursor(getLength()); // Move cursor to AtrributeTag
+ getByte(); // Move cursor to AttributeValue
+ incrementCursor(getLength()); // Move cursor to next SET element
+ }
+
+ private short header(short tag) {
+ short t = getByte();
+ if (t != tag) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ return getLength();
+ }
+
+ private byte getByte() {
+ byte d = data[dataInfo[DATA_CURSOR_OFFSET]];
+ incrementCursor((short) 1);
+ return d;
+ }
+
+ private short getShort() {
+ short d = Util.getShort(data, dataInfo[DATA_CURSOR_OFFSET]);
+ incrementCursor((short) 2);
+ return d;
+ }
+
+ private void getBytes(short blob) {
+ short len = KMByteBlob.cast(blob).length();
+ Util.arrayCopyNonAtomic(
+ data,
+ dataInfo[DATA_CURSOR_OFFSET],
+ KMByteBlob.cast(blob).getBuffer(),
+ KMByteBlob.cast(blob).getStartOff(),
+ len);
+ incrementCursor(len);
+ }
+
+ private void getBytes(byte[] buffer, short offset, short len) {
+ Util.arrayCopyNonAtomic(data, dataInfo[DATA_CURSOR_OFFSET], buffer, offset, len);
+ incrementCursor(len);
+ }
+
+ private short getLength() {
+ byte len = getByte();
+ if (len >= 0) {
+ return len;
+ }
+ len = (byte) (len & 0x7F);
+ if (len == 1) {
+ return (short) (getByte() & 0xFF);
+ } else if (len == 2) {
+ return getShort();
+ } else {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ return KMType.INVALID_VALUE; // should not come here
+ }
+
+ public void init(short blob) {
+ data = KMByteBlob.cast(blob).getBuffer();
+ dataInfo[DATA_START_OFFSET] = KMByteBlob.cast(blob).getStartOff();
+ dataInfo[DATA_LENGTH_OFFSET] = KMByteBlob.cast(blob).length();
+ dataInfo[DATA_CURSOR_OFFSET] = dataInfo[DATA_START_OFFSET];
+ }
+
+ public void incrementCursor(short n) {
+ dataInfo[DATA_CURSOR_OFFSET] += n;
+ if (dataInfo[DATA_CURSOR_OFFSET]
+ > ((short) (dataInfo[DATA_START_OFFSET] + dataInfo[DATA_LENGTH_OFFSET]))) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMBignumTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMBignumTag.java
new file mode 100644
index 0000000..5eb7eae
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMBignumTag.java
@@ -0,0 +1,110 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMBignumTag represents BIGNUM Tag Type from android keymaster hal specifications. The tag value
+ * of this tag is the KMByteBlob pointer i.e. offset of KMByteBlob in memory heap. struct{byte
+ * TAG_TYPE; short length; struct{short BIGNUM_TAG; short tagKey; short blobPtr}}
+ */
+public class KMBignumTag extends KMTag {
+
+ private static KMBignumTag prototype;
+
+ private KMBignumTag() {}
+
+ private static KMBignumTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMBignumTag();
+ }
+ KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short blobPtr = KMByteBlob.exp();
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BIGNUM_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), blobPtr);
+ return ptr;
+ }
+
+ public static short instance(short key, short byteBlob) {
+ if (!validateKey(key, byteBlob)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (heap[byteBlob] != BYTE_BLOB_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BIGNUM_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), byteBlob);
+ return ptr;
+ }
+
+ public static KMBignumTag cast(short ptr) {
+ if (heap[ptr] != TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BIGNUM_TAG) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ private static boolean validateKey(short key, short byteBlob) {
+ short valueLen = KMByteBlob.cast(byteBlob).length();
+ switch (key) {
+ case CERTIFICATE_SERIAL_NUM:
+ if (valueLen > MAX_CERTIFICATE_SERIAL_SIZE) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ public short getKey() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short getTagType() {
+ return KMType.BIGNUM_TAG;
+ }
+
+ public short getValue() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+
+ public short length() {
+ short blobPtr =
+ Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ return KMByteBlob.cast(blobPtr).length();
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMBoolTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMBoolTag.java
new file mode 100644
index 0000000..27730a5
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMBoolTag.java
@@ -0,0 +1,115 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMBoolTag represents BOOL TAG type from the android keymaster hal specifications. If it is
+ * present in the key parameter list then its value is always true. A KMTag always requires a value
+ * because it is a key value pair. The bool tag always has 0x01 as its value. struct{byte TAG_TYPE;
+ * short length; struct{short BOOL_TAG; short tagKey; byte value 1}}
+ */
+public class KMBoolTag extends KMTag {
+
+ // The allowed tag keys of type bool tag.
+ private static final short[] tags = {
+ CALLER_NONCE,
+ INCLUDE_UNIQUE_ID,
+ BOOTLOADER_ONLY,
+ ROLLBACK_RESISTANCE,
+ NO_AUTH_REQUIRED,
+ ALLOW_WHILE_ON_BODY,
+ TRUSTED_USER_PRESENCE_REQUIRED,
+ TRUSTED_CONFIRMATION_REQUIRED,
+ UNLOCKED_DEVICE_REQUIRED,
+ RESET_SINCE_ID_ROTATION,
+ EARLY_BOOT_ONLY,
+ DEVICE_UNIQUE_ATTESTATION
+ };
+ private static KMBoolTag prototype;
+
+ private KMBoolTag() {}
+
+ private static KMBoolTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMBoolTag();
+ }
+ KMType.instanceTable[KM_BOOL_TAG_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short ptr = instance(TAG_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BOOL_TAG);
+ return ptr;
+ }
+
+ public static short instance(short key) {
+ if (!validateKey(key)) {
+ KMException.throwIt(KMError.INVALID_TAG);
+ }
+ short ptr = KMType.instance(TAG_TYPE, (short) 5);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BOOL_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+ // Value is always 1.
+ heap[(short) (ptr + TLV_HEADER_SIZE + 4)] = 0x01;
+ return ptr;
+ }
+
+ public static KMBoolTag cast(short ptr) {
+ if (heap[ptr] != TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BOOL_TAG) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ // validate the tag key.
+ private static boolean validateKey(short key) {
+ short index = (short) tags.length;
+ while (--index >= 0) {
+ if (tags[index] == key) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static short[] getTags() {
+ return tags;
+ }
+
+ public short getKey() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_BOOL_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short getTagType() {
+ return KMType.BOOL_TAG;
+ }
+
+ public byte getVal() {
+ return heap[(short) (KMType.instanceTable[KM_BOOL_TAG_OFFSET] + TLV_HEADER_SIZE + 4)];
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMByteBlob.java
new file mode 100644
index 0000000..98d81fc
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMByteBlob.java
@@ -0,0 +1,142 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMByteBlob represents contiguous block of bytes. It corresponds to CBOR type of Byte String. It
+ * extends KMType by specifying value field as zero or more sequence of bytes. struct{byte
+ * BYTE_BLOB_TYPE; short length; sequence of bytes}
+ */
+public class KMByteBlob extends KMType {
+
+ private static byte OFFSET_SIZE = 2;
+ private static KMByteBlob prototype;
+
+ protected KMByteBlob() {}
+
+ private static KMByteBlob proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMByteBlob();
+ }
+ KMType.instanceTable[KM_BYTE_BLOB_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ return KMType.exp(BYTE_BLOB_TYPE);
+ }
+
+ // return an empty byte blob instance
+ public static short instance(short length) {
+ short ptr = KMType.instance(BYTE_BLOB_TYPE, (short) (length + 2));
+ Util.setShort(
+ heap, (short) (ptr + TLV_HEADER_SIZE), (short) (ptr + TLV_HEADER_SIZE + OFFSET_SIZE));
+ Util.setShort(heap, (short) (ptr + 1), length);
+ return ptr;
+ }
+
+ // byte blob from existing buf
+ public static short instance(byte[] buf, short startOff, short length) {
+ short ptr = instance(length);
+ Util.arrayCopyNonAtomic(
+ buf, startOff, heap, (short) (ptr + TLV_HEADER_SIZE + OFFSET_SIZE), length);
+ return ptr;
+ }
+
+ // cast the ptr to KMByteBlob
+ public static KMByteBlob cast(short ptr) {
+ if (heap[ptr] != BYTE_BLOB_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ // Add the byte
+ public void add(short index, byte val) {
+ short len = length();
+ if (index >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ heap[(short) (getStartOff() + index)] = val;
+ }
+
+ // Get the byte
+ public byte get(short index) {
+ short len = length();
+ if (index >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ return heap[(short) (getStartOff() + index)];
+ }
+
+ // Get the start of blob
+ public short getStartOff() {
+ return Util.getShort(heap, (short) (getBaseOffset() + TLV_HEADER_SIZE));
+ }
+
+ public void setStartOff(short offset) {
+ Util.setShort(heap, (short) (getBaseOffset() + TLV_HEADER_SIZE), offset);
+ }
+
+ // Get the length of the blob
+ public short length() {
+ return Util.getShort(heap, (short) (getBaseOffset() + 1));
+ }
+
+ // Get the buffer pointer in which blob is contained.
+ public byte[] getBuffer() {
+ return heap;
+ }
+
+ public void getValue(byte[] destBuf, short destStart, short destLength) {
+ Util.arrayCopyNonAtomic(heap, getStartOff(), destBuf, destStart, destLength);
+ }
+
+ public short getValues(byte[] destBuf, short destStart) {
+ short destLength = length();
+ Util.arrayCopyNonAtomic(heap, getStartOff(), destBuf, destStart, destLength);
+ return destLength;
+ }
+
+ public void setValue(byte[] srcBuf, short srcStart, short srcLength) {
+ if (length() < srcLength) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ Util.arrayCopyNonAtomic(srcBuf, srcStart, heap, getStartOff(), srcLength);
+ setLength(srcLength);
+ }
+
+ public boolean isValid() {
+ return (length() != 0);
+ }
+
+ protected short getBaseOffset() {
+ return instanceTable[KM_BYTE_BLOB_OFFSET];
+ }
+
+ public void setLength(short len) {
+ Util.setShort(heap, (short) (getBaseOffset() + 1), len);
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMByteTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMByteTag.java
new file mode 100644
index 0000000..ac49c0e
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMByteTag.java
@@ -0,0 +1,145 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMByteTag represents BYTES Tag Type from android keymaster hal specifications. The tag value of
+ * this tag is the KMByteBlob pointer i.e. offset of KMByteBlob in memory heap. struct{byte
+ * TAG_TYPE; short length; struct{short BYTES_TAG; short tagKey; short blobPtr}}
+ */
+public class KMByteTag extends KMTag {
+
+ private static KMByteTag prototype;
+
+ private KMByteTag() {}
+
+ private static KMByteTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMByteTag();
+ }
+ KMType.instanceTable[KM_BYTE_TAG_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short blobPtr = KMByteBlob.exp();
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BYTES_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), blobPtr);
+ return ptr;
+ }
+
+ public static short instance(short key, short byteBlob) {
+ if (!validateKey(key, byteBlob)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (heap[byteBlob] != BYTE_BLOB_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BYTES_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), byteBlob);
+ return ptr;
+ }
+
+ public static KMByteTag cast(short ptr) {
+ if (heap[ptr] != TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BYTES_TAG) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ private static boolean validateKey(short key, short byteBlob) {
+ short valueLen = KMByteBlob.cast(byteBlob).length();
+ switch (key) {
+ case ATTESTATION_APPLICATION_ID:
+ if (valueLen > MAX_ATTESTATION_APP_ID_SIZE) {
+ return false;
+ }
+ break;
+ case CERTIFICATE_SUBJECT_NAME:
+ {
+ if (valueLen > KMConfigurations.MAX_SUBJECT_DER_LEN) {
+ return false;
+ }
+ KMAsn1Parser asn1Decoder = KMAsn1Parser.instance();
+ asn1Decoder.validateDerSubject(byteBlob);
+ }
+ break;
+ case APPLICATION_ID:
+ case APPLICATION_DATA:
+ if (valueLen > MAX_APP_ID_APP_DATA_SIZE) {
+ return false;
+ }
+ break;
+ case ATTESTATION_CHALLENGE:
+ if (valueLen > MAX_ATTESTATION_CHALLENGE_SIZE) {
+ return false;
+ }
+ break;
+ case ATTESTATION_ID_BRAND:
+ case ATTESTATION_ID_DEVICE:
+ case ATTESTATION_ID_PRODUCT:
+ case ATTESTATION_ID_SERIAL:
+ case ATTESTATION_ID_IMEI:
+ case ATTESTATION_ID_MEID:
+ case ATTESTATION_ID_MANUFACTURER:
+ case ATTESTATION_ID_MODEL:
+ if (valueLen > KMConfigurations.MAX_ATTESTATION_IDS_SIZE) {
+ return false;
+ }
+ break;
+ case ROOT_OF_TRUST:
+ case NONCE:
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ public short getKey() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short getTagType() {
+ return KMType.BYTES_TAG;
+ }
+
+ public short getValue() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+
+ public short length() {
+ short blobPtr =
+ Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ return KMByteBlob.cast(blobPtr).length();
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCose.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCose.java
new file mode 100644
index 0000000..0c2244c
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCose.java
@@ -0,0 +1,602 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+
+/**
+ * This class constructs the Cose messages like CoseKey, CoseMac0, MacStructure, CoseSign1,
+ * SignStructure, CoseEncrypt, EncryptStructure and ReceipientStructures.
+ */
+public class KMCose {
+
+ // COSE SIGN1
+ public static final byte COSE_SIGN1_ENTRY_COUNT = 4;
+ public static final byte COSE_SIGN1_PROTECTED_PARAMS_OFFSET = 0;
+ public static final byte COSE_SIGN1_PAYLOAD_OFFSET = 2;
+ public static final byte COSE_SIGN1_SIGNATURE_OFFSET = 3;
+ // COSE MAC0
+ public static final byte COSE_MAC0_ENTRY_COUNT = 4;
+ public static final byte COSE_MAC0_PROTECTED_PARAMS_OFFSET = 0;
+ public static final byte COSE_MAC0_PAYLOAD_OFFSET = 2;
+ public static final byte COSE_MAC0_TAG_OFFSET = 3;
+ // COSE ENCRYPT
+ public static final byte COSE_ENCRYPT_ENTRY_COUNT = 4;
+ public static final byte COSE_ENCRYPT_STRUCTURE_ENTRY_COUNT = 3;
+ public static final byte COSE_ENCRYPT_RECIPIENT_ENTRY_COUNT = 3;
+
+ // COSE Labels
+ public static final byte COSE_LABEL_ALGORITHM = 1;
+ public static final byte COSE_LABEL_KEYID = 4;
+ public static final byte COSE_LABEL_IV = 5;
+ public static final byte COSE_LABEL_COSE_KEY = (byte) 0xFF; // -1
+
+ // COSE Algorithms
+ public static final byte COSE_ALG_AES_GCM_256 = 3; // AES-GCM mode w/ 256-bit key, 128-bit tag.
+ public static final byte COSE_ALG_HMAC_256 = 5; // HMAC w/ SHA-256
+ public static final byte COSE_ALG_ES256 = (byte) 0xF9; // ECDSA w/ SHA-256; -7
+ public static final byte COSE_ALG_ECDH_ES_HKDF_256 = (byte) 0xE7; // ECDH-EC+HKDF-256; -25
+
+ // COSE P256 EC Curve
+ public static final byte COSE_ECCURVE_256 = 1;
+
+ // COSE key types
+ public static final byte COSE_KEY_TYPE_EC2 = 2;
+ public static final byte COSE_KEY_TYPE_SYMMETRIC_KEY = 4;
+
+ // COSE Key Operations
+ public static final byte COSE_KEY_OP_SIGN = 1;
+ public static final byte COSE_KEY_OP_VERIFY = 2;
+ public static final byte COSE_KEY_OP_ENCRYPT = 3;
+ public static final byte COSE_KEY_OP_DECRYPT = 4;
+
+ // AES GCM
+ public static final short AES_GCM_KEY_SIZE_BITS = 256;
+ // Cose key parameters.
+ public static final short COSE_KEY_KEY_TYPE = 1;
+ public static final short COSE_KEY_KEY_ID = 2;
+ public static final short COSE_KEY_ALGORITHM = 3;
+ public static final short COSE_KEY_CURVE = -1;
+ public static final short COSE_KEY_PUBKEY_X = -2;
+ public static final short COSE_KEY_PUBKEY_Y = -3;
+ public static final short COSE_KEY_PRIV_KEY = -4;
+ public static final byte[] COSE_TEST_KEY = {
+ (byte) 0xFF, (byte) 0xFE, (byte) 0xEE, (byte) 0x90
+ }; // -70000
+ public static final byte COSE_KEY_MAX_SIZE = 4;
+
+ // kdfcontext strings
+ public static final byte[] client = {0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74};
+ public static final byte[] server = {0x73, 0x65, 0x72, 0x76, 0x65, 0x72};
+ // Context strings
+ public static final byte[] MAC_CONTEXT = {0x4d, 0x41, 0x43, 0x30}; // MAC0
+ public static final byte[] SIGNATURE1_CONTEXT = {
+ 0x53, 0x69, 0x67, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x31
+ }; // Signature1
+ public static final byte[] ENCRYPT_CONTEXT = {
+ 0x45, 0x6E, 0x63, 0x72, 0x79, 0x70, 0x74
+ }; // Encrypt
+ // Certificate payload supported keys
+ public static final byte ISSUER = (byte) 0x01;
+ public static final byte SUBJECT = (byte) 0x02;
+ public static final byte[] SUBJECT_PUBLIC_KEY = {
+ (byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA8
+ };
+ public static final byte[] KEY_USAGE = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA7};
+ // text strings
+ public static final byte[] TEST_ISSUER_NAME = {
+ (byte) 0x49, 0x73, 0x73, 0x75, 0x65, 0x72
+ }; // "Issuer"
+ public static final byte[] TEST_SUBJECT_NAME = {
+ 0x53, 0x75, 0x62, 0x6A, 0x65, 0x63, 0x74
+ }; // "Subject"
+ public static final byte[] KEY_USAGE_SIGN = {0x20}; // Key usage sign
+
+ public static final short[] COSE_KEY_LABELS = {
+ KMCose.COSE_KEY_KEY_TYPE,
+ KMCose.COSE_KEY_KEY_ID,
+ KMCose.COSE_KEY_ALGORITHM,
+ KMCose.COSE_KEY_CURVE,
+ KMCose.COSE_KEY_PUBKEY_X,
+ KMCose.COSE_KEY_PUBKEY_Y,
+ KMCose.COSE_KEY_PRIV_KEY
+ };
+ public static final short[] COSE_HEADER_LABELS = {
+ KMCose.COSE_LABEL_ALGORITHM,
+ KMCose.COSE_LABEL_KEYID,
+ KMCose.COSE_LABEL_IV,
+ KMCose.COSE_LABEL_COSE_KEY
+ };
+
+ /**
+ * Constructs the Cose MAC structure.
+ *
+ * @param protectedHeader Bstr pointer which holds the protected header.
+ * @param extAad Bstr pointer which holds the external Aad.
+ * @param payload Bstr pointer which holds the payload of the MAC structure.
+ * @return KMArray instance of MAC structure.
+ */
+ public static short constructCoseMacStructure(
+ short protectedHeader, short extAad, short payload) {
+ // Create MAC Structure and compute HMAC as per https://tools.ietf.org/html/rfc8152#section-6.3
+ // MAC_structure = [
+ // context : "MAC" / "MAC0",
+ // protected : empty_or_serialized_map,
+ // external_aad : bstr,
+ // payload : bstr
+ // ]
+ short arrPtr = KMArray.instance(KMCose.COSE_MAC0_ENTRY_COUNT);
+ // 1 - Context
+ KMArray.cast(arrPtr)
+ .add(
+ (short) 0,
+ KMTextString.instance(
+ KMCose.MAC_CONTEXT, (short) 0, (short) KMCose.MAC_CONTEXT.length));
+ // 2 - Protected headers.
+ KMArray.cast(arrPtr).add((short) 1, protectedHeader);
+ // 3 - external aad
+ KMArray.cast(arrPtr).add((short) 2, extAad);
+ // 4 - payload.
+ KMArray.cast(arrPtr).add((short) 3, payload);
+ return arrPtr;
+ }
+
+ /**
+ * Constructs the COSE_MAC0 object.
+ *
+ * @param protectedHeader Bstr pointer which holds the protected header.
+ * @param unprotectedHeader Bstr pointer which holds the unprotected header.
+ * @param payload Bstr pointer which holds the payload of the MAC structure.
+ * @param tag Bstr pointer which holds the tag value.
+ * @return KMArray instance of COSE_MAC0 object.
+ */
+ public static short constructCoseMac0(
+ short protectedHeader, short unprotectedHeader, short payload, short tag) {
+ // Construct Cose_MAC0
+ // COSE_Mac0 = [
+ // protectedHeader,
+ // unprotectedHeader,
+ // payload : bstr / nil,
+ // tag : bstr,
+ // ]
+ short arrPtr = KMArray.instance(KMCose.COSE_MAC0_ENTRY_COUNT);
+ // 1 - protected headers
+ KMArray.cast(arrPtr).add((short) 0, protectedHeader);
+ // 2 - unprotected headers
+ KMArray.cast(arrPtr).add((short) 1, unprotectedHeader);
+ // 2 - payload
+ KMArray.cast(arrPtr).add((short) 2, payload);
+ // 3 - tag
+ KMArray.cast(arrPtr).add((short) 3, tag);
+ // Do encode.
+ return arrPtr;
+ }
+
+ /**
+ * Constructs the COSE_Signature structure.
+ *
+ * @param protectedHeader Bstr pointer which holds the protected header.
+ * @param extAad Bstr pointer which holds the aad.
+ * @param payload Bstr pointer which holds the payload.
+ * @return KMArray instance of COSE_Signature object.
+ */
+ public static short constructCoseSignStructure(
+ short protectedHeader, short extAad, short payload) {
+ // Sig_structure = [
+ // context : "Signature" / "Signature1" / "CounterSignature",
+ // body_protected : empty_or_serialized_map,
+ // ? sign_protected : empty_or_serialized_map,
+ // external_aad : bstr,
+ // payload : bstr
+ // ]
+ short arrPtr = KMArray.instance(KMCose.COSE_SIGN1_ENTRY_COUNT);
+ // 1 - Context
+ KMArray.cast(arrPtr)
+ .add(
+ (short) 0,
+ KMTextString.instance(
+ KMCose.SIGNATURE1_CONTEXT, (short) 0, (short) KMCose.SIGNATURE1_CONTEXT.length));
+ // 2 - Protected headers.
+ KMArray.cast(arrPtr).add((short) 1, protectedHeader);
+ // 3 - external aad
+ KMArray.cast(arrPtr).add((short) 2, extAad);
+ // 4 - payload.
+ KMArray.cast(arrPtr).add((short) 3, payload);
+ return arrPtr;
+ }
+
+ /**
+ * Constructs the COSE_Sign1 object.
+ *
+ * @param protectedHeader Bstr pointer which holds the protected header.
+ * @param unProtectedHeader Bstr pointer which holds the unprotected header.
+ * @param payload Bstr pointer which holds the payload.
+ * @param signature Bstr pointer which holds the signature.
+ * @return KMArray instance of COSE_Sign1 object.
+ */
+ public static short constructCoseSign1(
+ short protectedHeader, short unProtectedHeader, short payload, short signature) {
+ // COSE_Sign = [
+ // protectedHeader,
+ // unprotectedHeader,
+ // payload : bstr / nil,
+ // signatures : [+ COSE_Signature]
+ // ]
+ short arrPtr = KMArray.instance(KMCose.COSE_SIGN1_ENTRY_COUNT);
+ // 1 - protected headers
+ KMArray.cast(arrPtr).add((short) 0, protectedHeader);
+ // 2 - unprotected headers
+ KMArray.cast(arrPtr).add((short) 1, unProtectedHeader);
+ // 2 - payload
+ KMArray.cast(arrPtr).add((short) 2, payload);
+ // 3 - tag
+ KMArray.cast(arrPtr).add((short) 3, signature);
+ return arrPtr;
+ }
+
+ /**
+ * Constructs array based on the tag values provided.
+ *
+ * @param tag array of tag values to be constructed.
+ * @param includeTestMode flag which indicates if TEST_COSE_KEY should be included or not.
+ * @return instance of KMArray.
+ */
+ private static short handleCosePairTags(
+ short[] tag, short[] keyValues, short valueIndex, boolean includeTestMode) {
+ short index = 0;
+ // var is used to calculate the length of the array.
+ short var = 0;
+ short tagLen = (short) tag.length;
+ // var is used to calculate the length of the array.
+ while (index < tagLen) {
+ if (keyValues[index] != KMType.INVALID_VALUE) {
+ keyValues[(short) (index + valueIndex)] =
+ buildCosePairTag((byte) tag[index], keyValues[index]);
+ var++;
+ }
+ index++;
+ }
+ var += includeTestMode ? 1 : 0;
+ short arrPtr = KMArray.instance(var);
+ index = 0;
+ // var is used to index the array.
+ var = 0;
+ while (index < tagLen) {
+ if (keyValues[(short) (index + valueIndex)] != KMType.INVALID_VALUE) {
+ KMArray.cast(arrPtr).add(var++, keyValues[(short) (index + valueIndex)]);
+ }
+ index++;
+ }
+ return arrPtr;
+ }
+
+ /**
+ * Constructs the COSE_sign1 payload for certificate.
+ *
+ * @param issuer instance of KMCosePairTextStringTag which contains issuer value.
+ * @param subject instance of KMCosePairTextStringTag which contains subject value.
+ * @param subPublicKey instance of KMCosePairByteBlobTag which contains encoded KMCoseKey.
+ * @param keyUsage instance of KMCosePairByteBlobTag which contains key usage value.
+ * @return instance of KMArray.
+ */
+ public static short constructCoseCertPayload(
+ short issuer, short subject, short subPublicKey, short keyUsage) {
+ short certPayload = KMArray.instance((short) 4);
+ KMArray.cast(certPayload).add((short) 0, issuer);
+ KMArray.cast(certPayload).add((short) 1, subject);
+ KMArray.cast(certPayload).add((short) 2, subPublicKey);
+ KMArray.cast(certPayload).add((short) 3, keyUsage);
+ certPayload = KMCoseCertPayload.instance(certPayload);
+ KMCoseCertPayload.cast(certPayload).canonicalize();
+ return certPayload;
+ }
+
+ /**
+ * Construct headers structure. Headers can be part of COSE_Sign1, COSE_Encrypt, COSE_Mac0 and
+ * COSE_Key.
+ *
+ * @param alg instance of either KMNInteger or KMInteger, based on the sign of algorithm value.
+ * @param keyId instance of KMByteBlob which contains the key identifier.
+ * @param iv instance of KMByteblob which contains the iv buffer.
+ * @param ephemeralKey instance of KMCoseKey.
+ * @return instance of KMCoseHeaders.
+ */
+ public static short constructHeaders(
+ short[] buff, short alg, short keyId, short iv, short ephemeralKey) {
+ buff[0] = alg;
+ buff[1] = keyId;
+ buff[2] = iv;
+ buff[3] = ephemeralKey;
+ for (short i = 4; i < 8; i++) {
+ buff[i] = KMType.INVALID_VALUE;
+ }
+ short ptr = handleCosePairTags(COSE_HEADER_LABELS, buff, (short) 4, false);
+ ptr = KMCoseHeaders.instance(ptr);
+ KMCoseHeaders.cast(ptr).canonicalize();
+ return ptr;
+ }
+
+ /**
+ * Construct Recipients structure for COSE_Encrypt message.
+ *
+ * @param protectedHeaders instance of KMByteBlob which contains encoded KMCoseHeaders.
+ * @param unprotectedHeaders instance of KMCoseHeaders.
+ * @param cipherText instance of KMSimple
+ * @return instance of KMArray.
+ */
+ public static short constructRecipientsStructure(
+ short protectedHeaders, short unprotectedHeaders, short cipherText) {
+ // recipients : [+COSE_recipient]
+ // COSE_recipient = [
+ // Headers,
+ // ciphertext : bstr / nil,
+ // ? recipients : [+COSE_recipient]
+ // ]
+ short arrPtr = KMArray.instance(COSE_ENCRYPT_RECIPIENT_ENTRY_COUNT);
+ // 1 - protected headers
+ KMArray.cast(arrPtr).add((short) 0, protectedHeaders);
+ // 2 - unprotected headers
+ KMArray.cast(arrPtr).add((short) 1, unprotectedHeaders);
+ // 2 - payload
+ KMArray.cast(arrPtr).add((short) 2, cipherText);
+
+ short recipientsArrayPtr = KMArray.instance((short) 1);
+ KMArray.cast(recipientsArrayPtr).add((short) 0, arrPtr);
+ return recipientsArrayPtr;
+ }
+
+ /**
+ * Construct Encrypt structure required for COSE_Encrypt message.
+ *
+ * @param protectedHeader instance of KMByteBlob which wraps KMCoseHeaders.
+ * @param aad instance of KMByteBlob.
+ * @return instance of KMArray.
+ */
+ public static short constructCoseEncryptStructure(short protectedHeader, short aad) {
+ // Enc_structure = [
+ // context : "Encrypt" / "Encrypt0" / "Enc_Recipient" /
+ // "Mac_Recipient" / "Rec_Recipient",
+ // protected : empty_or_serialized_map,
+ // external_aad : bstr
+ // ]
+ short arrPtr = KMArray.instance(COSE_ENCRYPT_STRUCTURE_ENTRY_COUNT);
+ // 1 - protected headers
+ KMArray.cast(arrPtr)
+ .add(
+ (short) 0,
+ KMTextString.instance(
+ KMCose.ENCRYPT_CONTEXT, (short) 0, (short) KMCose.ENCRYPT_CONTEXT.length));
+ // 2 - unprotected headers
+ KMArray.cast(arrPtr).add((short) 1, protectedHeader);
+ // 2 - payload
+ KMArray.cast(arrPtr).add((short) 2, aad);
+ return arrPtr;
+ }
+
+ /**
+ * Constructs COSE_Encrypt message.
+ *
+ * @param protectedHeader instance of KMByteBlob which wraps KMCoseHeaders.
+ * @param unProtectedHeader instance of KMCoseHeaders.
+ * @param cipherText instance of KMByteBlob containing the cipher text.
+ * @param recipients instance of KMArray containing the recipients instance
+ * @return instance of KMArray.
+ */
+ public static short constructCoseEncrypt(
+ short protectedHeader, short unProtectedHeader, short cipherText, short recipients) {
+ // COSE_Encrypt = [
+ // protectedHeader,
+ // unprotectedHeader,
+ // ciphertext : bstr / nil,
+ // recipients : [+COSE_recipient]
+ // ]
+ short arrPtr = KMArray.instance(KMCose.COSE_ENCRYPT_ENTRY_COUNT);
+ // 1 - protected headers
+ KMArray.cast(arrPtr).add((short) 0, protectedHeader);
+ // 2 - unprotected headers
+ KMArray.cast(arrPtr).add((short) 1, unProtectedHeader);
+ // 2 - payload
+ KMArray.cast(arrPtr).add((short) 2, cipherText);
+ // 3 - tag
+ KMArray.cast(arrPtr).add((short) 3, recipients);
+ return arrPtr;
+ }
+
+ /**
+ * Constructs the instance of KMCosePair*Tag.
+ *
+ * @param key value of the key.
+ * @param valuePtr instance of one of KMType.
+ * @return instance of KMCosePair*Value object.
+ */
+ public static short buildCosePairTag(byte key, short valuePtr) {
+ short type = KMType.getType(valuePtr);
+ short keyPtr;
+ if (key < 0) {
+ keyPtr = KMNInteger.uint_8(key);
+ } else {
+ keyPtr = KMInteger.uint_8(key);
+ }
+ switch (type) {
+ case KMType.INTEGER_TYPE:
+ return KMCosePairIntegerTag.instance(keyPtr, valuePtr);
+ case KMType.NEG_INTEGER_TYPE:
+ return KMCosePairNegIntegerTag.instance(keyPtr, valuePtr);
+ case KMType.BYTE_BLOB_TYPE:
+ return KMCosePairByteBlobTag.instance(keyPtr, valuePtr);
+ case KMType.TEXT_STRING_TYPE:
+ return KMCosePairTextStringTag.instance(keyPtr, valuePtr);
+ case KMType.COSE_KEY_TYPE:
+ return KMCosePairCoseKeyTag.instance(keyPtr, valuePtr);
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ return 0;
+ }
+ }
+
+ /**
+ * Constructs a CoseKey with the provided input parameters. Note that construction of the key_ops
+ * label is not needed to be supported. In the KeyMint2.0 specifications: The CoseKey inside
+ * MacedPublicKeys and DiceCertChain does not have key_ops label.
+ *
+ * @param keyType Instance of the identification of the key type.
+ * @param keyId Instance of key identification value.
+ * @param keyAlg Instance of the algorithm that is used with this key.
+ * @param curve Instance of the EC curve that is used with this key.
+ * @param pubKey Buffer containing the public key.
+ * @param pubKeyOff Start offset of the buffer.
+ * @param pubKeyLen Length of the public key.
+ * @param privKeyPtr Instance of the private key.
+ * @param testMode Represents if key is used in test mode or production mode.
+ * @return Instance of the CoseKey structure.
+ */
+ public static short constructCoseKey(
+ short[] buff,
+ short keyType,
+ short keyId,
+ short keyAlg,
+ short curve,
+ byte[] pubKey,
+ short pubKeyOff,
+ short pubKeyLen,
+ short privKeyPtr,
+ boolean testMode) {
+ if (pubKey[pubKeyOff] == 0x04) { // uncompressed format
+ pubKeyOff += 1;
+ pubKeyLen -= 1;
+ }
+ pubKeyLen = (short) (pubKeyLen / 2);
+ short xPtr = KMByteBlob.instance(pubKey, pubKeyOff, pubKeyLen);
+ short yPtr = KMByteBlob.instance(pubKey, (short) (pubKeyOff + pubKeyLen), pubKeyLen);
+ short coseKey =
+ constructCoseKey(buff, keyType, keyId, keyAlg, curve, xPtr, yPtr, privKeyPtr, testMode);
+ KMCoseKey.cast(coseKey).canonicalize();
+ return coseKey;
+ }
+
+ /**
+ * Constructs the cose key based on input parameters supplied. All the parameters must be
+ * instantiated from either KMInteger or KMNInteger or KMByteblob types.
+ *
+ * @param keyType instance of KMInteger/KMNInteger which holds valid COSE key types.
+ * @param keyId instance of KMByteBlob which holds key identifier value.
+ * @param keyAlg instance of KMInteger/KMNInteger which holds valid COSE key algorithm.
+ * @param curve instance of KMInteger/KMNInteger which holds valid COSE EC curve.
+ * @param pubX instance of KMByteBlob which holds EC public key's x value.
+ * @param pubY instance of KMByteBlob which holds EC public key's y value.
+ * @param priv instance of KMByteBlob which holds EC private value.
+ * @param includeTestKey flag which identifies whether to construct test key or production key.
+ * @return instance of the KMCoseKey object.
+ */
+ public static short constructCoseKey(
+ short[] buff,
+ short keyType,
+ short keyId,
+ short keyAlg,
+ short curve,
+ short pubX,
+ short pubY,
+ short priv,
+ boolean includeTestKey) {
+ short valueIndex = 7;
+ buff[0] = keyType;
+ buff[1] = keyId;
+ buff[2] = keyAlg;
+ buff[3] = curve;
+ buff[4] = pubX;
+ buff[5] = pubY;
+ buff[6] = priv;
+ for (short i = valueIndex; i < 16; i++) {
+ buff[i] = KMType.INVALID_VALUE;
+ }
+ short arrPtr = handleCosePairTags(COSE_KEY_LABELS, buff, valueIndex, includeTestKey);
+ if (includeTestKey) {
+ short testKey =
+ KMCosePairSimpleValueTag.instance(
+ KMNInteger.uint_32(KMCose.COSE_TEST_KEY, (short) 0),
+ KMSimpleValue.instance(KMSimpleValue.NULL));
+ KMArray.cast(arrPtr).add((short) (KMArray.cast(arrPtr).length() - 1), testKey);
+ }
+ arrPtr = KMCoseKey.instance(arrPtr);
+ KMCoseKey.cast(arrPtr).canonicalize();
+ return arrPtr;
+ }
+
+ /**
+ * Constructs key derivation context which is required to compute HKDF.
+ *
+ * @param publicKeyA public key buffer from the first party.
+ * @param publicKeyAOff start position of the public key buffer from first party.
+ * @param publicKeyALen length of the public key buffer from first party.
+ * @param publicKeyB public key buffer from the second party.
+ * @param publicKeyBOff start position of the public key buffer from second party.
+ * @param publicKeyBLen length of the public key buffer from second party.
+ * @param senderIsA true if caller is first party, false if caller is second party.
+ * @return instance of KMArray.
+ */
+ public static short constructKdfContext(
+ byte[] publicKeyA,
+ short publicKeyAOff,
+ short publicKeyALen,
+ byte[] publicKeyB,
+ short publicKeyBOff,
+ short publicKeyBLen,
+ boolean senderIsA) {
+ short index = 0;
+ // Prepare sender info
+ short senderInfo = KMArray.instance((short) 3);
+ KMArray.cast(senderInfo)
+ .add(index++, KMByteBlob.instance(client, (short) 0, (short) client.length));
+ KMArray.cast(senderInfo).add(index++, KMByteBlob.instance((short) 0));
+ KMArray.cast(senderInfo)
+ .add(
+ index,
+ senderIsA
+ ? KMByteBlob.instance(publicKeyA, publicKeyAOff, publicKeyALen)
+ : KMByteBlob.instance(publicKeyB, publicKeyBOff, publicKeyBLen));
+
+ // Prepare recipient info
+ index = 0;
+ short recipientInfo = KMArray.instance((short) 3);
+ KMArray.cast(recipientInfo)
+ .add(index++, KMByteBlob.instance(server, (short) 0, (short) server.length));
+ KMArray.cast(recipientInfo).add(index++, KMByteBlob.instance((short) 0));
+ KMArray.cast(recipientInfo)
+ .add(
+ index,
+ senderIsA
+ ? KMByteBlob.instance(publicKeyB, publicKeyBOff, publicKeyBLen)
+ : KMByteBlob.instance(publicKeyA, publicKeyAOff, publicKeyALen));
+
+ // supply public info
+ index = 0;
+ short publicInfo = KMArray.instance((short) 2);
+ KMArray.cast(publicInfo).add(index++, KMInteger.uint_16(AES_GCM_KEY_SIZE_BITS));
+ KMArray.cast(publicInfo).add(index, KMByteBlob.instance((short) 0));
+
+ // construct kdf context
+ index = 0;
+ short arrPtr = KMArray.instance((short) 4);
+ KMArray.cast(arrPtr).add(index++, KMInteger.uint_8(COSE_ALG_AES_GCM_256));
+ KMArray.cast(arrPtr).add(index++, senderInfo);
+ KMArray.cast(arrPtr).add(index++, recipientInfo);
+ KMArray.cast(arrPtr).add(index, publicInfo);
+
+ return arrPtr;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java
new file mode 100644
index 0000000..fff9cf8
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCoseCertPayload represents the COSE_Sign1 payload for each certificate in BCC. The supported
+ * key types are KMInteger, KMNInteger and the supported value types are KMByteBlob and
+ * KMTextString. It corresponds to a CBOR Map type. struct{byte TAG_TYPE; short length; short
+ * arrayPtr } where arrayPtr is a pointer to array with any KMCosePairTagType subtype instances.
+ */
+public class KMCoseCertPayload extends KMCoseMap {
+
+ private static KMCoseCertPayload prototype;
+
+ private KMCoseCertPayload() {}
+
+ private static KMCoseCertPayload proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCoseCertPayload();
+ }
+ instanceTable[KM_COSE_CERT_PAYLOAD_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp() {
+ short arrPtr = KMArray.instance((short) 2);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add((short) 0, KMCosePairTextStringTag.exp());
+ arr.add((short) 1, KMCosePairByteBlobTag.exp());
+ return KMCoseCertPayload.instance(arrPtr);
+ }
+
+ public static short instance(short vals) {
+ short ptr = KMType.instance(COSE_CERT_PAYLOAD_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+ return ptr;
+ }
+
+ public static KMCoseCertPayload cast(short ptr) {
+ if (heap[ptr] != COSE_CERT_PAYLOAD_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (heap[arrPtr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ @Override
+ public short getVals() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_CERT_PAYLOAD_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ @Override
+ public short length() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).length();
+ }
+
+ @Override
+ public void canonicalize() {
+ KMCoseMap.canonicalize(getVals());
+ }
+
+ private short getValueType(short key, short significantKey) {
+ short arr = getVals();
+ short length = length();
+ short keyPtr;
+ short valPtr = 0;
+ short index = 0;
+ short tagType;
+ boolean found = false;
+ while (index < length) {
+ tagType = KMCosePairTagType.getTagValueType(KMArray.cast(arr).get(index));
+ switch (tagType) {
+ case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+ keyPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == KMCosePairTagType.getKeyValueShort(keyPtr)
+ && significantKey == KMCosePairTagType.getKeyValueSignificantShort(keyPtr)) {
+ valPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE:
+ keyPtr = KMCosePairTextStringTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+ valPtr = KMCosePairTextStringTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ default:
+ break;
+ }
+ if (found) {
+ break;
+ }
+ index++;
+ }
+ return valPtr;
+ }
+
+ public short getSubjectPublicKey() {
+ return getValueType(
+ Util.getShort(KMCose.SUBJECT_PUBLIC_KEY, (short) 2), // LSB
+ Util.getShort(KMCose.SUBJECT_PUBLIC_KEY, (short) 0) // MSB (Significant)
+ );
+ }
+
+ public short getSubject() {
+ return getValueType(KMCose.SUBJECT, KMType.INVALID_VALUE);
+ }
+
+ public short getIssuer() {
+ return getValueType(KMCose.ISSUER, KMType.INVALID_VALUE);
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java
new file mode 100644
index 0000000..0e722d2
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright(C) 2021 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.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCoseHeaders represents headers section from the Cose standard
+ * https://datatracker.ietf.org/doc/html/rfc8152#section-3. The supported key types are KMInteger,
+ * KMNInteger and the supported value types are KMInteger, KMNInteger, KMByteBlob, KMCoseKey. It
+ * corresponds to a CBOR Map type. struct{byte TAG_TYPE; short length; short arrayPtr } where
+ * arrayPtr is a pointer to array with any KMTag subtype instances.
+ */
+public class KMCoseHeaders extends KMCoseMap {
+
+ private static KMCoseHeaders prototype;
+
+ private KMCoseHeaders() {}
+
+ private static KMCoseHeaders proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCoseHeaders();
+ }
+ instanceTable[KM_COSE_HEADERS_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp() {
+ short arrPtr = KMArray.instance((short) 4);
+ // CoseKey is internally an Array so evaluate it separately.
+ short coseKeyValueExp = KMCosePairCoseKeyTag.exp();
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add((short) 0, KMCosePairIntegerTag.exp());
+ arr.add((short) 1, KMCosePairNegIntegerTag.exp());
+ arr.add((short) 2, KMCosePairByteBlobTag.exp());
+ arr.add((short) 3, coseKeyValueExp);
+ return KMCoseHeaders.instance(arrPtr);
+ }
+
+ public static short instance(short vals) {
+ short ptr = KMType.instance(COSE_HEADERS_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+ return ptr;
+ }
+
+ public static KMCoseHeaders cast(short ptr) {
+ if (heap[ptr] != COSE_HEADERS_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (heap[arrPtr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ @Override
+ public short getVals() {
+ return Util.getShort(heap, (short) (instanceTable[KM_COSE_HEADERS_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ @Override
+ public short length() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).length();
+ }
+
+ @Override
+ public void canonicalize() {
+ KMCoseMap.canonicalize(getVals());
+ }
+
+ private short getValueType(short key) {
+ short index = 0;
+ short len = length();
+ short arr = getVals();
+ short tagType;
+ short valPtr = 0;
+ short keyPtr;
+ boolean found = false;
+ while (index < len) {
+ tagType = KMCosePairTagType.getTagValueType(KMArray.cast(arr).get(index));
+ switch (tagType) {
+ case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+ keyPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+ valPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE:
+ keyPtr = KMCosePairCoseKeyTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+ valPtr = KMCosePairCoseKeyTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ case KMType.COSE_PAIR_INT_TAG_TYPE:
+ keyPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+ valPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+ keyPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+ valPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ default:
+ break;
+ }
+ if (found) {
+ break;
+ }
+ index++;
+ }
+ return valPtr;
+ }
+
+ public short getKeyIdentifier() {
+ return getValueType(KMCose.COSE_LABEL_KEYID);
+ }
+
+ public short getCoseKey() {
+ return getValueType(KMCose.COSE_LABEL_COSE_KEY);
+ }
+
+ public short getIV() {
+ return getValueType(KMCose.COSE_LABEL_IV);
+ }
+
+ public short getAlgorithm() {
+ return getValueType(KMCose.COSE_LABEL_ALGORITHM);
+ }
+
+ public boolean isDataValid(short[] buff, short alg, short keyIdPtr) {
+ short bufLen = 4;
+ buff[0] = KMCose.COSE_LABEL_ALGORITHM;
+ buff[1] = alg;
+ buff[2] = KMCose.COSE_LABEL_KEYID;
+ buff[3] = keyIdPtr;
+ boolean valid = false;
+ short value;
+ short ptr;
+ short tagIndex = 0;
+ while (tagIndex < bufLen) {
+ value = buff[(short) (tagIndex + 1)];
+ if (value != KMType.INVALID_VALUE) {
+ valid = false;
+ ptr = getValueType(buff[tagIndex]);
+ switch (KMType.getType(ptr)) {
+ case KMType.BYTE_BLOB_TYPE:
+ if ((KMByteBlob.cast(value).length() == KMByteBlob.cast(ptr).length())
+ && (0
+ == Util.arrayCompare(
+ KMByteBlob.cast(value).getBuffer(),
+ KMByteBlob.cast(value).getStartOff(),
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length()))) {
+ valid = true;
+ }
+ break;
+ case KMType.INTEGER_TYPE:
+ if (value == KMInteger.cast(ptr).getShort()) {
+ valid = true;
+ }
+ break;
+ case KMType.NEG_INTEGER_TYPE:
+ if ((byte) value == (byte) KMNInteger.cast(ptr).getShort()) {
+ valid = true;
+ }
+ break;
+ default:
+ break;
+ }
+ if (!valid) {
+ break;
+ }
+ }
+ tagIndex += 2;
+ }
+ return valid;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseKey.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseKey.java
new file mode 100644
index 0000000..d3edc5f
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseKey.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCoseKey represents COSE_Key section from the Cose standard
+ * https://datatracker.ietf.org/doc/html/rfc8152#section-7 The supported key types are KMNInteger,
+ * KMInteger and the supported value types are KMInteger, KMNInteger, KMByteBlob, KMSimpleValue. It
+ * corresponds to a CBOR Map type. struct{byte TAG_TYPE; short length; short arrayPtr } where
+ * arrayPtr is a pointer to array with any KMTag subtype instances. Note that construction of the
+ * key_ops label is not needed to be supported. In the KeyMint2.0 specifications: The CoseKey inside
+ * MacedPublicKeys and DiceCertChain does not have key_ops label.
+ */
+public class KMCoseKey extends KMCoseMap {
+
+ private static KMCoseKey prototype;
+
+ private KMCoseKey() {}
+
+ private static KMCoseKey proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCoseKey();
+ }
+ instanceTable[KM_COSE_KEY_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp() {
+ short arrPtr = KMArray.instance((short) 4);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add((short) 0, KMCosePairIntegerTag.exp());
+ arr.add((short) 1, KMCosePairNegIntegerTag.exp());
+ arr.add((short) 2, KMCosePairByteBlobTag.exp());
+ arr.add((short) 3, KMCosePairSimpleValueTag.exp());
+ return KMCoseKey.instance(arrPtr);
+ }
+
+ public static short instance(short vals) {
+ short ptr = KMType.instance(COSE_KEY_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+ return ptr;
+ }
+
+ public static KMCoseKey cast(short ptr) {
+ if (heap[ptr] != COSE_KEY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (heap[arrPtr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ @Override
+ public short getVals() {
+ return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ @Override
+ public short length() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).length();
+ }
+
+ private short getValueType(short key, short significantKey) {
+ short arr = getVals();
+ short length = length();
+ short keyPtr;
+ short valPtr = 0;
+ short index = 0;
+ short tagType;
+ boolean found = false;
+ while (index < length) {
+ tagType = KMCosePairTagType.getTagValueType(KMArray.cast(arr).get(index));
+ switch (tagType) {
+ case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+ keyPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+ valPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ case KMType.COSE_PAIR_INT_TAG_TYPE:
+ keyPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+ valPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+ keyPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+ valPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE:
+ keyPtr = KMCosePairSimpleValueTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == KMCosePairTagType.getKeyValueShort(keyPtr)
+ && significantKey == KMCosePairTagType.getKeyValueSignificantShort(keyPtr)) {
+ valPtr = KMCosePairSimpleValueTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ default:
+ break;
+ }
+ if (found) {
+ break;
+ }
+ index++;
+ }
+ return valPtr;
+ }
+
+ public short getKeyIdentifier() {
+ return getValueType(KMCose.COSE_KEY_KEY_ID, KMType.INVALID_VALUE);
+ }
+
+ public short getEcdsa256PublicKey(byte[] pubKey, short pubKeyOff) {
+ short baseOffset = pubKeyOff;
+ pubKey[pubKeyOff] = (byte) 0x04; // uncompressed.
+ pubKeyOff++;
+ short ptr = getValueType(KMCose.COSE_KEY_PUBKEY_X, KMType.INVALID_VALUE);
+ Util.arrayCopy(
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ pubKey,
+ pubKeyOff,
+ KMByteBlob.cast(ptr).length());
+ pubKeyOff += KMByteBlob.cast(ptr).length();
+ ptr = getValueType(KMCose.COSE_KEY_PUBKEY_Y, KMType.INVALID_VALUE);
+ Util.arrayCopy(
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ pubKey,
+ pubKeyOff,
+ KMByteBlob.cast(ptr).length());
+ pubKeyOff += KMByteBlob.cast(ptr).length();
+ return (short) (pubKeyOff - baseOffset);
+ }
+
+ public short getPrivateKey(byte[] priv, short privOff) {
+ short ptr = getValueType(KMCose.COSE_KEY_PRIV_KEY, KMType.INVALID_VALUE);
+ Util.arrayCopy(
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ priv,
+ privOff,
+ KMByteBlob.cast(ptr).length());
+ return KMByteBlob.cast(ptr).length();
+ }
+
+ public boolean isTestKey() {
+ short ptr =
+ getValueType(
+ Util.getShort(KMCose.COSE_TEST_KEY, (short) 2), // LSB
+ Util.getShort(KMCose.COSE_TEST_KEY, (short) 0) // MSB (Significant)
+ );
+ boolean isTestKey = false;
+ if (ptr != 0) {
+ isTestKey = (KMSimpleValue.cast(ptr).getValue() == KMSimpleValue.NULL);
+ }
+ return isTestKey;
+ }
+
+ /**
+ * Verifies the KMCoseKey values against the input values. Note that construction of the key_ops
+ * label is not needed to be supported. In the KeyMint2.0 specifications: The CoseKey inside
+ * MacedPublicKeys and DiceCertChain does not have key_ops label.
+ *
+ * @param keyType value of the key type
+ * @param keyIdPtr instance of KMByteBlob containing the key id.
+ * @param keyAlg value of the algorithm.
+ * @param keyOps value of the key operations.
+ * @param curve value of the curve.
+ * @return true if valid, otherwise false.
+ */
+ public boolean isDataValid(
+ short[] buff, short keyType, short keyIdPtr, short keyAlg, short curve) {
+ short buffLen = 8;
+ buff[0] = KMCose.COSE_KEY_KEY_TYPE;
+ buff[1] = keyType;
+ buff[2] = KMCose.COSE_KEY_KEY_ID;
+ buff[3] = keyIdPtr;
+ buff[4] = KMCose.COSE_KEY_ALGORITHM;
+ buff[5] = keyAlg;
+ buff[6] = KMCose.COSE_KEY_CURVE;
+ buff[7] = curve;
+ boolean valid = false;
+ short ptr;
+ short tagIndex = 0;
+ short value;
+ while (tagIndex < buffLen) {
+ value = buff[(short) (tagIndex + 1)];
+ if (value != KMType.INVALID_VALUE) {
+ valid = false;
+ ptr = getValueType(buff[tagIndex], KMType.INVALID_VALUE);
+ switch (KMType.getType(ptr)) {
+ case KMType.BYTE_BLOB_TYPE:
+ if ((KMByteBlob.cast(value).length() == KMByteBlob.cast(ptr).length())
+ && (0
+ == Util.arrayCompare(
+ KMByteBlob.cast(value).getBuffer(),
+ KMByteBlob.cast(value).getStartOff(),
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length()))) {
+ valid = true;
+ }
+ break;
+ case KMType.INTEGER_TYPE:
+ if (value == KMInteger.cast(ptr).getShort()) {
+ valid = true;
+ }
+ break;
+ case KMType.NEG_INTEGER_TYPE:
+ if ((byte) value == (byte) KMNInteger.cast(ptr).getShort()) {
+ valid = true;
+ }
+ break;
+ }
+ if (!valid) {
+ break;
+ }
+ }
+ tagIndex += 2;
+ }
+ return valid;
+ }
+
+ @Override
+ public void canonicalize() {
+ KMCoseMap.canonicalize(getVals());
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseMap.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseMap.java
new file mode 100644
index 0000000..5d373a7
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseMap.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright(C) 2021 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.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * This class represents either a Cose_key or Cose headers as defined in
+ * https://datatracker.ietf.org/doc/html/rfc8152 This is basically a map containing key value pairs.
+ * The label for the key can be (uint / int / tstr) and the value can be of any type. But this class
+ * is confined to support only key and value types which are required for remote key provisioning.
+ * So keys of type (int / uint) and values of type (int / uint / simple / bstr) only are supported.
+ * KMCoseHeaders and KMCoseKey implements this class.
+ */
+public abstract class KMCoseMap extends KMType {
+
+ public static byte[] scratchpad;
+
+ /**
+ * This function creates an instance of either KMCoseHeaders or KMCoseKey based on the type
+ * information provided.
+ *
+ * @param typePtr type information of the underlying KMType.
+ * @param arrPtr instance of KMArray.
+ * @return instance type of either KMCoseHeaders or KMCoseKey.
+ */
+ public static short createInstanceFromType(short typePtr, short arrPtr) {
+ short mapType = KMType.getType(typePtr);
+ switch (mapType) {
+ case KMType.COSE_HEADERS_TYPE:
+ return KMCoseHeaders.instance(arrPtr);
+ case KMType.COSE_KEY_TYPE:
+ return KMCoseKey.instance(arrPtr);
+ case KMType.COSE_CERT_PAYLOAD_TYPE:
+ return KMCoseCertPayload.instance(arrPtr);
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ return 0;
+ }
+ }
+
+ public static short getVals(short ptr) {
+ short mapType = KMType.getType(ptr);
+ switch (mapType) {
+ case KMType.COSE_HEADERS_TYPE:
+ return KMCoseHeaders.cast(ptr).getVals();
+ case KMType.COSE_KEY_TYPE:
+ return KMCoseKey.cast(ptr).getVals();
+ case KMType.COSE_CERT_PAYLOAD_TYPE:
+ return KMCoseCertPayload.cast(ptr).getVals();
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ return 0;
+ }
+ }
+
+ private static short getKey(short tagPtr) {
+ short tagType = KMCosePairTagType.getTagValueType(tagPtr);
+ switch (tagType) {
+ case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+ return KMCosePairByteBlobTag.cast(tagPtr).getKeyPtr();
+ case KMType.COSE_PAIR_INT_TAG_TYPE:
+ return KMCosePairIntegerTag.cast(tagPtr).getKeyPtr();
+ case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+ return KMCosePairNegIntegerTag.cast(tagPtr).getKeyPtr();
+ case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE:
+ return KMCosePairSimpleValueTag.cast(tagPtr).getKeyPtr();
+ case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE:
+ return KMCosePairCoseKeyTag.cast(tagPtr).getKeyPtr();
+ case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE:
+ return KMCosePairTextStringTag.cast(tagPtr).getKeyPtr();
+ default:
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return 0;
+ }
+
+ private static void createScratchBuffer() {
+ if (scratchpad == null) {
+ scratchpad = JCSystem.makeTransientByteArray((short) 120, JCSystem.CLEAR_ON_RESET);
+ }
+ }
+
+ protected static void canonicalize(short arr) {
+ canonicalize(arr, KMArray.cast(arr).length());
+ }
+
+ private static void swap(short ptr, short firstIndex, short secondIndex) {
+ if (KMType.getType(ptr) == KMType.ARRAY_TYPE) {
+ KMArray.cast(ptr).swap(firstIndex, secondIndex);
+ } else {
+ KMMap.cast(ptr).swap(firstIndex, secondIndex);
+ }
+ }
+
+ private static boolean compareAndSwap(short ptr, short index) {
+ short firstKey;
+ short secondKey;
+ short firstKeyLen;
+ short secondKeyLen;
+ if (KMType.getType(ptr) == KMType.ARRAY_TYPE) {
+ firstKey = getKey(KMArray.cast(ptr).get(index));
+ secondKey = getKey(KMArray.cast(ptr).get((short) (index + 1)));
+ } else { // Map
+ firstKey = KMMap.cast(ptr).getKey(index);
+ secondKey = KMMap.cast(ptr).getKey((short) (index + 1));
+ }
+ firstKeyLen =
+ KMKeymasterApplet.encoder.encode(
+ firstKey, scratchpad, (short) 0, (short) scratchpad.length);
+ secondKeyLen =
+ KMKeymasterApplet.encoder.encode(
+ secondKey, scratchpad, firstKeyLen, (short) scratchpad.length);
+ if ((firstKeyLen > secondKeyLen)
+ || ((firstKeyLen == secondKeyLen)
+ && (0
+ < Util.arrayCompare(
+ scratchpad, (short) 0, scratchpad, firstKeyLen, firstKeyLen)))) {
+ swap(ptr, index, (short) (index + 1));
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Canonicalizes using bubble sort.
+ *
+ * @param ptr instance pointer of either array or map.
+ * @param length length of the array or map instance.
+ */
+ public static void canonicalize(short ptr, short length) {
+ short index = 0;
+ short innerIndex = 0;
+ createScratchBuffer();
+ boolean swapped;
+ while (index < length) {
+ swapped = false;
+ innerIndex = 0;
+ while (innerIndex < (short) (length - index - 1)) {
+ swapped |= compareAndSwap(ptr, innerIndex);
+ innerIndex++;
+ }
+ if (!swapped) {
+ break;
+ }
+ index++;
+ }
+ }
+
+ public abstract short getVals();
+
+ public abstract short length();
+
+ public abstract void canonicalize();
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java
new file mode 100644
index 0000000..04c3abe
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairByteBlobTag represents a key-value type, where key can be KMInteger or KMNInteger and
+ * value is KMByteBlob type. struct{byte TAG_TYPE; short length; struct{short BYTE_BLOB_TYPE; short
+ * key; short value}}.
+ */
+public class KMCosePairByteBlobTag extends KMCosePairTagType {
+
+ public static Object[] keys;
+ private static KMCosePairByteBlobTag prototype;
+
+ private KMCosePairByteBlobTag() {}
+
+ private static KMCosePairByteBlobTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCosePairByteBlobTag();
+ }
+ instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMByteBlob.exp());
+ return ptr;
+ }
+
+ public static short instance(short keyPtr, short valuePtr) {
+ if (!isKeyValueValid(keyPtr)) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (KMType.getType(valuePtr) != BYTE_BLOB_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+ return ptr;
+ }
+
+ public static KMCosePairByteBlobTag cast(short ptr) {
+ byte[] heap = repository.getHeap();
+ if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ // Validate the value pointer.
+ short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+ if (KMType.getType(valuePtr) != BYTE_BLOB_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ private static void createKeys() {
+ if (keys == null) {
+ keys =
+ new Object[] {
+ (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PUBKEY_X},
+ (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PUBKEY_Y},
+ (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PRIV_KEY},
+ (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_LABEL_IV},
+ (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_LABEL_KEYID},
+ (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_KEY_ID},
+ (Object) KMCose.SUBJECT_PUBLIC_KEY,
+ (Object) KMCose.KEY_USAGE
+ };
+ }
+ }
+
+ public static boolean isKeyValueValid(short keyPtr) {
+ createKeys();
+ short type = KMType.getType(keyPtr);
+ short offset = 0;
+ if (type == INTEGER_TYPE) {
+ offset = KMInteger.cast(keyPtr).getStartOff();
+ } else if (type == NEG_INTEGER_TYPE) {
+ offset = KMNInteger.cast(keyPtr).getStartOff();
+ } else {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short index = 0;
+ while (index < (short) keys.length) {
+ if (0
+ == Util.arrayCompare(
+ (byte[]) keys[index],
+ (short) 0,
+ heap,
+ offset,
+ (short) ((byte[]) keys[index]).length)) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ public short getValueType() {
+ return BYTE_BLOB_TYPE;
+ }
+
+ @Override
+ public short getKeyPtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ @Override
+ public short getValuePtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java
new file mode 100644
index 0000000..5290da2
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java
@@ -0,0 +1,89 @@
+package com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairCoseKeyTag represents a key-value type, where key can be KMInteger or KMNInteger and
+ * value is KMCoseKey type. struct{byte TAG_TYPE; short length; struct{short COSE_KEY_VALUE_TYPE;
+ * short key; short value}}.
+ */
+public class KMCosePairCoseKeyTag extends KMCosePairTagType {
+
+ public static final byte[] keys = {KMCose.COSE_LABEL_COSE_KEY};
+ private static KMCosePairCoseKeyTag prototype;
+
+ private KMCosePairCoseKeyTag() {}
+
+ private static KMCosePairCoseKeyTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCosePairCoseKeyTag();
+ }
+ instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_COSE_KEY_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMCoseKey.exp());
+ return ptr;
+ }
+
+ public static short instance(short keyPtr, short valuePtr) {
+ if (!isKeyValueValid(KMCosePairTagType.getKeyValueShort(keyPtr))) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (KMType.getType(valuePtr) != COSE_KEY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_COSE_KEY_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+ return ptr;
+ }
+
+ public static KMCosePairCoseKeyTag cast(short ptr) {
+ byte[] heap = repository.getHeap();
+ if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ // Validate the value pointer.
+ short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+ if (KMType.getType(valuePtr) != COSE_KEY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static boolean isKeyValueValid(short keyVal) {
+ short index = 0;
+ while (index < (short) keys.length) {
+ if ((byte) (keyVal & 0xFF) == keys[index]) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ public short getValueType() {
+ return COSE_KEY_TYPE;
+ }
+
+ @Override
+ public short getKeyPtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ @Override
+ public short getValuePtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java
new file mode 100644
index 0000000..ea052a6
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairIntegerTag represents a key-value type, where key can be KMInteger or KMNInteger and
+ * value is KMInteger type. struct{byte TAG_TYPE; short length; struct{short INT_VALUE_TYPE; short
+ * key; short value}}.
+ */
+public class KMCosePairIntegerTag extends KMCosePairTagType {
+
+ private static KMCosePairIntegerTag prototype;
+
+ private KMCosePairIntegerTag() {}
+
+ private static KMCosePairIntegerTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCosePairIntegerTag();
+ }
+ instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_INT_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMInteger.exp());
+ return ptr;
+ }
+
+ public static short instance(short keyPtr, short valuePtr) {
+ short offset = KMCosePairTagType.getKeyStartOffset(keyPtr);
+ if (!KMCosePairTagType.isKeyPairValid(
+ heap, offset, KMCose.COSE_KEY_MAX_SIZE, KMInteger.cast(valuePtr).getShort())) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_INT_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+ return ptr;
+ }
+
+ public static KMCosePairIntegerTag cast(short ptr) {
+ byte[] heap = repository.getHeap();
+ if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ // Validate the value ptr.
+ short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+ if (INTEGER_TYPE != getType(valuePtr)) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public short getValueType() {
+ return INTEGER_TYPE;
+ }
+
+ @Override
+ public short getKeyPtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ @Override
+ public short getValuePtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java
new file mode 100644
index 0000000..7f01202
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairNegIntegerTag represents a key-value type, where key can be KMInteger or KMNInteger and
+ * value is KMNInteger type. struct{byte TAG_TYPE; short length; struct{short NINT_VALUE_TYPE; short
+ * key; short value}}.
+ */
+public class KMCosePairNegIntegerTag extends KMCosePairTagType {
+
+ private static KMCosePairNegIntegerTag prototype;
+
+ private KMCosePairNegIntegerTag() {}
+
+ private static KMCosePairNegIntegerTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCosePairNegIntegerTag();
+ }
+ instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_NEG_INT_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMNInteger.exp());
+ return ptr;
+ }
+
+ public static KMCosePairNegIntegerTag cast(short ptr) {
+ byte[] heap = repository.getHeap();
+ if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ // Validate the value ptr.
+ short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+ if (NEG_INTEGER_TYPE != getType(valuePtr)) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static short instance(short keyPtr, short valuePtr) {
+ short offset = KMCosePairTagType.getKeyStartOffset(keyPtr);
+ if (!KMCosePairTagType.isKeyPairValid(
+ heap, offset, KMCose.COSE_KEY_MAX_SIZE, KMNInteger.cast(valuePtr).getShort())) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_NEG_INT_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+ return ptr;
+ }
+
+ public short getValueType() {
+ return NEG_INTEGER_TYPE;
+ }
+
+ @Override
+ public short getKeyPtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ @Override
+ public short getValuePtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java
new file mode 100644
index 0000000..a0d7da8
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java
@@ -0,0 +1,76 @@
+package com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairSimpleValueTag represents a key-value type, where key can be KMInteger or KMNInteger
+ * and value is KMSimpleValue type. struct{byte TAG_TYPE; short length; struct{short
+ * SIMPLE_VALUE_TYPE; short key; short value}}.
+ */
+public class KMCosePairSimpleValueTag extends KMCosePairTagType {
+
+ private static KMCosePairSimpleValueTag prototype;
+
+ private KMCosePairSimpleValueTag() {}
+
+ private static KMCosePairSimpleValueTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCosePairSimpleValueTag();
+ }
+ instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMSimpleValue.exp());
+ return ptr;
+ }
+
+ public static short instance(short keyPtr, short valuePtr) {
+ short offset = KMCosePairTagType.getKeyStartOffset(keyPtr);
+ if (!KMCosePairTagType.isKeyPairValid(
+ heap, offset, KMCose.COSE_KEY_MAX_SIZE, KMSimpleValue.cast(valuePtr).getValue())) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+ return ptr;
+ }
+
+ public static KMCosePairSimpleValueTag cast(short ptr) {
+ byte[] heap = repository.getHeap();
+ if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ // Validate the value pointer.
+ short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+ if (KMType.getType(valuePtr) != SIMPLE_VALUE_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public short getValueType() {
+ return SIMPLE_VALUE_TYPE;
+ }
+
+ @Override
+ public short getKeyPtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ @Override
+ public short getValuePtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java
new file mode 100644
index 0000000..baa0855
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * This class represents the a key-value types. This is basically a map containing key value pairs.
+ * The label for the key can be (uint / int / tstr) and the value can be of any type. But this class
+ * is confined to support only key and value types which are required for remote key provisioning.
+ * So keys of type (int / uint) and values of type (int / uint / simple / bstr) only are supported.
+ * The structure representing all the sub classes of KMCosePairTagType is as follows:
+ * KM_COSE_PAIR_TAG_TYPE(1byte), Length(2 bytes), COSE_PAIR_*_TAG_TYPE(2 bytes), Key(2 bytes),
+ * Value(2 bytes). Key can be either KMInteger or KMNInteger and Value can be either KMIntger or
+ * KMNinteger or KMSimpleValue or KMByteBlob or KMTextString or KMCoseKey. Each subclass of
+ * KMCosePairTagType is named after their corresponding value type of the Cose pair.
+ */
+public abstract class KMCosePairTagType extends KMType {
+
+ /**
+ * Below table represents the allowed values for a key. The maximum length of the key can be 4
+ * bytes so each key is represented as 4 bytes. The allowed values are placed next to their
+ * corresponding key.
+ */
+ public static Object[] allowedKeyPairs;
+
+ private static void createAllowedKeyPairs() {
+ if (allowedKeyPairs == null) {
+ allowedKeyPairs =
+ new Object[] {
+ // Key type
+ (Object) new byte[] {0, 0, 0, KMCose.COSE_KEY_KEY_TYPE},
+ (Object) new byte[] {KMCose.COSE_KEY_TYPE_EC2, KMCose.COSE_KEY_TYPE_SYMMETRIC_KEY},
+ // Key Algorithm
+ (Object) new byte[] {0, 0, 0, KMCose.COSE_KEY_ALGORITHM},
+ (Object)
+ new byte[] {
+ KMCose.COSE_ALG_AES_GCM_256,
+ KMCose.COSE_ALG_HMAC_256,
+ KMCose.COSE_ALG_ECDH_ES_HKDF_256,
+ KMCose.COSE_ALG_ES256
+ },
+ // Key Curve
+ (Object) new byte[] {0, 0, 0, KMCose.COSE_KEY_CURVE},
+ (Object) new byte[] {KMCose.COSE_ECCURVE_256},
+ // Header Label Algorithm
+ (Object) new byte[] {0, 0, 0, KMCose.COSE_LABEL_ALGORITHM},
+ (Object)
+ new byte[] {
+ KMCose.COSE_ALG_AES_GCM_256,
+ KMCose.COSE_ALG_HMAC_256,
+ KMCose.COSE_ALG_ES256,
+ KMCose.COSE_ALG_ECDH_ES_HKDF_256
+ },
+ // Test Key
+ KMCose.COSE_TEST_KEY,
+ (Object) new byte[] {KMSimpleValue.NULL},
+ };
+ }
+ }
+
+ /**
+ * Validates the key and the values corresponding to key.
+ *
+ * @param key Buffer containing the key.
+ * @param keyOff Offset in the buffer from where key starts.
+ * @param keyLen Length of the key buffer.
+ * @param value Value corresponding to the key.
+ * @return true if key pair is valid, otherwise false.
+ */
+ public static boolean isKeyPairValid(byte[] key, short keyOff, short keyLen, short value) {
+ short index = 0;
+ short valueIdx;
+ byte[] values;
+ boolean valid = false;
+ createAllowedKeyPairs();
+ while (index < allowedKeyPairs.length) {
+ valueIdx = 0;
+ if (isEqual(
+ (byte[]) allowedKeyPairs[index],
+ (short) 0,
+ (short) ((byte[]) allowedKeyPairs[index]).length,
+ key,
+ keyOff,
+ keyLen)) {
+ values = (byte[]) allowedKeyPairs[(short) (index + 1)];
+ while (valueIdx < values.length) {
+ if (values[valueIdx] == (byte) value) {
+ valid = true;
+ break;
+ }
+ valueIdx++;
+ }
+ if (valid) {
+ break;
+ }
+ }
+ index += (short) 2;
+ }
+ return valid;
+ }
+
+ /**
+ * Compares two key buffers.
+ *
+ * @param key1 First buffer containing the key.
+ * @param offset1 Offset of the first buffer.
+ * @param length1 Length of the first buffer.
+ * @param key2 Second buffer containing the key.
+ * @param offset2 Offset of the second buffer.
+ * @param length2 Length of the second buffer.
+ * @return true if both keys are equal, otherwise false.
+ */
+ private static boolean isEqual(
+ byte[] key1, short offset1, short length1, byte[] key2, short offset2, short length2) {
+ if (length1 != length2) {
+ return false;
+ }
+ return (0 == KMInteger.unsignedByteArrayCompare(key1, offset1, key2, offset2, length1));
+ }
+
+ /**
+ * Returns the short value of the key.
+ *
+ * @param keyPtr Pointer to either KMInteger or KMNInteger
+ * @return value of the key as short.
+ */
+ public static short getKeyValueShort(short keyPtr) {
+ short type = KMType.getType(keyPtr);
+ short value = 0;
+ if (type == INTEGER_TYPE) {
+ value = KMInteger.cast(keyPtr).getShort();
+ } else if (type == NEG_INTEGER_TYPE) {
+ value = KMNInteger.cast(keyPtr).getShort();
+ } else {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return value;
+ }
+
+ /**
+ * Returns the significant short value of the key.
+ *
+ * @param keyPtr Pointer to either KMInteger or KMNInteger
+ * @return value of the key as short.
+ */
+ public static short getKeyValueSignificantShort(short keyPtr) {
+ short type = KMType.getType(keyPtr);
+ short value = 0;
+ if (type == INTEGER_TYPE) {
+ value = KMInteger.cast(keyPtr).getSignificantShort();
+ } else if (type == NEG_INTEGER_TYPE) {
+ value = KMNInteger.cast(keyPtr).getSignificantShort();
+ } else {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return value;
+ }
+
+ public static void getKeyValue(short keyPtr, byte[] dest, short offset, short len) {
+ short type = KMType.getType(keyPtr);
+ if (type == INTEGER_TYPE) {
+ KMInteger.cast(keyPtr).getValue(dest, offset, len);
+ } else if (type == NEG_INTEGER_TYPE) {
+ KMNInteger.cast(keyPtr).getValue(dest, offset, len);
+ } else {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ }
+
+ /**
+ * Returns the key offset from the key pointer.
+ *
+ * @param keyPtr Pointer to either KMInteger or KMNInteger
+ * @return offset from where the key starts.
+ */
+ public static short getKeyStartOffset(short keyPtr) {
+ short type = KMType.getType(keyPtr);
+ short offset = 0;
+ if (type == INTEGER_TYPE) {
+ offset = KMInteger.cast(keyPtr).getStartOff();
+ } else if (type == NEG_INTEGER_TYPE) {
+ offset = KMNInteger.cast(keyPtr).getStartOff();
+ } else {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return offset;
+ }
+
+ /**
+ * Returns the key length.
+ *
+ * @param keyPtr pointer to either KMInteger/KMInteger.
+ * @return length of the key.
+ */
+ public static short getKeyLength(short keyPtr) {
+ short type = KMType.getType(keyPtr);
+ short len = 0;
+ if (type == INTEGER_TYPE) {
+ len = KMInteger.cast(keyPtr).length();
+ } else if (type == NEG_INTEGER_TYPE) {
+ len = KMNInteger.cast(keyPtr).length();
+ } else {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return len;
+ }
+
+ /**
+ * This function returns one of COSE_KEY_TAG_*_VALUE_TYPE tag information.
+ *
+ * @param ptr Pointer to one of the KMCoseKey*Value class.
+ * @return Tag value type.
+ */
+ public static short getTagValueType(short ptr) {
+ return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ }
+
+ /**
+ * This function returns the key pointer.
+ *
+ * @return key pointer.
+ */
+ public abstract short getKeyPtr();
+
+ /**
+ * This function returns the value pointer.
+ *
+ * @return value pointer.
+ */
+ public abstract short getValuePtr();
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java
new file mode 100644
index 0000000..99506b6
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java
@@ -0,0 +1,91 @@
+package com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairTextStringTag represents a key-value type, where key can be KMInteger or KMNInteger and
+ * value is KMTextString type. struct{byte TAG_TYPE; short length; struct{short TXT_STR_VALUE_TYPE;
+ * short key; short value}}.
+ */
+public class KMCosePairTextStringTag extends KMCosePairTagType {
+
+ public static final byte[] keys = {
+ KMCose.ISSUER, KMCose.SUBJECT,
+ };
+ private static KMCosePairTextStringTag prototype;
+
+ private KMCosePairTextStringTag() {}
+
+ private static KMCosePairTextStringTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCosePairTextStringTag();
+ }
+ instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_TEXT_STR_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMTextString.exp());
+ return ptr;
+ }
+
+ public static short instance(short keyPtr, short valuePtr) {
+ if (!isKeyValueValid(KMCosePairTagType.getKeyValueShort(keyPtr))) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (KMType.getType(valuePtr) != TEXT_STRING_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_TEXT_STR_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+ return ptr;
+ }
+
+ public static KMCosePairTextStringTag cast(short ptr) {
+ byte[] heap = repository.getHeap();
+ if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ // Validate the value pointer.
+ short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+ if (KMType.getType(valuePtr) != TEXT_STRING_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static boolean isKeyValueValid(short keyVal) {
+ short index = 0;
+ while (index < (short) keys.length) {
+ if ((byte) (keyVal & 0xFF) == keys[index]) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ public short getValueType() {
+ return TEXT_STRING_TYPE;
+ }
+
+ @Override
+ public short getKeyPtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ @Override
+ public short getValuePtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMDecoder.java
new file mode 100644
index 0000000..dd05b13
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMDecoder.java
@@ -0,0 +1,781 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * This class decodes the CBOR format data into a KMType structure. It interprets the input CBOR
+ * format using the input expression provided. Validation of KeyMint tags and tag types happens in
+ * the process of decoding, while constructing the subtype of a KMType structure.
+ */
+public class KMDecoder {
+
+ // major types
+ private static final short UINT_TYPE = 0x00;
+ private static final short NEG_INT_TYPE = 0x20;
+ private static final short BYTES_TYPE = 0x40;
+ private static final short TSTR_TYPE = 0x60;
+ private static final short ARRAY_TYPE = 0x80;
+ private static final short MAP_TYPE = 0xA0;
+ private static final short SIMPLE_VALUE_TYPE = 0xE0;
+ private static final short SEMANTIC_TAG_TYPE = 0xC0;
+
+ // masks
+ private static final short ADDITIONAL_MASK = 0x1F;
+ private static final short MAJOR_TYPE_MASK = 0xE0;
+
+ // value length
+ private static final short UINT8_LENGTH = 0x18;
+ private static final short UINT16_LENGTH = 0x19;
+ private static final short UINT32_LENGTH = 0x1A;
+ private static final short UINT64_LENGTH = 0x1B;
+
+ private static final byte SCRATCH_BUF_SIZE = 6;
+ private static final byte START_OFFSET = 0;
+ private static final byte LEN_OFFSET = 2;
+ private static final byte TAG_KEY_OFFSET = 4;
+ private Object[] bufferRef;
+ private short[] scratchBuf;
+
+ public KMDecoder() {
+ bufferRef = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET);
+ scratchBuf = JCSystem.makeTransientShortArray(SCRATCH_BUF_SIZE, JCSystem.CLEAR_ON_RESET);
+ bufferRef[0] = null;
+ scratchBuf[START_OFFSET] = (short) 0;
+ scratchBuf[LEN_OFFSET] = (short) 0;
+ scratchBuf[TAG_KEY_OFFSET] = (short) 0;
+ }
+
+ public short decode(short expression, byte[] buffer, short startOff, short length) {
+ bufferRef[0] = buffer;
+ scratchBuf[START_OFFSET] = startOff;
+ scratchBuf[LEN_OFFSET] = (short) (startOff + length);
+ return decode(expression);
+ }
+
+ public short decodeArray(short exp, byte[] buffer, short startOff, short length) {
+ bufferRef[0] = buffer;
+ scratchBuf[START_OFFSET] = startOff;
+ scratchBuf[LEN_OFFSET] = (short) (startOff + length);
+ short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE);
+ short expLength = KMArray.cast(exp).length();
+ if (payloadLength > expLength) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ short index = 0;
+ short obj;
+ short type;
+ short arrPtr = KMArray.instance(payloadLength);
+ while (index < payloadLength) {
+ type = KMArray.cast(exp).get(index);
+ obj = decode(type);
+ KMArray.cast(arrPtr).add(index, obj);
+ index++;
+ }
+ return arrPtr;
+ }
+
+ private short decode(short exp) {
+ byte type = KMType.getType(exp);
+ switch (type) {
+ case KMType.BYTE_BLOB_TYPE:
+ return decodeByteBlob(exp);
+ case KMType.TEXT_STRING_TYPE:
+ return decodeTstr(exp);
+ case KMType.INTEGER_TYPE:
+ return decodeInteger(exp);
+ case KMType.SIMPLE_VALUE_TYPE:
+ return decodeSimpleValue(exp);
+ case KMType.SEMANTIC_TAG_TYPE:
+ return decodeSemanticTagValue(exp);
+ case KMType.NEG_INTEGER_TYPE:
+ return decodeNegInteger(exp);
+ case KMType.ARRAY_TYPE:
+ return decodeArray(exp);
+ case KMType.MAP_TYPE:
+ return decodeMap(exp);
+ case KMType.ENUM_TYPE:
+ return decodeEnum(exp);
+ case KMType.KEY_PARAM_TYPE:
+ return decodeKeyParam(exp);
+ case KMType.KEY_CHAR_TYPE:
+ return decodeKeyChar(exp);
+ case KMType.VERIFICATION_TOKEN_TYPE:
+ return decodeVerificationToken(exp);
+ case KMType.HMAC_SHARING_PARAM_TYPE:
+ return decodeHmacSharingParam(exp);
+ case KMType.HW_AUTH_TOKEN_TYPE:
+ return decodeHwAuthToken(exp);
+ case KMType.COSE_KEY_TYPE:
+ case KMType.COSE_HEADERS_TYPE:
+ case KMType.COSE_CERT_PAYLOAD_TYPE:
+ return decodeCoseMap(exp);
+ case KMType.COSE_PAIR_TAG_TYPE:
+ short tagValueType = KMCosePairTagType.getTagValueType(exp);
+ return decodeCosePairTag(tagValueType, exp);
+ case KMType.TAG_TYPE:
+ short tagType = KMTag.getTagType(exp);
+ return decodeTag(tagType, exp);
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ return 0;
+ }
+ }
+
+ private short decodeTag(short tagType, short exp) {
+ switch (tagType) {
+ case KMType.BIGNUM_TAG:
+ return decodeBignumTag(exp);
+ case KMType.BYTES_TAG:
+ return decodeBytesTag(exp);
+ case KMType.BOOL_TAG:
+ return decodeBoolTag(exp);
+ case KMType.UINT_TAG:
+ case KMType.ULONG_TAG:
+ case KMType.DATE_TAG:
+ return decodeIntegerTag(exp);
+ case KMType.ULONG_ARRAY_TAG:
+ case KMType.UINT_ARRAY_TAG:
+ return decodeIntegerArrayTag(exp);
+ case KMType.ENUM_TAG:
+ return decodeEnumTag(exp);
+ case KMType.ENUM_ARRAY_TAG:
+ return decodeEnumArrayTag(exp);
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ return 0;
+ }
+ }
+
+ private short decodeVerificationToken(short exp) {
+ short vals = decode(KMVerificationToken.cast(exp).getVals());
+ return KMVerificationToken.instance(vals);
+ }
+
+ private short decodeHwAuthToken(short exp) {
+ short vals = decode(KMHardwareAuthToken.cast(exp).getVals());
+ return KMHardwareAuthToken.instance(vals);
+ }
+
+ private short decodeHmacSharingParam(short exp) {
+ short vals = decode(KMHmacSharingParameters.cast(exp).getVals());
+ return KMHmacSharingParameters.instance(vals);
+ }
+
+ private short decodeKeyChar(short exp) {
+ short vals = decode(KMKeyCharacteristics.cast(exp).getVals());
+ return KMKeyCharacteristics.instance(vals);
+ }
+
+ private short decodeCosePairKey(short exp) {
+ byte[] buffer = (byte[]) bufferRef[0];
+ short startOff = scratchBuf[START_OFFSET];
+ short keyPtr = (short) 0;
+ // Cose Key should be always either UINT or Negative int
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) == UINT_TYPE) {
+ keyPtr = decodeInteger(exp);
+ } else if ((buffer[startOff] & MAJOR_TYPE_MASK) == NEG_INT_TYPE) {
+ keyPtr = decodeNegInteger(exp);
+ } else {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ return keyPtr;
+ }
+
+ private short decodeCosePairSimpleValueTag(short exp) {
+ short keyPtr = decodeCosePairKey((KMCosePairSimpleValueTag.cast(exp).getKeyPtr()));
+ short valuePtr = decode(KMCosePairSimpleValueTag.cast(exp).getValuePtr());
+ return KMCosePairSimpleValueTag.instance(keyPtr, valuePtr);
+ }
+
+ private short decodeCosePairIntegerValueTag(short exp) {
+ short keyPtr = decodeCosePairKey((KMCosePairIntegerTag.cast(exp).getKeyPtr()));
+ short valuePtr = decode(KMCosePairIntegerTag.cast(exp).getValuePtr());
+ return KMCosePairIntegerTag.instance(keyPtr, valuePtr);
+ }
+
+ private short decodeCosePairNegIntegerTag(short exp) {
+ short keyPtr = decodeCosePairKey((KMCosePairNegIntegerTag.cast(exp).getKeyPtr()));
+ short valuePtr = decode(KMCosePairNegIntegerTag.cast(exp).getValuePtr());
+ return KMCosePairNegIntegerTag.instance(keyPtr, valuePtr);
+ }
+
+ private short decodeCosePairTxtStringTag(short exp) {
+ short keyPtr = decodeCosePairKey((KMCosePairTextStringTag.cast(exp).getKeyPtr()));
+ short valuePtr = decode(KMCosePairTextStringTag.cast(exp).getValuePtr());
+ return KMCosePairTextStringTag.instance(keyPtr, valuePtr);
+ }
+
+ private short decodeCosePairCoseKeyTag(short exp) {
+ short keyPtr = decodeCosePairKey((KMCosePairCoseKeyTag.cast(exp).getKeyPtr()));
+ short valuePtr = decode(KMCosePairCoseKeyTag.cast(exp).getValuePtr());
+ return KMCosePairCoseKeyTag.instance(keyPtr, valuePtr);
+ }
+
+ private short decodeCosePairByteBlobTag(short exp) {
+ short keyPtr = decodeCosePairKey((KMCosePairByteBlobTag.cast(exp).getKeyPtr()));
+ short valuePtr = decode(KMCosePairByteBlobTag.cast(exp).getValuePtr());
+ return KMCosePairByteBlobTag.instance(keyPtr, valuePtr);
+ }
+
+ private short peekCosePairTagType() {
+ byte[] buffer = (byte[]) bufferRef[0];
+ short startOff = scratchBuf[START_OFFSET];
+ // This decoder is confined to support only key and value types which are required for remote
+ // key provisioning. So keys of type (int / uint) and values of type (int / uint / simple / bstr
+ // / tstr / Cosekey) only are supported.
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE
+ && (buffer[startOff] & MAJOR_TYPE_MASK) != NEG_INT_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+
+ short additionalMask = (short) (buffer[startOff] & ADDITIONAL_MASK);
+ short increment = 0;
+ if (additionalMask < UINT8_LENGTH) {
+ increment++;
+ } else if (additionalMask == UINT8_LENGTH) {
+ increment += 2;
+ } else if (additionalMask == UINT16_LENGTH) {
+ increment += 3;
+ } else if (additionalMask == UINT32_LENGTH) {
+ increment += 5;
+ } else {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short majorType = (short) (buffer[(short) (startOff + increment)] & MAJOR_TYPE_MASK);
+ short tagValueType = 0;
+ if (majorType == BYTES_TYPE) {
+ tagValueType = KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE;
+ } else if (majorType == UINT_TYPE) {
+ tagValueType = KMType.COSE_PAIR_INT_TAG_TYPE;
+ } else if (majorType == NEG_INT_TYPE) {
+ tagValueType = KMType.COSE_PAIR_NEG_INT_TAG_TYPE;
+ } else if (majorType == MAP_TYPE) {
+ tagValueType = KMType.COSE_PAIR_COSE_KEY_TAG_TYPE;
+ } else if (majorType == SIMPLE_VALUE_TYPE) {
+ tagValueType = KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE;
+ } else if (majorType == TSTR_TYPE) {
+ tagValueType = KMType.COSE_PAIR_TEXT_STR_TAG_TYPE;
+ } else {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ return tagValueType;
+ }
+
+ private short decodeCosePairTag(short tagValueType, short exp) {
+ switch (tagValueType) {
+ case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+ return decodeCosePairByteBlobTag(exp);
+ case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+ return decodeCosePairNegIntegerTag(exp);
+ case KMType.COSE_PAIR_INT_TAG_TYPE:
+ return decodeCosePairIntegerValueTag(exp);
+ case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE:
+ return decodeCosePairSimpleValueTag(exp);
+ case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE:
+ return decodeCosePairCoseKeyTag(exp);
+ case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE:
+ return decodeCosePairTxtStringTag(exp);
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ return 0;
+ }
+ }
+
+ private short decodeCoseMap(short exp) {
+ short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE);
+ // get allowed key pairs
+ short allowedKeyPairs = KMCoseMap.getVals(exp);
+ short vals = KMArray.instance(payloadLength);
+ short length = KMArray.cast(allowedKeyPairs).length();
+ short index = 0;
+ boolean tagFound;
+ short tagInd;
+ short cosePairTagType;
+ short tagClass;
+ short allowedType;
+ short obj;
+
+ // For each tag in payload ...
+ while (index < payloadLength) {
+ tagFound = false;
+ tagInd = 0;
+ cosePairTagType = peekCosePairTagType();
+ // Check against the allowed tags ...
+ while (tagInd < length) {
+ tagClass = KMArray.cast(allowedKeyPairs).get(tagInd);
+ allowedType = KMCosePairTagType.getTagValueType(tagClass);
+ if (allowedType == cosePairTagType) {
+ obj = decode(tagClass);
+ KMArray.cast(vals).add(index, obj);
+ tagFound = true;
+ break;
+ }
+ tagInd++;
+ }
+ if (!tagFound) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ } else {
+ index++;
+ }
+ }
+ return KMCoseMap.createInstanceFromType(exp, vals);
+ }
+
+ private short decodeKeyParam(short exp) {
+ short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE);
+ // allowed tags
+ short allowedTags = KMKeyParameters.cast(exp).getVals();
+ short tagRule = KMArray.cast(allowedTags).get((short) 0);
+ boolean ignoreInvalidTags = KMEnum.cast(tagRule).getVal() == KMType.IGNORE_INVALID_TAGS;
+ short vals = KMArray.instance(payloadLength);
+ short length = KMArray.cast(allowedTags).length();
+ short index = 0;
+ boolean tagFound;
+ short tagInd;
+ short tagType;
+ short tagClass;
+ short allowedType;
+ short obj;
+ short arrPos = 0;
+ // For each tag in payload ...
+ while (index < payloadLength) {
+ tagFound = false;
+ tagInd = 1;
+ tagType = peekTagType();
+ // Check against the allowed tags ...
+ while (tagInd < length) {
+ tagClass = KMArray.cast(allowedTags).get(tagInd);
+ allowedType = KMTag.getTagType(tagClass);
+ // If it is part of allowed tags ...
+ if (tagType == allowedType) {
+ // then decodeByteBlob and add that to the array.
+ try {
+ tagFound = true;
+ obj = decode(tagClass);
+ KMArray.cast(vals).add(arrPos++, obj);
+ break;
+ } catch (KMException e) {
+ if (KMException.reason() == KMError.INVALID_TAG) {
+ if (!ignoreInvalidTags) {
+ KMException.throwIt(KMError.INVALID_TAG);
+ }
+ } else {
+ KMException.throwIt(KMException.reason());
+ }
+ break;
+ }
+ }
+ tagInd++;
+ }
+ if (!tagFound) {
+ KMException.throwIt(KMError.INVALID_TAG);
+ } else {
+ index++;
+ }
+ }
+ KMArray.cast(vals).setLength(arrPos);
+ return KMKeyParameters.instance(vals);
+ }
+
+ private short decodeEnumArrayTag(short exp) {
+ readTagKey(KMEnumArrayTag.cast(exp).getTagType());
+ return KMEnumArrayTag.instance(
+ scratchBuf[TAG_KEY_OFFSET], decode(KMEnumArrayTag.cast(exp).getValues()));
+ }
+
+ private short decodeIntegerArrayTag(short exp) {
+ readTagKey(KMIntegerArrayTag.cast(exp).getTagType());
+ // the values are array of integers.
+ return KMIntegerArrayTag.instance(
+ KMIntegerArrayTag.cast(exp).getTagType(),
+ scratchBuf[TAG_KEY_OFFSET],
+ decode(KMIntegerArrayTag.cast(exp).getValues()));
+ }
+
+ private short decodeIntegerTag(short exp) {
+ readTagKey(KMIntegerTag.cast(exp).getTagType());
+ // the value is an integer
+ return KMIntegerTag.instance(
+ KMIntegerTag.cast(exp).getTagType(),
+ scratchBuf[TAG_KEY_OFFSET],
+ decode(KMIntegerTag.cast(exp).getValue()));
+ }
+
+ private short decodeBytesTag(short exp) {
+ readTagKey(KMByteTag.cast(exp).getTagType());
+ // The value must be byte blob
+ return KMByteTag.instance(scratchBuf[TAG_KEY_OFFSET], decode(KMByteTag.cast(exp).getValue()));
+ }
+
+ private short decodeBignumTag(short exp) {
+ readTagKey(KMBignumTag.cast(exp).getTagType());
+ // The value must be byte blob
+ return KMBignumTag.instance(
+ scratchBuf[TAG_KEY_OFFSET], decode(KMBignumTag.cast(exp).getValue()));
+ }
+
+ private short decodeMap(short exp) {
+ short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE);
+ short mapPtr = KMMap.instance(payloadLength);
+ short index = 0;
+ short type;
+ short keyobj;
+ short valueobj;
+ while (index < payloadLength) {
+ type = KMMap.cast(exp).getKey(index);
+ keyobj = decode(type);
+ type = KMMap.cast(exp).getKeyValue(index);
+ valueobj = decode(type);
+ KMMap.cast(mapPtr).add(index, keyobj, valueobj);
+ index++;
+ }
+ return mapPtr;
+ }
+
+ private short decodeArray(short exp) {
+ short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE);
+ short arrPtr = KMArray.instance(payloadLength);
+ short index = 0;
+ short type;
+ short obj;
+ // check whether array contains one type of objects or multiple types
+ if (KMArray.cast(exp).containedType()
+ == KMType.INVALID_VALUE) { // multiple types specified by expression.
+ if (KMArray.cast(exp).length() != KMArray.ANY_ARRAY_LENGTH) {
+ if (KMArray.cast(exp).length() != payloadLength) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ }
+ while (index < payloadLength) {
+ type = KMArray.cast(exp).get(index);
+ obj = decode(type);
+ KMArray.cast(arrPtr).add(index, obj);
+ index++;
+ }
+ } else { // Array is a Vector containing objects of one type
+ type = KMArray.cast(exp).containedType();
+ while (index < payloadLength) {
+ obj = decode(type);
+ KMArray.cast(arrPtr).add(index, obj);
+ index++;
+ }
+ }
+ return arrPtr;
+ }
+
+ private short decodeEnumTag(short exp) {
+ readTagKey(KMEnumTag.cast(exp).getTagType());
+ byte[] buffer = (byte[]) bufferRef[0];
+ short startOff = scratchBuf[START_OFFSET];
+ // Enum Tag value will always be integer with max 1 byte length.
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short len = (short) (buffer[startOff] & ADDITIONAL_MASK);
+ byte enumVal = 0;
+ if (len > UINT8_LENGTH) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ if (len < UINT8_LENGTH) {
+ enumVal = (byte) (len & ADDITIONAL_MASK);
+ incrementStartOff((short) 1);
+ } else if (len == UINT8_LENGTH) {
+ incrementStartOff((short) 1);
+ // startOff is incremented so update the startOff
+ // with latest value before using it.
+ startOff = scratchBuf[START_OFFSET];
+ enumVal = buffer[startOff];
+ incrementStartOff((short) 1);
+ }
+ return KMEnumTag.instance(scratchBuf[TAG_KEY_OFFSET], enumVal);
+ }
+
+ private short decodeBoolTag(short exp) {
+ readTagKey(KMBoolTag.cast(exp).getTagType());
+ byte[] buffer = (byte[]) bufferRef[0];
+ short startOff = scratchBuf[START_OFFSET];
+ // BOOL Tag is a leaf node and it must always have tiny encoded uint value = 1.
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != 0x01) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ incrementStartOff((short) 1);
+ return KMBoolTag.instance(scratchBuf[TAG_KEY_OFFSET]);
+ }
+
+ private short decodeEnum(short exp) {
+ byte[] buffer = (byte[]) bufferRef[0];
+ short startOff = scratchBuf[START_OFFSET];
+ // Enum value will always be integer with max 1 byte length.
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short len = (short) (buffer[startOff] & ADDITIONAL_MASK);
+ byte enumVal;
+ if (len > UINT8_LENGTH) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ if (len < UINT8_LENGTH) {
+ enumVal = (byte) (len & ADDITIONAL_MASK);
+ incrementStartOff((short) 1);
+ } else {
+ incrementStartOff((short) 1);
+ // startOff is incremented so update the startOff
+ // with latest value before using it.
+ startOff = scratchBuf[START_OFFSET];
+ enumVal = buffer[startOff];
+ incrementStartOff((short) 1);
+ }
+ return KMEnum.instance(KMEnum.cast(exp).getEnumType(), enumVal);
+ }
+
+ private short decodeSimpleValue(short exp) {
+ short startOff = scratchBuf[START_OFFSET];
+ byte[] buffer = (byte[]) bufferRef[0];
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != SIMPLE_VALUE_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ byte addInfo = (byte) (buffer[startOff] & ADDITIONAL_MASK);
+ incrementStartOff((short) 1);
+ return KMSimpleValue.instance(addInfo);
+ }
+
+ private short decodeSemanticTagValue(short exp) {
+ // Decode tag.
+ short tag = readMajorTypeWithInteger(exp, SEMANTIC_TAG_TYPE, UINT32_LENGTH);
+ // Decode value pointer.
+ short valuePtr = decode(KMSemanticTag.cast(exp).getValuePtr());
+ return KMSemanticTag.instance(tag, valuePtr);
+ }
+
+ private short readMajorTypeWithInteger(short exp, short majorType, short maxLimit) {
+ short inst;
+ short startOff = scratchBuf[START_OFFSET];
+ byte[] buffer = (byte[]) bufferRef[0];
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != majorType) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short len = (short) (buffer[startOff] & ADDITIONAL_MASK);
+ if (len > maxLimit) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ incrementStartOff((short) 1);
+ // startOff is incremented so update the startOff
+ // with latest value before using it.
+ startOff = scratchBuf[START_OFFSET];
+ if (len < UINT8_LENGTH) {
+ inst = KMInteger.uint_8((byte) (len & ADDITIONAL_MASK));
+ } else if (len == UINT8_LENGTH) {
+ inst = KMInteger.instance(buffer, startOff, (short) 1);
+ incrementStartOff((short) 1);
+ } else if (len == UINT16_LENGTH) {
+ inst = KMInteger.instance(buffer, startOff, (short) 2);
+ incrementStartOff((short) 2);
+ } else if (len == UINT32_LENGTH) {
+ inst = KMInteger.instance(buffer, startOff, (short) 4);
+ incrementStartOff((short) 4);
+ } else {
+ inst = KMInteger.instance(buffer, startOff, (short) 8);
+ incrementStartOff((short) 8);
+ }
+ return inst;
+ }
+
+ private short decodeInteger(short exp) {
+ return readMajorTypeWithInteger(exp, UINT_TYPE, UINT64_LENGTH);
+ }
+
+ private short decodeNegIntegerValue(byte addInfo, byte[] buf, short startOffset) {
+ short inst;
+ short len = 0;
+ short scratchpad;
+ if (addInfo < UINT8_LENGTH) {
+ addInfo = (byte) (-1 - addInfo);
+ inst = KMNInteger.uint_8(addInfo);
+ } else {
+ switch (addInfo) {
+ case UINT8_LENGTH:
+ len = 1;
+ break;
+ case UINT16_LENGTH:
+ len = 2;
+ break;
+ case UINT32_LENGTH:
+ len = 4;
+ break;
+ case UINT64_LENGTH:
+ len = 8;
+ break;
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ // Do (-1 - N), as per cbor negative integer decoding rule.
+ // N is the integer value.
+ scratchpad = KMByteBlob.instance((short) (len * 3));
+ byte[] input = KMByteBlob.cast(scratchpad).getBuffer();
+ short offset = KMByteBlob.cast(scratchpad).getStartOff();
+ Util.arrayFillNonAtomic(input, offset, len, (byte) -1);
+ Util.arrayCopyNonAtomic(buf, startOffset, input, (short) (offset + len), len);
+ KMUtils.subtract(
+ input, offset, (short) (offset + len), (short) (offset + 2 * len), (byte) len);
+ inst = KMNInteger.instance(input, (short) (offset + 2 * len), len);
+ incrementStartOff(len);
+ }
+ return inst;
+ }
+
+ private short decodeNegInteger(short exp) {
+ short startOff = scratchBuf[START_OFFSET];
+ byte[] buffer = (byte[]) bufferRef[0];
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != NEG_INT_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short len = (short) (buffer[startOff] & ADDITIONAL_MASK);
+ if (len > UINT64_LENGTH) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ incrementStartOff((short) 1);
+ // startOff is incremented so update the startOff
+ // with latest value before using it.
+ startOff = scratchBuf[START_OFFSET];
+ return decodeNegIntegerValue((byte) len, buffer, startOff);
+ }
+
+ private short decodeTstr(short exp) {
+ short payloadLength = readMajorTypeWithPayloadLength(TSTR_TYPE);
+ short inst =
+ KMTextString.instance((byte[]) bufferRef[0], scratchBuf[START_OFFSET], payloadLength);
+ incrementStartOff(payloadLength);
+ return inst;
+ }
+
+ private short decodeByteBlob(short exp) {
+ short payloadLength = readMajorTypeWithPayloadLength(BYTES_TYPE);
+ short inst =
+ KMByteBlob.instance((byte[]) bufferRef[0], scratchBuf[START_OFFSET], payloadLength);
+ incrementStartOff(payloadLength);
+ return inst;
+ }
+
+ private short peekTagType() {
+ byte[] buffer = (byte[]) bufferRef[0];
+ short startOff = scratchBuf[START_OFFSET];
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+
+ if ((short) (buffer[startOff] & ADDITIONAL_MASK) != UINT32_LENGTH) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ return (short)
+ ((Util.makeShort(buffer[(short) (startOff + 1)], buffer[(short) (startOff + 2)]))
+ & KMType.TAG_TYPE_MASK);
+ }
+
+ private void readTagKey(short expectedTagType) {
+ byte[] buffer = (byte[]) bufferRef[0];
+ short startOff = scratchBuf[START_OFFSET];
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != UINT32_LENGTH) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ incrementStartOff((short) 1);
+ short tagType = readShort();
+ scratchBuf[TAG_KEY_OFFSET] = readShort();
+ if (tagType != expectedTagType) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ }
+
+ // payload length cannot be more then 16 bits.
+ private short readMajorTypeWithPayloadLength(short majorType) {
+ short payloadLength;
+ byte val = readByte();
+ if ((short) (val & MAJOR_TYPE_MASK) != majorType) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short lenType = (short) (val & ADDITIONAL_MASK);
+ if (lenType > UINT16_LENGTH) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ if (lenType < UINT8_LENGTH) {
+ payloadLength = lenType;
+ } else if (lenType == UINT8_LENGTH) {
+ payloadLength = (short) (readByte() & 0xFF);
+ } else {
+ payloadLength = readShort();
+ }
+ return payloadLength;
+ }
+
+ private short readShort() {
+ byte[] buffer = (byte[]) bufferRef[0];
+ short startOff = scratchBuf[START_OFFSET];
+ short val = Util.makeShort(buffer[startOff], buffer[(short) (startOff + 1)]);
+ incrementStartOff((short) 2);
+ return val;
+ }
+
+ private byte readByte() {
+ short startOff = scratchBuf[START_OFFSET];
+ byte val = ((byte[]) bufferRef[0])[startOff];
+ incrementStartOff((short) 1);
+ return val;
+ }
+
+ private void incrementStartOff(short inc) {
+ scratchBuf[START_OFFSET] += inc;
+ if (scratchBuf[START_OFFSET] > scratchBuf[LEN_OFFSET]) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ }
+
+ public short readKeyblobVersion(byte[] buf, short bufOffset, short bufLen) {
+ bufferRef[0] = buf;
+ scratchBuf[START_OFFSET] = bufOffset;
+ scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen);
+ short arrayLen = readMajorTypeWithPayloadLength(ARRAY_TYPE);
+ if (arrayLen == 0) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short version = KMType.INVALID_VALUE;
+ try {
+ version = decodeInteger(KMInteger.exp());
+ } catch (Exception e) {
+ // Fail to decode Integer. It can happen if it is an old KeyBlob.
+ }
+ return version;
+ }
+
+ public short readCertificateChainHeaderLen(byte[] buf, short bufOffset, short bufLen) {
+ bufferRef[0] = buf;
+ scratchBuf[START_OFFSET] = bufOffset;
+ scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen);
+ readMajorTypeWithPayloadLength(BYTES_TYPE);
+ return (short) (scratchBuf[START_OFFSET] - bufOffset);
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEncoder.java
new file mode 100644
index 0000000..65394bd
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEncoder.java
@@ -0,0 +1,777 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * This class encodes KMType structures to a cbor format data recursively. Encoded bytes are written
+ * on the buffer provided by the caller. An exception will be thrown if the encoded data length is
+ * greater than the buffer length provided.
+ */
+public class KMEncoder {
+
+ // major types
+ private static final byte UINT_TYPE = 0x00;
+ private static final byte NEG_INT_TYPE = 0x20;
+ private static final byte BYTES_TYPE = 0x40;
+ private static final byte TSTR_TYPE = 0x60;
+ private static final byte ARRAY_TYPE = (byte) 0x80;
+ private static final byte MAP_TYPE = (byte) 0xA0;
+ private static final byte SIMPLE_VALUE_TYPE = (byte) 0xE0;
+ private static final byte SEMANTIC_TAG_TYPE = (byte) 0xC0;
+
+ // masks
+ private static final byte ADDITIONAL_MASK = 0x1F;
+
+ // value length
+ private static final byte UINT8_LENGTH = (byte) 0x18;
+ private static final byte UINT16_LENGTH = (byte) 0x19;
+ private static final byte UINT32_LENGTH = (byte) 0x1A;
+ private static final byte UINT64_LENGTH = (byte) 0x1B;
+ private static final short TINY_PAYLOAD = 0x17;
+ private static final short SHORT_PAYLOAD = 0x100;
+ private static final byte STACK_SIZE = 50;
+ private static final byte SCRATCH_BUF_SIZE = 6;
+ private static final byte START_OFFSET = 0;
+ private static final byte LEN_OFFSET = 2;
+ private static final byte STACK_PTR_OFFSET = 4;
+
+ private Object[] bufferRef;
+ private short[] scratchBuf;
+ private short[] stack;
+
+ public KMEncoder() {
+ bufferRef = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET);
+ scratchBuf = JCSystem.makeTransientShortArray(SCRATCH_BUF_SIZE, JCSystem.CLEAR_ON_RESET);
+ stack = JCSystem.makeTransientShortArray(STACK_SIZE, JCSystem.CLEAR_ON_RESET);
+ bufferRef[0] = null;
+ scratchBuf[START_OFFSET] = (short) 0;
+ scratchBuf[LEN_OFFSET] = (short) 0;
+ scratchBuf[STACK_PTR_OFFSET] = (short) 0;
+ }
+
+ private void push(short objPtr) {
+ stack[scratchBuf[STACK_PTR_OFFSET]] = objPtr;
+ scratchBuf[STACK_PTR_OFFSET]++;
+ }
+
+ private short pop() {
+ scratchBuf[STACK_PTR_OFFSET]--;
+ return stack[scratchBuf[STACK_PTR_OFFSET]];
+ }
+
+ private void encode(short obj) {
+ push(obj);
+ }
+
+ /**
+ * This functions encodes the given object into the provider buffer space in cbor format.
+ *
+ * @param object Object to be encoded into cbor data.
+ * @param buffer Output where cbor data is copied.
+ * @param startOff is the start offset of the buffer.
+ * @param bufLen length of the buffer
+ * @param encoderOutLimitLen excepted encoded output length.
+ * @return length of the encoded buffer.
+ */
+ public short encode(
+ short object, byte[] buffer, short startOff, short bufLen, short encoderOutLimitLen) {
+ scratchBuf[STACK_PTR_OFFSET] = 0;
+ bufferRef[0] = buffer;
+ scratchBuf[START_OFFSET] = startOff;
+ if ((short) (startOff + encoderOutLimitLen) > bufLen) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ scratchBuf[LEN_OFFSET] = (short) (startOff + encoderOutLimitLen);
+ push(object);
+ encode();
+ return (short) (scratchBuf[START_OFFSET] - startOff);
+ }
+
+ public short encode(short object, byte[] buffer, short startOff, short bufLen) {
+ return encode(object, buffer, startOff, bufLen, (short) (bufLen - startOff));
+ }
+
+ // array{KMError.OK,Array{KMByteBlobs}}
+ public short encodeCert(byte[] certBuffer, short bufferStart, short certStart, short certLength) {
+ if (bufferStart > certStart) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ bufferRef[0] = certBuffer;
+ scratchBuf[START_OFFSET] = certStart;
+ scratchBuf[LEN_OFFSET] = (short) (certStart + 1);
+ // Byte Header + cert length
+ scratchBuf[START_OFFSET] -= getEncodedBytesLength(certLength);
+ // Array header - 1 elements i.e. 1 byte
+ scratchBuf[START_OFFSET]--;
+ if (scratchBuf[START_OFFSET] < bufferStart) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ bufferStart = scratchBuf[START_OFFSET];
+ writeMajorTypeWithLength(ARRAY_TYPE, (short) 1); // Array of 1 elements
+ writeMajorTypeWithLength(BYTES_TYPE, certLength); // Cert Byte Blob of length
+ return bufferStart;
+ }
+
+ private void encode() {
+ while (scratchBuf[STACK_PTR_OFFSET] > 0) {
+ short exp = pop();
+ byte type = KMType.getType(exp);
+ switch (type) {
+ case KMType.BYTE_BLOB_TYPE:
+ encodeByteBlob(exp);
+ break;
+ case KMType.TEXT_STRING_TYPE:
+ encodeTextString(exp);
+ break;
+ case KMType.INTEGER_TYPE:
+ encodeUnsignedInteger(exp);
+ break;
+ case KMType.SIMPLE_VALUE_TYPE:
+ encodeSimpleValue(exp);
+ break;
+ case KMType.NEG_INTEGER_TYPE:
+ encodeNegInteger(exp);
+ break;
+ case KMType.ARRAY_TYPE:
+ encodeArray(exp);
+ break;
+ case KMType.MAP_TYPE:
+ encodeMap(exp);
+ break;
+ case KMType.ENUM_TYPE:
+ encodeEnum(exp);
+ break;
+ case KMType.KEY_PARAM_TYPE:
+ encodeKeyParam(exp);
+ break;
+ case KMType.SEMANTIC_TAG_TYPE:
+ encodeSemanticTag(exp);
+ break;
+ case KMType.COSE_KEY_TYPE:
+ case KMType.COSE_HEADERS_TYPE:
+ case KMType.COSE_CERT_PAYLOAD_TYPE:
+ encodeCoseMap(exp);
+ break;
+ case KMType.KEY_CHAR_TYPE:
+ encodeKeyChar(exp);
+ break;
+ case KMType.VERIFICATION_TOKEN_TYPE:
+ encodeVeriToken(exp);
+ break;
+ case KMType.HMAC_SHARING_PARAM_TYPE:
+ encodeHmacSharingParam(exp);
+ break;
+ case KMType.HW_AUTH_TOKEN_TYPE:
+ encodeHwAuthToken(exp);
+ break;
+ case KMType.TAG_TYPE:
+ short tagType = KMTag.getTagType(exp);
+ encodeTag(tagType, exp);
+ break;
+ case KMType.COSE_PAIR_TAG_TYPE:
+ short cosePairTagType = KMCosePairTagType.getTagValueType(exp);
+ encodeCosePairTag(cosePairTagType, exp);
+ break;
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ }
+ }
+
+ private void encodeCosePairIntegerTag(short exp) {
+ KMCosePairIntegerTag cosePairIntTag = KMCosePairIntegerTag.cast(exp);
+ // push key and value ptr in stack to get encoded.
+ encode(cosePairIntTag.getValuePtr());
+ encode(cosePairIntTag.getKeyPtr());
+ }
+
+ private void encodeCosePairByteBlobTag(short exp) {
+ KMCosePairByteBlobTag cosePairByteBlobTag = KMCosePairByteBlobTag.cast(exp);
+ // push key and value ptr in stack to get encoded.
+ encode(cosePairByteBlobTag.getValuePtr());
+ encode(cosePairByteBlobTag.getKeyPtr());
+ }
+
+ private void encodeCosePairCoseKeyTag(short exp) {
+ KMCosePairCoseKeyTag cosePairCoseKeyTag = KMCosePairCoseKeyTag.cast(exp);
+ // push key and value ptr in stack to get encoded.
+ encode(cosePairCoseKeyTag.getValuePtr());
+ encode(cosePairCoseKeyTag.getKeyPtr());
+ }
+
+ private void encodeCosePairTextStringTag(short exp) {
+ KMCosePairTextStringTag cosePairTextStringTag = KMCosePairTextStringTag.cast(exp);
+ // push key and value ptr in stack to get encoded.
+ encode(cosePairTextStringTag.getValuePtr());
+ encode(cosePairTextStringTag.getKeyPtr());
+ }
+
+ private void encodeCosePairSimpleValueTag(short exp) {
+ KMCosePairSimpleValueTag cosePairSimpleValueTag = KMCosePairSimpleValueTag.cast(exp);
+ // push key and value ptr in stack to get encoded.
+ encode(cosePairSimpleValueTag.getValuePtr());
+ encode(cosePairSimpleValueTag.getKeyPtr());
+ }
+
+ private void encodeCosePairNegIntegerTag(short exp) {
+ KMCosePairNegIntegerTag cosePairNegIntegerTag = KMCosePairNegIntegerTag.cast(exp);
+ // push key and value ptr in stack to get encoded.
+ encode(cosePairNegIntegerTag.getValuePtr());
+ encode(cosePairNegIntegerTag.getKeyPtr());
+ }
+
+ private void encodeCosePairTag(short tagType, short exp) {
+ switch (tagType) {
+ case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+ encodeCosePairByteBlobTag(exp);
+ return;
+ case KMType.COSE_PAIR_INT_TAG_TYPE:
+ encodeCosePairIntegerTag(exp);
+ return;
+ case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+ encodeCosePairNegIntegerTag(exp);
+ return;
+ case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE:
+ encodeCosePairSimpleValueTag(exp);
+ return;
+ case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE:
+ encodeCosePairTextStringTag(exp);
+ return;
+ case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE:
+ encodeCosePairCoseKeyTag(exp);
+ return;
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ }
+
+ private void encodeTag(short tagType, short exp) {
+ switch (tagType) {
+ case KMType.BIGNUM_TAG:
+ encodeBignumTag(exp);
+ return;
+ case KMType.BYTES_TAG:
+ encodeBytesTag(exp);
+ return;
+ case KMType.BOOL_TAG:
+ encodeBoolTag(exp);
+ return;
+ case KMType.UINT_TAG:
+ case KMType.ULONG_TAG:
+ case KMType.DATE_TAG:
+ encodeIntegerTag(exp);
+ return;
+ case KMType.ULONG_ARRAY_TAG:
+ case KMType.UINT_ARRAY_TAG:
+ encodeIntegerArrayTag(exp);
+ return;
+ case KMType.ENUM_TAG:
+ encodeEnumTag(exp);
+ return;
+ case KMType.ENUM_ARRAY_TAG:
+ encodeEnumArrayTag(exp);
+ return;
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ }
+
+ private void encodeCoseMap(short obj) {
+ encodeAsMap(KMCoseMap.getVals(obj));
+ }
+
+ private void encodeKeyParam(short obj) {
+ encodeAsMap(KMKeyParameters.cast(obj).getVals());
+ }
+
+ private void encodeKeyChar(short obj) {
+ encode(KMKeyCharacteristics.cast(obj).getVals());
+ }
+
+ private void encodeVeriToken(short obj) {
+ encode(KMVerificationToken.cast(obj).getVals());
+ }
+
+ private void encodeHwAuthToken(short obj) {
+ encode(KMHardwareAuthToken.cast(obj).getVals());
+ }
+
+ private void encodeHmacSharingParam(short obj) {
+ encode(KMHmacSharingParameters.cast(obj).getVals());
+ }
+
+ private void encodeArray(short obj) {
+ writeMajorTypeWithLength(ARRAY_TYPE, KMArray.cast(obj).length());
+ short len = KMArray.cast(obj).length();
+ short index = (short) (len - 1);
+ short subObj;
+ while (index >= 0) {
+ subObj = KMArray.cast(obj).get(index);
+ if (subObj != KMType.INVALID_VALUE) {
+ encode(subObj);
+ }
+ index--;
+ }
+ }
+
+ public void encodeArrayOnlyLength(short arrLength, byte[] buffer, short offset, short length) {
+ bufferRef[0] = buffer;
+ scratchBuf[START_OFFSET] = offset;
+ scratchBuf[LEN_OFFSET] = (short) (offset + length + 1);
+ writeMajorTypeWithLength(ARRAY_TYPE, length);
+ }
+
+ private void encodeMap(short obj) {
+ writeMajorTypeWithLength(MAP_TYPE, KMMap.cast(obj).length());
+ short len = KMMap.cast(obj).length();
+ short index = (short) (len - 1);
+ while (index >= 0) {
+ encode(KMMap.cast(obj).getKeyValue(index));
+ encode(KMMap.cast(obj).getKey(index));
+ index--;
+ }
+ }
+
+ private void encodeAsMap(short obj) {
+ writeMajorTypeWithLength(MAP_TYPE, KMArray.cast(obj).length());
+ short len = KMArray.cast(obj).length();
+ short index = (short) (len - 1);
+ short inst;
+ while (index >= 0) {
+ inst = KMArray.cast(obj).get(index);
+ encode(inst);
+ index--;
+ }
+ }
+
+ private void encodeIntegerArrayTag(short obj) {
+ writeTag(KMIntegerArrayTag.cast(obj).getTagType(), KMIntegerArrayTag.cast(obj).getKey());
+ encode(KMIntegerArrayTag.cast(obj).getValues());
+ }
+
+ private void encodeEnumArrayTag(short obj) {
+ writeTag(KMEnumArrayTag.cast(obj).getTagType(), KMEnumArrayTag.cast(obj).getKey());
+ encode(KMEnumArrayTag.cast(obj).getValues());
+ }
+
+ private void encodeIntegerTag(short obj) {
+ writeTag(KMIntegerTag.cast(obj).getTagType(), KMIntegerTag.cast(obj).getKey());
+ encode(KMIntegerTag.cast(obj).getValue());
+ }
+
+ private void encodeBignumTag(short obj) {
+ writeTag(KMBignumTag.getTagType(obj), KMBignumTag.getKey(obj));
+ encode(KMBignumTag.cast(obj).getValue());
+ }
+
+ private void encodeBytesTag(short obj) {
+ writeTag(KMByteTag.cast(obj).getTagType(), KMByteTag.cast(obj).getKey());
+ encode(KMByteTag.cast(obj).getValue());
+ }
+
+ private void encodeBoolTag(short obj) {
+ writeTag(KMBoolTag.cast(obj).getTagType(), KMBoolTag.cast(obj).getKey());
+ writeByteValue(KMBoolTag.cast(obj).getVal());
+ }
+
+ private void encodeEnumTag(short obj) {
+ writeTag(KMEnumTag.cast(obj).getTagType(), KMEnumTag.cast(obj).getKey());
+ writeByteValue(KMEnumTag.cast(obj).getValue());
+ }
+
+ private void encodeEnum(short obj) {
+ writeByteValue(KMEnum.cast(obj).getVal());
+ }
+
+ private void encodeInteger(byte[] val, short len, short startOff, short majorType) {
+ // find out the most significant byte
+ short msbIndex = findMsb(val, startOff, len);
+ // find the difference between most significant byte and len
+ short diff = (short) (len - msbIndex);
+ if (diff == 0) {
+ writeByte((byte) (majorType | 0));
+ } else if ((diff == 1)
+ && (val[(short) (startOff + msbIndex)] < UINT8_LENGTH)
+ && (val[(short) (startOff + msbIndex)] >= 0)) {
+ writeByte((byte) (majorType | val[(short) (startOff + msbIndex)]));
+ } else if (diff == 1) {
+ writeByte((byte) (majorType | UINT8_LENGTH));
+ writeByte(val[(short) (startOff + msbIndex)]);
+ } else if (diff == 2) {
+ writeByte((byte) (majorType | UINT16_LENGTH));
+ writeBytes(val, (short) (startOff + msbIndex), (short) 2);
+ } else if (diff <= 4) {
+ writeByte((byte) (majorType | UINT32_LENGTH));
+ writeBytes(val, (short) (startOff + len - 4), (short) 4);
+ } else {
+ writeByte((byte) (majorType | UINT64_LENGTH));
+ writeBytes(val, startOff, (short) 8);
+ }
+ }
+
+ // find out the most significant byte
+ public short findMsb(byte[] buf, short offset, short len) {
+ byte index = 0;
+ // find out the most significant byte
+ while (index < len) {
+ if (buf[(short) (offset + index)] > 0) {
+ break;
+ } else if (buf[(short) (offset + index)] < 0) {
+ break;
+ }
+ index++; // index will be equal to len if value is 0.
+ }
+ return index;
+ }
+
+ public void computeOnesCompliment(short msbIndex, byte[] buf, short offset, short len) {
+ // find the difference between most significant byte and len
+ short diff = (short) (len - msbIndex);
+ short correctedOffset = offset;
+ short correctedLen = len;
+ // The offset and length of the buffer for Short and Byte types should be
+ // corrected before computing the 1s compliment. The reason for doing this
+ // is to avoid computation of 1s compliment on the MSB bytes.
+ if (diff == 0) {
+ // Fail
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ } else if (diff == 1) {
+ correctedOffset = (short) (offset + 3);
+ correctedLen = 1;
+ } else if (diff == 2) {
+ correctedOffset = (short) (offset + 2);
+ correctedLen = 2;
+ }
+ // For int and long values the len and offset values are always proper.
+ // int - 4 bytes
+ // long - 8 bytes.
+ KMUtils.computeOnesCompliment(buf, correctedOffset, correctedLen);
+ }
+
+ // Encoding rule for negative Integers is taken from
+ // https://datatracker.ietf.org/doc/html/rfc7049#section-2.1, Major type 1.
+ public short handleNegIntegerEncodingRule(byte[] buf, short offset, short len) {
+ short msbIndex = findMsb(buf, offset, len);
+ // Do -1-N, where N is the negative integer
+ // The value of -1-N is equal to the 1s compliment of N.
+ computeOnesCompliment(msbIndex, buf, offset, len);
+ return msbIndex;
+ }
+
+ // Note: This function modifies the buffer's actual value. So after encoding, restore the original
+ // value by calling removeNegIntegerEncodingRule().
+ public short applyNegIntegerEncodingRule(byte[] buf, short offset, short len) {
+ return handleNegIntegerEncodingRule(buf, offset, len);
+ }
+
+ public void removeNegIntegerEncodingRule(
+ byte[] buf, short offset, short len, short origMsbIndex) {
+ // Do -1-N, where N is the negative integer
+ // The value of -1-N is equal to the 1s compliment of N.
+ computeOnesCompliment(origMsbIndex, buf, offset, len);
+ }
+
+ private void encodeNegInteger(short obj) {
+ byte[] val = KMNInteger.cast(obj).getBuffer();
+ short len = KMNInteger.cast(obj).length();
+ short startOff = KMNInteger.cast(obj).getStartOff();
+ short msbIndex = applyNegIntegerEncodingRule(val, startOff, len);
+ encodeInteger(val, len, startOff, NEG_INT_TYPE);
+ removeNegIntegerEncodingRule(val, startOff, len, msbIndex);
+ }
+
+ private void encodeSemanticTag(short obj) {
+ short tag = KMSemanticTag.cast(obj).getKeyPtr();
+ encode(KMSemanticTag.cast(obj).getValuePtr());
+ encodeInteger(
+ KMInteger.cast(tag).getBuffer(),
+ KMInteger.cast(tag).length(),
+ KMInteger.cast(tag).getStartOff(),
+ SEMANTIC_TAG_TYPE);
+ }
+
+ private void encodeUnsignedInteger(short obj) {
+ byte[] val = KMInteger.cast(obj).getBuffer();
+ short len = KMInteger.cast(obj).length();
+ short startOff = KMInteger.cast(obj).getStartOff();
+ encodeInteger(val, len, startOff, UINT_TYPE);
+ }
+
+ private void encodeSimpleValue(short obj) {
+ byte value = KMSimpleValue.cast(obj).getValue();
+ writeByte((byte) (SIMPLE_VALUE_TYPE | value));
+ }
+
+ private void encodeTextString(short obj) {
+ writeMajorTypeWithLength(TSTR_TYPE, KMTextString.cast(obj).length());
+ writeBytes(
+ KMTextString.cast(obj).getBuffer(),
+ KMTextString.cast(obj).getStartOff(),
+ KMTextString.cast(obj).length());
+ }
+
+ public short encodeByteBlobHeader(short bufLen, byte[] buffer, short startOff, short length) {
+ bufferRef[0] = buffer;
+ scratchBuf[START_OFFSET] = startOff;
+ scratchBuf[LEN_OFFSET] = (short) (startOff + length + 1);
+ writeMajorTypeWithLength(BYTES_TYPE, bufLen);
+ return (short) (scratchBuf[START_OFFSET] - startOff);
+ }
+
+ private void encodeByteBlob(short obj) {
+ writeMajorTypeWithLength(BYTES_TYPE, KMByteBlob.cast(obj).length());
+ writeBytes(
+ KMByteBlob.cast(obj).getBuffer(),
+ KMByteBlob.cast(obj).getStartOff(),
+ KMByteBlob.cast(obj).length());
+ }
+
+ public short getEncodedLength(short ptr) {
+ short len = 0;
+ short type = KMType.getType(ptr);
+ switch (type) {
+ case KMType.BYTE_BLOB_TYPE:
+ len += getEncodedByteBlobLength(ptr);
+ break;
+ case KMType.TEXT_STRING_TYPE:
+ len += getEncodedTextStringLength(ptr);
+ break;
+ case KMType.INTEGER_TYPE:
+ len += getEncodedIntegerLength(ptr);
+ break;
+ case KMType.NEG_INTEGER_TYPE:
+ len += getEncodedNegIntegerLength(ptr);
+ break;
+ case KMType.ARRAY_TYPE:
+ len += getEncodedArrayLen(ptr);
+ break;
+ case KMType.MAP_TYPE:
+ len += getEncodedMapLen(ptr);
+ break;
+ case KMType.COSE_PAIR_TAG_TYPE:
+ short cosePairTagType = KMCosePairTagType.getTagValueType(ptr);
+ len += getEncodedCosePairTagLen(cosePairTagType, ptr);
+ break;
+ case KMType.COSE_KEY_TYPE:
+ case KMType.COSE_HEADERS_TYPE:
+ case KMType.COSE_CERT_PAYLOAD_TYPE:
+ len += getEncodedArrayLen(KMCoseMap.getVals(ptr));
+ break;
+ default:
+ KMException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return len;
+ }
+
+ private short getEncodedCosePairTagLen(short tagType, short exp) {
+ short length = 0;
+ switch (tagType) {
+ case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+ KMCosePairByteBlobTag cosePairByteBlobTag = KMCosePairByteBlobTag.cast(exp);
+ length = getEncodedLength(cosePairByteBlobTag.getKeyPtr());
+ length += getEncodedLength(cosePairByteBlobTag.getValuePtr());
+ break;
+ case KMType.COSE_PAIR_INT_TAG_TYPE:
+ KMCosePairIntegerTag cosePairIntTag = KMCosePairIntegerTag.cast(exp);
+ length = getEncodedLength(cosePairIntTag.getValuePtr());
+ length += getEncodedLength(cosePairIntTag.getKeyPtr());
+ break;
+ case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+ KMCosePairNegIntegerTag cosePairNegIntegerTag = KMCosePairNegIntegerTag.cast(exp);
+ length = getEncodedLength(cosePairNegIntegerTag.getValuePtr());
+ length += getEncodedLength(cosePairNegIntegerTag.getKeyPtr());
+ break;
+ case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE:
+ KMCosePairSimpleValueTag cosePairSimpleValueTag = KMCosePairSimpleValueTag.cast(exp);
+ length = getEncodedLength(cosePairSimpleValueTag.getValuePtr());
+ length += getEncodedLength(cosePairSimpleValueTag.getKeyPtr());
+ break;
+ case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE:
+ KMCosePairTextStringTag cosePairTextStringTag = KMCosePairTextStringTag.cast(exp);
+ length = getEncodedLength(cosePairTextStringTag.getValuePtr());
+ length += getEncodedLength(cosePairTextStringTag.getKeyPtr());
+ break;
+ case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE:
+ KMCosePairCoseKeyTag cosePairCoseKeyTag = KMCosePairCoseKeyTag.cast(exp);
+ length = getEncodedLength(cosePairCoseKeyTag.getValuePtr());
+ length += getEncodedLength(cosePairCoseKeyTag.getKeyPtr());
+ break;
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ return length;
+ }
+
+ private short getEncodedMapLen(short obj) {
+ short mapLen = KMMap.cast(obj).length();
+ short len = getEncodedBytesLength(mapLen);
+ short index = 0;
+ while (index < mapLen) {
+ len += getEncodedLength(KMMap.cast(obj).getKey(index));
+ len += getEncodedLength(KMMap.cast(obj).getKeyValue(index));
+ index++;
+ }
+ return len;
+ }
+
+ private short getEncodedArrayLen(short obj) {
+ short arrLen = KMArray.cast(obj).length();
+ short len = getEncodedBytesLength(arrLen);
+ short index = 0;
+ short subObj;
+ while (index < arrLen) {
+ subObj = KMArray.cast(obj).get(index);
+ if (subObj != KMType.INVALID_VALUE) {
+ len += getEncodedLength(subObj);
+ }
+ index++;
+ }
+ return len;
+ }
+
+ public short getEncodedBytesLength(short len) {
+ short ret = 0;
+ if (len < KMEncoder.UINT8_LENGTH && len >= 0) {
+ ret = 1;
+ } else if (len >= KMEncoder.UINT8_LENGTH && len <= (short) 0x00FF) {
+ ret = 2;
+ } else if (len > (short) 0x00FF && len <= (short) 0x7FFF) {
+ ret = 3;
+ } else {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ return ret;
+ }
+
+ private short getEncodedByteBlobLength(short obj) {
+ short len = KMByteBlob.cast(obj).length();
+ len += getEncodedBytesLength(len);
+ return len;
+ }
+
+ private short getEncodedTextStringLength(short obj) {
+ short len = KMTextString.cast(obj).length();
+ len += getEncodedBytesLength(len);
+ return len;
+ }
+
+ private short getEncodedNegIntegerLength(short obj) {
+ byte[] buf = KMNInteger.cast(obj).getBuffer();
+ short len = KMNInteger.cast(obj).length();
+ short offset = KMNInteger.cast(obj).getStartOff();
+ short msbIndex = applyNegIntegerEncodingRule(buf, offset, len);
+ short ret = getEncodedIntegerLength(buf, offset, len);
+ removeNegIntegerEncodingRule(buf, offset, len, msbIndex);
+ return ret;
+ }
+
+ private short getEncodedIntegerLength(byte[] val, short startOff, short len) {
+ short msbIndex = findMsb(val, startOff, len);
+ // find the difference between most significant byte and len
+ short diff = (short) (len - msbIndex);
+ switch (diff) {
+ case 0:
+ case 1: // Byte
+ if ((val[(short) (startOff + msbIndex)] < KMEncoder.UINT8_LENGTH)
+ && (val[(short) (startOff + msbIndex)] >= 0)) {
+ return (short) 1;
+ } else {
+ return (short) 2;
+ }
+ case 2: // Short
+ return (short) 3;
+ case 3:
+ case 4: // UInt32
+ return (short) 5;
+ case 5:
+ case 6:
+ case 7:
+ case 8: // UInt64
+ return (short) 9;
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ return 0;
+ }
+
+ private short getEncodedIntegerLength(short obj) {
+ byte[] val = KMInteger.cast(obj).getBuffer();
+ short len = KMInteger.cast(obj).length();
+ short startOff = KMInteger.cast(obj).getStartOff();
+ return getEncodedIntegerLength(val, startOff, len);
+ }
+
+ private void writeByteValue(byte val) {
+ if ((val < UINT8_LENGTH) && (val >= 0)) {
+ writeByte((byte) (UINT_TYPE | val));
+ } else {
+ writeByte((byte) (UINT_TYPE | UINT8_LENGTH));
+ writeByte(val);
+ }
+ }
+
+ private void writeTag(short tagType, short tagKey) {
+ writeByte((byte) (UINT_TYPE | UINT32_LENGTH));
+ writeShort(tagType);
+ writeShort(tagKey);
+ }
+
+ private void writeMajorTypeWithLength(byte majorType, short len) {
+ if (len <= TINY_PAYLOAD) {
+ writeByte((byte) (majorType | (byte) (len & ADDITIONAL_MASK)));
+ } else if (len < SHORT_PAYLOAD) {
+ writeByte((byte) (majorType | UINT8_LENGTH));
+ writeByte((byte) (len & 0xFF));
+ } else {
+ writeByte((byte) (majorType | UINT16_LENGTH));
+ writeShort(len);
+ }
+ }
+
+ private void writeBytes(byte[] buf, short start, short len) {
+ byte[] buffer = (byte[]) bufferRef[0];
+ Util.arrayCopyNonAtomic(buf, start, buffer, scratchBuf[START_OFFSET], len);
+ incrementStartOff(len);
+ }
+
+ private void writeShort(short val) {
+ byte[] buffer = (byte[]) bufferRef[0];
+ buffer[scratchBuf[START_OFFSET]] = (byte) ((val >> 8) & 0xFF);
+ incrementStartOff((short) 1);
+ buffer[scratchBuf[START_OFFSET]] = (byte) ((val & 0xFF));
+ incrementStartOff((short) 1);
+ }
+
+ private void writeByte(byte val) {
+ byte[] buffer = (byte[]) bufferRef[0];
+ buffer[scratchBuf[START_OFFSET]] = val;
+ incrementStartOff((short) 1);
+ }
+
+ private void incrementStartOff(short inc) {
+ scratchBuf[START_OFFSET] += inc;
+ if (scratchBuf[START_OFFSET] >= scratchBuf[LEN_OFFSET]) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnum.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnum.java
new file mode 100644
index 0000000..44bf477
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnum.java
@@ -0,0 +1,166 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMEnum represents an enumeration specified in android keymaster hal specifications. It
+ * corresponds to uint CBOR type and it is a byte value. struct{byte ENUM_TYPE; short length;
+ * struct{short enumType; byte val}}
+ */
+public class KMEnum extends KMType {
+
+ private static KMEnum prototype;
+
+ // The allowed enum types.
+ private static short[] types = {
+ HARDWARE_TYPE,
+ KEY_FORMAT,
+ KEY_DERIVATION_FUNCTION,
+ VERIFIED_BOOT_STATE,
+ DEVICE_LOCKED,
+ USER_AUTH_TYPE,
+ PURPOSE,
+ ECCURVE,
+ RULE
+ };
+
+ private static Object[] enums = null;
+
+ private KMEnum() {}
+
+ private static KMEnum proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMEnum();
+ }
+ KMType.instanceTable[KM_ENUM_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ return KMType.exp(ENUM_TYPE);
+ }
+
+ public static KMEnum cast(short ptr) {
+ if (heap[ptr] != ENUM_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static short instance(short enumType) {
+ if (!validateEnum(enumType, NO_VALUE)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = KMType.instance(ENUM_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), enumType);
+ return ptr;
+ }
+
+ public static short instance(short enumType, byte val) {
+ if (!validateEnum(enumType, val)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = KMType.instance(ENUM_TYPE, (short) 3);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), enumType);
+ heap[(short) (ptr + TLV_HEADER_SIZE + 2)] = val;
+ return ptr;
+ }
+
+ private static void create() {
+ // The allowed enum values to corresponding enum types in the types array.
+ if (enums == null) {
+ enums =
+ new Object[] {
+ new byte[] {SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX},
+ new byte[] {X509, PKCS8, RAW},
+ new byte[] {
+ DERIVATION_NONE,
+ RFC5869_SHA256,
+ ISO18033_2_KDF1_SHA1,
+ ISO18033_2_KDF1_SHA256,
+ ISO18033_2_KDF2_SHA1,
+ ISO18033_2_KDF2_SHA256
+ },
+ new byte[] {SELF_SIGNED_BOOT, VERIFIED_BOOT, UNVERIFIED_BOOT, FAILED_BOOT},
+ new byte[] {DEVICE_LOCKED_TRUE, DEVICE_LOCKED_FALSE},
+ new byte[] {USER_AUTH_NONE, PASSWORD, FINGERPRINT, BOTH},
+ new byte[] {ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY, AGREE_KEY},
+ new byte[] {P_224, P_256, P_384, P_521},
+ new byte[] {IGNORE_INVALID_TAGS, FAIL_ON_INVALID_TAGS}
+ };
+ }
+ }
+
+ // isValidTag enumeration keys and values.
+ private static boolean validateEnum(short key, byte value) {
+ create();
+ byte[] vals;
+ short enumInd;
+ // check if key exists
+ short index = (short) types.length;
+ while (--index >= 0) {
+ if (types[index] == key) {
+ // check if value given
+ if (value != NO_VALUE) {
+ // check if the value exist
+ vals = (byte[]) enums[index];
+ enumInd = (short) vals.length;
+ while (--enumInd >= 0) {
+ if (vals[enumInd] == value) {
+ // return true if value exist
+ return true;
+ }
+ }
+ // return false if value does not exist
+ return false;
+ }
+ // return true if key exist and value not given
+ return true;
+ }
+ }
+ // return false if key does not exist
+ return false;
+ }
+
+ public short length() {
+ return Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + 1));
+ }
+
+ public byte getVal() {
+ return heap[(short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2)];
+ }
+
+ public void setVal(byte val) {
+ heap[(short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2)] = val;
+ }
+
+ public short getEnumType() {
+ return Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public void setEnumType(short type) {
+ Util.setShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE), type);
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java
new file mode 100644
index 0000000..ea73c40
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java
@@ -0,0 +1,305 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMEnumArrayTag represents ENUM_REP tag type. It has following structure, struct{byte TAG_TYPE;
+ * short length; struct{short ENUM_ARRAY_TAG; short tagKey; sequence of byte values}}
+ */
+public class KMEnumArrayTag extends KMTag {
+
+ private static KMEnumArrayTag prototype;
+
+ // The allowed tag keys of enum array type.
+ private static short[] tags = {PURPOSE, BLOCK_MODE, DIGEST, PADDING, RSA_OAEP_MGF_DIGEST};
+
+ // Tag Values.
+ private static Object[] enums = null;
+
+ private KMEnumArrayTag() {}
+
+ private static KMEnumArrayTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMEnumArrayTag();
+ }
+ KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short blobPtr = KMByteBlob.exp();
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), ENUM_ARRAY_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), blobPtr);
+ return ptr;
+ }
+
+ public static short instance(short key) {
+ byte[] vals = getAllowedEnumValues(key);
+ if (vals == null) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short blobPtr = KMByteBlob.exp();
+ return instance(key, blobPtr);
+ }
+
+ public static short instance(short key, short byteBlob) {
+ byte[] allowedVals = getAllowedEnumValues(key);
+ if (allowedVals == null) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ KMByteBlob blob = KMByteBlob.cast(byteBlob);
+ short byteIndex = 0;
+ short enumIndex;
+ boolean validValue;
+ while (byteIndex < blob.length()) {
+ enumIndex = 0;
+ validValue = false;
+ while (enumIndex < allowedVals.length) {
+ if (blob.get(byteIndex) == allowedVals[enumIndex]) {
+ validValue = true;
+ break;
+ }
+ enumIndex++;
+ }
+ if (!validValue) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ byteIndex++;
+ }
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), ENUM_ARRAY_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), byteBlob);
+ return ptr;
+ }
+
+ public static KMEnumArrayTag cast(short ptr) {
+ if (heap[ptr] != TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != ENUM_ARRAY_TAG) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static void create() {
+ if (enums == null) {
+ // allowed tag values.
+ enums =
+ new Object[] {
+ new byte[] {ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY, AGREE_KEY},
+ new byte[] {ECB, CBC, CTR, GCM},
+ new byte[] {DIGEST_NONE, MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512},
+ new byte[] {
+ PADDING_NONE, RSA_OAEP, RSA_PSS, RSA_PKCS1_1_5_ENCRYPT, RSA_PKCS1_1_5_SIGN, PKCS7
+ },
+ new byte[] {DIGEST_NONE, MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512},
+ };
+ }
+ }
+
+ private static byte[] getAllowedEnumValues(short key) {
+ create();
+ short index = (short) tags.length;
+ while (--index >= 0) {
+ if (tags[index] == key) {
+ return (byte[]) enums[index];
+ }
+ }
+ return null;
+ }
+
+ public static short getValues(short tagId, short params, byte[] buf, short start) {
+ short tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params);
+ if (tag == KMType.INVALID_VALUE) {
+ return KMType.INVALID_VALUE;
+ }
+ tag = KMEnumArrayTag.cast(tag).getValues();
+ return KMByteBlob.cast(tag).getValues(buf, start);
+ }
+
+ public static boolean contains(short tagId, short tagValue, short params) {
+ short tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params);
+ if (tag != KMType.INVALID_VALUE) {
+ short index = 0;
+ while (index < KMEnumArrayTag.cast(tag).length()) {
+ if (tagValue == KMEnumArrayTag.cast(tag).get(index)) {
+ return true;
+ }
+ index++;
+ }
+ }
+ return false;
+ }
+
+ public static short length(short tagId, short params) {
+ short tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params);
+ if (tag != KMType.INVALID_VALUE) {
+ return KMEnumArrayTag.cast(tag).length();
+ }
+ return KMType.INVALID_VALUE;
+ }
+
+ public short getKey() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short getTagType() {
+ return KMType.ENUM_ARRAY_TAG;
+ }
+
+ public short getValues() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+
+ public short length() {
+ short blobPtr =
+ Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ return KMByteBlob.cast(blobPtr).length();
+ }
+
+ public short get(short index) {
+ return KMByteBlob.cast(getValues()).get(index);
+ }
+
+ public boolean contains(short tagValue) {
+ short index = 0;
+ while (index < length()) {
+ if (get(index) == (byte) tagValue) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ public boolean isValidDigests(byte alg) {
+ short index = 0;
+ short digest;
+ while (index < length()) {
+ digest = get(index);
+ switch (alg) {
+ case KMType.EC:
+ case KMType.RSA:
+ if (digest != KMType.DIGEST_NONE && digest != KMType.SHA2_256 && digest != KMType.SHA1) {
+ return false;
+ }
+ break;
+ case KMType.HMAC:
+ if (digest != KMType.SHA2_256) {
+ return false;
+ }
+ break;
+ case KMType.AES:
+ case KMType.DES:
+ if (digest != KMType.DIGEST_NONE) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ index++;
+ }
+ return true;
+ }
+
+ public boolean isValidPaddingModes(byte alg) {
+ short index = 0;
+ short padding;
+ while (index < length()) {
+ padding = get(index);
+ switch (alg) {
+ case KMType.RSA:
+ if (padding != KMType.RSA_OAEP
+ && padding != KMType.PADDING_NONE
+ && padding != KMType.RSA_PKCS1_1_5_SIGN
+ && padding != KMType.RSA_PKCS1_1_5_ENCRYPT
+ && padding != KMType.RSA_PSS) {
+ return false;
+ }
+ break;
+ case KMType.AES:
+ case KMType.DES:
+ if (padding != KMType.PKCS7 && padding != KMType.PADDING_NONE) {
+ return false;
+ }
+ break;
+ case KMType.EC:
+ case KMType.HMAC:
+ if (padding != PADDING_NONE) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ index++;
+ }
+ return true;
+ }
+
+ public boolean isValidPurpose(byte alg) {
+ short index = 0;
+ short purpose;
+ while (index < length()) {
+ purpose = get(index);
+ switch (purpose) {
+ case KMType.DECRYPT:
+ case KMType.ENCRYPT:
+ if (alg != KMType.RSA && alg != KMType.AES && alg != KMType.DES) {
+ return false;
+ }
+ break;
+ case KMType.SIGN:
+ case KMType.VERIFY:
+ if (alg != KMType.HMAC && alg != KMType.RSA && alg != KMType.EC) {
+ return false;
+ }
+ break;
+ case KMType.WRAP_KEY:
+ if (alg != KMType.RSA) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ index++;
+ }
+ return true;
+ }
+
+ public boolean isValidBlockMode(byte alg) {
+ if (alg == KMType.AES || alg == KMType.DES) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumTag.java
new file mode 100644
index 0000000..a7bcbe6
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumTag.java
@@ -0,0 +1,152 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMEnumTag represents ENUM Tag type specified in android keymaster hal specifications. struct{byte
+ * TAG_TYPE; short length; struct{short ENUM_TAG; short tagKey; byte value}}
+ */
+public class KMEnumTag extends KMTag {
+
+ private static KMEnumTag prototype;
+
+ // The allowed tag keys of type enum tag.
+ private static short[] tags = {
+ ALGORITHM, ECCURVE, BLOB_USAGE_REQ, USER_AUTH_TYPE, ORIGIN, HARDWARE_TYPE
+ };
+
+ private static Object[] enums = null;
+
+ private KMEnumTag() {}
+
+ private static KMEnumTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMEnumTag();
+ }
+ KMType.instanceTable[KM_ENUM_TAG_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short ptr = instance(TAG_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), ENUM_TAG);
+ return ptr;
+ }
+
+ public static short instance(short key) {
+ if (!validateEnum(key, NO_VALUE)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = KMType.instance(TAG_TYPE, (short) 4);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), ENUM_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+ return ptr;
+ }
+
+ public static short instance(short key, byte val) {
+ if (!validateEnum(key, val)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(TAG_TYPE, (short) 5);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), ENUM_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+ heap[(short) (ptr + TLV_HEADER_SIZE + 4)] = val;
+ return ptr;
+ }
+
+ public static KMEnumTag cast(short ptr) {
+ if (heap[ptr] != TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != ENUM_TAG) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static void create() {
+ if (enums == null) {
+ // enum tag values.
+ enums =
+ new Object[] {
+ new byte[] {RSA, DES, EC, AES, HMAC},
+ new byte[] {P_224, P_256, P_384, P_521, CURVE_25519},
+ new byte[] {STANDALONE, REQUIRES_FILE_SYSTEM},
+ new byte[] {USER_AUTH_NONE, PASSWORD, FINGERPRINT, BOTH, ANY},
+ new byte[] {GENERATED, DERIVED, IMPORTED, UNKNOWN, SECURELY_IMPORTED},
+ new byte[] {SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX}
+ };
+ }
+ }
+
+ // isValidTag enumeration keys and values.
+ private static boolean validateEnum(short key, byte value) {
+ create();
+ byte[] vals;
+ short enumInd;
+ // check if key exists
+ short index = (short) tags.length;
+ while (--index >= 0) {
+ if (tags[index] == key) {
+ // check if value given
+ if (value != NO_VALUE) {
+ // check if the value exist
+ vals = (byte[]) enums[index];
+ enumInd = (short) vals.length;
+ while (--enumInd >= 0) {
+ if (vals[enumInd] == value) {
+ // return true if value exist
+ return true;
+ }
+ }
+ // return false if value does not exist
+ return false;
+ }
+ // return true if key exist and value not given
+ return true;
+ }
+ }
+ // return false if key does not exist
+ return false;
+ }
+
+ public static short getValue(short tagKey, short keyParameters) {
+ short tagPtr = KMKeyParameters.findTag(KMType.ENUM_TAG, tagKey, keyParameters);
+ if (tagPtr != KMType.INVALID_VALUE) {
+ return heap[(short) (tagPtr + TLV_HEADER_SIZE + 4)];
+ }
+ return KMType.INVALID_VALUE;
+ }
+
+ public short getKey() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_ENUM_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short getTagType() {
+ return KMType.ENUM_TAG;
+ }
+
+ public byte getValue() {
+ return heap[(short) (KMType.instanceTable[KM_ENUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4)];
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMError.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMError.java
new file mode 100644
index 0000000..bbae870
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMError.java
@@ -0,0 +1,134 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+/**
+ * KMError includes all the error codes from android keymaster hal specifications. The values are
+ * positive unlike negative values in keymaster hal.
+ */
+public class KMError {
+
+ public static final short OK = 0;
+ public static final short UNSUPPORTED_PURPOSE = 2;
+ public static final short INCOMPATIBLE_PURPOSE = 3;
+ public static final short UNSUPPORTED_ALGORITHM = 4;
+ public static final short INCOMPATIBLE_ALGORITHM = 5;
+ public static final short UNSUPPORTED_KEY_SIZE = 6;
+ public static final short UNSUPPORTED_BLOCK_MODE = 7;
+ public static final short INCOMPATIBLE_BLOCK_MODE = 8;
+ public static final short UNSUPPORTED_MAC_LENGTH = 9;
+ public static final short UNSUPPORTED_PADDING_MODE = 10;
+ public static final short INCOMPATIBLE_PADDING_MODE = 11;
+ public static final short UNSUPPORTED_DIGEST = 12;
+ public static final short INCOMPATIBLE_DIGEST = 13;
+
+ public static final short UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = 19;
+
+ /** For PKCS8 & PKCS12 */
+ public static final short INVALID_INPUT_LENGTH = 21;
+
+ public static final short KEY_USER_NOT_AUTHENTICATED = 26;
+ public static final short INVALID_OPERATION_HANDLE = 28;
+ public static final short INSUFFICIENT_BUFFER_SPACE = 29;
+ public static final short VERIFICATION_FAILED = 30;
+ public static final short TOO_MANY_OPERATIONS = 31;
+ public static final short INVALID_KEY_BLOB = 33;
+
+ public static final short INVALID_ARGUMENT = 38;
+ public static final short UNSUPPORTED_TAG = 39;
+ public static final short INVALID_TAG = 40;
+ public static final short IMPORT_PARAMETER_MISMATCH = 44;
+ public static final short OPERATION_CANCELLED = 46;
+
+ public static final short MISSING_NONCE = 51;
+ public static final short INVALID_NONCE = 52;
+ public static final short MISSING_MAC_LENGTH = 53;
+ public static final short CALLER_NONCE_PROHIBITED = 55;
+ public static final short KEY_MAX_OPS_EXCEEDED = 56;
+ public static final short INVALID_MAC_LENGTH = 57;
+ public static final short MISSING_MIN_MAC_LENGTH = 58;
+ public static final short UNSUPPORTED_MIN_MAC_LENGTH = 59;
+ public static final short UNSUPPORTED_EC_CURVE = 61;
+ public static final short KEY_REQUIRES_UPGRADE = 62;
+
+ public static final short ATTESTATION_CHALLENGE_MISSING = 63;
+ public static final short ATTESTATION_APPLICATION_ID_MISSING = 65;
+ public static final short CANNOT_ATTEST_IDS = 66;
+ public static final short ROLLBACK_RESISTANCE_UNAVAILABLE = 67;
+
+ public static final short NO_USER_CONFIRMATION = 71;
+ public static final short DEVICE_LOCKED = 72;
+ public static final short EARLY_BOOT_ENDED = 73;
+ public static final short ATTESTATION_KEYS_NOT_PROVISIONED = 74;
+ public static final short INCOMPATIBLE_MGF_DIGEST = 78;
+ public static final short UNSUPPORTED_MGF_DIGEST = 79;
+ public static final short MISSING_NOT_BEFORE = 80;
+ public static final short MISSING_NOT_AFTER = 81;
+ public static final short MISSING_ISSUER_SUBJECT_NAME = 82;
+ public static final short INVALID_ISSUER_SUBJECT_NAME = 83;
+
+ public static final short UNIMPLEMENTED = 100;
+ public static final short UNKNOWN_ERROR = 1000;
+
+ // Extended errors
+ public static final short SW_CONDITIONS_NOT_SATISFIED = 10001;
+ public static final short UNSUPPORTED_CLA = 10002;
+ public static final short INVALID_P1P2 = 10003;
+ public static final short UNSUPPORTED_INSTRUCTION = 10004;
+ public static final short CMD_NOT_ALLOWED = 10005;
+ public static final short SW_WRONG_LENGTH = 10006;
+ public static final short INVALID_DATA = 10007;
+
+ // Crypto errors
+ public static final short CRYPTO_ILLEGAL_USE = 10008;
+ public static final short CRYPTO_ILLEGAL_VALUE = 10009;
+ public static final short CRYPTO_INVALID_INIT = 10010;
+ public static final short CRYPTO_NO_SUCH_ALGORITHM = 10011;
+ public static final short CRYPTO_UNINITIALIZED_KEY = 10012;
+ // Generic Unknown error.
+ public static final short GENERIC_UNKNOWN_ERROR = 10013;
+
+ // Remote key provisioning error codes.
+ public static final short STATUS_FAILED = 32000;
+ public static final short STATUS_INVALID_MAC = 32001;
+ public static final short STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 32002;
+ public static final short STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 32003;
+ public static final short STATUS_INVALID_EEK = 32004;
+ public static final short INVALID_STATE = 32005;
+
+ public static short translate(short err) {
+ switch (err) {
+ case SW_CONDITIONS_NOT_SATISFIED:
+ case UNSUPPORTED_CLA:
+ case INVALID_P1P2:
+ case INVALID_DATA:
+ case CRYPTO_ILLEGAL_USE:
+ case CRYPTO_ILLEGAL_VALUE:
+ case CRYPTO_INVALID_INIT:
+ case CRYPTO_UNINITIALIZED_KEY:
+ case GENERIC_UNKNOWN_ERROR:
+ case CMD_NOT_ALLOWED:
+ case UNKNOWN_ERROR:
+ return UNKNOWN_ERROR;
+ case CRYPTO_NO_SUCH_ALGORITHM:
+ return UNSUPPORTED_ALGORITHM;
+ case UNSUPPORTED_INSTRUCTION:
+ case SW_WRONG_LENGTH:
+ return UNIMPLEMENTED;
+ }
+ return err;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java
new file mode 100644
index 0000000..e6b1d37
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java
@@ -0,0 +1,171 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMHardwareAuthToken represents HardwareAuthToken structure from android keymaster hal
+ * specifications. It corresponds to CBOR array type. struct{byte HW_AUTH_TOKEN_TYPE; short
+ * length=2; short arrayPtr} where arrayPtr is a pointer to ordered array with following elements:
+ * {KMInteger Challenge; KMInteger UserId; KMInteger AuthenticatorId; UserAuthType
+ * HwAuthenticatorId; KMInteger TimeStamp; KMByteBlob Mac}
+ */
+public class KMHardwareAuthToken extends KMType {
+
+ public static final byte CHALLENGE = 0x00;
+ public static final byte USER_ID = 0x01;
+ public static final byte AUTHENTICATOR_ID = 0x02;
+ public static final byte HW_AUTHENTICATOR_TYPE = 0x03;
+ public static final byte TIMESTAMP = 0x04;
+ public static final byte MAC = 0x05;
+
+ private static KMHardwareAuthToken prototype;
+
+ private KMHardwareAuthToken() {}
+
+ public static short exp() {
+ short arrPtr = KMArray.instance((short) 6);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add(CHALLENGE, KMInteger.exp());
+ arr.add(USER_ID, KMInteger.exp());
+ arr.add(AUTHENTICATOR_ID, KMInteger.exp());
+ arr.add(HW_AUTHENTICATOR_TYPE, KMEnum.instance(KMType.USER_AUTH_TYPE));
+ arr.add(TIMESTAMP, KMInteger.exp());
+ arr.add(MAC, KMByteBlob.exp());
+ return instance(arrPtr);
+ }
+
+ private static KMHardwareAuthToken proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMHardwareAuthToken();
+ }
+ KMType.instanceTable[KM_HARDWARE_AUTH_TOKEN_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short instance() {
+ short arrPtr = KMArray.instance((short) 6);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add(CHALLENGE, KMInteger.uint_16((short) 0));
+ arr.add(USER_ID, KMInteger.uint_16((short) 0));
+ arr.add(AUTHENTICATOR_ID, KMInteger.uint_16((short) 0));
+ arr.add(HW_AUTHENTICATOR_TYPE, KMEnum.instance(KMType.USER_AUTH_TYPE, KMType.USER_AUTH_NONE));
+ arr.add(TIMESTAMP, KMInteger.uint_16((short) 0));
+ arr.add(MAC, KMByteBlob.instance((short) 0));
+ return instance(arrPtr);
+ }
+
+ public static short instance(short vals) {
+ KMArray arr = KMArray.cast(vals);
+ if (arr.length() != 6) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ short ptr = KMType.instance(HW_AUTH_TOKEN_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+ return ptr;
+ }
+
+ public static KMHardwareAuthToken cast(short ptr) {
+ if (heap[ptr] != HW_AUTH_TOKEN_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (heap[arrPtr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public short getVals() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_HARDWARE_AUTH_TOKEN_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short length() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).length();
+ }
+
+ public short getChallenge() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(CHALLENGE);
+ }
+
+ public void setChallenge(short vals) {
+ KMInteger.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(CHALLENGE, vals);
+ }
+
+ public short getUserId() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(USER_ID);
+ }
+
+ public void setUserId(short vals) {
+ KMInteger.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(USER_ID, vals);
+ }
+
+ public short getAuthenticatorId() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(AUTHENTICATOR_ID);
+ }
+
+ public void setAuthenticatorId(short vals) {
+ KMInteger.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(AUTHENTICATOR_ID, vals);
+ }
+
+ public short getHwAuthenticatorType() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(HW_AUTHENTICATOR_TYPE);
+ }
+
+ public void setHwAuthenticatorType(short vals) {
+ KMEnum.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(HW_AUTHENTICATOR_TYPE, vals);
+ }
+
+ public short getTimestamp() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(TIMESTAMP);
+ }
+
+ public void setTimestamp(short vals) {
+ KMInteger.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(TIMESTAMP, vals);
+ }
+
+ public short getMac() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(MAC);
+ }
+
+ public void setMac(short vals) {
+ KMByteBlob.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(MAC, vals);
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java
new file mode 100644
index 0000000..8642803
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java
@@ -0,0 +1,110 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMHmacSharingParameters represents HmacSharingParameters structure from android keymaster hal
+ * specifications. It corresponds to CBOR array type. struct{byte HMAC_SHARING_PARAM_TYPE; short
+ * length=2; short arrayPtr} where arrayPtr is a pointer to ordered array with following elements:
+ * {KMByteBlob Seed; KMByteBlob Nonce}
+ */
+public class KMHmacSharingParameters extends KMType {
+
+ public static final byte SEED = 0x00;
+ public static final byte NONCE = 0x01;
+
+ private static KMHmacSharingParameters prototype;
+
+ private KMHmacSharingParameters() {}
+
+ public static short exp() {
+ short arrPtr = KMArray.instance((short) 2);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add(SEED, KMByteBlob.exp());
+ arr.add(NONCE, KMByteBlob.exp());
+ return instance(arrPtr);
+ }
+
+ private static KMHmacSharingParameters proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMHmacSharingParameters();
+ }
+ KMType.instanceTable[KM_HMAC_SHARING_PARAMETERS_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short instance() {
+ short arrPtr = KMArray.instance((short) 2);
+ return instance(arrPtr);
+ }
+
+ public static short instance(short vals) {
+ short ptr = KMType.instance(HMAC_SHARING_PARAM_TYPE, (short) 2);
+ if (KMArray.cast(vals).length() != 2) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+ return ptr;
+ }
+
+ public static KMHmacSharingParameters cast(short ptr) {
+ if (heap[ptr] != HMAC_SHARING_PARAM_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (heap[arrPtr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public short getVals() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_HMAC_SHARING_PARAMETERS_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short length() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).length();
+ }
+
+ public short getNonce() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(NONCE);
+ }
+
+ public void setNonce(short vals) {
+ KMByteBlob.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(NONCE, vals);
+ }
+
+ public short getSeed() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(SEED);
+ }
+
+ public void setSeed(short vals) {
+ KMByteBlob.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(SEED, vals);
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMInteger.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMInteger.java
new file mode 100644
index 0000000..b09de0f
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMInteger.java
@@ -0,0 +1,215 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * Represents 8 bit, 16 bit, 32 bit and 64 bit unsigned integer. It corresponds to CBOR uint type.
+ * struct{byte INTEGER_TYPE; short length; 4 or 8 bytes of value}
+ */
+public class KMInteger extends KMType {
+
+ public static final byte UINT_32 = 4;
+ public static final byte UINT_64 = 8;
+ private static KMInteger prototype;
+
+ protected KMInteger() {}
+
+ private static KMInteger proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMInteger();
+ }
+ KMType.instanceTable[KM_INTEGER_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // | TYPE(1) | LEN(2) | DATA(4 / 8) |
+ public static short exp() {
+ return KMType.exp(INTEGER_TYPE);
+ }
+
+ // return an empty integer instance
+ public static short instance(short length) {
+ if ((length <= 0) || (length > 8)) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ if (length > 4) {
+ length = UINT_64;
+ } else {
+ length = UINT_32;
+ }
+ return KMType.instance(INTEGER_TYPE, length);
+ }
+
+ public static short instance(byte[] num, short srcOff, short length) {
+ if (length > 8) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ if (length == 1) {
+ return uint_8(num[srcOff]);
+ } else if (length == 2) {
+ return uint_16(Util.getShort(num, srcOff));
+ } else if (length == 4) {
+ return uint_32(num, srcOff);
+ } else {
+ return uint_64(num, srcOff);
+ }
+ }
+
+ public static KMInteger cast(short ptr) {
+ byte[] heap = repository.getHeap();
+ if (heap[ptr] != INTEGER_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ // create integer and copy byte value
+ public static short uint_8(byte num) {
+ short ptr = instance(UINT_32);
+ heap[(short) (ptr + TLV_HEADER_SIZE + 3)] = num;
+ return ptr;
+ }
+
+ // create integer and copy short value
+ public static short uint_16(short num) {
+ short ptr = instance(UINT_32);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), num);
+ return ptr;
+ }
+
+ // create integer and copy integer value
+ public static short uint_32(byte[] num, short offset) {
+ short ptr = instance(UINT_32);
+ Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_32);
+ return ptr;
+ }
+
+ // create integer and copy integer value
+ public static short uint_64(byte[] num, short offset) {
+ short ptr = instance(UINT_64);
+ Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_64);
+ return ptr;
+ }
+
+ public static short compare(short num1, short num2) {
+ short num1Buf = repository.alloc((short) 8);
+ short num2Buf = repository.alloc((short) 8);
+ Util.arrayFillNonAtomic(repository.getHeap(), num1Buf, (short) 8, (byte) 0);
+ Util.arrayFillNonAtomic(repository.getHeap(), num2Buf, (short) 8, (byte) 0);
+ short len = KMInteger.cast(num1).length();
+ KMInteger.cast(num1).getValue(repository.getHeap(), (short) (num1Buf + (short) (8 - len)), len);
+ len = KMInteger.cast(num2).length();
+ KMInteger.cast(num2).getValue(repository.getHeap(), (short) (num2Buf + (short) (8 - len)), len);
+ return KMInteger.unsignedByteArrayCompare(
+ repository.getHeap(), num1Buf, repository.getHeap(), num2Buf, (short) 8);
+ }
+
+ public static byte unsignedByteArrayCompare(
+ byte[] a1, short offset1, byte[] a2, short offset2, short length) {
+ byte count = (byte) 0;
+ short val1 = (short) 0;
+ short val2 = (short) 0;
+
+ for (; count < length; count++) {
+ val1 = (short) (a1[(short) (count + offset1)] & 0x00FF);
+ val2 = (short) (a2[(short) (count + offset2)] & 0x00FF);
+
+ if (val1 < val2) {
+ return -1;
+ }
+ if (val1 > val2) {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ // Get the length of the integer
+ public short length() {
+ return Util.getShort(heap, (short) (getBaseOffset() + 1));
+ }
+
+ // Get the buffer pointer in which blob is contained.
+ public byte[] getBuffer() {
+ return heap;
+ }
+
+ // Get the start of value
+ public short getStartOff() {
+ return (short) (getBaseOffset() + TLV_HEADER_SIZE);
+ }
+
+ public void getValue(byte[] dest, short destOff, short length) {
+ if (length < length()) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ if (length > length()) {
+ length = length();
+ destOff += length;
+ }
+ Util.arrayCopyNonAtomic(heap, getStartOff(), dest, destOff, length);
+ }
+
+ public void setValue(byte[] src, short srcOff) {
+ Util.arrayCopyNonAtomic(src, srcOff, heap, getStartOff(), length());
+ }
+
+ public short value(byte[] dest, short destOff) {
+ Util.arrayCopyNonAtomic(heap, getStartOff(), dest, destOff, length());
+ return length();
+ }
+
+ public short toLittleEndian(byte[] dest, short destOff) {
+ short index = (short) (length() - 1);
+ while (index >= 0) {
+ dest[destOff++] = heap[(short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE + index)];
+ index--;
+ }
+ return length();
+ }
+
+ public short getShort() {
+ return Util.getShort(heap, (short) (getStartOff() + 2));
+ }
+
+ public short getSignificantShort() {
+ return Util.getShort(heap, getStartOff());
+ }
+
+ public byte getByte() {
+ return heap[(short) (getStartOff() + 3)];
+ }
+
+ public boolean isZero() {
+ if (getShort() == 0 && getSignificantShort() == 0) {
+ return true;
+ }
+ return false;
+ }
+
+ protected short getBaseOffset() {
+ return instanceTable[KM_INTEGER_OFFSET];
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java
new file mode 100644
index 0000000..bf45981
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java
@@ -0,0 +1,163 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMIntegerArrayTag represents UINT_REP and ULONG_REP tags specified in keymaster hal specs.
+ * struct{byte TAG_TYPE; short length; struct{short UINT_TAG/ULONG_TAG; short tagKey; short arrPtr},
+ * where arrPtr is the pointer to KMArray of KMInteger instances.
+ */
+public class KMIntegerArrayTag extends KMTag {
+
+ private static final short[] tags = {USER_SECURE_ID};
+ private static KMIntegerArrayTag prototype;
+
+ private KMIntegerArrayTag() {}
+
+ private static KMIntegerArrayTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMIntegerArrayTag();
+ }
+ KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp(short tagType) {
+ if (!validateTagType(tagType)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short arrPtr = KMArray.exp(KMInteger.exp());
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), tagType);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), arrPtr);
+ return ptr;
+ }
+
+ public static short instance(short tagType, short key) {
+ if (!validateTagType(tagType)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (!validateKey(key)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short arrPtr = KMArray.exp();
+ return instance(tagType, key, arrPtr);
+ }
+
+ public static short instance(short tagType, short key, short arrObj) {
+ if (!validateTagType(tagType)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (!validateKey(key)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (heap[arrObj] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), tagType);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), arrObj);
+ return ptr;
+ }
+
+ public static KMIntegerArrayTag cast(short ptr) {
+ if (heap[ptr] != TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short tagType = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (!validateTagType(tagType)) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ private static boolean validateKey(short key) {
+ short index = (short) tags.length;
+ while (--index >= 0) {
+ if (tags[index] == key) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean validateTagType(short tagType) {
+ return (tagType == ULONG_ARRAY_TAG) || (tagType == UINT_ARRAY_TAG);
+ }
+
+ public static boolean contains(short tagId, short tagValue, short params) {
+ short tag = KMKeyParameters.findTag(KMType.UINT_ARRAY_TAG, tagId, params);
+ if (tag != KMType.INVALID_VALUE) {
+ short index = 0;
+ tag = KMIntegerArrayTag.cast(tag).getValues();
+ while (index < KMArray.cast(tag).length()) {
+ if (KMInteger.compare(tagValue, KMArray.cast(tag).get(index)) == 0) {
+ return true;
+ }
+ index++;
+ }
+ }
+ return false;
+ }
+
+ public short getTagType() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short getKey() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short getValues() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+
+ public short length() {
+ short ptr = getValues();
+ return KMArray.cast(ptr).length();
+ }
+
+ public void add(short index, short val) {
+ KMArray arr = KMArray.cast(getValues());
+ arr.add(index, val);
+ }
+
+ public short get(short index) {
+ KMArray arr = KMArray.cast(getValues());
+ return arr.get(index);
+ }
+
+ public boolean contains(short tagValue) {
+ short index = 0;
+ while (index < length()) {
+ if (KMInteger.compare(tagValue, get(index)) == 0) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java
new file mode 100644
index 0000000..d4c4458
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java
@@ -0,0 +1,218 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMIntegerTag represents UINT, ULONG and DATE tags specified in keymaster hal specs. struct{byte
+ * TAG_TYPE; short length; struct{short UINT_TAG/ULONG_TAG/DATE_TAG; short tagKey; 4 or 8 byte
+ * value}}
+ */
+public class KMIntegerTag extends KMTag {
+
+ // Allowed tag keys.
+ private static final short[] tags = {
+ // UINT
+ KEYSIZE,
+ MIN_MAC_LENGTH,
+ MIN_SEC_BETWEEN_OPS,
+ MAX_USES_PER_BOOT,
+ USERID,
+ AUTH_TIMEOUT,
+ OS_VERSION,
+ OS_PATCH_LEVEL,
+ VENDOR_PATCH_LEVEL,
+ BOOT_PATCH_LEVEL,
+ MAC_LENGTH,
+ // ULONG
+ RSA_PUBLIC_EXPONENT,
+ // DATE
+ ACTIVE_DATETIME,
+ ORIGINATION_EXPIRE_DATETIME,
+ USAGE_EXPIRE_DATETIME,
+ CREATION_DATETIME,
+ CERTIFICATE_NOT_BEFORE,
+ CERTIFICATE_NOT_AFTER,
+ USAGE_COUNT_LIMIT,
+ // custom tag
+ AUTH_TIMEOUT_MILLIS,
+ };
+ private static KMIntegerTag prototype;
+
+ private KMIntegerTag() {}
+
+ private static KMIntegerTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMIntegerTag();
+ }
+ KMType.instanceTable[KM_INTEGER_TAG_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp(short tagType) {
+ if (!validateTagType(tagType)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short intPtr = KMInteger.exp();
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), tagType);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), intPtr);
+ return ptr;
+ }
+
+ public static short instance(short tagType, short key) {
+ if (!validateTagType(tagType)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (!validateKey(key)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short intPtr = KMInteger.exp();
+ return instance(tagType, key, intPtr);
+ }
+
+ public static short instance(short tagType, short key, short intObj) {
+ if (!validateTagType(tagType)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (!validateKey(key)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (heap[intObj] != INTEGER_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), tagType);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), intObj);
+ return ptr;
+ }
+
+ public static KMIntegerTag cast(short ptr) {
+ if (heap[ptr] != TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short tagType = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (!validateTagType(tagType)) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ private static boolean validateKey(short key) {
+ short index = (short) tags.length;
+ while (--index >= 0) {
+ if (tags[index] == key) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean validateTagType(short tagType) {
+ return (tagType == DATE_TAG) || (tagType == UINT_TAG) || (tagType == ULONG_TAG);
+ }
+
+ public static short getShortValue(short tagType, short tagKey, short keyParameters) {
+ short ptr;
+ if (tagType == UINT_TAG) {
+ ptr = KMKeyParameters.findTag(KMType.UINT_TAG, tagKey, keyParameters);
+ if (ptr != KMType.INVALID_VALUE) {
+ ptr = KMIntegerTag.cast(ptr).getValue();
+ if (KMInteger.cast(ptr).getSignificantShort() == 0) {
+ return KMInteger.cast(ptr).getShort();
+ }
+ }
+ }
+ return KMType.INVALID_VALUE;
+ }
+
+ public static short getValue(
+ byte[] buf, short offset, short tagType, short tagKey, short keyParameters) {
+ short ptr;
+ if ((tagType == UINT_TAG) || (tagType == ULONG_TAG) || (tagType == DATE_TAG)) {
+ ptr = KMKeyParameters.findTag(tagType, tagKey, keyParameters);
+ if (ptr != KMType.INVALID_VALUE) {
+ ptr = KMIntegerTag.cast(ptr).getValue();
+ return KMInteger.cast(ptr).value(buf, offset);
+ }
+ }
+ return KMType.INVALID_VALUE;
+ }
+
+ public short getTagType() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short getKey() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short getValue() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+
+ public short length() {
+ KMInteger obj = KMInteger.cast(getValue());
+ return obj.length();
+ }
+
+ public boolean isValidKeySize(byte alg) {
+ short val = KMIntegerTag.cast(KMType.instanceTable[KM_INTEGER_TAG_OFFSET]).getValue();
+ if (KMInteger.cast(val).getSignificantShort() != 0) {
+ return false;
+ }
+ val = KMInteger.cast(val).getShort();
+ switch (alg) {
+ case KMType.RSA:
+ if (val == 2048) {
+ return true;
+ }
+ break;
+ case KMType.AES:
+ if (val == 128 || val == 256) {
+ return true;
+ }
+ break;
+ case KMType.DES:
+ if (val == 168) {
+ return true;
+ }
+ break;
+ case KMType.EC:
+ if (val == 256) {
+ return true;
+ }
+ break;
+ case KMType.HMAC:
+ if (val % 8 == 0 && val >= 64 && val <= 512) {
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java
new file mode 100644
index 0000000..37b8b7f
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java
@@ -0,0 +1,124 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMKeyCharacteristics represents KeyCharacteristics structure from android keymaster hal
+ * specifications. It corresponds to CBOR array type. struct{byte KEY_CHAR_TYPE; short length=3;
+ * short arrayPtr} where arrayPtr is a pointer to ordered array with 1 or 3 following elements:
+ * {KMKeyParameters sb; KMKeyParameters tee; KMKeyParameters keystore}
+ */
+public class KMKeyCharacteristics extends KMType {
+
+ public static final byte STRONGBOX_ENFORCED = 0x00;
+ public static final byte TEE_ENFORCED = 0x01;
+ public static final byte KEYSTORE_ENFORCED = 0x02;
+ private static KMKeyCharacteristics prototype;
+
+ private KMKeyCharacteristics() {}
+
+ public static short exp() {
+ short keyParamExp = KMKeyParameters.exp();
+ short arrPtr = KMArray.instance((short) 3);
+
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add(STRONGBOX_ENFORCED, keyParamExp);
+ arr.add(TEE_ENFORCED, keyParamExp);
+ arr.add(KEYSTORE_ENFORCED, keyParamExp);
+ return instance(arrPtr);
+ }
+
+ private static KMKeyCharacteristics proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMKeyCharacteristics();
+ }
+ KMType.instanceTable[KM_KEY_CHARACTERISTICS_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short instance() {
+ short arrPtr = KMArray.instance((short) 3);
+ return instance(arrPtr);
+ }
+
+ public static short instance(short vals) {
+ short ptr = KMType.instance(KEY_CHAR_TYPE, (short) 3);
+ if (KMArray.cast(vals).length() != 3) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+ return ptr;
+ }
+
+ public static KMKeyCharacteristics cast(short ptr) {
+ if (heap[ptr] != KEY_CHAR_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (heap[arrPtr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public short getVals() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_KEY_CHARACTERISTICS_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short length() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).length();
+ }
+
+ public short getKeystoreEnforced() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(KEYSTORE_ENFORCED);
+ }
+
+ public void setKeystoreEnforced(short ptr) {
+ KMKeyParameters.cast(ptr);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(KEYSTORE_ENFORCED, ptr);
+ }
+
+ public short getTeeEnforced() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(TEE_ENFORCED);
+ }
+
+ public void setTeeEnforced(short ptr) {
+ KMKeyParameters.cast(ptr);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(TEE_ENFORCED, ptr);
+ }
+
+ public short getStrongboxEnforced() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(STRONGBOX_ENFORCED);
+ }
+
+ public void setStrongboxEnforced(short ptr) {
+ KMKeyParameters.cast(ptr);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(STRONGBOX_ENFORCED, ptr);
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java
new file mode 100644
index 0000000..54ab6ee
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java
@@ -0,0 +1,472 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMKeyParameters represents KeyParameters structure from android keymaster hal specifications. It
+ * corresponds to CBOR map type. struct{byte KEY_PARAM_TYPE; short length=2; short arrayPtr} where
+ * arrayPtr is a pointer to array with any KMTag subtype instances.
+ */
+public class KMKeyParameters extends KMType {
+
+ private static final short[] customTags = {
+ KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS,
+ };
+ private static final short[] tagArr = {
+ // Unsupported tags.
+ KMType.BOOL_TAG, KMType.TRUSTED_USER_PRESENCE_REQUIRED,
+ KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS
+ };
+ private static final short[] hwEnforcedTagArr = {
+ // HW Enforced
+ KMType.ENUM_ARRAY_TAG, KMType.PURPOSE,
+ KMType.ENUM_TAG, KMType.ALGORITHM,
+ KMType.UINT_TAG, KMType.KEYSIZE,
+ KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT,
+ KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ,
+ KMType.ENUM_ARRAY_TAG, KMType.DIGEST,
+ KMType.ENUM_ARRAY_TAG, KMType.PADDING,
+ KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE,
+ KMType.ENUM_ARRAY_TAG, KMType.RSA_OAEP_MGF_DIGEST,
+ KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED,
+ KMType.BOOL_TAG, KMType.CALLER_NONCE,
+ KMType.UINT_TAG, KMType.MIN_MAC_LENGTH,
+ KMType.ENUM_TAG, KMType.ECCURVE,
+ KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID,
+ KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE,
+ KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY,
+ KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY,
+ KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT,
+ };
+ private static final short[] swEnforcedTagsArr = {
+ KMType.DATE_TAG, KMType.ACTIVE_DATETIME,
+ KMType.DATE_TAG, KMType.ORIGINATION_EXPIRE_DATETIME,
+ KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME,
+ KMType.UINT_TAG, KMType.USERID,
+ KMType.DATE_TAG, KMType.CREATION_DATETIME,
+ KMType.UINT_TAG, KMType.USAGE_COUNT_LIMIT,
+ KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY,
+ KMType.UINT_TAG, KMType.MAX_BOOT_LEVEL,
+ };
+ private static final short[] teeEnforcedTagsArr = {
+ KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID,
+ KMType.UINT_TAG, KMType.AUTH_TIMEOUT,
+ KMType.ENUM_TAG, KMType.USER_AUTH_TYPE,
+ KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED,
+ KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED,
+ };
+ private static final short[] invalidTagsArr = {
+ KMType.BYTES_TAG, KMType.NONCE,
+ KMType.BYTES_TAG, KMType.ASSOCIATED_DATA,
+ KMType.BYTES_TAG, KMType.UNIQUE_ID,
+ KMType.UINT_TAG, KMType.MAC_LENGTH,
+ };
+ private static KMKeyParameters prototype;
+
+ private KMKeyParameters() {}
+
+ private static KMKeyParameters proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMKeyParameters();
+ }
+ KMType.instanceTable[KM_KEY_PARAMETERS_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp() {
+ short arrPtr = KMArray.instance((short) 11);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add((short) 0, KMEnum.instance(KMType.RULE, KMType.FAIL_ON_INVALID_TAGS));
+ arr.add((short) 1, KMIntegerTag.exp(UINT_TAG));
+ arr.add((short) 2, KMIntegerArrayTag.exp(UINT_ARRAY_TAG));
+ arr.add((short) 3, KMIntegerTag.exp(ULONG_TAG));
+ arr.add((short) 4, KMIntegerTag.exp(DATE_TAG));
+ arr.add((short) 5, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG));
+ arr.add((short) 6, KMEnumTag.exp());
+ arr.add((short) 7, KMEnumArrayTag.exp());
+ arr.add((short) 8, KMByteTag.exp());
+ arr.add((short) 9, KMBoolTag.exp());
+ arr.add((short) 10, KMBignumTag.exp());
+ return instance(arrPtr);
+ }
+
+ public static short expAny() {
+ short arrPtr = KMArray.instance((short) 11);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add((short) 0, KMEnum.instance(KMType.RULE, KMType.IGNORE_INVALID_TAGS));
+ arr.add((short) 1, KMIntegerTag.exp(UINT_TAG));
+ arr.add((short) 2, KMIntegerArrayTag.exp(UINT_ARRAY_TAG));
+ arr.add((short) 3, KMIntegerTag.exp(ULONG_TAG));
+ arr.add((short) 4, KMIntegerTag.exp(DATE_TAG));
+ arr.add((short) 5, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG));
+ arr.add((short) 6, KMEnumTag.exp());
+ arr.add((short) 7, KMEnumArrayTag.exp());
+ arr.add((short) 8, KMByteTag.exp());
+ arr.add((short) 9, KMBoolTag.exp());
+ arr.add((short) 10, KMBignumTag.exp());
+ return instance(arrPtr);
+ }
+
+ public static short instance(short vals) {
+ short ptr = KMType.instance(KEY_PARAM_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+ return ptr;
+ }
+
+ public static KMKeyParameters cast(short ptr) {
+ if (heap[ptr] != KEY_PARAM_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (heap[arrPtr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static short findTag(short tagType, short tagKey, short keyParam) {
+ KMKeyParameters instParam = KMKeyParameters.cast(keyParam);
+ return instParam.findTag(tagType, tagKey);
+ }
+
+ public static boolean hasUnsupportedTags(short keyParamsPtr) {
+ byte index = 0;
+ short tagInd;
+ short tagPtr;
+ short tagKey;
+ short tagType;
+ short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals();
+ short len = KMArray.cast(arrPtr).length();
+ while (index < len) {
+ tagInd = 0;
+ tagPtr = KMArray.cast(arrPtr).get(index);
+ tagKey = KMTag.getKey(tagPtr);
+ tagType = KMTag.getTagType(tagPtr);
+ while (tagInd < (short) tagArr.length) {
+ if ((tagArr[tagInd] == tagType) && (tagArr[(short) (tagInd + 1)] == tagKey)) {
+ return true;
+ }
+ tagInd += 2;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ // KDF, ECIES_SINGLE_HASH_MODE missing from types.hal
+ public static short makeSbEnforced(
+ short keyParamsPtr,
+ byte origin,
+ short osVersionObjPtr,
+ short osPatchObjPtr,
+ short vendorPatchObjPtr,
+ short bootPatchObjPtr,
+ byte[] scratchPad) {
+ byte index = 0;
+ short tagInd;
+ short arrInd = 0;
+ short tagPtr;
+ short tagKey;
+ short tagType;
+ short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals();
+ short len = KMArray.cast(arrPtr).length();
+ while (index < len) {
+ tagInd = 0;
+ tagPtr = KMArray.cast(arrPtr).get(index);
+ tagKey = KMTag.getKey(tagPtr);
+ tagType = KMTag.getTagType(tagPtr);
+ if (!isValidTag(tagType, tagKey)) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ while (tagInd < (short) hwEnforcedTagArr.length) {
+ if ((hwEnforcedTagArr[tagInd] == tagType)
+ && (hwEnforcedTagArr[(short) (tagInd + 1)] == tagKey)) {
+ Util.setShort(scratchPad, arrInd, tagPtr);
+ arrInd += 2;
+ break;
+ }
+ tagInd += 2;
+ }
+ index++;
+ }
+ short originTag = KMEnumTag.instance(KMType.ORIGIN, origin);
+ Util.setShort(scratchPad, arrInd, originTag);
+ arrInd += 2;
+ short osVersionTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION, osVersionObjPtr);
+ Util.setShort(scratchPad, arrInd, osVersionTag);
+ arrInd += 2;
+ short osPatchTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, osPatchObjPtr);
+ Util.setShort(scratchPad, arrInd, osPatchTag);
+ arrInd += 2;
+ short vendorPatchTag =
+ KMIntegerTag.instance(KMType.UINT_TAG, KMType.VENDOR_PATCH_LEVEL, vendorPatchObjPtr);
+ Util.setShort(scratchPad, arrInd, vendorPatchTag);
+ arrInd += 2;
+ short bootPatchTag =
+ KMIntegerTag.instance(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, bootPatchObjPtr);
+ Util.setShort(scratchPad, arrInd, bootPatchTag);
+ arrInd += 2;
+ return createKeyParameters(scratchPad, (short) (arrInd / 2));
+ }
+
+ public static short makeHwEnforced(short sb, short tee) {
+ short len = KMKeyParameters.cast(sb).length();
+ len += KMKeyParameters.cast(tee).length();
+ short hwEnf = KMArray.instance(len);
+ sb = KMKeyParameters.cast(sb).getVals();
+ tee = KMKeyParameters.cast(tee).getVals();
+ len = KMArray.cast(sb).length();
+ short src = 0;
+ short dest = 0;
+ short val = 0;
+ while (src < len) {
+ val = KMArray.cast(sb).get(src);
+ KMArray.cast(hwEnf).add(dest, val);
+ src++;
+ dest++;
+ }
+ src = 0;
+ len = KMArray.cast(tee).length();
+ while (src < len) {
+ val = KMArray.cast(tee).get(src);
+ KMArray.cast(hwEnf).add(dest, val);
+ src++;
+ dest++;
+ }
+ return KMKeyParameters.instance(hwEnf);
+ }
+
+ // ALL_USERS, EXPORTABLE missing from types.hal
+ public static short makeKeystoreEnforced(short keyParamsPtr, byte[] scratchPad) {
+ byte index = 0;
+ short tagInd;
+ short arrInd = 0;
+ short tagPtr;
+ short tagKey;
+ short tagType;
+ short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals();
+ short len = KMArray.cast(arrPtr).length();
+ while (index < len) {
+ tagInd = 0;
+ tagPtr = KMArray.cast(arrPtr).get(index);
+ tagKey = KMTag.getKey(tagPtr);
+ tagType = KMTag.getTagType(tagPtr);
+ if (!isValidTag(tagType, tagKey)) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ while (tagInd < (short) swEnforcedTagsArr.length) {
+ if ((swEnforcedTagsArr[tagInd] == tagType)
+ && (swEnforcedTagsArr[(short) (tagInd + 1)] == tagKey)) {
+ Util.setShort(scratchPad, arrInd, tagPtr);
+ arrInd += 2;
+ break;
+ }
+ tagInd += 2;
+ }
+ index++;
+ }
+ return createKeyParameters(scratchPad, (short) (arrInd / 2));
+ }
+
+ public static short makeTeeEnforced(short keyParamsPtr, byte[] scratchPad) {
+ byte index = 0;
+ short tagInd;
+ short arrInd = 0;
+ short tagPtr;
+ short tagKey;
+ short tagType;
+ short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals();
+ short len = KMArray.cast(arrPtr).length();
+ while (index < len) {
+ tagInd = 0;
+ tagPtr = KMArray.cast(arrPtr).get(index);
+ tagKey = KMTag.getKey(tagPtr);
+ tagType = KMTag.getTagType(tagPtr);
+ if (!isValidTag(tagType, tagKey)) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ while (tagInd < (short) teeEnforcedTagsArr.length) {
+ if ((teeEnforcedTagsArr[tagInd] == tagType)
+ && (teeEnforcedTagsArr[(short) (tagInd + 1)] == tagKey)) {
+ Util.setShort(scratchPad, arrInd, tagPtr);
+ arrInd += 2;
+ break;
+ }
+ tagInd += 2;
+ }
+ index++;
+ }
+ return createKeyParameters(scratchPad, (short) (arrInd / 2));
+ }
+
+ public static short makeHidden(short keyParamsPtr, short rootOfTrustBlob, byte[] scratchPad) {
+ short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, keyParamsPtr);
+ if (appId != KMTag.INVALID_VALUE) {
+ appId = KMByteTag.cast(appId).getValue();
+ if (KMByteBlob.cast(appId).length() == 0) {
+ appId = KMTag.INVALID_VALUE;
+ }
+ }
+ short appData =
+ KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, keyParamsPtr);
+ if (appData != KMTag.INVALID_VALUE) {
+ appData = KMByteTag.cast(appData).getValue();
+ if (KMByteBlob.cast(appData).length() == 0) {
+ appData = KMTag.INVALID_VALUE;
+ }
+ }
+ return makeHidden(appId, appData, rootOfTrustBlob, scratchPad);
+ }
+
+ public static short makeHidden(
+ short appIdBlob, short appDataBlob, short rootOfTrustBlob, byte[] scratchPad) {
+ // Order in which the hidden array is created should not change.
+ short index = 0;
+ KMByteBlob.cast(rootOfTrustBlob);
+ Util.setShort(scratchPad, index, rootOfTrustBlob);
+ index += 2;
+ if (appIdBlob != KMTag.INVALID_VALUE) {
+ KMByteBlob.cast(appIdBlob);
+ Util.setShort(scratchPad, index, appIdBlob);
+ index += 2;
+ }
+ if (appDataBlob != KMTag.INVALID_VALUE) {
+ KMByteBlob.cast(appDataBlob);
+ Util.setShort(scratchPad, index, appDataBlob);
+ index += 2;
+ }
+ return createKeyParameters(scratchPad, (short) (index / 2));
+ }
+
+ public static boolean isValidTag(short tagType, short tagKey) {
+ short index = 0;
+ if (tagKey == KMType.INVALID_TAG) {
+ return false;
+ }
+ while (index < invalidTagsArr.length) {
+ if ((tagType == invalidTagsArr[index]) && (tagKey == invalidTagsArr[(short) (index + 1)])) {
+ return false;
+ }
+ index += 2;
+ }
+ return true;
+ }
+
+ public static short createKeyParameters(byte[] ptrArr, short len) {
+ short arrPtr = KMArray.instance(len);
+ short index = 0;
+ short ptr = 0;
+ while (index < len) {
+ KMArray.cast(arrPtr).add(index, Util.getShort(ptrArr, ptr));
+ index++;
+ ptr += 2;
+ }
+ return KMKeyParameters.instance(arrPtr);
+ }
+
+ public static short makeCustomTags(short keyParams, byte[] scratchPad) {
+ short index = 0;
+ short tagPtr;
+ short offset = 0;
+ short len = (short) customTags.length;
+ short tagType;
+ while (index < len) {
+ tagType = customTags[(short) (index + 1)];
+ switch (tagType) {
+ case KMType.AUTH_TIMEOUT_MILLIS:
+ short authTimeOutTag =
+ KMKeyParameters.cast(keyParams).findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT);
+ if (authTimeOutTag != KMType.INVALID_VALUE) {
+ tagPtr = createAuthTimeOutMillisTag(authTimeOutTag, scratchPad, offset);
+ Util.setShort(scratchPad, offset, tagPtr);
+ offset += 2;
+ }
+ break;
+ default:
+ break;
+ }
+ index += 2;
+ }
+ return createKeyParameters(scratchPad, (short) (offset / 2));
+ }
+
+ public static short createAuthTimeOutMillisTag(
+ short authTimeOutTag, byte[] scratchPad, short offset) {
+ short authTime = KMIntegerTag.cast(authTimeOutTag).getValue();
+ Util.arrayFillNonAtomic(scratchPad, offset, (short) 40, (byte) 0);
+ Util.arrayCopyNonAtomic(
+ KMInteger.cast(authTime).getBuffer(),
+ KMInteger.cast(authTime).getStartOff(),
+ scratchPad,
+ (short) (offset + 8 - KMInteger.cast(authTime).length()),
+ KMInteger.cast(authTime).length());
+ KMUtils.convertToMilliseconds(scratchPad, offset, (short) (offset + 8), (short) (offset + 16));
+ return KMIntegerTag.instance(
+ KMType.ULONG_TAG,
+ KMType.AUTH_TIMEOUT_MILLIS,
+ KMInteger.uint_64(scratchPad, (short) (offset + 8)));
+ }
+
+ public short getVals() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_KEY_PARAMETERS_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short length() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).length();
+ }
+
+ public short findTag(short tagType, short tagKey) {
+ KMArray vals = KMArray.cast(getVals());
+ short index = 0;
+ short length = vals.length();
+ short key;
+ short type;
+ short ret = KMType.INVALID_VALUE;
+ short obj;
+ while (index < length) {
+ obj = vals.get(index);
+ key = KMTag.getKey(obj);
+ type = KMTag.getTagType(obj);
+ if ((tagKey == key) && (tagType == type)) {
+ ret = obj;
+ break;
+ }
+ index++;
+ }
+ return ret;
+ }
+
+ public void deleteCustomTags() {
+ short arrPtr = getVals();
+ short index = (short) (customTags.length - 1);
+ short obj;
+ while (index >= 0) {
+ obj = findTag(customTags[(short) (index - 1)], customTags[index]);
+ if (obj != KMType.INVALID_VALUE) {
+ KMArray.cast(arrPtr).deleteLastEntry();
+ }
+ index -= 2;
+ }
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java
new file mode 100644
index 0000000..02401e7
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java
@@ -0,0 +1,5146 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMAttestationCert;
+import com.android.javacard.seprovider.KMDataStoreConstants;
+import com.android.javacard.seprovider.KMException;
+import com.android.javacard.seprovider.KMKey;
+import com.android.javacard.seprovider.KMOperation;
+import com.android.javacard.seprovider.KMSEProvider;
+import javacard.framework.APDU;
+import javacard.framework.Applet;
+import javacard.framework.AppletEvent;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+import javacard.security.CryptoException;
+import javacardx.apdu.ExtendedLength;
+
+/**
+ * KMKeymasterApplet implements the javacard applet. It creates an instance of the KMRepository and
+ * other install time objects. It also implements the keymaster state machine and handles javacard
+ * applet life cycle events.
+ */
+public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLength {
+
+ // Constants.
+ // Represents RSA_PUBLIC_EXPONENT value 65537.
+ public static final byte[] F4 = {0x01, 0x00, 0x01};
+ // Block size of AES algorithm.
+ public static final byte AES_BLOCK_SIZE = 16;
+ // Block size of DES algorithm.
+ public static final byte DES_BLOCK_SIZE = 8;
+ // The Key size in bits for the master key.
+ public static final short MASTER_KEY_SIZE = 128;
+ // The Key size of the transport key used in importWrappedKey.
+ public static final byte WRAPPING_KEY_SIZE = 32;
+ // The maximum allowed simultaneous operations.
+ public static final byte MAX_OPERATIONS_COUNT = 4;
+ // The size of the verified boot key in ROT.
+ public static final byte VERIFIED_BOOT_KEY_SIZE = 32;
+ // The size of the verified boot hash in ROT.
+ public static final byte VERIFIED_BOOT_HASH_SIZE = 32;
+ // The security level of TEE.
+ public static final byte TRUSTED_ENVIRONMENT = 1;
+ // "Keymaster HMAC Verification" - used for HMAC key verification.
+ public static final byte[] sharingCheck = {
+ 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x48, 0x4D, 0x41, 0x43, 0x20, 0x56,
+ 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E
+ };
+ // The ckdfLabel "KeymasterSharedMac" in hex.
+ public static final byte[] ckdfLabel = {
+ 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4D,
+ 0x61, 0x63
+ };
+ // The "Auth Verification" string in hex.
+ public static final byte[] authVerification = {
+ 0x41, 0x75, 0x74, 0x68, 0x20, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F,
+ 0x6E
+ };
+ // The "confirmation token" string in hex.
+ public static final byte[] confirmationToken = {
+ 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x72, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x74, 0x6F, 0x6B,
+ 0x65, 0x6E
+ };
+ // The maximum buffer size for the encoded COSE structures.
+ public static final short MAX_COSE_BUF_SIZE = (short) 1024;
+ // Maximum allowed buffer size for to encode the key parameters
+ // which is used while creating mac for key parameters.
+ public static final short MAX_KEY_PARAMS_BUF_SIZE = (short) 3072; // 3K
+ // Temporary variables array size to store intermediary results.
+ public static final byte TMP_VARIABLE_ARRAY_SIZE = 5;
+ // Data Dictionary items
+ // Maximum Dictionary size.
+ public static final byte DATA_ARRAY_SIZE = 39;
+ // Below are the offsets of the data dictionary items.
+ public static final byte KEY_PARAMETERS = 0;
+ public static final byte KEY_CHARACTERISTICS = 1;
+ public static final byte HIDDEN_PARAMETERS = 2;
+ public static final byte HW_PARAMETERS = 3;
+ public static final byte SW_PARAMETERS = 4;
+ public static final byte AUTH_DATA = 5;
+ public static final byte AUTH_TAG = 6;
+ public static final byte NONCE = 7;
+ public static final byte KEY_BLOB = 8;
+ public static final byte AUTH_DATA_LENGTH = 9;
+ public static final byte SECRET = 10;
+ public static final byte ROT = 11;
+ public static final byte DERIVED_KEY = 12;
+ public static final byte RSA_PUB_EXPONENT = 13;
+ public static final byte APP_ID = 14;
+ public static final byte APP_DATA = 15;
+ public static final byte PUB_KEY = 16;
+ public static final byte IMPORTED_KEY_BLOB = 17;
+ public static final byte ORIGIN = 18;
+ public static final byte NOT_USED = 19;
+ public static final byte MASKING_KEY = 20;
+ public static final byte HMAC_SHARING_PARAMS = 21;
+ public static final byte OP_HANDLE = 22;
+ public static final byte IV = 23;
+ public static final byte INPUT_DATA = 24;
+ public static final byte OUTPUT_DATA = 25;
+ public static final byte HW_TOKEN = 26;
+ public static final byte VERIFICATION_TOKEN = 27;
+ public static final byte SIGNATURE = 28;
+ public static final byte ATTEST_KEY_BLOB = 29;
+ public static final byte ATTEST_KEY_PARAMS = 30;
+ public static final byte ATTEST_KEY_ISSUER = 31;
+ public static final byte CERTIFICATE = 32;
+ public static final byte PLAIN_SECRET = 33;
+ public static final byte TEE_PARAMETERS = 34;
+ public static final byte SB_PARAMETERS = 35;
+ public static final byte CONFIRMATION_TOKEN = 36;
+ public static final byte KEY_BLOB_VERSION_DATA_OFFSET = 37;
+ public static final byte CUSTOM_TAGS = 38;
+ // Below are the Keyblob offsets.
+ public static final byte KEY_BLOB_VERSION_OFFSET = 0;
+ public static final byte KEY_BLOB_SECRET = 1;
+ public static final byte KEY_BLOB_NONCE = 2;
+ public static final byte KEY_BLOB_AUTH_TAG = 3;
+ public static final byte KEY_BLOB_PARAMS = 4;
+ public static final byte KEY_BLOB_CUSTOM_TAGS = 5;
+ public static final byte KEY_BLOB_PUB_KEY = 6;
+ // AES GCM Auth tag length to be used while encrypting or decrypting the KeyBlob.
+ public static final byte AES_GCM_AUTH_TAG_LENGTH = 16;
+ // AES GCM nonce length to be used while encrypting or decrypting the KeyBlob.
+ public static final byte AES_GCM_NONCE_LENGTH = 12;
+ // KEYBLOB_CURRENT_VERSION goes into KeyBlob and will affect all
+ // the KeyBlobs if it is changed. please increment this
+ // version number whenever you change anything related to
+ // KeyBlob (structure, encryption algorithm etc).
+ public static final short KEYBLOB_CURRENT_VERSION = 3;
+ // KeyBlob Verion 1 constant.
+ public static final short KEYBLOB_VERSION_1 = 1;
+ // Array sizes of KeyBlob under different versions.
+ // The array size of a Symmetric key's KeyBlob for Version2 and Version3
+ public static final byte SYM_KEY_BLOB_SIZE_V2_V3 = 6;
+ // The array size of a Asymmetric key's KeyBlob for Version2 and Version3
+ public static final byte ASYM_KEY_BLOB_SIZE_V2_V3 = 7;
+ // The array size of a Symmetric key's KeyBlob for Version1
+ public static final byte SYM_KEY_BLOB_SIZE_V1 = 5;
+ // The array size of a Asymmetric key's KeyBlob for Version1
+ public static final byte ASYM_KEY_BLOB_SIZE_V1 = 6;
+ // The array size of a Symmetric key's KeyBlob for Version0
+ public static final byte SYM_KEY_BLOB_SIZE_V0 = 4;
+ // The array size of a Asymmetric key's KeyBlob for Version0
+ public static final byte ASYM_KEY_BLOB_SIZE_V0 = 5;
+ // Key type constants
+ // Represents the type of the Symmetric key.
+ public static final byte SYM_KEY_TYPE = 0;
+ // Represents the type of the Asymmetric key.
+ public static final byte ASYM_KEY_TYPE = 1;
+ // SHA-256 Digest length in bits
+ public static final short SHA256_DIGEST_LEN_BITS = 256;
+ // Minimum HMAC length in bits
+ public static final short MIN_HMAC_LENGTH_BITS = 64;
+ // Below are the constants for provision reporting status
+ public static final short NOT_PROVISIONED = 0x0000;
+ public static final short PROVISION_STATUS_ATTESTATION_KEY = 0x0001;
+ public static final short PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x0002;
+ public static final short PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x0004;
+ public static final short PROVISION_STATUS_ATTEST_IDS = 0x0008;
+ public static final short PROVISION_STATUS_PRESHARED_SECRET = 0x0010;
+ public static final short PROVISION_STATUS_PROVISIONING_LOCKED = 0x0020;
+ public static final short PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR = 0x0040;
+ public static final short PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = 0x0080;
+ public static final short PROVISION_STATUS_SE_LOCKED = 0x0100;
+ public static final short PROVISION_STATUS_OEM_PUBLIC_KEY = 0x0200;
+ public static final short PROVISION_STATUS_SECURE_BOOT_MODE = 0x0400;
+ // This is the P1P2 constant of the APDU command header.
+ protected static final short KM_HAL_VERSION = (short) 0x5000;
+ // OEM lock / unlock verification constants.
+ // This is the verification label to authenticate the OEM to lock the provisioning for the
+ // OEM provision commands.
+ protected static final byte[] OEM_LOCK_PROVISION_VERIFICATION_LABEL = { // "OEM Provisioning Lock"
+ 0x4f, 0x45, 0x4d, 0x20, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e, 0x67,
+ 0x20, 0x4c, 0x6f, 0x63, 0x6b
+ };
+ // This is the verification label to authenticate the OEM to unlock the provisioning for the
+ // OEM provision commands.
+ protected static final byte[] OEM_UNLOCK_PROVISION_VERIFICATION_LABEL = { // "Enable RMA"
+ 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x52, 0x4d, 0x41
+ };
+ // The maximum size of the seed allowed for the RNG entropy
+ protected static final short MAX_SEED_SIZE = 2048;
+ // The maximum size of the certificate returned by the generate key command.
+ protected static final short MAX_CERT_SIZE = 3000;
+ // The maximum size of the encoded key characteristics in CBOR.
+ protected static final short MAX_KEY_CHARS_SIZE = 512;
+ // The maximum size of the serialized KeyBlob.
+ protected static final short MAX_KEYBLOB_SIZE = 1024;
+ // The maximum size of the Auth data which is used while encrypting/decrypting the KeyBlob.
+ private static final short MAX_AUTH_DATA_SIZE = (short) 512;
+ // The minimum bits in length for AES-GCM tag.
+ private static final short MIN_GCM_TAG_LENGTH_BITS = (short) 96;
+ // The maximum bits in length for AES-GCM tag.
+ private static final short MAX_GCM_TAG_LENGTH_BITS = (short) 128;
+ // Subject is a fixed field with only CN= Android Keystore Key - same for all the keys
+ private static final byte[] defaultSubject = {
+ 0x30, 0x1F, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x14, 0x41, 0x6e, 0x64,
+ 0x72, 0x6f, 0x69, 0x64, 0x20, 0x4B, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x4B, 0x65,
+ 0x79
+ };
+ // Constant for Dec 31, 9999 in milliseconds in hex.
+ private static final byte[] dec319999Ms = {
+ (byte) 0, (byte) 0, (byte) 0xE6, (byte) 0x77, (byte) 0xD2, (byte) 0x1F, (byte) 0xD8, (byte) 0x18
+ };
+ // Dec 31, 9999 represented in Generalized time format YYYYMMDDhhmmssZ.
+ // "99991231235959Z" in hex. Refer RFC 5280 section 4.1.2.5.2
+ private static final byte[] dec319999 = {
+ 0x39, 0x39, 0x39, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a,
+ };
+ // Jan 01, 1970 represented in UTC time format YYMMDDhhmmssZ.
+ // "700101000000Z" in hex. Refer RFC 5280 section 4.1.2.5.1
+ private static final byte[] jan01970 = {
+ 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a,
+ };
+ // The KeyMint name "JavacardKeymintDevice" returned from getHwInfo.
+ private static final byte[] JavacardKeymintDevice = {
+ 0x4a, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x6d, 0x69, 0x6e, 0x74, 0x44,
+ 0x65, 0x76, 0x69, 0x63, 0x65,
+ };
+ // The KeyMint author name "Google" returned from getHwInfo.
+ public static final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65};
+ // Attestation ID tags to be included in attestation record.
+ private static final short[] attTags = {
+ KMType.ATTESTATION_ID_BRAND,
+ KMType.ATTESTATION_ID_DEVICE,
+ KMType.ATTESTATION_ID_IMEI,
+ KMType.ATTESTATION_ID_MANUFACTURER,
+ KMType.ATTESTATION_ID_MEID,
+ KMType.ATTESTATION_ID_MODEL,
+ KMType.ATTESTATION_ID_PRODUCT,
+ KMType.ATTESTATION_ID_SERIAL
+ };
+ // Below are the constants of instructions in APDU command header.
+ // Top 32 commands are reserved for provisioning.
+ private static final byte KEYMINT_CMD_APDU_START = 0x20;
+ // RKP
+ public static final byte INS_GET_RKP_HARDWARE_INFO = KEYMINT_CMD_APDU_START + 27; // 0x3B
+ public static final byte INS_GENERATE_RKP_KEY_CMD = KEYMINT_CMD_APDU_START + 28; // 0x3C
+ public static final byte INS_BEGIN_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 29; // 0x3D
+ public static final byte INS_UPDATE_KEY_CMD = KEYMINT_CMD_APDU_START + 30; // 0x3E
+ // Constant
+ public static final byte INS_UPDATE_EEK_CHAIN_CMD = KEYMINT_CMD_APDU_START + 31; // 0x3F
+ public static final byte INS_UPDATE_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 32; // 0x40
+ public static final byte INS_FINISH_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 33; // 0x41
+ public static final byte INS_GET_RESPONSE_CMD = KEYMINT_CMD_APDU_START + 34; // 0x42
+ private static final byte INS_GENERATE_KEY_CMD = KEYMINT_CMD_APDU_START + 1; // 0x21
+ private static final byte INS_IMPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 2; // 0x22
+ private static final byte INS_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 3; // 0x23
+ private static final byte INS_EXPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 4; // 0x24
+ private static final byte INS_ATTEST_KEY_CMD = KEYMINT_CMD_APDU_START + 5; // 0x25
+ private static final byte INS_UPGRADE_KEY_CMD = KEYMINT_CMD_APDU_START + 6; // 0x26
+ private static final byte INS_DELETE_KEY_CMD = KEYMINT_CMD_APDU_START + 7; // 0x27
+ private static final byte INS_DELETE_ALL_KEYS_CMD = KEYMINT_CMD_APDU_START + 8; // 0x28
+ private static final byte INS_ADD_RNG_ENTROPY_CMD = KEYMINT_CMD_APDU_START + 9; // 0x29
+ private static final byte INS_COMPUTE_SHARED_HMAC_CMD = KEYMINT_CMD_APDU_START + 10; // 0x2A
+ private static final byte INS_DESTROY_ATT_IDS_CMD = KEYMINT_CMD_APDU_START + 11; // 0x2B
+ private static final byte INS_VERIFY_AUTHORIZATION_CMD = KEYMINT_CMD_APDU_START + 12; // 0x2C
+ private static final byte INS_GET_HMAC_SHARING_PARAM_CMD = KEYMINT_CMD_APDU_START + 13; // 0x2D
+ private static final byte INS_GET_KEY_CHARACTERISTICS_CMD = KEYMINT_CMD_APDU_START + 14; // 0x2E
+ private static final byte INS_GET_HW_INFO_CMD = KEYMINT_CMD_APDU_START + 15; // 0x2F
+ private static final byte INS_BEGIN_OPERATION_CMD = KEYMINT_CMD_APDU_START + 16; // 0x30
+ private static final byte INS_UPDATE_OPERATION_CMD = KEYMINT_CMD_APDU_START + 17; // 0x31
+ private static final byte INS_FINISH_OPERATION_CMD = KEYMINT_CMD_APDU_START + 18; // 0x32
+ private static final byte INS_ABORT_OPERATION_CMD = KEYMINT_CMD_APDU_START + 19; // 0x33
+ private static final byte INS_DEVICE_LOCKED_CMD = KEYMINT_CMD_APDU_START + 20; // 0x34
+ private static final byte INS_EARLY_BOOT_ENDED_CMD = KEYMINT_CMD_APDU_START + 21; // 0x35
+ private static final byte INS_GET_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 22; // 0x36
+ private static final byte INS_UPDATE_AAD_OPERATION_CMD = KEYMINT_CMD_APDU_START + 23; // 0x37
+ private static final byte INS_BEGIN_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 24; // 0x38
+ private static final byte INS_FINISH_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 25; // 0x39
+ private static final byte INS_INIT_STRONGBOX_CMD = KEYMINT_CMD_APDU_START + 26; // 0x3A
+ // The instructions from 0x43 to 0x4C will be reserved for KeyMint 1.0 for any future use.
+ // KeyMint 2.0 Instructions
+ private static final byte INS_GET_ROT_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 45; // 0x4D
+ private static final byte INS_GET_ROT_DATA_CMD = KEYMINT_CMD_APDU_START + 46; // 0x4E
+ private static final byte INS_SEND_ROT_DATA_CMD = KEYMINT_CMD_APDU_START + 47; // 0x4F
+ private static final byte KEYMINT_CMD_APDU_END = KEYMINT_CMD_APDU_START + 48; // 0x50
+ private static final byte INS_END_KM_CMD = 0x7F;
+ // Instruction values from 0xCD to 0xFF are completely reserved for Vendors to use and
+ // will never be used by the base line code in future.
+ private static final byte INS_KM_VENDOR_START_CMD = (byte) 0xCD;
+ private static final byte INS_KM_VENDOR_END_CMD = (byte) 0xFF;
+ // Index in apduFlagsStatus[] to check if instruction command is case 4 type in the Apdu
+ protected static final byte APDU_CASE4_COMMAND_STATUS_INDEX = 0;
+ // Index in apduFlagsStatus[] to check if Apdu setIncomingAndReceive function is called
+ protected static final byte APDU_INCOMING_AND_RECEIVE_STATUS_INDEX = 1;
+ // The maximum buffer size of combined seed and nonce.
+ private static final byte HMAC_SHARED_PARAM_MAX_SIZE = 64;
+ // Instance of RemotelyProvisionedComponentDevice, used to redirect the rkp commands.
+ protected static KMRemotelyProvisionedComponentDevice rkp;
+ // Instance of Cbor encoder.
+ protected static KMEncoder encoder;
+ // Instance of Cbor decoder.
+ protected static KMDecoder decoder;
+ // Instance of KMRepository class for memory management.
+ protected static KMRepository repository;
+ // Instance of KMSEProvider for doing crypto operations.
+ protected static KMSEProvider seProvider;
+ // Holds the instance of KMOperationStates. A maximum of 4 instances of KMOperatioState is
+ // allowed.
+ protected static KMOperationState[] opTable;
+ // Instance of KMKeymintDataStore which helps to store and retrieve the data.
+ protected static KMKeymintDataStore kmDataStore;
+
+ // Short array used to store the temporary results.
+ protected static short[] tmpVariables;
+ // Short array used to hold the dictionary items.
+ protected static short[] data;
+ // Buffer to store the transportKey which is used in the import wrapped key. Import wrapped
+ // key is divided into two stages 1. BEGIN_IMPORT_WRAPPED_KEY 2. FINISH_IMPORT_WRAPPED_KEY.
+ // The transportKey is retrieved and stored in this buffer at stage 1) and is later used in
+ // stage 2).
+ protected static byte[] wrappingKey;
+ // Transient byte array used to store the flags if APDU command type is of case 4 and if
+ // APDU setIncomingAndReceive() function is called or not.
+ protected static byte[] apduStatusFlags;
+
+ /** Registers this applet. */
+ protected KMKeymasterApplet(KMSEProvider seImpl) {
+ seProvider = seImpl;
+ boolean isUpgrading = seProvider.isUpgrading();
+ repository = new KMRepository(isUpgrading);
+ encoder = new KMEncoder();
+ decoder = new KMDecoder();
+ kmDataStore = new KMKeymintDataStore(seProvider, repository);
+ data = JCSystem.makeTransientShortArray(DATA_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT);
+ tmpVariables =
+ JCSystem.makeTransientShortArray(TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT);
+ wrappingKey =
+ JCSystem.makeTransientByteArray((short) (WRAPPING_KEY_SIZE + 1), JCSystem.CLEAR_ON_RESET);
+ resetWrappingKey();
+ apduStatusFlags = JCSystem.makeTransientByteArray((short) 2, JCSystem.CLEAR_ON_RESET);
+ opTable = new KMOperationState[MAX_OPERATIONS_COUNT];
+ short index = 0;
+ while (index < MAX_OPERATIONS_COUNT) {
+ opTable[index] = new KMOperationState();
+ index++;
+ }
+ KMType.initialize();
+ if (!isUpgrading) {
+ kmDataStore.createMasterKey(MASTER_KEY_SIZE);
+ }
+ // initialize default values
+ initHmacNonceAndSeed();
+ rkp =
+ new KMRemotelyProvisionedComponentDevice(
+ encoder, decoder, repository, seProvider, kmDataStore);
+ }
+
+ /** Sends a response, may be extended response, as requested by the command. */
+ public static void sendOutgoing(APDU apdu, short resp) {
+ // TODO handle the extended buffer stuff. We can reuse this.
+ short bufferStartOffset = repository.allocAvailableMemory();
+ byte[] buffer = repository.getHeap();
+ // TODO we can change the following to incremental send.
+ short bufferLength =
+ encoder.encode(resp, buffer, bufferStartOffset, repository.getHeapReclaimIndex());
+ if (((short) (bufferLength + bufferStartOffset)) > ((short) repository.getHeap().length)) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+
+ /* In T=0 protocol, On a case 4 command, setIncomingAndReceive() must
+ * be invoked prior to calling setOutgoing(). Otherwise, erroneous
+ * behavior may result
+ * */
+ if (apduStatusFlags[APDU_CASE4_COMMAND_STATUS_INDEX] == 1
+ && apduStatusFlags[APDU_INCOMING_AND_RECEIVE_STATUS_INDEX] == 0
+ && APDU.getProtocol() == APDU.PROTOCOL_T0) {
+ apdu.setIncomingAndReceive();
+ }
+ // Send data
+ apdu.setOutgoing();
+ apdu.setOutgoingLength(bufferLength);
+ apdu.sendBytesLong(buffer, bufferStartOffset, bufferLength);
+ }
+
+ /** Receives data, which can be extended data, as requested by the command instance. */
+ public static short receiveIncoming(APDU apdu, short reqExp) {
+ byte[] srcBuffer = apdu.getBuffer();
+ short recvLen = apdu.setIncomingAndReceive();
+ short srcOffset = apdu.getOffsetCdata();
+ apduStatusFlags[APDU_INCOMING_AND_RECEIVE_STATUS_INDEX] = 1;
+ // TODO add logic to handle the extended length buffer. In this case the memory can be reused
+ // from extended buffer.
+ short bufferLength = apdu.getIncomingLength();
+ short bufferStartOffset = repository.allocReclaimableMemory(bufferLength);
+ short index = bufferStartOffset;
+ byte[] buffer = repository.getHeap();
+ while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) {
+ Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen);
+ index += recvLen;
+ recvLen = apdu.receiveBytes(srcOffset);
+ }
+ short req = decoder.decode(reqExp, buffer, bufferStartOffset, bufferLength);
+ repository.reclaimMemory(bufferLength);
+ return req;
+ }
+
+ private static short createKeyBlobInstance(byte keyType) {
+ short arrayLen = 0;
+ switch (keyType) {
+ case ASYM_KEY_TYPE:
+ arrayLen = ASYM_KEY_BLOB_SIZE_V2_V3;
+ break;
+ case SYM_KEY_TYPE:
+ arrayLen = SYM_KEY_BLOB_SIZE_V2_V3;
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ return KMArray.instance(arrayLen);
+ }
+
+ private static void addTags(short params, boolean hwEnforced, KMAttestationCert cert) {
+ short index = 0;
+ short arr = KMKeyParameters.cast(params).getVals();
+ short len = KMArray.cast(arr).length();
+ short tag;
+ while (index < len) {
+ tag = KMArray.cast(arr).get(index);
+ cert.extensionTag(tag, hwEnforced);
+ index++;
+ }
+ }
+
+ private static void setUniqueId(KMAttestationCert cert, short attAppId, byte[] scratchPad) {
+ if (!KMTag.isPresent(data[KEY_PARAMETERS], KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID)) {
+ return;
+ }
+ // temporal count T
+ short time =
+ KMKeyParameters.findTag(KMType.DATE_TAG, KMType.CREATION_DATETIME, data[KEY_PARAMETERS]);
+ if (time == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ time = KMIntegerTag.cast(time).getValue();
+
+ // Reset After Rotation R - it will be part of HW Enforced key
+ // characteristics
+ byte resetAfterRotation = 0;
+ if (KMTag.isPresent(data[KEY_PARAMETERS], KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION)) {
+ resetAfterRotation = 0x01;
+ }
+
+ cert.makeUniqueId(
+ scratchPad,
+ (short) 0,
+ KMInteger.cast(time).getBuffer(),
+ KMInteger.cast(time).getStartOff(),
+ KMInteger.cast(time).length(),
+ KMByteBlob.cast(attAppId).getBuffer(),
+ KMByteBlob.cast(attAppId).getStartOff(),
+ KMByteBlob.cast(attAppId).length(),
+ resetAfterRotation,
+ kmDataStore.getMasterKey());
+ }
+
+ private static void validateRSAKey(byte[] scratchPad) {
+ // Read key size
+ if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ if (!KMTag.isValidPublicExponent(data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ }
+
+ // Generate key handlers
+ private static void generateRSAKey(byte[] scratchPad) {
+ // Validate RSA Key
+ validateRSAKey(scratchPad);
+ // Now generate 2048 bit RSA keypair for the given exponent
+ short[] lengths = tmpVariables;
+ data[PUB_KEY] = KMByteBlob.instance((short) 256);
+ data[SECRET] = KMByteBlob.instance((short) 256);
+ seProvider.createAsymmetricKey(
+ KMType.RSA,
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ KMByteBlob.cast(data[PUB_KEY]).getBuffer(),
+ KMByteBlob.cast(data[PUB_KEY]).getStartOff(),
+ KMByteBlob.cast(data[PUB_KEY]).length(),
+ lengths);
+
+ data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]);
+ }
+
+ private static void validateAESKey() {
+ // Read key size
+ if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ // Read Block mode - array of byte values
+ if (KMTag.isPresent(data[KEY_PARAMETERS], KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE)) {
+ short blockModes =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]);
+ // If it is a GCM mode
+ if (KMEnumArrayTag.cast(blockModes).contains(KMType.GCM)) {
+ // Min mac length must be present
+ KMTag.assertPresence(
+ data[KEY_PARAMETERS],
+ KMType.UINT_TAG,
+ KMType.MIN_MAC_LENGTH,
+ KMError.MISSING_MIN_MAC_LENGTH);
+ short macLength =
+ KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]);
+ macLength = KMIntegerTag.cast(macLength).getValue();
+ // Validate the MIN_MAC_LENGTH for AES - should be multiple of 8, less then 128 bits
+ // and greater the 96 bits
+ if (KMInteger.cast(macLength).getSignificantShort() != 0
+ || KMInteger.cast(macLength).getShort() > 128
+ || KMInteger.cast(macLength).getShort() < 96
+ || (KMInteger.cast(macLength).getShort() % 8) != 0) {
+ KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH);
+ }
+ }
+ }
+ }
+
+ private static void generateAESKey(byte[] scratchPad) {
+ validateAESKey();
+ short keysize =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+ short len = seProvider.createSymmetricKey(KMType.AES, keysize, scratchPad, (short) 0);
+ data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len);
+ data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+ }
+
+ private static void validateECKeys() {
+ // Read key size
+ short ecCurve = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]);
+ /* In KeyMint 2.0, If EC_CURVE not provided, generateKey
+ * must return ErrorCode::UNSUPPORTED_KEY_SIZE or ErrorCode::UNSUPPORTED_EC_CURVE.
+ */
+ if (ecCurve != KMType.P_256) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ short ecKeySize = KMEnumTag.getValue(KMType.KEYSIZE, data[KEY_PARAMETERS]);
+ if ((ecKeySize != KMType.INVALID_VALUE) && !KMTag.isValidKeySize(data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ }
+
+ private static void generateECKeys(byte[] scratchPad) {
+ validateECKeys();
+ short[] lengths = tmpVariables;
+ seProvider.createAsymmetricKey(
+ KMType.EC,
+ scratchPad,
+ (short) 0,
+ (short) 128,
+ scratchPad,
+ (short) 128,
+ (short) 128,
+ lengths);
+ data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 128, lengths[1]);
+ data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, lengths[0]);
+ data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]);
+ }
+
+ private static void validateTDESKey() {
+ if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ // Read Minimum Mac length - it must not be present
+ KMTag.assertAbsence(
+ data[KEY_PARAMETERS], KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMError.INVALID_TAG);
+ }
+
+ private static void generateTDESKey(byte[] scratchPad) {
+ validateTDESKey();
+ short len = seProvider.createSymmetricKey(KMType.DES, (short) 168, scratchPad, (short) 0);
+ data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len);
+ data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+ }
+
+ private static void validateHmacKey() {
+ // If params does not contain any digest throw unsupported digest error.
+ KMTag.assertPresence(
+ data[KEY_PARAMETERS], KMType.ENUM_ARRAY_TAG, KMType.DIGEST, KMError.UNSUPPORTED_DIGEST);
+
+ // check whether digest sizes are greater then or equal to min mac length.
+ // Only SHA256 digest must be supported.
+ if (!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+ }
+ // Read Minimum Mac length
+ KMTag.assertPresence(
+ data[KEY_PARAMETERS],
+ KMType.UINT_TAG,
+ KMType.MIN_MAC_LENGTH,
+ KMError.MISSING_MIN_MAC_LENGTH);
+ short minMacLength =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]);
+
+ if (((short) (minMacLength % 8) != 0)
+ || minMacLength < MIN_HMAC_LENGTH_BITS
+ || minMacLength > SHA256_DIGEST_LEN_BITS) {
+ KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH);
+ }
+ // Read Keysize
+ if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ }
+
+ private static void generateHmacKey(byte[] scratchPad) {
+ validateHmacKey();
+ short keysize =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+ // generate HMAC Key
+ short len = seProvider.createSymmetricKey(KMType.HMAC, keysize, scratchPad, (short) 0);
+ data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len);
+ data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+ }
+
+ // This function is only called from processUpgradeKey command.
+ // 1. Update the latest values of OSVersion, OSPatch, VendorPatch and BootPatch in the
+ // KeyBlob's KeyCharacteristics.
+ // 2. Re-create KeyBlob's KeyCharacteristics from HW_PARAMS to make sure we don't miss
+ // anything which happens in these functions makeSbEnforced and makeTeeEnforced in
+ // the future. Like validations.
+ // 3. No need to create Keystore Enforced list here as it is not required to be included in
+ // the KeyBlob's KeyCharacteristics.
+ // 4. No need to create KeyCharacteristics as upgradeKey does not require to return any
+ // KeyCharacteristics back.
+ private static void upgradeKeyBlobKeyCharacteristics(short hwParams, byte[] scratchPad) {
+ short osVersion = kmDataStore.getOsVersion();
+ short osPatch = kmDataStore.getOsPatch();
+ short vendorPatch = kmDataStore.getVendorPatchLevel();
+ short bootPatch = kmDataStore.getBootPatchLevel();
+ data[SB_PARAMETERS] =
+ KMKeyParameters.makeSbEnforced(
+ hwParams, (byte) data[ORIGIN], osVersion, osPatch, vendorPatch, bootPatch, scratchPad);
+ data[TEE_PARAMETERS] = KMKeyParameters.makeTeeEnforced(hwParams, scratchPad);
+ data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]);
+ }
+
+ private static void makeKeyCharacteristics(byte[] scratchPad) {
+ short osVersion = kmDataStore.getOsVersion();
+ short osPatch = kmDataStore.getOsPatch();
+ short vendorPatch = kmDataStore.getVendorPatchLevel();
+ short bootPatch = kmDataStore.getBootPatchLevel();
+ data[SB_PARAMETERS] =
+ KMKeyParameters.makeSbEnforced(
+ data[KEY_PARAMETERS],
+ (byte) data[ORIGIN],
+ osVersion,
+ osPatch,
+ vendorPatch,
+ bootPatch,
+ scratchPad);
+ data[TEE_PARAMETERS] = KMKeyParameters.makeTeeEnforced(data[KEY_PARAMETERS], scratchPad);
+ data[SW_PARAMETERS] = KMKeyParameters.makeKeystoreEnforced(data[KEY_PARAMETERS], scratchPad);
+ data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]);
+ data[KEY_CHARACTERISTICS] = KMKeyCharacteristics.instance();
+ KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setStrongboxEnforced(data[SB_PARAMETERS]);
+ KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setKeystoreEnforced(data[SW_PARAMETERS]);
+ KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setTeeEnforced(data[TEE_PARAMETERS]);
+ }
+
+ private static void createEncryptedKeyBlob(byte[] scratchPad) {
+ // make root of trust blob
+ data[ROT] = readROT(scratchPad, KEYBLOB_CURRENT_VERSION);
+ if (data[ROT] == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ // make hidden key params list
+ data[HIDDEN_PARAMETERS] =
+ KMKeyParameters.makeHidden(data[KEY_PARAMETERS], data[ROT], scratchPad);
+ data[KEY_BLOB_VERSION_DATA_OFFSET] = KMInteger.uint_16(KEYBLOB_CURRENT_VERSION);
+ // create custom tags
+ data[CUSTOM_TAGS] = KMKeyParameters.makeCustomTags(data[HW_PARAMETERS], scratchPad);
+ // encrypt the secret and cryptographically attach that to authorization data
+ encryptSecret(scratchPad);
+ // create key blob array
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_SECRET, data[SECRET]);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_AUTH_TAG, data[AUTH_TAG]);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_NONCE, data[NONCE]);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_VERSION_OFFSET, data[KEY_BLOB_VERSION_DATA_OFFSET]);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_CUSTOM_TAGS, data[CUSTOM_TAGS]);
+
+ short tempChar = KMKeyCharacteristics.instance();
+ short emptyParam = KMArray.instance((short) 0);
+ emptyParam = KMKeyParameters.instance(emptyParam);
+ KMKeyCharacteristics.cast(tempChar).setStrongboxEnforced(data[SB_PARAMETERS]);
+ KMKeyCharacteristics.cast(tempChar).setKeystoreEnforced(emptyParam);
+ KMKeyCharacteristics.cast(tempChar).setTeeEnforced(data[TEE_PARAMETERS]);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PARAMS, tempChar);
+ }
+
+ // Read RoT
+ public static short readROT(byte[] scratchPad, short version) {
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+ short len = kmDataStore.getBootKey(scratchPad, (short) 0);
+ // As per IKeyMintDevice.aidl specification The root of trust
+ // consists of verifyBootKey, boot state and device locked.
+ if (version <= KEYBLOB_VERSION_1) {
+ // To parse old keyblobs verified boot hash is included in
+ // the root of trust.
+ len += kmDataStore.getVerifiedBootHash(scratchPad, (short) len);
+ }
+ short bootState = kmDataStore.getBootState();
+ len = Util.setShort(scratchPad, len, bootState);
+ if (kmDataStore.isDeviceBootLocked()) {
+ scratchPad[len] = (byte) 1;
+ } else {
+ scratchPad[len] = (byte) 0;
+ }
+ len++;
+ return KMByteBlob.instance(scratchPad, (short) 0, len);
+ }
+
+ private static void encryptSecret(byte[] scratchPad) {
+ // make nonce
+ data[NONCE] = KMByteBlob.instance(AES_GCM_NONCE_LENGTH);
+ data[AUTH_TAG] = KMByteBlob.instance(AES_GCM_AUTH_TAG_LENGTH);
+ seProvider.newRandomNumber(
+ KMByteBlob.cast(data[NONCE]).getBuffer(),
+ KMByteBlob.cast(data[NONCE]).getStartOff(),
+ KMByteBlob.cast(data[NONCE]).length());
+ // derive master key - stored in derivedKey
+ short len = deriveKey(scratchPad);
+ len =
+ seProvider.aesGCMEncrypt(
+ KMByteBlob.cast(data[DERIVED_KEY]).getBuffer(),
+ KMByteBlob.cast(data[DERIVED_KEY]).getStartOff(),
+ KMByteBlob.cast(data[DERIVED_KEY]).length(),
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(data[NONCE]).getBuffer(),
+ KMByteBlob.cast(data[NONCE]).getStartOff(),
+ KMByteBlob.cast(data[NONCE]).length(),
+ null,
+ (short) 0,
+ (short) 0,
+ KMByteBlob.cast(data[AUTH_TAG]).getBuffer(),
+ KMByteBlob.cast(data[AUTH_TAG]).getStartOff(),
+ KMByteBlob.cast(data[AUTH_TAG]).length());
+
+ if (len > 0 && len != KMByteBlob.cast(data[SECRET]).length()) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len);
+ }
+
+ private static byte getKeyType(short hardwareParams) {
+ short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hardwareParams);
+ if (KMEnumTag.cast(alg).getValue() == KMType.RSA
+ || KMEnumTag.cast(alg).getValue() == KMType.EC) {
+ return ASYM_KEY_TYPE;
+ }
+ return SYM_KEY_TYPE;
+ }
+
+ private static void makeAuthData(short version, byte[] scratchPad) {
+ // For KeyBlob V2: Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS, CUSTOM_TAGS, VERSION and
+ // PUB_KEY.
+ // For KeyBlob V1: Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS, VERSION and PUB_KEY.
+ // For KeyBlob V0: Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS and PUB_KEY.
+ // VERSION is included only for KeyBlobs having version >= 1.
+ // PUB_KEY is included for only ASYMMETRIC KeyBlobs.
+ short index = 0;
+ short numParams = 0;
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 10, (byte) 0);
+ byte keyType = getKeyType(data[HW_PARAMETERS]);
+ // Copy the relevant parameters in the scratchPad in the order
+ // 1. HW_PARAMETERS
+ // 2. HIDDEN_PARAMETERS
+ // 3. VERSION ( Only Version >= 1)
+ // 4. PUB_KEY ( Only for Asymmetric Keys)
+ switch (version) {
+ case (short) 0:
+ numParams = 2;
+ Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals());
+ Util.setShort(
+ scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals());
+ // For Asymmetric Keys include the PUB_KEY.
+ if (keyType == ASYM_KEY_TYPE) {
+ numParams = 3;
+ Util.setShort(scratchPad, (short) 4, data[PUB_KEY]);
+ }
+ break;
+ case (short) 1:
+ numParams = 3;
+ Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals());
+ Util.setShort(
+ scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals());
+ Util.setShort(scratchPad, (short) 4, data[KEY_BLOB_VERSION_DATA_OFFSET]);
+ // For Asymmetric Keys include the PUB_KEY.
+ if (keyType == ASYM_KEY_TYPE) {
+ numParams = 4;
+ Util.setShort(scratchPad, (short) 6, data[PUB_KEY]);
+ }
+ break;
+ case (short) 2:
+ numParams = 4;
+ Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals());
+ Util.setShort(
+ scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals());
+ Util.setShort(scratchPad, (short) 4, KMKeyParameters.cast(data[CUSTOM_TAGS]).getVals());
+ Util.setShort(scratchPad, (short) 6, data[KEY_BLOB_VERSION_DATA_OFFSET]);
+ // For Asymmetric Keys include the PUB_KEY.
+ if (keyType == ASYM_KEY_TYPE) {
+ numParams = 5;
+ Util.setShort(scratchPad, (short) 8, data[PUB_KEY]);
+ }
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ short prevReclaimIndex = repository.getHeapReclaimIndex();
+ short authIndex = repository.allocReclaimableMemory(MAX_AUTH_DATA_SIZE);
+ index = 0;
+ short len = 0;
+ Util.arrayFillNonAtomic(repository.getHeap(), authIndex, MAX_AUTH_DATA_SIZE, (byte) 0);
+ while (index < numParams) {
+ short tag = Util.getShort(scratchPad, (short) (index * 2));
+ len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32), prevReclaimIndex);
+ Util.arrayCopyNonAtomic(
+ repository.getHeap(),
+ authIndex,
+ repository.getHeap(),
+ (short) (authIndex + len + 32),
+ (short) 32);
+ len =
+ seProvider.messageDigest256(
+ repository.getHeap(),
+ (short) (authIndex + 32),
+ (short) (len + 32),
+ repository.getHeap(),
+ authIndex);
+ if (len != 32) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ index++;
+ }
+ short authDataIndex = repository.alloc(len);
+ Util.arrayCopyNonAtomic(
+ repository.getHeap(), authIndex, repository.getHeap(), authDataIndex, len);
+ repository.reclaimMemory(MAX_AUTH_DATA_SIZE);
+ data[AUTH_DATA] = authDataIndex;
+ data[AUTH_DATA_LENGTH] = len;
+ }
+
+ private static short deriveKeyForOldKeyBlobs(byte[] scratchPad) {
+ // KeyDerivation:
+ // 1. Do HMAC Sign, Auth data.
+ // 2. HMAC Sign generates an output of 32 bytes length.
+ // Consume only first 16 bytes as derived key.
+ // Hmac sign.
+ short len =
+ seProvider.hmacKDF(
+ kmDataStore.getMasterKey(),
+ repository.getHeap(),
+ data[AUTH_DATA],
+ data[AUTH_DATA_LENGTH],
+ scratchPad,
+ (short) 0);
+ if (len < 16) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ len = 16;
+ data[DERIVED_KEY] = KMByteBlob.instance(scratchPad, (short) 0, len);
+ return len;
+ }
+
+ private static short deriveKey(byte[] scratchPad) {
+ // For KeyBlob V3: Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS, CUSTOM_TAGS, VERSION and
+ // PUB_KEY.
+ short index = 0;
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 10, (byte) 0);
+ byte keyType = getKeyType(data[HW_PARAMETERS]);
+ // Copy the relevant parameters in the scratchPad in the order
+ // 1. HW_PARAMETERS
+ // 2. HIDDEN_PARAMETERS
+ // 3. CUSTOM_TAGS
+ // 3. VERSION ( Only Version >= 1)
+ // 4. PUB_KEY ( Only for Asymmetric Keys)
+ short numParams = 4;
+ Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals());
+ Util.setShort(scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals());
+ Util.setShort(scratchPad, (short) 4, KMKeyParameters.cast(data[CUSTOM_TAGS]).getVals());
+ Util.setShort(scratchPad, (short) 6, data[KEY_BLOB_VERSION_DATA_OFFSET]);
+ // For Asymmetric Keys include the PUB_KEY.
+ if (keyType == ASYM_KEY_TYPE) {
+ numParams = 5;
+ Util.setShort(scratchPad, (short) 8, data[PUB_KEY]);
+ }
+ short prevReclaimIndex = repository.getHeapReclaimIndex();
+ short authIndex = repository.allocReclaimableMemory(MAX_AUTH_DATA_SIZE);
+ Util.arrayFillNonAtomic(repository.getHeap(), authIndex, MAX_AUTH_DATA_SIZE, (byte) 0);
+ short len = 0;
+ KMOperation operation = null;
+ try {
+ operation =
+ seProvider.initSymmetricOperation(
+ KMType.SIGN,
+ KMType.HMAC,
+ KMType.SHA2_256,
+ KMType.PADDING_NONE,
+ (byte) KMType.INVALID_VALUE,
+ (Object) kmDataStore.getMasterKey(),
+ KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY,
+ (byte[]) null,
+ (short) 0,
+ (short) 0,
+ (short) 0,
+ false);
+
+ byte arrayHeader = (byte) 0x80;
+ arrayHeader |= (byte) numParams;
+ ((byte[]) repository.getHeap())[authIndex] = arrayHeader;
+ operation.update(repository.getHeap(), authIndex, (short) 1);
+
+ while (index < numParams) {
+ short tag = Util.getShort(scratchPad, (short) (index * 2));
+ len = encoder.encode(tag, repository.getHeap(), (short) authIndex, prevReclaimIndex);
+ operation.update(repository.getHeap(), authIndex, len);
+ index++;
+ }
+ repository.reclaimMemory(MAX_AUTH_DATA_SIZE);
+ // KeyDerivation:
+ // 1. Do HMAC Sign, Auth data.
+ // 2. HMAC Sign generates an output of 32 bytes length.
+ // Consume only first 16 bytes as derived key.
+ // Hmac sign.
+ len = operation.sign(scratchPad, (short) 0, (short) 0, scratchPad, (short) 0);
+ } finally {
+ if (operation != null) {
+ operation.abort();
+ }
+ }
+ if (len < 16) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ len = 16;
+ data[DERIVED_KEY] = KMByteBlob.instance(scratchPad, (short) 0, len);
+ return len;
+ }
+
+ public static void sendResponse(APDU apdu, short err) {
+ short resp = KMArray.instance((short) 1);
+ err = KMError.translate(err);
+ short error = KMInteger.uint_16(err);
+ KMArray.cast(resp).add((short) 0, error);
+ sendOutgoing(apdu, resp);
+ }
+
+ public static void generateRkpKey(byte[] scratchPad, short keyParams) {
+ data[KEY_PARAMETERS] = keyParams;
+ generateECKeys(scratchPad);
+ // create key blob
+ data[ORIGIN] = KMType.GENERATED;
+ makeKeyCharacteristics(scratchPad);
+ createEncryptedKeyBlob(scratchPad);
+ short prevReclaimIndex = repository.getHeapReclaimIndex();
+ short offset = repository.allocReclaimableMemory(MAX_KEYBLOB_SIZE);
+ data[KEY_BLOB] =
+ encoder.encode(
+ data[KEY_BLOB], repository.getHeap(), offset, prevReclaimIndex, MAX_KEYBLOB_SIZE);
+ data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), offset, data[KEY_BLOB]);
+ repository.reclaimMemory(MAX_KEYBLOB_SIZE);
+ }
+
+ public static short getPubKey() {
+ return data[PUB_KEY];
+ }
+
+ public static short getPivateKey() {
+ return data[KEY_BLOB];
+ }
+
+ /**
+ * Encodes the object to the provided apdu buffer.
+ *
+ * @param object Object to be encoded.
+ * @param apduBuf Buffer on which the encoded data is copied.
+ * @param apduOff Start offset of the buffer.
+ * @param maxLen Max value of the expected out length.
+ * @return length of the encoded buffer.
+ */
+ public static short encodeToApduBuffer(
+ short object, byte[] apduBuf, short apduOff, short maxLen) {
+ short prevReclaimIndex = repository.getHeapReclaimIndex();
+ short offset = repository.allocReclaimableMemory(maxLen);
+ short len = encoder.encode(object, repository.getHeap(), offset, prevReclaimIndex, maxLen);
+ Util.arrayCopyNonAtomic(repository.getHeap(), offset, apduBuf, apduOff, len);
+ // release memory
+ repository.reclaimMemory(maxLen);
+ return len;
+ }
+
+ public static short validateCertChain(
+ boolean validateEekRoot,
+ byte expCertAlg,
+ byte expLeafCertAlg,
+ short certChainArr,
+ byte[] scratchPad,
+ Object[] authorizedEekRoots) {
+ short len = KMArray.cast(certChainArr).length();
+ short coseHeadersExp = KMCoseHeaders.exp();
+ // prepare exp for coseky
+ short coseKeyExp = KMCoseKey.exp();
+ short ptr1;
+ short ptr2;
+ short signStructure;
+ short encodedLen;
+ short prevCoseKey = 0;
+ short keySize;
+ short alg = expCertAlg;
+ short index;
+ for (index = 0; index < len; index++) {
+ ptr1 = KMArray.cast(certChainArr).get(index);
+
+ // validate protected Headers
+ ptr2 = KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PROTECTED_PARAMS_OFFSET);
+ ptr2 =
+ decoder.decode(
+ coseHeadersExp,
+ KMByteBlob.cast(ptr2).getBuffer(),
+ KMByteBlob.cast(ptr2).getStartOff(),
+ KMByteBlob.cast(ptr2).length());
+ if (!KMCoseHeaders.cast(ptr2).isDataValid(rkp.rkpTmpVariables, alg, KMType.INVALID_VALUE)) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+
+ // parse and get the public key from payload.
+ ptr2 = KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PAYLOAD_OFFSET);
+ ptr2 =
+ decoder.decode(
+ coseKeyExp,
+ KMByteBlob.cast(ptr2).getBuffer(),
+ KMByteBlob.cast(ptr2).getStartOff(),
+ KMByteBlob.cast(ptr2).length());
+ if ((index == (short) (len - 1)) && len > 1) {
+ alg = expLeafCertAlg;
+ }
+ if (!KMCoseKey.cast(ptr2)
+ .isDataValid(
+ rkp.rkpTmpVariables,
+ KMCose.COSE_KEY_TYPE_EC2,
+ KMType.INVALID_VALUE,
+ alg,
+ KMCose.COSE_ECCURVE_256)) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+ if (prevCoseKey == 0) {
+ prevCoseKey = ptr2;
+ }
+ // Get the public key.
+ keySize = KMCoseKey.cast(prevCoseKey).getEcdsa256PublicKey(scratchPad, (short) 0);
+ if (keySize != 65) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+ if (validateEekRoot && (index == 0)) {
+ boolean found = false;
+ // In prod mode the first pubkey should match a well-known Google public key.
+ for (short i = 0; i < (short) authorizedEekRoots.length; i++) {
+ if (0
+ == Util.arrayCompare(
+ scratchPad,
+ (short) 0,
+ (byte[]) authorizedEekRoots[i],
+ (short) 0,
+ (short) ((byte[]) authorizedEekRoots[i]).length)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+ }
+ // Validate signature.
+ signStructure =
+ KMCose.constructCoseSignStructure(
+ KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PROTECTED_PARAMS_OFFSET),
+ KMByteBlob.instance((short) 0),
+ KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PAYLOAD_OFFSET));
+ encodedLen =
+ KMKeymasterApplet.encodeToApduBuffer(
+ signStructure, scratchPad, keySize, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+
+ short signatureLen =
+ rkp.encodeES256CoseSignSignature(
+ KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET))
+ .getBuffer(),
+ KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET))
+ .getStartOff(),
+ KMByteBlob.length(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)),
+ scratchPad,
+ (short) (keySize + encodedLen));
+
+ if (!seProvider.ecVerify256(
+ scratchPad,
+ (short) 0,
+ keySize,
+ scratchPad,
+ keySize,
+ encodedLen,
+ scratchPad,
+ (short) (keySize + encodedLen),
+ signatureLen)) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+ prevCoseKey = ptr2;
+ }
+ return prevCoseKey;
+ }
+
+ public static short generateBcc(boolean testMode, byte[] scratchPad) {
+ if (!testMode && kmDataStore.isProvisionLocked()) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+ KMKey deviceUniqueKey = kmDataStore.getRkpDeviceUniqueKeyPair(testMode);
+ short temp = deviceUniqueKey.getPublicKey(scratchPad, (short) 0);
+ short coseKey =
+ KMCose.constructCoseKey(
+ rkp.rkpTmpVariables,
+ KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2),
+ KMType.INVALID_VALUE,
+ KMNInteger.uint_8(KMCose.COSE_ALG_ES256),
+ KMInteger.uint_8(KMCose.COSE_ECCURVE_256),
+ scratchPad,
+ (short) 0,
+ temp,
+ KMType.INVALID_VALUE,
+ false);
+ temp =
+ KMKeymasterApplet.encodeToApduBuffer(
+ coseKey, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ // Construct payload.
+ short payload =
+ KMCose.constructCoseCertPayload(
+ KMCosePairTextStringTag.instance(
+ KMInteger.uint_8(KMCose.ISSUER),
+ KMTextString.instance(
+ KMCose.TEST_ISSUER_NAME, (short) 0, (short) KMCose.TEST_ISSUER_NAME.length)),
+ KMCosePairTextStringTag.instance(
+ KMInteger.uint_8(KMCose.SUBJECT),
+ KMTextString.instance(
+ KMCose.TEST_SUBJECT_NAME, (short) 0, (short) KMCose.TEST_SUBJECT_NAME.length)),
+ KMCosePairByteBlobTag.instance(
+ KMNInteger.uint_32(KMCose.SUBJECT_PUBLIC_KEY, (short) 0),
+ KMByteBlob.instance(scratchPad, (short) 0, temp)),
+ KMCosePairByteBlobTag.instance(
+ KMNInteger.uint_32(KMCose.KEY_USAGE, (short) 0),
+ KMByteBlob.instance(
+ KMCose.KEY_USAGE_SIGN, (short) 0, (short) KMCose.KEY_USAGE_SIGN.length)));
+ // temp temporarily holds the length of encoded cert payload.
+ temp =
+ KMKeymasterApplet.encodeToApduBuffer(
+ payload, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ payload = KMByteBlob.instance(scratchPad, (short) 0, temp);
+
+ // protected header
+ short protectedHeader =
+ KMCose.constructHeaders(
+ rkp.rkpTmpVariables,
+ KMNInteger.uint_8(KMCose.COSE_ALG_ES256),
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE);
+ // temp temporarily holds the length of encoded headers.
+ temp =
+ KMKeymasterApplet.encodeToApduBuffer(
+ protectedHeader, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, temp);
+
+ // unprotected headers.
+ short arr = KMArray.instance((short) 0);
+ short unprotectedHeader = KMCoseHeaders.instance(arr);
+
+ // construct cose sign structure.
+ short coseSignStructure =
+ KMCose.constructCoseSignStructure(protectedHeader, KMByteBlob.instance((short) 0), payload);
+ // temp temporarily holds the length of encoded sign structure.
+ // Encode cose Sign_Structure.
+ temp =
+ KMKeymasterApplet.encodeToApduBuffer(
+ coseSignStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ // do sign
+ short len =
+ seProvider.signWithDeviceUniqueKey(
+ deviceUniqueKey, scratchPad, (short) 0, temp, scratchPad, temp);
+ len =
+ KMAsn1Parser.instance()
+ .decodeEcdsa256Signature(KMByteBlob.instance(scratchPad, temp, len), scratchPad, temp);
+ coseSignStructure = KMByteBlob.instance(scratchPad, temp, len);
+
+ // construct cose_sign1
+ short coseSign1 =
+ KMCose.constructCoseSign1(protectedHeader, unprotectedHeader, payload, coseSignStructure);
+
+ // [Cose_Key, Cose_Sign1]
+ short bcc = KMArray.instance((short) 2);
+ KMArray.cast(bcc).add((short) 0, coseKey);
+ KMArray.cast(bcc).add((short) 1, coseSign1);
+ return bcc;
+ }
+
+ protected void initHmacNonceAndSeed() {
+ short nonce = repository.alloc((short) 32);
+ seProvider.newRandomNumber(
+ repository.getHeap(), nonce, KMKeymintDataStore.HMAC_SEED_NONCE_SIZE);
+ kmDataStore.initHmacNonce(repository.getHeap(), nonce, KMKeymintDataStore.HMAC_SEED_NONCE_SIZE);
+ }
+
+ private void releaseAllOperations() {
+ short index = 0;
+ while (index < MAX_OPERATIONS_COUNT) {
+ opTable[index].reset();
+ index++;
+ }
+ }
+
+ private KMOperationState reserveOperation(short algorithm, short opHandle) {
+ short index = 0;
+ while (index < MAX_OPERATIONS_COUNT) {
+ if (opTable[index].getAlgorithm() == KMType.INVALID_VALUE) {
+ opTable[index].reset();
+ opTable[index].setAlgorithm(algorithm);
+ opTable[index].setHandle(
+ KMInteger.cast(opHandle).getBuffer(),
+ KMInteger.cast(opHandle).getStartOff(),
+ KMInteger.cast(opHandle).length());
+ return opTable[index];
+ }
+ index++;
+ }
+ return null;
+ }
+
+ private KMOperationState findOperation(short handle) {
+ return findOperation(
+ KMInteger.cast(handle).getBuffer(),
+ KMInteger.cast(handle).getStartOff(),
+ KMInteger.cast(handle).length());
+ }
+
+ private KMOperationState findOperation(byte[] opHandle, short start, short len) {
+ short index = 0;
+ while (index < MAX_OPERATIONS_COUNT) {
+ if (opTable[index].compare(opHandle, start, len) == 0) {
+ if (opTable[index].getAlgorithm() != KMType.INVALID_VALUE) {
+ return opTable[index];
+ }
+ }
+ index++;
+ }
+ return null;
+ }
+
+ private void releaseOperation(KMOperationState op) {
+ op.reset();
+ }
+
+ /**
+ * Selects this applet.
+ *
+ * @return Returns true if the keymaster is in correct state
+ */
+ @Override
+ public boolean select() {
+ repository.onSelect();
+ return true;
+ }
+
+ /** De-selects this applet. */
+ @Override
+ public void deselect() {
+ repository.onDeselect();
+ }
+
+ /** Uninstalls the applet after cleaning the repository. */
+ @Override
+ public void uninstall() {
+ repository.onUninstall();
+ }
+
+ protected short mapISOErrorToKMError(short reason) {
+ switch (reason) {
+ case ISO7816.SW_CLA_NOT_SUPPORTED:
+ return KMError.UNSUPPORTED_CLA;
+ case ISO7816.SW_CONDITIONS_NOT_SATISFIED:
+ return KMError.SW_CONDITIONS_NOT_SATISFIED;
+ case ISO7816.SW_COMMAND_NOT_ALLOWED:
+ return KMError.CMD_NOT_ALLOWED;
+ case ISO7816.SW_DATA_INVALID:
+ return KMError.INVALID_DATA;
+ case ISO7816.SW_INCORRECT_P1P2:
+ return KMError.INVALID_P1P2;
+ case ISO7816.SW_INS_NOT_SUPPORTED:
+ return KMError.UNSUPPORTED_INSTRUCTION;
+ case ISO7816.SW_WRONG_LENGTH:
+ return KMError.SW_WRONG_LENGTH;
+ case ISO7816.SW_UNKNOWN:
+ default:
+ return KMError.UNKNOWN_ERROR;
+ }
+ }
+
+ protected short mapCryptoErrorToKMError(short reason) {
+ switch (reason) {
+ case CryptoException.ILLEGAL_USE:
+ return KMError.CRYPTO_ILLEGAL_USE;
+ case CryptoException.ILLEGAL_VALUE:
+ return KMError.CRYPTO_ILLEGAL_VALUE;
+ case CryptoException.INVALID_INIT:
+ return KMError.CRYPTO_INVALID_INIT;
+ case CryptoException.NO_SUCH_ALGORITHM:
+ return KMError.CRYPTO_NO_SUCH_ALGORITHM;
+ case CryptoException.UNINITIALIZED_KEY:
+ return KMError.CRYPTO_UNINITIALIZED_KEY;
+ default:
+ return KMError.UNKNOWN_ERROR;
+ }
+ }
+
+ public void updateApduStatusFlags(short apduIns) {
+ switch (apduIns) {
+ case INS_EXPORT_KEY_CMD:
+ case INS_DELETE_ALL_KEYS_CMD:
+ case INS_DESTROY_ATT_IDS_CMD:
+ case INS_VERIFY_AUTHORIZATION_CMD:
+ case INS_GET_HMAC_SHARING_PARAM_CMD:
+ case INS_GET_HW_INFO_CMD:
+ case INS_EARLY_BOOT_ENDED_CMD:
+ case INS_GET_ROT_CHALLENGE_CMD:
+ case INS_GET_ROT_DATA_CMD:
+ case INS_GET_RKP_HARDWARE_INFO:
+ case INS_FINISH_SEND_DATA_CMD:
+ case INS_GET_RESPONSE_CMD:
+ apduStatusFlags[APDU_CASE4_COMMAND_STATUS_INDEX] = 0;
+ break;
+ default:
+ // By default the instruction is set to case 4 command instruction.
+ break;
+ }
+ }
+
+ /**
+ * Processes an incoming APDU and handles it using command objects.
+ *
+ * @param apdu the incoming APDU
+ */
+ @Override
+ public void process(APDU apdu) {
+ try {
+ resetTransientBuffers();
+ repository.onProcess();
+ // If this is select applet apdu which is selecting this applet then return
+ if (apdu.isISOInterindustryCLA()) {
+ if (selectingApplet()) {
+ return;
+ }
+ }
+ byte[] apduBuffer = apdu.getBuffer();
+ byte apduIns = apduBuffer[ISO7816.OFFSET_INS];
+ if (!isKeyMintReady(apduIns)) {
+ ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED);
+ }
+ switch (apduIns) {
+ case INS_INIT_STRONGBOX_CMD:
+ processInitStrongBoxCmd(apdu);
+ sendResponse(apdu, KMError.OK);
+ return;
+ case INS_GENERATE_KEY_CMD:
+ processGenerateKey(apdu);
+ break;
+ case INS_IMPORT_KEY_CMD:
+ processImportKeyCmd(apdu);
+ break;
+ case INS_BEGIN_IMPORT_WRAPPED_KEY_CMD:
+ processBeginImportWrappedKeyCmd(apdu);
+ break;
+ case INS_FINISH_IMPORT_WRAPPED_KEY_CMD:
+ processFinishImportWrappedKeyCmd(apdu);
+ break;
+ case INS_EXPORT_KEY_CMD:
+ processExportKeyCmd(apdu);
+ break;
+ case INS_UPGRADE_KEY_CMD:
+ processUpgradeKeyCmd(apdu);
+ break;
+ case INS_DELETE_KEY_CMD:
+ processDeleteKeyCmd(apdu);
+ break;
+ case INS_DELETE_ALL_KEYS_CMD:
+ processDeleteAllKeysCmd(apdu);
+ break;
+ case INS_ADD_RNG_ENTROPY_CMD:
+ processAddRngEntropyCmd(apdu);
+ break;
+ case INS_COMPUTE_SHARED_HMAC_CMD:
+ processComputeSharedHmacCmd(apdu);
+ break;
+ case INS_DESTROY_ATT_IDS_CMD:
+ processDestroyAttIdsCmd(apdu);
+ break;
+ case INS_VERIFY_AUTHORIZATION_CMD:
+ processVerifyAuthorizationCmd(apdu);
+ break;
+ case INS_GET_HMAC_SHARING_PARAM_CMD:
+ processGetHmacSharingParamCmd(apdu);
+ break;
+ case INS_GET_KEY_CHARACTERISTICS_CMD:
+ processGetKeyCharacteristicsCmd(apdu);
+ break;
+ case INS_GET_HW_INFO_CMD:
+ processGetHwInfoCmd(apdu);
+ break;
+ case INS_BEGIN_OPERATION_CMD:
+ processBeginOperationCmd(apdu);
+ break;
+ case INS_UPDATE_OPERATION_CMD:
+ processUpdateOperationCmd(apdu);
+ break;
+ case INS_FINISH_OPERATION_CMD:
+ processFinishOperationCmd(apdu);
+ break;
+ case INS_ABORT_OPERATION_CMD:
+ processAbortOperationCmd(apdu);
+ break;
+ case INS_DEVICE_LOCKED_CMD:
+ processDeviceLockedCmd(apdu);
+ break;
+ case INS_EARLY_BOOT_ENDED_CMD:
+ processEarlyBootEndedCmd(apdu);
+ break;
+ case INS_UPDATE_AAD_OPERATION_CMD:
+ processUpdateAadOperationCmd(apdu);
+ break;
+ case INS_GENERATE_RKP_KEY_CMD:
+ case INS_BEGIN_SEND_DATA_CMD:
+ case INS_UPDATE_CHALLENGE_CMD:
+ case INS_UPDATE_EEK_CHAIN_CMD:
+ case INS_UPDATE_KEY_CMD:
+ case INS_FINISH_SEND_DATA_CMD:
+ case INS_GET_RESPONSE_CMD:
+ case INS_GET_RKP_HARDWARE_INFO:
+ rkp.process(apduIns, apdu);
+ break;
+ // KeyMint 2.0
+ case INS_GET_ROT_CHALLENGE_CMD:
+ processGetRootOfTrustChallenge(apdu);
+ break;
+ case INS_GET_ROT_DATA_CMD:
+ sendResponse(apdu, KMError.UNIMPLEMENTED);
+ break;
+ case INS_SEND_ROT_DATA_CMD:
+ processSendRootOfTrust(apdu);
+ break;
+ default:
+ ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
+ }
+ } catch (KMException exception) {
+ freeOperations();
+ resetWrappingKey();
+ sendResponse(apdu, KMException.reason());
+ } catch (ISOException exp) {
+ freeOperations();
+ resetWrappingKey();
+ sendResponse(apdu, mapISOErrorToKMError(exp.getReason()));
+ } catch (CryptoException e) {
+ freeOperations();
+ resetWrappingKey();
+ sendResponse(apdu, mapCryptoErrorToKMError(e.getReason()));
+ } catch (Exception e) {
+ freeOperations();
+ resetWrappingKey();
+ sendResponse(apdu, KMError.GENERIC_UNKNOWN_ERROR);
+ } finally {
+ repository.clean();
+ }
+ }
+
+ private void processGetRootOfTrustChallenge(APDU apdu) {
+ byte[] scratchpad = apdu.getBuffer();
+ // Generate 16-byte random challenge nonce, used to prove freshness when exchanging root of
+ // trust data.
+ seProvider.newRandomNumber(scratchpad, (short) 0, (short) 16);
+ kmDataStore.setChallenge(scratchpad, (short) 0, (short) 16);
+ short challenge = KMByteBlob.instance(scratchpad, (short) 0, (short) 16);
+ short arr = KMArray.instance((short) 2);
+ KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(arr).add((short) 1, challenge);
+ sendOutgoing(apdu, arr);
+ }
+
+ private short sendRootOfTrustCmd(APDU apdu) {
+ short arrInst = KMArray.instance((short) 4);
+ short headers = KMCoseHeaders.exp();
+ KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp());
+ KMArray.cast(arrInst).add((short) 1, headers);
+ KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp());
+ KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp());
+ short semanticTag = KMSemanticTag.exp(arrInst);
+ short arr = KMArray.exp(semanticTag);
+ return receiveIncoming(apdu, arr);
+ }
+
+ private void processSendRootOfTrust(APDU apdu) {
+ byte[] scratchPad = apdu.getBuffer();
+ short cmd = KMType.INVALID_VALUE;
+ // As per VTS if the input data is empty or not well-formed
+ // CoseMac return VERIFICATION_FAILED error.
+ try {
+ cmd = sendRootOfTrustCmd(apdu);
+ } catch (Exception e) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+
+ short semanticTag = KMArray.cast(cmd).get((short) 0);
+ short coseMacPtr = KMSemanticTag.cast(semanticTag).getValuePtr();
+ // Exp for KMCoseHeaders
+ short coseHeadersExp = KMCoseHeaders.exp();
+ // validate protected Headers
+ short ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET);
+ ptr =
+ decoder.decode(
+ coseHeadersExp,
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length());
+
+ if (!KMCoseHeaders.cast(ptr)
+ .isDataValid(tmpVariables, KMCose.COSE_ALG_HMAC_256, KMType.INVALID_VALUE)) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+
+ // Validate the Mac
+ short len = kmDataStore.getChallenge(scratchPad, (short) 0);
+ short extAad = KMByteBlob.instance(scratchPad, (short) 0, len);
+ // Compute CoseMac Structure and compare the macs.
+ short rotPayload = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET);
+ short macStructure =
+ KMCose.constructCoseMacStructure(
+ KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET),
+ extAad,
+ rotPayload);
+ short encodedLen =
+ KMKeymasterApplet.encodeToApduBuffer(
+ macStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+
+ if (!seProvider.hmacVerify(
+ kmDataStore.getComputedHmacKey(),
+ scratchPad,
+ (short) 0,
+ encodedLen,
+ KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getBuffer(),
+ KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getStartOff(),
+ KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).length())) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+ // Store the data only once after reboot.
+ // Allow set boot params only when the host device reboots and the applet is in
+ // active state. If host does not support boot signal event, then allow this
+ // instruction any time.
+ kmDataStore.getDeviceBootStatus(scratchPad, (short) 0);
+ if (((scratchPad[0] & KMKeymintDataStore.SET_BOOT_PARAMS_SUCCESS) == 0)) {
+ // store the data.
+ storeRootOfTrust(rotPayload, scratchPad);
+ kmDataStore.setDeviceBootStatus(KMKeymintDataStore.SET_BOOT_PARAMS_SUCCESS);
+ }
+ // Invalidate the challenge
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 16, (byte) 0);
+ kmDataStore.setChallenge(scratchPad, (short) 0, (short) 16);
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private void storeRootOfTrust(short rotPayload, byte[] scratchPad) {
+ short byteBlobExp = KMByteBlob.exp();
+ short intExp = KMInteger.exp();
+ short boolExp = KMSimpleValue.exp();
+ short arr = KMArray.instance((short) 5);
+ KMArray.cast(arr).add((short) 0, byteBlobExp); // Verfied boot key.
+ KMArray.cast(arr).add((short) 1, boolExp); // deviceLocked.
+ KMArray.cast(arr).add((short) 2, intExp); // Verified Boot State.
+ KMArray.cast(arr).add((short) 3, byteBlobExp); // Verfied boot hash.
+ KMArray.cast(arr).add((short) 4, intExp); // Boot patch level
+ short semanticExp = KMSemanticTag.exp(arr);
+
+ short semanticPtr =
+ decoder.decode(
+ semanticExp,
+ KMByteBlob.cast(rotPayload).getBuffer(),
+ KMByteBlob.cast(rotPayload).getStartOff(),
+ KMByteBlob.cast(rotPayload).length());
+ short rotArr = KMSemanticTag.cast(semanticPtr).getValuePtr();
+ // Store verified boot key
+ short ptr = KMArray.cast(rotArr).get((short) 0);
+ kmDataStore.setBootKey(
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length());
+ // Store Boot device locked.
+ ptr = KMArray.cast(rotArr).get((short) 1);
+ kmDataStore.setDeviceLocked(
+ (KMSimpleValue.cast(ptr).getValue() == KMSimpleValue.TRUE) ? true : false);
+ // Store verified boot state
+ ptr = KMArray.cast(rotArr).get((short) 2);
+ kmDataStore.setBootState(KMInteger.cast(ptr).getShort());
+ // Store Verified boot hash
+ ptr = KMArray.cast(rotArr).get((short) 3);
+ kmDataStore.setVerifiedBootHash(
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length());
+ // Store boot patch level
+ ptr = KMArray.cast(rotArr).get((short) 4);
+ kmDataStore.setBootPatchLevel(
+ KMInteger.cast(ptr).getBuffer(),
+ KMInteger.cast(ptr).getStartOff(),
+ KMInteger.cast(ptr).length());
+ }
+
+ // After every device boot, the Keymaster becomes ready to execute all the commands only after
+ // 1. boot parameters are set,
+ // 2. system properties are set and
+ // 3. computed the shared secret successfully.
+ private boolean isKeyMintReady(byte apduIns) {
+ if (kmDataStore.isDeviceReady()) {
+ return true;
+ }
+ // Below commands are allowed even if the Keymaster is not ready.
+ switch (apduIns) {
+ case INS_GET_HW_INFO_CMD:
+ case INS_GET_RKP_HARDWARE_INFO:
+ case INS_ADD_RNG_ENTROPY_CMD:
+ case INS_GET_HMAC_SHARING_PARAM_CMD:
+ case INS_COMPUTE_SHARED_HMAC_CMD:
+ case INS_EARLY_BOOT_ENDED_CMD:
+ case INS_INIT_STRONGBOX_CMD:
+ case INS_GET_ROT_CHALLENGE_CMD:
+ case INS_SEND_ROT_DATA_CMD:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ private void generateUniqueOperationHandle(byte[] buf, short offset, short len) {
+ do {
+ seProvider.newRandomNumber(buf, offset, len);
+ } while (null != findOperation(buf, offset, len));
+ }
+
+ private void freeOperations() {
+ if (data[OP_HANDLE] != KMType.INVALID_VALUE) {
+ KMOperationState op = findOperation(data[OP_HANDLE]);
+ if (op != null) {
+ releaseOperation(op);
+ }
+ }
+ }
+
+ private void processEarlyBootEndedCmd(APDU apdu) {
+ kmDataStore.setEarlyBootEndedStatus(true);
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private short deviceLockedCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 2);
+ short ptr = KMVerificationToken.exp();
+ // passwordOnly
+ KMArray.cast(cmd).add((short) 0, KMInteger.exp());
+ // verification token
+ KMArray.cast(cmd).add((short) 1, ptr);
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processDeviceLockedCmd(APDU apdu) {
+ short cmd = deviceLockedCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+ short passwordOnly = KMArray.cast(cmd).get((short) 0);
+ short verToken = KMArray.cast(cmd).get((short) 1);
+ passwordOnly = KMInteger.cast(passwordOnly).getByte();
+ validateVerificationToken(verToken, scratchPad);
+ short verTime = KMVerificationToken.cast(verToken).getTimestamp();
+ short lastDeviceLockedTime;
+ try {
+ lastDeviceLockedTime = kmDataStore.getDeviceTimeStamp();
+ } catch (KMException e) {
+ lastDeviceLockedTime = KMInteger.uint_8((byte) 0);
+ }
+ if (KMInteger.compare(verTime, lastDeviceLockedTime) > 0) {
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, KMInteger.UINT_64, (byte) 0);
+ KMInteger.cast(verTime).getValue(scratchPad, (short) 0, KMInteger.UINT_64);
+ kmDataStore.setDeviceLock(true);
+ kmDataStore.setDeviceLockPasswordOnly(passwordOnly == 0x01);
+ kmDataStore.setDeviceLockTimestamp(scratchPad, (short) 0, KMInteger.UINT_64);
+ }
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private void resetWrappingKey() {
+ if (!isValidWrappingKey()) {
+ return;
+ }
+ Util.arrayFillNonAtomic(wrappingKey, (short) 1, WRAPPING_KEY_SIZE, (byte) 0);
+ wrappingKey[0] = -1;
+ }
+
+ private boolean isValidWrappingKey() {
+ return wrappingKey[0] != -1;
+ }
+
+ private short getWrappingKey() {
+ return KMByteBlob.instance(wrappingKey, (short) 1, WRAPPING_KEY_SIZE);
+ }
+
+ private void setWrappingKey(short key) {
+ if (KMByteBlob.cast(key).length() != WRAPPING_KEY_SIZE) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ wrappingKey[0] = 0;
+ Util.arrayCopyNonAtomic(
+ KMByteBlob.cast(key).getBuffer(),
+ KMByteBlob.cast(key).getStartOff(),
+ wrappingKey,
+ (short) 1,
+ WRAPPING_KEY_SIZE);
+ }
+
+ protected void resetTransientBuffers() {
+ short index = 0;
+ while (index < data.length) {
+ data[index] = KMType.INVALID_VALUE;
+ index++;
+ }
+ index = 0;
+ while (index < tmpVariables.length) {
+ tmpVariables[index] = KMType.INVALID_VALUE;
+ index++;
+ }
+ }
+
+ public void sendOutgoing(
+ APDU apdu, KMAttestationCert cert, short certStart, short keyblob, short keyChars) {
+ // This is the special case where the output is encoded manually without using
+ // the encoder algorithm. Encoder creates a duplicate copy for each KMType Object.
+ // The output of the generateKey, importKey and importWrappedKey commands are huge so
+ // by manually encoding we can avoid duplicate copies.
+ // The output data is directly written to the end of heap in the below order
+ // output = [
+ // errorCode : uint // ErrorCode
+ // keyBlob : bstr // KeyBlob.
+ // keyChars
+ // certifcate
+ // ]
+ // certificate = [
+ // x509_cert : bstr // X509 certificate
+ // ]
+ // keyChars = { // Map
+ // }
+ byte[] buffer = repository.getHeap();
+
+ if (cert == null) {
+ // This happens for Symmetric keys.
+ short bufferStart = repository.allocReclaimableMemory((short) 1);
+ buffer[bufferStart] = (byte) 0x80; // Array of 0 length.
+ } else {
+ // Encode the certificate into cbor data at the end of the heap
+ // certData = [
+ // x509_cert : bstr // X509 certificate
+ // ]
+ short bufferStart =
+ encoder.encodeCert(
+ repository.getHeap(), certStart, cert.getCertStart(), cert.getCertLength());
+ // reclaim the unused memory in the certificate.
+ repository.reclaimMemory((short) (bufferStart - certStart));
+ }
+
+ // Encode KeyCharacteristics at the end of heap just before data[CERTIFICATE]
+ encodeKeyCharacteristics(keyChars);
+ // and encode it to the end of the buffer before KEY_CHARACTERISTICS
+ encodeKeyBlob(keyblob);
+ // Write Array header and ErrorCode before data[KEY_BLOB]
+ short bufferStartOffset = repository.allocReclaimableMemory((short) 2);
+ Util.setShort(buffer, bufferStartOffset, (short) 0x8400);
+
+ short bufferLength = (short) (KMRepository.HEAP_SIZE - bufferStartOffset);
+ /* In T=0 protocol, On a case 4 command, setIncomingAndReceive() must
+ * be invoked prior to calling setOutgoing(). Otherwise, erroneous
+ * behavior may result
+ * */
+ if (apduStatusFlags[APDU_CASE4_COMMAND_STATUS_INDEX] == 1
+ && apduStatusFlags[APDU_INCOMING_AND_RECEIVE_STATUS_INDEX] == 0
+ && APDU.getProtocol() == APDU.PROTOCOL_T0) {
+ apdu.setIncomingAndReceive();
+ }
+ // Send data
+ apdu.setOutgoing();
+ apdu.setOutgoingLength(bufferLength);
+ apdu.sendBytesLong(buffer, bufferStartOffset, bufferLength);
+ }
+
+ private void processGetHwInfoCmd(APDU apdu) {
+ // No arguments expected
+ final byte version = 2;
+ // Make the response
+ short respPtr = KMArray.instance((short) 6);
+ KMArray resp = KMArray.cast(respPtr);
+ resp.add((short) 0, KMInteger.uint_16(KMError.OK));
+ resp.add((short) 1, KMInteger.uint_8(version));
+ resp.add((short) 2, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX));
+ resp.add(
+ (short) 3,
+ KMByteBlob.instance(
+ JavacardKeymintDevice, (short) 0, (short) JavacardKeymintDevice.length));
+ resp.add((short) 4, KMByteBlob.instance(Google, (short) 0, (short) Google.length));
+ resp.add((short) 5, KMInteger.uint_8((byte) 1));
+ // send buffer to host
+ sendOutgoing(apdu, respPtr);
+ }
+
+ private short addRngEntropyCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 1);
+ // Rng entropy
+ KMArray.cast(cmd).add((short) 0, KMByteBlob.exp());
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processAddRngEntropyCmd(APDU apdu) {
+ // Receive the incoming request fully from the host.
+ short cmd = addRngEntropyCmd(apdu);
+ // Process
+ KMByteBlob blob = KMByteBlob.cast(KMArray.cast(cmd).get((short) 0));
+ // Maximum 2KiB of seed is allowed.
+ if (blob.length() > MAX_SEED_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ seProvider.addRngEntropy(blob.getBuffer(), blob.getStartOff(), blob.length());
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private short getKeyCharacteristicsCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 3);
+ KMArray.cast(cmd).add((short) 0, KMByteBlob.exp());
+ KMArray.cast(cmd).add((short) 1, KMByteBlob.exp());
+ KMArray.cast(cmd).add((short) 2, KMByteBlob.exp());
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processGetKeyCharacteristicsCmd(APDU apdu) {
+ // Receive the incoming request fully from the host.
+ short cmd = getKeyCharacteristicsCmd(apdu);
+ // Re-purpose the apdu buffer as scratch pad.
+ byte[] scratchPad = apdu.getBuffer();
+ data[KEY_BLOB] = KMArray.cast(cmd).get((short) 0);
+ data[APP_ID] = KMArray.cast(cmd).get((short) 1);
+ data[APP_DATA] = KMArray.cast(cmd).get((short) 2);
+ if (KMByteBlob.cast(data[APP_ID]).length() > KMByteTag.MAX_APP_ID_APP_DATA_SIZE
+ || KMByteBlob.cast(data[APP_DATA]).length() > KMByteTag.MAX_APP_ID_APP_DATA_SIZE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (!KMByteBlob.cast(data[APP_ID]).isValid()) {
+ data[APP_ID] = KMType.INVALID_VALUE;
+ }
+ if (!KMByteBlob.cast(data[APP_DATA]).isValid()) {
+ data[APP_DATA] = KMType.INVALID_VALUE;
+ }
+ // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired
+ // function itself.
+ if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) {
+ KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE);
+ }
+ // make response.
+ short resp = KMArray.instance((short) 2);
+ KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(resp).add((short) 1, data[KEY_CHARACTERISTICS]);
+ sendOutgoing(apdu, resp);
+ }
+
+ private void processGetHmacSharingParamCmd(APDU apdu) {
+ // No Arguments
+ // Create HMAC Sharing Parameters
+ short params = KMHmacSharingParameters.instance();
+ short nonce = kmDataStore.getHmacNonce();
+ short seed = KMByteBlob.instance((short) 0);
+ KMHmacSharingParameters.cast(params).setNonce(nonce);
+ KMHmacSharingParameters.cast(params).setSeed(seed);
+ // prepare the response
+ short resp = KMArray.instance((short) 2);
+ KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(resp).add((short) 1, params);
+ sendOutgoing(apdu, resp);
+ }
+
+ private void processDeleteAllKeysCmd(APDU apdu) {
+ // No arguments
+ // This function is triggered when a factory reset event occurs.
+ // Regenerate the master key to render all keys unusable.
+ kmDataStore.regenerateMasterKey();
+ // Send ok
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private short createKeyBlobExp(short version) {
+ short keyBlob = KMType.INVALID_VALUE;
+ short byteBlobExp = KMByteBlob.exp();
+ short keyChar = KMKeyCharacteristics.exp();
+ short keyParam = KMKeyParameters.exp();
+ switch (version) {
+ case (short) 0:
+ // Old KeyBlob has a maximum of 5 elements.
+ keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V0);
+ KMArray.cast(keyBlob).add((short) 0, byteBlobExp); // Secret
+ KMArray.cast(keyBlob).add((short) 1, byteBlobExp); // Nonce
+ KMArray.cast(keyBlob).add((short) 2, byteBlobExp); // AuthTag
+ KMArray.cast(keyBlob).add((short) 3, keyChar); // KeyChars
+ KMArray.cast(keyBlob).add((short) 4, byteBlobExp); // PubKey
+ break;
+ case (short) 1:
+ keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V1);
+ KMArray.cast(keyBlob).add((short) 0, KMInteger.exp()); // Version
+ KMArray.cast(keyBlob).add((short) 1, byteBlobExp); // Secret
+ KMArray.cast(keyBlob).add((short) 2, byteBlobExp); // Nonce
+ KMArray.cast(keyBlob).add((short) 3, byteBlobExp); // AuthTag
+ KMArray.cast(keyBlob).add((short) 4, keyChar); // KeyChars
+ KMArray.cast(keyBlob).add((short) 5, byteBlobExp); // PubKey
+ break;
+ case (short) 2:
+ case (short) 3:
+ keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V2_V3);
+ KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_VERSION_OFFSET, KMInteger.exp());
+ KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_SECRET, byteBlobExp);
+ KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, byteBlobExp);
+ KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_NONCE, byteBlobExp);
+ KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PARAMS, keyChar);
+ KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_CUSTOM_TAGS, keyParam);
+ KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, byteBlobExp);
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ return keyBlob;
+ }
+
+ private void processDeleteKeyCmd(APDU apdu) {
+ // Send ok
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private short computeSharedHmacCmd(APDU apdu) {
+ short params = KMHmacSharingParameters.exp();
+ short paramsVec = KMArray.exp(params);
+ short cmd = KMArray.instance((short) 1);
+ KMArray.cast(cmd).add((short) 0, paramsVec);
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processComputeSharedHmacCmd(APDU apdu) {
+ // Receive the incoming request fully from the host into buffer.
+ short cmd = computeSharedHmacCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+ data[HMAC_SHARING_PARAMS] = KMArray.cast(cmd).get((short) 0);
+ // Concatenate HMAC Params
+ // tmpVariables[0]
+ short paramsLen = KMArray.cast(data[HMAC_SHARING_PARAMS]).length(); // total number of params
+ // tmpVariables[1]
+ short concateBuffer = repository.alloc((short) (paramsLen * HMAC_SHARED_PARAM_MAX_SIZE));
+ // tmpVariables[2]
+ short paramIndex = 0; // index for params
+ // tmpVariables[3]
+ short bufferIndex = 0; // index for concatenation buffer
+ // To check if nonce created by Strongbox is found. This value becomes 1 if both
+ // seed and nonce created here are found in hmac sharing parameters received.
+ // tmpVariables[7] = 0;
+ short found = 0;
+ // tmpVariables[9]
+ short nonce = kmDataStore.getHmacNonce();
+
+ while (paramIndex < paramsLen) {
+ // read HmacSharingParam
+ // tmpVariables[4]
+ short param = KMArray.cast(data[HMAC_SHARING_PARAMS]).get(paramIndex);
+ // get seed - 32 bytes max
+ // tmpVariables[5]
+ short seed = KMHmacSharingParameters.cast(param).getSeed();
+ // tmpVariables[6]
+ short seedLength = KMByteBlob.cast(seed).length();
+ // if seed is present
+ if (seedLength != 0) {
+ // then copy that to concatenation buffer
+ Util.arrayCopyNonAtomic(
+ KMByteBlob.cast(seed).getBuffer(),
+ KMByteBlob.cast(seed).getStartOff(),
+ repository.getHeap(),
+ (short) (concateBuffer + bufferIndex), // concat index
+ seedLength);
+ bufferIndex += seedLength; // increment the concat index
+ } else if (found == 0) {
+ found = 1; // Applet does not have any seed. Potentially
+ }
+ // if nonce is present get nonce - 32 bytes
+ // tmpVariables[5]
+ short paramNonce = KMHmacSharingParameters.cast(param).getNonce();
+ short nonceLen = KMByteBlob.cast(paramNonce).length();
+ // if nonce is less then 32 - it is an error
+ if (nonceLen < 32) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ // copy nonce to concatenation buffer
+ Util.arrayCopyNonAtomic(
+ KMByteBlob.cast(paramNonce).getBuffer(),
+ KMByteBlob.cast(paramNonce).getStartOff(),
+ repository.getHeap(),
+ (short) (concateBuffer + bufferIndex), // index
+ nonceLen);
+
+ // Check if the nonce generated here is present in the hmacSharingParameters array.
+ // Otherwise throw INVALID_ARGUMENT error.
+ if (found == 1) {
+ if (0
+ == Util.arrayCompare(
+ repository.getHeap(),
+ (short) (concateBuffer + bufferIndex),
+ KMByteBlob.cast(nonce).getBuffer(),
+ KMByteBlob.cast(nonce).getStartOff(),
+ nonceLen)) {
+ found = 2; // hmac nonce for this keymaster found.
+ } else {
+ found = 0;
+ }
+ }
+ bufferIndex += nonceLen; // increment by nonce length
+ paramIndex++; // go to next hmac param in the vector
+ }
+ if (found != 2) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ // generate the key and store it in scratch pad - 32 bytes
+ // tmpVariables[6]
+ short keyLen =
+ seProvider.cmacKDF(
+ kmDataStore.getPresharedKey(),
+ ckdfLabel,
+ (short) 0,
+ (short) ckdfLabel.length,
+ repository.getHeap(),
+ concateBuffer,
+ bufferIndex,
+ scratchPad,
+ (short) 0);
+
+ // persist the computed hmac key.
+ kmDataStore.createComputedHmacKey(scratchPad, (short) 0, keyLen);
+ // Generate sharingKey verification signature and store that in scratch pad.
+ // tmpVariables[5]
+ short signLen =
+ seProvider.hmacSign(
+ scratchPad,
+ (short) 0,
+ keyLen,
+ sharingCheck,
+ (short) 0,
+ (short) sharingCheck.length,
+ scratchPad,
+ keyLen);
+ kmDataStore.setDeviceBootStatus(KMKeymintDataStore.NEGOTIATED_SHARED_SECRET_SUCCESS);
+ // verification signature blob - 32 bytes
+ // tmpVariables[1]
+ short signature = KMByteBlob.instance(scratchPad, keyLen, signLen);
+ // prepare the response
+ short resp = KMArray.instance((short) 2);
+ KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(resp).add((short) 1, signature);
+ sendOutgoing(apdu, resp);
+ }
+
+ private short upgradeKeyCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 2);
+ short keyParams = KMKeyParameters.exp();
+ KMArray.cast(cmd).add((short) 0, KMByteBlob.exp()); // Key Blob
+ KMArray.cast(cmd).add((short) 1, keyParams); // Key Params
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private boolean isKeyUpgradeRequired(
+ short keyBlob, short appId, short appData, byte[] scratchPad) {
+ // Check if the KeyBlob is compatible. If there is any change in the KeyBlob, the version
+ // Parameter in the KeyBlob should be updated to the next version.
+ short version = readKeyBlobVersion(keyBlob);
+ parseEncryptedKeyBlob(keyBlob, appId, appData, scratchPad, version);
+ if (version < KEYBLOB_CURRENT_VERSION) {
+ return true;
+ }
+ short bootPatchLevel = kmDataStore.getBootPatchLevel();
+ // Fill the key-value properties in the scratchpad
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 16, (byte) 0);
+ Util.setShort(scratchPad, (short) 0, KMType.OS_VERSION);
+ Util.setShort(scratchPad, (short) 2, kmDataStore.getOsVersion());
+ Util.setShort(scratchPad, (short) 4, KMType.OS_PATCH_LEVEL);
+ Util.setShort(scratchPad, (short) 6, kmDataStore.getOsPatch());
+ Util.setShort(scratchPad, (short) 8, KMType.VENDOR_PATCH_LEVEL);
+ Util.setShort(scratchPad, (short) 10, kmDataStore.getVendorPatchLevel());
+ Util.setShort(scratchPad, (short) 12, KMType.BOOT_PATCH_LEVEL);
+ Util.setShort(scratchPad, (short) 14, bootPatchLevel);
+ short index = 0;
+ short tag;
+ short systemParam;
+ boolean isKeyUpgradeRequired = false;
+ while (index < 16) {
+ tag = Util.getShort(scratchPad, index);
+ systemParam = Util.getShort(scratchPad, (short) (index + 2));
+ // validate the tag and check if key needs upgrade.
+ short tagValue = KMKeyParameters.findTag(KMType.UINT_TAG, tag, data[HW_PARAMETERS]);
+ tagValue = KMIntegerTag.cast(tagValue).getValue();
+ short zero = KMInteger.uint_8((byte) 0);
+ if (tagValue != KMType.INVALID_VALUE) {
+ // OS version in key characteristics must be less the OS version stored in Javacard or the
+ // stored version must be zero. Then only upgrade is allowed else it is invalid argument.
+ if ((tag == KMType.OS_VERSION
+ && KMInteger.compare(tagValue, systemParam) == 1
+ && KMInteger.compare(systemParam, zero) == 0)) {
+ // Key needs upgrade.
+ isKeyUpgradeRequired = true;
+ } else if ((KMInteger.compare(tagValue, systemParam) == -1)) {
+ // Each os version or patch level associated with the key must be less than it's
+ // corresponding value stored in Javacard, then only upgrade is allowed otherwise it
+ // is invalid argument.
+ isKeyUpgradeRequired = true;
+ } else if (KMInteger.compare(tagValue, systemParam) == 1) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ } else {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ index += 4;
+ }
+ return isKeyUpgradeRequired;
+ }
+
+ private void processUpgradeKeyCmd(APDU apdu) {
+ // Receive the incoming request fully from the host into buffer.
+ short cmd = upgradeKeyCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+
+ short keyBlob = KMArray.cast(cmd).get((short) 0);
+ data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 1);
+ short appId = getApplicationId(data[KEY_PARAMETERS]);
+ short appData = getApplicationData(data[KEY_PARAMETERS]);
+
+ data[KEY_BLOB] = KMType.INVALID_VALUE;
+ // Check if the KeyBlob requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired
+ // function itself, but if there is a difference in the KeyBlob version isKeyUpgradeRequired()
+ // does not parse the KeyBlob.
+ boolean isKeyUpgradeRequired = isKeyUpgradeRequired(keyBlob, appId, appData, scratchPad);
+ if (isKeyUpgradeRequired) {
+ // copy origin
+ data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]);
+ byte keyType = getKeyType(data[HW_PARAMETERS]);
+ switch (keyType) {
+ case ASYM_KEY_TYPE:
+ data[KEY_BLOB] = KMArray.instance(ASYM_KEY_BLOB_SIZE_V2_V3);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]);
+ break;
+ case SYM_KEY_TYPE:
+ data[KEY_BLOB] = KMArray.instance(SYM_KEY_BLOB_SIZE_V2_V3);
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ // Update the system properties to the latest values and also re-create the KeyBlob's
+ // KeyCharacteristics to make sure all the values are up-to-date with the latest applet
+ // changes.
+ upgradeKeyBlobKeyCharacteristics(data[HW_PARAMETERS], scratchPad);
+ // create new key blob with current os version etc.
+ createEncryptedKeyBlob(scratchPad);
+ short prevReclaimIndex = repository.getHeapReclaimIndex();
+ short offset = repository.allocReclaimableMemory(MAX_KEYBLOB_SIZE);
+ data[KEY_BLOB] =
+ encoder.encode(
+ data[KEY_BLOB], repository.getHeap(), offset, prevReclaimIndex, MAX_KEYBLOB_SIZE);
+ data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), offset, data[KEY_BLOB]);
+ repository.reclaimMemory(MAX_KEYBLOB_SIZE);
+ } else {
+ data[KEY_BLOB] = KMByteBlob.instance((short) 0);
+ }
+ // prepare the response
+ short resp = KMArray.instance((short) 2);
+ KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(resp).add((short) 1, data[KEY_BLOB]);
+ sendOutgoing(apdu, resp);
+ }
+
+ private void processExportKeyCmd(APDU apdu) {
+ sendResponse(apdu, KMError.UNIMPLEMENTED);
+ }
+
+ private void processWrappingKeyBlob(short keyBlob, short wrapParams, byte[] scratchPad) {
+ // Read App Id and App Data if any from un wrapping key params
+ data[APP_ID] = getApplicationId(wrapParams);
+ data[APP_DATA] = getApplicationData(wrapParams);
+ data[KEY_PARAMETERS] = wrapParams;
+ data[KEY_BLOB] = keyBlob;
+ // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired
+ // function itself.
+ if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) {
+ KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE);
+ }
+ validateWrappingKeyBlob();
+ }
+
+ private void validateWrappingKeyBlob() {
+ // check whether the wrapping key is RSA with purpose KEY_WRAP, padding RSA_OAEP and Digest
+ // SHA2_256.
+ KMTag.assertPresence(
+ data[SB_PARAMETERS],
+ KMType.ENUM_TAG,
+ KMType.ALGORITHM,
+ KMError.UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM);
+ if (KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]) != KMType.RSA) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM);
+ }
+ if (!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[HW_PARAMETERS])) {
+ KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+ }
+ if (!KMEnumArrayTag.contains(KMType.PADDING, KMType.RSA_OAEP, data[HW_PARAMETERS])) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE);
+ }
+ if (!KMEnumArrayTag.contains(KMType.PURPOSE, KMType.WRAP_KEY, data[HW_PARAMETERS])) {
+ KMException.throwIt((KMError.INCOMPATIBLE_PURPOSE));
+ }
+
+ // Check that the digest and padding mode specified in unwrapping parameters are SHA2_256
+ // and RSA_OAEP respectively.
+ if (!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+ }
+ if (!KMEnumArrayTag.contains(KMType.PADDING, KMType.RSA_OAEP, data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE);
+ }
+ }
+
+ private short decryptTransportKey(
+ short privExp, short modulus, short transportKey, byte[] scratchPad) {
+ short length =
+ seProvider.rsaDecipherOAEP256(
+ KMByteBlob.cast(privExp).getBuffer(),
+ KMByteBlob.cast(privExp).getStartOff(),
+ KMByteBlob.cast(privExp).length(),
+ KMByteBlob.cast(modulus).getBuffer(),
+ KMByteBlob.cast(modulus).getStartOff(),
+ KMByteBlob.cast(modulus).length(),
+ KMByteBlob.cast(transportKey).getBuffer(),
+ KMByteBlob.cast(transportKey).getStartOff(),
+ KMByteBlob.cast(transportKey).length(),
+ scratchPad,
+ (short) 0);
+ return KMByteBlob.instance(scratchPad, (short) 0, length);
+ }
+
+ private void unmask(short data, short maskingKey) {
+ short dataLength = KMByteBlob.cast(data).length();
+ short maskLength = KMByteBlob.cast(maskingKey).length();
+ // Length of masking key and transport key must be same.
+ if (maskLength != dataLength) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ short index = 0; // index
+ // Xor every byte of masking and key and store the result in data[SECRET]
+ while (index < maskLength) {
+ short var1 = (short) (((short) KMByteBlob.cast(maskingKey).get(index)) & 0x00FF);
+ short var2 = (short) (((short) KMByteBlob.cast(data).get(index)) & 0x00FF);
+ KMByteBlob.cast(data).add(index, (byte) (var1 ^ var2));
+ index++;
+ }
+ }
+
+ private short beginImportWrappedKeyCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 4);
+ short params = KMKeyParameters.expAny();
+ KMArray.cast(cmd).add((short) 0, KMByteBlob.exp()); // Encrypted Transport Key
+ KMArray.cast(cmd).add((short) 1, KMByteBlob.exp()); // Wrapping Key KeyBlob
+ KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); // Masking Key
+ params = KMKeyParameters.exp();
+ KMArray.cast(cmd).add((short) 3, params); // Wrapping key blob Params
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processBeginImportWrappedKeyCmd(APDU apdu) {
+ // Receive the incoming request fully from the host into buffer.
+ short cmd = beginImportWrappedKeyCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+ // Step -1 parse the wrapping key blob
+ // read wrapping key blob
+ short keyBlob = KMArray.cast(cmd).get((short) 1);
+ // read un wrapping key params
+ short wrappingKeyParameters = KMArray.cast(cmd).get((short) 3);
+ processWrappingKeyBlob(keyBlob, wrappingKeyParameters, scratchPad);
+ // Step 2 - decrypt the encrypted transport key - 32 bytes AES-GCM key
+ short transportKey =
+ decryptTransportKey(
+ data[SECRET], data[PUB_KEY], KMArray.cast(cmd).get((short) 0), scratchPad);
+ // Step 3 - XOR the decrypted AES-GCM key with with masking key
+ unmask(transportKey, KMArray.cast(cmd).get((short) 2));
+ if (isValidWrappingKey()) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ setWrappingKey(transportKey);
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private short aesGCMEncrypt(
+ short aesSecret, short input, short nonce, short authData, short authTag, byte[] scratchPad) {
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, KMByteBlob.cast(input).length(), (byte) 0);
+ short len =
+ seProvider.aesGCMEncrypt(
+ KMByteBlob.cast(aesSecret).getBuffer(),
+ KMByteBlob.cast(aesSecret).getStartOff(),
+ KMByteBlob.cast(aesSecret).length(),
+ KMByteBlob.cast(input).getBuffer(),
+ KMByteBlob.cast(input).getStartOff(),
+ KMByteBlob.cast(input).length(),
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(nonce).getBuffer(),
+ KMByteBlob.cast(nonce).getStartOff(),
+ KMByteBlob.cast(nonce).length(),
+ KMByteBlob.cast(authData).getBuffer(),
+ KMByteBlob.cast(authData).getStartOff(),
+ KMByteBlob.cast(authData).length(),
+ KMByteBlob.cast(authTag).getBuffer(),
+ KMByteBlob.cast(authTag).getStartOff(),
+ KMByteBlob.cast(authTag).length());
+ return KMByteBlob.instance(scratchPad, (short) 0, len);
+ }
+
+ private short aesGCMDecrypt(
+ short aesSecret, short input, short nonce, short authData, short authTag, byte[] scratchPad) {
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, KMByteBlob.cast(input).length(), (byte) 0);
+ if (!seProvider.aesGCMDecrypt(
+ KMByteBlob.cast(aesSecret).getBuffer(),
+ KMByteBlob.cast(aesSecret).getStartOff(),
+ KMByteBlob.cast(aesSecret).length(),
+ KMByteBlob.cast(input).getBuffer(),
+ KMByteBlob.cast(input).getStartOff(),
+ KMByteBlob.cast(input).length(),
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(nonce).getBuffer(),
+ KMByteBlob.cast(nonce).getStartOff(),
+ KMByteBlob.cast(nonce).length(),
+ KMByteBlob.cast(authData).getBuffer(),
+ KMByteBlob.cast(authData).getStartOff(),
+ KMByteBlob.cast(authData).length(),
+ KMByteBlob.cast(authTag).getBuffer(),
+ KMByteBlob.cast(authTag).getStartOff(),
+ KMByteBlob.cast(authTag).length())) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+ return KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.cast(input).length());
+ }
+
+ private short finishImportWrappedKeyCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 8);
+ short params = KMKeyParameters.expAny();
+ KMArray.cast(cmd).add((short) 0, params); // Key Params of wrapped key
+ KMArray.cast(cmd).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); // Key Format
+ KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); // Wrapped Import Key Blob
+ KMArray.cast(cmd).add((short) 3, KMByteBlob.exp()); // Auth Tag
+ KMArray.cast(cmd).add((short) 4, KMByteBlob.exp()); // IV - Nonce
+ KMArray.cast(cmd).add((short) 5, KMByteBlob.exp()); // Wrapped Key ASSOCIATED AUTH DATA
+ KMArray.cast(cmd).add((short) 6, KMInteger.exp()); // Password Sid
+ KMArray.cast(cmd).add((short) 7, KMInteger.exp()); // Biometric Sid
+ return receiveIncoming(apdu, cmd);
+ }
+
+ // TODO remove cmd later on
+ private void processFinishImportWrappedKeyCmd(APDU apdu) {
+ short cmd = finishImportWrappedKeyCmd(apdu);
+ short keyParameters = KMArray.cast(cmd).get((short) 0);
+ short keyFmt = KMArray.cast(cmd).get((short) 1);
+ keyFmt = KMEnum.cast(keyFmt).getVal();
+ validateImportKey(keyParameters, keyFmt);
+ byte[] scratchPad = apdu.getBuffer();
+ // Step 4 - AES-GCM decrypt the wrapped key
+ data[INPUT_DATA] = KMArray.cast(cmd).get((short) 2);
+ data[AUTH_TAG] = KMArray.cast(cmd).get((short) 3);
+ data[NONCE] = KMArray.cast(cmd).get((short) 4);
+ data[AUTH_DATA] = KMArray.cast(cmd).get((short) 5);
+
+ if (!isValidWrappingKey()) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ data[IMPORTED_KEY_BLOB] =
+ aesGCMDecrypt(
+ getWrappingKey(),
+ data[INPUT_DATA],
+ data[NONCE],
+ data[AUTH_DATA],
+ data[AUTH_TAG],
+ scratchPad);
+ resetWrappingKey();
+ // Step 5 - Import decrypted key
+ data[ORIGIN] = KMType.SECURELY_IMPORTED;
+ data[KEY_PARAMETERS] = keyParameters;
+ // create key blob array
+ importKey(apdu, keyFmt, scratchPad);
+ }
+
+ private KMAttestationCert makeCommonCert(byte[] scratchPad) {
+ short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[KEY_PARAMETERS]);
+ boolean rsaCert = KMEnumTag.cast(alg).getValue() == KMType.RSA;
+ KMAttestationCert cert = KMAttestationCertImpl.instance(rsaCert, seProvider);
+
+ short subject =
+ KMKeyParameters.findTag(
+ KMType.BYTES_TAG, KMType.CERTIFICATE_SUBJECT_NAME, data[KEY_PARAMETERS]);
+
+ // If no subject name is specified then use the default subject name.
+ if (subject == KMType.INVALID_VALUE || KMByteTag.cast(subject).length() == 0) {
+ subject = KMByteBlob.instance(defaultSubject, (short) 0, (short) defaultSubject.length);
+ } else {
+ subject = KMByteTag.cast(subject).getValue();
+ }
+ cert.subjectName(subject);
+ // Validity period must be specified
+ short notBefore =
+ KMKeyParameters.findTag(
+ KMType.DATE_TAG, KMType.CERTIFICATE_NOT_BEFORE, data[KEY_PARAMETERS]);
+ if (notBefore == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.MISSING_NOT_BEFORE);
+ }
+ notBefore = KMIntegerTag.cast(notBefore).getValue();
+ short notAfter =
+ KMKeyParameters.findTag(
+ KMType.DATE_TAG, KMType.CERTIFICATE_NOT_AFTER, data[KEY_PARAMETERS]);
+ if (notAfter == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.MISSING_NOT_AFTER);
+ }
+ notAfter = KMIntegerTag.cast(notAfter).getValue();
+ // VTS sends notBefore == Epoch.
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 8, (byte) 0);
+ short epoch = KMInteger.instance(scratchPad, (short) 0, (short) 8);
+ short end = KMInteger.instance(dec319999Ms, (short) 0, (short) dec319999Ms.length);
+ if (KMInteger.compare(notBefore, epoch) == 0) {
+ cert.notBefore(
+ KMByteBlob.instance(jan01970, (short) 0, (short) jan01970.length), true, scratchPad);
+ } else {
+ cert.notBefore(notBefore, false, scratchPad);
+ }
+ // VTS sends notAfter == Dec 31st 9999
+ if (KMInteger.compare(notAfter, end) == 0) {
+ cert.notAfter(
+ KMByteBlob.instance(dec319999, (short) 0, (short) dec319999.length), true, scratchPad);
+ } else {
+ cert.notAfter(notAfter, false, scratchPad);
+ }
+ // Serial number
+ short serialNum =
+ KMKeyParameters.findTag(
+ KMType.BIGNUM_TAG, KMType.CERTIFICATE_SERIAL_NUM, data[KEY_PARAMETERS]);
+ if (serialNum != KMType.INVALID_VALUE) {
+ serialNum = KMBignumTag.cast(serialNum).getValue();
+ } else {
+ serialNum = KMByteBlob.instance((short) 1);
+ KMByteBlob.cast(serialNum).add((short) 0, (byte) 1);
+ }
+ cert.serialNumber(serialNum);
+ return cert;
+ }
+
+ private KMAttestationCert makeAttestationCert(
+ short attKeyBlob, short attKeyParam, short attChallenge, short issuer, byte[] scratchPad) {
+ KMAttestationCert cert = makeCommonCert(scratchPad);
+
+ // Read App Id and App Data.
+ short appId = getApplicationId(attKeyParam);
+ short appData = getApplicationData(attKeyParam);
+ // Take backup of the required global variables KEY_BLOB, PUB_KEY, SECRET, KEY_CHAR
+ // and HW_PARAMS before they get overridden by isKeyUpgradeRequired() function.
+ short origBlob = data[KEY_BLOB];
+ short pubKey = data[PUB_KEY];
+ short privKey = data[SECRET];
+ short hwParams = data[HW_PARAMETERS];
+ short keyChars = data[KEY_CHARACTERISTICS];
+ short customTags = data[CUSTOM_TAGS];
+ // Check if key requires upgrade for attestKeyBlob. The KeyBlob is parsed inside
+ // isKeyUpgradeRequired function itself.
+ if (isKeyUpgradeRequired(attKeyBlob, appId, appData, scratchPad)) {
+ KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE);
+ }
+ // Get the private key of the attest key.
+ short attestationKeySecret = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_SECRET);
+ // Get the KeyCharacteristics and SB param of the attest key
+ short attestKeyCharacteristics = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_PARAMS);
+ short attestKeySbParams =
+ KMKeyCharacteristics.cast(attestKeyCharacteristics).getStrongboxEnforced();
+ // If the attest key's purpose is not "attest key" then error.
+ short attKeyPurpose =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, attestKeySbParams);
+ if (!KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY)) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+ }
+ KMAsn1Parser asn1Decoder = KMAsn1Parser.instance();
+ try {
+ asn1Decoder.validateDerSubject(issuer);
+ } catch (KMException e) {
+ KMException.throwIt(KMError.INVALID_ISSUER_SUBJECT_NAME);
+ }
+ if (KMByteBlob.cast(issuer).length() > KMConfigurations.MAX_SUBJECT_DER_LEN) {
+ KMException.throwIt(KMError.INVALID_ISSUER_SUBJECT_NAME);
+ }
+ // If issuer is not present then it is an error
+ if (KMByteBlob.cast(issuer).length() <= 0) {
+ KMException.throwIt(KMError.MISSING_ISSUER_SUBJECT_NAME);
+ }
+ short alg = KMEnumTag.getValue(KMType.ALGORITHM, attestKeySbParams);
+ if (alg == KMType.RSA) {
+ short attestationKeyPublic = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_PUB_KEY);
+ cert.rsaAttestKey(attestationKeySecret, attestationKeyPublic, KMType.ATTESTATION_CERT);
+ } else if (alg == KMType.EC) {
+ cert.ecAttestKey(attestationKeySecret, KMType.ATTESTATION_CERT);
+ } else {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ cert.attestationChallenge(attChallenge);
+ cert.issuer(issuer);
+
+ // Restore back the global variables.
+ data[PUB_KEY] = pubKey;
+ data[SECRET] = privKey;
+ data[KEY_BLOB] = origBlob;
+ data[HW_PARAMETERS] = hwParams;
+ data[KEY_CHARACTERISTICS] = keyChars;
+ data[CUSTOM_TAGS] = customTags;
+ data[SW_PARAMETERS] =
+ KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getKeystoreEnforced();
+ data[TEE_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced();
+ data[SB_PARAMETERS] =
+ KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getStrongboxEnforced();
+ cert.publicKey(data[PUB_KEY]);
+
+ // Save attestation application id - must be present.
+ short attAppId =
+ KMKeyParameters.findTag(
+ KMType.BYTES_TAG, KMType.ATTESTATION_APPLICATION_ID, data[KEY_PARAMETERS]);
+ if (attAppId == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.ATTESTATION_APPLICATION_ID_MISSING);
+ }
+ cert.extensionTag(attAppId, false);
+ // unique id byte blob - uses application id and temporal month count of
+ // creation time.
+ attAppId = KMByteTag.cast(attAppId).getValue();
+ setUniqueId(cert, attAppId, scratchPad);
+ // Add Attestation Ids if present
+ addAttestationIds(cert, scratchPad);
+
+ // Add Tags
+ addTags(data[HW_PARAMETERS], true, cert);
+ addTags(data[SW_PARAMETERS], false, cert);
+ // Add Device Boot locked status
+ cert.deviceLocked(kmDataStore.isDeviceBootLocked());
+ // VB data
+ cert.verifiedBootHash(getVerifiedBootHash(scratchPad));
+ cert.verifiedBootKey(getBootKey(scratchPad));
+ cert.verifiedBootState((byte) kmDataStore.getBootState());
+ return cert;
+ }
+
+ private KMAttestationCert makeSelfSignedCert(
+ short attPrivKey, short attPubKey, short mode, byte[] scratchPad) {
+ KMAttestationCert cert = makeCommonCert(scratchPad);
+ short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]);
+ short subject =
+ KMKeyParameters.findTag(
+ KMType.BYTES_TAG, KMType.CERTIFICATE_SUBJECT_NAME, data[KEY_PARAMETERS]);
+ // If no subject name is specified then use the default subject name.
+ if (subject == KMType.INVALID_VALUE || KMByteTag.cast(subject).length() == 0) {
+ subject = KMByteBlob.instance(defaultSubject, (short) 0, (short) defaultSubject.length);
+ } else {
+ subject = KMByteTag.cast(subject).getValue();
+ }
+
+ if (alg == KMType.RSA) {
+ cert.rsaAttestKey(attPrivKey, attPubKey, (byte) mode);
+ } else {
+ cert.ecAttestKey(attPrivKey, (byte) mode);
+ }
+ cert.issuer(subject);
+ cert.subjectName(subject);
+ cert.publicKey(attPubKey);
+ return cert;
+ }
+
+ protected short getBootKey(byte[] scratchPad) {
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, VERIFIED_BOOT_KEY_SIZE, (byte) 0);
+ short len = kmDataStore.getBootKey(scratchPad, (short) 0);
+ if (len != VERIFIED_BOOT_KEY_SIZE) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ return KMByteBlob.instance(scratchPad, (short) 0, VERIFIED_BOOT_KEY_SIZE);
+ }
+
+ protected short getVerifiedBootHash(byte[] scratchPad) {
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, VERIFIED_BOOT_HASH_SIZE, (byte) 0);
+ short len = kmDataStore.getVerifiedBootHash(scratchPad, (short) 0);
+ if (len != VERIFIED_BOOT_HASH_SIZE) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ return KMByteBlob.instance(scratchPad, (short) 0, VERIFIED_BOOT_HASH_SIZE);
+ }
+
+ // --------------------------------
+ // Only add the Attestation ids which are requested in the attestation parameters.
+ // If the requested attestation ids are not provisioned or deleted then
+ // throw CANNOT_ATTEST_IDS error. If there is mismatch in the attestation
+ // id values of both the requested parameters and the provisioned parameters
+ // then throw INVALID_TAG error.
+ private void addAttestationIds(KMAttestationCert cert, byte[] scratchPad) {
+ byte index = 0;
+ short attIdTag;
+ short attIdTagValue;
+ short storedAttIdLen;
+ while (index < (short) attTags.length) {
+ attIdTag = KMKeyParameters.findTag(KMType.BYTES_TAG, attTags[index], data[KEY_PARAMETERS]);
+ if (attIdTag != KMType.INVALID_VALUE) {
+ attIdTagValue = KMByteTag.cast(attIdTag).getValue();
+ storedAttIdLen = kmDataStore.getAttestationId(attTags[index], scratchPad, (short) 0);
+ // Return CANNOT_ATTEST_IDS if Attestation IDs are not provisioned or
+ // Attestation IDs are deleted.
+ if (storedAttIdLen == 0) {
+ KMException.throwIt(KMError.CANNOT_ATTEST_IDS);
+ }
+ // Return INVALID_TAG if Attestation IDs does not match.
+ if ((storedAttIdLen != KMByteBlob.cast(attIdTagValue).length())
+ || (0
+ != Util.arrayCompare(
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(attIdTagValue).getBuffer(),
+ KMByteBlob.cast(attIdTagValue).getStartOff(),
+ storedAttIdLen))) {
+ KMException.throwIt(KMError.CANNOT_ATTEST_IDS);
+ }
+ short blob = KMByteBlob.instance(scratchPad, (short) 0, storedAttIdLen);
+ cert.extensionTag(KMByteTag.instance(attTags[index], blob), true);
+ }
+ index++;
+ }
+ }
+
+ private void processDestroyAttIdsCmd(APDU apdu) {
+ kmDataStore.deleteAttestationIds();
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private void processVerifyAuthorizationCmd(APDU apdu) {
+ sendResponse(apdu, KMError.UNIMPLEMENTED);
+ }
+
+ private short abortOperationCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 1);
+ KMArray.cast(cmd).add((short) 0, KMInteger.exp());
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processAbortOperationCmd(APDU apdu) {
+ short cmd = abortOperationCmd(apdu);
+ data[OP_HANDLE] = KMArray.cast(cmd).get((short) 0);
+ KMOperationState op = findOperation(data[OP_HANDLE]);
+ if (op == null) {
+ sendResponse(apdu, KMError.INVALID_OPERATION_HANDLE);
+ } else {
+ releaseOperation(op);
+ sendResponse(apdu, KMError.OK);
+ }
+ }
+
+ private short finishOperationCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 6);
+ KMArray.cast(cmd).add((short) 0, KMInteger.exp()); // op handle
+ KMArray.cast(cmd).add((short) 1, KMByteBlob.exp()); // input data
+ KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); // signature
+ short authToken = KMHardwareAuthToken.exp();
+ KMArray.cast(cmd).add((short) 3, authToken); // auth token
+ short verToken = KMVerificationToken.exp();
+ KMArray.cast(cmd).add((short) 4, verToken); // time stamp token
+ KMArray.cast(cmd).add((short) 5, KMByteBlob.exp()); // confirmation token
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processFinishOperationCmd(APDU apdu) {
+ short cmd = finishOperationCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+ data[OP_HANDLE] = KMArray.cast(cmd).get((short) 0);
+ data[INPUT_DATA] = KMArray.cast(cmd).get((short) 1);
+ data[SIGNATURE] = KMArray.cast(cmd).get((short) 2);
+ data[HW_TOKEN] = KMArray.cast(cmd).get((short) 3);
+ data[VERIFICATION_TOKEN] = KMArray.cast(cmd).get((short) 4);
+ data[CONFIRMATION_TOKEN] = KMArray.cast(cmd).get((short) 5);
+ // Check Operation Handle
+ KMOperationState op = findOperation(data[OP_HANDLE]);
+ if (op == null) {
+ KMException.throwIt(KMError.INVALID_OPERATION_HANDLE);
+ }
+ // Authorize the finish operation
+ authorizeUpdateFinishOperation(op, scratchPad);
+ switch (op.getPurpose()) {
+ case KMType.SIGN:
+ finishTrustedConfirmationOperation(op);
+ case KMType.VERIFY:
+ finishSigningVerifyingOperation(op, scratchPad);
+ break;
+ case KMType.ENCRYPT:
+ finishEncryptOperation(op, scratchPad);
+ break;
+ case KMType.DECRYPT:
+ finishDecryptOperation(op, scratchPad);
+ break;
+ case KMType.AGREE_KEY:
+ finishKeyAgreementOperation(op, scratchPad);
+ break;
+ }
+ if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) {
+ data[OUTPUT_DATA] = KMByteBlob.instance((short) 0);
+ }
+ // Remove the operation handle
+ releaseOperation(op);
+
+ // make response
+ short resp = KMArray.instance((short) 2);
+ KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(resp).add((short) 1, data[OUTPUT_DATA]);
+ sendOutgoing(apdu, resp);
+ }
+
+ private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) {
+ if (op.getAlgorithm() != KMType.AES && op.getAlgorithm() != KMType.DES) {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ finishAesDesOperation(op);
+ }
+
+ private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) {
+ short len = KMByteBlob.cast(data[INPUT_DATA]).length();
+ switch (op.getAlgorithm()) {
+ case KMType.RSA:
+ // Fill the scratch pad with zero
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+ if (op.getPadding() == KMType.PADDING_NONE && len != 256) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ len =
+ op.getOperation()
+ .finish(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ len,
+ scratchPad,
+ (short) 0);
+
+ data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short) 0, len);
+ break;
+ case KMType.AES:
+ case KMType.DES:
+ finishAesDesOperation(op);
+ break;
+ }
+ }
+
+ private void finishAesDesOperation(KMOperationState op) {
+ short len = KMByteBlob.cast(data[INPUT_DATA]).length();
+ short blockSize = AES_BLOCK_SIZE;
+ if (op.getAlgorithm() == KMType.DES) {
+ blockSize = DES_BLOCK_SIZE;
+ }
+
+ if (op.getPurpose() == KMType.DECRYPT
+ && len > 0
+ && (op.getBlockMode() == KMType.ECB || op.getBlockMode() == KMType.CBC)
+ && ((short) (len % blockSize) != 0)) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+
+ if (op.getBlockMode() == KMType.GCM) {
+ if (op.getPurpose() == KMType.DECRYPT && (len < (short) (op.getMacLength() / 8))) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ if (op.isAesGcmUpdateAllowed()) {
+ op.setAesGcmUpdateComplete();
+ }
+ // Get the output size
+ len = op.getOperation().getAESGCMOutputSize(len, (short) (op.getMacLength() / 8));
+ }
+ // If padding i.e. pkcs7 then add padding to right
+ // Output data can at most one block size more the input data in case of pkcs7 encryption
+ // In case of gcm we will allocate extra memory of the size equal to blocksize.
+ data[OUTPUT_DATA] = KMByteBlob.instance((short) (len + 2 * blockSize));
+ try {
+ len =
+ op.getOperation()
+ .finish(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ KMByteBlob.cast(data[INPUT_DATA]).length(),
+ KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff());
+ } catch (CryptoException e) {
+ if (e.getReason() == CryptoException.ILLEGAL_USE) {
+ // As per VTS, zero length input on AES/DES with PADDING_NONE Should return a zero length
+ // output. But JavaCard fails with CryptoException.ILLEGAL_USE if no input data is
+ // provided via update() method. So ignore this exception in case if all below conditions
+ // are satisfied and simply return empty output.
+ // 1. padding mode is PADDING_NONE.
+ // 2. No input message is processed in update().
+ // 3. Zero length input data is passed in finish operation.
+ if ((op.getPadding() == KMType.PADDING_NONE)
+ && !op.isInputMsgProcessed()
+ && (KMByteBlob.cast(data[INPUT_DATA]).length() == 0)) {
+ len = 0;
+ } else {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ }
+ }
+ KMByteBlob.cast(data[OUTPUT_DATA]).setLength(len);
+ }
+
+ private void finishKeyAgreementOperation(KMOperationState op, byte[] scratchPad) {
+ try {
+ KMAsn1Parser pkcs8 = KMAsn1Parser.instance();
+ short blob = pkcs8.decodeEcSubjectPublicKeyInfo(data[INPUT_DATA]);
+ short len =
+ op.getOperation()
+ .finish(
+ KMByteBlob.cast(blob).getBuffer(),
+ KMByteBlob.cast(blob).getStartOff(),
+ KMByteBlob.cast(blob).length(),
+ scratchPad,
+ (short) 0);
+ data[OUTPUT_DATA] = KMByteBlob.instance((short) 32);
+ Util.arrayCopyNonAtomic(
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff(),
+ len);
+ } catch (CryptoException e) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ }
+
+ private void finishSigningVerifyingOperation(KMOperationState op, byte[] scratchPad) {
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+ switch (op.getAlgorithm()) {
+ case KMType.RSA:
+ // If there is no padding we can treat signing as a RSA decryption operation.
+ try {
+ if (op.getPurpose() == KMType.SIGN) {
+ // len of signature will be 256 bytes - but it can be less then 256 bytes
+ short len =
+ op.getOperation()
+ .sign(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ KMByteBlob.cast(data[INPUT_DATA]).length(),
+ scratchPad,
+ (short) 0);
+ // Maximum output size of signature is 256 bytes. - the signature will always be
+ // positive
+ data[OUTPUT_DATA] = KMByteBlob.instance((short) 256);
+ Util.arrayCopyNonAtomic(
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(),
+ (short) (KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff() + 256 - len),
+ len);
+ } else {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ } catch (CryptoException e) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ break;
+ case KMType.EC:
+ short len = KMByteBlob.cast(data[INPUT_DATA]).length();
+ // If DIGEST NONE then truncate the input data to 32 bytes.
+ if (op.getDigest() == KMType.DIGEST_NONE && len > 32) {
+ len = 32;
+ }
+ if (op.getPurpose() == KMType.SIGN) {
+ // len of signature will be 512 bits i.e. 64 bytes
+ len =
+ op.getOperation()
+ .sign(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ len,
+ scratchPad,
+ (short) 0);
+ data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short) 0, len);
+ } else {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ break;
+ case KMType.HMAC:
+ // As per Keymaster HAL documentation, the length of the Hmac output can
+ // be decided by using TAG_MAC_LENGTH in Keyparameters. But there is no
+ // such provision to control the length of the Hmac output using JavaCard
+ // crypto APIs and the current implementation always returns 32 bytes
+ // length of Hmac output. So to provide support to TAG_MAC_LENGTH
+ // feature, we truncate the output signature to TAG_MAC_LENGTH and return
+ // the truncated signature back to the caller. At the time of verfication
+ // we again compute the signature of the plain text input, truncate it to
+ // TAG_MAC_LENGTH and compare it with the input signature for
+ // verification.
+ op.getOperation()
+ .sign(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ KMByteBlob.cast(data[INPUT_DATA]).length(),
+ scratchPad,
+ (short) 0);
+ if (op.getPurpose() == KMType.SIGN) {
+ // Copy only signature of mac length size.
+ data[OUTPUT_DATA] =
+ KMByteBlob.instance(scratchPad, (short) 0, (short) (op.getMacLength() / 8));
+ } else if (op.getPurpose() == KMType.VERIFY) {
+ if ((KMByteBlob.cast(data[SIGNATURE]).length() < (MIN_HMAC_LENGTH_BITS / 8))
+ || KMByteBlob.cast(data[SIGNATURE]).length() > (SHA256_DIGEST_LEN_BITS / 8)) {
+ KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH);
+ }
+ if ((KMByteBlob.cast(data[SIGNATURE]).length() < (short) (op.getMinMacLength() / 8))) {
+ KMException.throwIt(KMError.INVALID_MAC_LENGTH);
+ }
+
+ if (0
+ != Util.arrayCompare(
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(data[SIGNATURE]).getBuffer(),
+ KMByteBlob.cast(data[SIGNATURE]).getStartOff(),
+ KMByteBlob.cast(data[SIGNATURE]).length())) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+ data[OUTPUT_DATA] = KMByteBlob.instance((short) 0);
+ }
+ break;
+ default: // This is should never happen
+ KMException.throwIt(KMError.OPERATION_CANCELLED);
+ break;
+ }
+ }
+
+ private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchPad) {
+ // If one time user Authentication is required
+ if (op.isSecureUserIdReqd() && !op.isAuthTimeoutValidated()) {
+ // Validate Verification Token.
+ validateVerificationToken(data[VERIFICATION_TOKEN], scratchPad);
+ // validate operation handle.
+ short ptr = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getChallenge();
+ if (KMInteger.compare(ptr, op.getHandle()) != 0) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+ tmpVariables[0] = op.getAuthTime();
+ tmpVariables[2] = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp();
+ if (tmpVariables[2] == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+ if (KMInteger.compare(tmpVariables[0], tmpVariables[2]) < 0) {
+ KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+ }
+ op.setAuthTimeoutValidated(true);
+ } else if (op.isAuthPerOperationReqd()) { // If Auth per operation is required
+ if (!validateHwToken(data[HW_TOKEN], scratchPad)) {
+ KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+ }
+ tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getChallenge();
+ if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) {
+ KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+ }
+ if (!authTokenMatches(op.getUserSecureId(), op.getAuthType(), scratchPad)) {
+ KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+ }
+ }
+ }
+
+ private void authorizeKeyUsageForCount(byte[] scratchPad) {
+ short scratchPadOff = 0;
+ Util.arrayFillNonAtomic(scratchPad, scratchPadOff, (short) 12, (byte) 0);
+
+ short usageLimitBufLen =
+ KMIntegerTag.getValue(
+ scratchPad,
+ scratchPadOff,
+ KMType.UINT_TAG,
+ KMType.MAX_USES_PER_BOOT,
+ data[HW_PARAMETERS]);
+
+ if (usageLimitBufLen == KMType.INVALID_VALUE) {
+ return;
+ }
+
+ if (usageLimitBufLen > 4) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+
+ if (kmDataStore.isAuthTagPersisted(data[AUTH_TAG])) {
+ // Get current counter, update and increment it.
+ short len =
+ kmDataStore.getRateLimitedKeyCount(
+ data[AUTH_TAG], scratchPad, (short) (scratchPadOff + 4));
+ if (len != 4) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ if (0
+ >= KMInteger.unsignedByteArrayCompare(
+ scratchPad, scratchPadOff, scratchPad, (short) (scratchPadOff + 4), (short) 4)) {
+ KMException.throwIt(KMError.KEY_MAX_OPS_EXCEEDED);
+ }
+ // Increment the counter.
+ Util.arrayFillNonAtomic(scratchPad, scratchPadOff, len, (byte) 0);
+ Util.setShort(scratchPad, (short) (scratchPadOff + 2), (short) 1);
+ KMUtils.add(
+ scratchPad,
+ scratchPadOff,
+ (short) (scratchPadOff + len),
+ (short) (scratchPadOff + len * 2));
+
+ kmDataStore.setRateLimitedKeyCount(
+ data[AUTH_TAG], scratchPad, (short) (scratchPadOff + len * 2), len);
+ } else {
+ // Persist auth tag.
+ if (!kmDataStore.persistAuthTag(data[AUTH_TAG])) {
+ KMException.throwIt(KMError.TOO_MANY_OPERATIONS);
+ }
+ }
+ }
+
+ private void authorizeDeviceUnlock(byte[] scratchPad) {
+ // If device is locked and key characteristics requires unlocked device then check whether
+ // HW auth token has correct timestamp.
+ short ptr =
+ KMKeyParameters.findTag(
+ KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, data[HW_PARAMETERS]);
+
+ if (ptr != KMType.INVALID_VALUE && kmDataStore.getDeviceLock()) {
+ if (data[HW_TOKEN] == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.DEVICE_LOCKED);
+ }
+ ptr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp();
+ // Check if the current auth time stamp is greater than device locked time stamp
+ short ts = kmDataStore.getDeviceTimeStamp();
+ if (KMInteger.compare(ptr, ts) <= 0) {
+ KMException.throwIt(KMError.DEVICE_LOCKED);
+ }
+ // Now check if the device unlock requires password only authentication and whether
+ // auth token is generated through password authentication or not.
+ if (kmDataStore.getDeviceLockPasswordOnly()) {
+ ptr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType();
+ ptr = KMEnum.cast(ptr).getVal();
+ if (((byte) ptr & KMType.PASSWORD) == 0) {
+ KMException.throwIt(KMError.DEVICE_LOCKED);
+ }
+ }
+ // Unlock the device
+ // repository.deviceLockedFlag = false;
+ kmDataStore.setDeviceLock(false);
+ kmDataStore.clearDeviceLockTimeStamp();
+ }
+ }
+
+ private boolean verifyVerificationTokenMacInBigEndian(short verToken, byte[] scratchPad) {
+ // concatenation length will be 37 + length of verified parameters list - which
+ // is typically empty
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+ // Add "Auth Verification" - 17 bytes.
+ Util.arrayCopyNonAtomic(
+ authVerification, (short) 0, scratchPad, (short) 0, (short) authVerification.length);
+ short len = (short) authVerification.length;
+ // concatenate challenge - 8 bytes
+ short ptr = KMVerificationToken.cast(verToken).getChallenge();
+ KMInteger.cast(ptr)
+ .value(
+ scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+ len += KMInteger.UINT_64;
+ // concatenate timestamp -8 bytes
+ ptr = KMVerificationToken.cast(verToken).getTimestamp();
+ KMInteger.cast(ptr)
+ .value(
+ scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+ len += KMInteger.UINT_64;
+ // concatenate security level - 4 bytes
+ scratchPad[(short) (len + 3)] = TRUSTED_ENVIRONMENT;
+ len += KMInteger.UINT_32;
+ // hmac the data
+ ptr = KMVerificationToken.cast(verToken).getMac();
+
+ return seProvider.hmacVerify(
+ kmDataStore.getComputedHmacKey(),
+ scratchPad,
+ (short) 0,
+ len,
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length());
+ }
+
+ private void validateVerificationToken(short verToken, byte[] scratchPad) {
+ short ptr = KMVerificationToken.cast(verToken).getMac();
+ // If mac length is zero then token is empty.
+ if (KMByteBlob.cast(ptr).length() == 0) {
+ KMException.throwIt(KMError.INVALID_MAC_LENGTH);
+ }
+ if (!verifyVerificationTokenMacInBigEndian(verToken, scratchPad)) {
+ // Throw Exception if none of the combination works.
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+ }
+
+ private short updateOperationCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 4);
+ // Arguments
+ KMArray.cast(cmd).add((short) 0, KMInteger.exp());
+ KMArray.cast(cmd).add((short) 1, KMByteBlob.exp());
+ short authToken = KMHardwareAuthToken.exp();
+ KMArray.cast(cmd).add((short) 2, authToken);
+ short verToken = KMVerificationToken.exp();
+ KMArray.cast(cmd).add((short) 3, verToken);
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processUpdateOperationCmd(APDU apdu) {
+ short cmd = updateOperationCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+ data[OP_HANDLE] = KMArray.cast(cmd).get((short) 0);
+ data[INPUT_DATA] = KMArray.cast(cmd).get((short) 1);
+ data[HW_TOKEN] = KMArray.cast(cmd).get((short) 2);
+ data[VERIFICATION_TOKEN] = KMArray.cast(cmd).get((short) 3);
+
+ // Input data must be present even if it is zero length.
+ if (data[INPUT_DATA] == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+
+ // Check Operation Handle and get op state
+ // Check Operation Handle
+ KMOperationState op = findOperation(data[OP_HANDLE]);
+ if (op == null) {
+ KMException.throwIt(KMError.INVALID_OPERATION_HANDLE);
+ }
+ // authorize the update operation
+ authorizeUpdateFinishOperation(op, scratchPad);
+
+ if (op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY) {
+ // update the data.
+ op.getOperation()
+ .update(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ KMByteBlob.cast(data[INPUT_DATA]).length());
+ // update trusted confirmation operation
+ updateTrustedConfirmationOperation(op);
+
+ data[OUTPUT_DATA] = KMType.INVALID_VALUE;
+ } else if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) {
+ // Update for encrypt/decrypt using RSA will not be supported because to do this op state
+ // will have to buffer the data - so reject the update if it is rsa algorithm.
+ if (op.getAlgorithm() == KMType.RSA) {
+ KMException.throwIt(KMError.OPERATION_CANCELLED);
+ }
+ short len = KMByteBlob.cast(data[INPUT_DATA]).length();
+ short blockSize = DES_BLOCK_SIZE;
+ if (op.getAlgorithm() == KMType.AES) {
+ blockSize = AES_BLOCK_SIZE;
+ if (op.getBlockMode() == KMType.GCM) {
+ // if input data present
+ if (len > 0) {
+ // no more future updateAAD allowed if input data present.
+ if (op.isAesGcmUpdateAllowed()) {
+ op.setAesGcmUpdateComplete();
+ }
+ }
+ }
+ }
+ // Allocate output buffer as input data is already block aligned
+ data[OUTPUT_DATA] = KMByteBlob.instance((short) (len + 2 * blockSize));
+ // Otherwise just update the data.
+ // HAL consumes all the input and maintains a buffered data inside it. So the
+ // applet sends the inputConsumed length as same as the input length.
+ try {
+ len =
+ op.getOperation()
+ .update(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ KMByteBlob.cast(data[INPUT_DATA]).length(),
+ KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff());
+ } catch (CryptoException e) {
+ KMException.throwIt(KMError.INVALID_TAG);
+ }
+ if (KMByteBlob.cast(data[INPUT_DATA]).length() > 0) {
+ // This flag is used to denote that an input data of length > 0 is received and processed
+ // successfully in update command. This flag is later used in the finish operation
+ // to handle a particular use case, where a zero length input data on AES/DES algorithm
+ // with PADDING_NONE should return a zero length output with OK response.
+ op.setProcessedInputMsg(true);
+ }
+ // Adjust the Output data if it is not equal to input data.
+ // This happens in case of JCardSim provider.
+ KMByteBlob.cast(data[OUTPUT_DATA]).setLength(len);
+ }
+
+ if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) {
+ data[OUTPUT_DATA] = KMByteBlob.instance((short) 0);
+ }
+ // Persist if there are any updates.
+ // op.persist();
+ // make response
+ short resp = KMArray.instance((short) 2);
+ KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(resp).add((short) 1, data[OUTPUT_DATA]);
+ sendOutgoing(apdu, resp);
+ }
+
+ private short updateAadOperationCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 4);
+ KMArray.cast(cmd).add((short) 0, KMInteger.exp());
+ KMArray.cast(cmd).add((short) 1, KMByteBlob.exp());
+ short authToken = KMHardwareAuthToken.exp();
+ KMArray.cast(cmd).add((short) 2, authToken);
+ short verToken = KMVerificationToken.exp();
+ KMArray.cast(cmd).add((short) 3, verToken);
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processUpdateAadOperationCmd(APDU apdu) {
+ short cmd = updateAadOperationCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+ data[OP_HANDLE] = KMArray.cast(cmd).get((short) 0);
+ data[INPUT_DATA] = KMArray.cast(cmd).get((short) 1);
+ data[HW_TOKEN] = KMArray.cast(cmd).get((short) 2);
+ data[VERIFICATION_TOKEN] = KMArray.cast(cmd).get((short) 3);
+
+ // Input data must be present even if it is zero length.
+ if (data[INPUT_DATA] == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ // Check Operation Handle and get op state
+ // Check Operation Handle
+ KMOperationState op = findOperation(data[OP_HANDLE]);
+ if (op == null) {
+ KMException.throwIt(KMError.INVALID_OPERATION_HANDLE);
+ }
+ if (op.getAlgorithm() != KMType.AES) {
+ KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM);
+ }
+ if (op.getBlockMode() != KMType.GCM) {
+ KMException.throwIt(KMError.INCOMPATIBLE_BLOCK_MODE);
+ }
+ if (!op.isAesGcmUpdateAllowed()) {
+ KMException.throwIt(KMError.INVALID_TAG);
+ }
+ if (op.getPurpose() != KMType.ENCRYPT && op.getPurpose() != KMType.DECRYPT) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+ }
+ // authorize the update operation
+ authorizeUpdateFinishOperation(op, scratchPad);
+ try {
+ op.getOperation()
+ .updateAAD(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ KMByteBlob.cast(data[INPUT_DATA]).length());
+ } catch (CryptoException exp) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ // make response
+ short resp = KMArray.instance((short) 1);
+ KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+ sendOutgoing(apdu, resp);
+ }
+
+ private short beginOperationCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 4);
+ // Arguments
+ short params = KMKeyParameters.expAny();
+ KMArray.cast(cmd).add((short) 0, KMEnum.instance(KMType.PURPOSE));
+ KMArray.cast(cmd).add((short) 1, KMByteBlob.exp());
+ KMArray.cast(cmd).add((short) 2, params);
+ short authToken = KMHardwareAuthToken.exp();
+ KMArray.cast(cmd).add((short) 3, authToken);
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processBeginOperationCmd(APDU apdu) {
+ // Receive the incoming request fully from the host into buffer.
+ short cmd = beginOperationCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+ short purpose = KMArray.cast(cmd).get((short) 0);
+ data[KEY_BLOB] = KMArray.cast(cmd).get((short) 1);
+ data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 2);
+ data[HW_TOKEN] = KMArray.cast(cmd).get((short) 3);
+ purpose = KMEnum.cast(purpose).getVal();
+ // Check for app id and app data.
+ data[APP_ID] = getApplicationId(data[KEY_PARAMETERS]);
+ data[APP_DATA] = getApplicationData(data[KEY_PARAMETERS]);
+ // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired
+ // function itself.
+ if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) {
+ KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE);
+ }
+ KMTag.assertPresence(
+ data[SB_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, KMError.UNSUPPORTED_ALGORITHM);
+ short algorithm = KMEnumTag.getValue(KMType.ALGORITHM, data[SB_PARAMETERS]);
+ // If Blob usage tag is present in key characteristics then it should be standalone.
+ if (KMTag.isPresent(data[SB_PARAMETERS], KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ)) {
+ if (KMEnumTag.getValue(KMType.BLOB_USAGE_REQ, data[SB_PARAMETERS]) != KMType.STANDALONE) {
+ KMException.throwIt(KMError.UNSUPPORTED_TAG);
+ }
+ }
+
+ // Generate a random number for operation handle
+ short buf = KMByteBlob.instance(KMOperationState.OPERATION_HANDLE_SIZE);
+ generateUniqueOperationHandle(
+ KMByteBlob.cast(buf).getBuffer(),
+ KMByteBlob.cast(buf).getStartOff(),
+ KMByteBlob.cast(buf).length());
+ /* opHandle is a KMInteger and is encoded as KMInteger when it is returned back. */
+ short opHandle =
+ KMInteger.instance(
+ KMByteBlob.cast(buf).getBuffer(),
+ KMByteBlob.cast(buf).getStartOff(),
+ KMByteBlob.cast(buf).length());
+ KMOperationState op = reserveOperation(algorithm, opHandle);
+ if (op == null) {
+ KMException.throwIt(KMError.TOO_MANY_OPERATIONS);
+ }
+ data[OP_HANDLE] = op.getHandle();
+ op.setPurpose((byte) purpose);
+ op.setKeySize(KMByteBlob.cast(data[SECRET]).length());
+ authorizeAndBeginOperation(op, scratchPad);
+ switch (op.getPurpose()) {
+ case KMType.SIGN:
+ beginTrustedConfirmationOperation(op);
+ case KMType.VERIFY:
+ beginSignVerifyOperation(op);
+ break;
+ case KMType.ENCRYPT:
+ case KMType.DECRYPT:
+ beginCipherOperation(op);
+ break;
+ case KMType.AGREE_KEY:
+ beginKeyAgreementOperation(op);
+ break;
+ default:
+ KMException.throwIt(KMError.UNIMPLEMENTED);
+ break;
+ }
+ short iv = KMType.INVALID_VALUE;
+ // If the data[IV] is required to be returned.
+ // As per VTS, for the decryption operation don't send the iv back.
+ if (data[IV] != KMType.INVALID_VALUE
+ && op.getPurpose() != KMType.DECRYPT
+ && op.getBlockMode() != KMType.ECB) {
+ iv = KMArray.instance((short) 1);
+ if (op.getAlgorithm() == KMType.DES && op.getBlockMode() == KMType.CBC) {
+ // For AES/DES we are generate an random iv of length 16 bytes.
+ // While sending the iv back for DES/CBC mode of opeation only send
+ // 8 bytes back.
+ short ivBlob = KMByteBlob.instance((short) 8);
+ Util.arrayCopy(
+ KMByteBlob.cast(data[IV]).getBuffer(),
+ KMByteBlob.cast(data[IV]).getStartOff(),
+ KMByteBlob.cast(ivBlob).getBuffer(),
+ KMByteBlob.cast(ivBlob).getStartOff(),
+ (short) 8);
+ data[IV] = ivBlob;
+ }
+ KMArray.cast(iv).add((short) 0, KMByteTag.instance(KMType.NONCE, data[IV]));
+ } else {
+ iv = KMArray.instance((short) 0);
+ }
+ short macLen = 0;
+ if (op.getMacLength() != KMType.INVALID_VALUE) {
+ macLen = (short) (op.getMacLength() / 8);
+ }
+ short params = KMKeyParameters.instance(iv);
+ short resp = KMArray.instance((short) 5);
+ KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(resp).add((short) 1, params);
+ KMArray.cast(resp).add((short) 2, data[OP_HANDLE]);
+ KMArray.cast(resp).add((short) 3, KMInteger.uint_8(op.getBufferingMode()));
+ KMArray.cast(resp).add((short) 4, KMInteger.uint_16(macLen));
+ sendOutgoing(apdu, resp);
+ }
+
+ private void authorizeAlgorithm(KMOperationState op) {
+ short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]);
+ if (alg == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ op.setAlgorithm((byte) alg);
+ }
+
+ private void authorizePurpose(KMOperationState op) {
+ switch (op.getAlgorithm()) {
+ case KMType.AES:
+ case KMType.DES:
+ if (op.getPurpose() == KMType.SIGN
+ || op.getPurpose() == KMType.VERIFY
+ || op.getPurpose() == KMType.AGREE_KEY) {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ break;
+ case KMType.EC:
+ if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ break;
+ case KMType.HMAC:
+ if (op.getPurpose() == KMType.ENCRYPT
+ || op.getPurpose() == KMType.DECRYPT
+ || op.getPurpose() == KMType.AGREE_KEY) {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ break;
+ case KMType.RSA:
+ if (op.getPurpose() == KMType.AGREE_KEY) {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ break;
+ default:
+ break;
+ }
+ if (!KMEnumArrayTag.contains(KMType.PURPOSE, op.getPurpose(), data[HW_PARAMETERS])) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+ }
+ }
+
+ private void authorizeDigest(KMOperationState op) {
+ short digests =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[HW_PARAMETERS]);
+ op.setDigest(KMType.DIGEST_NONE);
+ short param =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]);
+ if (param != KMType.INVALID_VALUE) {
+ if (KMEnumArrayTag.cast(param).length() != 1) {
+ KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+ }
+ param = KMEnumArrayTag.cast(param).get((short) 0);
+ if (!KMEnumArrayTag.cast(digests).contains(param)) {
+ KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+ }
+ op.setDigest((byte) param);
+ } else if (KMEnumArrayTag.contains(
+ KMType.PADDING, KMType.RSA_PKCS1_1_5_SIGN, data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+ }
+ short paramPadding =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[KEY_PARAMETERS]);
+ if (paramPadding != KMType.INVALID_VALUE) {
+ if (KMEnumArrayTag.cast(paramPadding).length() != 1) {
+ // TODO vts fails because it expects UNSUPPORTED_PADDING_MODE
+ KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+ }
+ paramPadding = KMEnumArrayTag.cast(paramPadding).get((short) 0);
+ }
+ switch (op.getAlgorithm()) {
+ case KMType.RSA:
+ if ((paramPadding == KMType.RSA_OAEP || paramPadding == KMType.RSA_PSS)
+ && param == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+ }
+ break;
+ case KMType.EC:
+ case KMType.HMAC:
+ if ((param == KMType.INVALID_VALUE && op.getPurpose() != KMType.AGREE_KEY)
+ || !isDigestSupported(op.getAlgorithm(), op.getDigest())) {
+ KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void authorizePadding(KMOperationState op) {
+ short paddings =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[HW_PARAMETERS]);
+ op.setPadding(KMType.PADDING_NONE);
+ short param =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[KEY_PARAMETERS]);
+ if (param != KMType.INVALID_VALUE) {
+ if (KMEnumArrayTag.cast(param).length() != 1) {
+ KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+ }
+ param = KMEnumArrayTag.cast(param).get((short) 0);
+ if (!KMEnumArrayTag.cast(paddings).contains(param)) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE);
+ }
+ }
+ switch (op.getAlgorithm()) {
+ case KMType.RSA:
+ if (param == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+ }
+ if ((op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY)
+ && param != KMType.PADDING_NONE
+ && param != KMType.RSA_PSS
+ && param != KMType.RSA_PKCS1_1_5_SIGN) {
+ KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+ }
+ if ((op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT)
+ && param != KMType.PADDING_NONE
+ && param != KMType.RSA_OAEP
+ && param != KMType.RSA_PKCS1_1_5_ENCRYPT) {
+ KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+ }
+
+ if (param == KMType.PADDING_NONE && op.getDigest() != KMType.DIGEST_NONE) {
+ KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+ }
+ if ((param == KMType.RSA_OAEP || param == KMType.RSA_PSS)
+ && op.getDigest() == KMType.DIGEST_NONE) {
+ KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+ }
+ if (op.getPurpose() == KMType.SIGN
+ || op.getPurpose() == KMType.VERIFY
+ || param == KMType.RSA_OAEP) {
+ // Digest is mandatory in these cases.
+ if (!isDigestSupported(op.getAlgorithm(), op.getDigest())) {
+ KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+ }
+ }
+ if (param == KMType.RSA_OAEP) {
+ short mgfDigest =
+ KMKeyParameters.findTag(
+ KMType.ENUM_ARRAY_TAG, KMType.RSA_OAEP_MGF_DIGEST, data[KEY_PARAMETERS]);
+ if (mgfDigest != KMType.INVALID_VALUE) {
+ if (KMEnumArrayTag.cast(mgfDigest).length() != 1) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ mgfDigest = KMEnumArrayTag.cast(mgfDigest).get((short) 0);
+ if (mgfDigest == KMType.DIGEST_NONE) {
+ KMException.throwIt(KMError.UNSUPPORTED_MGF_DIGEST);
+ }
+
+ } else {
+ mgfDigest = KMType.SHA1;
+ }
+ short mgfDigestHwParams =
+ KMKeyParameters.findTag(
+ KMType.ENUM_ARRAY_TAG, KMType.RSA_OAEP_MGF_DIGEST, data[HW_PARAMETERS]);
+ if ((mgfDigestHwParams != KMType.INVALID_VALUE)
+ && (!KMEnumArrayTag.cast(mgfDigestHwParams).contains(mgfDigest))) {
+ KMException.throwIt(KMError.INCOMPATIBLE_MGF_DIGEST);
+ }
+ if (mgfDigest != KMType.SHA1 && mgfDigest != KMType.SHA2_256) {
+ KMException.throwIt(KMError.UNSUPPORTED_MGF_DIGEST);
+ }
+ op.setMgfDigest((byte) mgfDigest);
+ }
+ op.setPadding((byte) param);
+ break;
+ case KMType.DES:
+ case KMType.AES:
+ if (param == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+ }
+ op.setPadding((byte) param);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void authorizeBlockModeAndMacLength(KMOperationState op) {
+ short param =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]);
+ if (param != KMType.INVALID_VALUE) {
+ if (KMEnumArrayTag.cast(param).length() != 1) {
+ KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE);
+ }
+ param = KMEnumArrayTag.cast(param).get((short) 0);
+ }
+ if (KMType.AES == op.getAlgorithm() || KMType.DES == op.getAlgorithm()) {
+ if (!KMEnumArrayTag.contains(KMType.BLOCK_MODE, param, data[HW_PARAMETERS])) {
+ KMException.throwIt(KMError.INCOMPATIBLE_BLOCK_MODE);
+ }
+ }
+ short macLen =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAC_LENGTH, data[KEY_PARAMETERS]);
+ switch (op.getAlgorithm()) {
+ case KMType.AES:
+ // Validate the block mode.
+ switch (param) {
+ case KMType.ECB:
+ case KMType.CBC:
+ case KMType.CTR:
+ case KMType.GCM:
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE);
+ }
+ if (param == KMType.GCM) {
+ if (op.getPadding() != KMType.PADDING_NONE || op.getPadding() == KMType.PKCS7) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE);
+ }
+ if (macLen == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.MISSING_MAC_LENGTH);
+ }
+ short minMacLen =
+ KMIntegerTag.getShortValue(
+ KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS]);
+ if (minMacLen == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ if (macLen % 8 != 0
+ || macLen > MAX_GCM_TAG_LENGTH_BITS
+ || macLen < MIN_GCM_TAG_LENGTH_BITS) {
+ KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH);
+ }
+ if (macLen < minMacLen) {
+ KMException.throwIt(KMError.INVALID_MAC_LENGTH);
+ }
+ op.setMacLength(macLen);
+ }
+ if (param == KMType.CTR) {
+ if (op.getPadding() != KMType.PADDING_NONE || op.getPadding() == KMType.PKCS7) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE);
+ }
+ }
+ break;
+ case KMType.DES:
+ // Validate the block mode.
+ switch (param) {
+ case KMType.ECB:
+ case KMType.CBC:
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE);
+ }
+ if (param == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ break;
+ case KMType.HMAC:
+ short minMacLen =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS]);
+ if (minMacLen == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ op.setMinMacLength(minMacLen);
+ if (macLen == KMType.INVALID_VALUE) {
+ if (op.getPurpose() == KMType.SIGN) {
+ KMException.throwIt(KMError.MISSING_MAC_LENGTH);
+ }
+ } else {
+ // MAC length may not be specified for verify.
+ if (op.getPurpose() == KMType.VERIFY) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ if (macLen % 8 != 0 || macLen > SHA256_DIGEST_LEN_BITS || macLen < MIN_HMAC_LENGTH_BITS) {
+ KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH);
+ }
+ if (macLen < minMacLen) {
+ KMException.throwIt(KMError.INVALID_MAC_LENGTH);
+ }
+ op.setMacLength(macLen);
+ }
+ break;
+ default:
+ break;
+ }
+ op.setBlockMode((byte) param);
+ }
+
+ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) {
+ authorizePurpose(op);
+ authorizeDigest(op);
+ authorizePadding(op);
+ authorizeBlockModeAndMacLength(op);
+ if (!validateHwToken(data[HW_TOKEN], scratchPad)) {
+ data[HW_TOKEN] = KMType.INVALID_VALUE;
+ }
+ authorizeUserSecureIdAuthTimeout(op, scratchPad);
+ authorizeDeviceUnlock(scratchPad);
+ authorizeKeyUsageForCount(scratchPad);
+
+ KMTag.assertAbsence(
+ data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, KMError.INVALID_KEY_BLOB);
+
+ // Validate early boot
+ // VTS expects error code EARLY_BOOT_ONLY during begin operation if early boot ended tag is
+ // present
+ if (kmDataStore.getEarlyBootEndedStatus()) {
+ KMTag.assertAbsence(
+ data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.EARLY_BOOT_ENDED);
+ }
+
+ // Authorize Caller Nonce - if caller nonce absent in key char and nonce present in
+ // key params then fail if it is not a Decrypt operation
+ data[IV] = KMType.INVALID_VALUE;
+
+ if (!KMTag.isPresent(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.CALLER_NONCE)
+ && KMTag.isPresent(data[KEY_PARAMETERS], KMType.BYTES_TAG, KMType.NONCE)
+ && op.getPurpose() != KMType.DECRYPT) {
+ KMException.throwIt(KMError.CALLER_NONCE_PROHIBITED);
+ }
+
+ short nonce = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.NONCE, data[KEY_PARAMETERS]);
+ // If Nonce is present then check whether the size of nonce is correct.
+ if (nonce != KMType.INVALID_VALUE) {
+ data[IV] = KMByteTag.cast(nonce).getValue();
+ // For CBC mode - iv must be 8 bytes
+ if (op.getBlockMode() == KMType.CBC
+ && op.getAlgorithm() == KMType.DES
+ && KMByteBlob.cast(data[IV]).length() != 8) {
+ KMException.throwIt(KMError.INVALID_NONCE);
+ }
+
+ // For GCM mode - IV must be 12 bytes
+ if (KMByteBlob.cast(data[IV]).length() != 12 && op.getBlockMode() == KMType.GCM) {
+ KMException.throwIt(KMError.INVALID_NONCE);
+ }
+
+ // For AES CBC and CTR modes IV must be 16 bytes
+ if ((op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.CTR)
+ && op.getAlgorithm() == KMType.AES
+ && KMByteBlob.cast(data[IV]).length() != 16) {
+ KMException.throwIt(KMError.INVALID_NONCE);
+ }
+ } else if (op.getAlgorithm() == KMType.AES || op.getAlgorithm() == KMType.DES) {
+
+ // For symmetric decryption iv is required
+ if (op.getPurpose() == KMType.DECRYPT
+ && (op.getBlockMode() == KMType.CBC
+ || op.getBlockMode() == KMType.GCM
+ || op.getBlockMode() == KMType.CTR)) {
+ KMException.throwIt(KMError.MISSING_NONCE);
+ } else if (op.getBlockMode() == KMType.ECB) {
+ // For ECB we create zero length nonce
+ data[IV] = KMByteBlob.instance((short) 0);
+ } else if (op.getPurpose() == KMType.ENCRYPT) {
+
+ // For encrypt mode if nonce is absent then create random nonce of correct length
+ byte ivLen = 16;
+ if (op.getBlockMode() == KMType.GCM) {
+ ivLen = 12;
+ } else if (op.getAlgorithm() == KMType.DES) {
+ ivLen = 8;
+ }
+ data[IV] = KMByteBlob.instance(ivLen);
+ seProvider.newRandomNumber(
+ KMByteBlob.cast(data[IV]).getBuffer(),
+ KMByteBlob.cast(data[IV]).getStartOff(),
+ KMByteBlob.cast(data[IV]).length());
+ }
+ }
+ }
+
+ private void beginKeyAgreementOperation(KMOperationState op) {
+ if (op.getAlgorithm() != KMType.EC) {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+
+ op.setOperation(
+ seProvider.initAsymmetricOperation(
+ (byte) op.getPurpose(),
+ (byte) op.getAlgorithm(),
+ (byte) op.getPadding(),
+ (byte) op.getDigest(),
+ KMType.DIGEST_NONE, /* No MGF1 Digest */
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ null,
+ (short) 0,
+ (short) 0));
+ }
+
+ private void beginCipherOperation(KMOperationState op) {
+ switch (op.getAlgorithm()) {
+ case KMType.RSA:
+ try {
+ if (op.getPurpose() == KMType.DECRYPT) {
+ op.setOperation(
+ seProvider.initAsymmetricOperation(
+ (byte) op.getPurpose(),
+ (byte) op.getAlgorithm(),
+ (byte) op.getPadding(),
+ (byte) op.getDigest(),
+ (byte) op.getMgfDigest(),
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ KMByteBlob.cast(data[PUB_KEY]).getBuffer(),
+ KMByteBlob.cast(data[PUB_KEY]).getStartOff(),
+ KMByteBlob.cast(data[PUB_KEY]).length()));
+ } else {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ } catch (CryptoException exp) {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ break;
+ case KMType.AES:
+ case KMType.DES:
+ if (op.getBlockMode() == KMType.GCM) {
+ op.setAesGcmUpdateStart();
+ }
+ try {
+ op.setOperation(
+ seProvider.initSymmetricOperation(
+ (byte) op.getPurpose(),
+ (byte) op.getAlgorithm(),
+ (byte) op.getDigest(),
+ (byte) op.getPadding(),
+ (byte) op.getBlockMode(),
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ KMByteBlob.cast(data[IV]).getBuffer(),
+ KMByteBlob.cast(data[IV]).getStartOff(),
+ KMByteBlob.cast(data[IV]).length(),
+ op.getMacLength()));
+ } catch (CryptoException exception) {
+ if (exception.getReason() == CryptoException.ILLEGAL_VALUE) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ } else if (exception.getReason() == CryptoException.NO_SUCH_ALGORITHM) {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ }
+ }
+ }
+
+ private void beginTrustedConfirmationOperation(KMOperationState op) {
+ // Check for trusted confirmation - if required then set the signer in op state.
+ if (KMKeyParameters.findTag(
+ KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, data[HW_PARAMETERS])
+ != KMType.INVALID_VALUE) {
+
+ op.setTrustedConfirmationSigner(
+ seProvider.initTrustedConfirmationSymmetricOperation(kmDataStore.getComputedHmacKey()));
+
+ op.getTrustedConfirmationSigner()
+ .update(confirmationToken, (short) 0, (short) confirmationToken.length);
+ }
+ }
+
+ private void beginSignVerifyOperation(KMOperationState op) {
+ switch (op.getAlgorithm()) {
+ case KMType.RSA:
+ try {
+ if (op.getPurpose() == KMType.SIGN) {
+ op.setOperation(
+ seProvider.initAsymmetricOperation(
+ (byte) op.getPurpose(),
+ (byte) op.getAlgorithm(),
+ (byte) op.getPadding(),
+ (byte) op.getDigest(),
+ KMType.DIGEST_NONE, /* No MGF Digest */
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ KMByteBlob.cast(data[PUB_KEY]).getBuffer(),
+ KMByteBlob.cast(data[PUB_KEY]).getStartOff(),
+ KMByteBlob.cast(data[PUB_KEY]).length()));
+ } else {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ } catch (CryptoException exp) {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ break;
+ case KMType.EC:
+ try {
+ if (op.getPurpose() == KMType.SIGN) {
+ op.setOperation(
+ seProvider.initAsymmetricOperation(
+ (byte) op.getPurpose(),
+ (byte) op.getAlgorithm(),
+ (byte) op.getPadding(),
+ (byte) op.getDigest(),
+ KMType.DIGEST_NONE, /* No MGF Digest */
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ null,
+ (short) 0,
+ (short) 0));
+ } else {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ } catch (CryptoException exp) {
+ // Javacard does not support NO digest based signing.
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ break;
+ case KMType.HMAC:
+ // As per Keymaster HAL documentation, the length of the Hmac output can
+ // be decided by using TAG_MAC_LENGTH in Keyparameters. But there is no
+ // such provision to control the length of the Hmac output using JavaCard
+ // crypto APIs and the current implementation always returns 32 bytes
+ // length of Hmac output. So to provide support to TAG_MAC_LENGTH
+ // feature, we truncate the output signature to TAG_MAC_LENGTH and return
+ // the truncated signature back to the caller. At the time of verfication
+ // we again compute the signature of the plain text input, truncate it to
+ // TAG_MAC_LENGTH and compare it with the input signature for
+ // verification. So this is the reason we are using KMType.SIGN directly
+ // instead of using op.getPurpose().
+ try {
+ op.setOperation(
+ seProvider.initSymmetricOperation(
+ (byte) KMType.SIGN,
+ (byte) op.getAlgorithm(),
+ (byte) op.getDigest(),
+ (byte) op.getPadding(),
+ (byte) op.getBlockMode(),
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ null,
+ (short) 0,
+ (short) 0,
+ (short) 0));
+ } catch (CryptoException exp) {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ break;
+ }
+ }
+
+ private boolean isHwAuthTokenContainsMatchingSecureId(short hwAuthToken, short secureUserIdsObj) {
+ short secureUserId = KMHardwareAuthToken.cast(hwAuthToken).getUserId();
+ if (!KMInteger.cast(secureUserId).isZero()) {
+ if (KMIntegerArrayTag.cast(secureUserIdsObj).contains(secureUserId)) {
+ return true;
+ }
+ }
+
+ short authenticatorId = KMHardwareAuthToken.cast(hwAuthToken).getAuthenticatorId();
+ if (!KMInteger.cast(authenticatorId).isZero()) {
+ if (KMIntegerArrayTag.cast(secureUserIdsObj).contains(authenticatorId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean authTokenMatches(short userSecureIdsPtr, short authType, byte[] scratchPad) {
+ if (data[HW_TOKEN] == KMType.INVALID_VALUE) {
+ return false;
+ }
+ if (!isHwAuthTokenContainsMatchingSecureId(data[HW_TOKEN], userSecureIdsPtr)) {
+ return false;
+ }
+ // check auth type
+ tmpVariables[2] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType();
+ tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal();
+ if (((byte) tmpVariables[2] & (byte) authType) == 0) {
+ return false;
+ }
+ return true;
+ }
+
+ private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratchPad) {
+ short authTime;
+ short authType;
+ // Authorize User Secure Id and Auth timeout
+ short userSecureIdPtr =
+ KMKeyParameters.findTag(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, data[HW_PARAMETERS]);
+ if (userSecureIdPtr != KMType.INVALID_VALUE) {
+ // Authentication required.
+ if (KMType.INVALID_VALUE
+ != KMKeyParameters.findTag(
+ KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, data[HW_PARAMETERS])) {
+ // Key has both USER_SECURE_ID and NO_AUTH_REQUIRED
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ // authenticator type must be provided.
+ if (KMType.INVALID_VALUE
+ == (authType = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]))) {
+ // Authentication required, but no auth type found.
+ KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+ }
+
+ short authTimeoutTagPtr =
+ KMKeyParameters.findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT, data[HW_PARAMETERS]);
+ if (authTimeoutTagPtr != KMType.INVALID_VALUE) {
+ // authenticate user
+ if (!authTokenMatches(userSecureIdPtr, authType, scratchPad)) {
+ KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+ }
+
+ authTimeoutTagPtr =
+ KMKeyParameters.findTag(
+ KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, data[CUSTOM_TAGS]);
+ if (authTimeoutTagPtr == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ authTime = KMIntegerTag.cast(authTimeoutTagPtr).getValue();
+ // set the one time auth
+ op.setOneTimeAuthReqd(true);
+ // set the authentication time stamp in operation state
+ authTime =
+ addIntegers(
+ authTime, KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp(), scratchPad);
+ op.setAuthTime(
+ KMInteger.cast(authTime).getBuffer(), KMInteger.cast(authTime).getStartOff());
+ // auth time validation will happen in update or finish
+ op.setAuthTimeoutValidated(false);
+ } else {
+ // auth per operation required
+ // store user secure id and authType in OperationState.
+ op.setUserSecureId(userSecureIdPtr);
+ op.setAuthType((byte) authType);
+ // set flags
+ op.setOneTimeAuthReqd(false);
+ op.setAuthPerOperationReqd(true);
+ }
+ }
+ }
+
+ private boolean verifyHwTokenMacInBigEndian(short hwToken, byte[] scratchPad) {
+ // The challenge, userId and authenticatorId, authenticatorType and timestamp
+ // are in network order (big-endian).
+ short len = 0;
+ // add 0
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+ len = 1;
+ // concatenate challenge - 8 bytes
+ short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge();
+ KMInteger.cast(ptr)
+ .value(
+ scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+ len += KMInteger.UINT_64;
+ // concatenate user id - 8 bytes
+ ptr = KMHardwareAuthToken.cast(hwToken).getUserId();
+ KMInteger.cast(ptr)
+ .value(
+ scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+ len += KMInteger.UINT_64;
+ // concatenate authenticator id - 8 bytes
+ ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId();
+ KMInteger.cast(ptr)
+ .value(
+ scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+ len += KMInteger.UINT_64;
+ // concatenate authenticator type - 4 bytes
+ ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType();
+ scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal();
+ len += KMInteger.UINT_32;
+ // concatenate timestamp -8 bytes
+ ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp();
+ KMInteger.cast(ptr)
+ .value(
+ scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+ len += KMInteger.UINT_64;
+
+ ptr = KMHardwareAuthToken.cast(hwToken).getMac();
+
+ return seProvider.hmacVerify(
+ kmDataStore.getComputedHmacKey(),
+ scratchPad,
+ (short) 0,
+ len,
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length());
+ }
+
+ private boolean verifyHwTokenMacInLittleEndian(short hwToken, byte[] scratchPad) {
+ // The challenge, userId and authenticatorId values are in little endian order,
+ // but authenticatorType and timestamp are in network order (big-endian).
+ short len = 0;
+ // add 0
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+ len = 1;
+ // concatenate challenge - 8 bytes
+ short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge();
+ KMInteger.cast(ptr).toLittleEndian(scratchPad, len);
+ len += KMInteger.UINT_64;
+ // concatenate user id - 8 bytes
+ ptr = KMHardwareAuthToken.cast(hwToken).getUserId();
+ KMInteger.cast(ptr).toLittleEndian(scratchPad, len);
+ len += KMInteger.UINT_64;
+ // concatenate authenticator id - 8 bytes
+ ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId();
+ KMInteger.cast(ptr).toLittleEndian(scratchPad, len);
+ len += KMInteger.UINT_64;
+ // concatenate authenticator type - 4 bytes
+ ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType();
+ scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal();
+ len += KMInteger.UINT_32;
+ // concatenate timestamp - 8 bytes
+ ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp();
+ KMInteger.cast(ptr)
+ .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length())));
+ len += KMInteger.UINT_64;
+
+ ptr = KMHardwareAuthToken.cast(hwToken).getMac();
+
+ return seProvider.hmacVerify(
+ kmDataStore.getComputedHmacKey(),
+ scratchPad,
+ (short) 0,
+ len,
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length());
+ }
+
+ private boolean validateHwToken(short hwToken, byte[] scratchPad) {
+ // CBOR Encoding is always big endian
+ short ptr = KMHardwareAuthToken.cast(hwToken).getMac();
+ // If mac length is zero then token is empty.
+ if (KMByteBlob.cast(ptr).length() == 0) {
+ return false;
+ }
+ if (KMConfigurations.TEE_MACHINE_TYPE == KMConfigurations.LITTLE_ENDIAN) {
+ return verifyHwTokenMacInLittleEndian(hwToken, scratchPad);
+ } else {
+ return verifyHwTokenMacInBigEndian(hwToken, scratchPad);
+ }
+ }
+
+ private short importKeyCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 6);
+ // Arguments
+ short params = KMKeyParameters.expAny();
+ KMArray.cast(cmd).add((short) 0, params);
+ KMArray.cast(cmd).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT));
+ KMArray.cast(cmd).add((short) 2, KMByteBlob.exp());
+ KMArray.cast(cmd).add((short) 3, KMByteBlob.exp()); // attest key
+ KMArray.cast(cmd).add((short) 4, params); // attest key params
+ KMArray.cast(cmd).add((short) 5, KMByteBlob.exp()); // issuer
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processImportKeyCmd(APDU apdu) {
+ // Receive the incoming request fully from the host into buffer.
+ short cmd = importKeyCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+ data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 0);
+ short keyFmt = KMArray.cast(cmd).get((short) 1);
+ data[IMPORTED_KEY_BLOB] = KMArray.cast(cmd).get((short) 2);
+ data[ATTEST_KEY_BLOB] = KMArray.cast(cmd).get((short) 3);
+ data[ATTEST_KEY_PARAMS] = KMArray.cast(cmd).get((short) 4);
+ data[ATTEST_KEY_ISSUER] = KMArray.cast(cmd).get((short) 5);
+ keyFmt = KMEnum.cast(keyFmt).getVal();
+
+ data[CERTIFICATE] = KMArray.instance((short) 0); // by default the cert is empty.
+ data[ORIGIN] = KMType.IMPORTED;
+ importKey(apdu, keyFmt, scratchPad);
+ }
+
+ private void validateImportKey(short params, short keyFmt) {
+ short attKeyPurpose = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, params);
+ // ATTEST_KEY cannot be combined with any other purpose.
+ if (attKeyPurpose != KMType.INVALID_VALUE
+ && KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY)
+ && KMEnumArrayTag.cast(attKeyPurpose).length() > 1) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+ }
+ // Rollback protection not supported
+ KMTag.assertAbsence(
+ params,
+ KMType.BOOL_TAG,
+ KMType.ROLLBACK_RESISTANCE,
+ KMError.ROLLBACK_RESISTANCE_UNAVAILABLE);
+ // As per specification, Early boot keys may not be imported at all, if Tag::EARLY_BOOT_ONLY is
+ // provided to IKeyMintDevice::importKey
+ KMTag.assertAbsence(params, KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.EARLY_BOOT_ENDED);
+ // Check if the tags are supported.
+ if (KMKeyParameters.hasUnsupportedTags(params)) {
+ KMException.throwIt(KMError.UNSUPPORTED_TAG);
+ }
+ // Algorithm must be present
+ KMTag.assertPresence(params, KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT);
+ short alg = KMEnumTag.getValue(KMType.ALGORITHM, params);
+ // key format must be raw if aes, des or hmac and pkcs8 for rsa and ec.
+ if ((alg == KMType.AES || alg == KMType.DES || alg == KMType.HMAC) && keyFmt != KMType.RAW) {
+ KMException.throwIt(KMError.UNIMPLEMENTED);
+ }
+ if ((alg == KMType.RSA || alg == KMType.EC) && keyFmt != KMType.PKCS8) {
+ KMException.throwIt(KMError.UNIMPLEMENTED);
+ }
+ }
+
+ private void importKey(APDU apdu, short keyFmt, byte[] scratchPad) {
+ validateImportKey(data[KEY_PARAMETERS], keyFmt);
+ // Check algorithm and dispatch to appropriate handler.
+ short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]);
+ switch (alg) {
+ case KMType.RSA:
+ importRSAKey(scratchPad);
+ break;
+ case KMType.AES:
+ importAESKey(scratchPad);
+ break;
+ case KMType.DES:
+ importTDESKey(scratchPad);
+ break;
+ case KMType.HMAC:
+ importHmacKey(scratchPad);
+ break;
+ case KMType.EC:
+ importECKeys(scratchPad);
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ break;
+ }
+ makeKeyCharacteristics(scratchPad);
+ KMAttestationCert cert =
+ generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS], scratchPad);
+ createEncryptedKeyBlob(scratchPad);
+ sendOutgoing(apdu, cert, data[CERTIFICATE], data[KEY_BLOB], data[KEY_CHARACTERISTICS]);
+ }
+
+ private void importECKeys(byte[] scratchPad) {
+ // Decode key material
+ KMAsn1Parser pkcs8 = KMAsn1Parser.instance();
+ short keyBlob = pkcs8.decodeEc(data[IMPORTED_KEY_BLOB]);
+ data[PUB_KEY] = KMArray.cast(keyBlob).get((short) 0);
+ data[SECRET] = KMArray.cast(keyBlob).get((short) 1);
+ // initialize 256 bit p256 key for given private key and public key.
+ short index = 0;
+ // check whether the key size tag is present in key parameters.
+ short SecretLen = (short) (KMByteBlob.length(data[SECRET]) * 8);
+ short keySize =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+ if (keySize != KMType.INVALID_VALUE) {
+ // As per NIST.SP.800-186 page 9, secret for 256 curve should be between
+ // 256-383
+ if (((256 <= SecretLen) && (383 >= SecretLen)) ^ keySize == 256) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ if (keySize != 256) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ } else {
+ if ((256 > SecretLen) || (383 < SecretLen)) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ // add the key size to scratchPad
+ keySize = KMInteger.uint_16((short) 256);
+ keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keySize);
+ Util.setShort(scratchPad, index, keySize);
+ index += 2;
+ }
+ // check the curve if present in key parameters.
+ short curve = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]);
+ if (curve != KMType.INVALID_VALUE) {
+ // As per NIST.SP.800-186 page 9, secret length for 256 curve should be between
+ // 256-383
+ if (((256 <= SecretLen) && (383 >= SecretLen)) ^ curve == KMType.P_256) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ if (curve != KMType.P_256) {
+ KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE);
+ }
+ } else {
+ if ((256 > SecretLen) || (383 < SecretLen)) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ // add the curve to scratchPad
+ curve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256);
+ Util.setShort(scratchPad, index, curve);
+ index += 2;
+ }
+
+ // Check whether key can be created
+ seProvider.importAsymmetricKey(
+ KMType.EC,
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ KMByteBlob.cast(data[PUB_KEY]).getBuffer(),
+ KMByteBlob.cast(data[PUB_KEY]).getStartOff(),
+ KMByteBlob.cast(data[PUB_KEY]).length());
+
+ // add scratch pad to key parameters
+ updateKeyParameters(scratchPad, index);
+ data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]);
+ }
+
+ private void importHmacKey(byte[] scratchPad) {
+ // Get Key
+ data[SECRET] = data[IMPORTED_KEY_BLOB];
+ // create HMAC key of up to 512 bit
+ short index = 0; // index in scratchPad for update params
+ // check the keysize tag if present in key parameters.
+ short keysize =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+ if (keysize != KMType.INVALID_VALUE) {
+ if (!(keysize >= 64 && keysize <= 512 && keysize % 8 == 0)) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ if (keysize != (short) (KMByteBlob.length(data[SECRET]) * 8)) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ } else {
+ // add the key size to scratchPad
+ keysize = (short) (KMByteBlob.length(data[SECRET]) * 8);
+ if (!(keysize >= 64 && keysize <= 512 && keysize % 8 == 0)) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ keysize = KMInteger.uint_16(keysize);
+ short keySizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize);
+ Util.setShort(scratchPad, index, keySizeTag);
+ index += 2;
+ }
+ // Check whether key can be created
+ seProvider.importSymmetricKey(
+ KMType.HMAC,
+ keysize,
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length());
+
+ // update the key parameters list
+ updateKeyParameters(scratchPad, index);
+ // validate HMAC Key parameters
+ validateHmacKey();
+ data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+ }
+
+ private void importTDESKey(byte[] scratchPad) {
+ // Decode Key Material
+ data[SECRET] = data[IMPORTED_KEY_BLOB];
+ short index = 0; // index in scratchPad for update params
+ // check the keysize tag if present in key parameters.
+ short keysize =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+ if (keysize != KMType.INVALID_VALUE) {
+ if (keysize != 168) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ if (192 != (short) (8 * KMByteBlob.length(data[SECRET]))) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ } else {
+ keysize = (short) (KMByteBlob.length(data[SECRET]) * 8);
+ if (keysize != 192) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ // add the key size to scratchPad
+ keysize = KMInteger.uint_16((short) 168);
+ short keysizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize);
+ Util.setShort(scratchPad, index, keysizeTag);
+ index += 2;
+ }
+ // Read Minimum Mac length - it must not be present
+ KMTag.assertAbsence(
+ data[KEY_PARAMETERS], KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMError.INVALID_TAG);
+ // Check whether key can be created
+ seProvider.importSymmetricKey(
+ KMType.DES,
+ keysize,
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length());
+ // update the key parameters list
+ updateKeyParameters(scratchPad, index);
+ data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+ }
+
+ private void validateAesKeySize(short keySizeBits) {
+ if (keySizeBits != 128 && keySizeBits != 256) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ }
+
+ private void importAESKey(byte[] scratchPad) {
+ // Get Key
+ data[SECRET] = data[IMPORTED_KEY_BLOB];
+ // create 128 or 256 bit AES key
+ short index = 0; // index in scratchPad for update params
+ // check the keysize tag if present in key parameters.
+ short keysize =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+ if (keysize != KMType.INVALID_VALUE) {
+ if (keysize != (short) (8 * KMByteBlob.length(data[SECRET]))) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ validateAesKeySize(keysize);
+ } else {
+ // add the key size to scratchPad
+ keysize = (short) (8 * KMByteBlob.cast(data[SECRET]).length());
+ validateAesKeySize(keysize);
+ keysize = KMInteger.uint_16(keysize);
+ short keysizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize);
+ Util.setShort(scratchPad, index, keysizeTag);
+ index += 2;
+ }
+ // Check whether key can be created
+ seProvider.importSymmetricKey(
+ KMType.AES,
+ keysize,
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length());
+
+ // update the key parameters list
+ updateKeyParameters(scratchPad, index);
+ // validate AES Key parameters
+ validateAESKey();
+ data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+ }
+
+ private void importRSAKey(byte[] scratchPad) {
+ // Decode key material
+ KMAsn1Parser pkcs8 = KMAsn1Parser.instance();
+ short keyblob = pkcs8.decodeRsa(data[IMPORTED_KEY_BLOB]);
+ data[PUB_KEY] = KMArray.cast(keyblob).get((short) 0);
+ short pubKeyExp = KMArray.cast(keyblob).get((short) 1);
+ data[SECRET] = KMArray.cast(keyblob).get((short) 2);
+ if (F4.length != KMByteBlob.cast(pubKeyExp).length()) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ if (Util.arrayCompare(
+ F4,
+ (short) 0,
+ KMByteBlob.cast(pubKeyExp).getBuffer(),
+ KMByteBlob.cast(pubKeyExp).getStartOff(),
+ (short) F4.length)
+ != 0) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ short index = 0; // index in scratchPad for update parameters.
+ // validate public exponent if present in key params - it must be 0x010001
+ short len =
+ KMIntegerTag.getValue(
+ scratchPad,
+ (short) 10, // using offset 10 as first 10 bytes reserved for update params
+ KMType.ULONG_TAG,
+ KMType.RSA_PUBLIC_EXPONENT,
+ data[KEY_PARAMETERS]);
+ if (len != KMTag.INVALID_VALUE) {
+ if (len != 4
+ || Util.getShort(scratchPad, (short) 10) != 0x01
+ || Util.getShort(scratchPad, (short) 12) != 0x01) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ } else {
+ // add public exponent to scratchPad
+ Util.setShort(scratchPad, (short) 10, (short) 0x01);
+ Util.setShort(scratchPad, (short) 12, (short) 0x01);
+ pubKeyExp = KMInteger.uint_32(scratchPad, (short) 10);
+ pubKeyExp = KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, pubKeyExp);
+ Util.setShort(scratchPad, index, pubKeyExp);
+ index += 2;
+ }
+
+ // check the keysize tag if present in key parameters.
+ short keysize =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+ short kSize = (short) (KMByteBlob.length(data[PUB_KEY]) * 8);
+ if (keysize != KMType.INVALID_VALUE) {
+ if (keysize != 2048 || (keysize != kSize)) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ } else {
+ if (2048 != kSize) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ // add the key size to scratchPad
+ keysize = KMInteger.uint_16((short) 2048);
+ keysize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize);
+ Util.setShort(scratchPad, index, keysize);
+ index += 2;
+ }
+
+ // Check whether key can be created
+ seProvider.importAsymmetricKey(
+ KMType.RSA,
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ KMByteBlob.cast(data[PUB_KEY]).getBuffer(),
+ KMByteBlob.cast(data[PUB_KEY]).getStartOff(),
+ KMByteBlob.cast(data[PUB_KEY]).length());
+
+ // update the key parameters list
+ updateKeyParameters(scratchPad, index);
+ // validate RSA Key parameters
+ validateRSAKey(scratchPad);
+ data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]);
+ }
+
+ private void updateKeyParameters(byte[] newParams, short len) {
+ if (len == 0) {
+ return; // nothing to update
+ }
+ // Create Update Param array and copy current params
+ short params = KMKeyParameters.cast(data[KEY_PARAMETERS]).getVals();
+ len = (short) (KMArray.cast(params).length() + (short) (len / 2));
+ short updatedParams = KMArray.instance(len); // update params
+
+ len = KMArray.cast(params).length();
+ short index = 0;
+
+ // copy the existing key parameters to updated array
+ while (index < len) {
+ short tag = KMArray.cast(params).get(index);
+ KMArray.cast(updatedParams).add(index, tag);
+ index++;
+ }
+
+ // copy new parameters to updated array
+ len = KMArray.cast(updatedParams).length();
+ short newParamIndex = 0; // index in ptrArr
+ while (index < len) {
+ short tag = Util.getShort(newParams, newParamIndex);
+ KMArray.cast(updatedParams).add(index, tag);
+ index++;
+ newParamIndex += 2;
+ }
+ // replace with updated key parameters.
+ data[KEY_PARAMETERS] = KMKeyParameters.instance(updatedParams);
+ }
+
+ private short initStrongBoxCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 3);
+ KMArray.cast(cmd).add((short) 0, KMInteger.exp()); // OS version
+ KMArray.cast(cmd).add((short) 1, KMInteger.exp()); // OS patch level
+ KMArray.cast(cmd).add((short) 2, KMInteger.exp()); // Vendor patch level
+ return receiveIncoming(apdu, cmd);
+ }
+
+ // This command is executed to set the boot parameters.
+ // releaseAllOperations has to be called on every boot, so
+ // it is called from inside initStrongBoxCmd. Later in future if
+ // initStrongBoxCmd is removed, then make sure that releaseAllOperations
+ // is moved to a place where it is called on every boot.
+ private void processInitStrongBoxCmd(APDU apdu) {
+ short cmd = initStrongBoxCmd(apdu);
+
+ short osVersion = KMArray.cast(cmd).get((short) 0);
+ short osPatchLevel = KMArray.cast(cmd).get((short) 1);
+ short vendorPatchLevel = KMArray.cast(cmd).get((short) 2);
+ setOsVersion(osVersion);
+ setOsPatchLevel(osPatchLevel);
+ setVendorPatchLevel(vendorPatchLevel);
+ kmDataStore.setDeviceBootStatus(KMKeymintDataStore.SET_SYSTEM_PROPERTIES_SUCCESS);
+ }
+
+ public void reboot() {
+ // flag to maintain early boot ended state
+ kmDataStore.setEarlyBootEndedStatus(false);
+ // Clear all the operation state.
+ releaseAllOperations();
+ // Hmac is cleared, so generate a new Hmac nonce.
+ initHmacNonceAndSeed();
+ // Clear all auth tags.
+ kmDataStore.removeAllAuthTags();
+ }
+
+ protected void initSystemBootParams(
+ short osVersion, short osPatchLevel, short vendorPatchLevel, short bootPatchLevel) {
+ osVersion = KMInteger.uint_16(osVersion);
+ osPatchLevel = KMInteger.uint_16(osPatchLevel);
+ vendorPatchLevel = KMInteger.uint_16((short) vendorPatchLevel);
+ setOsVersion(osVersion);
+ setOsPatchLevel(osPatchLevel);
+ setVendorPatchLevel(vendorPatchLevel);
+ }
+
+ protected void setOsVersion(short version) {
+ kmDataStore.setOsVersion(
+ KMInteger.cast(version).getBuffer(),
+ KMInteger.cast(version).getStartOff(),
+ KMInteger.cast(version).length());
+ }
+
+ protected void setOsPatchLevel(short patch) {
+ kmDataStore.setOsPatch(
+ KMInteger.cast(patch).getBuffer(),
+ KMInteger.cast(patch).getStartOff(),
+ KMInteger.cast(patch).length());
+ }
+
+ protected void setVendorPatchLevel(short patch) {
+ kmDataStore.setVendorPatchLevel(
+ KMInteger.cast(patch).getBuffer(),
+ KMInteger.cast(patch).getStartOff(),
+ KMInteger.cast(patch).length());
+ }
+
+ private short generateKeyCmd(APDU apdu) {
+ short params = KMKeyParameters.expAny();
+ short blob = KMByteBlob.exp();
+ // Array of expected arguments
+ short cmd = KMArray.instance((short) 4);
+ KMArray.cast(cmd).add((short) 0, params); // key params
+ KMArray.cast(cmd).add((short) 1, blob); // attest key
+ KMArray.cast(cmd).add((short) 2, params); // attest key params
+ KMArray.cast(cmd).add((short) 3, blob); // issuer
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processGenerateKey(APDU apdu) {
+ // Receive the incoming request fully from the host into buffer.
+ short cmd = generateKeyCmd(apdu);
+ // Re-purpose the apdu buffer as scratch pad.
+ byte[] scratchPad = apdu.getBuffer();
+ data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 0);
+ data[ATTEST_KEY_BLOB] = KMArray.cast(cmd).get((short) 1);
+ data[ATTEST_KEY_PARAMS] = KMArray.cast(cmd).get((short) 2);
+ data[ATTEST_KEY_ISSUER] = KMArray.cast(cmd).get((short) 3);
+ data[CERTIFICATE] = KMType.INVALID_VALUE; // by default the cert is empty.
+ // ROLLBACK_RESISTANCE not supported.
+ KMTag.assertAbsence(
+ data[KEY_PARAMETERS],
+ KMType.BOOL_TAG,
+ KMType.ROLLBACK_RESISTANCE,
+ KMError.ROLLBACK_RESISTANCE_UNAVAILABLE);
+
+ // Algorithm must be present
+ KMTag.assertPresence(
+ data[KEY_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT);
+
+ // Check if the tags are supported.
+ if (KMKeyParameters.hasUnsupportedTags(data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.UNSUPPORTED_TAG);
+ }
+ short attKeyPurpose =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, data[KEY_PARAMETERS]);
+ // ATTEST_KEY cannot be combined with any other purpose.
+ if (attKeyPurpose != KMType.INVALID_VALUE
+ && KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY)
+ && KMEnumArrayTag.cast(attKeyPurpose).length() > 1) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+ }
+ short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]);
+ // Check algorithm and dispatch to appropriate handler.
+ switch (alg) {
+ case KMType.RSA:
+ generateRSAKey(scratchPad);
+ break;
+ case KMType.AES:
+ generateAESKey(scratchPad);
+ break;
+ case KMType.DES:
+ generateTDESKey(scratchPad);
+ break;
+ case KMType.HMAC:
+ generateHmacKey(scratchPad);
+ break;
+ case KMType.EC:
+ generateECKeys(scratchPad);
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ break;
+ }
+ // create key blob and associated attestation.
+ data[ORIGIN] = KMType.GENERATED;
+ makeKeyCharacteristics(scratchPad);
+ // construct the certificate and place the encoded data in data[CERTIFICATE]
+ KMAttestationCert cert =
+ generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS], scratchPad);
+ createEncryptedKeyBlob(scratchPad);
+ sendOutgoing(apdu, cert, data[CERTIFICATE], data[KEY_BLOB], data[KEY_CHARACTERISTICS]);
+ }
+
+ private short getApplicationId(short params) {
+ short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, params);
+ if (appId != KMTag.INVALID_VALUE) {
+ appId = KMByteTag.cast(appId).getValue();
+ if (KMByteBlob.cast(appId).length() == 0) {
+ // Treat empty as INVALID.
+ return KMType.INVALID_VALUE;
+ }
+ }
+ return appId;
+ }
+
+ private short getApplicationData(short params) {
+ short appData = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, params);
+ if (appData != KMTag.INVALID_VALUE) {
+ appData = KMByteTag.cast(appData).getValue();
+ if (KMByteBlob.cast(appData).length() == 0) {
+ // Treat empty as INVALID.
+ return KMType.INVALID_VALUE;
+ }
+ }
+ return appData;
+ }
+
+ private short getAttestationMode(short attKeyBlob, short attChallenge) {
+ short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[KEY_PARAMETERS]);
+ short mode = KMType.NO_CERT;
+ if (KMEnumTag.cast(alg).getValue() != KMType.RSA
+ && KMEnumTag.cast(alg).getValue() != KMType.EC) {
+ return mode;
+ }
+ // If attestation keyblob preset
+ if (attKeyBlob != KMType.INVALID_VALUE && KMByteBlob.cast(attKeyBlob).length() > 0) {
+ // No attestation challenge present then it is an error
+ if (attChallenge == KMType.INVALID_VALUE || KMByteBlob.cast(attChallenge).length() <= 0) {
+ KMException.throwIt(KMError.ATTESTATION_CHALLENGE_MISSING);
+ } else {
+ mode = KMType.ATTESTATION_CERT;
+ }
+ } else { // no attestation key blob
+ // Attestation challenge present then it is an error because no factory provisioned attest key
+ if (attChallenge != KMType.INVALID_VALUE && KMByteBlob.cast(attChallenge).length() > 0) {
+ KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED);
+ } else if (KMEnumArrayTag.contains(KMType.PURPOSE, KMType.ATTEST_KEY, data[HW_PARAMETERS])
+ || KMEnumArrayTag.contains(KMType.PURPOSE, KMType.SIGN, data[HW_PARAMETERS])) {
+ // The Purpose value can be read from either data[HW_PARAMETERS] or data[KEY_PARAMETERS]
+ // as the values will be same, and they are cryptographically bound.
+ mode = KMType.SELF_SIGNED_CERT;
+ } else {
+ mode = KMType.FAKE_CERT;
+ }
+ }
+ return mode;
+ }
+
+ private KMAttestationCert generateAttestation(
+ short attKeyBlob, short attKeyParam, byte[] scratchPad) {
+ // 1) If attestation key is present and attestation challenge is absent then it is an error.
+ // 2) If attestation key is absent and attestation challenge is present then it is an error as
+ // factory provisioned attestation key is not supported.
+ // 3) If both are present and issuer is absent or attest key purpose is not ATTEST_KEY then it
+ // is an error.
+ // 4) If the generated/imported keys are RSA or EC then validity period must be specified.
+ // Device Unique Attestation is not supported.
+ short heapStart = repository.getHeapIndex();
+ KMTag.assertAbsence(
+ data[KEY_PARAMETERS],
+ KMType.BOOL_TAG,
+ KMType.DEVICE_UNIQUE_ATTESTATION,
+ KMError.CANNOT_ATTEST_IDS);
+ // Read attestation challenge if present
+ short attChallenge =
+ KMKeyParameters.findTag(
+ KMType.BYTES_TAG, KMType.ATTESTATION_CHALLENGE, data[KEY_PARAMETERS]);
+ if (attChallenge != KMType.INVALID_VALUE) {
+ attChallenge = KMByteTag.cast(attChallenge).getValue();
+ }
+ // No attestation required for symmetric keys
+ short mode = getAttestationMode(attKeyBlob, attChallenge);
+ KMAttestationCert cert = null;
+
+ switch (mode) {
+ case KMType.ATTESTATION_CERT:
+ cert =
+ makeAttestationCert(
+ attKeyBlob, attKeyParam, attChallenge, data[ATTEST_KEY_ISSUER], scratchPad);
+ break;
+ case KMType.SELF_SIGNED_CERT:
+ cert = makeSelfSignedCert(data[SECRET], data[PUB_KEY], mode, scratchPad);
+ break;
+ case KMType.FAKE_CERT:
+ // Generate certificate with no signature.
+ cert = makeSelfSignedCert(KMType.INVALID_VALUE, data[PUB_KEY], mode, scratchPad);
+ break;
+ default:
+ data[CERTIFICATE] = KMType.INVALID_VALUE;
+ return null;
+ }
+ // Certificate Data is converted to cbor and written to the end of the stack.
+ short certData = repository.allocReclaimableMemory(MAX_CERT_SIZE);
+ // Leave first 4 bytes for Array header and ByteBlob header.
+ cert.buffer(repository.getHeap(), (short) (certData + 4), (short) (MAX_CERT_SIZE - 4));
+ // Build the certificate - this will sign the cert
+ cert.build();
+ // Certificate is now built so the data in the heap starting from heapStart to the current
+ // heap index can be reused. So resetting the heap index to heapStart.
+ repository.setHeapIndex(heapStart);
+ data[CERTIFICATE] = certData;
+ return cert;
+ }
+
+ // Encodes KeyCharacteristics at the end of the heap
+ private void encodeKeyCharacteristics(short keyChars) {
+ byte[] buffer = repository.getHeap();
+ short prevReclaimIndex = repository.getHeapReclaimIndex();
+ short ptr = repository.allocReclaimableMemory(MAX_KEY_CHARS_SIZE);
+ short len = encoder.encode(keyChars, buffer, ptr, prevReclaimIndex, MAX_KEY_CHARS_SIZE);
+ // shift the encoded KeyCharacteristics data towards the right till the data[CERTIFICATE]
+ // offset.
+ Util.arrayCopyNonAtomic(buffer, ptr, buffer, (short) (ptr + (MAX_KEY_CHARS_SIZE - len)), len);
+ // Reclaim the unused memory.
+ repository.reclaimMemory((short) (MAX_KEY_CHARS_SIZE - len));
+ }
+
+ // Encodes KeyBlob at the end of the heap
+ private void encodeKeyBlob(short keyBlobPtr) {
+ // allocate reclaimable memory.
+ byte[] buffer = repository.getHeap();
+ short prevReclaimIndex = repository.getHeapReclaimIndex();
+ short top = repository.allocReclaimableMemory(MAX_KEYBLOB_SIZE);
+ short keyBlob = encoder.encode(keyBlobPtr, buffer, top, prevReclaimIndex, MAX_KEYBLOB_SIZE);
+ Util.arrayCopyNonAtomic(
+ repository.getHeap(),
+ top,
+ repository.getHeap(),
+ (short) (top + MAX_KEYBLOB_SIZE - keyBlob),
+ keyBlob);
+ short newTop = (short) (top + MAX_KEYBLOB_SIZE - keyBlob);
+ // Encode the KeyBlob array inside a ByteString. Get the length of
+ // the ByteString header.
+ short encodedBytesLength = encoder.getEncodedBytesLength(keyBlob);
+ newTop -= encodedBytesLength;
+ encoder.encodeByteBlobHeader(keyBlob, buffer, newTop, encodedBytesLength);
+ // Reclaim unused memory.
+ repository.reclaimMemory((short) (newTop - top));
+ }
+
+ private short readKeyBlobVersion(short keyBlob) {
+ short version = KMType.INVALID_VALUE;
+ try {
+ version =
+ decoder.readKeyblobVersion(
+ KMByteBlob.cast(keyBlob).getBuffer(),
+ KMByteBlob.cast(keyBlob).getStartOff(),
+ KMByteBlob.cast(keyBlob).length());
+ if (version == KMType.INVALID_VALUE) {
+ // If Version is not present. Then it is either an old KeyBlob or
+ // corrupted KeyBlob.
+ version = 0;
+ } else {
+ version = KMInteger.cast(version).getShort();
+ if (version > KEYBLOB_CURRENT_VERSION || version < 0) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ }
+ } catch (Exception e) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ return version;
+ }
+
+ private void readKeyBlobParams(short version, short parsedKeyBlob) {
+ data[KEY_BLOB] = parsedKeyBlob;
+ // initialize data
+ switch (version) {
+ case (short) 0:
+ data[SECRET] = KMArray.cast(parsedKeyBlob).get((short) 0);
+ data[NONCE] = KMArray.cast(parsedKeyBlob).get((short) 1);
+ data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get((short) 2);
+ data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get((short) 3);
+ data[PUB_KEY] = KMType.INVALID_VALUE;
+ if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE_V0) {
+ data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get((short) 4);
+ }
+ // Set the data[KEY_BLOB_VERSION_DATA_OFFSET] with integer value of 0 so
+ // that it will used at later point of time.
+ data[KEY_BLOB_VERSION_DATA_OFFSET] = KMInteger.uint_8((byte) 0);
+ break;
+ case (short) 1:
+ data[KEY_BLOB_VERSION_DATA_OFFSET] = KMArray.cast(parsedKeyBlob).get((short) 0);
+ data[SECRET] = KMArray.cast(parsedKeyBlob).get((short) 1);
+ data[NONCE] = KMArray.cast(parsedKeyBlob).get((short) 2);
+ data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get((short) 3);
+ data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get((short) 4);
+ data[PUB_KEY] = KMType.INVALID_VALUE;
+ if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE_V1) {
+ data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get((short) 5);
+ }
+ break;
+ case (short) 2:
+ case (short) 3:
+ data[SECRET] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_SECRET);
+ data[NONCE] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_NONCE);
+ data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_AUTH_TAG);
+ data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_PARAMS);
+ data[KEY_BLOB_VERSION_DATA_OFFSET] =
+ KMArray.cast(parsedKeyBlob).get(KEY_BLOB_VERSION_OFFSET);
+ data[CUSTOM_TAGS] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_CUSTOM_TAGS);
+ data[PUB_KEY] = KMType.INVALID_VALUE;
+ if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE_V2_V3) {
+ data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_PUB_KEY);
+ }
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ }
+
+ // Decode the KeyBlob from CBOR structures to the sub types of KMType.
+ private void decodeKeyBlob(short version, short keyBlob) {
+ // Decode KeyBlob and read the KeyBlob params based on the version.
+ short parsedBlob =
+ decoder.decodeArray(
+ createKeyBlobExp(version),
+ KMByteBlob.cast(keyBlob).getBuffer(),
+ KMByteBlob.cast(keyBlob).getStartOff(),
+ KMByteBlob.cast(keyBlob).length());
+ short minArraySize = 0;
+ switch (version) {
+ case 0:
+ minArraySize = SYM_KEY_BLOB_SIZE_V0;
+ break;
+ case 1:
+ minArraySize = SYM_KEY_BLOB_SIZE_V1;
+ break;
+ case 2:
+ case 3:
+ minArraySize = SYM_KEY_BLOB_SIZE_V2_V3;
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ ;
+ // KeyBlob size should not be less than the minimum KeyBlob size.
+ if (KMArray.cast(parsedBlob).length() < minArraySize) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ readKeyBlobParams(version, parsedBlob);
+ }
+
+ // Decrypts the secret key in the KeyBlob. The secret can be a Symmetric or Asymmetric key.
+ private void processDecryptSecret(short version, short appId, short appData, byte[] scratchPad) {
+ data[TEE_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced();
+ data[SB_PARAMETERS] =
+ KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getStrongboxEnforced();
+ data[SW_PARAMETERS] =
+ KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getKeystoreEnforced();
+ data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]);
+
+ data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(appId, appData, data[ROT], scratchPad);
+ // Decrypt Secret and verify auth tag
+ decryptSecret(scratchPad, version);
+ short keyBlobSecretOff = 0;
+ switch (version) {
+ case 0:
+ // V0 KeyBlob
+ // KEY_BLOB = [
+ // SECRET,
+ // NONCE,
+ // AUTH_TAG,
+ // KEY_CHARACTERISTICS,
+ // PUBKEY
+ // ]
+ keyBlobSecretOff = (short) 0;
+ break;
+ case 1:
+ // V1 KeyBlob
+ // KEY_BLOB = [
+ // VERSION,
+ // SECRET,
+ // NONCE,
+ // AUTH_TAG,
+ // KEY_CHARACTERISTICS,
+ // PUBKEY
+ // ]
+ keyBlobSecretOff = (short) 1;
+ break;
+ case 2:
+ case 3:
+ // V2 KeyBlob
+ // KEY_BLOB = [
+ // VERSION,
+ // SECRET,
+ // NONCE,
+ // AUTH_TAG,
+ // KEY_CHARACTERISTICS,
+ // CUSTOM_TAGS,
+ // PUBKEY
+ // ]
+ keyBlobSecretOff = KEY_BLOB_SECRET;
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ ;
+ KMArray.cast(data[KEY_BLOB]).add(keyBlobSecretOff, data[SECRET]);
+ }
+
+ private void parseEncryptedKeyBlob(
+ short keyBlob, short appId, short appData, byte[] scratchPad, short version) {
+ // make root of trust blob
+ data[ROT] = readROT(scratchPad, version);
+ if (data[ROT] == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ try {
+ decodeKeyBlob(version, keyBlob);
+ processDecryptSecret(version, appId, appData, scratchPad);
+ } catch (Exception e) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ }
+
+ private void decryptSecret(byte[] scratchPad, short version) {
+ // derive master key - stored in derivedKey
+ short len;
+ short authDataOff = 0;
+ short authDataLen = 0;
+ byte[] authDataBuff = null;
+ switch (version) {
+ case 3:
+ len = deriveKey(scratchPad);
+ break;
+
+ case 2:
+ case 1:
+ case 0:
+ makeAuthData(version, scratchPad);
+ len = deriveKeyForOldKeyBlobs(scratchPad);
+ authDataBuff = repository.getHeap();
+ authDataOff = data[AUTH_DATA];
+ authDataLen = data[AUTH_DATA_LENGTH];
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ if (!seProvider.aesGCMDecrypt(
+ KMByteBlob.cast(data[DERIVED_KEY]).getBuffer(),
+ KMByteBlob.cast(data[DERIVED_KEY]).getStartOff(),
+ KMByteBlob.cast(data[DERIVED_KEY]).length(),
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(data[NONCE]).getBuffer(),
+ KMByteBlob.cast(data[NONCE]).getStartOff(),
+ KMByteBlob.cast(data[NONCE]).length(),
+ authDataBuff,
+ authDataOff,
+ authDataLen,
+ KMByteBlob.cast(data[AUTH_TAG]).getBuffer(),
+ KMByteBlob.cast(data[AUTH_TAG]).getStartOff(),
+ KMByteBlob.cast(data[AUTH_TAG]).length())) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ // Copy the decrypted secret
+ data[SECRET] =
+ KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.cast(data[SECRET]).length());
+ }
+
+ private short addIntegers(short authTime, short timeStamp, byte[] scratchPad) {
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 24, (byte) 0);
+ Util.arrayCopyNonAtomic(
+ KMInteger.cast(authTime).getBuffer(),
+ KMInteger.cast(authTime).getStartOff(),
+ scratchPad,
+ (short) (8 - KMInteger.cast(timeStamp).length()),
+ KMInteger.cast(timeStamp).length());
+
+ // Copy timestamp to scratchpad
+ Util.arrayCopyNonAtomic(
+ KMInteger.cast(timeStamp).getBuffer(),
+ KMInteger.cast(timeStamp).getStartOff(),
+ scratchPad,
+ (short) (16 - KMInteger.cast(timeStamp).length()),
+ KMInteger.cast(timeStamp).length());
+
+ // add authTime in millis to timestamp.
+ KMUtils.add(scratchPad, (short) 0, (short) 8, (short) 16);
+ return KMInteger.uint_64(scratchPad, (short) 16);
+ }
+
+ public void powerReset() {
+ // TODO handle power reset signal.
+ releaseAllOperations();
+ resetWrappingKey();
+ }
+
+ private void updateTrustedConfirmationOperation(KMOperationState op) {
+ if (op.isTrustedConfirmationRequired()) {
+ op.getTrustedConfirmationSigner()
+ .update(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ KMByteBlob.cast(data[INPUT_DATA]).length());
+ }
+ }
+
+ private void finishTrustedConfirmationOperation(KMOperationState op) {
+ // Perform trusted confirmation if required
+ if (op.isTrustedConfirmationRequired()) {
+ if (0 == KMByteBlob.cast(data[CONFIRMATION_TOKEN]).length()) {
+ KMException.throwIt(KMError.NO_USER_CONFIRMATION);
+ }
+
+ boolean verified =
+ op.getTrustedConfirmationSigner()
+ .verify(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ KMByteBlob.cast(data[INPUT_DATA]).length(),
+ KMByteBlob.cast(data[CONFIRMATION_TOKEN]).getBuffer(),
+ KMByteBlob.cast(data[CONFIRMATION_TOKEN]).getStartOff(),
+ KMByteBlob.cast(data[CONFIRMATION_TOKEN]).length());
+ if (!verified) {
+ KMException.throwIt(KMError.NO_USER_CONFIRMATION);
+ }
+ }
+ }
+
+ private boolean isDigestSupported(short alg, short digest) {
+ switch (alg) {
+ case KMType.RSA:
+ case KMType.EC:
+ if (digest != KMType.DIGEST_NONE && digest != KMType.SHA2_256) {
+ return false;
+ }
+ break;
+ case KMType.HMAC:
+ if (digest != KMType.SHA2_256) {
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java
new file mode 100644
index 0000000..56d99a0
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java
@@ -0,0 +1,1057 @@
+package com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMDataStoreConstants;
+import com.android.javacard.seprovider.KMException;
+import com.android.javacard.seprovider.KMKey;
+import com.android.javacard.seprovider.KMSEProvider;
+import com.android.javacard.seprovider.KMUpgradable;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+import org.globalplatform.upgrade.Element;
+
+/**
+ * This is a storage class which helps in storing the provisioned data, ROT, OS version, Boot patch
+ * level, Vendor Patchlevel, HMAC nonce, computed shared secret, 8 auth tags, device-locked,
+ * device-locked timestamp and device-locked password only. Only the provisioned data is restored
+ * back during applet upgrades and the remaining data is flushed.
+ */
+public class KMKeymintDataStore implements KMUpgradable {
+
+ // Data table configuration
+ public static final short KM_APPLET_PACKAGE_VERSION_1 = 0x0100;
+ public static final short KM_APPLET_PACKAGE_VERSION_2 = 0x0200;
+ public static final short KM_APPLET_PACKAGE_VERSION_3 = 0x0300;
+ public static final byte DATA_INDEX_SIZE = 17;
+ public static final byte DATA_INDEX_ENTRY_SIZE = 4;
+ public static final byte DATA_INDEX_ENTRY_LENGTH = 0;
+ public static final byte DATA_INDEX_ENTRY_OFFSET = 2;
+ public static final short DATA_MEM_SIZE = 300;
+ // Data table offsets
+ public static final byte HMAC_NONCE = 0;
+ public static final byte BOOT_OS_VERSION = 1;
+ public static final byte BOOT_OS_PATCH_LEVEL = 2;
+ public static final byte VENDOR_PATCH_LEVEL = 3;
+ public static final byte DEVICE_LOCKED_TIME = 4;
+ public static final byte DEVICE_LOCKED = 5;
+ public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 6;
+ // Total 8 auth tags, so the next offset is AUTH_TAG_1 + 8
+ public static final byte AUTH_TAG_1 = 7;
+ public static final byte DEVICE_STATUS_FLAG = 15;
+ public static final byte EARLY_BOOT_ENDED_FLAG = 16;
+ // Data Item sizes
+ public static final byte HMAC_SEED_NONCE_SIZE = 32;
+ public static final byte COMPUTED_HMAC_KEY_SIZE = 32;
+ public static final byte OS_VERSION_SIZE = 4;
+ public static final byte OS_PATCH_SIZE = 4;
+ public static final byte VENDOR_PATCH_SIZE = 4;
+ public static final byte DEVICE_LOCK_TS_SIZE = 8;
+ public static final byte MAX_BLOB_STORAGE = 8;
+ public static final byte AUTH_TAG_LENGTH = 16;
+ public static final byte AUTH_TAG_COUNTER_SIZE = 4;
+ public static final byte AUTH_TAG_ENTRY_SIZE = (AUTH_TAG_LENGTH + AUTH_TAG_COUNTER_SIZE + 1);
+ // Device boot states. Applet starts executing the
+ // core commands once all the states are set. The commands
+ // that are allowed irrespective of these states are:
+ // All the provision commands
+ // INS_GET_HW_INFO_CMD
+ // INS_ADD_RNG_ENTROPY_CMD
+ // INS_COMPUTE_SHARED_HMAC_CMD
+ // INS_GET_HMAC_SHARING_PARAM_CMD
+ public static final byte SET_BOOT_PARAMS_SUCCESS = 0x01;
+ public static final byte SET_SYSTEM_PROPERTIES_SUCCESS = 0x02;
+ public static final byte NEGOTIATED_SHARED_SECRET_SUCCESS = 0x04;
+ // Old Data table offsets
+ private static final byte OLD_PROVISIONED_STATUS_OFFSET = 18;
+ private static final byte SHARED_SECRET_KEY_SIZE = 32;
+ private static final byte DEVICE_STATUS_FLAG_SIZE = 1;
+ private static final short ADDITIONAL_CERT_CHAIN_MAX_SIZE = 2500; // First 2 bytes for length.
+ private static final short BCC_MAX_SIZE = 512;
+ private static KMKeymintDataStore kmDataStore;
+ // Secure Boot Mode
+ public byte secureBootMode;
+ // Data - originally was in repository
+ private byte[] attIdBrand;
+ private byte[] attIdDevice;
+ private byte[] attIdProduct;
+ private byte[] attIdSerial;
+ private byte[] attIdImei;
+ private byte[] attIdMeId;
+ private byte[] attIdManufacturer;
+ private byte[] attIdModel;
+ // Boot parameters
+ private byte[] verifiedHash;
+ private byte[] bootKey;
+ private byte[] bootPatchLevel;
+ private boolean deviceBootLocked;
+ private short bootState;
+ // Challenge for Root of trust
+ private byte[] challenge;
+ private short dataIndex;
+ private byte[] dataTable;
+ private KMSEProvider seProvider;
+ private KMRepository repository;
+ private byte[] additionalCertChain;
+ private byte[] bcc;
+ private KMKey masterKey;
+ private KMKey testDeviceUniqueKeyPair;
+ private KMKey deviceUniqueKeyPair;
+ private KMKey preSharedKey;
+ private KMKey computedHmacKey;
+ private KMKey rkpMacKey;
+ private byte[] oemRootPublicKey;
+ private short provisionStatus;
+
+ public KMKeymintDataStore(KMSEProvider provider, KMRepository repo) {
+ seProvider = provider;
+ repository = repo;
+ boolean isUpgrading = provider.isUpgrading();
+ initDataTable();
+ // Initialize the device locked status
+ if (!isUpgrading) {
+ additionalCertChain = new byte[ADDITIONAL_CERT_CHAIN_MAX_SIZE];
+ bcc = new byte[BCC_MAX_SIZE];
+ oemRootPublicKey = new byte[65];
+ }
+ setDeviceLockPasswordOnly(false);
+ setDeviceLock(false);
+ kmDataStore = this;
+ }
+
+ public static KMKeymintDataStore instance() {
+ return kmDataStore;
+ }
+
+ private void initDataTable() {
+ if (dataTable == null) {
+ dataTable = new byte[DATA_MEM_SIZE];
+ dataIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE);
+ }
+ }
+
+ private short dataAlloc(short length) {
+ if (((short) (dataIndex + length)) > dataTable.length) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ dataIndex += length;
+ return (short) (dataIndex - length);
+ }
+
+ private void clearDataEntry(short id) {
+ id = (short) (id * DATA_INDEX_ENTRY_SIZE);
+ short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH));
+ if (dataLen != 0) {
+ short dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET));
+ JCSystem.beginTransaction();
+ Util.arrayFillNonAtomic(dataTable, dataPtr, dataLen, (byte) 0);
+ JCSystem.commitTransaction();
+ }
+ }
+
+ private void writeDataEntry(short id, byte[] buf, short offset, short len) {
+ short dataPtr;
+ id = (short) (id * DATA_INDEX_ENTRY_SIZE);
+ short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH));
+ if (dataLen == 0) {
+ dataPtr = dataAlloc(len);
+ JCSystem.beginTransaction();
+ Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET), dataPtr);
+ Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH), len);
+ Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len);
+ JCSystem.commitTransaction();
+ } else {
+ if (len != dataLen) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET));
+ JCSystem.beginTransaction();
+ Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len);
+ JCSystem.commitTransaction();
+ }
+ }
+
+ private short readDataEntry(short id, byte[] buf, short offset) {
+ id = (short) (id * DATA_INDEX_ENTRY_SIZE);
+ short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH));
+ if (len != 0) {
+ Util.arrayCopyNonAtomic(
+ dataTable,
+ Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)),
+ buf,
+ offset,
+ len);
+ }
+ return len;
+ }
+
+ private short readDataEntry(byte[] dataTable, short id, byte[] buf, short offset) {
+ id = (short) (id * DATA_INDEX_ENTRY_SIZE);
+ short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH));
+ if (len != 0) {
+ Util.arrayCopyNonAtomic(
+ dataTable,
+ Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)),
+ buf,
+ offset,
+ len);
+ }
+ return len;
+ }
+
+ private short dataLength(short id) {
+ id = (short) (id * DATA_INDEX_ENTRY_SIZE);
+ return Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH));
+ }
+
+ public short readData(short id) {
+ short len = dataLength(id);
+ if (len != 0) {
+ short blob = KMByteBlob.instance(dataLength(id));
+ readDataEntry(id, KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff());
+ return blob;
+ }
+ return KMType.INVALID_VALUE;
+ }
+
+ public short getHmacNonce() {
+ return readData(HMAC_NONCE);
+ }
+
+ public short getOsVersion() {
+ short blob = readData(BOOT_OS_VERSION);
+ if (blob == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return KMInteger.uint_32(
+ KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff());
+ }
+
+ public short getVendorPatchLevel() {
+ short blob = readData(VENDOR_PATCH_LEVEL);
+ if (blob == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return KMInteger.uint_32(
+ KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff());
+ }
+
+ public short getOsPatch() {
+ short blob = readData(BOOT_OS_PATCH_LEVEL);
+ if (blob == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return KMInteger.uint_32(
+ KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff());
+ }
+
+ private boolean readBoolean(short id) {
+ short blob = readData(id);
+ if (blob == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return (byte) ((repository.getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01;
+ }
+
+ public boolean getDeviceLock() {
+ return readBoolean(DEVICE_LOCKED);
+ }
+
+ public void setDeviceLock(boolean flag) {
+ writeBoolean(DEVICE_LOCKED, flag);
+ }
+
+ public boolean getDeviceLockPasswordOnly() {
+ return readBoolean(DEVICE_LOCKED_PASSWORD_ONLY);
+ }
+
+ public void setDeviceLockPasswordOnly(boolean flag) {
+ writeBoolean(DEVICE_LOCKED_PASSWORD_ONLY, flag);
+ }
+
+ public boolean getEarlyBootEndedStatus() {
+ return readBoolean(EARLY_BOOT_ENDED_FLAG);
+ }
+
+ public void setEarlyBootEndedStatus(boolean flag) {
+ writeBoolean(EARLY_BOOT_ENDED_FLAG, flag);
+ }
+
+ public short getDeviceTimeStamp() {
+ short blob = readData(DEVICE_LOCKED_TIME);
+ if (blob == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return KMInteger.uint_64(
+ KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff());
+ }
+
+ public void setOsVersion(byte[] buf, short start, short len) {
+ if (len != OS_VERSION_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ writeDataEntry(BOOT_OS_VERSION, buf, start, len);
+ }
+
+ public void setVendorPatchLevel(byte[] buf, short start, short len) {
+ if (len != VENDOR_PATCH_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ writeDataEntry(VENDOR_PATCH_LEVEL, buf, start, len);
+ }
+
+ private void writeBoolean(short id, boolean flag) {
+ short start = repository.alloc((short) 1);
+ if (flag) {
+ (repository.getHeap())[start] = (byte) 0x01;
+ } else {
+ (repository.getHeap())[start] = (byte) 0x00;
+ }
+ writeDataEntry(id, repository.getHeap(), start, (short) 1);
+ }
+
+ public void setDeviceLockTimestamp(byte[] buf, short start, short len) {
+ if (len != DEVICE_LOCK_TS_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ writeDataEntry(DEVICE_LOCKED_TIME, buf, start, len);
+ }
+
+ public void clearDeviceBootStatus() {
+ clearDataEntry(DEVICE_STATUS_FLAG);
+ }
+
+ public void setDeviceBootStatus(byte initStatus) {
+ short offset = repository.allocReclaimableMemory(DEVICE_STATUS_FLAG_SIZE);
+ byte[] buf = repository.getHeap();
+ getDeviceBootStatus(buf, offset);
+ buf[offset] |= initStatus;
+ writeDataEntry(DEVICE_STATUS_FLAG, buf, offset, DEVICE_STATUS_FLAG_SIZE);
+ repository.reclaimMemory(DEVICE_STATUS_FLAG_SIZE);
+ }
+
+ public boolean isDeviceReady() {
+ boolean result = false;
+ short offset = repository.allocReclaimableMemory(DEVICE_STATUS_FLAG_SIZE);
+ byte[] buf = repository.getHeap();
+ getDeviceBootStatus(buf, offset);
+ byte bootCompleteStatus =
+ (SET_BOOT_PARAMS_SUCCESS
+ | SET_SYSTEM_PROPERTIES_SUCCESS
+ | NEGOTIATED_SHARED_SECRET_SUCCESS);
+ if (bootCompleteStatus == (buf[offset] & bootCompleteStatus)) {
+ result = true;
+ }
+ repository.reclaimMemory(DEVICE_STATUS_FLAG_SIZE);
+ return result;
+ }
+
+ public short getDeviceBootStatus(byte[] scratchpad, short offset) {
+ scratchpad[offset] = 0;
+ return readDataEntry(DEVICE_STATUS_FLAG, scratchpad, offset);
+ }
+
+ public void clearDeviceLockTimeStamp() {
+ clearDataEntry(DEVICE_LOCKED_TIME);
+ }
+
+ public void setOsPatch(byte[] buf, short start, short len) {
+ if (len != OS_PATCH_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ writeDataEntry(BOOT_OS_PATCH_LEVEL, buf, start, len);
+ }
+
+ private boolean isAuthTagSlotAvailable(short tagId, byte[] buf, short offset) {
+ readDataEntry(tagId, buf, offset);
+ return (0 == buf[offset]);
+ }
+
+ public void initHmacNonce(byte[] nonce, short offset, short len) {
+ if (len != HMAC_SEED_NONCE_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ writeDataEntry(HMAC_NONCE, nonce, offset, len);
+ }
+
+ public void clearHmacNonce() {
+ clearDataEntry(HMAC_NONCE);
+ }
+
+ public boolean persistAuthTag(short authTag) {
+
+ if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+
+ short authTagEntry = repository.alloc(AUTH_TAG_ENTRY_SIZE);
+ short scratchPadOff = repository.alloc(AUTH_TAG_ENTRY_SIZE);
+ byte[] scratchPad = repository.getHeap();
+ writeAuthTagState(repository.getHeap(), authTagEntry, (byte) 1);
+ Util.arrayCopyNonAtomic(
+ KMByteBlob.cast(authTag).getBuffer(),
+ KMByteBlob.cast(authTag).getStartOff(),
+ repository.getHeap(),
+ (short) (authTagEntry + 1),
+ AUTH_TAG_LENGTH);
+ Util.setShort(
+ repository.getHeap(), (short) (authTagEntry + AUTH_TAG_LENGTH + 1 + 2), (short) 1);
+ short index = 0;
+ while (index < MAX_BLOB_STORAGE) {
+ if ((dataLength((short) (index + AUTH_TAG_1)) == 0)
+ || isAuthTagSlotAvailable((short) (index + AUTH_TAG_1), scratchPad, scratchPadOff)) {
+
+ writeDataEntry(
+ (short) (index + AUTH_TAG_1), repository.getHeap(), authTagEntry, AUTH_TAG_ENTRY_SIZE);
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ public void removeAllAuthTags() {
+ short index = 0;
+ while (index < MAX_BLOB_STORAGE) {
+ clearDataEntry((short) (index + AUTH_TAG_1));
+ index++;
+ }
+ }
+
+ public boolean isAuthTagPersisted(short authTag) {
+ return (KMType.INVALID_VALUE != findTag(authTag));
+ }
+
+ private short findTag(short authTag) {
+ if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ short index = 0;
+ short found;
+ short offset = repository.alloc(AUTH_TAG_ENTRY_SIZE);
+ while (index < MAX_BLOB_STORAGE) {
+ if (dataLength((short) (index + AUTH_TAG_1)) != 0) {
+ readDataEntry((short) (index + AUTH_TAG_1), repository.getHeap(), offset);
+ found =
+ Util.arrayCompare(
+ repository.getHeap(),
+ (short) (offset + 1),
+ KMByteBlob.cast(authTag).getBuffer(),
+ KMByteBlob.cast(authTag).getStartOff(),
+ AUTH_TAG_LENGTH);
+ if (found == 0) {
+ return (short) (index + AUTH_TAG_1);
+ }
+ }
+ index++;
+ }
+ return KMType.INVALID_VALUE;
+ }
+
+ public short getRateLimitedKeyCount(short authTag, byte[] out, short outOff) {
+ short tag = findTag(authTag);
+ short blob;
+ if (tag != KMType.INVALID_VALUE) {
+ blob = readData(tag);
+ Util.arrayCopyNonAtomic(
+ KMByteBlob.cast(blob).getBuffer(),
+ (short) (KMByteBlob.cast(blob).getStartOff() + AUTH_TAG_LENGTH + 1),
+ out,
+ outOff,
+ AUTH_TAG_COUNTER_SIZE);
+ return AUTH_TAG_COUNTER_SIZE;
+ }
+ return (short) 0;
+ }
+
+ public void setRateLimitedKeyCount(short authTag, byte[] buf, short off, short len) {
+ short tag = findTag(authTag);
+ if (tag != KMType.INVALID_VALUE) {
+ short dataPtr = readData(tag);
+ Util.arrayCopyNonAtomic(
+ buf,
+ off,
+ KMByteBlob.cast(dataPtr).getBuffer(),
+ (short) (KMByteBlob.cast(dataPtr).getStartOff() + AUTH_TAG_LENGTH + 1),
+ len);
+ writeDataEntry(
+ tag,
+ KMByteBlob.cast(dataPtr).getBuffer(),
+ KMByteBlob.cast(dataPtr).getStartOff(),
+ KMByteBlob.cast(dataPtr).length());
+ }
+ }
+
+ public void persistAdditionalCertChain(byte[] buf, short offset, short len) {
+ // Input buffer contains encoded additional certificate chain as shown below.
+ // AdditionalDKSignatures = {
+ // + SignerName => DKCertChain
+ // }
+ // SignerName = tstr
+ // DKCertChain = [
+ // 2* Certificate // Root -> Leaf. Root is the vendo r
+ // // self-signed cert, leaf contains DK_pu b
+ // ]
+ // Certificate = COSE_Sign1 of a public key
+ if ((short) (len + 2) > ADDITIONAL_CERT_CHAIN_MAX_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ JCSystem.beginTransaction();
+ Util.setShort(additionalCertChain, (short) 0, (short) len);
+ Util.arrayCopyNonAtomic(buf, offset, additionalCertChain, (short) 2, len);
+ JCSystem.commitTransaction();
+ }
+
+ public short getAdditionalCertChainLength() {
+ return Util.getShort(additionalCertChain, (short) 0);
+ }
+
+ public byte[] getAdditionalCertChain() {
+ return additionalCertChain;
+ }
+
+ public byte[] getBootCertificateChain() {
+ return bcc;
+ }
+
+ public void persistBootCertificateChain(byte[] buf, short offset, short len) {
+ if ((short) (len + 2) > BCC_MAX_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ JCSystem.beginTransaction();
+ Util.setShort(bcc, (short) 0, (short) len);
+ Util.arrayCopyNonAtomic(buf, offset, bcc, (short) 2, len);
+ JCSystem.commitTransaction();
+ }
+
+ private void writeAuthTagState(byte[] buf, short offset, byte state) {
+ buf[offset] = state;
+ }
+
+ // The master key should only be generated during applet installation and
+ // during a device factory reset event.
+ public KMKey createMasterKey(short keySizeBits) {
+ if (masterKey == null) {
+ masterKey = seProvider.createMasterKey(masterKey, keySizeBits);
+ }
+ return (KMKey) masterKey;
+ }
+
+ public KMKey regenerateMasterKey() {
+ return seProvider.createMasterKey(masterKey, KMKeymasterApplet.MASTER_KEY_SIZE);
+ }
+
+ public KMKey getMasterKey() {
+ return masterKey;
+ }
+
+ public void createPresharedKey(byte[] keyData, short offset, short length) {
+ if (length != SHARED_SECRET_KEY_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ if (preSharedKey == null) {
+ preSharedKey = seProvider.createPreSharedKey(preSharedKey, keyData, offset, length);
+ }
+ }
+
+ public KMKey getPresharedKey() {
+ if (preSharedKey == null) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return preSharedKey;
+ }
+
+ public void createComputedHmacKey(byte[] keyData, short offset, short length) {
+ if (length != COMPUTED_HMAC_KEY_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ if (computedHmacKey == null) {
+ computedHmacKey = seProvider.createComputedHmacKey(computedHmacKey, keyData, offset, length);
+ } else {
+ seProvider.createComputedHmacKey(computedHmacKey, keyData, offset, length);
+ }
+ }
+
+ public KMKey getComputedHmacKey() {
+ if (computedHmacKey == null) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return computedHmacKey;
+ }
+
+ public KMKey createRkpTestDeviceUniqueKeyPair(
+ byte[] pubKey,
+ short pubKeyOff,
+ short pubKeyLen,
+ byte[] privKey,
+ short privKeyOff,
+ short privKeyLen) {
+ if (testDeviceUniqueKeyPair == null) {
+ testDeviceUniqueKeyPair =
+ seProvider.createRkpDeviceUniqueKeyPair(
+ testDeviceUniqueKeyPair,
+ pubKey,
+ pubKeyOff,
+ pubKeyLen,
+ privKey,
+ privKeyOff,
+ privKeyLen);
+ } else {
+ seProvider.createRkpDeviceUniqueKeyPair(
+ testDeviceUniqueKeyPair, pubKey, pubKeyOff, pubKeyLen, privKey, privKeyOff, privKeyLen);
+ }
+ return testDeviceUniqueKeyPair;
+ }
+
+ public KMKey createRkpDeviceUniqueKeyPair(
+ byte[] pubKey,
+ short pubKeyOff,
+ short pubKeyLen,
+ byte[] privKey,
+ short privKeyOff,
+ short privKeyLen) {
+ if (deviceUniqueKeyPair == null) {
+ deviceUniqueKeyPair =
+ seProvider.createRkpDeviceUniqueKeyPair(
+ deviceUniqueKeyPair, pubKey, pubKeyOff, pubKeyLen, privKey, privKeyOff, privKeyLen);
+ } else {
+ seProvider.createRkpDeviceUniqueKeyPair(
+ deviceUniqueKeyPair, pubKey, pubKeyOff, pubKeyLen, privKey, privKeyOff, privKeyLen);
+ }
+ return deviceUniqueKeyPair;
+ }
+
+ public KMKey getRkpDeviceUniqueKeyPair(boolean testMode) {
+ return ((KMKey) (testMode ? testDeviceUniqueKeyPair : deviceUniqueKeyPair));
+ }
+
+ public void createRkpMacKey(byte[] keydata, short offset, short length) {
+ if (rkpMacKey == null) {
+ rkpMacKey = seProvider.createRkpMacKey(rkpMacKey, keydata, offset, length);
+ } else {
+ seProvider.createRkpMacKey(rkpMacKey, keydata, offset, length);
+ }
+ }
+
+ public KMKey getRkpMacKey() {
+ if (rkpMacKey == null) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return rkpMacKey;
+ }
+
+ public short getAttestationId(short tag, byte[] buffer, short start) {
+ byte[] attestId = null;
+ switch (tag) {
+ // Attestation Id Brand
+ case KMType.ATTESTATION_ID_BRAND:
+ attestId = attIdBrand;
+ break;
+ // Attestation Id Device
+ case KMType.ATTESTATION_ID_DEVICE:
+ attestId = attIdDevice;
+ break;
+ // Attestation Id Product
+ case KMType.ATTESTATION_ID_PRODUCT:
+ attestId = attIdProduct;
+ break;
+ // Attestation Id Serial
+ case KMType.ATTESTATION_ID_SERIAL:
+ attestId = attIdSerial;
+ break;
+ // Attestation Id IMEI
+ case KMType.ATTESTATION_ID_IMEI:
+ attestId = attIdImei;
+ break;
+ // Attestation Id MEID
+ case KMType.ATTESTATION_ID_MEID:
+ attestId = attIdMeId;
+ break;
+ // Attestation Id Manufacturer
+ case KMType.ATTESTATION_ID_MANUFACTURER:
+ attestId = attIdManufacturer;
+ break;
+ // Attestation Id Model
+ case KMType.ATTESTATION_ID_MODEL:
+ attestId = attIdModel;
+ break;
+ }
+ if (attestId == null) {
+ KMException.throwIt(KMError.CANNOT_ATTEST_IDS);
+ }
+ Util.arrayCopyNonAtomic(attestId, (short) 0, buffer, start, (short) attestId.length);
+ return (short) attestId.length;
+ }
+
+ public void setAttestationId(short tag, byte[] buffer, short start, short length) {
+ switch (tag) {
+ // Attestation Id Brand
+ case KMType.ATTESTATION_ID_BRAND:
+ JCSystem.beginTransaction();
+ attIdBrand = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdBrand, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ // Attestation Id Device
+ case KMType.ATTESTATION_ID_DEVICE:
+ JCSystem.beginTransaction();
+ attIdDevice = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdDevice, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ // Attestation Id Product
+ case KMType.ATTESTATION_ID_PRODUCT:
+ JCSystem.beginTransaction();
+ attIdProduct = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdProduct, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ // Attestation Id Serial
+ case KMType.ATTESTATION_ID_SERIAL:
+ JCSystem.beginTransaction();
+ attIdSerial = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdSerial, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ // Attestation Id IMEI
+ case KMType.ATTESTATION_ID_IMEI:
+ JCSystem.beginTransaction();
+ attIdImei = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdImei, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ // Attestation Id MEID
+ case KMType.ATTESTATION_ID_MEID:
+ JCSystem.beginTransaction();
+ attIdMeId = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdMeId, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ // Attestation Id Manufacturer
+ case KMType.ATTESTATION_ID_MANUFACTURER:
+ JCSystem.beginTransaction();
+ attIdManufacturer = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdManufacturer, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ // Attestation Id Model
+ case KMType.ATTESTATION_ID_MODEL:
+ JCSystem.beginTransaction();
+ attIdModel = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdModel, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ }
+ }
+
+ public void deleteAttestationIds() {
+ attIdBrand = null;
+ attIdDevice = null;
+ attIdProduct = null;
+ attIdSerial = null;
+ attIdImei = null;
+ attIdMeId = null;
+ attIdManufacturer = null;
+ attIdModel = null;
+ // Trigger garbage collection.
+ JCSystem.requestObjectDeletion();
+ }
+
+ public short getVerifiedBootHash(byte[] buffer, short start) {
+ if (verifiedHash == null) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ Util.arrayCopyNonAtomic(verifiedHash, (short) 0, buffer, start, (short) verifiedHash.length);
+ return (short) verifiedHash.length;
+ }
+
+ public short getBootKey(byte[] buffer, short start) {
+ if (bootKey == null) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ Util.arrayCopyNonAtomic(bootKey, (short) 0, buffer, start, (short) bootKey.length);
+ return (short) bootKey.length;
+ }
+
+ public short getBootState() {
+ return bootState;
+ }
+
+ public void setBootState(short state) {
+ bootState = state;
+ }
+
+ public boolean isDeviceBootLocked() {
+ return deviceBootLocked;
+ }
+
+ public short getBootPatchLevel() {
+ if (bootPatchLevel == null) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return KMInteger.uint_32(bootPatchLevel, (short) 0);
+ }
+
+ public void setVerifiedBootHash(byte[] buffer, short start, short length) {
+ if (verifiedHash == null) {
+ verifiedHash = new byte[32];
+ }
+ if (length != 32) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ Util.arrayCopy(buffer, start, verifiedHash, (short) 0, (short) 32);
+ }
+
+ public void setBootKey(byte[] buffer, short start, short length) {
+ if (bootKey == null) {
+ bootKey = new byte[32];
+ }
+ if (length != 32) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ Util.arrayCopy(buffer, start, bootKey, (short) 0, (short) 32);
+ }
+
+ public void setDeviceLocked(boolean state) {
+ deviceBootLocked = state;
+ }
+
+ public void setBootPatchLevel(byte[] buffer, short start, short length) {
+ if (bootPatchLevel == null) {
+ bootPatchLevel = new byte[4];
+ }
+ if (length > 4 || length < 0) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ Util.arrayCopy(buffer, start, bootPatchLevel, (short) 0, (short) length);
+ }
+
+ public void setChallenge(byte[] buf, short start, short length) {
+ if (challenge == null) {
+ challenge = new byte[16];
+ }
+ if (length != 16) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ Util.arrayCopy(buf, start, challenge, (short) 0, (short) length);
+ }
+
+ public short getChallenge(byte[] buffer, short start) {
+ if (challenge == null) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ Util.arrayCopyNonAtomic(challenge, (short) 0, buffer, start, (short) challenge.length);
+ return (short) challenge.length;
+ }
+
+ public boolean isProvisionLocked() {
+ if (0 != (provisionStatus & KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED)) {
+ return true;
+ }
+ return false;
+ }
+
+ public short getProvisionStatus() {
+ return provisionStatus;
+ }
+
+ public void setProvisionStatus(short pStatus) {
+ JCSystem.beginTransaction();
+ provisionStatus |= pStatus;
+ JCSystem.commitTransaction();
+ }
+
+ public void unlockProvision() {
+ JCSystem.beginTransaction();
+ provisionStatus &= ~KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED;
+ JCSystem.commitTransaction();
+ }
+
+ public void persistOEMRootPublicKey(byte[] inBuff, short inOffset, short inLength) {
+ if (inLength != 65) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ if (oemRootPublicKey == null) {
+ oemRootPublicKey = new byte[65];
+ }
+ Util.arrayCopy(inBuff, inOffset, oemRootPublicKey, (short) 0, inLength);
+ }
+
+ public byte[] getOEMRootPublicKey() {
+ if (oemRootPublicKey == null) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return oemRootPublicKey;
+ }
+
+ @Override
+ public void onSave(Element element) {
+ // Prmitives
+ element.write(provisionStatus);
+ element.write(secureBootMode);
+ // Objects
+ element.write(attIdBrand);
+ element.write(attIdDevice);
+ element.write(attIdProduct);
+ element.write(attIdSerial);
+ element.write(attIdImei);
+ element.write(attIdMeId);
+ element.write(attIdManufacturer);
+ element.write(attIdModel);
+ element.write(additionalCertChain);
+ element.write(bcc);
+ element.write(oemRootPublicKey);
+
+ // Key Objects
+ seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY, masterKey);
+ seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY, preSharedKey);
+ seProvider.onSave(
+ element, KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR, deviceUniqueKeyPair);
+ seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY, rkpMacKey);
+ }
+
+ @Override
+ public void onRestore(Element element, short oldVersion, short currentVersion) {
+ if (oldVersion <= KM_APPLET_PACKAGE_VERSION_1) {
+ // 1.0 to 3.1 Upgrade happens here.
+ handlePreviousVersionUpgrade(element);
+ return;
+ } else if (oldVersion == KM_APPLET_PACKAGE_VERSION_2) {
+ handleUpgrade(element, oldVersion);
+ JCSystem.beginTransaction();
+ // While upgrading Secure Boot Mode flag from 2.0 to 3.0, implementations
+ // have to update the secureBootMode with the correct input.
+ secureBootMode = 0;
+ provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_SECURE_BOOT_MODE;
+ // Applet package Versions till 2 had CoseSign1 for additionalCertificateChain.
+ // From package version 3, the additionalCertificateChain is in X.509 format.
+ // So Unreference the old address and allocate new persistent memory.
+ additionalCertChain = new byte[ADDITIONAL_CERT_CHAIN_MAX_SIZE];
+ JCSystem.commitTransaction();
+ // Request for ObjectDeletion for unreferenced address of additionalCertChain.
+ JCSystem.requestObjectDeletion();
+ return;
+ }
+ handleUpgrade(element, oldVersion);
+ }
+
+ private void handlePreviousVersionUpgrade(Element element) {
+ // Read Primitives
+ // restore old data table index
+ short oldDataIndex = element.readShort();
+ element.readBoolean(); // pop deviceBootLocked
+ element.readShort(); // pop bootState
+
+ // Read Objects
+ // restore old data table
+ byte[] oldDataTable = (byte[]) element.readObject();
+
+ attIdBrand = (byte[]) element.readObject();
+ attIdDevice = (byte[]) element.readObject();
+ attIdProduct = (byte[]) element.readObject();
+ attIdSerial = (byte[]) element.readObject();
+ attIdImei = (byte[]) element.readObject();
+ attIdMeId = (byte[]) element.readObject();
+ attIdManufacturer = (byte[]) element.readObject();
+ attIdModel = (byte[]) element.readObject();
+ element.readObject(); // pop verifiedHash
+ element.readObject(); // pop bootKey
+ element.readObject(); // pop bootPatchLevel
+ additionalCertChain = (byte[]) element.readObject();
+ bcc = (byte[]) element.readObject();
+
+ // Read Key Objects
+ masterKey = (KMKey) seProvider.onRestore(element);
+ seProvider.onRestore(element); // pop computedHmacKey
+ preSharedKey = (KMKey) seProvider.onRestore(element);
+ deviceUniqueKeyPair = (KMKey) seProvider.onRestore(element);
+ rkpMacKey = (KMKey) seProvider.onRestore(element);
+ handleProvisionStatusUpgrade(oldDataTable, oldDataIndex);
+ }
+
+ private void handleUpgrade(Element element, short oldVersion) {
+ // Read Primitives
+ provisionStatus = element.readShort();
+ if (oldVersion >= KM_APPLET_PACKAGE_VERSION_3) {
+ secureBootMode = element.readByte();
+ }
+ // Read Objects
+ attIdBrand = (byte[]) element.readObject();
+ attIdDevice = (byte[]) element.readObject();
+ attIdProduct = (byte[]) element.readObject();
+ attIdSerial = (byte[]) element.readObject();
+ attIdImei = (byte[]) element.readObject();
+ attIdMeId = (byte[]) element.readObject();
+ attIdManufacturer = (byte[]) element.readObject();
+ attIdModel = (byte[]) element.readObject();
+ additionalCertChain = (byte[]) element.readObject();
+ bcc = (byte[]) element.readObject();
+ oemRootPublicKey = (byte[]) element.readObject();
+ // Read Key Objects
+ masterKey = (KMKey) seProvider.onRestore(element);
+ preSharedKey = (KMKey) seProvider.onRestore(element);
+ deviceUniqueKeyPair = (KMKey) seProvider.onRestore(element);
+ rkpMacKey = (KMKey) seProvider.onRestore(element);
+ }
+
+ public void getProvisionStatus(byte[] dataTable, byte[] scratchpad, short offset) {
+ Util.setShort(scratchpad, offset, (short) 0);
+ readDataEntry(dataTable, OLD_PROVISIONED_STATUS_OFFSET, scratchpad, offset);
+ }
+
+ void handleProvisionStatusUpgrade(byte[] dataTable, short dataTableIndex) {
+ short dInex = repository.allocReclaimableMemory((short) 2);
+ byte data[] = repository.getHeap();
+ getProvisionStatus(dataTable, data, dInex);
+ short pStatus = (short) (data[dInex] & 0x00ff);
+ if (KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED
+ == (pStatus & KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED)) {
+ pStatus |=
+ KMKeymasterApplet.PROVISION_STATUS_SE_LOCKED
+ | KMKeymasterApplet.PROVISION_STATUS_SECURE_BOOT_MODE;
+ }
+ JCSystem.beginTransaction();
+ // While upgrading Secure Boot Mode flag from 1.0 to 3.0, implementations
+ // have to update the secureBootMode with the correct input.
+ secureBootMode = 0;
+ provisionStatus = pStatus;
+ // Applet package Versions till 2 had CoseSign1 for additionalCertificateChain.
+ // From package version 3, the additionalCertificateChain is in X.509 format.
+ // So Unreference the old address and allocate new persistent memory.
+ additionalCertChain = new byte[ADDITIONAL_CERT_CHAIN_MAX_SIZE];
+ JCSystem.commitTransaction();
+ repository.reclaimMemory((short) 2);
+ // Request object deletion for unreferenced address for additionalCertChain
+ JCSystem.requestObjectDeletion();
+ }
+
+ @Override
+ public short getBackupPrimitiveByteCount() {
+ // provisionStatus - 2 bytes
+ // secureBootMode - 1 byte
+ return (short)
+ (3
+ + seProvider.getBackupPrimitiveByteCount(KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY)
+ + seProvider.getBackupPrimitiveByteCount(
+ KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY)
+ + seProvider.getBackupPrimitiveByteCount(
+ KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR)
+ + seProvider.getBackupPrimitiveByteCount(
+ KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY));
+ }
+
+ @Override
+ public short getBackupObjectCount() {
+ // AttestationIds - 8
+ // AdditionalCertificateChain - 1
+ // BCC - 1
+ // oemRootPublicKey - 1
+ return (short)
+ (11
+ + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY)
+ + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY)
+ + seProvider.getBackupObjectCount(
+ KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR)
+ + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY));
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMMap.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMMap.java
new file mode 100644
index 0000000..2418204
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMMap.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMMap represents an array of a KMType key and a KMType value. Map is the sequence of pairs. Each
+ * pair is one or more sub-types of KMType. The KMMap instance maps to the CBOR type map. KMMap is a
+ * KMType and it further extends the value field in TLV_HEADER as MAP_HEADER struct{ short
+ * subType;short length;} followed by a sequence of pairs. Each pair contains a key and a value as
+ * short pointers to KMType instances.
+ */
+public class KMMap extends KMType {
+
+ public static final short ANY_MAP_LENGTH = 0x1000;
+ private static final byte MAP_HEADER_SIZE = 4;
+ private static KMMap prototype;
+
+ private KMMap() {}
+
+ private static KMMap proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMMap();
+ }
+ instanceTable[KM_MAP_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp() {
+ short ptr = instance(MAP_TYPE, (short) MAP_HEADER_SIZE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), (short) 0);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), ANY_MAP_LENGTH);
+ return ptr;
+ }
+
+ public static short instance(short length) {
+ short ptr = KMType.instance(MAP_TYPE, (short) (MAP_HEADER_SIZE + (length * 4)));
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), (short) 0);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), length);
+ return ptr;
+ }
+
+ public static short instance(short length, byte type) {
+ short ptr = instance(length);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), type);
+ return ptr;
+ }
+
+ public static KMMap cast(short ptr) {
+ if (heap[ptr] != MAP_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public void add(short index, short keyPtr, short valPtr) {
+ short len = length();
+ if (index >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ short keyIndex =
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index * 4));
+ Util.setShort(heap, keyIndex, keyPtr);
+ Util.setShort(heap, (short) (keyIndex + 2), valPtr);
+ }
+
+ public short getKey(short index) {
+ short len = length();
+ if (index >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ return Util.getShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index * 4)));
+ }
+
+ public short getKeyValue(short index) {
+ short len = length();
+ if (index >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ return Util.getShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index * 4 + 2)));
+ }
+
+ public void swap(short index1, short index2) {
+ short len = length();
+ if (index1 >= len || index2 >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ // Swap keys
+ short indexPtr1 =
+ Util.getShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index1 * 4)));
+ short indexPtr2 =
+ Util.getShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index2 * 4)));
+ Util.setShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index1 * 4)),
+ indexPtr2);
+ Util.setShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index2 * 4)),
+ indexPtr1);
+
+ // Swap Values
+ indexPtr1 =
+ Util.getShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index1 * 4 + 2)));
+ indexPtr2 =
+ Util.getShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index2 * 4 + 2)));
+ Util.setShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index1 * 4 + 2)),
+ indexPtr2);
+ Util.setShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index2 * 4 + 2)),
+ indexPtr1);
+ }
+
+ public void canonicalize() {
+ KMCoseMap.canonicalize(instanceTable[KM_MAP_OFFSET], length());
+ }
+
+ public short containedType() {
+ return Util.getShort(heap, (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short getStartOff() {
+ return (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE);
+ }
+
+ public short length() {
+ return Util.getShort(heap, (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public byte[] getBuffer() {
+ return heap;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMNInteger.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMNInteger.java
new file mode 100644
index 0000000..3a6404e
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMNInteger.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * Represents 8-bit, 16-bit, 32-bit and 64-bit signed integer. It corresponds to CBOR int type.
+ * struct{byte NEG_INTEGER_TYPE; short length; 4 or 8 bytes of value}
+ */
+public class KMNInteger extends KMInteger {
+
+ public static final byte SIGNED_MASK = (byte) 0x80;
+ private static KMNInteger prototype;
+
+ private KMNInteger() {}
+
+ private static KMNInteger proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMNInteger();
+ }
+ instanceTable[KM_NEG_INTEGER_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp() {
+ return KMType.exp(NEG_INTEGER_TYPE);
+ }
+
+ // return an empty integer instance
+ public static short instance(short length) {
+ if ((length <= 0) || (length > 8)) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ if (length > 4) {
+ length = KMInteger.UINT_64;
+ } else {
+ length = KMInteger.UINT_32;
+ }
+ return KMType.instance(NEG_INTEGER_TYPE, length);
+ }
+
+ public static short instance(byte[] num, short srcOff, short length) {
+ if (length > 8) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ if (length == 1) {
+ return uint_8(num[srcOff]);
+ } else if (length == 2) {
+ return uint_16(Util.getShort(num, srcOff));
+ } else if (length == 4) {
+ return uint_32(num, srcOff);
+ } else {
+ return uint_64(num, srcOff);
+ }
+ }
+
+ public static KMNInteger cast(short ptr) {
+ byte[] heap = repository.getHeap();
+ if (heap[ptr] != NEG_INTEGER_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ // create integer and copy byte value
+ public static short uint_8(byte num) {
+ if (num >= 0) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(KMInteger.UINT_32);
+ heap[(short) (ptr + TLV_HEADER_SIZE + 3)] = num;
+ return ptr;
+ }
+
+ // create integer and copy short value
+ public static short uint_16(short num) {
+ if (num >= 0) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(KMInteger.UINT_32);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), num);
+ return ptr;
+ }
+
+ // create integer and copy integer value
+ public static short uint_32(byte[] num, short offset) {
+ if (!isSignedInteger(num, offset)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(KMInteger.UINT_32);
+ Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), KMInteger.UINT_32);
+ return ptr;
+ }
+
+ // create integer and copy integer value
+ public static short uint_64(byte[] num, short offset) {
+ if (!isSignedInteger(num, offset)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(KMInteger.UINT_64);
+ Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), KMInteger.UINT_64);
+ return ptr;
+ }
+
+ public static boolean isSignedInteger(byte[] num, short offset) {
+ byte val = num[offset];
+ return SIGNED_MASK == (val & SIGNED_MASK);
+ }
+
+ @Override
+ protected short getBaseOffset() {
+ return instanceTable[KM_NEG_INTEGER_OFFSET];
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMOperationState.java
new file mode 100644
index 0000000..2a53acd
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMOperationState.java
@@ -0,0 +1,354 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import com.android.javacard.seprovider.KMOperation;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * KMOperationState is the container of an active operation started by beginOperation function. This
+ * operation state is persisted by the applet in non volatile memory. However, this state is not
+ * retained if applet is upgraded. There will be four operation state records maintained i.e. only
+ * four active operations are supported at any given time.
+ */
+public class KMOperationState {
+
+ // sizes
+ public static final byte OPERATION_HANDLE_SIZE = 8;
+ public static final byte DATA_SIZE = 11;
+ public static final byte AUTH_TIME_SIZE = 8;
+ // Secure user ids 5 * 8 = 40 bytes ( Considering Maximum 5 SECURE USER IDs)
+ // First two bytes are reserved to store number of secure ids. So total 42 bytes.
+ public static final byte USER_SECURE_IDS_SIZE = 42;
+ // byte type
+ private static final byte ALG = 0;
+ private static final byte PURPOSE = 1;
+ private static final byte PADDING = 2;
+ private static final byte BLOCK_MODE = 3;
+ private static final byte DIGEST = 4;
+ private static final byte FLAGS = 5;
+ private static final byte KEY_SIZE = 6;
+ private static final byte MAC_LENGTH = 7;
+ private static final byte MGF_DIGEST = 8;
+ private static final byte AUTH_TYPE = 9;
+ private static final byte MIN_MAC_LENGTH = 10;
+ private static final byte OPERATION = 0;
+ private static final byte HMAC_SIGNER_OPERATION = 1;
+ // Flag masks
+ private static final byte AUTH_PER_OP_REQD = 1;
+ private static final byte SECURE_USER_ID_REQD = 2;
+ private static final byte AUTH_TIMEOUT_VALIDATED = 4;
+ private static final byte AES_GCM_UPDATE_ALLOWED = 8;
+ private static final byte PROCESSED_INPUT_MSG = 16;
+ // Max user secure ids.
+ private static final byte MAX_SECURE_USER_IDS = 5;
+
+ // Object References
+ private byte[] opHandle;
+ private byte[] authTime;
+ private byte[] userSecureIds;
+ private short[] data;
+ private Object[] operations;
+
+ public KMOperationState() {
+ opHandle = JCSystem.makeTransientByteArray(OPERATION_HANDLE_SIZE, JCSystem.CLEAR_ON_RESET);
+ authTime = JCSystem.makeTransientByteArray(AUTH_TIME_SIZE, JCSystem.CLEAR_ON_RESET);
+ data = JCSystem.makeTransientShortArray(DATA_SIZE, JCSystem.CLEAR_ON_RESET);
+ operations = JCSystem.makeTransientObjectArray((short) 2, JCSystem.CLEAR_ON_RESET);
+ userSecureIds = JCSystem.makeTransientByteArray(USER_SECURE_IDS_SIZE, JCSystem.CLEAR_ON_RESET);
+ reset();
+ }
+
+ public void reset() {
+ byte index = 0;
+ while (index < DATA_SIZE) {
+ data[index] = KMType.INVALID_VALUE;
+ index++;
+ }
+ Util.arrayFillNonAtomic(opHandle, (short) 0, OPERATION_HANDLE_SIZE, (byte) 0);
+ Util.arrayFillNonAtomic(authTime, (short) 0, AUTH_TIME_SIZE, (byte) 0);
+
+ if (null != operations[OPERATION]) {
+ ((KMOperation) operations[OPERATION]).abort();
+ }
+ operations[OPERATION] = null;
+
+ if (null != operations[HMAC_SIGNER_OPERATION]) {
+ ((KMOperation) operations[HMAC_SIGNER_OPERATION]).abort();
+ }
+ operations[HMAC_SIGNER_OPERATION] = null;
+ }
+
+ public short compare(byte[] handle, short start, short len) {
+ return Util.arrayCompare(handle, start, opHandle, (short) 0, (short) opHandle.length);
+ }
+
+ public short getKeySize() {
+ return data[KEY_SIZE];
+ }
+
+ public void setKeySize(short keySize) {
+ data[KEY_SIZE] = keySize;
+ }
+
+ public short getHandle() {
+ return KMInteger.uint_64(opHandle, (short) 0);
+ }
+
+ public void setHandle(byte[] buf, short start, short len) {
+ Util.arrayCopyNonAtomic(buf, start, opHandle, (short) 0, (short) opHandle.length);
+ }
+
+ public short getPurpose() {
+ return data[PURPOSE];
+ }
+
+ public void setPurpose(short purpose) {
+ data[PURPOSE] = purpose;
+ }
+
+ public boolean isInputMsgProcessed() {
+ return (data[FLAGS] & PROCESSED_INPUT_MSG) != 0;
+ }
+
+ public KMOperation getOperation() {
+ return (KMOperation) operations[OPERATION];
+ }
+
+ public void setOperation(KMOperation op) {
+ operations[OPERATION] = op;
+ }
+
+ public boolean isAuthPerOperationReqd() {
+ return (data[FLAGS] & AUTH_PER_OP_REQD) != 0;
+ }
+
+ public void setAuthPerOperationReqd(boolean flag) {
+ if (flag) {
+ data[FLAGS] = (short) (data[FLAGS] | AUTH_PER_OP_REQD);
+ } else {
+ data[FLAGS] = (short) (data[FLAGS] & (~AUTH_PER_OP_REQD));
+ }
+ }
+
+ public boolean isAuthTimeoutValidated() {
+ return (data[FLAGS] & AUTH_TIMEOUT_VALIDATED) != 0;
+ }
+
+ public void setAuthTimeoutValidated(boolean flag) {
+ if (flag) {
+ data[FLAGS] = (byte) (data[FLAGS] | AUTH_TIMEOUT_VALIDATED);
+ } else {
+ data[FLAGS] = (byte) (data[FLAGS] & (~AUTH_TIMEOUT_VALIDATED));
+ }
+ }
+
+ public boolean isSecureUserIdReqd() {
+ return (data[FLAGS] & SECURE_USER_ID_REQD) != 0;
+ }
+
+ public short getAuthTime() {
+ return KMInteger.uint_64(authTime, (short) 0);
+ }
+
+ public void setAuthTime(byte[] timeBuf, short start) {
+ Util.arrayCopyNonAtomic(timeBuf, start, authTime, (short) 0, AUTH_TIME_SIZE);
+ }
+
+ public void setProcessedInputMsg(boolean flag) {
+ if (flag) {
+ data[FLAGS] = (byte) (data[FLAGS] | PROCESSED_INPUT_MSG);
+ } else {
+ data[FLAGS] = (byte) (data[FLAGS] & (~PROCESSED_INPUT_MSG));
+ }
+ }
+
+ public void setOneTimeAuthReqd(boolean flag) {
+ if (flag) {
+ data[FLAGS] = (short) (data[FLAGS] | SECURE_USER_ID_REQD);
+ } else {
+ data[FLAGS] = (short) (data[FLAGS] & (~SECURE_USER_ID_REQD));
+ }
+ }
+
+ public short getAuthType() {
+ return data[AUTH_TYPE];
+ }
+
+ public void setAuthType(byte authType) {
+ data[AUTH_TYPE] = authType;
+ }
+
+ public short getUserSecureId() {
+ short offset = 0;
+ short length = Util.getShort(userSecureIds, offset);
+ offset += 2;
+ if (length == 0) {
+ return KMType.INVALID_VALUE;
+ }
+ short arrObj = KMArray.instance(length);
+ short index = 0;
+ short obj;
+ while (index < length) {
+ obj = KMInteger.instance(userSecureIds, (short) (offset + index * 8), (short) 8);
+ KMArray.cast(arrObj).add(index, obj);
+ index++;
+ }
+ return KMIntegerArrayTag.instance(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, arrObj);
+ }
+
+ public void setUserSecureId(short integerArrayPtr) {
+ short length = KMIntegerArrayTag.cast(integerArrayPtr).length();
+ if (length > MAX_SECURE_USER_IDS) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ Util.arrayFillNonAtomic(userSecureIds, (short) 0, USER_SECURE_IDS_SIZE, (byte) 0);
+ short index = 0;
+ short obj;
+ short offset = 0;
+ offset = Util.setShort(userSecureIds, offset, length);
+ while (index < length) {
+ obj = KMIntegerArrayTag.cast(integerArrayPtr).get(index);
+ Util.arrayCopyNonAtomic(
+ KMInteger.cast(obj).getBuffer(),
+ KMInteger.cast(obj).getStartOff(),
+ userSecureIds,
+ (short) (8 - KMInteger.cast(obj).length() + offset + 8 * index),
+ KMInteger.cast(obj).length());
+ index++;
+ }
+ }
+
+ public short getAlgorithm() {
+ return data[ALG];
+ }
+
+ public void setAlgorithm(short algorithm) {
+ data[ALG] = algorithm;
+ }
+
+ public short getPadding() {
+ return data[PADDING];
+ }
+
+ public void setPadding(short padding) {
+ data[PADDING] = padding;
+ }
+
+ public short getBlockMode() {
+ return data[BLOCK_MODE];
+ }
+
+ public void setBlockMode(short blockMode) {
+ data[BLOCK_MODE] = blockMode;
+ }
+
+ public short getDigest() {
+ return data[DIGEST];
+ }
+
+ public void setDigest(byte digest) {
+ data[DIGEST] = digest;
+ }
+
+ public short getMgfDigest() {
+ return data[MGF_DIGEST];
+ }
+
+ public void setMgfDigest(byte mgfDigest) {
+ data[MGF_DIGEST] = mgfDigest;
+ }
+
+ public boolean isAesGcmUpdateAllowed() {
+ return (data[FLAGS] & AES_GCM_UPDATE_ALLOWED) != 0;
+ }
+
+ public void setAesGcmUpdateComplete() {
+ data[FLAGS] = (byte) (data[FLAGS] & (~AES_GCM_UPDATE_ALLOWED));
+ }
+
+ public void setAesGcmUpdateStart() {
+ data[FLAGS] = (byte) (data[FLAGS] | AES_GCM_UPDATE_ALLOWED);
+ }
+
+ public short getMinMacLength() {
+ return data[MIN_MAC_LENGTH];
+ }
+
+ public void setMinMacLength(short length) {
+ data[MIN_MAC_LENGTH] = length;
+ }
+
+ public short getMacLength() {
+ return data[MAC_LENGTH];
+ }
+
+ public void setMacLength(short length) {
+ data[MAC_LENGTH] = length;
+ }
+
+ public byte getBufferingMode() {
+ short alg = getAlgorithm();
+ short purpose = getPurpose();
+ short digest = getDigest();
+ short padding = getPadding();
+ short blockMode = getBlockMode();
+
+ if (alg == KMType.RSA
+ && ((digest == KMType.DIGEST_NONE && purpose == KMType.SIGN)
+ || purpose == KMType.DECRYPT)) {
+ return KMType.BUF_RSA_DECRYPT_OR_NO_DIGEST;
+ }
+
+ if (alg == KMType.EC && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN) {
+ return KMType.BUF_EC_NO_DIGEST;
+ }
+
+ switch (alg) {
+ case KMType.AES:
+ if (purpose == KMType.ENCRYPT && padding == KMType.PKCS7) {
+ return KMType.BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGN;
+ } else if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) {
+ return KMType.BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGN;
+ } else if (purpose == KMType.DECRYPT && blockMode == KMType.GCM) {
+ return KMType.BUF_AES_GCM_DECRYPT_BLOCK_ALIGN;
+ }
+ break;
+ case KMType.DES:
+ if (purpose == KMType.ENCRYPT && padding == KMType.PKCS7) {
+ return KMType.BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGN;
+ } else if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) {
+ return KMType.BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGN;
+ }
+ }
+ return KMType.BUF_NONE;
+ }
+
+ public KMOperation getTrustedConfirmationSigner() {
+ return (KMOperation) operations[HMAC_SIGNER_OPERATION];
+ }
+
+ public void setTrustedConfirmationSigner(KMOperation hmacSignerOp) {
+ operations[HMAC_SIGNER_OPERATION] = hmacSignerOp;
+ }
+
+ public boolean isTrustedConfirmationRequired() {
+ return operations[HMAC_SIGNER_OPERATION] != null;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMRemotelyProvisionedComponentDevice.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMRemotelyProvisionedComponentDevice.java
new file mode 100644
index 0000000..8ba0e2f
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMRemotelyProvisionedComponentDevice.java
@@ -0,0 +1,1758 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import com.android.javacard.seprovider.KMKey;
+import com.android.javacard.seprovider.KMOperation;
+import com.android.javacard.seprovider.KMSEProvider;
+import javacard.framework.APDU;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * This class handles remote key provisioning. Generates an RKP key and generates a certificate
+ * signing request(CSR). The generation of CSR is divided among multiple functions to the save the
+ * memory inside the Applet. The set of functions to be called sequentially in the order to complete
+ * the process of generating the CSR are processBeginSendData, processUpdateKey,
+ * processUpdateEekChain, processUpdateChallenge, processFinishSendData, and getResponse.
+ * ProcessUpdateKey is called Ntimes, where N is the number of keys. Similarly, getResponse is
+ * called multiple times till the client receives the response completely.
+ */
+public class KMRemotelyProvisionedComponentDevice {
+
+ // Below are the device info labels
+ // The string "brand" in hex
+ public static final byte[] BRAND = {0x62, 0x72, 0x61, 0x6E, 0x64};
+ // The string "manufacturer" in hex
+ public static final byte[] MANUFACTURER = {
+ 0x6D, 0x61, 0x6E, 0x75, 0x66, 0x61, 0x63, 0x74, 0x75, 0x72, 0x65, 0x72
+ };
+ // The string "product" in hex
+ public static final byte[] PRODUCT = {0x70, 0x72, 0x6F, 0x64, 0x75, 0x63, 0x74};
+ // The string "model" in hex
+ public static final byte[] MODEL = {0x6D, 0x6F, 0x64, 0x65, 0x6C};
+ // // The string "device" in hex
+ public static final byte[] DEVICE = {0x64, 0x65, 0x76, 0x69, 0x63, 0x65};
+ // The string "vb_state" in hex
+ public static final byte[] VB_STATE = {0x76, 0x62, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x65};
+ // The string "bootloader_state" in hex.
+ public static final byte[] BOOTLOADER_STATE = {
+ 0x62, 0x6F, 0x6F, 0x74, 0x6C, 0x6F, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x65
+ };
+ // The string "vb_meta_digest" in hdex.
+ public static final byte[] VB_META_DIGEST = {
+ 0X76, 0X62, 0X6D, 0X65, 0X74, 0X61, 0X5F, 0X64, 0X69, 0X67, 0X65, 0X73, 0X74
+ };
+ // The string "os_version" in hex.
+ public static final byte[] OS_VERSION = {
+ 0x6F, 0x73, 0x5F, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E
+ };
+ // The string "system_patch_level" in hex.
+ public static final byte[] SYSTEM_PATCH_LEVEL = {
+ 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, 0x76,
+ 0x65, 0x6C
+ };
+ // The string "boot_patch_level" in hex.
+ public static final byte[] BOOT_PATCH_LEVEL = {
+ 0x62, 0x6F, 0x6F, 0x74, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, 0x76, 0x65, 0x6C
+ };
+ // The string "vendor_patch_level" in hex.
+ public static final byte[] VENDOR_PATCH_LEVEL = {
+ 0x76, 0x65, 0x6E, 0x64, 0x6F, 0x72, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, 0x76,
+ 0x65, 0x6C
+ };
+ // The string "version" in hex.
+ public static final byte[] DEVICE_INFO_VERSION = {0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E};
+ // The string "security_level" in hex.
+ public static final byte[] SECURITY_LEVEL = {
+ 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5F, 0x6C, 0x65, 0x76, 0x65, 0x6C
+ };
+ // The string "fused" in hex.
+ public static final byte[] FUSED = {0x66, 0x75, 0x73, 0x65, 0x64};
+ // Below are the Verified boot state values
+ // The string "green" in hex.
+ public static final byte[] VB_STATE_GREEN = {0x67, 0x72, 0x65, 0x65, 0x6E};
+ // The string "yellow" in hex.
+ public static final byte[] VB_STATE_YELLOW = {0x79, 0x65, 0x6C, 0x6C, 0x6F, 0x77};
+ // The string "orange" in hex.
+ public static final byte[] VB_STATE_ORANGE = {0x6F, 0x72, 0x61, 0x6E, 0x67, 0x65};
+ // The string "red" in hex.
+ public static final byte[] VB_STATE_RED = {0x72, 0x65, 0x64};
+ // Below are the boot loader state values
+ // The string "unlocked" in hex.
+ public static final byte[] UNLOCKED = {0x75, 0x6E, 0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64};
+ // The string "locked" in hex.
+ public static final byte[] LOCKED = {0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64};
+ // Device info CDDL schema version
+ public static final byte DI_SCHEMA_VERSION = 2;
+ // The string "strongbox" in hex.
+ public static final byte[] DI_SECURITY_LEVEL = {
+ 0x73, 0x74, 0x72, 0x6F, 0x6E, 0x67, 0x62, 0x6F, 0x78
+ };
+ // Represents each element size inside the data buffer. Each element has two entries
+ // 1) Length of the element and 2) offset of the element in the data buffer.
+ public static final byte DATA_INDEX_ENTRY_SIZE = 4;
+ // It is the offset, which represents the position where the element is present
+ // in the data buffer.
+ public static final byte DATA_INDEX_ENTRY_OFFSET = 2;
+ // Flag to denote TRUE
+ private static final byte TRUE = 0x01;
+ // Flag to denote FALSE
+ private static final byte FALSE = 0x00;
+ // RKP hardware info Version
+ private static final short RKP_VERSION = (short) 0x02;
+ // Below constants used to denote the type of the boot parameters. Note that these
+ // constants are only defined to be used internally.
+ private static final byte OS_VERSION_ID = 0x00;
+ private static final byte SYSTEM_PATCH_LEVEL_ID = 0x01;
+ private static final byte BOOT_PATCH_LEVEL_ID = 0x02;
+ private static final byte VENDOR_PATCH_LEVEL_ID = 0x03;
+ // Configurable flag to denote if Additional certificate chain is supported in the
+ // RKP server.
+ private static final boolean IS_ACC_SUPPORTED_IN_RKP_SERVER = false;
+ // The maximum possible output buffer.
+ private static final short MAX_SEND_DATA = 512;
+ // The string "Google Strongbox KeyMint 2" in hex.
+ private static final byte[] uniqueId = {
+ 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x53, 0x74, 0x72, 0x6f, 0x6e, 0x67, 0x62, 0x6f, 0x78,
+ 0x20, 0x4b, 0x65, 0x79, 0x4d, 0x69, 0x6e, 0x74, 0x20, 0x32
+ };
+ // Flag to denote more response is available to the clients.
+ private static final byte MORE_DATA = 0x01;
+ // Flag to denote no response is available to the clients.
+ private static final byte NO_DATA = 0x00;
+ // Below are the response processing states. As the protected data response is huge it is
+ // sent back incrementally and the clients are responsible to call get response multiple times
+ // based on MORE_DATA or NO_DATA flags. BCC - Boot Certificate Chain.
+ // ACC - Additional Certificate Chain.
+ private static final byte START_PROCESSING = 0x00;
+ private static final byte PROCESSING_BCC_IN_PROGRESS = 0x02;
+ private static final byte PROCESSING_BCC_COMPLETE = 0x04;
+ private static final byte PROCESSING_ACC_IN_PROGRESS = 0x08;
+ private static final byte PROCESSING_ACC_COMPLETE = 0x0A;
+ // The data table size.
+ private static final short DATA_SIZE = 512;
+ // Number of entries in the data table.
+ private static final byte DATA_INDEX_SIZE = 11;
+ // Below are the data table offsets.
+ private static final byte EPHEMERAL_MAC_KEY = 0;
+ private static final byte TOTAL_KEYS_TO_SIGN = 1;
+ private static final byte KEYS_TO_SIGN_COUNT = 2;
+ private static final byte TEST_MODE = 3;
+ private static final byte EEK_KEY = 4;
+ private static final byte EEK_KEY_ID = 5;
+ private static final byte CHALLENGE = 6;
+ private static final byte GENERATE_CSR_PHASE = 7;
+ private static final byte EPHEMERAL_PUB_KEY = 8;
+ private static final byte RESPONSE_PROCESSING_STATE = 9;
+ private static final byte ACC_PROCESSED_LENGTH = 10;
+
+ // Below are some of the sizes defined in the data table.
+ // The size of the Ephemeral Mac key used to sign the rkp public key.
+ private static final byte EPHEMERAL_MAC_KEY_SIZE = 32;
+ // The size of short types.
+ private static final byte SHORT_SIZE = 2;
+ // The size of byte types
+ private static final byte BYTE_SIZE = 1;
+ // The size of the test mode flag.
+ private static final byte TEST_MODE_SIZE = 1;
+ // Below are the different processing stages for generateCSR.
+ // BEGIN - It is the initial stage where the process is initialized and construction of
+ // MacedPublickeys are initiated.
+ // UPDATE - Challenge, EEK and RKP keys are sent to the applet for further process.
+ // FINISH - MacedPublicKeys are constructed and construction of protected data is initiated.
+ // GET_RESPONSE - Called multiple times by the client till all the protected data is received.
+ private static final byte BEGIN = 0x01;
+ private static final byte UPDATE = 0x02;
+ private static final byte FINISH = 0x04;
+ private static final byte GET_RESPONSE = 0x06;
+
+ // RKP mac key size
+ private static final byte RKP_MAC_KEY_SIZE = 32;
+ // This holds the Google ECDSA P256 root key for the Endpoint Encryption Key.
+ public static Object[] authorizedEekRoots;
+ // Used to hold the temporary results.
+ public short[] rkpTmpVariables;
+ // Data table to hold the entries at the initial stages of generateCSR and which are used
+ // at later stages to construct the response data.
+ private byte[] data;
+ // Instance of the CBOR encoder.
+ private KMEncoder encoder;
+ // Instance of the CBOR decoder.
+ private KMDecoder decoder;
+ // Instance of the KMRepository for memory management.
+ private KMRepository repository;
+ // Instance of the provider for cyrpto operations.
+ private KMSEProvider seProvider;
+ // Instance of the KMKeymintDataStore to save or retrieve the data.
+ private KMKeymintDataStore storeDataInst;
+ // Holds the KMOperation instance. This is used to do multi part update operations.
+ private Object[] operation;
+ // Holds the current index in the data table.
+ private short[] dataIndex;
+
+ public KMRemotelyProvisionedComponentDevice(
+ KMEncoder encoder,
+ KMDecoder decoder,
+ KMRepository repository,
+ KMSEProvider seProvider,
+ KMKeymintDataStore storeDInst) {
+ this.encoder = encoder;
+ this.decoder = decoder;
+ this.repository = repository;
+ this.seProvider = seProvider;
+ this.storeDataInst = storeDInst;
+ rkpTmpVariables = JCSystem.makeTransientShortArray((short) 32, JCSystem.CLEAR_ON_RESET);
+ data = JCSystem.makeTransientByteArray(DATA_SIZE, JCSystem.CLEAR_ON_RESET);
+ operation = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET);
+ dataIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET);
+ // Initialize RKP mac key
+ if (!seProvider.isUpgrading()) {
+ short offset = repository.allocReclaimableMemory((short) RKP_MAC_KEY_SIZE);
+ byte[] buffer = repository.getHeap();
+ seProvider.getTrueRandomNumber(buffer, offset, RKP_MAC_KEY_SIZE);
+ storeDataInst.createRkpMacKey(buffer, offset, RKP_MAC_KEY_SIZE);
+ repository.reclaimMemory(RKP_MAC_KEY_SIZE);
+ }
+ operation[0] = null;
+ createAuthorizedEEKRoot();
+ }
+
+ private void createAuthorizedEEKRoot() {
+ if (authorizedEekRoots == null) {
+ authorizedEekRoots =
+ new Object[] {
+ new byte[] {
+ (byte) 0x04, (byte) 0xf7, (byte) 0x14, (byte) 0x8a, (byte) 0xdb, (byte) 0x97,
+ (byte) 0xf4, (byte) 0xcc, (byte) 0x53, (byte) 0xef, (byte) 0xd2, (byte) 0x64,
+ (byte) 0x11, (byte) 0xc4, (byte) 0xe3, (byte) 0x75, (byte) 0x1f, (byte) 0x66,
+ (byte) 0x1f, (byte) 0xa4, (byte) 0x71, (byte) 0x0c, (byte) 0x6c, (byte) 0xcf,
+ (byte) 0xfa, (byte) 0x09, (byte) 0x46, (byte) 0x80, (byte) 0x74, (byte) 0x87,
+ (byte) 0x54, (byte) 0xf2, (byte) 0xad, (byte) 0x5e, (byte) 0x7f, (byte) 0x5b,
+ (byte) 0xf6, (byte) 0xec, (byte) 0xe4, (byte) 0xf6, (byte) 0x19, (byte) 0xcc,
+ (byte) 0xff, (byte) 0x13, (byte) 0x37, (byte) 0xfd, (byte) 0x0f, (byte) 0xa1,
+ (byte) 0xc8, (byte) 0x93, (byte) 0xdb, (byte) 0x18, (byte) 0x06, (byte) 0x76,
+ (byte) 0xc4, (byte) 0x5d, (byte) 0xe6, (byte) 0xd7, (byte) 0x6a, (byte) 0x77,
+ (byte) 0x86, (byte) 0xc3, (byte) 0x2d, (byte) 0xaf, (byte) 0x8f
+ },
+ };
+ }
+ }
+
+ private void initializeDataTable() {
+ clearDataTable();
+ releaseOperation();
+ dataIndex[0] = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE);
+ }
+
+ private short dataAlloc(short length) {
+ if ((short) (dataIndex[0] + length) > (short) data.length) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ dataIndex[0] += length;
+ return (short) (dataIndex[0] - length);
+ }
+
+ private void clearDataTable() {
+ Util.arrayFillNonAtomic(data, (short) 0, (short) data.length, (byte) 0x00);
+ dataIndex[0] = 0x00;
+ }
+
+ private void releaseOperation() {
+ if (operation[0] != null) {
+ ((KMOperation) operation[0]).abort();
+ operation[0] = null;
+ }
+ }
+
+ private short createEntry(short index, short length) {
+ index = (short) (index * DATA_INDEX_ENTRY_SIZE);
+ short ptr = dataAlloc(length);
+ Util.setShort(data, index, length);
+ Util.setShort(data, (short) (index + DATA_INDEX_ENTRY_OFFSET), ptr);
+ return ptr;
+ }
+
+ private short getEntry(short index) {
+ index = (short) (index * DATA_INDEX_ENTRY_SIZE);
+ return Util.getShort(data, (short) (index + DATA_INDEX_ENTRY_OFFSET));
+ }
+
+ private short getEntryLength(short index) {
+ index = (short) (index * DATA_INDEX_ENTRY_SIZE);
+ return Util.getShort(data, index);
+ }
+
+ private void processGetRkpHwInfoCmd(APDU apdu) {
+ // Make the response
+ // Author name - Google.
+ short respPtr = KMArray.instance((short) 5);
+ KMArray resp = KMArray.cast(respPtr);
+ resp.add((short) 0, KMInteger.uint_16(KMError.OK));
+ resp.add((short) 1, KMInteger.uint_16(RKP_VERSION));
+ resp.add(
+ (short) 2,
+ KMByteBlob.instance(
+ KMKeymasterApplet.Google, (short) 0, (short) KMKeymasterApplet.Google.length));
+ resp.add((short) 3, KMInteger.uint_8(KMType.RKP_CURVE_P256));
+ resp.add((short) 4, KMByteBlob.instance(uniqueId, (short) 0, (short) uniqueId.length));
+ KMKeymasterApplet.sendOutgoing(apdu, respPtr);
+ }
+
+ /**
+ * This function generates an EC key pair with attest key as purpose and creates an encrypted key
+ * blob. It then generates a COSEMac message which includes the ECDSA public key.
+ */
+ public void processGenerateRkpKey(APDU apdu) {
+ short arr = KMArray.instance((short) 1);
+ KMArray.cast(arr).add((short) 0, KMSimpleValue.exp());
+ arr = KMKeymasterApplet.receiveIncoming(apdu, arr);
+ // Re-purpose the apdu buffer as scratch pad.
+ byte[] scratchPad = apdu.getBuffer();
+ // test mode flag.
+ boolean testMode =
+ (KMSimpleValue.TRUE == KMSimpleValue.cast(KMArray.cast(arr).get((short) 0)).getValue());
+ KMKeymasterApplet.generateRkpKey(scratchPad, getEcAttestKeyParameters());
+ short pubKey = KMKeymasterApplet.getPubKey();
+ short coseMac0 = constructCoseMacForRkpKey(testMode, scratchPad, pubKey);
+ // Encode the COSE_MAC0 object
+ arr = KMArray.instance((short) 3);
+ KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(arr).add((short) 1, coseMac0);
+ KMArray.cast(arr).add((short) 2, KMKeymasterApplet.getPivateKey());
+ KMKeymasterApplet.sendOutgoing(apdu, arr);
+ }
+
+ /**
+ * This is the first command of the generateCSR.
+ * Input:
+ * 1) Number of RKP keys.
+ * 2) Total length of the encoded CoseKeys (Each RKP key is represented in CoseKey)
+ * 3) Flag which represents Test mode or Production mode.
+ * Process:
+ * 1) Generate Ephemeral mac key and store in the temporary data buffer. This key is
+ * used to sign Mac_Structure, which contains array of encoded RKP keys,
+ * 2) Initialize the HMAC operation with the ephemeral mac key and do partial sign of the
+ * Mac_Structure with the input initial data received. A Multipart update on HMAC is
+ * called on each updateKey command in the second stage.
+ * 3) Store the number of RKP keys and the test mode flag in the temporary data buffer.
+ * 4) Update the phase of the generateCSR function to BEGIN.
+ * Response:
+ * Send OK response.
+ *
+ * @param apdu Input apdu
+ */
+ public void processBeginSendData(APDU apdu) throws Exception {
+ try {
+ initializeDataTable();
+ short arr = KMArray.instance((short) 3);
+ KMArray.cast(arr).add((short) 0, KMInteger.exp()); // Array length
+ KMArray.cast(arr).add((short) 1, KMInteger.exp()); // Total length of the encoded CoseKeys.
+ KMArray.cast(arr).add((short) 2, KMSimpleValue.exp());
+ arr = KMKeymasterApplet.receiveIncoming(apdu, arr);
+ // Re-purpose the apdu buffer as scratch pad.
+ byte[] scratchPad = apdu.getBuffer();
+ // Generate ephemeral mac key.
+ short dataEntryIndex = createEntry(EPHEMERAL_MAC_KEY, EPHEMERAL_MAC_KEY_SIZE);
+ seProvider.newRandomNumber(data, dataEntryIndex, EPHEMERAL_MAC_KEY_SIZE);
+ // Initialize hmac operation.
+ initHmacOperation();
+ // Partially encode CoseMac structure with partial payload.
+ constructPartialPubKeysToSignMac(
+ scratchPad,
+ KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort(),
+ KMInteger.cast(KMArray.cast(arr).get((short) 1)).getShort());
+ // Store the total keys in data table.
+ dataEntryIndex = createEntry(TOTAL_KEYS_TO_SIGN, SHORT_SIZE);
+ Util.setShort(
+ data, dataEntryIndex, KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort());
+ // Store the test mode value in data table.
+ dataEntryIndex = createEntry(TEST_MODE, TEST_MODE_SIZE);
+ data[dataEntryIndex] =
+ (KMSimpleValue.TRUE == KMSimpleValue.cast(KMArray.cast(arr).get((short) 2)).getValue())
+ ? TRUE
+ : FALSE;
+ // Store the current csr status, which is BEGIN.
+ createEntry(GENERATE_CSR_PHASE, BYTE_SIZE);
+ updateState(BEGIN);
+ // Send response.
+ KMKeymasterApplet.sendResponse(apdu, KMError.OK);
+ } catch (Exception e) {
+ clearDataTable();
+ releaseOperation();
+ throw e;
+ }
+ }
+
+ /**
+ * This is the second command of the generateCSR.
+ * Input:
+ * CoseMac0 containing the RKP Key
+ * Process:
+ * 1) Validate the phase of generateCSR. Prior state should be either BEGIN or UPDATE.
+ * 2) Validate the number of RKP Keys received against the value received in first command.
+ * 3) Validate the CoseMac0 structure and extract the RKP Key.
+ * 4) Do Multipart HMAC update operation with the input as RKP key.
+ * 5) Update the number of keys received count into the data buffer.
+ * 6) Update the phase of the generateCSR function to UPDATE.
+ * Response:
+ * Send OK response.
+ * @param apdu Input apdu
+ */
+ public void processUpdateKey(APDU apdu) throws Exception {
+ try {
+ // The prior state can be BEGIN or UPDATE
+ validateState((byte) (BEGIN | UPDATE));
+ validateKeysToSignCount();
+ short headers = KMCoseHeaders.exp();
+ short arrInst = KMArray.instance((short) 4);
+ KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp());
+ KMArray.cast(arrInst).add((short) 1, headers);
+ KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp());
+ KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp());
+ short arr = KMArray.exp(arrInst);
+ arr = KMKeymasterApplet.receiveIncoming(apdu, arr);
+ arrInst = KMArray.cast(arr).get((short) 0);
+ // Re-purpose the apdu buffer as scratch pad.
+ byte[] scratchPad = apdu.getBuffer();
+
+ // Validate and extract the CoseKey from CoseMac0 message.
+ short coseKey = validateAndExtractPublicKey(arrInst, scratchPad);
+ // Encode CoseKey
+ short length =
+ KMKeymasterApplet.encodeToApduBuffer(
+ coseKey, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ // Do Hmac update with input as encoded CoseKey.
+ ((KMOperation) operation[0]).update(scratchPad, (short) 0, length);
+ // Increment the count each time this function gets executed.
+ // Store the count in data table.
+ short dataEntryIndex = getEntry(KEYS_TO_SIGN_COUNT);
+ if (dataEntryIndex == 0) {
+ dataEntryIndex = createEntry(KEYS_TO_SIGN_COUNT, SHORT_SIZE);
+ }
+ length = Util.getShort(data, dataEntryIndex);
+ Util.setShort(data, dataEntryIndex, ++length);
+ // Update the csr state
+ updateState(UPDATE);
+ // Send response.
+ KMKeymasterApplet.sendResponse(apdu, KMError.OK);
+ } catch (Exception e) {
+ clearDataTable();
+ releaseOperation();
+ throw e;
+ }
+ }
+
+ /**
+ * This is the third command of generateCSR.
+ * Input:
+ * EEK chain ordered from Root to Leaf in CoseSign1 format.
+ * Process:
+ * 1) Validate the phase of generateCSR. Prior state should be UPDATE.
+ * 2) Validate the EEK chain and extract the Leaf EEK
+ * 3) Retrieve the EEK Id and Public key of Leaf EEK and store in the data buffer.
+ * 4) Update the phase of the generateCSR function to UPDATE.
+ * Response:
+ * Send OK response.
+ * @param apdu Input apdu.
+ */
+ public void processUpdateEekChain(APDU apdu) throws Exception {
+ try {
+ // The prior state can be BEGIN or UPDATE
+ validateState((byte) (BEGIN | UPDATE));
+ short headers = KMCoseHeaders.exp();
+ short arrInst = KMArray.instance((short) 4);
+ KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp());
+ KMArray.cast(arrInst).add((short) 1, headers);
+ KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp());
+ KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp());
+ short arrSignPtr = KMArray.exp(arrInst);
+ arrInst = KMKeymasterApplet.receiveIncoming(apdu, arrSignPtr);
+ if (KMArray.cast(arrInst).length() == 0) {
+ KMException.throwIt(KMError.STATUS_INVALID_EEK);
+ }
+ // Re-purpose the apdu buffer as scratch pad.
+ byte[] scratchPad = apdu.getBuffer();
+ // Validate eek chain.
+ short eekKey = validateAndExtractEekPub(arrInst, scratchPad);
+ // Store eek public key and eek id in the data table.
+ short eekKeyId = KMCoseKey.cast(eekKey).getKeyIdentifier();
+ short dataEntryIndex = createEntry(EEK_KEY_ID, KMByteBlob.cast(eekKeyId).length());
+ Util.arrayCopyNonAtomic(
+ KMByteBlob.cast(eekKeyId).getBuffer(),
+ KMByteBlob.cast(eekKeyId).getStartOff(),
+ data,
+ dataEntryIndex,
+ KMByteBlob.cast(eekKeyId).length());
+ // Convert the coseKey to a public key.
+ short len = KMCoseKey.cast(eekKey).getEcdsa256PublicKey(scratchPad, (short) 0);
+ dataEntryIndex = createEntry(EEK_KEY, len);
+ Util.arrayCopyNonAtomic(scratchPad, (short) 0, data, dataEntryIndex, len);
+ // Update the state
+ updateState(UPDATE);
+ KMKeymasterApplet.sendResponse(apdu, KMError.OK);
+ } catch (Exception e) {
+ clearDataTable();
+ releaseOperation();
+ throw e;
+ }
+ }
+
+ /**
+ * This is the fourth command of generateCSR.
+ * Input:
+ * Challenge
+ * Process:
+ * 1) Validate the phase of generateCSR. Prior state should be either UPDATE or BEGIN.
+ * 2) Store the challenge in the data buffer.
+ * 3) Update the phase of the generateCSR function to UPDATE.
+ * Response:
+ * Send OK response.
+ * @param apdu Input apdu.
+ */
+ public void processUpdateChallenge(APDU apdu) throws Exception {
+ try {
+ // The prior state can be BEGIN or UPDATE
+ validateState((byte) (BEGIN | UPDATE));
+ short arr = KMArray.instance((short) 1);
+ KMArray.cast(arr).add((short) 0, KMByteBlob.exp());
+ arr = KMKeymasterApplet.receiveIncoming(apdu, arr);
+ // Store the challenge in the data table.
+ short challenge = KMArray.cast(arr).get((short) 0);
+ short challengeLen = KMByteBlob.cast(challenge).length();
+ if (challengeLen > 64) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ short dataEntryIndex = createEntry(CHALLENGE, challengeLen);
+ Util.arrayCopyNonAtomic(
+ KMByteBlob.cast(challenge).getBuffer(),
+ KMByteBlob.cast(challenge).getStartOff(),
+ data,
+ dataEntryIndex,
+ challengeLen);
+ // Update the state
+ updateState(UPDATE);
+ KMKeymasterApplet.sendResponse(apdu, KMError.OK);
+ } catch (Exception e) {
+ clearDataTable();
+ releaseOperation();
+ throw e;
+ }
+ }
+
+ /**
+ * This is the fifth command of generateCSR.
+ * Input:
+ * No input data.
+ * Process:
+ * 1) Validate the phase of generateCSR. Prior state should be UPDATE.
+ * 2) Check if all the RKP keys are received if not throw exception.
+ * 3) Finalize the HMAC operation and get the signed Mac_Structure which is called as
+ * pubKeysToSignMac.
+ * 4) Start constructing the partial protected data. Create a random
+ * nonce, initialize the AESGCM operation with the session key derived from
+ * HKDF(ECDH(EPHEMERAL_EC_KEY, EEK_KEY), KdfContext)
+ * 5) Construct Encrypt_Structure which acts as AAD for AES-GCM operation.
+ * 6) The payload for the Protected Data is [SignedMac, BCC, ACC]. Construct partial
+ * SignedMac structure.
+ * 7) Note that the HAL has to construct the CoseEncrypt structure by collecting all
+ * the pieces returned from Applet.
+ * Response:
+ * OK
+ * pubKeysToSignMac - Containes the maced RKP public keys.
+ * deviceInfo - CBOR encoded device info
+ * protectedHeader - CoseEncrypt protected header
+ * unProtectedHeader - CoseEncrypt unprotected header.
+ * ParitalCipherText - partial encrypted payload of CoseEncrypt structure.
+ * Flag to represent there is more data to retrieve.
+ * @param apdu Input apdu.
+ */
+ public void processFinishSendData(APDU apdu) throws Exception {
+ try {
+ // The prior state should be UPDATE.
+ validateState(UPDATE);
+ byte[] scratchPad = apdu.getBuffer();
+ if (data[getEntry(TOTAL_KEYS_TO_SIGN)] != data[getEntry(KEYS_TO_SIGN_COUNT)]) {
+ // Mismatch in the number of keys sent.
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ // PubKeysToSignMac
+ short empty = repository.alloc((short) 0);
+ short len =
+ ((KMOperation) operation[0])
+ .sign(repository.getHeap(), (short) empty, (short) 0, scratchPad, (short) 0);
+ // release operation
+ releaseOperation();
+ short pubKeysToSignMac = KMByteBlob.instance(scratchPad, (short) 0, len);
+ // Create DeviceInfo
+ short deviceInfo = createDeviceInfo(scratchPad);
+ // Generate Nonce for AES-GCM
+ seProvider.newRandomNumber(scratchPad, (short) 0, KMKeymasterApplet.AES_GCM_NONCE_LENGTH);
+ short nonce =
+ KMByteBlob.instance(scratchPad, (short) 0, KMKeymasterApplet.AES_GCM_NONCE_LENGTH);
+ // Initializes cipher instance.
+ initAesGcmOperation(scratchPad, nonce);
+ // Encode Enc_Structure as additional data for AES-GCM.
+ processAesGcmUpdateAad(scratchPad);
+ short partialPayloadLen = processSignedMac(scratchPad, pubKeysToSignMac, deviceInfo);
+ short partialCipherText = KMByteBlob.instance(scratchPad, (short) 0, partialPayloadLen);
+ short coseEncryptProtectedHeader = getCoseEncryptProtectedHeader(scratchPad);
+ short coseEncryptUnProtectedHeader = getCoseEncryptUnprotectedHeader(scratchPad, nonce);
+ len =
+ KMKeymasterApplet.encodeToApduBuffer(
+ deviceInfo, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ short encodedDeviceInfo = KMByteBlob.instance(scratchPad, (short) 0, len);
+ updateState(FINISH);
+ short arr = KMArray.instance((short) 7);
+ KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(arr).add((short) 1, pubKeysToSignMac);
+ KMArray.cast(arr).add((short) 2, encodedDeviceInfo);
+ KMArray.cast(arr).add((short) 3, coseEncryptProtectedHeader);
+ KMArray.cast(arr).add((short) 4, coseEncryptUnProtectedHeader);
+ KMArray.cast(arr).add((short) 5, partialCipherText);
+ KMArray.cast(arr).add((short) 6, KMInteger.uint_8(MORE_DATA));
+ KMKeymasterApplet.sendOutgoing(apdu, arr);
+ } catch (Exception e) {
+ clearDataTable();
+ releaseOperation();
+ throw e;
+ }
+ }
+
+ /**
+ * This is the sixth and the last command of generateCSR. This command is called multiple
+ * times by the HAL until all the cipher data and receipient structure is received.
+ * Input:
+ * No input data.
+ * Process:
+ * First the BootCertificateChain is processed: Encrypt the boot certificate chain and return
+ * the BCC. Mark the state as PROCESSING_BCC_COMPLETE.
+ * Next the AdditionalCertificateChain is processed: Incrementally encrypt ACC and send back
+ * a chunks of data. Each chunk is 512 bytes. if the processing of ACC is still in progress
+ * mark the state as PROCESSING_ACC_IN_PROGRESS otherwise mark the state as
+ * PROCESSING_ACC_COMPLETE.
+ * Finally construct and return the CoseReceipient structure.
+ * Response:
+ * OK
+ * cipher text: It can be either encrypted bcc or encrypted acc
+ * Receipient structure: This will be empty will returning the encrypted acc or bcc.
+ * Flag to represent there is more data to retrieve.
+ * @param apdu Input apdu.
+ */
+ public void processGetResponse(APDU apdu) throws Exception {
+ try {
+ // The prior state should be FINISH.
+ validateState((byte) (FINISH | GET_RESPONSE));
+ byte[] scratchPad = apdu.getBuffer();
+ short len = 0;
+ short recipientStructure = KMArray.instance((short) 0);
+ byte moreData = MORE_DATA;
+ byte state = getCurrentOutputProcessingState();
+ switch (state) {
+ case START_PROCESSING:
+ case PROCESSING_BCC_IN_PROGRESS:
+ len = processBcc(scratchPad);
+ updateState(GET_RESPONSE);
+ break;
+ case PROCESSING_BCC_COMPLETE:
+ case PROCESSING_ACC_IN_PROGRESS:
+ len = processAdditionalCertificateChain(scratchPad);
+ updateState(GET_RESPONSE);
+ break;
+ case PROCESSING_ACC_COMPLETE:
+ recipientStructure = processRecipientStructure(scratchPad);
+ len = processFinalData(scratchPad);
+ moreData = NO_DATA;
+ releaseOperation();
+ clearDataTable();
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_STATE);
+ }
+ short data = KMByteBlob.instance(scratchPad, (short) 0, len);
+ short arr = KMArray.instance((short) 4);
+ KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(arr).add((short) 1, data);
+ KMArray.cast(arr).add((short) 2, recipientStructure);
+ // represents there is more output to retrieve
+ KMArray.cast(arr).add((short) 3, KMInteger.uint_8(moreData));
+ KMKeymasterApplet.sendOutgoing(apdu, arr);
+ } catch (Exception e) {
+ clearDataTable();
+ releaseOperation();
+ throw e;
+ }
+ }
+
+ public void process(short ins, APDU apdu) throws Exception {
+ switch (ins) {
+ case KMKeymasterApplet.INS_GET_RKP_HARDWARE_INFO:
+ processGetRkpHwInfoCmd(apdu);
+ break;
+ case KMKeymasterApplet.INS_GENERATE_RKP_KEY_CMD:
+ processGenerateRkpKey(apdu);
+ break;
+ case KMKeymasterApplet.INS_BEGIN_SEND_DATA_CMD:
+ processBeginSendData(apdu);
+ break;
+ case KMKeymasterApplet.INS_UPDATE_KEY_CMD:
+ processUpdateKey(apdu);
+ break;
+ case KMKeymasterApplet.INS_UPDATE_EEK_CHAIN_CMD:
+ processUpdateEekChain(apdu);
+ break;
+ case KMKeymasterApplet.INS_UPDATE_CHALLENGE_CMD:
+ processUpdateChallenge(apdu);
+ break;
+ case KMKeymasterApplet.INS_FINISH_SEND_DATA_CMD:
+ processFinishSendData(apdu);
+ break;
+ case KMKeymasterApplet.INS_GET_RESPONSE_CMD:
+ processGetResponse(apdu);
+ break;
+ default:
+ ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
+ }
+ }
+
+ private boolean isAdditionalCertificateChainPresent() {
+ if (!IS_ACC_SUPPORTED_IN_RKP_SERVER || (TRUE == data[getEntry(TEST_MODE)])) {
+ // Don't include AdditionalCertificateChain in ProtectedData if either
+ // 1. RKP server does not support processing of X.509 Additional Certificate Chain.
+ // 2. Requested CSR for test mode.
+ return false;
+ }
+ return (storeDataInst.getAdditionalCertChainLength() == 0 ? false : true);
+ }
+
+ private short processFinalData(byte[] scratchPad) {
+ // Call finish on AES GCM Cipher
+ short empty = repository.alloc((short) 0);
+ short len =
+ ((KMOperation) operation[0])
+ .finish(repository.getHeap(), (short) empty, (short) 0, scratchPad, (short) 0);
+ return len;
+ }
+
+ private byte getCurrentOutputProcessingState() {
+ short index = getEntry(RESPONSE_PROCESSING_STATE);
+ if (index == 0) {
+ return START_PROCESSING;
+ }
+ return data[index];
+ }
+
+ private void updateOutputProcessingState(byte state) {
+ short dataEntryIndex = getEntry(RESPONSE_PROCESSING_STATE);
+ data[dataEntryIndex] = state;
+ }
+
+ /**
+ * Validates the CoseMac message and extracts the CoseKey from it.
+ *
+ * @param coseMacPtr CoseMac instance to be validated.
+ * @param scratchPad Scratch buffer used to store temp results.
+ * @return CoseKey instance.
+ */
+ private short validateAndExtractPublicKey(short coseMacPtr, byte[] scratchPad) {
+ boolean testMode = (TRUE == data[getEntry(TEST_MODE)]) ? true : false;
+ // Exp for KMCoseHeaders
+ short coseHeadersExp = KMCoseHeaders.exp();
+ // Exp for coseky
+ short coseKeyExp = KMCoseKey.exp();
+
+ // validate protected Headers
+ short ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET);
+ ptr =
+ decoder.decode(
+ coseHeadersExp,
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length());
+
+ if (!KMCoseHeaders.cast(ptr)
+ .isDataValid(rkpTmpVariables, KMCose.COSE_ALG_HMAC_256, KMType.INVALID_VALUE)) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+
+ // Validate payload.
+ ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET);
+ ptr =
+ decoder.decode(
+ coseKeyExp,
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length());
+
+ if (!KMCoseKey.cast(ptr)
+ .isDataValid(
+ rkpTmpVariables,
+ KMCose.COSE_KEY_TYPE_EC2,
+ KMType.INVALID_VALUE,
+ KMCose.COSE_ALG_ES256,
+ KMCose.COSE_ECCURVE_256)) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+
+ boolean isTestKey = KMCoseKey.cast(ptr).isTestKey();
+ if (isTestKey && !testMode) {
+ KMException.throwIt(KMError.STATUS_TEST_KEY_IN_PRODUCTION_REQUEST);
+ } else if (!isTestKey && testMode) {
+ KMException.throwIt(KMError.STATUS_PRODUCTION_KEY_IN_TEST_REQUEST);
+ }
+
+ // Compute CoseMac Structure and compare the macs.
+ short macStructure =
+ KMCose.constructCoseMacStructure(
+ KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET),
+ KMByteBlob.instance((short) 0),
+ KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET));
+ short encodedLen =
+ KMKeymasterApplet.encodeToApduBuffer(
+ macStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+
+ short hmacLen =
+ rkpHmacSign(testMode, scratchPad, (short) 0, encodedLen, scratchPad, encodedLen);
+
+ if (hmacLen
+ != KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).length()) {
+ KMException.throwIt(KMError.STATUS_INVALID_MAC);
+ }
+
+ if (0
+ != Util.arrayCompare(
+ scratchPad,
+ encodedLen,
+ KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getBuffer(),
+ KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET))
+ .getStartOff(),
+ hmacLen)) {
+ KMException.throwIt(KMError.STATUS_INVALID_MAC);
+ }
+ return ptr;
+ }
+
+ /**
+ * This function validates the EEK Chain and extracts the leaf public key, which is used to
+ * generate shared secret using ECDH.
+ *
+ * @param eekArr EEK cert chain array pointer.
+ * @param scratchPad Scratch buffer used to store temp results.
+ * @return CoseKey instance.
+ */
+ private short validateAndExtractEekPub(short eekArr, byte[] scratchPad) {
+ short leafPubKey = 0;
+ try {
+ leafPubKey =
+ KMKeymasterApplet.validateCertChain(
+ (TRUE == data[getEntry(TEST_MODE)]) ? false : true, // validate EEK root
+ KMCose.COSE_ALG_ES256,
+ KMCose.COSE_ALG_ECDH_ES_HKDF_256,
+ eekArr,
+ scratchPad,
+ authorizedEekRoots);
+ } catch (KMException e) {
+ KMException.throwIt(KMError.STATUS_INVALID_EEK);
+ }
+ return leafPubKey;
+ }
+
+ private void validateKeysToSignCount() {
+ short index = getEntry(KEYS_TO_SIGN_COUNT);
+ short keysToSignCount = 0;
+ if (index != 0) {
+ keysToSignCount = Util.getShort(data, index);
+ }
+ if (Util.getShort(data, getEntry(TOTAL_KEYS_TO_SIGN)) <= keysToSignCount) {
+ // Mismatch in the number of keys sent.
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ }
+
+ private void validateState(byte expectedState) {
+ short dataEntryIndex = getEntry(GENERATE_CSR_PHASE);
+ if (0 == (data[dataEntryIndex] & expectedState)) {
+ KMException.throwIt(KMError.INVALID_STATE);
+ }
+ }
+
+ private void updateState(byte state) {
+ short dataEntryIndex = getEntry(GENERATE_CSR_PHASE);
+ if (dataEntryIndex == 0) {
+ KMException.throwIt(KMError.INVALID_STATE);
+ }
+ data[dataEntryIndex] = state;
+ }
+
+ /**
+ * This function constructs a Mac Structure, encode it and signs the encoded buffer with the
+ * ephemeral mac key.
+ */
+ private void constructPartialPubKeysToSignMac(
+ byte[] scratchPad, short arrayLength, short encodedCoseKeysLen) {
+ short ptr;
+ short len;
+ short headerPtr =
+ KMCose.constructHeaders(
+ rkpTmpVariables,
+ KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256),
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE);
+ // Encode the protected header as byte blob.
+ len =
+ KMKeymasterApplet.encodeToApduBuffer(
+ headerPtr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ short protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, len);
+ // create MAC_Structure
+ ptr =
+ KMCose.constructCoseMacStructure(
+ protectedHeader, KMByteBlob.instance((short) 0), KMType.INVALID_VALUE);
+ // Encode the Mac_structure and do HMAC_Sign to produce the tag for COSE_MAC0
+ len =
+ KMKeymasterApplet.encodeToApduBuffer(
+ ptr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ // Construct partial payload - Bstr Header + Array Header
+ // The maximum combined length of bstr header and array header length is 6 bytes.
+ // The lengths will never exceed Max SHORT value.
+ short arrPtr = KMArray.instance(arrayLength);
+ for (short i = 0; i < arrayLength; i++) {
+ KMArray.cast(arrPtr).add(i, KMType.INVALID_VALUE);
+ }
+ arrayLength = encoder.getEncodedLength(arrPtr);
+ short bufIndex = repository.alloc((short) 6);
+ short partialPayloadLen =
+ encoder.encodeByteBlobHeader(
+ (short) (arrayLength + encodedCoseKeysLen), repository.getHeap(), bufIndex, (short) 3);
+
+ partialPayloadLen +=
+ encoder.encode(
+ arrPtr,
+ repository.getHeap(),
+ (short) (bufIndex + partialPayloadLen),
+ repository.getHeapReclaimIndex());
+ Util.arrayCopyNonAtomic(repository.getHeap(), bufIndex, scratchPad, len, partialPayloadLen);
+ ((KMOperation) operation[0]).update(scratchPad, (short) 0, (short) (len + partialPayloadLen));
+ }
+
+ private short createSignedMac(
+ KMKey deviceUniqueKeyPair, byte[] scratchPad, short deviceMapPtr, short pubKeysToSign) {
+ // Challenge
+ short dataEntryIndex = getEntry(CHALLENGE);
+ short challengePtr = KMByteBlob.instance(data, dataEntryIndex, getEntryLength(CHALLENGE));
+ // Ephemeral mac key
+ dataEntryIndex = getEntry(EPHEMERAL_MAC_KEY);
+ short ephmeralMacKey =
+ KMByteBlob.instance(data, dataEntryIndex, getEntryLength(EPHEMERAL_MAC_KEY));
+
+ /* Prepare AAD */
+ short aad = KMArray.instance((short) 3);
+ KMArray.cast(aad).add((short) 0, challengePtr);
+ KMArray.cast(aad).add((short) 1, deviceMapPtr);
+ KMArray.cast(aad).add((short) 2, pubKeysToSign);
+ aad =
+ KMKeymasterApplet.encodeToApduBuffer(
+ aad, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ aad = KMByteBlob.instance(scratchPad, (short) 0, aad);
+
+ /* construct protected header */
+ short protectedHeaders =
+ KMCose.constructHeaders(
+ rkpTmpVariables,
+ KMNInteger.uint_8(KMCose.COSE_ALG_ES256),
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE);
+ protectedHeaders =
+ KMKeymasterApplet.encodeToApduBuffer(
+ protectedHeaders, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ protectedHeaders = KMByteBlob.instance(scratchPad, (short) 0, protectedHeaders);
+
+ /* construct cose sign structure */
+ short signStructure = KMCose.constructCoseSignStructure(protectedHeaders, aad, ephmeralMacKey);
+ signStructure =
+ KMKeymasterApplet.encodeToApduBuffer(
+ signStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ // 72 is the maximum ECDSA Signature length.
+ short maxEcdsaSignLen = 72;
+ short reclaimIndex = repository.allocReclaimableMemory(maxEcdsaSignLen);
+ short len =
+ seProvider.signWithDeviceUniqueKey(
+ deviceUniqueKeyPair,
+ scratchPad,
+ (short) 0,
+ signStructure,
+ repository.getHeap(),
+ reclaimIndex);
+ Util.arrayCopyNonAtomic(repository.getHeap(), reclaimIndex, scratchPad, (short) 0, len);
+ repository.reclaimMemory(maxEcdsaSignLen);
+ len =
+ KMAsn1Parser.instance()
+ .decodeEcdsa256Signature(
+ KMByteBlob.instance(scratchPad, (short) 0, len), scratchPad, (short) 0);
+ signStructure = KMByteBlob.instance(scratchPad, (short) 0, len);
+
+ /* Construct unprotected headers */
+ short unprotectedHeader = KMArray.instance((short) 0);
+ unprotectedHeader = KMCoseHeaders.instance(unprotectedHeader);
+
+ /* construct Cose_Sign1 */
+ return KMCose.constructCoseSign1(
+ protectedHeaders, unprotectedHeader, ephmeralMacKey, signStructure);
+ }
+
+ private KMKey createDeviceUniqueKeyPair(boolean testMode, byte[] scratchPad) {
+ KMKey deviceUniqueKeyPair;
+ rkpTmpVariables[0] = 0;
+ rkpTmpVariables[1] = 0;
+ if (testMode) {
+ seProvider.createAsymmetricKey(
+ KMType.EC,
+ scratchPad,
+ (short) 0,
+ (short) 128,
+ scratchPad,
+ (short) 128,
+ (short) 128,
+ rkpTmpVariables);
+ deviceUniqueKeyPair =
+ storeDataInst.createRkpTestDeviceUniqueKeyPair(
+ scratchPad,
+ (short) 128,
+ rkpTmpVariables[1],
+ scratchPad,
+ (short) 0,
+ rkpTmpVariables[0]);
+ } else {
+ deviceUniqueKeyPair = storeDataInst.getRkpDeviceUniqueKeyPair(false);
+ }
+ return deviceUniqueKeyPair;
+ }
+
+ /**
+ * DeviceInfo is a CBOR Map structure described by the following CDDL.
+ *
+ * <p>DeviceInfo = { "brand" : tstr, "manufacturer" : tstr, "product" : tstr, "model" : tstr,
+ * "device" : tstr, "vb_state" : "green" / "yellow" / "orange", // Taken from the AVB values
+ * "bootloader_state" : "locked" / "unlocked", // Taken from the AVB values "vbmeta_digest": bstr,
+ * // Taken from the AVB values ? "os_version" : tstr, // Same as android.os.Build.VERSION.release
+ * "system_patch_level" : uint, // YYYYMMDD "boot_patch_level" : uint, //YYYYMMDD
+ * "vendor_patch_level" : uint, // YYYYMMDD "version" : 2, // TheCDDL schema version
+ * "security_level" : "tee" / "strongbox" "fused": 1 / 0, }
+ */
+ private short createDeviceInfo(byte[] scratchpad) {
+ // Device Info Key Value pairs.
+ for (short i = 0; i < 32; i++) {
+ rkpTmpVariables[i] = KMType.INVALID_VALUE;
+ }
+ short dataOffset = 2;
+ rkpTmpVariables[0] = dataOffset;
+ rkpTmpVariables[1] = 0;
+ short metaOffset = 0;
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ BRAND,
+ getAttestationId(KMType.ATTESTATION_ID_BRAND, scratchpad));
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ MANUFACTURER,
+ getAttestationId(KMType.ATTESTATION_ID_MANUFACTURER, scratchpad));
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ PRODUCT,
+ getAttestationId(KMType.ATTESTATION_ID_PRODUCT, scratchpad));
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ MODEL,
+ getAttestationId(KMType.ATTESTATION_ID_MODEL, scratchpad));
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ DEVICE,
+ getAttestationId(KMType.ATTESTATION_ID_DEVICE, scratchpad));
+ updateItem(rkpTmpVariables, metaOffset, VB_STATE, getVbState());
+ updateItem(rkpTmpVariables, metaOffset, BOOTLOADER_STATE, getBootloaderState());
+ updateItem(rkpTmpVariables, metaOffset, VB_META_DIGEST, getVerifiedBootHash(scratchpad));
+ updateItem(rkpTmpVariables, metaOffset, OS_VERSION, getBootParams(OS_VERSION_ID, scratchpad));
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ SYSTEM_PATCH_LEVEL,
+ getBootParams(SYSTEM_PATCH_LEVEL_ID, scratchpad));
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ BOOT_PATCH_LEVEL,
+ getBootParams(BOOT_PATCH_LEVEL_ID, scratchpad));
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ VENDOR_PATCH_LEVEL,
+ getBootParams(VENDOR_PATCH_LEVEL_ID, scratchpad));
+ updateItem(
+ rkpTmpVariables, metaOffset, DEVICE_INFO_VERSION, KMInteger.uint_8(DI_SCHEMA_VERSION));
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ SECURITY_LEVEL,
+ KMTextString.instance(DI_SECURITY_LEVEL, (short) 0, (short) DI_SECURITY_LEVEL.length));
+ updateItem(rkpTmpVariables, metaOffset, FUSED, KMInteger.uint_8(storeDataInst.secureBootMode));
+ // Create device info map.
+ short map = KMMap.instance(rkpTmpVariables[1]);
+ short mapIndex = 0;
+ short index = 2;
+ while (index < (short) 32) {
+ if (rkpTmpVariables[index] != KMType.INVALID_VALUE) {
+ KMMap.cast(map)
+ .add(mapIndex++, rkpTmpVariables[index], rkpTmpVariables[(short) (index + 1)]);
+ }
+ index += 2;
+ }
+ KMMap.cast(map).canonicalize();
+ return map;
+ }
+
+ // Below 6 methods are helper methods to create device info structure.
+ // ----------------------------------------------------------------------------
+
+ /**
+ * Update the item inside the device info structure.
+ *
+ * @param deviceIds Device Info structure to be updated.
+ * @param metaOffset Out parameter meta information. Offset 0 is index and Offset 1 is length.
+ * @param item Key info to be updated.
+ * @param value value to be updated.
+ */
+ private void updateItem(short[] deviceIds, short metaOffset, byte[] item, short value) {
+ if (KMType.INVALID_VALUE != value) {
+ deviceIds[deviceIds[metaOffset]++] =
+ KMTextString.instance(item, (short) 0, (short) item.length);
+ deviceIds[deviceIds[metaOffset]++] = value;
+ deviceIds[(short) (metaOffset + 1)]++;
+ }
+ }
+
+ private short getAttestationId(short attestId, byte[] scratchpad) {
+ short attIdTagLen = storeDataInst.getAttestationId(attestId, scratchpad, (short) 0);
+ if (attIdTagLen == 0) {
+ KMException.throwIt(KMError.INVALID_STATE);
+ }
+ return KMTextString.instance(scratchpad, (short) 0, attIdTagLen);
+ }
+
+ private short getVerifiedBootHash(byte[] scratchPad) {
+ short len = storeDataInst.getVerifiedBootHash(scratchPad, (short) 0);
+ if (len == 0) {
+ KMException.throwIt(KMError.INVALID_STATE);
+ }
+ return KMByteBlob.instance(scratchPad, (short) 0, len);
+ }
+
+ private short getBootloaderState() {
+ short bootloaderState;
+ if (storeDataInst.isDeviceBootLocked()) {
+ bootloaderState = KMTextString.instance(LOCKED, (short) 0, (short) LOCKED.length);
+ } else {
+ bootloaderState = KMTextString.instance(UNLOCKED, (short) 0, (short) UNLOCKED.length);
+ }
+ return bootloaderState;
+ }
+
+ private short getVbState() {
+ short state = storeDataInst.getBootState();
+ short vbState = KMType.INVALID_VALUE;
+ if (state == KMType.VERIFIED_BOOT) {
+ vbState = KMTextString.instance(VB_STATE_GREEN, (short) 0, (short) VB_STATE_GREEN.length);
+ } else if (state == KMType.SELF_SIGNED_BOOT) {
+ vbState = KMTextString.instance(VB_STATE_YELLOW, (short) 0, (short) VB_STATE_YELLOW.length);
+ } else if (state == KMType.UNVERIFIED_BOOT) {
+ vbState = KMTextString.instance(VB_STATE_ORANGE, (short) 0, (short) VB_STATE_ORANGE.length);
+ } else if (state == KMType.FAILED_BOOT) {
+ vbState = KMTextString.instance(VB_STATE_RED, (short) 0, (short) VB_STATE_RED.length);
+ }
+ return vbState;
+ }
+
+ private short converIntegerToTextString(short intPtr, byte[] scratchPad) {
+ // Prepare Hex Values
+ short index = 1;
+ scratchPad[0] = 0x30; // Ascii 0
+ while (index < 10) {
+ scratchPad[index] = (byte) (scratchPad[(short) (index - 1)] + 1);
+ index++;
+ }
+ scratchPad[index++] = 0x41; // Ascii 'A'
+ while (index < 16) {
+ scratchPad[index] = (byte) (scratchPad[(short) (index - 1)] + 1);
+ index++;
+ }
+
+ short intLen = KMInteger.cast(intPtr).length();
+ short intOffset = KMInteger.cast(intPtr).getStartOff();
+ byte[] buf = repository.getHeap();
+ short tsPtr = KMTextString.instance((short) (intLen * 2));
+ short tsStartOff = KMTextString.cast(tsPtr).getStartOff();
+ index = 0;
+ byte nibble;
+ while (index < intLen) {
+ nibble = (byte) ((byte) (buf[intOffset] >> 4) & (byte) 0x0F);
+ buf[tsStartOff] = scratchPad[nibble];
+ nibble = (byte) (buf[intOffset] & 0x0F);
+ buf[(short) (tsStartOff + 1)] = scratchPad[nibble];
+ index++;
+ intOffset++;
+ tsStartOff += 2;
+ }
+ return tsPtr;
+ }
+
+ private short getBootParams(byte bootParam, byte[] scratchPad) {
+ short value = KMType.INVALID_VALUE;
+ switch (bootParam) {
+ case OS_VERSION_ID:
+ value = storeDataInst.getOsVersion();
+ break;
+ case SYSTEM_PATCH_LEVEL_ID:
+ value = storeDataInst.getOsPatch();
+ break;
+ case BOOT_PATCH_LEVEL_ID:
+ value = storeDataInst.getBootPatchLevel();
+ break;
+ case VENDOR_PATCH_LEVEL_ID:
+ value = storeDataInst.getVendorPatchLevel();
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ // Convert Integer to Text String for OS_VERSION.
+ if (bootParam == OS_VERSION_ID) {
+ value = converIntegerToTextString(value, scratchPad);
+ }
+ return value;
+ }
+ // ----------------------------------------------------------------------------
+
+ // ----------------------------------------------------------------------------
+ // ECDH HKDF
+ private short ecdhHkdfDeriveKey(
+ byte[] privKeyA,
+ short privKeyAOff,
+ short privKeyALen,
+ byte[] pubKeyA,
+ short pubKeyAOff,
+ short pubKeyALen,
+ byte[] pubKeyB,
+ short pubKeyBOff,
+ short pubKeyBLen,
+ byte[] scratchPad) {
+ short key =
+ seProvider.ecdhKeyAgreement(
+ privKeyA,
+ privKeyAOff,
+ privKeyALen,
+ pubKeyB,
+ pubKeyBOff,
+ pubKeyBLen,
+ scratchPad,
+ (short) 0);
+ key = KMByteBlob.instance(scratchPad, (short) 0, key);
+
+ // ignore 0x04 for ephemerical public key as kdfContext should not include 0x04.
+ pubKeyAOff += 1;
+ pubKeyALen -= 1;
+ pubKeyBOff += 1;
+ pubKeyBLen -= 1;
+ short kdfContext =
+ KMCose.constructKdfContext(
+ pubKeyA, pubKeyAOff, pubKeyALen, pubKeyB, pubKeyBOff, pubKeyBLen, true);
+ kdfContext =
+ KMKeymasterApplet.encodeToApduBuffer(
+ kdfContext, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ kdfContext = KMByteBlob.instance(scratchPad, (short) 0, kdfContext);
+
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 32, (byte) 0);
+ seProvider.hkdf(
+ KMByteBlob.cast(key).getBuffer(),
+ KMByteBlob.cast(key).getStartOff(),
+ KMByteBlob.cast(key).length(),
+ scratchPad,
+ (short) 0,
+ (short) 32,
+ KMByteBlob.cast(kdfContext).getBuffer(),
+ KMByteBlob.cast(kdfContext).getStartOff(),
+ KMByteBlob.cast(kdfContext).length(),
+ scratchPad,
+ (short) 32, // offset
+ (short) 32 // Length of expected output.
+ );
+ Util.arrayCopy(scratchPad, (short) 32, scratchPad, (short) 0, (short) 32);
+ return (short) 32;
+ }
+
+ // ----------------------------------------------------------------------------
+ // This function returns the instance of private key and It stores the public key in the
+ // data table for later usage.
+ private short generateEphemeralEcKey(byte[] scratchPad) {
+ // Generate ephemeral ec key.
+ rkpTmpVariables[0] = 0;
+ rkpTmpVariables[1] = 0;
+ seProvider.createAsymmetricKey(
+ KMType.EC,
+ scratchPad,
+ (short) 0,
+ (short) 128,
+ scratchPad,
+ (short) 128,
+ (short) 128,
+ rkpTmpVariables);
+ // Copy the ephemeral private key from scratch pad
+ short ptr = KMByteBlob.instance(rkpTmpVariables[0]);
+ Util.arrayCopyNonAtomic(
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ rkpTmpVariables[0]);
+ // Store ephemeral public key in data table for later usage.
+ short dataEntryIndex = createEntry(EPHEMERAL_PUB_KEY, rkpTmpVariables[1]);
+ Util.arrayCopyNonAtomic(scratchPad, (short) 128, data, dataEntryIndex, rkpTmpVariables[1]);
+ return ptr;
+ }
+
+ private void initHmacOperation() {
+ short dataEntryIndex = getEntry(EPHEMERAL_MAC_KEY);
+ operation[0] =
+ seProvider.getRkpOperation(
+ KMType.SIGN,
+ KMType.HMAC,
+ KMType.SHA2_256,
+ KMType.PADDING_NONE,
+ (byte) 0,
+ data,
+ dataEntryIndex,
+ getEntryLength(EPHEMERAL_MAC_KEY),
+ null,
+ (short) 0,
+ (short) 0,
+ (short) 0);
+ if (operation[0] == null) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+ }
+
+ private void initAesGcmOperation(byte[] scratchPad, short nonce) {
+ // Generate Ephemeral mac key
+ short privKey = generateEphemeralEcKey(scratchPad);
+ short pubKeyIndex = getEntry(EPHEMERAL_PUB_KEY);
+ // Generate session key
+ short eekIndex = getEntry(EEK_KEY);
+ // Generate session key
+ short sessionKeyLen =
+ ecdhHkdfDeriveKey(
+ KMByteBlob.cast(privKey).getBuffer(), /* Ephemeral Private Key */
+ KMByteBlob.cast(privKey).getStartOff(),
+ KMByteBlob.cast(privKey).length(),
+ data, /* Ephemeral Public key */
+ pubKeyIndex,
+ getEntryLength(EPHEMERAL_PUB_KEY),
+ data, /* EEK Public key */
+ eekIndex,
+ getEntryLength(EEK_KEY),
+ scratchPad /* scratchpad */);
+ // Initialize the Cipher object.
+ operation[0] =
+ seProvider.getRkpOperation(
+ KMType.ENCRYPT,
+ KMType.AES,
+ (byte) 0,
+ KMType.PADDING_NONE,
+ KMType.GCM,
+ scratchPad, /* key */
+ (short) 0,
+ sessionKeyLen,
+ KMByteBlob.cast(nonce).getBuffer(), /* nonce */
+ KMByteBlob.cast(nonce).getStartOff(),
+ KMByteBlob.cast(nonce).length(),
+ (short) (KMKeymasterApplet.AES_GCM_AUTH_TAG_LENGTH * 8));
+ if (operation[0] == null) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+ }
+
+ private short processRecipientStructure(byte[] scratchPad) {
+ short protectedHeaderRecipient =
+ KMCose.constructHeaders(
+ rkpTmpVariables,
+ KMNInteger.uint_8(KMCose.COSE_ALG_ECDH_ES_HKDF_256),
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE);
+ // Encode the protected header as byte blob.
+ protectedHeaderRecipient =
+ KMKeymasterApplet.encodeToApduBuffer(
+ protectedHeaderRecipient, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ protectedHeaderRecipient = KMByteBlob.instance(scratchPad, (short) 0, protectedHeaderRecipient);
+
+ /* Construct unprotected headers */
+ short pubKeyIndex = getEntry(EPHEMERAL_PUB_KEY);
+ // prepare cosekey
+ short coseKey =
+ KMCose.constructCoseKey(
+ rkpTmpVariables,
+ KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2),
+ KMType.INVALID_VALUE,
+ KMNInteger.uint_8(KMCose.COSE_ALG_ES256),
+ KMInteger.uint_8(KMCose.COSE_ECCURVE_256),
+ data,
+ pubKeyIndex,
+ getEntryLength(EPHEMERAL_PUB_KEY),
+ KMType.INVALID_VALUE,
+ false);
+ short keyIdentifierPtr =
+ KMByteBlob.instance(data, getEntry(EEK_KEY_ID), getEntryLength(EEK_KEY_ID));
+ short unprotectedHeaderRecipient =
+ KMCose.constructHeaders(
+ rkpTmpVariables, KMType.INVALID_VALUE, keyIdentifierPtr, KMType.INVALID_VALUE, coseKey);
+
+ // Construct recipients structure.
+ return KMCose.constructRecipientsStructure(
+ protectedHeaderRecipient,
+ unprotectedHeaderRecipient,
+ KMSimpleValue.instance(KMSimpleValue.NULL));
+ }
+
+ private short getAdditionalCertChainProcessedLength() {
+ short dataEntryIndex = getEntry(ACC_PROCESSED_LENGTH);
+ if (dataEntryIndex == 0) {
+ dataEntryIndex = createEntry(ACC_PROCESSED_LENGTH, SHORT_SIZE);
+ Util.setShort(data, dataEntryIndex, (short) 0);
+ return (short) 0;
+ }
+ return Util.getShort(data, dataEntryIndex);
+ }
+
+ private void updateAdditionalCertChainProcessedLength(short processedLen) {
+ short dataEntryIndex = getEntry(ACC_PROCESSED_LENGTH);
+ Util.setShort(data, dataEntryIndex, processedLen);
+ }
+
+ private short processAdditionalCertificateChain(byte[] scratchPad) {
+ byte[] persistedData = storeDataInst.getAdditionalCertChain();
+ short totalAccLen = Util.getShort(persistedData, (short) 0);
+ if (totalAccLen == 0) {
+ // No Additional certificate chain present.
+ return 0;
+ }
+ short processedLen = getAdditionalCertChainProcessedLength();
+ short lengthToSend = (short) (totalAccLen - processedLen);
+ if (lengthToSend > MAX_SEND_DATA) {
+ lengthToSend = MAX_SEND_DATA;
+ }
+ short cipherTextLen =
+ ((KMOperation) operation[0])
+ .update(persistedData, (short) (2 + processedLen), lengthToSend, scratchPad, (short) 0);
+ processedLen += lengthToSend;
+ updateAdditionalCertChainProcessedLength(processedLen);
+ // Update the output processing state.
+ updateOutputProcessingState(
+ (processedLen == totalAccLen) ? PROCESSING_ACC_COMPLETE : PROCESSING_ACC_IN_PROGRESS);
+ return cipherTextLen;
+ }
+
+ // BCC for STRONGBOX has chain length of 2. So it can be returned in a single go.
+ private short processBcc(byte[] scratchPad) {
+ // Construct BCC
+ boolean testMode = (TRUE == data[getEntry(TEST_MODE)]) ? true : false;
+ short len;
+ if (testMode) {
+ short bcc = KMKeymasterApplet.generateBcc(true, scratchPad);
+ len =
+ KMKeymasterApplet.encodeToApduBuffer(
+ bcc, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ } else {
+ byte[] bcc = storeDataInst.getBootCertificateChain();
+ len = Util.getShort(bcc, (short) 0);
+ Util.arrayCopyNonAtomic(bcc, (short) 2, scratchPad, (short) 0, len);
+ }
+ short cipherTextLen =
+ ((KMOperation) operation[0]).update(scratchPad, (short) 0, len, scratchPad, len);
+ // move cipher text on scratch pad from starting position.
+ Util.arrayCopyNonAtomic(scratchPad, len, scratchPad, (short) 0, cipherTextLen);
+ createEntry(RESPONSE_PROCESSING_STATE, BYTE_SIZE);
+ // If there is no additional certificate chain present then put the state to
+ // PROCESSING_ACC_COMPLETE.
+ updateOutputProcessingState(
+ isAdditionalCertificateChainPresent() ? PROCESSING_BCC_COMPLETE : PROCESSING_ACC_COMPLETE);
+ return cipherTextLen;
+ }
+
+ // AAD is the CoseEncrypt structure
+ private void processAesGcmUpdateAad(byte[] scratchPad) {
+ short protectedHeader =
+ KMCose.constructHeaders(
+ rkpTmpVariables,
+ KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256),
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE);
+ // Encode the protected header as byte blob.
+ protectedHeader =
+ KMKeymasterApplet.encodeToApduBuffer(
+ protectedHeader, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, protectedHeader);
+ short coseEncryptStr =
+ KMCose.constructCoseEncryptStructure(protectedHeader, KMByteBlob.instance((short) 0));
+ coseEncryptStr =
+ KMKeymasterApplet.encodeToApduBuffer(
+ coseEncryptStr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ ((KMOperation) operation[0]).updateAAD(scratchPad, (short) 0, coseEncryptStr);
+ }
+
+ private short processSignedMac(byte[] scratchPad, short pubKeysToSignMac, short deviceInfo) {
+ // Construct SignedMac
+ KMKey deviceUniqueKeyPair =
+ createDeviceUniqueKeyPair((TRUE == data[getEntry(TEST_MODE)]) ? true : false, scratchPad);
+ // Create signedMac
+ short signedMac =
+ createSignedMac(deviceUniqueKeyPair, scratchPad, deviceInfo, pubKeysToSignMac);
+ // Prepare partial data for encryption.
+ short arrLength = (short) (isAdditionalCertificateChainPresent() ? 3 : 2);
+ short arr = KMArray.instance(arrLength);
+ KMArray.cast(arr).add((short) 0, signedMac);
+ KMArray.cast(arr).add((short) 1, KMType.INVALID_VALUE);
+ if (arrLength == 3) {
+ KMArray.cast(arr).add((short) 2, KMType.INVALID_VALUE);
+ }
+ short len =
+ KMKeymasterApplet.encodeToApduBuffer(
+ arr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ short cipherTextLen =
+ ((KMOperation) operation[0]).update(scratchPad, (short) 0, len, scratchPad, len);
+ Util.arrayCopyNonAtomic(scratchPad, len, scratchPad, (short) 0, cipherTextLen);
+ return cipherTextLen;
+ }
+
+ private short getCoseEncryptProtectedHeader(byte[] scratchPad) {
+ // CoseEncrypt protected headers.
+ short protectedHeader =
+ KMCose.constructHeaders(
+ rkpTmpVariables,
+ KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256),
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE);
+ // Encode the protected header as byte blob.
+ protectedHeader =
+ KMKeymasterApplet.encodeToApduBuffer(
+ protectedHeader, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ return KMByteBlob.instance(scratchPad, (short) 0, protectedHeader);
+ }
+
+ private short getCoseEncryptUnprotectedHeader(byte[] scratchPad, short nonce) {
+ /* CoseEncrypt unprotected headers */
+ return KMCose.constructHeaders(
+ rkpTmpVariables, KMType.INVALID_VALUE, KMType.INVALID_VALUE, nonce, KMType.INVALID_VALUE);
+ }
+
+ private short constructCoseMacForRkpKey(boolean testMode, byte[] scratchPad, short pubKey) {
+ // prepare cosekey
+ short coseKey =
+ KMCose.constructCoseKey(
+ rkpTmpVariables,
+ KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2),
+ KMType.INVALID_VALUE,
+ KMNInteger.uint_8(KMCose.COSE_ALG_ES256),
+ KMInteger.uint_8(KMCose.COSE_ECCURVE_256),
+ KMByteBlob.cast(pubKey).getBuffer(),
+ KMByteBlob.cast(pubKey).getStartOff(),
+ KMByteBlob.cast(pubKey).length(),
+ KMType.INVALID_VALUE,
+ testMode);
+ // Encode the cose key and make it as payload.
+ short len =
+ KMKeymasterApplet.encodeToApduBuffer(
+ coseKey, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ short payload = KMByteBlob.instance(scratchPad, (short) 0, len);
+ // Prepare protected header, which is required to construct the COSE_MAC0
+ short headerPtr =
+ KMCose.constructHeaders(
+ rkpTmpVariables,
+ KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256),
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE);
+ // Encode the protected header as byte blob.
+ len =
+ KMKeymasterApplet.encodeToApduBuffer(
+ headerPtr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ short protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, len);
+ // create MAC_Structure
+ short macStructure =
+ KMCose.constructCoseMacStructure(protectedHeader, KMByteBlob.instance((short) 0), payload);
+ // Encode the Mac_structure and do HMAC_Sign to produce the tag for COSE_MAC0
+ len =
+ KMKeymasterApplet.encodeToApduBuffer(
+ macStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ // HMAC Sign.
+ short hmacLen = rkpHmacSign(testMode, scratchPad, (short) 0, len, scratchPad, len);
+ // Create COSE_MAC0 object
+ short coseMac0 =
+ KMCose.constructCoseMac0(
+ protectedHeader,
+ KMCoseHeaders.instance(KMArray.instance((short) 0)),
+ payload,
+ KMByteBlob.instance(scratchPad, len, hmacLen));
+ len =
+ KMKeymasterApplet.encodeToApduBuffer(
+ coseMac0, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ return KMByteBlob.instance(scratchPad, (short) 0, len);
+ }
+
+ private short getEcAttestKeyParameters() {
+ short tagIndex = 0;
+ short arrPtr = KMArray.instance((short) 6);
+ // Key size - 256
+ short keySize =
+ KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 256));
+ // Digest - SHA256
+ short byteBlob = KMByteBlob.instance((short) 1);
+ KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256);
+ short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob);
+ // Purpose - Attest
+ byteBlob = KMByteBlob.instance((short) 1);
+ KMByteBlob.cast(byteBlob).add((short) 0, KMType.ATTEST_KEY);
+ short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob);
+
+ KMArray.cast(arrPtr).add(tagIndex++, purpose);
+ // Algorithm - EC
+ KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC));
+ KMArray.cast(arrPtr).add(tagIndex++, keySize);
+ KMArray.cast(arrPtr).add(tagIndex++, digest);
+ // Curve - P256
+ KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ECCURVE, KMType.P_256));
+ // No Authentication is required to use this key.
+ KMArray.cast(arrPtr).add(tagIndex, KMBoolTag.instance(KMType.NO_AUTH_REQUIRED));
+ return KMKeyParameters.instance(arrPtr);
+ }
+
+ private boolean isSignedByte(byte b) {
+ return ((b & 0x0080) != 0);
+ }
+
+ private short writeIntegerHeader(short valueLen, byte[] data, short offset) {
+ // write length
+ data[offset] = (byte) valueLen;
+ // write INTEGER tag
+ offset--;
+ data[offset] = 0x02;
+ return offset;
+ }
+
+ private short writeSequenceHeader(short valueLen, byte[] data, short offset) {
+ // write length
+ data[offset] = (byte) valueLen;
+ // write INTEGER tag
+ offset--;
+ data[offset] = 0x30;
+ return offset;
+ }
+
+ private short writeSignatureData(
+ byte[] input, short inputOff, short inputlen, byte[] output, short offset) {
+ Util.arrayCopyNonAtomic(input, inputOff, output, offset, inputlen);
+ if (isSignedByte(input[inputOff])) {
+ offset--;
+ output[offset] = (byte) 0;
+ }
+ return offset;
+ }
+
+ public short encodeES256CoseSignSignature(
+ byte[] input, short offset, short len, byte[] scratchPad, short scratchPadOff) {
+ // SEQ [ INTEGER(r), INTEGER(s)]
+ // write from bottom to the top
+ if (len != 64) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ short maxTotalLen = 72;
+ short end = (short) (scratchPadOff + maxTotalLen);
+ // write s.
+ short start = (short) (end - 32);
+ start = writeSignatureData(input, (short) (offset + 32), (short) 32, scratchPad, start);
+ // write length and header
+ short length = (short) (end - start);
+ start--;
+ start = writeIntegerHeader(length, scratchPad, start);
+ // write r
+ short rEnd = start;
+ start = (short) (start - 32);
+ start = writeSignatureData(input, offset, (short) 32, scratchPad, start);
+ // write length and header
+ length = (short) (rEnd - start);
+ start--;
+ start = writeIntegerHeader(length, scratchPad, start);
+ // write length and sequence header
+ length = (short) (end - start);
+ start--;
+ start = writeSequenceHeader(length, scratchPad, start);
+ length = (short) (end - start);
+ if (start > scratchPadOff) {
+ // re adjust the buffer
+ Util.arrayCopyNonAtomic(scratchPad, start, scratchPad, scratchPadOff, length);
+ }
+ return length;
+ }
+
+ private short rkpHmacSign(
+ boolean testMode,
+ byte[] data,
+ short dataStart,
+ short dataLength,
+ byte[] signature,
+ short signatureStart) {
+ short result;
+ if (testMode) {
+ short macKey = KMByteBlob.instance(EPHEMERAL_MAC_KEY_SIZE);
+ Util.arrayFillNonAtomic(
+ KMByteBlob.cast(macKey).getBuffer(),
+ KMByteBlob.cast(macKey).getStartOff(),
+ EPHEMERAL_MAC_KEY_SIZE,
+ (byte) 0);
+ result =
+ seProvider.hmacSign(
+ KMByteBlob.cast(macKey).getBuffer(),
+ KMByteBlob.cast(macKey).getStartOff(),
+ EPHEMERAL_MAC_KEY_SIZE,
+ data,
+ dataStart,
+ dataLength,
+ signature,
+ signatureStart);
+ } else {
+ result =
+ seProvider.hmacSign(
+ storeDataInst.getRkpMacKey(), data, dataStart, dataLength, signature, signatureStart);
+ }
+ return result;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMRepository.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMRepository.java
new file mode 100644
index 0000000..9fd2406
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMRepository.java
@@ -0,0 +1,130 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * KMRepository class manages volatile memory usage by the applet. Note the repository is only used
+ * by applet and it is not intended to be used by seProvider.
+ */
+public class KMRepository {
+
+ // The maximum available heap memory.
+ public static final short HEAP_SIZE = 10000;
+ // Index pointing from the back of heap.
+ private static short[] reclaimIndex;
+ // Singleton instance
+ private static KMRepository repository;
+ // Heap buffer
+ private byte[] heap;
+ // Index to the heap buffer.
+ private short[] heapIndex;
+
+ public KMRepository(boolean isUpgrading) {
+ heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET);
+ heapIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET);
+ reclaimIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET);
+ reclaimIndex[0] = HEAP_SIZE;
+ repository = this;
+ }
+
+ public static KMRepository instance() {
+ return repository;
+ }
+
+ public void onUninstall() {
+ // Javacard Runtime environment cleans up the data.
+
+ }
+
+ public void onProcess() {}
+
+ public void clean() {
+ Util.arrayFillNonAtomic(heap, (short) 0, HEAP_SIZE, (byte) 0);
+ heapIndex[0] = 0;
+ reclaimIndex[0] = HEAP_SIZE;
+ }
+
+ public void onDeselect() {}
+
+ public void onSelect() {
+ // If write through caching is implemented then this method will restore the data into cache
+ }
+
+ // This function uses memory from the back of the heap(transient memory). Call
+ // reclaimMemory function immediately after the use.
+ public short allocReclaimableMemory(short length) {
+ if ((((short) (reclaimIndex[0] - length)) <= heapIndex[0]) || (length >= HEAP_SIZE / 2)) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ reclaimIndex[0] -= length;
+ return reclaimIndex[0];
+ }
+
+ // Reclaims the memory back.
+ public void reclaimMemory(short length) {
+ if (reclaimIndex[0] < heapIndex[0]) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ Util.arrayFillNonAtomic(heap, reclaimIndex[0], length, (byte) 0);
+ reclaimIndex[0] += length;
+ }
+
+ public short allocAvailableMemory() {
+ if (heapIndex[0] >= heap.length) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short index = heapIndex[0];
+ heapIndex[0] = reclaimIndex[0];
+ return index;
+ }
+
+ public short alloc(short length) {
+ if ((((short) (heapIndex[0] + length)) > heap.length)
+ || (((short) (heapIndex[0] + length)) > reclaimIndex[0])) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ heapIndex[0] += length;
+ return (short) (heapIndex[0] - length);
+ }
+
+ public byte[] getHeap() {
+ return heap;
+ }
+
+ public short getHeapIndex() {
+ return heapIndex[0];
+ }
+
+ // Use this function to reset the heapIndex to its previous state.
+ // Some of the data might be lost so use it carefully.
+ public void setHeapIndex(short offset) {
+ if (offset > heapIndex[0] || offset < 0) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ Util.arrayFillNonAtomic(heap, offset, (short) (heapIndex[0] - offset), (byte) 0);
+ heapIndex[0] = offset;
+ }
+
+ public short getHeapReclaimIndex() {
+ return reclaimIndex[0];
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMSemanticTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMSemanticTag.java
new file mode 100644
index 0000000..07b2675
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMSemanticTag.java
@@ -0,0 +1,80 @@
+package com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMSemanticTag corresponds to CBOR type of tagged item. The structure is defined as struct{byte
+ * SEMANTIC_TAG_TYPE; short length; tag, short ptr }. Tag is INTEGER_TYPE and the possible values
+ * are defined here https://www.rfc-editor.org/rfc/rfc7049#section-2.4
+ */
+public class KMSemanticTag extends KMType {
+
+ public static final short COSE_MAC_SEMANTIC_TAG = (short) 0x0011;
+ public static final short ROT_SEMANTIC_TAG = (short) 0x9C41;
+ private static KMSemanticTag prototype;
+
+ private KMSemanticTag() {}
+
+ private static KMSemanticTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMSemanticTag();
+ }
+ instanceTable[KM_SEMANTIC_TAG_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp(short valuePtr) {
+ short ptr = KMType.instance(SEMANTIC_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMInteger.exp());
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+ return ptr;
+ }
+
+ public static KMSemanticTag cast(short ptr) {
+ if (heap[ptr] != SEMANTIC_TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static short instance(short tag, short value) {
+ if (!isSemanticTagSupported(tag)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ // The maximum tag size can be UINT32. Currently, we support
+ // only two tags which are short.
+ short ptr = KMType.instance(SEMANTIC_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), tag);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), value);
+ return ptr;
+ }
+
+ private static boolean isSemanticTagSupported(short tag) {
+ tag = KMInteger.cast(tag).getShort();
+ switch (tag) {
+ case COSE_MAC_SEMANTIC_TAG:
+ case ROT_SEMANTIC_TAG:
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ public short length() {
+ return Util.getShort(heap, (short) (instanceTable[KM_SEMANTIC_TAG_OFFSET] + 1));
+ }
+
+ public short getKeyPtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_SEMANTIC_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short getValuePtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_SEMANTIC_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMSimpleValue.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMSimpleValue.java
new file mode 100644
index 0000000..6dffd73
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMSimpleValue.java
@@ -0,0 +1,71 @@
+package com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMSimpleValue corresponds to CBOR type of Simple value. It holds either true, false or NULL
+ * values. The structure is defined as struct{byte SIMPLE_VALUE_TYPE; short length; simple value }
+ */
+public class KMSimpleValue extends KMType {
+
+ public static final byte FALSE = (byte) 20;
+ public static final byte TRUE = (byte) 21;
+ public static final byte NULL = (byte) 22;
+ private static KMSimpleValue prototype;
+
+ private KMSimpleValue() {}
+
+ private static KMSimpleValue proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMSimpleValue();
+ }
+ instanceTable[KM_SIMPLE_VALUE_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ return KMType.exp(SIMPLE_VALUE_TYPE);
+ }
+
+ public static KMSimpleValue cast(short ptr) {
+ if (heap[ptr] != SIMPLE_VALUE_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (!isSimpleValueValid(heap[(short) (ptr + 3)])) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static short instance(byte value) {
+ if (!isSimpleValueValid(value)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = KMType.instance(SIMPLE_VALUE_TYPE, (short) 1);
+ heap[(short) (ptr + 3)] = value;
+ return ptr;
+ }
+
+ private static boolean isSimpleValueValid(byte value) {
+ switch (value) {
+ case TRUE:
+ case FALSE:
+ case NULL:
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ public short length() {
+ return Util.getShort(heap, (short) (instanceTable[KM_SIMPLE_VALUE_OFFSET] + 1));
+ }
+
+ public byte getValue() {
+ return heap[(short) (instanceTable[KM_SIMPLE_VALUE_OFFSET] + 3)];
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMTag.java
new file mode 100644
index 0000000..3033a70
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMTag.java
@@ -0,0 +1,102 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.Util;
+
+/**
+ * This class represents a tag as defined by keymaster hal specifications. It is composed of key
+ * value pair. The key consists of short tag type e.g. KMType.ENUM and short tag key e.g.
+ * KMType.ALGORITHM. The key is encoded as uint CBOR type with 4 bytes. This is followed by value
+ * which can be any CBOR type based on key. struct{byte tag=KMType.TAG_TYPE, short length, value)
+ * where value is subtype of KMTag i.e. struct{short tagType=one of tag types declared in KMType ,
+ * short tagKey=one of the tag keys declared in KMType, value} where value is one of the sub-types
+ * of KMType.
+ */
+public class KMTag extends KMType {
+
+ public static short getTagType(short ptr) {
+ return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ }
+
+ public static short getKey(short ptr) {
+ return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2));
+ }
+
+ public static void assertPresence(short params, short tagType, short tagKey, short error) {
+ if (!isPresent(params, tagType, tagKey)) {
+ KMException.throwIt(error);
+ }
+ }
+
+ public static void assertAbsence(short params, short tagType, short tagKey, short error) {
+ if (isPresent(params, tagType, tagKey)) {
+ KMException.throwIt(error);
+ }
+ }
+
+ public static boolean isPresent(short params, short tagType, short tagKey) {
+ short tag = KMKeyParameters.findTag(tagType, tagKey, params);
+ return tag != KMType.INVALID_VALUE;
+ }
+
+ public static boolean isEqual(short params, short tagType, short tagKey, short value) {
+ switch (tagType) {
+ case KMType.ENUM_TAG:
+ return KMEnumTag.getValue(tagKey, params) == value;
+ case KMType.UINT_TAG:
+ case KMType.DATE_TAG:
+ case KMType.ULONG_TAG:
+ return KMIntegerTag.isEqual(params, tagType, tagKey, value);
+ case KMType.ENUM_ARRAY_TAG:
+ return KMEnumArrayTag.contains(tagKey, value, params);
+ case KMType.UINT_ARRAY_TAG:
+ case KMType.ULONG_ARRAY_TAG:
+ return KMIntegerArrayTag.contains(tagKey, value, params);
+ }
+ return false;
+ }
+
+ public static void assertTrue(boolean condition, short error) {
+ if (!condition) {
+ KMException.throwIt(error);
+ }
+ }
+
+ public static boolean isValidPublicExponent(short params) {
+ short pubExp = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, params);
+ if (pubExp == KMType.INVALID_VALUE) {
+ return false;
+ }
+ pubExp = KMIntegerTag.cast(pubExp).getValue();
+ if (!(KMInteger.cast(pubExp).getShort() == 0x01
+ && KMInteger.cast(pubExp).getSignificantShort() == 0x01)) {
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean isValidKeySize(short params) {
+ short keysize = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, params);
+ if (keysize == KMType.INVALID_VALUE) {
+ return false;
+ }
+ short alg = KMEnumTag.getValue(KMType.ALGORITHM, params);
+ return KMIntegerTag.cast(keysize).isValidKeySize((byte) alg);
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMTextString.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMTextString.java
new file mode 100644
index 0000000..80aebd2
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMTextString.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMTextString represents contiguous block of bytes. It corresponds to CBOR type of Text String. It
+ * extends KMByteBlob by specifying value field as zero or more sequence of bytes. struct{ byte
+ * TEXT_STR_TYPE; short length; sequence of bytes}
+ */
+public class KMTextString extends KMByteBlob {
+
+ private static byte OFFSET_SIZE = 2;
+
+ private static KMTextString prototype;
+
+ private KMTextString() {}
+
+ private static KMTextString proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMTextString();
+ }
+ instanceTable[KM_TEXT_STRING_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ return KMType.exp(TEXT_STRING_TYPE);
+ }
+
+ // return an empty byte blob instance
+ public static short instance(short length) {
+ short ptr = KMType.instance(TEXT_STRING_TYPE, (short) (length + OFFSET_SIZE));
+ Util.setShort(
+ heap, (short) (ptr + TLV_HEADER_SIZE), (short) (ptr + TLV_HEADER_SIZE + OFFSET_SIZE));
+ Util.setShort(heap, (short) (ptr + 1), length);
+ return ptr;
+ }
+
+ // byte blob from existing buf
+ public static short instance(byte[] buf, short startOff, short length) {
+ short ptr = instance(length);
+ Util.arrayCopyNonAtomic(
+ buf, startOff, heap, (short) (ptr + TLV_HEADER_SIZE + OFFSET_SIZE), length);
+ return ptr;
+ }
+
+ // cast the ptr to KMTextString
+ public static KMTextString cast(short ptr) {
+ if (heap[ptr] != TEXT_STRING_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ protected short getBaseOffset() {
+ return instanceTable[KM_TEXT_STRING_OFFSET];
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMType.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMType.java
new file mode 100644
index 0000000..a18cb7f
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMType.java
@@ -0,0 +1,407 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * This class declares all types, tag types, and tag keys. It also establishes basic structure of
+ * any KMType i.e. struct{byte type, short length, value} where value can any of the KMType. Also,
+ * KMType refers to transient memory heap in the repository. Finally KMType's subtypes are singleton
+ * prototype objects which just cast the structure over contiguous memory buffer.
+ */
+public abstract class KMType {
+
+ public static final short INVALID_VALUE = (short) 0x8000;
+ // Types
+ public static final byte BYTE_BLOB_TYPE = 0x01;
+ public static final byte INTEGER_TYPE = 0x02;
+ public static final byte ENUM_TYPE = 0x03;
+ public static final byte TAG_TYPE = 0x04;
+ public static final byte ARRAY_TYPE = 0x05;
+ public static final byte KEY_PARAM_TYPE = 0x06;
+ public static final byte KEY_CHAR_TYPE = 0x07;
+ public static final byte HW_AUTH_TOKEN_TYPE = 0x08;
+ public static final byte VERIFICATION_TOKEN_TYPE = 0x09;
+ public static final byte HMAC_SHARING_PARAM_TYPE = 0x0A;
+ public static final byte X509_CERT = 0x0B;
+ public static final byte NEG_INTEGER_TYPE = 0x0C;
+ public static final byte TEXT_STRING_TYPE = 0x0D;
+ public static final byte MAP_TYPE = 0x0E;
+ public static final byte COSE_KEY_TYPE = 0x0F;
+ public static final byte COSE_PAIR_TAG_TYPE = 0x10;
+ public static final byte COSE_PAIR_INT_TAG_TYPE = 0x20;
+ public static final byte COSE_PAIR_NEG_INT_TAG_TYPE = 0x30;
+ public static final byte COSE_PAIR_BYTE_BLOB_TAG_TYPE = 0x40;
+ public static final byte COSE_PAIR_COSE_KEY_TAG_TYPE = 0x60;
+ public static final byte COSE_PAIR_SIMPLE_VALUE_TAG_TYPE = 0x70;
+ public static final byte COSE_PAIR_TEXT_STR_TAG_TYPE = (byte) 0x80;
+ public static final byte SIMPLE_VALUE_TYPE = (byte) 0x90;
+ public static final byte COSE_HEADERS_TYPE = (byte) 0xA0;
+ public static final byte COSE_CERT_PAYLOAD_TYPE = (byte) 0xB0;
+ public static final byte SEMANTIC_TAG_TYPE = (byte) 0xC0;
+ // Tag Types
+ public static final short INVALID_TAG = 0x0000;
+ public static final short ENUM_TAG = 0x1000;
+ public static final short ENUM_ARRAY_TAG = 0x2000;
+ public static final short UINT_TAG = 0x3000;
+ public static final short UINT_ARRAY_TAG = 0x4000;
+ public static final short ULONG_TAG = 0x5000;
+ public static final short DATE_TAG = 0x6000;
+ public static final short BOOL_TAG = 0x7000;
+ public static final short BIGNUM_TAG = (short) 0x8000;
+ public static final short BYTES_TAG = (short) 0x9000;
+ public static final short ULONG_ARRAY_TAG = (short) 0xA000;
+ public static final short TAG_TYPE_MASK = (short) 0xF000;
+
+ // Enum Tag
+ // Internal tags
+ public static final short RULE = 0x7FFF;
+ public static final byte IGNORE_INVALID_TAGS = 0x00;
+ public static final byte FAIL_ON_INVALID_TAGS = 0x01;
+
+ // Algorithm Enum Tag key and values
+ public static final short ALGORITHM = 0x0002;
+ public static final byte RSA = 0x01;
+ public static final byte DES = 0x21;
+ public static final byte EC = 0x03;
+ public static final byte AES = 0x20;
+ public static final byte HMAC = (byte) 0x80;
+
+ // EcCurve Enum Tag key and values.
+ public static final short ECCURVE = 0x000A;
+ public static final byte P_224 = 0x00;
+ public static final byte P_256 = 0x01;
+ public static final byte P_384 = 0x02;
+ public static final byte P_521 = 0x03;
+ public static final byte CURVE_25519 = 0x04;
+
+ // KeyBlobUsageRequirements Enum Tag key and values.
+ public static final short BLOB_USAGE_REQ = 0x012D;
+ public static final byte STANDALONE = 0x00;
+ public static final byte REQUIRES_FILE_SYSTEM = 0x01;
+
+ // HardwareAuthenticatorType Enum Tag key and values.
+ public static final short USER_AUTH_TYPE = 0x01F8;
+ public static final byte USER_AUTH_NONE = 0x00;
+ public static final byte PASSWORD = 0x01;
+ public static final byte FINGERPRINT = 0x02;
+ public static final byte BOTH = 0x03;
+ // have to be power of 2
+ public static final byte ANY = (byte) 0xFF;
+
+ // Origin Enum Tag key and values.
+ public static final short ORIGIN = 0x02BE;
+ public static final byte GENERATED = 0x00;
+ public static final byte DERIVED = 0x01;
+ public static final byte IMPORTED = 0x02;
+ public static final byte UNKNOWN = 0x03;
+ public static final byte SECURELY_IMPORTED = 0x04;
+
+ // Hardware Type tag key and values
+ public static final short HARDWARE_TYPE = 0x0130;
+ public static final byte SOFTWARE = 0x00;
+ public static final byte TRUSTED_ENVIRONMENT = 0x01;
+ public static final byte STRONGBOX = 0x02;
+
+ // No Tag
+ // Derivation Function - No Tag defined
+ public static final short KEY_DERIVATION_FUNCTION = (short) 0xF001;
+ public static final byte DERIVATION_NONE = 0x00;
+ public static final byte RFC5869_SHA256 = 0x01;
+ public static final byte ISO18033_2_KDF1_SHA1 = 0x02;
+ public static final byte ISO18033_2_KDF1_SHA256 = 0x03;
+ public static final byte ISO18033_2_KDF2_SHA1 = 0x04;
+ public static final byte ISO18033_2_KDF2_SHA256 = 0x05;
+
+ // KeyFormat - No Tag defined.
+ public static final short KEY_FORMAT = (short) 0xF002;
+ public static final byte X509 = 0x00;
+ public static final byte PKCS8 = 0x01;
+ public static final byte RAW = 0x03;
+
+ // Verified Boot State
+ public static final short VERIFIED_BOOT_STATE = (short) 0xF003;
+ public static final byte VERIFIED_BOOT = 0x00;
+ public static final byte SELF_SIGNED_BOOT = 0x01;
+ public static final byte UNVERIFIED_BOOT = 0x02;
+ public static final byte FAILED_BOOT = 0x03;
+
+ // Device Locked
+ public static final short DEVICE_LOCKED = (short) 0xF006;
+ public static final byte DEVICE_LOCKED_TRUE = 0x01;
+ public static final byte DEVICE_LOCKED_FALSE = 0x00;
+
+ // Enum Array Tag
+ // Purpose
+ public static final short PURPOSE = 0x0001;
+ public static final byte ENCRYPT = 0x00;
+ public static final byte DECRYPT = 0x01;
+ public static final byte SIGN = 0x02;
+ public static final byte VERIFY = 0x03;
+ public static final byte DERIVE_KEY = 0x04;
+ public static final byte WRAP_KEY = 0x05;
+ public static final byte AGREE_KEY = 0x06;
+ public static final byte ATTEST_KEY = (byte) 0x07;
+ // Block mode
+ public static final short BLOCK_MODE = 0x0004;
+ public static final byte ECB = 0x01;
+ public static final byte CBC = 0x02;
+ public static final byte CTR = 0x03;
+ public static final byte GCM = 0x20;
+
+ // Digest
+ public static final short DIGEST = 0x0005;
+ public static final byte DIGEST_NONE = 0x00;
+ public static final byte MD5 = 0x01;
+ public static final byte SHA1 = 0x02;
+ public static final byte SHA2_224 = 0x03;
+ public static final byte SHA2_256 = 0x04;
+ public static final byte SHA2_384 = 0x05;
+ public static final byte SHA2_512 = 0x06;
+
+ // Padding mode
+ public static final short PADDING = 0x0006;
+ public static final byte PADDING_NONE = 0x01;
+ public static final byte RSA_OAEP = 0x02;
+ public static final byte RSA_PSS = 0x03;
+ public static final byte RSA_PKCS1_1_5_ENCRYPT = 0x04;
+ public static final byte RSA_PKCS1_1_5_SIGN = 0x05;
+ public static final byte PKCS7 = 0x40;
+
+ // OAEP MGF Digests - only SHA-1 is supported in Javacard
+ public static final short RSA_OAEP_MGF_DIGEST = 0xCB;
+
+ // Integer Tag - UINT, ULONG and DATE
+ // UINT tags
+ // Keysize
+ public static final short KEYSIZE = 0x0003;
+ // Min Mac Length
+ public static final short MIN_MAC_LENGTH = 0x0008;
+ // Min Seconds between OPS
+ public static final short MIN_SEC_BETWEEN_OPS = 0x0193;
+ // Max Uses per Boot
+ public static final short MAX_USES_PER_BOOT = 0x0194;
+ // UserId
+ public static final short USERID = 0x01F5;
+ // Auth Timeout
+ public static final short AUTH_TIMEOUT = 0x01F9;
+ // Auth Timeout in Milliseconds
+ public static final short AUTH_TIMEOUT_MILLIS = 0x7FFF;
+ // OS Version
+ public static final short OS_VERSION = 0x02C1;
+ // OS Patch Level
+ public static final short OS_PATCH_LEVEL = 0x02C2;
+ // Vendor Patch Level
+ public static final short VENDOR_PATCH_LEVEL = 0x02CE;
+ // Boot Patch Level
+ public static final short BOOT_PATCH_LEVEL = 0x02CF;
+ // Mac Length
+ public static final short MAC_LENGTH = 0x03EB;
+ // Usage Count Limit
+ public static final short USAGE_COUNT_LIMIT = 0x195;
+
+ // ULONG tags
+ // RSA Public Exponent
+ public static final short RSA_PUBLIC_EXPONENT = 0x00C8;
+
+ // DATE tags
+ public static final short ACTIVE_DATETIME = 0x0190;
+ public static final short ORIGINATION_EXPIRE_DATETIME = 0x0191;
+ public static final short USAGE_EXPIRE_DATETIME = 0x0192;
+ public static final short CREATION_DATETIME = 0x02BD;
+ ;
+ public static final short CERTIFICATE_NOT_BEFORE = 0x03F0;
+ public static final short CERTIFICATE_NOT_AFTER = 0x03F1;
+ // Integer Array Tags - ULONG_REP and UINT_REP.
+ // User Secure Id
+ public static final short USER_SECURE_ID = (short) 0x01F6;
+
+ // Boolean Tag
+ // Caller Nonce
+ public static final short CALLER_NONCE = (short) 0x0007;
+ // Include Unique Id
+ public static final short INCLUDE_UNIQUE_ID = (short) 0x00CA;
+ // Bootloader Only
+ public static final short BOOTLOADER_ONLY = (short) 0x012E;
+ // Rollback Resistance
+ public static final short ROLLBACK_RESISTANCE = (short) 0x012F;
+ // No Auth Required
+ public static final short NO_AUTH_REQUIRED = (short) 0x01F7;
+ // Allow While On Body
+ public static final short ALLOW_WHILE_ON_BODY = (short) 0x01FA;
+ // Max Boot Level
+ public static final short MAX_BOOT_LEVEL = (short) 0x03F2;
+ // Trusted User Presence Required
+ public static final short TRUSTED_USER_PRESENCE_REQUIRED = (short) 0x01FB;
+ // Trusted Confirmation Required
+ public static final short TRUSTED_CONFIRMATION_REQUIRED = (short) 0x01FC;
+ // Unlocked Device Required
+ public static final short UNLOCKED_DEVICE_REQUIRED = (short) 0x01FD;
+ // Reset Since Id Rotation
+ public static final short RESET_SINCE_ID_ROTATION = (short) 0x03EC;
+ // Early boot ended.
+ public static final short EARLY_BOOT_ONLY = (short) 0x0131;
+ // Device unique attestation.
+ public static final short DEVICE_UNIQUE_ATTESTATION = (short) 0x02D0;
+
+ // Byte Tag
+ // Application Id
+ public static final short APPLICATION_ID = (short) 0x0259;
+ // Application Data
+ public static final short APPLICATION_DATA = (short) 0x02BC;
+ // Root Of Trust
+ public static final short ROOT_OF_TRUST = (short) 0x02C0;
+ // Unique Id
+ public static final short UNIQUE_ID = (short) 0x02C3;
+ // Attestation Challenge
+ public static final short ATTESTATION_CHALLENGE = (short) 0x02C4;
+ // Attestation Application Id
+ public static final short ATTESTATION_APPLICATION_ID = (short) 0x02C5;
+ // Attestation Id Brand
+ public static final short ATTESTATION_ID_BRAND = (short) 0x02C6;
+ // Attestation Id Device
+ public static final short ATTESTATION_ID_DEVICE = (short) 0x02C7;
+ // Attestation Id Product
+ public static final short ATTESTATION_ID_PRODUCT = (short) 0x02C8;
+ // Attestation Id Serial
+ public static final short ATTESTATION_ID_SERIAL = (short) 0x02C9;
+ // Attestation Id IMEI
+ public static final short ATTESTATION_ID_IMEI = (short) 0x02CA;
+ // Attestation Id MEID
+ public static final short ATTESTATION_ID_MEID = (short) 0x02CB;
+ // Attestation Id Manufacturer
+ public static final short ATTESTATION_ID_MANUFACTURER = (short) 0x02CC;
+ // Attestation Id Model
+ public static final short ATTESTATION_ID_MODEL = (short) 0x02CD;
+ // Associated Data
+ public static final short ASSOCIATED_DATA = (short) 0x03E8;
+ // Nonce
+ public static final short NONCE = (short) 0x03E9;
+ // Confirmation Token
+ public static final short CONFIRMATION_TOKEN = (short) 0x03ED;
+ // Serial Number - this is a big num but in applet we handle it as byte blob
+ public static final short CERTIFICATE_SERIAL_NUM = (short) 0x03EE;
+ // Subject Name
+ public static final short CERTIFICATE_SUBJECT_NAME = (short) 0x03EF;
+
+ public static final short LENGTH_FROM_PDU = (short) 0xFFFF;
+
+ public static final byte NO_VALUE = (byte) 0xff;
+ // Support Curves for Eek Chain validation.
+ public static final byte RKP_CURVE_P256 = 1;
+ // Type offsets.
+ public static final byte KM_TYPE_BASE_OFFSET = 0;
+ public static final byte KM_ARRAY_OFFSET = KM_TYPE_BASE_OFFSET;
+ public static final byte KM_BOOL_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 1;
+ public static final byte KM_BYTE_BLOB_OFFSET = KM_TYPE_BASE_OFFSET + 2;
+ public static final byte KM_BYTE_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 3;
+ public static final byte KM_ENUM_OFFSET = KM_TYPE_BASE_OFFSET + 4;
+ public static final byte KM_ENUM_ARRAY_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 5;
+ public static final byte KM_ENUM_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 6;
+ public static final byte KM_HARDWARE_AUTH_TOKEN_OFFSET = KM_TYPE_BASE_OFFSET + 7;
+ public static final byte KM_HMAC_SHARING_PARAMETERS_OFFSET = KM_TYPE_BASE_OFFSET + 8;
+ public static final byte KM_INTEGER_OFFSET = KM_TYPE_BASE_OFFSET + 9;
+ public static final byte KM_INTEGER_ARRAY_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 10;
+ public static final byte KM_INTEGER_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 11;
+ public static final byte KM_KEY_CHARACTERISTICS_OFFSET = KM_TYPE_BASE_OFFSET + 12;
+ public static final byte KM_KEY_PARAMETERS_OFFSET = KM_TYPE_BASE_OFFSET + 13;
+ public static final byte KM_VERIFICATION_TOKEN_OFFSET = KM_TYPE_BASE_OFFSET + 14;
+ public static final byte KM_NEG_INTEGER_OFFSET = KM_TYPE_BASE_OFFSET + 15;
+ public static final byte KM_TEXT_STRING_OFFSET = KM_TYPE_BASE_OFFSET + 16;
+ public static final byte KM_MAP_OFFSET = KM_TYPE_BASE_OFFSET + 17;
+ public static final byte KM_COSE_KEY_OFFSET = KM_TYPE_BASE_OFFSET + 18;
+ public static final byte KM_COSE_KEY_INT_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 19;
+ public static final byte KM_COSE_KEY_NINT_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 20;
+ public static final byte KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 21;
+ public static final byte KM_COSE_KEY_COSE_KEY_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 22;
+ public static final byte KM_COSE_KEY_SIMPLE_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 23;
+ public static final byte KM_SIMPLE_VALUE_OFFSET = KM_TYPE_BASE_OFFSET + 24;
+ public static final byte KM_COSE_HEADERS_OFFSET = KM_TYPE_BASE_OFFSET + 25;
+ public static final byte KM_COSE_KEY_TXT_STR_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 26;
+ public static final byte KM_COSE_CERT_PAYLOAD_OFFSET = KM_TYPE_BASE_OFFSET + 27;
+ public static final byte KM_BIGNUM_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 28;
+ public static final byte KM_SEMANTIC_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 29;
+
+ // Attestation types
+ public static final byte NO_CERT = 0;
+ public static final byte ATTESTATION_CERT = 1;
+ public static final byte SELF_SIGNED_CERT = 2;
+ public static final byte FAKE_CERT = 3;
+ // Buffering Mode
+ public static final byte BUF_NONE = 0;
+ public static final byte BUF_RSA_DECRYPT_OR_NO_DIGEST = 1;
+ public static final byte BUF_EC_NO_DIGEST = 2;
+ public static final byte BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGN = 3;
+ public static final byte BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGN = 4;
+ public static final byte BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGN = 5;
+ public static final byte BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGN = 6;
+ public static final byte BUF_AES_GCM_DECRYPT_BLOCK_ALIGN = 7;
+
+ // MAX ApplicationID or Application Data size
+ public static final byte MAX_APP_ID_APP_DATA_SIZE = 64;
+ // Max attestation challenge size.
+ public static final short MAX_ATTESTATION_CHALLENGE_SIZE = 128;
+ // Max certificate serial size.
+ public static final byte MAX_CERTIFICATE_SERIAL_SIZE = 20;
+ // Attestation Application ID
+ public static final short MAX_ATTESTATION_APP_ID_SIZE = 1024;
+ // Instance table
+ public static final byte INSTANCE_TABLE_SIZE = 30;
+ protected static final byte TLV_HEADER_SIZE = 3;
+ protected static KMRepository repository;
+ protected static byte[] heap;
+ protected static short[] instanceTable;
+
+ public static void initialize() {
+ instanceTable = JCSystem.makeTransientShortArray(INSTANCE_TABLE_SIZE, JCSystem.CLEAR_ON_RESET);
+ KMType.repository = KMRepository.instance();
+ KMType.heap = repository.getHeap();
+ }
+
+ public static byte getType(short ptr) {
+ return heap[ptr];
+ }
+
+ public static short length(short ptr) {
+ return Util.getShort(heap, (short) (ptr + 1));
+ }
+
+ public static short getValue(short ptr) {
+ return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ }
+
+ protected static short instance(byte type, short length) {
+ if (length < 0) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ short ptr = repository.alloc((short) (length + TLV_HEADER_SIZE));
+ heap[ptr] = type;
+ Util.setShort(heap, (short) (ptr + 1), length);
+ return ptr;
+ }
+
+ protected static short exp(byte type) {
+ short ptr = repository.alloc(TLV_HEADER_SIZE);
+ heap[ptr] = type;
+ Util.setShort(heap, (short) (ptr + 1), INVALID_VALUE);
+ return ptr;
+ }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java
new file mode 100644
index 0000000..590e73a
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java
@@ -0,0 +1,129 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMVerificationToken represents VerificationToken structure from android keymaster hal
+ * specifications. It corresponds to CBOR array type. struct{byte type=VERIFICATION_TOKEN_TYPE;
+ * short length=2; short arrayPtr} where arrayPtr is a pointer to ordered array with following
+ * elements: {KMInteger Challenge; KMInteger Timestamp; KMByteBlob PARAMETERS_VERIFIED;
+ * SecurityLevel level; KMByteBlob Mac}.
+ */
+public class KMVerificationToken extends KMType {
+
+ public static final byte CHALLENGE = 0x00;
+ public static final byte TIMESTAMP = 0x01;
+ public static final byte MAC = 0x02;
+
+ private static KMVerificationToken prototype;
+
+ private KMVerificationToken() {}
+
+ public static short exp() {
+ short arrPtr = KMArray.instance((short) 3);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add(CHALLENGE, KMInteger.exp());
+ arr.add(TIMESTAMP, KMInteger.exp());
+ arr.add(MAC, KMByteBlob.exp());
+ return instance(arrPtr);
+ }
+
+ private static KMVerificationToken proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMVerificationToken();
+ }
+ KMType.instanceTable[KM_VERIFICATION_TOKEN_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short instance() {
+ short arrPtr = KMArray.instance((short) 3);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add(CHALLENGE, KMInteger.uint_16((short) 0));
+ arr.add(TIMESTAMP, KMInteger.uint_16((short) 0));
+ arr.add(MAC, KMByteBlob.instance((short) 0));
+ return instance(arrPtr);
+ }
+
+ public static short instance(short vals) {
+ KMArray arr = KMArray.cast(vals);
+ if (arr.length() != 3) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ short ptr = KMType.instance(VERIFICATION_TOKEN_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+ return ptr;
+ }
+
+ public static KMVerificationToken cast(short ptr) {
+ if (heap[ptr] != VERIFICATION_TOKEN_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (heap[arrPtr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public short getVals() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_VERIFICATION_TOKEN_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short length() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).length();
+ }
+
+ public short getChallenge() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(CHALLENGE);
+ }
+
+ public void setChallenge(short vals) {
+ KMInteger.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(CHALLENGE, vals);
+ }
+
+ public short getTimestamp() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(TIMESTAMP);
+ }
+
+ public void setTimestamp(short vals) {
+ KMInteger.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(TIMESTAMP, vals);
+ }
+
+ public short getMac() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(MAC);
+ }
+
+ public void setMac(short vals) {
+ KMByteBlob.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(MAC, vals);
+ }
+}
diff --git a/ready_se/google/keymint/KM200/HAL/.clang-format b/ready_se/google/keymint/KM200/HAL/.clang-format
new file mode 100644
index 0000000..b0dc94c
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/.clang-format
@@ -0,0 +1,10 @@
+BasedOnStyle: LLVM
+IndentWidth: 4
+UseTab: Never
+BreakBeforeBraces: Attach
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: true
+IndentCaseLabels: false
+ColumnLimit: 100
+PointerBindsToType: true
+SpacesBeforeTrailingComments: 2
diff --git a/ready_se/google/keymint/KM200/HAL/Android.bp b/ready_se/google/keymint/KM200/HAL/Android.bp
new file mode 100644
index 0000000..b05bb3e
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/Android.bp
@@ -0,0 +1,140 @@
+// 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" 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 {
+ default_applicable_licenses: [
+ "external_libese_ready_se_google_keymint_KM200_HAL_license",
+ ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "external_libese_ready_se_google_keymint_KM200_HAL_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "LICENSE",
+ ],
+}
+
+cc_library {
+ name: "libjc_keymint",
+ defaults: [
+ "keymaster_defaults",
+ "keymint_use_latest_hal_aidl_ndk_shared",
+ ],
+ srcs: [
+ "CborConverter.cpp",
+ "JavacardKeyMintDevice.cpp",
+ "JavacardKeyMintOperation.cpp",
+ "JavacardRemotelyProvisionedComponentDevice.cpp",
+ "JavacardSecureElement.cpp",
+ "JavacardSharedSecret.cpp",
+ "keymint_utils.cpp",
+ ],
+ cflags: ["-O0"],
+ shared_libs: [
+ "android.hardware.security.secureclock-V1-ndk",
+ "android.hardware.security.sharedsecret-V1-ndk",
+ "android.hardware.security.rkp-V3-ndk",
+ "lib_android_keymaster_keymint_utils",
+ "libbase",
+ "libcppbor_external",
+ "libkeymaster_portable",
+ "libkeymaster_messages",
+ "libsoft_attestation_cert",
+ "liblog",
+ "libcrypto",
+ "libcutils",
+ "libjc_keymint_transport",
+ "libbinder_ndk",
+ ],
+ export_include_dirs: [
+ ".",
+ ],
+ vendor_available: true,
+}
+
+cc_library {
+ name: "libjc_keymint_transport",
+ vendor_available: true,
+ defaults: [
+ "keymint_use_latest_hal_aidl_ndk_shared",
+ ],
+ srcs: [
+ "SocketTransport.cpp",
+ "OmapiTransport.cpp",
+ ],
+ export_include_dirs: [
+ ".",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libbase",
+ "liblog",
+ "libbinder_ndk",
+ "android.se.omapi-V1-ndk",
+ "libhardware",
+ ],
+}
+
+cc_binary {
+ name: "android.hardware.security.keymint-service.strongbox",
+ relative_install_path: "hw",
+ init_rc: ["android.hardware.security.keymint-service.strongbox.rc"],
+ vintf_fragments: [
+ "android.hardware.security.keymint-service.strongbox.xml",
+ "android.hardware.security.sharedsecret-service.strongbox.xml",
+ ],
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ ],
+ defaults: [
+ "keymint_use_latest_hal_aidl_ndk_shared",
+ ],
+ shared_libs: [
+ "android.hardware.security.sharedsecret-V1-ndk",
+ "lib_android_keymaster_keymint_utils",
+ "android.hardware.security.rkp-V3-ndk",
+ "libbase",
+ "libbinder_ndk",
+ "libcppbor_external",
+ "libcrypto",
+ "libkeymaster_portable",
+ "libjc_keymint",
+ "libjc_keymint_transport",
+ "liblog",
+ "libutils",
+ "android.se.omapi-V1-ndk",
+ ],
+ srcs: [
+ "service.cpp",
+ ],
+ required: [
+ "android.hardware.hardware_keystore.jc-strongbox-keymint.xml",
+ ],
+}
+
+prebuilt_etc {
+ name: "android.hardware.hardware_keystore.jc-strongbox-keymint.xml",
+ sub_dir: "permissions",
+ vendor: true,
+ src: "android.hardware.hardware_keystore.jc-strongbox-keymint.xml",
+}
diff --git a/ready_se/google/keymint/KM200/HAL/CborConverter.cpp b/ready_se/google/keymint/KM200/HAL/CborConverter.cpp
new file mode 100644
index 0000000..d7e6c11
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/CborConverter.cpp
@@ -0,0 +1,512 @@
+/*
+ **
+ ** Copyright 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" 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.
+ */
+
+#include "CborConverter.h"
+
+#include <map>
+#include <string>
+
+#include <android-base/logging.h>
+
+#include <KeyMintUtils.h>
+
+namespace keymint::javacard {
+using ::aidl::android::hardware::security::keymint::KeyParameterValue;
+using ::aidl::android::hardware::security::keymint::SecurityLevel;
+using ::aidl::android::hardware::security::keymint::km_utils::kmParam2Aidl;
+using ::aidl::android::hardware::security::keymint::km_utils::legacy_enum_conversion;
+using ::aidl::android::hardware::security::keymint::km_utils::typeFromTag;
+
+constexpr int SB_ENFORCED = 0;
+constexpr int TEE_ENFORCED = 1;
+constexpr int SW_ENFORCED = 2;
+
+namespace {
+
+template <KeyParameterValue::Tag aidl_tag>
+std::optional<uint32_t> aidlEnumVal2Uint32(const KeyParameterValue& value) {
+ return (value.getTag() == aidl_tag)
+ ? std::optional(static_cast<uint32_t>(value.get<aidl_tag>()))
+ : std::nullopt;
+}
+
+std::optional<uint32_t> aidlEnumParam2Uint32(const KeyParameter& param) {
+ auto tag = legacy_enum_conversion(param.tag);
+ switch (tag) {
+ case KM_TAG_PURPOSE:
+ return aidlEnumVal2Uint32<KeyParameterValue::keyPurpose>(param.value);
+ case KM_TAG_ALGORITHM:
+ return aidlEnumVal2Uint32<KeyParameterValue::algorithm>(param.value);
+ case KM_TAG_BLOCK_MODE:
+ return aidlEnumVal2Uint32<KeyParameterValue::blockMode>(param.value);
+ case KM_TAG_DIGEST:
+ case KM_TAG_RSA_OAEP_MGF_DIGEST:
+ return aidlEnumVal2Uint32<KeyParameterValue::digest>(param.value);
+ case KM_TAG_PADDING:
+ return aidlEnumVal2Uint32<KeyParameterValue::paddingMode>(param.value);
+ case KM_TAG_EC_CURVE:
+ return aidlEnumVal2Uint32<KeyParameterValue::ecCurve>(param.value);
+ case KM_TAG_USER_AUTH_TYPE:
+ return aidlEnumVal2Uint32<KeyParameterValue::hardwareAuthenticatorType>(param.value);
+ case KM_TAG_ORIGIN:
+ return aidlEnumVal2Uint32<KeyParameterValue::origin>(param.value);
+ case KM_TAG_BLOB_USAGE_REQUIREMENTS:
+ case KM_TAG_KDF:
+ default:
+ CHECK(false) << "Unknown or unused enum tag: Something is broken";
+ return std::nullopt;
+ }
+}
+
+} // namespace
+
+bool CborConverter::addAttestationKey(Array& array,
+ const std::optional<AttestationKey>& attestationKey) {
+ if (attestationKey.has_value()) {
+ array.add(Bstr(attestationKey->keyBlob));
+ addKeyparameters(array, attestationKey->attestKeyParams);
+ array.add(Bstr(attestationKey->issuerSubjectName));
+ } else {
+ array.add(std::move(Bstr(vector<uint8_t>(0))));
+ array.add(std::move(Map()));
+ array.add(std::move(Bstr(vector<uint8_t>(0))));
+ }
+ return true;
+}
+
+bool CborConverter::addKeyparameters(Array& array, const vector<KeyParameter>& keyParams) {
+ Map map;
+ std::map<uint64_t, vector<uint8_t>> enum_repetition;
+ std::map<uint64_t, Array> uint_repetition;
+ for (auto& param : keyParams) {
+ auto tag = legacy_enum_conversion(param.tag);
+ switch (typeFromTag(tag)) {
+ case KM_ENUM: {
+ auto paramEnum = aidlEnumParam2Uint32(param);
+ if (paramEnum.has_value()) {
+ map.add(static_cast<uint64_t>(tag), *paramEnum);
+ }
+ break;
+ }
+ case KM_UINT:
+ if (param.value.getTag() == KeyParameterValue::integer) {
+ auto intVal = param.value.get<KeyParameterValue::integer>();
+ map.add(static_cast<uint64_t>(tag), intVal);
+ }
+ break;
+ case KM_UINT_REP:
+ if (param.value.getTag() == KeyParameterValue::integer) {
+ auto intVal = param.value.get<KeyParameterValue::integer>();
+ uint_repetition[static_cast<uint64_t>(tag)].add(intVal);
+ }
+ break;
+ case KM_ENUM_REP: {
+ auto paramEnumRep = aidlEnumParam2Uint32(param);
+ if (paramEnumRep.has_value()) {
+ enum_repetition[static_cast<uint64_t>(tag)].push_back(*paramEnumRep);
+ }
+ break;
+ }
+ case KM_ULONG:
+ if (param.value.getTag() == KeyParameterValue::longInteger) {
+ auto longVal = param.value.get<KeyParameterValue::longInteger>();
+ map.add(static_cast<uint64_t>(tag), longVal);
+ }
+ break;
+ case KM_ULONG_REP:
+ if (param.value.getTag() == KeyParameterValue::longInteger) {
+ auto longVal = param.value.get<KeyParameterValue::longInteger>();
+ uint_repetition[static_cast<uint64_t>(tag & 0x00000000ffffffff)].add(longVal);
+ }
+ break;
+ case KM_DATE:
+ if (param.value.getTag() == KeyParameterValue::dateTime) {
+ auto dateVal = param.value.get<KeyParameterValue::dateTime>();
+ map.add(static_cast<uint64_t>(tag), dateVal);
+ }
+ break;
+ case KM_BOOL:
+ map.add(static_cast<uint64_t>(tag), 1 /* true */);
+ break;
+ case KM_BIGNUM:
+ case KM_BYTES:
+ if (param.value.getTag() == KeyParameterValue::blob) {
+ const auto& value = param.value.get<KeyParameterValue::blob>();
+ map.add(static_cast<uint64_t>(tag & 0x00000000ffffffff), value);
+ }
+ break;
+ case KM_INVALID:
+ break;
+ }
+ }
+
+ for (auto const& [key, val] : enum_repetition) {
+ Bstr bstr(val);
+ map.add(key, std::move(bstr));
+ }
+
+ for (auto& [key, val] : uint_repetition) {
+ map.add(key, std::move(val));
+ }
+ array.add(std::move(map));
+ return true;
+}
+
+// Array of three maps
+std::optional<vector<KeyCharacteristics>>
+CborConverter::getKeyCharacteristics(const unique_ptr<Item>& item, const uint32_t pos) {
+ vector<KeyCharacteristics> keyCharacteristics;
+ auto arrayItem = getItemAtPos(item, pos);
+ if (!arrayItem || (MajorType::ARRAY != getType(arrayItem.value()))) {
+ return std::nullopt;
+ }
+ KeyCharacteristics swEnf{SecurityLevel::KEYSTORE, {}};
+ KeyCharacteristics teeEnf{SecurityLevel::TRUSTED_ENVIRONMENT, {}};
+ KeyCharacteristics sbEnf{SecurityLevel::STRONGBOX, {}};
+
+ auto optSbEnf = getKeyParameters(arrayItem.value(), SB_ENFORCED);
+ if (!optSbEnf) {
+ return std::nullopt;
+ }
+ sbEnf.authorizations = std::move(optSbEnf.value());
+ auto optTeeEnf = getKeyParameters(arrayItem.value(), TEE_ENFORCED);
+ if (!optTeeEnf) {
+ return std::nullopt;
+ }
+ teeEnf.authorizations = std::move(optTeeEnf.value());
+ auto optSwEnf = getKeyParameters(arrayItem.value(), SW_ENFORCED);
+ if (!optSwEnf) {
+ return std::nullopt;
+ }
+ swEnf.authorizations = std::move(optSwEnf.value());
+ // VTS will fail if the authorizations list is empty.
+ if (!sbEnf.authorizations.empty()) keyCharacteristics.push_back(std::move(sbEnf));
+ if (!teeEnf.authorizations.empty()) keyCharacteristics.push_back(std::move(teeEnf));
+ if (!swEnf.authorizations.empty()) keyCharacteristics.push_back(std::move(swEnf));
+ return keyCharacteristics;
+}
+
+std::optional<std::vector<KeyParameter>> CborConverter::getKeyParameter(
+ const std::pair<const std::unique_ptr<Item>&, const std::unique_ptr<Item>&> pair) {
+ std::vector<KeyParameter> keyParams;
+ keymaster_tag_t key;
+ auto optValue = getUint64(pair.first);
+ if (!optValue) {
+ return std::nullopt;
+ }
+ key = static_cast<keymaster_tag_t>(optValue.value());
+ switch (keymaster_tag_get_type(key)) {
+ case KM_ENUM_REP: {
+ /* ENUM_REP contains values encoded in a Byte string */
+ const Bstr* bstr = pair.second.get()->asBstr();
+ if (bstr == nullptr) {
+ return std::nullopt;
+ }
+ for (auto bchar : bstr->value()) {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ keyParam.enumerated = bchar;
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ }
+ return keyParams;
+ }
+ case KM_ENUM: {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ if (!(optValue = getUint64(pair.second))) {
+ return std::nullopt;
+ }
+ keyParam.enumerated = static_cast<uint32_t>(optValue.value());
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ return keyParams;
+ }
+ case KM_UINT: {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ if (!(optValue = getUint64(pair.second))) {
+ return std::nullopt;
+ }
+ keyParam.integer = static_cast<uint32_t>(optValue.value());
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ return keyParams;
+ }
+ case KM_ULONG: {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ if (!(optValue = getUint64(pair.second))) {
+ return std::nullopt;
+ }
+ keyParam.long_integer = optValue.value();
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ return keyParams;
+ }
+ case KM_UINT_REP: {
+ /* UINT_REP contains values encoded in a Array */
+ Array* array = const_cast<Array*>(pair.second.get()->asArray());
+ if (array == nullptr) return std::nullopt;
+ for (int i = 0; i < array->size(); i++) {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ const std::unique_ptr<Item>& item = array->get(i);
+ if (!(optValue = getUint64(item))) {
+ return std::nullopt;
+ }
+ keyParam.integer = static_cast<uint32_t>(optValue.value());
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ }
+ return keyParams;
+ }
+ case KM_ULONG_REP: {
+ /* ULONG_REP contains values encoded in a Array */
+ Array* array = const_cast<Array*>(pair.second.get()->asArray());
+ if (array == nullptr) return std::nullopt;
+ for (int i = 0; i < array->size(); i++) {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ const std::unique_ptr<Item>& item = array->get(i);
+ if (!(optValue = getUint64(item))) {
+ return std::nullopt;
+ }
+ keyParam.long_integer = optValue.value();
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ }
+ return keyParams;
+ }
+ case KM_DATE: {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ if (!(optValue = getUint64(pair.second))) {
+ return std::nullopt;
+ }
+ keyParam.date_time = optValue.value();
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ return keyParams;
+ }
+ case KM_BOOL: {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ if (!(optValue = getUint64(pair.second))) {
+ return std::nullopt;
+ }
+ // If a tag with this type is present, the value is true. If absent, false.
+ keyParam.boolean = true;
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ return keyParams;
+ }
+ case KM_BIGNUM:
+ case KM_BYTES: {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ const Bstr* bstr = pair.second.get()->asBstr();
+ if (bstr == nullptr) return std::nullopt;
+ keyParam.blob.data = bstr->value().data();
+ keyParam.blob.data_length = bstr->value().size();
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ return keyParams;
+ }
+ case KM_INVALID:
+ break;
+ }
+ return std::nullopt;
+}
+
+// array of a blobs
+std::optional<vector<Certificate>>
+CborConverter::getCertificateChain(const std::unique_ptr<Item>& item, const uint32_t pos) {
+ vector<Certificate> certChain;
+ auto arrayItem = getItemAtPos(item, pos);
+ if (!arrayItem || (MajorType::ARRAY != getType(arrayItem.value()))) return std::nullopt;
+
+ const Array* arr = arrayItem.value().get()->asArray();
+ for (int i = 0; i < arr->size(); i++) {
+ Certificate cert;
+ auto optTemp = getByteArrayVec(arrayItem.value(), i);
+ if (!optTemp) return std::nullopt;
+ cert.encodedCertificate = std::move(optTemp.value());
+ certChain.push_back(std::move(cert));
+ }
+ return certChain;
+}
+
+std::optional<string> CborConverter::getByteArrayStr(const unique_ptr<Item>& item,
+ const uint32_t pos) {
+ auto optTemp = getByteArrayVec(item, pos);
+ if (!optTemp) {
+ return std::nullopt;
+ }
+ std::string str(optTemp->begin(), optTemp->end());
+ return str;
+}
+
+std::optional<std::vector<uint8_t>> CborConverter::getByteArrayVec(const unique_ptr<Item>& item,
+ const uint32_t pos) {
+ auto strItem = getItemAtPos(item, pos);
+ if (!strItem || (MajorType::BSTR != getType(strItem.value()))) {
+ return std::nullopt;
+ }
+ const Bstr* bstr = strItem.value().get()->asBstr();
+ return bstr->value();
+}
+
+std::optional<SharedSecretParameters>
+CborConverter::getSharedSecretParameters(const unique_ptr<Item>& item, const uint32_t pos) {
+ SharedSecretParameters params;
+ // Array [seed, nonce]
+ auto arrayItem = getItemAtPos(item, pos);
+ if (!arrayItem || (MajorType::ARRAY != getType(arrayItem.value()))) {
+ return std::nullopt;
+ }
+ auto optSeed = getByteArrayVec(arrayItem.value(), 0);
+ auto optNonce = getByteArrayVec(arrayItem.value(), 1);
+ if (!optSeed || !optNonce) {
+ return std::nullopt;
+ }
+ params.seed = std::move(optSeed.value());
+ params.nonce = std::move(optNonce.value());
+ return params;
+}
+
+bool CborConverter::addSharedSecretParameters(Array& array,
+ const vector<SharedSecretParameters>& params) {
+ Array cborParamsVec;
+ for (auto param : params) {
+ Array cborParam;
+ cborParam.add(Bstr(param.seed));
+ cborParam.add(Bstr(param.nonce));
+ cborParamsVec.add(std::move(cborParam));
+ }
+ array.add(std::move(cborParamsVec));
+ return true;
+}
+
+bool CborConverter::addTimeStampToken(Array& array, const TimeStampToken& token) {
+ Array vToken;
+ vToken.add(static_cast<uint64_t>(token.challenge));
+ vToken.add(static_cast<uint64_t>(token.timestamp.milliSeconds));
+ vToken.add((std::vector<uint8_t>(token.mac)));
+ array.add(std::move(vToken));
+ return true;
+}
+
+bool CborConverter::addHardwareAuthToken(Array& array, const HardwareAuthToken& authToken) {
+
+ Array hwAuthToken;
+ hwAuthToken.add(static_cast<uint64_t>(authToken.challenge));
+ hwAuthToken.add(static_cast<uint64_t>(authToken.userId));
+ hwAuthToken.add(static_cast<uint64_t>(authToken.authenticatorId));
+ hwAuthToken.add(static_cast<uint64_t>(authToken.authenticatorType));
+ hwAuthToken.add(static_cast<uint64_t>(authToken.timestamp.milliSeconds));
+ hwAuthToken.add((std::vector<uint8_t>(authToken.mac)));
+ array.add(std::move(hwAuthToken));
+ return true;
+}
+
+std::optional<TimeStampToken> CborConverter::getTimeStampToken(const unique_ptr<Item>& item,
+ const uint32_t pos) {
+ TimeStampToken token;
+ // {challenge, timestamp, Mac}
+ auto optChallenge = getUint64(item, pos);
+ auto optTimestampMillis = getUint64(item, pos + 1);
+ auto optTemp = getByteArrayVec(item, pos + 2);
+ if (!optChallenge || !optTimestampMillis || !optTemp) {
+ return std::nullopt;
+ }
+ token.mac = std::move(optTemp.value());
+ token.challenge = static_cast<long>(std::move(optChallenge.value()));
+ token.timestamp.milliSeconds = static_cast<long>(std::move(optTimestampMillis.value()));
+ return token;
+}
+
+std::optional<Array> CborConverter::getArrayItem(const std::unique_ptr<Item>& item,
+ const uint32_t pos) {
+ Array array;
+ auto arrayItem = getItemAtPos(item, pos);
+ if (!arrayItem || (MajorType::ARRAY != getType(arrayItem.value()))) {
+ return std::nullopt;
+ }
+ array = std::move(*(arrayItem.value().get()->asArray()));
+ return array;
+}
+
+std::optional<Map> CborConverter::getMapItem(const std::unique_ptr<Item>& item,
+ const uint32_t pos) {
+ Map map;
+ auto mapItem = getItemAtPos(item, pos);
+ if (!mapItem || (MajorType::MAP != getType(mapItem.value()))) {
+ return std::nullopt;
+ }
+ map = std::move(*(mapItem.value().get()->asMap()));
+ return map;
+}
+
+std::optional<vector<KeyParameter>> CborConverter::getKeyParameters(const unique_ptr<Item>& item,
+ const uint32_t pos) {
+ vector<KeyParameter> params;
+ auto mapItem = getItemAtPos(item, pos);
+ if (!mapItem || (MajorType::MAP != getType(mapItem.value()))) return std::nullopt;
+ const Map* map = mapItem.value().get()->asMap();
+ size_t mapSize = map->size();
+ for (int i = 0; i < mapSize; i++) {
+ auto optKeyParams = getKeyParameter((*map)[i]);
+ if (optKeyParams) {
+ params.insert(params.end(), optKeyParams->begin(), optKeyParams->end());
+ } else {
+ return std::nullopt;
+ }
+ }
+ return params;
+}
+
+std::tuple<std::unique_ptr<Item>, keymaster_error_t>
+CborConverter::decodeData(const std::vector<uint8_t>& response) {
+ auto [item, pos, message] = cppbor::parse(response);
+ if (!item || MajorType::ARRAY != getType(item)) {
+ return {nullptr, KM_ERROR_UNKNOWN_ERROR};
+ }
+ auto optErrorCode = getErrorCode(item, 0);
+ if (!optErrorCode) {
+ return {nullptr, KM_ERROR_UNKNOWN_ERROR};
+ }
+ return {std::move(item), optErrorCode.value()};
+}
+
+std::optional<keymaster_error_t>
+CborConverter::getErrorCode(const std::unique_ptr<cppbor::Item>& item, const uint32_t pos) {
+ auto optErrorVal = getUint64(item, pos);
+ if (!optErrorVal) {
+ return std::nullopt;
+ }
+ return static_cast<keymaster_error_t>(0 - optErrorVal.value());
+}
+
+std::optional<uint64_t> CborConverter::getUint64(const unique_ptr<Item>& item) {
+ if ((item == nullptr) || (MajorType::UINT != getType(item))) {
+ return std::nullopt;
+ }
+ const Uint* uintVal = item.get()->asUint();
+ return uintVal->unsignedValue();
+}
+
+std::optional<uint64_t> CborConverter::getUint64(const unique_ptr<Item>& item, const uint32_t pos) {
+ auto intItem = getItemAtPos(item, pos);
+ if (!intItem) {
+ return std::nullopt;
+ }
+ return getUint64(intItem.value());
+}
+
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM200/HAL/CborConverter.h b/ready_se/google/keymint/KM200/HAL/CborConverter.h
new file mode 100644
index 0000000..b49273b
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/CborConverter.h
@@ -0,0 +1,139 @@
+/*
+ **
+ ** Copyright 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" 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.
+ */
+#pragma once
+
+#include <iostream>
+#include <memory>
+#include <numeric>
+#include <vector>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+#include <aidl/android/hardware/security/keymint/Certificate.h>
+#include <aidl/android/hardware/security/keymint/IKeyMintDevice.h>
+#include <aidl/android/hardware/security/secureclock/TimeStampToken.h>
+#include <aidl/android/hardware/security/sharedsecret/ISharedSecret.h>
+
+#include <keymaster/android_keymaster_messages.h>
+
+namespace keymint::javacard {
+using aidl::android::hardware::security::keymint::AttestationKey;
+using aidl::android::hardware::security::keymint::Certificate;
+using aidl::android::hardware::security::keymint::HardwareAuthToken;
+using aidl::android::hardware::security::keymint::KeyCharacteristics;
+using aidl::android::hardware::security::keymint::KeyParameter;
+using aidl::android::hardware::security::secureclock::TimeStampToken;
+using aidl::android::hardware::security::sharedsecret::SharedSecretParameters;
+using cppbor::Array;
+using cppbor::Bstr;
+using cppbor::EncodedItem;
+using cppbor::Item;
+using cppbor::MajorType;
+using cppbor::Map;
+using cppbor::Nint;
+using cppbor::Uint;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+class CborConverter {
+ public:
+ CborConverter() = default;
+
+ ~CborConverter() = default;
+
+ std::tuple<std::unique_ptr<Item>, keymaster_error_t>
+ decodeData(const std::vector<uint8_t>& response);
+
+ std::optional<uint64_t> getUint64(const unique_ptr<Item>& item);
+
+ std::optional<uint64_t> getUint64(const unique_ptr<Item>& item, const uint32_t pos);
+
+ std::optional<SharedSecretParameters>
+ getSharedSecretParameters(const std::unique_ptr<Item>& item, const uint32_t pos);
+
+ std::optional<string> getByteArrayStr(const unique_ptr<Item>& item, const uint32_t pos);
+
+ std::optional<std::vector<uint8_t>> getByteArrayVec(const unique_ptr<Item>& item,
+ const uint32_t pos);
+
+ std::optional<vector<KeyParameter>> getKeyParameters(const unique_ptr<Item>& item,
+ const uint32_t pos);
+
+ bool addKeyparameters(Array& array, const vector<KeyParameter>& keyParams);
+
+ bool addAttestationKey(Array& array, const std::optional<AttestationKey>& attestationKey);
+
+ bool addHardwareAuthToken(Array& array, const HardwareAuthToken& authToken);
+
+ bool addSharedSecretParameters(Array& array, const vector<SharedSecretParameters>& params);
+
+ std::optional<TimeStampToken> getTimeStampToken(const std::unique_ptr<Item>& item,
+ const uint32_t pos);
+
+ std::optional<vector<KeyCharacteristics>>
+ getKeyCharacteristics(const std::unique_ptr<Item>& item, const uint32_t pos);
+
+ std::optional<vector<Certificate>> getCertificateChain(const std::unique_ptr<Item>& item,
+ const uint32_t pos);
+
+ std::optional<vector<vector<uint8_t>>> getMultiByteArray(const unique_ptr<Item>& item,
+ const uint32_t pos);
+
+ bool addTimeStampToken(Array& array, const TimeStampToken& token);
+
+ std::optional<Map> getMapItem(const std::unique_ptr<Item>& item, const uint32_t pos);
+
+ std::optional<Array> getArrayItem(const std::unique_ptr<Item>& item, const uint32_t pos);
+
+ std::optional<keymaster_error_t> getErrorCode(const std::unique_ptr<Item>& item,
+ const uint32_t pos);
+
+ private:
+ /**
+ * Get the type of the Item pointer.
+ */
+ inline MajorType getType(const unique_ptr<Item>& item) { return item.get()->type(); }
+
+ /**
+ * Construct Keyparameter structure from the pair of key and value. If TagType is ENUM_REP the
+ * value contains binary string. If TagType is UINT_REP or ULONG_REP the value contains Array of
+ * unsigned integers.
+ */
+ std::optional<std::vector<KeyParameter>> getKeyParameter(
+ const std::pair<const std::unique_ptr<Item>&, const std::unique_ptr<Item>&> pair);
+
+ /**
+ * Get the sub item pointer from the root item pointer at the given position.
+ */
+ inline std::optional<unique_ptr<Item>> getItemAtPos(const unique_ptr<Item>& item,
+ const uint32_t pos) {
+ Array* arr = nullptr;
+
+ if (MajorType::ARRAY != getType(item)) {
+ return std::nullopt;
+ }
+ arr = const_cast<Array*>(item.get()->asArray());
+ if (arr->size() < (pos + 1)) {
+ return std::nullopt;
+ }
+ return std::move((*arr)[pos]);
+ }
+};
+
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM200/HAL/ITransport.h b/ready_se/google/keymint/KM200/HAL/ITransport.h
new file mode 100644
index 0000000..ca100be
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/ITransport.h
@@ -0,0 +1,55 @@
+/*
+ **
+ ** Copyright 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" 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.
+ */
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include <hardware/keymaster_defs.h>
+
+namespace keymint::javacard {
+using std::shared_ptr;
+using std::vector;
+
+/**
+ * ITransport is an interface with a set of virtual methods that allow communication between the
+ * HAL and the applet on the secure element.
+ */
+class ITransport {
+ public:
+ virtual ~ITransport() {}
+
+ /**
+ * Opens connection.
+ */
+ virtual keymaster_error_t openConnection() = 0;
+ /**
+ * Send data over communication channel and receives data back from the remote end.
+ */
+ virtual keymaster_error_t sendData(const vector<uint8_t>& inData, vector<uint8_t>& output) = 0;
+ /**
+ * Closes the connection.
+ */
+ virtual keymaster_error_t closeConnection() = 0;
+ /**
+ * Returns the state of the connection status. Returns true if the connection is active, false
+ * if connection is broken.
+ */
+ virtual bool isConnected() = 0;
+};
+
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM200/HAL/JavacardKeyMintDevice.cpp b/ready_se/google/keymint/KM200/HAL/JavacardKeyMintDevice.cpp
new file mode 100644
index 0000000..bd68b48
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/JavacardKeyMintDevice.cpp
@@ -0,0 +1,454 @@
+/*
+ * Copyright 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" 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.
+ */
+
+#define LOG_TAG "javacard.keymint.device.strongbox-impl"
+
+#include "JavacardKeyMintDevice.h"
+
+#include <regex.h>
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <KeyMintUtils.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <hardware/hw_auth_token.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/wrapped_key.h>
+
+#include "JavacardKeyMintOperation.h"
+#include "JavacardSharedSecret.h"
+
+namespace aidl::android::hardware::security::keymint {
+using cppbor::Bstr;
+using cppbor::EncodedItem;
+using cppbor::Uint;
+using ::keymaster::AuthorizationSet;
+using ::keymaster::dup_buffer;
+using ::keymaster::KeymasterBlob;
+using ::keymaster::KeymasterKeyBlob;
+using ::keymint::javacard::Instruction;
+using std::string;
+
+ScopedAStatus JavacardKeyMintDevice::defaultHwInfo(KeyMintHardwareInfo* info) {
+ info->versionNumber = 2;
+ info->keyMintAuthorName = "Google";
+ info->keyMintName = "JavacardKeymintDevice";
+ info->securityLevel = securitylevel_;
+ info->timestampTokenRequired = true;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) {
+ auto [item, err] = card_->sendRequest(Instruction::INS_GET_HW_INFO_CMD);
+ std::optional<string> optKeyMintName;
+ std::optional<string> optKeyMintAuthorName;
+ std::optional<uint64_t> optSecLevel;
+ std::optional<uint64_t> optVersion;
+ std::optional<uint64_t> optTsRequired;
+ if (err != KM_ERROR_OK || !(optVersion = cbor_.getUint64(item, 1)) ||
+ !(optSecLevel = cbor_.getUint64(item, 2)) ||
+ !(optKeyMintName = cbor_.getByteArrayStr(item, 3)) ||
+ !(optKeyMintAuthorName = cbor_.getByteArrayStr(item, 4)) ||
+ !(optTsRequired = cbor_.getUint64(item, 5))) {
+ LOG(ERROR) << "Error in response of getHardwareInfo.";
+ LOG(INFO) << "Returning defaultHwInfo in getHardwareInfo.";
+ return defaultHwInfo(info);
+ }
+ card_->initializeJavacard();
+ info->keyMintName = std::move(optKeyMintName.value());
+ info->keyMintAuthorName = std::move(optKeyMintAuthorName.value());
+ info->timestampTokenRequired = (optTsRequired.value() == 1);
+ info->securityLevel = static_cast<SecurityLevel>(std::move(optSecLevel.value()));
+ info->versionNumber = static_cast<int32_t>(std::move(optVersion.value()));
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::generateKey(const vector<KeyParameter>& keyParams,
+ const optional<AttestationKey>& attestationKey,
+ KeyCreationResult* creationResult) {
+ cppbor::Array array;
+ // add key params
+ cbor_.addKeyparameters(array, keyParams);
+ // add attestation key if any
+ cbor_.addAttestationKey(array, attestationKey);
+ auto [item, err] = card_->sendRequest(Instruction::INS_GENERATE_KEY_CMD, array);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending generateKey.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ auto optKeyBlob = cbor_.getByteArrayVec(item, 1);
+ auto optKeyChars = cbor_.getKeyCharacteristics(item, 2);
+ auto optCertChain = cbor_.getCertificateChain(item, 3);
+ if (!optKeyBlob || !optKeyChars || !optCertChain) {
+ LOG(ERROR) << "Error in decoding og response in generateKey.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ creationResult->keyCharacteristics = std::move(optKeyChars.value());
+ creationResult->certificateChain = std::move(optCertChain.value());
+ creationResult->keyBlob = std::move(optKeyBlob.value());
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::addRngEntropy(const vector<uint8_t>& data) {
+ cppbor::Array request;
+ // add key data
+ request.add(Bstr(data));
+ auto [item, err] = card_->sendRequest(Instruction::INS_ADD_RNG_ENTROPY_CMD, request);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending addRngEntropy.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::importKey(const vector<KeyParameter>& keyParams,
+ KeyFormat keyFormat, const vector<uint8_t>& keyData,
+ const optional<AttestationKey>& attestationKey,
+ KeyCreationResult* creationResult) {
+
+ cppbor::Array request;
+ // add key params
+ cbor_.addKeyparameters(request, keyParams);
+ // add key format
+ request.add(Uint(static_cast<uint8_t>(keyFormat)));
+ // add key data
+ request.add(Bstr(keyData));
+ // add attestation key if any
+ cbor_.addAttestationKey(request, attestationKey);
+
+ auto [item, err] = card_->sendRequest(Instruction::INS_IMPORT_KEY_CMD, request);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending data in importKey.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ auto optKeyBlob = cbor_.getByteArrayVec(item, 1);
+ auto optKeyChars = cbor_.getKeyCharacteristics(item, 2);
+ auto optCertChain = cbor_.getCertificateChain(item, 3);
+ if (!optKeyBlob || !optKeyChars || !optCertChain) {
+ LOG(ERROR) << "Error in decoding response in importKey.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ creationResult->keyCharacteristics = std::move(optKeyChars.value());
+ creationResult->certificateChain = std::move(optCertChain.value());
+ creationResult->keyBlob = std::move(optKeyBlob.value());
+ return ScopedAStatus::ok();
+}
+
+// import wrapped key is divided into 2 stage operation.
+ScopedAStatus JavacardKeyMintDevice::importWrappedKey(const vector<uint8_t>& wrappedKeyData,
+ const vector<uint8_t>& wrappingKeyBlob,
+ const vector<uint8_t>& maskingKey,
+ const vector<KeyParameter>& unwrappingParams,
+ int64_t passwordSid, int64_t biometricSid,
+ KeyCreationResult* creationResult) {
+ cppbor::Array request;
+ std::unique_ptr<Item> item;
+ vector<uint8_t> keyBlob;
+ std::vector<uint8_t> response;
+ vector<KeyCharacteristics> keyCharacteristics;
+ std::vector<uint8_t> iv;
+ std::vector<uint8_t> transitKey;
+ std::vector<uint8_t> secureKey;
+ std::vector<uint8_t> tag;
+ vector<KeyParameter> authList;
+ KeyFormat keyFormat;
+ std::vector<uint8_t> wrappedKeyDescription;
+ keymaster_error_t errorCode = parseWrappedKey(wrappedKeyData, iv, transitKey, secureKey, tag,
+ authList, keyFormat, wrappedKeyDescription);
+ if (errorCode != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in parse wrapped key in importWrappedKey.";
+ return km_utils::kmError2ScopedAStatus(errorCode);
+ }
+
+ // begin import
+ std::tie(item, errorCode) =
+ sendBeginImportWrappedKeyCmd(transitKey, wrappingKeyBlob, maskingKey, unwrappingParams);
+ if (errorCode != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in send begin import wrapped key in importWrappedKey.";
+ return km_utils::kmError2ScopedAStatus(errorCode);
+ }
+ // Finish the import
+ std::tie(item, errorCode) = sendFinishImportWrappedKeyCmd(
+ authList, keyFormat, secureKey, tag, iv, wrappedKeyDescription, passwordSid, biometricSid);
+ if (errorCode != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in send finish import wrapped key in importWrappedKey.";
+ return km_utils::kmError2ScopedAStatus(errorCode);
+ }
+ auto optKeyBlob = cbor_.getByteArrayVec(item, 1);
+ auto optKeyChars = cbor_.getKeyCharacteristics(item, 2);
+ auto optCertChain = cbor_.getCertificateChain(item, 3);
+ if (!optKeyBlob || !optKeyChars || !optCertChain) {
+ LOG(ERROR) << "Error in decoding the response in importWrappedKey.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ creationResult->keyCharacteristics = std::move(optKeyChars.value());
+ creationResult->certificateChain = std::move(optCertChain.value());
+ creationResult->keyBlob = std::move(optKeyBlob.value());
+ return ScopedAStatus::ok();
+}
+
+std::tuple<std::unique_ptr<Item>, keymaster_error_t>
+JavacardKeyMintDevice::sendBeginImportWrappedKeyCmd(const std::vector<uint8_t>& transitKey,
+ const std::vector<uint8_t>& wrappingKeyBlob,
+ const std::vector<uint8_t>& maskingKey,
+ const vector<KeyParameter>& unwrappingParams) {
+ Array request;
+ request.add(std::vector<uint8_t>(transitKey));
+ request.add(std::vector<uint8_t>(wrappingKeyBlob));
+ request.add(std::vector<uint8_t>(maskingKey));
+ cbor_.addKeyparameters(request, unwrappingParams);
+ return card_->sendRequest(Instruction::INS_BEGIN_IMPORT_WRAPPED_KEY_CMD, request);
+}
+
+std::tuple<std::unique_ptr<Item>, keymaster_error_t>
+JavacardKeyMintDevice::sendFinishImportWrappedKeyCmd(
+ const vector<KeyParameter>& keyParams, KeyFormat keyFormat,
+ const std::vector<uint8_t>& secureKey, const std::vector<uint8_t>& tag,
+ const std::vector<uint8_t>& iv, const std::vector<uint8_t>& wrappedKeyDescription,
+ int64_t passwordSid, int64_t biometricSid) {
+ Array request;
+ cbor_.addKeyparameters(request, keyParams);
+ request.add(static_cast<uint64_t>(keyFormat));
+ request.add(std::vector<uint8_t>(secureKey));
+ request.add(std::vector<uint8_t>(tag));
+ request.add(std::vector<uint8_t>(iv));
+ request.add(std::vector<uint8_t>(wrappedKeyDescription));
+ request.add(Uint(passwordSid));
+ request.add(Uint(biometricSid));
+ return card_->sendRequest(Instruction::INS_FINISH_IMPORT_WRAPPED_KEY_CMD, request);
+}
+
+ScopedAStatus JavacardKeyMintDevice::upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,
+ const vector<KeyParameter>& upgradeParams,
+ vector<uint8_t>* keyBlob) {
+ cppbor::Array request;
+ // add key blob
+ request.add(Bstr(keyBlobToUpgrade));
+ // add key params
+ cbor_.addKeyparameters(request, upgradeParams);
+ auto [item, err] = card_->sendRequest(Instruction::INS_UPGRADE_KEY_CMD, request);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in upgradeKey.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ auto optKeyBlob = cbor_.getByteArrayVec(item, 1);
+ if (!optKeyBlob) {
+ LOG(ERROR) << "Error in decoding the response in upgradeKey.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ *keyBlob = std::move(optKeyBlob.value());
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::deleteKey(const vector<uint8_t>& keyBlob) {
+ Array request;
+ request.add(Bstr(keyBlob));
+ auto [item, err] = card_->sendRequest(Instruction::INS_DELETE_KEY_CMD, request);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in deleteKey.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::deleteAllKeys() {
+ auto [item, err] = card_->sendRequest(Instruction::INS_DELETE_ALL_KEYS_CMD);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in deleteAllKeys.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::destroyAttestationIds() {
+ auto [item, err] = card_->sendRequest(Instruction::INS_DESTROY_ATT_IDS_CMD);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in destroyAttestationIds.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::begin(KeyPurpose purpose, const std::vector<uint8_t>& keyBlob,
+ const std::vector<KeyParameter>& params,
+ const std::optional<HardwareAuthToken>& authToken,
+ BeginResult* result) {
+
+ cppbor::Array array;
+ std::vector<uint8_t> response;
+ // make request
+ array.add(Uint(static_cast<uint64_t>(purpose)));
+ array.add(Bstr(keyBlob));
+ cbor_.addKeyparameters(array, params);
+ HardwareAuthToken token = authToken.value_or(HardwareAuthToken());
+ cbor_.addHardwareAuthToken(array, token);
+
+ // Send earlyBootEnded if there is any pending earlybootEnded event.
+ auto retErr = card_->sendEarlyBootEndedEvent(false);
+ if (retErr != KM_ERROR_OK) {
+ return km_utils::kmError2ScopedAStatus(retErr);
+ ;
+ }
+
+ auto [item, err] = card_->sendRequest(Instruction::INS_BEGIN_OPERATION_CMD, array);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in begin.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ // return the result
+ auto keyParams = cbor_.getKeyParameters(item, 1);
+ auto optOpHandle = cbor_.getUint64(item, 2);
+ auto optBufMode = cbor_.getUint64(item, 3);
+ auto optMacLength = cbor_.getUint64(item, 4);
+
+ if (!keyParams || !optOpHandle || !optBufMode || !optMacLength) {
+ LOG(ERROR) << "Error in decoding the response in begin.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ result->params = std::move(keyParams.value());
+ result->challenge = optOpHandle.value();
+ result->operation = ndk::SharedRefBase::make<JavacardKeyMintOperation>(
+ static_cast<keymaster_operation_handle_t>(optOpHandle.value()),
+ static_cast<BufferingMode>(optBufMode.value()), optMacLength.value(), card_);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus
+JavacardKeyMintDevice::deviceLocked(bool passwordOnly,
+ const std::optional<TimeStampToken>& timestampToken) {
+ Array request;
+ int8_t password = 1;
+ if (!passwordOnly) {
+ password = 0;
+ }
+ request.add(Uint(password));
+ cbor_.addTimeStampToken(request, timestampToken.value_or(TimeStampToken()));
+ auto [item, err] = card_->sendRequest(Instruction::INS_DEVICE_LOCKED_CMD, request);
+ if (err != KM_ERROR_OK) {
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::earlyBootEnded() {
+ auto err = card_->sendEarlyBootEndedEvent(true);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending earlyBootEndedEvent.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::getKeyCharacteristics(
+ const std::vector<uint8_t>& keyBlob, const std::vector<uint8_t>& appId,
+ const std::vector<uint8_t>& appData, std::vector<KeyCharacteristics>* result) {
+ cppbor::Array request;
+ request.add(vector<uint8_t>(keyBlob));
+ request.add(vector<uint8_t>(appId));
+ request.add(vector<uint8_t>(appData));
+ auto [item, err] = card_->sendRequest(Instruction::INS_GET_KEY_CHARACTERISTICS_CMD, request);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in getKeyCharacteristics.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ auto optKeyChars = cbor_.getKeyCharacteristics(item, 1);
+ if (!optKeyChars) {
+ LOG(ERROR) << "Error in sending in upgradeKey.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ *result = std::move(optKeyChars.value());
+ return ScopedAStatus::ok();
+}
+
+keymaster_error_t
+JavacardKeyMintDevice::parseWrappedKey(const vector<uint8_t>& wrappedKeyData,
+ std::vector<uint8_t>& iv, std::vector<uint8_t>& transitKey,
+ std::vector<uint8_t>& secureKey, std::vector<uint8_t>& tag,
+ vector<KeyParameter>& authList, KeyFormat& keyFormat,
+ std::vector<uint8_t>& wrappedKeyDescription) {
+ KeymasterBlob kmIv;
+ KeymasterKeyBlob kmTransitKey;
+ KeymasterKeyBlob kmSecureKey;
+ KeymasterBlob kmTag;
+ AuthorizationSet authSet;
+ keymaster_key_format_t kmKeyFormat;
+ KeymasterBlob kmWrappedKeyDescription;
+
+ size_t keyDataLen = wrappedKeyData.size();
+ uint8_t* keyData = dup_buffer(wrappedKeyData.data(), keyDataLen);
+ keymaster_key_blob_t keyMaterial = {keyData, keyDataLen};
+ keymaster_error_t error =
+ parse_wrapped_key(KeymasterKeyBlob(keyMaterial), &kmIv, &kmTransitKey, &kmSecureKey, &kmTag,
+ &authSet, &kmKeyFormat, &kmWrappedKeyDescription);
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "Error parsing wrapped key.";
+ return error;
+ }
+ iv = km_utils::kmBlob2vector(kmIv);
+ transitKey = km_utils::kmBlob2vector(kmTransitKey);
+ secureKey = km_utils::kmBlob2vector(kmSecureKey);
+ tag = km_utils::kmBlob2vector(kmTag);
+ authList = km_utils::kmParamSet2Aidl(authSet);
+ keyFormat = static_cast<KeyFormat>(kmKeyFormat);
+ wrappedKeyDescription = km_utils::kmBlob2vector(kmWrappedKeyDescription);
+ return KM_ERROR_OK;
+}
+
+ScopedAStatus JavacardKeyMintDevice::convertStorageKeyToEphemeral(
+ const std::vector<uint8_t>& /* storageKeyBlob */,
+ std::vector<uint8_t>* /* ephemeralKeyBlob */) {
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
+ScopedAStatus JavacardKeyMintDevice::getRootOfTrustChallenge(array<uint8_t, 16>* challenge) {
+ auto [item, err] = card_->sendRequest(Instruction::INS_GET_ROT_CHALLENGE_CMD);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in getRootOfTrustChallenge.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ auto optChallenge = cbor_.getByteArrayVec(item, 1);
+ if (!optChallenge) {
+ LOG(ERROR) << "Error in sending in upgradeKey.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ std::move(optChallenge->begin(), optChallenge->begin() + 16, challenge->begin());
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::getRootOfTrust(const array<uint8_t, 16>& /*challenge*/,
+ vector<uint8_t>* /*rootOfTrust*/) {
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
+ScopedAStatus JavacardKeyMintDevice::sendRootOfTrust(const vector<uint8_t>& rootOfTrust) {
+ cppbor::Array request;
+ request.add(EncodedItem(rootOfTrust)); // taggedItem.
+ auto [item, err] = card_->sendRequest(Instruction::INS_SEND_ROT_DATA_CMD, request);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in sendRootOfTrust.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ LOG(INFO) << "JavacardKeyMintDevice::sendRootOfTrust success";
+ return ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/ready_se/google/keymint/KM200/HAL/JavacardKeyMintDevice.h b/ready_se/google/keymint/KM200/HAL/JavacardKeyMintDevice.h
new file mode 100644
index 0000000..adf0f7d
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/JavacardKeyMintDevice.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 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" 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/security/keymint/BnKeyMintDevice.h>
+#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
+#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
+#include <aidl/android/hardware/security/sharedsecret/SharedSecretParameters.h>
+
+#include "CborConverter.h"
+#include "JavacardSecureElement.h"
+
+namespace aidl::android::hardware::security::keymint {
+using cppbor::Item;
+using ::keymint::javacard::CborConverter;
+using ::keymint::javacard::JavacardSecureElement;
+using ndk::ScopedAStatus;
+using secureclock::TimeStampToken;
+using std::array;
+using std::optional;
+using std::shared_ptr;
+using std::vector;
+
+class JavacardKeyMintDevice : public BnKeyMintDevice {
+ public:
+ explicit JavacardKeyMintDevice(shared_ptr<JavacardSecureElement> card)
+ : securitylevel_(SecurityLevel::STRONGBOX), card_(card) {
+ card_->initializeJavacard();
+ }
+ virtual ~JavacardKeyMintDevice() {}
+
+ ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* info) override;
+
+ ScopedAStatus addRngEntropy(const vector<uint8_t>& data) override;
+
+ ScopedAStatus generateKey(const vector<KeyParameter>& keyParams,
+ const optional<AttestationKey>& attestationKey,
+ KeyCreationResult* creationResult) override;
+
+ ScopedAStatus importKey(const vector<KeyParameter>& keyParams, KeyFormat keyFormat,
+ const vector<uint8_t>& keyData,
+ const optional<AttestationKey>& attestationKey,
+ KeyCreationResult* creationResult) override;
+
+ ScopedAStatus importWrappedKey(const vector<uint8_t>& wrappedKeyData,
+ const vector<uint8_t>& wrappingKeyBlob,
+ const vector<uint8_t>& maskingKey,
+ const vector<KeyParameter>& unwrappingParams,
+ int64_t passwordSid, int64_t biometricSid,
+ KeyCreationResult* creationResult) override;
+
+ ScopedAStatus upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,
+ const vector<KeyParameter>& upgradeParams,
+ vector<uint8_t>* keyBlob) override;
+
+ ScopedAStatus deleteKey(const vector<uint8_t>& keyBlob) override;
+ ScopedAStatus deleteAllKeys() override;
+ ScopedAStatus destroyAttestationIds() override;
+
+ virtual ScopedAStatus begin(KeyPurpose in_purpose, const std::vector<uint8_t>& in_keyBlob,
+ const std::vector<KeyParameter>& in_params,
+ const std::optional<HardwareAuthToken>& in_authToken,
+ BeginResult* _aidl_return) override;
+
+ ScopedAStatus deviceLocked(bool passwordOnly,
+ const optional<TimeStampToken>& timestampToken) override;
+
+ ScopedAStatus earlyBootEnded() override;
+
+ ScopedAStatus getKeyCharacteristics(const std::vector<uint8_t>& in_keyBlob,
+ const std::vector<uint8_t>& in_appId,
+ const std::vector<uint8_t>& in_appData,
+ std::vector<KeyCharacteristics>* _aidl_return) override;
+
+ ScopedAStatus convertStorageKeyToEphemeral(const std::vector<uint8_t>& storageKeyBlob,
+ std::vector<uint8_t>* ephemeralKeyBlob) override;
+
+ ScopedAStatus getRootOfTrustChallenge(array<uint8_t, 16>* challenge) override;
+
+ ScopedAStatus getRootOfTrust(const array<uint8_t, 16>& challenge,
+ vector<uint8_t>* rootOfTrust) override;
+
+ ScopedAStatus sendRootOfTrust(const vector<uint8_t>& rootOfTrust) override;
+
+ private:
+ keymaster_error_t parseWrappedKey(const vector<uint8_t>& wrappedKeyData,
+ std::vector<uint8_t>& iv, std::vector<uint8_t>& transitKey,
+ std::vector<uint8_t>& secureKey, std::vector<uint8_t>& tag,
+ vector<KeyParameter>& authList, KeyFormat& keyFormat,
+ std::vector<uint8_t>& wrappedKeyDescription);
+
+ std::tuple<std::unique_ptr<Item>, keymaster_error_t> sendBeginImportWrappedKeyCmd(
+ const std::vector<uint8_t>& transitKey, const std::vector<uint8_t>& wrappingKeyBlob,
+ const std::vector<uint8_t>& maskingKey, const vector<KeyParameter>& unwrappingParams);
+
+ std::tuple<std::unique_ptr<Item>, keymaster_error_t>
+ sendFinishImportWrappedKeyCmd(const vector<KeyParameter>& keyParams, KeyFormat keyFormat,
+ const std::vector<uint8_t>& secureKey,
+ const std::vector<uint8_t>& tag, const std::vector<uint8_t>& iv,
+ const std::vector<uint8_t>& wrappedKeyDescription,
+ int64_t passwordSid, int64_t biometricSid);
+
+ ScopedAStatus defaultHwInfo(KeyMintHardwareInfo* info);
+
+ const SecurityLevel securitylevel_;
+ const shared_ptr<JavacardSecureElement> card_;
+ CborConverter cbor_;
+};
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/ready_se/google/keymint/KM200/HAL/JavacardKeyMintOperation.cpp b/ready_se/google/keymint/KM200/HAL/JavacardKeyMintOperation.cpp
new file mode 100644
index 0000000..a46f066
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/JavacardKeyMintOperation.cpp
@@ -0,0 +1,300 @@
+/*
+ * Copyright 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" 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.
+ */
+
+#define LOG_TAG "javacard.strongbox.keymint.operation-impl"
+
+#include "JavacardKeyMintOperation.h"
+
+#include <KeyMintUtils.h>
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/secureclock/ISecureClock.h>
+#include <android-base/logging.h>
+
+#include "CborConverter.h"
+
+namespace aidl::android::hardware::security::keymint {
+using cppbor::Bstr;
+using cppbor::Uint;
+using secureclock::TimeStampToken;
+
+JavacardKeyMintOperation::~JavacardKeyMintOperation() {
+ if (opHandle_ != 0) {
+ JavacardKeyMintOperation::abort();
+ }
+}
+
+ScopedAStatus JavacardKeyMintOperation::updateAad(const vector<uint8_t>& input,
+ const optional<HardwareAuthToken>& authToken,
+ const optional<TimeStampToken>& timestampToken) {
+ cppbor::Array request;
+ request.add(Uint(opHandle_));
+ request.add(Bstr(input));
+ cbor_.addHardwareAuthToken(request, authToken.value_or(HardwareAuthToken()));
+ cbor_.addTimeStampToken(request, timestampToken.value_or(TimeStampToken()));
+ auto [item, err] = card_->sendRequest(Instruction::INS_UPDATE_AAD_OPERATION_CMD, request);
+ if (err != KM_ERROR_OK) {
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintOperation::update(const vector<uint8_t>& input,
+ const optional<HardwareAuthToken>& authToken,
+ const optional<TimeStampToken>& timestampToken,
+ vector<uint8_t>* output) {
+ HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken());
+ TimeStampToken tToken = timestampToken.value_or(TimeStampToken());
+ DataView view = {.buffer = {}, .data = input, .start = 0, .length = input.size()};
+ keymaster_error_t err = bufferData(view);
+ if (err != KM_ERROR_OK) {
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ if (!(bufferingMode_ == BufferingMode::EC_NO_DIGEST ||
+ bufferingMode_ == BufferingMode::RSA_DECRYPT_OR_NO_DIGEST)) {
+ if (view.length > MAX_CHUNK_SIZE) {
+ err = updateInChunks(view, aToken, tToken, output);
+ if (err != KM_ERROR_OK) {
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ }
+ vector<uint8_t> remaining = popNextChunk(view, view.length);
+ err = sendUpdate(remaining, aToken, tToken, *output);
+ }
+ return km_utils::kmError2ScopedAStatus(err);
+}
+
+ScopedAStatus JavacardKeyMintOperation::finish(const optional<vector<uint8_t>>& input,
+ const optional<vector<uint8_t>>& signature,
+ const optional<HardwareAuthToken>& authToken,
+ const optional<TimeStampToken>& timestampToken,
+ const optional<vector<uint8_t>>& confirmationToken,
+ vector<uint8_t>* output) {
+ HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken());
+ TimeStampToken tToken = timestampToken.value_or(TimeStampToken());
+ const vector<uint8_t> confToken = confirmationToken.value_or(vector<uint8_t>());
+ const vector<uint8_t> inData = input.value_or(vector<uint8_t>());
+ DataView view = {.buffer = {}, .data = inData, .start = 0, .length = inData.size()};
+ const vector<uint8_t> sign = signature.value_or(vector<uint8_t>());
+ if (!(bufferingMode_ == BufferingMode::EC_NO_DIGEST ||
+ bufferingMode_ == BufferingMode::RSA_DECRYPT_OR_NO_DIGEST)) {
+ appendBufferedData(view);
+ if (view.length > MAX_CHUNK_SIZE) {
+ auto err = updateInChunks(view, aToken, tToken, output);
+ if (err != KM_ERROR_OK) {
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ }
+ } else {
+ keymaster_error_t err = bufferData(view);
+ if (err != KM_ERROR_OK) {
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ appendBufferedData(view);
+ }
+ vector<uint8_t> remaining = popNextChunk(view, view.length);
+ return km_utils::kmError2ScopedAStatus(
+ sendFinish(remaining, sign, aToken, tToken, confToken, *output));
+}
+
+ScopedAStatus JavacardKeyMintOperation::abort() {
+ Array request;
+ request.add(Uint(opHandle_));
+ auto [item, err] = card_->sendRequest(Instruction::INS_ABORT_OPERATION_CMD, request);
+ opHandle_ = 0;
+ buffer_.clear();
+ return km_utils::kmError2ScopedAStatus(err);
+}
+
+void JavacardKeyMintOperation::blockAlign(DataView& view, uint16_t blockSize) {
+ appendBufferedData(view);
+ uint16_t offset = getDataViewOffset(view, blockSize);
+ if (view.buffer.empty() && !view.data.empty()) {
+ buffer_.insert(buffer_.end(), view.data.begin() + offset, view.data.end());
+ } else if (view.data.empty() && !view.buffer.empty()) {
+ buffer_.insert(buffer_.end(), view.buffer.begin() + offset, view.buffer.end());
+ } else {
+ if (offset < view.buffer.size()) {
+ buffer_.insert(buffer_.end(), view.buffer.begin() + offset, view.buffer.end());
+ buffer_.insert(buffer_.end(), view.data.begin(), view.data.end());
+ } else {
+ offset = offset - view.buffer.size();
+ buffer_.insert(buffer_.end(), view.data.begin() + offset, view.data.end());
+ }
+ }
+ // adjust the view length by removing the buffered data size from it.
+ view.length = view.length - buffer_.size();
+}
+
+uint16_t JavacardKeyMintOperation::getDataViewOffset(DataView& view, uint16_t blockSize) {
+ uint16_t offset = 0;
+ uint16_t remaining = 0;
+ switch (bufferingMode_) {
+ case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED:
+ case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED:
+ offset = ((view.length / blockSize)) * blockSize;
+ remaining = (view.length % blockSize);
+ if (offset >= blockSize && remaining == 0) {
+ offset -= blockSize;
+ }
+ break;
+ case BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED:
+ case BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED:
+ offset = ((view.length / blockSize)) * blockSize;
+ break;
+ case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED:
+ if (view.length > macLength_) {
+ offset = (view.length - macLength_);
+ }
+ break;
+ default:
+ break;
+ }
+ return offset;
+}
+
+keymaster_error_t JavacardKeyMintOperation::bufferData(DataView& view) {
+ if (view.data.empty()) return KM_ERROR_OK; // nothing to buffer
+ switch (bufferingMode_) {
+ case BufferingMode::RSA_DECRYPT_OR_NO_DIGEST:
+ buffer_.insert(buffer_.end(), view.data.begin(), view.data.end());
+ if (buffer_.size() > RSA_BUFFER_SIZE) {
+ abort();
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+ }
+ view.start = 0;
+ view.length = 0;
+ break;
+ case BufferingMode::EC_NO_DIGEST:
+ if (buffer_.size() < EC_BUFFER_SIZE) {
+ buffer_.insert(buffer_.end(), view.data.begin(), view.data.end());
+ // Truncate the buffered data if greater then allowed EC buffer size.
+ if (buffer_.size() > EC_BUFFER_SIZE) {
+ buffer_.erase(buffer_.begin() + EC_BUFFER_SIZE, buffer_.end());
+ }
+ }
+ view.start = 0;
+ view.length = 0;
+ break;
+ case BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED:
+ case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED:
+ blockAlign(view, AES_BLOCK_SIZE);
+ break;
+ case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED:
+ blockAlign(view, macLength_);
+ break;
+ case BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED:
+ case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED:
+ blockAlign(view, DES_BLOCK_SIZE);
+ break;
+ case BufferingMode::NONE:
+ break;
+ }
+ return KM_ERROR_OK;
+}
+
+// Incrementally send the request using multiple updates.
+keymaster_error_t JavacardKeyMintOperation::updateInChunks(DataView& view,
+ HardwareAuthToken& authToken,
+ TimeStampToken& timestampToken,
+ vector<uint8_t>* output) {
+ keymaster_error_t sendError = KM_ERROR_UNKNOWN_ERROR;
+ while (view.length > MAX_CHUNK_SIZE) {
+ vector<uint8_t> chunk = popNextChunk(view, MAX_CHUNK_SIZE);
+ sendError = sendUpdate(chunk, authToken, timestampToken, *output);
+ if (sendError != KM_ERROR_OK) {
+ return sendError;
+ }
+ // Clear tokens
+ if (!authToken.mac.empty()) authToken = HardwareAuthToken();
+ if (!timestampToken.mac.empty()) timestampToken = TimeStampToken();
+ }
+ return KM_ERROR_OK;
+}
+
+vector<uint8_t> JavacardKeyMintOperation::popNextChunk(DataView& view, uint32_t chunkSize) {
+ uint32_t start = view.start;
+ uint32_t end = start + ((view.length < chunkSize) ? view.length : chunkSize);
+ vector<uint8_t> chunk;
+ if (start < view.buffer.size()) {
+ if (end < view.buffer.size()) {
+ chunk = {view.buffer.begin() + start, view.buffer.begin() + end};
+ } else {
+ end = end - view.buffer.size();
+ chunk = {view.buffer.begin() + start, view.buffer.end()};
+ chunk.insert(chunk.end(), view.data.begin(), view.data.begin() + end);
+ }
+ } else {
+ start = start - view.buffer.size();
+ end = end - view.buffer.size();
+ chunk = {view.data.begin() + start, view.data.begin() + end};
+ }
+ view.start = view.start + chunk.size();
+ view.length = view.length - chunk.size();
+ return chunk;
+}
+
+keymaster_error_t JavacardKeyMintOperation::sendUpdate(const vector<uint8_t>& input,
+ const HardwareAuthToken& authToken,
+ const TimeStampToken& timestampToken,
+ vector<uint8_t>& output) {
+ if (input.empty()) {
+ return KM_ERROR_OK;
+ }
+ cppbor::Array request;
+ request.add(Uint(opHandle_));
+ request.add(Bstr(input));
+ cbor_.addHardwareAuthToken(request, authToken);
+ cbor_.addTimeStampToken(request, timestampToken);
+ auto [item, error] = card_->sendRequest(Instruction::INS_UPDATE_OPERATION_CMD, request);
+ if (error != KM_ERROR_OK) {
+ return error;
+ }
+ auto optTemp = cbor_.getByteArrayVec(item, 1);
+ if (!optTemp) {
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ output.insert(output.end(), optTemp.value().begin(), optTemp.value().end());
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t JavacardKeyMintOperation::sendFinish(const vector<uint8_t>& data,
+ const vector<uint8_t>& sign,
+ const HardwareAuthToken& authToken,
+ const TimeStampToken& timestampToken,
+ const vector<uint8_t>& confToken,
+ vector<uint8_t>& output) {
+ cppbor::Array request;
+ request.add(Uint(opHandle_));
+ request.add(Bstr(data));
+ request.add(Bstr(sign));
+ cbor_.addHardwareAuthToken(request, authToken);
+ cbor_.addTimeStampToken(request, timestampToken);
+ request.add(Bstr(confToken));
+
+ auto [item, err] = card_->sendRequest(Instruction::INS_FINISH_OPERATION_CMD, request);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+ auto optTemp = cbor_.getByteArrayVec(item, 1);
+ if (!optTemp) {
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ opHandle_ = 0;
+ output.insert(output.end(), optTemp.value().begin(), optTemp.value().end());
+ return KM_ERROR_OK;
+}
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/ready_se/google/keymint/KM200/HAL/JavacardKeyMintOperation.h b/ready_se/google/keymint/KM200/HAL/JavacardKeyMintOperation.h
new file mode 100644
index 0000000..c1d967a
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/JavacardKeyMintOperation.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright 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" 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.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
+#include <aidl/android/hardware/security/secureclock/ISecureClock.h>
+#include <hardware/keymaster_defs.h>
+
+#include "CborConverter.h"
+#include "JavacardSecureElement.h"
+
+#define AES_BLOCK_SIZE 16
+#define DES_BLOCK_SIZE 8
+#define RSA_BUFFER_SIZE 256
+#define EC_BUFFER_SIZE 32
+#define MAX_CHUNK_SIZE 256
+
+namespace aidl::android::hardware::security::keymint {
+using cppbor::Array;
+using cppbor::Item;
+using ::keymint::javacard::CborConverter;
+using ::keymint::javacard::Instruction;
+using ::keymint::javacard::JavacardSecureElement;
+using ::ndk::ScopedAStatus;
+using secureclock::TimeStampToken;
+using std::optional;
+using std::shared_ptr;
+using std::vector;
+
+// Bufferig modes for update
+enum class BufferingMode : int32_t {
+ NONE = 0, // Send everything to javacard - most of the assymteric operations
+ RSA_DECRYPT_OR_NO_DIGEST =
+ 1, // Buffer everything in update upto 256 bytes and send in finish. If
+ // input data is greater then 256 bytes then it is an error. Javacard
+ // will further check according to exact key size and crypto provider.
+ EC_NO_DIGEST = 2, // Buffer upto 65 bytes and then truncate. Javacard will further truncate
+ // upto exact keysize.
+ BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED = 3, // Buffer 16 bytes.
+ BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED = 4, // Buffer 16 bytes.
+ BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED = 5, // Buffer 8 bytes.
+ BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED = 6, // Buffer 8 bytes.
+ BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED = 7, // Buffer 16 bytes.
+
+};
+
+// The is the view in the input data being processed by update/finish funcion.
+
+struct DataView {
+ vector<uint8_t> buffer; // previously buffered data from cycle n-1
+ const vector<uint8_t>& data; // current data in cycle n.
+ uint32_t start; // start of the view
+ size_t length; // length of the view
+};
+
+class JavacardKeyMintOperation : public BnKeyMintOperation {
+ public:
+ explicit JavacardKeyMintOperation(keymaster_operation_handle_t opHandle,
+ BufferingMode bufferingMode, uint16_t macLength,
+ shared_ptr<JavacardSecureElement> card)
+ : buffer_(vector<uint8_t>()), bufferingMode_(bufferingMode), macLength_(macLength),
+ card_(card), opHandle_(opHandle) {}
+ virtual ~JavacardKeyMintOperation();
+
+ ScopedAStatus updateAad(const vector<uint8_t>& input,
+ const optional<HardwareAuthToken>& authToken,
+ const optional<TimeStampToken>& timestampToken) override;
+
+ ScopedAStatus update(const vector<uint8_t>& input, const optional<HardwareAuthToken>& authToken,
+ const optional<TimeStampToken>& timestampToken,
+ vector<uint8_t>* output) override;
+
+ ScopedAStatus finish(const optional<vector<uint8_t>>& input,
+ const optional<vector<uint8_t>>& signature,
+ const optional<HardwareAuthToken>& authToken,
+ const optional<TimeStampToken>& timestampToken,
+ const optional<vector<uint8_t>>& confirmationToken,
+ vector<uint8_t>* output) override;
+
+ ScopedAStatus abort() override;
+
+ private:
+ vector<uint8_t> popNextChunk(DataView& view, uint32_t chunkSize);
+
+ keymaster_error_t updateInChunks(DataView& data, HardwareAuthToken& authToken,
+ TimeStampToken& timestampToken, vector<uint8_t>* output);
+
+ keymaster_error_t sendFinish(const vector<uint8_t>& data, const vector<uint8_t>& signature,
+ const HardwareAuthToken& authToken,
+ const TimeStampToken& timestampToken,
+ const vector<uint8_t>& confToken, vector<uint8_t>& output);
+
+ keymaster_error_t sendUpdate(const vector<uint8_t>& data, const HardwareAuthToken& authToken,
+ const TimeStampToken& timestampToken, vector<uint8_t>& output);
+
+ inline void appendBufferedData(DataView& view) {
+ if (!buffer_.empty()) {
+ view.buffer = buffer_;
+ view.length = view.length + buffer_.size();
+ view.start = 0;
+ // view.buffer = insert(data.begin(), buffer_.begin(), buffer_.end());
+ buffer_.clear();
+ }
+ }
+
+ std::tuple<std::unique_ptr<Item>, keymaster_error_t> sendRequest(Instruction ins,
+ Array& request);
+ keymaster_error_t bufferData(DataView& data);
+ void blockAlign(DataView& data, uint16_t blockSize);
+ uint16_t getDataViewOffset(DataView& view, uint16_t blockSize);
+
+ vector<uint8_t> buffer_;
+ BufferingMode bufferingMode_;
+ uint16_t macLength_;
+ const shared_ptr<JavacardSecureElement> card_;
+ keymaster_operation_handle_t opHandle_;
+ CborConverter cbor_;
+};
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/ready_se/google/keymint/KM200/HAL/JavacardRemotelyProvisionedComponentDevice.cpp b/ready_se/google/keymint/KM200/HAL/JavacardRemotelyProvisionedComponentDevice.cpp
new file mode 100644
index 0000000..fe9821b
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/JavacardRemotelyProvisionedComponentDevice.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2021, 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.
+ */
+
+#define LOG_TAG "javacard.keymint.device.rkp.strongbox-impl"
+
+#include "JavacardRemotelyProvisionedComponentDevice.h"
+
+#include <aidl/android/hardware/security/keymint/MacedPublicKey.h>
+
+#include <KeyMintUtils.h>
+#include <android-base/logging.h>
+#include <keymaster/cppcose/cppcose.h>
+#include <keymaster/remote_provisioning_utils.h>
+
+namespace aidl::android::hardware::security::keymint {
+using cppbor::Array;
+using cppbor::EncodedItem;
+using cppcose::kCoseMac0EntryCount;
+using cppcose::kCoseMac0Payload;
+using ::keymint::javacard::Instruction;
+using std::string;
+
+// RKP error codes defined in keymint applet.
+constexpr int32_t kStatusFailed = 32000;
+constexpr int32_t kStatusInvalidMac = 32001;
+constexpr int32_t kStatusProductionKeyInTestRequest = 32002;
+constexpr int32_t kStatusTestKeyInProductionRequest = 32003;
+constexpr int32_t kStatusInvalidEek = 32004;
+constexpr int32_t kStatusInvalidState = 32005;
+
+namespace {
+
+keymaster_error_t translateRkpErrorCode(int32_t error) {
+ switch (-error) {
+ case kStatusFailed:
+ case kStatusInvalidState:
+ return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_FAILED);
+ case kStatusInvalidMac:
+ return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_INVALID_MAC);
+ case kStatusProductionKeyInTestRequest:
+ return static_cast<keymaster_error_t>(
+ BnRemotelyProvisionedComponent::STATUS_PRODUCTION_KEY_IN_TEST_REQUEST);
+ case kStatusTestKeyInProductionRequest:
+ return static_cast<keymaster_error_t>(
+ BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST);
+ case kStatusInvalidEek:
+ return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
+ }
+ return static_cast<keymaster_error_t>(error);
+}
+
+ScopedAStatus defaultHwInfo(RpcHardwareInfo* info) {
+ info->versionNumber = 2;
+ info->rpcAuthorName = "Google";
+ info->supportedEekCurve = RpcHardwareInfo::CURVE_P256;
+ info->uniqueId = "strongbox keymint";
+ return ScopedAStatus::ok();
+}
+
+uint32_t coseKeyEncodedSize(const std::vector<MacedPublicKey>& keysToSign) {
+ uint32_t size = 0;
+ for (auto& macKey : keysToSign) {
+ auto [macedKeyItem, _, coseMacErrMsg] = cppbor::parse(macKey.macedKey);
+ if (!macedKeyItem || !macedKeyItem->asArray() ||
+ macedKeyItem->asArray()->size() != kCoseMac0EntryCount) {
+ LOG(ERROR) << "Invalid COSE_Mac0 structure";
+ return 0;
+ }
+ auto payload = macedKeyItem->asArray()->get(kCoseMac0Payload)->asBstr();
+ if (!payload) return 0;
+ size += payload->value().size();
+ }
+ return size;
+}
+
+} // namespace
+
+ScopedAStatus JavacardRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) {
+ auto [item, err] = card_->sendRequest(Instruction::INS_GET_RKP_HARDWARE_INFO);
+ std::optional<uint64_t> optVersionNumber;
+ std::optional<uint64_t> optSupportedEekCurve;
+ std::optional<string> optRpcAuthorName;
+ std::optional<string> optUniqueId;
+ if (err != KM_ERROR_OK || !(optVersionNumber = cbor_.getUint64(item, 1)) ||
+ !(optRpcAuthorName = cbor_.getByteArrayStr(item, 2)) ||
+ !(optSupportedEekCurve = cbor_.getUint64(item, 3)) ||
+ !(optUniqueId = cbor_.getByteArrayStr(item, 4))) {
+ LOG(ERROR) << "Error in response of getHardwareInfo.";
+ LOG(INFO) << "Returning defaultHwInfo in getHardwareInfo.";
+ return defaultHwInfo(info);
+ }
+ info->rpcAuthorName = std::move(optRpcAuthorName.value());
+ info->versionNumber = static_cast<int32_t>(std::move(optVersionNumber.value()));
+ info->supportedEekCurve = static_cast<int32_t>(std::move(optSupportedEekCurve.value()));
+ info->uniqueId = std::move(optUniqueId.value());
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardRemotelyProvisionedComponentDevice::generateEcdsaP256KeyPair(
+ bool testMode, MacedPublicKey* macedPublicKey, std::vector<uint8_t>* privateKeyHandle) {
+ cppbor::Array array;
+ array.add(testMode);
+ auto [item, err] = card_->sendRequest(Instruction::INS_GENERATE_RKP_KEY_CMD, array);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending generateEcdsaP256KeyPair.";
+ return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err));
+ }
+ std::optional<std::vector<uint8_t>> optMacedKey;
+ std::optional<std::vector<uint8_t>> optPKeyHandle;
+ if (!(optMacedKey = cbor_.getByteArrayVec(item, 1)) ||
+ !(optPKeyHandle = cbor_.getByteArrayVec(item, 2))) {
+ LOG(ERROR) << "Error in decoding og response in generateEcdsaP256KeyPair.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ *privateKeyHandle = std::move(optPKeyHandle.value());
+ macedPublicKey->macedKey = std::move(optMacedKey.value());
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardRemotelyProvisionedComponentDevice::beginSendData(
+ bool testMode, const std::vector<MacedPublicKey>& keysToSign) {
+ uint32_t totalEncodedSize = coseKeyEncodedSize(keysToSign);
+ cppbor::Array array;
+ array.add(keysToSign.size());
+ array.add(totalEncodedSize);
+ array.add(testMode);
+ auto [_, err] = card_->sendRequest(Instruction::INS_BEGIN_SEND_DATA_CMD, array);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in beginSendData.";
+ return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err));
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardRemotelyProvisionedComponentDevice::updateMacedKey(
+ const std::vector<MacedPublicKey>& keysToSign) {
+ for (auto& macedPublicKey : keysToSign) {
+ cppbor::Array array;
+ array.add(EncodedItem(macedPublicKey.macedKey));
+ auto [_, err] = card_->sendRequest(Instruction::INS_UPDATE_KEY_CMD, array);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in updateMacedKey.";
+ return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err));
+ }
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus
+JavacardRemotelyProvisionedComponentDevice::updateChallenge(const std::vector<uint8_t>& challenge) {
+ Array array;
+ array.add(challenge);
+ auto [_, err] = card_->sendRequest(Instruction::INS_UPDATE_CHALLENGE_CMD, array);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in updateChallenge.";
+ return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err));
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardRemotelyProvisionedComponentDevice::updateEEK(
+ const std::vector<uint8_t>& endpointEncCertChain) {
+ std::vector<uint8_t> eekChain = endpointEncCertChain;
+ auto [_, err] = card_->sendRequest(Instruction::INS_UPDATE_EEK_CHAIN_CMD, eekChain);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in updateEEK.";
+ return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err));
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardRemotelyProvisionedComponentDevice::finishSendData(
+ std::vector<uint8_t>* keysToSignMac, DeviceInfo* deviceInfo,
+ std::vector<uint8_t>& coseEncryptProtectedHeader, cppbor::Map& coseEncryptUnProtectedHeader,
+ std::vector<uint8_t>& partialCipheredData, uint32_t& respFlag) {
+
+ auto [item, err] = card_->sendRequest(Instruction::INS_FINISH_SEND_DATA_CMD);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in finishSendData.";
+ return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err));
+ }
+ auto optDecodedKeysToSignMac = cbor_.getByteArrayVec(item, 1);
+ auto optDecodedDeviceInfo = cbor_.getByteArrayVec(item, 2);
+ auto optCEncryptProtectedHeader = cbor_.getByteArrayVec(item, 3);
+ auto optCEncryptUnProtectedHeader = cbor_.getMapItem(item, 4);
+ auto optPCipheredData = cbor_.getByteArrayVec(item, 5);
+ auto optRespFlag = cbor_.getUint64(item, 6);
+ if (!optDecodedKeysToSignMac || !optDecodedDeviceInfo || !optCEncryptProtectedHeader ||
+ !optCEncryptUnProtectedHeader || !optPCipheredData || !optRespFlag) {
+ LOG(ERROR) << "Error in decoding og response in finishSendData.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ *keysToSignMac = std::move(optDecodedKeysToSignMac.value());
+ deviceInfo->deviceInfo = std::move(optDecodedDeviceInfo.value());
+ coseEncryptProtectedHeader = std::move(optCEncryptProtectedHeader.value());
+ coseEncryptUnProtectedHeader = std::move(optCEncryptUnProtectedHeader.value());
+ partialCipheredData.insert(partialCipheredData.end(), optPCipheredData->begin(),
+ optPCipheredData->end());
+ respFlag = std::move(optRespFlag.value());
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus
+JavacardRemotelyProvisionedComponentDevice::getResponse(std::vector<uint8_t>& partialCipheredData,
+ cppbor::Array& recepientStructure,
+ uint32_t& respFlag) {
+ auto [item, err] = card_->sendRequest(Instruction::INS_GET_RESPONSE_CMD);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in getResponse.";
+ return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err));
+ }
+ auto optPCipheredData = cbor_.getByteArrayVec(item, 1);
+ auto optArray = cbor_.getArrayItem(item, 2);
+ auto optRespFlag = cbor_.getUint64(item, 3);
+ if (!optPCipheredData || !optArray || !optRespFlag) {
+ LOG(ERROR) << "Error in decoding og response in getResponse.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ recepientStructure = std::move(optArray.value());
+ partialCipheredData.insert(partialCipheredData.end(), optPCipheredData->begin(),
+ optPCipheredData->end());
+ respFlag = std::move(optRespFlag.value());
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardRemotelyProvisionedComponentDevice::generateCertificateRequest(
+ bool testMode, const std::vector<MacedPublicKey>& keysToSign,
+ const std::vector<uint8_t>& endpointEncCertChain, const std::vector<uint8_t>& challenge,
+ DeviceInfo* deviceInfo, ProtectedData* protectedData, std::vector<uint8_t>* keysToSignMac) {
+ std::vector<uint8_t> coseEncryptProtectedHeader;
+ cppbor::Map coseEncryptUnProtectedHeader;
+ cppbor::Array recipients;
+ std::vector<uint8_t> cipheredData;
+ uint32_t respFlag;
+ auto ret = beginSendData(testMode, keysToSign);
+ if (!ret.isOk()) return ret;
+
+ ret = updateMacedKey(keysToSign);
+ if (!ret.isOk()) return ret;
+
+ ret = updateChallenge(challenge);
+ if (!ret.isOk()) return ret;
+
+ ret = updateEEK(endpointEncCertChain);
+ if (!ret.isOk()) return ret;
+
+ ret = finishSendData(keysToSignMac, deviceInfo, coseEncryptProtectedHeader,
+ coseEncryptUnProtectedHeader, cipheredData, respFlag);
+ if (!ret.isOk()) return ret;
+
+ while (respFlag != 0) { // more data is pending to receive
+ ret = getResponse(cipheredData, recipients, respFlag);
+ if (!ret.isOk()) return ret;
+ }
+ // Create ConseEncrypt structure.
+ protectedData->protectedData = cppbor::Array()
+ .add(coseEncryptProtectedHeader) // Protected
+ .add(std::move(coseEncryptUnProtectedHeader)) // Unprotected
+ .add(cipheredData) // Payload
+ .add(std::move(recipients))
+ .encode();
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardRemotelyProvisionedComponentDevice::generateCertificateRequestV2(
+ const std::vector<MacedPublicKey>& /*keysToSign*/, const std::vector<uint8_t>& /*challenge*/,
+ std::vector<uint8_t>* /*csr*/) {
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/ready_se/google/keymint/KM200/HAL/JavacardRemotelyProvisionedComponentDevice.h b/ready_se/google/keymint/KM200/HAL/JavacardRemotelyProvisionedComponentDevice.h
new file mode 100644
index 0000000..7f41891
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/JavacardRemotelyProvisionedComponentDevice.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2021, 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.
+ */
+
+#pragma once
+
+#include <cppbor.h>
+
+#include <aidl/android/hardware/security/keymint/BnRemotelyProvisionedComponent.h>
+#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
+#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
+
+#include <keymaster/UniquePtr.h>
+#include <keymaster/android_keymaster.h>
+
+#include "CborConverter.h"
+#include "JavacardSecureElement.h"
+
+namespace aidl::android::hardware::security::keymint {
+using ::keymint::javacard::CborConverter;
+using ::keymint::javacard::JavacardSecureElement;
+using ndk::ScopedAStatus;
+using std::shared_ptr;
+
+class JavacardRemotelyProvisionedComponentDevice : public BnRemotelyProvisionedComponent {
+ public:
+ explicit JavacardRemotelyProvisionedComponentDevice(shared_ptr<JavacardSecureElement> card)
+ : card_(card) {}
+
+ virtual ~JavacardRemotelyProvisionedComponentDevice() = default;
+
+ ScopedAStatus getHardwareInfo(RpcHardwareInfo* info) override;
+
+ ScopedAStatus generateEcdsaP256KeyPair(bool testMode, MacedPublicKey* macedPublicKey,
+ std::vector<uint8_t>* privateKeyHandle) override;
+
+ ScopedAStatus generateCertificateRequest(bool testMode,
+ const std::vector<MacedPublicKey>& keysToSign,
+ const std::vector<uint8_t>& endpointEncCertChain,
+ const std::vector<uint8_t>& challenge,
+ DeviceInfo* deviceInfo, ProtectedData* protectedData,
+ std::vector<uint8_t>* keysToSignMac) override;
+
+ ScopedAStatus generateCertificateRequestV2(const std::vector<MacedPublicKey>& keysToSign,
+ const std::vector<uint8_t>& challenge,
+ std::vector<uint8_t>* csr) override;
+
+ private:
+ ScopedAStatus beginSendData(bool testMode, const std::vector<MacedPublicKey>& keysToSign);
+
+ ScopedAStatus updateMacedKey(const std::vector<MacedPublicKey>& keysToSign);
+
+ ScopedAStatus updateChallenge(const std::vector<uint8_t>& challenge);
+
+ ScopedAStatus updateEEK(const std::vector<uint8_t>& endpointEncCertChain);
+
+ ScopedAStatus finishSendData(std::vector<uint8_t>* keysToSignMac, DeviceInfo* deviceInfo,
+ std::vector<uint8_t>& coseEncryptProtectedHeader,
+ cppbor::Map& coseEncryptUnProtectedHeader,
+ std::vector<uint8_t>& partialCipheredData, uint32_t& respFlag);
+
+ ScopedAStatus getResponse(std::vector<uint8_t>& partialCipheredData,
+ cppbor::Array& recepientStructure, uint32_t& respFlag);
+ std::shared_ptr<JavacardSecureElement> card_;
+ CborConverter cbor_;
+};
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/ready_se/google/keymint/KM200/HAL/JavacardSecureElement.cpp b/ready_se/google/keymint/KM200/HAL/JavacardSecureElement.cpp
new file mode 100644
index 0000000..7c4f038
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/JavacardSecureElement.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright 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" 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.
+ */
+
+#define LOG_TAG "javacard.keymint.device.strongbox-impl"
+#include "JavacardSecureElement.h"
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <memory>
+#include <regex.h>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <keymaster/android_keymaster_messages.h>
+
+#include "keymint_utils.h"
+
+namespace keymint::javacard {
+
+keymaster_error_t JavacardSecureElement::initializeJavacard() {
+ Array request;
+ request.add(Uint(getOsVersion()));
+ request.add(Uint(getOsPatchlevel()));
+ request.add(Uint(getVendorPatchlevel()));
+ auto [item, err] = sendRequest(Instruction::INS_INIT_STRONGBOX_CMD, request);
+ return err;
+}
+
+keymaster_error_t JavacardSecureElement::sendEarlyBootEndedEvent(bool eventTriggered) {
+ isEarlyBootEventPending |= eventTriggered;
+ if (!isEarlyBootEventPending) {
+ return KM_ERROR_OK;
+ }
+ auto [item, err] = sendRequest(Instruction::INS_EARLY_BOOT_ENDED_CMD);
+ if (err != KM_ERROR_OK) {
+ // Incase of failure cache the event and send in the next immediate request to Applet.
+ isEarlyBootEventPending = true;
+ return err;
+ }
+ isEarlyBootEventPending = false;
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t JavacardSecureElement::constructApduMessage(Instruction& ins,
+ std::vector<uint8_t>& inputData,
+ std::vector<uint8_t>& apduOut) {
+ apduOut.push_back(static_cast<uint8_t>(APDU_CLS)); // CLS
+ apduOut.push_back(static_cast<uint8_t>(ins)); // INS
+ apduOut.push_back(static_cast<uint8_t>(APDU_P1)); // P1
+ apduOut.push_back(static_cast<uint8_t>(APDU_P2)); // P2
+
+ if (USHRT_MAX >= inputData.size()) {
+ // Send extended length APDU always as response size is not known to HAL.
+ // Case 1: Lc > 0 CLS | INS | P1 | P2 | 00 | 2 bytes of Lc | CommandData | 2 bytes of Le
+ // all set to 00. Case 2: Lc = 0 CLS | INS | P1 | P2 | 3 bytes of Le all set to 00.
+ // Extended length 3 bytes, starts with 0x00
+ apduOut.push_back(static_cast<uint8_t>(0x00));
+ if (inputData.size() > 0) {
+ apduOut.push_back(static_cast<uint8_t>(inputData.size() >> 8));
+ apduOut.push_back(static_cast<uint8_t>(inputData.size() & 0xFF));
+ // Data
+ apduOut.insert(apduOut.end(), inputData.begin(), inputData.end());
+ }
+ // Expected length of output.
+ // Accepting complete length of output every time.
+ apduOut.push_back(static_cast<uint8_t>(0x00));
+ apduOut.push_back(static_cast<uint8_t>(0x00));
+ } else {
+ LOG(ERROR) << "Error in constructApduMessage.";
+ return (KM_ERROR_INVALID_INPUT_LENGTH);
+ }
+ return (KM_ERROR_OK); // success
+}
+
+keymaster_error_t JavacardSecureElement::sendData(Instruction ins, std::vector<uint8_t>& inData,
+ std::vector<uint8_t>& response) {
+ keymaster_error_t ret = KM_ERROR_UNKNOWN_ERROR;
+ std::vector<uint8_t> apdu;
+
+ ret = constructApduMessage(ins, inData, apdu);
+
+ if (ret != KM_ERROR_OK) {
+ return ret;
+ }
+
+ ret = transport_->sendData(apdu, response);
+ if (ret != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending data in sendData. " << static_cast<int>(ret);
+ return ret;
+ }
+
+ // Response size should be greater than 2. Cbor output data followed by two bytes of APDU
+ // status.
+ if ((response.size() <= 2) || (getApduStatus(response) != APDU_RESP_STATUS_OK)) {
+ LOG(ERROR) << "Response of the sendData is wrong: response size = " << response.size()
+ << " apdu status = " << getApduStatus(response);
+ return (KM_ERROR_UNKNOWN_ERROR);
+ }
+ // remove the status bytes
+ response.pop_back();
+ response.pop_back();
+ return (KM_ERROR_OK); // success
+}
+
+std::tuple<std::unique_ptr<Item>, keymaster_error_t>
+JavacardSecureElement::sendRequest(Instruction ins, Array& request) {
+ vector<uint8_t> response;
+ // encode request
+ std::vector<uint8_t> command = request.encode();
+ auto sendError = sendData(ins, command, response);
+ if (sendError != KM_ERROR_OK) {
+ return {unique_ptr<Item>(nullptr), sendError};
+ }
+ // decode the response and send that back
+ return cbor_.decodeData(response);
+}
+
+std::tuple<std::unique_ptr<Item>, keymaster_error_t>
+JavacardSecureElement::sendRequest(Instruction ins, std::vector<uint8_t>& command) {
+ vector<uint8_t> response;
+ auto sendError = sendData(ins, command, response);
+ if (sendError != KM_ERROR_OK) {
+ return {unique_ptr<Item>(nullptr), sendError};
+ }
+ // decode the response and send that back
+ return cbor_.decodeData(response);
+}
+
+std::tuple<std::unique_ptr<Item>, keymaster_error_t>
+JavacardSecureElement::sendRequest(Instruction ins) {
+ vector<uint8_t> response;
+ vector<uint8_t> emptyRequest;
+ auto sendError = sendData(ins, emptyRequest, response);
+ if (sendError != KM_ERROR_OK) {
+ return {unique_ptr<Item>(nullptr), sendError};
+ }
+ // decode the response and send that back
+ return cbor_.decodeData(response);
+}
+
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM200/HAL/JavacardSecureElement.h b/ready_se/google/keymint/KM200/HAL/JavacardSecureElement.h
new file mode 100644
index 0000000..2ea5fe4
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/JavacardSecureElement.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright 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" 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.
+ */
+
+#pragma once
+
+#include <ITransport.h>
+
+#include "CborConverter.h"
+
+#define APDU_CLS 0x80
+#define APDU_P1 0x50
+#define APDU_P2 0x00
+#define APDU_RESP_STATUS_OK 0x9000
+
+#define KEYMINT_CMD_APDU_START 0x20
+
+namespace keymint::javacard {
+using std::shared_ptr;
+using std::vector;
+
+enum class Instruction {
+ // Keymaster commands
+ INS_GENERATE_KEY_CMD = KEYMINT_CMD_APDU_START + 1,
+ INS_IMPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 2,
+ INS_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 3,
+ INS_EXPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 4,
+ INS_ATTEST_KEY_CMD = KEYMINT_CMD_APDU_START + 5,
+ INS_UPGRADE_KEY_CMD = KEYMINT_CMD_APDU_START + 6,
+ INS_DELETE_KEY_CMD = KEYMINT_CMD_APDU_START + 7,
+ INS_DELETE_ALL_KEYS_CMD = KEYMINT_CMD_APDU_START + 8,
+ INS_ADD_RNG_ENTROPY_CMD = KEYMINT_CMD_APDU_START + 9,
+ INS_COMPUTE_SHARED_SECRET_CMD = KEYMINT_CMD_APDU_START + 10,
+ INS_DESTROY_ATT_IDS_CMD = KEYMINT_CMD_APDU_START + 11,
+ INS_VERIFY_AUTHORIZATION_CMD = KEYMINT_CMD_APDU_START + 12,
+ INS_GET_SHARED_SECRET_PARAM_CMD = KEYMINT_CMD_APDU_START + 13,
+ INS_GET_KEY_CHARACTERISTICS_CMD = KEYMINT_CMD_APDU_START + 14,
+ INS_GET_HW_INFO_CMD = KEYMINT_CMD_APDU_START + 15,
+ INS_BEGIN_OPERATION_CMD = KEYMINT_CMD_APDU_START + 16,
+ INS_UPDATE_OPERATION_CMD = KEYMINT_CMD_APDU_START + 17,
+ INS_FINISH_OPERATION_CMD = KEYMINT_CMD_APDU_START + 18,
+ INS_ABORT_OPERATION_CMD = KEYMINT_CMD_APDU_START + 19,
+ INS_DEVICE_LOCKED_CMD = KEYMINT_CMD_APDU_START + 20,
+ INS_EARLY_BOOT_ENDED_CMD = KEYMINT_CMD_APDU_START + 21,
+ INS_GET_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 22,
+ INS_UPDATE_AAD_OPERATION_CMD = KEYMINT_CMD_APDU_START + 23,
+ INS_BEGIN_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 24,
+ INS_FINISH_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 25,
+ INS_INIT_STRONGBOX_CMD = KEYMINT_CMD_APDU_START + 26,
+ // RKP Commands
+ INS_GET_RKP_HARDWARE_INFO = KEYMINT_CMD_APDU_START + 27,
+ INS_GENERATE_RKP_KEY_CMD = KEYMINT_CMD_APDU_START + 28,
+ INS_BEGIN_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 29,
+ INS_UPDATE_KEY_CMD = KEYMINT_CMD_APDU_START + 30,
+ INS_UPDATE_EEK_CHAIN_CMD = KEYMINT_CMD_APDU_START + 31,
+ INS_UPDATE_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 32,
+ INS_FINISH_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 33,
+ INS_GET_RESPONSE_CMD = KEYMINT_CMD_APDU_START + 34,
+ // SE ROT Commands
+ INS_GET_ROT_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 45,
+ INS_GET_ROT_DATA_CMD = KEYMINT_CMD_APDU_START + 46,
+ INS_SEND_ROT_DATA_CMD = KEYMINT_CMD_APDU_START + 47,
+};
+
+class JavacardSecureElement {
+ public:
+ explicit JavacardSecureElement(shared_ptr<ITransport> transport, uint32_t osVersion,
+ uint32_t osPatchLevel, uint32_t vendorPatchLevel)
+ : transport_(transport), osVersion_(osVersion), osPatchLevel_(osPatchLevel),
+ vendorPatchLevel_(vendorPatchLevel), isEarlyBootEventPending(false) {
+ transport_->openConnection();
+ }
+ virtual ~JavacardSecureElement() { transport_->closeConnection(); }
+
+ std::tuple<std::unique_ptr<Item>, keymaster_error_t> sendRequest(Instruction ins,
+ Array& request);
+ std::tuple<std::unique_ptr<Item>, keymaster_error_t> sendRequest(Instruction ins);
+ std::tuple<std::unique_ptr<Item>, keymaster_error_t> sendRequest(Instruction ins,
+ std::vector<uint8_t>& command);
+
+ keymaster_error_t sendData(Instruction ins, std::vector<uint8_t>& inData,
+ std::vector<uint8_t>& response);
+
+ keymaster_error_t constructApduMessage(Instruction& ins, std::vector<uint8_t>& inputData,
+ std::vector<uint8_t>& apduOut);
+ keymaster_error_t initializeJavacard();
+ keymaster_error_t sendEarlyBootEndedEvent(bool eventTriggered);
+ inline uint16_t getApduStatus(std::vector<uint8_t>& inputData) {
+ // Last two bytes are the status SW0SW1
+ uint8_t SW0 = inputData.at(inputData.size() - 2);
+ uint8_t SW1 = inputData.at(inputData.size() - 1);
+ return (SW0 << 8 | SW1);
+ }
+
+ shared_ptr<ITransport> transport_;
+ uint32_t osVersion_;
+ uint32_t osPatchLevel_;
+ uint32_t vendorPatchLevel_;
+ bool isEarlyBootEventPending;
+ CborConverter cbor_;
+};
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM200/HAL/JavacardSharedSecret.cpp b/ready_se/google/keymint/KM200/HAL/JavacardSharedSecret.cpp
new file mode 100644
index 0000000..c5cf9a2
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/JavacardSharedSecret.cpp
@@ -0,0 +1,61 @@
+#define LOG_TAG "javacard.strongbox.keymint.operation-impl"
+#include "JavacardSharedSecret.h"
+
+#include <android-base/logging.h>
+
+#include <KeyMintUtils.h>
+
+namespace aidl::android::hardware::security::sharedsecret {
+using ::keymint::javacard::Instruction;
+
+ScopedAStatus JavacardSharedSecret::getSharedSecretParameters(SharedSecretParameters* params) {
+ auto error = card_->initializeJavacard();
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in initializing javacard.";
+ return keymint::km_utils::kmError2ScopedAStatus(error);
+ }
+ auto [item, err] = card_->sendRequest(Instruction::INS_GET_SHARED_SECRET_PARAM_CMD);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in getSharedSecretParameters.";
+ return keymint::km_utils::kmError2ScopedAStatus(err);
+ }
+ auto optSSParams = cbor_.getSharedSecretParameters(item, 1);
+ if (!optSSParams) {
+ LOG(ERROR) << "Error in sending in getSharedSecretParameters.";
+ return keymint::km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ *params = std::move(optSSParams.value());
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus
+JavacardSharedSecret::computeSharedSecret(const std::vector<SharedSecretParameters>& params,
+ std::vector<uint8_t>* secret) {
+
+ auto error = card_->sendEarlyBootEndedEvent(false);
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending earlyBoot event javacard.";
+ return keymint::km_utils::kmError2ScopedAStatus(error);
+ }
+ error = card_->initializeJavacard();
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in initializing javacard.";
+ return keymint::km_utils::kmError2ScopedAStatus(error);
+ }
+ cppbor::Array request;
+ cbor_.addSharedSecretParameters(request, params);
+ auto [item, err] = card_->sendRequest(Instruction::INS_COMPUTE_SHARED_SECRET_CMD, request);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in computeSharedSecret.";
+ return keymint::km_utils::kmError2ScopedAStatus(err);
+ }
+ auto optSecret = cbor_.getByteArrayVec(item, 1);
+ if (!optSecret) {
+ LOG(ERROR) << "Error in decoding the response in computeSharedSecret.";
+ return keymint::km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ *secret = std::move(optSecret.value());
+ return ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::security::sharedsecret
diff --git a/ready_se/google/keymint/KM200/HAL/JavacardSharedSecret.h b/ready_se/google/keymint/KM200/HAL/JavacardSharedSecret.h
new file mode 100644
index 0000000..340853a
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/JavacardSharedSecret.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include <aidl/android/hardware/security/sharedsecret/BnSharedSecret.h>
+#include <aidl/android/hardware/security/sharedsecret/SharedSecretParameters.h>
+
+#include "CborConverter.h"
+#include "JavacardSecureElement.h"
+
+namespace aidl::android::hardware::security::sharedsecret {
+using ::keymint::javacard::CborConverter;
+using ::keymint::javacard::JavacardSecureElement;
+using ndk::ScopedAStatus;
+using std::shared_ptr;
+using std::vector;
+
+class JavacardSharedSecret : public BnSharedSecret {
+ public:
+ explicit JavacardSharedSecret(shared_ptr<JavacardSecureElement> card) : card_(card) {}
+ virtual ~JavacardSharedSecret() {}
+
+ ScopedAStatus getSharedSecretParameters(SharedSecretParameters* params) override;
+
+ ScopedAStatus computeSharedSecret(const std::vector<SharedSecretParameters>& params,
+ std::vector<uint8_t>* secret) override;
+
+ private:
+ shared_ptr<JavacardSecureElement> card_;
+ CborConverter cbor_;
+};
+
+} // namespace aidl::android::hardware::security::sharedsecret
diff --git a/ready_se/google/keymint/KM200/HAL/LICENSE b/ready_se/google/keymint/KM200/HAL/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/ready_se/google/keymint/KM200/HAL/METADATA b/ready_se/google/keymint/KM200/HAL/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/METADATA
@@ -0,0 +1,3 @@
+third_party {
+ license_type: NOTICE
+}
diff --git a/ready_se/google/keymint/KM200/HAL/OWNERS b/ready_se/google/keymint/KM200/HAL/OWNERS
new file mode 100644
index 0000000..0bd972b
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/OWNERS
@@ -0,0 +1,3 @@
+pathakc@google.com
+subrahmanyaman@google.com
+avinashh@google.com
diff --git a/ready_se/google/keymint/KM200/HAL/OmapiTransport.cpp b/ready_se/google/keymint/KM200/HAL/OmapiTransport.cpp
new file mode 100644
index 0000000..3fd5e43
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/OmapiTransport.cpp
@@ -0,0 +1,276 @@
+/*
+ **
+ ** Copyright 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" 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.
+ */
+#include "OmapiTransport.h"
+
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <vector>
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <android-base/logging.h>
+
+namespace keymint::javacard {
+using ::aidl::android::hardware::security::keymint::ErrorCode;
+
+constexpr uint8_t KEYMINT_APPLET_AID[] = {0xA0, 0x00, 0x00, 0x00, 0x62, 0x03,
+ 0x02, 0x0C, 0x01, 0x01, 0x01};
+std::string const ESE_READER_PREFIX = "eSE";
+constexpr const char omapiServiceName[] = "android.se.omapi.ISecureElementService/default";
+
+class SEListener : public ::aidl::android::se::omapi::BnSecureElementListener {};
+
+keymaster_error_t OmapiTransport::initialize() {
+
+ LOG(DEBUG) << "Initialize the secure element connection";
+
+ // Get OMAPI vendor stable service handler
+ ::ndk::SpAIBinder ks2Binder(AServiceManager_checkService(omapiServiceName));
+ omapiSeService = aidl::android::se::omapi::ISecureElementService::fromBinder(ks2Binder);
+
+ if (omapiSeService == nullptr) {
+ LOG(ERROR) << "Failed to start omapiSeService null";
+ return static_cast<keymaster_error_t>(ErrorCode::HARDWARE_NOT_YET_AVAILABLE);
+ }
+
+ int size = sizeof(KEYMINT_APPLET_AID) / sizeof(KEYMINT_APPLET_AID[0]);
+ // reset readers, clear readers if already existing
+ if (mVSReaders.size() > 0) {
+ closeConnection();
+ }
+
+ std::vector<std::string> readers = {};
+ // Get available readers
+ auto status = omapiSeService->getReaders(&readers);
+ if (!status.isOk()) {
+ LOG(ERROR) << "getReaders failed to get available readers: " << status.getMessage();
+ return static_cast<keymaster_error_t>(ErrorCode::HARDWARE_TYPE_UNAVAILABLE);
+ }
+
+ // Get SE readers handlers
+ for (auto readerName : readers) {
+ std::shared_ptr<::aidl::android::se::omapi::ISecureElementReader> reader;
+ status = omapiSeService->getReader(readerName, &reader);
+ if (!status.isOk()) {
+ LOG(ERROR) << "getReader for " << readerName.c_str()
+ << " Failed: " << status.getMessage();
+ return static_cast<keymaster_error_t>(ErrorCode::HARDWARE_TYPE_UNAVAILABLE);
+ }
+ mVSReaders[readerName] = reader;
+ }
+
+ // Find eSE reader, as of now assumption is only eSE available on device
+ LOG(DEBUG) << "Finding eSE reader";
+ eSEReader = nullptr;
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ if (name.find(ESE_READER_PREFIX, 0) != std::string::npos) {
+ LOG(DEBUG) << "eSE reader found: " << name;
+ eSEReader = reader;
+ break;
+ }
+ }
+ }
+
+ if (eSEReader == nullptr) {
+ LOG(ERROR) << "secure element reader " << ESE_READER_PREFIX << " not found";
+ return static_cast<keymaster_error_t>(ErrorCode::HARDWARE_TYPE_UNAVAILABLE);
+ }
+
+ bool isSecureElementPresent = false;
+ auto res = eSEReader->isSecureElementPresent(&isSecureElementPresent);
+ if (!res.isOk()) {
+ eSEReader = nullptr;
+ LOG(ERROR) << "isSecureElementPresent error: " << res.getMessage();
+ return static_cast<keymaster_error_t>(ErrorCode::HARDWARE_TYPE_UNAVAILABLE);
+ }
+ if (!isSecureElementPresent) {
+ LOG(ERROR) << "secure element not found";
+ eSEReader = nullptr;
+ return static_cast<keymaster_error_t>(ErrorCode::HARDWARE_TYPE_UNAVAILABLE);
+ }
+
+ status = eSEReader->openSession(&session);
+ if (!status.isOk()) {
+ LOG(ERROR) << "openSession error: " << status.getMessage();
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+ }
+ if (session == nullptr) {
+ LOG(ERROR) << "Could not open session null";
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+ }
+
+ std::vector<uint8_t> aid(KEYMINT_APPLET_AID, KEYMINT_APPLET_AID + size);
+ auto mSEListener = ndk::SharedRefBase::make<SEListener>();
+ status = session->openLogicalChannel(aid, 0x00, mSEListener, &channel);
+ if (!status.isOk()) {
+ LOG(ERROR) << "openLogicalChannel error: " << status.getMessage();
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+ }
+ if (channel == nullptr) {
+ LOG(ERROR) << "Could not open channel null";
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+ }
+
+ return KM_ERROR_OK;
+}
+
+bool OmapiTransport::internalTransmitApdu(
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader,
+ std::vector<uint8_t> apdu, std::vector<uint8_t>& transmitResponse) {
+
+ LOG(DEBUG) << "internalTransmitApdu: trasmitting data to secure element";
+ if (reader == nullptr) {
+ LOG(ERROR) << "eSE reader is null";
+ return false;
+ }
+
+ bool result = true;
+ auto res = ndk::ScopedAStatus::ok();
+ if (session != nullptr) {
+ res = session->isClosed(&result);
+ if (!res.isOk()) {
+ LOG(ERROR) << "isClosed error: " << res.getMessage();
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+ }
+ }
+ if (result) {
+ res = reader->openSession(&session);
+ if (!res.isOk()) {
+ LOG(ERROR) << "openSession error: " << res.getMessage();
+ return false;
+ }
+ if (session == nullptr) {
+ LOG(ERROR) << "Could not open session null";
+ return false;
+ }
+ }
+
+ result = true;
+ if (channel != nullptr) {
+ res = channel->isClosed(&result);
+ if (!res.isOk()) {
+ LOG(ERROR) << "isClosed error: " << res.getMessage();
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+ }
+ }
+
+ int size = sizeof(KEYMINT_APPLET_AID) / sizeof(KEYMINT_APPLET_AID[0]);
+ std::vector<uint8_t> aid(KEYMINT_APPLET_AID, KEYMINT_APPLET_AID + size);
+ if (result) {
+ auto mSEListener = ndk::SharedRefBase::make<SEListener>();
+ res = session->openLogicalChannel(aid, 0x00, mSEListener, &channel);
+ if (!res.isOk()) {
+ LOG(ERROR) << "openLogicalChannel error: " << res.getMessage();
+ return false;
+ }
+ if (channel == nullptr) {
+ LOG(ERROR) << "Could not open channel null";
+ return false;
+ }
+ }
+
+ std::vector<uint8_t> selectResponse = {};
+ res = channel->getSelectResponse(&selectResponse);
+ if (!res.isOk()) {
+ LOG(ERROR) << "getSelectResponse error: " << res.getMessage();
+ return false;
+ }
+
+ if ((selectResponse.size() < 2) ||
+ ((selectResponse[selectResponse.size() - 1] & 0xFF) != 0x00) ||
+ ((selectResponse[selectResponse.size() - 2] & 0xFF) != 0x90)) {
+ LOG(ERROR) << "Failed to select the Applet.";
+ return false;
+ }
+
+ res = channel->transmit(apdu, &transmitResponse);
+
+ LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode()
+ << " Message: " << res.getMessage();
+ if (!res.isOk()) {
+ LOG(ERROR) << "transmit error: " << res.getMessage();
+ return false;
+ }
+
+ return true;
+}
+
+keymaster_error_t OmapiTransport::openConnection() {
+
+ // if already conection setup done, no need to initialise it again.
+ if (isConnected()) {
+ return KM_ERROR_OK;
+ }
+ return initialize();
+}
+
+keymaster_error_t OmapiTransport::sendData(const vector<uint8_t>& inData, vector<uint8_t>& output) {
+
+ if (!isConnected()) {
+ // Try to initialize connection to eSE
+ LOG(INFO) << "Failed to send data, try to initialize connection SE connection";
+ auto res = initialize();
+ if (res != KM_ERROR_OK) {
+ LOG(ERROR) << "Failed to send data, initialization not completed";
+ closeConnection();
+ return res;
+ }
+ }
+
+ if (eSEReader != nullptr) {
+ LOG(DEBUG) << "Sending apdu data to secure element: " << ESE_READER_PREFIX;
+ if (internalTransmitApdu(eSEReader, inData, output)) {
+ return KM_ERROR_OK;
+ } else {
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+ }
+ } else {
+ LOG(ERROR) << "secure element reader " << ESE_READER_PREFIX << " not found";
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+ }
+}
+
+keymaster_error_t OmapiTransport::closeConnection() {
+ LOG(DEBUG) << "Closing all connections";
+ if (omapiSeService != nullptr) {
+ if (mVSReaders.size() > 0) {
+ for (const auto& [name, reader] : mVSReaders) {
+ reader->closeSessions();
+ }
+ mVSReaders.clear();
+ }
+ }
+ if (channel != nullptr) channel->close();
+ if (session != nullptr) session->close();
+ return KM_ERROR_OK;
+}
+
+bool OmapiTransport::isConnected() {
+ // Check already initialization completed or not
+ if (omapiSeService != nullptr && eSEReader != nullptr) {
+ LOG(DEBUG) << "Connection initialization already completed";
+ return true;
+ }
+
+ LOG(DEBUG) << "Connection initialization not completed";
+ return false;
+}
+
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM200/HAL/OmapiTransport.h b/ready_se/google/keymint/KM200/HAL/OmapiTransport.h
new file mode 100644
index 0000000..a199bbb
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/OmapiTransport.h
@@ -0,0 +1,65 @@
+#pragma once
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <aidl/android/se/omapi/BnSecureElementListener.h>
+#include <aidl/android/se/omapi/ISecureElementChannel.h>
+#include <aidl/android/se/omapi/ISecureElementListener.h>
+#include <aidl/android/se/omapi/ISecureElementReader.h>
+#include <aidl/android/se/omapi/ISecureElementService.h>
+#include <aidl/android/se/omapi/ISecureElementSession.h>
+
+#include <android/binder_manager.h>
+
+#include "ITransport.h"
+
+namespace keymint::javacard {
+using std::vector;
+
+/**
+ * OmapiTransport is derived from ITransport. This class gets the OMAPI service binder instance and
+ * uses IPC to communicate with OMAPI service. OMAPI inturn communicates with hardware via
+ * ISecureElement.
+ */
+class OmapiTransport : public ITransport {
+
+ public:
+ OmapiTransport()
+ : omapiSeService(nullptr), eSEReader(nullptr), session(nullptr), channel(nullptr),
+ mVSReaders({}) {}
+ /**
+ * Gets the binder instance of ISEService, gets te reader corresponding to secure element,
+ * establishes a session and opens a basic channel.
+ */
+ keymaster_error_t openConnection() override;
+ /**
+ * Transmists the data over the opened basic channel and receives the data back.
+ */
+ keymaster_error_t sendData(const vector<uint8_t>& inData, vector<uint8_t>& output) override;
+
+ /**
+ * Closes the connection.
+ */
+ keymaster_error_t closeConnection() override;
+ /**
+ * Returns the state of the connection status. Returns true if the connection is active, false
+ * if connection is broken.
+ */
+ bool isConnected() override;
+
+ private:
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementService> omapiSeService;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> eSEReader;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session;
+ std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel;
+ std::map<std::string, std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>>
+ mVSReaders;
+ keymaster_error_t initialize();
+ bool
+ internalTransmitApdu(std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader,
+ std::vector<uint8_t> apdu, std::vector<uint8_t>& transmitResponse);
+};
+
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM200/HAL/SocketTransport.cpp b/ready_se/google/keymint/KM200/HAL/SocketTransport.cpp
new file mode 100644
index 0000000..a3595fe
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/SocketTransport.cpp
@@ -0,0 +1,144 @@
+/*
+ **
+ ** Copyright 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" 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.
+ */
+#include "SocketTransport.h"
+
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include <memory>
+#include <vector>
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <android-base/logging.h>
+#include <sys/socket.h>
+
+#include "ITransport.h"
+
+#define PORT 8080
+#define IPADDR "192.168.9.112"
+#define MAX_RECV_BUFFER_SIZE 2500
+
+namespace keymint::javacard {
+using ::aidl::android::hardware::security::keymint::ErrorCode;
+using std::shared_ptr;
+using std::vector;
+
+keymaster_error_t SocketTransport::openConnection() {
+ struct sockaddr_in serv_addr;
+ if ((mSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ LOG(ERROR) << "Socket creation failed"
+ << " Error: " << strerror(errno);
+ return static_cast<keymaster_error_t>(ErrorCode::HARDWARE_TYPE_UNAVAILABLE);
+ }
+
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_port = htons(PORT);
+
+ // Convert IPv4 and IPv6 addresses from text to binary form
+ if (inet_pton(AF_INET, IPADDR, &serv_addr.sin_addr) <= 0) {
+ LOG(ERROR) << "Invalid address/ Address not supported.";
+ return static_cast<keymaster_error_t>(ErrorCode::HARDWARE_TYPE_UNAVAILABLE);
+ }
+
+ if (connect(mSocket, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
+ close(mSocket);
+ LOG(ERROR) << "Connection failed. Error: " << strerror(errno);
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+ }
+ socketStatus = true;
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t SocketTransport::sendData(const vector<uint8_t>& inData,
+ vector<uint8_t>& output) {
+ int count = 1;
+ while (!socketStatus && count++ < 5) {
+ sleep(1);
+ LOG(ERROR) << "Trying to open socket connection... count: " << count;
+ openConnection();
+ }
+
+ if (count >= 5) {
+ LOG(ERROR) << "Failed to open socket connection";
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+ }
+ // Prepend the input length to the inputData before sending.
+ vector<uint8_t> inDataPrependedLength;
+ inDataPrependedLength.push_back(static_cast<uint8_t>(inData.size() >> 8));
+ inDataPrependedLength.push_back(static_cast<uint8_t>(inData.size() & 0xFF));
+ inDataPrependedLength.insert(inDataPrependedLength.end(), inData.begin(), inData.end());
+
+ if (0 >
+ send(mSocket, inDataPrependedLength.data(), inDataPrependedLength.size(), MSG_NOSIGNAL)) {
+ static int connectionResetCnt = 0; /* To avoid loop */
+ if ((ECONNRESET == errno || EPIPE == errno) && connectionResetCnt == 0) {
+ // Connection reset. Try open socket and then sendData.
+ socketStatus = false;
+ connectionResetCnt++;
+ return sendData(inData, output);
+ }
+ LOG(ERROR) << "Failed to send data over socket err: " << errno;
+ connectionResetCnt = 0;
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+ }
+
+ if (!readData(output)) {
+ return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;
+ }
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t SocketTransport::closeConnection() {
+ close(mSocket);
+ socketStatus = false;
+ return KM_ERROR_OK;
+}
+
+bool SocketTransport::isConnected() {
+ return socketStatus;
+}
+
+bool SocketTransport::readData(vector<uint8_t>& output) {
+ uint8_t buffer[MAX_RECV_BUFFER_SIZE];
+ ssize_t expectedResponseLen = 0;
+ ssize_t totalBytesRead = 0;
+ // The first 2 bytes in the response contains the expected response length.
+ do {
+ size_t i = 0;
+ ssize_t numBytes = read(mSocket, buffer, MAX_RECV_BUFFER_SIZE);
+ if (0 > numBytes) {
+ LOG(ERROR) << "Failed to read data from socket.";
+ return false;
+ }
+ totalBytesRead += numBytes;
+ if (expectedResponseLen == 0) {
+ // First two bytes in the response contains the expected response length.
+ expectedResponseLen |= static_cast<ssize_t>(buffer[1] & 0xFF);
+ expectedResponseLen |= static_cast<ssize_t>((buffer[0] << 8) & 0xFF00);
+ // 2 bytes for storing the length.
+ expectedResponseLen += 2;
+ i = 2;
+ }
+ for (; i < numBytes; i++) {
+ output.push_back(buffer[i]);
+ }
+ } while (totalBytesRead < expectedResponseLen);
+
+ return true;
+}
+
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM200/HAL/SocketTransport.h b/ready_se/google/keymint/KM200/HAL/SocketTransport.h
new file mode 100644
index 0000000..73725d0
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/SocketTransport.h
@@ -0,0 +1,55 @@
+/*
+ **
+ ** Copyright 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" 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.
+ */
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include "ITransport.h"
+
+namespace keymint::javacard {
+using std::shared_ptr;
+using std::vector;
+
+class SocketTransport : public ITransport {
+
+ public:
+ SocketTransport() : mSocket(-1), socketStatus(false) {}
+ /**
+ * Creates a socket instance and connects to the provided server IP and port.
+ */
+ keymaster_error_t openConnection() override;
+ /**
+ * Sends data over socket and receives data back.
+ */
+ keymaster_error_t sendData(const vector<uint8_t>& inData, vector<uint8_t>& output) override;
+ /**
+ * Closes the connection.
+ */
+ keymaster_error_t closeConnection() override;
+ /**
+ * Returns the state of the connection status. Returns true if the connection is active,
+ * false if connection is broken.
+ */
+ bool isConnected() override;
+
+ private:
+ bool readData(vector<uint8_t>& output);
+ int mSocket;
+ bool socketStatus;
+};
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM200/HAL/android.hardware.hardware_keystore.jc-strongbox-keymint.xml b/ready_se/google/keymint/KM200/HAL/android.hardware.hardware_keystore.jc-strongbox-keymint.xml
new file mode 100644
index 0000000..c6ca188
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/android.hardware.hardware_keystore.jc-strongbox-keymint.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+<!-- Feature for devices with Keymaster in StrongBox. -->
+<permissions>
+ <feature name="android.hardware.strongbox_keystore" version="200"/>
+ <feature name="android.hardware.keystore.app_attest_key" />
+</permissions>
diff --git a/ready_se/google/keymint/KM200/HAL/android.hardware.security.keymint-service.strongbox.rc b/ready_se/google/keymint/KM200/HAL/android.hardware.security.keymint-service.strongbox.rc
new file mode 100644
index 0000000..7bb96f0
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/android.hardware.security.keymint-service.strongbox.rc
@@ -0,0 +1,3 @@
+service vendor.keymint-strongbox /vendor/bin/hw/android.hardware.security.keymint-service.strongbox
+ class early_hal
+ user jc_strongbox
diff --git a/ready_se/google/keymint/KM200/HAL/android.hardware.security.keymint-service.strongbox.xml b/ready_se/google/keymint/KM200/HAL/android.hardware.security.keymint-service.strongbox.xml
new file mode 100644
index 0000000..0631f12
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/android.hardware.security.keymint-service.strongbox.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.security.keymint</name>
+ <fqname>IKeyMintDevice/strongbox</fqname>
+ </hal>
+ <hal format="aidl">
+ <name>android.hardware.security.keymint</name>
+ <fqname>IRemotelyProvisionedComponent/strongbox</fqname>
+ </hal>
+</manifest>
diff --git a/ready_se/google/keymint/KM200/HAL/android.hardware.security.sharedsecret-service.strongbox.xml b/ready_se/google/keymint/KM200/HAL/android.hardware.security.sharedsecret-service.strongbox.xml
new file mode 100644
index 0000000..5492100
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/android.hardware.security.sharedsecret-service.strongbox.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.security.sharedsecret</name>
+ <fqname>ISharedSecret/strongbox</fqname>
+ </hal>
+</manifest>
diff --git a/ready_se/google/keymint/KM200/HAL/keymint_utils.cpp b/ready_se/google/keymint/KM200/HAL/keymint_utils.cpp
new file mode 100644
index 0000000..f613eda
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/keymint_utils.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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" 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.
+ */
+#include "keymint_utils.h"
+
+#include <regex.h>
+
+#include <android-base/properties.h>
+
+namespace keymint::javacard {
+
+namespace {
+
+constexpr char kPlatformVersionProp[] = "ro.build.version.release";
+constexpr char kPlatformVersionRegex[] = "^([0-9]{1,2})(\\.([0-9]{1,2}))?(\\.([0-9]{1,2}))?";
+constexpr size_t kMajorVersionMatch = 1;
+constexpr size_t kMinorVersionMatch = 3;
+constexpr size_t kSubminorVersionMatch = 5;
+constexpr size_t kPlatformVersionMatchCount = kSubminorVersionMatch + 1;
+
+constexpr char kPlatformPatchlevelProp[] = "ro.build.version.security_patch";
+constexpr char kVendorPatchlevelProp[] = "ro.vendor.build.security_patch";
+constexpr char kPatchlevelRegex[] = "^([0-9]{4})-([0-9]{2})-([0-9]{2})$";
+constexpr size_t kYearMatch = 1;
+constexpr size_t kMonthMatch = 2;
+constexpr size_t kDayMatch = 3;
+constexpr size_t kPatchlevelMatchCount = kDayMatch + 1;
+
+uint32_t match_to_uint32(const char* expression, const regmatch_t& match) {
+ if (match.rm_so == -1) return 0;
+
+ size_t len = match.rm_eo - match.rm_so;
+ std::string s(expression + match.rm_so, len);
+ return std::stoul(s);
+}
+
+std::string wait_and_get_property(const char* prop) {
+ std::string prop_value;
+ while (!::android::base::WaitForPropertyCreation(prop))
+ ;
+ prop_value = ::android::base::GetProperty(prop, "" /* default */);
+ return prop_value;
+}
+
+uint32_t getOsVersion(const char* version_str) {
+ regex_t regex;
+ if (regcomp(&regex, kPlatformVersionRegex, REG_EXTENDED)) {
+ return 0;
+ }
+
+ regmatch_t matches[kPlatformVersionMatchCount];
+ int not_match =
+ regexec(&regex, version_str, kPlatformVersionMatchCount, matches, 0 /* flags */);
+ regfree(&regex);
+ if (not_match) {
+ return 0;
+ }
+
+ uint32_t major = match_to_uint32(version_str, matches[kMajorVersionMatch]);
+ uint32_t minor = match_to_uint32(version_str, matches[kMinorVersionMatch]);
+ uint32_t subminor = match_to_uint32(version_str, matches[kSubminorVersionMatch]);
+
+ return (major * 100 + minor) * 100 + subminor;
+}
+
+enum class PatchlevelOutput { kYearMonthDay, kYearMonth };
+
+uint32_t getPatchlevel(const char* patchlevel_str, PatchlevelOutput detail) {
+ regex_t regex;
+ if (regcomp(&regex, kPatchlevelRegex, REG_EXTENDED) != 0) {
+ return 0;
+ }
+
+ regmatch_t matches[kPatchlevelMatchCount];
+ int not_match = regexec(&regex, patchlevel_str, kPatchlevelMatchCount, matches, 0 /* flags */);
+ regfree(&regex);
+ if (not_match) {
+ return 0;
+ }
+
+ uint32_t year = match_to_uint32(patchlevel_str, matches[kYearMatch]);
+ uint32_t month = match_to_uint32(patchlevel_str, matches[kMonthMatch]);
+
+ if (month < 1 || month > 12) {
+ return 0;
+ }
+
+ switch (detail) {
+ case PatchlevelOutput::kYearMonthDay: {
+ uint32_t day = match_to_uint32(patchlevel_str, matches[kDayMatch]);
+ if (day < 1 || day > 31) {
+ return 0;
+ }
+ return year * 10000 + month * 100 + day;
+ }
+ case PatchlevelOutput::kYearMonth:
+ return year * 100 + month;
+ }
+}
+
+} // anonymous namespace
+
+uint32_t getOsVersion() {
+ std::string version = wait_and_get_property(kPlatformVersionProp);
+ return getOsVersion(version.c_str());
+}
+
+uint32_t getOsPatchlevel() {
+ std::string patchlevel = wait_and_get_property(kPlatformPatchlevelProp);
+ return getPatchlevel(patchlevel.c_str(), PatchlevelOutput::kYearMonth);
+}
+
+uint32_t getVendorPatchlevel() {
+ std::string patchlevel = wait_and_get_property(kVendorPatchlevelProp);
+ return getPatchlevel(patchlevel.c_str(), PatchlevelOutput::kYearMonthDay);
+}
+
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM200/HAL/keymint_utils.h b/ready_se/google/keymint/KM200/HAL/keymint_utils.h
new file mode 100644
index 0000000..65cda63
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/keymint_utils.h
@@ -0,0 +1,47 @@
+/*
+ * 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" 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.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+// #include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
+
+// namespace aidl::android::hardware::security::keymint {
+namespace keymint::javacard {
+
+using std::vector;
+
+inline static std::vector<uint8_t> blob2vector(const uint8_t* data, const size_t length) {
+ std::vector<uint8_t> result(data, data + length);
+ return result;
+}
+
+inline static std::vector<uint8_t> blob2vector(const std::string& value) {
+ vector<uint8_t> result(reinterpret_cast<const uint8_t*>(value.data()),
+ reinterpret_cast<const uint8_t*>(value.data()) + value.size());
+ return result;
+}
+
+// HardwareAuthToken vector2AuthToken(const vector<uint8_t>& buffer);
+// vector<uint8_t> authToken2vector(const HardwareAuthToken& token);
+
+uint32_t getOsVersion();
+uint32_t getOsPatchlevel();
+uint32_t getVendorPatchlevel();
+
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM200/HAL/service.cpp b/ready_se/google/keymint/KM200/HAL/service.cpp
new file mode 100644
index 0000000..e83ee3d
--- /dev/null
+++ b/ready_se/google/keymint/KM200/HAL/service.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 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" 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.
+ */
+
+#define LOG_TAG "javacard.strongbox-service"
+
+#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "JavacardKeyMintDevice.h"
+#include "JavacardRemotelyProvisionedComponentDevice.h"
+#include "JavacardSecureElement.h"
+#include "JavacardSharedSecret.h"
+#include "OmapiTransport.h"
+#include "SocketTransport.h"
+#include "keymint_utils.h"
+
+using aidl::android::hardware::security::keymint::JavacardKeyMintDevice;
+using aidl::android::hardware::security::keymint::JavacardRemotelyProvisionedComponentDevice;
+using aidl::android::hardware::security::keymint::SecurityLevel;
+using aidl::android::hardware::security::sharedsecret::JavacardSharedSecret;
+using keymint::javacard::getOsPatchlevel;
+using keymint::javacard::getOsVersion;
+using keymint::javacard::getVendorPatchlevel;
+using keymint::javacard::ITransport;
+using keymint::javacard::JavacardSecureElement;
+using keymint::javacard::OmapiTransport;
+using keymint::javacard::SocketTransport;
+
+#define PROP_BUILD_QEMU "ro.kernel.qemu"
+#define PROP_BUILD_FINGERPRINT "ro.build.fingerprint"
+// Cuttlefish build fingerprint substring.
+#define CUTTLEFISH_FINGERPRINT_SS "aosp_cf_"
+
+template <typename T, class... Args> std::shared_ptr<T> addService(Args&&... args) {
+ std::shared_ptr<T> ser = ndk::SharedRefBase::make<T>(std::forward<Args>(args)...);
+ auto instanceName = std::string(T::descriptor) + "/strongbox";
+ LOG(INFO) << "adding javacard strongbox service instance: " << instanceName;
+ binder_status_t status =
+ AServiceManager_addService(ser->asBinder().get(), instanceName.c_str());
+ CHECK(status == STATUS_OK);
+ return ser;
+}
+
+std::shared_ptr<ITransport> getTransportInstance() {
+ bool isEmulator = false;
+ // Check if the current build is for emulator or device.
+ isEmulator = android::base::GetBoolProperty(PROP_BUILD_QEMU, false);
+ if (!isEmulator) {
+ std::string fingerprint = android::base::GetProperty(PROP_BUILD_FINGERPRINT, "");
+ if (!fingerprint.empty()) {
+ if (fingerprint.find(CUTTLEFISH_FINGERPRINT_SS, 0) != std::string::npos) {
+ isEmulator = true;
+ }
+ }
+ }
+
+ if (!isEmulator) {
+ return std::make_shared<OmapiTransport>();
+ } else {
+ return std::make_shared<SocketTransport>();
+ }
+}
+
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ // Javacard Secure Element
+ std::shared_ptr<JavacardSecureElement> card = std::make_shared<JavacardSecureElement>(
+ getTransportInstance(), getOsVersion(), getOsPatchlevel(), getVendorPatchlevel());
+ // Add Keymint Service
+ addService<JavacardKeyMintDevice>(card);
+ // Add Shared Secret Service
+ addService<JavacardSharedSecret>(card);
+ // Add Remotely Provisioned Component Service
+ addService<JavacardRemotelyProvisionedComponentDevice>(card);
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reach
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java
new file mode 100644
index 0000000..018ac02
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java
@@ -0,0 +1,622 @@
+/*
+ * 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.keymaster;
+
+import com.android.javacard.seprovider.KMAndroidSEProvider;
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.APDU;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+import javacard.security.CryptoException;
+import org.globalplatform.upgrade.Element;
+import org.globalplatform.upgrade.OnUpgradeListener;
+import org.globalplatform.upgrade.UpgradeManager;
+
+/**
+ * This class extends from KMKeymasterApplet which is main entry point to receive apdu commands. All
+ * the provision commands are processed here and later the data is handed over to the KMDataStore
+ * class which stores the data in the flash memory.
+ */
+public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener {
+ // Magic number version stored along with provisioned data. This is used to differentiate
+ // between data before and after the magic number is used.
+ private static final byte KM_MAGIC_NUMBER = (byte) 0x82;
+ // MSB byte is for Major version and LSB byte is for Minor version.
+ public static final short KM_APPLET_PACKAGE_VERSION = 0x0400;
+ // This flag is used to know if card reset happened.
+ private static final short POWER_RESET_MASK_FLAG = (short) 0x4000;
+
+ // Provider specific Commands
+ private static final byte INS_KEYMINT_PROVIDER_APDU_START = 0x00;
+ private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 3;
+ // Commands 4, 5 and 6 are reserved for vendor usage.
+ private static final byte INS_GET_PROVISION_STATUS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 7;
+ // 0x08 was reserved for INS_INIT_STRONGBOX_CMD
+ // 0x09 was reserved for INS_SET_BOOT_ENDED_CMD earlier. it is unused now.
+ private static final byte INS_SE_FACTORY_PROVISIONING_LOCK_CMD =
+ INS_KEYMINT_PROVIDER_APDU_START + 10;
+ private static final byte INS_PROVISION_OEM_ROOT_PUBLIC_KEY_CMD =
+ INS_KEYMINT_PROVIDER_APDU_START + 11;
+ private static final byte INS_OEM_UNLOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 12;
+ private static final byte INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD =
+ INS_KEYMINT_PROVIDER_APDU_START + 13;
+ private static final byte INS_PROVISION_RKP_UDS_CERT_CHAIN_CMD =
+ INS_KEYMINT_PROVIDER_APDU_START + 14;
+ private static final byte INS_PROVISION_PRESHARED_SECRET_CMD =
+ INS_KEYMINT_PROVIDER_APDU_START + 15;
+ private static final byte INS_SET_BOOT_PARAMS_CMD =
+ INS_KEYMINT_PROVIDER_APDU_START + 16; // Unused
+ private static final byte INS_OEM_LOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 17;
+ private static final byte INS_PROVISION_SECURE_BOOT_MODE_CMD =
+ INS_KEYMINT_PROVIDER_APDU_START + 18;
+
+ private static final byte INS_KEYMINT_PROVIDER_APDU_END = 0x1F;
+ // The length of the provisioned pre shared key.
+ public static final byte SHARED_SECRET_KEY_SIZE = 32;
+
+ // Version of the database which is used to differentiate between different version of the
+ // database.
+ protected short packageVersion;
+
+ KMAndroidSEApplet() {
+ super(new KMAndroidSEProvider());
+ packageVersion = KM_APPLET_PACKAGE_VERSION;
+ }
+
+ /**
+ * Installs this applet.
+ *
+ * @param bArray the array containing installation parameters
+ * @param bOffset the starting offset in bArray
+ * @param bLength the length in bytes of the parameter data in bArray
+ */
+ public static void install(byte[] bArray, short bOffset, byte bLength) {
+ new KMAndroidSEApplet().register(bArray, (short) (bOffset + 1), bArray[bOffset]);
+ }
+
+ public void handleDeviceBooted() {
+ if (seProvider.isBootSignalEventSupported() && seProvider.isDeviceRebooted()) {
+ kmDataStore.clearDeviceBootStatus();
+ super.reboot();
+ seProvider.clearDeviceBooted(true);
+ }
+ }
+
+ @Override
+ public void updateApduStatusFlags(short apduIns) {
+ apduStatusFlags[APDU_INCOMING_AND_RECEIVE_STATUS_INDEX] = 0;
+ apduStatusFlags[APDU_CASE4_COMMAND_STATUS_INDEX] = 1;
+ switch (apduIns) {
+ case INS_GET_PROVISION_STATUS_CMD:
+ case INS_SE_FACTORY_PROVISIONING_LOCK_CMD:
+ apduStatusFlags[APDU_CASE4_COMMAND_STATUS_INDEX] = 0;
+ break;
+ default:
+ super.updateApduStatusFlags(apduIns);
+ }
+ }
+
+ @Override
+ public void process(APDU apdu) {
+ try {
+ handleDeviceBooted();
+ // If this is select applet apdu which is selecting this applet then return
+ if (apdu.isISOInterindustryCLA()) {
+ if (selectingApplet()) {
+ return;
+ }
+ }
+ short apduIns = validateApdu(apdu);
+ if (apduIns == KMType.INVALID_VALUE) {
+ return;
+ }
+ updateApduStatusFlags(apduIns);
+ if (((KMAndroidSEProvider) seProvider).isPowerReset()) {
+ super.powerReset();
+ }
+
+ if (isCommandAllowed(apduIns)) {
+ switch (apduIns) {
+ case INS_PROVISION_ATTEST_IDS_CMD:
+ processProvisionAttestIdsCmd(apdu);
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_ATTEST_IDS);
+ sendResponse(apdu, KMError.OK);
+ break;
+
+ case INS_PROVISION_PRESHARED_SECRET_CMD:
+ processProvisionPreSharedSecretCmd(apdu);
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_PRESHARED_SECRET);
+ sendResponse(apdu, KMError.OK);
+ break;
+
+ case INS_GET_PROVISION_STATUS_CMD:
+ processGetProvisionStatusCmd(apdu);
+ break;
+
+ case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD:
+ processProvisionRkpDeviceUniqueKeyPair(apdu);
+ break;
+
+ case INS_PROVISION_RKP_UDS_CERT_CHAIN_CMD:
+ processProvisionRkpUdsCertChain(apdu);
+ break;
+
+ case INS_SE_FACTORY_PROVISIONING_LOCK_CMD:
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_SE_LOCKED);
+ sendResponse(apdu, KMError.OK);
+ break;
+
+ case INS_PROVISION_OEM_ROOT_PUBLIC_KEY_CMD:
+ processProvisionOEMRootPublicKeyCmd(apdu);
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_OEM_PUBLIC_KEY);
+ sendResponse(apdu, KMError.OK);
+ break;
+
+ case INS_OEM_LOCK_PROVISIONING_CMD:
+ processOEMLockProvisionCmd(apdu);
+ break;
+
+ case INS_OEM_UNLOCK_PROVISIONING_CMD:
+ processOEMUnlockProvisionCmd(apdu);
+ break;
+
+ case INS_PROVISION_SECURE_BOOT_MODE_CMD:
+ processSecureBootCmd(apdu);
+ break;
+
+ default:
+ super.process(apdu);
+ break;
+ }
+ } else {
+ ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED);
+ }
+ } catch (KMException exception) {
+ sendResponse(apdu, KMException.reason());
+ } catch (ISOException exp) {
+ sendResponse(apdu, mapISOErrorToKMError(exp.getReason()));
+ } catch (CryptoException e) {
+ sendResponse(apdu, mapCryptoErrorToKMError(e.getReason()));
+ } catch (Exception e) {
+ sendResponse(apdu, KMError.GENERIC_UNKNOWN_ERROR);
+ } finally {
+ repository.clean();
+ }
+ }
+
+ private boolean isCommandAllowed(short apduIns) {
+ boolean result = true;
+ switch (apduIns) {
+ case INS_PROVISION_ATTEST_IDS_CMD:
+ case INS_PROVISION_PRESHARED_SECRET_CMD:
+ case INS_PROVISION_SECURE_BOOT_MODE_CMD:
+ case INS_PROVISION_OEM_ROOT_PUBLIC_KEY_CMD:
+ if (kmDataStore.isProvisionLocked()) {
+ result = false;
+ }
+ break;
+
+ case INS_OEM_UNLOCK_PROVISIONING_CMD:
+ if (!kmDataStore.isProvisionLocked()) {
+ result = false;
+ }
+ break;
+
+ case INS_SE_FACTORY_PROVISIONING_LOCK_CMD:
+ if (isSeFactoryProvisioningLocked() || !isSeFactoryProvisioningComplete()) {
+ result = false;
+ }
+ break;
+
+ case INS_OEM_LOCK_PROVISIONING_CMD:
+ // Allow lock only when
+ // 1. All the necessary provisioning commands are succcessfully executed
+ // 2. SE provision is locked
+ // 3. OEM Root Public is provisioned.
+ if (kmDataStore.isProvisionLocked()
+ || !(isProvisioningComplete() && isSeFactoryProvisioningLocked())) {
+ result = false;
+ }
+ break;
+
+ case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD:
+ case INS_PROVISION_RKP_UDS_CERT_CHAIN_CMD:
+ if (isSeFactoryProvisioningLocked()) {
+ result = false;
+ }
+ break;
+
+ case INS_GET_PROVISION_STATUS_CMD:
+ break;
+
+ default:
+ // Allow other commands only if provision is completed.
+ if (!isProvisioningComplete()) {
+ result = false;
+ }
+ }
+ return result;
+ }
+
+ private boolean isSeFactoryProvisioningLocked() {
+ short pStatus = kmDataStore.getProvisionStatus();
+ boolean result = false;
+ if ((0 != (pStatus & PROVISION_STATUS_SE_LOCKED))) {
+ result = true;
+ }
+ return result;
+ }
+
+ private boolean isSeFactoryProvisioningComplete() {
+ short pStatus = kmDataStore.getProvisionStatus();
+ if (PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR
+ == (pStatus & PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR)) {
+ return true;
+ }
+ return false;
+ }
+
+ private void processSecureBootCmd(APDU apdu) {
+ short argsProto = KMArray.instance((short) 1);
+ KMArray.cast(argsProto).add((short) 0, KMInteger.exp());
+ short args = receiveIncoming(apdu, argsProto);
+ short val = KMInteger.cast(KMArray.cast(args).get((short) 0)).getShort();
+ if (val != 1 && val != 0) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ // Store secure boot mode value.
+ JCSystem.beginTransaction();
+ kmDataStore.secureBootMode = (byte) val;
+ JCSystem.commitTransaction();
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_SECURE_BOOT_MODE);
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private void processOEMUnlockProvisionCmd(APDU apdu) {
+ authenticateOEM(OEM_UNLOCK_PROVISION_VERIFICATION_LABEL, apdu);
+ kmDataStore.unlockProvision();
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private void processOEMLockProvisionCmd(APDU apdu) {
+ authenticateOEM(OEM_LOCK_PROVISION_VERIFICATION_LABEL, apdu);
+ // Enable the lock bit in provision status.
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_PROVISIONING_LOCKED);
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private void authenticateOEM(byte[] plainMsg, APDU apdu) {
+
+ tmpVariables[0] = KMArray.instance((short) 1);
+ KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp());
+ short args = receiveIncoming(apdu, tmpVariables[0]);
+ // Get the signature input.
+ short signature = KMArray.cast(args).get((short) 0);
+ byte[] oemPublicKey = kmDataStore.getOEMRootPublicKey();
+
+ if (!seProvider.ecVerify256(
+ oemPublicKey,
+ (short) 0,
+ (short) oemPublicKey.length,
+ plainMsg,
+ (short) 0,
+ (short) plainMsg.length,
+ KMByteBlob.cast(signature).getBuffer(),
+ KMByteBlob.cast(signature).getStartOff(),
+ KMByteBlob.cast(signature).length())) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+ }
+
+ private void processProvisionOEMRootPublicKeyCmd(APDU apdu) {
+ // Arguments
+ short keyparams = KMKeyParameters.exp();
+ short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT);
+ short blob = KMByteBlob.exp();
+ short argsProto = KMArray.instance((short) 3);
+ KMArray.cast(argsProto).add((short) 0, keyparams);
+ KMArray.cast(argsProto).add((short) 1, keyFormatPtr);
+ KMArray.cast(argsProto).add((short) 2, blob);
+ short args = receiveIncoming(apdu, argsProto);
+
+ // key params should have os patch, os version and verified root of trust
+ data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 0);
+ tmpVariables[0] = KMArray.cast(args).get((short) 1);
+ // Key format must be RAW format
+ byte keyFormat = KMEnum.cast(tmpVariables[0]).getVal();
+ if (keyFormat != KMType.RAW) {
+ KMException.throwIt(KMError.UNIMPLEMENTED);
+ }
+
+ // get algorithm - only EC keys expected
+ tmpVariables[0] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]);
+ if (tmpVariables[0] != KMType.EC) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ // get digest - only SHA256 supported
+ tmpVariables[0] =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]);
+ if (tmpVariables[0] != KMType.INVALID_VALUE) {
+ if (KMEnumArrayTag.cast(tmpVariables[0]).length() != 1) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ tmpVariables[0] = KMEnumArrayTag.cast(tmpVariables[0]).get((short) 0);
+ if (tmpVariables[0] != KMType.SHA2_256) {
+ KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+ }
+ } else {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ // Purpose should be VERIFY
+ tmpVariables[0] =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, data[KEY_PARAMETERS]);
+ if (tmpVariables[0] != KMType.INVALID_VALUE) {
+ if (KMEnumArrayTag.cast(tmpVariables[0]).length() != 1) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ tmpVariables[0] = KMEnumArrayTag.cast(tmpVariables[0]).get((short) 0);
+ if (tmpVariables[0] != KMType.VERIFY) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+ }
+ } else {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+
+ tmpVariables[0] = KMArray.cast(args).get((short) 2);
+ // persist OEM Root Public Key.
+ kmDataStore.persistOEMRootPublicKey(
+ KMByteBlob.cast(tmpVariables[0]).getBuffer(),
+ KMByteBlob.cast(tmpVariables[0]).getStartOff(),
+ KMByteBlob.cast(tmpVariables[0]).length());
+ }
+
+ private static void processProvisionRkpDeviceUniqueKeyPair(APDU apdu) {
+ // Re-purpose the apdu buffer as scratch pad.
+ byte[] scratchPad = apdu.getBuffer();
+ short arr = KMArray.instance((short) 1);
+ short coseKeyExp = KMCoseKey.exp();
+ KMArray.cast(arr).add((short) 0, coseKeyExp); // [ CoseKey ]
+ arr = receiveIncoming(apdu, arr);
+ // Get cose key.
+ short coseKey = KMArray.cast(arr).get((short) 0);
+ short pubKeyLen = KMCoseKey.cast(coseKey).getEcdsa256PublicKey(scratchPad, (short) 0);
+ short privKeyLen = KMCoseKey.cast(coseKey).getPrivateKey(scratchPad, pubKeyLen);
+ // Store the Device unique Key.
+ kmDataStore.createRkpDeviceUniqueKeyPair(
+ scratchPad, (short) 0, pubKeyLen, scratchPad, pubKeyLen, privKeyLen);
+ short dcc = generateDiceCertChain(scratchPad);
+ short len = KMKeymasterApplet.encodeToApduBuffer(dcc, scratchPad, (short) 0, MAX_COSE_BUF_SIZE);
+ kmDataStore.persistBootCertificateChain(scratchPad, (short) 0, len);
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR);
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private void processProvisionRkpUdsCertChain(APDU apdu) {
+ // X509 certificate chain is received as shown below:
+ /**
+ * x509CertChain = bstr .cbor UdsCerts
+ *
+ * <p>UdsCerts = { * SignerName => UdsCertChain }
+ *
+ * <p>; SignerName is a string identifier that indicates both the signing authority as ; well as
+ * the format of the UdsCertChain SignerName = tstr
+ *
+ * <p>UdsCertChain = [ 2* X509Certificate ; Root -> ... -> Leaf. "Root" is the vendor
+ * self-signed ; cert, "Leaf" contains UDS_Public. There may also be ; intermediate certificates
+ * between Root and Leaf. ]
+ *
+ * <p>; A bstr containing a DER-encoded X.509 certificate (RSA, NIST P-curve, or EdDSA)
+ * X509Certificate = bstr
+ */
+ // Store the CBOR encoded UdsCerts as it is in the persistent memory so cbor decoding is
+ // required here.
+ byte[] srcBuffer = apdu.getBuffer();
+ short recvLen = apdu.setIncomingAndReceive();
+ apduStatusFlags[APDU_INCOMING_AND_RECEIVE_STATUS_INDEX] = 1;
+ short srcOffset = apdu.getOffsetCdata();
+ short bufferLength = apdu.getIncomingLength();
+ short bufferStartOffset = repository.allocReclaimableMemory(bufferLength);
+ short index = bufferStartOffset;
+ byte[] buffer = repository.getHeap();
+ while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) {
+ Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen);
+ index += recvLen;
+ recvLen = apdu.receiveBytes(srcOffset);
+ }
+ short byteHeaderLen =
+ decoder.readCertificateChainHeaderLen(buffer, bufferStartOffset, bufferLength);
+ kmDataStore.persistUdsCertChain(
+ buffer,
+ (short) (bufferStartOffset + byteHeaderLen),
+ (short) (bufferLength - byteHeaderLen));
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_UDS_CERT_CHAIN);
+ // reclaim memory
+ repository.reclaimMemory(bufferLength);
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private void processProvisionAttestIdsCmd(APDU apdu) {
+ short keyparams = KMKeyParameters.exp();
+ short cmd = KMArray.instance((short) 1);
+ KMArray.cast(cmd).add((short) 0, keyparams);
+ short args = receiveIncoming(apdu, cmd);
+
+ short attData = KMArray.cast(args).get((short) 0);
+ // persist attestation Ids - if any is missing then exception occurs
+ setAttestationIds(attData);
+ }
+
+ public void setAttestationIds(short attIdVals) {
+ KMKeyParameters instParam = KMKeyParameters.cast(attIdVals);
+ KMArray vals = KMArray.cast(instParam.getVals());
+ short index = 0;
+ short length = vals.length();
+ short key;
+ short type;
+ short obj;
+ while (index < length) {
+ obj = vals.get(index);
+ key = KMTag.getKey(obj);
+ type = KMTag.getTagType(obj);
+
+ if (KMType.BYTES_TAG != type) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ obj = KMByteTag.cast(obj).getValue();
+ if (KMByteBlob.cast(obj).length() > KMConfigurations.MAX_ATTESTATION_IDS_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ kmDataStore.setAttestationId(
+ key,
+ KMByteBlob.cast(obj).getBuffer(),
+ KMByteBlob.cast(obj).getStartOff(),
+ KMByteBlob.cast(obj).length());
+ index++;
+ }
+ }
+
+ private void processProvisionPreSharedSecretCmd(APDU apdu) {
+ short blob = KMByteBlob.exp();
+ short argsProto = KMArray.instance((short) 1);
+ KMArray.cast(argsProto).add((short) 0, blob);
+ short args = receiveIncoming(apdu, argsProto);
+
+ short val = KMArray.cast(args).get((short) 0);
+
+ if (val != KMType.INVALID_VALUE && KMByteBlob.cast(val).length() != SHARED_SECRET_KEY_SIZE) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ // Persist shared Hmac.
+ kmDataStore.createPresharedKey(
+ KMByteBlob.cast(val).getBuffer(),
+ KMByteBlob.cast(val).getStartOff(),
+ KMByteBlob.cast(val).length());
+ }
+
+ // This function masks the error code with POWER_RESET_MASK_FLAG
+ // in case if card reset event occurred. The clients of the Applet
+ // has to extract the power reset status from the error code and
+ // process accordingly.
+ private static short buildErrorStatus(short err) {
+ short int32Ptr = KMInteger.instance((short) 4);
+ short powerResetStatus = 0;
+ if (((KMAndroidSEProvider) seProvider).isPowerReset()) {
+ powerResetStatus = POWER_RESET_MASK_FLAG;
+ }
+
+ Util.setShort(
+ KMInteger.cast(int32Ptr).getBuffer(),
+ KMInteger.cast(int32Ptr).getStartOff(),
+ powerResetStatus);
+
+ Util.setShort(
+ KMInteger.cast(int32Ptr).getBuffer(),
+ (short) (KMInteger.cast(int32Ptr).getStartOff() + 2),
+ err);
+ // reset power reset status flag to its default value.
+ // repository.restorePowerResetStatus(); //TODO
+ return int32Ptr;
+ }
+
+ private void processGetProvisionStatusCmd(APDU apdu) {
+ byte[] scratchpad = apdu.getBuffer();
+ short pStatus = kmDataStore.getProvisionStatus();
+ Util.setShort(scratchpad, (short) 0, pStatus);
+ short resp = KMArray.instance((short) 2);
+ KMArray.cast(resp).add((short) 0, buildErrorStatus(KMError.OK));
+ KMArray.cast(resp).add((short) 1, KMInteger.instance(scratchpad, (short) 0, (short) 2));
+ sendOutgoing(apdu, resp);
+ }
+
+ private boolean isProvisioningComplete() {
+ short pStatus = kmDataStore.getProvisionStatus();
+ short pCompleteStatus =
+ PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR
+ | PROVISION_STATUS_PRESHARED_SECRET
+ | PROVISION_STATUS_ATTEST_IDS
+ | PROVISION_STATUS_OEM_PUBLIC_KEY
+ | PROVISION_STATUS_SECURE_BOOT_MODE;
+ if (kmDataStore.isProvisionLocked() || (pCompleteStatus == (pStatus & pCompleteStatus))) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onCleanup() {}
+
+ @Override
+ public void onConsolidate() {}
+
+ private boolean isUpgradeAllowed(short oldVersion) {
+ boolean upgradeAllowed = false;
+ // Downgrade of the Applet is not allowed.
+ if (KM_APPLET_PACKAGE_VERSION >= oldVersion) {
+ upgradeAllowed = true;
+ }
+ return upgradeAllowed;
+ }
+
+ @Override
+ public void onRestore(Element element) {
+ element.initRead();
+ byte magicNumber = element.readByte();
+ if (magicNumber != KM_MAGIC_NUMBER) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short oldPackageVersion = element.readShort();
+ // Validate version.
+ if (!isUpgradeAllowed(oldPackageVersion)) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ kmDataStore.onRestore(element, oldPackageVersion, KM_APPLET_PACKAGE_VERSION);
+ }
+
+ @Override
+ public Element onSave() {
+ short primitiveCount = 3;
+ primitiveCount += kmDataStore.getBackupPrimitiveByteCount();
+ short objectCount = kmDataStore.getBackupObjectCount();
+ // Create element.
+ Element element =
+ UpgradeManager.createElement(Element.TYPE_SIMPLE, primitiveCount, objectCount);
+
+ element.write(KM_MAGIC_NUMBER);
+ element.write(packageVersion);
+ kmDataStore.onSave(element);
+ return element;
+ }
+
+ private short validateApdu(APDU apdu) {
+ // Read the apdu header and buffer.
+ byte[] apduBuffer = apdu.getBuffer();
+ short P1P2 = Util.getShort(apduBuffer, ISO7816.OFFSET_P1);
+
+ // Validate CLA
+ if (!apdu.isValidCLA()) {
+ ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
+ }
+
+ // Validate P1P2.
+ if (P1P2 != KMKeymasterApplet.KM_HAL_VERSION) {
+ sendResponse(apdu, KMError.INVALID_P1P2);
+ return KMType.INVALID_VALUE;
+ }
+ return apduBuffer[ISO7816.OFFSET_INS];
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java
new file mode 100644
index 0000000..bf9e7ac
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java
@@ -0,0 +1,1056 @@
+/*
+ * 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.keymaster;
+
+import com.android.javacard.seprovider.KMAESKey;
+import com.android.javacard.seprovider.KMAttestationCert;
+import com.android.javacard.seprovider.KMException;
+import com.android.javacard.seprovider.KMKey;
+import com.android.javacard.seprovider.KMSEProvider;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * The class encodes strongbox generated and signed attestation certificates. It only encodes the
+ * required fields of the certificates. This class is not meant to be a generic X509 cert encoder.
+ * Any fields that are fixed are added as byte arrays. Extensions are encoded as per the values. The
+ * certificate is assembled with leafs first and then the sequences.
+ */
+public class KMAttestationCertImpl implements KMAttestationCert {
+
+ // The maximum size of the either software or hardware parameters.
+ private static final byte MAX_PARAMS = 30;
+ // DER encoded object identifiers required by the cert.
+ // rsaEncryption - 1.2.840.113549.1.1.1
+ private static final byte[] rsaEncryption = {
+ 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01
+ };
+ // ecPublicKey - 1.2.840.10045.2.1
+ private static final byte[] eccPubKey = {
+ 0x06, 0x07, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x02, 0x01
+ };
+ // prime256v1 curve - 1.2.840.10045.3.1.7
+ private static final byte[] prime256v1 = {
+ 0x06, 0x08, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x03, 0x01, 0x07
+ };
+ // Key Usage Extn - 2.5.29.15
+ private static final byte[] keyUsageExtn = {0x06, 0x03, 0x55, 0x1D, 0x0F};
+ // Android Extn - 1.3.6.1.4.1.11129.2.1.17
+ private static final byte[] androidExtn = {
+ 0x06, 0x0A, 0X2B, 0X06, 0X01, 0X04, 0X01, (byte) 0XD6, 0X79, 0X02, 0X01, 0X11
+ };
+ // The length of the RSA signature.
+ private static final short RSA_SIG_LEN = 256;
+ // The maximum length of the ECDSA signature.
+ private static final byte ECDSA_MAX_SIG_LEN = 72;
+ // Signature algorithm identifier - ecdsaWithSha256 - 1.2.840.10045.4.3.2
+ // SEQUENCE of alg OBJ ID and parameters = NULL.
+ private static final byte[] X509EcdsaSignAlgIdentifier = {
+ 0x30, 0x0A, 0x06, 0x08, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, (byte) 0x3D, 0x04, 0x03, 0x02
+ };
+ // Signature algorithm identifier - sha256WithRSAEncryption - 1.2.840.113549.1.1.11
+ // SEQUENCE of alg OBJ ID and parameters = NULL.
+ private static final byte[] X509RsaSignAlgIdentifier = {
+ 0x30,
+ 0x0D,
+ 0x06,
+ 0x09,
+ 0x2A,
+ (byte) 0x86,
+ 0x48,
+ (byte) 0x86,
+ (byte) 0xF7,
+ 0x0D,
+ 0x01,
+ 0x01,
+ 0x0B,
+ 0x05,
+ 0x00
+ };
+
+ // Below are the allowed softwareEnforced Authorization tags inside the attestation certificate's
+ // extension.
+ private static final short[] swTagIds = {
+ KMType.ATTESTATION_APPLICATION_ID,
+ KMType.CREATION_DATETIME,
+ KMType.ALLOW_WHILE_ON_BODY,
+ KMType.USAGE_COUNT_LIMIT,
+ KMType.USAGE_EXPIRE_DATETIME,
+ KMType.ORIGINATION_EXPIRE_DATETIME,
+ KMType.ACTIVE_DATETIME,
+ };
+
+ // Below are the allowed hardwareEnforced Authorization tags inside the attestation certificate's
+ // extension.
+ private static final short[] hwTagIds = {
+ KMType.ATTESTATION_ID_SECOND_IMEI,
+ KMType.BOOT_PATCH_LEVEL,
+ KMType.VENDOR_PATCH_LEVEL,
+ KMType.ATTESTATION_ID_MODEL,
+ KMType.ATTESTATION_ID_MANUFACTURER,
+ KMType.ATTESTATION_ID_MEID,
+ KMType.ATTESTATION_ID_IMEI,
+ KMType.ATTESTATION_ID_SERIAL,
+ KMType.ATTESTATION_ID_PRODUCT,
+ KMType.ATTESTATION_ID_DEVICE,
+ KMType.ATTESTATION_ID_BRAND,
+ KMType.OS_PATCH_LEVEL,
+ KMType.OS_VERSION,
+ KMType.ROOT_OF_TRUST,
+ KMType.ORIGIN,
+ KMType.UNLOCKED_DEVICE_REQUIRED,
+ KMType.TRUSTED_CONFIRMATION_REQUIRED,
+ KMType.AUTH_TIMEOUT,
+ KMType.USER_AUTH_TYPE,
+ KMType.NO_AUTH_REQUIRED,
+ KMType.EARLY_BOOT_ONLY,
+ KMType.ROLLBACK_RESISTANCE,
+ KMType.RSA_OAEP_MGF_DIGEST,
+ KMType.RSA_PUBLIC_EXPONENT,
+ KMType.ECCURVE,
+ KMType.PADDING,
+ KMType.DIGEST,
+ KMType.KEYSIZE,
+ KMType.ALGORITHM,
+ KMType.PURPOSE
+ };
+ // Below are the constants for the key usage extension.
+ private static final byte keyUsageSign = (byte) 0x80; // 0 bit
+ private static final byte keyUsageKeyEncipher = (byte) 0x20; // 2nd- bit
+ private static final byte keyUsageDataEncipher = (byte) 0x10; // 3rd- bit
+ private static final byte keyUsageKeyAgreement = (byte) 0x08; // 4th- bit
+ private static final byte keyUsageCertSign = (byte) 0x04; // 5th- bit
+ // KeyMint HAL Version constant.
+ private static final short KEYMINT_VERSION = 300;
+ // Attestation version constant.
+ private static final short ATTESTATION_VERSION = 300;
+ // The X.509 version as per rfc5280#section-4.1.2.1
+ private static final byte X509_VERSION = (byte) 0x02;
+
+ // Buffer indexes in transient array
+ private static final byte NUM_INDEX_ENTRIES = 21;
+ private static final byte CERT_START = (byte) 0;
+ private static final byte CERT_LENGTH = (byte) 1;
+ private static final byte TBS_START = (byte) 2;
+ private static final byte TBS_LENGTH = (byte) 3;
+ private static final byte BUF_START = (byte) 4;
+ private static final byte BUF_LENGTH = (byte) 5;
+ private static final byte SW_PARAM_INDEX = (byte) 6;
+ private static final byte HW_PARAM_INDEX = (byte) 7;
+ // Data indexes in transient array
+ private static final byte STACK_PTR = (byte) 8;
+ private static final byte UNIQUE_ID = (byte) 9;
+ private static final byte ATT_CHALLENGE = (byte) 10;
+ private static final byte NOT_BEFORE = (byte) 11;
+ private static final byte NOT_AFTER = (byte) 12;
+ private static final byte PUB_KEY = (byte) 13;
+ private static final byte VERIFIED_BOOT_KEY = (byte) 14;
+ private static final byte VERIFIED_HASH = (byte) 15;
+ private static final byte ISSUER = (byte) 16;
+ private static final byte SUBJECT_NAME = (byte) 17;
+ private static final byte SERIAL_NUMBER = (byte) 18;
+ private static final byte CERT_ATT_KEY_SECRET = (byte) 19;
+ private static final byte CERT_ATT_KEY_RSA_PUB_MOD = (byte) 20;
+ // State indexes in transient array
+ private static final byte NUM_STATE_ENTRIES = 7;
+ private static final byte KEY_USAGE = (byte) 0;
+ private static final byte UNUSED_BITS = (byte) 1;
+ private static final byte DEVICE_LOCKED = (byte) 2;
+ private static final byte VERIFIED_STATE = (byte) 3;
+ private static final byte CERT_MODE = (byte) 4;
+ private static final byte RSA_CERT = (byte) 5;
+ private static final byte CERT_RSA_SIGN = (byte) 6;
+
+ private static KMAttestationCert inst;
+ private static KMSEProvider seProvider;
+
+ private static short[] indexes;
+ private static byte[] states;
+
+ private static byte[] stack;
+ private static short[] swParams;
+ private static short[] hwParams;
+ // The maximum size of the serial number.
+ private static final byte SERIAL_NUM_MAX_LEN = 20;
+
+ private KMAttestationCertImpl() {}
+
+ public static KMAttestationCert instance(boolean rsaCert, KMSEProvider provider) {
+ if (inst == null) {
+ inst = new KMAttestationCertImpl();
+ seProvider = provider;
+
+ // Allocate transient memory
+ indexes = JCSystem.makeTransientShortArray(NUM_INDEX_ENTRIES, JCSystem.CLEAR_ON_RESET);
+ states = JCSystem.makeTransientByteArray(NUM_STATE_ENTRIES, JCSystem.CLEAR_ON_RESET);
+ swParams = JCSystem.makeTransientShortArray(MAX_PARAMS, JCSystem.CLEAR_ON_RESET);
+ hwParams = JCSystem.makeTransientShortArray(MAX_PARAMS, JCSystem.CLEAR_ON_RESET);
+ }
+ init(rsaCert);
+ return inst;
+ }
+
+ private static void init(boolean rsaCert) {
+ for (short i = 0; i < NUM_INDEX_ENTRIES; i++) {
+ indexes[i] = 0;
+ }
+ Util.arrayFillNonAtomic(states, (short) 0, NUM_STATE_ENTRIES, (byte) 0);
+ stack = null;
+ states[CERT_MODE] = KMType.NO_CERT;
+ states[UNUSED_BITS] = 8;
+ states[RSA_CERT] = rsaCert ? (byte) 1 : (byte) 0;
+ states[CERT_RSA_SIGN] = 1;
+ indexes[CERT_ATT_KEY_SECRET] = KMType.INVALID_VALUE;
+ indexes[CERT_ATT_KEY_RSA_PUB_MOD] = KMType.INVALID_VALUE;
+ indexes[ISSUER] = KMType.INVALID_VALUE;
+ indexes[SUBJECT_NAME] = KMType.INVALID_VALUE;
+ indexes[SERIAL_NUMBER] = KMType.INVALID_VALUE;
+ }
+
+ @Override
+ public KMAttestationCert verifiedBootHash(short obj) {
+ indexes[VERIFIED_HASH] = obj;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert verifiedBootKey(short obj) {
+ indexes[VERIFIED_BOOT_KEY] = obj;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert verifiedBootState(byte val) {
+ states[VERIFIED_STATE] = val;
+ return this;
+ }
+
+ private KMAttestationCert uniqueId(short obj) {
+ indexes[UNIQUE_ID] = obj;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert notBefore(short obj, boolean derEncoded, byte[] scratchpad) {
+ if (!derEncoded) {
+ // convert milliseconds to UTC date
+ indexes[NOT_BEFORE] = KMUtils.convertToDate(obj, scratchpad, true);
+ } else {
+ indexes[NOT_BEFORE] =
+ KMByteBlob.instance(
+ KMByteBlob.cast(obj).getBuffer(),
+ KMByteBlob.cast(obj).getStartOff(),
+ KMByteBlob.cast(obj).length());
+ }
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert notAfter(
+ short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad) {
+ if (!derEncoded) {
+ if (usageExpiryTimeObj != KMType.INVALID_VALUE) {
+ // compare if the expiry time is greater then 2050 then use generalized
+ // time format else use utc time format.
+ short tmpVar = KMInteger.uint_64(KMUtils.firstJan2050, (short) 0);
+ if (KMInteger.compare(usageExpiryTimeObj, tmpVar) >= 0) {
+ usageExpiryTimeObj = KMUtils.convertToDate(usageExpiryTimeObj, scratchPad, false);
+ } else {
+ usageExpiryTimeObj = KMUtils.convertToDate(usageExpiryTimeObj, scratchPad, true);
+ }
+ indexes[NOT_AFTER] = usageExpiryTimeObj;
+ } else {
+ // notAfter = certExpirtyTimeObj;
+ }
+ } else {
+ indexes[NOT_AFTER] = usageExpiryTimeObj;
+ }
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert deviceLocked(boolean val) {
+ if (val) {
+ states[DEVICE_LOCKED] = (byte) 0xFF;
+ } else {
+ states[DEVICE_LOCKED] = 0;
+ }
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert publicKey(short obj) {
+ indexes[PUB_KEY] = obj;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert attestationChallenge(short obj) {
+ indexes[ATT_CHALLENGE] = obj;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert extensionTag(short tag, boolean hwEnforced) {
+ if (hwEnforced) {
+ hwParams[indexes[HW_PARAM_INDEX]] = tag;
+ indexes[HW_PARAM_INDEX]++;
+ } else {
+ swParams[indexes[SW_PARAM_INDEX]] = tag;
+ indexes[SW_PARAM_INDEX]++;
+ }
+ if (KMTag.getKey(tag) == KMType.PURPOSE) {
+ createKeyUsage(tag);
+ }
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert issuer(short obj) {
+ indexes[ISSUER] = obj;
+ return this;
+ }
+
+ private void createKeyUsage(short tag) {
+ short len = KMEnumArrayTag.cast(tag).length();
+ byte index = 0;
+ while (index < len) {
+ if (KMEnumArrayTag.cast(tag).get(index) == KMType.SIGN) {
+ states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageSign);
+ } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.WRAP_KEY) {
+ states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageKeyEncipher);
+ } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.DECRYPT) {
+ states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageDataEncipher);
+ } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.AGREE_KEY) {
+ states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageKeyAgreement);
+ } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.ATTEST_KEY) {
+ states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageCertSign);
+ }
+ index++;
+ }
+ index = states[KEY_USAGE];
+ while (index != 0) {
+ index = (byte) (index << 1);
+ states[UNUSED_BITS]--;
+ }
+ }
+
+ private static void pushTbsCert(boolean rsaCert, boolean rsa) {
+ short last = indexes[STACK_PTR];
+ pushExtensions();
+ // subject public key info
+ if (rsaCert) {
+ pushRsaSubjectKeyInfo();
+ } else {
+ pushEccSubjectKeyInfo();
+ }
+ // subject
+ pushBytes(
+ KMByteBlob.cast(indexes[SUBJECT_NAME]).getBuffer(),
+ KMByteBlob.cast(indexes[SUBJECT_NAME]).getStartOff(),
+ KMByteBlob.cast(indexes[SUBJECT_NAME]).length());
+ pushValidity();
+ // issuer - der encoded
+ pushBytes(
+ KMByteBlob.cast(indexes[ISSUER]).getBuffer(),
+ KMByteBlob.cast(indexes[ISSUER]).getStartOff(),
+ KMByteBlob.cast(indexes[ISSUER]).length());
+ // Algorithm Id
+ if (rsa) {
+ pushAlgorithmId(X509RsaSignAlgIdentifier);
+ } else {
+ pushAlgorithmId(X509EcdsaSignAlgIdentifier);
+ }
+ // Serial Number
+ pushBytes(
+ KMByteBlob.cast(indexes[SERIAL_NUMBER]).getBuffer(),
+ KMByteBlob.cast(indexes[SERIAL_NUMBER]).getStartOff(),
+ KMByteBlob.cast(indexes[SERIAL_NUMBER]).length());
+ pushIntegerHeader(KMByteBlob.cast(indexes[SERIAL_NUMBER]).length());
+ // Version
+ pushByte(X509_VERSION);
+ pushIntegerHeader((short) 1);
+ pushByte((byte) 0x03);
+ pushByte((byte) 0xA0);
+ // Finally sequence header.
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushExtensions() {
+ short last = indexes[STACK_PTR];
+ // Push KeyUsage extension
+ if (states[KEY_USAGE] != 0) {
+ pushKeyUsage(states[KEY_USAGE], states[UNUSED_BITS]);
+ }
+ if (states[CERT_MODE] == KMType.ATTESTATION_CERT) {
+ pushKeyDescription();
+ }
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ // Extensions have explicit tag of [3]
+ pushLength((short) (last - indexes[STACK_PTR]));
+ pushByte((byte) 0xA3);
+ }
+
+ // Time SEQUENCE{UTCTime, UTC or Generalized Time)
+ private static void pushValidity() {
+ short last = indexes[STACK_PTR];
+ if (indexes[NOT_AFTER] != 0) {
+ pushBytes(
+ KMByteBlob.cast(indexes[NOT_AFTER]).getBuffer(),
+ KMByteBlob.cast(indexes[NOT_AFTER]).getStartOff(),
+ KMByteBlob.cast(indexes[NOT_AFTER]).length());
+ } else {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ pushTimeHeader(KMByteBlob.cast(indexes[NOT_AFTER]).length());
+ pushBytes(
+ KMByteBlob.cast(indexes[NOT_BEFORE]).getBuffer(),
+ KMByteBlob.cast(indexes[NOT_BEFORE]).getStartOff(),
+ KMByteBlob.cast(indexes[NOT_BEFORE]).length());
+ pushTimeHeader(KMByteBlob.cast(indexes[NOT_BEFORE]).length());
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushTimeHeader(short len) {
+ if (len == 13) { // UTC Time
+ pushLength((short) 0x0D);
+ pushByte((byte) 0x17);
+ } else if (len == 15) { // Generalized Time
+ pushLength((short) 0x0F);
+ pushByte((byte) 0x18);
+ } else {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ }
+
+ // SEQUENCE{SEQUENCE{algId, NULL}, bitString{SEQUENCE{ modulus as positive integer, public
+ // exponent
+ // as positive integer}
+ private static void pushRsaSubjectKeyInfo() {
+ short last = indexes[STACK_PTR];
+ pushBytes(KMKeymasterApplet.F4, (short) 0, (short) KMKeymasterApplet.F4.length);
+ pushIntegerHeader((short) KMKeymasterApplet.F4.length);
+ pushBytes(
+ KMByteBlob.cast(indexes[PUB_KEY]).getBuffer(),
+ KMByteBlob.cast(indexes[PUB_KEY]).getStartOff(),
+ KMByteBlob.cast(indexes[PUB_KEY]).length());
+
+ // encode modulus as positive if the MSB is 1.
+ if (KMByteBlob.cast(indexes[PUB_KEY]).get((short) 0) < 0) {
+ pushByte((byte) 0x00);
+ pushIntegerHeader((short) (KMByteBlob.cast(indexes[PUB_KEY]).length() + 1));
+ } else {
+ pushIntegerHeader(KMByteBlob.cast(indexes[PUB_KEY]).length());
+ }
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ pushBitStringHeader((byte) 0x00, (short) (last - indexes[STACK_PTR]));
+ pushRsaEncryption();
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ // SEQUENCE{SEQUENCE{ecPubKey, prime256v1}, bitString{pubKey}}
+ private static void pushEccSubjectKeyInfo() {
+ short last = indexes[STACK_PTR];
+ pushBytes(
+ KMByteBlob.cast(indexes[PUB_KEY]).getBuffer(),
+ KMByteBlob.cast(indexes[PUB_KEY]).getStartOff(),
+ KMByteBlob.cast(indexes[PUB_KEY]).length());
+ pushBitStringHeader((byte) 0x00, KMByteBlob.cast(indexes[PUB_KEY]).length());
+ pushEcDsa();
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushEcDsa() {
+ short last = indexes[STACK_PTR];
+ pushBytes(prime256v1, (short) 0, (short) prime256v1.length);
+ pushBytes(eccPubKey, (short) 0, (short) eccPubKey.length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushRsaEncryption() {
+ short last = indexes[STACK_PTR];
+ pushNullHeader();
+ pushBytes(rsaEncryption, (short) 0, (short) rsaEncryption.length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ // KeyDescription ::= SEQUENCE {
+ // attestationVersion INTEGER, # Value 200
+ // attestationSecurityLevel SecurityLevel, # See below
+ // keymasterVersion INTEGER, # Value 200
+ // keymasterSecurityLevel SecurityLevel, # See below
+ // attestationChallenge OCTET_STRING, # Tag::ATTESTATION_CHALLENGE from attestParams
+ // uniqueId OCTET_STRING, # Empty unless key has Tag::INCLUDE_UNIQUE_ID
+ // softwareEnforced AuthorizationList, # See below
+ // hardwareEnforced AuthorizationList, # See below
+ // }
+ private static void pushKeyDescription() {
+ short last = indexes[STACK_PTR];
+ pushHWParams();
+ pushSWParams();
+ if (indexes[UNIQUE_ID] != 0) {
+ pushOctetString(
+ KMByteBlob.cast(indexes[UNIQUE_ID]).getBuffer(),
+ KMByteBlob.cast(indexes[UNIQUE_ID]).getStartOff(),
+ KMByteBlob.cast(indexes[UNIQUE_ID]).length());
+ } else {
+ pushOctetStringHeader((short) 0);
+ }
+ pushOctetString(
+ KMByteBlob.cast(indexes[ATT_CHALLENGE]).getBuffer(),
+ KMByteBlob.cast(indexes[ATT_CHALLENGE]).getStartOff(),
+ KMByteBlob.cast(indexes[ATT_CHALLENGE]).length());
+ pushEnumerated(KMType.STRONGBOX);
+ pushShort(KEYMINT_VERSION);
+ pushIntegerHeader((short) 2);
+ pushEnumerated(KMType.STRONGBOX);
+ pushShort(ATTESTATION_VERSION);
+ pushIntegerHeader((short) 2);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ pushOctetStringHeader((short) (last - indexes[STACK_PTR]));
+ pushBytes(androidExtn, (short) 0, (short) androidExtn.length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushSWParams() {
+ short last = indexes[STACK_PTR];
+ byte index = 0;
+ short length = (short) swTagIds.length;
+ do {
+ pushParams(swParams, indexes[SW_PARAM_INDEX], swTagIds[index]);
+ } while (++index < length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushHWParams() {
+ short last = indexes[STACK_PTR];
+ byte index = 0;
+ short length = (short) hwTagIds.length;
+ do {
+ if (hwTagIds[index] == KMType.ROOT_OF_TRUST) {
+ pushRoT();
+ continue;
+ }
+ if (pushParams(hwParams, indexes[HW_PARAM_INDEX], hwTagIds[index])) {
+ continue;
+ }
+ } while (++index < length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static boolean pushParams(short[] params, short len, short tagId) {
+ short index = 0;
+ while (index < len) {
+ if (tagId == KMTag.getKey(params[index])) {
+ pushTag(params[index]);
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ private static void pushTag(short tag) {
+ short type = KMTag.getTagType(tag);
+ short tagId = KMTag.getKey(tag);
+ short val;
+ switch (type) {
+ case KMType.BYTES_TAG:
+ val = KMByteTag.cast(tag).getValue();
+ pushBytesTag(
+ tagId,
+ KMByteBlob.cast(val).getBuffer(),
+ KMByteBlob.cast(val).getStartOff(),
+ KMByteBlob.cast(val).length());
+ break;
+ case KMType.ENUM_TAG:
+ val = KMEnumTag.cast(tag).getValue();
+ pushEnumTag(tagId, (byte) val);
+ break;
+ case KMType.ENUM_ARRAY_TAG:
+ val = KMEnumArrayTag.cast(tag).getValues();
+ pushEnumArrayTag(
+ tagId,
+ KMByteBlob.cast(val).getBuffer(),
+ KMByteBlob.cast(val).getStartOff(),
+ KMByteBlob.cast(val).length());
+ break;
+ case KMType.UINT_TAG:
+ case KMType.ULONG_TAG:
+ case KMType.DATE_TAG:
+ val = KMIntegerTag.cast(tag).getValue();
+ pushIntegerTag(
+ tagId,
+ KMInteger.cast(val).getBuffer(),
+ KMInteger.cast(val).getStartOff(),
+ KMInteger.cast(val).length());
+ break;
+ case KMType.UINT_ARRAY_TAG:
+ case KMType.ULONG_ARRAY_TAG:
+ // According to KeyMint hal only one user secure id is used but this conflicts with
+ // tag type which is ULONG-REP. Currently this is encoded as SET OF INTEGERS
+ val = KMIntegerArrayTag.cast(tag).getValues();
+ pushIntegerArrayTag(tagId, val);
+ break;
+ case KMType.BOOL_TAG:
+ val = KMBoolTag.cast(tag).getVal();
+ pushBoolTag(tagId);
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_TAG);
+ break;
+ }
+ }
+
+ // RootOfTrust ::= SEQUENCE {
+ // verifiedBootKey OCTET_STRING,
+ // deviceLocked BOOLEAN,
+ // verifiedBootState VerifiedBootState,
+ // verifiedBootHash OCTET_STRING,
+ // }
+ // VerifiedBootState ::= ENUMERATED {
+ // Verified (0),
+ // SelfSigned (1),
+ // Unverified (2),
+ // Failed (3),
+ // }
+ private static void pushRoT() {
+ short last = indexes[STACK_PTR];
+ // verified boot hash
+ pushOctetString(
+ KMByteBlob.cast(indexes[VERIFIED_HASH]).getBuffer(),
+ KMByteBlob.cast(indexes[VERIFIED_HASH]).getStartOff(),
+ KMByteBlob.cast(indexes[VERIFIED_HASH]).length());
+
+ pushEnumerated(states[VERIFIED_STATE]);
+
+ pushBoolean(states[DEVICE_LOCKED]);
+ // verified boot Key
+ pushOctetString(
+ KMByteBlob.cast(indexes[VERIFIED_BOOT_KEY]).getBuffer(),
+ KMByteBlob.cast(indexes[VERIFIED_BOOT_KEY]).getStartOff(),
+ KMByteBlob.cast(indexes[VERIFIED_BOOT_KEY]).length());
+
+ // Finally sequence header
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ // ... and tag Id
+ pushTagIdHeader(KMType.ROOT_OF_TRUST, (short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushOctetString(byte[] buf, short start, short len) {
+ pushBytes(buf, start, len);
+ pushOctetStringHeader(len);
+ }
+
+ private static void pushBoolean(byte val) {
+ pushByte(val);
+ pushBooleanHeader((short) 1);
+ }
+
+ private static void pushBooleanHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x01);
+ }
+
+ // Only SET of INTEGERS supported are padding, digest, purpose and blockmode
+ // All of these are enum array tags i.e. byte long values
+ private static void pushEnumArrayTag(short tagId, byte[] buf, short start, short len) {
+ short last = indexes[STACK_PTR];
+ short index = 0;
+ while (index < len) {
+ pushByte(buf[(short) (start + index)]);
+ pushIntegerHeader((short) 1);
+ index++;
+ }
+ pushSetHeader((short) (last - indexes[STACK_PTR]));
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ // Only SET of INTEGERS supported are padding, digest, purpose and blockmode
+ // All of these are enum array tags i.e. byte long values
+ private static void pushIntegerArrayTag(short tagId, short arr) {
+ short last = indexes[STACK_PTR];
+ short index = 0;
+ short len = KMArray.cast(arr).length();
+ short ptr;
+ while (index < len) {
+ ptr = KMArray.cast(arr).get(index);
+ pushInteger(
+ KMInteger.cast(ptr).getBuffer(),
+ KMInteger.cast(ptr).getStartOff(),
+ KMInteger.cast(ptr).length());
+ index++;
+ }
+ pushSetHeader((short) (last - indexes[STACK_PTR]));
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushSetHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x31);
+ }
+
+ private static void pushEnumerated(byte val) {
+ short last = indexes[STACK_PTR];
+ pushByte(val);
+ pushEnumeratedHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushEnumeratedHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x0A);
+ }
+
+ private static void pushBoolTag(short tagId) {
+ short last = indexes[STACK_PTR];
+ pushNullHeader();
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushNullHeader() {
+ pushByte((byte) 0);
+ pushByte((byte) 0x05);
+ }
+
+ private static void pushEnumTag(short tagId, byte val) {
+ short last = indexes[STACK_PTR];
+ pushByte(val);
+ pushIntegerHeader((short) (last - indexes[STACK_PTR]));
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushIntegerTag(short tagId, byte[] buf, short start, short len) {
+ short last = indexes[STACK_PTR];
+ pushInteger(buf, start, len);
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ // Ignore leading zeros. Only Unsigned Integers are required hence if MSB is set then add 0x00
+ // as most significant byte.
+ private static void pushInteger(byte[] buf, short start, short len) {
+ short last = indexes[STACK_PTR];
+ byte index = 0;
+ while (index < (byte) len) {
+ if (buf[(short) (start + index)] != 0) {
+ break;
+ }
+ index++;
+ }
+ if (index == (byte) len) {
+ pushByte((byte) 0x00);
+ } else {
+ pushBytes(buf, (short) (start + index), (short) (len - index));
+ if (buf[(short) (start + index)] < 0) { // MSB is 1
+ pushByte((byte) 0x00); // always unsigned int
+ }
+ }
+ pushIntegerHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ // Bytes Tag is a octet string and tag id is added explicitly
+ private static void pushBytesTag(short tagId, byte[] buf, short start, short len) {
+ short last = indexes[STACK_PTR];
+ pushBytes(buf, start, len);
+ pushOctetStringHeader((short) (last - indexes[STACK_PTR]));
+ pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+ }
+
+ // tag id <= 30 ---> 0xA0 | {tagId}
+ // 30 < tagId < 128 ---> 0xBF 0x{tagId}
+ // tagId >= 128 ---> 0xBF 0x80+(tagId/128) 0x{tagId - (128*(tagId/128))}
+ private static void pushTagIdHeader(short tagId, short len) {
+ pushLength(len);
+ short count = (short) (tagId / 128);
+ if (count > 0) {
+ pushByte((byte) (tagId - (128 * count)));
+ pushByte((byte) (0x80 + count));
+ pushByte((byte) 0xBF);
+ } else if (tagId > 30) {
+ pushByte((byte) tagId);
+ pushByte((byte) 0xBF);
+ } else {
+ pushByte((byte) (0xA0 | (byte) tagId));
+ }
+ }
+
+ // SEQUENCE {ObjId, OCTET STRING{BIT STRING{keyUsage}}}
+ private static void pushKeyUsage(byte keyUsage, byte unusedBits) {
+ short last = indexes[STACK_PTR];
+ pushByte(keyUsage);
+ pushBitStringHeader(unusedBits, (short) (last - indexes[STACK_PTR]));
+ pushOctetStringHeader((short) (last - indexes[STACK_PTR]));
+ pushBytes(keyUsageExtn, (short) 0, (short) keyUsageExtn.length);
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ }
+
+ private static void pushAlgorithmId(byte[] algId) {
+ pushBytes(algId, (short) 0, (short) algId.length);
+ }
+
+ private static void pushIntegerHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x02);
+ }
+
+ private static void pushOctetStringHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x04);
+ }
+
+ private static void pushSequenceHeader(short len) {
+ pushLength(len);
+ pushByte((byte) 0x30);
+ }
+
+ private static void pushBitStringHeader(byte unusedBits, short len) {
+ pushByte(unusedBits);
+ pushLength((short) (len + 1)); // 1 extra byte for unused bits byte
+ pushByte((byte) 0x03);
+ }
+
+ private static void pushLength(short len) {
+ if (len < 128) {
+ pushByte((byte) len);
+ } else if (len < 256) {
+ pushByte((byte) len);
+ pushByte((byte) 0x81);
+ } else {
+ pushShort(len);
+ pushByte((byte) 0x82);
+ }
+ }
+
+ private static void pushShort(short val) {
+ decrementStackPtr((short) 2);
+ Util.setShort(stack, indexes[STACK_PTR], val);
+ }
+
+ private static void pushByte(byte val) {
+ decrementStackPtr((short) 1);
+ stack[indexes[STACK_PTR]] = val;
+ }
+
+ private static void pushBytes(byte[] buf, short start, short len) {
+ decrementStackPtr(len);
+ if (buf != null) {
+ Util.arrayCopyNonAtomic(buf, start, stack, indexes[STACK_PTR], len);
+ }
+ }
+
+ private static void decrementStackPtr(short cnt) {
+ indexes[STACK_PTR] = (short) (indexes[STACK_PTR] - cnt);
+ if (indexes[BUF_START] > indexes[STACK_PTR]) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ }
+
+ @Override
+ public KMAttestationCert buffer(byte[] buf, short start, short maxLen) {
+ stack = buf;
+ indexes[BUF_START] = start;
+ indexes[BUF_LENGTH] = maxLen;
+ indexes[STACK_PTR] = (short) (indexes[BUF_START] + indexes[BUF_LENGTH]);
+ return this;
+ }
+
+ @Override
+ public short getCertStart() {
+ return indexes[CERT_START];
+ }
+
+ @Override
+ public short getCertLength() {
+ return indexes[CERT_LENGTH];
+ }
+
+ public void build(short attSecret, short attMod, boolean rsaSign, boolean fakeCert) {
+ indexes[STACK_PTR] = (short) (indexes[BUF_START] + indexes[BUF_LENGTH]);
+ short last = indexes[STACK_PTR];
+ short sigLen = 0;
+ if (fakeCert) {
+ rsaSign = true;
+ pushByte((byte) 0);
+ sigLen = 1;
+ }
+ // Push placeholder signature Bit string header
+ // This will potentially change at the end
+ else if (rsaSign) {
+ decrementStackPtr(RSA_SIG_LEN);
+ } else {
+ decrementStackPtr(ECDSA_MAX_SIG_LEN);
+ }
+ short signatureOffset = indexes[STACK_PTR];
+ pushBitStringHeader((byte) 0, (short) (last - indexes[STACK_PTR]));
+ if (rsaSign) {
+ pushAlgorithmId(X509RsaSignAlgIdentifier);
+ } else {
+ pushAlgorithmId(X509EcdsaSignAlgIdentifier);
+ }
+ indexes[TBS_LENGTH] = indexes[STACK_PTR];
+ pushTbsCert((states[RSA_CERT] == 0 ? false : true), rsaSign);
+ indexes[TBS_START] = indexes[STACK_PTR];
+ indexes[TBS_LENGTH] = (short) (indexes[TBS_LENGTH] - indexes[TBS_START]);
+ if (attSecret != KMType.INVALID_VALUE) {
+ // Sign with the attestation key
+ // The pubKey is the modulus.
+ if (rsaSign) {
+ sigLen =
+ seProvider.rsaSign256Pkcs1(
+ KMByteBlob.cast(attSecret).getBuffer(),
+ KMByteBlob.cast(attSecret).getStartOff(),
+ KMByteBlob.cast(attSecret).length(),
+ KMByteBlob.cast(attMod).getBuffer(),
+ KMByteBlob.cast(attMod).getStartOff(),
+ KMByteBlob.cast(attMod).length(),
+ stack,
+ indexes[TBS_START],
+ indexes[TBS_LENGTH],
+ stack,
+ signatureOffset);
+ if (sigLen > RSA_SIG_LEN) KMException.throwIt(KMError.UNKNOWN_ERROR);
+ } else {
+ sigLen =
+ seProvider.ecSign256(
+ KMByteBlob.cast(attSecret).getBuffer(),
+ KMByteBlob.cast(attSecret).getStartOff(),
+ KMByteBlob.cast(attSecret).length(),
+ stack,
+ indexes[TBS_START],
+ indexes[TBS_LENGTH],
+ stack,
+ signatureOffset);
+ if (sigLen > ECDSA_MAX_SIG_LEN) KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ // Adjust signature length
+ indexes[STACK_PTR] = signatureOffset;
+ pushBitStringHeader((byte) 0, sigLen);
+ } else if (!fakeCert) { // No attestation key provisioned in the factory
+ KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED);
+ }
+ last = (short) (signatureOffset + sigLen);
+ // Add certificate sequence header
+ indexes[STACK_PTR] = indexes[TBS_START];
+ pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+ indexes[CERT_START] = indexes[STACK_PTR];
+ indexes[CERT_LENGTH] = (short) (last - indexes[CERT_START]);
+ }
+
+ @Override
+ public void build() {
+ if (states[CERT_MODE] == KMType.FAKE_CERT) {
+ build(KMType.INVALID_VALUE, KMType.INVALID_VALUE, true, true);
+ } else {
+ build(
+ indexes[CERT_ATT_KEY_SECRET],
+ indexes[CERT_ATT_KEY_RSA_PUB_MOD],
+ (states[CERT_RSA_SIGN] == 0 ? false : true),
+ false);
+ }
+ }
+
+ @Override
+ public KMAttestationCert makeUniqueId(
+ byte[] scratchPad,
+ short scratchPadOff,
+ byte[] creationTime,
+ short timeOffset,
+ short creationTimeLen,
+ byte[] attestAppId,
+ short appIdOff,
+ short attestAppIdLen,
+ byte resetSinceIdRotation,
+ KMKey masterKey) {
+ // Concatenate T||C||R
+ // temporal count T
+ short temp =
+ KMUtils.countTemporalCount(
+ creationTime, timeOffset, creationTimeLen, scratchPad, scratchPadOff);
+ Util.setShort(scratchPad, (short) scratchPadOff, temp);
+ temp = scratchPadOff;
+ scratchPadOff += 2;
+
+ // Application Id C
+ Util.arrayCopyNonAtomic(attestAppId, appIdOff, scratchPad, scratchPadOff, attestAppIdLen);
+ scratchPadOff += attestAppIdLen;
+
+ // Reset After Rotation R
+ scratchPad[scratchPadOff] = resetSinceIdRotation;
+ scratchPadOff++;
+
+ // Get the key data from the master key
+ KMAESKey aesKey = (KMAESKey) masterKey;
+ short mKeyData = KMByteBlob.instance((short) (aesKey.aesKey.getSize() / 8));
+ aesKey.aesKey.getKey(
+ KMByteBlob.cast(mKeyData).getBuffer(), /* Key */
+ KMByteBlob.cast(mKeyData).getStartOff()); /* Key start*/
+ timeOffset = KMByteBlob.instance((short) 32);
+ appIdOff =
+ seProvider.hmacSign(
+ KMByteBlob.cast(mKeyData).getBuffer(), /* Key */
+ KMByteBlob.cast(mKeyData).getStartOff(), /* Key start*/
+ KMByteBlob.cast(mKeyData).length(), /* Key length*/
+ scratchPad, /* data */
+ temp, /* data start */
+ scratchPadOff, /* data length */
+ KMByteBlob.cast(timeOffset).getBuffer(), /* signature buffer */
+ KMByteBlob.cast(timeOffset).getStartOff()); /* signature start */
+ if (appIdOff != 32) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ return uniqueId(timeOffset);
+ }
+
+ @Override
+ public boolean serialNumber(short number) {
+ // https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.2
+ short length = KMByteBlob.cast(number).length();
+ if (length > SERIAL_NUM_MAX_LEN) {
+ return false;
+ }
+ // The serial number Must be a positive integer.
+ byte msb = KMByteBlob.cast(number).get((short) 0);
+ if (msb < 0 && length > (SERIAL_NUM_MAX_LEN - 1)) {
+ return false;
+ }
+ indexes[SERIAL_NUMBER] = number;
+ return true;
+ }
+
+ @Override
+ public boolean subjectName(short sub) {
+ if (sub == KMType.INVALID_VALUE || KMByteBlob.cast(sub).length() == 0) return false;
+ indexes[SUBJECT_NAME] = sub;
+ return true;
+ }
+
+ @Override
+ public KMAttestationCert ecAttestKey(short attestKey, byte mode) {
+ states[CERT_MODE] = mode;
+ indexes[CERT_ATT_KEY_SECRET] = attestKey;
+ indexes[CERT_ATT_KEY_RSA_PUB_MOD] = KMType.INVALID_VALUE;
+ states[CERT_RSA_SIGN] = 0;
+ return this;
+ }
+
+ @Override
+ public KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte mode) {
+ states[CERT_MODE] = mode;
+ indexes[CERT_ATT_KEY_SECRET] = attestPrivExp;
+ indexes[CERT_ATT_KEY_RSA_PUB_MOD] = attestMod;
+ states[CERT_RSA_SIGN] = 1;
+ return this;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java
new file mode 100644
index 0000000..b7a66d2
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java
@@ -0,0 +1,32 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+/**
+ * This class contains all the configuration values. Vendors can modify these values accordingly
+ * based on their environment.
+ */
+public class KMConfigurations {
+ // Machine types
+ public static final byte LITTLE_ENDIAN = 0x00;
+ public static final byte BIG_ENDIAN = 0x01;
+ public static final byte TEE_MACHINE_TYPE = LITTLE_ENDIAN;
+ // If the size of the attestation ids is known and lesser than 64
+ // then reduce the size here. It reduces the heap memory usage.
+ public static final byte MAX_ATTESTATION_IDS_SIZE = 64;
+ // DER subject max length.
+ public static final short MAX_SUBJECT_DER_LEN = 1095;
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java
new file mode 100644
index 0000000..95ee67f
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java
@@ -0,0 +1,440 @@
+/*
+ * 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.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.Util;
+
+/**
+ * This is a utility class which helps in converting date to UTC format and doing some arithmetic
+ * Operations.
+ */
+public class KMUtils {
+
+ // 64 bit unsigned calculations for time
+ public static final byte[] oneSecMsec = {0, 0, 0, 0, 0, 0, 0x03, (byte) 0xE8}; // 1000 msec
+ public static final byte[] oneMinMsec = {0, 0, 0, 0, 0, 0, (byte) 0xEA, 0x60}; // 60000 msec
+ public static final byte[] oneHourMsec = {
+ 0, 0, 0, 0, 0, 0x36, (byte) 0xEE, (byte) 0x80
+ }; // 3600000 msec
+ public static final byte[] oneDayMsec = {0, 0, 0, 0, 0x05, 0x26, 0x5C, 0x00}; // 86400000 msec
+ public static final byte[] oneMonthMsec = {
+ 0, 0, 0, 0, (byte) 0x9C, (byte) 0xBE, (byte) 0xBD, 0x50
+ }; // 2629746000 msec
+ public static final byte[] leapYearMsec = {
+ 0, 0, 0, 0x07, (byte) 0x5C, (byte) 0xD7, (byte) 0x88, 0x00
+ }; // 31622400000;
+ public static final byte[] yearMsec = {
+ 0, 0, 0, 0x07, 0x57, (byte) 0xB1, 0x2C, 0x00
+ }; // 31536000000
+ // Leap year(366) + 3 * 365
+ public static final byte[] fourYrsMsec = {
+ 0, 0, 0, 0x1D, 0x63, (byte) 0xEB, 0x0C, 0x00
+ }; // 126230400000
+ public static final byte[] firstJan2020 = {
+ 0, 0, 0x01, 0x6F, 0x5E, 0x66, (byte) 0xE8, 0x00
+ }; // 1577836800000 msec
+ public static final byte[] firstJan2050 = {
+ 0, 0, 0x02, 0x4b, (byte) 0xCE, 0x5C, (byte) 0xF0, 0x00
+ }; // 2524608000000
+ // msec
+ public static final byte[] febMonthLeapMSec = {
+ 0, 0, 0, 0, (byte) 0x95, 0x58, 0x6C, 0x00
+ }; // 2505600000
+ public static final byte[] febMonthMsec = {
+ 0, 0, 0, 0, (byte) 0x90, 0x32, 0x10, 0x00
+ }; // 2419200000
+ public static final byte[] ThirtyOneDaysMonthMsec = {
+ 0, 0, 0, 0, (byte) 0x9F, (byte) 0xA5, 0x24, 0x00
+ }; // 2678400000
+ public static final byte[] ThirtDaysMonthMsec = {
+ 0, 0, 0, 0, (byte) 0x9A, 0x7E, (byte) 0xC8, 0x00
+ }; // 2592000000
+ public static final short year2051 = 2051;
+ public static final short year2020 = 2020;
+ // Convert to milliseconds constants
+ public static final byte[] SEC_TO_MILLIS_SHIFT_POS = {9, 8, 7, 6, 5, 3};
+
+ // --------------------------------------
+ public static short convertToDate(short time, byte[] scratchPad, boolean utcFlag) {
+
+ short yrsCount = 0;
+ short monthCount = 1;
+ short dayCount = 1;
+ short hhCount = 0;
+ short mmCount = 0;
+ short ssCount = 0;
+ byte Z = 0x5A;
+ boolean from2020 = true;
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+ Util.arrayCopyNonAtomic(
+ KMInteger.cast(time).getBuffer(),
+ KMInteger.cast(time).getStartOff(),
+ scratchPad,
+ (short) (8 - KMInteger.cast(time).length()),
+ KMInteger.cast(time).length());
+ // If the time is less then 1 Jan 2020 then it is an error
+ if (KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, firstJan2020, (short) 0, (short) 8)
+ < 0) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ if (utcFlag
+ && KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, firstJan2050, (short) 0, (short) 8)
+ >= 0) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+
+ if (KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, firstJan2050, (short) 0, (short) 8)
+ < 0) {
+ Util.arrayCopyNonAtomic(firstJan2020, (short) 0, scratchPad, (short) 8, (short) 8);
+ subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8);
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ } else {
+ from2020 = false;
+ Util.arrayCopyNonAtomic(firstJan2050, (short) 0, scratchPad, (short) 8, (short) 8);
+ subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8);
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ }
+ // divide the given time with four yrs msec count
+ if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, fourYrsMsec, (short) 0, (short) 8)
+ >= 0) {
+ Util.arrayCopyNonAtomic(fourYrsMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ // quotient is multiple of 4
+ yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16);
+ yrsCount = (short) (yrsCount * 4); // number of yrs.
+ // copy reminder as new dividend
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ }
+
+ // Get the leap year index starting from the (base Year + yrsCount) Year.
+ short leapYrIdx = getLeapYrIndex(from2020, yrsCount);
+
+ // if leap year index is 0, then the number of days for the 1st year will be 366 days.
+ // if leap year index is not 0, then the number of days for the 1st year will be 365 days.
+ if (((leapYrIdx == 0)
+ && (KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, leapYearMsec, (short) 0, (short) 8)
+ >= 0))
+ || ((leapYrIdx != 0)
+ && (KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, yearMsec, (short) 0, (short) 8)
+ >= 0))) {
+ for (short i = 0; i < 4; i++) {
+ yrsCount++;
+ if (i == leapYrIdx) {
+ Util.arrayCopyNonAtomic(leapYearMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ } else {
+ Util.arrayCopyNonAtomic(yearMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ }
+ subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8);
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ if (((short) (i + 1) == leapYrIdx)) {
+ if (KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, leapYearMsec, (short) 0, (short) 8)
+ < 0) {
+ break;
+ }
+ } else {
+ if (KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, yearMsec, (short) 0, (short) 8)
+ < 0) {
+ break;
+ }
+ }
+ }
+ }
+
+ // total yrs from 1970
+ if (from2020) {
+ yrsCount = (short) (year2020 + yrsCount);
+ } else {
+ yrsCount = (short) (year2051 + yrsCount);
+ }
+
+ // divide the given time with one month msec count
+ if (KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, oneMonthMsec, (short) 0, (short) 8)
+ >= 0) {
+ for (short i = 0; i < 12; i++) {
+ if (i == 1) {
+ // Feb month
+ if (isLeapYear(yrsCount)) {
+ // Leap year 29 days
+ Util.arrayCopyNonAtomic(febMonthLeapMSec, (short) 0, scratchPad, (short) 8, (short) 8);
+ } else {
+ // 28 days
+ Util.arrayCopyNonAtomic(febMonthMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ }
+ } else if (((i <= 6) && ((i % 2 == 0))) || ((i > 6) && ((i % 2 == 1)))) {
+ Util.arrayCopyNonAtomic(
+ ThirtyOneDaysMonthMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ } else {
+ // 30 Days
+ Util.arrayCopyNonAtomic(ThirtDaysMonthMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ }
+
+ if (KMInteger.unsignedByteArrayCompare(
+ scratchPad, (short) 0, scratchPad, (short) 8, (short) 8)
+ >= 0) {
+ subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8);
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ } else {
+ break;
+ }
+ monthCount++;
+ }
+ }
+
+ // divide the given time with one day msec count
+ if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneDayMsec, (short) 0, (short) 8)
+ >= 0) {
+ Util.arrayCopyNonAtomic(oneDayMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ dayCount = divide(scratchPad, (short) 0, (short) 8, (short) 16);
+ dayCount++;
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ }
+
+ // divide the given time with one hour msec count
+ if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneHourMsec, (short) 0, (short) 8)
+ >= 0) {
+ Util.arrayCopyNonAtomic(oneHourMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ hhCount = divide(scratchPad, (short) 0, (short) 8, (short) 16);
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ }
+
+ // divide the given time with one minute msec count
+ if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneMinMsec, (short) 0, (short) 8)
+ >= 0) {
+ Util.arrayCopyNonAtomic(oneMinMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ mmCount = divide(scratchPad, (short) 0, (short) 8, (short) 16);
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ }
+
+ // divide the given time with one second msec count
+ if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneSecMsec, (short) 0, (short) 8)
+ >= 0) {
+ Util.arrayCopyNonAtomic(oneSecMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+ ssCount = divide(scratchPad, (short) 0, (short) 8, (short) 16);
+ Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+ }
+
+ // Now convert to ascii string YYMMDDhhmmssZ or YYYYMMDDhhmmssZ
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+ short len = numberToString(yrsCount, scratchPad, (short) 0); // returns YYYY
+ len += numberToString(monthCount, scratchPad, len);
+ len += numberToString(dayCount, scratchPad, len);
+ len += numberToString(hhCount, scratchPad, len);
+ len += numberToString(mmCount, scratchPad, len);
+ len += numberToString(ssCount, scratchPad, len);
+ scratchPad[len] = Z;
+ len++;
+ if (utcFlag) {
+ return KMByteBlob.instance(scratchPad, (short) 2, (short) (len - 2)); // YY
+ } else {
+ return KMByteBlob.instance(scratchPad, (short) 0, len); // YYYY
+ }
+ }
+
+ public static short numberToString(short number, byte[] scratchPad, short offset) {
+ byte zero = 0x30;
+ byte len = 2;
+ byte digit;
+ if (number > 999) {
+ len = 4;
+ }
+ byte index = len;
+ while (index > 0) {
+ digit = (byte) (number % 10);
+ number = (short) (number / 10);
+ scratchPad[(short) (offset + index - 1)] = (byte) (digit + zero);
+ index--;
+ }
+ return len;
+ }
+
+ // Use Euclid's formula: dividend = quotient*divisor + remainder
+ // i.e. dividend - quotient*divisor = remainder where remainder < divisor.
+ // so this is division by subtraction until remainder remains.
+ public static short divide(byte[] buf, short dividend, short divisor, short remainder) {
+ short expCnt = 1;
+ short q = 0;
+ // first increase divisor so that it becomes greater then dividend.
+ while (compare(buf, divisor, dividend) < 0) {
+ shiftLeft(buf, divisor);
+ expCnt = (short) (expCnt << 1);
+ }
+ // Now subtract divisor from dividend if dividend is greater then divisor.
+ // Copy remainder in the dividend and repeat.
+ while (expCnt != 0) {
+ if (compare(buf, dividend, divisor) >= 0) {
+ subtract(buf, dividend, divisor, remainder, (byte) 8);
+ copy(buf, remainder, dividend);
+ q = (short) (q + expCnt);
+ }
+ expCnt = (short) (expCnt >> 1);
+ shiftRight(buf, divisor);
+ }
+ return q;
+ }
+
+ public static void copy(byte[] buf, short from, short to) {
+ Util.arrayCopyNonAtomic(buf, from, buf, to, (short) 8);
+ }
+
+ public static byte compare(byte[] buf, short lhs, short rhs) {
+ return KMInteger.unsignedByteArrayCompare(buf, lhs, buf, rhs, (short) 8);
+ }
+
+ public static void shiftLeft(byte[] buf, short start, short count) {
+ short index = 0;
+ while (index < count) {
+ shiftLeft(buf, start);
+ index++;
+ }
+ }
+
+ public static void shiftLeft(byte[] buf, short start) {
+ byte index = 7;
+ byte carry = 0;
+ byte tmp;
+ while (index >= 0) {
+ tmp = buf[(short) (start + index)];
+ buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] << 1);
+ buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] + carry);
+ if (tmp < 0) {
+ carry = 1;
+ } else {
+ carry = 0;
+ }
+ index--;
+ }
+ }
+
+ public static void shiftRight(byte[] buf, short start) {
+ byte index = 0;
+ byte carry = 0;
+ byte tmp;
+ while (index < 8) {
+ tmp = (byte) (buf[(short) (start + index)] & 0x01);
+ buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] >> 1);
+ buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] & 0x7F);
+ buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] | carry);
+ if (tmp == 1) {
+ carry = (byte) 0x80;
+ } else {
+ carry = 0;
+ }
+ index++;
+ }
+ }
+
+ public static void add(byte[] buf, short op1, short op2, short result) {
+ byte index = 7;
+ byte carry = 0;
+ short tmp;
+ short val1 = 0;
+ short val2 = 0;
+ while (index >= 0) {
+ val1 = (short) (buf[(short) (op1 + index)] & 0x00FF);
+ val2 = (short) (buf[(short) (op2 + index)] & 0x00FF);
+ tmp = (short) (val1 + val2 + carry);
+ carry = 0;
+ if (tmp > 255) {
+ carry = 1; // max unsigned byte value is 255
+ }
+ buf[(short) (result + index)] = (byte) (tmp & (byte) 0xFF);
+ index--;
+ }
+ }
+
+ // subtraction by borrowing.
+ public static void subtract(byte[] buf, short op1, short op2, short result, byte sizeBytes) {
+ byte borrow = 0;
+ byte index = (byte) (sizeBytes - 1);
+ short r;
+ short x;
+ short y;
+ while (index >= 0) {
+ x = (short) (buf[(short) (op1 + index)] & 0xFF);
+ y = (short) (buf[(short) (op2 + index)] & 0xFF);
+ r = (short) (x - y - borrow);
+ borrow = 0;
+ if (r < 0) {
+ borrow = 1;
+ r = (short) (r + 256); // max unsigned byte value is 255
+ }
+ buf[(short) (result + index)] = (byte) (r & 0xFF);
+ index--;
+ }
+ }
+
+ public static short countTemporalCount(
+ byte[] bufTime, short timeOff, short timeLen, byte[] scratchPad, short offset) {
+ Util.arrayFillNonAtomic(scratchPad, (short) offset, (short) 24, (byte) 0);
+ Util.arrayCopyNonAtomic(bufTime, timeOff, scratchPad, (short) (offset + 8 - timeLen), timeLen);
+ Util.arrayCopyNonAtomic(
+ ThirtDaysMonthMsec, (short) 0, scratchPad, (short) (offset + 8), (short) 8);
+ return divide(scratchPad, (short) 0, (short) 8, (short) 16);
+ }
+
+ public static boolean isLeapYear(short year) {
+ if ((short) (year % 4) == (short) 0) {
+ if (((short) (year % 100) == (short) 0) && ((short) (year % 400)) != (short) 0) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public static short getLeapYrIndex(boolean from2020, short yrsCount) {
+ short newBaseYr = (short) (from2020 ? (year2020 + yrsCount) : (year2051 + yrsCount));
+ for (short i = 0; i < 4; i++) {
+ if (isLeapYear((short) (newBaseYr + i))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public static void computeOnesCompliment(byte[] buf, short offset, short len) {
+ short index = offset;
+ // Compute 1s compliment
+ while (index < (short) (len + offset)) {
+ buf[index] = (byte) ~buf[index];
+ index++;
+ }
+ }
+
+ // i * 1000 = (i << 9) + (i << 8) + (i << 7) + (i << 6) + (i << 5) + ( i << 3)
+ public static void convertToMilliseconds(
+ byte[] buf, short inputOff, short outputOff, short scratchPadOff) {
+ short index = 0;
+ short length = (short) SEC_TO_MILLIS_SHIFT_POS.length;
+ while (index < length) {
+ Util.arrayCopyNonAtomic(buf, inputOff, buf, scratchPadOff, (short) 8);
+ shiftLeft(buf, scratchPadOff, SEC_TO_MILLIS_SHIFT_POS[index]);
+ Util.arrayCopyNonAtomic(buf, outputOff, buf, (short) (scratchPadOff + 8), (short) 8);
+ add(buf, scratchPadOff, (short) (8 + scratchPadOff), (short) (16 + scratchPadOff));
+ Util.arrayCopyNonAtomic(buf, (short) (scratchPadOff + 16), buf, outputOff, (short) 8);
+ Util.arrayFillNonAtomic(buf, scratchPadOff, (short) 24, (byte) 0);
+ index++;
+ }
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java
new file mode 100644
index 0000000..41059bb
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java
@@ -0,0 +1,53 @@
+/*
+ * 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.security.AESKey;
+import org.globalplatform.upgrade.Element;
+
+/** This is a wrapper class for AESKey. */
+public class KMAESKey implements KMKey {
+
+ public AESKey aesKey;
+
+ public KMAESKey(AESKey key) {
+ aesKey = key;
+ }
+
+ public static void onSave(Element element, KMAESKey kmKey) {
+ element.write(kmKey.aesKey);
+ }
+
+ public static KMAESKey onRestore(AESKey aesKey) {
+ if (aesKey == null) {
+ return null;
+ }
+ return new KMAESKey(aesKey);
+ }
+
+ public static short getBackupPrimitiveByteCount() {
+ return (short) 0;
+ }
+
+ public static short getBackupObjectCount() {
+ return (short) 1;
+ }
+
+ @Override
+ public short getPublicKey(byte[] buf, short offset) {
+ return (short) 0;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java
new file mode 100644
index 0000000..b8e78a0
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java
@@ -0,0 +1,1572 @@
+/*
+ * 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" 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.javacard.seprovider;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+import javacard.security.AESKey;
+import javacard.security.CryptoException;
+import javacard.security.DESKey;
+import javacard.security.ECPrivateKey;
+import javacard.security.ECPublicKey;
+import javacard.security.HMACKey;
+import javacard.security.Key;
+import javacard.security.KeyAgreement;
+import javacard.security.KeyBuilder;
+import javacard.security.KeyPair;
+import javacard.security.MessageDigest;
+import javacard.security.RSAPrivateKey;
+import javacard.security.RandomData;
+import javacard.security.Signature;
+import javacardx.crypto.AEADCipher;
+import javacardx.crypto.Cipher;
+import org.globalplatform.upgrade.Element;
+import org.globalplatform.upgrade.UpgradeManager;
+
+/**
+ * This class implements KMSEProvider and provides all the necessary crypto operations required to
+ * support the KeyMint specification. This class supports AES, 3DES, HMAC, RSA, ECDSA, ECDH
+ * algorithms additionally it also supports ECDSA_NO_DIGEST, RSA_NO_DIGEST and RSA_OAEP_MGF1_SHA1
+ * and RSA_OAEP_MGF1_SHA256 algorithms. This class follows the pattern of Init-Update-Final for the
+ * crypto operations.
+ */
+public class KMAndroidSEProvider implements KMSEProvider {
+
+ // The tag length for AES GCM algorithm.
+ public static final byte AES_GCM_TAG_LENGTH = 16;
+ // The nonce length for AES GCM algorithm.
+ public static final byte AES_GCM_NONCE_LENGTH = 12;
+ // AES keysize offsets in aesKeys[] for 128 and 256 sizes respectively.
+ public static final byte KEYSIZE_128_OFFSET = 0x00;
+ public static final byte KEYSIZE_256_OFFSET = 0x01;
+ // The size of the temporary buffer.
+ public static final short TMP_ARRAY_SIZE = 300;
+ // The length of the rsa key in bytes.
+ private static final short RSA_KEY_SIZE = 256;
+ // Below are the flag to denote device reset events
+ public static final byte POWER_RESET_FALSE = (byte) 0xAA;
+ public static final byte POWER_RESET_TRUE = (byte) 0x00;
+ // The computed HMAC key size.
+ private static final byte COMPUTED_HMAC_KEY_SIZE = 32;
+ // The constant 'L' as defiend in
+ // https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf, page 12.
+ private static byte[] CMAC_KDF_CONSTANT_L;
+ // Constant to represent 0.
+ private static byte[] CMAC_KDF_CONSTANT_ZERO;
+ // KeyAgreement instance.
+ private static KeyAgreement keyAgreement;
+
+ // AESKey
+ private AESKey aesKeys[];
+ // DES3Key
+ private DESKey triDesKey;
+ // HMACKey
+ private HMACKey hmacKey;
+ // RSA Key Pair
+ private KeyPair rsaKeyPair;
+ // EC Key Pair.
+ private KeyPair ecKeyPair;
+ // Temporary array.
+ public byte[] tmpArray;
+ // This is used for internal encryption/decryption operations.
+ private static AEADCipher aesGcmCipher;
+ // Instance of Signature algorithm used in KDF.
+ private Signature kdf;
+ // Flag used to denote the power reset event.
+ public static byte[] resetFlag;
+ // Instance of HMAC Signature algorithm.
+ private Signature hmacSignature;
+ // For ImportwrappedKey operations.
+ private KMRsaOAEPEncoding rsaOaepDecipher;
+ // Instance of pool manager.
+ private KMPoolManager poolMgr;
+ // Instance of KMOperationImpl used only to encrypt/decrypt the KeyBlobs.
+ private KMOperationImpl globalOperation;
+ // Entropy
+ private RandomData rng;
+ // Singleton instance.
+ private static KMAndroidSEProvider androidSEProvider = null;
+
+ public static KMAndroidSEProvider getInstance() {
+ return androidSEProvider;
+ }
+
+ public KMAndroidSEProvider() {
+ initStatics();
+ // Re-usable AES,DES and HMAC keys in persisted memory.
+ aesKeys = new AESKey[2];
+ aesKeys[KEYSIZE_128_OFFSET] =
+ (AESKey)
+ KeyBuilder.buildKey(
+ KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_128, false);
+ aesKeys[KEYSIZE_256_OFFSET] =
+ (AESKey)
+ KeyBuilder.buildKey(
+ KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_256, false);
+ triDesKey =
+ (DESKey)
+ KeyBuilder.buildKey(
+ KeyBuilder.TYPE_DES_TRANSIENT_RESET, KeyBuilder.LENGTH_DES3_3KEY, false);
+ hmacKey =
+ (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_RESET, (short) 512, false);
+ rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048);
+ ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256);
+ keyAgreement = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false);
+ poolMgr = KMPoolManager.getInstance();
+ poolMgr.initECKey(ecKeyPair);
+ // RsaOAEP Decipher
+ rsaOaepDecipher = new KMRsaOAEPEncoding(KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1);
+
+ kdf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false);
+ hmacSignature = Signature.getInstance(Signature.ALG_HMAC_SHA_256, false);
+
+ globalOperation = new KMOperationImpl();
+
+ // Temporary transient array created to use locally inside functions.
+ tmpArray = JCSystem.makeTransientByteArray(TMP_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT);
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0);
+ // Random number generator initialisation.
+ rng = RandomData.getInstance(RandomData.ALG_KEYGENERATION);
+ androidSEProvider = this;
+ resetFlag = JCSystem.makeTransientByteArray((short) 1, JCSystem.CLEAR_ON_RESET);
+ resetFlag[0] = (byte) POWER_RESET_FALSE;
+ }
+
+ void initStatics() {
+ CMAC_KDF_CONSTANT_L = new byte[] {0x00, 0x00, 0x01, 0x00};
+ CMAC_KDF_CONSTANT_ZERO = new byte[] {0x00};
+ }
+
+ public void clean() {
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0);
+ }
+
+ public AESKey createAESKey(short keysize) {
+ try {
+ if (keysize > TMP_ARRAY_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ newRandomNumber(tmpArray, (short) 0, (short) (keysize / 8));
+ return createAESKey(tmpArray, (short) 0, (short) (keysize / 8));
+ } finally {
+ clean();
+ }
+ }
+
+ public AESKey createAESKey(byte[] buf, short startOff, short length) {
+ AESKey key = null;
+ short keysize = (short) (length * 8);
+ if (keysize == 128) {
+ key = (AESKey) aesKeys[KEYSIZE_128_OFFSET];
+ key.setKey(buf, (short) startOff);
+ } else if (keysize == 256) {
+ key = (AESKey) aesKeys[KEYSIZE_256_OFFSET];
+ key.setKey(buf, (short) startOff);
+ } else {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ return key;
+ }
+
+ public DESKey createTDESKey() {
+ try {
+ newRandomNumber(tmpArray, (short) 0, (short) (KeyBuilder.LENGTH_DES3_3KEY / 8));
+ return createTDESKey(tmpArray, (short) 0, (short) (KeyBuilder.LENGTH_DES3_3KEY / 8));
+ } finally {
+ clean();
+ }
+ }
+
+ public DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength) {
+ triDesKey.setKey(secretBuffer, secretOff);
+ return triDesKey;
+ }
+
+ public HMACKey createHMACKey(short keysize) {
+ // As per the KeyMint2.0 specification
+ // The minimum supported HMAC key size is 64 bits
+ // The maximum supported HMAC key size is 512 bits
+ // The keysize should be a multiple of 8.
+ if ((keysize % 8 != 0) || !(keysize >= 64 && keysize <= 512)) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ try {
+ newRandomNumber(tmpArray, (short) 0, (short) (keysize / 8));
+ return createHMACKey(tmpArray, (short) 0, (short) (keysize / 8));
+ } finally {
+ clean();
+ }
+ }
+
+ public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength) {
+ hmacKey.setKey(secretBuffer, secretOff, secretLength);
+ return hmacKey;
+ }
+
+ public KeyPair createRsaKeyPair() {
+ rsaKeyPair.genKeyPair();
+ return rsaKeyPair;
+ }
+
+ public RSAPrivateKey createRsaKey(
+ byte[] modBuffer,
+ short modOff,
+ short modLength,
+ byte[] privBuffer,
+ short privOff,
+ short privLength) {
+ RSAPrivateKey privKey = (RSAPrivateKey) rsaKeyPair.getPrivate();
+ privKey.setExponent(privBuffer, privOff, privLength);
+ privKey.setModulus(modBuffer, modOff, modLength);
+ return privKey;
+ }
+
+ public KeyPair createECKeyPair() {
+ ecKeyPair.genKeyPair();
+ return ecKeyPair;
+ }
+
+ public ECPrivateKey createEcKey(byte[] privBuffer, short privOff, short privLength) {
+ ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate();
+ privKey.setS(privBuffer, privOff, privLength);
+ return privKey;
+ }
+
+ @Override
+ public short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff) {
+ switch (alg) {
+ case KMType.AES:
+ AESKey aesKey = createAESKey(keysize);
+ return aesKey.getKey(buf, startOff);
+ case KMType.DES:
+ DESKey desKey = createTDESKey();
+ return desKey.getKey(buf, startOff);
+ case KMType.HMAC:
+ HMACKey hmacKey = createHMACKey(keysize);
+ return hmacKey.getKey(buf, startOff);
+ default:
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ break;
+ }
+ return 0;
+ }
+
+ @Override
+ public void createAsymmetricKey(
+ byte alg,
+ byte[] privKeyBuf,
+ short privKeyStart,
+ short privKeyLength,
+ byte[] pubModBuf,
+ short pubModStart,
+ short pubModLength,
+ short[] lengths) {
+ switch (alg) {
+ case KMType.RSA:
+ if (RSA_KEY_SIZE != privKeyLength || RSA_KEY_SIZE != pubModLength) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ KeyPair rsaKey = createRsaKeyPair();
+ RSAPrivateKey privKey = (RSAPrivateKey) rsaKey.getPrivate();
+ // Copy exponent.
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, RSA_KEY_SIZE, (byte) 0);
+ lengths[0] = privKey.getExponent(tmpArray, (short) 0);
+ if (lengths[0] > privKeyLength) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ Util.arrayFillNonAtomic(privKeyBuf, privKeyStart, privKeyLength, (byte) 0);
+ Util.arrayCopyNonAtomic(
+ tmpArray,
+ (short) 0,
+ privKeyBuf,
+ (short) (privKeyStart + privKeyLength - lengths[0]),
+ lengths[0]);
+ // Copy modulus
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, RSA_KEY_SIZE, (byte) 0);
+ lengths[1] = privKey.getModulus(tmpArray, (short) 0);
+ if (lengths[1] > pubModLength) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ Util.arrayFillNonAtomic(pubModBuf, pubModStart, pubModLength, (byte) 0);
+ Util.arrayCopyNonAtomic(
+ tmpArray,
+ (short) 0,
+ pubModBuf,
+ (short) (pubModStart + pubModLength - lengths[1]),
+ lengths[1]);
+ break;
+ case KMType.EC:
+ KeyPair ecKey = createECKeyPair();
+ ECPublicKey ecPubKey = (ECPublicKey) ecKey.getPublic();
+ ECPrivateKey ecPrivKey = (ECPrivateKey) ecKey.getPrivate();
+ lengths[0] = ecPrivKey.getS(privKeyBuf, privKeyStart);
+ lengths[1] = ecPubKey.getW(pubModBuf, pubModStart);
+ if (lengths[0] > privKeyLength || lengths[1] > pubModLength) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ break;
+ default:
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ break;
+ }
+ }
+
+ @Override
+ public boolean importSymmetricKey(
+ byte alg, short keysize, byte[] buf, short startOff, short length) {
+ switch (alg) {
+ case KMType.AES:
+ createAESKey(buf, startOff, length);
+ break;
+ case KMType.DES:
+ createTDESKey(buf, startOff, length);
+ break;
+ case KMType.HMAC:
+ createHMACKey(buf, startOff, length);
+ break;
+ default:
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean importAsymmetricKey(
+ byte alg,
+ byte[] privKeyBuf,
+ short privKeyStart,
+ short privKeyLength,
+ byte[] pubModBuf,
+ short pubModStart,
+ short pubModLength) {
+ switch (alg) {
+ case KMType.RSA:
+ createRsaKey(pubModBuf, pubModStart, pubModLength, privKeyBuf, privKeyStart, privKeyLength);
+ break;
+ case KMType.EC:
+ createEcKey(privKeyBuf, privKeyStart, privKeyLength);
+ break;
+ default:
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ public void getTrueRandomNumber(byte[] buf, short start, short length) {
+ newRandomNumber(buf, start, length);
+ }
+
+ @Override
+ public void newRandomNumber(byte[] num, short startOff, short length) {
+ rng.nextBytes(num, startOff, length);
+ }
+
+ @Override
+ public void addRngEntropy(byte[] num, short offset, short length) {
+ rng.setSeed(num, offset, length);
+ }
+
+ public short aesGCMEncrypt(
+ AESKey key,
+ byte[] secret,
+ short secretStart,
+ short secretLen,
+ byte[] encSecret,
+ short encSecretStart,
+ byte[] nonce,
+ short nonceStart,
+ short nonceLen,
+ byte[] authData,
+ short authDataStart,
+ short authDataLen,
+ byte[] authTag,
+ short authTagStart,
+ short authTagLen) {
+ if (authTagLen != AES_GCM_TAG_LENGTH) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ if (nonceLen != AES_GCM_NONCE_LENGTH) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ if (aesGcmCipher == null) {
+ aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false);
+ }
+ aesGcmCipher.init(key, Cipher.MODE_ENCRYPT, nonce, nonceStart, nonceLen);
+ if (authDataLen != 0) {
+ aesGcmCipher.updateAAD(authData, authDataStart, authDataLen);
+ }
+ short ciphLen = aesGcmCipher.doFinal(secret, secretStart, secretLen, encSecret, encSecretStart);
+ aesGcmCipher.retrieveTag(authTag, authTagStart, authTagLen);
+ return ciphLen;
+ }
+
+ @Override
+ public short aesGCMEncrypt(
+ byte[] aesKey,
+ short aesKeyStart,
+ short aesKeyLen,
+ byte[] secret,
+ short secretStart,
+ short secretLen,
+ byte[] encSecret,
+ short encSecretStart,
+ byte[] nonce,
+ short nonceStart,
+ short nonceLen,
+ byte[] authData,
+ short authDataStart,
+ short authDataLen,
+ byte[] authTag,
+ short authTagStart,
+ short authTagLen) {
+
+ AESKey key = createAESKey(aesKey, aesKeyStart, aesKeyLen);
+ return aesGCMEncrypt(
+ key,
+ secret,
+ secretStart,
+ secretLen,
+ encSecret,
+ encSecretStart,
+ nonce,
+ nonceStart,
+ nonceLen,
+ authData,
+ authDataStart,
+ authDataLen,
+ authTag,
+ authTagStart,
+ authTagLen);
+ }
+
+ @Override
+ public boolean aesGCMDecrypt(
+ byte[] aesKey,
+ short aesKeyStart,
+ short aesKeyLen,
+ byte[] encSecret,
+ short encSecretStart,
+ short encSecretLen,
+ byte[] secret,
+ short secretStart,
+ byte[] nonce,
+ short nonceStart,
+ short nonceLen,
+ byte[] authData,
+ short authDataStart,
+ short authDataLen,
+ byte[] authTag,
+ short authTagStart,
+ short authTagLen) {
+ if (aesGcmCipher == null) {
+ aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false);
+ }
+ boolean verification = false;
+ AESKey key = createAESKey(aesKey, aesKeyStart, aesKeyLen);
+ aesGcmCipher.init(key, Cipher.MODE_DECRYPT, nonce, nonceStart, nonceLen);
+ if (authDataLen != 0) {
+ aesGcmCipher.updateAAD(authData, authDataStart, authDataLen);
+ }
+ // encrypt the secret
+ aesGcmCipher.doFinal(encSecret, encSecretStart, encSecretLen, secret, secretStart);
+ verification =
+ aesGcmCipher.verifyTag(
+ authTag, authTagStart, (short) authTagLen, (short) AES_GCM_TAG_LENGTH);
+ return verification;
+ }
+
+ public HMACKey cmacKdf(
+ KMKey preSharedKey,
+ byte[] label,
+ short labelStart,
+ short labelLen,
+ byte[] context,
+ short contextStart,
+ short contextLength) {
+ // Note: the variables i and L correspond to i and L in the standard. See page 12 of
+ // http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf.
+ try {
+ // This is hardcoded to requirement - 32 byte output with two concatenated
+ // 16 bytes K1 and K2.
+ final byte n = 2; // hardcoded
+
+ // [i] counter - 32 bits
+ short iBufLen = 4;
+ short keyOutLen = n * 16;
+ // Convert Hmackey to AES Key as the algorithm is ALG_AES_CMAC_128.
+ KMHmacKey hmacKey = ((KMHmacKey) preSharedKey);
+ hmacKey.hmacKey.getKey(tmpArray, (short) 0);
+ aesKeys[KEYSIZE_256_OFFSET].setKey(tmpArray, (short) 0);
+ // Initialize the key derivation function.
+ kdf.init(aesKeys[KEYSIZE_256_OFFSET], Signature.MODE_SIGN);
+ // Clear the tmpArray buffer.
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0);
+
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, iBufLen, (byte) 0);
+ Util.arrayFillNonAtomic(tmpArray, (short) iBufLen, keyOutLen, (byte) 0);
+
+ byte i = 1;
+ short pos = 0;
+ while (i <= n) {
+ tmpArray[3] = i;
+ // 4 bytes of iBuf with counter in it
+ kdf.update(tmpArray, (short) 0, (short) iBufLen);
+ kdf.update(label, labelStart, (short) labelLen); // label
+ kdf.update(
+ CMAC_KDF_CONSTANT_ZERO,
+ (short) 0,
+ (short) CMAC_KDF_CONSTANT_ZERO.length); // 1 byte of 0x00
+ kdf.update(context, contextStart, contextLength); // context
+ // 4 bytes of L - signature of 16 bytes
+ pos =
+ kdf.sign(
+ CMAC_KDF_CONSTANT_L,
+ (short) 0,
+ (short) CMAC_KDF_CONSTANT_L.length,
+ tmpArray,
+ (short) (iBufLen + pos));
+ i++;
+ }
+ return createHMACKey(tmpArray, (short) iBufLen, (short) keyOutLen);
+ } finally {
+ clean();
+ }
+ }
+
+ public short hmacSign(
+ HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) {
+ hmacSignature.init(key, Signature.MODE_SIGN);
+ return hmacSignature.sign(data, dataStart, dataLength, mac, macStart);
+ }
+
+ @Override
+ public short hmacSign(
+ byte[] keyBuf,
+ short keyStart,
+ short keyLength,
+ byte[] data,
+ short dataStart,
+ short dataLength,
+ byte[] mac,
+ short macStart) {
+ HMACKey key = createHMACKey(keyBuf, keyStart, keyLength);
+ return hmacSign(key, data, dataStart, dataLength, mac, macStart);
+ }
+
+ @Override
+ public short hmacSign(
+ Object key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) {
+ if (!(key instanceof KMHmacKey)) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ KMHmacKey hmacKey = (KMHmacKey) key;
+ return hmacSign(hmacKey.hmacKey, data, dataStart, dataLength, mac, macStart);
+ }
+
+ @Override
+ public short hmacKDF(
+ KMKey masterkey,
+ byte[] data,
+ short dataStart,
+ short dataLength,
+ byte[] signature,
+ short signatureStart) {
+ try {
+ KMAESKey aesKey = (KMAESKey) masterkey;
+ short keyLen = (short) (aesKey.aesKey.getSize() / 8);
+ aesKey.aesKey.getKey(tmpArray, (short) 0);
+ return hmacSign(
+ tmpArray, (short) 0, keyLen, data, dataStart, dataLength, signature, signatureStart);
+ } finally {
+ clean();
+ }
+ }
+
+ @Override
+ public boolean hmacVerify(
+ KMKey key,
+ byte[] data,
+ short dataStart,
+ short dataLength,
+ byte[] mac,
+ short macStart,
+ short macLength) {
+ KMHmacKey hmacKey = (KMHmacKey) key;
+ hmacSignature.init(hmacKey.hmacKey, Signature.MODE_VERIFY);
+ return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, macLength);
+ }
+
+ @Override
+ public short rsaDecipherOAEP256(
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ byte[] modBuffer,
+ short modOff,
+ short modLength,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart) {
+ RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate();
+ key.setExponent(secret, (short) secretStart, (short) secretLength);
+ key.setModulus(modBuffer, (short) modOff, (short) modLength);
+ rsaOaepDecipher.init(key, Cipher.MODE_DECRYPT);
+ return rsaOaepDecipher.doFinal(
+ inputDataBuf,
+ (short) inputDataStart,
+ (short) inputDataLength,
+ outputDataBuf,
+ (short) outputDataStart);
+ }
+
+ private byte mapSignature256Alg(byte alg, byte padding, byte digest) {
+ switch (alg) {
+ case KMType.RSA:
+ switch (padding) {
+ case KMType.RSA_PKCS1_1_5_SIGN:
+ {
+ if (digest == KMType.DIGEST_NONE) {
+ return KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST;
+ } else {
+ return Signature.ALG_RSA_SHA_256_PKCS1;
+ }
+ }
+ case KMType.RSA_PSS:
+ return Signature.ALG_RSA_SHA_256_PKCS1_PSS;
+ case KMType.PADDING_NONE:
+ return KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD;
+ }
+ break;
+ case KMType.EC:
+ if (digest == KMType.DIGEST_NONE) {
+ return KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST;
+ } else {
+ return Signature.ALG_ECDSA_SHA_256;
+ }
+ case KMType.HMAC:
+ return Signature.ALG_HMAC_SHA_256;
+ }
+ return -1;
+ }
+
+ private byte mapCipherAlg(byte alg, byte padding, byte blockmode, byte digest) {
+ switch (alg) {
+ case KMType.AES:
+ switch (blockmode) {
+ case KMType.ECB:
+ return Cipher.ALG_AES_BLOCK_128_ECB_NOPAD;
+ case KMType.CBC:
+ return Cipher.ALG_AES_BLOCK_128_CBC_NOPAD;
+ case KMType.CTR:
+ return Cipher.ALG_AES_CTR;
+ case KMType.GCM:
+ return AEADCipher.ALG_AES_GCM;
+ }
+ break;
+ case KMType.DES:
+ switch (blockmode) {
+ case KMType.ECB:
+ return Cipher.ALG_DES_ECB_NOPAD;
+ case KMType.CBC:
+ return Cipher.ALG_DES_CBC_NOPAD;
+ }
+ break;
+ case KMType.RSA:
+ switch (padding) {
+ case KMType.PADDING_NONE:
+ return Cipher.ALG_RSA_NOPAD;
+ case KMType.RSA_PKCS1_1_5_ENCRYPT:
+ return Cipher.ALG_RSA_PKCS1;
+ case KMType.RSA_OAEP:
+ {
+ if (digest == KMType.SHA1) {
+ /* MGF Digest is SHA1 */
+ return KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1;
+ } else if (digest == KMType.SHA2_256) {
+ /* MGF Digest is SHA256 */
+ return KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256;
+ } else {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ }
+ }
+ break;
+ }
+ return -1;
+ }
+
+ public KMOperation createSymmetricCipher(
+ short alg,
+ short purpose,
+ short macLength,
+ short blockMode,
+ short padding,
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ byte[] ivBuffer,
+ short ivStart,
+ short ivLength,
+ boolean isRkp) {
+
+ short cipherAlg = mapCipherAlg((byte) alg, (byte) padding, (byte) blockMode, (byte) 0);
+ KMOperation operation = null;
+ if (isRkp) {
+ operation = poolMgr.getRKpOperation(purpose, cipherAlg, alg, padding, blockMode, macLength);
+ } else {
+ operation =
+ poolMgr.getOperationImpl(
+ purpose, cipherAlg, alg, padding, blockMode, macLength, secretLength, false);
+ }
+ // Get the KeyObject from the operation and update the key with the secret key material.
+ KMKeyObject keyObj = operation.getKeyObject();
+ Key key = (Key) keyObj.keyObjectInst;
+ switch (secretLength) {
+ case 32:
+ case 16:
+ ((AESKey) key).setKey(secret, secretStart);
+ break;
+ case 24:
+ ((DESKey) key).setKey(secret, secretStart);
+ break;
+ default:
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ break;
+ }
+ ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, ivBuffer, ivStart, ivLength);
+ return operation;
+ }
+
+ public KMOperation createHmacSignerVerifier(
+ short purpose,
+ short digest,
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ boolean isRkp) {
+ KMOperation operation = null;
+ if (digest != KMType.SHA2_256) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ if (isRkp) {
+ operation =
+ poolMgr.getRKpOperation(
+ purpose,
+ Signature.ALG_HMAC_SHA_256,
+ KMType.HMAC,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE);
+ } else {
+ operation =
+ poolMgr.getOperationImpl(
+ purpose,
+ Signature.ALG_HMAC_SHA_256,
+ KMType.HMAC,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ (short) 0,
+ false);
+ }
+ // Get the KeyObject from the operation and update the key with the secret key material.
+ KMKeyObject keyObj = operation.getKeyObject();
+ HMACKey key = (HMACKey) keyObj.keyObjectInst;
+ key.setKey(secret, secretStart, secretLength);
+ ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0);
+ return operation;
+ }
+
+ private KMOperation createHmacSignerVerifier(
+ short purpose, short digest, HMACKey hmacKey, boolean isTrustedConf) {
+ if (digest != KMType.SHA2_256) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ KMOperation operation =
+ poolMgr.getOperationImpl(
+ purpose,
+ Signature.ALG_HMAC_SHA_256,
+ KMType.HMAC,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ (short) 0,
+ isTrustedConf);
+ // Get the KeyObject from the operation and update the key with the secret key material.
+ KMKeyObject keyObj = operation.getKeyObject();
+ HMACKey key = (HMACKey) keyObj.keyObjectInst;
+ short len = hmacKey.getKey(tmpArray, (short) 0);
+ key.setKey(tmpArray, (short) 0, len);
+ ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0);
+ return operation;
+ }
+
+ @Override
+ public KMOperation getRkpOperation(
+ byte purpose,
+ byte alg,
+ byte digest,
+ byte padding,
+ byte blockMode,
+ KMKey keyPair,
+ byte[] ivBuf,
+ short ivStart,
+ short ivLength,
+ short macLength) {
+ KMOperation opr = null;
+ switch (alg) {
+ case KMType.EC:
+ // get EC private key buffer
+ ECPrivateKey ecPrivKey =
+ (ECPrivateKey) ((KMECDeviceUniqueKeyPair) keyPair).ecKeyPair.getPrivate();
+ short ecPrivKeyLen = ecPrivKey.getS(tmpArray, (short) 0);
+ opr = createEcSigner(digest, tmpArray, (short) 0, ecPrivKeyLen, true /* isRKP */);
+ break;
+ default:
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ break;
+ }
+ return opr;
+ }
+
+ @Override
+ public KMOperation initSymmetricOperation(
+ byte purpose,
+ byte alg,
+ byte digest,
+ byte padding,
+ byte blockMode,
+ byte[] keyBuf,
+ short keyStart,
+ short keyLength,
+ byte[] ivBuf,
+ short ivStart,
+ short ivLength,
+ short macLength) {
+ KMOperation opr = null;
+ switch (alg) {
+ case KMType.AES:
+ case KMType.DES:
+ // Convert macLength to bytes
+ macLength = (short) (macLength / 8);
+ opr =
+ createSymmetricCipher(
+ alg,
+ purpose,
+ macLength,
+ blockMode,
+ padding,
+ keyBuf,
+ keyStart,
+ keyLength,
+ ivBuf,
+ ivStart,
+ ivLength,
+ false /* isRKP */);
+ break;
+ case KMType.HMAC:
+ opr =
+ createHmacSignerVerifier(
+ purpose, digest, keyBuf, keyStart, keyLength, false /* isRKP */);
+ break;
+ default:
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ break;
+ }
+ return opr;
+ }
+
+ @Override
+ public KMOperation initSymmetricOperation(
+ byte purpose,
+ byte alg,
+ byte digest,
+ byte padding,
+ byte blockMode,
+ Object key,
+ byte interfaceType,
+ byte[] ivBuf,
+ short ivStart,
+ short ivLength,
+ short macLength,
+ boolean oneShot) {
+ short keyLen = 0;
+ globalOperation.setPurpose(purpose);
+ globalOperation.setAlgorithmType(alg);
+ globalOperation.setPaddingAlgorithm(padding);
+ globalOperation.setBlockMode(blockMode);
+ try {
+ switch (interfaceType) {
+ case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY:
+ KMAESKey aesKey = (KMAESKey) key;
+ keyLen = (short) (aesKey.aesKey.getSize() / 8);
+ aesKey.aesKey.getKey(tmpArray, (short) 0);
+ break;
+
+ default:
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+
+ switch (alg) {
+ case KMType.HMAC:
+ HMACKey hmackey = createHMACKey(tmpArray, (short) 0, keyLen);
+ globalOperation.setSignature(hmacSignature);
+ globalOperation.init(hmackey, digest, null, (short) 0, (short) 0);
+ break;
+
+ default:
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ } finally {
+ clean();
+ }
+ return globalOperation;
+ }
+
+ @Override
+ public KMOperation initTrustedConfirmationSymmetricOperation(KMKey computedHmacKey) {
+ KMHmacKey key = (KMHmacKey) computedHmacKey;
+ return createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.hmacKey, true);
+ }
+
+ public KMOperation createRsaSigner(
+ short digest,
+ short padding,
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ byte[] modBuffer,
+ short modOff,
+ short modLength) {
+ byte alg = mapSignature256Alg(KMType.RSA, (byte) padding, (byte) digest);
+ KMOperation operation =
+ poolMgr.getOperationImpl(
+ KMType.SIGN,
+ alg,
+ KMType.RSA,
+ padding,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ secretLength,
+ false);
+ // Get the KeyObject from the operation and update the key with the secret key material.
+ KMKeyObject keyObj = operation.getKeyObject();
+ RSAPrivateKey key = (RSAPrivateKey) ((KeyPair) (keyObj.keyObjectInst)).getPrivate();
+ key.setExponent(secret, secretStart, secretLength);
+ key.setModulus(modBuffer, modOff, modLength);
+ ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0);
+ return operation;
+ }
+
+ public KMOperation createRsaDecipher(
+ short padding,
+ short mgfDigest,
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ byte[] modBuffer,
+ short modOff,
+ short modLength) {
+ byte cipherAlg = mapCipherAlg(KMType.RSA, (byte) padding, (byte) 0, (byte) mgfDigest);
+ KMOperation operation =
+ poolMgr.getOperationImpl(
+ KMType.DECRYPT,
+ cipherAlg,
+ KMType.RSA,
+ padding,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ secretLength,
+ false);
+ // Get the KeyObject from the operation and update the key with the secret key material.
+ KMKeyObject keyObj = operation.getKeyObject();
+ RSAPrivateKey key = (RSAPrivateKey) ((KeyPair) (keyObj.keyObjectInst)).getPrivate();
+ key.setExponent(secret, secretStart, secretLength);
+ key.setModulus(modBuffer, modOff, modLength);
+ ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, null, (short) 0, (short) 0);
+ return operation;
+ }
+
+ public KMOperation createEcSigner(
+ short digest, byte[] secret, short secretStart, short secretLength, boolean isRkp) {
+ KMOperation operation = null;
+ byte alg = mapSignature256Alg(KMType.EC, (byte) 0, (byte) digest);
+ if (isRkp) {
+ operation =
+ poolMgr.getRKpOperation(
+ KMType.SIGN,
+ Signature.ALG_ECDSA_SHA_256,
+ KMType.EC,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE);
+ } else {
+ operation =
+ poolMgr.getOperationImpl(
+ KMType.SIGN,
+ alg,
+ KMType.EC,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ secretLength,
+ false);
+ }
+ KMKeyObject keyObj = operation.getKeyObject();
+ ECPrivateKey key = (ECPrivateKey) ((KeyPair) (keyObj.keyObjectInst)).getPrivate();
+ key.setS(secret, secretStart, secretLength);
+ ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0);
+ return operation;
+ }
+
+ public KMOperation createKeyAgreement(byte[] secret, short secretStart, short secretLength) {
+ KMOperation operation =
+ poolMgr.getOperationImpl(
+ KMType.AGREE_KEY,
+ KeyAgreement.ALG_EC_SVDP_DH_PLAIN,
+ KMType.EC,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ (short) 0,
+ false);
+ KMKeyObject keyObj = operation.getKeyObject();
+ ECPrivateKey key = (ECPrivateKey) ((KeyPair) (keyObj.keyObjectInst)).getPrivate();
+ key.setS(secret, secretStart, secretLength);
+ ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, null, (short) 0, (short) 0);
+ return operation;
+ }
+
+ @Override
+ public KMOperation initAsymmetricOperation(
+ byte purpose,
+ byte alg,
+ byte padding,
+ byte digest,
+ byte mgfDigest,
+ byte[] privKeyBuf,
+ short privKeyStart,
+ short privKeyLength,
+ byte[] pubModBuf,
+ short pubModStart,
+ short pubModLength) {
+ KMOperation opr = null;
+ if (alg == KMType.RSA) {
+ switch (purpose) {
+ case KMType.SIGN:
+ opr =
+ createRsaSigner(
+ digest,
+ padding,
+ privKeyBuf,
+ privKeyStart,
+ privKeyLength,
+ pubModBuf,
+ pubModStart,
+ pubModLength);
+ break;
+ case KMType.DECRYPT:
+ opr =
+ createRsaDecipher(
+ padding,
+ mgfDigest,
+ privKeyBuf,
+ privKeyStart,
+ privKeyLength,
+ pubModBuf,
+ pubModStart,
+ pubModLength);
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ break;
+ }
+ } else if (alg == KMType.EC) {
+ switch (purpose) {
+ case KMType.SIGN:
+ opr = createEcSigner(digest, privKeyBuf, privKeyStart, privKeyLength, false);
+ break;
+
+ case KMType.AGREE_KEY:
+ opr = createKeyAgreement(privKeyBuf, privKeyStart, privKeyLength);
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ break;
+ }
+ } else {
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ }
+ return opr;
+ }
+
+ @Override
+ public short cmacKDF(
+ KMKey pSharedKey,
+ byte[] label,
+ short labelStart,
+ short labelLen,
+ byte[] context,
+ short contextStart,
+ short contextLength,
+ byte[] keyBuf,
+ short keyStart) {
+ HMACKey key =
+ cmacKdf(pSharedKey, label, labelStart, labelLen, context, contextStart, contextLength);
+ return key.getKey(keyBuf, keyStart);
+ }
+
+ @Override
+ public boolean isUpgrading() {
+ return UpgradeManager.isUpgrading();
+ }
+
+ @Override
+ public KMKey createMasterKey(KMKey masterKey, short keySizeBits) {
+ try {
+ if (masterKey == null) {
+ AESKey key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, keySizeBits, false);
+ masterKey = new KMAESKey(key);
+ }
+ short keyLen = (short) (keySizeBits / 8);
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, keyLen, (byte) 0);
+ getTrueRandomNumber(tmpArray, (short) 0, keyLen);
+ ((KMAESKey) masterKey).aesKey.setKey(tmpArray, (short) 0);
+ return (KMKey) masterKey;
+ } finally {
+ clean();
+ }
+ }
+
+ @Override
+ public KMKey createPreSharedKey(KMKey preSharedKey, byte[] keyData, short offset, short length) {
+ short lengthInBits = (short) (length * 8);
+ if ((lengthInBits % 8 != 0) || !(lengthInBits >= 64 && lengthInBits <= 512)) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ if (preSharedKey == null) {
+ HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, lengthInBits, false);
+ preSharedKey = new KMHmacKey(key);
+ }
+ ((KMHmacKey) preSharedKey).hmacKey.setKey(keyData, offset, length);
+ return (KMKey) preSharedKey;
+ }
+
+ @Override
+ public KMKey createComputedHmacKey(
+ KMKey computedHmacKey, byte[] keyData, short offset, short length) {
+ if (length != COMPUTED_HMAC_KEY_SIZE) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ if (computedHmacKey == null) {
+ HMACKey key =
+ (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), false);
+ computedHmacKey = new KMHmacKey(key);
+ }
+ ((KMHmacKey) computedHmacKey).hmacKey.setKey(keyData, offset, length);
+ return (KMKey) computedHmacKey;
+ }
+
+ @Override
+ public short ecSign256(
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart) {
+
+ ECPrivateKey key = (ECPrivateKey) ecKeyPair.getPrivate();
+ key.setS(secret, secretStart, secretLength);
+
+ Signature.OneShot signer = null;
+ try {
+
+ signer =
+ Signature.OneShot.open(
+ MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL);
+ signer.init(key, Signature.MODE_SIGN);
+ return signer.sign(
+ inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart);
+ } finally {
+ if (signer != null) {
+ signer.close();
+ }
+ }
+ }
+
+ @Override
+ public short rsaSign256Pkcs1(
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ byte[] modBuf,
+ short modStart,
+ short modLength,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart) {
+
+ Signature.OneShot signer = null;
+ try {
+
+ signer =
+ Signature.OneShot.open(
+ MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_RSA, Cipher.PAD_PKCS1);
+
+ RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate();
+ ;
+ key.setExponent(secret, secretStart, secretLength);
+ key.setModulus(modBuf, modStart, modLength);
+
+ signer.init(key, Signature.MODE_SIGN);
+ return signer.sign(
+ inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart);
+ } finally {
+ if (signer != null) {
+ signer.close();
+ }
+ }
+ }
+
+ @Override
+ public boolean isAttestationKeyProvisioned() {
+ return false;
+ }
+
+ @Override
+ public short getAttestationKeyAlgorithm() {
+ return KMType.INVALID_VALUE;
+ }
+
+ @Override
+ public short hkdf(
+ byte[] ikm,
+ short ikmOff,
+ short ikmLen,
+ byte[] salt,
+ short saltOff,
+ short saltLen,
+ byte[] info,
+ short infoOff,
+ short infoLen,
+ byte[] out,
+ short outOff,
+ short outLen) {
+ // HMAC_extract
+ hkdfExtract(ikm, ikmOff, ikmLen, salt, saltOff, saltLen, tmpArray, (short) 0);
+ // HMAC_expand
+ return hkdfExpand(tmpArray, (short) 0, (short) 32, info, infoOff, infoLen, out, outOff, outLen);
+ }
+
+ private short hkdfExtract(
+ byte[] ikm,
+ short ikmOff,
+ short ikmLen,
+ byte[] salt,
+ short saltOff,
+ short saltLen,
+ byte[] out,
+ short off) {
+ // https://tools.ietf.org/html/rfc5869#section-2.2
+ HMACKey hmacKey = createHMACKey(salt, saltOff, saltLen);
+ hmacSignature.init(hmacKey, Signature.MODE_SIGN);
+ return hmacSignature.sign(ikm, ikmOff, ikmLen, out, off);
+ }
+
+ private short hkdfExpand(
+ byte[] prk,
+ short prkOff,
+ short prkLen,
+ byte[] info,
+ short infoOff,
+ short infoLen,
+ byte[] out,
+ short outOff,
+ short outLen) {
+ // https://tools.ietf.org/html/rfc5869#section-2.3
+ short digestLen = (short) 32; // SHA256 digest length.
+ // Calculate no of iterations N.
+ short n = (short) ((short) (outLen + digestLen - 1) / digestLen);
+ if (n > 255) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ HMACKey hmacKey = createHMACKey(prk, prkOff, prkLen);
+ Util.arrayFill(tmpArray, (short) 0, (short) 33, (byte) 0);
+ short bytesCopied = 0;
+ short len = 0;
+ for (short i = 0; i < n; i++) {
+ tmpArray[0]++;
+ hmacSignature.init(hmacKey, Signature.MODE_SIGN);
+ if (i != 0) {
+ hmacSignature.update(tmpArray, (short) 1, (short) 32);
+ }
+ hmacSignature.update(info, infoOff, infoLen);
+ len = hmacSignature.sign(tmpArray, (short) 0, (short) 1, tmpArray, (short) 1);
+ if ((short) (bytesCopied + len) > outLen) {
+ len = (short) (outLen - bytesCopied);
+ }
+ Util.arrayCopyNonAtomic(tmpArray, (short) 1, out, (short) (outOff + bytesCopied), len);
+ bytesCopied += len;
+ }
+ return outLen;
+ }
+
+ @Override
+ public short ecdhKeyAgreement(
+ byte[] privKey,
+ short privKeyOff,
+ short privKeyLen,
+ byte[] publicKey,
+ short publicKeyOff,
+ short publicKeyLen,
+ byte[] secret,
+ short secretOff) {
+ keyAgreement.init(createEcKey(privKey, privKeyOff, privKeyLen));
+ return keyAgreement.generateSecret(publicKey, publicKeyOff, publicKeyLen, secret, secretOff);
+ }
+
+ @Override
+ public boolean ecVerify256(
+ byte[] pubKey,
+ short pubKeyOffset,
+ short pubKeyLen,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] signatureDataBuf,
+ short signatureDataStart,
+ short signatureDataLen) {
+ Signature.OneShot signer = null;
+ try {
+ signer =
+ Signature.OneShot.open(
+ MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL);
+ ECPublicKey key = (ECPublicKey) ecKeyPair.getPublic();
+ key.setW(pubKey, pubKeyOffset, pubKeyLen);
+ signer.init(key, Signature.MODE_VERIFY);
+ return signer.verify(
+ inputDataBuf,
+ inputDataStart,
+ inputDataLength,
+ signatureDataBuf,
+ signatureDataStart,
+ (short) (signatureDataBuf[(short) (signatureDataStart + 1)] + 2));
+ } finally {
+ if (signer != null) {
+ signer.close();
+ }
+ }
+ }
+
+ @Override
+ public short signWithDeviceUniqueKey(
+ KMKey ecPrivKey,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart) {
+ Signature.OneShot signer = null;
+ try {
+ signer =
+ Signature.OneShot.open(
+ MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL);
+ signer.init(
+ ((KMECDeviceUniqueKeyPair) ecPrivKey).ecKeyPair.getPrivate(), Signature.MODE_SIGN);
+ return signer.sign(
+ inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart);
+ } finally {
+ if (signer != null) {
+ signer.close();
+ }
+ }
+ }
+
+ @Override
+ public KMKey createRkpDeviceUniqueKeyPair(
+ KMKey key,
+ byte[] pubKey,
+ short pubKeyOff,
+ short pubKeyLen,
+ byte[] privKey,
+ short privKeyOff,
+ short privKeyLen) {
+ if (key == null) {
+ KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256);
+ poolMgr.initECKey(ecKeyPair);
+ key = new KMECDeviceUniqueKeyPair(ecKeyPair);
+ }
+ ECPrivateKey ecKeyPair = (ECPrivateKey) ((KMECDeviceUniqueKeyPair) key).ecKeyPair.getPrivate();
+ ECPublicKey ecPublicKey = (ECPublicKey) ((KMECDeviceUniqueKeyPair) key).ecKeyPair.getPublic();
+ ecKeyPair.setS(privKey, privKeyOff, privKeyLen);
+ ecPublicKey.setW(pubKey, pubKeyOff, pubKeyLen);
+ return (KMKey) key;
+ }
+
+ @Override
+ public KMKey createRkpMacKey(KMKey rkpMacKey, byte[] keyData, short offset, short length) {
+ if (rkpMacKey == null) {
+ HMACKey key =
+ (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), false);
+ rkpMacKey = new KMHmacKey(key);
+ }
+ ((KMHmacKey) rkpMacKey).hmacKey.setKey(keyData, offset, length);
+ return rkpMacKey;
+ }
+
+ @Override
+ public short messageDigest256(
+ byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset) {
+ MessageDigest.OneShot mDigest = null;
+ short len = 0;
+ try {
+ mDigest = MessageDigest.OneShot.open(MessageDigest.ALG_SHA_256);
+ len = mDigest.doFinal(inBuff, inOffset, inLength, outBuff, outOffset);
+ } finally {
+ if (mDigest != null) {
+ mDigest.close();
+ mDigest = null;
+ }
+ }
+ return len;
+ }
+
+ public boolean isPowerReset() {
+ boolean flag = false;
+ if (resetFlag[0] == POWER_RESET_TRUE) {
+ resetFlag[0] = POWER_RESET_FALSE;
+ flag = true;
+ if (poolMgr != null) {
+ poolMgr.powerReset();
+ }
+ }
+ return flag;
+ }
+
+ @Override
+ public void onSave(Element element, byte interfaceType, Object object) {
+ element.write(interfaceType);
+ if (object == null) {
+ element.write(null);
+ return;
+ }
+ switch (interfaceType) {
+ case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY:
+ KMAESKey.onSave(element, (KMAESKey) object);
+ break;
+ case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY:
+ KMHmacKey.onSave(element, (KMHmacKey) object);
+ break;
+ case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR:
+ KMECDeviceUniqueKeyPair.onSave(element, (KMECDeviceUniqueKeyPair) object);
+ break;
+ case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY:
+ KMHmacKey.onSave(element, (KMHmacKey) object);
+ break;
+ default:
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ }
+
+ @Override
+ public Object onRestore(Element element) {
+ if (element == null) {
+ return null;
+ }
+ byte interfaceType = element.readByte();
+ switch (interfaceType) {
+ case KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY:
+ return KMHmacKey.onRestore((HMACKey) element.readObject());
+ case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY:
+ return KMAESKey.onRestore((AESKey) element.readObject());
+ case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY:
+ return KMHmacKey.onRestore((HMACKey) element.readObject());
+ case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR:
+ return KMECDeviceUniqueKeyPair.onRestore((KeyPair) element.readObject());
+ case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY:
+ return KMHmacKey.onRestore((HMACKey) element.readObject());
+ default:
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return null;
+ }
+
+ @Override
+ public short getBackupPrimitiveByteCount(byte interfaceType) {
+ short primitiveCount = 1; // interface type
+ switch (interfaceType) {
+ case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY:
+ primitiveCount += KMAESKey.getBackupPrimitiveByteCount();
+ break;
+ case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY:
+ primitiveCount += KMHmacKey.getBackupPrimitiveByteCount();
+ break;
+ case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR:
+ primitiveCount += KMECDeviceUniqueKeyPair.getBackupPrimitiveByteCount();
+ break;
+ case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY:
+ primitiveCount += KMHmacKey.getBackupPrimitiveByteCount();
+ break;
+ default:
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return primitiveCount;
+ }
+
+ @Override
+ public short getBackupObjectCount(byte interfaceType) {
+ switch (interfaceType) {
+ case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY:
+ return KMAESKey.getBackupObjectCount();
+ case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY:
+ return KMHmacKey.getBackupObjectCount();
+ case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR:
+ return KMECDeviceUniqueKeyPair.getBackupObjectCount();
+ case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY:
+ return KMHmacKey.getBackupObjectCount();
+ default:
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean isBootSignalEventSupported() {
+ return false;
+ }
+
+ @Override
+ public boolean isDeviceRebooted() {
+ return false;
+ }
+
+ @Override
+ public void clearDeviceBooted(boolean resetBootFlag) {
+ // To be filled
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java
new file mode 100644
index 0000000..05801b0
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java
@@ -0,0 +1,198 @@
+/*
+ * 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" 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.javacard.seprovider;
+
+/**
+ * The KMAttestationCert interface represents a X509 compliant attestation certificate required to
+ * support keymaster's attestKey function. This cert will be created according to the specifications
+ * given in android keymaster hal documentation. KMSeProvider has to provide the instance of this
+ * certificate. This interface is designed based on builder pattern and hence each method returns
+ * instance of cert.
+ */
+public interface KMAttestationCert {
+
+ /**
+ * Set verified boot hash.
+ *
+ * @param obj This is a KMByteBlob containing hash
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert verifiedBootHash(short obj);
+
+ /**
+ * Set verified boot key received during booting up.
+ *
+ * @param obj This is a KMByteBlob containing verified boot key.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert verifiedBootKey(short obj);
+
+ /**
+ * Set verified boot state received during booting up.
+ *
+ * @param val This is a byte containing verified boot state value.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert verifiedBootState(byte val);
+
+ /**
+ * Set uniqueId received from CA certificate during provisioning.
+ *
+ * @param scratchpad Buffer to store intermediate results.
+ * @param scratchPadOff Start offset of the scratchpad buffer.
+ * @param creationTime This buffer contains the CREATION_TIME value.
+ * @param creationTimeOff Start offset of creattionTime buffer.
+ * @param creationTimeLen Length of the creationTime buffer.
+ * @param attestAppId This buffer contains the ATTESTATION_APPLICATION_ID value.
+ * @param attestAppIdOff Start offset of the attestAppId buffer.
+ * @param attestAppIdLen Length of the attestAppId buffer.
+ * @param resetSinceIdRotation This holds the information of RESET_SINCE_ID_ROTATION.
+ * @param masterKey
+ * @return instance of KMAttestationCert.
+ */
+ KMAttestationCert makeUniqueId(
+ byte[] scratchpad,
+ short scratchPadOff,
+ byte[] creationTime,
+ short creationTimeOff,
+ short creationTimeLen,
+ byte[] attestAppId,
+ short attestAppIdOff,
+ short attestAppIdLen,
+ byte resetSinceIdRotation,
+ KMKey masterKey);
+
+ /**
+ * Set start time received from creation/activation time tag. Used for certificate's valid period.
+ *
+ * @param obj This is a KMByteBlob object containing start time.
+ * @param scratchpad Buffer to store intermediate results.
+ * @return instance of KMAttestationCert.
+ */
+ KMAttestationCert notBefore(short obj, boolean derEncoded, byte[] scratchpad);
+
+ /**
+ * Set expiry time received from expiry time tag or ca certificates expiry time. Used for
+ * certificate's valid period.
+ *
+ * @param usageExpiryTimeObj This is a KMByteBlob containing expiry time. certificate.
+ * @param scratchPad Buffer to store intermediate results.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert notAfter(short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad);
+
+ /**
+ * Set device lock status received during booting time or due to device lock command.
+ *
+ * @param val This is true if device is locked.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert deviceLocked(boolean val);
+
+ /**
+ * Set public key to be attested received from attestKey command.
+ *
+ * @param obj This is KMByteBlob containing the public key.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert publicKey(short obj);
+
+ /**
+ * Set attestation challenge received from attestKey command.
+ *
+ * @param obj This is KMByteBlob containing the attestation challenge.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert attestationChallenge(short obj);
+
+ /**
+ * Set extension tag received from key characteristics which needs to be added to android
+ * extension. This method will called once for each tag.
+ *
+ * @param tag is the KMByteBlob containing KMTag.
+ * @param hwEnforced is true if the tag has to be added to hw enforced list or else added to sw
+ * enforced list.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert extensionTag(short tag, boolean hwEnforced);
+
+ /**
+ * Set ASN.1 encoded X509 issuer field received from attestation key CA cert.
+ *
+ * @param obj This is KMByteBlob containing the issuer.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert issuer(short obj);
+
+ /**
+ * Set byte buffer to be used to generate certificate.
+ *
+ * @param buf This is byte[] buffer.
+ * @param bufStart This is short start offset.
+ * @param maxLen This is short length of the buffer.
+ * @return instance of KMAttestationCert
+ */
+ KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen);
+
+ /**
+ * Get the start of the certificate
+ *
+ * @return start of the attestation cert.
+ */
+ short getCertStart();
+
+ /**
+ * Get the length of the certificate
+ *
+ * @return length of the attestation cert.
+ */
+ short getCertLength();
+
+ /**
+ * Build a fake signed certificate. After this method executes the certificate is ready with the
+ * signature equal to 1 byte which is 0 and with rsa signature algorithm.
+ */
+ void build();
+
+ /**
+ * Set the Serial number in the certificate. If no serial number is set then serial number is 1.
+ *
+ * @param serialNumber
+ */
+ boolean serialNumber(short serialNumber);
+
+ /**
+ * Set the Subject Name in the certificate.
+ *
+ * @param subject
+ */
+ boolean subjectName(short subject);
+
+ /**
+ * Set attestation key and mode.
+ *
+ * @param attestKey KMByteBlob of the key
+ * @param mode
+ */
+ KMAttestationCert ecAttestKey(short attestKey, byte mode);
+ /**
+ * Set attestation key and mode.
+ *
+ * @param attestKey KMByteBlob of the key
+ * @param mode
+ */
+ KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte mode);
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java
new file mode 100644
index 0000000..61ddb36
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java
@@ -0,0 +1,17 @@
+package com.android.javacard.seprovider;
+
+/**
+ * This class holds different interface type constants to differentiate between the instances of
+ * Computed Hmac key, device unique key pair, RKP Mac key, and master key when passed as generic
+ * objects. These constants are used in upgrade flow to retrieve the size of the object and
+ * primitive types saved and restored for respective key types.
+ */
+public class KMDataStoreConstants {
+ // INTERFACE Types
+ public static final byte INTERFACE_TYPE_COMPUTED_HMAC_KEY = 0x01;
+ // 0x02 reserved for INTERFACE_TYPE_ATTESTATION_KEY
+ public static final byte INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR = 0x03;
+ public static final byte INTERFACE_TYPE_MASTER_KEY = 0x04;
+ public static final byte INTERFACE_TYPE_PRE_SHARED_KEY = 0x05;
+ public static final byte INTERFACE_TYPE_RKP_MAC_KEY = 0x06;
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKeyPair.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKeyPair.java
new file mode 100644
index 0000000..0e430a3
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKeyPair.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright(C) 2021 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.security.ECPublicKey;
+import javacard.security.KeyPair;
+import org.globalplatform.upgrade.Element;
+
+/** This is a wrapper class for KeyPair. */
+public class KMECDeviceUniqueKeyPair implements KMKey {
+
+ public KeyPair ecKeyPair;
+
+ @Override
+ public short getPublicKey(byte[] buf, short offset) {
+ ECPublicKey publicKey = (ECPublicKey) ecKeyPair.getPublic();
+ return publicKey.getW(buf, offset);
+ }
+
+ public KMECDeviceUniqueKeyPair(KeyPair ecPair) {
+ ecKeyPair = ecPair;
+ }
+
+ public static void onSave(Element element, KMECDeviceUniqueKeyPair kmKey) {
+ element.write(kmKey.ecKeyPair);
+ }
+
+ public static KMECDeviceUniqueKeyPair onRestore(KeyPair ecKey) {
+ if (ecKey == null) {
+ return null;
+ }
+ return new KMECDeviceUniqueKeyPair(ecKey);
+ }
+
+ public static short getBackupPrimitiveByteCount() {
+ return (short) 0;
+ }
+
+ public static short getBackupObjectCount() {
+ return (short) 1;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java
new file mode 100644
index 0000000..83774ab
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java
@@ -0,0 +1,138 @@
+/*
+ * 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.Util;
+import javacard.security.CryptoException;
+import javacard.security.Key;
+import javacard.security.MessageDigest;
+import javacard.security.Signature;
+import javacardx.crypto.Cipher;
+
+/**
+ * This class provides support for ECDSA_NO_DIGEST signature algorithm. Added this because javacard
+ * 3.0.5 does not support this
+ */
+public class KMEcdsa256NoDigestSignature extends Signature {
+
+ public static final byte ALG_ECDSA_NODIGEST = (byte) 0x67;
+ public static final byte MAX_NO_DIGEST_MSG_LEN = 32;
+ private byte algorithm;
+ private Signature inst;
+
+ public KMEcdsa256NoDigestSignature(byte alg) {
+ algorithm = alg;
+ // There is no constant for no digest so ALG_ECDSA_SHA_256 is used. However,
+ // signPreComputedHash is used for signing which is equivalent to no digest sign.
+ inst = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false);
+ }
+
+ @Override
+ public void init(Key key, byte b) throws CryptoException {
+ inst.init(key, b);
+ }
+
+ @Override
+ public void init(Key key, byte b, byte[] bytes, short i, short i1) throws CryptoException {
+ inst.init(key, b, bytes, i, i1);
+ }
+
+ @Override
+ public void setInitialDigest(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3)
+ throws CryptoException {}
+
+ @Override
+ public byte getAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public byte getMessageDigestAlgorithm() {
+ return MessageDigest.ALG_NULL;
+ }
+
+ @Override
+ public byte getCipherAlgorithm() {
+ return 0;
+ }
+
+ @Override
+ public byte getPaddingAlgorithm() {
+ return Cipher.PAD_NULL;
+ }
+
+ @Override
+ public short getLength() throws CryptoException {
+ return inst.getLength();
+ }
+
+ @Override
+ public void update(byte[] message, short msgStart, short messageLength) throws CryptoException {
+ // HAL accumulates the data and send it at finish operation.
+ }
+
+ @Override
+ public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2)
+ throws CryptoException {
+ try {
+ if (i1 > MAX_NO_DIGEST_MSG_LEN) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_USE);
+ }
+ // add zeros to the left
+ if (i1 < MAX_NO_DIGEST_MSG_LEN) {
+ Util.arrayFillNonAtomic(
+ KMAndroidSEProvider.getInstance().tmpArray,
+ (short) 0,
+ (short) MAX_NO_DIGEST_MSG_LEN,
+ (byte) 0);
+ }
+ Util.arrayCopyNonAtomic(
+ bytes,
+ i,
+ KMAndroidSEProvider.getInstance().tmpArray,
+ (short) (MAX_NO_DIGEST_MSG_LEN - i1),
+ i1);
+ return inst.signPreComputedHash(
+ KMAndroidSEProvider.getInstance().tmpArray,
+ (short) 0,
+ (short) MAX_NO_DIGEST_MSG_LEN,
+ bytes1,
+ i2);
+ } finally {
+ KMAndroidSEProvider.getInstance().clean();
+ }
+ }
+
+ @Override
+ public short signPreComputedHash(byte[] bytes, short i, short i1, byte[] bytes1, short i2)
+ throws CryptoException {
+ return inst.sign(bytes, i, i1, bytes1, i2);
+ }
+
+ @Override
+ public boolean verify(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3)
+ throws CryptoException {
+ // Verification is handled inside HAL
+ return false;
+ }
+
+ @Override
+ public boolean verifyPreComputedHash(
+ byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) throws CryptoException {
+ // Verification is handled inside HAL
+ return false;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java
new file mode 100644
index 0000000..69cb069
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java
@@ -0,0 +1,32 @@
+/*
+ * 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" 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.javacard.seprovider;
+
+/**
+ * KMError includes all the error codes from android keymaster hal specifications. The values are
+ * positive unlike negative values in keymaster hal.
+ */
+public class KMError {
+
+ public static final short OK = 0;
+ public static final short UNSUPPORTED_PURPOSE = 2;
+ public static final short UNSUPPORTED_ALGORITHM = 4;
+ public static final short INVALID_INPUT_LENGTH = 21;
+ public static final short VERIFICATION_FAILED = 30;
+ public static final short TOO_MANY_OPERATIONS = 31;
+ public static final short INVALID_ARGUMENT = 38;
+ public static final short UNKNOWN_ERROR = 1000;
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java
new file mode 100644
index 0000000..79983a2
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java
@@ -0,0 +1,46 @@
+/*
+ * 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" 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.javacard.seprovider;
+
+import javacard.framework.JCSystem;
+
+/**
+ * KMException is shared instance of exception used for all exceptions in the applet. It is used to
+ * throw EMError errors.
+ */
+public class KMException extends RuntimeException {
+
+ private static short[] reason;
+ private static KMException exception;
+
+ private KMException() {}
+
+ public static short reason() {
+ return reason[0];
+ }
+
+ public static void throwIt(short e) {
+ if (reason == null) {
+ reason = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_DESELECT);
+ }
+ if (exception == null) {
+ exception = new KMException();
+ }
+ reason[0] = e;
+ throw exception;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java
new file mode 100644
index 0000000..e938a2b
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java
@@ -0,0 +1,53 @@
+/*
+ * 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.security.HMACKey;
+import org.globalplatform.upgrade.Element;
+
+/** This is a wrapper class for HMACKey. */
+public class KMHmacKey implements KMKey {
+
+ public HMACKey hmacKey;
+
+ public KMHmacKey(HMACKey key) {
+ hmacKey = key;
+ }
+
+ public static void onSave(Element element, KMHmacKey kmKey) {
+ element.write(kmKey.hmacKey);
+ }
+
+ public static KMHmacKey onRestore(HMACKey hmacKey) {
+ if (hmacKey == null) {
+ return null;
+ }
+ return new KMHmacKey(hmacKey);
+ }
+
+ public static short getBackupPrimitiveByteCount() {
+ return (short) 0;
+ }
+
+ public static short getBackupObjectCount() {
+ return (short) 1;
+ }
+
+ @Override
+ public short getPublicKey(byte[] buf, short offset) {
+ return (short) 0;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKey.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKey.java
new file mode 100644
index 0000000..9894382
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKey.java
@@ -0,0 +1,10 @@
+package com.android.javacard.seprovider;
+
+/**
+ * This interface helps to decouple Javacard internal key objects from the keymaster package. Using
+ * Javacard key objects provides security by providing protection against side channel attacks.
+ * KMAESKey, KMECDeviceUniqueKey and KMHmacKey implements this interface.
+ */
+public interface KMKey {
+ short getPublicKey(byte[] buf, short offset);
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKeyObject.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKeyObject.java
new file mode 100644
index 0000000..a37da08
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKeyObject.java
@@ -0,0 +1,10 @@
+package com.android.javacard.seprovider;
+
+/**
+ * This class holds the KeyObject and its associated algorithm value. Each KMKeyObject is tied to
+ * one of the crypto operations.
+ */
+public class KMKeyObject {
+ public byte algorithm;
+ public Object keyObjectInst;
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java
new file mode 100644
index 0000000..12e691e
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java
@@ -0,0 +1,75 @@
+/*
+ * 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" 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.javacard.seprovider;
+
+/**
+ * KMOperation represents a persistent operation started by keymaster hal's beginOperation function.
+ * This operation is persistent i.e. it will be stored in non volatile memory of se card. It will be
+ * returned back to KMSEProvider for the reuse when the operation is finished.
+ */
+public interface KMOperation {
+
+ // Used for cipher operations
+ short update(
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart);
+
+ // Used for signature operations
+ short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength);
+
+ // Used for finishing cipher operations or ecdh keyAgreement.
+ short finish(
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart);
+
+ // Used for finishing signing operations.
+ short sign(
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] signBuf,
+ short signStart);
+
+ // Used for finishing verifying operations.
+ boolean verify(
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] signBuf,
+ short signStart,
+ short signLength);
+
+ // Used for aborting the ongoing operations.
+ void abort();
+
+ // Used for AES GCM cipher operation.
+ void updateAAD(byte[] dataBuf, short dataStart, short dataLength);
+
+ // Used for getting output size before finishing a AES GCM cipher operation. For encryption this
+ // will
+ // include the auth tag which is appended at the end of the encrypted data. For decryption this
+ // will be
+ // size of the decrypted data only.
+ short getAESGCMOutputSize(short dataSize, short macLength);
+
+ KMKeyObject getKeyObject();
+}
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);
+ }
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java
new file mode 100644
index 0000000..9bc258c
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java
@@ -0,0 +1,657 @@
+/*
+ * Copyright(C) 2021 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.security.AESKey;
+import javacard.security.CryptoException;
+import javacard.security.DESKey;
+import javacard.security.ECPrivateKey;
+import javacard.security.ECPublicKey;
+import javacard.security.HMACKey;
+import javacard.security.KeyAgreement;
+import javacard.security.KeyBuilder;
+import javacard.security.KeyPair;
+import javacard.security.Signature;
+import javacardx.crypto.AEADCipher;
+import javacardx.crypto.Cipher;
+
+/**
+ * This class creates and manages all the cipher, signer, key agreement, operation and trusted
+ * confirmation pool instances. Each cipher or signer pool can hold a maximum of 4 instances per
+ * algorithm; however, only one instance of each algorithm is created initially and if required more
+ * instances are created dynamically. A maximum of four operations can be performed simultaneously.
+ * Upon reaching the maximum limit of 4, further operations or crypto instances will throw a
+ * TOO_MANY_OPERATIONS error. TrustedConfirmation pool is to support any operation which has the
+ * TRUSTED_CONFIRMATION tag in its key parameters.
+ */
+public class KMPoolManager {
+
+ public static final byte MAX_OPERATION_INSTANCES = 4;
+ private static final byte HMAC_MAX_OPERATION_INSTANCES = 8;
+ public static final byte AES_128 = 0x04;
+ public static final byte AES_256 = 0x05;
+ // Resource type constants
+ public static final byte RESOURCE_TYPE_CRYPTO = 0x00;
+ public static final byte RESOURCE_TYPE_KEY = 0x01;
+ // static final variables
+ // --------------------------------------------------------------
+ // P-256 Curve Parameters
+ static byte[] secp256r1_P;
+ static byte[] secp256r1_A;
+
+ static byte[] secp256r1_B;
+ static byte[] secp256r1_S;
+
+ // Uncompressed form
+ static byte[] secp256r1_UCG;
+ static byte[] secp256r1_N;
+ static final byte secp256r1_H = 1;
+ // --------------------------------------------------------------
+
+ // Cipher pool
+ private Object[] cipherPool;
+ // Signature pool
+ private Object[] signerPool;
+ // Keyagreement pool
+ private Object[] keyAgreementPool;
+ // KMOperationImpl pool
+ private Object[] operationPool;
+ // Hmac signer pool which is used to support TRUSTED_CONFIRMATION_REQUIRED tag.
+ private Object[] hmacSignOperationPool;
+
+ private Object[] keysPool;
+ // RKP uses AESGCM and HMAC in generateCSR flow.
+ KMOperation rkpOPeration;
+ Signature rkpEc;
+ KMKeyObject rkpEcKey;
+
+ final byte[] KEY_ALGS = {
+ AES_128, AES_256, KMType.DES, KMType.RSA, KMType.EC, KMType.HMAC,
+ };
+
+ final byte[] CIPHER_ALGS = {
+ Cipher.ALG_AES_BLOCK_128_CBC_NOPAD,
+ Cipher.ALG_AES_BLOCK_128_ECB_NOPAD,
+ Cipher.ALG_DES_CBC_NOPAD,
+ Cipher.ALG_DES_ECB_NOPAD,
+ Cipher.ALG_AES_CTR,
+ Cipher.ALG_RSA_PKCS1,
+ KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1,
+ KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256,
+ Cipher.ALG_RSA_NOPAD,
+ AEADCipher.ALG_AES_GCM
+ };
+
+ final byte[] SIG_ALGS = {
+ Signature.ALG_RSA_SHA_256_PKCS1,
+ Signature.ALG_RSA_SHA_256_PKCS1_PSS,
+ Signature.ALG_ECDSA_SHA_256,
+ Signature.ALG_HMAC_SHA_256,
+ KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD,
+ KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST,
+ KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST
+ };
+
+ final byte[] KEY_AGREE_ALGS = {KeyAgreement.ALG_EC_SVDP_DH_PLAIN};
+
+ private static KMPoolManager poolManager;
+
+ public static KMPoolManager getInstance() {
+ if (poolManager == null) {
+ poolManager = new KMPoolManager();
+ }
+ return poolManager;
+ }
+
+ public static void initStatics() {
+ secp256r1_P =
+ new byte[] {
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF
+ };
+
+ secp256r1_A =
+ new byte[] {
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFC
+ };
+
+ secp256r1_B =
+ new byte[] {
+ (byte) 0x5A, (byte) 0xC6, (byte) 0x35, (byte) 0xD8, (byte) 0xAA, (byte) 0x3A,
+ (byte) 0x93, (byte) 0xE7, (byte) 0xB3, (byte) 0xEB, (byte) 0xBD, (byte) 0x55,
+ (byte) 0x76, (byte) 0x98, (byte) 0x86, (byte) 0xBC, (byte) 0x65, (byte) 0x1D,
+ (byte) 0x06, (byte) 0xB0, (byte) 0xCC, (byte) 0x53, (byte) 0xB0, (byte) 0xF6,
+ (byte) 0x3B, (byte) 0xCE, (byte) 0x3C, (byte) 0x3E, (byte) 0x27, (byte) 0xD2,
+ (byte) 0x60, (byte) 0x4B
+ };
+
+ secp256r1_S =
+ new byte[] {
+ (byte) 0xC4, (byte) 0x9D, (byte) 0x36, (byte) 0x08, (byte) 0x86, (byte) 0xE7,
+ (byte) 0x04, (byte) 0x93, (byte) 0x6A, (byte) 0x66, (byte) 0x78, (byte) 0xE1,
+ (byte) 0x13, (byte) 0x9D, (byte) 0x26, (byte) 0xB7, (byte) 0x81, (byte) 0x9F,
+ (byte) 0x7E, (byte) 0x90
+ };
+
+ // Uncompressed form
+ secp256r1_UCG =
+ new byte[] {
+ (byte) 0x04, (byte) 0x6B, (byte) 0x17, (byte) 0xD1, (byte) 0xF2, (byte) 0xE1,
+ (byte) 0x2C, (byte) 0x42, (byte) 0x47, (byte) 0xF8, (byte) 0xBC, (byte) 0xE6,
+ (byte) 0xE5, (byte) 0x63, (byte) 0xA4, (byte) 0x40, (byte) 0xF2, (byte) 0x77,
+ (byte) 0x03, (byte) 0x7D, (byte) 0x81, (byte) 0x2D, (byte) 0xEB, (byte) 0x33,
+ (byte) 0xA0, (byte) 0xF4, (byte) 0xA1, (byte) 0x39, (byte) 0x45, (byte) 0xD8,
+ (byte) 0x98, (byte) 0xC2, (byte) 0x96, (byte) 0x4F, (byte) 0xE3, (byte) 0x42,
+ (byte) 0xE2, (byte) 0xFE, (byte) 0x1A, (byte) 0x7F, (byte) 0x9B, (byte) 0x8E,
+ (byte) 0xE7, (byte) 0xEB, (byte) 0x4A, (byte) 0x7C, (byte) 0x0F, (byte) 0x9E,
+ (byte) 0x16, (byte) 0x2B, (byte) 0xCE, (byte) 0x33, (byte) 0x57, (byte) 0x6B,
+ (byte) 0x31, (byte) 0x5E, (byte) 0xCE, (byte) 0xCB, (byte) 0xB6, (byte) 0x40,
+ (byte) 0x68, (byte) 0x37, (byte) 0xBF, (byte) 0x51, (byte) 0xF5
+ };
+
+ secp256r1_N =
+ new byte[] {
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+ (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xBC, (byte) 0xE6,
+ (byte) 0xFA, (byte) 0xAD, (byte) 0xA7, (byte) 0x17, (byte) 0x9E, (byte) 0x84,
+ (byte) 0xF3, (byte) 0xB9, (byte) 0xCA, (byte) 0xC2, (byte) 0xFC, (byte) 0x63,
+ (byte) 0x25, (byte) 0x51
+ };
+ }
+
+ private KMPoolManager() {
+ initStatics();
+ cipherPool = new Object[(short) (CIPHER_ALGS.length * MAX_OPERATION_INSTANCES)];
+ // Extra 4 algorithms are used to support TRUSTED_CONFIRMATION_REQUIRED feature.
+ signerPool =
+ new Object[(short) ((SIG_ALGS.length * MAX_OPERATION_INSTANCES) + MAX_OPERATION_INSTANCES)];
+ keyAgreementPool = new Object[(short) (KEY_AGREE_ALGS.length * MAX_OPERATION_INSTANCES)];
+
+ keysPool =
+ new Object[(short) ((KEY_ALGS.length * MAX_OPERATION_INSTANCES) + MAX_OPERATION_INSTANCES)];
+ operationPool = new Object[MAX_OPERATION_INSTANCES];
+ hmacSignOperationPool = new Object[MAX_OPERATION_INSTANCES];
+ /* Initialize pools */
+ initializeOperationPool();
+ initializeHmacSignOperationPool();
+ initializeSignerPool();
+ initializeCipherPool();
+ initializeKeyAgreementPool();
+ initializeKeysPool();
+ // Initialize the Crypto and Key objects required for RKP flow.
+ initializeRKpObjects();
+ }
+
+ private void initializeRKpObjects() {
+ rkpOPeration = new KMOperationImpl();
+ rkpEc = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false);
+ rkpEcKey = createKeyObjectInstance(KMType.EC);
+ }
+
+ private void initializeKeysPool() {
+ for (short index = 0; index < KEY_ALGS.length; index++) {
+ keysPool[index] = createKeyObjectInstance(KEY_ALGS[index]);
+ }
+ }
+
+ private void initializeOperationPool() {
+ for (short index = 0; index < MAX_OPERATION_INSTANCES; index++) {
+ operationPool[index] = new KMOperationImpl();
+ }
+ }
+
+ private void initializeHmacSignOperationPool() {
+ for (short index = 0; index < MAX_OPERATION_INSTANCES; index++) {
+ hmacSignOperationPool[index] = new KMOperationImpl();
+ }
+ }
+
+ // Create a signature instance of each algorithm once.
+ private void initializeSignerPool() {
+ short index;
+ for (index = 0; index < SIG_ALGS.length; index++) {
+ signerPool[index] = getSignatureInstance(SIG_ALGS[index]);
+ }
+
+ // Allocate extra 4 HMAC signer instances required for trusted confirmation
+ for (short len = (short) (index + 4); index < len; index++) {
+ signerPool[index] = getSignatureInstance(Signature.ALG_HMAC_SHA_256);
+ }
+ }
+
+ // Create a cipher instance of each algorithm once.
+ private void initializeCipherPool() {
+ for (short index = 0; index < CIPHER_ALGS.length; index++) {
+ cipherPool[index] = getCipherInstance(CIPHER_ALGS[index]);
+ }
+ }
+
+ private void initializeKeyAgreementPool() {
+ for (short index = 0; index < KEY_AGREE_ALGS.length; index++) {
+ keyAgreementPool[index] = getKeyAgreementInstance(KEY_AGREE_ALGS[index]);
+ }
+ }
+
+ private Object[] getCryptoPoolInstance(short purpose) {
+ switch (purpose) {
+ case KMType.AGREE_KEY:
+ return keyAgreementPool;
+
+ case KMType.ENCRYPT:
+ case KMType.DECRYPT:
+ return cipherPool;
+
+ case KMType.SIGN:
+ case KMType.VERIFY:
+ return signerPool;
+
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ return null;
+ }
+
+ private Object createInstance(short purpose, short alg) {
+ switch (purpose) {
+ case KMType.AGREE_KEY:
+ return getKeyAgreementInstance((byte) alg);
+
+ case KMType.ENCRYPT:
+ case KMType.DECRYPT:
+ return getCipherInstance((byte) alg);
+
+ case KMType.SIGN:
+ case KMType.VERIFY:
+ return getSignatureInstance((byte) alg);
+
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ return null;
+ }
+
+ private KeyAgreement getKeyAgreementInstance(byte alg) {
+ return KeyAgreement.getInstance(alg, false);
+ }
+
+ private Signature getSignatureInstance(byte alg) {
+ if (KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD == alg
+ || KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST == alg) {
+ return new KMRsa2048NoDigestSignature(alg);
+ } else if (KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST == alg) {
+ return new KMEcdsa256NoDigestSignature(alg);
+ } else {
+ return Signature.getInstance(alg, false);
+ }
+ }
+
+ private KMKeyObject createKeyObjectInstance(byte alg) {
+ Object keyObject = null;
+ switch (alg) {
+ case AES_128:
+ keyObject =
+ (AESKey)
+ KeyBuilder.buildKey(
+ KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_128, false);
+ break;
+ case AES_256:
+ keyObject =
+ (AESKey)
+ KeyBuilder.buildKey(
+ KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_256, false);
+ break;
+ case KMType.DES:
+ keyObject =
+ (DESKey)
+ KeyBuilder.buildKey(
+ KeyBuilder.TYPE_DES_TRANSIENT_RESET, KeyBuilder.LENGTH_DES3_3KEY, false);
+ break;
+ case KMType.RSA:
+ keyObject = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048);
+ break;
+ case KMType.EC:
+ keyObject = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256);
+ initECKey((KeyPair) keyObject);
+ break;
+ case KMType.HMAC:
+ keyObject =
+ (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_RESET, (short) 512, false);
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ KMKeyObject ptr = new KMKeyObject();
+ ptr.algorithm = alg;
+ ptr.keyObjectInst = keyObject;
+ return ptr;
+ }
+
+ private Cipher getCipherInstance(byte alg) {
+ if ((KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1 == alg)
+ || (KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256 == alg)) {
+ return new KMRsaOAEPEncoding(alg);
+ } else {
+ return Cipher.getInstance(alg, false);
+ }
+ }
+
+ /**
+ * Returns the first available resource from operation pool.
+ *
+ * @return instance of the available resource or null if no resource is available.
+ */
+ public KMOperation getResourceFromOperationPool(boolean isTrustedConfOpr) {
+ short index = 0;
+ KMOperationImpl impl;
+ Object[] oprPool;
+ if (isTrustedConfOpr) {
+ oprPool = hmacSignOperationPool;
+ } else {
+ oprPool = operationPool;
+ }
+ while (index < oprPool.length) {
+ impl = (KMOperationImpl) oprPool[index];
+ // Mode is always set. so compare using mode value.
+ if (impl.getPurpose() == KMType.INVALID_VALUE) {
+ return impl;
+ }
+ index++;
+ }
+ return null;
+ }
+
+ private byte getAlgorithm(short purpose, Object object) {
+ switch (purpose) {
+ case KMType.AGREE_KEY:
+ return ((KeyAgreement) object).getAlgorithm();
+
+ case KMType.ENCRYPT:
+ case KMType.DECRYPT:
+ return ((Cipher) object).getAlgorithm();
+
+ case KMType.SIGN:
+ case KMType.VERIFY:
+ return ((Signature) object).getAlgorithm();
+
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ return 0;
+ }
+
+ private boolean isResourceBusy(Object obj, byte resourceType) {
+ short index = 0;
+ while (index < MAX_OPERATION_INSTANCES) {
+ if (((KMOperationImpl) operationPool[index]).isResourceMatches(obj, resourceType)
+ || ((KMOperationImpl) hmacSignOperationPool[index])
+ .isResourceMatches(obj, resourceType)) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ private void setObject(short purpose, KMOperation operation, Object obj) {
+ switch (purpose) {
+ case KMType.AGREE_KEY:
+ ((KMOperationImpl) operation).setKeyAgreement((KeyAgreement) obj);
+ break;
+ case KMType.ENCRYPT:
+ case KMType.DECRYPT:
+ ((KMOperationImpl) operation).setCipher((Cipher) obj);
+ break;
+ case KMType.SIGN:
+ case KMType.VERIFY:
+ ((KMOperationImpl) operation).setSignature((Signature) obj);
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ }
+
+ private void reserveOperation(
+ KMOperation operation,
+ short purpose,
+ short strongboxAlgType,
+ short padding,
+ short blockMode,
+ short macLength,
+ Object obj,
+ KMKeyObject keyObject) {
+ ((KMOperationImpl) operation).setPurpose(purpose);
+ ((KMOperationImpl) operation).setAlgorithmType(strongboxAlgType);
+ ((KMOperationImpl) operation).setPaddingAlgorithm(padding);
+ ((KMOperationImpl) operation).setBlockMode(blockMode);
+ ((KMOperationImpl) operation).setMacLength(macLength);
+ ((KMOperationImpl) operation).setKeyObject(keyObject);
+ setObject(purpose, operation, obj);
+ }
+
+ public KMOperation getRKpOperation(
+ short purpose,
+ short alg,
+ short strongboxAlgType,
+ short padding,
+ short blockMode,
+ short macLength) {
+ if (((KMOperationImpl) rkpOPeration).getPurpose() != KMType.INVALID_VALUE) {
+ // Should not come here.
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ Object cryptoObj = null;
+ KMKeyObject keyObject = null;
+
+ switch (alg) {
+ case Signature.ALG_ECDSA_SHA_256:
+ cryptoObj = rkpEc;
+ keyObject = rkpEcKey;
+ break;
+ default:
+ // Should not come here.
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ break;
+ }
+ reserveOperation(
+ rkpOPeration,
+ purpose,
+ strongboxAlgType,
+ padding,
+ blockMode,
+ macLength,
+ cryptoObj,
+ keyObject);
+ return rkpOPeration;
+ }
+
+ public KMOperation getOperationImpl(
+ short purpose,
+ short alg,
+ short strongboxAlgType,
+ short padding,
+ short blockMode,
+ short macLength,
+ short secretLength,
+ boolean isTrustedConfOpr) {
+ KMOperation operation;
+ // Throw exception if no resource from operation pool is available.
+ if (null == (operation = getResourceFromOperationPool(isTrustedConfOpr))) {
+ KMException.throwIt(KMError.TOO_MANY_OPERATIONS);
+ }
+ // Get one of the pool instances (cipher / signer / keyAgreement) based on purpose.
+ Object[] pool = getCryptoPoolInstance(purpose);
+ short index = 0;
+ short usageCount = 0;
+ short maxOperations = MAX_OPERATION_INSTANCES;
+ if (Signature.ALG_HMAC_SHA_256 == alg) {
+ maxOperations = HMAC_MAX_OPERATION_INSTANCES;
+ }
+
+ KMKeyObject keyObject = getKeyObjectFromPool(alg, secretLength, maxOperations);
+ while (index < pool.length) {
+ if (usageCount >= maxOperations) {
+ KMException.throwIt(KMError.TOO_MANY_OPERATIONS);
+ }
+ if (pool[index] == null) {
+ // Create one of the instance (Cipher / Signer / KeyAgreement] based on purpose.
+ Object cipherObject = createInstance(purpose, alg);
+ JCSystem.beginTransaction();
+ pool[index] = cipherObject;
+ JCSystem.commitTransaction();
+ reserveOperation(
+ operation,
+ purpose,
+ strongboxAlgType,
+ padding,
+ blockMode,
+ macLength,
+ pool[index],
+ keyObject);
+ break;
+ }
+ if (alg == getAlgorithm(purpose, pool[index])) {
+ // Check if the crypto instance is not busy and free to use.
+ if (!isResourceBusy(pool[index], RESOURCE_TYPE_CRYPTO)) {
+ reserveOperation(
+ operation,
+ purpose,
+ strongboxAlgType,
+ padding,
+ blockMode,
+ macLength,
+ pool[index],
+ keyObject);
+ break;
+ }
+ usageCount++;
+ }
+ index++;
+ }
+ return operation;
+ }
+
+ public KMKeyObject getKeyObjectFromPool(short alg, short secretLength, short maxOperations) {
+ KMKeyObject keyObject = null;
+ byte algo = mapAlgorithm(alg, secretLength);
+ short index = 0;
+ short usageCount = 0;
+ while (index < keysPool.length) {
+ if (usageCount >= maxOperations) {
+ KMException.throwIt(KMError.TOO_MANY_OPERATIONS);
+ }
+ if (keysPool[index] == null) {
+ keyObject = createKeyObjectInstance(algo);
+ JCSystem.beginTransaction();
+ keysPool[index] = keyObject;
+ JCSystem.commitTransaction();
+ break;
+ }
+ keyObject = (KMKeyObject) keysPool[index];
+ if (algo == keyObject.algorithm) {
+ // Check if the Object instance is not busy and free to use.
+ if (!isResourceBusy(keyObject, RESOURCE_TYPE_KEY)) {
+ break;
+ }
+ usageCount++;
+ }
+ index++;
+ }
+ return keyObject;
+ }
+
+ private byte mapAlgorithm(short alg, short secretLength) {
+ byte algo = 0;
+ switch (alg) {
+ case Cipher.ALG_AES_BLOCK_128_CBC_NOPAD:
+ case Cipher.ALG_AES_BLOCK_128_ECB_NOPAD:
+ case Cipher.ALG_AES_CTR:
+ case AEADCipher.ALG_AES_GCM:
+ if (secretLength == 16) {
+ algo = AES_128;
+ } else if (secretLength == 32) {
+ algo = AES_256;
+ } else {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ break;
+ case Cipher.ALG_DES_CBC_NOPAD:
+ case Cipher.ALG_DES_ECB_NOPAD:
+ algo = KMType.DES;
+ break;
+ case Cipher.ALG_RSA_PKCS1:
+ case KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1:
+ case KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256:
+ case Cipher.ALG_RSA_NOPAD:
+ case Signature.ALG_RSA_SHA_256_PKCS1:
+ case Signature.ALG_RSA_SHA_256_PKCS1_PSS:
+ case KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD:
+ case KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST:
+ algo = KMType.RSA;
+ break;
+ case Signature.ALG_ECDSA_SHA_256:
+ case KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST:
+ case KeyAgreement.ALG_EC_SVDP_DH_PLAIN:
+ algo = KMType.EC;
+ break;
+ case Signature.ALG_HMAC_SHA_256:
+ algo = KMType.HMAC;
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ return algo;
+ }
+
+ public void initECKey(KeyPair ecKeyPair) {
+ ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate();
+ ECPublicKey pubkey = (ECPublicKey) ecKeyPair.getPublic();
+ pubkey.setFieldFP(secp256r1_P, (short) 0, (short) secp256r1_P.length);
+ pubkey.setA(secp256r1_A, (short) 0, (short) secp256r1_A.length);
+ pubkey.setB(secp256r1_B, (short) 0, (short) secp256r1_B.length);
+ pubkey.setG(secp256r1_UCG, (short) 0, (short) secp256r1_UCG.length);
+ pubkey.setK(secp256r1_H);
+ pubkey.setR(secp256r1_N, (short) 0, (short) secp256r1_N.length);
+
+ privKey.setFieldFP(secp256r1_P, (short) 0, (short) secp256r1_P.length);
+ privKey.setA(secp256r1_A, (short) 0, (short) secp256r1_A.length);
+ privKey.setB(secp256r1_B, (short) 0, (short) secp256r1_B.length);
+ privKey.setG(secp256r1_UCG, (short) 0, (short) secp256r1_UCG.length);
+ privKey.setK(secp256r1_H);
+ privKey.setR(secp256r1_N, (short) 0, (short) secp256r1_N.length);
+ }
+
+ public void powerReset() {
+ short index = 0;
+ while (index < operationPool.length) {
+ ((KMOperationImpl) operationPool[index]).abort();
+ ((KMOperationImpl) hmacSignOperationPool[index]).abort();
+ index++;
+ }
+ // release rkp operation
+ rkpOPeration.abort();
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java
new file mode 100644
index 0000000..4890ce2
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java
@@ -0,0 +1,140 @@
+/*
+ * 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.Util;
+import javacard.security.CryptoException;
+import javacard.security.Key;
+import javacard.security.MessageDigest;
+import javacard.security.Signature;
+import javacardx.crypto.Cipher;
+
+/** This class provides support for RSA_NO_DIGEST signature algorithm. */
+public class KMRsa2048NoDigestSignature extends Signature {
+
+ public static final byte ALG_RSA_SIGN_NOPAD = (byte) 0x65;
+ public static final byte ALG_RSA_PKCS1_NODIGEST = (byte) 0x66;
+ private byte algorithm;
+ private Cipher inst;
+
+ public KMRsa2048NoDigestSignature(byte alg) {
+ algorithm = alg;
+ inst = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false);
+ }
+
+ @Override
+ public void init(Key key, byte b) throws CryptoException {
+ inst.init(key, b);
+ }
+
+ @Override
+ public void init(Key key, byte b, byte[] bytes, short i, short i1) throws CryptoException {
+ inst.init(key, b, bytes, i, i1);
+ }
+
+ @Override
+ public void setInitialDigest(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3)
+ throws CryptoException {}
+
+ @Override
+ public byte getAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public byte getMessageDigestAlgorithm() {
+ return MessageDigest.ALG_NULL;
+ }
+
+ @Override
+ public byte getCipherAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public byte getPaddingAlgorithm() {
+ return Cipher.PAD_NULL;
+ }
+
+ @Override
+ public short getLength() throws CryptoException {
+ return 0;
+ }
+
+ @Override
+ public void update(byte[] bytes, short i, short i1) throws CryptoException {
+ // HAL accumulates the data and send it at finish operation.
+ }
+
+ @Override
+ public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2)
+ throws CryptoException {
+ padData(bytes, i, i1, KMAndroidSEProvider.getInstance().tmpArray, (short) 0);
+ return inst.doFinal(
+ KMAndroidSEProvider.getInstance().tmpArray, (short) 0, (short) 256, bytes1, i2);
+ }
+
+ @Override
+ public short signPreComputedHash(byte[] bytes, short i, short i1, byte[] bytes1, short i2)
+ throws CryptoException {
+ return 0;
+ }
+
+ @Override
+ public boolean verify(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3)
+ throws CryptoException {
+ // Verification is handled inside HAL
+ return false;
+ }
+
+ @Override
+ public boolean verifyPreComputedHash(
+ byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) throws CryptoException {
+ // Verification is handled inside HAL
+ return false;
+ }
+
+ private void padData(byte[] buf, short start, short len, byte[] outBuf, short outBufStart) {
+ if (!isValidData(buf, start, len)) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ Util.arrayFillNonAtomic(outBuf, (short) outBufStart, (short) 256, (byte) 0x00);
+ if (algorithm == ALG_RSA_SIGN_NOPAD) { // add zero to right
+ } else if (algorithm == ALG_RSA_PKCS1_NODIGEST) { // 0x00||0x01||PS||0x00
+ outBuf[0] = 0x00;
+ outBuf[1] = 0x01;
+ Util.arrayFillNonAtomic(outBuf, (short) 2, (short) (256 - len - 3), (byte) 0xFF);
+ outBuf[(short) (256 - len - 1)] = 0x00;
+ } else {
+ CryptoException.throwIt(CryptoException.ILLEGAL_USE);
+ }
+ Util.arrayCopyNonAtomic(buf, start, outBuf, (short) (256 - len), len);
+ }
+
+ private boolean isValidData(byte[] buf, short start, short len) {
+ if (algorithm == ALG_RSA_SIGN_NOPAD) {
+ if (len > 256) {
+ return false;
+ }
+ } else { // ALG_RSA_PKCS1_NODIGEST
+ if (len > 245) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java
new file mode 100644
index 0000000..bf34ea1
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java
@@ -0,0 +1,289 @@
+/*
+ * 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.MessageDigest;
+import javacardx.crypto.Cipher;
+
+/** This class has the implementation for RSA_OAEP decoding algorithm. */
+public class KMRsaOAEPEncoding extends Cipher {
+
+ public static final byte ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1 = (byte) 0x1E;
+ public static final byte ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256 = (byte) 0x1F;
+
+ final short MGF1_BUF_SIZE = 256;
+ static byte[] mgf1Buf;
+ private Cipher cipher;
+ private byte hash;
+ private byte mgf1Hash;
+ private byte algorithm;
+
+ public KMRsaOAEPEncoding(byte alg) {
+ setDigests(alg);
+ cipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false);
+ algorithm = alg;
+ if (null == mgf1Buf) {
+ mgf1Buf =
+ JCSystem.makeTransientByteArray(MGF1_BUF_SIZE, JCSystem.MEMORY_TYPE_TRANSIENT_DESELECT);
+ }
+ }
+
+ private void setDigests(byte alg) {
+ switch (alg) {
+ case ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1:
+ hash = MessageDigest.ALG_SHA_256;
+ mgf1Hash = MessageDigest.ALG_SHA;
+ break;
+ case ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256:
+ hash = MessageDigest.ALG_SHA_256;
+ mgf1Hash = MessageDigest.ALG_SHA_256;
+ break;
+ default:
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ }
+ }
+
+ private short getDigestLength() {
+ switch (hash) {
+ case MessageDigest.ALG_SHA:
+ return MessageDigest.LENGTH_SHA;
+ case MessageDigest.ALG_SHA_224:
+ return MessageDigest.LENGTH_SHA_224;
+ case MessageDigest.ALG_SHA_256:
+ return MessageDigest.LENGTH_SHA_256;
+ case MessageDigest.ALG_SHA_384:
+ return MessageDigest.LENGTH_SHA_384;
+ case MessageDigest.ALG_SHA_512:
+ return MessageDigest.LENGTH_SHA_512;
+ default:
+ CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+ }
+ return 0;
+ }
+
+ @Override
+ public void init(Key theKey, byte theMode) throws CryptoException {
+ cipher.init(theKey, theMode);
+ }
+
+ @Override
+ public void init(Key theKey, byte theMode, byte[] bArray, short bOff, short bLen)
+ throws CryptoException {
+ cipher.init(theKey, theMode, bArray, bOff, bLen);
+ }
+
+ @Override
+ public byte getAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public byte getCipherAlgorithm() {
+ return 0;
+ }
+
+ @Override
+ public byte getPaddingAlgorithm() {
+ return 0;
+ }
+
+ @Override
+ public short doFinal(
+ byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset)
+ throws CryptoException {
+ short len = cipher.doFinal(inBuff, inOffset, inLength, outBuff, outOffset);
+
+ // https://tools.ietf.org/html/rfc8017#section-7.1
+ // https://www.inf.pucrs.br/~calazans/graduate/TPVLSI_I/RSA-oaep_spec.pdf
+ // RSA OAEP Encoding and Decoding Mechanism for a 2048 bit RSA Key.
+ // Msg -> RSA-OAEP-ENCODE -> RSAEncryption -> RSADecryption ->
+ // RSA-OAEP-DECODE -> Msg
+ // RSA-OAEP-ENCODE generates an output length of 255, but RSAEncryption
+ // requires and input of length 256 so we pad 0 to the left of the input
+ // message and make the length equal to 256 and pass to RSAEncryption.
+ // RSADecryption takes input length equal to 256 and generates an
+ // output of length 256. After decryption the first byte of the output
+ // should be 0(left padding we did in encryption).
+ // RSA-OAEP-DECODE takes input of length 255 so remove the left padding of 1
+ // byte.
+ if (len != 256 || outBuff[0] != 0) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ Util.arrayCopyNonAtomic(
+ outBuff, (short) (outOffset + 1), outBuff, (short) 0, (short) (len - 1));
+ return rsaOAEPDecode(outBuff, (short) 0, (short) (len - 1));
+ }
+
+ @Override
+ public short update(
+ byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset)
+ throws CryptoException {
+ return cipher.update(inBuff, inOffset, inLength, outBuff, outOffset);
+ }
+
+ private void maskGenerationFunction1(
+ byte[] input,
+ short inputOffset,
+ short inputLen,
+ short expectedOutLen,
+ byte[] outBuf,
+ short outOffset) {
+ short counter = 0;
+ MessageDigest.OneShot md = null;
+ try {
+ md = MessageDigest.OneShot.open(mgf1Hash);
+ short digestLen = md.getLength();
+
+ Util.arrayCopyNonAtomic(input, inputOffset, mgf1Buf, (short) 0, inputLen);
+ while (counter < (short) (expectedOutLen / digestLen)) {
+ I2OS(counter, mgf1Buf, (short) inputLen);
+ md.doFinal(
+ mgf1Buf,
+ (short) 0,
+ (short) (4 + inputLen),
+ outBuf,
+ (short) (outOffset + (counter * digestLen)));
+ counter++;
+ }
+
+ if ((short) (counter * digestLen) < expectedOutLen) {
+ I2OS(counter, mgf1Buf, (short) inputLen);
+ md.doFinal(
+ mgf1Buf,
+ (short) 0,
+ (short) (4 + inputLen),
+ outBuf,
+ (short) (outOffset + (counter * digestLen)));
+ }
+
+ } finally {
+ if (md != null) {
+ md.close();
+ }
+ Util.arrayFillNonAtomic(mgf1Buf, (short) 0, (short) MGF1_BUF_SIZE, (byte) 0);
+ }
+ }
+
+ // Integer to Octet String conversion.
+ private void I2OS(short i, byte[] out, short offset) {
+ Util.arrayFillNonAtomic(out, (short) offset, (short) 4, (byte) 0);
+ out[(short) (offset + 3)] = (byte) (i >>> 0);
+ out[(short) (offset + 2)] = (byte) (i >>> 8);
+ }
+
+ private short rsaOAEPDecode(byte[] encodedMsg, short encodedMsgOff, short encodedMsgLen) {
+ MessageDigest.OneShot md = null;
+ byte[] tmpArray = KMAndroidSEProvider.getInstance().tmpArray;
+
+ try {
+ short hLen = getDigestLength();
+
+ if (encodedMsgLen < (short) (2 * hLen + 1)) {
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ // encodedMsg will be in the format of maskedSeed||maskedDB.
+ // maskedSeed length is hLen and maskedDB length is (encodedMsgLen - hLen)
+ // Now retrieve the seedMask by calling MGF(maskedDB, hLen). The length
+ // of the seedMask is hLen.
+ // seedMask = MGF(maskedDB, hLen)
+ maskGenerationFunction1(
+ encodedMsg,
+ (short) (encodedMsgOff + hLen),
+ (short) (encodedMsgLen - hLen),
+ hLen,
+ tmpArray,
+ (short) 0);
+
+ // Get the seed by doing XOR of (maskedSeed ^ seedMask).
+ // seed = (maskedSeed ^ seedMask)
+ for (short i = 0; i < hLen; i++) {
+ // Store the seed in encodeMsg itself.
+ encodedMsg[(short) (encodedMsgOff + i)] ^= tmpArray[i];
+ }
+
+ // Now get the dbMask by calling MGF(seed , (emLen-hLen)).
+ // dbMask = MGF(seed , (emLen-hLen)).
+ maskGenerationFunction1(
+ encodedMsg,
+ (short) encodedMsgOff,
+ hLen,
+ (short) (encodedMsgLen - hLen),
+ tmpArray,
+ (short) 0);
+
+ // Get the DB value. DB = (maskedDB ^ dbMask)
+ // DB = Hash(P)||00||01||Msg, where P is encoding parameters. (P = NULL)
+ for (short i = 0; i < (short) (encodedMsgLen - hLen); i++) {
+ // Store the DB inside encodeMsg itself.
+ encodedMsg[(short) (encodedMsgOff + i + hLen)] ^= tmpArray[i];
+ }
+
+ // Verify Hash.
+ md = MessageDigest.OneShot.open(hash);
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0);
+ md.doFinal(tmpArray, (short) 0, (short) 0, tmpArray, (short) 0);
+ if (0
+ != Util.arrayCompare(
+ encodedMsg, (short) (encodedMsgOff + hLen), tmpArray, (short) 0, hLen)) {
+ // Verification failed.
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+
+ // Find the Message block in DB.
+ // DB = Hash(P)||00||01||Msg, where P is encoding parameters. (P = NULL)
+ // The message will be located at the end of the Data block (DB).
+ // The DB block is first constructed by keeping the message at the end and
+ // to the message 0x01 byte is prepended. The hash of the
+ // encoding parameters is calculated and then copied from the
+ // starting of the block and a variable length of 0's are
+ // appended to the end of the hash till the 0x01 byte.
+ short start = (short) (encodedMsgOff + encodedMsgLen);
+ for (short i = (short) (encodedMsgOff + 2 * hLen);
+ i < (short) (encodedMsgOff + encodedMsgLen);
+ i++) {
+ if ((encodedMsg[i] != 0)) {
+ start = i;
+ break;
+ }
+ }
+ if ((start >= (short) (encodedMsgOff + encodedMsgLen)) || (encodedMsg[start] != 0x01)) {
+ // Bad Padding.
+ CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+ }
+ start++; // Message starting pos.
+ if (start < (short) (encodedMsgOff + encodedMsgLen)) {
+ // Copy the message
+ Util.arrayCopyNonAtomic(
+ encodedMsg,
+ start,
+ encodedMsg,
+ encodedMsgOff,
+ (short) (encodedMsgLen - (start - encodedMsgOff)));
+ }
+ return (short) (encodedMsgLen - (start - encodedMsgOff));
+
+ } finally {
+ if (md != null) {
+ md.close();
+ }
+ Util.arrayFillNonAtomic(tmpArray, (short) 0, KMAndroidSEProvider.TMP_ARRAY_SIZE, (byte) 0);
+ }
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java
new file mode 100644
index 0000000..76f45e4
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java
@@ -0,0 +1,780 @@
+/*
+ * 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 org.globalplatform.upgrade.Element;
+
+/**
+ * KMSEProvider is facade to use SE specific methods. The main intention of this interface is to
+ * abstract the cipher, signature and backup and restore related functions. The instance of this
+ * interface is created by the singleton KMSEProviderImpl class for each provider. At a time there
+ * can be only one provider in the applet package.
+ */
+public interface KMSEProvider {
+
+ /**
+ * This function tells if boot signal event is supported or not.
+ *
+ * @return true if supported, false otherwise.
+ */
+ boolean isBootSignalEventSupported();
+
+ /**
+ * This function tells if the device is booted or not.
+ *
+ * @return true if device booted, false otherwise.
+ */
+ boolean isDeviceRebooted();
+
+ /**
+ * This function is supposed to be used to reset the device booted stated after set boot param is
+ * handled
+ *
+ * @param resetBootFlag is false if event has been handled
+ */
+ void clearDeviceBooted(boolean resetBootFlag);
+
+ /**
+ * Create a symmetric key instance. If the algorithm and/or keysize are not supported then it
+ * should throw a CryptoException.
+ *
+ * @param alg will be KMType.AES, KMType.DES or KMType.HMAC.
+ * @param keysize will be 128 or 256 for AES or DES. It can be 64 to 512 (multiple of 8) for HMAC.
+ * @param buf is the buffer in which key has to be returned
+ * @param startOff is the start offset.
+ * @return length of the data in the buf. This should match the keysize (in bytes).
+ */
+ short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff);
+
+ /**
+ * Create a asymmetric key pair. If the algorithms are not supported then it should throw a
+ * CryptoException. For RSA the public key exponent must always be 0x010001. The key size of RSA
+ * key pair must be 2048 bits and key size of EC key pair must be for p256 curve.
+ *
+ * @param alg will be KMType.RSA or KMType.EC.
+ * @param privKeyBuf is the buffer to return the private key exponent in case of RSA or private
+ * key in case of EC.
+ * @param privKeyStart is the start offset.
+ * @param privKeyMaxLength is the maximum length of this private key buffer.
+ * @param pubModBuf is the buffer to return the modulus in case of RSA or public key in case of
+ * EC.
+ * @param pubModStart is the start of offset.
+ * @param pubModMaxLength is the maximum length of this public key buffer.
+ * @param lengths is the actual length of the key pair - lengths[0] should be private key and
+ * lengths[1] should be public key.
+ */
+ void createAsymmetricKey(
+ byte alg,
+ byte[] privKeyBuf,
+ short privKeyStart,
+ short privKeyMaxLength,
+ byte[] pubModBuf,
+ short pubModStart,
+ short pubModMaxLength,
+ short[] lengths);
+
+ /**
+ * Initializes the trusted confirmation operation.
+ *
+ * @param computedHmacKey Instance of the computed Hmac key.
+ * @return instance of KMOperation.
+ */
+ KMOperation initTrustedConfirmationSymmetricOperation(KMKey computedHmacKey);
+
+ /**
+ * Verify that the imported key is valid. If the algorithm and/or keysize are not supported then
+ * it should throw a CryptoException.
+ *
+ * @param alg will be KMType.AES, KMType.DES or KMType.HMAC.
+ * @param keysize will be 128 or 256 for AES or DES. It can be 64 to 512 (multiple of 8) for HMAC.
+ * @param buf is the buffer that contains the symmetric key.
+ * @param startOff is the start offset.
+ * @param length of the data in the buf. This should match the keysize (in bytes).
+ * @return true if the symmetric key is supported and valid.
+ */
+ boolean importSymmetricKey(byte alg, short keysize, byte[] buf, short startOff, short length);
+
+ /**
+ * Validate that the imported asymmetric key pair is valid. For RSA the public key exponent must
+ * always be 0x010001. The key size of RSA key pair must be 2048 bits and key size of EC key pair
+ * must be for p256 curve. If the algorithms are not supported then it should throw a
+ * CryptoException.
+ *
+ * @param alg will be KMType.RSA or KMType.EC.
+ * @param privKeyBuf is the buffer that contains the private key exponent in case of RSA or
+ * private key in case of EC.
+ * @param privKeyStart is the start offset.
+ * @param privKeyLength is the length of this private key buffer.
+ * @param pubModBuf is the buffer that contains the modulus in case of RSA or public key in case
+ * of EC.
+ * @param pubModStart is the start of offset.
+ * @param pubModLength is the length of this public key buffer.
+ * @return true if the key pair is supported and valid.
+ */
+ boolean importAsymmetricKey(
+ byte alg,
+ byte[] privKeyBuf,
+ short privKeyStart,
+ short privKeyLength,
+ byte[] pubModBuf,
+ short pubModStart,
+ short pubModLength);
+
+ /**
+ * This is a oneshot operation that generates random number of desired length.
+ *
+ * @param num is the buffer in which random number is returned to the applet.
+ * @param offset is start of the buffer.
+ * @param length indicates the size of buffer and desired length of random number in bytes.
+ */
+ void newRandomNumber(byte[] num, short offset, short length);
+
+ /**
+ * This is a oneshot operation that adds the entropy to the entropy pool. This operation
+ * corresponds to addRndEntropy command. This method may ignore the added entropy value if the SE
+ * provider does not support it.
+ *
+ * @param num is the buffer in which entropy value is given.
+ * @param offset is start of the buffer.
+ * @param length length of the buffer.
+ */
+ void addRngEntropy(byte[] num, short offset, short length);
+
+ /**
+ * This is a oneshot operation that generates and returns back a true random number.
+ *
+ * @param num is the buffer in which entropy value is returned.
+ * @param offset is start of the buffer.
+ * @param length length of the buffer.
+ */
+ void getTrueRandomNumber(byte[] num, short offset, short length);
+
+ /**
+ * This is a oneshot operation that performs encryption operation using AES GCM algorithm. It
+ * throws CryptoException if algorithm is not supported or if tag length is not equal to 16 or
+ * nonce length is not equal to 12.
+ *
+ * @param aesKey is the buffer that contains 128 bit or 256 bit aes key used to encrypt.
+ * @param aesKeyStart is the start in aes key buffer.
+ * @param aesKeyLen is the length of aes key buffer in bytes (16 or 32 bytes).
+ * @param data is the buffer that contains data to encrypt.
+ * @param dataStart is the start of the data buffer.
+ * @param dataLen is the length of the data buffer.
+ * @param encData is the buffer of the output encrypted data.
+ * @param encDataStart is the start of the encrypted data buffer.
+ * @param nonce is the buffer of nonce.
+ * @param nonceStart is the start of the nonce buffer.
+ * @param nonceLen is the length of the nonce buffer.
+ * @param authData is the authentication data buffer.
+ * @param authDataStart is the start of the authentication buffer.
+ * @param authDataLen is the length of the authentication buffer.
+ * @param authTag is the buffer to output authentication tag.
+ * @param authTagStart is the start of the buffer.
+ * @param authTagLen is the length of the buffer.
+ * @return length of the encrypted data.
+ */
+ short aesGCMEncrypt(
+ byte[] aesKey,
+ short aesKeyStart,
+ short aesKeyLen,
+ byte[] data,
+ short dataStart,
+ short dataLen,
+ byte[] encData,
+ short encDataStart,
+ byte[] nonce,
+ short nonceStart,
+ short nonceLen,
+ byte[] authData,
+ short authDataStart,
+ short authDataLen,
+ byte[] authTag,
+ short authTagStart,
+ short authTagLen);
+
+ /**
+ * This is a oneshot operation that performs decryption operation using AES GCM algorithm. It
+ * throws CryptoException if algorithm is not supported.
+ *
+ * @param aesKey is the buffer that contains 128 bit or 256 bit aes key used to encrypt.
+ * @param aesKeyStart is the start in aes key buffer.
+ * @param aesKeyLen is the length of aes key buffer in bytes (16 or 32 bytes).
+ * @param encData is the buffer of the input encrypted data.
+ * @param encDataStart is the start of the encrypted data buffer.
+ * @param encDataLen is the length of the data buffer.
+ * @param data is the buffer that contains output decrypted data.
+ * @param dataStart is the start of the data buffer.
+ * @param nonce is the buffer of nonce.
+ * @param nonceStart is the start of the nonce buffer.
+ * @param nonceLen is the length of the nonce buffer.
+ * @param authData is the authentication data buffer.
+ * @param authDataStart is the start of the authentication buffer.
+ * @param authDataLen is the length of the authentication buffer.
+ * @param authTag is the buffer to output authentication tag.
+ * @param authTagStart is the start of the buffer.
+ * @param authTagLen is the length of the buffer.
+ * @return true if the authentication is valid.
+ */
+ boolean aesGCMDecrypt(
+ byte[] aesKey,
+ short aesKeyStart,
+ short aesKeyLen,
+ byte[] encData,
+ short encDataStart,
+ short encDataLen,
+ byte[] data,
+ short dataStart,
+ byte[] nonce,
+ short nonceStart,
+ short nonceLen,
+ byte[] authData,
+ short authDataStart,
+ short authDataLen,
+ byte[] authTag,
+ short authTagStart,
+ short authTagLen);
+
+ /**
+ * This is a oneshot operation that performs key derivation function using cmac kdf (CKDF) as
+ * defined in android keymaster hal definition.
+ *
+ * @param hmacKey of pre-shared key.
+ * @param label is the label to be used for ckdf.
+ * @param labelStart is the start of label.
+ * @param labelLen is the length of the label.
+ * @param context is the context to be used for ckdf.
+ * @param contextStart is the start of the context
+ * @param contextLength is the length of the context
+ * @param key is the output buffer to return the derived key
+ * @param keyStart is the start of the output buffer.
+ * @return length of the derived key buffer in bytes.
+ */
+ short cmacKDF(
+ KMKey hmacKey,
+ byte[] label,
+ short labelStart,
+ short labelLen,
+ byte[] context,
+ short contextStart,
+ short contextLength,
+ byte[] key,
+ short keyStart);
+
+ /**
+ * This is a oneshot operation that signs the data using hmac algorithm.
+ *
+ * @param keyBuf is the buffer with hmac key.
+ * @param keyStart is the start of the buffer.
+ * @param keyLength is the length of the buffer which will be in bytes from 8 to 64.
+ * @param data is the buffer containing data to be signed.
+ * @param dataStart is the start of the data.
+ * @param dataLength is the length of the data.
+ * @param signature is the output signature buffer
+ * @param signatureStart is the start of the signature
+ * @return length of the signature buffer in bytes.
+ */
+ short hmacSign(
+ byte[] keyBuf,
+ short keyStart,
+ short keyLength,
+ byte[] data,
+ short dataStart,
+ short dataLength,
+ byte[] signature,
+ short signatureStart);
+
+ /**
+ * This is a oneshot operation that signs the data using hmac algorithm.
+ *
+ * @param hmacKey is the KMHmacKey.
+ * @param data is the buffer containing data to be signed.
+ * @param dataStart is the start of the data.
+ * @param dataLength is the length of the data.
+ * @param signature is the output signature buffer
+ * @param signatureStart is the start of the signature
+ * @return length of the signature buffer in bytes.
+ */
+ short hmacSign(
+ Object hmacKey,
+ byte[] data,
+ short dataStart,
+ short dataLength,
+ byte[] signature,
+ short signatureStart);
+
+ /**
+ * This is a oneshot operation that signs the data using hmac algorithm. This is used to derive
+ * the key, which is used to encrypt the keyblob.
+ *
+ * @param masterkey of masterkey.
+ * @param data is the buffer containing data to be signed.
+ * @param dataStart is the start of the data.
+ * @param dataLength is the length of the data.
+ * @param signature is the output signature buffer
+ * @param signatureStart is the start of the signature
+ * @return length of the signature buffer in bytes.
+ */
+ short hmacKDF(
+ KMKey masterkey,
+ byte[] data,
+ short dataStart,
+ short dataLength,
+ byte[] signature,
+ short signatureStart);
+
+ /**
+ * This is a oneshot operation that verifies the signature using hmac algorithm.
+ *
+ * @param keyBuf is the buffer with hmac key.
+ * @param keyStart is the start of the buffer.
+ * @param keyLength is the length of the buffer which will be in bytes from 8 to 64.
+ * @param data is the buffer containing data.
+ * @param dataStart is the start of the data.
+ * @param dataLength is the length of the data.
+ * @param signature is the signature buffer.
+ * @param signatureStart is the start of the signature buffer.
+ * @param signatureLen is the length of the signature buffer in bytes.
+ * @return true if the signature matches.
+ */
+ boolean hmacVerify(
+ KMKey hmacKey,
+ byte[] data,
+ short dataStart,
+ short dataLength,
+ byte[] signature,
+ short signatureStart,
+ short signatureLen);
+
+ /**
+ * This is a oneshot operation that decrypts the data using RSA algorithm with oaep256 padding.
+ * The public exponent is always 0x010001. It throws CryptoException if OAEP encoding validation
+ * fails.
+ *
+ * @param privExp is the private exponent (2048 bit) buffer.
+ * @param privExpStart is the start of the private exponent buffer.
+ * @param privExpLength is the length of the private exponent buffer in bytes.
+ * @param modBuffer is the modulus (2048 bit) buffer.
+ * @param modOff is the start of the modulus buffer.
+ * @param modLength is the length of the modulus buffer in bytes.
+ * @param inputDataBuf is the buffer of the input data.
+ * @param inputDataStart is the start of the input data buffer.
+ * @param inputDataLength is the length of the input data buffer in bytes.
+ * @param outputDataBuf is the output buffer that contains the decrypted data.
+ * @param outputDataStart is the start of the output data buffer.
+ * @return length of the decrypted data.
+ */
+ short rsaDecipherOAEP256(
+ byte[] privExp,
+ short privExpStart,
+ short privExpLength,
+ byte[] modBuffer,
+ short modOff,
+ short modLength,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart);
+
+ /**
+ * Implementation of HKDF as per RFC5869 https://datatracker.ietf.org/doc/html/rfc5869#section-2
+ *
+ * @param ikm is the buffer containing input key material.
+ * @param ikmOff is the start of the input key.
+ * @param ikmLen is the length of the input key.
+ * @param salt is the buffer containing the salt.
+ * @param saltOff is the start of the salt buffer.
+ * @param saltLen is the length of the salt buffer.
+ * @param info is the buffer containing the application specific information
+ * @param infoOff is the start of the info buffer.
+ * @param infoLen is the length of the info buffer.
+ * @param out is the output buffer.
+ * @param outOff is the start of the output buffer.
+ * @param outLen is the length of the expected out buffer.
+ * @return Length of the out buffer which is outLen.
+ */
+ short hkdf(
+ byte[] ikm,
+ short ikmOff,
+ short ikmLen,
+ byte[] salt,
+ short saltOff,
+ short saltLen,
+ byte[] info,
+ short infoOff,
+ short infoLen,
+ byte[] out,
+ short outOff,
+ short outLen);
+
+ /**
+ * This function performs ECDH key agreement and generates a secret.
+ *
+ * @param privKey is the buffer containing the private key from first party.
+ * @param privKeyOff is the offset of the private key buffer.
+ * @param privKeyLen is the length of the private key buffer.
+ * @param publicKey is the buffer containing the public key from second party.
+ * @param publicKeyOff is the offset of the public key buffer.
+ * @param publicKeyLen is the length of the public key buffer.
+ * @param secret is the output buffer.
+ * @param secretOff is the offset of the output buffer.
+ * @return The length of the secret.
+ */
+ short ecdhKeyAgreement(
+ byte[] privKey,
+ short privKeyOff,
+ short privKeyLen,
+ byte[] publicKey,
+ short publicKeyOff,
+ short publicKeyLen,
+ byte[] secret,
+ short secretOff);
+
+ /**
+ * This is a oneshort operation that verifies the data using EC public key
+ *
+ * @param pubKey is the public key buffer.
+ * @param pubKeyOffset is the start of the public key buffer.
+ * @param pubKeyLen is the length of the public key.
+ * @param inputDataBuf is the buffer of the input data.
+ * @param inputDataStart is the start of the input data buffer.
+ * @param inputDataLength is the length of the input data buffer in bytes.
+ * @param signatureDataBuf is the buffer the signature input data.
+ * @param signatureDataStart is the start of the signature input data.
+ * @param signatureDataLen is the length of the signature input data.
+ * @return true if verification is successful, otherwise false.
+ */
+ boolean ecVerify256(
+ byte[] pubKey,
+ short pubKeyOffset,
+ short pubKeyLen,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] signatureDataBuf,
+ short signatureDataStart,
+ short signatureDataLen);
+
+ /**
+ * This is a oneshot operation that signs the data using device unique key.
+ *
+ * @param ecPrivKey instance of KMECDeviceUniqueKey to sign the input data.
+ * @param inputDataBuf is the buffer of the input data.
+ * @param inputDataStart is the start of the input data buffer.
+ * @param inputDataLength is the length of the input data buffer in bytes.
+ * @param outputDataBuf is the output buffer that contains the signature.
+ * @param outputDataStart is the start of the output data buffer.
+ * @return length of the decrypted data.
+ */
+ short signWithDeviceUniqueKey(
+ KMKey deviceUniqueKey,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart);
+
+ short ecSign256(
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart);
+
+ short rsaSign256Pkcs1(
+ byte[] secret,
+ short secretStart,
+ short secretLength,
+ byte[] modBuf,
+ short modStart,
+ short modLength,
+ byte[] inputDataBuf,
+ short inputDataStart,
+ short inputDataLength,
+ byte[] outputDataBuf,
+ short outputDataStart);
+
+ /**
+ * This creates a persistent operation for signing, verify, encryption and decryption using HMAC,
+ * AES and DES algorithms when keymaster hal's beginOperation function is executed. The
+ * KMOperation instance can be reclaimed by the seProvider when KMOperation is finished or
+ * aborted. It throws CryptoException if algorithm is not supported.
+ *
+ * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be
+ * KMType.SIGN and KMType.VERIFY for HMAC algorithm
+ * @param alg is KMType.HMAC, KMType.AES or KMType.DES.
+ * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE.
+ * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES).
+ * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is
+ * 0.
+ * @param keyBuf is aes, des or hmac key buffer.
+ * @param keyStart is the start of the key buffer.
+ * @param keyLength is the length of the key buffer.
+ * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode)
+ * @param ivStart is the start of the iv buffer.
+ * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES
+ * with ECB mode.
+ * @param macLength is the mac length in case of signing operation for hmac algorithm.
+ * @return KMOperation instance.
+ */
+ KMOperation initSymmetricOperation(
+ byte purpose,
+ byte alg,
+ byte digest,
+ byte padding,
+ byte blockMode,
+ byte[] keyBuf,
+ short keyStart,
+ short keyLength,
+ byte[] ivBuf,
+ short ivStart,
+ short ivLength,
+ short macLength);
+
+ /**
+ * This creates a persistent operation for signing, verify, encryption and decryption using HMAC,
+ * AES and DES algorithms when keymaster hal's beginOperation function is executed. The
+ * KMOperation instance can be reclaimed by the seProvider when KMOperation is finished or
+ * aborted. It throws CryptoException if algorithm is not supported.
+ *
+ * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be
+ * KMType.SIGN and KMType.VERIFY for HMAC algorithm
+ * @param alg is KMType.HMAC, KMType.AES or KMType.DES.
+ * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE.
+ * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES).
+ * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is
+ * 0.
+ * @param key is a key object.
+ * @param interfaceType defines the type of key in the key object.
+ * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode)
+ * @param ivStart is the start of the iv buffer.
+ * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES
+ * with ECB mode.
+ * @param macLength is the mac length in case of signing operation for hmac algorithm.
+ * @param oneShot if true, creates oneshot operation.
+ * @return KMOperation instance.
+ */
+ KMOperation initSymmetricOperation(
+ byte purpose,
+ byte alg,
+ byte digest,
+ byte padding,
+ byte blockMode,
+ Object key,
+ byte interfaceType,
+ byte[] ivBuf,
+ short ivStart,
+ short ivLength,
+ short macLength,
+ boolean oneShot);
+
+ /**
+ * This function creates an Operation instance only for RKP module.
+ *
+ * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be
+ * KMType.SIGN and KMType.VERIFY for HMAC algorithm
+ * @param alg is KMType.HMAC, KMType.AES or KMType.DES.
+ * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE.
+ * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES).
+ * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is
+ * 0.
+ * @param keyBuf is aes, des or hmac key buffer.
+ * @param keyStart is the start of the key buffer.
+ * @param keyLength is the length of the key buffer.
+ * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode)
+ * @param ivStart is the start of the iv buffer.
+ * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES
+ * with ECB mode.
+ * @param macLength is the mac length in case of signing operation for hmac algorithm.
+ * @return KMOperation instance.
+ */
+ KMOperation getRkpOperation(
+ byte purpose,
+ byte alg,
+ byte digest,
+ byte padding,
+ byte blockMode,
+ KMKey ecPrivKey,
+ byte[] ivBuf,
+ short ivStart,
+ short ivLength,
+ short macLength);
+
+ /**
+ * This creates a persistent operation for signing, verify, encryption and decryption using RSA
+ * and EC algorithms when keymaster hal's beginOperation function is executed. For RSA the public
+ * exponent is always 0x0100101. For EC the curve is always p256. The KMOperation instance can be
+ * reclaimed by the seProvider when KMOperation is finished or aborted. It throws CryptoException
+ * if algorithm is not supported.
+ *
+ * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for RSA. It will be * KMType.SIGN and
+ * KMType.VERIFY for RSA and EC algorithms.
+ * @param alg is KMType.RSA or KMType.EC algorithms.
+ * @param padding is KMType.PADDING_NONE or KMType.RSA_OAEP, KMType.RSA_PKCS1_1_5_ENCRYPT,
+ * KMType.RSA_PKCS1_1_5_SIGN or KMType.RSA_PSS.
+ * @param digest is KMType.DIGEST_NONE or KMType.SHA2_256.
+ * @param mgfDigest is the MGF digest.
+ * @param privKeyBuf is the private key in case of EC or private key exponent is case of RSA.
+ * @param privKeyStart is the start of the private key.
+ * @param privKeyLength is the length of the private key.
+ * @param pubModBuf is the modulus (in case of RSA) or public key (in case of EC).
+ * @param pubModStart is the start of the modulus.
+ * @param pubModLength is the length of the modulus.
+ * @return KMOperation instance that can be executed.
+ */
+ KMOperation initAsymmetricOperation(
+ byte purpose,
+ byte alg,
+ byte padding,
+ byte digest,
+ byte mgfDigest,
+ byte[] privKeyBuf,
+ short privKeyStart,
+ short privKeyLength,
+ byte[] pubModBuf,
+ short pubModStart,
+ short pubModLength);
+
+ /**
+ * This function tells if applet is upgrading or not.
+ *
+ * @return true if upgrading, otherwise false.
+ */
+ boolean isUpgrading();
+
+ /**
+ * This function generates an AES Key of keySizeBits, which is used as an master key. This
+ * generated key is maintained by the SEProvider. This function should be called only once at the
+ * time of installation.
+ *
+ * @param instance of the masterkey.
+ * @param keySizeBits key size in bits.
+ * @return An instance of KMMasterKey.
+ */
+ KMKey createMasterKey(KMKey masterKey, short keySizeBits);
+
+ /**
+ * This function creates an HMACKey and initializes the key with the provided input key data.
+ *
+ * @param keyData buffer containing the key data.
+ * @param offset start of the buffer.
+ * @param length length of the buffer.
+ * @return An instance of the KMComputedHmacKey.
+ */
+ KMKey createComputedHmacKey(KMKey computedHmacKey, byte[] keyData, short offset, short length);
+
+ /** Returns true if factory provisioned attestation key is supported. */
+ boolean isAttestationKeyProvisioned();
+
+ /**
+ * Returns algorithm type of the attestation key. It can be KMType.EC or KMType.RSA if the
+ * attestation key is provisioned in the factory.
+ */
+ short getAttestationKeyAlgorithm();
+
+ /**
+ * Creates an ECKey instance and sets the public and private keys to it.
+ *
+ * @param testMode to indicate if current execution is for test or production.
+ * @param pubKey buffer containing the public key.
+ * @param pubKeyOff public key buffer start offset.
+ * @param pubKeyLen public key buffer length.
+ * @param privKey buffer containing the private key.
+ * @param privKeyOff private key buffer start offset.
+ * @param privKeyLen private key buffer length.
+ * @return instance of KMDeviceUniqueKey.
+ */
+ KMKey createRkpDeviceUniqueKeyPair(
+ KMKey key,
+ byte[] pubKey,
+ short pubKeyOff,
+ short pubKeyLen,
+ byte[] privKey,
+ short privKeyOff,
+ short privKeyLen);
+
+ /**
+ * This is a one-shot operation the does digest of the input mesage.
+ *
+ * @param inBuff input buffer to be digested.
+ * @param inOffset start offset of the input buffer.
+ * @param inLength length of the input buffer.
+ * @param outBuff is the output buffer that contains the digested data.
+ * @param outOffset start offset of the digested output buffer.
+ * @return length of the digested data.
+ */
+ short messageDigest256(
+ byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset);
+
+ /**
+ * This function generates a HMAC key from the provided key buffers.
+ *
+ * @param presharedKey instance of the presharedkey.
+ * @param key buffer containing the key data.
+ * @param offset start offset of the buffer.
+ * @param length is the length of the key.
+ * @return instance of KMPresharedKey.
+ */
+ KMKey createPreSharedKey(KMKey presharedKey, byte[] key, short offset, short length);
+
+ /**
+ * This function saves the key objects while upgrade.
+ *
+ * @param element instance of the Element class where the objects to be stored.
+ * @param interfaceType the type interface of the parent object.
+ * @param object instance of the object to be saved.
+ */
+ void onSave(Element element, byte interfaceType, Object object);
+
+ /**
+ * This function restores the the object from element instance.
+ *
+ * @param element instance of the Element class.
+ * @return restored object.
+ */
+ Object onRestore(Element element);
+
+ /**
+ * This function returns the count of the primitive bytes required to be stored by the
+ * implementation of the interface type.
+ *
+ * @param interfaceType type interface of the parent object.
+ * @return count of the primitive bytes.
+ */
+ short getBackupPrimitiveByteCount(byte interfaceType);
+
+ /**
+ * This function returns the object count required to be stored by the implementation of the
+ * interface type.
+ *
+ * @param interfaceType type interface of the parent object.
+ * @return count of the objects.
+ */
+ short getBackupObjectCount(byte interfaceType);
+
+ /**
+ * This function creates an HMACKey and initializes the key with the provided input key data.
+ *
+ * @param keyData buffer containing the key data.
+ * @param offset start of the buffer.
+ * @param length length of the buffer.
+ * @return An instance of the KMRkpMacKey.
+ */
+ KMKey createRkpMacKey(KMKey createComputedHmacKey, byte[] keyData, short offset, short length);
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java
new file mode 100644
index 0000000..82cbe26
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java
@@ -0,0 +1,79 @@
+/*
+ * 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" 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.javacard.seprovider;
+
+/**
+ * This class declares all types, tag types, and tag keys. It also establishes basic structure of
+ * any KMType i.e. struct{byte type, short length, value} where value can any of the KMType. Also,
+ * KMType refers to transient memory heap in the repository. Finally KMType's subtypes are singleton
+ * prototype objects which just cast the structure over contiguous memory buffer.
+ */
+public abstract class KMType {
+
+ public static final short INVALID_VALUE = (short) 0x8000;
+
+ // Algorithm Enum Tag key and values
+ public static final short ALGORITHM = 0x0002;
+ public static final byte RSA = 0x01;
+ public static final byte DES = 0x21;
+ public static final byte EC = 0x03;
+ public static final byte AES = 0x20;
+ public static final byte HMAC = (byte) 0x80;
+
+ // EcCurve Enum Tag key and values.
+ public static final short ECCURVE = 0x000A;
+ public static final byte P_224 = 0x00;
+ public static final byte P_256 = 0x01;
+ public static final byte P_384 = 0x02;
+ public static final byte P_521 = 0x03;
+
+ // Purpose
+ public static final short PURPOSE = 0x0001;
+ public static final byte ENCRYPT = 0x00;
+ public static final byte DECRYPT = 0x01;
+ public static final byte SIGN = 0x02;
+ public static final byte VERIFY = 0x03;
+ public static final byte DERIVE_KEY = 0x04;
+ public static final byte WRAP_KEY = 0x05;
+ public static final byte AGREE_KEY = 0x06;
+ public static final byte ATTEST_KEY = (byte) 0x07;
+ // Block mode
+ public static final short BLOCK_MODE = 0x0004;
+ public static final byte ECB = 0x01;
+ public static final byte CBC = 0x02;
+ public static final byte CTR = 0x03;
+ public static final byte GCM = 0x20;
+
+ // Digest
+ public static final short DIGEST = 0x0005;
+ public static final byte DIGEST_NONE = 0x00;
+ public static final byte MD5 = 0x01;
+ public static final byte SHA1 = 0x02;
+ public static final byte SHA2_224 = 0x03;
+ public static final byte SHA2_256 = 0x04;
+ public static final byte SHA2_384 = 0x05;
+ public static final byte SHA2_512 = 0x06;
+
+ // Padding mode
+ public static final short PADDING = 0x0006;
+ public static final byte PADDING_NONE = 0x01;
+ public static final byte RSA_OAEP = 0x02;
+ public static final byte RSA_PSS = 0x03;
+ public static final byte RSA_PKCS1_1_5_ENCRYPT = 0x04;
+ public static final byte RSA_PKCS1_1_5_SIGN = 0x05;
+ public static final byte PKCS7 = 0x40;
+}
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java
new file mode 100644
index 0000000..420b2c7
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java
@@ -0,0 +1,30 @@
+/*
+ * 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 org.globalplatform.upgrade.Element;
+
+/** This interface helps in storing and restoring the applet data during the applet upgrades. */
+public interface KMUpgradable {
+
+ void onSave(Element ele);
+
+ void onRestore(Element ele, short oldVersion, short currentVersion);
+
+ short getBackupPrimitiveByteCount();
+
+ short getBackupObjectCount();
+}
diff --git a/ready_se/google/keymint/KM300/Applet/README.md b/ready_se/google/keymint/KM300/Applet/README.md
new file mode 100644
index 0000000..17e2e49
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/README.md
@@ -0,0 +1,16 @@
+# JavaCardKeymaster Applet
+
+This directory contains the implementation of the Keymint 3.0
+interface, in the form of a JavaCard 3.0.5 applet which runs in a secure
+element. It must be deployed in conjuction with the associated HAL,
+which mediates between Android Keystore and this applet.
+
+# Supported Features!
+
+ - Keymint 3.0 supported functions for required VTS compliance.
+ - SharedSecret 1.0 supported functions for required VTS compliance.
+ - RemotelyProvisionedComponent 3.0 supported functions for required VTS compliance.
+
+# Not supported features
+ - Factory provisioned attestation key will not be supported in this applet.
+ - Limited usage keys will not be supported in this applet.
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMArray.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMArray.java
new file mode 100644
index 0000000..aa54d54
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMArray.java
@@ -0,0 +1,165 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMArray represents an array of KMTypes. Array is the sequence of elements of one or more sub
+ * types of KMType. It also acts as a vector of one subtype of KMTypes on the lines of class KMArray
+ * <subType>, where subType is subclass of KMType. Vector is the sequence of elements of one sub
+ * type e.g. KMType.BYTE_BLOB_TYPE. The KMArray instance maps to the CBOR type array. KMArray is a
+ * KMType and it further extends the value field in TLV_HEADER as ARRAY_HEADER struct{short subType;
+ * short length;} followed by sequence of short pointers to KMType instances. The subType can be 0
+ * if this is an array or subType is short KMType value e.g. KMType.BYTE_BLOB_TYPE if this is a
+ * vector of that sub type.
+ */
+public class KMArray extends KMType {
+
+ public static final short ANY_ARRAY_LENGTH = 0x1000;
+ private static final byte ARRAY_HEADER_SIZE = 4;
+ private static KMArray prototype;
+
+ private KMArray() {}
+
+ private static KMArray proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMArray();
+ }
+ KMType.instanceTable[KM_ARRAY_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp() {
+ short ptr = instance(ARRAY_TYPE, (short) ARRAY_HEADER_SIZE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.INVALID_VALUE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), ANY_ARRAY_LENGTH);
+ return ptr;
+ }
+
+ public static short exp(short type) {
+ short ptr = instance(ARRAY_TYPE, (short) ARRAY_HEADER_SIZE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), type);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), ANY_ARRAY_LENGTH);
+ return ptr;
+ }
+
+ public static short instance(short length) {
+ short ptr = KMType.instance(ARRAY_TYPE, (short) (ARRAY_HEADER_SIZE + (length * 2)));
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.INVALID_VALUE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), length);
+ return ptr;
+ }
+
+ public static short instance(short length, byte type) {
+ short ptr = instance(length);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), type);
+ return ptr;
+ }
+
+ public static KMArray cast(short ptr) {
+ if (heap[ptr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public void add(short index, short objPtr) {
+ short len = length();
+ if (index >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ Util.setShort(heap, (short) (getStartOff() + (short) (index * 2)), objPtr);
+ }
+
+ public short get(short index) {
+ short len = length();
+ if (index >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ return Util.getShort(heap, (short) (getStartOff() + (short) (index * 2)));
+ }
+
+ public void swap(short index1, short index2) {
+ short len = length();
+ if (index1 >= len || index2 >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ short indexPtr1 =
+ Util.getShort(
+ heap,
+ (short)
+ (instanceTable[KM_ARRAY_OFFSET]
+ + TLV_HEADER_SIZE
+ + ARRAY_HEADER_SIZE
+ + (short) (index1 * 2)));
+ short indexPtr2 =
+ Util.getShort(
+ heap,
+ (short)
+ (instanceTable[KM_ARRAY_OFFSET]
+ + TLV_HEADER_SIZE
+ + ARRAY_HEADER_SIZE
+ + (short) (index2 * 2)));
+ Util.setShort(
+ heap,
+ (short)
+ (instanceTable[KM_ARRAY_OFFSET]
+ + TLV_HEADER_SIZE
+ + ARRAY_HEADER_SIZE
+ + (short) (index1 * 2)),
+ indexPtr2);
+ Util.setShort(
+ heap,
+ (short)
+ (instanceTable[KM_ARRAY_OFFSET]
+ + TLV_HEADER_SIZE
+ + ARRAY_HEADER_SIZE
+ + (short) (index2 * 2)),
+ indexPtr1);
+ }
+
+ public short containedType() {
+ return Util.getShort(heap, (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short getStartOff() {
+ return (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE);
+ }
+
+ public short length() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short setLength(short len) {
+ return Util.setShort(
+ heap, (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2), len);
+ }
+
+ public byte[] getBuffer() {
+ return heap;
+ }
+
+ public void deleteLastEntry() {
+ short len = length();
+ Util.setShort(
+ heap, (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2), (short) (len - 1));
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMAsn1Parser.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMAsn1Parser.java
new file mode 100644
index 0000000..22a16a3
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMAsn1Parser.java
@@ -0,0 +1,471 @@
+package com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * This is a utility class that helps in parsing the PKCS8 encoded RSA and EC keys, certificate
+ * subject, subjectPublicKey info and ECDSA signatures.
+ */
+public class KMAsn1Parser {
+
+ // Below are the ASN.1 tag types
+ public static final byte ASN1_OCTET_STRING = 0x04;
+ public static final byte ASN1_SEQUENCE = 0x30;
+ public static final byte ASN1_SET = 0x31;
+ public static final byte ASN1_INTEGER = 0x02;
+ public static final byte OBJECT_IDENTIFIER = 0x06;
+ public static final byte ASN1_A0_TAG = (byte) 0xA0;
+ public static final byte ASN1_A1_TAG = (byte) 0xA1;
+ public static final byte ASN1_BIT_STRING = 0x03;
+
+ public static final byte ASN1_UTF8_STRING = 0x0C;
+ public static final byte ASN1_TELETEX_STRING = 0x14;
+ public static final byte ASN1_PRINTABLE_STRING = 0x13;
+ public static final byte ASN1_UNIVERSAL_STRING = 0x1C;
+ public static final byte ASN1_BMP_STRING = 0x1E;
+ public static final byte IA5_STRING = 0x16;
+ // OID of the EC P256 curve 1.2.840.10045.3.1.7
+ public static final byte[] EC_CURVE = {
+ 0x06, 0x08, 0x2a, (byte) 0x86, 0x48, (byte) 0xce, 0x3d, 0x03, 0x01, 0x07
+ };
+ // Constant for rsaEncryption pkcs#1 (1.2.840.113549.1.1.1) and NULL
+ public static final byte[] RSA_ALGORITHM = {
+ 0x06,
+ 0x09,
+ 0x2A,
+ (byte) 0x86,
+ 0x48,
+ (byte) 0x86,
+ (byte) 0xF7,
+ 0x0D,
+ 0x01,
+ 0x01,
+ 0x01,
+ 0x05,
+ 0x00
+ };
+ // Constant for ecPublicKey (1.2.840.10045.2.1) and prime256v1 (1.2.840.10045.3.1.7)
+ public static final byte[] EC_ALGORITHM = {
+ 0x06,
+ 0x07,
+ 0x2a,
+ (byte) 0x86,
+ 0x48,
+ (byte) 0xce,
+ 0x3d,
+ 0x02,
+ 0x01,
+ 0x06,
+ 0x08,
+ 0x2a,
+ (byte) 0x86,
+ 0x48,
+ (byte) 0xce,
+ 0x3d,
+ 0x03,
+ 0x01,
+ 0x07
+ };
+ // The maximum length of email id attribute.
+ public static final short MAX_EMAIL_ADD_LEN = 255;
+ // Datatable offsets.
+ private static final byte DATA_START_OFFSET = 0;
+ private static final byte DATA_LENGTH_OFFSET = 1;
+ private static final byte DATA_CURSOR_OFFSET = 2;
+ // This array contains the last byte of OID for each oid type.
+ // The first 4 bytes are common as shown above in COMMON_OID
+ private static final byte[] attributeOIds = {
+ 0x03, /* commonName COMMON_OID.3 */ 0x04, /* surName COMMON_OID.4*/
+ 0x05, /* serialNumber COMMON_OID.5 */ 0x06, /* countryName COMMON_OID.6 */
+ 0x07, /* locality COMMON_OID.7 */ 0x08, /* stateOrProviince COMMON_OID.8 */
+ 0x0A, /* organizationName COMMON_OID.10 */ 0x0B, /* organizationalUnitName COMMON_OID.11 */
+ 0x0C, /* title COMMON_OID.10 */ 0x29, /* name COMMON_OID.41 */
+ 0x2A, /* givenName COMMON_OID.42 */ 0x2B, /* initials COMMON_OID.43 */
+ 0x2C, /* generationQualifier COMMON_OID.44 */ 0x2E, /* dnQualifer COMMON_OID.46 */
+ 0x41, /* pseudonym COMMON_OID.65 */
+ };
+ // https://datatracker.ietf.org/doc/html/rfc5280, RFC 5280, Page 124
+ // TODO Specification does not mention about the DN_QUALIFIER_OID max length.
+ // So the max limit is set at 64.
+ // For name the RFC 5280 supports up to 32768, as Javacard doesn't support
+ // that much length, the max limit for name is set to 128.
+ private static final byte[] attributeValueMaxLen = {
+ 0x40, /* 1-64 commonName */
+ 0x28, /* 1-40 surname */
+ 0x40, /* 1-64 serial */
+ 0x02, /* 1-2 country */
+ (byte) 0x80, /* 1-128 locality */
+ (byte) 0x80, /* 1-128 state */
+ 0x40, /* 1-64 organization */
+ 0x40, /* 1-64 organization unit*/
+ 0x40, /* 1-64 title */
+ 0x29, /* 1-128 name */
+ 0x10, /* 1-16 givenName */
+ 0x05, /* 1-5 initials */
+ 0x03, /* 1-3 gen qualifier */
+ 0x40, /* 1-64 dn-qualifier */
+ (byte) 0x80 /* 1-128 pseudonym */
+ };
+ private static KMAsn1Parser inst;
+ // https://datatracker.ietf.org/doc/html/rfc5280, RFC 5280, Page 21
+ // 2.5.4
+ public byte[] COMMON_OID = new byte[] {0x06, 0x03, 0x55, 0x04};
+ public byte[] EMAIL_ADDRESS_OID =
+ new byte[] {
+ 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x09, 0x01
+ };
+ private byte[] data;
+ private short[] dataInfo;
+
+ private KMAsn1Parser() {
+ dataInfo = JCSystem.makeTransientShortArray((short) 3, JCSystem.CLEAR_ON_RESET);
+ dataInfo[DATA_START_OFFSET] = 0;
+ dataInfo[DATA_LENGTH_OFFSET] = 0;
+ dataInfo[DATA_CURSOR_OFFSET] = 0;
+ }
+
+ public static KMAsn1Parser instance() {
+ if (inst == null) {
+ inst = new KMAsn1Parser();
+ }
+ return inst;
+ }
+
+ public short decodeRsa(short blob) {
+ init(blob);
+ decodeCommon((short) 0, RSA_ALGORITHM);
+ return decodeRsaPrivateKey((short) 0);
+ }
+
+ public short decodeEc(short blob) {
+ init(blob);
+ decodeCommon((short) 0, EC_ALGORITHM);
+ return decodeEcPrivateKey((short) 1);
+ }
+
+ /*
+ Name ::= CHOICE { -- only one possibility for now --
+ rdnSequence RDNSequence }
+ RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+ RelativeDistinguishedName ::=
+ SET SIZE (1..MAX) OF AttributeTypeAndValue
+ AttributeTypeAndValue ::= SEQUENCE {
+ type AttributeType,
+ value AttributeValue }
+ AttributeType ::= OBJECT IDENTIFIER
+ AttributeValue ::= ANY -- DEFINED BY AttributeType
+ */
+ public void validateDerSubject(short blob) {
+ init(blob);
+ header(ASN1_SEQUENCE);
+ while (dataInfo[DATA_CURSOR_OFFSET]
+ < ((short) (dataInfo[DATA_START_OFFSET] + dataInfo[DATA_LENGTH_OFFSET]))) {
+ header(ASN1_SET);
+ header(ASN1_SEQUENCE);
+ // Parse and validate OBJECT-IDENTIFIER and Value fields
+ // Cursor is incremented in validateAttributeTypeAndValue.
+ validateAttributeTypeAndValue();
+ }
+ }
+
+ public short decodeEcSubjectPublicKeyInfo(short blob) {
+ init(blob);
+ header(ASN1_SEQUENCE);
+ short len = header(ASN1_SEQUENCE);
+ short ecPublicInfo = KMByteBlob.instance(len);
+ getBytes(ecPublicInfo);
+ if (Util.arrayCompare(
+ KMByteBlob.cast(ecPublicInfo).getBuffer(),
+ KMByteBlob.cast(ecPublicInfo).getStartOff(),
+ EC_ALGORITHM,
+ (short) 0,
+ KMByteBlob.cast(ecPublicInfo).length())
+ != 0) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ len = header(ASN1_BIT_STRING);
+ if (len < 1) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ // TODO need to handle if unused bits are not zero
+ byte unusedBits = getByte();
+ if (unusedBits != 0) {
+ KMException.throwIt(KMError.UNIMPLEMENTED);
+ }
+ short pubKey = KMByteBlob.instance((short) (len - 1));
+ getBytes(pubKey);
+ return pubKey;
+ }
+
+ // Seq[Int,Int,Int,Int,<ignore rest>]
+ public short decodeRsaPrivateKey(short version) {
+ short resp = KMArray.instance((short) 3);
+ header(ASN1_OCTET_STRING);
+ header(ASN1_SEQUENCE);
+ short len = header(ASN1_INTEGER);
+ if (len != 1) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ short ver = getByte();
+ if (ver != version) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ len = header(ASN1_INTEGER);
+ short modulus = KMByteBlob.instance(len);
+ getBytes(modulus);
+ updateRsaKeyBuffer(modulus);
+ len = header(ASN1_INTEGER);
+ short pubKey = KMByteBlob.instance(len);
+ getBytes(pubKey);
+ len = header(ASN1_INTEGER);
+ short privKey = KMByteBlob.instance(len);
+ getBytes(privKey);
+ updateRsaKeyBuffer(privKey);
+ KMArray.cast(resp).add((short) 0, modulus);
+ KMArray.cast(resp).add((short) 1, pubKey);
+ KMArray.cast(resp).add((short) 2, privKey);
+ return resp;
+ }
+
+ private void updateRsaKeyBuffer(short blob) {
+ byte[] buffer = KMByteBlob.cast(blob).getBuffer();
+ short startOff = KMByteBlob.cast(blob).getStartOff();
+ short len = KMByteBlob.cast(blob).length();
+ if (0 == buffer[startOff] && len > 256) {
+ KMByteBlob.cast(blob).setStartOff(++startOff);
+ KMByteBlob.cast(blob).setLength(--len);
+ }
+ }
+
+ private short readEcdsa256SigIntegerHeader() {
+ short len = header(ASN1_INTEGER);
+ if (len == 33) {
+ if (0 != getByte()) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ len--;
+ } else if (len > 33) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return len;
+ }
+
+ // Seq [Int, Int]
+ public short decodeEcdsa256Signature(short blob, byte[] scratchPad, short scratchPadOff) {
+ init(blob);
+ short len = header(ASN1_SEQUENCE);
+ len = readEcdsa256SigIntegerHeader();
+ // concatenate r and s in the buffer (r||s)
+ Util.arrayFillNonAtomic(scratchPad, scratchPadOff, (short) 64, (byte) 0);
+ // read r
+ getBytes(scratchPad, (short) (scratchPadOff + 32 - len), len);
+ len = readEcdsa256SigIntegerHeader();
+ // read s
+ getBytes(scratchPad, (short) (scratchPadOff + 64 - len), len);
+ return (short) 64;
+ }
+
+ // Seq [Int, Blob]
+ public void decodeCommon(short version, byte[] alg) {
+ short len = header(ASN1_SEQUENCE);
+ len = header(ASN1_INTEGER);
+ if (len != 1) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ short ver = getByte();
+ if (ver != version) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ len = header(ASN1_SEQUENCE);
+ short blob = KMByteBlob.instance(len);
+ getBytes(blob);
+ if (Util.arrayCompare(
+ KMByteBlob.cast(blob).getBuffer(),
+ KMByteBlob.cast(blob).getStartOff(),
+ alg,
+ (short) 0,
+ KMByteBlob.cast(blob).length())
+ != 0) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ }
+
+ // Seq[Int,blob,blob]
+ public short decodeEcPrivateKey(short version) {
+ short resp = KMArray.instance((short) 2);
+ header(ASN1_OCTET_STRING);
+ header(ASN1_SEQUENCE);
+ short len = header(ASN1_INTEGER);
+ if (len != 1) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ short ver = getByte();
+ if (ver != version) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ len = header(ASN1_OCTET_STRING);
+ short privKey = KMByteBlob.instance(len);
+ getBytes(privKey);
+ validateTag0IfPresent();
+ header(ASN1_A1_TAG);
+ len = header(ASN1_BIT_STRING);
+ if (len < 1) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ // TODO need to handle if unused bits are not zero
+ byte unusedBits = getByte();
+ if (unusedBits != 0) {
+ KMException.throwIt(KMError.UNIMPLEMENTED);
+ }
+ short pubKey = KMByteBlob.instance((short) (len - 1));
+ getBytes(pubKey);
+ KMArray.cast(resp).add((short) 0, pubKey);
+ KMArray.cast(resp).add((short) 1, privKey);
+ return resp;
+ }
+
+ private void validateTag0IfPresent() {
+ if (data[dataInfo[DATA_CURSOR_OFFSET]] != ASN1_A0_TAG) {
+ return;
+ }
+ ;
+ short len = header(ASN1_A0_TAG);
+ if (len != EC_CURVE.length) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ if (Util.arrayCompare(data, dataInfo[DATA_CURSOR_OFFSET], EC_CURVE, (short) 0, len) != 0) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ incrementCursor(len);
+ }
+
+ private void validateAttributeTypeAndValue() {
+ // First byte should be OBJECT_IDENTIFIER, otherwise it is not well-formed DER Subject.
+ if (data[dataInfo[DATA_CURSOR_OFFSET]] != OBJECT_IDENTIFIER) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ // Check if the OID matches the email address
+ if ((Util.arrayCompare(
+ data,
+ dataInfo[DATA_CURSOR_OFFSET],
+ EMAIL_ADDRESS_OID,
+ (short) 0,
+ (short) EMAIL_ADDRESS_OID.length)
+ == 0)) {
+ incrementCursor((short) EMAIL_ADDRESS_OID.length);
+ // Validate the length of the attribute value.
+ if (getByte() != IA5_STRING) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ short emailLength = getLength();
+ if (emailLength <= 0 && emailLength > MAX_EMAIL_ADD_LEN) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ incrementCursor(emailLength);
+ return;
+ }
+ // Check other OIDs.
+ for (short i = 0; i < (short) attributeOIds.length; i++) {
+ if ((Util.arrayCompare(
+ data,
+ dataInfo[DATA_CURSOR_OFFSET],
+ COMMON_OID,
+ (short) 0,
+ (short) COMMON_OID.length)
+ == 0)
+ && (attributeOIds[i]
+ == data[(short) (dataInfo[DATA_CURSOR_OFFSET] + COMMON_OID.length)])) {
+ incrementCursor((short) (COMMON_OID.length + 1));
+ // Validate the length of the attribute value.
+ short tag = getByte();
+ if (tag != ASN1_UTF8_STRING
+ && tag != ASN1_TELETEX_STRING
+ && tag != ASN1_PRINTABLE_STRING
+ && tag != ASN1_UNIVERSAL_STRING
+ && tag != ASN1_BMP_STRING) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ short attrValueLength = getLength();
+ if (attrValueLength <= 0 && attrValueLength > attributeValueMaxLen[i]) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ incrementCursor(attrValueLength);
+ return;
+ }
+ }
+ // If no match is found above then move the cursor to next element.
+ getByte(); // Move Cursor by one byte (OID)
+ incrementCursor(getLength()); // Move cursor to AtrributeTag
+ getByte(); // Move cursor to AttributeValue
+ incrementCursor(getLength()); // Move cursor to next SET element
+ }
+
+ private short header(short tag) {
+ short t = getByte();
+ if (t != tag) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ return getLength();
+ }
+
+ private byte getByte() {
+ byte d = data[dataInfo[DATA_CURSOR_OFFSET]];
+ incrementCursor((short) 1);
+ return d;
+ }
+
+ private short getShort() {
+ short d = Util.getShort(data, dataInfo[DATA_CURSOR_OFFSET]);
+ incrementCursor((short) 2);
+ return d;
+ }
+
+ private void getBytes(short blob) {
+ short len = KMByteBlob.cast(blob).length();
+ Util.arrayCopyNonAtomic(
+ data,
+ dataInfo[DATA_CURSOR_OFFSET],
+ KMByteBlob.cast(blob).getBuffer(),
+ KMByteBlob.cast(blob).getStartOff(),
+ len);
+ incrementCursor(len);
+ }
+
+ private void getBytes(byte[] buffer, short offset, short len) {
+ Util.arrayCopyNonAtomic(data, dataInfo[DATA_CURSOR_OFFSET], buffer, offset, len);
+ incrementCursor(len);
+ }
+
+ private short getLength() {
+ byte len = getByte();
+ if (len >= 0) {
+ return len;
+ }
+ len = (byte) (len & 0x7F);
+ if (len == 1) {
+ return (short) (getByte() & 0xFF);
+ } else if (len == 2) {
+ return getShort();
+ } else {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ return KMType.INVALID_VALUE; // should not come here
+ }
+
+ public void init(short blob) {
+ data = KMByteBlob.cast(blob).getBuffer();
+ dataInfo[DATA_START_OFFSET] = KMByteBlob.cast(blob).getStartOff();
+ dataInfo[DATA_LENGTH_OFFSET] = KMByteBlob.cast(blob).length();
+ dataInfo[DATA_CURSOR_OFFSET] = dataInfo[DATA_START_OFFSET];
+ }
+
+ public void incrementCursor(short n) {
+ dataInfo[DATA_CURSOR_OFFSET] += n;
+ if (dataInfo[DATA_CURSOR_OFFSET]
+ > ((short) (dataInfo[DATA_START_OFFSET] + dataInfo[DATA_LENGTH_OFFSET]))) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMBignumTag.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMBignumTag.java
new file mode 100644
index 0000000..5eb7eae
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMBignumTag.java
@@ -0,0 +1,110 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMBignumTag represents BIGNUM Tag Type from android keymaster hal specifications. The tag value
+ * of this tag is the KMByteBlob pointer i.e. offset of KMByteBlob in memory heap. struct{byte
+ * TAG_TYPE; short length; struct{short BIGNUM_TAG; short tagKey; short blobPtr}}
+ */
+public class KMBignumTag extends KMTag {
+
+ private static KMBignumTag prototype;
+
+ private KMBignumTag() {}
+
+ private static KMBignumTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMBignumTag();
+ }
+ KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short blobPtr = KMByteBlob.exp();
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BIGNUM_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), blobPtr);
+ return ptr;
+ }
+
+ public static short instance(short key, short byteBlob) {
+ if (!validateKey(key, byteBlob)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (heap[byteBlob] != BYTE_BLOB_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BIGNUM_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), byteBlob);
+ return ptr;
+ }
+
+ public static KMBignumTag cast(short ptr) {
+ if (heap[ptr] != TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BIGNUM_TAG) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ private static boolean validateKey(short key, short byteBlob) {
+ short valueLen = KMByteBlob.cast(byteBlob).length();
+ switch (key) {
+ case CERTIFICATE_SERIAL_NUM:
+ if (valueLen > MAX_CERTIFICATE_SERIAL_SIZE) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ public short getKey() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short getTagType() {
+ return KMType.BIGNUM_TAG;
+ }
+
+ public short getValue() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+
+ public short length() {
+ short blobPtr =
+ Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ return KMByteBlob.cast(blobPtr).length();
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMBoolTag.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMBoolTag.java
new file mode 100644
index 0000000..27730a5
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMBoolTag.java
@@ -0,0 +1,115 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMBoolTag represents BOOL TAG type from the android keymaster hal specifications. If it is
+ * present in the key parameter list then its value is always true. A KMTag always requires a value
+ * because it is a key value pair. The bool tag always has 0x01 as its value. struct{byte TAG_TYPE;
+ * short length; struct{short BOOL_TAG; short tagKey; byte value 1}}
+ */
+public class KMBoolTag extends KMTag {
+
+ // The allowed tag keys of type bool tag.
+ private static final short[] tags = {
+ CALLER_NONCE,
+ INCLUDE_UNIQUE_ID,
+ BOOTLOADER_ONLY,
+ ROLLBACK_RESISTANCE,
+ NO_AUTH_REQUIRED,
+ ALLOW_WHILE_ON_BODY,
+ TRUSTED_USER_PRESENCE_REQUIRED,
+ TRUSTED_CONFIRMATION_REQUIRED,
+ UNLOCKED_DEVICE_REQUIRED,
+ RESET_SINCE_ID_ROTATION,
+ EARLY_BOOT_ONLY,
+ DEVICE_UNIQUE_ATTESTATION
+ };
+ private static KMBoolTag prototype;
+
+ private KMBoolTag() {}
+
+ private static KMBoolTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMBoolTag();
+ }
+ KMType.instanceTable[KM_BOOL_TAG_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short ptr = instance(TAG_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BOOL_TAG);
+ return ptr;
+ }
+
+ public static short instance(short key) {
+ if (!validateKey(key)) {
+ KMException.throwIt(KMError.INVALID_TAG);
+ }
+ short ptr = KMType.instance(TAG_TYPE, (short) 5);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BOOL_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+ // Value is always 1.
+ heap[(short) (ptr + TLV_HEADER_SIZE + 4)] = 0x01;
+ return ptr;
+ }
+
+ public static KMBoolTag cast(short ptr) {
+ if (heap[ptr] != TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BOOL_TAG) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ // validate the tag key.
+ private static boolean validateKey(short key) {
+ short index = (short) tags.length;
+ while (--index >= 0) {
+ if (tags[index] == key) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static short[] getTags() {
+ return tags;
+ }
+
+ public short getKey() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_BOOL_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short getTagType() {
+ return KMType.BOOL_TAG;
+ }
+
+ public byte getVal() {
+ return heap[(short) (KMType.instanceTable[KM_BOOL_TAG_OFFSET] + TLV_HEADER_SIZE + 4)];
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMByteBlob.java
new file mode 100644
index 0000000..98d81fc
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMByteBlob.java
@@ -0,0 +1,142 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMByteBlob represents contiguous block of bytes. It corresponds to CBOR type of Byte String. It
+ * extends KMType by specifying value field as zero or more sequence of bytes. struct{byte
+ * BYTE_BLOB_TYPE; short length; sequence of bytes}
+ */
+public class KMByteBlob extends KMType {
+
+ private static byte OFFSET_SIZE = 2;
+ private static KMByteBlob prototype;
+
+ protected KMByteBlob() {}
+
+ private static KMByteBlob proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMByteBlob();
+ }
+ KMType.instanceTable[KM_BYTE_BLOB_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ return KMType.exp(BYTE_BLOB_TYPE);
+ }
+
+ // return an empty byte blob instance
+ public static short instance(short length) {
+ short ptr = KMType.instance(BYTE_BLOB_TYPE, (short) (length + 2));
+ Util.setShort(
+ heap, (short) (ptr + TLV_HEADER_SIZE), (short) (ptr + TLV_HEADER_SIZE + OFFSET_SIZE));
+ Util.setShort(heap, (short) (ptr + 1), length);
+ return ptr;
+ }
+
+ // byte blob from existing buf
+ public static short instance(byte[] buf, short startOff, short length) {
+ short ptr = instance(length);
+ Util.arrayCopyNonAtomic(
+ buf, startOff, heap, (short) (ptr + TLV_HEADER_SIZE + OFFSET_SIZE), length);
+ return ptr;
+ }
+
+ // cast the ptr to KMByteBlob
+ public static KMByteBlob cast(short ptr) {
+ if (heap[ptr] != BYTE_BLOB_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ // Add the byte
+ public void add(short index, byte val) {
+ short len = length();
+ if (index >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ heap[(short) (getStartOff() + index)] = val;
+ }
+
+ // Get the byte
+ public byte get(short index) {
+ short len = length();
+ if (index >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ return heap[(short) (getStartOff() + index)];
+ }
+
+ // Get the start of blob
+ public short getStartOff() {
+ return Util.getShort(heap, (short) (getBaseOffset() + TLV_HEADER_SIZE));
+ }
+
+ public void setStartOff(short offset) {
+ Util.setShort(heap, (short) (getBaseOffset() + TLV_HEADER_SIZE), offset);
+ }
+
+ // Get the length of the blob
+ public short length() {
+ return Util.getShort(heap, (short) (getBaseOffset() + 1));
+ }
+
+ // Get the buffer pointer in which blob is contained.
+ public byte[] getBuffer() {
+ return heap;
+ }
+
+ public void getValue(byte[] destBuf, short destStart, short destLength) {
+ Util.arrayCopyNonAtomic(heap, getStartOff(), destBuf, destStart, destLength);
+ }
+
+ public short getValues(byte[] destBuf, short destStart) {
+ short destLength = length();
+ Util.arrayCopyNonAtomic(heap, getStartOff(), destBuf, destStart, destLength);
+ return destLength;
+ }
+
+ public void setValue(byte[] srcBuf, short srcStart, short srcLength) {
+ if (length() < srcLength) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ Util.arrayCopyNonAtomic(srcBuf, srcStart, heap, getStartOff(), srcLength);
+ setLength(srcLength);
+ }
+
+ public boolean isValid() {
+ return (length() != 0);
+ }
+
+ protected short getBaseOffset() {
+ return instanceTable[KM_BYTE_BLOB_OFFSET];
+ }
+
+ public void setLength(short len) {
+ Util.setShort(heap, (short) (getBaseOffset() + 1), len);
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMByteTag.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMByteTag.java
new file mode 100644
index 0000000..5f7231f
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMByteTag.java
@@ -0,0 +1,146 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMByteTag represents BYTES Tag Type from android keymaster hal specifications. The tag value of
+ * this tag is the KMByteBlob pointer i.e. offset of KMByteBlob in memory heap. struct{byte
+ * TAG_TYPE; short length; struct{short BYTES_TAG; short tagKey; short blobPtr}}
+ */
+public class KMByteTag extends KMTag {
+
+ private static KMByteTag prototype;
+
+ private KMByteTag() {}
+
+ private static KMByteTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMByteTag();
+ }
+ KMType.instanceTable[KM_BYTE_TAG_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short blobPtr = KMByteBlob.exp();
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BYTES_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), blobPtr);
+ return ptr;
+ }
+
+ public static short instance(short key, short byteBlob) {
+ if (!validateKey(key, byteBlob)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (heap[byteBlob] != BYTE_BLOB_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BYTES_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), byteBlob);
+ return ptr;
+ }
+
+ public static KMByteTag cast(short ptr) {
+ if (heap[ptr] != TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BYTES_TAG) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ private static boolean validateKey(short key, short byteBlob) {
+ short valueLen = KMByteBlob.cast(byteBlob).length();
+ switch (key) {
+ case ATTESTATION_APPLICATION_ID:
+ if (valueLen > MAX_ATTESTATION_APP_ID_SIZE) {
+ return false;
+ }
+ break;
+ case CERTIFICATE_SUBJECT_NAME:
+ {
+ if (valueLen > KMConfigurations.MAX_SUBJECT_DER_LEN) {
+ return false;
+ }
+ KMAsn1Parser asn1Decoder = KMAsn1Parser.instance();
+ asn1Decoder.validateDerSubject(byteBlob);
+ }
+ break;
+ case APPLICATION_ID:
+ case APPLICATION_DATA:
+ if (valueLen > MAX_APP_ID_APP_DATA_SIZE) {
+ return false;
+ }
+ break;
+ case ATTESTATION_CHALLENGE:
+ if (valueLen > MAX_ATTESTATION_CHALLENGE_SIZE) {
+ return false;
+ }
+ break;
+ case ATTESTATION_ID_BRAND:
+ case ATTESTATION_ID_DEVICE:
+ case ATTESTATION_ID_PRODUCT:
+ case ATTESTATION_ID_SERIAL:
+ case ATTESTATION_ID_IMEI:
+ case ATTESTATION_ID_SECOND_IMEI:
+ case ATTESTATION_ID_MEID:
+ case ATTESTATION_ID_MANUFACTURER:
+ case ATTESTATION_ID_MODEL:
+ if (valueLen > KMConfigurations.MAX_ATTESTATION_IDS_SIZE) {
+ return false;
+ }
+ break;
+ case ROOT_OF_TRUST:
+ case NONCE:
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ public short getKey() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short getTagType() {
+ return KMType.BYTES_TAG;
+ }
+
+ public short getValue() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+
+ public short length() {
+ short blobPtr =
+ Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ return KMByteBlob.cast(blobPtr).length();
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCose.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCose.java
new file mode 100644
index 0000000..bcfb5f8
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCose.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+
+/**
+ * This class constructs the Cose messages like CoseKey, CoseMac0, MacStructure, CoseSign1,
+ * SignStructure, CoseEncrypt, EncryptStructure and ReceipientStructures.
+ */
+public class KMCose {
+
+ // COSE SIGN1
+ public static final byte COSE_SIGN1_ENTRY_COUNT = 4;
+ public static final byte COSE_SIGN1_PROTECTED_PARAMS_OFFSET = 0;
+ public static final byte COSE_SIGN1_PAYLOAD_OFFSET = 2;
+ public static final byte COSE_SIGN1_SIGNATURE_OFFSET = 3;
+ // COSE MAC0
+ public static final byte COSE_MAC0_ENTRY_COUNT = 4;
+ public static final byte COSE_MAC0_PROTECTED_PARAMS_OFFSET = 0;
+ public static final byte COSE_MAC0_PAYLOAD_OFFSET = 2;
+ public static final byte COSE_MAC0_TAG_OFFSET = 3;
+ // COSE ENCRYPT
+ public static final byte COSE_ENCRYPT_ENTRY_COUNT = 4;
+ public static final byte COSE_ENCRYPT_STRUCTURE_ENTRY_COUNT = 3;
+ public static final byte COSE_ENCRYPT_RECIPIENT_ENTRY_COUNT = 3;
+
+ // COSE Labels
+ public static final byte COSE_LABEL_ALGORITHM = 1;
+ public static final byte COSE_LABEL_KEYID = 4;
+ public static final byte COSE_LABEL_IV = 5;
+ public static final byte COSE_LABEL_COSE_KEY = (byte) 0xFF; // -1
+
+ // COSE Algorithms
+ public static final byte COSE_ALG_AES_GCM_256 = 3; // AES-GCM mode w/ 256-bit key, 128-bit tag.
+ public static final byte COSE_ALG_HMAC_256 = 5; // HMAC w/ SHA-256
+ public static final byte COSE_ALG_ES256 = (byte) 0xF9; // ECDSA w/ SHA-256; -7
+ public static final byte COSE_ALG_ECDH_ES_HKDF_256 = (byte) 0xE7; // ECDH-EC+HKDF-256; -25
+
+ // COSE P256 EC Curve
+ public static final byte COSE_ECCURVE_256 = 1;
+
+ // COSE key types
+ public static final byte COSE_KEY_TYPE_EC2 = 2;
+ public static final byte COSE_KEY_TYPE_SYMMETRIC_KEY = 4;
+
+ // COSE Key Operations
+ public static final byte COSE_KEY_OP_SIGN = 1;
+ public static final byte COSE_KEY_OP_VERIFY = 2;
+ public static final byte COSE_KEY_OP_ENCRYPT = 3;
+ public static final byte COSE_KEY_OP_DECRYPT = 4;
+
+ // AES GCM
+ public static final short AES_GCM_KEY_SIZE_BITS = 256;
+ // Cose key parameters.
+ public static final byte COSE_KEY_KEY_TYPE = 1;
+ public static final byte COSE_KEY_KEY_ID = 2;
+ public static final byte COSE_KEY_ALGORITHM = 3;
+ public static final byte COSE_KEY_CURVE = -1;
+ public static final byte COSE_KEY_PUBKEY_X = -2;
+ public static final byte COSE_KEY_PUBKEY_Y = -3;
+ public static final byte COSE_KEY_PRIV_KEY = -4;
+ public static final byte[] COSE_TEST_KEY = {
+ (byte) 0xFF, (byte) 0xFE, (byte) 0xEE, (byte) 0x90
+ }; // -70000
+ public static final byte COSE_KEY_MAX_SIZE = 4;
+
+ // kdfcontext strings
+ public static final byte[] client = {0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74};
+ public static final byte[] server = {0x73, 0x65, 0x72, 0x76, 0x65, 0x72};
+ // Context strings
+ public static final byte[] MAC_CONTEXT = {0x4d, 0x41, 0x43, 0x30}; // MAC0
+ public static final byte[] SIGNATURE1_CONTEXT = {
+ 0x53, 0x69, 0x67, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x31
+ }; // Signature1
+ // Certificate payload supported keys
+ public static final byte ISSUER = (byte) 0x01;
+ public static final byte SUBJECT = (byte) 0x02;
+ public static final byte[] SUBJECT_PUBLIC_KEY = {
+ (byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA8
+ };
+ public static final byte[] KEY_USAGE = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA7};
+ // text strings
+ public static final byte[] TEST_ISSUER_NAME = {
+ (byte) 0x49, 0x73, 0x73, 0x75, 0x65, 0x72
+ }; // "Issuer"
+ public static final byte[] TEST_SUBJECT_NAME = {
+ 0x53, 0x75, 0x62, 0x6A, 0x65, 0x63, 0x74
+ }; // "Subject"
+ public static final byte[] KEY_USAGE_SIGN = {0x20}; // Key usage sign
+
+ public static final short[] COSE_KEY_LABELS = {
+ KMCose.COSE_KEY_KEY_TYPE,
+ KMCose.COSE_KEY_KEY_ID,
+ KMCose.COSE_KEY_ALGORITHM,
+ KMCose.COSE_KEY_CURVE,
+ KMCose.COSE_KEY_PUBKEY_X,
+ KMCose.COSE_KEY_PUBKEY_Y,
+ KMCose.COSE_KEY_PRIV_KEY
+ };
+ public static final short[] COSE_HEADER_LABELS = {
+ KMCose.COSE_LABEL_ALGORITHM,
+ KMCose.COSE_LABEL_KEYID,
+ KMCose.COSE_LABEL_IV,
+ KMCose.COSE_LABEL_COSE_KEY
+ };
+
+ /**
+ * Constructs the Cose MAC structure.
+ *
+ * @param protectedHeader Bstr pointer which holds the protected header.
+ * @param extAad Bstr pointer which holds the external Aad.
+ * @param payload Bstr pointer which holds the payload of the MAC structure.
+ * @return KMArray instance of MAC structure.
+ */
+ public static short constructCoseMacStructure(
+ short protectedHeader, short extAad, short payload) {
+ // Create MAC Structure and compute HMAC as per https://tools.ietf.org/html/rfc8152#section-6.3
+ // MAC_structure = [
+ // context : "MAC" / "MAC0",
+ // protected : empty_or_serialized_map,
+ // external_aad : bstr,
+ // payload : bstr
+ // ]
+ short arrPtr = KMArray.instance(KMCose.COSE_MAC0_ENTRY_COUNT);
+ // 1 - Context
+ KMArray.cast(arrPtr)
+ .add(
+ (short) 0,
+ KMTextString.instance(
+ KMCose.MAC_CONTEXT, (short) 0, (short) KMCose.MAC_CONTEXT.length));
+ // 2 - Protected headers.
+ KMArray.cast(arrPtr).add((short) 1, protectedHeader);
+ // 3 - external aad
+ KMArray.cast(arrPtr).add((short) 2, extAad);
+ // 4 - payload.
+ KMArray.cast(arrPtr).add((short) 3, payload);
+ return arrPtr;
+ }
+
+ /**
+ * Constructs the COSE_MAC0 object.
+ *
+ * @param protectedHeader Bstr pointer which holds the protected header.
+ * @param unprotectedHeader Bstr pointer which holds the unprotected header.
+ * @param payload Bstr pointer which holds the payload of the MAC structure.
+ * @param tag Bstr pointer which holds the tag value.
+ * @return KMArray instance of COSE_MAC0 object.
+ */
+ public static short constructCoseMac0(
+ short protectedHeader, short unprotectedHeader, short payload, short tag) {
+ // Construct Cose_MAC0
+ // COSE_Mac0 = [
+ // protectedHeader,
+ // unprotectedHeader,
+ // payload : bstr / nil,
+ // tag : bstr,
+ // ]
+ short arrPtr = KMArray.instance(KMCose.COSE_MAC0_ENTRY_COUNT);
+ // 1 - protected headers
+ KMArray.cast(arrPtr).add((short) 0, protectedHeader);
+ // 2 - unprotected headers
+ KMArray.cast(arrPtr).add((short) 1, unprotectedHeader);
+ // 2 - payload
+ KMArray.cast(arrPtr).add((short) 2, payload);
+ // 3 - tag
+ KMArray.cast(arrPtr).add((short) 3, tag);
+ // Do encode.
+ return arrPtr;
+ }
+
+ /**
+ * Constructs the COSE_Signature structure.
+ *
+ * @param protectedHeader Bstr pointer which holds the protected header.
+ * @param extAad Bstr pointer which holds the aad.
+ * @param payload Bstr pointer which holds the payload.
+ * @return KMArray instance of COSE_Signature object.
+ */
+ public static short constructCoseSignStructure(
+ short protectedHeader, short extAad, short payload) {
+ // Sig_structure = [
+ // context : "Signature" / "Signature1" / "CounterSignature",
+ // body_protected : empty_or_serialized_map,
+ // ? sign_protected : empty_or_serialized_map,
+ // external_aad : bstr,
+ // payload : bstr
+ // ]
+ short arrPtr = KMArray.instance(KMCose.COSE_SIGN1_ENTRY_COUNT);
+ // 1 - Context
+ KMArray.cast(arrPtr)
+ .add(
+ (short) 0,
+ KMTextString.instance(
+ KMCose.SIGNATURE1_CONTEXT, (short) 0, (short) KMCose.SIGNATURE1_CONTEXT.length));
+ // 2 - Protected headers.
+ KMArray.cast(arrPtr).add((short) 1, protectedHeader);
+ // 3 - external aad
+ KMArray.cast(arrPtr).add((short) 2, extAad);
+ // 4 - payload.
+ KMArray.cast(arrPtr).add((short) 3, payload);
+ return arrPtr;
+ }
+
+ /**
+ * Constructs the COSE_Sign1 object.
+ *
+ * @param protectedHeader Bstr pointer which holds the protected header.
+ * @param unProtectedHeader Bstr pointer which holds the unprotected header.
+ * @param payload Bstr pointer which holds the payload.
+ * @param signature Bstr pointer which holds the signature.
+ * @return KMArray instance of COSE_Sign1 object.
+ */
+ public static short constructCoseSign1(
+ short protectedHeader, short unProtectedHeader, short payload, short signature) {
+ // COSE_Sign = [
+ // protectedHeader,
+ // unprotectedHeader,
+ // payload : bstr / nil,
+ // signatures : [+ COSE_Signature]
+ // ]
+ short arrPtr = KMArray.instance(KMCose.COSE_SIGN1_ENTRY_COUNT);
+ // 1 - protected headers
+ KMArray.cast(arrPtr).add((short) 0, protectedHeader);
+ // 2 - unprotected headers
+ KMArray.cast(arrPtr).add((short) 1, unProtectedHeader);
+ // 2 - payload
+ KMArray.cast(arrPtr).add((short) 2, payload);
+ // 3 - tag
+ KMArray.cast(arrPtr).add((short) 3, signature);
+ return arrPtr;
+ }
+
+ /**
+ * Constructs array based on the tag values provided.
+ *
+ * @param tag array of tag values to be constructed.
+ * @param includeTestMode flag which indicates if TEST_COSE_KEY should be included or not.
+ * @return instance of KMArray.
+ */
+ private static short handleCosePairTags(
+ short[] tag, short[] keyValues, short valueIndex, boolean includeTestMode) {
+ short index = 0;
+ // var is used to calculate the length of the array.
+ short var = 0;
+ short tagLen = (short) tag.length;
+ // var is used to calculate the length of the array.
+ while (index < tagLen) {
+ if (keyValues[index] != KMType.INVALID_VALUE) {
+ keyValues[(short) (index + valueIndex)] =
+ buildCosePairTag((byte) tag[index], keyValues[index]);
+ var++;
+ }
+ index++;
+ }
+ var += includeTestMode ? 1 : 0;
+ short arrPtr = KMArray.instance(var);
+ index = 0;
+ // var is used to index the array.
+ var = 0;
+ while (index < tagLen) {
+ if (keyValues[(short) (index + valueIndex)] != KMType.INVALID_VALUE) {
+ KMArray.cast(arrPtr).add(var++, keyValues[(short) (index + valueIndex)]);
+ }
+ index++;
+ }
+ return arrPtr;
+ }
+
+ /**
+ * Constructs the COSE_sign1 payload for certificate.
+ *
+ * @param issuer instance of KMCosePairTextStringTag which contains issuer value.
+ * @param subject instance of KMCosePairTextStringTag which contains subject value.
+ * @param subPublicKey instance of KMCosePairByteBlobTag which contains encoded KMCoseKey.
+ * @param keyUsage instance of KMCosePairByteBlobTag which contains key usage value.
+ * @return instance of KMArray.
+ */
+ public static short constructCoseCertPayload(
+ short issuer, short subject, short subPublicKey, short keyUsage) {
+ short certPayload = KMArray.instance((short) 4);
+ KMArray.cast(certPayload).add((short) 0, issuer);
+ KMArray.cast(certPayload).add((short) 1, subject);
+ KMArray.cast(certPayload).add((short) 2, subPublicKey);
+ KMArray.cast(certPayload).add((short) 3, keyUsage);
+ certPayload = KMCoseCertPayload.instance(certPayload);
+ KMCoseCertPayload.cast(certPayload).canonicalize();
+ return certPayload;
+ }
+
+ /**
+ * Construct headers structure. Headers can be part of COSE_Sign1, COSE_Encrypt, COSE_Mac0 and
+ * COSE_Key.
+ *
+ * @param alg instance of either KMNInteger or KMInteger, based on the sign of algorithm value.
+ * @param keyId instance of KMByteBlob which contains the key identifier.
+ * @param iv instance of KMByteblob which contains the iv buffer.
+ * @param ephemeralKey instance of KMCoseKey.
+ * @return instance of KMCoseHeaders.
+ */
+ public static short constructHeaders(
+ short[] buff, short alg, short keyId, short iv, short ephemeralKey) {
+ buff[0] = alg;
+ buff[1] = keyId;
+ buff[2] = iv;
+ buff[3] = ephemeralKey;
+ for (short i = 4; i < 8; i++) {
+ buff[i] = KMType.INVALID_VALUE;
+ }
+ short ptr = handleCosePairTags(COSE_HEADER_LABELS, buff, (short) 4, false);
+ ptr = KMCoseHeaders.instance(ptr);
+ KMCoseHeaders.cast(ptr).canonicalize();
+ return ptr;
+ }
+
+ /**
+ * Constructs the instance of KMCosePair*Tag.
+ *
+ * @param key value of the key.
+ * @param valuePtr instance of one of KMType.
+ * @return instance of KMCosePair*Value object.
+ */
+ public static short buildCosePairTag(byte key, short valuePtr) {
+ short type = KMType.getType(valuePtr);
+ short keyPtr;
+ if (key < 0) {
+ keyPtr = KMNInteger.uint_8(key);
+ } else {
+ keyPtr = KMInteger.uint_8(key);
+ }
+ switch (type) {
+ case KMType.INTEGER_TYPE:
+ return KMCosePairIntegerTag.instance(keyPtr, valuePtr);
+ case KMType.NEG_INTEGER_TYPE:
+ return KMCosePairNegIntegerTag.instance(keyPtr, valuePtr);
+ case KMType.BYTE_BLOB_TYPE:
+ return KMCosePairByteBlobTag.instance(keyPtr, valuePtr);
+ case KMType.TEXT_STRING_TYPE:
+ return KMCosePairTextStringTag.instance(keyPtr, valuePtr);
+ case KMType.COSE_KEY_TYPE:
+ return KMCosePairCoseKeyTag.instance(keyPtr, valuePtr);
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ return 0;
+ }
+ }
+
+ /**
+ * Constructs a CoseKey with the provided input parameters. Note that construction of the key_ops
+ * label is not needed to be supported. In the KeyMint3.0 specifications: The CoseKey inside
+ * MacedPublicKeys and DiceCertChain does not have key_ops label.
+ *
+ * @param keyType Instance of the identification of the key type.
+ * @param keyId Instance of key identification value.
+ * @param keyAlg Instance of the algorithm that is used with this key.
+ * @param curve Instance of the EC curve that is used with this key.
+ * @param pubKey Buffer containing the public key.
+ * @param pubKeyOff Start offset of the buffer.
+ * @param pubKeyLen Length of the public key.
+ * @param privKeyPtr Instance of the private key.
+ * @param testMode Represents if key is used in test mode or production mode.
+ * @return Instance of the CoseKey structure.
+ */
+ public static short constructCoseKey(
+ short[] buff,
+ short keyType,
+ short keyId,
+ short keyAlg,
+ short curve,
+ byte[] pubKey,
+ short pubKeyOff,
+ short pubKeyLen,
+ short privKeyPtr,
+ boolean testMode) {
+ if (pubKey[pubKeyOff] == 0x04) { // uncompressed format
+ pubKeyOff += 1;
+ pubKeyLen -= 1;
+ }
+ pubKeyLen = (short) (pubKeyLen / 2);
+ short xPtr = KMByteBlob.instance(pubKey, pubKeyOff, pubKeyLen);
+ short yPtr = KMByteBlob.instance(pubKey, (short) (pubKeyOff + pubKeyLen), pubKeyLen);
+ short coseKey =
+ constructCoseKey(buff, keyType, keyId, keyAlg, curve, xPtr, yPtr, privKeyPtr, testMode);
+ KMCoseKey.cast(coseKey).canonicalize();
+ return coseKey;
+ }
+
+ /**
+ * Constructs the cose key based on input parameters supplied. All the parameters must be
+ * instantiated from either KMInteger or KMNInteger or KMByteblob types.
+ *
+ * @param keyType instance of KMInteger/KMNInteger which holds valid COSE key types.
+ * @param keyId instance of KMByteBlob which holds key identifier value.
+ * @param keyAlg instance of KMInteger/KMNInteger which holds valid COSE key algorithm.
+ * @param curve instance of KMInteger/KMNInteger which holds valid COSE EC curve.
+ * @param pubX instance of KMByteBlob which holds EC public key's x value.
+ * @param pubY instance of KMByteBlob which holds EC public key's y value.
+ * @param priv instance of KMByteBlob which holds EC private value.
+ * @param includeTestKey flag which identifies whether to construct test key or production key.
+ * @return instance of the KMCoseKey object.
+ */
+ public static short constructCoseKey(
+ short[] buff,
+ short keyType,
+ short keyId,
+ short keyAlg,
+ short curve,
+ short pubX,
+ short pubY,
+ short priv,
+ boolean includeTestKey) {
+ short valueIndex = 7;
+ buff[0] = keyType;
+ buff[1] = keyId;
+ buff[2] = keyAlg;
+ buff[3] = curve;
+ buff[4] = pubX;
+ buff[5] = pubY;
+ buff[6] = priv;
+ for (short i = valueIndex; i < 16; i++) {
+ buff[i] = KMType.INVALID_VALUE;
+ }
+ short arrPtr = handleCosePairTags(COSE_KEY_LABELS, buff, valueIndex, includeTestKey);
+ if (includeTestKey) {
+ short testKey =
+ KMCosePairSimpleValueTag.instance(
+ KMNInteger.uint_32(KMCose.COSE_TEST_KEY, (short) 0),
+ KMSimpleValue.instance(KMSimpleValue.NULL));
+ KMArray.cast(arrPtr).add((short) (KMArray.cast(arrPtr).length() - 1), testKey);
+ }
+ arrPtr = KMCoseKey.instance(arrPtr);
+ KMCoseKey.cast(arrPtr).canonicalize();
+ return arrPtr;
+ }
+
+ /**
+ * Constructs key derivation context which is required to compute HKDF.
+ *
+ * @param publicKeyA public key buffer from the first party.
+ * @param publicKeyAOff start position of the public key buffer from first party.
+ * @param publicKeyALen length of the public key buffer from first party.
+ * @param publicKeyB public key buffer from the second party.
+ * @param publicKeyBOff start position of the public key buffer from second party.
+ * @param publicKeyBLen length of the public key buffer from second party.
+ * @param senderIsA true if caller is first party, false if caller is second party.
+ * @return instance of KMArray.
+ */
+ public static short constructKdfContext(
+ byte[] publicKeyA,
+ short publicKeyAOff,
+ short publicKeyALen,
+ byte[] publicKeyB,
+ short publicKeyBOff,
+ short publicKeyBLen,
+ boolean senderIsA) {
+ short index = 0;
+ // Prepare sender info
+ short senderInfo = KMArray.instance((short) 3);
+ KMArray.cast(senderInfo)
+ .add(index++, KMByteBlob.instance(client, (short) 0, (short) client.length));
+ KMArray.cast(senderInfo).add(index++, KMByteBlob.instance((short) 0));
+ KMArray.cast(senderInfo)
+ .add(
+ index,
+ senderIsA
+ ? KMByteBlob.instance(publicKeyA, publicKeyAOff, publicKeyALen)
+ : KMByteBlob.instance(publicKeyB, publicKeyBOff, publicKeyBLen));
+
+ // Prepare recipient info
+ index = 0;
+ short recipientInfo = KMArray.instance((short) 3);
+ KMArray.cast(recipientInfo)
+ .add(index++, KMByteBlob.instance(server, (short) 0, (short) server.length));
+ KMArray.cast(recipientInfo).add(index++, KMByteBlob.instance((short) 0));
+ KMArray.cast(recipientInfo)
+ .add(
+ index,
+ senderIsA
+ ? KMByteBlob.instance(publicKeyB, publicKeyBOff, publicKeyBLen)
+ : KMByteBlob.instance(publicKeyA, publicKeyAOff, publicKeyALen));
+
+ // supply public info
+ index = 0;
+ short publicInfo = KMArray.instance((short) 2);
+ KMArray.cast(publicInfo).add(index++, KMInteger.uint_16(AES_GCM_KEY_SIZE_BITS));
+ KMArray.cast(publicInfo).add(index, KMByteBlob.instance((short) 0));
+
+ // construct kdf context
+ index = 0;
+ short arrPtr = KMArray.instance((short) 4);
+ KMArray.cast(arrPtr).add(index++, KMInteger.uint_8(COSE_ALG_AES_GCM_256));
+ KMArray.cast(arrPtr).add(index++, senderInfo);
+ KMArray.cast(arrPtr).add(index++, recipientInfo);
+ KMArray.cast(arrPtr).add(index, publicInfo);
+
+ return arrPtr;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java
new file mode 100644
index 0000000..fff9cf8
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCoseCertPayload represents the COSE_Sign1 payload for each certificate in BCC. The supported
+ * key types are KMInteger, KMNInteger and the supported value types are KMByteBlob and
+ * KMTextString. It corresponds to a CBOR Map type. struct{byte TAG_TYPE; short length; short
+ * arrayPtr } where arrayPtr is a pointer to array with any KMCosePairTagType subtype instances.
+ */
+public class KMCoseCertPayload extends KMCoseMap {
+
+ private static KMCoseCertPayload prototype;
+
+ private KMCoseCertPayload() {}
+
+ private static KMCoseCertPayload proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCoseCertPayload();
+ }
+ instanceTable[KM_COSE_CERT_PAYLOAD_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp() {
+ short arrPtr = KMArray.instance((short) 2);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add((short) 0, KMCosePairTextStringTag.exp());
+ arr.add((short) 1, KMCosePairByteBlobTag.exp());
+ return KMCoseCertPayload.instance(arrPtr);
+ }
+
+ public static short instance(short vals) {
+ short ptr = KMType.instance(COSE_CERT_PAYLOAD_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+ return ptr;
+ }
+
+ public static KMCoseCertPayload cast(short ptr) {
+ if (heap[ptr] != COSE_CERT_PAYLOAD_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (heap[arrPtr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ @Override
+ public short getVals() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_CERT_PAYLOAD_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ @Override
+ public short length() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).length();
+ }
+
+ @Override
+ public void canonicalize() {
+ KMCoseMap.canonicalize(getVals());
+ }
+
+ private short getValueType(short key, short significantKey) {
+ short arr = getVals();
+ short length = length();
+ short keyPtr;
+ short valPtr = 0;
+ short index = 0;
+ short tagType;
+ boolean found = false;
+ while (index < length) {
+ tagType = KMCosePairTagType.getTagValueType(KMArray.cast(arr).get(index));
+ switch (tagType) {
+ case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+ keyPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == KMCosePairTagType.getKeyValueShort(keyPtr)
+ && significantKey == KMCosePairTagType.getKeyValueSignificantShort(keyPtr)) {
+ valPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE:
+ keyPtr = KMCosePairTextStringTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+ valPtr = KMCosePairTextStringTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ default:
+ break;
+ }
+ if (found) {
+ break;
+ }
+ index++;
+ }
+ return valPtr;
+ }
+
+ public short getSubjectPublicKey() {
+ return getValueType(
+ Util.getShort(KMCose.SUBJECT_PUBLIC_KEY, (short) 2), // LSB
+ Util.getShort(KMCose.SUBJECT_PUBLIC_KEY, (short) 0) // MSB (Significant)
+ );
+ }
+
+ public short getSubject() {
+ return getValueType(KMCose.SUBJECT, KMType.INVALID_VALUE);
+ }
+
+ public short getIssuer() {
+ return getValueType(KMCose.ISSUER, KMType.INVALID_VALUE);
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java
new file mode 100644
index 0000000..0e722d2
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright(C) 2021 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.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCoseHeaders represents headers section from the Cose standard
+ * https://datatracker.ietf.org/doc/html/rfc8152#section-3. The supported key types are KMInteger,
+ * KMNInteger and the supported value types are KMInteger, KMNInteger, KMByteBlob, KMCoseKey. It
+ * corresponds to a CBOR Map type. struct{byte TAG_TYPE; short length; short arrayPtr } where
+ * arrayPtr is a pointer to array with any KMTag subtype instances.
+ */
+public class KMCoseHeaders extends KMCoseMap {
+
+ private static KMCoseHeaders prototype;
+
+ private KMCoseHeaders() {}
+
+ private static KMCoseHeaders proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCoseHeaders();
+ }
+ instanceTable[KM_COSE_HEADERS_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp() {
+ short arrPtr = KMArray.instance((short) 4);
+ // CoseKey is internally an Array so evaluate it separately.
+ short coseKeyValueExp = KMCosePairCoseKeyTag.exp();
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add((short) 0, KMCosePairIntegerTag.exp());
+ arr.add((short) 1, KMCosePairNegIntegerTag.exp());
+ arr.add((short) 2, KMCosePairByteBlobTag.exp());
+ arr.add((short) 3, coseKeyValueExp);
+ return KMCoseHeaders.instance(arrPtr);
+ }
+
+ public static short instance(short vals) {
+ short ptr = KMType.instance(COSE_HEADERS_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+ return ptr;
+ }
+
+ public static KMCoseHeaders cast(short ptr) {
+ if (heap[ptr] != COSE_HEADERS_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (heap[arrPtr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ @Override
+ public short getVals() {
+ return Util.getShort(heap, (short) (instanceTable[KM_COSE_HEADERS_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ @Override
+ public short length() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).length();
+ }
+
+ @Override
+ public void canonicalize() {
+ KMCoseMap.canonicalize(getVals());
+ }
+
+ private short getValueType(short key) {
+ short index = 0;
+ short len = length();
+ short arr = getVals();
+ short tagType;
+ short valPtr = 0;
+ short keyPtr;
+ boolean found = false;
+ while (index < len) {
+ tagType = KMCosePairTagType.getTagValueType(KMArray.cast(arr).get(index));
+ switch (tagType) {
+ case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+ keyPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+ valPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE:
+ keyPtr = KMCosePairCoseKeyTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+ valPtr = KMCosePairCoseKeyTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ case KMType.COSE_PAIR_INT_TAG_TYPE:
+ keyPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+ valPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+ keyPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+ valPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ default:
+ break;
+ }
+ if (found) {
+ break;
+ }
+ index++;
+ }
+ return valPtr;
+ }
+
+ public short getKeyIdentifier() {
+ return getValueType(KMCose.COSE_LABEL_KEYID);
+ }
+
+ public short getCoseKey() {
+ return getValueType(KMCose.COSE_LABEL_COSE_KEY);
+ }
+
+ public short getIV() {
+ return getValueType(KMCose.COSE_LABEL_IV);
+ }
+
+ public short getAlgorithm() {
+ return getValueType(KMCose.COSE_LABEL_ALGORITHM);
+ }
+
+ public boolean isDataValid(short[] buff, short alg, short keyIdPtr) {
+ short bufLen = 4;
+ buff[0] = KMCose.COSE_LABEL_ALGORITHM;
+ buff[1] = alg;
+ buff[2] = KMCose.COSE_LABEL_KEYID;
+ buff[3] = keyIdPtr;
+ boolean valid = false;
+ short value;
+ short ptr;
+ short tagIndex = 0;
+ while (tagIndex < bufLen) {
+ value = buff[(short) (tagIndex + 1)];
+ if (value != KMType.INVALID_VALUE) {
+ valid = false;
+ ptr = getValueType(buff[tagIndex]);
+ switch (KMType.getType(ptr)) {
+ case KMType.BYTE_BLOB_TYPE:
+ if ((KMByteBlob.cast(value).length() == KMByteBlob.cast(ptr).length())
+ && (0
+ == Util.arrayCompare(
+ KMByteBlob.cast(value).getBuffer(),
+ KMByteBlob.cast(value).getStartOff(),
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length()))) {
+ valid = true;
+ }
+ break;
+ case KMType.INTEGER_TYPE:
+ if (value == KMInteger.cast(ptr).getShort()) {
+ valid = true;
+ }
+ break;
+ case KMType.NEG_INTEGER_TYPE:
+ if ((byte) value == (byte) KMNInteger.cast(ptr).getShort()) {
+ valid = true;
+ }
+ break;
+ default:
+ break;
+ }
+ if (!valid) {
+ break;
+ }
+ }
+ tagIndex += 2;
+ }
+ return valid;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCoseKey.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCoseKey.java
new file mode 100644
index 0000000..d1bfec1
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCoseKey.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCoseKey represents COSE_Key section from the Cose standard
+ * https://datatracker.ietf.org/doc/html/rfc8152#section-7 The supported key types are KMNInteger,
+ * KMInteger and the supported value types are KMInteger, KMNInteger, KMByteBlob, KMSimpleValue. It
+ * corresponds to a CBOR Map type. struct{byte TAG_TYPE; short length; short arrayPtr } where
+ * arrayPtr is a pointer to array with any KMTag subtype instances. Note that construction of the
+ * key_ops label is not needed to be supported. In the KeyMint3.0 specifications: The CoseKey inside
+ * MacedPublicKeys and DiceCertChain does not have key_ops label.
+ */
+public class KMCoseKey extends KMCoseMap {
+
+ private static KMCoseKey prototype;
+
+ private KMCoseKey() {}
+
+ private static KMCoseKey proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCoseKey();
+ }
+ instanceTable[KM_COSE_KEY_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp() {
+ short arrPtr = KMArray.instance((short) 4);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add((short) 0, KMCosePairIntegerTag.exp());
+ arr.add((short) 1, KMCosePairNegIntegerTag.exp());
+ arr.add((short) 2, KMCosePairByteBlobTag.exp());
+ arr.add((short) 3, KMCosePairSimpleValueTag.exp());
+ return KMCoseKey.instance(arrPtr);
+ }
+
+ public static short instance(short vals) {
+ short ptr = KMType.instance(COSE_KEY_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+ return ptr;
+ }
+
+ public static KMCoseKey cast(short ptr) {
+ if (heap[ptr] != COSE_KEY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (heap[arrPtr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ @Override
+ public short getVals() {
+ return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ @Override
+ public short length() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).length();
+ }
+
+ private short getValueType(short key, short significantKey) {
+ short arr = getVals();
+ short length = length();
+ short keyPtr;
+ short valPtr = 0;
+ short index = 0;
+ short tagType;
+ boolean found = false;
+ while (index < length) {
+ tagType = KMCosePairTagType.getTagValueType(KMArray.cast(arr).get(index));
+ switch (tagType) {
+ case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+ keyPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+ valPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ case KMType.COSE_PAIR_INT_TAG_TYPE:
+ keyPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+ valPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+ keyPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+ valPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE:
+ keyPtr = KMCosePairSimpleValueTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+ if (key == KMCosePairTagType.getKeyValueShort(keyPtr)
+ && significantKey == KMCosePairTagType.getKeyValueSignificantShort(keyPtr)) {
+ valPtr = KMCosePairSimpleValueTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+ found = true;
+ }
+ break;
+ default:
+ break;
+ }
+ if (found) {
+ break;
+ }
+ index++;
+ }
+ return valPtr;
+ }
+
+ public short getKeyIdentifier() {
+ return getValueType(KMCose.COSE_KEY_KEY_ID, KMType.INVALID_VALUE);
+ }
+
+ public short getEcdsa256PublicKey(byte[] pubKey, short pubKeyOff) {
+ short baseOffset = pubKeyOff;
+ pubKey[pubKeyOff] = (byte) 0x04; // uncompressed.
+ pubKeyOff++;
+ short ptr = getValueType(KMCose.COSE_KEY_PUBKEY_X, KMType.INVALID_VALUE);
+ Util.arrayCopy(
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ pubKey,
+ pubKeyOff,
+ KMByteBlob.cast(ptr).length());
+ pubKeyOff += KMByteBlob.cast(ptr).length();
+ ptr = getValueType(KMCose.COSE_KEY_PUBKEY_Y, KMType.INVALID_VALUE);
+ Util.arrayCopy(
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ pubKey,
+ pubKeyOff,
+ KMByteBlob.cast(ptr).length());
+ pubKeyOff += KMByteBlob.cast(ptr).length();
+ return (short) (pubKeyOff - baseOffset);
+ }
+
+ public short getPrivateKey(byte[] priv, short privOff) {
+ short ptr = getValueType(KMCose.COSE_KEY_PRIV_KEY, KMType.INVALID_VALUE);
+ Util.arrayCopy(
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ priv,
+ privOff,
+ KMByteBlob.cast(ptr).length());
+ return KMByteBlob.cast(ptr).length();
+ }
+
+ public boolean isTestKey() {
+ short ptr =
+ getValueType(
+ Util.getShort(KMCose.COSE_TEST_KEY, (short) 2), // LSB
+ Util.getShort(KMCose.COSE_TEST_KEY, (short) 0) // MSB (Significant)
+ );
+ boolean isTestKey = false;
+ if (ptr != 0) {
+ isTestKey = (KMSimpleValue.cast(ptr).getValue() == KMSimpleValue.NULL);
+ }
+ return isTestKey;
+ }
+
+ /**
+ * Verifies the KMCoseKey values against the input values. Note that construction of the key_ops
+ * label is not needed to be supported. In the KeyMint3.0 specifications: The CoseKey inside
+ * MacedPublicKeys and DiceCertChain does not have key_ops label.
+ *
+ * @param keyType value of the key type
+ * @param keyIdPtr instance of KMByteBlob containing the key id.
+ * @param keyAlg value of the algorithm.
+ * @param keyOps value of the key operations.
+ * @param curve value of the curve.
+ * @return true if valid, otherwise false.
+ */
+ public boolean isDataValid(
+ short[] buff, short keyType, short keyIdPtr, short keyAlg, short curve) {
+ short buffLen = 8;
+ buff[0] = KMCose.COSE_KEY_KEY_TYPE;
+ buff[1] = keyType;
+ buff[2] = KMCose.COSE_KEY_KEY_ID;
+ buff[3] = keyIdPtr;
+ buff[4] = KMCose.COSE_KEY_ALGORITHM;
+ buff[5] = keyAlg;
+ buff[6] = KMCose.COSE_KEY_CURVE;
+ buff[7] = curve;
+ boolean valid = false;
+ short ptr;
+ short tagIndex = 0;
+ short value;
+ while (tagIndex < buffLen) {
+ value = buff[(short) (tagIndex + 1)];
+ if (value != KMType.INVALID_VALUE) {
+ valid = false;
+ ptr = getValueType(buff[tagIndex], KMType.INVALID_VALUE);
+ switch (KMType.getType(ptr)) {
+ case KMType.BYTE_BLOB_TYPE:
+ if ((KMByteBlob.cast(value).length() == KMByteBlob.cast(ptr).length())
+ && (0
+ == Util.arrayCompare(
+ KMByteBlob.cast(value).getBuffer(),
+ KMByteBlob.cast(value).getStartOff(),
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length()))) {
+ valid = true;
+ }
+ break;
+ case KMType.INTEGER_TYPE:
+ if (value == KMInteger.cast(ptr).getShort()) {
+ valid = true;
+ }
+ break;
+ case KMType.NEG_INTEGER_TYPE:
+ if ((byte) value == (byte) KMNInteger.cast(ptr).getShort()) {
+ valid = true;
+ }
+ break;
+ }
+ if (!valid) {
+ break;
+ }
+ }
+ tagIndex += 2;
+ }
+ return valid;
+ }
+
+ @Override
+ public void canonicalize() {
+ KMCoseMap.canonicalize(getVals());
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCoseMap.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCoseMap.java
new file mode 100644
index 0000000..5d373a7
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCoseMap.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright(C) 2021 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.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * This class represents either a Cose_key or Cose headers as defined in
+ * https://datatracker.ietf.org/doc/html/rfc8152 This is basically a map containing key value pairs.
+ * The label for the key can be (uint / int / tstr) and the value can be of any type. But this class
+ * is confined to support only key and value types which are required for remote key provisioning.
+ * So keys of type (int / uint) and values of type (int / uint / simple / bstr) only are supported.
+ * KMCoseHeaders and KMCoseKey implements this class.
+ */
+public abstract class KMCoseMap extends KMType {
+
+ public static byte[] scratchpad;
+
+ /**
+ * This function creates an instance of either KMCoseHeaders or KMCoseKey based on the type
+ * information provided.
+ *
+ * @param typePtr type information of the underlying KMType.
+ * @param arrPtr instance of KMArray.
+ * @return instance type of either KMCoseHeaders or KMCoseKey.
+ */
+ public static short createInstanceFromType(short typePtr, short arrPtr) {
+ short mapType = KMType.getType(typePtr);
+ switch (mapType) {
+ case KMType.COSE_HEADERS_TYPE:
+ return KMCoseHeaders.instance(arrPtr);
+ case KMType.COSE_KEY_TYPE:
+ return KMCoseKey.instance(arrPtr);
+ case KMType.COSE_CERT_PAYLOAD_TYPE:
+ return KMCoseCertPayload.instance(arrPtr);
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ return 0;
+ }
+ }
+
+ public static short getVals(short ptr) {
+ short mapType = KMType.getType(ptr);
+ switch (mapType) {
+ case KMType.COSE_HEADERS_TYPE:
+ return KMCoseHeaders.cast(ptr).getVals();
+ case KMType.COSE_KEY_TYPE:
+ return KMCoseKey.cast(ptr).getVals();
+ case KMType.COSE_CERT_PAYLOAD_TYPE:
+ return KMCoseCertPayload.cast(ptr).getVals();
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ return 0;
+ }
+ }
+
+ private static short getKey(short tagPtr) {
+ short tagType = KMCosePairTagType.getTagValueType(tagPtr);
+ switch (tagType) {
+ case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+ return KMCosePairByteBlobTag.cast(tagPtr).getKeyPtr();
+ case KMType.COSE_PAIR_INT_TAG_TYPE:
+ return KMCosePairIntegerTag.cast(tagPtr).getKeyPtr();
+ case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+ return KMCosePairNegIntegerTag.cast(tagPtr).getKeyPtr();
+ case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE:
+ return KMCosePairSimpleValueTag.cast(tagPtr).getKeyPtr();
+ case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE:
+ return KMCosePairCoseKeyTag.cast(tagPtr).getKeyPtr();
+ case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE:
+ return KMCosePairTextStringTag.cast(tagPtr).getKeyPtr();
+ default:
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return 0;
+ }
+
+ private static void createScratchBuffer() {
+ if (scratchpad == null) {
+ scratchpad = JCSystem.makeTransientByteArray((short) 120, JCSystem.CLEAR_ON_RESET);
+ }
+ }
+
+ protected static void canonicalize(short arr) {
+ canonicalize(arr, KMArray.cast(arr).length());
+ }
+
+ private static void swap(short ptr, short firstIndex, short secondIndex) {
+ if (KMType.getType(ptr) == KMType.ARRAY_TYPE) {
+ KMArray.cast(ptr).swap(firstIndex, secondIndex);
+ } else {
+ KMMap.cast(ptr).swap(firstIndex, secondIndex);
+ }
+ }
+
+ private static boolean compareAndSwap(short ptr, short index) {
+ short firstKey;
+ short secondKey;
+ short firstKeyLen;
+ short secondKeyLen;
+ if (KMType.getType(ptr) == KMType.ARRAY_TYPE) {
+ firstKey = getKey(KMArray.cast(ptr).get(index));
+ secondKey = getKey(KMArray.cast(ptr).get((short) (index + 1)));
+ } else { // Map
+ firstKey = KMMap.cast(ptr).getKey(index);
+ secondKey = KMMap.cast(ptr).getKey((short) (index + 1));
+ }
+ firstKeyLen =
+ KMKeymasterApplet.encoder.encode(
+ firstKey, scratchpad, (short) 0, (short) scratchpad.length);
+ secondKeyLen =
+ KMKeymasterApplet.encoder.encode(
+ secondKey, scratchpad, firstKeyLen, (short) scratchpad.length);
+ if ((firstKeyLen > secondKeyLen)
+ || ((firstKeyLen == secondKeyLen)
+ && (0
+ < Util.arrayCompare(
+ scratchpad, (short) 0, scratchpad, firstKeyLen, firstKeyLen)))) {
+ swap(ptr, index, (short) (index + 1));
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Canonicalizes using bubble sort.
+ *
+ * @param ptr instance pointer of either array or map.
+ * @param length length of the array or map instance.
+ */
+ public static void canonicalize(short ptr, short length) {
+ short index = 0;
+ short innerIndex = 0;
+ createScratchBuffer();
+ boolean swapped;
+ while (index < length) {
+ swapped = false;
+ innerIndex = 0;
+ while (innerIndex < (short) (length - index - 1)) {
+ swapped |= compareAndSwap(ptr, innerIndex);
+ innerIndex++;
+ }
+ if (!swapped) {
+ break;
+ }
+ index++;
+ }
+ }
+
+ public abstract short getVals();
+
+ public abstract short length();
+
+ public abstract void canonicalize();
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java
new file mode 100644
index 0000000..04c3abe
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairByteBlobTag represents a key-value type, where key can be KMInteger or KMNInteger and
+ * value is KMByteBlob type. struct{byte TAG_TYPE; short length; struct{short BYTE_BLOB_TYPE; short
+ * key; short value}}.
+ */
+public class KMCosePairByteBlobTag extends KMCosePairTagType {
+
+ public static Object[] keys;
+ private static KMCosePairByteBlobTag prototype;
+
+ private KMCosePairByteBlobTag() {}
+
+ private static KMCosePairByteBlobTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCosePairByteBlobTag();
+ }
+ instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMByteBlob.exp());
+ return ptr;
+ }
+
+ public static short instance(short keyPtr, short valuePtr) {
+ if (!isKeyValueValid(keyPtr)) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (KMType.getType(valuePtr) != BYTE_BLOB_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+ return ptr;
+ }
+
+ public static KMCosePairByteBlobTag cast(short ptr) {
+ byte[] heap = repository.getHeap();
+ if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ // Validate the value pointer.
+ short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+ if (KMType.getType(valuePtr) != BYTE_BLOB_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ private static void createKeys() {
+ if (keys == null) {
+ keys =
+ new Object[] {
+ (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PUBKEY_X},
+ (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PUBKEY_Y},
+ (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PRIV_KEY},
+ (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_LABEL_IV},
+ (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_LABEL_KEYID},
+ (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_KEY_ID},
+ (Object) KMCose.SUBJECT_PUBLIC_KEY,
+ (Object) KMCose.KEY_USAGE
+ };
+ }
+ }
+
+ public static boolean isKeyValueValid(short keyPtr) {
+ createKeys();
+ short type = KMType.getType(keyPtr);
+ short offset = 0;
+ if (type == INTEGER_TYPE) {
+ offset = KMInteger.cast(keyPtr).getStartOff();
+ } else if (type == NEG_INTEGER_TYPE) {
+ offset = KMNInteger.cast(keyPtr).getStartOff();
+ } else {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short index = 0;
+ while (index < (short) keys.length) {
+ if (0
+ == Util.arrayCompare(
+ (byte[]) keys[index],
+ (short) 0,
+ heap,
+ offset,
+ (short) ((byte[]) keys[index]).length)) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ public short getValueType() {
+ return BYTE_BLOB_TYPE;
+ }
+
+ @Override
+ public short getKeyPtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ @Override
+ public short getValuePtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java
new file mode 100644
index 0000000..5290da2
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java
@@ -0,0 +1,89 @@
+package com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairCoseKeyTag represents a key-value type, where key can be KMInteger or KMNInteger and
+ * value is KMCoseKey type. struct{byte TAG_TYPE; short length; struct{short COSE_KEY_VALUE_TYPE;
+ * short key; short value}}.
+ */
+public class KMCosePairCoseKeyTag extends KMCosePairTagType {
+
+ public static final byte[] keys = {KMCose.COSE_LABEL_COSE_KEY};
+ private static KMCosePairCoseKeyTag prototype;
+
+ private KMCosePairCoseKeyTag() {}
+
+ private static KMCosePairCoseKeyTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCosePairCoseKeyTag();
+ }
+ instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_COSE_KEY_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMCoseKey.exp());
+ return ptr;
+ }
+
+ public static short instance(short keyPtr, short valuePtr) {
+ if (!isKeyValueValid(KMCosePairTagType.getKeyValueShort(keyPtr))) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (KMType.getType(valuePtr) != COSE_KEY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_COSE_KEY_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+ return ptr;
+ }
+
+ public static KMCosePairCoseKeyTag cast(short ptr) {
+ byte[] heap = repository.getHeap();
+ if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ // Validate the value pointer.
+ short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+ if (KMType.getType(valuePtr) != COSE_KEY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static boolean isKeyValueValid(short keyVal) {
+ short index = 0;
+ while (index < (short) keys.length) {
+ if ((byte) (keyVal & 0xFF) == keys[index]) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ public short getValueType() {
+ return COSE_KEY_TYPE;
+ }
+
+ @Override
+ public short getKeyPtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ @Override
+ public short getValuePtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java
new file mode 100644
index 0000000..ea052a6
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairIntegerTag represents a key-value type, where key can be KMInteger or KMNInteger and
+ * value is KMInteger type. struct{byte TAG_TYPE; short length; struct{short INT_VALUE_TYPE; short
+ * key; short value}}.
+ */
+public class KMCosePairIntegerTag extends KMCosePairTagType {
+
+ private static KMCosePairIntegerTag prototype;
+
+ private KMCosePairIntegerTag() {}
+
+ private static KMCosePairIntegerTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCosePairIntegerTag();
+ }
+ instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_INT_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMInteger.exp());
+ return ptr;
+ }
+
+ public static short instance(short keyPtr, short valuePtr) {
+ short offset = KMCosePairTagType.getKeyStartOffset(keyPtr);
+ if (!KMCosePairTagType.isKeyPairValid(
+ heap, offset, KMCose.COSE_KEY_MAX_SIZE, KMInteger.cast(valuePtr).getShort())) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_INT_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+ return ptr;
+ }
+
+ public static KMCosePairIntegerTag cast(short ptr) {
+ byte[] heap = repository.getHeap();
+ if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ // Validate the value ptr.
+ short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+ if (INTEGER_TYPE != getType(valuePtr)) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public short getValueType() {
+ return INTEGER_TYPE;
+ }
+
+ @Override
+ public short getKeyPtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ @Override
+ public short getValuePtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java
new file mode 100644
index 0000000..7f01202
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairNegIntegerTag represents a key-value type, where key can be KMInteger or KMNInteger and
+ * value is KMNInteger type. struct{byte TAG_TYPE; short length; struct{short NINT_VALUE_TYPE; short
+ * key; short value}}.
+ */
+public class KMCosePairNegIntegerTag extends KMCosePairTagType {
+
+ private static KMCosePairNegIntegerTag prototype;
+
+ private KMCosePairNegIntegerTag() {}
+
+ private static KMCosePairNegIntegerTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCosePairNegIntegerTag();
+ }
+ instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_NEG_INT_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMNInteger.exp());
+ return ptr;
+ }
+
+ public static KMCosePairNegIntegerTag cast(short ptr) {
+ byte[] heap = repository.getHeap();
+ if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ // Validate the value ptr.
+ short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+ if (NEG_INTEGER_TYPE != getType(valuePtr)) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static short instance(short keyPtr, short valuePtr) {
+ short offset = KMCosePairTagType.getKeyStartOffset(keyPtr);
+ if (!KMCosePairTagType.isKeyPairValid(
+ heap, offset, KMCose.COSE_KEY_MAX_SIZE, KMNInteger.cast(valuePtr).getShort())) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_NEG_INT_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+ return ptr;
+ }
+
+ public short getValueType() {
+ return NEG_INTEGER_TYPE;
+ }
+
+ @Override
+ public short getKeyPtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ @Override
+ public short getValuePtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java
new file mode 100644
index 0000000..a0d7da8
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java
@@ -0,0 +1,76 @@
+package com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairSimpleValueTag represents a key-value type, where key can be KMInteger or KMNInteger
+ * and value is KMSimpleValue type. struct{byte TAG_TYPE; short length; struct{short
+ * SIMPLE_VALUE_TYPE; short key; short value}}.
+ */
+public class KMCosePairSimpleValueTag extends KMCosePairTagType {
+
+ private static KMCosePairSimpleValueTag prototype;
+
+ private KMCosePairSimpleValueTag() {}
+
+ private static KMCosePairSimpleValueTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCosePairSimpleValueTag();
+ }
+ instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMSimpleValue.exp());
+ return ptr;
+ }
+
+ public static short instance(short keyPtr, short valuePtr) {
+ short offset = KMCosePairTagType.getKeyStartOffset(keyPtr);
+ if (!KMCosePairTagType.isKeyPairValid(
+ heap, offset, KMCose.COSE_KEY_MAX_SIZE, KMSimpleValue.cast(valuePtr).getValue())) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+ return ptr;
+ }
+
+ public static KMCosePairSimpleValueTag cast(short ptr) {
+ byte[] heap = repository.getHeap();
+ if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ // Validate the value pointer.
+ short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+ if (KMType.getType(valuePtr) != SIMPLE_VALUE_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public short getValueType() {
+ return SIMPLE_VALUE_TYPE;
+ }
+
+ @Override
+ public short getKeyPtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ @Override
+ public short getValuePtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java
new file mode 100644
index 0000000..baa0855
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * This class represents the a key-value types. This is basically a map containing key value pairs.
+ * The label for the key can be (uint / int / tstr) and the value can be of any type. But this class
+ * is confined to support only key and value types which are required for remote key provisioning.
+ * So keys of type (int / uint) and values of type (int / uint / simple / bstr) only are supported.
+ * The structure representing all the sub classes of KMCosePairTagType is as follows:
+ * KM_COSE_PAIR_TAG_TYPE(1byte), Length(2 bytes), COSE_PAIR_*_TAG_TYPE(2 bytes), Key(2 bytes),
+ * Value(2 bytes). Key can be either KMInteger or KMNInteger and Value can be either KMIntger or
+ * KMNinteger or KMSimpleValue or KMByteBlob or KMTextString or KMCoseKey. Each subclass of
+ * KMCosePairTagType is named after their corresponding value type of the Cose pair.
+ */
+public abstract class KMCosePairTagType extends KMType {
+
+ /**
+ * Below table represents the allowed values for a key. The maximum length of the key can be 4
+ * bytes so each key is represented as 4 bytes. The allowed values are placed next to their
+ * corresponding key.
+ */
+ public static Object[] allowedKeyPairs;
+
+ private static void createAllowedKeyPairs() {
+ if (allowedKeyPairs == null) {
+ allowedKeyPairs =
+ new Object[] {
+ // Key type
+ (Object) new byte[] {0, 0, 0, KMCose.COSE_KEY_KEY_TYPE},
+ (Object) new byte[] {KMCose.COSE_KEY_TYPE_EC2, KMCose.COSE_KEY_TYPE_SYMMETRIC_KEY},
+ // Key Algorithm
+ (Object) new byte[] {0, 0, 0, KMCose.COSE_KEY_ALGORITHM},
+ (Object)
+ new byte[] {
+ KMCose.COSE_ALG_AES_GCM_256,
+ KMCose.COSE_ALG_HMAC_256,
+ KMCose.COSE_ALG_ECDH_ES_HKDF_256,
+ KMCose.COSE_ALG_ES256
+ },
+ // Key Curve
+ (Object) new byte[] {0, 0, 0, KMCose.COSE_KEY_CURVE},
+ (Object) new byte[] {KMCose.COSE_ECCURVE_256},
+ // Header Label Algorithm
+ (Object) new byte[] {0, 0, 0, KMCose.COSE_LABEL_ALGORITHM},
+ (Object)
+ new byte[] {
+ KMCose.COSE_ALG_AES_GCM_256,
+ KMCose.COSE_ALG_HMAC_256,
+ KMCose.COSE_ALG_ES256,
+ KMCose.COSE_ALG_ECDH_ES_HKDF_256
+ },
+ // Test Key
+ KMCose.COSE_TEST_KEY,
+ (Object) new byte[] {KMSimpleValue.NULL},
+ };
+ }
+ }
+
+ /**
+ * Validates the key and the values corresponding to key.
+ *
+ * @param key Buffer containing the key.
+ * @param keyOff Offset in the buffer from where key starts.
+ * @param keyLen Length of the key buffer.
+ * @param value Value corresponding to the key.
+ * @return true if key pair is valid, otherwise false.
+ */
+ public static boolean isKeyPairValid(byte[] key, short keyOff, short keyLen, short value) {
+ short index = 0;
+ short valueIdx;
+ byte[] values;
+ boolean valid = false;
+ createAllowedKeyPairs();
+ while (index < allowedKeyPairs.length) {
+ valueIdx = 0;
+ if (isEqual(
+ (byte[]) allowedKeyPairs[index],
+ (short) 0,
+ (short) ((byte[]) allowedKeyPairs[index]).length,
+ key,
+ keyOff,
+ keyLen)) {
+ values = (byte[]) allowedKeyPairs[(short) (index + 1)];
+ while (valueIdx < values.length) {
+ if (values[valueIdx] == (byte) value) {
+ valid = true;
+ break;
+ }
+ valueIdx++;
+ }
+ if (valid) {
+ break;
+ }
+ }
+ index += (short) 2;
+ }
+ return valid;
+ }
+
+ /**
+ * Compares two key buffers.
+ *
+ * @param key1 First buffer containing the key.
+ * @param offset1 Offset of the first buffer.
+ * @param length1 Length of the first buffer.
+ * @param key2 Second buffer containing the key.
+ * @param offset2 Offset of the second buffer.
+ * @param length2 Length of the second buffer.
+ * @return true if both keys are equal, otherwise false.
+ */
+ private static boolean isEqual(
+ byte[] key1, short offset1, short length1, byte[] key2, short offset2, short length2) {
+ if (length1 != length2) {
+ return false;
+ }
+ return (0 == KMInteger.unsignedByteArrayCompare(key1, offset1, key2, offset2, length1));
+ }
+
+ /**
+ * Returns the short value of the key.
+ *
+ * @param keyPtr Pointer to either KMInteger or KMNInteger
+ * @return value of the key as short.
+ */
+ public static short getKeyValueShort(short keyPtr) {
+ short type = KMType.getType(keyPtr);
+ short value = 0;
+ if (type == INTEGER_TYPE) {
+ value = KMInteger.cast(keyPtr).getShort();
+ } else if (type == NEG_INTEGER_TYPE) {
+ value = KMNInteger.cast(keyPtr).getShort();
+ } else {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return value;
+ }
+
+ /**
+ * Returns the significant short value of the key.
+ *
+ * @param keyPtr Pointer to either KMInteger or KMNInteger
+ * @return value of the key as short.
+ */
+ public static short getKeyValueSignificantShort(short keyPtr) {
+ short type = KMType.getType(keyPtr);
+ short value = 0;
+ if (type == INTEGER_TYPE) {
+ value = KMInteger.cast(keyPtr).getSignificantShort();
+ } else if (type == NEG_INTEGER_TYPE) {
+ value = KMNInteger.cast(keyPtr).getSignificantShort();
+ } else {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return value;
+ }
+
+ public static void getKeyValue(short keyPtr, byte[] dest, short offset, short len) {
+ short type = KMType.getType(keyPtr);
+ if (type == INTEGER_TYPE) {
+ KMInteger.cast(keyPtr).getValue(dest, offset, len);
+ } else if (type == NEG_INTEGER_TYPE) {
+ KMNInteger.cast(keyPtr).getValue(dest, offset, len);
+ } else {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ }
+
+ /**
+ * Returns the key offset from the key pointer.
+ *
+ * @param keyPtr Pointer to either KMInteger or KMNInteger
+ * @return offset from where the key starts.
+ */
+ public static short getKeyStartOffset(short keyPtr) {
+ short type = KMType.getType(keyPtr);
+ short offset = 0;
+ if (type == INTEGER_TYPE) {
+ offset = KMInteger.cast(keyPtr).getStartOff();
+ } else if (type == NEG_INTEGER_TYPE) {
+ offset = KMNInteger.cast(keyPtr).getStartOff();
+ } else {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return offset;
+ }
+
+ /**
+ * Returns the key length.
+ *
+ * @param keyPtr pointer to either KMInteger/KMInteger.
+ * @return length of the key.
+ */
+ public static short getKeyLength(short keyPtr) {
+ short type = KMType.getType(keyPtr);
+ short len = 0;
+ if (type == INTEGER_TYPE) {
+ len = KMInteger.cast(keyPtr).length();
+ } else if (type == NEG_INTEGER_TYPE) {
+ len = KMNInteger.cast(keyPtr).length();
+ } else {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return len;
+ }
+
+ /**
+ * This function returns one of COSE_KEY_TAG_*_VALUE_TYPE tag information.
+ *
+ * @param ptr Pointer to one of the KMCoseKey*Value class.
+ * @return Tag value type.
+ */
+ public static short getTagValueType(short ptr) {
+ return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ }
+
+ /**
+ * This function returns the key pointer.
+ *
+ * @return key pointer.
+ */
+ public abstract short getKeyPtr();
+
+ /**
+ * This function returns the value pointer.
+ *
+ * @return value pointer.
+ */
+ public abstract short getValuePtr();
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java
new file mode 100644
index 0000000..99506b6
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java
@@ -0,0 +1,91 @@
+package com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairTextStringTag represents a key-value type, where key can be KMInteger or KMNInteger and
+ * value is KMTextString type. struct{byte TAG_TYPE; short length; struct{short TXT_STR_VALUE_TYPE;
+ * short key; short value}}.
+ */
+public class KMCosePairTextStringTag extends KMCosePairTagType {
+
+ public static final byte[] keys = {
+ KMCose.ISSUER, KMCose.SUBJECT,
+ };
+ private static KMCosePairTextStringTag prototype;
+
+ private KMCosePairTextStringTag() {}
+
+ private static KMCosePairTextStringTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMCosePairTextStringTag();
+ }
+ instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_TEXT_STR_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMTextString.exp());
+ return ptr;
+ }
+
+ public static short instance(short keyPtr, short valuePtr) {
+ if (!isKeyValueValid(KMCosePairTagType.getKeyValueShort(keyPtr))) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (KMType.getType(valuePtr) != TEXT_STRING_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_TEXT_STR_TAG_TYPE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+ return ptr;
+ }
+
+ public static KMCosePairTextStringTag cast(short ptr) {
+ byte[] heap = repository.getHeap();
+ if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ // Validate the value pointer.
+ short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+ if (KMType.getType(valuePtr) != TEXT_STRING_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static boolean isKeyValueValid(short keyVal) {
+ short index = 0;
+ while (index < (short) keys.length) {
+ if ((byte) (keyVal & 0xFF) == keys[index]) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ public short getValueType() {
+ return TEXT_STRING_TYPE;
+ }
+
+ @Override
+ public short getKeyPtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ @Override
+ public short getValuePtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMDecoder.java
new file mode 100644
index 0000000..dc0101a
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMDecoder.java
@@ -0,0 +1,781 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * This class decodes the CBOR format data into a KMType structure. It interprets the input CBOR
+ * format using the input expression provided. Validation of KeyMint tags and tag types happens in
+ * the process of decoding, while constructing the subtype of a KMType structure.
+ */
+public class KMDecoder {
+
+ // major types
+ private static final short UINT_TYPE = 0x00;
+ private static final short NEG_INT_TYPE = 0x20;
+ private static final short BYTES_TYPE = 0x40;
+ private static final short TSTR_TYPE = 0x60;
+ private static final short ARRAY_TYPE = 0x80;
+ private static final short MAP_TYPE = 0xA0;
+ private static final short SIMPLE_VALUE_TYPE = 0xE0;
+ private static final short SEMANTIC_TAG_TYPE = 0xC0;
+
+ // masks
+ private static final short ADDITIONAL_MASK = 0x1F;
+ private static final short MAJOR_TYPE_MASK = 0xE0;
+
+ // value length
+ private static final short UINT8_LENGTH = 0x18;
+ private static final short UINT16_LENGTH = 0x19;
+ private static final short UINT32_LENGTH = 0x1A;
+ private static final short UINT64_LENGTH = 0x1B;
+
+ private static final byte SCRATCH_BUF_SIZE = 6;
+ private static final byte START_OFFSET = 0;
+ private static final byte LEN_OFFSET = 2;
+ private static final byte TAG_KEY_OFFSET = 4;
+ private Object[] bufferRef;
+ private short[] scratchBuf;
+
+ public KMDecoder() {
+ bufferRef = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET);
+ scratchBuf = JCSystem.makeTransientShortArray(SCRATCH_BUF_SIZE, JCSystem.CLEAR_ON_RESET);
+ bufferRef[0] = null;
+ scratchBuf[START_OFFSET] = (short) 0;
+ scratchBuf[LEN_OFFSET] = (short) 0;
+ scratchBuf[TAG_KEY_OFFSET] = (short) 0;
+ }
+
+ public short decode(short expression, byte[] buffer, short startOff, short length) {
+ bufferRef[0] = buffer;
+ scratchBuf[START_OFFSET] = startOff;
+ scratchBuf[LEN_OFFSET] = (short) (startOff + length);
+ return decode(expression);
+ }
+
+ public short decodeArray(short exp, byte[] buffer, short startOff, short length) {
+ bufferRef[0] = buffer;
+ scratchBuf[START_OFFSET] = startOff;
+ scratchBuf[LEN_OFFSET] = (short) (startOff + length);
+ short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE);
+ short expLength = KMArray.cast(exp).length();
+ if (payloadLength > expLength) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ short index = 0;
+ short obj;
+ short type;
+ short arrPtr = KMArray.instance(payloadLength);
+ while (index < payloadLength) {
+ type = KMArray.cast(exp).get(index);
+ obj = decode(type);
+ KMArray.cast(arrPtr).add(index, obj);
+ index++;
+ }
+ return arrPtr;
+ }
+
+ private short decode(short exp) {
+ byte type = KMType.getType(exp);
+ switch (type) {
+ case KMType.BYTE_BLOB_TYPE:
+ return decodeByteBlob(exp);
+ case KMType.TEXT_STRING_TYPE:
+ return decodeTstr(exp);
+ case KMType.INTEGER_TYPE:
+ return decodeInteger(exp);
+ case KMType.SIMPLE_VALUE_TYPE:
+ return decodeSimpleValue(exp);
+ case KMType.SEMANTIC_TAG_TYPE:
+ return decodeSemanticTagValue(exp);
+ case KMType.NEG_INTEGER_TYPE:
+ return decodeNegInteger(exp);
+ case KMType.ARRAY_TYPE:
+ return decodeArray(exp);
+ case KMType.MAP_TYPE:
+ return decodeMap(exp);
+ case KMType.ENUM_TYPE:
+ return decodeEnum(exp);
+ case KMType.KEY_PARAM_TYPE:
+ return decodeKeyParam(exp);
+ case KMType.KEY_CHAR_TYPE:
+ return decodeKeyChar(exp);
+ case KMType.VERIFICATION_TOKEN_TYPE:
+ return decodeVerificationToken(exp);
+ case KMType.HMAC_SHARING_PARAM_TYPE:
+ return decodeHmacSharingParam(exp);
+ case KMType.HW_AUTH_TOKEN_TYPE:
+ return decodeHwAuthToken(exp);
+ case KMType.COSE_KEY_TYPE:
+ case KMType.COSE_HEADERS_TYPE:
+ case KMType.COSE_CERT_PAYLOAD_TYPE:
+ return decodeCoseMap(exp);
+ case KMType.COSE_PAIR_TAG_TYPE:
+ short tagValueType = KMCosePairTagType.getTagValueType(exp);
+ return decodeCosePairTag(tagValueType, exp);
+ case KMType.TAG_TYPE:
+ short tagType = KMTag.getTagType(exp);
+ return decodeTag(tagType, exp);
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ return 0;
+ }
+ }
+
+ private short decodeTag(short tagType, short exp) {
+ switch (tagType) {
+ case KMType.BIGNUM_TAG:
+ return decodeBignumTag(exp);
+ case KMType.BYTES_TAG:
+ return decodeBytesTag(exp);
+ case KMType.BOOL_TAG:
+ return decodeBoolTag(exp);
+ case KMType.UINT_TAG:
+ case KMType.ULONG_TAG:
+ case KMType.DATE_TAG:
+ return decodeIntegerTag(exp);
+ case KMType.ULONG_ARRAY_TAG:
+ case KMType.UINT_ARRAY_TAG:
+ return decodeIntegerArrayTag(exp);
+ case KMType.ENUM_TAG:
+ return decodeEnumTag(exp);
+ case KMType.ENUM_ARRAY_TAG:
+ return decodeEnumArrayTag(exp);
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ return 0;
+ }
+ }
+
+ private short decodeVerificationToken(short exp) {
+ short vals = decode(KMVerificationToken.cast(exp).getVals());
+ return KMVerificationToken.instance(vals);
+ }
+
+ private short decodeHwAuthToken(short exp) {
+ short vals = decode(KMHardwareAuthToken.cast(exp).getVals());
+ return KMHardwareAuthToken.instance(vals);
+ }
+
+ private short decodeHmacSharingParam(short exp) {
+ short vals = decode(KMHmacSharingParameters.cast(exp).getVals());
+ return KMHmacSharingParameters.instance(vals);
+ }
+
+ private short decodeKeyChar(short exp) {
+ short vals = decode(KMKeyCharacteristics.cast(exp).getVals());
+ return KMKeyCharacteristics.instance(vals);
+ }
+
+ private short decodeCosePairKey(short exp) {
+ byte[] buffer = (byte[]) bufferRef[0];
+ short startOff = scratchBuf[START_OFFSET];
+ short keyPtr = (short) 0;
+ // Cose Key should be always either UINT or Negative int
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) == UINT_TYPE) {
+ keyPtr = decodeInteger(exp);
+ } else if ((buffer[startOff] & MAJOR_TYPE_MASK) == NEG_INT_TYPE) {
+ keyPtr = decodeNegInteger(exp);
+ } else {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ return keyPtr;
+ }
+
+ private short decodeCosePairSimpleValueTag(short exp) {
+ short keyPtr = decodeCosePairKey((KMCosePairSimpleValueTag.cast(exp).getKeyPtr()));
+ short valuePtr = decode(KMCosePairSimpleValueTag.cast(exp).getValuePtr());
+ return KMCosePairSimpleValueTag.instance(keyPtr, valuePtr);
+ }
+
+ private short decodeCosePairIntegerValueTag(short exp) {
+ short keyPtr = decodeCosePairKey((KMCosePairIntegerTag.cast(exp).getKeyPtr()));
+ short valuePtr = decode(KMCosePairIntegerTag.cast(exp).getValuePtr());
+ return KMCosePairIntegerTag.instance(keyPtr, valuePtr);
+ }
+
+ private short decodeCosePairNegIntegerTag(short exp) {
+ short keyPtr = decodeCosePairKey((KMCosePairNegIntegerTag.cast(exp).getKeyPtr()));
+ short valuePtr = decode(KMCosePairNegIntegerTag.cast(exp).getValuePtr());
+ return KMCosePairNegIntegerTag.instance(keyPtr, valuePtr);
+ }
+
+ private short decodeCosePairTxtStringTag(short exp) {
+ short keyPtr = decodeCosePairKey((KMCosePairTextStringTag.cast(exp).getKeyPtr()));
+ short valuePtr = decode(KMCosePairTextStringTag.cast(exp).getValuePtr());
+ return KMCosePairTextStringTag.instance(keyPtr, valuePtr);
+ }
+
+ private short decodeCosePairCoseKeyTag(short exp) {
+ short keyPtr = decodeCosePairKey((KMCosePairCoseKeyTag.cast(exp).getKeyPtr()));
+ short valuePtr = decode(KMCosePairCoseKeyTag.cast(exp).getValuePtr());
+ return KMCosePairCoseKeyTag.instance(keyPtr, valuePtr);
+ }
+
+ private short decodeCosePairByteBlobTag(short exp) {
+ short keyPtr = decodeCosePairKey((KMCosePairByteBlobTag.cast(exp).getKeyPtr()));
+ short valuePtr = decode(KMCosePairByteBlobTag.cast(exp).getValuePtr());
+ return KMCosePairByteBlobTag.instance(keyPtr, valuePtr);
+ }
+
+ private short peekCosePairTagType() {
+ byte[] buffer = (byte[]) bufferRef[0];
+ short startOff = scratchBuf[START_OFFSET];
+ // This decoder is confined to support only key and value types which are required for remote
+ // key provisioning. So keys of type (int / uint) and values of type (int / uint / simple / bstr /
+ // tstr / Cosekey) only are supported.
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE
+ && (buffer[startOff] & MAJOR_TYPE_MASK) != NEG_INT_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+
+ short additionalMask = (short) (buffer[startOff] & ADDITIONAL_MASK);
+ short increment = 0;
+ if (additionalMask < UINT8_LENGTH) {
+ increment++;
+ } else if (additionalMask == UINT8_LENGTH) {
+ increment += 2;
+ } else if (additionalMask == UINT16_LENGTH) {
+ increment += 3;
+ } else if (additionalMask == UINT32_LENGTH) {
+ increment += 5;
+ } else {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short majorType = (short) (buffer[(short) (startOff + increment)] & MAJOR_TYPE_MASK);
+ short tagValueType = 0;
+ if (majorType == BYTES_TYPE) {
+ tagValueType = KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE;
+ } else if (majorType == UINT_TYPE) {
+ tagValueType = KMType.COSE_PAIR_INT_TAG_TYPE;
+ } else if (majorType == NEG_INT_TYPE) {
+ tagValueType = KMType.COSE_PAIR_NEG_INT_TAG_TYPE;
+ } else if (majorType == MAP_TYPE) {
+ tagValueType = KMType.COSE_PAIR_COSE_KEY_TAG_TYPE;
+ } else if (majorType == SIMPLE_VALUE_TYPE) {
+ tagValueType = KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE;
+ } else if (majorType == TSTR_TYPE) {
+ tagValueType = KMType.COSE_PAIR_TEXT_STR_TAG_TYPE;
+ } else {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ return tagValueType;
+ }
+
+ private short decodeCosePairTag(short tagValueType, short exp) {
+ switch (tagValueType) {
+ case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+ return decodeCosePairByteBlobTag(exp);
+ case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+ return decodeCosePairNegIntegerTag(exp);
+ case KMType.COSE_PAIR_INT_TAG_TYPE:
+ return decodeCosePairIntegerValueTag(exp);
+ case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE:
+ return decodeCosePairSimpleValueTag(exp);
+ case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE:
+ return decodeCosePairCoseKeyTag(exp);
+ case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE:
+ return decodeCosePairTxtStringTag(exp);
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ return 0;
+ }
+ }
+
+ private short decodeCoseMap(short exp) {
+ short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE);
+ // get allowed key pairs
+ short allowedKeyPairs = KMCoseMap.getVals(exp);
+ short vals = KMArray.instance(payloadLength);
+ short length = KMArray.cast(allowedKeyPairs).length();
+ short index = 0;
+ boolean tagFound;
+ short tagInd;
+ short cosePairTagType;
+ short tagClass;
+ short allowedType;
+ short obj;
+
+ // For each tag in payload ...
+ while (index < payloadLength) {
+ tagFound = false;
+ tagInd = 0;
+ cosePairTagType = peekCosePairTagType();
+ // Check against the allowed tags ...
+ while (tagInd < length) {
+ tagClass = KMArray.cast(allowedKeyPairs).get(tagInd);
+ allowedType = KMCosePairTagType.getTagValueType(tagClass);
+ if (allowedType == cosePairTagType) {
+ obj = decode(tagClass);
+ KMArray.cast(vals).add(index, obj);
+ tagFound = true;
+ break;
+ }
+ tagInd++;
+ }
+ if (!tagFound) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ } else {
+ index++;
+ }
+ }
+ return KMCoseMap.createInstanceFromType(exp, vals);
+ }
+
+ private short decodeKeyParam(short exp) {
+ short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE);
+ // allowed tags
+ short allowedTags = KMKeyParameters.cast(exp).getVals();
+ short tagRule = KMArray.cast(allowedTags).get((short) 0);
+ boolean ignoreInvalidTags = KMEnum.cast(tagRule).getVal() == KMType.IGNORE_INVALID_TAGS;
+ short vals = KMArray.instance(payloadLength);
+ short length = KMArray.cast(allowedTags).length();
+ short index = 0;
+ boolean tagFound;
+ short tagInd;
+ short tagType;
+ short tagClass;
+ short allowedType;
+ short obj;
+ short arrPos = 0;
+ // For each tag in payload ...
+ while (index < payloadLength) {
+ tagFound = false;
+ tagInd = 1;
+ tagType = peekTagType();
+ // Check against the allowed tags ...
+ while (tagInd < length) {
+ tagClass = KMArray.cast(allowedTags).get(tagInd);
+ allowedType = KMTag.getTagType(tagClass);
+ // If it is part of allowed tags ...
+ if (tagType == allowedType) {
+ // then decodeByteBlob and add that to the array.
+ try {
+ tagFound = true;
+ obj = decode(tagClass);
+ KMArray.cast(vals).add(arrPos++, obj);
+ break;
+ } catch (KMException e) {
+ if (KMException.reason() == KMError.INVALID_TAG) {
+ if (!ignoreInvalidTags) {
+ KMException.throwIt(KMError.INVALID_TAG);
+ }
+ } else {
+ KMException.throwIt(KMException.reason());
+ }
+ break;
+ }
+ }
+ tagInd++;
+ }
+ if (!tagFound) {
+ KMException.throwIt(KMError.INVALID_TAG);
+ } else {
+ index++;
+ }
+ }
+ KMArray.cast(vals).setLength(arrPos);
+ return KMKeyParameters.instance(vals);
+ }
+
+ private short decodeEnumArrayTag(short exp) {
+ readTagKey(KMEnumArrayTag.cast(exp).getTagType());
+ return KMEnumArrayTag.instance(
+ scratchBuf[TAG_KEY_OFFSET], decode(KMEnumArrayTag.cast(exp).getValues()));
+ }
+
+ private short decodeIntegerArrayTag(short exp) {
+ readTagKey(KMIntegerArrayTag.cast(exp).getTagType());
+ // the values are array of integers.
+ return KMIntegerArrayTag.instance(
+ KMIntegerArrayTag.cast(exp).getTagType(),
+ scratchBuf[TAG_KEY_OFFSET],
+ decode(KMIntegerArrayTag.cast(exp).getValues()));
+ }
+
+ private short decodeIntegerTag(short exp) {
+ readTagKey(KMIntegerTag.cast(exp).getTagType());
+ // the value is an integer
+ return KMIntegerTag.instance(
+ KMIntegerTag.cast(exp).getTagType(),
+ scratchBuf[TAG_KEY_OFFSET],
+ decode(KMIntegerTag.cast(exp).getValue()));
+ }
+
+ private short decodeBytesTag(short exp) {
+ readTagKey(KMByteTag.cast(exp).getTagType());
+ // The value must be byte blob
+ return KMByteTag.instance(scratchBuf[TAG_KEY_OFFSET], decode(KMByteTag.cast(exp).getValue()));
+ }
+
+ private short decodeBignumTag(short exp) {
+ readTagKey(KMBignumTag.cast(exp).getTagType());
+ // The value must be byte blob
+ return KMBignumTag.instance(
+ scratchBuf[TAG_KEY_OFFSET], decode(KMBignumTag.cast(exp).getValue()));
+ }
+
+ private short decodeMap(short exp) {
+ short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE);
+ short mapPtr = KMMap.instance(payloadLength);
+ short index = 0;
+ short type;
+ short keyobj;
+ short valueobj;
+ while (index < payloadLength) {
+ type = KMMap.cast(exp).getKey(index);
+ keyobj = decode(type);
+ type = KMMap.cast(exp).getKeyValue(index);
+ valueobj = decode(type);
+ KMMap.cast(mapPtr).add(index, keyobj, valueobj);
+ index++;
+ }
+ return mapPtr;
+ }
+
+ private short decodeArray(short exp) {
+ short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE);
+ short arrPtr = KMArray.instance(payloadLength);
+ short index = 0;
+ short type;
+ short obj;
+ // check whether array contains one type of objects or multiple types
+ if (KMArray.cast(exp).containedType()
+ == KMType.INVALID_VALUE) { // multiple types specified by expression.
+ if (KMArray.cast(exp).length() != KMArray.ANY_ARRAY_LENGTH) {
+ if (KMArray.cast(exp).length() != payloadLength) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ }
+ while (index < payloadLength) {
+ type = KMArray.cast(exp).get(index);
+ obj = decode(type);
+ KMArray.cast(arrPtr).add(index, obj);
+ index++;
+ }
+ } else { // Array is a Vector containing objects of one type
+ type = KMArray.cast(exp).containedType();
+ while (index < payloadLength) {
+ obj = decode(type);
+ KMArray.cast(arrPtr).add(index, obj);
+ index++;
+ }
+ }
+ return arrPtr;
+ }
+
+ private short decodeEnumTag(short exp) {
+ readTagKey(KMEnumTag.cast(exp).getTagType());
+ byte[] buffer = (byte[]) bufferRef[0];
+ short startOff = scratchBuf[START_OFFSET];
+ // Enum Tag value will always be integer with max 1 byte length.
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short len = (short) (buffer[startOff] & ADDITIONAL_MASK);
+ byte enumVal = 0;
+ if (len > UINT8_LENGTH) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ if (len < UINT8_LENGTH) {
+ enumVal = (byte) (len & ADDITIONAL_MASK);
+ incrementStartOff((short) 1);
+ } else if (len == UINT8_LENGTH) {
+ incrementStartOff((short) 1);
+ // startOff is incremented so update the startOff
+ // with latest value before using it.
+ startOff = scratchBuf[START_OFFSET];
+ enumVal = buffer[startOff];
+ incrementStartOff((short) 1);
+ }
+ return KMEnumTag.instance(scratchBuf[TAG_KEY_OFFSET], enumVal);
+ }
+
+ private short decodeBoolTag(short exp) {
+ readTagKey(KMBoolTag.cast(exp).getTagType());
+ byte[] buffer = (byte[]) bufferRef[0];
+ short startOff = scratchBuf[START_OFFSET];
+ // BOOL Tag is a leaf node and it must always have tiny encoded uint value = 1.
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != 0x01) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ incrementStartOff((short) 1);
+ return KMBoolTag.instance(scratchBuf[TAG_KEY_OFFSET]);
+ }
+
+ private short decodeEnum(short exp) {
+ byte[] buffer = (byte[]) bufferRef[0];
+ short startOff = scratchBuf[START_OFFSET];
+ // Enum value will always be integer with max 1 byte length.
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short len = (short) (buffer[startOff] & ADDITIONAL_MASK);
+ byte enumVal;
+ if (len > UINT8_LENGTH) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ if (len < UINT8_LENGTH) {
+ enumVal = (byte) (len & ADDITIONAL_MASK);
+ incrementStartOff((short) 1);
+ } else {
+ incrementStartOff((short) 1);
+ // startOff is incremented so update the startOff
+ // with latest value before using it.
+ startOff = scratchBuf[START_OFFSET];
+ enumVal = buffer[startOff];
+ incrementStartOff((short) 1);
+ }
+ return KMEnum.instance(KMEnum.cast(exp).getEnumType(), enumVal);
+ }
+
+ private short decodeSimpleValue(short exp) {
+ short startOff = scratchBuf[START_OFFSET];
+ byte[] buffer = (byte[]) bufferRef[0];
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != SIMPLE_VALUE_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ byte addInfo = (byte) (buffer[startOff] & ADDITIONAL_MASK);
+ incrementStartOff((short) 1);
+ return KMSimpleValue.instance(addInfo);
+ }
+
+ private short decodeSemanticTagValue(short exp) {
+ // Decode tag.
+ short tag = readMajorTypeWithInteger(exp, SEMANTIC_TAG_TYPE, UINT32_LENGTH);
+ // Decode value pointer.
+ short valuePtr = decode(KMSemanticTag.cast(exp).getValuePtr());
+ return KMSemanticTag.instance(tag, valuePtr);
+ }
+
+ private short readMajorTypeWithInteger(short exp, short majorType, short maxLimit) {
+ short inst;
+ short startOff = scratchBuf[START_OFFSET];
+ byte[] buffer = (byte[]) bufferRef[0];
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != majorType) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short len = (short) (buffer[startOff] & ADDITIONAL_MASK);
+ if (len > maxLimit) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ incrementStartOff((short) 1);
+ // startOff is incremented so update the startOff
+ // with latest value before using it.
+ startOff = scratchBuf[START_OFFSET];
+ if (len < UINT8_LENGTH) {
+ inst = KMInteger.uint_8((byte) (len & ADDITIONAL_MASK));
+ } else if (len == UINT8_LENGTH) {
+ inst = KMInteger.instance(buffer, startOff, (short) 1);
+ incrementStartOff((short) 1);
+ } else if (len == UINT16_LENGTH) {
+ inst = KMInteger.instance(buffer, startOff, (short) 2);
+ incrementStartOff((short) 2);
+ } else if (len == UINT32_LENGTH) {
+ inst = KMInteger.instance(buffer, startOff, (short) 4);
+ incrementStartOff((short) 4);
+ } else {
+ inst = KMInteger.instance(buffer, startOff, (short) 8);
+ incrementStartOff((short) 8);
+ }
+ return inst;
+ }
+
+ private short decodeInteger(short exp) {
+ return readMajorTypeWithInteger(exp, UINT_TYPE, UINT64_LENGTH);
+ }
+
+ private short decodeNegIntegerValue(byte addInfo, byte[] buf, short startOffset) {
+ short inst;
+ short len = 0;
+ short scratchpad;
+ if (addInfo < UINT8_LENGTH) {
+ addInfo = (byte) (-1 - addInfo);
+ inst = KMNInteger.uint_8(addInfo);
+ } else {
+ switch (addInfo) {
+ case UINT8_LENGTH:
+ len = 1;
+ break;
+ case UINT16_LENGTH:
+ len = 2;
+ break;
+ case UINT32_LENGTH:
+ len = 4;
+ break;
+ case UINT64_LENGTH:
+ len = 8;
+ break;
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ // Do (-1 - N), as per cbor negative integer decoding rule.
+ // N is the integer value.
+ scratchpad = KMByteBlob.instance((short) (len * 3));
+ byte[] input = KMByteBlob.cast(scratchpad).getBuffer();
+ short offset = KMByteBlob.cast(scratchpad).getStartOff();
+ Util.arrayFillNonAtomic(input, offset, len, (byte) -1);
+ Util.arrayCopyNonAtomic(buf, startOffset, input, (short) (offset + len), len);
+ KMUtils.subtract(
+ input, offset, (short) (offset + len), (short) (offset + 2 * len), (byte) len);
+ inst = KMNInteger.instance(input, (short) (offset + 2 * len), len);
+ incrementStartOff(len);
+ }
+ return inst;
+ }
+
+ private short decodeNegInteger(short exp) {
+ short startOff = scratchBuf[START_OFFSET];
+ byte[] buffer = (byte[]) bufferRef[0];
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != NEG_INT_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short len = (short) (buffer[startOff] & ADDITIONAL_MASK);
+ if (len > UINT64_LENGTH) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ incrementStartOff((short) 1);
+ // startOff is incremented so update the startOff
+ // with latest value before using it.
+ startOff = scratchBuf[START_OFFSET];
+ return decodeNegIntegerValue((byte) len, buffer, startOff);
+ }
+
+ private short decodeTstr(short exp) {
+ short payloadLength = readMajorTypeWithPayloadLength(TSTR_TYPE);
+ short inst =
+ KMTextString.instance((byte[]) bufferRef[0], scratchBuf[START_OFFSET], payloadLength);
+ incrementStartOff(payloadLength);
+ return inst;
+ }
+
+ private short decodeByteBlob(short exp) {
+ short payloadLength = readMajorTypeWithPayloadLength(BYTES_TYPE);
+ short inst =
+ KMByteBlob.instance((byte[]) bufferRef[0], scratchBuf[START_OFFSET], payloadLength);
+ incrementStartOff(payloadLength);
+ return inst;
+ }
+
+ private short peekTagType() {
+ byte[] buffer = (byte[]) bufferRef[0];
+ short startOff = scratchBuf[START_OFFSET];
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+
+ if ((short) (buffer[startOff] & ADDITIONAL_MASK) != UINT32_LENGTH) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ return (short)
+ ((Util.makeShort(buffer[(short) (startOff + 1)], buffer[(short) (startOff + 2)]))
+ & KMType.TAG_TYPE_MASK);
+ }
+
+ private void readTagKey(short expectedTagType) {
+ byte[] buffer = (byte[]) bufferRef[0];
+ short startOff = scratchBuf[START_OFFSET];
+ if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != UINT32_LENGTH) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ incrementStartOff((short) 1);
+ short tagType = readShort();
+ scratchBuf[TAG_KEY_OFFSET] = readShort();
+ if (tagType != expectedTagType) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ }
+
+ // payload length cannot be more then 16 bits.
+ private short readMajorTypeWithPayloadLength(short majorType) {
+ short payloadLength;
+ byte val = readByte();
+ if ((short) (val & MAJOR_TYPE_MASK) != majorType) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short lenType = (short) (val & ADDITIONAL_MASK);
+ if (lenType > UINT16_LENGTH) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ if (lenType < UINT8_LENGTH) {
+ payloadLength = lenType;
+ } else if (lenType == UINT8_LENGTH) {
+ payloadLength = (short) (readByte() & 0xFF);
+ } else {
+ payloadLength = readShort();
+ }
+ return payloadLength;
+ }
+
+ private short readShort() {
+ byte[] buffer = (byte[]) bufferRef[0];
+ short startOff = scratchBuf[START_OFFSET];
+ short val = Util.makeShort(buffer[startOff], buffer[(short) (startOff + 1)]);
+ incrementStartOff((short) 2);
+ return val;
+ }
+
+ private byte readByte() {
+ short startOff = scratchBuf[START_OFFSET];
+ byte val = ((byte[]) bufferRef[0])[startOff];
+ incrementStartOff((short) 1);
+ return val;
+ }
+
+ private void incrementStartOff(short inc) {
+ scratchBuf[START_OFFSET] += inc;
+ if (scratchBuf[START_OFFSET] > scratchBuf[LEN_OFFSET]) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ }
+
+ public short readKeyblobVersion(byte[] buf, short bufOffset, short bufLen) {
+ bufferRef[0] = buf;
+ scratchBuf[START_OFFSET] = bufOffset;
+ scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen);
+ short arrayLen = readMajorTypeWithPayloadLength(ARRAY_TYPE);
+ if (arrayLen == 0) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short version = KMType.INVALID_VALUE;
+ try {
+ version = decodeInteger(KMInteger.exp());
+ } catch (Exception e) {
+ // Fail to decode Integer. It can happen if it is an old KeyBlob.
+ }
+ return version;
+ }
+
+ public short readCertificateChainHeaderLen(byte[] buf, short bufOffset, short bufLen) {
+ bufferRef[0] = buf;
+ scratchBuf[START_OFFSET] = bufOffset;
+ scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen);
+ readMajorTypeWithPayloadLength(BYTES_TYPE);
+ return (short) (scratchBuf[START_OFFSET] - bufOffset);
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEncoder.java
new file mode 100644
index 0000000..941a0ac
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEncoder.java
@@ -0,0 +1,785 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * This class encodes KMType structures to a cbor format data recursively. Encoded bytes are written
+ * on the buffer provided by the caller. An exception will be thrown if the encoded data length is
+ * greater than the buffer length provided.
+ */
+public class KMEncoder {
+
+ // major types
+ private static final byte UINT_TYPE = 0x00;
+ private static final byte NEG_INT_TYPE = 0x20;
+ private static final byte BYTES_TYPE = 0x40;
+ private static final byte TSTR_TYPE = 0x60;
+ private static final byte ARRAY_TYPE = (byte) 0x80;
+ private static final byte MAP_TYPE = (byte) 0xA0;
+ private static final byte SIMPLE_VALUE_TYPE = (byte) 0xE0;
+ private static final byte SEMANTIC_TAG_TYPE = (byte) 0xC0;
+
+ // masks
+ private static final byte ADDITIONAL_MASK = 0x1F;
+
+ // value length
+ private static final byte UINT8_LENGTH = (byte) 0x18;
+ private static final byte UINT16_LENGTH = (byte) 0x19;
+ private static final byte UINT32_LENGTH = (byte) 0x1A;
+ private static final byte UINT64_LENGTH = (byte) 0x1B;
+ private static final short TINY_PAYLOAD = 0x17;
+ private static final short SHORT_PAYLOAD = 0x100;
+ private static final byte STACK_SIZE = 50;
+ private static final byte SCRATCH_BUF_SIZE = 6;
+ private static final byte START_OFFSET = 0;
+ private static final byte LEN_OFFSET = 2;
+ private static final byte STACK_PTR_OFFSET = 4;
+
+ private Object[] bufferRef;
+ private short[] scratchBuf;
+ private short[] stack;
+
+ public KMEncoder() {
+ bufferRef = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET);
+ scratchBuf = JCSystem.makeTransientShortArray(SCRATCH_BUF_SIZE, JCSystem.CLEAR_ON_RESET);
+ stack = JCSystem.makeTransientShortArray(STACK_SIZE, JCSystem.CLEAR_ON_RESET);
+ bufferRef[0] = null;
+ scratchBuf[START_OFFSET] = (short) 0;
+ scratchBuf[LEN_OFFSET] = (short) 0;
+ scratchBuf[STACK_PTR_OFFSET] = (short) 0;
+ }
+
+ private void push(short objPtr) {
+ stack[scratchBuf[STACK_PTR_OFFSET]] = objPtr;
+ scratchBuf[STACK_PTR_OFFSET]++;
+ }
+
+ private short pop() {
+ scratchBuf[STACK_PTR_OFFSET]--;
+ return stack[scratchBuf[STACK_PTR_OFFSET]];
+ }
+
+ private void encode(short obj) {
+ push(obj);
+ }
+
+ /**
+ * This functions encodes the given object into the provider buffer space in cbor format.
+ *
+ * @param object Object to be encoded into cbor data.
+ * @param buffer Output where cbor data is copied.
+ * @param startOff is the start offset of the buffer.
+ * @param bufLen length of the buffer
+ * @param encoderOutLimitLen excepted encoded output length.
+ * @return length of the encoded buffer.
+ */
+ public short encode(
+ short object, byte[] buffer, short startOff, short bufLen, short encoderOutLimitLen) {
+ scratchBuf[STACK_PTR_OFFSET] = 0;
+ bufferRef[0] = buffer;
+ scratchBuf[START_OFFSET] = startOff;
+ if ((short) (startOff + encoderOutLimitLen) > bufLen) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ scratchBuf[LEN_OFFSET] = (short) (startOff + encoderOutLimitLen);
+ push(object);
+ encode();
+ return (short) (scratchBuf[START_OFFSET] - startOff);
+ }
+
+ public short encode(short object, byte[] buffer, short startOff, short bufLen) {
+ return encode(object, buffer, startOff, bufLen, (short) (bufLen - startOff));
+ }
+
+ // array{KMError.OK,Array{KMByteBlobs}}
+ public short encodeCert(byte[] certBuffer, short bufferStart, short certStart, short certLength) {
+ if (bufferStart > certStart) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ bufferRef[0] = certBuffer;
+ scratchBuf[START_OFFSET] = certStart;
+ scratchBuf[LEN_OFFSET] = (short) (certStart + 1);
+ // Byte Header + cert length
+ scratchBuf[START_OFFSET] -= getEncodedBytesLength(certLength);
+ // Array header - 1 elements i.e. 1 byte
+ scratchBuf[START_OFFSET]--;
+ if (scratchBuf[START_OFFSET] < bufferStart) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ bufferStart = scratchBuf[START_OFFSET];
+ writeMajorTypeWithLength(ARRAY_TYPE, (short) 1); // Array of 1 elements
+ writeMajorTypeWithLength(BYTES_TYPE, certLength); // Cert Byte Blob of length
+ return bufferStart;
+ }
+
+ private void encode() {
+ while (scratchBuf[STACK_PTR_OFFSET] > 0) {
+ short exp = pop();
+ byte type = KMType.getType(exp);
+ switch (type) {
+ case KMType.BYTE_BLOB_TYPE:
+ encodeByteBlob(exp);
+ break;
+ case KMType.TEXT_STRING_TYPE:
+ encodeTextString(exp);
+ break;
+ case KMType.INTEGER_TYPE:
+ encodeUnsignedInteger(exp);
+ break;
+ case KMType.SIMPLE_VALUE_TYPE:
+ encodeSimpleValue(exp);
+ break;
+ case KMType.NEG_INTEGER_TYPE:
+ encodeNegInteger(exp);
+ break;
+ case KMType.ARRAY_TYPE:
+ encodeArray(exp);
+ break;
+ case KMType.MAP_TYPE:
+ encodeMap(exp);
+ break;
+ case KMType.ENUM_TYPE:
+ encodeEnum(exp);
+ break;
+ case KMType.KEY_PARAM_TYPE:
+ encodeKeyParam(exp);
+ break;
+ case KMType.SEMANTIC_TAG_TYPE:
+ encodeSemanticTag(exp);
+ break;
+ case KMType.COSE_KEY_TYPE:
+ case KMType.COSE_HEADERS_TYPE:
+ case KMType.COSE_CERT_PAYLOAD_TYPE:
+ encodeCoseMap(exp);
+ break;
+ case KMType.KEY_CHAR_TYPE:
+ encodeKeyChar(exp);
+ break;
+ case KMType.VERIFICATION_TOKEN_TYPE:
+ encodeVeriToken(exp);
+ break;
+ case KMType.HMAC_SHARING_PARAM_TYPE:
+ encodeHmacSharingParam(exp);
+ break;
+ case KMType.HW_AUTH_TOKEN_TYPE:
+ encodeHwAuthToken(exp);
+ break;
+ case KMType.TAG_TYPE:
+ short tagType = KMTag.getTagType(exp);
+ encodeTag(tagType, exp);
+ break;
+ case KMType.COSE_PAIR_TAG_TYPE:
+ short cosePairTagType = KMCosePairTagType.getTagValueType(exp);
+ encodeCosePairTag(cosePairTagType, exp);
+ break;
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ }
+ }
+
+ private void encodeCosePairIntegerTag(short exp) {
+ KMCosePairIntegerTag cosePairIntTag = KMCosePairIntegerTag.cast(exp);
+ // push key and value ptr in stack to get encoded.
+ encode(cosePairIntTag.getValuePtr());
+ encode(cosePairIntTag.getKeyPtr());
+ }
+
+ private void encodeCosePairByteBlobTag(short exp) {
+ KMCosePairByteBlobTag cosePairByteBlobTag = KMCosePairByteBlobTag.cast(exp);
+ // push key and value ptr in stack to get encoded.
+ encode(cosePairByteBlobTag.getValuePtr());
+ encode(cosePairByteBlobTag.getKeyPtr());
+ }
+
+ private void encodeCosePairCoseKeyTag(short exp) {
+ KMCosePairCoseKeyTag cosePairCoseKeyTag = KMCosePairCoseKeyTag.cast(exp);
+ // push key and value ptr in stack to get encoded.
+ encode(cosePairCoseKeyTag.getValuePtr());
+ encode(cosePairCoseKeyTag.getKeyPtr());
+ }
+
+ private void encodeCosePairTextStringTag(short exp) {
+ KMCosePairTextStringTag cosePairTextStringTag = KMCosePairTextStringTag.cast(exp);
+ // push key and value ptr in stack to get encoded.
+ encode(cosePairTextStringTag.getValuePtr());
+ encode(cosePairTextStringTag.getKeyPtr());
+ }
+
+ private void encodeCosePairSimpleValueTag(short exp) {
+ KMCosePairSimpleValueTag cosePairSimpleValueTag = KMCosePairSimpleValueTag.cast(exp);
+ // push key and value ptr in stack to get encoded.
+ encode(cosePairSimpleValueTag.getValuePtr());
+ encode(cosePairSimpleValueTag.getKeyPtr());
+ }
+
+ private void encodeCosePairNegIntegerTag(short exp) {
+ KMCosePairNegIntegerTag cosePairNegIntegerTag = KMCosePairNegIntegerTag.cast(exp);
+ // push key and value ptr in stack to get encoded.
+ encode(cosePairNegIntegerTag.getValuePtr());
+ encode(cosePairNegIntegerTag.getKeyPtr());
+ }
+
+ private void encodeCosePairTag(short tagType, short exp) {
+ switch (tagType) {
+ case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+ encodeCosePairByteBlobTag(exp);
+ return;
+ case KMType.COSE_PAIR_INT_TAG_TYPE:
+ encodeCosePairIntegerTag(exp);
+ return;
+ case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+ encodeCosePairNegIntegerTag(exp);
+ return;
+ case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE:
+ encodeCosePairSimpleValueTag(exp);
+ return;
+ case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE:
+ encodeCosePairTextStringTag(exp);
+ return;
+ case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE:
+ encodeCosePairCoseKeyTag(exp);
+ return;
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ }
+
+ private void encodeTag(short tagType, short exp) {
+ switch (tagType) {
+ case KMType.BIGNUM_TAG:
+ encodeBignumTag(exp);
+ return;
+ case KMType.BYTES_TAG:
+ encodeBytesTag(exp);
+ return;
+ case KMType.BOOL_TAG:
+ encodeBoolTag(exp);
+ return;
+ case KMType.UINT_TAG:
+ case KMType.ULONG_TAG:
+ case KMType.DATE_TAG:
+ encodeIntegerTag(exp);
+ return;
+ case KMType.ULONG_ARRAY_TAG:
+ case KMType.UINT_ARRAY_TAG:
+ encodeIntegerArrayTag(exp);
+ return;
+ case KMType.ENUM_TAG:
+ encodeEnumTag(exp);
+ return;
+ case KMType.ENUM_ARRAY_TAG:
+ encodeEnumArrayTag(exp);
+ return;
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ }
+
+ private void encodeCoseMap(short obj) {
+ encodeAsMap(KMCoseMap.getVals(obj));
+ }
+
+ private void encodeKeyParam(short obj) {
+ encodeAsMap(KMKeyParameters.cast(obj).getVals());
+ }
+
+ private void encodeKeyChar(short obj) {
+ encode(KMKeyCharacteristics.cast(obj).getVals());
+ }
+
+ private void encodeVeriToken(short obj) {
+ encode(KMVerificationToken.cast(obj).getVals());
+ }
+
+ private void encodeHwAuthToken(short obj) {
+ encode(KMHardwareAuthToken.cast(obj).getVals());
+ }
+
+ private void encodeHmacSharingParam(short obj) {
+ encode(KMHmacSharingParameters.cast(obj).getVals());
+ }
+
+ private void encodeArray(short obj) {
+ writeMajorTypeWithLength(ARRAY_TYPE, KMArray.cast(obj).length());
+ short len = KMArray.cast(obj).length();
+ short index = (short) (len - 1);
+ short subObj;
+ while (index >= 0) {
+ subObj = KMArray.cast(obj).get(index);
+ if (subObj != KMType.INVALID_VALUE) {
+ encode(subObj);
+ }
+ index--;
+ }
+ }
+
+ public void encodeArrayOnlyLength(short arrLength, byte[] buffer, short offset, short length) {
+ bufferRef[0] = buffer;
+ scratchBuf[START_OFFSET] = offset;
+ scratchBuf[LEN_OFFSET] = (short) (offset + length + 1);
+ writeMajorTypeWithLength(ARRAY_TYPE, length);
+ }
+
+ private void encodeMap(short obj) {
+ writeMajorTypeWithLength(MAP_TYPE, KMMap.cast(obj).length());
+ short len = KMMap.cast(obj).length();
+ short index = (short) (len - 1);
+ while (index >= 0) {
+ encode(KMMap.cast(obj).getKeyValue(index));
+ encode(KMMap.cast(obj).getKey(index));
+ index--;
+ }
+ }
+
+ private void encodeAsMap(short obj) {
+ writeMajorTypeWithLength(MAP_TYPE, KMArray.cast(obj).length());
+ short len = KMArray.cast(obj).length();
+ short index = (short) (len - 1);
+ short inst;
+ while (index >= 0) {
+ inst = KMArray.cast(obj).get(index);
+ encode(inst);
+ index--;
+ }
+ }
+
+ private void encodeIntegerArrayTag(short obj) {
+ writeTag(KMIntegerArrayTag.cast(obj).getTagType(), KMIntegerArrayTag.cast(obj).getKey());
+ encode(KMIntegerArrayTag.cast(obj).getValues());
+ }
+
+ private void encodeEnumArrayTag(short obj) {
+ writeTag(KMEnumArrayTag.cast(obj).getTagType(), KMEnumArrayTag.cast(obj).getKey());
+ encode(KMEnumArrayTag.cast(obj).getValues());
+ }
+
+ private void encodeIntegerTag(short obj) {
+ writeTag(KMIntegerTag.cast(obj).getTagType(), KMIntegerTag.cast(obj).getKey());
+ encode(KMIntegerTag.cast(obj).getValue());
+ }
+
+ private void encodeBignumTag(short obj) {
+ writeTag(KMBignumTag.getTagType(obj), KMBignumTag.getKey(obj));
+ encode(KMBignumTag.cast(obj).getValue());
+ }
+
+ private void encodeBytesTag(short obj) {
+ writeTag(KMByteTag.cast(obj).getTagType(), KMByteTag.cast(obj).getKey());
+ encode(KMByteTag.cast(obj).getValue());
+ }
+
+ private void encodeBoolTag(short obj) {
+ writeTag(KMBoolTag.cast(obj).getTagType(), KMBoolTag.cast(obj).getKey());
+ writeByteValue(KMBoolTag.cast(obj).getVal());
+ }
+
+ private void encodeEnumTag(short obj) {
+ writeTag(KMEnumTag.cast(obj).getTagType(), KMEnumTag.cast(obj).getKey());
+ writeByteValue(KMEnumTag.cast(obj).getValue());
+ }
+
+ private void encodeEnum(short obj) {
+ writeByteValue(KMEnum.cast(obj).getVal());
+ }
+
+ private void encodeInteger(byte[] val, short len, short startOff, short majorType) {
+ // find out the most significant byte
+ short msbIndex = findMsb(val, startOff, len);
+ // find the difference between most significant byte and len
+ short diff = (short) (len - msbIndex);
+ if (diff == 0) {
+ writeByte((byte) (majorType | 0));
+ } else if ((diff == 1)
+ && (val[(short) (startOff + msbIndex)] < UINT8_LENGTH)
+ && (val[(short) (startOff + msbIndex)] >= 0)) {
+ writeByte((byte) (majorType | val[(short) (startOff + msbIndex)]));
+ } else if (diff == 1) {
+ writeByte((byte) (majorType | UINT8_LENGTH));
+ writeByte(val[(short) (startOff + msbIndex)]);
+ } else if (diff == 2) {
+ writeByte((byte) (majorType | UINT16_LENGTH));
+ writeBytes(val, (short) (startOff + msbIndex), (short) 2);
+ } else if (diff <= 4) {
+ writeByte((byte) (majorType | UINT32_LENGTH));
+ writeBytes(val, (short) (startOff + len - 4), (short) 4);
+ } else {
+ writeByte((byte) (majorType | UINT64_LENGTH));
+ writeBytes(val, startOff, (short) 8);
+ }
+ }
+
+ // find out the most significant byte
+ public short findMsb(byte[] buf, short offset, short len) {
+ byte index = 0;
+ // find out the most significant byte
+ while (index < len) {
+ if (buf[(short) (offset + index)] > 0) {
+ break;
+ } else if (buf[(short) (offset + index)] < 0) {
+ break;
+ }
+ index++; // index will be equal to len if value is 0.
+ }
+ return index;
+ }
+
+ public void computeOnesCompliment(short msbIndex, byte[] buf, short offset, short len) {
+ // find the difference between most significant byte and len
+ short diff = (short) (len - msbIndex);
+ short correctedOffset = offset;
+ short correctedLen = len;
+ // The offset and length of the buffer for Short and Byte types should be
+ // corrected before computing the 1s compliment. The reason for doing this
+ // is to avoid computation of 1s compliment on the MSB bytes.
+ if (diff == 0) {
+ // Fail
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ } else if (diff == 1) {
+ correctedOffset = (short) (offset + 3);
+ correctedLen = 1;
+ } else if (diff == 2) {
+ correctedOffset = (short) (offset + 2);
+ correctedLen = 2;
+ }
+ // For int and long values the len and offset values are always proper.
+ // int - 4 bytes
+ // long - 8 bytes.
+ KMUtils.computeOnesCompliment(buf, correctedOffset, correctedLen);
+ }
+
+ // Encoding rule for negative Integers is taken from
+ // https://datatracker.ietf.org/doc/html/rfc7049#section-2.1, Major type 1.
+ public short handleNegIntegerEncodingRule(byte[] buf, short offset, short len) {
+ short msbIndex = findMsb(buf, offset, len);
+ // Do -1-N, where N is the negative integer
+ // The value of -1-N is equal to the 1s compliment of N.
+ computeOnesCompliment(msbIndex, buf, offset, len);
+ return msbIndex;
+ }
+
+ // Note: This function modifies the buffer's actual value. So after encoding, restore the original
+ // value by calling removeNegIntegerEncodingRule().
+ public short applyNegIntegerEncodingRule(byte[] buf, short offset, short len) {
+ return handleNegIntegerEncodingRule(buf, offset, len);
+ }
+
+ public void removeNegIntegerEncodingRule(
+ byte[] buf, short offset, short len, short origMsbIndex) {
+ // Do -1-N, where N is the negative integer
+ // The value of -1-N is equal to the 1s compliment of N.
+ computeOnesCompliment(origMsbIndex, buf, offset, len);
+ }
+
+ private void encodeNegInteger(short obj) {
+ byte[] val = KMNInteger.cast(obj).getBuffer();
+ short len = KMNInteger.cast(obj).length();
+ short startOff = KMNInteger.cast(obj).getStartOff();
+ short msbIndex = applyNegIntegerEncodingRule(val, startOff, len);
+ encodeInteger(val, len, startOff, NEG_INT_TYPE);
+ removeNegIntegerEncodingRule(val, startOff, len, msbIndex);
+ }
+
+ private void encodeSemanticTag(short obj) {
+ short tag = KMSemanticTag.cast(obj).getKeyPtr();
+ encode(KMSemanticTag.cast(obj).getValuePtr());
+ encodeInteger(
+ KMInteger.cast(tag).getBuffer(),
+ KMInteger.cast(tag).length(),
+ KMInteger.cast(tag).getStartOff(),
+ SEMANTIC_TAG_TYPE);
+ }
+
+ private void encodeUnsignedInteger(short obj) {
+ byte[] val = KMInteger.cast(obj).getBuffer();
+ short len = KMInteger.cast(obj).length();
+ short startOff = KMInteger.cast(obj).getStartOff();
+ encodeInteger(val, len, startOff, UINT_TYPE);
+ }
+
+ private void encodeSimpleValue(short obj) {
+ byte value = KMSimpleValue.cast(obj).getValue();
+ writeByte((byte) (SIMPLE_VALUE_TYPE | value));
+ }
+
+ private void encodeTextString(short obj) {
+ writeMajorTypeWithLength(TSTR_TYPE, KMTextString.cast(obj).length());
+ writeBytes(
+ KMTextString.cast(obj).getBuffer(),
+ KMTextString.cast(obj).getStartOff(),
+ KMTextString.cast(obj).length());
+ }
+
+ public short encodeByteBlobHeader(short bufLen, byte[] buffer, short startOff, short length) {
+ bufferRef[0] = buffer;
+ scratchBuf[START_OFFSET] = startOff;
+ scratchBuf[LEN_OFFSET] = (short) (startOff + length + 1);
+ writeMajorTypeWithLength(BYTES_TYPE, bufLen);
+ return (short) (scratchBuf[START_OFFSET] - startOff);
+ }
+
+ private void encodeByteBlob(short obj) {
+ writeMajorTypeWithLength(BYTES_TYPE, KMByteBlob.cast(obj).length());
+ writeBytes(
+ KMByteBlob.cast(obj).getBuffer(),
+ KMByteBlob.cast(obj).getStartOff(),
+ KMByteBlob.cast(obj).length());
+ }
+
+ public short getEncodedLength(short ptr) {
+ short len = 0;
+ short type = KMType.getType(ptr);
+ switch (type) {
+ case KMType.BYTE_BLOB_TYPE:
+ len += getEncodedByteBlobLength(ptr);
+ break;
+ case KMType.TEXT_STRING_TYPE:
+ len += getEncodedTextStringLength(ptr);
+ break;
+ case KMType.INTEGER_TYPE:
+ len += getEncodedIntegerLength(ptr);
+ break;
+ case KMType.NEG_INTEGER_TYPE:
+ len += getEncodedNegIntegerLength(ptr);
+ break;
+ case KMType.ARRAY_TYPE:
+ len += getEncodedArrayLen(ptr);
+ break;
+ case KMType.MAP_TYPE:
+ len += getEncodedMapLen(ptr);
+ break;
+ case KMType.COSE_PAIR_TAG_TYPE:
+ short cosePairTagType = KMCosePairTagType.getTagValueType(ptr);
+ len += getEncodedCosePairTagLen(cosePairTagType, ptr);
+ break;
+ case KMType.COSE_KEY_TYPE:
+ case KMType.COSE_HEADERS_TYPE:
+ case KMType.COSE_CERT_PAYLOAD_TYPE:
+ len += getEncodedArrayLen(KMCoseMap.getVals(ptr));
+ break;
+ default:
+ KMException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return len;
+ }
+
+ private short getEncodedCosePairTagLen(short tagType, short exp) {
+ short length = 0;
+ switch (tagType) {
+ case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+ KMCosePairByteBlobTag cosePairByteBlobTag = KMCosePairByteBlobTag.cast(exp);
+ length = getEncodedLength(cosePairByteBlobTag.getKeyPtr());
+ length += getEncodedLength(cosePairByteBlobTag.getValuePtr());
+ break;
+ case KMType.COSE_PAIR_INT_TAG_TYPE:
+ KMCosePairIntegerTag cosePairIntTag = KMCosePairIntegerTag.cast(exp);
+ length = getEncodedLength(cosePairIntTag.getValuePtr());
+ length += getEncodedLength(cosePairIntTag.getKeyPtr());
+ break;
+ case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+ KMCosePairNegIntegerTag cosePairNegIntegerTag = KMCosePairNegIntegerTag.cast(exp);
+ length = getEncodedLength(cosePairNegIntegerTag.getValuePtr());
+ length += getEncodedLength(cosePairNegIntegerTag.getKeyPtr());
+ break;
+ case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE:
+ KMCosePairSimpleValueTag cosePairSimpleValueTag = KMCosePairSimpleValueTag.cast(exp);
+ length = getEncodedLength(cosePairSimpleValueTag.getValuePtr());
+ length += getEncodedLength(cosePairSimpleValueTag.getKeyPtr());
+ break;
+ case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE:
+ KMCosePairTextStringTag cosePairTextStringTag = KMCosePairTextStringTag.cast(exp);
+ length = getEncodedLength(cosePairTextStringTag.getValuePtr());
+ length += getEncodedLength(cosePairTextStringTag.getKeyPtr());
+ break;
+ case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE:
+ KMCosePairCoseKeyTag cosePairCoseKeyTag = KMCosePairCoseKeyTag.cast(exp);
+ length = getEncodedLength(cosePairCoseKeyTag.getValuePtr());
+ length += getEncodedLength(cosePairCoseKeyTag.getKeyPtr());
+ break;
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ return length;
+ }
+
+ private short getEncodedMapLen(short obj) {
+ short mapLen = KMMap.cast(obj).length();
+ short len = getEncodedBytesLength(mapLen);
+ short index = 0;
+ while (index < mapLen) {
+ len += getEncodedLength(KMMap.cast(obj).getKey(index));
+ len += getEncodedLength(KMMap.cast(obj).getKeyValue(index));
+ index++;
+ }
+ return len;
+ }
+
+ private short getEncodedArrayLen(short obj) {
+ short arrLen = KMArray.cast(obj).length();
+ short len = getEncodedBytesLength(arrLen);
+ short index = 0;
+ short subObj;
+ while (index < arrLen) {
+ subObj = KMArray.cast(obj).get(index);
+ if (subObj != KMType.INVALID_VALUE) {
+ len += getEncodedLength(subObj);
+ }
+ index++;
+ }
+ return len;
+ }
+
+ public short getEncodedBytesLength(short len) {
+ short ret = 0;
+ if (len < KMEncoder.UINT8_LENGTH && len >= 0) {
+ ret = 1;
+ } else if (len >= KMEncoder.UINT8_LENGTH && len <= (short) 0x00FF) {
+ ret = 2;
+ } else if (len > (short) 0x00FF && len <= (short) 0x7FFF) {
+ ret = 3;
+ } else {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ return ret;
+ }
+
+ private short getEncodedByteBlobLength(short obj) {
+ short len = KMByteBlob.cast(obj).length();
+ len += getEncodedBytesLength(len);
+ return len;
+ }
+
+ private short getEncodedTextStringLength(short obj) {
+ short len = KMTextString.cast(obj).length();
+ len += getEncodedBytesLength(len);
+ return len;
+ }
+
+ private short getEncodedNegIntegerLength(short obj) {
+ byte[] buf = KMNInteger.cast(obj).getBuffer();
+ short len = KMNInteger.cast(obj).length();
+ short offset = KMNInteger.cast(obj).getStartOff();
+ short msbIndex = applyNegIntegerEncodingRule(buf, offset, len);
+ short ret = getEncodedIntegerLength(buf, offset, len);
+ removeNegIntegerEncodingRule(buf, offset, len, msbIndex);
+ return ret;
+ }
+
+ private short getEncodedIntegerLength(byte[] val, short startOff, short len) {
+ short msbIndex = findMsb(val, startOff, len);
+ // find the difference between most significant byte and len
+ short diff = (short) (len - msbIndex);
+ switch (diff) {
+ case 0:
+ case 1: // Byte
+ if ((val[(short) (startOff + msbIndex)] < KMEncoder.UINT8_LENGTH)
+ && (val[(short) (startOff + msbIndex)] >= 0)) {
+ return (short) 1;
+ } else {
+ return (short) 2;
+ }
+ case 2: // Short
+ return (short) 3;
+ case 3:
+ case 4: // UInt32
+ return (short) 5;
+ case 5:
+ case 6:
+ case 7:
+ case 8: // UInt64
+ return (short) 9;
+ default:
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ return 0;
+ }
+
+ private short getEncodedIntegerLength(short obj) {
+ byte[] val = KMInteger.cast(obj).getBuffer();
+ short len = KMInteger.cast(obj).length();
+ short startOff = KMInteger.cast(obj).getStartOff();
+ return getEncodedIntegerLength(val, startOff, len);
+ }
+
+ private void writeByteValue(byte val) {
+ if ((val < UINT8_LENGTH) && (val >= 0)) {
+ writeByte((byte) (UINT_TYPE | val));
+ } else {
+ writeByte((byte) (UINT_TYPE | UINT8_LENGTH));
+ writeByte(val);
+ }
+ }
+
+ private void writeTag(short tagType, short tagKey) {
+ writeByte((byte) (UINT_TYPE | UINT32_LENGTH));
+ writeShort(tagType);
+ writeShort(tagKey);
+ }
+
+ private void writeMajorTypeWithLength(byte majorType, short len) {
+ if (len <= TINY_PAYLOAD) {
+ writeByte((byte) (majorType | (byte) (len & ADDITIONAL_MASK)));
+ } else if (len < SHORT_PAYLOAD) {
+ writeByte((byte) (majorType | UINT8_LENGTH));
+ writeByte((byte) (len & 0xFF));
+ } else {
+ writeByte((byte) (majorType | UINT16_LENGTH));
+ writeShort(len);
+ }
+ }
+
+ private void writeBytes(byte[] buf, short start, short len) {
+ byte[] buffer = (byte[]) bufferRef[0];
+ Util.arrayCopyNonAtomic(buf, start, buffer, scratchBuf[START_OFFSET], len);
+ incrementStartOff(len);
+ }
+
+ private void writeShort(short val) {
+ byte[] buffer = (byte[]) bufferRef[0];
+ buffer[scratchBuf[START_OFFSET]] = (byte) ((val >> 8) & 0xFF);
+ incrementStartOff((short) 1);
+ buffer[scratchBuf[START_OFFSET]] = (byte) ((val & 0xFF));
+ incrementStartOff((short) 1);
+ }
+
+ private void writeByte(byte val) {
+ byte[] buffer = (byte[]) bufferRef[0];
+ buffer[scratchBuf[START_OFFSET]] = val;
+ incrementStartOff((short) 1);
+ }
+
+ private void incrementStartOff(short inc) {
+ scratchBuf[START_OFFSET] += inc;
+ if (scratchBuf[START_OFFSET] >= scratchBuf[LEN_OFFSET]) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ }
+
+ public short encodeArrayHeader(short bufLen, byte[] buffer, short startOff, short length) {
+ bufferRef[0] = buffer;
+ scratchBuf[START_OFFSET] = startOff;
+ scratchBuf[LEN_OFFSET] = (short) (startOff + length + 1);
+ writeMajorTypeWithLength(ARRAY_TYPE, bufLen);
+ return (short) (scratchBuf[START_OFFSET] - startOff);
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEnum.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEnum.java
new file mode 100644
index 0000000..44bf477
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEnum.java
@@ -0,0 +1,166 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMEnum represents an enumeration specified in android keymaster hal specifications. It
+ * corresponds to uint CBOR type and it is a byte value. struct{byte ENUM_TYPE; short length;
+ * struct{short enumType; byte val}}
+ */
+public class KMEnum extends KMType {
+
+ private static KMEnum prototype;
+
+ // The allowed enum types.
+ private static short[] types = {
+ HARDWARE_TYPE,
+ KEY_FORMAT,
+ KEY_DERIVATION_FUNCTION,
+ VERIFIED_BOOT_STATE,
+ DEVICE_LOCKED,
+ USER_AUTH_TYPE,
+ PURPOSE,
+ ECCURVE,
+ RULE
+ };
+
+ private static Object[] enums = null;
+
+ private KMEnum() {}
+
+ private static KMEnum proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMEnum();
+ }
+ KMType.instanceTable[KM_ENUM_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ return KMType.exp(ENUM_TYPE);
+ }
+
+ public static KMEnum cast(short ptr) {
+ if (heap[ptr] != ENUM_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static short instance(short enumType) {
+ if (!validateEnum(enumType, NO_VALUE)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = KMType.instance(ENUM_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), enumType);
+ return ptr;
+ }
+
+ public static short instance(short enumType, byte val) {
+ if (!validateEnum(enumType, val)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = KMType.instance(ENUM_TYPE, (short) 3);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), enumType);
+ heap[(short) (ptr + TLV_HEADER_SIZE + 2)] = val;
+ return ptr;
+ }
+
+ private static void create() {
+ // The allowed enum values to corresponding enum types in the types array.
+ if (enums == null) {
+ enums =
+ new Object[] {
+ new byte[] {SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX},
+ new byte[] {X509, PKCS8, RAW},
+ new byte[] {
+ DERIVATION_NONE,
+ RFC5869_SHA256,
+ ISO18033_2_KDF1_SHA1,
+ ISO18033_2_KDF1_SHA256,
+ ISO18033_2_KDF2_SHA1,
+ ISO18033_2_KDF2_SHA256
+ },
+ new byte[] {SELF_SIGNED_BOOT, VERIFIED_BOOT, UNVERIFIED_BOOT, FAILED_BOOT},
+ new byte[] {DEVICE_LOCKED_TRUE, DEVICE_LOCKED_FALSE},
+ new byte[] {USER_AUTH_NONE, PASSWORD, FINGERPRINT, BOTH},
+ new byte[] {ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY, AGREE_KEY},
+ new byte[] {P_224, P_256, P_384, P_521},
+ new byte[] {IGNORE_INVALID_TAGS, FAIL_ON_INVALID_TAGS}
+ };
+ }
+ }
+
+ // isValidTag enumeration keys and values.
+ private static boolean validateEnum(short key, byte value) {
+ create();
+ byte[] vals;
+ short enumInd;
+ // check if key exists
+ short index = (short) types.length;
+ while (--index >= 0) {
+ if (types[index] == key) {
+ // check if value given
+ if (value != NO_VALUE) {
+ // check if the value exist
+ vals = (byte[]) enums[index];
+ enumInd = (short) vals.length;
+ while (--enumInd >= 0) {
+ if (vals[enumInd] == value) {
+ // return true if value exist
+ return true;
+ }
+ }
+ // return false if value does not exist
+ return false;
+ }
+ // return true if key exist and value not given
+ return true;
+ }
+ }
+ // return false if key does not exist
+ return false;
+ }
+
+ public short length() {
+ return Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + 1));
+ }
+
+ public byte getVal() {
+ return heap[(short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2)];
+ }
+
+ public void setVal(byte val) {
+ heap[(short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2)] = val;
+ }
+
+ public short getEnumType() {
+ return Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public void setEnumType(short type) {
+ Util.setShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE), type);
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java
new file mode 100644
index 0000000..ea73c40
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java
@@ -0,0 +1,305 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMEnumArrayTag represents ENUM_REP tag type. It has following structure, struct{byte TAG_TYPE;
+ * short length; struct{short ENUM_ARRAY_TAG; short tagKey; sequence of byte values}}
+ */
+public class KMEnumArrayTag extends KMTag {
+
+ private static KMEnumArrayTag prototype;
+
+ // The allowed tag keys of enum array type.
+ private static short[] tags = {PURPOSE, BLOCK_MODE, DIGEST, PADDING, RSA_OAEP_MGF_DIGEST};
+
+ // Tag Values.
+ private static Object[] enums = null;
+
+ private KMEnumArrayTag() {}
+
+ private static KMEnumArrayTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMEnumArrayTag();
+ }
+ KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short blobPtr = KMByteBlob.exp();
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), ENUM_ARRAY_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), blobPtr);
+ return ptr;
+ }
+
+ public static short instance(short key) {
+ byte[] vals = getAllowedEnumValues(key);
+ if (vals == null) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short blobPtr = KMByteBlob.exp();
+ return instance(key, blobPtr);
+ }
+
+ public static short instance(short key, short byteBlob) {
+ byte[] allowedVals = getAllowedEnumValues(key);
+ if (allowedVals == null) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ KMByteBlob blob = KMByteBlob.cast(byteBlob);
+ short byteIndex = 0;
+ short enumIndex;
+ boolean validValue;
+ while (byteIndex < blob.length()) {
+ enumIndex = 0;
+ validValue = false;
+ while (enumIndex < allowedVals.length) {
+ if (blob.get(byteIndex) == allowedVals[enumIndex]) {
+ validValue = true;
+ break;
+ }
+ enumIndex++;
+ }
+ if (!validValue) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ byteIndex++;
+ }
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), ENUM_ARRAY_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), byteBlob);
+ return ptr;
+ }
+
+ public static KMEnumArrayTag cast(short ptr) {
+ if (heap[ptr] != TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != ENUM_ARRAY_TAG) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static void create() {
+ if (enums == null) {
+ // allowed tag values.
+ enums =
+ new Object[] {
+ new byte[] {ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY, AGREE_KEY},
+ new byte[] {ECB, CBC, CTR, GCM},
+ new byte[] {DIGEST_NONE, MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512},
+ new byte[] {
+ PADDING_NONE, RSA_OAEP, RSA_PSS, RSA_PKCS1_1_5_ENCRYPT, RSA_PKCS1_1_5_SIGN, PKCS7
+ },
+ new byte[] {DIGEST_NONE, MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512},
+ };
+ }
+ }
+
+ private static byte[] getAllowedEnumValues(short key) {
+ create();
+ short index = (short) tags.length;
+ while (--index >= 0) {
+ if (tags[index] == key) {
+ return (byte[]) enums[index];
+ }
+ }
+ return null;
+ }
+
+ public static short getValues(short tagId, short params, byte[] buf, short start) {
+ short tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params);
+ if (tag == KMType.INVALID_VALUE) {
+ return KMType.INVALID_VALUE;
+ }
+ tag = KMEnumArrayTag.cast(tag).getValues();
+ return KMByteBlob.cast(tag).getValues(buf, start);
+ }
+
+ public static boolean contains(short tagId, short tagValue, short params) {
+ short tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params);
+ if (tag != KMType.INVALID_VALUE) {
+ short index = 0;
+ while (index < KMEnumArrayTag.cast(tag).length()) {
+ if (tagValue == KMEnumArrayTag.cast(tag).get(index)) {
+ return true;
+ }
+ index++;
+ }
+ }
+ return false;
+ }
+
+ public static short length(short tagId, short params) {
+ short tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params);
+ if (tag != KMType.INVALID_VALUE) {
+ return KMEnumArrayTag.cast(tag).length();
+ }
+ return KMType.INVALID_VALUE;
+ }
+
+ public short getKey() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short getTagType() {
+ return KMType.ENUM_ARRAY_TAG;
+ }
+
+ public short getValues() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+
+ public short length() {
+ short blobPtr =
+ Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ return KMByteBlob.cast(blobPtr).length();
+ }
+
+ public short get(short index) {
+ return KMByteBlob.cast(getValues()).get(index);
+ }
+
+ public boolean contains(short tagValue) {
+ short index = 0;
+ while (index < length()) {
+ if (get(index) == (byte) tagValue) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ public boolean isValidDigests(byte alg) {
+ short index = 0;
+ short digest;
+ while (index < length()) {
+ digest = get(index);
+ switch (alg) {
+ case KMType.EC:
+ case KMType.RSA:
+ if (digest != KMType.DIGEST_NONE && digest != KMType.SHA2_256 && digest != KMType.SHA1) {
+ return false;
+ }
+ break;
+ case KMType.HMAC:
+ if (digest != KMType.SHA2_256) {
+ return false;
+ }
+ break;
+ case KMType.AES:
+ case KMType.DES:
+ if (digest != KMType.DIGEST_NONE) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ index++;
+ }
+ return true;
+ }
+
+ public boolean isValidPaddingModes(byte alg) {
+ short index = 0;
+ short padding;
+ while (index < length()) {
+ padding = get(index);
+ switch (alg) {
+ case KMType.RSA:
+ if (padding != KMType.RSA_OAEP
+ && padding != KMType.PADDING_NONE
+ && padding != KMType.RSA_PKCS1_1_5_SIGN
+ && padding != KMType.RSA_PKCS1_1_5_ENCRYPT
+ && padding != KMType.RSA_PSS) {
+ return false;
+ }
+ break;
+ case KMType.AES:
+ case KMType.DES:
+ if (padding != KMType.PKCS7 && padding != KMType.PADDING_NONE) {
+ return false;
+ }
+ break;
+ case KMType.EC:
+ case KMType.HMAC:
+ if (padding != PADDING_NONE) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ index++;
+ }
+ return true;
+ }
+
+ public boolean isValidPurpose(byte alg) {
+ short index = 0;
+ short purpose;
+ while (index < length()) {
+ purpose = get(index);
+ switch (purpose) {
+ case KMType.DECRYPT:
+ case KMType.ENCRYPT:
+ if (alg != KMType.RSA && alg != KMType.AES && alg != KMType.DES) {
+ return false;
+ }
+ break;
+ case KMType.SIGN:
+ case KMType.VERIFY:
+ if (alg != KMType.HMAC && alg != KMType.RSA && alg != KMType.EC) {
+ return false;
+ }
+ break;
+ case KMType.WRAP_KEY:
+ if (alg != KMType.RSA) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ index++;
+ }
+ return true;
+ }
+
+ public boolean isValidBlockMode(byte alg) {
+ if (alg == KMType.AES || alg == KMType.DES) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEnumTag.java
new file mode 100644
index 0000000..a7bcbe6
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMEnumTag.java
@@ -0,0 +1,152 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMEnumTag represents ENUM Tag type specified in android keymaster hal specifications. struct{byte
+ * TAG_TYPE; short length; struct{short ENUM_TAG; short tagKey; byte value}}
+ */
+public class KMEnumTag extends KMTag {
+
+ private static KMEnumTag prototype;
+
+ // The allowed tag keys of type enum tag.
+ private static short[] tags = {
+ ALGORITHM, ECCURVE, BLOB_USAGE_REQ, USER_AUTH_TYPE, ORIGIN, HARDWARE_TYPE
+ };
+
+ private static Object[] enums = null;
+
+ private KMEnumTag() {}
+
+ private static KMEnumTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMEnumTag();
+ }
+ KMType.instanceTable[KM_ENUM_TAG_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ short ptr = instance(TAG_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), ENUM_TAG);
+ return ptr;
+ }
+
+ public static short instance(short key) {
+ if (!validateEnum(key, NO_VALUE)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = KMType.instance(TAG_TYPE, (short) 4);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), ENUM_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+ return ptr;
+ }
+
+ public static short instance(short key, byte val) {
+ if (!validateEnum(key, val)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(TAG_TYPE, (short) 5);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), ENUM_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+ heap[(short) (ptr + TLV_HEADER_SIZE + 4)] = val;
+ return ptr;
+ }
+
+ public static KMEnumTag cast(short ptr) {
+ if (heap[ptr] != TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != ENUM_TAG) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static void create() {
+ if (enums == null) {
+ // enum tag values.
+ enums =
+ new Object[] {
+ new byte[] {RSA, DES, EC, AES, HMAC},
+ new byte[] {P_224, P_256, P_384, P_521, CURVE_25519},
+ new byte[] {STANDALONE, REQUIRES_FILE_SYSTEM},
+ new byte[] {USER_AUTH_NONE, PASSWORD, FINGERPRINT, BOTH, ANY},
+ new byte[] {GENERATED, DERIVED, IMPORTED, UNKNOWN, SECURELY_IMPORTED},
+ new byte[] {SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX}
+ };
+ }
+ }
+
+ // isValidTag enumeration keys and values.
+ private static boolean validateEnum(short key, byte value) {
+ create();
+ byte[] vals;
+ short enumInd;
+ // check if key exists
+ short index = (short) tags.length;
+ while (--index >= 0) {
+ if (tags[index] == key) {
+ // check if value given
+ if (value != NO_VALUE) {
+ // check if the value exist
+ vals = (byte[]) enums[index];
+ enumInd = (short) vals.length;
+ while (--enumInd >= 0) {
+ if (vals[enumInd] == value) {
+ // return true if value exist
+ return true;
+ }
+ }
+ // return false if value does not exist
+ return false;
+ }
+ // return true if key exist and value not given
+ return true;
+ }
+ }
+ // return false if key does not exist
+ return false;
+ }
+
+ public static short getValue(short tagKey, short keyParameters) {
+ short tagPtr = KMKeyParameters.findTag(KMType.ENUM_TAG, tagKey, keyParameters);
+ if (tagPtr != KMType.INVALID_VALUE) {
+ return heap[(short) (tagPtr + TLV_HEADER_SIZE + 4)];
+ }
+ return KMType.INVALID_VALUE;
+ }
+
+ public short getKey() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_ENUM_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short getTagType() {
+ return KMType.ENUM_TAG;
+ }
+
+ public byte getValue() {
+ return heap[(short) (KMType.instanceTable[KM_ENUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4)];
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMError.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMError.java
new file mode 100644
index 0000000..bbae870
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMError.java
@@ -0,0 +1,134 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+/**
+ * KMError includes all the error codes from android keymaster hal specifications. The values are
+ * positive unlike negative values in keymaster hal.
+ */
+public class KMError {
+
+ public static final short OK = 0;
+ public static final short UNSUPPORTED_PURPOSE = 2;
+ public static final short INCOMPATIBLE_PURPOSE = 3;
+ public static final short UNSUPPORTED_ALGORITHM = 4;
+ public static final short INCOMPATIBLE_ALGORITHM = 5;
+ public static final short UNSUPPORTED_KEY_SIZE = 6;
+ public static final short UNSUPPORTED_BLOCK_MODE = 7;
+ public static final short INCOMPATIBLE_BLOCK_MODE = 8;
+ public static final short UNSUPPORTED_MAC_LENGTH = 9;
+ public static final short UNSUPPORTED_PADDING_MODE = 10;
+ public static final short INCOMPATIBLE_PADDING_MODE = 11;
+ public static final short UNSUPPORTED_DIGEST = 12;
+ public static final short INCOMPATIBLE_DIGEST = 13;
+
+ public static final short UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = 19;
+
+ /** For PKCS8 & PKCS12 */
+ public static final short INVALID_INPUT_LENGTH = 21;
+
+ public static final short KEY_USER_NOT_AUTHENTICATED = 26;
+ public static final short INVALID_OPERATION_HANDLE = 28;
+ public static final short INSUFFICIENT_BUFFER_SPACE = 29;
+ public static final short VERIFICATION_FAILED = 30;
+ public static final short TOO_MANY_OPERATIONS = 31;
+ public static final short INVALID_KEY_BLOB = 33;
+
+ public static final short INVALID_ARGUMENT = 38;
+ public static final short UNSUPPORTED_TAG = 39;
+ public static final short INVALID_TAG = 40;
+ public static final short IMPORT_PARAMETER_MISMATCH = 44;
+ public static final short OPERATION_CANCELLED = 46;
+
+ public static final short MISSING_NONCE = 51;
+ public static final short INVALID_NONCE = 52;
+ public static final short MISSING_MAC_LENGTH = 53;
+ public static final short CALLER_NONCE_PROHIBITED = 55;
+ public static final short KEY_MAX_OPS_EXCEEDED = 56;
+ public static final short INVALID_MAC_LENGTH = 57;
+ public static final short MISSING_MIN_MAC_LENGTH = 58;
+ public static final short UNSUPPORTED_MIN_MAC_LENGTH = 59;
+ public static final short UNSUPPORTED_EC_CURVE = 61;
+ public static final short KEY_REQUIRES_UPGRADE = 62;
+
+ public static final short ATTESTATION_CHALLENGE_MISSING = 63;
+ public static final short ATTESTATION_APPLICATION_ID_MISSING = 65;
+ public static final short CANNOT_ATTEST_IDS = 66;
+ public static final short ROLLBACK_RESISTANCE_UNAVAILABLE = 67;
+
+ public static final short NO_USER_CONFIRMATION = 71;
+ public static final short DEVICE_LOCKED = 72;
+ public static final short EARLY_BOOT_ENDED = 73;
+ public static final short ATTESTATION_KEYS_NOT_PROVISIONED = 74;
+ public static final short INCOMPATIBLE_MGF_DIGEST = 78;
+ public static final short UNSUPPORTED_MGF_DIGEST = 79;
+ public static final short MISSING_NOT_BEFORE = 80;
+ public static final short MISSING_NOT_AFTER = 81;
+ public static final short MISSING_ISSUER_SUBJECT_NAME = 82;
+ public static final short INVALID_ISSUER_SUBJECT_NAME = 83;
+
+ public static final short UNIMPLEMENTED = 100;
+ public static final short UNKNOWN_ERROR = 1000;
+
+ // Extended errors
+ public static final short SW_CONDITIONS_NOT_SATISFIED = 10001;
+ public static final short UNSUPPORTED_CLA = 10002;
+ public static final short INVALID_P1P2 = 10003;
+ public static final short UNSUPPORTED_INSTRUCTION = 10004;
+ public static final short CMD_NOT_ALLOWED = 10005;
+ public static final short SW_WRONG_LENGTH = 10006;
+ public static final short INVALID_DATA = 10007;
+
+ // Crypto errors
+ public static final short CRYPTO_ILLEGAL_USE = 10008;
+ public static final short CRYPTO_ILLEGAL_VALUE = 10009;
+ public static final short CRYPTO_INVALID_INIT = 10010;
+ public static final short CRYPTO_NO_SUCH_ALGORITHM = 10011;
+ public static final short CRYPTO_UNINITIALIZED_KEY = 10012;
+ // Generic Unknown error.
+ public static final short GENERIC_UNKNOWN_ERROR = 10013;
+
+ // Remote key provisioning error codes.
+ public static final short STATUS_FAILED = 32000;
+ public static final short STATUS_INVALID_MAC = 32001;
+ public static final short STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 32002;
+ public static final short STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 32003;
+ public static final short STATUS_INVALID_EEK = 32004;
+ public static final short INVALID_STATE = 32005;
+
+ public static short translate(short err) {
+ switch (err) {
+ case SW_CONDITIONS_NOT_SATISFIED:
+ case UNSUPPORTED_CLA:
+ case INVALID_P1P2:
+ case INVALID_DATA:
+ case CRYPTO_ILLEGAL_USE:
+ case CRYPTO_ILLEGAL_VALUE:
+ case CRYPTO_INVALID_INIT:
+ case CRYPTO_UNINITIALIZED_KEY:
+ case GENERIC_UNKNOWN_ERROR:
+ case CMD_NOT_ALLOWED:
+ case UNKNOWN_ERROR:
+ return UNKNOWN_ERROR;
+ case CRYPTO_NO_SUCH_ALGORITHM:
+ return UNSUPPORTED_ALGORITHM;
+ case UNSUPPORTED_INSTRUCTION:
+ case SW_WRONG_LENGTH:
+ return UNIMPLEMENTED;
+ }
+ return err;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java
new file mode 100644
index 0000000..e6b1d37
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java
@@ -0,0 +1,171 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMHardwareAuthToken represents HardwareAuthToken structure from android keymaster hal
+ * specifications. It corresponds to CBOR array type. struct{byte HW_AUTH_TOKEN_TYPE; short
+ * length=2; short arrayPtr} where arrayPtr is a pointer to ordered array with following elements:
+ * {KMInteger Challenge; KMInteger UserId; KMInteger AuthenticatorId; UserAuthType
+ * HwAuthenticatorId; KMInteger TimeStamp; KMByteBlob Mac}
+ */
+public class KMHardwareAuthToken extends KMType {
+
+ public static final byte CHALLENGE = 0x00;
+ public static final byte USER_ID = 0x01;
+ public static final byte AUTHENTICATOR_ID = 0x02;
+ public static final byte HW_AUTHENTICATOR_TYPE = 0x03;
+ public static final byte TIMESTAMP = 0x04;
+ public static final byte MAC = 0x05;
+
+ private static KMHardwareAuthToken prototype;
+
+ private KMHardwareAuthToken() {}
+
+ public static short exp() {
+ short arrPtr = KMArray.instance((short) 6);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add(CHALLENGE, KMInteger.exp());
+ arr.add(USER_ID, KMInteger.exp());
+ arr.add(AUTHENTICATOR_ID, KMInteger.exp());
+ arr.add(HW_AUTHENTICATOR_TYPE, KMEnum.instance(KMType.USER_AUTH_TYPE));
+ arr.add(TIMESTAMP, KMInteger.exp());
+ arr.add(MAC, KMByteBlob.exp());
+ return instance(arrPtr);
+ }
+
+ private static KMHardwareAuthToken proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMHardwareAuthToken();
+ }
+ KMType.instanceTable[KM_HARDWARE_AUTH_TOKEN_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short instance() {
+ short arrPtr = KMArray.instance((short) 6);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add(CHALLENGE, KMInteger.uint_16((short) 0));
+ arr.add(USER_ID, KMInteger.uint_16((short) 0));
+ arr.add(AUTHENTICATOR_ID, KMInteger.uint_16((short) 0));
+ arr.add(HW_AUTHENTICATOR_TYPE, KMEnum.instance(KMType.USER_AUTH_TYPE, KMType.USER_AUTH_NONE));
+ arr.add(TIMESTAMP, KMInteger.uint_16((short) 0));
+ arr.add(MAC, KMByteBlob.instance((short) 0));
+ return instance(arrPtr);
+ }
+
+ public static short instance(short vals) {
+ KMArray arr = KMArray.cast(vals);
+ if (arr.length() != 6) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ short ptr = KMType.instance(HW_AUTH_TOKEN_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+ return ptr;
+ }
+
+ public static KMHardwareAuthToken cast(short ptr) {
+ if (heap[ptr] != HW_AUTH_TOKEN_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (heap[arrPtr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public short getVals() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_HARDWARE_AUTH_TOKEN_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short length() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).length();
+ }
+
+ public short getChallenge() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(CHALLENGE);
+ }
+
+ public void setChallenge(short vals) {
+ KMInteger.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(CHALLENGE, vals);
+ }
+
+ public short getUserId() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(USER_ID);
+ }
+
+ public void setUserId(short vals) {
+ KMInteger.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(USER_ID, vals);
+ }
+
+ public short getAuthenticatorId() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(AUTHENTICATOR_ID);
+ }
+
+ public void setAuthenticatorId(short vals) {
+ KMInteger.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(AUTHENTICATOR_ID, vals);
+ }
+
+ public short getHwAuthenticatorType() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(HW_AUTHENTICATOR_TYPE);
+ }
+
+ public void setHwAuthenticatorType(short vals) {
+ KMEnum.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(HW_AUTHENTICATOR_TYPE, vals);
+ }
+
+ public short getTimestamp() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(TIMESTAMP);
+ }
+
+ public void setTimestamp(short vals) {
+ KMInteger.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(TIMESTAMP, vals);
+ }
+
+ public short getMac() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(MAC);
+ }
+
+ public void setMac(short vals) {
+ KMByteBlob.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(MAC, vals);
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java
new file mode 100644
index 0000000..8642803
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java
@@ -0,0 +1,110 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMHmacSharingParameters represents HmacSharingParameters structure from android keymaster hal
+ * specifications. It corresponds to CBOR array type. struct{byte HMAC_SHARING_PARAM_TYPE; short
+ * length=2; short arrayPtr} where arrayPtr is a pointer to ordered array with following elements:
+ * {KMByteBlob Seed; KMByteBlob Nonce}
+ */
+public class KMHmacSharingParameters extends KMType {
+
+ public static final byte SEED = 0x00;
+ public static final byte NONCE = 0x01;
+
+ private static KMHmacSharingParameters prototype;
+
+ private KMHmacSharingParameters() {}
+
+ public static short exp() {
+ short arrPtr = KMArray.instance((short) 2);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add(SEED, KMByteBlob.exp());
+ arr.add(NONCE, KMByteBlob.exp());
+ return instance(arrPtr);
+ }
+
+ private static KMHmacSharingParameters proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMHmacSharingParameters();
+ }
+ KMType.instanceTable[KM_HMAC_SHARING_PARAMETERS_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short instance() {
+ short arrPtr = KMArray.instance((short) 2);
+ return instance(arrPtr);
+ }
+
+ public static short instance(short vals) {
+ short ptr = KMType.instance(HMAC_SHARING_PARAM_TYPE, (short) 2);
+ if (KMArray.cast(vals).length() != 2) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+ return ptr;
+ }
+
+ public static KMHmacSharingParameters cast(short ptr) {
+ if (heap[ptr] != HMAC_SHARING_PARAM_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (heap[arrPtr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public short getVals() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_HMAC_SHARING_PARAMETERS_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short length() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).length();
+ }
+
+ public short getNonce() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(NONCE);
+ }
+
+ public void setNonce(short vals) {
+ KMByteBlob.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(NONCE, vals);
+ }
+
+ public short getSeed() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(SEED);
+ }
+
+ public void setSeed(short vals) {
+ KMByteBlob.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(SEED, vals);
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMInteger.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMInteger.java
new file mode 100644
index 0000000..b09de0f
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMInteger.java
@@ -0,0 +1,215 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * Represents 8 bit, 16 bit, 32 bit and 64 bit unsigned integer. It corresponds to CBOR uint type.
+ * struct{byte INTEGER_TYPE; short length; 4 or 8 bytes of value}
+ */
+public class KMInteger extends KMType {
+
+ public static final byte UINT_32 = 4;
+ public static final byte UINT_64 = 8;
+ private static KMInteger prototype;
+
+ protected KMInteger() {}
+
+ private static KMInteger proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMInteger();
+ }
+ KMType.instanceTable[KM_INTEGER_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // | TYPE(1) | LEN(2) | DATA(4 / 8) |
+ public static short exp() {
+ return KMType.exp(INTEGER_TYPE);
+ }
+
+ // return an empty integer instance
+ public static short instance(short length) {
+ if ((length <= 0) || (length > 8)) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ if (length > 4) {
+ length = UINT_64;
+ } else {
+ length = UINT_32;
+ }
+ return KMType.instance(INTEGER_TYPE, length);
+ }
+
+ public static short instance(byte[] num, short srcOff, short length) {
+ if (length > 8) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ if (length == 1) {
+ return uint_8(num[srcOff]);
+ } else if (length == 2) {
+ return uint_16(Util.getShort(num, srcOff));
+ } else if (length == 4) {
+ return uint_32(num, srcOff);
+ } else {
+ return uint_64(num, srcOff);
+ }
+ }
+
+ public static KMInteger cast(short ptr) {
+ byte[] heap = repository.getHeap();
+ if (heap[ptr] != INTEGER_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ // create integer and copy byte value
+ public static short uint_8(byte num) {
+ short ptr = instance(UINT_32);
+ heap[(short) (ptr + TLV_HEADER_SIZE + 3)] = num;
+ return ptr;
+ }
+
+ // create integer and copy short value
+ public static short uint_16(short num) {
+ short ptr = instance(UINT_32);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), num);
+ return ptr;
+ }
+
+ // create integer and copy integer value
+ public static short uint_32(byte[] num, short offset) {
+ short ptr = instance(UINT_32);
+ Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_32);
+ return ptr;
+ }
+
+ // create integer and copy integer value
+ public static short uint_64(byte[] num, short offset) {
+ short ptr = instance(UINT_64);
+ Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_64);
+ return ptr;
+ }
+
+ public static short compare(short num1, short num2) {
+ short num1Buf = repository.alloc((short) 8);
+ short num2Buf = repository.alloc((short) 8);
+ Util.arrayFillNonAtomic(repository.getHeap(), num1Buf, (short) 8, (byte) 0);
+ Util.arrayFillNonAtomic(repository.getHeap(), num2Buf, (short) 8, (byte) 0);
+ short len = KMInteger.cast(num1).length();
+ KMInteger.cast(num1).getValue(repository.getHeap(), (short) (num1Buf + (short) (8 - len)), len);
+ len = KMInteger.cast(num2).length();
+ KMInteger.cast(num2).getValue(repository.getHeap(), (short) (num2Buf + (short) (8 - len)), len);
+ return KMInteger.unsignedByteArrayCompare(
+ repository.getHeap(), num1Buf, repository.getHeap(), num2Buf, (short) 8);
+ }
+
+ public static byte unsignedByteArrayCompare(
+ byte[] a1, short offset1, byte[] a2, short offset2, short length) {
+ byte count = (byte) 0;
+ short val1 = (short) 0;
+ short val2 = (short) 0;
+
+ for (; count < length; count++) {
+ val1 = (short) (a1[(short) (count + offset1)] & 0x00FF);
+ val2 = (short) (a2[(short) (count + offset2)] & 0x00FF);
+
+ if (val1 < val2) {
+ return -1;
+ }
+ if (val1 > val2) {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ // Get the length of the integer
+ public short length() {
+ return Util.getShort(heap, (short) (getBaseOffset() + 1));
+ }
+
+ // Get the buffer pointer in which blob is contained.
+ public byte[] getBuffer() {
+ return heap;
+ }
+
+ // Get the start of value
+ public short getStartOff() {
+ return (short) (getBaseOffset() + TLV_HEADER_SIZE);
+ }
+
+ public void getValue(byte[] dest, short destOff, short length) {
+ if (length < length()) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ if (length > length()) {
+ length = length();
+ destOff += length;
+ }
+ Util.arrayCopyNonAtomic(heap, getStartOff(), dest, destOff, length);
+ }
+
+ public void setValue(byte[] src, short srcOff) {
+ Util.arrayCopyNonAtomic(src, srcOff, heap, getStartOff(), length());
+ }
+
+ public short value(byte[] dest, short destOff) {
+ Util.arrayCopyNonAtomic(heap, getStartOff(), dest, destOff, length());
+ return length();
+ }
+
+ public short toLittleEndian(byte[] dest, short destOff) {
+ short index = (short) (length() - 1);
+ while (index >= 0) {
+ dest[destOff++] = heap[(short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE + index)];
+ index--;
+ }
+ return length();
+ }
+
+ public short getShort() {
+ return Util.getShort(heap, (short) (getStartOff() + 2));
+ }
+
+ public short getSignificantShort() {
+ return Util.getShort(heap, getStartOff());
+ }
+
+ public byte getByte() {
+ return heap[(short) (getStartOff() + 3)];
+ }
+
+ public boolean isZero() {
+ if (getShort() == 0 && getSignificantShort() == 0) {
+ return true;
+ }
+ return false;
+ }
+
+ protected short getBaseOffset() {
+ return instanceTable[KM_INTEGER_OFFSET];
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java
new file mode 100644
index 0000000..bf45981
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java
@@ -0,0 +1,163 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMIntegerArrayTag represents UINT_REP and ULONG_REP tags specified in keymaster hal specs.
+ * struct{byte TAG_TYPE; short length; struct{short UINT_TAG/ULONG_TAG; short tagKey; short arrPtr},
+ * where arrPtr is the pointer to KMArray of KMInteger instances.
+ */
+public class KMIntegerArrayTag extends KMTag {
+
+ private static final short[] tags = {USER_SECURE_ID};
+ private static KMIntegerArrayTag prototype;
+
+ private KMIntegerArrayTag() {}
+
+ private static KMIntegerArrayTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMIntegerArrayTag();
+ }
+ KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp(short tagType) {
+ if (!validateTagType(tagType)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short arrPtr = KMArray.exp(KMInteger.exp());
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), tagType);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), arrPtr);
+ return ptr;
+ }
+
+ public static short instance(short tagType, short key) {
+ if (!validateTagType(tagType)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (!validateKey(key)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short arrPtr = KMArray.exp();
+ return instance(tagType, key, arrPtr);
+ }
+
+ public static short instance(short tagType, short key, short arrObj) {
+ if (!validateTagType(tagType)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (!validateKey(key)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (heap[arrObj] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), tagType);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), arrObj);
+ return ptr;
+ }
+
+ public static KMIntegerArrayTag cast(short ptr) {
+ if (heap[ptr] != TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short tagType = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (!validateTagType(tagType)) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ private static boolean validateKey(short key) {
+ short index = (short) tags.length;
+ while (--index >= 0) {
+ if (tags[index] == key) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean validateTagType(short tagType) {
+ return (tagType == ULONG_ARRAY_TAG) || (tagType == UINT_ARRAY_TAG);
+ }
+
+ public static boolean contains(short tagId, short tagValue, short params) {
+ short tag = KMKeyParameters.findTag(KMType.UINT_ARRAY_TAG, tagId, params);
+ if (tag != KMType.INVALID_VALUE) {
+ short index = 0;
+ tag = KMIntegerArrayTag.cast(tag).getValues();
+ while (index < KMArray.cast(tag).length()) {
+ if (KMInteger.compare(tagValue, KMArray.cast(tag).get(index)) == 0) {
+ return true;
+ }
+ index++;
+ }
+ }
+ return false;
+ }
+
+ public short getTagType() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short getKey() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short getValues() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+
+ public short length() {
+ short ptr = getValues();
+ return KMArray.cast(ptr).length();
+ }
+
+ public void add(short index, short val) {
+ KMArray arr = KMArray.cast(getValues());
+ arr.add(index, val);
+ }
+
+ public short get(short index) {
+ KMArray arr = KMArray.cast(getValues());
+ return arr.get(index);
+ }
+
+ public boolean contains(short tagValue) {
+ short index = 0;
+ while (index < length()) {
+ if (KMInteger.compare(tagValue, get(index)) == 0) {
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java
new file mode 100644
index 0000000..d4c4458
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java
@@ -0,0 +1,218 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMIntegerTag represents UINT, ULONG and DATE tags specified in keymaster hal specs. struct{byte
+ * TAG_TYPE; short length; struct{short UINT_TAG/ULONG_TAG/DATE_TAG; short tagKey; 4 or 8 byte
+ * value}}
+ */
+public class KMIntegerTag extends KMTag {
+
+ // Allowed tag keys.
+ private static final short[] tags = {
+ // UINT
+ KEYSIZE,
+ MIN_MAC_LENGTH,
+ MIN_SEC_BETWEEN_OPS,
+ MAX_USES_PER_BOOT,
+ USERID,
+ AUTH_TIMEOUT,
+ OS_VERSION,
+ OS_PATCH_LEVEL,
+ VENDOR_PATCH_LEVEL,
+ BOOT_PATCH_LEVEL,
+ MAC_LENGTH,
+ // ULONG
+ RSA_PUBLIC_EXPONENT,
+ // DATE
+ ACTIVE_DATETIME,
+ ORIGINATION_EXPIRE_DATETIME,
+ USAGE_EXPIRE_DATETIME,
+ CREATION_DATETIME,
+ CERTIFICATE_NOT_BEFORE,
+ CERTIFICATE_NOT_AFTER,
+ USAGE_COUNT_LIMIT,
+ // custom tag
+ AUTH_TIMEOUT_MILLIS,
+ };
+ private static KMIntegerTag prototype;
+
+ private KMIntegerTag() {}
+
+ private static KMIntegerTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMIntegerTag();
+ }
+ KMType.instanceTable[KM_INTEGER_TAG_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp(short tagType) {
+ if (!validateTagType(tagType)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short intPtr = KMInteger.exp();
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), tagType);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), intPtr);
+ return ptr;
+ }
+
+ public static short instance(short tagType, short key) {
+ if (!validateTagType(tagType)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (!validateKey(key)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short intPtr = KMInteger.exp();
+ return instance(tagType, key, intPtr);
+ }
+
+ public static short instance(short tagType, short key, short intObj) {
+ if (!validateTagType(tagType)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (!validateKey(key)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (heap[intObj] != INTEGER_TYPE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), tagType);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), intObj);
+ return ptr;
+ }
+
+ public static KMIntegerTag cast(short ptr) {
+ if (heap[ptr] != TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short tagType = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (!validateTagType(tagType)) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ private static boolean validateKey(short key) {
+ short index = (short) tags.length;
+ while (--index >= 0) {
+ if (tags[index] == key) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean validateTagType(short tagType) {
+ return (tagType == DATE_TAG) || (tagType == UINT_TAG) || (tagType == ULONG_TAG);
+ }
+
+ public static short getShortValue(short tagType, short tagKey, short keyParameters) {
+ short ptr;
+ if (tagType == UINT_TAG) {
+ ptr = KMKeyParameters.findTag(KMType.UINT_TAG, tagKey, keyParameters);
+ if (ptr != KMType.INVALID_VALUE) {
+ ptr = KMIntegerTag.cast(ptr).getValue();
+ if (KMInteger.cast(ptr).getSignificantShort() == 0) {
+ return KMInteger.cast(ptr).getShort();
+ }
+ }
+ }
+ return KMType.INVALID_VALUE;
+ }
+
+ public static short getValue(
+ byte[] buf, short offset, short tagType, short tagKey, short keyParameters) {
+ short ptr;
+ if ((tagType == UINT_TAG) || (tagType == ULONG_TAG) || (tagType == DATE_TAG)) {
+ ptr = KMKeyParameters.findTag(tagType, tagKey, keyParameters);
+ if (ptr != KMType.INVALID_VALUE) {
+ ptr = KMIntegerTag.cast(ptr).getValue();
+ return KMInteger.cast(ptr).value(buf, offset);
+ }
+ }
+ return KMType.INVALID_VALUE;
+ }
+
+ public short getTagType() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short getKey() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short getValue() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+
+ public short length() {
+ KMInteger obj = KMInteger.cast(getValue());
+ return obj.length();
+ }
+
+ public boolean isValidKeySize(byte alg) {
+ short val = KMIntegerTag.cast(KMType.instanceTable[KM_INTEGER_TAG_OFFSET]).getValue();
+ if (KMInteger.cast(val).getSignificantShort() != 0) {
+ return false;
+ }
+ val = KMInteger.cast(val).getShort();
+ switch (alg) {
+ case KMType.RSA:
+ if (val == 2048) {
+ return true;
+ }
+ break;
+ case KMType.AES:
+ if (val == 128 || val == 256) {
+ return true;
+ }
+ break;
+ case KMType.DES:
+ if (val == 168) {
+ return true;
+ }
+ break;
+ case KMType.EC:
+ if (val == 256) {
+ return true;
+ }
+ break;
+ case KMType.HMAC:
+ if (val % 8 == 0 && val >= 64 && val <= 512) {
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java
new file mode 100644
index 0000000..37b8b7f
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java
@@ -0,0 +1,124 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMKeyCharacteristics represents KeyCharacteristics structure from android keymaster hal
+ * specifications. It corresponds to CBOR array type. struct{byte KEY_CHAR_TYPE; short length=3;
+ * short arrayPtr} where arrayPtr is a pointer to ordered array with 1 or 3 following elements:
+ * {KMKeyParameters sb; KMKeyParameters tee; KMKeyParameters keystore}
+ */
+public class KMKeyCharacteristics extends KMType {
+
+ public static final byte STRONGBOX_ENFORCED = 0x00;
+ public static final byte TEE_ENFORCED = 0x01;
+ public static final byte KEYSTORE_ENFORCED = 0x02;
+ private static KMKeyCharacteristics prototype;
+
+ private KMKeyCharacteristics() {}
+
+ public static short exp() {
+ short keyParamExp = KMKeyParameters.exp();
+ short arrPtr = KMArray.instance((short) 3);
+
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add(STRONGBOX_ENFORCED, keyParamExp);
+ arr.add(TEE_ENFORCED, keyParamExp);
+ arr.add(KEYSTORE_ENFORCED, keyParamExp);
+ return instance(arrPtr);
+ }
+
+ private static KMKeyCharacteristics proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMKeyCharacteristics();
+ }
+ KMType.instanceTable[KM_KEY_CHARACTERISTICS_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short instance() {
+ short arrPtr = KMArray.instance((short) 3);
+ return instance(arrPtr);
+ }
+
+ public static short instance(short vals) {
+ short ptr = KMType.instance(KEY_CHAR_TYPE, (short) 3);
+ if (KMArray.cast(vals).length() != 3) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+ return ptr;
+ }
+
+ public static KMKeyCharacteristics cast(short ptr) {
+ if (heap[ptr] != KEY_CHAR_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (heap[arrPtr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public short getVals() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_KEY_CHARACTERISTICS_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short length() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).length();
+ }
+
+ public short getKeystoreEnforced() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(KEYSTORE_ENFORCED);
+ }
+
+ public void setKeystoreEnforced(short ptr) {
+ KMKeyParameters.cast(ptr);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(KEYSTORE_ENFORCED, ptr);
+ }
+
+ public short getTeeEnforced() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(TEE_ENFORCED);
+ }
+
+ public void setTeeEnforced(short ptr) {
+ KMKeyParameters.cast(ptr);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(TEE_ENFORCED, ptr);
+ }
+
+ public short getStrongboxEnforced() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(STRONGBOX_ENFORCED);
+ }
+
+ public void setStrongboxEnforced(short ptr) {
+ KMKeyParameters.cast(ptr);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(STRONGBOX_ENFORCED, ptr);
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java
new file mode 100644
index 0000000..54ab6ee
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java
@@ -0,0 +1,472 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMKeyParameters represents KeyParameters structure from android keymaster hal specifications. It
+ * corresponds to CBOR map type. struct{byte KEY_PARAM_TYPE; short length=2; short arrayPtr} where
+ * arrayPtr is a pointer to array with any KMTag subtype instances.
+ */
+public class KMKeyParameters extends KMType {
+
+ private static final short[] customTags = {
+ KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS,
+ };
+ private static final short[] tagArr = {
+ // Unsupported tags.
+ KMType.BOOL_TAG, KMType.TRUSTED_USER_PRESENCE_REQUIRED,
+ KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS
+ };
+ private static final short[] hwEnforcedTagArr = {
+ // HW Enforced
+ KMType.ENUM_ARRAY_TAG, KMType.PURPOSE,
+ KMType.ENUM_TAG, KMType.ALGORITHM,
+ KMType.UINT_TAG, KMType.KEYSIZE,
+ KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT,
+ KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ,
+ KMType.ENUM_ARRAY_TAG, KMType.DIGEST,
+ KMType.ENUM_ARRAY_TAG, KMType.PADDING,
+ KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE,
+ KMType.ENUM_ARRAY_TAG, KMType.RSA_OAEP_MGF_DIGEST,
+ KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED,
+ KMType.BOOL_TAG, KMType.CALLER_NONCE,
+ KMType.UINT_TAG, KMType.MIN_MAC_LENGTH,
+ KMType.ENUM_TAG, KMType.ECCURVE,
+ KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID,
+ KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE,
+ KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY,
+ KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY,
+ KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT,
+ };
+ private static final short[] swEnforcedTagsArr = {
+ KMType.DATE_TAG, KMType.ACTIVE_DATETIME,
+ KMType.DATE_TAG, KMType.ORIGINATION_EXPIRE_DATETIME,
+ KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME,
+ KMType.UINT_TAG, KMType.USERID,
+ KMType.DATE_TAG, KMType.CREATION_DATETIME,
+ KMType.UINT_TAG, KMType.USAGE_COUNT_LIMIT,
+ KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY,
+ KMType.UINT_TAG, KMType.MAX_BOOT_LEVEL,
+ };
+ private static final short[] teeEnforcedTagsArr = {
+ KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID,
+ KMType.UINT_TAG, KMType.AUTH_TIMEOUT,
+ KMType.ENUM_TAG, KMType.USER_AUTH_TYPE,
+ KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED,
+ KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED,
+ };
+ private static final short[] invalidTagsArr = {
+ KMType.BYTES_TAG, KMType.NONCE,
+ KMType.BYTES_TAG, KMType.ASSOCIATED_DATA,
+ KMType.BYTES_TAG, KMType.UNIQUE_ID,
+ KMType.UINT_TAG, KMType.MAC_LENGTH,
+ };
+ private static KMKeyParameters prototype;
+
+ private KMKeyParameters() {}
+
+ private static KMKeyParameters proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMKeyParameters();
+ }
+ KMType.instanceTable[KM_KEY_PARAMETERS_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp() {
+ short arrPtr = KMArray.instance((short) 11);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add((short) 0, KMEnum.instance(KMType.RULE, KMType.FAIL_ON_INVALID_TAGS));
+ arr.add((short) 1, KMIntegerTag.exp(UINT_TAG));
+ arr.add((short) 2, KMIntegerArrayTag.exp(UINT_ARRAY_TAG));
+ arr.add((short) 3, KMIntegerTag.exp(ULONG_TAG));
+ arr.add((short) 4, KMIntegerTag.exp(DATE_TAG));
+ arr.add((short) 5, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG));
+ arr.add((short) 6, KMEnumTag.exp());
+ arr.add((short) 7, KMEnumArrayTag.exp());
+ arr.add((short) 8, KMByteTag.exp());
+ arr.add((short) 9, KMBoolTag.exp());
+ arr.add((short) 10, KMBignumTag.exp());
+ return instance(arrPtr);
+ }
+
+ public static short expAny() {
+ short arrPtr = KMArray.instance((short) 11);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add((short) 0, KMEnum.instance(KMType.RULE, KMType.IGNORE_INVALID_TAGS));
+ arr.add((short) 1, KMIntegerTag.exp(UINT_TAG));
+ arr.add((short) 2, KMIntegerArrayTag.exp(UINT_ARRAY_TAG));
+ arr.add((short) 3, KMIntegerTag.exp(ULONG_TAG));
+ arr.add((short) 4, KMIntegerTag.exp(DATE_TAG));
+ arr.add((short) 5, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG));
+ arr.add((short) 6, KMEnumTag.exp());
+ arr.add((short) 7, KMEnumArrayTag.exp());
+ arr.add((short) 8, KMByteTag.exp());
+ arr.add((short) 9, KMBoolTag.exp());
+ arr.add((short) 10, KMBignumTag.exp());
+ return instance(arrPtr);
+ }
+
+ public static short instance(short vals) {
+ short ptr = KMType.instance(KEY_PARAM_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+ return ptr;
+ }
+
+ public static KMKeyParameters cast(short ptr) {
+ if (heap[ptr] != KEY_PARAM_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (heap[arrPtr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static short findTag(short tagType, short tagKey, short keyParam) {
+ KMKeyParameters instParam = KMKeyParameters.cast(keyParam);
+ return instParam.findTag(tagType, tagKey);
+ }
+
+ public static boolean hasUnsupportedTags(short keyParamsPtr) {
+ byte index = 0;
+ short tagInd;
+ short tagPtr;
+ short tagKey;
+ short tagType;
+ short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals();
+ short len = KMArray.cast(arrPtr).length();
+ while (index < len) {
+ tagInd = 0;
+ tagPtr = KMArray.cast(arrPtr).get(index);
+ tagKey = KMTag.getKey(tagPtr);
+ tagType = KMTag.getTagType(tagPtr);
+ while (tagInd < (short) tagArr.length) {
+ if ((tagArr[tagInd] == tagType) && (tagArr[(short) (tagInd + 1)] == tagKey)) {
+ return true;
+ }
+ tagInd += 2;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ // KDF, ECIES_SINGLE_HASH_MODE missing from types.hal
+ public static short makeSbEnforced(
+ short keyParamsPtr,
+ byte origin,
+ short osVersionObjPtr,
+ short osPatchObjPtr,
+ short vendorPatchObjPtr,
+ short bootPatchObjPtr,
+ byte[] scratchPad) {
+ byte index = 0;
+ short tagInd;
+ short arrInd = 0;
+ short tagPtr;
+ short tagKey;
+ short tagType;
+ short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals();
+ short len = KMArray.cast(arrPtr).length();
+ while (index < len) {
+ tagInd = 0;
+ tagPtr = KMArray.cast(arrPtr).get(index);
+ tagKey = KMTag.getKey(tagPtr);
+ tagType = KMTag.getTagType(tagPtr);
+ if (!isValidTag(tagType, tagKey)) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ while (tagInd < (short) hwEnforcedTagArr.length) {
+ if ((hwEnforcedTagArr[tagInd] == tagType)
+ && (hwEnforcedTagArr[(short) (tagInd + 1)] == tagKey)) {
+ Util.setShort(scratchPad, arrInd, tagPtr);
+ arrInd += 2;
+ break;
+ }
+ tagInd += 2;
+ }
+ index++;
+ }
+ short originTag = KMEnumTag.instance(KMType.ORIGIN, origin);
+ Util.setShort(scratchPad, arrInd, originTag);
+ arrInd += 2;
+ short osVersionTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION, osVersionObjPtr);
+ Util.setShort(scratchPad, arrInd, osVersionTag);
+ arrInd += 2;
+ short osPatchTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, osPatchObjPtr);
+ Util.setShort(scratchPad, arrInd, osPatchTag);
+ arrInd += 2;
+ short vendorPatchTag =
+ KMIntegerTag.instance(KMType.UINT_TAG, KMType.VENDOR_PATCH_LEVEL, vendorPatchObjPtr);
+ Util.setShort(scratchPad, arrInd, vendorPatchTag);
+ arrInd += 2;
+ short bootPatchTag =
+ KMIntegerTag.instance(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, bootPatchObjPtr);
+ Util.setShort(scratchPad, arrInd, bootPatchTag);
+ arrInd += 2;
+ return createKeyParameters(scratchPad, (short) (arrInd / 2));
+ }
+
+ public static short makeHwEnforced(short sb, short tee) {
+ short len = KMKeyParameters.cast(sb).length();
+ len += KMKeyParameters.cast(tee).length();
+ short hwEnf = KMArray.instance(len);
+ sb = KMKeyParameters.cast(sb).getVals();
+ tee = KMKeyParameters.cast(tee).getVals();
+ len = KMArray.cast(sb).length();
+ short src = 0;
+ short dest = 0;
+ short val = 0;
+ while (src < len) {
+ val = KMArray.cast(sb).get(src);
+ KMArray.cast(hwEnf).add(dest, val);
+ src++;
+ dest++;
+ }
+ src = 0;
+ len = KMArray.cast(tee).length();
+ while (src < len) {
+ val = KMArray.cast(tee).get(src);
+ KMArray.cast(hwEnf).add(dest, val);
+ src++;
+ dest++;
+ }
+ return KMKeyParameters.instance(hwEnf);
+ }
+
+ // ALL_USERS, EXPORTABLE missing from types.hal
+ public static short makeKeystoreEnforced(short keyParamsPtr, byte[] scratchPad) {
+ byte index = 0;
+ short tagInd;
+ short arrInd = 0;
+ short tagPtr;
+ short tagKey;
+ short tagType;
+ short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals();
+ short len = KMArray.cast(arrPtr).length();
+ while (index < len) {
+ tagInd = 0;
+ tagPtr = KMArray.cast(arrPtr).get(index);
+ tagKey = KMTag.getKey(tagPtr);
+ tagType = KMTag.getTagType(tagPtr);
+ if (!isValidTag(tagType, tagKey)) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ while (tagInd < (short) swEnforcedTagsArr.length) {
+ if ((swEnforcedTagsArr[tagInd] == tagType)
+ && (swEnforcedTagsArr[(short) (tagInd + 1)] == tagKey)) {
+ Util.setShort(scratchPad, arrInd, tagPtr);
+ arrInd += 2;
+ break;
+ }
+ tagInd += 2;
+ }
+ index++;
+ }
+ return createKeyParameters(scratchPad, (short) (arrInd / 2));
+ }
+
+ public static short makeTeeEnforced(short keyParamsPtr, byte[] scratchPad) {
+ byte index = 0;
+ short tagInd;
+ short arrInd = 0;
+ short tagPtr;
+ short tagKey;
+ short tagType;
+ short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals();
+ short len = KMArray.cast(arrPtr).length();
+ while (index < len) {
+ tagInd = 0;
+ tagPtr = KMArray.cast(arrPtr).get(index);
+ tagKey = KMTag.getKey(tagPtr);
+ tagType = KMTag.getTagType(tagPtr);
+ if (!isValidTag(tagType, tagKey)) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ while (tagInd < (short) teeEnforcedTagsArr.length) {
+ if ((teeEnforcedTagsArr[tagInd] == tagType)
+ && (teeEnforcedTagsArr[(short) (tagInd + 1)] == tagKey)) {
+ Util.setShort(scratchPad, arrInd, tagPtr);
+ arrInd += 2;
+ break;
+ }
+ tagInd += 2;
+ }
+ index++;
+ }
+ return createKeyParameters(scratchPad, (short) (arrInd / 2));
+ }
+
+ public static short makeHidden(short keyParamsPtr, short rootOfTrustBlob, byte[] scratchPad) {
+ short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, keyParamsPtr);
+ if (appId != KMTag.INVALID_VALUE) {
+ appId = KMByteTag.cast(appId).getValue();
+ if (KMByteBlob.cast(appId).length() == 0) {
+ appId = KMTag.INVALID_VALUE;
+ }
+ }
+ short appData =
+ KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, keyParamsPtr);
+ if (appData != KMTag.INVALID_VALUE) {
+ appData = KMByteTag.cast(appData).getValue();
+ if (KMByteBlob.cast(appData).length() == 0) {
+ appData = KMTag.INVALID_VALUE;
+ }
+ }
+ return makeHidden(appId, appData, rootOfTrustBlob, scratchPad);
+ }
+
+ public static short makeHidden(
+ short appIdBlob, short appDataBlob, short rootOfTrustBlob, byte[] scratchPad) {
+ // Order in which the hidden array is created should not change.
+ short index = 0;
+ KMByteBlob.cast(rootOfTrustBlob);
+ Util.setShort(scratchPad, index, rootOfTrustBlob);
+ index += 2;
+ if (appIdBlob != KMTag.INVALID_VALUE) {
+ KMByteBlob.cast(appIdBlob);
+ Util.setShort(scratchPad, index, appIdBlob);
+ index += 2;
+ }
+ if (appDataBlob != KMTag.INVALID_VALUE) {
+ KMByteBlob.cast(appDataBlob);
+ Util.setShort(scratchPad, index, appDataBlob);
+ index += 2;
+ }
+ return createKeyParameters(scratchPad, (short) (index / 2));
+ }
+
+ public static boolean isValidTag(short tagType, short tagKey) {
+ short index = 0;
+ if (tagKey == KMType.INVALID_TAG) {
+ return false;
+ }
+ while (index < invalidTagsArr.length) {
+ if ((tagType == invalidTagsArr[index]) && (tagKey == invalidTagsArr[(short) (index + 1)])) {
+ return false;
+ }
+ index += 2;
+ }
+ return true;
+ }
+
+ public static short createKeyParameters(byte[] ptrArr, short len) {
+ short arrPtr = KMArray.instance(len);
+ short index = 0;
+ short ptr = 0;
+ while (index < len) {
+ KMArray.cast(arrPtr).add(index, Util.getShort(ptrArr, ptr));
+ index++;
+ ptr += 2;
+ }
+ return KMKeyParameters.instance(arrPtr);
+ }
+
+ public static short makeCustomTags(short keyParams, byte[] scratchPad) {
+ short index = 0;
+ short tagPtr;
+ short offset = 0;
+ short len = (short) customTags.length;
+ short tagType;
+ while (index < len) {
+ tagType = customTags[(short) (index + 1)];
+ switch (tagType) {
+ case KMType.AUTH_TIMEOUT_MILLIS:
+ short authTimeOutTag =
+ KMKeyParameters.cast(keyParams).findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT);
+ if (authTimeOutTag != KMType.INVALID_VALUE) {
+ tagPtr = createAuthTimeOutMillisTag(authTimeOutTag, scratchPad, offset);
+ Util.setShort(scratchPad, offset, tagPtr);
+ offset += 2;
+ }
+ break;
+ default:
+ break;
+ }
+ index += 2;
+ }
+ return createKeyParameters(scratchPad, (short) (offset / 2));
+ }
+
+ public static short createAuthTimeOutMillisTag(
+ short authTimeOutTag, byte[] scratchPad, short offset) {
+ short authTime = KMIntegerTag.cast(authTimeOutTag).getValue();
+ Util.arrayFillNonAtomic(scratchPad, offset, (short) 40, (byte) 0);
+ Util.arrayCopyNonAtomic(
+ KMInteger.cast(authTime).getBuffer(),
+ KMInteger.cast(authTime).getStartOff(),
+ scratchPad,
+ (short) (offset + 8 - KMInteger.cast(authTime).length()),
+ KMInteger.cast(authTime).length());
+ KMUtils.convertToMilliseconds(scratchPad, offset, (short) (offset + 8), (short) (offset + 16));
+ return KMIntegerTag.instance(
+ KMType.ULONG_TAG,
+ KMType.AUTH_TIMEOUT_MILLIS,
+ KMInteger.uint_64(scratchPad, (short) (offset + 8)));
+ }
+
+ public short getVals() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_KEY_PARAMETERS_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short length() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).length();
+ }
+
+ public short findTag(short tagType, short tagKey) {
+ KMArray vals = KMArray.cast(getVals());
+ short index = 0;
+ short length = vals.length();
+ short key;
+ short type;
+ short ret = KMType.INVALID_VALUE;
+ short obj;
+ while (index < length) {
+ obj = vals.get(index);
+ key = KMTag.getKey(obj);
+ type = KMTag.getTagType(obj);
+ if ((tagKey == key) && (tagType == type)) {
+ ret = obj;
+ break;
+ }
+ index++;
+ }
+ return ret;
+ }
+
+ public void deleteCustomTags() {
+ short arrPtr = getVals();
+ short index = (short) (customTags.length - 1);
+ short obj;
+ while (index >= 0) {
+ obj = findTag(customTags[(short) (index - 1)], customTags[index]);
+ if (obj != KMType.INVALID_VALUE) {
+ KMArray.cast(arrPtr).deleteLastEntry();
+ }
+ index -= 2;
+ }
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java
new file mode 100644
index 0000000..d8bc21d
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java
@@ -0,0 +1,5172 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMAttestationCert;
+import com.android.javacard.seprovider.KMDataStoreConstants;
+import com.android.javacard.seprovider.KMException;
+import com.android.javacard.seprovider.KMKey;
+import com.android.javacard.seprovider.KMOperation;
+import com.android.javacard.seprovider.KMSEProvider;
+import javacard.framework.APDU;
+import javacard.framework.Applet;
+import javacard.framework.AppletEvent;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+import javacard.security.CryptoException;
+import javacardx.apdu.ExtendedLength;
+
+/**
+ * KMKeymasterApplet implements the javacard applet. It creates an instance of the KMRepository and
+ * other install time objects. It also implements the keymaster state machine and handles javacard
+ * applet life cycle events.
+ */
+public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLength {
+
+ // Constants.
+ // Represents RSA_PUBLIC_EXPONENT value 65537.
+ public static final byte[] F4 = {0x01, 0x00, 0x01};
+ // Block size of AES algorithm.
+ public static final byte AES_BLOCK_SIZE = 16;
+ // Block size of DES algorithm.
+ public static final byte DES_BLOCK_SIZE = 8;
+ // The Key size in bits for the master key.
+ public static final short MASTER_KEY_SIZE = 128;
+ // The Key size of the transport key used in importWrappedKey.
+ public static final byte WRAPPING_KEY_SIZE = 32;
+ // The maximum allowed simultaneous operations.
+ public static final byte MAX_OPERATIONS_COUNT = 4;
+ // The size of the verified boot key in ROT.
+ public static final byte VERIFIED_BOOT_KEY_SIZE = 32;
+ // The size of the verified boot hash in ROT.
+ public static final byte VERIFIED_BOOT_HASH_SIZE = 32;
+ // The security level of TEE.
+ public static final byte TRUSTED_ENVIRONMENT = 1;
+ // "Keymaster HMAC Verification" - used for HMAC key verification.
+ public static final byte[] sharingCheck = {
+ 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x48, 0x4D, 0x41, 0x43, 0x20, 0x56,
+ 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E
+ };
+ // The ckdfLabel "KeymasterSharedMac" in hex.
+ public static final byte[] ckdfLabel = {
+ 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4D,
+ 0x61, 0x63
+ };
+ // The "Auth Verification" string in hex.
+ public static final byte[] authVerification = {
+ 0x41, 0x75, 0x74, 0x68, 0x20, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F,
+ 0x6E
+ };
+ // The "confirmation token" string in hex.
+ public static final byte[] confirmationToken = {
+ 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x72, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x74, 0x6F, 0x6B,
+ 0x65, 0x6E
+ };
+ // The maximum buffer size for the encoded COSE structures.
+ public static final short MAX_COSE_BUF_SIZE = (short) 512;
+ // Maximum allowed buffer size for to encode the key parameters
+ // which is used while creating mac for key parameters.
+ public static final short MAX_KEY_PARAMS_BUF_SIZE = (short) 3072; // 3K
+ // Temporary variables array size to store intermediary results.
+ public static final byte TMP_VARIABLE_ARRAY_SIZE = 5;
+ // Data Dictionary items
+ // Maximum Dictionary size.
+ public static final byte DATA_ARRAY_SIZE = 39;
+ // Below are the offsets of the data dictionary items.
+ public static final byte KEY_PARAMETERS = 0;
+ public static final byte KEY_CHARACTERISTICS = 1;
+ public static final byte HIDDEN_PARAMETERS = 2;
+ public static final byte HW_PARAMETERS = 3;
+ public static final byte SW_PARAMETERS = 4;
+ public static final byte AUTH_DATA = 5;
+ public static final byte AUTH_TAG = 6;
+ public static final byte NONCE = 7;
+ public static final byte KEY_BLOB = 8;
+ public static final byte AUTH_DATA_LENGTH = 9;
+ public static final byte SECRET = 10;
+ public static final byte ROT = 11;
+ public static final byte DERIVED_KEY = 12;
+ public static final byte RSA_PUB_EXPONENT = 13;
+ public static final byte APP_ID = 14;
+ public static final byte APP_DATA = 15;
+ public static final byte PUB_KEY = 16;
+ public static final byte IMPORTED_KEY_BLOB = 17;
+ public static final byte ORIGIN = 18;
+ public static final byte NOT_USED = 19;
+ public static final byte MASKING_KEY = 20;
+ public static final byte HMAC_SHARING_PARAMS = 21;
+ public static final byte OP_HANDLE = 22;
+ public static final byte IV = 23;
+ public static final byte INPUT_DATA = 24;
+ public static final byte OUTPUT_DATA = 25;
+ public static final byte HW_TOKEN = 26;
+ public static final byte VERIFICATION_TOKEN = 27;
+ public static final byte SIGNATURE = 28;
+ public static final byte ATTEST_KEY_BLOB = 29;
+ public static final byte ATTEST_KEY_PARAMS = 30;
+ public static final byte ATTEST_KEY_ISSUER = 31;
+ public static final byte CERTIFICATE = 32;
+ public static final byte PLAIN_SECRET = 33;
+ public static final byte TEE_PARAMETERS = 34;
+ public static final byte SB_PARAMETERS = 35;
+ public static final byte CONFIRMATION_TOKEN = 36;
+ public static final byte KEY_BLOB_VERSION_DATA_OFFSET = 37;
+ public static final byte CUSTOM_TAGS = 38;
+ // Below are the Keyblob offsets.
+ public static final byte KEY_BLOB_VERSION_OFFSET = 0;
+ public static final byte KEY_BLOB_SECRET = 1;
+ public static final byte KEY_BLOB_NONCE = 2;
+ public static final byte KEY_BLOB_AUTH_TAG = 3;
+ public static final byte KEY_BLOB_PARAMS = 4;
+ public static final byte KEY_BLOB_CUSTOM_TAGS = 5;
+ public static final byte KEY_BLOB_PUB_KEY = 6;
+ // AES GCM Auth tag length to be used while encrypting or decrypting the KeyBlob.
+ public static final byte AES_GCM_AUTH_TAG_LENGTH = 16;
+ // AES GCM nonce length to be used while encrypting or decrypting the KeyBlob.
+ public static final byte AES_GCM_NONCE_LENGTH = 12;
+ // KEYBLOB_CURRENT_VERSION goes into KeyBlob and will affect all
+ // the KeyBlobs if it is changed. please increment this
+ // version number whenever you change anything related to
+ // KeyBlob (structure, encryption algorithm etc).
+ public static final byte KEYBLOB_CURRENT_VERSION = 3;
+ // KeyBlob Verion 1 constant.
+ public static final byte KEYBLOB_VERSION_1 = 1;
+ // Array sizes of KeyBlob under different versions.
+ // The array size of a Symmetric key's KeyBlob for Version2 and Version3
+ public static final byte SYM_KEY_BLOB_SIZE_V2_V3 = 6;
+ // The array size of a Asymmetric key's KeyBlob for Version2 and Version3
+ public static final byte ASYM_KEY_BLOB_SIZE_V2_V3 = 7;
+ // The array size of a Symmetric key's KeyBlob for Version1
+ public static final byte SYM_KEY_BLOB_SIZE_V1 = 5;
+ // The array size of a Asymmetric key's KeyBlob for Version1
+ public static final byte ASYM_KEY_BLOB_SIZE_V1 = 6;
+ // The array size of a Symmetric key's KeyBlob for Version0
+ public static final byte SYM_KEY_BLOB_SIZE_V0 = 4;
+ // The array size of a Asymmetric key's KeyBlob for Version0
+ public static final byte ASYM_KEY_BLOB_SIZE_V0 = 5;
+ // Key type constants
+ // Represents the type of the Symmetric key.
+ public static final byte SYM_KEY_TYPE = 0;
+ // Represents the type of the Asymmetric key.
+ public static final byte ASYM_KEY_TYPE = 1;
+ // SHA-256 Digest length in bits
+ public static final short SHA256_DIGEST_LEN_BITS = 256;
+ // Minimum HMAC length in bits
+ public static final byte MIN_HMAC_LENGTH_BITS = 64;
+ // Below are the constants for provision reporting status
+ public static final short NOT_PROVISIONED = 0x0000;
+ public static final short PROVISION_STATUS_ATTESTATION_KEY = 0x0001;
+ public static final short PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x0002;
+ public static final short PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x0004;
+ public static final short PROVISION_STATUS_ATTEST_IDS = 0x0008;
+ public static final short PROVISION_STATUS_PRESHARED_SECRET = 0x0010;
+ public static final short PROVISION_STATUS_PROVISIONING_LOCKED = 0x0020;
+ public static final short PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR = 0x0040;
+ public static final short PROVISION_STATUS_UDS_CERT_CHAIN = 0x0080;
+ public static final short PROVISION_STATUS_SE_LOCKED = 0x0100;
+ public static final short PROVISION_STATUS_OEM_PUBLIC_KEY = 0x0200;
+ public static final short PROVISION_STATUS_SECURE_BOOT_MODE = 0x0400;
+ // This is the P1P2 constant of the APDU command header.
+ protected static final short KM_HAL_VERSION = (short) 0x6000;
+ // OEM lock / unlock verification constants.
+ // This is the verification label to authenticate the OEM to lock the provisioning for the
+ // OEM provision commands.
+ protected static final byte[] OEM_LOCK_PROVISION_VERIFICATION_LABEL = { // "OEM Provisioning Lock"
+ 0x4f, 0x45, 0x4d, 0x20, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e, 0x67,
+ 0x20, 0x4c, 0x6f, 0x63, 0x6b
+ };
+ // This is the verification label to authenticate the OEM to unlock the provisioning for the
+ // OEM provision commands.
+ protected static final byte[] OEM_UNLOCK_PROVISION_VERIFICATION_LABEL = { // "Enable RMA"
+ 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x52, 0x4d, 0x41
+ };
+ // The maximum size of the seed allowed for the RNG entropy
+ protected static final short MAX_SEED_SIZE = 2048;
+ // The maximum size of the certificate returned by the generate key command.
+ protected static final short MAX_CERT_SIZE = 3000;
+ // The maximum size of the encoded key characteristics in CBOR.
+ protected static final short MAX_KEY_CHARS_SIZE = 512;
+ // The maximum size of the serialized KeyBlob.
+ protected static final short MAX_KEYBLOB_SIZE = 1024;
+ // The maximum size of the Auth data which is used while encrypting/decrypting the KeyBlob.
+ private static final short MAX_AUTH_DATA_SIZE = (short) 512;
+ // The minimum bits in length for AES-GCM tag.
+ private static final byte MIN_GCM_TAG_LENGTH_BITS = (short) 96;
+ // The maximum bits in length for AES-GCM tag.
+ private static final short MAX_GCM_TAG_LENGTH_BITS = (short) 128;
+ // Subject is a fixed field with only CN= Android Keystore Key - same for all the keys
+ private static final byte[] defaultSubject = {
+ 0x30, 0x1F, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x14, 0x41, 0x6e, 0x64,
+ 0x72, 0x6f, 0x69, 0x64, 0x20, 0x4B, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x4B, 0x65,
+ 0x79
+ };
+ // Constant for Dec 31, 9999 in milliseconds in hex.
+ private static final byte[] dec319999Ms = {
+ (byte) 0, (byte) 0, (byte) 0xE6, (byte) 0x77, (byte) 0xD2, (byte) 0x1F, (byte) 0xD8, (byte) 0x18
+ };
+ // Dec 31, 9999 represented in Generalized time format YYYYMMDDhhmmssZ.
+ // "99991231235959Z" in hex. Refer RFC 5280 section 4.1.2.5.2
+ private static final byte[] dec319999 = {
+ 0x39, 0x39, 0x39, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a,
+ };
+ // Jan 01, 1970 represented in UTC time format YYMMDDhhmmssZ.
+ // "700101000000Z" in hex. Refer RFC 5280 section 4.1.2.5.1
+ private static final byte[] jan01970 = {
+ 0x37, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a,
+ };
+ // The KeyMint name "JavacardKeymintDevice" returned from getHwInfo.
+ private static final byte[] JavacardKeymintDevice = {
+ 0x4a, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x6d, 0x69, 0x6e, 0x74, 0x44,
+ 0x65, 0x76, 0x69, 0x63, 0x65,
+ };
+ // The KeyMint author name "Google" returned from getHwInfo.
+ public static final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65};
+ // Attestation ID tags to be included in attestation record.
+ private static final short[] attTags = {
+ KMType.ATTESTATION_ID_BRAND,
+ KMType.ATTESTATION_ID_DEVICE,
+ KMType.ATTESTATION_ID_IMEI,
+ KMType.ATTESTATION_ID_MANUFACTURER,
+ KMType.ATTESTATION_ID_MEID,
+ KMType.ATTESTATION_ID_MODEL,
+ KMType.ATTESTATION_ID_PRODUCT,
+ KMType.ATTESTATION_ID_SERIAL
+ };
+ // Below are the constants of instructions in APDU command header.
+ // Top 32 commands are reserved for provisioning.
+ private static final byte KEYMINT_CMD_APDU_START = 0x20;
+ // RKP
+ public static final byte INS_GET_RKP_HARDWARE_INFO = KEYMINT_CMD_APDU_START + 27; // 0x3B
+ public static final byte INS_GENERATE_RKP_KEY_CMD = KEYMINT_CMD_APDU_START + 28; // 0x3C
+ public static final byte INS_BEGIN_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 29; // 0x3D
+ public static final byte INS_UPDATE_KEY_CMD = KEYMINT_CMD_APDU_START + 30; // 0x3E
+ // Constant
+ public static final byte INS_UPDATE_EEK_CHAIN_CMD = KEYMINT_CMD_APDU_START + 31; // 0x3F
+ public static final byte INS_UPDATE_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 32; // 0x40
+ public static final byte INS_FINISH_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 33; // 0x41
+ public static final byte INS_GET_RESPONSE_CMD = KEYMINT_CMD_APDU_START + 34; // 0x42
+ public static final byte INS_GET_UDS_CERTS_CMD = KEYMINT_CMD_APDU_START + 35; // 0x43
+ public static final byte INS_GET_DICE_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 36; // 0x44
+ private static final byte INS_GENERATE_KEY_CMD = KEYMINT_CMD_APDU_START + 1; // 0x21
+ private static final byte INS_IMPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 2; // 0x22
+ private static final byte INS_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 3; // 0x23
+ private static final byte INS_EXPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 4; // 0x24
+ private static final byte INS_ATTEST_KEY_CMD = KEYMINT_CMD_APDU_START + 5; // 0x25
+ private static final byte INS_UPGRADE_KEY_CMD = KEYMINT_CMD_APDU_START + 6; // 0x26
+ private static final byte INS_DELETE_KEY_CMD = KEYMINT_CMD_APDU_START + 7; // 0x27
+ private static final byte INS_DELETE_ALL_KEYS_CMD = KEYMINT_CMD_APDU_START + 8; // 0x28
+ private static final byte INS_ADD_RNG_ENTROPY_CMD = KEYMINT_CMD_APDU_START + 9; // 0x29
+ private static final byte INS_COMPUTE_SHARED_HMAC_CMD = KEYMINT_CMD_APDU_START + 10; // 0x2A
+ private static final byte INS_DESTROY_ATT_IDS_CMD = KEYMINT_CMD_APDU_START + 11; // 0x2B
+ private static final byte INS_VERIFY_AUTHORIZATION_CMD = KEYMINT_CMD_APDU_START + 12; // 0x2C
+ private static final byte INS_GET_HMAC_SHARING_PARAM_CMD = KEYMINT_CMD_APDU_START + 13; // 0x2D
+ private static final byte INS_GET_KEY_CHARACTERISTICS_CMD = KEYMINT_CMD_APDU_START + 14; // 0x2E
+ private static final byte INS_GET_HW_INFO_CMD = KEYMINT_CMD_APDU_START + 15; // 0x2F
+ private static final byte INS_BEGIN_OPERATION_CMD = KEYMINT_CMD_APDU_START + 16; // 0x30
+ private static final byte INS_UPDATE_OPERATION_CMD = KEYMINT_CMD_APDU_START + 17; // 0x31
+ private static final byte INS_FINISH_OPERATION_CMD = KEYMINT_CMD_APDU_START + 18; // 0x32
+ private static final byte INS_ABORT_OPERATION_CMD = KEYMINT_CMD_APDU_START + 19; // 0x33
+ private static final byte INS_DEVICE_LOCKED_CMD = KEYMINT_CMD_APDU_START + 20; // 0x34
+ private static final byte INS_EARLY_BOOT_ENDED_CMD = KEYMINT_CMD_APDU_START + 21; // 0x35
+ private static final byte INS_GET_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 22; // 0x36
+ private static final byte INS_UPDATE_AAD_OPERATION_CMD = KEYMINT_CMD_APDU_START + 23; // 0x37
+ private static final byte INS_BEGIN_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 24; // 0x38
+ private static final byte INS_FINISH_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 25; // 0x39
+ private static final byte INS_INIT_STRONGBOX_CMD = KEYMINT_CMD_APDU_START + 26; // 0x3A
+ // The instructions from 0x43 to 0x4C will be reserved for KeyMint 1.0 for any future use.
+ // KeyMint 2.0 Instructions
+ private static final byte INS_GET_ROT_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 45; // 0x4D
+ private static final byte INS_GET_ROT_DATA_CMD = KEYMINT_CMD_APDU_START + 46; // 0x4E
+ private static final byte INS_SEND_ROT_DATA_CMD = KEYMINT_CMD_APDU_START + 47; // 0x4F
+ private static final byte KEYMINT_CMD_APDU_END = KEYMINT_CMD_APDU_START + 48; // 0x50
+ private static final byte INS_END_KM_CMD = 0x7F;
+ // Instruction values from 0xCD to 0xFF are completely reserved for Vendors to use and
+ // will never be used by the base line code in future.
+ private static final byte INS_KM_VENDOR_START_CMD = (byte) 0xCD;
+ private static final byte INS_KM_VENDOR_END_CMD = (byte) 0xFF;
+ // Index in apduFlagsStatus[] to check if instruction command is case 4 type in the Apdu
+ protected static final byte APDU_CASE4_COMMAND_STATUS_INDEX = 0;
+ // Index in apduFlagsStatus[] to check if Apdu setIncomingAndReceive function is called
+ protected static final byte APDU_INCOMING_AND_RECEIVE_STATUS_INDEX = 1;
+ // The maximum buffer size of combined seed and nonce.
+ private static final byte HMAC_SHARED_PARAM_MAX_SIZE = 64;
+ // Instance of RemotelyProvisionedComponentDevice, used to redirect the rkp commands.
+ protected static KMRemotelyProvisionedComponentDevice rkp;
+ // Instance of Cbor encoder.
+ protected static KMEncoder encoder;
+ // Instance of Cbor decoder.
+ protected static KMDecoder decoder;
+ // Instance of KMRepository class for memory management.
+ protected static KMRepository repository;
+ // Instance of KMSEProvider for doing crypto operations.
+ protected static KMSEProvider seProvider;
+ // Holds the instance of KMOperationStates. A maximum of 4 instances of KMOperatioState is
+ // allowed.
+ protected static KMOperationState[] opTable;
+ // Instance of KMKeymintDataStore which helps to store and retrieve the data.
+ protected static KMKeymintDataStore kmDataStore;
+
+ // Short array used to store the temporary results.
+ protected static short[] tmpVariables;
+ // Short array used to hold the dictionary items.
+ protected static short[] data;
+ // Buffer to store the transportKey which is used in the import wrapped key. Import wrapped
+ // key is divided into two stages 1. BEGIN_IMPORT_WRAPPED_KEY 2. FINISH_IMPORT_WRAPPED_KEY.
+ // The transportKey is retrieved and stored in this buffer at stage 1) and is later used in
+ // stage 2).
+ protected static byte[] wrappingKey;
+ // Transient byte array used to store the flags if APDU command type is of case 4 and if
+ // APDU setIncomingAndReceive() function is called or not.
+ protected static byte[] apduStatusFlags;
+
+ /** Registers this applet. */
+ protected KMKeymasterApplet(KMSEProvider seImpl) {
+ seProvider = seImpl;
+ boolean isUpgrading = seProvider.isUpgrading();
+ repository = new KMRepository(isUpgrading);
+ encoder = new KMEncoder();
+ decoder = new KMDecoder();
+ kmDataStore = new KMKeymintDataStore(seProvider, repository);
+ data = JCSystem.makeTransientShortArray(DATA_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT);
+ tmpVariables =
+ JCSystem.makeTransientShortArray(TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT);
+ wrappingKey =
+ JCSystem.makeTransientByteArray((short) (WRAPPING_KEY_SIZE + 1), JCSystem.CLEAR_ON_RESET);
+ resetWrappingKey();
+ apduStatusFlags = JCSystem.makeTransientByteArray((short) 2, JCSystem.CLEAR_ON_RESET);
+ opTable = new KMOperationState[MAX_OPERATIONS_COUNT];
+ short index = 0;
+ while (index < MAX_OPERATIONS_COUNT) {
+ opTable[index] = new KMOperationState();
+ index++;
+ }
+ KMType.initialize();
+ if (!isUpgrading) {
+ // For keyMint 3.0 and above installation, set ignore second Imei flag to false.
+ kmDataStore.ignoreSecondImei = false;
+ kmDataStore.createMasterKey(MASTER_KEY_SIZE);
+ }
+ // initialize default values
+ initHmacNonceAndSeed();
+ rkp =
+ new KMRemotelyProvisionedComponentDevice(
+ encoder, decoder, repository, seProvider, kmDataStore);
+ }
+
+ /** Sends a response, may be extended response, as requested by the command. */
+ public static void sendOutgoing(APDU apdu, short resp) {
+ // TODO handle the extended buffer stuff. We can reuse this.
+ short bufferStartOffset = repository.allocAvailableMemory();
+ byte[] buffer = repository.getHeap();
+ // TODO we can change the following to incremental send.
+ short bufferLength =
+ encoder.encode(resp, buffer, bufferStartOffset, repository.getHeapReclaimIndex());
+ if (((short) (bufferLength + bufferStartOffset)) > ((short) repository.getHeap().length)) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+
+ /* In T=0 protocol, On a case 4 command, setIncomingAndReceive() must
+ * be invoked prior to calling setOutgoing(). Otherwise, erroneous
+ * behavior may result
+ * */
+ if (apduStatusFlags[APDU_CASE4_COMMAND_STATUS_INDEX] == 1
+ && apduStatusFlags[APDU_INCOMING_AND_RECEIVE_STATUS_INDEX] == 0
+ && APDU.getProtocol() == APDU.PROTOCOL_T0) {
+ apdu.setIncomingAndReceive();
+ }
+ // Send data
+ apdu.setOutgoing();
+ apdu.setOutgoingLength(bufferLength);
+ apdu.sendBytesLong(buffer, bufferStartOffset, bufferLength);
+ }
+
+ /** Receives data, which can be extended data, as requested by the command instance. */
+ public static short receiveIncoming(APDU apdu, short reqExp) {
+ byte[] srcBuffer = apdu.getBuffer();
+ short recvLen = apdu.setIncomingAndReceive();
+ short srcOffset = apdu.getOffsetCdata();
+ apduStatusFlags[APDU_INCOMING_AND_RECEIVE_STATUS_INDEX] = 1;
+ // TODO add logic to handle the extended length buffer. In this case the memory can be reused
+ // from extended buffer.
+ short bufferLength = apdu.getIncomingLength();
+ short bufferStartOffset = repository.allocReclaimableMemory(bufferLength);
+ short index = bufferStartOffset;
+ byte[] buffer = repository.getHeap();
+ while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) {
+ Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen);
+ index += recvLen;
+ recvLen = apdu.receiveBytes(srcOffset);
+ }
+ short req = decoder.decode(reqExp, buffer, bufferStartOffset, bufferLength);
+ repository.reclaimMemory(bufferLength);
+ return req;
+ }
+
+ private static short createKeyBlobInstance(byte keyType) {
+ short arrayLen = 0;
+ switch (keyType) {
+ case ASYM_KEY_TYPE:
+ arrayLen = ASYM_KEY_BLOB_SIZE_V2_V3;
+ break;
+ case SYM_KEY_TYPE:
+ arrayLen = SYM_KEY_BLOB_SIZE_V2_V3;
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ return KMArray.instance(arrayLen);
+ }
+
+ private static void addTags(short params, boolean hwEnforced, KMAttestationCert cert) {
+ short index = 0;
+ short arr = KMKeyParameters.cast(params).getVals();
+ short len = KMArray.cast(arr).length();
+ short tag;
+ while (index < len) {
+ tag = KMArray.cast(arr).get(index);
+ cert.extensionTag(tag, hwEnforced);
+ index++;
+ }
+ }
+
+ private static void setUniqueId(KMAttestationCert cert, short attAppId, byte[] scratchPad) {
+ if (!KMTag.isPresent(data[KEY_PARAMETERS], KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID)) {
+ return;
+ }
+ // temporal count T
+ short time =
+ KMKeyParameters.findTag(KMType.DATE_TAG, KMType.CREATION_DATETIME, data[KEY_PARAMETERS]);
+ if (time == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ time = KMIntegerTag.cast(time).getValue();
+
+ // Reset After Rotation R - it will be part of HW Enforced key
+ // characteristics
+ byte resetAfterRotation = 0;
+ if (KMTag.isPresent(data[KEY_PARAMETERS], KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION)) {
+ resetAfterRotation = 0x01;
+ }
+
+ cert.makeUniqueId(
+ scratchPad,
+ (short) 0,
+ KMInteger.cast(time).getBuffer(),
+ KMInteger.cast(time).getStartOff(),
+ KMInteger.cast(time).length(),
+ KMByteBlob.cast(attAppId).getBuffer(),
+ KMByteBlob.cast(attAppId).getStartOff(),
+ KMByteBlob.cast(attAppId).length(),
+ resetAfterRotation,
+ kmDataStore.getMasterKey());
+ }
+
+ private static void validateRSAKey(byte[] scratchPad) {
+ // Read key size
+ if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ if (!KMTag.isValidPublicExponent(data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ }
+
+ // Generate key handlers
+ private static void generateRSAKey(byte[] scratchPad) {
+ // Validate RSA Key
+ validateRSAKey(scratchPad);
+ // Now generate 2048 bit RSA keypair for the given exponent
+ short[] lengths = tmpVariables;
+ data[PUB_KEY] = KMByteBlob.instance((short) 256);
+ data[SECRET] = KMByteBlob.instance((short) 256);
+ seProvider.createAsymmetricKey(
+ KMType.RSA,
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ KMByteBlob.cast(data[PUB_KEY]).getBuffer(),
+ KMByteBlob.cast(data[PUB_KEY]).getStartOff(),
+ KMByteBlob.cast(data[PUB_KEY]).length(),
+ lengths);
+
+ data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]);
+ }
+
+ private static void validateAESKey() {
+ // Read key size
+ if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ // Read Block mode - array of byte values
+ if (KMTag.isPresent(data[KEY_PARAMETERS], KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE)) {
+ short blockModes =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]);
+ // If it is a GCM mode
+ if (KMEnumArrayTag.cast(blockModes).contains(KMType.GCM)) {
+ // Min mac length must be present
+ KMTag.assertPresence(
+ data[KEY_PARAMETERS],
+ KMType.UINT_TAG,
+ KMType.MIN_MAC_LENGTH,
+ KMError.MISSING_MIN_MAC_LENGTH);
+ short macLength =
+ KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]);
+ macLength = KMIntegerTag.cast(macLength).getValue();
+ // Validate the MIN_MAC_LENGTH for AES - should be multiple of 8, less then 128 bits
+ // and greater the 96 bits
+ if (KMInteger.cast(macLength).getSignificantShort() != 0
+ || KMInteger.cast(macLength).getShort() > 128
+ || KMInteger.cast(macLength).getShort() < 96
+ || (KMInteger.cast(macLength).getShort() % 8) != 0) {
+ KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH);
+ }
+ }
+ }
+ }
+
+ private static void generateAESKey(byte[] scratchPad) {
+ validateAESKey();
+ short keysize =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+ short len = seProvider.createSymmetricKey(KMType.AES, keysize, scratchPad, (short) 0);
+ data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len);
+ data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+ }
+
+ private static void validateECKeys() {
+ // Read key size
+ short ecCurve = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]);
+ /* In KeyMint 2.0, If EC_CURVE not provided, generateKey
+ * must return ErrorCode::UNSUPPORTED_KEY_SIZE or ErrorCode::UNSUPPORTED_EC_CURVE.
+ */
+ if (ecCurve != KMType.P_256) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ short ecKeySize = KMEnumTag.getValue(KMType.KEYSIZE, data[KEY_PARAMETERS]);
+ if ((ecKeySize != KMType.INVALID_VALUE) && !KMTag.isValidKeySize(data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ }
+
+ private static void generateECKeys(byte[] scratchPad) {
+ validateECKeys();
+ short[] lengths = tmpVariables;
+ seProvider.createAsymmetricKey(
+ KMType.EC,
+ scratchPad,
+ (short) 0,
+ (short) 128,
+ scratchPad,
+ (short) 128,
+ (short) 128,
+ lengths);
+ data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 128, lengths[1]);
+ data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, lengths[0]);
+ data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]);
+ }
+
+ private static void validateTDESKey() {
+ if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ // Read Minimum Mac length - it must not be present
+ KMTag.assertAbsence(
+ data[KEY_PARAMETERS], KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMError.INVALID_TAG);
+ }
+
+ private static void generateTDESKey(byte[] scratchPad) {
+ validateTDESKey();
+ short len = seProvider.createSymmetricKey(KMType.DES, (short) 168, scratchPad, (short) 0);
+ data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len);
+ data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+ }
+
+ private static void validateHmacKey() {
+ // If params does not contain any digest throw unsupported digest error.
+ KMTag.assertPresence(
+ data[KEY_PARAMETERS], KMType.ENUM_ARRAY_TAG, KMType.DIGEST, KMError.UNSUPPORTED_DIGEST);
+
+ // check whether digest sizes are greater then or equal to min mac length.
+ // Only SHA256 digest must be supported.
+ if (!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+ }
+ // Read Minimum Mac length
+ KMTag.assertPresence(
+ data[KEY_PARAMETERS],
+ KMType.UINT_TAG,
+ KMType.MIN_MAC_LENGTH,
+ KMError.MISSING_MIN_MAC_LENGTH);
+ short minMacLength =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]);
+
+ if (((short) (minMacLength % 8) != 0)
+ || minMacLength < MIN_HMAC_LENGTH_BITS
+ || minMacLength > SHA256_DIGEST_LEN_BITS) {
+ KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH);
+ }
+ // Read Keysize
+ if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ }
+
+ private static void generateHmacKey(byte[] scratchPad) {
+ validateHmacKey();
+ short keysize =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+ // generate HMAC Key
+ short len = seProvider.createSymmetricKey(KMType.HMAC, keysize, scratchPad, (short) 0);
+ data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len);
+ data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+ }
+
+ // This function is only called from processUpgradeKey command.
+ // 1. Update the latest values of OSVersion, OSPatch, VendorPatch and BootPatch in the
+ // KeyBlob's KeyCharacteristics.
+ // 2. Re-create KeyBlob's KeyCharacteristics from HW_PARAMS to make sure we don't miss
+ // anything which happens in these functions makeSbEnforced and makeTeeEnforced in
+ // the future. Like validations.
+ // 3. No need to create Keystore Enforced list here as it is not required to be included in
+ // the KeyBlob's KeyCharacteristics.
+ // 4. No need to create KeyCharacteristics as upgradeKey does not require to return any
+ // KeyCharacteristics back.
+ private static void upgradeKeyBlobKeyCharacteristics(short hwParams, byte[] scratchPad) {
+ short osVersion = kmDataStore.getOsVersion();
+ short osPatch = kmDataStore.getOsPatch();
+ short vendorPatch = kmDataStore.getVendorPatchLevel();
+ short bootPatch = kmDataStore.getBootPatchLevel();
+ data[SB_PARAMETERS] =
+ KMKeyParameters.makeSbEnforced(
+ hwParams, (byte) data[ORIGIN], osVersion, osPatch, vendorPatch, bootPatch, scratchPad);
+ data[TEE_PARAMETERS] = KMKeyParameters.makeTeeEnforced(hwParams, scratchPad);
+ data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]);
+ }
+
+ private static void makeKeyCharacteristics(byte[] scratchPad) {
+ short osVersion = kmDataStore.getOsVersion();
+ short osPatch = kmDataStore.getOsPatch();
+ short vendorPatch = kmDataStore.getVendorPatchLevel();
+ short bootPatch = kmDataStore.getBootPatchLevel();
+ data[SB_PARAMETERS] =
+ KMKeyParameters.makeSbEnforced(
+ data[KEY_PARAMETERS],
+ (byte) data[ORIGIN],
+ osVersion,
+ osPatch,
+ vendorPatch,
+ bootPatch,
+ scratchPad);
+ data[TEE_PARAMETERS] = KMKeyParameters.makeTeeEnforced(data[KEY_PARAMETERS], scratchPad);
+ data[SW_PARAMETERS] = KMKeyParameters.makeKeystoreEnforced(data[KEY_PARAMETERS], scratchPad);
+ data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]);
+ data[KEY_CHARACTERISTICS] = KMKeyCharacteristics.instance();
+ KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setStrongboxEnforced(data[SB_PARAMETERS]);
+ KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setKeystoreEnforced(data[SW_PARAMETERS]);
+ KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setTeeEnforced(data[TEE_PARAMETERS]);
+ }
+
+ private static void createEncryptedKeyBlob(byte[] scratchPad) {
+ // make root of trust blob
+ data[ROT] = readROT(scratchPad, KEYBLOB_CURRENT_VERSION);
+ if (data[ROT] == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ // make hidden key params list
+ data[HIDDEN_PARAMETERS] =
+ KMKeyParameters.makeHidden(data[KEY_PARAMETERS], data[ROT], scratchPad);
+ data[KEY_BLOB_VERSION_DATA_OFFSET] = KMInteger.uint_16(KEYBLOB_CURRENT_VERSION);
+ // create custom tags
+ data[CUSTOM_TAGS] = KMKeyParameters.makeCustomTags(data[HW_PARAMETERS], scratchPad);
+ // encrypt the secret and cryptographically attach that to authorization data
+ encryptSecret(scratchPad);
+ // create key blob array
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_SECRET, data[SECRET]);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_AUTH_TAG, data[AUTH_TAG]);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_NONCE, data[NONCE]);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_VERSION_OFFSET, data[KEY_BLOB_VERSION_DATA_OFFSET]);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_CUSTOM_TAGS, data[CUSTOM_TAGS]);
+
+ short tempChar = KMKeyCharacteristics.instance();
+ short emptyParam = KMArray.instance((short) 0);
+ emptyParam = KMKeyParameters.instance(emptyParam);
+ KMKeyCharacteristics.cast(tempChar).setStrongboxEnforced(data[SB_PARAMETERS]);
+ KMKeyCharacteristics.cast(tempChar).setKeystoreEnforced(emptyParam);
+ KMKeyCharacteristics.cast(tempChar).setTeeEnforced(data[TEE_PARAMETERS]);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PARAMS, tempChar);
+ }
+
+ // Read RoT
+ public static short readROT(byte[] scratchPad, short version) {
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+ short len = kmDataStore.getBootKey(scratchPad, (short) 0);
+ // As per IKeyMintDevice.aidl specification The root of trust
+ // consists of verifyBootKey, boot state and device locked.
+ if (version <= KEYBLOB_VERSION_1) {
+ // To parse old keyblobs verified boot hash is included in
+ // the root of trust.
+ len += kmDataStore.getVerifiedBootHash(scratchPad, (short) len);
+ }
+ short bootState = kmDataStore.getBootState();
+ len = Util.setShort(scratchPad, len, bootState);
+ if (kmDataStore.isDeviceBootLocked()) {
+ scratchPad[len] = (byte) 1;
+ } else {
+ scratchPad[len] = (byte) 0;
+ }
+ len++;
+ return KMByteBlob.instance(scratchPad, (short) 0, len);
+ }
+
+ private static void encryptSecret(byte[] scratchPad) {
+ // make nonce
+ data[NONCE] = KMByteBlob.instance(AES_GCM_NONCE_LENGTH);
+ data[AUTH_TAG] = KMByteBlob.instance(AES_GCM_AUTH_TAG_LENGTH);
+ seProvider.newRandomNumber(
+ KMByteBlob.cast(data[NONCE]).getBuffer(),
+ KMByteBlob.cast(data[NONCE]).getStartOff(),
+ KMByteBlob.cast(data[NONCE]).length());
+ // derive master key - stored in derivedKey
+ short len = deriveKey(scratchPad);
+ len =
+ seProvider.aesGCMEncrypt(
+ KMByteBlob.cast(data[DERIVED_KEY]).getBuffer(),
+ KMByteBlob.cast(data[DERIVED_KEY]).getStartOff(),
+ KMByteBlob.cast(data[DERIVED_KEY]).length(),
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(data[NONCE]).getBuffer(),
+ KMByteBlob.cast(data[NONCE]).getStartOff(),
+ KMByteBlob.cast(data[NONCE]).length(),
+ null,
+ (short) 0,
+ (short) 0,
+ KMByteBlob.cast(data[AUTH_TAG]).getBuffer(),
+ KMByteBlob.cast(data[AUTH_TAG]).getStartOff(),
+ KMByteBlob.cast(data[AUTH_TAG]).length());
+
+ if (len > 0 && len != KMByteBlob.cast(data[SECRET]).length()) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len);
+ }
+
+ private static byte getKeyType(short hardwareParams) {
+ short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hardwareParams);
+ if (KMEnumTag.cast(alg).getValue() == KMType.RSA
+ || KMEnumTag.cast(alg).getValue() == KMType.EC) {
+ return ASYM_KEY_TYPE;
+ }
+ return SYM_KEY_TYPE;
+ }
+
+ private static void makeAuthData(short version, byte[] scratchPad) {
+ // For KeyBlob V2: Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS, CUSTOM_TAGS, VERSION and
+ // PUB_KEY.
+ // For KeyBlob V1: Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS, VERSION and PUB_KEY.
+ // For KeyBlob V0: Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS and PUB_KEY.
+ // VERSION is included only for KeyBlobs having version >= 1.
+ // PUB_KEY is included for only ASYMMETRIC KeyBlobs.
+ short index = 0;
+ short numParams = 0;
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 10, (byte) 0);
+ byte keyType = getKeyType(data[HW_PARAMETERS]);
+ // Copy the relevant parameters in the scratchPad in the order
+ // 1. HW_PARAMETERS
+ // 2. HIDDEN_PARAMETERS
+ // 3. VERSION ( Only Version >= 1)
+ // 4. PUB_KEY ( Only for Asymmetric Keys)
+ switch (version) {
+ case (short) 0:
+ numParams = 2;
+ Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals());
+ Util.setShort(
+ scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals());
+ // For Asymmetric Keys include the PUB_KEY.
+ if (keyType == ASYM_KEY_TYPE) {
+ numParams = 3;
+ Util.setShort(scratchPad, (short) 4, data[PUB_KEY]);
+ }
+ break;
+ case (short) 1:
+ numParams = 3;
+ Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals());
+ Util.setShort(
+ scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals());
+ Util.setShort(scratchPad, (short) 4, data[KEY_BLOB_VERSION_DATA_OFFSET]);
+ // For Asymmetric Keys include the PUB_KEY.
+ if (keyType == ASYM_KEY_TYPE) {
+ numParams = 4;
+ Util.setShort(scratchPad, (short) 6, data[PUB_KEY]);
+ }
+ break;
+ case (short) 2:
+ numParams = 4;
+ Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals());
+ Util.setShort(
+ scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals());
+ Util.setShort(scratchPad, (short) 4, KMKeyParameters.cast(data[CUSTOM_TAGS]).getVals());
+ Util.setShort(scratchPad, (short) 6, data[KEY_BLOB_VERSION_DATA_OFFSET]);
+ // For Asymmetric Keys include the PUB_KEY.
+ if (keyType == ASYM_KEY_TYPE) {
+ numParams = 5;
+ Util.setShort(scratchPad, (short) 8, data[PUB_KEY]);
+ }
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ short prevReclaimIndex = repository.getHeapReclaimIndex();
+ short authIndex = repository.allocReclaimableMemory(MAX_AUTH_DATA_SIZE);
+ index = 0;
+ short len = 0;
+ Util.arrayFillNonAtomic(repository.getHeap(), authIndex, MAX_AUTH_DATA_SIZE, (byte) 0);
+ while (index < numParams) {
+ short tag = Util.getShort(scratchPad, (short) (index * 2));
+ len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32), prevReclaimIndex);
+ Util.arrayCopyNonAtomic(
+ repository.getHeap(),
+ authIndex,
+ repository.getHeap(),
+ (short) (authIndex + len + 32),
+ (short) 32);
+ len =
+ seProvider.messageDigest256(
+ repository.getHeap(),
+ (short) (authIndex + 32),
+ (short) (len + 32),
+ repository.getHeap(),
+ authIndex);
+ if (len != 32) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ index++;
+ }
+ short authDataIndex = repository.alloc(len);
+ Util.arrayCopyNonAtomic(
+ repository.getHeap(), authIndex, repository.getHeap(), authDataIndex, len);
+ repository.reclaimMemory(MAX_AUTH_DATA_SIZE);
+ data[AUTH_DATA] = authDataIndex;
+ data[AUTH_DATA_LENGTH] = len;
+ }
+
+ private static short deriveKeyForOldKeyBlobs(byte[] scratchPad) {
+ // KeyDerivation:
+ // 1. Do HMAC Sign, Auth data.
+ // 2. HMAC Sign generates an output of 32 bytes length.
+ // Consume only first 16 bytes as derived key.
+ // Hmac sign.
+ short len =
+ seProvider.hmacKDF(
+ kmDataStore.getMasterKey(),
+ repository.getHeap(),
+ data[AUTH_DATA],
+ data[AUTH_DATA_LENGTH],
+ scratchPad,
+ (short) 0);
+ if (len < 16) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ len = 16;
+ data[DERIVED_KEY] = KMByteBlob.instance(scratchPad, (short) 0, len);
+ return len;
+ }
+
+ private static short deriveKey(byte[] scratchPad) {
+ // For KeyBlob V3: Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS, CUSTOM_TAGS, VERSION and
+ // PUB_KEY.
+ short index = 0;
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 10, (byte) 0);
+ byte keyType = getKeyType(data[HW_PARAMETERS]);
+ // Copy the relevant parameters in the scratchPad in the order
+ // 1. HW_PARAMETERS
+ // 2. HIDDEN_PARAMETERS
+ // 3. CUSTOM_TAGS
+ // 3. VERSION ( Only Version >= 1)
+ // 4. PUB_KEY ( Only for Asymmetric Keys)
+ short numParams = 4;
+ Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals());
+ Util.setShort(scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals());
+ Util.setShort(scratchPad, (short) 4, KMKeyParameters.cast(data[CUSTOM_TAGS]).getVals());
+ Util.setShort(scratchPad, (short) 6, data[KEY_BLOB_VERSION_DATA_OFFSET]);
+ // For Asymmetric Keys include the PUB_KEY.
+ if (keyType == ASYM_KEY_TYPE) {
+ numParams = 5;
+ Util.setShort(scratchPad, (short) 8, data[PUB_KEY]);
+ }
+ short prevReclaimIndex = repository.getHeapReclaimIndex();
+ short authIndex = repository.allocReclaimableMemory(MAX_AUTH_DATA_SIZE);
+ Util.arrayFillNonAtomic(repository.getHeap(), authIndex, MAX_AUTH_DATA_SIZE, (byte) 0);
+ short len = 0;
+ KMOperation operation = null;
+ try {
+ operation =
+ seProvider.initSymmetricOperation(
+ KMType.SIGN,
+ KMType.HMAC,
+ KMType.SHA2_256,
+ KMType.PADDING_NONE,
+ (byte) KMType.INVALID_VALUE,
+ (Object) kmDataStore.getMasterKey(),
+ KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY,
+ (byte[]) null,
+ (short) 0,
+ (short) 0,
+ (short) 0,
+ false);
+
+ byte arrayHeader = (byte) 0x80;
+ arrayHeader |= (byte) numParams;
+ ((byte[]) repository.getHeap())[authIndex] = arrayHeader;
+ operation.update(repository.getHeap(), authIndex, (short) 1);
+
+ while (index < numParams) {
+ short tag = Util.getShort(scratchPad, (short) (index * 2));
+ len = encoder.encode(tag, repository.getHeap(), (short) authIndex, prevReclaimIndex);
+ operation.update(repository.getHeap(), authIndex, len);
+ index++;
+ }
+ repository.reclaimMemory(MAX_AUTH_DATA_SIZE);
+ // KeyDerivation:
+ // 1. Do HMAC Sign, Auth data.
+ // 2. HMAC Sign generates an output of 32 bytes length.
+ // Consume only first 16 bytes as derived key.
+ // Hmac sign.
+ len = operation.sign(scratchPad, (short) 0, (short) 0, scratchPad, (short) 0);
+ } finally {
+ if (operation != null) {
+ operation.abort();
+ }
+ }
+ if (len < 16) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ len = 16;
+ data[DERIVED_KEY] = KMByteBlob.instance(scratchPad, (short) 0, len);
+ return len;
+ }
+
+ public static void sendResponse(APDU apdu, short err) {
+ short resp = KMArray.instance((short) 1);
+ err = KMError.translate(err);
+ short error = KMInteger.uint_16(err);
+ KMArray.cast(resp).add((short) 0, error);
+ sendOutgoing(apdu, resp);
+ }
+
+ public static void generateRkpKey(byte[] scratchPad, short keyParams) {
+ data[KEY_PARAMETERS] = keyParams;
+ generateECKeys(scratchPad);
+ // create key blob
+ data[ORIGIN] = KMType.GENERATED;
+ makeKeyCharacteristics(scratchPad);
+ createEncryptedKeyBlob(scratchPad);
+ short prevReclaimIndex = repository.getHeapReclaimIndex();
+ short offset = repository.allocReclaimableMemory(MAX_KEYBLOB_SIZE);
+ data[KEY_BLOB] =
+ encoder.encode(
+ data[KEY_BLOB], repository.getHeap(), offset, prevReclaimIndex, MAX_KEYBLOB_SIZE);
+ data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), offset, data[KEY_BLOB]);
+ repository.reclaimMemory(MAX_KEYBLOB_SIZE);
+ }
+
+ public static short getPubKey() {
+ return data[PUB_KEY];
+ }
+
+ public static short getPivateKey() {
+ return data[KEY_BLOB];
+ }
+
+ /**
+ * Encodes the object to the provided apdu buffer.
+ *
+ * @param object Object to be encoded.
+ * @param apduBuf Buffer on which the encoded data is copied.
+ * @param apduOff Start offset of the buffer.
+ * @param maxLen Max value of the expected out length.
+ * @return length of the encoded buffer.
+ */
+ public static short encodeToApduBuffer(
+ short object, byte[] apduBuf, short apduOff, short maxLen) {
+ short prevReclaimIndex = repository.getHeapReclaimIndex();
+ short offset = repository.allocReclaimableMemory(maxLen);
+ short len = encoder.encode(object, repository.getHeap(), offset, prevReclaimIndex, maxLen);
+ Util.arrayCopyNonAtomic(repository.getHeap(), offset, apduBuf, apduOff, len);
+ // release memory
+ repository.reclaimMemory(maxLen);
+ return len;
+ }
+
+ public static short validateCertChain(
+ boolean validateEekRoot,
+ byte expCertAlg,
+ byte expLeafCertAlg,
+ short certChainArr,
+ byte[] scratchPad,
+ Object[] authorizedEekRoots) {
+ short len = KMArray.cast(certChainArr).length();
+ short coseHeadersExp = KMCoseHeaders.exp();
+ // prepare exp for coseky
+ short coseKeyExp = KMCoseKey.exp();
+ short ptr1;
+ short ptr2;
+ short signStructure;
+ short encodedLen;
+ short prevCoseKey = 0;
+ short keySize;
+ short alg = expCertAlg;
+ short index;
+ for (index = 0; index < len; index++) {
+ ptr1 = KMArray.cast(certChainArr).get(index);
+
+ // validate protected Headers
+ ptr2 = KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PROTECTED_PARAMS_OFFSET);
+ ptr2 =
+ decoder.decode(
+ coseHeadersExp,
+ KMByteBlob.cast(ptr2).getBuffer(),
+ KMByteBlob.cast(ptr2).getStartOff(),
+ KMByteBlob.cast(ptr2).length());
+ if (!KMCoseHeaders.cast(ptr2).isDataValid(rkp.rkpTmpVariables, alg, KMType.INVALID_VALUE)) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+
+ // parse and get the public key from payload.
+ ptr2 = KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PAYLOAD_OFFSET);
+ ptr2 =
+ decoder.decode(
+ coseKeyExp,
+ KMByteBlob.cast(ptr2).getBuffer(),
+ KMByteBlob.cast(ptr2).getStartOff(),
+ KMByteBlob.cast(ptr2).length());
+ if ((index == (short) (len - 1)) && len > 1) {
+ alg = expLeafCertAlg;
+ }
+ if (!KMCoseKey.cast(ptr2)
+ .isDataValid(
+ rkp.rkpTmpVariables,
+ KMCose.COSE_KEY_TYPE_EC2,
+ KMType.INVALID_VALUE,
+ alg,
+ KMCose.COSE_ECCURVE_256)) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+ if (prevCoseKey == 0) {
+ prevCoseKey = ptr2;
+ }
+ // Get the public key.
+ keySize = KMCoseKey.cast(prevCoseKey).getEcdsa256PublicKey(scratchPad, (short) 0);
+ if (keySize != 65) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+ if (validateEekRoot && (index == 0)) {
+ boolean found = false;
+ // In prod mode the first pubkey should match a well-known Google public key.
+ for (short i = 0; i < (short) authorizedEekRoots.length; i++) {
+ if (0
+ == Util.arrayCompare(
+ scratchPad,
+ (short) 0,
+ (byte[]) authorizedEekRoots[i],
+ (short) 0,
+ (short) ((byte[]) authorizedEekRoots[i]).length)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+ }
+ // Validate signature.
+ signStructure =
+ KMCose.constructCoseSignStructure(
+ KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PROTECTED_PARAMS_OFFSET),
+ KMByteBlob.instance((short) 0),
+ KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PAYLOAD_OFFSET));
+ encodedLen =
+ KMKeymasterApplet.encodeToApduBuffer(
+ signStructure, scratchPad, keySize, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+
+ short signatureLen =
+ rkp.encodeES256CoseSignSignature(
+ KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET))
+ .getBuffer(),
+ KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET))
+ .getStartOff(),
+ KMByteBlob.length(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)),
+ scratchPad,
+ (short) (keySize + encodedLen));
+
+ if (!seProvider.ecVerify256(
+ scratchPad,
+ (short) 0,
+ keySize,
+ scratchPad,
+ keySize,
+ encodedLen,
+ scratchPad,
+ (short) (keySize + encodedLen),
+ signatureLen)) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+ prevCoseKey = ptr2;
+ }
+ return prevCoseKey;
+ }
+
+ public static short generateDiceCertChain(byte[] scratchPad) {
+ if (kmDataStore.isProvisionLocked()) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+ KMKey deviceUniqueKey = kmDataStore.getRkpDeviceUniqueKeyPair();
+ short temp = deviceUniqueKey.getPublicKey(scratchPad, (short) 0);
+ short coseKey =
+ KMCose.constructCoseKey(
+ rkp.rkpTmpVariables,
+ KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2),
+ KMType.INVALID_VALUE,
+ KMNInteger.uint_8(KMCose.COSE_ALG_ES256),
+ KMInteger.uint_8(KMCose.COSE_ECCURVE_256),
+ scratchPad,
+ (short) 0,
+ temp,
+ KMType.INVALID_VALUE,
+ false);
+ temp =
+ KMKeymasterApplet.encodeToApduBuffer(
+ coseKey, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ // Construct payload.
+ short payload =
+ KMCose.constructCoseCertPayload(
+ KMCosePairTextStringTag.instance(
+ KMInteger.uint_8(KMCose.ISSUER),
+ KMTextString.instance(
+ KMCose.TEST_ISSUER_NAME, (short) 0, (short) KMCose.TEST_ISSUER_NAME.length)),
+ KMCosePairTextStringTag.instance(
+ KMInteger.uint_8(KMCose.SUBJECT),
+ KMTextString.instance(
+ KMCose.TEST_SUBJECT_NAME, (short) 0, (short) KMCose.TEST_SUBJECT_NAME.length)),
+ KMCosePairByteBlobTag.instance(
+ KMNInteger.uint_32(KMCose.SUBJECT_PUBLIC_KEY, (short) 0),
+ KMByteBlob.instance(scratchPad, (short) 0, temp)),
+ KMCosePairByteBlobTag.instance(
+ KMNInteger.uint_32(KMCose.KEY_USAGE, (short) 0),
+ KMByteBlob.instance(
+ KMCose.KEY_USAGE_SIGN, (short) 0, (short) KMCose.KEY_USAGE_SIGN.length)));
+ // temp temporarily holds the length of encoded cert payload.
+ temp =
+ KMKeymasterApplet.encodeToApduBuffer(
+ payload, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ payload = KMByteBlob.instance(scratchPad, (short) 0, temp);
+
+ // protected header
+ short protectedHeader =
+ KMCose.constructHeaders(
+ rkp.rkpTmpVariables,
+ KMNInteger.uint_8(KMCose.COSE_ALG_ES256),
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE);
+ // temp temporarily holds the length of encoded headers.
+ temp =
+ KMKeymasterApplet.encodeToApduBuffer(
+ protectedHeader, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, temp);
+
+ // unprotected headers.
+ short arr = KMArray.instance((short) 0);
+ short unprotectedHeader = KMCoseHeaders.instance(arr);
+
+ // construct cose sign structure.
+ short coseSignStructure =
+ KMCose.constructCoseSignStructure(protectedHeader, KMByteBlob.instance((short) 0), payload);
+ // temp temporarily holds the length of encoded sign structure.
+ // Encode cose Sign_Structure.
+ temp =
+ KMKeymasterApplet.encodeToApduBuffer(
+ coseSignStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ // do sign
+ short len =
+ seProvider.signWithDeviceUniqueKey(
+ deviceUniqueKey, scratchPad, (short) 0, temp, scratchPad, temp);
+ len =
+ KMAsn1Parser.instance()
+ .decodeEcdsa256Signature(KMByteBlob.instance(scratchPad, temp, len), scratchPad, temp);
+ coseSignStructure = KMByteBlob.instance(scratchPad, temp, len);
+
+ // construct cose_sign1
+ short coseSign1 =
+ KMCose.constructCoseSign1(protectedHeader, unprotectedHeader, payload, coseSignStructure);
+
+ // [Cose_Key, Cose_Sign1]
+ short dcc = KMArray.instance((short) 2);
+ KMArray.cast(dcc).add((short) 0, coseKey);
+ KMArray.cast(dcc).add((short) 1, coseSign1);
+ return dcc;
+ }
+
+ protected void initHmacNonceAndSeed() {
+ short nonce = repository.alloc((short) 32);
+ seProvider.newRandomNumber(
+ repository.getHeap(), nonce, KMKeymintDataStore.HMAC_SEED_NONCE_SIZE);
+ kmDataStore.initHmacNonce(repository.getHeap(), nonce, KMKeymintDataStore.HMAC_SEED_NONCE_SIZE);
+ }
+
+ private void releaseAllOperations() {
+ short index = 0;
+ while (index < MAX_OPERATIONS_COUNT) {
+ opTable[index].reset();
+ index++;
+ }
+ }
+
+ private KMOperationState reserveOperation(short algorithm, short opHandle) {
+ short index = 0;
+ while (index < MAX_OPERATIONS_COUNT) {
+ if (opTable[index].getAlgorithm() == KMType.INVALID_VALUE) {
+ opTable[index].reset();
+ opTable[index].setAlgorithm(algorithm);
+ opTable[index].setHandle(
+ KMInteger.cast(opHandle).getBuffer(),
+ KMInteger.cast(opHandle).getStartOff(),
+ KMInteger.cast(opHandle).length());
+ return opTable[index];
+ }
+ index++;
+ }
+ return null;
+ }
+
+ private KMOperationState findOperation(short handle) {
+ return findOperation(
+ KMInteger.cast(handle).getBuffer(),
+ KMInteger.cast(handle).getStartOff(),
+ KMInteger.cast(handle).length());
+ }
+
+ private KMOperationState findOperation(byte[] opHandle, short start, short len) {
+ short index = 0;
+ while (index < MAX_OPERATIONS_COUNT) {
+ if (opTable[index].compare(opHandle, start, len) == 0) {
+ if (opTable[index].getAlgorithm() != KMType.INVALID_VALUE) {
+ return opTable[index];
+ }
+ }
+ index++;
+ }
+ return null;
+ }
+
+ private void releaseOperation(KMOperationState op) {
+ op.reset();
+ }
+
+ /**
+ * Selects this applet.
+ *
+ * @return Returns true if the keymaster is in correct state
+ */
+ @Override
+ public boolean select() {
+ repository.onSelect();
+ return true;
+ }
+
+ /** De-selects this applet. */
+ @Override
+ public void deselect() {
+ repository.onDeselect();
+ }
+
+ /** Uninstalls the applet after cleaning the repository. */
+ @Override
+ public void uninstall() {
+ repository.onUninstall();
+ }
+
+ protected short mapISOErrorToKMError(short reason) {
+ switch (reason) {
+ case ISO7816.SW_CLA_NOT_SUPPORTED:
+ return KMError.UNSUPPORTED_CLA;
+ case ISO7816.SW_CONDITIONS_NOT_SATISFIED:
+ return KMError.SW_CONDITIONS_NOT_SATISFIED;
+ case ISO7816.SW_COMMAND_NOT_ALLOWED:
+ return KMError.CMD_NOT_ALLOWED;
+ case ISO7816.SW_DATA_INVALID:
+ return KMError.INVALID_DATA;
+ case ISO7816.SW_INCORRECT_P1P2:
+ return KMError.INVALID_P1P2;
+ case ISO7816.SW_INS_NOT_SUPPORTED:
+ return KMError.UNSUPPORTED_INSTRUCTION;
+ case ISO7816.SW_WRONG_LENGTH:
+ return KMError.SW_WRONG_LENGTH;
+ case ISO7816.SW_UNKNOWN:
+ default:
+ return KMError.UNKNOWN_ERROR;
+ }
+ }
+
+ protected short mapCryptoErrorToKMError(short reason) {
+ switch (reason) {
+ case CryptoException.ILLEGAL_USE:
+ return KMError.CRYPTO_ILLEGAL_USE;
+ case CryptoException.ILLEGAL_VALUE:
+ return KMError.CRYPTO_ILLEGAL_VALUE;
+ case CryptoException.INVALID_INIT:
+ return KMError.CRYPTO_INVALID_INIT;
+ case CryptoException.NO_SUCH_ALGORITHM:
+ return KMError.CRYPTO_NO_SUCH_ALGORITHM;
+ case CryptoException.UNINITIALIZED_KEY:
+ return KMError.CRYPTO_UNINITIALIZED_KEY;
+ default:
+ return KMError.UNKNOWN_ERROR;
+ }
+ }
+
+ public void updateApduStatusFlags(short apduIns) {
+ switch (apduIns) {
+ case INS_EXPORT_KEY_CMD:
+ case INS_DELETE_ALL_KEYS_CMD:
+ case INS_DESTROY_ATT_IDS_CMD:
+ case INS_VERIFY_AUTHORIZATION_CMD:
+ case INS_GET_HMAC_SHARING_PARAM_CMD:
+ case INS_GET_HW_INFO_CMD:
+ case INS_EARLY_BOOT_ENDED_CMD:
+ case INS_GET_ROT_CHALLENGE_CMD:
+ case INS_GET_ROT_DATA_CMD:
+ case INS_GET_RKP_HARDWARE_INFO:
+ case INS_FINISH_SEND_DATA_CMD:
+ case INS_GET_UDS_CERTS_CMD:
+ case INS_GET_DICE_CERT_CHAIN_CMD:
+ apduStatusFlags[APDU_CASE4_COMMAND_STATUS_INDEX] = 0;
+ break;
+ default:
+ // By default the instruction is set to case 4 command instruction.
+ break;
+ }
+ }
+
+ /**
+ * Processes an incoming APDU and handles it using command objects.
+ *
+ * @param apdu the incoming APDU
+ */
+ @Override
+ public void process(APDU apdu) {
+ try {
+ resetTransientBuffers();
+ repository.onProcess();
+ // If this is select applet apdu which is selecting this applet then return
+ if (apdu.isISOInterindustryCLA()) {
+ if (selectingApplet()) {
+ return;
+ }
+ }
+ byte[] apduBuffer = apdu.getBuffer();
+ byte apduIns = apduBuffer[ISO7816.OFFSET_INS];
+ if (!isKeyMintReady(apduIns)) {
+ ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED);
+ }
+ switch (apduIns) {
+ case INS_INIT_STRONGBOX_CMD:
+ processInitStrongBoxCmd(apdu);
+ sendResponse(apdu, KMError.OK);
+ return;
+ case INS_GENERATE_KEY_CMD:
+ processGenerateKey(apdu);
+ break;
+ case INS_IMPORT_KEY_CMD:
+ processImportKeyCmd(apdu);
+ break;
+ case INS_BEGIN_IMPORT_WRAPPED_KEY_CMD:
+ processBeginImportWrappedKeyCmd(apdu);
+ break;
+ case INS_FINISH_IMPORT_WRAPPED_KEY_CMD:
+ processFinishImportWrappedKeyCmd(apdu);
+ break;
+ case INS_EXPORT_KEY_CMD:
+ processExportKeyCmd(apdu);
+ break;
+ case INS_UPGRADE_KEY_CMD:
+ processUpgradeKeyCmd(apdu);
+ break;
+ case INS_DELETE_KEY_CMD:
+ processDeleteKeyCmd(apdu);
+ break;
+ case INS_DELETE_ALL_KEYS_CMD:
+ processDeleteAllKeysCmd(apdu);
+ break;
+ case INS_ADD_RNG_ENTROPY_CMD:
+ processAddRngEntropyCmd(apdu);
+ break;
+ case INS_COMPUTE_SHARED_HMAC_CMD:
+ processComputeSharedHmacCmd(apdu);
+ break;
+ case INS_DESTROY_ATT_IDS_CMD:
+ processDestroyAttIdsCmd(apdu);
+ break;
+ case INS_VERIFY_AUTHORIZATION_CMD:
+ processVerifyAuthorizationCmd(apdu);
+ break;
+ case INS_GET_HMAC_SHARING_PARAM_CMD:
+ processGetHmacSharingParamCmd(apdu);
+ break;
+ case INS_GET_KEY_CHARACTERISTICS_CMD:
+ processGetKeyCharacteristicsCmd(apdu);
+ break;
+ case INS_GET_HW_INFO_CMD:
+ processGetHwInfoCmd(apdu);
+ break;
+ case INS_BEGIN_OPERATION_CMD:
+ processBeginOperationCmd(apdu);
+ break;
+ case INS_UPDATE_OPERATION_CMD:
+ processUpdateOperationCmd(apdu);
+ break;
+ case INS_FINISH_OPERATION_CMD:
+ processFinishOperationCmd(apdu);
+ break;
+ case INS_ABORT_OPERATION_CMD:
+ processAbortOperationCmd(apdu);
+ break;
+ case INS_DEVICE_LOCKED_CMD:
+ processDeviceLockedCmd(apdu);
+ break;
+ case INS_EARLY_BOOT_ENDED_CMD:
+ processEarlyBootEndedCmd(apdu);
+ break;
+ case INS_UPDATE_AAD_OPERATION_CMD:
+ processUpdateAadOperationCmd(apdu);
+ break;
+ case INS_GENERATE_RKP_KEY_CMD:
+ case INS_BEGIN_SEND_DATA_CMD:
+ case INS_UPDATE_KEY_CMD:
+ case INS_FINISH_SEND_DATA_CMD:
+ case INS_GET_RKP_HARDWARE_INFO:
+ case INS_GET_UDS_CERTS_CMD:
+ case INS_GET_DICE_CERT_CHAIN_CMD:
+ rkp.process(apduIns, apdu);
+ break;
+ // KeyMint 2.0
+ case INS_GET_ROT_CHALLENGE_CMD:
+ processGetRootOfTrustChallenge(apdu);
+ break;
+ case INS_GET_ROT_DATA_CMD:
+ sendResponse(apdu, KMError.UNIMPLEMENTED);
+ break;
+ case INS_SEND_ROT_DATA_CMD:
+ processSendRootOfTrust(apdu);
+ break;
+ default:
+ ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
+ }
+ } catch (KMException exception) {
+ freeOperations();
+ resetWrappingKey();
+ sendResponse(apdu, KMException.reason());
+ } catch (ISOException exp) {
+ freeOperations();
+ resetWrappingKey();
+ sendResponse(apdu, mapISOErrorToKMError(exp.getReason()));
+ } catch (CryptoException e) {
+ freeOperations();
+ resetWrappingKey();
+ sendResponse(apdu, mapCryptoErrorToKMError(e.getReason()));
+ } catch (Exception e) {
+ freeOperations();
+ resetWrappingKey();
+ sendResponse(apdu, KMError.GENERIC_UNKNOWN_ERROR);
+ } finally {
+ repository.clean();
+ }
+ }
+
+ private void processGetRootOfTrustChallenge(APDU apdu) {
+ byte[] scratchpad = apdu.getBuffer();
+ // Generate 16-byte random challenge nonce, used to prove freshness when exchanging root of
+ // trust data.
+ seProvider.newRandomNumber(scratchpad, (short) 0, (short) 16);
+ kmDataStore.setChallenge(scratchpad, (short) 0, (short) 16);
+ short challenge = KMByteBlob.instance(scratchpad, (short) 0, (short) 16);
+ short arr = KMArray.instance((short) 2);
+ KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(arr).add((short) 1, challenge);
+ sendOutgoing(apdu, arr);
+ }
+
+ private short sendRootOfTrustCmd(APDU apdu) {
+ short arrInst = KMArray.instance((short) 4);
+ short headers = KMCoseHeaders.exp();
+ KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp());
+ KMArray.cast(arrInst).add((short) 1, headers);
+ KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp());
+ KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp());
+ short semanticTag = KMSemanticTag.exp(arrInst);
+ short arr = KMArray.exp(semanticTag);
+ return receiveIncoming(apdu, arr);
+ }
+
+ private void processSendRootOfTrust(APDU apdu) {
+ byte[] scratchPad = apdu.getBuffer();
+ short cmd = KMType.INVALID_VALUE;
+ // As per VTS if the input data is empty or not well-formed
+ // CoseMac return VERIFICATION_FAILED error.
+ try {
+ cmd = sendRootOfTrustCmd(apdu);
+ } catch (Exception e) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+
+ short semanticTag = KMArray.cast(cmd).get((short) 0);
+ short coseMacPtr = KMSemanticTag.cast(semanticTag).getValuePtr();
+ // Exp for KMCoseHeaders
+ short coseHeadersExp = KMCoseHeaders.exp();
+ // validate protected Headers
+ short ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET);
+ ptr =
+ decoder.decode(
+ coseHeadersExp,
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length());
+
+ if (!KMCoseHeaders.cast(ptr)
+ .isDataValid(tmpVariables, KMCose.COSE_ALG_HMAC_256, KMType.INVALID_VALUE)) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+
+ // Validate the Mac
+ short len = kmDataStore.getChallenge(scratchPad, (short) 0);
+ short extAad = KMByteBlob.instance(scratchPad, (short) 0, len);
+ // Compute CoseMac Structure and compare the macs.
+ short rotPayload = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET);
+ short macStructure =
+ KMCose.constructCoseMacStructure(
+ KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET),
+ extAad,
+ rotPayload);
+ short encodedLen =
+ KMKeymasterApplet.encodeToApduBuffer(
+ macStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+
+ if (!seProvider.hmacVerify(
+ kmDataStore.getComputedHmacKey(),
+ scratchPad,
+ (short) 0,
+ encodedLen,
+ KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getBuffer(),
+ KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getStartOff(),
+ KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).length())) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+ // Store the data only once after reboot.
+ // Allow set boot params only when the host device reboots and the applet is in
+ // active state. If host does not support boot signal event, then allow this
+ // instruction any time.
+ kmDataStore.getDeviceBootStatus(scratchPad, (short) 0);
+ if (((scratchPad[0] & KMKeymintDataStore.SET_BOOT_PARAMS_SUCCESS) == 0)) {
+ // store the data.
+ storeRootOfTrust(rotPayload, scratchPad);
+ kmDataStore.setDeviceBootStatus(KMKeymintDataStore.SET_BOOT_PARAMS_SUCCESS);
+ }
+ // Invalidate the challenge
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 16, (byte) 0);
+ kmDataStore.setChallenge(scratchPad, (short) 0, (short) 16);
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private void storeRootOfTrust(short rotPayload, byte[] scratchPad) {
+ short byteBlobExp = KMByteBlob.exp();
+ short intExp = KMInteger.exp();
+ short boolExp = KMSimpleValue.exp();
+ short arr = KMArray.instance((short) 5);
+ KMArray.cast(arr).add((short) 0, byteBlobExp); // Verfied boot key.
+ KMArray.cast(arr).add((short) 1, boolExp); // deviceLocked.
+ KMArray.cast(arr).add((short) 2, intExp); // Verified Boot State.
+ KMArray.cast(arr).add((short) 3, byteBlobExp); // Verfied boot hash.
+ KMArray.cast(arr).add((short) 4, intExp); // Boot patch level
+ short semanticExp = KMSemanticTag.exp(arr);
+
+ short semanticPtr =
+ decoder.decode(
+ semanticExp,
+ KMByteBlob.cast(rotPayload).getBuffer(),
+ KMByteBlob.cast(rotPayload).getStartOff(),
+ KMByteBlob.cast(rotPayload).length());
+ short rotArr = KMSemanticTag.cast(semanticPtr).getValuePtr();
+ // Store verified boot key
+ short ptr = KMArray.cast(rotArr).get((short) 0);
+ kmDataStore.setBootKey(
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length());
+ // Store Boot device locked.
+ ptr = KMArray.cast(rotArr).get((short) 1);
+ kmDataStore.setDeviceLocked(
+ (KMSimpleValue.cast(ptr).getValue() == KMSimpleValue.TRUE) ? true : false);
+ // Store verified boot state
+ ptr = KMArray.cast(rotArr).get((short) 2);
+ kmDataStore.setBootState(KMInteger.cast(ptr).getShort());
+ // Store Verified boot hash
+ ptr = KMArray.cast(rotArr).get((short) 3);
+ kmDataStore.setVerifiedBootHash(
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length());
+ // Store boot patch level
+ ptr = KMArray.cast(rotArr).get((short) 4);
+ kmDataStore.setBootPatchLevel(
+ KMInteger.cast(ptr).getBuffer(),
+ KMInteger.cast(ptr).getStartOff(),
+ KMInteger.cast(ptr).length());
+ }
+
+ // After every device boot, the Keymaster becomes ready to execute all the commands only after
+ // 1. boot parameters are set,
+ // 2. system properties are set and
+ // 3. computed the shared secret successfully.
+ private boolean isKeyMintReady(byte apduIns) {
+ if (kmDataStore.isDeviceReady()) {
+ return true;
+ }
+ // Below commands are allowed even if the Keymaster is not ready.
+ switch (apduIns) {
+ case INS_GET_HW_INFO_CMD:
+ case INS_GET_RKP_HARDWARE_INFO:
+ case INS_ADD_RNG_ENTROPY_CMD:
+ case INS_GET_HMAC_SHARING_PARAM_CMD:
+ case INS_COMPUTE_SHARED_HMAC_CMD:
+ case INS_EARLY_BOOT_ENDED_CMD:
+ case INS_INIT_STRONGBOX_CMD:
+ case INS_GET_ROT_CHALLENGE_CMD:
+ case INS_SEND_ROT_DATA_CMD:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ private void generateUniqueOperationHandle(byte[] buf, short offset, short len) {
+ do {
+ seProvider.newRandomNumber(buf, offset, len);
+ } while (null != findOperation(buf, offset, len));
+ }
+
+ private void freeOperations() {
+ if (data[OP_HANDLE] != KMType.INVALID_VALUE) {
+ KMOperationState op = findOperation(data[OP_HANDLE]);
+ if (op != null) {
+ releaseOperation(op);
+ }
+ }
+ }
+
+ private void processEarlyBootEndedCmd(APDU apdu) {
+ kmDataStore.setEarlyBootEndedStatus(true);
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private short deviceLockedCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 2);
+ short ptr = KMVerificationToken.exp();
+ // passwordOnly
+ KMArray.cast(cmd).add((short) 0, KMInteger.exp());
+ // verification token
+ KMArray.cast(cmd).add((short) 1, ptr);
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processDeviceLockedCmd(APDU apdu) {
+ short cmd = deviceLockedCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+ short passwordOnly = KMArray.cast(cmd).get((short) 0);
+ short verToken = KMArray.cast(cmd).get((short) 1);
+ passwordOnly = KMInteger.cast(passwordOnly).getByte();
+ validateVerificationToken(verToken, scratchPad);
+ short verTime = KMVerificationToken.cast(verToken).getTimestamp();
+ short lastDeviceLockedTime;
+ try {
+ lastDeviceLockedTime = kmDataStore.getDeviceTimeStamp();
+ } catch (KMException e) {
+ lastDeviceLockedTime = KMInteger.uint_8((byte) 0);
+ }
+ if (KMInteger.compare(verTime, lastDeviceLockedTime) > 0) {
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, KMInteger.UINT_64, (byte) 0);
+ KMInteger.cast(verTime).getValue(scratchPad, (short) 0, KMInteger.UINT_64);
+ kmDataStore.setDeviceLock(true);
+ kmDataStore.setDeviceLockPasswordOnly(passwordOnly == 0x01);
+ kmDataStore.setDeviceLockTimestamp(scratchPad, (short) 0, KMInteger.UINT_64);
+ }
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private void resetWrappingKey() {
+ if (!isValidWrappingKey()) {
+ return;
+ }
+ Util.arrayFillNonAtomic(wrappingKey, (short) 1, WRAPPING_KEY_SIZE, (byte) 0);
+ wrappingKey[0] = -1;
+ }
+
+ private boolean isValidWrappingKey() {
+ return wrappingKey[0] != -1;
+ }
+
+ private short getWrappingKey() {
+ return KMByteBlob.instance(wrappingKey, (short) 1, WRAPPING_KEY_SIZE);
+ }
+
+ private void setWrappingKey(short key) {
+ if (KMByteBlob.cast(key).length() != WRAPPING_KEY_SIZE) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ wrappingKey[0] = 0;
+ Util.arrayCopyNonAtomic(
+ KMByteBlob.cast(key).getBuffer(),
+ KMByteBlob.cast(key).getStartOff(),
+ wrappingKey,
+ (short) 1,
+ WRAPPING_KEY_SIZE);
+ }
+
+ protected void resetTransientBuffers() {
+ short index = 0;
+ while (index < data.length) {
+ data[index] = KMType.INVALID_VALUE;
+ index++;
+ }
+ index = 0;
+ while (index < tmpVariables.length) {
+ tmpVariables[index] = KMType.INVALID_VALUE;
+ index++;
+ }
+ }
+
+ public void sendOutgoing(
+ APDU apdu, KMAttestationCert cert, short certStart, short keyblob, short keyChars) {
+ // This is the special case where the output is encoded manually without using
+ // the encoder algorithm. Encoder creates a duplicate copy for each KMType Object.
+ // The output of the generateKey, importKey and importWrappedKey commands are huge so
+ // by manually encoding we can avoid duplicate copies.
+ // The output data is directly written to the end of heap in the below order
+ // output = [
+ // errorCode : uint // ErrorCode
+ // keyBlob : bstr // KeyBlob.
+ // keyChars
+ // certifcate
+ // ]
+ // certificate = [
+ // x509_cert : bstr // X509 certificate
+ // ]
+ // keyChars = { // Map
+ // }
+ byte[] buffer = repository.getHeap();
+
+ if (cert == null) {
+ // This happens for Symmetric keys.
+ short bufferStart = repository.allocReclaimableMemory((short) 1);
+ buffer[bufferStart] = (byte) 0x80; // Array of 0 length.
+ } else {
+ // Encode the certificate into cbor data at the end of the heap
+ // certData = [
+ // x509_cert : bstr // X509 certificate
+ // ]
+ short bufferStart =
+ encoder.encodeCert(
+ repository.getHeap(), certStart, cert.getCertStart(), cert.getCertLength());
+ // reclaim the unused memory in the certificate.
+ repository.reclaimMemory((short) (bufferStart - certStart));
+ }
+
+ // Encode KeyCharacteristics at the end of heap just before data[CERTIFICATE]
+ encodeKeyCharacteristics(keyChars);
+ // and encode it to the end of the buffer before KEY_CHARACTERISTICS
+ encodeKeyBlob(keyblob);
+ // Write Array header and ErrorCode before data[KEY_BLOB]
+ short bufferStartOffset = repository.allocReclaimableMemory((short) 2);
+ Util.setShort(buffer, bufferStartOffset, (short) 0x8400);
+
+ short bufferLength = (short) (KMRepository.HEAP_SIZE - bufferStartOffset);
+ /* In T=0 protocol, On a case 4 command, setIncomingAndReceive() must
+ * be invoked prior to calling setOutgoing(). Otherwise, erroneous
+ * behavior may result
+ * */
+ if (apduStatusFlags[APDU_CASE4_COMMAND_STATUS_INDEX] == 1
+ && apduStatusFlags[APDU_INCOMING_AND_RECEIVE_STATUS_INDEX] == 0
+ && APDU.getProtocol() == APDU.PROTOCOL_T0) {
+ apdu.setIncomingAndReceive();
+ }
+ // Send data
+ apdu.setOutgoing();
+ apdu.setOutgoingLength(bufferLength);
+ apdu.sendBytesLong(buffer, bufferStartOffset, bufferLength);
+ }
+
+ private void processGetHwInfoCmd(APDU apdu) {
+ // No arguments expected
+ final byte version = 3;
+ // Make the response
+ short respPtr = KMArray.instance((short) 6);
+ KMArray resp = KMArray.cast(respPtr);
+ resp.add((short) 0, KMInteger.uint_16(KMError.OK));
+ resp.add((short) 1, KMInteger.uint_8(version));
+ resp.add((short) 2, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX));
+ resp.add(
+ (short) 3,
+ KMByteBlob.instance(
+ JavacardKeymintDevice, (short) 0, (short) JavacardKeymintDevice.length));
+ resp.add((short) 4, KMByteBlob.instance(Google, (short) 0, (short) Google.length));
+ resp.add((short) 5, KMInteger.uint_8((byte) 1));
+ // send buffer to host
+ sendOutgoing(apdu, respPtr);
+ }
+
+ private short addRngEntropyCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 1);
+ // Rng entropy
+ KMArray.cast(cmd).add((short) 0, KMByteBlob.exp());
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processAddRngEntropyCmd(APDU apdu) {
+ // Receive the incoming request fully from the host.
+ short cmd = addRngEntropyCmd(apdu);
+ // Process
+ KMByteBlob blob = KMByteBlob.cast(KMArray.cast(cmd).get((short) 0));
+ // Maximum 2KiB of seed is allowed.
+ if (blob.length() > MAX_SEED_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ seProvider.addRngEntropy(blob.getBuffer(), blob.getStartOff(), blob.length());
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private short getKeyCharacteristicsCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 3);
+ KMArray.cast(cmd).add((short) 0, KMByteBlob.exp());
+ KMArray.cast(cmd).add((short) 1, KMByteBlob.exp());
+ KMArray.cast(cmd).add((short) 2, KMByteBlob.exp());
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processGetKeyCharacteristicsCmd(APDU apdu) {
+ // Receive the incoming request fully from the host.
+ short cmd = getKeyCharacteristicsCmd(apdu);
+ // Re-purpose the apdu buffer as scratch pad.
+ byte[] scratchPad = apdu.getBuffer();
+ data[KEY_BLOB] = KMArray.cast(cmd).get((short) 0);
+ data[APP_ID] = KMArray.cast(cmd).get((short) 1);
+ data[APP_DATA] = KMArray.cast(cmd).get((short) 2);
+ if (KMByteBlob.cast(data[APP_ID]).length() > KMByteTag.MAX_APP_ID_APP_DATA_SIZE
+ || KMByteBlob.cast(data[APP_DATA]).length() > KMByteTag.MAX_APP_ID_APP_DATA_SIZE) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ if (!KMByteBlob.cast(data[APP_ID]).isValid()) {
+ data[APP_ID] = KMType.INVALID_VALUE;
+ }
+ if (!KMByteBlob.cast(data[APP_DATA]).isValid()) {
+ data[APP_DATA] = KMType.INVALID_VALUE;
+ }
+ // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired
+ // function itself.
+ if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) {
+ KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE);
+ }
+ // make response.
+ short resp = KMArray.instance((short) 2);
+ KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(resp).add((short) 1, data[KEY_CHARACTERISTICS]);
+ sendOutgoing(apdu, resp);
+ }
+
+ private void processGetHmacSharingParamCmd(APDU apdu) {
+ // No Arguments
+ // Create HMAC Sharing Parameters
+ short params = KMHmacSharingParameters.instance();
+ short nonce = kmDataStore.getHmacNonce();
+ short seed = KMByteBlob.instance((short) 0);
+ KMHmacSharingParameters.cast(params).setNonce(nonce);
+ KMHmacSharingParameters.cast(params).setSeed(seed);
+ // prepare the response
+ short resp = KMArray.instance((short) 2);
+ KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(resp).add((short) 1, params);
+ sendOutgoing(apdu, resp);
+ }
+
+ private void processDeleteAllKeysCmd(APDU apdu) {
+ // No arguments
+ // This function is triggered when a factory reset event occurs.
+ // Regenerate the master key to render all keys unusable.
+ kmDataStore.regenerateMasterKey();
+ // Send ok
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private short createKeyBlobExp(short version) {
+ short keyBlob = KMType.INVALID_VALUE;
+ short byteBlobExp = KMByteBlob.exp();
+ short keyChar = KMKeyCharacteristics.exp();
+ short keyParam = KMKeyParameters.exp();
+ switch (version) {
+ case (short) 0:
+ // Old KeyBlob has a maximum of 5 elements.
+ keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V0);
+ KMArray.cast(keyBlob).add((short) 0, byteBlobExp); // Secret
+ KMArray.cast(keyBlob).add((short) 1, byteBlobExp); // Nonce
+ KMArray.cast(keyBlob).add((short) 2, byteBlobExp); // AuthTag
+ KMArray.cast(keyBlob).add((short) 3, keyChar); // KeyChars
+ KMArray.cast(keyBlob).add((short) 4, byteBlobExp); // PubKey
+ break;
+ case (short) 1:
+ keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V1);
+ KMArray.cast(keyBlob).add((short) 0, KMInteger.exp()); // Version
+ KMArray.cast(keyBlob).add((short) 1, byteBlobExp); // Secret
+ KMArray.cast(keyBlob).add((short) 2, byteBlobExp); // Nonce
+ KMArray.cast(keyBlob).add((short) 3, byteBlobExp); // AuthTag
+ KMArray.cast(keyBlob).add((short) 4, keyChar); // KeyChars
+ KMArray.cast(keyBlob).add((short) 5, byteBlobExp); // PubKey
+ break;
+ case (short) 2:
+ case (short) 3:
+ keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V2_V3);
+ KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_VERSION_OFFSET, KMInteger.exp());
+ KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_SECRET, byteBlobExp);
+ KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, byteBlobExp);
+ KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_NONCE, byteBlobExp);
+ KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PARAMS, keyChar);
+ KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_CUSTOM_TAGS, keyParam);
+ KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, byteBlobExp);
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ return keyBlob;
+ }
+
+ private void processDeleteKeyCmd(APDU apdu) {
+ // Send ok
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private short computeSharedHmacCmd(APDU apdu) {
+ short params = KMHmacSharingParameters.exp();
+ short paramsVec = KMArray.exp(params);
+ short cmd = KMArray.instance((short) 1);
+ KMArray.cast(cmd).add((short) 0, paramsVec);
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processComputeSharedHmacCmd(APDU apdu) {
+ // Receive the incoming request fully from the host into buffer.
+ short cmd = computeSharedHmacCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+ data[HMAC_SHARING_PARAMS] = KMArray.cast(cmd).get((short) 0);
+ // Concatenate HMAC Params
+ // tmpVariables[0]
+ short paramsLen = KMArray.cast(data[HMAC_SHARING_PARAMS]).length(); // total number of params
+ // tmpVariables[1]
+ short concateBuffer = repository.alloc((short) (paramsLen * HMAC_SHARED_PARAM_MAX_SIZE));
+ // tmpVariables[2]
+ short paramIndex = 0; // index for params
+ // tmpVariables[3]
+ short bufferIndex = 0; // index for concatenation buffer
+ // To check if nonce created by Strongbox is found. This value becomes 1 if both
+ // seed and nonce created here are found in hmac sharing parameters received.
+ // tmpVariables[7] = 0;
+ short found = 0;
+ // tmpVariables[9]
+ short nonce = kmDataStore.getHmacNonce();
+
+ while (paramIndex < paramsLen) {
+ // read HmacSharingParam
+ // tmpVariables[4]
+ short param = KMArray.cast(data[HMAC_SHARING_PARAMS]).get(paramIndex);
+ // get seed - 32 bytes max
+ // tmpVariables[5]
+ short seed = KMHmacSharingParameters.cast(param).getSeed();
+ // tmpVariables[6]
+ short seedLength = KMByteBlob.cast(seed).length();
+ // if seed is present
+ if (seedLength != 0) {
+ // then copy that to concatenation buffer
+ Util.arrayCopyNonAtomic(
+ KMByteBlob.cast(seed).getBuffer(),
+ KMByteBlob.cast(seed).getStartOff(),
+ repository.getHeap(),
+ (short) (concateBuffer + bufferIndex), // concat index
+ seedLength);
+ bufferIndex += seedLength; // increment the concat index
+ } else if (found == 0) {
+ found = 1; // Applet does not have any seed. Potentially
+ }
+ // if nonce is present get nonce - 32 bytes
+ // tmpVariables[5]
+ short paramNonce = KMHmacSharingParameters.cast(param).getNonce();
+ short nonceLen = KMByteBlob.cast(paramNonce).length();
+ // if nonce is less then 32 - it is an error
+ if (nonceLen < 32) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ // copy nonce to concatenation buffer
+ Util.arrayCopyNonAtomic(
+ KMByteBlob.cast(paramNonce).getBuffer(),
+ KMByteBlob.cast(paramNonce).getStartOff(),
+ repository.getHeap(),
+ (short) (concateBuffer + bufferIndex), // index
+ nonceLen);
+
+ // Check if the nonce generated here is present in the hmacSharingParameters array.
+ // Otherwise throw INVALID_ARGUMENT error.
+ if (found == 1) {
+ if (0
+ == Util.arrayCompare(
+ repository.getHeap(),
+ (short) (concateBuffer + bufferIndex),
+ KMByteBlob.cast(nonce).getBuffer(),
+ KMByteBlob.cast(nonce).getStartOff(),
+ nonceLen)) {
+ found = 2; // hmac nonce for this keymaster found.
+ } else {
+ found = 0;
+ }
+ }
+ bufferIndex += nonceLen; // increment by nonce length
+ paramIndex++; // go to next hmac param in the vector
+ }
+ if (found != 2) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ // generate the key and store it in scratch pad - 32 bytes
+ // tmpVariables[6]
+ short keyLen =
+ seProvider.cmacKDF(
+ kmDataStore.getPresharedKey(),
+ ckdfLabel,
+ (short) 0,
+ (short) ckdfLabel.length,
+ repository.getHeap(),
+ concateBuffer,
+ bufferIndex,
+ scratchPad,
+ (short) 0);
+
+ // persist the computed hmac key.
+ kmDataStore.createComputedHmacKey(scratchPad, (short) 0, keyLen);
+ // Generate sharingKey verification signature and store that in scratch pad.
+ // tmpVariables[5]
+ short signLen =
+ seProvider.hmacSign(
+ scratchPad,
+ (short) 0,
+ keyLen,
+ sharingCheck,
+ (short) 0,
+ (short) sharingCheck.length,
+ scratchPad,
+ keyLen);
+ kmDataStore.setDeviceBootStatus(KMKeymintDataStore.NEGOTIATED_SHARED_SECRET_SUCCESS);
+ // verification signature blob - 32 bytes
+ // tmpVariables[1]
+ short signature = KMByteBlob.instance(scratchPad, keyLen, signLen);
+ // prepare the response
+ short resp = KMArray.instance((short) 2);
+ KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(resp).add((short) 1, signature);
+ sendOutgoing(apdu, resp);
+ }
+
+ private short upgradeKeyCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 2);
+ short keyParams = KMKeyParameters.exp();
+ KMArray.cast(cmd).add((short) 0, KMByteBlob.exp()); // Key Blob
+ KMArray.cast(cmd).add((short) 1, keyParams); // Key Params
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private boolean isKeyUpgradeRequired(
+ short keyBlob, short appId, short appData, byte[] scratchPad) {
+ // Check if the KeyBlob is compatible. If there is any change in the KeyBlob, the version
+ // Parameter in the KeyBlob should be updated to the next version.
+ short version = readKeyBlobVersion(keyBlob);
+ parseEncryptedKeyBlob(keyBlob, appId, appData, scratchPad, version);
+ if (version < KEYBLOB_CURRENT_VERSION) {
+ return true;
+ }
+ short bootPatchLevel = kmDataStore.getBootPatchLevel();
+ // Fill the key-value properties in the scratchpad
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 16, (byte) 0);
+ Util.setShort(scratchPad, (short) 0, KMType.OS_VERSION);
+ Util.setShort(scratchPad, (short) 2, kmDataStore.getOsVersion());
+ Util.setShort(scratchPad, (short) 4, KMType.OS_PATCH_LEVEL);
+ Util.setShort(scratchPad, (short) 6, kmDataStore.getOsPatch());
+ Util.setShort(scratchPad, (short) 8, KMType.VENDOR_PATCH_LEVEL);
+ Util.setShort(scratchPad, (short) 10, kmDataStore.getVendorPatchLevel());
+ Util.setShort(scratchPad, (short) 12, KMType.BOOT_PATCH_LEVEL);
+ Util.setShort(scratchPad, (short) 14, bootPatchLevel);
+ short index = 0;
+ short tag;
+ short systemParam;
+ boolean isKeyUpgradeRequired = false;
+ while (index < 16) {
+ tag = Util.getShort(scratchPad, index);
+ systemParam = Util.getShort(scratchPad, (short) (index + 2));
+ // validate the tag and check if key needs upgrade.
+ short tagValue = KMKeyParameters.findTag(KMType.UINT_TAG, tag, data[HW_PARAMETERS]);
+ tagValue = KMIntegerTag.cast(tagValue).getValue();
+ short zero = KMInteger.uint_8((byte) 0);
+ if (tagValue != KMType.INVALID_VALUE) {
+ // OS version in key characteristics must be less the OS version stored in Javacard or the
+ // stored version must be zero. Then only upgrade is allowed else it is invalid argument.
+ if ((tag == KMType.OS_VERSION
+ && KMInteger.compare(tagValue, systemParam) == 1
+ && KMInteger.compare(systemParam, zero) == 0)) {
+ // Key needs upgrade.
+ isKeyUpgradeRequired = true;
+ } else if ((KMInteger.compare(tagValue, systemParam) == -1)) {
+ // Each os version or patch level associated with the key must be less than it's
+ // corresponding value stored in Javacard, then only upgrade is allowed otherwise it
+ // is invalid argument.
+ isKeyUpgradeRequired = true;
+ } else if (KMInteger.compare(tagValue, systemParam) == 1) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ } else {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ index += 4;
+ }
+ return isKeyUpgradeRequired;
+ }
+
+ private void processUpgradeKeyCmd(APDU apdu) {
+ // Receive the incoming request fully from the host into buffer.
+ short cmd = upgradeKeyCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+
+ short keyBlob = KMArray.cast(cmd).get((short) 0);
+ data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 1);
+ short appId = getApplicationId(data[KEY_PARAMETERS]);
+ short appData = getApplicationData(data[KEY_PARAMETERS]);
+
+ data[KEY_BLOB] = KMType.INVALID_VALUE;
+ // Check if the KeyBlob requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired
+ // function itself, but if there is a difference in the KeyBlob version isKeyUpgradeRequired()
+ // does not parse the KeyBlob.
+ boolean isKeyUpgradeRequired = isKeyUpgradeRequired(keyBlob, appId, appData, scratchPad);
+ if (isKeyUpgradeRequired) {
+ // copy origin
+ data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]);
+ byte keyType = getKeyType(data[HW_PARAMETERS]);
+ switch (keyType) {
+ case ASYM_KEY_TYPE:
+ data[KEY_BLOB] = KMArray.instance(ASYM_KEY_BLOB_SIZE_V2_V3);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]);
+ break;
+ case SYM_KEY_TYPE:
+ data[KEY_BLOB] = KMArray.instance(SYM_KEY_BLOB_SIZE_V2_V3);
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ // Update the system properties to the latest values and also re-create the KeyBlob's
+ // KeyCharacteristics to make sure all the values are up-to-date with the latest applet
+ // changes.
+ upgradeKeyBlobKeyCharacteristics(data[HW_PARAMETERS], scratchPad);
+ // create new key blob with current os version etc.
+ createEncryptedKeyBlob(scratchPad);
+ short prevReclaimIndex = repository.getHeapReclaimIndex();
+ short offset = repository.allocReclaimableMemory(MAX_KEYBLOB_SIZE);
+ data[KEY_BLOB] =
+ encoder.encode(
+ data[KEY_BLOB], repository.getHeap(), offset, prevReclaimIndex, MAX_KEYBLOB_SIZE);
+ data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), offset, data[KEY_BLOB]);
+ repository.reclaimMemory(MAX_KEYBLOB_SIZE);
+ } else {
+ data[KEY_BLOB] = KMByteBlob.instance((short) 0);
+ }
+ // prepare the response
+ short resp = KMArray.instance((short) 2);
+ KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(resp).add((short) 1, data[KEY_BLOB]);
+ sendOutgoing(apdu, resp);
+ }
+
+ private void processExportKeyCmd(APDU apdu) {
+ sendResponse(apdu, KMError.UNIMPLEMENTED);
+ }
+
+ private void processWrappingKeyBlob(short keyBlob, short wrapParams, byte[] scratchPad) {
+ // Read App Id and App Data if any from un wrapping key params
+ data[APP_ID] = getApplicationId(wrapParams);
+ data[APP_DATA] = getApplicationData(wrapParams);
+ data[KEY_PARAMETERS] = wrapParams;
+ data[KEY_BLOB] = keyBlob;
+ // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired
+ // function itself.
+ if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) {
+ KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE);
+ }
+ validateWrappingKeyBlob();
+ }
+
+ private void validateWrappingKeyBlob() {
+ // check whether the wrapping key is RSA with purpose KEY_WRAP, padding RSA_OAEP and Digest
+ // SHA2_256.
+ KMTag.assertPresence(
+ data[SB_PARAMETERS],
+ KMType.ENUM_TAG,
+ KMType.ALGORITHM,
+ KMError.UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM);
+ if (KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]) != KMType.RSA) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM);
+ }
+ if (!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[HW_PARAMETERS])) {
+ KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+ }
+ if (!KMEnumArrayTag.contains(KMType.PADDING, KMType.RSA_OAEP, data[HW_PARAMETERS])) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE);
+ }
+ if (!KMEnumArrayTag.contains(KMType.PURPOSE, KMType.WRAP_KEY, data[HW_PARAMETERS])) {
+ KMException.throwIt((KMError.INCOMPATIBLE_PURPOSE));
+ }
+
+ // Check that the digest and padding mode specified in unwrapping parameters are SHA2_256
+ // and RSA_OAEP respectively.
+ if (!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+ }
+ if (!KMEnumArrayTag.contains(KMType.PADDING, KMType.RSA_OAEP, data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE);
+ }
+ }
+
+ private short decryptTransportKey(
+ short privExp, short modulus, short transportKey, byte[] scratchPad) {
+ short length =
+ seProvider.rsaDecipherOAEP256(
+ KMByteBlob.cast(privExp).getBuffer(),
+ KMByteBlob.cast(privExp).getStartOff(),
+ KMByteBlob.cast(privExp).length(),
+ KMByteBlob.cast(modulus).getBuffer(),
+ KMByteBlob.cast(modulus).getStartOff(),
+ KMByteBlob.cast(modulus).length(),
+ KMByteBlob.cast(transportKey).getBuffer(),
+ KMByteBlob.cast(transportKey).getStartOff(),
+ KMByteBlob.cast(transportKey).length(),
+ scratchPad,
+ (short) 0);
+ return KMByteBlob.instance(scratchPad, (short) 0, length);
+ }
+
+ private void unmask(short data, short maskingKey) {
+ short dataLength = KMByteBlob.cast(data).length();
+ short maskLength = KMByteBlob.cast(maskingKey).length();
+ // Length of masking key and transport key must be same.
+ if (maskLength != dataLength) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ short index = 0; // index
+ // Xor every byte of masking and key and store the result in data[SECRET]
+ while (index < maskLength) {
+ short var1 = (short) (((short) KMByteBlob.cast(maskingKey).get(index)) & 0x00FF);
+ short var2 = (short) (((short) KMByteBlob.cast(data).get(index)) & 0x00FF);
+ KMByteBlob.cast(data).add(index, (byte) (var1 ^ var2));
+ index++;
+ }
+ }
+
+ private short beginImportWrappedKeyCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 4);
+ short params = KMKeyParameters.expAny();
+ KMArray.cast(cmd).add((short) 0, KMByteBlob.exp()); // Encrypted Transport Key
+ KMArray.cast(cmd).add((short) 1, KMByteBlob.exp()); // Wrapping Key KeyBlob
+ KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); // Masking Key
+ params = KMKeyParameters.exp();
+ KMArray.cast(cmd).add((short) 3, params); // Wrapping key blob Params
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processBeginImportWrappedKeyCmd(APDU apdu) {
+ // Receive the incoming request fully from the host into buffer.
+ short cmd = beginImportWrappedKeyCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+ // Step -1 parse the wrapping key blob
+ // read wrapping key blob
+ short keyBlob = KMArray.cast(cmd).get((short) 1);
+ // read un wrapping key params
+ short wrappingKeyParameters = KMArray.cast(cmd).get((short) 3);
+ processWrappingKeyBlob(keyBlob, wrappingKeyParameters, scratchPad);
+ // Step 2 - decrypt the encrypted transport key - 32 bytes AES-GCM key
+ short transportKey =
+ decryptTransportKey(
+ data[SECRET], data[PUB_KEY], KMArray.cast(cmd).get((short) 0), scratchPad);
+ // Step 3 - XOR the decrypted AES-GCM key with with masking key
+ unmask(transportKey, KMArray.cast(cmd).get((short) 2));
+ if (isValidWrappingKey()) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ setWrappingKey(transportKey);
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private short aesGCMEncrypt(
+ short aesSecret, short input, short nonce, short authData, short authTag, byte[] scratchPad) {
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, KMByteBlob.cast(input).length(), (byte) 0);
+ short len =
+ seProvider.aesGCMEncrypt(
+ KMByteBlob.cast(aesSecret).getBuffer(),
+ KMByteBlob.cast(aesSecret).getStartOff(),
+ KMByteBlob.cast(aesSecret).length(),
+ KMByteBlob.cast(input).getBuffer(),
+ KMByteBlob.cast(input).getStartOff(),
+ KMByteBlob.cast(input).length(),
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(nonce).getBuffer(),
+ KMByteBlob.cast(nonce).getStartOff(),
+ KMByteBlob.cast(nonce).length(),
+ KMByteBlob.cast(authData).getBuffer(),
+ KMByteBlob.cast(authData).getStartOff(),
+ KMByteBlob.cast(authData).length(),
+ KMByteBlob.cast(authTag).getBuffer(),
+ KMByteBlob.cast(authTag).getStartOff(),
+ KMByteBlob.cast(authTag).length());
+ return KMByteBlob.instance(scratchPad, (short) 0, len);
+ }
+
+ private short aesGCMDecrypt(
+ short aesSecret, short input, short nonce, short authData, short authTag, byte[] scratchPad) {
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, KMByteBlob.cast(input).length(), (byte) 0);
+ if (!seProvider.aesGCMDecrypt(
+ KMByteBlob.cast(aesSecret).getBuffer(),
+ KMByteBlob.cast(aesSecret).getStartOff(),
+ KMByteBlob.cast(aesSecret).length(),
+ KMByteBlob.cast(input).getBuffer(),
+ KMByteBlob.cast(input).getStartOff(),
+ KMByteBlob.cast(input).length(),
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(nonce).getBuffer(),
+ KMByteBlob.cast(nonce).getStartOff(),
+ KMByteBlob.cast(nonce).length(),
+ KMByteBlob.cast(authData).getBuffer(),
+ KMByteBlob.cast(authData).getStartOff(),
+ KMByteBlob.cast(authData).length(),
+ KMByteBlob.cast(authTag).getBuffer(),
+ KMByteBlob.cast(authTag).getStartOff(),
+ KMByteBlob.cast(authTag).length())) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+ return KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.cast(input).length());
+ }
+
+ private short finishImportWrappedKeyCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 8);
+ short params = KMKeyParameters.expAny();
+ KMArray.cast(cmd).add((short) 0, params); // Key Params of wrapped key
+ KMArray.cast(cmd).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); // Key Format
+ KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); // Wrapped Import Key Blob
+ KMArray.cast(cmd).add((short) 3, KMByteBlob.exp()); // Auth Tag
+ KMArray.cast(cmd).add((short) 4, KMByteBlob.exp()); // IV - Nonce
+ KMArray.cast(cmd).add((short) 5, KMByteBlob.exp()); // Wrapped Key ASSOCIATED AUTH DATA
+ KMArray.cast(cmd).add((short) 6, KMInteger.exp()); // Password Sid
+ KMArray.cast(cmd).add((short) 7, KMInteger.exp()); // Biometric Sid
+ return receiveIncoming(apdu, cmd);
+ }
+
+ // TODO remove cmd later on
+ private void processFinishImportWrappedKeyCmd(APDU apdu) {
+ short cmd = finishImportWrappedKeyCmd(apdu);
+ short keyParameters = KMArray.cast(cmd).get((short) 0);
+ short keyFmt = KMArray.cast(cmd).get((short) 1);
+ keyFmt = KMEnum.cast(keyFmt).getVal();
+ validateImportKey(keyParameters, keyFmt);
+ byte[] scratchPad = apdu.getBuffer();
+ // Step 4 - AES-GCM decrypt the wrapped key
+ data[INPUT_DATA] = KMArray.cast(cmd).get((short) 2);
+ data[AUTH_TAG] = KMArray.cast(cmd).get((short) 3);
+ data[NONCE] = KMArray.cast(cmd).get((short) 4);
+ data[AUTH_DATA] = KMArray.cast(cmd).get((short) 5);
+
+ if (!isValidWrappingKey()) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ data[IMPORTED_KEY_BLOB] =
+ aesGCMDecrypt(
+ getWrappingKey(),
+ data[INPUT_DATA],
+ data[NONCE],
+ data[AUTH_DATA],
+ data[AUTH_TAG],
+ scratchPad);
+ resetWrappingKey();
+ // Step 5 - Import decrypted key
+ data[ORIGIN] = KMType.SECURELY_IMPORTED;
+ data[KEY_PARAMETERS] = keyParameters;
+ // create key blob array
+ importKey(apdu, keyFmt, scratchPad);
+ }
+
+ private KMAttestationCert makeCommonCert(byte[] scratchPad) {
+ short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[KEY_PARAMETERS]);
+ boolean rsaCert = KMEnumTag.cast(alg).getValue() == KMType.RSA;
+ KMAttestationCert cert = KMAttestationCertImpl.instance(rsaCert, seProvider);
+
+ short subject =
+ KMKeyParameters.findTag(
+ KMType.BYTES_TAG, KMType.CERTIFICATE_SUBJECT_NAME, data[KEY_PARAMETERS]);
+
+ // If no subject name is specified then use the default subject name.
+ if (subject == KMType.INVALID_VALUE || KMByteTag.cast(subject).length() == 0) {
+ subject = KMByteBlob.instance(defaultSubject, (short) 0, (short) defaultSubject.length);
+ } else {
+ subject = KMByteTag.cast(subject).getValue();
+ }
+ cert.subjectName(subject);
+ // Validity period must be specified
+ short notBefore =
+ KMKeyParameters.findTag(
+ KMType.DATE_TAG, KMType.CERTIFICATE_NOT_BEFORE, data[KEY_PARAMETERS]);
+ if (notBefore == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.MISSING_NOT_BEFORE);
+ }
+ notBefore = KMIntegerTag.cast(notBefore).getValue();
+ short notAfter =
+ KMKeyParameters.findTag(
+ KMType.DATE_TAG, KMType.CERTIFICATE_NOT_AFTER, data[KEY_PARAMETERS]);
+ if (notAfter == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.MISSING_NOT_AFTER);
+ }
+ notAfter = KMIntegerTag.cast(notAfter).getValue();
+ // VTS sends notBefore == Epoch.
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 8, (byte) 0);
+ short epoch = KMInteger.instance(scratchPad, (short) 0, (short) 8);
+ short end = KMInteger.instance(dec319999Ms, (short) 0, (short) dec319999Ms.length);
+ if (KMInteger.compare(notBefore, epoch) == 0) {
+ cert.notBefore(
+ KMByteBlob.instance(jan01970, (short) 0, (short) jan01970.length), true, scratchPad);
+ } else {
+ cert.notBefore(notBefore, false, scratchPad);
+ }
+ // VTS sends notAfter == Dec 31st 9999
+ if (KMInteger.compare(notAfter, end) == 0) {
+ cert.notAfter(
+ KMByteBlob.instance(dec319999, (short) 0, (short) dec319999.length), true, scratchPad);
+ } else {
+ cert.notAfter(notAfter, false, scratchPad);
+ }
+ // Serial number
+ short serialNum =
+ KMKeyParameters.findTag(
+ KMType.BIGNUM_TAG, KMType.CERTIFICATE_SERIAL_NUM, data[KEY_PARAMETERS]);
+ if (serialNum != KMType.INVALID_VALUE) {
+ serialNum = KMBignumTag.cast(serialNum).getValue();
+ } else {
+ serialNum = KMByteBlob.instance((short) 1);
+ KMByteBlob.cast(serialNum).add((short) 0, (byte) 1);
+ }
+ cert.serialNumber(serialNum);
+ return cert;
+ }
+
+ private KMAttestationCert makeAttestationCert(
+ short attKeyBlob, short attKeyParam, short attChallenge, short issuer, byte[] scratchPad) {
+ KMAttestationCert cert = makeCommonCert(scratchPad);
+
+ // Read App Id and App Data.
+ short appId = getApplicationId(attKeyParam);
+ short appData = getApplicationData(attKeyParam);
+ // Take backup of the required global variables KEY_BLOB, PUB_KEY, SECRET, KEY_CHAR
+ // and HW_PARAMS before they get overridden by isKeyUpgradeRequired() function.
+ short origBlob = data[KEY_BLOB];
+ short pubKey = data[PUB_KEY];
+ short privKey = data[SECRET];
+ short hwParams = data[HW_PARAMETERS];
+ short keyChars = data[KEY_CHARACTERISTICS];
+ short customTags = data[CUSTOM_TAGS];
+ // Check if key requires upgrade for attestKeyBlob. The KeyBlob is parsed inside
+ // isKeyUpgradeRequired function itself.
+ if (isKeyUpgradeRequired(attKeyBlob, appId, appData, scratchPad)) {
+ KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE);
+ }
+ // Get the private key of the attest key.
+ short attestationKeySecret = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_SECRET);
+ // Get the KeyCharacteristics and SB param of the attest key
+ short attestKeyCharacteristics = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_PARAMS);
+ short attestKeySbParams =
+ KMKeyCharacteristics.cast(attestKeyCharacteristics).getStrongboxEnforced();
+ // If the attest key's purpose is not "attest key" then error.
+ short attKeyPurpose =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, attestKeySbParams);
+ if (!KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY)) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+ }
+ KMAsn1Parser asn1Decoder = KMAsn1Parser.instance();
+ try {
+ asn1Decoder.validateDerSubject(issuer);
+ } catch (KMException e) {
+ KMException.throwIt(KMError.INVALID_ISSUER_SUBJECT_NAME);
+ }
+ if (KMByteBlob.cast(issuer).length() > KMConfigurations.MAX_SUBJECT_DER_LEN) {
+ KMException.throwIt(KMError.INVALID_ISSUER_SUBJECT_NAME);
+ }
+ // If issuer is not present then it is an error
+ if (KMByteBlob.cast(issuer).length() <= 0) {
+ KMException.throwIt(KMError.MISSING_ISSUER_SUBJECT_NAME);
+ }
+ short alg = KMEnumTag.getValue(KMType.ALGORITHM, attestKeySbParams);
+ if (alg == KMType.RSA) {
+ short attestationKeyPublic = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_PUB_KEY);
+ cert.rsaAttestKey(attestationKeySecret, attestationKeyPublic, KMType.ATTESTATION_CERT);
+ } else if (alg == KMType.EC) {
+ cert.ecAttestKey(attestationKeySecret, KMType.ATTESTATION_CERT);
+ } else {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ cert.attestationChallenge(attChallenge);
+ cert.issuer(issuer);
+
+ // Restore back the global variables.
+ data[PUB_KEY] = pubKey;
+ data[SECRET] = privKey;
+ data[KEY_BLOB] = origBlob;
+ data[HW_PARAMETERS] = hwParams;
+ data[KEY_CHARACTERISTICS] = keyChars;
+ data[CUSTOM_TAGS] = customTags;
+ data[SW_PARAMETERS] =
+ KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getKeystoreEnforced();
+ data[TEE_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced();
+ data[SB_PARAMETERS] =
+ KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getStrongboxEnforced();
+ cert.publicKey(data[PUB_KEY]);
+
+ // Save attestation application id - must be present.
+ short attAppId =
+ KMKeyParameters.findTag(
+ KMType.BYTES_TAG, KMType.ATTESTATION_APPLICATION_ID, data[KEY_PARAMETERS]);
+ if (attAppId == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.ATTESTATION_APPLICATION_ID_MISSING);
+ }
+ cert.extensionTag(attAppId, false);
+ // unique id byte blob - uses application id and temporal month count of
+ // creation time.
+ attAppId = KMByteTag.cast(attAppId).getValue();
+ setUniqueId(cert, attAppId, scratchPad);
+ // Add Attestation Ids if present
+ addAttestationIds(cert, scratchPad);
+
+ // Add Tags
+ addTags(data[HW_PARAMETERS], true, cert);
+ addTags(data[SW_PARAMETERS], false, cert);
+ // Add Device Boot locked status
+ cert.deviceLocked(kmDataStore.isDeviceBootLocked());
+ // VB data
+ cert.verifiedBootHash(getVerifiedBootHash(scratchPad));
+ cert.verifiedBootKey(getBootKey(scratchPad));
+ cert.verifiedBootState((byte) kmDataStore.getBootState());
+ return cert;
+ }
+
+ private KMAttestationCert makeSelfSignedCert(
+ short attPrivKey, short attPubKey, short mode, byte[] scratchPad) {
+ KMAttestationCert cert = makeCommonCert(scratchPad);
+ short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]);
+ short subject =
+ KMKeyParameters.findTag(
+ KMType.BYTES_TAG, KMType.CERTIFICATE_SUBJECT_NAME, data[KEY_PARAMETERS]);
+ // If no subject name is specified then use the default subject name.
+ if (subject == KMType.INVALID_VALUE || KMByteTag.cast(subject).length() == 0) {
+ subject = KMByteBlob.instance(defaultSubject, (short) 0, (short) defaultSubject.length);
+ } else {
+ subject = KMByteTag.cast(subject).getValue();
+ }
+
+ if (alg == KMType.RSA) {
+ cert.rsaAttestKey(attPrivKey, attPubKey, (byte) mode);
+ } else {
+ cert.ecAttestKey(attPrivKey, (byte) mode);
+ }
+ cert.issuer(subject);
+ cert.subjectName(subject);
+ cert.publicKey(attPubKey);
+ return cert;
+ }
+
+ protected short getBootKey(byte[] scratchPad) {
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, VERIFIED_BOOT_KEY_SIZE, (byte) 0);
+ short len = kmDataStore.getBootKey(scratchPad, (short) 0);
+ if (len != VERIFIED_BOOT_KEY_SIZE) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ return KMByteBlob.instance(scratchPad, (short) 0, VERIFIED_BOOT_KEY_SIZE);
+ }
+
+ protected short getVerifiedBootHash(byte[] scratchPad) {
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, VERIFIED_BOOT_HASH_SIZE, (byte) 0);
+ short len = kmDataStore.getVerifiedBootHash(scratchPad, (short) 0);
+ if (len != VERIFIED_BOOT_HASH_SIZE) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ return KMByteBlob.instance(scratchPad, (short) 0, VERIFIED_BOOT_HASH_SIZE);
+ }
+
+ // --------------------------------
+ // Only add the Attestation ids which are requested in the attestation parameters.
+ // If the requested attestation ids are not provisioned or deleted then
+ // throw CANNOT_ATTEST_IDS error. If there is mismatch in the attestation
+ // id values of both the requested parameters and the provisioned parameters
+ // then throw INVALID_TAG error.
+ private void addAttestationIds(KMAttestationCert cert, byte[] scratchPad) {
+ byte index = 0;
+ short attIdTag;
+ short attIdTagValue;
+ short storedAttIdLen;
+ while (index < (short) attTags.length) {
+ attIdTag = KMKeyParameters.findTag(KMType.BYTES_TAG, attTags[index], data[KEY_PARAMETERS]);
+ if (attIdTag != KMType.INVALID_VALUE) {
+ attIdTagValue = KMByteTag.cast(attIdTag).getValue();
+ storedAttIdLen = kmDataStore.getAttestationId(attTags[index], scratchPad, (short) 0);
+ // Return CANNOT_ATTEST_IDS if Attestation IDs are not provisioned or
+ // Attestation IDs are deleted.
+ if (storedAttIdLen == 0) {
+ // Ignore the SECOND_IMEI tag if the previous Applet's KeyMint version is less than
+ // 3.0 and no SECOND_IMEI is provisioned.
+ if (!(kmDataStore.ignoreSecondImei
+ && attTags[index] == KMType.ATTESTATION_ID_SECOND_IMEI)) {
+ KMException.throwIt(KMError.CANNOT_ATTEST_IDS);
+ }
+ } else {
+ // Return INVALID_TAG if Attestation IDs does not match.
+ if ((storedAttIdLen != KMByteBlob.cast(attIdTagValue).length())
+ || (0
+ != Util.arrayCompare(
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(attIdTagValue).getBuffer(),
+ KMByteBlob.cast(attIdTagValue).getStartOff(),
+ storedAttIdLen))) {
+ KMException.throwIt(KMError.CANNOT_ATTEST_IDS);
+ }
+ short blob = KMByteBlob.instance(scratchPad, (short) 0, storedAttIdLen);
+ cert.extensionTag(KMByteTag.instance(attTags[index], blob), true);
+ }
+ }
+ index++;
+ }
+ }
+
+ private void processDestroyAttIdsCmd(APDU apdu) {
+ kmDataStore.deleteAttestationIds();
+ sendResponse(apdu, KMError.OK);
+ }
+
+ private void processVerifyAuthorizationCmd(APDU apdu) {
+ sendResponse(apdu, KMError.UNIMPLEMENTED);
+ }
+
+ private short abortOperationCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 1);
+ KMArray.cast(cmd).add((short) 0, KMInteger.exp());
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processAbortOperationCmd(APDU apdu) {
+ short cmd = abortOperationCmd(apdu);
+ data[OP_HANDLE] = KMArray.cast(cmd).get((short) 0);
+ KMOperationState op = findOperation(data[OP_HANDLE]);
+ if (op == null) {
+ sendResponse(apdu, KMError.INVALID_OPERATION_HANDLE);
+ } else {
+ releaseOperation(op);
+ sendResponse(apdu, KMError.OK);
+ }
+ }
+
+ private short finishOperationCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 6);
+ KMArray.cast(cmd).add((short) 0, KMInteger.exp()); // op handle
+ KMArray.cast(cmd).add((short) 1, KMByteBlob.exp()); // input data
+ KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); // signature
+ short authToken = KMHardwareAuthToken.exp();
+ KMArray.cast(cmd).add((short) 3, authToken); // auth token
+ short verToken = KMVerificationToken.exp();
+ KMArray.cast(cmd).add((short) 4, verToken); // time stamp token
+ KMArray.cast(cmd).add((short) 5, KMByteBlob.exp()); // confirmation token
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processFinishOperationCmd(APDU apdu) {
+ short cmd = finishOperationCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+ data[OP_HANDLE] = KMArray.cast(cmd).get((short) 0);
+ data[INPUT_DATA] = KMArray.cast(cmd).get((short) 1);
+ data[SIGNATURE] = KMArray.cast(cmd).get((short) 2);
+ data[HW_TOKEN] = KMArray.cast(cmd).get((short) 3);
+ data[VERIFICATION_TOKEN] = KMArray.cast(cmd).get((short) 4);
+ data[CONFIRMATION_TOKEN] = KMArray.cast(cmd).get((short) 5);
+ // Check Operation Handle
+ KMOperationState op = findOperation(data[OP_HANDLE]);
+ if (op == null) {
+ KMException.throwIt(KMError.INVALID_OPERATION_HANDLE);
+ }
+ // Authorize the finish operation
+ authorizeUpdateFinishOperation(op, scratchPad);
+ switch (op.getPurpose()) {
+ case KMType.SIGN:
+ finishTrustedConfirmationOperation(op);
+ case KMType.VERIFY:
+ finishSigningVerifyingOperation(op, scratchPad);
+ break;
+ case KMType.ENCRYPT:
+ finishEncryptOperation(op, scratchPad);
+ break;
+ case KMType.DECRYPT:
+ finishDecryptOperation(op, scratchPad);
+ break;
+ case KMType.AGREE_KEY:
+ finishKeyAgreementOperation(op, scratchPad);
+ break;
+ }
+ if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) {
+ data[OUTPUT_DATA] = KMByteBlob.instance((short) 0);
+ }
+ // Remove the operation handle
+ releaseOperation(op);
+
+ // make response
+ short resp = KMArray.instance((short) 2);
+ KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(resp).add((short) 1, data[OUTPUT_DATA]);
+ sendOutgoing(apdu, resp);
+ }
+
+ private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) {
+ if (op.getAlgorithm() != KMType.AES && op.getAlgorithm() != KMType.DES) {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ finishAesDesOperation(op);
+ }
+
+ private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) {
+ short len = KMByteBlob.cast(data[INPUT_DATA]).length();
+ switch (op.getAlgorithm()) {
+ case KMType.RSA:
+ // Fill the scratch pad with zero
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+ if (op.getPadding() == KMType.PADDING_NONE && len != 256) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ len =
+ op.getOperation()
+ .finish(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ len,
+ scratchPad,
+ (short) 0);
+
+ data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short) 0, len);
+ break;
+ case KMType.AES:
+ case KMType.DES:
+ finishAesDesOperation(op);
+ break;
+ }
+ }
+
+ private void finishAesDesOperation(KMOperationState op) {
+ short len = KMByteBlob.cast(data[INPUT_DATA]).length();
+ short blockSize = AES_BLOCK_SIZE;
+ if (op.getAlgorithm() == KMType.DES) {
+ blockSize = DES_BLOCK_SIZE;
+ }
+
+ if (op.getPurpose() == KMType.DECRYPT
+ && len > 0
+ && (op.getBlockMode() == KMType.ECB || op.getBlockMode() == KMType.CBC)
+ && ((short) (len % blockSize) != 0)) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+
+ if (op.getBlockMode() == KMType.GCM) {
+ if (op.getPurpose() == KMType.DECRYPT && (len < (short) (op.getMacLength() / 8))) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ if (op.isAesGcmUpdateAllowed()) {
+ op.setAesGcmUpdateComplete();
+ }
+ // Get the output size
+ len = op.getOperation().getAESGCMOutputSize(len, (short) (op.getMacLength() / 8));
+ }
+ // If padding i.e. pkcs7 then add padding to right
+ // Output data can at most one block size more the input data in case of pkcs7 encryption
+ // In case of gcm we will allocate extra memory of the size equal to blocksize.
+ data[OUTPUT_DATA] = KMByteBlob.instance((short) (len + 2 * blockSize));
+ try {
+ len =
+ op.getOperation()
+ .finish(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ KMByteBlob.cast(data[INPUT_DATA]).length(),
+ KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff());
+ } catch (CryptoException e) {
+ if (e.getReason() == CryptoException.ILLEGAL_USE) {
+ // As per VTS, zero length input on AES/DES with PADDING_NONE Should return a zero length
+ // output. But JavaCard fails with CryptoException.ILLEGAL_USE if no input data is
+ // provided via update() method. So ignore this exception in case if all below conditions
+ // are satisfied and simply return empty output.
+ // 1. padding mode is PADDING_NONE.
+ // 2. No input message is processed in update().
+ // 3. Zero length input data is passed in finish operation.
+ if ((op.getPadding() == KMType.PADDING_NONE)
+ && !op.isInputMsgProcessed()
+ && (KMByteBlob.cast(data[INPUT_DATA]).length() == 0)) {
+ len = 0;
+ } else {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ }
+ }
+ KMByteBlob.cast(data[OUTPUT_DATA]).setLength(len);
+ }
+
+ private void finishKeyAgreementOperation(KMOperationState op, byte[] scratchPad) {
+ try {
+ KMAsn1Parser pkcs8 = KMAsn1Parser.instance();
+ short blob = pkcs8.decodeEcSubjectPublicKeyInfo(data[INPUT_DATA]);
+ short len =
+ op.getOperation()
+ .finish(
+ KMByteBlob.cast(blob).getBuffer(),
+ KMByteBlob.cast(blob).getStartOff(),
+ KMByteBlob.cast(blob).length(),
+ scratchPad,
+ (short) 0);
+ data[OUTPUT_DATA] = KMByteBlob.instance((short) 32);
+ Util.arrayCopyNonAtomic(
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff(),
+ len);
+ } catch (CryptoException e) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ }
+
+ private void finishSigningVerifyingOperation(KMOperationState op, byte[] scratchPad) {
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+ switch (op.getAlgorithm()) {
+ case KMType.RSA:
+ // If there is no padding we can treat signing as a RSA decryption operation.
+ try {
+ if (op.getPurpose() == KMType.SIGN) {
+ // len of signature will be 256 bytes - but it can be less then 256 bytes
+ short len =
+ op.getOperation()
+ .sign(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ KMByteBlob.cast(data[INPUT_DATA]).length(),
+ scratchPad,
+ (short) 0);
+ // Maximum output size of signature is 256 bytes. - the signature will always be
+ // positive
+ data[OUTPUT_DATA] = KMByteBlob.instance((short) 256);
+ Util.arrayCopyNonAtomic(
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(),
+ (short) (KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff() + 256 - len),
+ len);
+ } else {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ } catch (CryptoException e) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ break;
+ case KMType.EC:
+ short len = KMByteBlob.cast(data[INPUT_DATA]).length();
+ // If DIGEST NONE then truncate the input data to 32 bytes.
+ if (op.getDigest() == KMType.DIGEST_NONE && len > 32) {
+ len = 32;
+ }
+ if (op.getPurpose() == KMType.SIGN) {
+ // len of signature will be 512 bits i.e. 64 bytes
+ len =
+ op.getOperation()
+ .sign(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ len,
+ scratchPad,
+ (short) 0);
+ data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short) 0, len);
+ } else {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ break;
+ case KMType.HMAC:
+ // As per Keymaster HAL documentation, the length of the Hmac output can
+ // be decided by using TAG_MAC_LENGTH in Keyparameters. But there is no
+ // such provision to control the length of the Hmac output using JavaCard
+ // crypto APIs and the current implementation always returns 32 bytes
+ // length of Hmac output. So to provide support to TAG_MAC_LENGTH
+ // feature, we truncate the output signature to TAG_MAC_LENGTH and return
+ // the truncated signature back to the caller. At the time of verfication
+ // we again compute the signature of the plain text input, truncate it to
+ // TAG_MAC_LENGTH and compare it with the input signature for
+ // verification.
+ op.getOperation()
+ .sign(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ KMByteBlob.cast(data[INPUT_DATA]).length(),
+ scratchPad,
+ (short) 0);
+ if (op.getPurpose() == KMType.SIGN) {
+ // Copy only signature of mac length size.
+ data[OUTPUT_DATA] =
+ KMByteBlob.instance(scratchPad, (short) 0, (short) (op.getMacLength() / 8));
+ } else if (op.getPurpose() == KMType.VERIFY) {
+ if ((KMByteBlob.cast(data[SIGNATURE]).length() < (MIN_HMAC_LENGTH_BITS / 8))
+ || KMByteBlob.cast(data[SIGNATURE]).length() > (SHA256_DIGEST_LEN_BITS / 8)) {
+ KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH);
+ }
+ if ((KMByteBlob.cast(data[SIGNATURE]).length() < (short) (op.getMinMacLength() / 8))) {
+ KMException.throwIt(KMError.INVALID_MAC_LENGTH);
+ }
+
+ if (0
+ != Util.arrayCompare(
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(data[SIGNATURE]).getBuffer(),
+ KMByteBlob.cast(data[SIGNATURE]).getStartOff(),
+ KMByteBlob.cast(data[SIGNATURE]).length())) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+ data[OUTPUT_DATA] = KMByteBlob.instance((short) 0);
+ }
+ break;
+ default: // This is should never happen
+ KMException.throwIt(KMError.OPERATION_CANCELLED);
+ break;
+ }
+ }
+
+ private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchPad) {
+ // If one time user Authentication is required
+ if (op.isSecureUserIdReqd() && !op.isAuthTimeoutValidated()) {
+ // Validate Verification Token.
+ validateVerificationToken(data[VERIFICATION_TOKEN], scratchPad);
+ // validate operation handle.
+ short ptr = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getChallenge();
+ if (KMInteger.compare(ptr, op.getHandle()) != 0) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+ tmpVariables[0] = op.getAuthTime();
+ tmpVariables[2] = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp();
+ if (tmpVariables[2] == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+ if (KMInteger.compare(tmpVariables[0], tmpVariables[2]) < 0) {
+ KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+ }
+ op.setAuthTimeoutValidated(true);
+ } else if (op.isAuthPerOperationReqd()) { // If Auth per operation is required
+ if (!validateHwToken(data[HW_TOKEN], scratchPad)) {
+ KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+ }
+ tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getChallenge();
+ if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) {
+ KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+ }
+ if (!authTokenMatches(op.getUserSecureId(), op.getAuthType(), scratchPad)) {
+ KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+ }
+ }
+ }
+
+ private void authorizeKeyUsageForCount(byte[] scratchPad) {
+ short scratchPadOff = 0;
+ Util.arrayFillNonAtomic(scratchPad, scratchPadOff, (short) 12, (byte) 0);
+
+ short usageLimitBufLen =
+ KMIntegerTag.getValue(
+ scratchPad,
+ scratchPadOff,
+ KMType.UINT_TAG,
+ KMType.MAX_USES_PER_BOOT,
+ data[HW_PARAMETERS]);
+
+ if (usageLimitBufLen == KMType.INVALID_VALUE) {
+ return;
+ }
+
+ if (usageLimitBufLen > 4) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+
+ if (kmDataStore.isAuthTagPersisted(data[AUTH_TAG])) {
+ // Get current counter, update and increment it.
+ short len =
+ kmDataStore.getRateLimitedKeyCount(
+ data[AUTH_TAG], scratchPad, (short) (scratchPadOff + 4));
+ if (len != 4) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ if (0
+ >= KMInteger.unsignedByteArrayCompare(
+ scratchPad, scratchPadOff, scratchPad, (short) (scratchPadOff + 4), (short) 4)) {
+ KMException.throwIt(KMError.KEY_MAX_OPS_EXCEEDED);
+ }
+ // Increment the counter.
+ Util.arrayFillNonAtomic(scratchPad, scratchPadOff, len, (byte) 0);
+ Util.setShort(scratchPad, (short) (scratchPadOff + 2), (short) 1);
+ KMUtils.add(
+ scratchPad,
+ scratchPadOff,
+ (short) (scratchPadOff + len),
+ (short) (scratchPadOff + len * 2));
+
+ kmDataStore.setRateLimitedKeyCount(
+ data[AUTH_TAG], scratchPad, (short) (scratchPadOff + len * 2), len);
+ } else {
+ // Persist auth tag.
+ if (!kmDataStore.persistAuthTag(data[AUTH_TAG])) {
+ KMException.throwIt(KMError.TOO_MANY_OPERATIONS);
+ }
+ }
+ }
+
+ private void authorizeDeviceUnlock(byte[] scratchPad) {
+ // If device is locked and key characteristics requires unlocked device then check whether
+ // HW auth token has correct timestamp.
+ short ptr =
+ KMKeyParameters.findTag(
+ KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, data[HW_PARAMETERS]);
+
+ if (ptr != KMType.INVALID_VALUE && kmDataStore.getDeviceLock()) {
+ if (data[HW_TOKEN] == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.DEVICE_LOCKED);
+ }
+ ptr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp();
+ // Check if the current auth time stamp is greater than device locked time stamp
+ short ts = kmDataStore.getDeviceTimeStamp();
+ if (KMInteger.compare(ptr, ts) <= 0) {
+ KMException.throwIt(KMError.DEVICE_LOCKED);
+ }
+ // Now check if the device unlock requires password only authentication and whether
+ // auth token is generated through password authentication or not.
+ if (kmDataStore.getDeviceLockPasswordOnly()) {
+ ptr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType();
+ ptr = KMEnum.cast(ptr).getVal();
+ if (((byte) ptr & KMType.PASSWORD) == 0) {
+ KMException.throwIt(KMError.DEVICE_LOCKED);
+ }
+ }
+ // Unlock the device
+ // repository.deviceLockedFlag = false;
+ kmDataStore.setDeviceLock(false);
+ kmDataStore.clearDeviceLockTimeStamp();
+ }
+ }
+
+ private boolean verifyVerificationTokenMacInBigEndian(short verToken, byte[] scratchPad) {
+ // concatenation length will be 37 + length of verified parameters list - which
+ // is typically empty
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+ // Add "Auth Verification" - 17 bytes.
+ Util.arrayCopyNonAtomic(
+ authVerification, (short) 0, scratchPad, (short) 0, (short) authVerification.length);
+ short len = (short) authVerification.length;
+ // concatenate challenge - 8 bytes
+ short ptr = KMVerificationToken.cast(verToken).getChallenge();
+ KMInteger.cast(ptr)
+ .value(
+ scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+ len += KMInteger.UINT_64;
+ // concatenate timestamp -8 bytes
+ ptr = KMVerificationToken.cast(verToken).getTimestamp();
+ KMInteger.cast(ptr)
+ .value(
+ scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+ len += KMInteger.UINT_64;
+ // concatenate security level - 4 bytes
+ scratchPad[(short) (len + 3)] = TRUSTED_ENVIRONMENT;
+ len += KMInteger.UINT_32;
+ // hmac the data
+ ptr = KMVerificationToken.cast(verToken).getMac();
+
+ return seProvider.hmacVerify(
+ kmDataStore.getComputedHmacKey(),
+ scratchPad,
+ (short) 0,
+ len,
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length());
+ }
+
+ private void validateVerificationToken(short verToken, byte[] scratchPad) {
+ short ptr = KMVerificationToken.cast(verToken).getMac();
+ // If mac length is zero then token is empty.
+ if (KMByteBlob.cast(ptr).length() == 0) {
+ KMException.throwIt(KMError.INVALID_MAC_LENGTH);
+ }
+ if (!verifyVerificationTokenMacInBigEndian(verToken, scratchPad)) {
+ // Throw Exception if none of the combination works.
+ KMException.throwIt(KMError.VERIFICATION_FAILED);
+ }
+ }
+
+ private short updateOperationCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 4);
+ // Arguments
+ KMArray.cast(cmd).add((short) 0, KMInteger.exp());
+ KMArray.cast(cmd).add((short) 1, KMByteBlob.exp());
+ short authToken = KMHardwareAuthToken.exp();
+ KMArray.cast(cmd).add((short) 2, authToken);
+ short verToken = KMVerificationToken.exp();
+ KMArray.cast(cmd).add((short) 3, verToken);
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processUpdateOperationCmd(APDU apdu) {
+ short cmd = updateOperationCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+ data[OP_HANDLE] = KMArray.cast(cmd).get((short) 0);
+ data[INPUT_DATA] = KMArray.cast(cmd).get((short) 1);
+ data[HW_TOKEN] = KMArray.cast(cmd).get((short) 2);
+ data[VERIFICATION_TOKEN] = KMArray.cast(cmd).get((short) 3);
+
+ // Input data must be present even if it is zero length.
+ if (data[INPUT_DATA] == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+
+ // Check Operation Handle and get op state
+ // Check Operation Handle
+ KMOperationState op = findOperation(data[OP_HANDLE]);
+ if (op == null) {
+ KMException.throwIt(KMError.INVALID_OPERATION_HANDLE);
+ }
+ // authorize the update operation
+ authorizeUpdateFinishOperation(op, scratchPad);
+
+ if (op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY) {
+ // update the data.
+ op.getOperation()
+ .update(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ KMByteBlob.cast(data[INPUT_DATA]).length());
+ // update trusted confirmation operation
+ updateTrustedConfirmationOperation(op);
+
+ data[OUTPUT_DATA] = KMType.INVALID_VALUE;
+ } else if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) {
+ // Update for encrypt/decrypt using RSA will not be supported because to do this op state
+ // will have to buffer the data - so reject the update if it is rsa algorithm.
+ if (op.getAlgorithm() == KMType.RSA) {
+ KMException.throwIt(KMError.OPERATION_CANCELLED);
+ }
+ short len = KMByteBlob.cast(data[INPUT_DATA]).length();
+ short blockSize = DES_BLOCK_SIZE;
+ if (op.getAlgorithm() == KMType.AES) {
+ blockSize = AES_BLOCK_SIZE;
+ if (op.getBlockMode() == KMType.GCM) {
+ // if input data present
+ if (len > 0) {
+ // no more future updateAAD allowed if input data present.
+ if (op.isAesGcmUpdateAllowed()) {
+ op.setAesGcmUpdateComplete();
+ }
+ }
+ }
+ }
+ // Allocate output buffer as input data is already block aligned
+ data[OUTPUT_DATA] = KMByteBlob.instance((short) (len + 2 * blockSize));
+ // Otherwise just update the data.
+ // HAL consumes all the input and maintains a buffered data inside it. So the
+ // applet sends the inputConsumed length as same as the input length.
+ try {
+ len =
+ op.getOperation()
+ .update(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ KMByteBlob.cast(data[INPUT_DATA]).length(),
+ KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff());
+ } catch (CryptoException e) {
+ KMException.throwIt(KMError.INVALID_TAG);
+ }
+ if (KMByteBlob.cast(data[INPUT_DATA]).length() > 0) {
+ // This flag is used to denote that an input data of length > 0 is received and processed
+ // successfully in update command. This flag is later used in the finish operation
+ // to handle a particular use case, where a zero length input data on AES/DES algorithm
+ // with PADDING_NONE should return a zero length output with OK response.
+ op.setProcessedInputMsg(true);
+ }
+ // Adjust the Output data if it is not equal to input data.
+ // This happens in case of JCardSim provider.
+ KMByteBlob.cast(data[OUTPUT_DATA]).setLength(len);
+ }
+
+ if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) {
+ data[OUTPUT_DATA] = KMByteBlob.instance((short) 0);
+ }
+ // Persist if there are any updates.
+ // op.persist();
+ // make response
+ short resp = KMArray.instance((short) 2);
+ KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(resp).add((short) 1, data[OUTPUT_DATA]);
+ sendOutgoing(apdu, resp);
+ }
+
+ private short updateAadOperationCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 4);
+ KMArray.cast(cmd).add((short) 0, KMInteger.exp());
+ KMArray.cast(cmd).add((short) 1, KMByteBlob.exp());
+ short authToken = KMHardwareAuthToken.exp();
+ KMArray.cast(cmd).add((short) 2, authToken);
+ short verToken = KMVerificationToken.exp();
+ KMArray.cast(cmd).add((short) 3, verToken);
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processUpdateAadOperationCmd(APDU apdu) {
+ short cmd = updateAadOperationCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+ data[OP_HANDLE] = KMArray.cast(cmd).get((short) 0);
+ data[INPUT_DATA] = KMArray.cast(cmd).get((short) 1);
+ data[HW_TOKEN] = KMArray.cast(cmd).get((short) 2);
+ data[VERIFICATION_TOKEN] = KMArray.cast(cmd).get((short) 3);
+
+ // Input data must be present even if it is zero length.
+ if (data[INPUT_DATA] == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ // Check Operation Handle and get op state
+ // Check Operation Handle
+ KMOperationState op = findOperation(data[OP_HANDLE]);
+ if (op == null) {
+ KMException.throwIt(KMError.INVALID_OPERATION_HANDLE);
+ }
+ if (op.getAlgorithm() != KMType.AES) {
+ KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM);
+ }
+ if (op.getBlockMode() != KMType.GCM) {
+ KMException.throwIt(KMError.INCOMPATIBLE_BLOCK_MODE);
+ }
+ if (!op.isAesGcmUpdateAllowed()) {
+ KMException.throwIt(KMError.INVALID_TAG);
+ }
+ if (op.getPurpose() != KMType.ENCRYPT && op.getPurpose() != KMType.DECRYPT) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+ }
+ // authorize the update operation
+ authorizeUpdateFinishOperation(op, scratchPad);
+ try {
+ op.getOperation()
+ .updateAAD(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ KMByteBlob.cast(data[INPUT_DATA]).length());
+ } catch (CryptoException exp) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ // make response
+ short resp = KMArray.instance((short) 1);
+ KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+ sendOutgoing(apdu, resp);
+ }
+
+ private short beginOperationCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 4);
+ // Arguments
+ short params = KMKeyParameters.expAny();
+ KMArray.cast(cmd).add((short) 0, KMEnum.instance(KMType.PURPOSE));
+ KMArray.cast(cmd).add((short) 1, KMByteBlob.exp());
+ KMArray.cast(cmd).add((short) 2, params);
+ short authToken = KMHardwareAuthToken.exp();
+ KMArray.cast(cmd).add((short) 3, authToken);
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processBeginOperationCmd(APDU apdu) {
+ // Receive the incoming request fully from the host into buffer.
+ short cmd = beginOperationCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+ short purpose = KMArray.cast(cmd).get((short) 0);
+ data[KEY_BLOB] = KMArray.cast(cmd).get((short) 1);
+ data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 2);
+ data[HW_TOKEN] = KMArray.cast(cmd).get((short) 3);
+ purpose = KMEnum.cast(purpose).getVal();
+ // Check for app id and app data.
+ data[APP_ID] = getApplicationId(data[KEY_PARAMETERS]);
+ data[APP_DATA] = getApplicationData(data[KEY_PARAMETERS]);
+ // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired
+ // function itself.
+ if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) {
+ KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE);
+ }
+ KMTag.assertPresence(
+ data[SB_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, KMError.UNSUPPORTED_ALGORITHM);
+ short algorithm = KMEnumTag.getValue(KMType.ALGORITHM, data[SB_PARAMETERS]);
+ // If Blob usage tag is present in key characteristics then it should be standalone.
+ if (KMTag.isPresent(data[SB_PARAMETERS], KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ)) {
+ if (KMEnumTag.getValue(KMType.BLOB_USAGE_REQ, data[SB_PARAMETERS]) != KMType.STANDALONE) {
+ KMException.throwIt(KMError.UNSUPPORTED_TAG);
+ }
+ }
+
+ // Generate a random number for operation handle
+ short buf = KMByteBlob.instance(KMOperationState.OPERATION_HANDLE_SIZE);
+ generateUniqueOperationHandle(
+ KMByteBlob.cast(buf).getBuffer(),
+ KMByteBlob.cast(buf).getStartOff(),
+ KMByteBlob.cast(buf).length());
+ /* opHandle is a KMInteger and is encoded as KMInteger when it is returned back. */
+ short opHandle =
+ KMInteger.instance(
+ KMByteBlob.cast(buf).getBuffer(),
+ KMByteBlob.cast(buf).getStartOff(),
+ KMByteBlob.cast(buf).length());
+ KMOperationState op = reserveOperation(algorithm, opHandle);
+ if (op == null) {
+ KMException.throwIt(KMError.TOO_MANY_OPERATIONS);
+ }
+ data[OP_HANDLE] = op.getHandle();
+ op.setPurpose((byte) purpose);
+ op.setKeySize(KMByteBlob.cast(data[SECRET]).length());
+ authorizeAndBeginOperation(op, scratchPad);
+ switch (op.getPurpose()) {
+ case KMType.SIGN:
+ beginTrustedConfirmationOperation(op);
+ case KMType.VERIFY:
+ beginSignVerifyOperation(op);
+ break;
+ case KMType.ENCRYPT:
+ case KMType.DECRYPT:
+ beginCipherOperation(op);
+ break;
+ case KMType.AGREE_KEY:
+ beginKeyAgreementOperation(op);
+ break;
+ default:
+ KMException.throwIt(KMError.UNIMPLEMENTED);
+ break;
+ }
+ short iv = KMType.INVALID_VALUE;
+ // If the data[IV] is required to be returned.
+ // As per VTS, for the decryption operation don't send the iv back.
+ if (data[IV] != KMType.INVALID_VALUE
+ && op.getPurpose() != KMType.DECRYPT
+ && op.getBlockMode() != KMType.ECB) {
+ iv = KMArray.instance((short) 1);
+ if (op.getAlgorithm() == KMType.DES && op.getBlockMode() == KMType.CBC) {
+ // For AES/DES we are generate an random iv of length 16 bytes.
+ // While sending the iv back for DES/CBC mode of opeation only send
+ // 8 bytes back.
+ short ivBlob = KMByteBlob.instance((short) 8);
+ Util.arrayCopy(
+ KMByteBlob.cast(data[IV]).getBuffer(),
+ KMByteBlob.cast(data[IV]).getStartOff(),
+ KMByteBlob.cast(ivBlob).getBuffer(),
+ KMByteBlob.cast(ivBlob).getStartOff(),
+ (short) 8);
+ data[IV] = ivBlob;
+ }
+ KMArray.cast(iv).add((short) 0, KMByteTag.instance(KMType.NONCE, data[IV]));
+ } else {
+ iv = KMArray.instance((short) 0);
+ }
+ short macLen = 0;
+ if (op.getMacLength() != KMType.INVALID_VALUE) {
+ macLen = (short) (op.getMacLength() / 8);
+ }
+ short params = KMKeyParameters.instance(iv);
+ short resp = KMArray.instance((short) 5);
+ KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(resp).add((short) 1, params);
+ KMArray.cast(resp).add((short) 2, data[OP_HANDLE]);
+ KMArray.cast(resp).add((short) 3, KMInteger.uint_8(op.getBufferingMode()));
+ KMArray.cast(resp).add((short) 4, KMInteger.uint_16(macLen));
+ sendOutgoing(apdu, resp);
+ }
+
+ private void authorizePurpose(KMOperationState op) {
+ switch (op.getAlgorithm()) {
+ case KMType.AES:
+ case KMType.DES:
+ if (op.getPurpose() == KMType.SIGN
+ || op.getPurpose() == KMType.VERIFY
+ || op.getPurpose() == KMType.AGREE_KEY) {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ break;
+ case KMType.EC:
+ if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ break;
+ case KMType.HMAC:
+ if (op.getPurpose() == KMType.ENCRYPT
+ || op.getPurpose() == KMType.DECRYPT
+ || op.getPurpose() == KMType.AGREE_KEY) {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ break;
+ case KMType.RSA:
+ if (op.getPurpose() == KMType.AGREE_KEY) {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ break;
+ default:
+ break;
+ }
+ if (!KMEnumArrayTag.contains(KMType.PURPOSE, op.getPurpose(), data[HW_PARAMETERS])) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+ }
+ }
+
+ private void authorizeDigest(KMOperationState op) {
+ short digests =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[HW_PARAMETERS]);
+ op.setDigest(KMType.DIGEST_NONE);
+ short param =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]);
+ if (param != KMType.INVALID_VALUE) {
+ if (KMEnumArrayTag.cast(param).length() != 1) {
+ KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+ }
+ param = KMEnumArrayTag.cast(param).get((short) 0);
+ if (!KMEnumArrayTag.cast(digests).contains(param)) {
+ KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+ }
+ op.setDigest((byte) param);
+ } else if (KMEnumArrayTag.contains(
+ KMType.PADDING, KMType.RSA_PKCS1_1_5_SIGN, data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+ }
+ short paramPadding =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[KEY_PARAMETERS]);
+ if (paramPadding != KMType.INVALID_VALUE) {
+ if (KMEnumArrayTag.cast(paramPadding).length() != 1) {
+ // TODO vts fails because it expects UNSUPPORTED_PADDING_MODE
+ KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+ }
+ paramPadding = KMEnumArrayTag.cast(paramPadding).get((short) 0);
+ }
+ switch (op.getAlgorithm()) {
+ case KMType.RSA:
+ if ((paramPadding == KMType.RSA_OAEP || paramPadding == KMType.RSA_PSS)
+ && param == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+ }
+ break;
+ case KMType.EC:
+ case KMType.HMAC:
+ if ((param == KMType.INVALID_VALUE && op.getPurpose() != KMType.AGREE_KEY)
+ || !isDigestSupported(op.getAlgorithm(), op.getDigest())) {
+ KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void authorizePadding(KMOperationState op) {
+ short paddings =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[HW_PARAMETERS]);
+ op.setPadding(KMType.PADDING_NONE);
+ short param =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[KEY_PARAMETERS]);
+ if (param != KMType.INVALID_VALUE) {
+ if (KMEnumArrayTag.cast(param).length() != 1) {
+ KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+ }
+ param = KMEnumArrayTag.cast(param).get((short) 0);
+ if (!KMEnumArrayTag.cast(paddings).contains(param)) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE);
+ }
+ }
+ switch (op.getAlgorithm()) {
+ case KMType.RSA:
+ if (param == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+ }
+ if ((op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY)
+ && param != KMType.PADDING_NONE
+ && param != KMType.RSA_PSS
+ && param != KMType.RSA_PKCS1_1_5_SIGN) {
+ KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+ }
+ if ((op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT)
+ && param != KMType.PADDING_NONE
+ && param != KMType.RSA_OAEP
+ && param != KMType.RSA_PKCS1_1_5_ENCRYPT) {
+ KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+ }
+
+ if (param == KMType.PADDING_NONE && op.getDigest() != KMType.DIGEST_NONE) {
+ KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+ }
+ if ((param == KMType.RSA_OAEP || param == KMType.RSA_PSS)
+ && op.getDigest() == KMType.DIGEST_NONE) {
+ KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+ }
+ if (op.getPurpose() == KMType.SIGN
+ || op.getPurpose() == KMType.VERIFY
+ || param == KMType.RSA_OAEP) {
+ // Digest is mandatory in these cases.
+ if (!isDigestSupported(op.getAlgorithm(), op.getDigest())) {
+ KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+ }
+ }
+ if (param == KMType.RSA_OAEP) {
+ short mgfDigest =
+ KMKeyParameters.findTag(
+ KMType.ENUM_ARRAY_TAG, KMType.RSA_OAEP_MGF_DIGEST, data[KEY_PARAMETERS]);
+ if (mgfDigest != KMType.INVALID_VALUE) {
+ if (KMEnumArrayTag.cast(mgfDigest).length() != 1) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ mgfDigest = KMEnumArrayTag.cast(mgfDigest).get((short) 0);
+ if (mgfDigest == KMType.DIGEST_NONE) {
+ KMException.throwIt(KMError.UNSUPPORTED_MGF_DIGEST);
+ }
+
+ } else {
+ mgfDigest = KMType.SHA1;
+ }
+ short mgfDigestHwParams =
+ KMKeyParameters.findTag(
+ KMType.ENUM_ARRAY_TAG, KMType.RSA_OAEP_MGF_DIGEST, data[HW_PARAMETERS]);
+ if ((mgfDigestHwParams != KMType.INVALID_VALUE)
+ && (!KMEnumArrayTag.cast(mgfDigestHwParams).contains(mgfDigest))) {
+ KMException.throwIt(KMError.INCOMPATIBLE_MGF_DIGEST);
+ }
+ if (mgfDigest != KMType.SHA1 && mgfDigest != KMType.SHA2_256) {
+ KMException.throwIt(KMError.UNSUPPORTED_MGF_DIGEST);
+ }
+ op.setMgfDigest((byte) mgfDigest);
+ }
+ op.setPadding((byte) param);
+ break;
+ case KMType.DES:
+ case KMType.AES:
+ if (param == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+ }
+ op.setPadding((byte) param);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void authorizeBlockModeAndMacLength(KMOperationState op) {
+ short param =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]);
+ if (param != KMType.INVALID_VALUE) {
+ if (KMEnumArrayTag.cast(param).length() != 1) {
+ KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE);
+ }
+ param = KMEnumArrayTag.cast(param).get((short) 0);
+ }
+ if (KMType.AES == op.getAlgorithm() || KMType.DES == op.getAlgorithm()) {
+ if (!KMEnumArrayTag.contains(KMType.BLOCK_MODE, param, data[HW_PARAMETERS])) {
+ KMException.throwIt(KMError.INCOMPATIBLE_BLOCK_MODE);
+ }
+ }
+ short macLen =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAC_LENGTH, data[KEY_PARAMETERS]);
+ switch (op.getAlgorithm()) {
+ case KMType.AES:
+ // Validate the block mode.
+ switch (param) {
+ case KMType.ECB:
+ case KMType.CBC:
+ case KMType.CTR:
+ case KMType.GCM:
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE);
+ }
+ if (param == KMType.GCM) {
+ if (op.getPadding() != KMType.PADDING_NONE || op.getPadding() == KMType.PKCS7) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE);
+ }
+ if (macLen == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.MISSING_MAC_LENGTH);
+ }
+ short minMacLen =
+ KMIntegerTag.getShortValue(
+ KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS]);
+ if (minMacLen == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ if (macLen % 8 != 0
+ || macLen > MAX_GCM_TAG_LENGTH_BITS
+ || macLen < MIN_GCM_TAG_LENGTH_BITS) {
+ KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH);
+ }
+ if (macLen < minMacLen) {
+ KMException.throwIt(KMError.INVALID_MAC_LENGTH);
+ }
+ op.setMacLength(macLen);
+ }
+ if (param == KMType.CTR) {
+ if (op.getPadding() != KMType.PADDING_NONE || op.getPadding() == KMType.PKCS7) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE);
+ }
+ }
+ break;
+ case KMType.DES:
+ // Validate the block mode.
+ switch (param) {
+ case KMType.ECB:
+ case KMType.CBC:
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE);
+ }
+ if (param == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ break;
+ case KMType.HMAC:
+ short minMacLen =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS]);
+ if (minMacLen == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ op.setMinMacLength(minMacLen);
+ if (macLen == KMType.INVALID_VALUE) {
+ if (op.getPurpose() == KMType.SIGN) {
+ KMException.throwIt(KMError.MISSING_MAC_LENGTH);
+ }
+ } else {
+ // MAC length may not be specified for verify.
+ if (op.getPurpose() == KMType.VERIFY) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ if (macLen % 8 != 0 || macLen > SHA256_DIGEST_LEN_BITS || macLen < MIN_HMAC_LENGTH_BITS) {
+ KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH);
+ }
+ if (macLen < minMacLen) {
+ KMException.throwIt(KMError.INVALID_MAC_LENGTH);
+ }
+ op.setMacLength(macLen);
+ }
+ break;
+ default:
+ break;
+ }
+ op.setBlockMode((byte) param);
+ }
+
+ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) {
+ authorizePurpose(op);
+ authorizeDigest(op);
+ authorizePadding(op);
+ authorizeBlockModeAndMacLength(op);
+ if (!validateHwToken(data[HW_TOKEN], scratchPad)) {
+ data[HW_TOKEN] = KMType.INVALID_VALUE;
+ }
+ authorizeUserSecureIdAuthTimeout(op, scratchPad);
+ authorizeDeviceUnlock(scratchPad);
+ authorizeKeyUsageForCount(scratchPad);
+
+ KMTag.assertAbsence(
+ data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, KMError.INVALID_KEY_BLOB);
+
+ // Validate early boot
+ // VTS expects error code EARLY_BOOT_ONLY during begin operation if early boot ended tag is
+ // present
+ if (kmDataStore.getEarlyBootEndedStatus()) {
+ KMTag.assertAbsence(
+ data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.EARLY_BOOT_ENDED);
+ }
+
+ // Authorize Caller Nonce - if caller nonce absent in key char and nonce present in
+ // key params then fail if it is not a Decrypt operation
+ data[IV] = KMType.INVALID_VALUE;
+
+ if (!KMTag.isPresent(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.CALLER_NONCE)
+ && KMTag.isPresent(data[KEY_PARAMETERS], KMType.BYTES_TAG, KMType.NONCE)
+ && op.getPurpose() != KMType.DECRYPT) {
+ KMException.throwIt(KMError.CALLER_NONCE_PROHIBITED);
+ }
+
+ short nonce = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.NONCE, data[KEY_PARAMETERS]);
+ // If Nonce is present then check whether the size of nonce is correct.
+ if (nonce != KMType.INVALID_VALUE) {
+ data[IV] = KMByteTag.cast(nonce).getValue();
+ // For CBC mode - iv must be 8 bytes
+ if (op.getBlockMode() == KMType.CBC
+ && op.getAlgorithm() == KMType.DES
+ && KMByteBlob.cast(data[IV]).length() != 8) {
+ KMException.throwIt(KMError.INVALID_NONCE);
+ }
+
+ // For GCM mode - IV must be 12 bytes
+ if (KMByteBlob.cast(data[IV]).length() != 12 && op.getBlockMode() == KMType.GCM) {
+ KMException.throwIt(KMError.INVALID_NONCE);
+ }
+
+ // For AES CBC and CTR modes IV must be 16 bytes
+ if ((op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.CTR)
+ && op.getAlgorithm() == KMType.AES
+ && KMByteBlob.cast(data[IV]).length() != 16) {
+ KMException.throwIt(KMError.INVALID_NONCE);
+ }
+ } else if (op.getAlgorithm() == KMType.AES || op.getAlgorithm() == KMType.DES) {
+
+ // For symmetric decryption iv is required
+ if (op.getPurpose() == KMType.DECRYPT
+ && (op.getBlockMode() == KMType.CBC
+ || op.getBlockMode() == KMType.GCM
+ || op.getBlockMode() == KMType.CTR)) {
+ KMException.throwIt(KMError.MISSING_NONCE);
+ } else if (op.getBlockMode() == KMType.ECB) {
+ // For ECB we create zero length nonce
+ data[IV] = KMByteBlob.instance((short) 0);
+ } else if (op.getPurpose() == KMType.ENCRYPT) {
+
+ // For encrypt mode if nonce is absent then create random nonce of correct length
+ byte ivLen = 16;
+ if (op.getBlockMode() == KMType.GCM) {
+ ivLen = 12;
+ } else if (op.getAlgorithm() == KMType.DES) {
+ ivLen = 8;
+ }
+ data[IV] = KMByteBlob.instance(ivLen);
+ seProvider.newRandomNumber(
+ KMByteBlob.cast(data[IV]).getBuffer(),
+ KMByteBlob.cast(data[IV]).getStartOff(),
+ KMByteBlob.cast(data[IV]).length());
+ }
+ }
+ }
+
+ private void beginKeyAgreementOperation(KMOperationState op) {
+ if (op.getAlgorithm() != KMType.EC) {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+
+ op.setOperation(
+ seProvider.initAsymmetricOperation(
+ (byte) op.getPurpose(),
+ (byte) op.getAlgorithm(),
+ (byte) op.getPadding(),
+ (byte) op.getDigest(),
+ KMType.DIGEST_NONE, /* No MGF1 Digest */
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ null,
+ (short) 0,
+ (short) 0));
+ }
+
+ private void beginCipherOperation(KMOperationState op) {
+ switch (op.getAlgorithm()) {
+ case KMType.RSA:
+ try {
+ if (op.getPurpose() == KMType.DECRYPT) {
+ op.setOperation(
+ seProvider.initAsymmetricOperation(
+ (byte) op.getPurpose(),
+ (byte) op.getAlgorithm(),
+ (byte) op.getPadding(),
+ (byte) op.getDigest(),
+ (byte) op.getMgfDigest(),
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ KMByteBlob.cast(data[PUB_KEY]).getBuffer(),
+ KMByteBlob.cast(data[PUB_KEY]).getStartOff(),
+ KMByteBlob.cast(data[PUB_KEY]).length()));
+ } else {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ } catch (CryptoException exp) {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ break;
+ case KMType.AES:
+ case KMType.DES:
+ if (op.getBlockMode() == KMType.GCM) {
+ op.setAesGcmUpdateStart();
+ }
+ try {
+ op.setOperation(
+ seProvider.initSymmetricOperation(
+ (byte) op.getPurpose(),
+ (byte) op.getAlgorithm(),
+ (byte) op.getDigest(),
+ (byte) op.getPadding(),
+ (byte) op.getBlockMode(),
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ KMByteBlob.cast(data[IV]).getBuffer(),
+ KMByteBlob.cast(data[IV]).getStartOff(),
+ KMByteBlob.cast(data[IV]).length(),
+ op.getMacLength()));
+ } catch (CryptoException exception) {
+ if (exception.getReason() == CryptoException.ILLEGAL_VALUE) {
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ } else if (exception.getReason() == CryptoException.NO_SUCH_ALGORITHM) {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ }
+ }
+ }
+
+ private void beginTrustedConfirmationOperation(KMOperationState op) {
+ // Check for trusted confirmation - if required then set the signer in op state.
+ if (KMKeyParameters.findTag(
+ KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, data[HW_PARAMETERS])
+ != KMType.INVALID_VALUE) {
+
+ op.setTrustedConfirmationSigner(
+ seProvider.initTrustedConfirmationSymmetricOperation(kmDataStore.getComputedHmacKey()));
+
+ op.getTrustedConfirmationSigner()
+ .update(confirmationToken, (short) 0, (short) confirmationToken.length);
+ }
+ }
+
+ private void beginSignVerifyOperation(KMOperationState op) {
+ switch (op.getAlgorithm()) {
+ case KMType.RSA:
+ try {
+ if (op.getPurpose() == KMType.SIGN) {
+ op.setOperation(
+ seProvider.initAsymmetricOperation(
+ (byte) op.getPurpose(),
+ (byte) op.getAlgorithm(),
+ (byte) op.getPadding(),
+ (byte) op.getDigest(),
+ KMType.DIGEST_NONE, /* No MGF Digest */
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ KMByteBlob.cast(data[PUB_KEY]).getBuffer(),
+ KMByteBlob.cast(data[PUB_KEY]).getStartOff(),
+ KMByteBlob.cast(data[PUB_KEY]).length()));
+ } else {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ } catch (CryptoException exp) {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ break;
+ case KMType.EC:
+ try {
+ if (op.getPurpose() == KMType.SIGN) {
+ op.setOperation(
+ seProvider.initAsymmetricOperation(
+ (byte) op.getPurpose(),
+ (byte) op.getAlgorithm(),
+ (byte) op.getPadding(),
+ (byte) op.getDigest(),
+ KMType.DIGEST_NONE, /* No MGF Digest */
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ null,
+ (short) 0,
+ (short) 0));
+ } else {
+ KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+ }
+ } catch (CryptoException exp) {
+ // Javacard does not support NO digest based signing.
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ break;
+ case KMType.HMAC:
+ // As per Keymaster HAL documentation, the length of the Hmac output can
+ // be decided by using TAG_MAC_LENGTH in Keyparameters. But there is no
+ // such provision to control the length of the Hmac output using JavaCard
+ // crypto APIs and the current implementation always returns 32 bytes
+ // length of Hmac output. So to provide support to TAG_MAC_LENGTH
+ // feature, we truncate the output signature to TAG_MAC_LENGTH and return
+ // the truncated signature back to the caller. At the time of verfication
+ // we again compute the signature of the plain text input, truncate it to
+ // TAG_MAC_LENGTH and compare it with the input signature for
+ // verification. So this is the reason we are using KMType.SIGN directly
+ // instead of using op.getPurpose().
+ try {
+ op.setOperation(
+ seProvider.initSymmetricOperation(
+ (byte) KMType.SIGN,
+ (byte) op.getAlgorithm(),
+ (byte) op.getDigest(),
+ (byte) op.getPadding(),
+ (byte) op.getBlockMode(),
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ null,
+ (short) 0,
+ (short) 0,
+ (short) 0));
+ } catch (CryptoException exp) {
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ }
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ break;
+ }
+ }
+
+ private boolean isHwAuthTokenContainsMatchingSecureId(short hwAuthToken, short secureUserIdsObj) {
+ short secureUserId = KMHardwareAuthToken.cast(hwAuthToken).getUserId();
+ if (!KMInteger.cast(secureUserId).isZero()) {
+ if (KMIntegerArrayTag.cast(secureUserIdsObj).contains(secureUserId)) {
+ return true;
+ }
+ }
+
+ short authenticatorId = KMHardwareAuthToken.cast(hwAuthToken).getAuthenticatorId();
+ if (!KMInteger.cast(authenticatorId).isZero()) {
+ if (KMIntegerArrayTag.cast(secureUserIdsObj).contains(authenticatorId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean authTokenMatches(short userSecureIdsPtr, short authType, byte[] scratchPad) {
+ if (data[HW_TOKEN] == KMType.INVALID_VALUE) {
+ return false;
+ }
+ if (!isHwAuthTokenContainsMatchingSecureId(data[HW_TOKEN], userSecureIdsPtr)) {
+ return false;
+ }
+ // check auth type
+ tmpVariables[2] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType();
+ tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal();
+ if (((byte) tmpVariables[2] & (byte) authType) == 0) {
+ return false;
+ }
+ return true;
+ }
+
+ private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratchPad) {
+ short authTime;
+ short authType;
+ // Authorize User Secure Id and Auth timeout
+ short userSecureIdPtr =
+ KMKeyParameters.findTag(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, data[HW_PARAMETERS]);
+ if (userSecureIdPtr != KMType.INVALID_VALUE) {
+ // Authentication required.
+ if (KMType.INVALID_VALUE
+ != KMKeyParameters.findTag(
+ KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, data[HW_PARAMETERS])) {
+ // Key has both USER_SECURE_ID and NO_AUTH_REQUIRED
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ // authenticator type must be provided.
+ if (KMType.INVALID_VALUE
+ == (authType = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]))) {
+ // Authentication required, but no auth type found.
+ KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+ }
+
+ short authTimeoutTagPtr =
+ KMKeyParameters.findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT, data[HW_PARAMETERS]);
+ if (authTimeoutTagPtr != KMType.INVALID_VALUE) {
+ // authenticate user
+ if (!authTokenMatches(userSecureIdPtr, authType, scratchPad)) {
+ KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+ }
+
+ authTimeoutTagPtr =
+ KMKeyParameters.findTag(
+ KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, data[CUSTOM_TAGS]);
+ if (authTimeoutTagPtr == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ authTime = KMIntegerTag.cast(authTimeoutTagPtr).getValue();
+ // set the one time auth
+ op.setOneTimeAuthReqd(true);
+ // set the authentication time stamp in operation state
+ authTime =
+ addIntegers(
+ authTime, KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp(), scratchPad);
+ op.setAuthTime(
+ KMInteger.cast(authTime).getBuffer(), KMInteger.cast(authTime).getStartOff());
+ // auth time validation will happen in update or finish
+ op.setAuthTimeoutValidated(false);
+ } else {
+ // auth per operation required
+ // store user secure id and authType in OperationState.
+ op.setUserSecureId(userSecureIdPtr);
+ op.setAuthType((byte) authType);
+ // set flags
+ op.setOneTimeAuthReqd(false);
+ op.setAuthPerOperationReqd(true);
+ }
+ }
+ }
+
+ private boolean verifyHwTokenMacInBigEndian(short hwToken, byte[] scratchPad) {
+ // The challenge, userId and authenticatorId, authenticatorType and timestamp
+ // are in network order (big-endian).
+ short len = 0;
+ // add 0
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+ len = 1;
+ // concatenate challenge - 8 bytes
+ short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge();
+ KMInteger.cast(ptr)
+ .value(
+ scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+ len += KMInteger.UINT_64;
+ // concatenate user id - 8 bytes
+ ptr = KMHardwareAuthToken.cast(hwToken).getUserId();
+ KMInteger.cast(ptr)
+ .value(
+ scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+ len += KMInteger.UINT_64;
+ // concatenate authenticator id - 8 bytes
+ ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId();
+ KMInteger.cast(ptr)
+ .value(
+ scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+ len += KMInteger.UINT_64;
+ // concatenate authenticator type - 4 bytes
+ ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType();
+ scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal();
+ len += KMInteger.UINT_32;
+ // concatenate timestamp -8 bytes
+ ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp();
+ KMInteger.cast(ptr)
+ .value(
+ scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+ len += KMInteger.UINT_64;
+
+ ptr = KMHardwareAuthToken.cast(hwToken).getMac();
+
+ return seProvider.hmacVerify(
+ kmDataStore.getComputedHmacKey(),
+ scratchPad,
+ (short) 0,
+ len,
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length());
+ }
+
+ private boolean verifyHwTokenMacInLittleEndian(short hwToken, byte[] scratchPad) {
+ // The challenge, userId and authenticatorId values are in little endian order,
+ // but authenticatorType and timestamp are in network order (big-endian).
+ short len = 0;
+ // add 0
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+ len = 1;
+ // concatenate challenge - 8 bytes
+ short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge();
+ KMInteger.cast(ptr).toLittleEndian(scratchPad, len);
+ len += KMInteger.UINT_64;
+ // concatenate user id - 8 bytes
+ ptr = KMHardwareAuthToken.cast(hwToken).getUserId();
+ KMInteger.cast(ptr).toLittleEndian(scratchPad, len);
+ len += KMInteger.UINT_64;
+ // concatenate authenticator id - 8 bytes
+ ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId();
+ KMInteger.cast(ptr).toLittleEndian(scratchPad, len);
+ len += KMInteger.UINT_64;
+ // concatenate authenticator type - 4 bytes
+ ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType();
+ scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal();
+ len += KMInteger.UINT_32;
+ // concatenate timestamp - 8 bytes
+ ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp();
+ KMInteger.cast(ptr)
+ .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length())));
+ len += KMInteger.UINT_64;
+
+ ptr = KMHardwareAuthToken.cast(hwToken).getMac();
+
+ return seProvider.hmacVerify(
+ kmDataStore.getComputedHmacKey(),
+ scratchPad,
+ (short) 0,
+ len,
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length());
+ }
+
+ private boolean validateHwToken(short hwToken, byte[] scratchPad) {
+ // CBOR Encoding is always big endian
+ short ptr = KMHardwareAuthToken.cast(hwToken).getMac();
+ // If mac length is zero then token is empty.
+ if (KMByteBlob.cast(ptr).length() == 0) {
+ return false;
+ }
+ if (KMConfigurations.TEE_MACHINE_TYPE == KMConfigurations.LITTLE_ENDIAN) {
+ return verifyHwTokenMacInLittleEndian(hwToken, scratchPad);
+ } else {
+ return verifyHwTokenMacInBigEndian(hwToken, scratchPad);
+ }
+ }
+
+ private short importKeyCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 6);
+ // Arguments
+ short params = KMKeyParameters.expAny();
+ KMArray.cast(cmd).add((short) 0, params);
+ KMArray.cast(cmd).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT));
+ KMArray.cast(cmd).add((short) 2, KMByteBlob.exp());
+ KMArray.cast(cmd).add((short) 3, KMByteBlob.exp()); // attest key
+ KMArray.cast(cmd).add((short) 4, params); // attest key params
+ KMArray.cast(cmd).add((short) 5, KMByteBlob.exp()); // issuer
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processImportKeyCmd(APDU apdu) {
+ // Receive the incoming request fully from the host into buffer.
+ short cmd = importKeyCmd(apdu);
+ byte[] scratchPad = apdu.getBuffer();
+ data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 0);
+ short keyFmt = KMArray.cast(cmd).get((short) 1);
+ data[IMPORTED_KEY_BLOB] = KMArray.cast(cmd).get((short) 2);
+ data[ATTEST_KEY_BLOB] = KMArray.cast(cmd).get((short) 3);
+ data[ATTEST_KEY_PARAMS] = KMArray.cast(cmd).get((short) 4);
+ data[ATTEST_KEY_ISSUER] = KMArray.cast(cmd).get((short) 5);
+ keyFmt = KMEnum.cast(keyFmt).getVal();
+
+ data[CERTIFICATE] = KMArray.instance((short) 0); // by default the cert is empty.
+ data[ORIGIN] = KMType.IMPORTED;
+ // ID_IMEI should be present if ID_SECOND_IMEI is present
+ short attIdTag =
+ KMKeyParameters.findTag(
+ KMType.BYTES_TAG, KMType.ATTESTATION_ID_SECOND_IMEI, data[KEY_PARAMETERS]);
+ if (attIdTag != KMType.INVALID_VALUE) {
+ KMTag.assertPresence(
+ data[KEY_PARAMETERS],
+ KMType.BYTES_TAG,
+ KMType.ATTESTATION_ID_IMEI,
+ KMError.CANNOT_ATTEST_IDS);
+ }
+ importKey(apdu, keyFmt, scratchPad);
+ }
+
+ private void validateImportKey(short params, short keyFmt) {
+ short attKeyPurpose = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, params);
+ // ATTEST_KEY cannot be combined with any other purpose.
+ if (attKeyPurpose != KMType.INVALID_VALUE
+ && KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY)
+ && KMEnumArrayTag.cast(attKeyPurpose).length() > 1) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+ }
+ // Rollback protection not supported
+ KMTag.assertAbsence(
+ params,
+ KMType.BOOL_TAG,
+ KMType.ROLLBACK_RESISTANCE,
+ KMError.ROLLBACK_RESISTANCE_UNAVAILABLE);
+ // As per specification, Early boot keys may not be imported at all, if Tag::EARLY_BOOT_ONLY is
+ // provided to IKeyMintDevice::importKey
+ KMTag.assertAbsence(params, KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.EARLY_BOOT_ENDED);
+ // Check if the tags are supported.
+ if (KMKeyParameters.hasUnsupportedTags(params)) {
+ KMException.throwIt(KMError.UNSUPPORTED_TAG);
+ }
+ // Algorithm must be present
+ KMTag.assertPresence(params, KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT);
+ short alg = KMEnumTag.getValue(KMType.ALGORITHM, params);
+ // key format must be raw if aes, des or hmac and pkcs8 for rsa and ec.
+ if ((alg == KMType.AES || alg == KMType.DES || alg == KMType.HMAC) && keyFmt != KMType.RAW) {
+ KMException.throwIt(KMError.UNIMPLEMENTED);
+ }
+ if ((alg == KMType.RSA || alg == KMType.EC) && keyFmt != KMType.PKCS8) {
+ KMException.throwIt(KMError.UNIMPLEMENTED);
+ }
+ }
+
+ private void importKey(APDU apdu, short keyFmt, byte[] scratchPad) {
+ validateImportKey(data[KEY_PARAMETERS], keyFmt);
+ // Check algorithm and dispatch to appropriate handler.
+ short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]);
+ switch (alg) {
+ case KMType.RSA:
+ importRSAKey(scratchPad);
+ break;
+ case KMType.AES:
+ importAESKey(scratchPad);
+ break;
+ case KMType.DES:
+ importTDESKey(scratchPad);
+ break;
+ case KMType.HMAC:
+ importHmacKey(scratchPad);
+ break;
+ case KMType.EC:
+ importECKeys(scratchPad);
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ break;
+ }
+ makeKeyCharacteristics(scratchPad);
+ KMAttestationCert cert =
+ generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS], scratchPad);
+ createEncryptedKeyBlob(scratchPad);
+ sendOutgoing(apdu, cert, data[CERTIFICATE], data[KEY_BLOB], data[KEY_CHARACTERISTICS]);
+ }
+
+ private void importECKeys(byte[] scratchPad) {
+ // Decode key material
+ KMAsn1Parser pkcs8 = KMAsn1Parser.instance();
+ short keyBlob = pkcs8.decodeEc(data[IMPORTED_KEY_BLOB]);
+ data[PUB_KEY] = KMArray.cast(keyBlob).get((short) 0);
+ data[SECRET] = KMArray.cast(keyBlob).get((short) 1);
+ // initialize 256 bit p256 key for given private key and public key.
+ short index = 0;
+ // check whether the key size tag is present in key parameters.
+ short SecretLen = (short) (KMByteBlob.length(data[SECRET]) * 8);
+ short keySize =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+ if (keySize != KMType.INVALID_VALUE) {
+ // As per NIST.SP.800-186 page 9, secret for 256 curve should be between
+ // 256-383
+ if (((256 <= SecretLen) && (383 >= SecretLen)) ^ keySize == 256) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ if (keySize != 256) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ } else {
+ if ((256 > SecretLen) || (383 < SecretLen)) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ // add the key size to scratchPad
+ keySize = KMInteger.uint_16((short) 256);
+ keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keySize);
+ Util.setShort(scratchPad, index, keySize);
+ index += 2;
+ }
+ // check the curve if present in key parameters.
+ short curve = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]);
+ if (curve != KMType.INVALID_VALUE) {
+ // As per NIST.SP.800-186 page 9, secret length for 256 curve should be between
+ // 256-383
+ if (((256 <= SecretLen) && (383 >= SecretLen)) ^ curve == KMType.P_256) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ if (curve != KMType.P_256) {
+ KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE);
+ }
+ } else {
+ if ((256 > SecretLen) || (383 < SecretLen)) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ // add the curve to scratchPad
+ curve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256);
+ Util.setShort(scratchPad, index, curve);
+ index += 2;
+ }
+
+ // Check whether key can be created
+ seProvider.importAsymmetricKey(
+ KMType.EC,
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ KMByteBlob.cast(data[PUB_KEY]).getBuffer(),
+ KMByteBlob.cast(data[PUB_KEY]).getStartOff(),
+ KMByteBlob.cast(data[PUB_KEY]).length());
+
+ // add scratch pad to key parameters
+ updateKeyParameters(scratchPad, index);
+ data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]);
+ }
+
+ private void importHmacKey(byte[] scratchPad) {
+ // Get Key
+ data[SECRET] = data[IMPORTED_KEY_BLOB];
+ // create HMAC key of up to 512 bit
+ short index = 0; // index in scratchPad for update params
+ // check the keysize tag if present in key parameters.
+ short keysize =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+ if (keysize != KMType.INVALID_VALUE) {
+ if (!(keysize >= 64 && keysize <= 512 && keysize % 8 == 0)) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ if (keysize != (short) (KMByteBlob.length(data[SECRET]) * 8)) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ } else {
+ // add the key size to scratchPad
+ keysize = (short) (KMByteBlob.length(data[SECRET]) * 8);
+ if (!(keysize >= 64 && keysize <= 512 && keysize % 8 == 0)) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ keysize = KMInteger.uint_16(keysize);
+ short keySizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize);
+ Util.setShort(scratchPad, index, keySizeTag);
+ index += 2;
+ }
+ // Check whether key can be created
+ seProvider.importSymmetricKey(
+ KMType.HMAC,
+ keysize,
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length());
+
+ // update the key parameters list
+ updateKeyParameters(scratchPad, index);
+ // validate HMAC Key parameters
+ validateHmacKey();
+ data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+ }
+
+ private void importTDESKey(byte[] scratchPad) {
+ // Decode Key Material
+ data[SECRET] = data[IMPORTED_KEY_BLOB];
+ short index = 0; // index in scratchPad for update params
+ // check the keysize tag if present in key parameters.
+ short keysize =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+ if (keysize != KMType.INVALID_VALUE) {
+ if (keysize != 168) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ if (192 != (short) (8 * KMByteBlob.length(data[SECRET]))) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ } else {
+ keysize = (short) (KMByteBlob.length(data[SECRET]) * 8);
+ if (keysize != 192) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ // add the key size to scratchPad
+ keysize = KMInteger.uint_16((short) 168);
+ short keysizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize);
+ Util.setShort(scratchPad, index, keysizeTag);
+ index += 2;
+ }
+ // Read Minimum Mac length - it must not be present
+ KMTag.assertAbsence(
+ data[KEY_PARAMETERS], KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMError.INVALID_TAG);
+ // Check whether key can be created
+ seProvider.importSymmetricKey(
+ KMType.DES,
+ keysize,
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length());
+ // update the key parameters list
+ updateKeyParameters(scratchPad, index);
+ data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+ }
+
+ private void validateAesKeySize(short keySizeBits) {
+ if (keySizeBits != 128 && keySizeBits != 256) {
+ KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+ }
+ }
+
+ private void importAESKey(byte[] scratchPad) {
+ // Get Key
+ data[SECRET] = data[IMPORTED_KEY_BLOB];
+ // create 128 or 256 bit AES key
+ short index = 0; // index in scratchPad for update params
+ // check the keysize tag if present in key parameters.
+ short keysize =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+ if (keysize != KMType.INVALID_VALUE) {
+ if (keysize != (short) (8 * KMByteBlob.length(data[SECRET]))) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ validateAesKeySize(keysize);
+ } else {
+ // add the key size to scratchPad
+ keysize = (short) (8 * KMByteBlob.cast(data[SECRET]).length());
+ validateAesKeySize(keysize);
+ keysize = KMInteger.uint_16(keysize);
+ short keysizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize);
+ Util.setShort(scratchPad, index, keysizeTag);
+ index += 2;
+ }
+ // Check whether key can be created
+ seProvider.importSymmetricKey(
+ KMType.AES,
+ keysize,
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length());
+
+ // update the key parameters list
+ updateKeyParameters(scratchPad, index);
+ // validate AES Key parameters
+ validateAESKey();
+ data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+ }
+
+ private void importRSAKey(byte[] scratchPad) {
+ // Decode key material
+ KMAsn1Parser pkcs8 = KMAsn1Parser.instance();
+ short keyblob = pkcs8.decodeRsa(data[IMPORTED_KEY_BLOB]);
+ data[PUB_KEY] = KMArray.cast(keyblob).get((short) 0);
+ short pubKeyExp = KMArray.cast(keyblob).get((short) 1);
+ data[SECRET] = KMArray.cast(keyblob).get((short) 2);
+ if (F4.length != KMByteBlob.cast(pubKeyExp).length()) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ if (Util.arrayCompare(
+ F4,
+ (short) 0,
+ KMByteBlob.cast(pubKeyExp).getBuffer(),
+ KMByteBlob.cast(pubKeyExp).getStartOff(),
+ (short) F4.length)
+ != 0) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ short index = 0; // index in scratchPad for update parameters.
+ // validate public exponent if present in key params - it must be 0x010001
+ short len =
+ KMIntegerTag.getValue(
+ scratchPad,
+ (short) 10, // using offset 10 as first 10 bytes reserved for update params
+ KMType.ULONG_TAG,
+ KMType.RSA_PUBLIC_EXPONENT,
+ data[KEY_PARAMETERS]);
+ if (len != KMTag.INVALID_VALUE) {
+ if (len != 4
+ || Util.getShort(scratchPad, (short) 10) != 0x01
+ || Util.getShort(scratchPad, (short) 12) != 0x01) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ } else {
+ // add public exponent to scratchPad
+ Util.setShort(scratchPad, (short) 10, (short) 0x01);
+ Util.setShort(scratchPad, (short) 12, (short) 0x01);
+ pubKeyExp = KMInteger.uint_32(scratchPad, (short) 10);
+ pubKeyExp = KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, pubKeyExp);
+ Util.setShort(scratchPad, index, pubKeyExp);
+ index += 2;
+ }
+
+ // check the keysize tag if present in key parameters.
+ short keysize =
+ KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+ short kSize = (short) (KMByteBlob.length(data[PUB_KEY]) * 8);
+ if (keysize != KMType.INVALID_VALUE) {
+ if (keysize != 2048 || (keysize != kSize)) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ } else {
+ if (2048 != kSize) {
+ KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+ }
+ // add the key size to scratchPad
+ keysize = KMInteger.uint_16((short) 2048);
+ keysize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize);
+ Util.setShort(scratchPad, index, keysize);
+ index += 2;
+ }
+
+ // Check whether key can be created
+ seProvider.importAsymmetricKey(
+ KMType.RSA,
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ KMByteBlob.cast(data[PUB_KEY]).getBuffer(),
+ KMByteBlob.cast(data[PUB_KEY]).getStartOff(),
+ KMByteBlob.cast(data[PUB_KEY]).length());
+
+ // update the key parameters list
+ updateKeyParameters(scratchPad, index);
+ // validate RSA Key parameters
+ validateRSAKey(scratchPad);
+ data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE);
+ KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]);
+ }
+
+ private void updateKeyParameters(byte[] newParams, short len) {
+ if (len == 0) {
+ return; // nothing to update
+ }
+ // Create Update Param array and copy current params
+ short params = KMKeyParameters.cast(data[KEY_PARAMETERS]).getVals();
+ len = (short) (KMArray.cast(params).length() + (short) (len / 2));
+ short updatedParams = KMArray.instance(len); // update params
+
+ len = KMArray.cast(params).length();
+ short index = 0;
+
+ // copy the existing key parameters to updated array
+ while (index < len) {
+ short tag = KMArray.cast(params).get(index);
+ KMArray.cast(updatedParams).add(index, tag);
+ index++;
+ }
+
+ // copy new parameters to updated array
+ len = KMArray.cast(updatedParams).length();
+ short newParamIndex = 0; // index in ptrArr
+ while (index < len) {
+ short tag = Util.getShort(newParams, newParamIndex);
+ KMArray.cast(updatedParams).add(index, tag);
+ index++;
+ newParamIndex += 2;
+ }
+ // replace with updated key parameters.
+ data[KEY_PARAMETERS] = KMKeyParameters.instance(updatedParams);
+ }
+
+ private short initStrongBoxCmd(APDU apdu) {
+ short cmd = KMArray.instance((short) 3);
+ KMArray.cast(cmd).add((short) 0, KMInteger.exp()); // OS version
+ KMArray.cast(cmd).add((short) 1, KMInteger.exp()); // OS patch level
+ KMArray.cast(cmd).add((short) 2, KMInteger.exp()); // Vendor patch level
+ return receiveIncoming(apdu, cmd);
+ }
+
+ // This command is executed to set the boot parameters.
+ // releaseAllOperations has to be called on every boot, so
+ // it is called from inside initStrongBoxCmd. Later in future if
+ // initStrongBoxCmd is removed, then make sure that releaseAllOperations
+ // is moved to a place where it is called on every boot.
+ private void processInitStrongBoxCmd(APDU apdu) {
+ short cmd = initStrongBoxCmd(apdu);
+
+ short osVersion = KMArray.cast(cmd).get((short) 0);
+ short osPatchLevel = KMArray.cast(cmd).get((short) 1);
+ short vendorPatchLevel = KMArray.cast(cmd).get((short) 2);
+ setOsVersion(osVersion);
+ setOsPatchLevel(osPatchLevel);
+ setVendorPatchLevel(vendorPatchLevel);
+ kmDataStore.setDeviceBootStatus(KMKeymintDataStore.SET_SYSTEM_PROPERTIES_SUCCESS);
+ }
+
+ public void reboot() {
+ // flag to maintain early boot ended state
+ kmDataStore.setEarlyBootEndedStatus(false);
+ // Clear all the operation state.
+ releaseAllOperations();
+ // Hmac is cleared, so generate a new Hmac nonce.
+ initHmacNonceAndSeed();
+ // Clear all auth tags.
+ kmDataStore.removeAllAuthTags();
+ }
+
+ protected void initSystemBootParams(
+ short osVersion, short osPatchLevel, short vendorPatchLevel, short bootPatchLevel) {
+ osVersion = KMInteger.uint_16(osVersion);
+ osPatchLevel = KMInteger.uint_16(osPatchLevel);
+ vendorPatchLevel = KMInteger.uint_16((short) vendorPatchLevel);
+ setOsVersion(osVersion);
+ setOsPatchLevel(osPatchLevel);
+ setVendorPatchLevel(vendorPatchLevel);
+ }
+
+ protected void setOsVersion(short version) {
+ kmDataStore.setOsVersion(
+ KMInteger.cast(version).getBuffer(),
+ KMInteger.cast(version).getStartOff(),
+ KMInteger.cast(version).length());
+ }
+
+ protected void setOsPatchLevel(short patch) {
+ kmDataStore.setOsPatch(
+ KMInteger.cast(patch).getBuffer(),
+ KMInteger.cast(patch).getStartOff(),
+ KMInteger.cast(patch).length());
+ }
+
+ protected void setVendorPatchLevel(short patch) {
+ kmDataStore.setVendorPatchLevel(
+ KMInteger.cast(patch).getBuffer(),
+ KMInteger.cast(patch).getStartOff(),
+ KMInteger.cast(patch).length());
+ }
+
+ private short generateKeyCmd(APDU apdu) {
+ short params = KMKeyParameters.expAny();
+ short blob = KMByteBlob.exp();
+ // Array of expected arguments
+ short cmd = KMArray.instance((short) 4);
+ KMArray.cast(cmd).add((short) 0, params); // key params
+ KMArray.cast(cmd).add((short) 1, blob); // attest key
+ KMArray.cast(cmd).add((short) 2, params); // attest key params
+ KMArray.cast(cmd).add((short) 3, blob); // issuer
+ return receiveIncoming(apdu, cmd);
+ }
+
+ private void processGenerateKey(APDU apdu) {
+ // Receive the incoming request fully from the host into buffer.
+ short cmd = generateKeyCmd(apdu);
+ // Re-purpose the apdu buffer as scratch pad.
+ byte[] scratchPad = apdu.getBuffer();
+ data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 0);
+ data[ATTEST_KEY_BLOB] = KMArray.cast(cmd).get((short) 1);
+ data[ATTEST_KEY_PARAMS] = KMArray.cast(cmd).get((short) 2);
+ data[ATTEST_KEY_ISSUER] = KMArray.cast(cmd).get((short) 3);
+ data[CERTIFICATE] = KMType.INVALID_VALUE; // by default the cert is empty.
+ // ROLLBACK_RESISTANCE not supported.
+ KMTag.assertAbsence(
+ data[KEY_PARAMETERS],
+ KMType.BOOL_TAG,
+ KMType.ROLLBACK_RESISTANCE,
+ KMError.ROLLBACK_RESISTANCE_UNAVAILABLE);
+
+ // Algorithm must be present
+ KMTag.assertPresence(
+ data[KEY_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT);
+
+ // Check if the tags are supported.
+ if (KMKeyParameters.hasUnsupportedTags(data[KEY_PARAMETERS])) {
+ KMException.throwIt(KMError.UNSUPPORTED_TAG);
+ }
+
+ // ID_IMEI should be present if ID_SECOND_IMEI is present
+ short attIdTag =
+ KMKeyParameters.findTag(
+ KMType.BYTES_TAG, KMType.ATTESTATION_ID_SECOND_IMEI, data[KEY_PARAMETERS]);
+ if (attIdTag != KMType.INVALID_VALUE) {
+ KMTag.assertPresence(
+ data[KEY_PARAMETERS],
+ KMType.BYTES_TAG,
+ KMType.ATTESTATION_ID_IMEI,
+ KMError.CANNOT_ATTEST_IDS);
+ }
+
+ short attKeyPurpose =
+ KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, data[KEY_PARAMETERS]);
+ // ATTEST_KEY cannot be combined with any other purpose.
+ if (attKeyPurpose != KMType.INVALID_VALUE
+ && KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY)
+ && KMEnumArrayTag.cast(attKeyPurpose).length() > 1) {
+ KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+ }
+ short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]);
+ // Check algorithm and dispatch to appropriate handler.
+ switch (alg) {
+ case KMType.RSA:
+ generateRSAKey(scratchPad);
+ break;
+ case KMType.AES:
+ generateAESKey(scratchPad);
+ break;
+ case KMType.DES:
+ generateTDESKey(scratchPad);
+ break;
+ case KMType.HMAC:
+ generateHmacKey(scratchPad);
+ break;
+ case KMType.EC:
+ generateECKeys(scratchPad);
+ break;
+ default:
+ KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+ break;
+ }
+ // create key blob and associated attestation.
+ data[ORIGIN] = KMType.GENERATED;
+ makeKeyCharacteristics(scratchPad);
+ // construct the certificate and place the encoded data in data[CERTIFICATE]
+ KMAttestationCert cert =
+ generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS], scratchPad);
+ createEncryptedKeyBlob(scratchPad);
+ sendOutgoing(apdu, cert, data[CERTIFICATE], data[KEY_BLOB], data[KEY_CHARACTERISTICS]);
+ }
+
+ private short getApplicationId(short params) {
+ short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, params);
+ if (appId != KMTag.INVALID_VALUE) {
+ appId = KMByteTag.cast(appId).getValue();
+ if (KMByteBlob.cast(appId).length() == 0) {
+ // Treat empty as INVALID.
+ return KMType.INVALID_VALUE;
+ }
+ }
+ return appId;
+ }
+
+ private short getApplicationData(short params) {
+ short appData = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, params);
+ if (appData != KMTag.INVALID_VALUE) {
+ appData = KMByteTag.cast(appData).getValue();
+ if (KMByteBlob.cast(appData).length() == 0) {
+ // Treat empty as INVALID.
+ return KMType.INVALID_VALUE;
+ }
+ }
+ return appData;
+ }
+
+ private short getAttestationMode(short attKeyBlob, short attChallenge) {
+ short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[KEY_PARAMETERS]);
+ short mode = KMType.NO_CERT;
+ if (KMEnumTag.cast(alg).getValue() != KMType.RSA
+ && KMEnumTag.cast(alg).getValue() != KMType.EC) {
+ return mode;
+ }
+ // If attestation keyblob preset
+ if (attKeyBlob != KMType.INVALID_VALUE && KMByteBlob.cast(attKeyBlob).length() > 0) {
+ // No attestation challenge present then it is an error
+ if (attChallenge == KMType.INVALID_VALUE || KMByteBlob.cast(attChallenge).length() <= 0) {
+ KMException.throwIt(KMError.ATTESTATION_CHALLENGE_MISSING);
+ } else {
+ mode = KMType.ATTESTATION_CERT;
+ }
+ } else { // no attestation key blob
+ // Attestation challenge present then it is an error because no factory provisioned attest key
+ if (attChallenge != KMType.INVALID_VALUE && KMByteBlob.cast(attChallenge).length() > 0) {
+ KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED);
+ } else if (KMEnumArrayTag.contains(KMType.PURPOSE, KMType.ATTEST_KEY, data[HW_PARAMETERS])
+ || KMEnumArrayTag.contains(KMType.PURPOSE, KMType.SIGN, data[HW_PARAMETERS])) {
+ // The Purpose value can be read from either data[HW_PARAMETERS] or data[KEY_PARAMETERS]
+ // as the values will be same, and they are cryptographically bound.
+ mode = KMType.SELF_SIGNED_CERT;
+ } else {
+ mode = KMType.FAKE_CERT;
+ }
+ }
+ return mode;
+ }
+
+ private KMAttestationCert generateAttestation(
+ short attKeyBlob, short attKeyParam, byte[] scratchPad) {
+ // 1) If attestation key is present and attestation challenge is absent then it is an error.
+ // 2) If attestation key is absent and attestation challenge is present then it is an error as
+ // factory provisioned attestation key is not supported.
+ // 3) If both are present and issuer is absent or attest key purpose is not ATTEST_KEY then it
+ // is an error.
+ // 4) If the generated/imported keys are RSA or EC then validity period must be specified.
+ // Device Unique Attestation is not supported.
+ short heapStart = repository.getHeapIndex();
+ KMTag.assertAbsence(
+ data[KEY_PARAMETERS],
+ KMType.BOOL_TAG,
+ KMType.DEVICE_UNIQUE_ATTESTATION,
+ KMError.CANNOT_ATTEST_IDS);
+ // Read attestation challenge if present
+ short attChallenge =
+ KMKeyParameters.findTag(
+ KMType.BYTES_TAG, KMType.ATTESTATION_CHALLENGE, data[KEY_PARAMETERS]);
+ if (attChallenge != KMType.INVALID_VALUE) {
+ attChallenge = KMByteTag.cast(attChallenge).getValue();
+ }
+ // No attestation required for symmetric keys
+ short mode = getAttestationMode(attKeyBlob, attChallenge);
+ KMAttestationCert cert = null;
+
+ switch (mode) {
+ case KMType.ATTESTATION_CERT:
+ cert =
+ makeAttestationCert(
+ attKeyBlob, attKeyParam, attChallenge, data[ATTEST_KEY_ISSUER], scratchPad);
+ break;
+ case KMType.SELF_SIGNED_CERT:
+ cert = makeSelfSignedCert(data[SECRET], data[PUB_KEY], mode, scratchPad);
+ break;
+ case KMType.FAKE_CERT:
+ // Generate certificate with no signature.
+ cert = makeSelfSignedCert(KMType.INVALID_VALUE, data[PUB_KEY], mode, scratchPad);
+ break;
+ default:
+ data[CERTIFICATE] = KMType.INVALID_VALUE;
+ return null;
+ }
+ // Certificate Data is converted to cbor and written to the end of the stack.
+ short certData = repository.allocReclaimableMemory(MAX_CERT_SIZE);
+ // Leave first 4 bytes for Array header and ByteBlob header.
+ cert.buffer(repository.getHeap(), (short) (certData + 4), (short) (MAX_CERT_SIZE - 4));
+ // Build the certificate - this will sign the cert
+ cert.build();
+ // Certificate is now built so the data in the heap starting from heapStart to the current
+ // heap index can be reused. So resetting the heap index to heapStart.
+ repository.setHeapIndex(heapStart);
+ data[CERTIFICATE] = certData;
+ return cert;
+ }
+
+ // Encodes KeyCharacteristics at the end of the heap
+ private void encodeKeyCharacteristics(short keyChars) {
+ byte[] buffer = repository.getHeap();
+ short prevReclaimIndex = repository.getHeapReclaimIndex();
+ short ptr = repository.allocReclaimableMemory(MAX_KEY_CHARS_SIZE);
+ short len = encoder.encode(keyChars, buffer, ptr, prevReclaimIndex, MAX_KEY_CHARS_SIZE);
+ // shift the encoded KeyCharacteristics data towards the right till the data[CERTIFICATE]
+ // offset.
+ Util.arrayCopyNonAtomic(buffer, ptr, buffer, (short) (ptr + (MAX_KEY_CHARS_SIZE - len)), len);
+ // Reclaim the unused memory.
+ repository.reclaimMemory((short) (MAX_KEY_CHARS_SIZE - len));
+ }
+
+ // Encodes KeyBlob at the end of the heap
+ private void encodeKeyBlob(short keyBlobPtr) {
+ // allocate reclaimable memory.
+ byte[] buffer = repository.getHeap();
+ short prevReclaimIndex = repository.getHeapReclaimIndex();
+ short top = repository.allocReclaimableMemory(MAX_KEYBLOB_SIZE);
+ short keyBlob = encoder.encode(keyBlobPtr, buffer, top, prevReclaimIndex, MAX_KEYBLOB_SIZE);
+ Util.arrayCopyNonAtomic(
+ repository.getHeap(),
+ top,
+ repository.getHeap(),
+ (short) (top + MAX_KEYBLOB_SIZE - keyBlob),
+ keyBlob);
+ short newTop = (short) (top + MAX_KEYBLOB_SIZE - keyBlob);
+ // Encode the KeyBlob array inside a ByteString. Get the length of
+ // the ByteString header.
+ short encodedBytesLength = encoder.getEncodedBytesLength(keyBlob);
+ newTop -= encodedBytesLength;
+ encoder.encodeByteBlobHeader(keyBlob, buffer, newTop, encodedBytesLength);
+ // Reclaim unused memory.
+ repository.reclaimMemory((short) (newTop - top));
+ }
+
+ private short readKeyBlobVersion(short keyBlob) {
+ short version = KMType.INVALID_VALUE;
+ try {
+ version =
+ decoder.readKeyblobVersion(
+ KMByteBlob.cast(keyBlob).getBuffer(),
+ KMByteBlob.cast(keyBlob).getStartOff(),
+ KMByteBlob.cast(keyBlob).length());
+ if (version == KMType.INVALID_VALUE) {
+ // If Version is not present. Then it is either an old KeyBlob or
+ // corrupted KeyBlob.
+ version = 0;
+ } else {
+ version = KMInteger.cast(version).getShort();
+ if (version > KEYBLOB_CURRENT_VERSION || version < 0) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ }
+ } catch (Exception e) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ return version;
+ }
+
+ private void readKeyBlobParams(short version, short parsedKeyBlob) {
+ data[KEY_BLOB] = parsedKeyBlob;
+ // initialize data
+ switch (version) {
+ case (short) 0:
+ data[SECRET] = KMArray.cast(parsedKeyBlob).get((short) 0);
+ data[NONCE] = KMArray.cast(parsedKeyBlob).get((short) 1);
+ data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get((short) 2);
+ data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get((short) 3);
+ data[PUB_KEY] = KMType.INVALID_VALUE;
+ if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE_V0) {
+ data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get((short) 4);
+ }
+ // Set the data[KEY_BLOB_VERSION_DATA_OFFSET] with integer value of 0 so
+ // that it will used at later point of time.
+ data[KEY_BLOB_VERSION_DATA_OFFSET] = KMInteger.uint_8((byte) 0);
+ break;
+ case (short) 1:
+ data[KEY_BLOB_VERSION_DATA_OFFSET] = KMArray.cast(parsedKeyBlob).get((short) 0);
+ data[SECRET] = KMArray.cast(parsedKeyBlob).get((short) 1);
+ data[NONCE] = KMArray.cast(parsedKeyBlob).get((short) 2);
+ data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get((short) 3);
+ data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get((short) 4);
+ data[PUB_KEY] = KMType.INVALID_VALUE;
+ if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE_V1) {
+ data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get((short) 5);
+ }
+ break;
+ case (short) 2:
+ case (short) 3:
+ data[SECRET] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_SECRET);
+ data[NONCE] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_NONCE);
+ data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_AUTH_TAG);
+ data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_PARAMS);
+ data[KEY_BLOB_VERSION_DATA_OFFSET] =
+ KMArray.cast(parsedKeyBlob).get(KEY_BLOB_VERSION_OFFSET);
+ data[CUSTOM_TAGS] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_CUSTOM_TAGS);
+ data[PUB_KEY] = KMType.INVALID_VALUE;
+ if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE_V2_V3) {
+ data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_PUB_KEY);
+ }
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ }
+
+ // Decode the KeyBlob from CBOR structures to the sub types of KMType.
+ private void decodeKeyBlob(short version, short keyBlob) {
+ // Decode KeyBlob and read the KeyBlob params based on the version.
+ short parsedBlob =
+ decoder.decodeArray(
+ createKeyBlobExp(version),
+ KMByteBlob.cast(keyBlob).getBuffer(),
+ KMByteBlob.cast(keyBlob).getStartOff(),
+ KMByteBlob.cast(keyBlob).length());
+ short minArraySize = 0;
+ switch (version) {
+ case 0:
+ minArraySize = SYM_KEY_BLOB_SIZE_V0;
+ break;
+ case 1:
+ minArraySize = SYM_KEY_BLOB_SIZE_V1;
+ break;
+ case 2:
+ case 3:
+ minArraySize = SYM_KEY_BLOB_SIZE_V2_V3;
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ ;
+ // KeyBlob size should not be less than the minimum KeyBlob size.
+ if (KMArray.cast(parsedBlob).length() < minArraySize) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ readKeyBlobParams(version, parsedBlob);
+ }
+
+ // Decrypts the secret key in the KeyBlob. The secret can be a Symmetric or Asymmetric key.
+ private void processDecryptSecret(short version, short appId, short appData, byte[] scratchPad) {
+ data[TEE_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced();
+ data[SB_PARAMETERS] =
+ KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getStrongboxEnforced();
+ data[SW_PARAMETERS] =
+ KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getKeystoreEnforced();
+ data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]);
+
+ data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(appId, appData, data[ROT], scratchPad);
+ // Decrypt Secret and verify auth tag
+ decryptSecret(scratchPad, version);
+ short keyBlobSecretOff = 0;
+ switch (version) {
+ case 0:
+ // V0 KeyBlob
+ // KEY_BLOB = [
+ // SECRET,
+ // NONCE,
+ // AUTH_TAG,
+ // KEY_CHARACTERISTICS,
+ // PUBKEY
+ // ]
+ keyBlobSecretOff = (short) 0;
+ break;
+ case 1:
+ // V1 KeyBlob
+ // KEY_BLOB = [
+ // VERSION,
+ // SECRET,
+ // NONCE,
+ // AUTH_TAG,
+ // KEY_CHARACTERISTICS,
+ // PUBKEY
+ // ]
+ keyBlobSecretOff = (short) 1;
+ break;
+ case 2:
+ case 3:
+ // V2 KeyBlob
+ // KEY_BLOB = [
+ // VERSION,
+ // SECRET,
+ // NONCE,
+ // AUTH_TAG,
+ // KEY_CHARACTERISTICS,
+ // CUSTOM_TAGS,
+ // PUBKEY
+ // ]
+ keyBlobSecretOff = KEY_BLOB_SECRET;
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ ;
+ KMArray.cast(data[KEY_BLOB]).add(keyBlobSecretOff, data[SECRET]);
+ }
+
+ private void parseEncryptedKeyBlob(
+ short keyBlob, short appId, short appData, byte[] scratchPad, short version) {
+ // make root of trust blob
+ data[ROT] = readROT(scratchPad, version);
+ if (data[ROT] == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ try {
+ decodeKeyBlob(version, keyBlob);
+ processDecryptSecret(version, appId, appData, scratchPad);
+ } catch (Exception e) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ }
+
+ private void decryptSecret(byte[] scratchPad, short version) {
+ // derive master key - stored in derivedKey
+ short len;
+ short authDataOff = 0;
+ short authDataLen = 0;
+ byte[] authDataBuff = null;
+ switch (version) {
+ case 3:
+ len = deriveKey(scratchPad);
+ break;
+
+ case 2:
+ case 1:
+ case 0:
+ makeAuthData(version, scratchPad);
+ len = deriveKeyForOldKeyBlobs(scratchPad);
+ authDataBuff = repository.getHeap();
+ authDataOff = data[AUTH_DATA];
+ authDataLen = data[AUTH_DATA_LENGTH];
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ if (!seProvider.aesGCMDecrypt(
+ KMByteBlob.cast(data[DERIVED_KEY]).getBuffer(),
+ KMByteBlob.cast(data[DERIVED_KEY]).getStartOff(),
+ KMByteBlob.cast(data[DERIVED_KEY]).length(),
+ KMByteBlob.cast(data[SECRET]).getBuffer(),
+ KMByteBlob.cast(data[SECRET]).getStartOff(),
+ KMByteBlob.cast(data[SECRET]).length(),
+ scratchPad,
+ (short) 0,
+ KMByteBlob.cast(data[NONCE]).getBuffer(),
+ KMByteBlob.cast(data[NONCE]).getStartOff(),
+ KMByteBlob.cast(data[NONCE]).length(),
+ authDataBuff,
+ authDataOff,
+ authDataLen,
+ KMByteBlob.cast(data[AUTH_TAG]).getBuffer(),
+ KMByteBlob.cast(data[AUTH_TAG]).getStartOff(),
+ KMByteBlob.cast(data[AUTH_TAG]).length())) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ // Copy the decrypted secret
+ data[SECRET] =
+ KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.cast(data[SECRET]).length());
+ }
+
+ private short addIntegers(short authTime, short timeStamp, byte[] scratchPad) {
+ Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 24, (byte) 0);
+ Util.arrayCopyNonAtomic(
+ KMInteger.cast(authTime).getBuffer(),
+ KMInteger.cast(authTime).getStartOff(),
+ scratchPad,
+ (short) (8 - KMInteger.cast(timeStamp).length()),
+ KMInteger.cast(timeStamp).length());
+
+ // Copy timestamp to scratchpad
+ Util.arrayCopyNonAtomic(
+ KMInteger.cast(timeStamp).getBuffer(),
+ KMInteger.cast(timeStamp).getStartOff(),
+ scratchPad,
+ (short) (16 - KMInteger.cast(timeStamp).length()),
+ KMInteger.cast(timeStamp).length());
+
+ // add authTime in millis to timestamp.
+ KMUtils.add(scratchPad, (short) 0, (short) 8, (short) 16);
+ return KMInteger.uint_64(scratchPad, (short) 16);
+ }
+
+ public void powerReset() {
+ // TODO handle power reset signal.
+ releaseAllOperations();
+ resetWrappingKey();
+ }
+
+ private void updateTrustedConfirmationOperation(KMOperationState op) {
+ if (op.isTrustedConfirmationRequired()) {
+ op.getTrustedConfirmationSigner()
+ .update(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ KMByteBlob.cast(data[INPUT_DATA]).length());
+ }
+ }
+
+ private void finishTrustedConfirmationOperation(KMOperationState op) {
+ // Perform trusted confirmation if required
+ if (op.isTrustedConfirmationRequired()) {
+ if (0 == KMByteBlob.cast(data[CONFIRMATION_TOKEN]).length()) {
+ KMException.throwIt(KMError.NO_USER_CONFIRMATION);
+ }
+
+ boolean verified =
+ op.getTrustedConfirmationSigner()
+ .verify(
+ KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+ KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+ KMByteBlob.cast(data[INPUT_DATA]).length(),
+ KMByteBlob.cast(data[CONFIRMATION_TOKEN]).getBuffer(),
+ KMByteBlob.cast(data[CONFIRMATION_TOKEN]).getStartOff(),
+ KMByteBlob.cast(data[CONFIRMATION_TOKEN]).length());
+ if (!verified) {
+ KMException.throwIt(KMError.NO_USER_CONFIRMATION);
+ }
+ }
+ }
+
+ private boolean isDigestSupported(short alg, short digest) {
+ switch (alg) {
+ case KMType.RSA:
+ case KMType.EC:
+ if (digest != KMType.DIGEST_NONE && digest != KMType.SHA2_256) {
+ return false;
+ }
+ break;
+ case KMType.HMAC:
+ if (digest != KMType.SHA2_256) {
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java
new file mode 100644
index 0000000..65117eb
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java
@@ -0,0 +1,1075 @@
+package com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMDataStoreConstants;
+import com.android.javacard.seprovider.KMException;
+import com.android.javacard.seprovider.KMKey;
+import com.android.javacard.seprovider.KMSEProvider;
+import com.android.javacard.seprovider.KMUpgradable;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+import org.globalplatform.upgrade.Element;
+
+/**
+ * This is a storage class which helps in storing the provisioned data, ROT, OS version, Boot patch
+ * level, Vendor Patchlevel, HMAC nonce, computed shared secret, 8 auth tags, device-locked,
+ * device-locked timestamp and device-locked password only. Only the provisioned data is restored
+ * back during applet upgrades and the remaining data is flushed.
+ */
+public class KMKeymintDataStore implements KMUpgradable {
+
+ // Data table configuration
+ public static final short KM_APPLET_PACKAGE_VERSION_1 = 0x0100;
+ public static final short KM_APPLET_PACKAGE_VERSION_2 = 0x0200;
+ public static final short KM_APPLET_PACKAGE_VERSION_3 = 0x0300;
+ public static final short KM_APPLET_PACKAGE_VERSION_4 = 0x0400;
+ public static final byte DATA_INDEX_SIZE = 17;
+ public static final byte DATA_INDEX_ENTRY_SIZE = 4;
+ public static final byte DATA_INDEX_ENTRY_LENGTH = 0;
+ public static final byte DATA_INDEX_ENTRY_OFFSET = 2;
+ public static final short DATA_MEM_SIZE = 300;
+ // Data table offsets
+ public static final byte HMAC_NONCE = 0;
+ public static final byte BOOT_OS_VERSION = 1;
+ public static final byte BOOT_OS_PATCH_LEVEL = 2;
+ public static final byte VENDOR_PATCH_LEVEL = 3;
+ public static final byte DEVICE_LOCKED_TIME = 4;
+ public static final byte DEVICE_LOCKED = 5;
+ public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 6;
+ // Total 8 auth tags, so the next offset is AUTH_TAG_1 + 8
+ public static final byte AUTH_TAG_1 = 7;
+ public static final byte DEVICE_STATUS_FLAG = 15;
+ public static final byte EARLY_BOOT_ENDED_FLAG = 16;
+ // Data Item sizes
+ public static final byte HMAC_SEED_NONCE_SIZE = 32;
+ public static final byte COMPUTED_HMAC_KEY_SIZE = 32;
+ public static final byte OS_VERSION_SIZE = 4;
+ public static final byte OS_PATCH_SIZE = 4;
+ public static final byte VENDOR_PATCH_SIZE = 4;
+ public static final byte DEVICE_LOCK_TS_SIZE = 8;
+ public static final byte MAX_BLOB_STORAGE = 8;
+ public static final byte AUTH_TAG_LENGTH = 16;
+ public static final byte AUTH_TAG_COUNTER_SIZE = 4;
+ public static final byte AUTH_TAG_ENTRY_SIZE = (AUTH_TAG_LENGTH + AUTH_TAG_COUNTER_SIZE + 1);
+ // Device boot states. Applet starts executing the
+ // core commands once all the states are set. The commands
+ // that are allowed irrespective of these states are:
+ // All the provision commands
+ // INS_GET_HW_INFO_CMD
+ // INS_ADD_RNG_ENTROPY_CMD
+ // INS_COMPUTE_SHARED_HMAC_CMD
+ // INS_GET_HMAC_SHARING_PARAM_CMD
+ public static final byte SET_BOOT_PARAMS_SUCCESS = 0x01;
+ public static final byte SET_SYSTEM_PROPERTIES_SUCCESS = 0x02;
+ public static final byte NEGOTIATED_SHARED_SECRET_SUCCESS = 0x04;
+ // Old Data table offsets
+ private static final byte OLD_PROVISIONED_STATUS_OFFSET = 18;
+ private static final byte SHARED_SECRET_KEY_SIZE = 32;
+ private static final byte DEVICE_STATUS_FLAG_SIZE = 1;
+ private static final short UDS_CERT_CHAIN_MAX_SIZE = 2500; // First 2 bytes for length.
+ private static final short DICE_CERT_CHAIN_MAX_SIZE = 512;
+ private static KMKeymintDataStore kmDataStore;
+ // Secure Boot Mode
+ public byte secureBootMode;
+ // Data - originally was in repository
+ private byte[] attIdBrand;
+ private byte[] attIdDevice;
+ private byte[] attIdProduct;
+ private byte[] attIdSerial;
+ private byte[] attIdImei;
+ private byte[] attIdSecondImei;
+ private byte[] attIdMeId;
+ private byte[] attIdManufacturer;
+ private byte[] attIdModel;
+ // Boot parameters
+ private byte[] verifiedHash;
+ private byte[] bootKey;
+ private byte[] bootPatchLevel;
+ private boolean deviceBootLocked;
+ private short bootState;
+ // Challenge for Root of trust
+ private byte[] challenge;
+
+ /*
+ * Applets upgrading to KeyMint3.0 may not have the second imei provisioned.
+ * So this flag is used to ignore the SECOND_IMEI tag if the previous Applet's
+ * KeyMint version is less than 3.0.
+ */
+ public boolean ignoreSecondImei;
+ private short dataIndex;
+ private byte[] dataTable;
+ private KMSEProvider seProvider;
+ private KMRepository repository;
+ private byte[] udsCertChain;
+ private byte[] diceCertChain;
+ private KMKey masterKey;
+ private KMKey deviceUniqueKeyPair;
+ private KMKey preSharedKey;
+ private KMKey computedHmacKey;
+ private KMKey rkpMacKey;
+ private byte[] oemRootPublicKey;
+ private short provisionStatus;
+
+ public KMKeymintDataStore(KMSEProvider provider, KMRepository repo) {
+ seProvider = provider;
+ repository = repo;
+ boolean isUpgrading = provider.isUpgrading();
+ initDataTable();
+ // Initialize the device locked status
+ if (!isUpgrading) {
+ udsCertChain = new byte[UDS_CERT_CHAIN_MAX_SIZE];
+ diceCertChain = new byte[DICE_CERT_CHAIN_MAX_SIZE];
+ oemRootPublicKey = new byte[65];
+ }
+ setDeviceLockPasswordOnly(false);
+ setDeviceLock(false);
+ kmDataStore = this;
+ }
+
+ public static KMKeymintDataStore instance() {
+ return kmDataStore;
+ }
+
+ private void initDataTable() {
+ if (dataTable == null) {
+ dataTable = new byte[DATA_MEM_SIZE];
+ dataIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE);
+ }
+ }
+
+ private short dataAlloc(short length) {
+ if (((short) (dataIndex + length)) > dataTable.length) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ dataIndex += length;
+ return (short) (dataIndex - length);
+ }
+
+ private void clearDataEntry(short id) {
+ id = (short) (id * DATA_INDEX_ENTRY_SIZE);
+ short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH));
+ if (dataLen != 0) {
+ short dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET));
+ JCSystem.beginTransaction();
+ Util.arrayFillNonAtomic(dataTable, dataPtr, dataLen, (byte) 0);
+ JCSystem.commitTransaction();
+ }
+ }
+
+ private void writeDataEntry(short id, byte[] buf, short offset, short len) {
+ short dataPtr;
+ id = (short) (id * DATA_INDEX_ENTRY_SIZE);
+ short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH));
+ if (dataLen == 0) {
+ dataPtr = dataAlloc(len);
+ JCSystem.beginTransaction();
+ Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET), dataPtr);
+ Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH), len);
+ Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len);
+ JCSystem.commitTransaction();
+ } else {
+ if (len != dataLen) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET));
+ JCSystem.beginTransaction();
+ Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len);
+ JCSystem.commitTransaction();
+ }
+ }
+
+ private short readDataEntry(short id, byte[] buf, short offset) {
+ id = (short) (id * DATA_INDEX_ENTRY_SIZE);
+ short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH));
+ if (len != 0) {
+ Util.arrayCopyNonAtomic(
+ dataTable,
+ Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)),
+ buf,
+ offset,
+ len);
+ }
+ return len;
+ }
+
+ private short readDataEntry(byte[] dataTable, short id, byte[] buf, short offset) {
+ id = (short) (id * DATA_INDEX_ENTRY_SIZE);
+ short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH));
+ if (len != 0) {
+ Util.arrayCopyNonAtomic(
+ dataTable,
+ Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)),
+ buf,
+ offset,
+ len);
+ }
+ return len;
+ }
+
+ private short dataLength(short id) {
+ id = (short) (id * DATA_INDEX_ENTRY_SIZE);
+ return Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH));
+ }
+
+ public short readData(short id) {
+ short len = dataLength(id);
+ if (len != 0) {
+ short blob = KMByteBlob.instance(dataLength(id));
+ readDataEntry(id, KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff());
+ return blob;
+ }
+ return KMType.INVALID_VALUE;
+ }
+
+ public short getHmacNonce() {
+ return readData(HMAC_NONCE);
+ }
+
+ public short getOsVersion() {
+ short blob = readData(BOOT_OS_VERSION);
+ if (blob == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return KMInteger.uint_32(
+ KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff());
+ }
+
+ public short getVendorPatchLevel() {
+ short blob = readData(VENDOR_PATCH_LEVEL);
+ if (blob == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return KMInteger.uint_32(
+ KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff());
+ }
+
+ public short getOsPatch() {
+ short blob = readData(BOOT_OS_PATCH_LEVEL);
+ if (blob == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return KMInteger.uint_32(
+ KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff());
+ }
+
+ private boolean readBoolean(short id) {
+ short blob = readData(id);
+ if (blob == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return (byte) ((repository.getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01;
+ }
+
+ public boolean getDeviceLock() {
+ return readBoolean(DEVICE_LOCKED);
+ }
+
+ public void setDeviceLock(boolean flag) {
+ writeBoolean(DEVICE_LOCKED, flag);
+ }
+
+ public boolean getDeviceLockPasswordOnly() {
+ return readBoolean(DEVICE_LOCKED_PASSWORD_ONLY);
+ }
+
+ public void setDeviceLockPasswordOnly(boolean flag) {
+ writeBoolean(DEVICE_LOCKED_PASSWORD_ONLY, flag);
+ }
+
+ public boolean getEarlyBootEndedStatus() {
+ return readBoolean(EARLY_BOOT_ENDED_FLAG);
+ }
+
+ public void setEarlyBootEndedStatus(boolean flag) {
+ writeBoolean(EARLY_BOOT_ENDED_FLAG, flag);
+ }
+
+ public short getDeviceTimeStamp() {
+ short blob = readData(DEVICE_LOCKED_TIME);
+ if (blob == KMType.INVALID_VALUE) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return KMInteger.uint_64(
+ KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff());
+ }
+
+ public void setOsVersion(byte[] buf, short start, short len) {
+ if (len != OS_VERSION_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ writeDataEntry(BOOT_OS_VERSION, buf, start, len);
+ }
+
+ public void setVendorPatchLevel(byte[] buf, short start, short len) {
+ if (len != VENDOR_PATCH_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ writeDataEntry(VENDOR_PATCH_LEVEL, buf, start, len);
+ }
+
+ private void writeBoolean(short id, boolean flag) {
+ short start = repository.alloc((short) 1);
+ if (flag) {
+ (repository.getHeap())[start] = (byte) 0x01;
+ } else {
+ (repository.getHeap())[start] = (byte) 0x00;
+ }
+ writeDataEntry(id, repository.getHeap(), start, (short) 1);
+ }
+
+ public void setDeviceLockTimestamp(byte[] buf, short start, short len) {
+ if (len != DEVICE_LOCK_TS_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ writeDataEntry(DEVICE_LOCKED_TIME, buf, start, len);
+ }
+
+ public void clearDeviceBootStatus() {
+ clearDataEntry(DEVICE_STATUS_FLAG);
+ }
+
+ public void setDeviceBootStatus(byte initStatus) {
+ short offset = repository.allocReclaimableMemory(DEVICE_STATUS_FLAG_SIZE);
+ byte[] buf = repository.getHeap();
+ getDeviceBootStatus(buf, offset);
+ buf[offset] |= initStatus;
+ writeDataEntry(DEVICE_STATUS_FLAG, buf, offset, DEVICE_STATUS_FLAG_SIZE);
+ repository.reclaimMemory(DEVICE_STATUS_FLAG_SIZE);
+ }
+
+ public boolean isDeviceReady() {
+ boolean result = false;
+ short offset = repository.allocReclaimableMemory(DEVICE_STATUS_FLAG_SIZE);
+ byte[] buf = repository.getHeap();
+ getDeviceBootStatus(buf, offset);
+ byte bootCompleteStatus =
+ (SET_BOOT_PARAMS_SUCCESS
+ | SET_SYSTEM_PROPERTIES_SUCCESS
+ | NEGOTIATED_SHARED_SECRET_SUCCESS);
+ if (bootCompleteStatus == (buf[offset] & bootCompleteStatus)) {
+ result = true;
+ }
+ repository.reclaimMemory(DEVICE_STATUS_FLAG_SIZE);
+ return result;
+ }
+
+ public short getDeviceBootStatus(byte[] scratchpad, short offset) {
+ scratchpad[offset] = 0;
+ return readDataEntry(DEVICE_STATUS_FLAG, scratchpad, offset);
+ }
+
+ public void clearDeviceLockTimeStamp() {
+ clearDataEntry(DEVICE_LOCKED_TIME);
+ }
+
+ public void setOsPatch(byte[] buf, short start, short len) {
+ if (len != OS_PATCH_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ writeDataEntry(BOOT_OS_PATCH_LEVEL, buf, start, len);
+ }
+
+ private boolean isAuthTagSlotAvailable(short tagId, byte[] buf, short offset) {
+ readDataEntry(tagId, buf, offset);
+ return (0 == buf[offset]);
+ }
+
+ public void initHmacNonce(byte[] nonce, short offset, short len) {
+ if (len != HMAC_SEED_NONCE_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ writeDataEntry(HMAC_NONCE, nonce, offset, len);
+ }
+
+ public void clearHmacNonce() {
+ clearDataEntry(HMAC_NONCE);
+ }
+
+ public boolean persistAuthTag(short authTag) {
+
+ if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+
+ short authTagEntry = repository.alloc(AUTH_TAG_ENTRY_SIZE);
+ short scratchPadOff = repository.alloc(AUTH_TAG_ENTRY_SIZE);
+ byte[] scratchPad = repository.getHeap();
+ writeAuthTagState(repository.getHeap(), authTagEntry, (byte) 1);
+ Util.arrayCopyNonAtomic(
+ KMByteBlob.cast(authTag).getBuffer(),
+ KMByteBlob.cast(authTag).getStartOff(),
+ repository.getHeap(),
+ (short) (authTagEntry + 1),
+ AUTH_TAG_LENGTH);
+ Util.setShort(
+ repository.getHeap(), (short) (authTagEntry + AUTH_TAG_LENGTH + 1 + 2), (short) 1);
+ short index = 0;
+ while (index < MAX_BLOB_STORAGE) {
+ if ((dataLength((short) (index + AUTH_TAG_1)) == 0)
+ || isAuthTagSlotAvailable((short) (index + AUTH_TAG_1), scratchPad, scratchPadOff)) {
+
+ writeDataEntry(
+ (short) (index + AUTH_TAG_1), repository.getHeap(), authTagEntry, AUTH_TAG_ENTRY_SIZE);
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ public void removeAllAuthTags() {
+ short index = 0;
+ while (index < MAX_BLOB_STORAGE) {
+ clearDataEntry((short) (index + AUTH_TAG_1));
+ index++;
+ }
+ }
+
+ public boolean isAuthTagPersisted(short authTag) {
+ return (KMType.INVALID_VALUE != findTag(authTag));
+ }
+
+ private short findTag(short authTag) {
+ if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ short index = 0;
+ short found;
+ short offset = repository.alloc(AUTH_TAG_ENTRY_SIZE);
+ while (index < MAX_BLOB_STORAGE) {
+ if (dataLength((short) (index + AUTH_TAG_1)) != 0) {
+ readDataEntry((short) (index + AUTH_TAG_1), repository.getHeap(), offset);
+ found =
+ Util.arrayCompare(
+ repository.getHeap(),
+ (short) (offset + 1),
+ KMByteBlob.cast(authTag).getBuffer(),
+ KMByteBlob.cast(authTag).getStartOff(),
+ AUTH_TAG_LENGTH);
+ if (found == 0) {
+ return (short) (index + AUTH_TAG_1);
+ }
+ }
+ index++;
+ }
+ return KMType.INVALID_VALUE;
+ }
+
+ public short getRateLimitedKeyCount(short authTag, byte[] out, short outOff) {
+ short tag = findTag(authTag);
+ short blob;
+ if (tag != KMType.INVALID_VALUE) {
+ blob = readData(tag);
+ Util.arrayCopyNonAtomic(
+ KMByteBlob.cast(blob).getBuffer(),
+ (short) (KMByteBlob.cast(blob).getStartOff() + AUTH_TAG_LENGTH + 1),
+ out,
+ outOff,
+ AUTH_TAG_COUNTER_SIZE);
+ return AUTH_TAG_COUNTER_SIZE;
+ }
+ return (short) 0;
+ }
+
+ public void setRateLimitedKeyCount(short authTag, byte[] buf, short off, short len) {
+ short tag = findTag(authTag);
+ if (tag != KMType.INVALID_VALUE) {
+ short dataPtr = readData(tag);
+ Util.arrayCopyNonAtomic(
+ buf,
+ off,
+ KMByteBlob.cast(dataPtr).getBuffer(),
+ (short) (KMByteBlob.cast(dataPtr).getStartOff() + AUTH_TAG_LENGTH + 1),
+ len);
+ writeDataEntry(
+ tag,
+ KMByteBlob.cast(dataPtr).getBuffer(),
+ KMByteBlob.cast(dataPtr).getStartOff(),
+ KMByteBlob.cast(dataPtr).length());
+ }
+ }
+
+ public void persistUdsCertChain(byte[] buf, short offset, short len) {
+ // Input buffer contains encoded Uds certificate chain as shown below.
+ // UdsDKSignatures = {
+ // + SignerName => DKCertChain
+ // }
+ // SignerName = tstr
+ // DKCertChain = [
+ // 2* Certificate // Root -> Leaf. Root is the vendo r
+ // // self-signed cert, leaf contains DK_pu b
+ // ]
+ // Certificate = COSE_Sign1 of a public key
+ if ((short) (len + 2) > UDS_CERT_CHAIN_MAX_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ JCSystem.beginTransaction();
+ Util.setShort(udsCertChain, (short) 0, (short) len);
+ Util.arrayCopyNonAtomic(buf, offset, udsCertChain, (short) 2, len);
+ JCSystem.commitTransaction();
+ }
+
+ public short getUdsCertChainLength() {
+ return Util.getShort(udsCertChain, (short) 0);
+ }
+
+ public byte[] getUdsCertChain() {
+ return udsCertChain;
+ }
+
+ public byte[] getDiceCertificateChain() {
+ return diceCertChain;
+ }
+
+ public void persistBootCertificateChain(byte[] buf, short offset, short len) {
+ if ((short) (len + 2) > DICE_CERT_CHAIN_MAX_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ JCSystem.beginTransaction();
+ Util.setShort(diceCertChain, (short) 0, (short) len);
+ Util.arrayCopyNonAtomic(buf, offset, diceCertChain, (short) 2, len);
+ JCSystem.commitTransaction();
+ }
+
+ private void writeAuthTagState(byte[] buf, short offset, byte state) {
+ buf[offset] = state;
+ }
+
+ // The master key should only be generated during applet installation and
+ // during a device factory reset event.
+ public KMKey createMasterKey(short keySizeBits) {
+ if (masterKey == null) {
+ masterKey = seProvider.createMasterKey(masterKey, keySizeBits);
+ }
+ return (KMKey) masterKey;
+ }
+
+ public KMKey regenerateMasterKey() {
+ return seProvider.createMasterKey(masterKey, KMKeymasterApplet.MASTER_KEY_SIZE);
+ }
+
+ public KMKey getMasterKey() {
+ return masterKey;
+ }
+
+ public void createPresharedKey(byte[] keyData, short offset, short length) {
+ if (length != SHARED_SECRET_KEY_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ if (preSharedKey == null) {
+ preSharedKey = seProvider.createPreSharedKey(preSharedKey, keyData, offset, length);
+ }
+ }
+
+ public KMKey getPresharedKey() {
+ if (preSharedKey == null) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return preSharedKey;
+ }
+
+ public void createComputedHmacKey(byte[] keyData, short offset, short length) {
+ if (length != COMPUTED_HMAC_KEY_SIZE) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ if (computedHmacKey == null) {
+ computedHmacKey = seProvider.createComputedHmacKey(computedHmacKey, keyData, offset, length);
+ } else {
+ seProvider.createComputedHmacKey(computedHmacKey, keyData, offset, length);
+ }
+ }
+
+ public KMKey getComputedHmacKey() {
+ if (computedHmacKey == null) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return computedHmacKey;
+ }
+
+ public KMKey createRkpDeviceUniqueKeyPair(
+ byte[] pubKey,
+ short pubKeyOff,
+ short pubKeyLen,
+ byte[] privKey,
+ short privKeyOff,
+ short privKeyLen) {
+ if (deviceUniqueKeyPair == null) {
+ deviceUniqueKeyPair =
+ seProvider.createRkpDeviceUniqueKeyPair(
+ deviceUniqueKeyPair, pubKey, pubKeyOff, pubKeyLen, privKey, privKeyOff, privKeyLen);
+ } else {
+ seProvider.createRkpDeviceUniqueKeyPair(
+ deviceUniqueKeyPair, pubKey, pubKeyOff, pubKeyLen, privKey, privKeyOff, privKeyLen);
+ }
+ return deviceUniqueKeyPair;
+ }
+
+ public KMKey getRkpDeviceUniqueKeyPair() {
+ return ((KMKey) deviceUniqueKeyPair);
+ }
+
+ public void createRkpMacKey(byte[] keydata, short offset, short length) {
+ if (rkpMacKey == null) {
+ rkpMacKey = seProvider.createRkpMacKey(rkpMacKey, keydata, offset, length);
+ } else {
+ seProvider.createRkpMacKey(rkpMacKey, keydata, offset, length);
+ }
+ }
+
+ public KMKey getRkpMacKey() {
+ if (rkpMacKey == null) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return rkpMacKey;
+ }
+
+ public short getAttestationId(short tag, byte[] buffer, short start) {
+ byte[] attestId = null;
+ switch (tag) {
+ // Attestation Id Brand
+ case KMType.ATTESTATION_ID_BRAND:
+ attestId = attIdBrand;
+ break;
+ // Attestation Id Device
+ case KMType.ATTESTATION_ID_DEVICE:
+ attestId = attIdDevice;
+ break;
+ // Attestation Id Product
+ case KMType.ATTESTATION_ID_PRODUCT:
+ attestId = attIdProduct;
+ break;
+ // Attestation Id Serial
+ case KMType.ATTESTATION_ID_SERIAL:
+ attestId = attIdSerial;
+ break;
+ // Attestation Id IMEI
+ case KMType.ATTESTATION_ID_IMEI:
+ attestId = attIdImei;
+ break;
+ // Attestation Id SECOND IMEI
+ case KMType.ATTESTATION_ID_SECOND_IMEI:
+ attestId = attIdSecondImei;
+ break;
+ // Attestation Id MEID
+ case KMType.ATTESTATION_ID_MEID:
+ attestId = attIdMeId;
+ break;
+ // Attestation Id Manufacturer
+ case KMType.ATTESTATION_ID_MANUFACTURER:
+ attestId = attIdManufacturer;
+ break;
+ // Attestation Id Model
+ case KMType.ATTESTATION_ID_MODEL:
+ attestId = attIdModel;
+ break;
+ }
+ if (attestId == null) {
+ /* Ignore the SECOND_IMEI tag if the previous Applet's KeyMint version is less than 3.0 and
+ * no SECOND_IMEI is provisioned.
+ */
+ if (kmDataStore.ignoreSecondImei && tag == KMType.ATTESTATION_ID_SECOND_IMEI) {
+ return (short) 0;
+ }
+ KMException.throwIt(KMError.CANNOT_ATTEST_IDS);
+ }
+ Util.arrayCopyNonAtomic(attestId, (short) 0, buffer, start, (short) attestId.length);
+ return (short) attestId.length;
+ }
+
+ public void setAttestationId(short tag, byte[] buffer, short start, short length) {
+ switch (tag) {
+ // Attestation Id Brand
+ case KMType.ATTESTATION_ID_BRAND:
+ JCSystem.beginTransaction();
+ attIdBrand = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdBrand, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ // Attestation Id Device
+ case KMType.ATTESTATION_ID_DEVICE:
+ JCSystem.beginTransaction();
+ attIdDevice = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdDevice, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ // Attestation Id Product
+ case KMType.ATTESTATION_ID_PRODUCT:
+ JCSystem.beginTransaction();
+ attIdProduct = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdProduct, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ // Attestation Id Serial
+ case KMType.ATTESTATION_ID_SERIAL:
+ JCSystem.beginTransaction();
+ attIdSerial = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdSerial, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ // Attestation Id IMEI
+ case KMType.ATTESTATION_ID_IMEI:
+ JCSystem.beginTransaction();
+ attIdImei = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdImei, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ // Attestation Id SECOND IMEI
+ case KMType.ATTESTATION_ID_SECOND_IMEI:
+ JCSystem.beginTransaction();
+ attIdSecondImei = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdSecondImei, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ // Attestation Id MEID
+ case KMType.ATTESTATION_ID_MEID:
+ JCSystem.beginTransaction();
+ attIdMeId = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdMeId, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ // Attestation Id Manufacturer
+ case KMType.ATTESTATION_ID_MANUFACTURER:
+ JCSystem.beginTransaction();
+ attIdManufacturer = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdManufacturer, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ // Attestation Id Model
+ case KMType.ATTESTATION_ID_MODEL:
+ JCSystem.beginTransaction();
+ attIdModel = new byte[length];
+ Util.arrayCopyNonAtomic(buffer, (short) start, attIdModel, (short) 0, length);
+ JCSystem.commitTransaction();
+ break;
+ }
+ }
+
+ public void deleteAttestationIds() {
+ attIdBrand = null;
+ attIdDevice = null;
+ attIdProduct = null;
+ attIdSerial = null;
+ attIdImei = null;
+ attIdSecondImei = null;
+ attIdMeId = null;
+ attIdManufacturer = null;
+ attIdModel = null;
+ // Trigger garbage collection.
+ JCSystem.requestObjectDeletion();
+ }
+
+ public short getVerifiedBootHash(byte[] buffer, short start) {
+ if (verifiedHash == null) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ Util.arrayCopyNonAtomic(verifiedHash, (short) 0, buffer, start, (short) verifiedHash.length);
+ return (short) verifiedHash.length;
+ }
+
+ public short getBootKey(byte[] buffer, short start) {
+ if (bootKey == null) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ Util.arrayCopyNonAtomic(bootKey, (short) 0, buffer, start, (short) bootKey.length);
+ return (short) bootKey.length;
+ }
+
+ public short getBootState() {
+ return bootState;
+ }
+
+ public void setBootState(short state) {
+ bootState = state;
+ }
+
+ public boolean isDeviceBootLocked() {
+ return deviceBootLocked;
+ }
+
+ public short getBootPatchLevel() {
+ if (bootPatchLevel == null) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return KMInteger.uint_32(bootPatchLevel, (short) 0);
+ }
+
+ public void setVerifiedBootHash(byte[] buffer, short start, short length) {
+ if (verifiedHash == null) {
+ verifiedHash = new byte[32];
+ }
+ if (length != KMKeymasterApplet.VERIFIED_BOOT_HASH_SIZE) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ Util.arrayCopy(buffer, start, verifiedHash, (short) 0, (short) 32);
+ }
+
+ public void setBootKey(byte[] buffer, short start, short length) {
+ if (bootKey == null) {
+ bootKey = new byte[32];
+ }
+ if (length != KMKeymasterApplet.VERIFIED_BOOT_KEY_SIZE) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ Util.arrayCopy(buffer, start, bootKey, (short) 0, (short) 32);
+ }
+
+ public void setDeviceLocked(boolean state) {
+ deviceBootLocked = state;
+ }
+
+ public void setBootPatchLevel(byte[] buffer, short start, short length) {
+ if (bootPatchLevel == null) {
+ bootPatchLevel = new byte[4];
+ }
+ if (length > 4 || length < 0) {
+ KMException.throwIt(KMError.UNKNOWN_ERROR);
+ }
+ Util.arrayCopy(buffer, start, bootPatchLevel, (short) 0, (short) length);
+ }
+
+ public void setChallenge(byte[] buf, short start, short length) {
+ if (challenge == null) {
+ challenge = new byte[16];
+ }
+ if (length != 16) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ Util.arrayCopy(buf, start, challenge, (short) 0, (short) length);
+ }
+
+ public short getChallenge(byte[] buffer, short start) {
+ if (challenge == null) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ Util.arrayCopyNonAtomic(challenge, (short) 0, buffer, start, (short) challenge.length);
+ return (short) challenge.length;
+ }
+
+ public boolean isProvisionLocked() {
+ if (0 != (provisionStatus & KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED)) {
+ return true;
+ }
+ return false;
+ }
+
+ public short getProvisionStatus() {
+ return provisionStatus;
+ }
+
+ public void setProvisionStatus(short pStatus) {
+ JCSystem.beginTransaction();
+ provisionStatus |= pStatus;
+ JCSystem.commitTransaction();
+ }
+
+ public void unlockProvision() {
+ JCSystem.beginTransaction();
+ provisionStatus &= ~KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED;
+ JCSystem.commitTransaction();
+ }
+
+ public void persistOEMRootPublicKey(byte[] inBuff, short inOffset, short inLength) {
+ if (inLength != 65) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ if (oemRootPublicKey == null) {
+ oemRootPublicKey = new byte[65];
+ }
+ Util.arrayCopy(inBuff, inOffset, oemRootPublicKey, (short) 0, inLength);
+ }
+
+ public byte[] getOEMRootPublicKey() {
+ if (oemRootPublicKey == null) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ return oemRootPublicKey;
+ }
+
+ @Override
+ public void onSave(Element element) {
+ // Prmitives
+ element.write(provisionStatus);
+ element.write(secureBootMode);
+ element.write(ignoreSecondImei);
+ // Objects
+ element.write(attIdBrand);
+ element.write(attIdDevice);
+ element.write(attIdProduct);
+ element.write(attIdSerial);
+ element.write(attIdImei);
+ element.write(attIdSecondImei);
+ element.write(attIdMeId);
+ element.write(attIdManufacturer);
+ element.write(attIdModel);
+ element.write(udsCertChain);
+ element.write(diceCertChain);
+ element.write(oemRootPublicKey);
+
+ // Key Objects
+ seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY, masterKey);
+ seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY, preSharedKey);
+ seProvider.onSave(
+ element, KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR, deviceUniqueKeyPair);
+ seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY, rkpMacKey);
+ }
+
+ @Override
+ public void onRestore(Element element, short oldVersion, short currentVersion) {
+ if (oldVersion <= KM_APPLET_PACKAGE_VERSION_1) {
+ // 1.0 to 4.0 Upgrade happens here.
+ handlePreviousVersionUpgrade(element);
+ return;
+ } else if (oldVersion == KM_APPLET_PACKAGE_VERSION_2) {
+ handleUpgrade(element, oldVersion);
+ JCSystem.beginTransaction();
+ // While upgrading Secure Boot Mode flag from 2.0 to 4.0, implementations
+ // have to update the secureBootMode with the correct input.
+ secureBootMode = 0;
+ provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_SECURE_BOOT_MODE;
+ // Applet package Versions till 2 had CoseSign1 for additionalCertificateChain.
+ // From package version 3, the additionalCertificateChain is in X.509 format.
+ // So Unreference the old address and allocate new persistent memory.
+ udsCertChain = new byte[UDS_CERT_CHAIN_MAX_SIZE];
+ JCSystem.commitTransaction();
+ // Request for ObjectDeletion for unreferenced address of additionalCertChain.
+ JCSystem.requestObjectDeletion();
+ return;
+ }
+ handleUpgrade(element, oldVersion);
+ }
+
+ private void handlePreviousVersionUpgrade(Element element) {
+ // set ignore Imei flag to true.
+ ignoreSecondImei = true;
+ // Read Primitives
+ // restore old data table index
+ short oldDataIndex = element.readShort();
+ element.readBoolean(); // pop deviceBootLocked
+ element.readShort(); // pop bootState
+
+ // Read Objects
+ // restore old data table
+ byte[] oldDataTable = (byte[]) element.readObject();
+
+ attIdBrand = (byte[]) element.readObject();
+ attIdDevice = (byte[]) element.readObject();
+ attIdProduct = (byte[]) element.readObject();
+ attIdSerial = (byte[]) element.readObject();
+ attIdImei = (byte[]) element.readObject();
+ attIdMeId = (byte[]) element.readObject();
+ attIdManufacturer = (byte[]) element.readObject();
+ attIdModel = (byte[]) element.readObject();
+ element.readObject(); // pop verifiedHash
+ element.readObject(); // pop bootKey
+ element.readObject(); // pop bootPatchLevel
+ udsCertChain = (byte[]) element.readObject();
+ diceCertChain = (byte[]) element.readObject();
+
+ // Read Key Objects
+ masterKey = (KMKey) seProvider.onRestore(element);
+ seProvider.onRestore(element); // pop computedHmacKey
+ preSharedKey = (KMKey) seProvider.onRestore(element);
+ deviceUniqueKeyPair = (KMKey) seProvider.onRestore(element);
+ rkpMacKey = (KMKey) seProvider.onRestore(element);
+ handleProvisionStatusUpgrade(oldDataTable, oldDataIndex);
+ }
+
+ private void handleUpgrade(Element element, short oldVersion) {
+ // Read Primitives
+ provisionStatus = element.readShort();
+ if (oldVersion >= KM_APPLET_PACKAGE_VERSION_3) {
+ secureBootMode = element.readByte();
+ }
+ /* check if KeyMint is upgrading from older HAL version to KM300
+ * and set the ignore second Imei flag
+ */
+ if (oldVersion < KM_APPLET_PACKAGE_VERSION_4) {
+ ignoreSecondImei = true;
+ } else {
+ ignoreSecondImei = element.readBoolean();
+ }
+ // Read Objects
+ attIdBrand = (byte[]) element.readObject();
+ attIdDevice = (byte[]) element.readObject();
+ attIdProduct = (byte[]) element.readObject();
+ attIdSerial = (byte[]) element.readObject();
+ attIdImei = (byte[]) element.readObject();
+ if (oldVersion >= KM_APPLET_PACKAGE_VERSION_4) {
+ attIdSecondImei = (byte[]) element.readObject();
+ }
+ attIdMeId = (byte[]) element.readObject();
+ attIdManufacturer = (byte[]) element.readObject();
+ attIdModel = (byte[]) element.readObject();
+ udsCertChain = (byte[]) element.readObject();
+ diceCertChain = (byte[]) element.readObject();
+ oemRootPublicKey = (byte[]) element.readObject();
+ // Read Key Objects
+ masterKey = (KMKey) seProvider.onRestore(element);
+ preSharedKey = (KMKey) seProvider.onRestore(element);
+ deviceUniqueKeyPair = (KMKey) seProvider.onRestore(element);
+ rkpMacKey = (KMKey) seProvider.onRestore(element);
+ }
+
+ public void getProvisionStatus(byte[] dataTable, byte[] scratchpad, short offset) {
+ Util.setShort(scratchpad, offset, (short) 0);
+ readDataEntry(dataTable, OLD_PROVISIONED_STATUS_OFFSET, scratchpad, offset);
+ }
+
+ void handleProvisionStatusUpgrade(byte[] dataTable, short dataTableIndex) {
+ short dInex = repository.allocReclaimableMemory((short) 2);
+ byte data[] = repository.getHeap();
+ getProvisionStatus(dataTable, data, dInex);
+ short pStatus = (short) (data[dInex] & 0x00ff);
+ if (KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED
+ == (pStatus & KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED)) {
+ pStatus |=
+ KMKeymasterApplet.PROVISION_STATUS_SE_LOCKED
+ | KMKeymasterApplet.PROVISION_STATUS_SECURE_BOOT_MODE;
+ }
+ JCSystem.beginTransaction();
+ // While upgrading Secure Boot Mode flag from 1.0 to 4.0, implementations
+ // have to update the secureBootMode with the correct input.
+ secureBootMode = 0;
+ provisionStatus = pStatus;
+ // Applet package Versions till 2 had CoseSign1 for additionalCertificateChain.
+ // From package version 3, the additionalCertificateChain is in X.509 format.
+ // So Unreference the old address and allocate new persistent memory.
+ udsCertChain = new byte[UDS_CERT_CHAIN_MAX_SIZE];
+ JCSystem.commitTransaction();
+ repository.reclaimMemory((short) 2);
+ // Request object deletion for unreferenced address for additionalCertChain
+ JCSystem.requestObjectDeletion();
+ }
+
+ @Override
+ public short getBackupPrimitiveByteCount() {
+ // provisionStatus - 2 bytes
+ // secureBootMode - 1 byte
+ // Flag for ignore second Imei- 1 byte
+ return (short)
+ (4
+ + seProvider.getBackupPrimitiveByteCount(KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY)
+ + seProvider.getBackupPrimitiveByteCount(
+ KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY)
+ + seProvider.getBackupPrimitiveByteCount(
+ KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR)
+ + seProvider.getBackupPrimitiveByteCount(
+ KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY));
+ }
+
+ @Override
+ public short getBackupObjectCount() {
+ // AttestationIds - 9
+ // UdsCertificateChain - 1
+ // diceCertificateChain - 1
+ // oemRootPublicKey - 1
+ return (short)
+ (12
+ + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY)
+ + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY)
+ + seProvider.getBackupObjectCount(
+ KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR)
+ + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY));
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMMap.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMMap.java
new file mode 100644
index 0000000..2418204
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMMap.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMMap represents an array of a KMType key and a KMType value. Map is the sequence of pairs. Each
+ * pair is one or more sub-types of KMType. The KMMap instance maps to the CBOR type map. KMMap is a
+ * KMType and it further extends the value field in TLV_HEADER as MAP_HEADER struct{ short
+ * subType;short length;} followed by a sequence of pairs. Each pair contains a key and a value as
+ * short pointers to KMType instances.
+ */
+public class KMMap extends KMType {
+
+ public static final short ANY_MAP_LENGTH = 0x1000;
+ private static final byte MAP_HEADER_SIZE = 4;
+ private static KMMap prototype;
+
+ private KMMap() {}
+
+ private static KMMap proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMMap();
+ }
+ instanceTable[KM_MAP_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp() {
+ short ptr = instance(MAP_TYPE, (short) MAP_HEADER_SIZE);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), (short) 0);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), ANY_MAP_LENGTH);
+ return ptr;
+ }
+
+ public static short instance(short length) {
+ short ptr = KMType.instance(MAP_TYPE, (short) (MAP_HEADER_SIZE + (length * 4)));
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), (short) 0);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), length);
+ return ptr;
+ }
+
+ public static short instance(short length, byte type) {
+ short ptr = instance(length);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), type);
+ return ptr;
+ }
+
+ public static KMMap cast(short ptr) {
+ if (heap[ptr] != MAP_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public void add(short index, short keyPtr, short valPtr) {
+ short len = length();
+ if (index >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ short keyIndex =
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index * 4));
+ Util.setShort(heap, keyIndex, keyPtr);
+ Util.setShort(heap, (short) (keyIndex + 2), valPtr);
+ }
+
+ public short getKey(short index) {
+ short len = length();
+ if (index >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ return Util.getShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index * 4)));
+ }
+
+ public short getKeyValue(short index) {
+ short len = length();
+ if (index >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ return Util.getShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index * 4 + 2)));
+ }
+
+ public void swap(short index1, short index2) {
+ short len = length();
+ if (index1 >= len || index2 >= len) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ // Swap keys
+ short indexPtr1 =
+ Util.getShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index1 * 4)));
+ short indexPtr2 =
+ Util.getShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index2 * 4)));
+ Util.setShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index1 * 4)),
+ indexPtr2);
+ Util.setShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index2 * 4)),
+ indexPtr1);
+
+ // Swap Values
+ indexPtr1 =
+ Util.getShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index1 * 4 + 2)));
+ indexPtr2 =
+ Util.getShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index2 * 4 + 2)));
+ Util.setShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index1 * 4 + 2)),
+ indexPtr2);
+ Util.setShort(
+ heap,
+ (short)
+ (instanceTable[KM_MAP_OFFSET]
+ + TLV_HEADER_SIZE
+ + MAP_HEADER_SIZE
+ + (short) (index2 * 4 + 2)),
+ indexPtr1);
+ }
+
+ public void canonicalize() {
+ KMCoseMap.canonicalize(instanceTable[KM_MAP_OFFSET], length());
+ }
+
+ public short containedType() {
+ return Util.getShort(heap, (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short getStartOff() {
+ return (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE);
+ }
+
+ public short length() {
+ return Util.getShort(heap, (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public byte[] getBuffer() {
+ return heap;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMNInteger.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMNInteger.java
new file mode 100644
index 0000000..3a6404e
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMNInteger.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * Represents 8-bit, 16-bit, 32-bit and 64-bit signed integer. It corresponds to CBOR int type.
+ * struct{byte NEG_INTEGER_TYPE; short length; 4 or 8 bytes of value}
+ */
+public class KMNInteger extends KMInteger {
+
+ public static final byte SIGNED_MASK = (byte) 0x80;
+ private static KMNInteger prototype;
+
+ private KMNInteger() {}
+
+ private static KMNInteger proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMNInteger();
+ }
+ instanceTable[KM_NEG_INTEGER_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short exp() {
+ return KMType.exp(NEG_INTEGER_TYPE);
+ }
+
+ // return an empty integer instance
+ public static short instance(short length) {
+ if ((length <= 0) || (length > 8)) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ if (length > 4) {
+ length = KMInteger.UINT_64;
+ } else {
+ length = KMInteger.UINT_32;
+ }
+ return KMType.instance(NEG_INTEGER_TYPE, length);
+ }
+
+ public static short instance(byte[] num, short srcOff, short length) {
+ if (length > 8) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ if (length == 1) {
+ return uint_8(num[srcOff]);
+ } else if (length == 2) {
+ return uint_16(Util.getShort(num, srcOff));
+ } else if (length == 4) {
+ return uint_32(num, srcOff);
+ } else {
+ return uint_64(num, srcOff);
+ }
+ }
+
+ public static KMNInteger cast(short ptr) {
+ byte[] heap = repository.getHeap();
+ if (heap[ptr] != NEG_INTEGER_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ // create integer and copy byte value
+ public static short uint_8(byte num) {
+ if (num >= 0) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(KMInteger.UINT_32);
+ heap[(short) (ptr + TLV_HEADER_SIZE + 3)] = num;
+ return ptr;
+ }
+
+ // create integer and copy short value
+ public static short uint_16(short num) {
+ if (num >= 0) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(KMInteger.UINT_32);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), num);
+ return ptr;
+ }
+
+ // create integer and copy integer value
+ public static short uint_32(byte[] num, short offset) {
+ if (!isSignedInteger(num, offset)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(KMInteger.UINT_32);
+ Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), KMInteger.UINT_32);
+ return ptr;
+ }
+
+ // create integer and copy integer value
+ public static short uint_64(byte[] num, short offset) {
+ if (!isSignedInteger(num, offset)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = instance(KMInteger.UINT_64);
+ Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), KMInteger.UINT_64);
+ return ptr;
+ }
+
+ public static boolean isSignedInteger(byte[] num, short offset) {
+ byte val = num[offset];
+ return SIGNED_MASK == (val & SIGNED_MASK);
+ }
+
+ @Override
+ protected short getBaseOffset() {
+ return instanceTable[KM_NEG_INTEGER_OFFSET];
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMOperationState.java
new file mode 100644
index 0000000..2a53acd
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMOperationState.java
@@ -0,0 +1,354 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import com.android.javacard.seprovider.KMOperation;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * KMOperationState is the container of an active operation started by beginOperation function. This
+ * operation state is persisted by the applet in non volatile memory. However, this state is not
+ * retained if applet is upgraded. There will be four operation state records maintained i.e. only
+ * four active operations are supported at any given time.
+ */
+public class KMOperationState {
+
+ // sizes
+ public static final byte OPERATION_HANDLE_SIZE = 8;
+ public static final byte DATA_SIZE = 11;
+ public static final byte AUTH_TIME_SIZE = 8;
+ // Secure user ids 5 * 8 = 40 bytes ( Considering Maximum 5 SECURE USER IDs)
+ // First two bytes are reserved to store number of secure ids. So total 42 bytes.
+ public static final byte USER_SECURE_IDS_SIZE = 42;
+ // byte type
+ private static final byte ALG = 0;
+ private static final byte PURPOSE = 1;
+ private static final byte PADDING = 2;
+ private static final byte BLOCK_MODE = 3;
+ private static final byte DIGEST = 4;
+ private static final byte FLAGS = 5;
+ private static final byte KEY_SIZE = 6;
+ private static final byte MAC_LENGTH = 7;
+ private static final byte MGF_DIGEST = 8;
+ private static final byte AUTH_TYPE = 9;
+ private static final byte MIN_MAC_LENGTH = 10;
+ private static final byte OPERATION = 0;
+ private static final byte HMAC_SIGNER_OPERATION = 1;
+ // Flag masks
+ private static final byte AUTH_PER_OP_REQD = 1;
+ private static final byte SECURE_USER_ID_REQD = 2;
+ private static final byte AUTH_TIMEOUT_VALIDATED = 4;
+ private static final byte AES_GCM_UPDATE_ALLOWED = 8;
+ private static final byte PROCESSED_INPUT_MSG = 16;
+ // Max user secure ids.
+ private static final byte MAX_SECURE_USER_IDS = 5;
+
+ // Object References
+ private byte[] opHandle;
+ private byte[] authTime;
+ private byte[] userSecureIds;
+ private short[] data;
+ private Object[] operations;
+
+ public KMOperationState() {
+ opHandle = JCSystem.makeTransientByteArray(OPERATION_HANDLE_SIZE, JCSystem.CLEAR_ON_RESET);
+ authTime = JCSystem.makeTransientByteArray(AUTH_TIME_SIZE, JCSystem.CLEAR_ON_RESET);
+ data = JCSystem.makeTransientShortArray(DATA_SIZE, JCSystem.CLEAR_ON_RESET);
+ operations = JCSystem.makeTransientObjectArray((short) 2, JCSystem.CLEAR_ON_RESET);
+ userSecureIds = JCSystem.makeTransientByteArray(USER_SECURE_IDS_SIZE, JCSystem.CLEAR_ON_RESET);
+ reset();
+ }
+
+ public void reset() {
+ byte index = 0;
+ while (index < DATA_SIZE) {
+ data[index] = KMType.INVALID_VALUE;
+ index++;
+ }
+ Util.arrayFillNonAtomic(opHandle, (short) 0, OPERATION_HANDLE_SIZE, (byte) 0);
+ Util.arrayFillNonAtomic(authTime, (short) 0, AUTH_TIME_SIZE, (byte) 0);
+
+ if (null != operations[OPERATION]) {
+ ((KMOperation) operations[OPERATION]).abort();
+ }
+ operations[OPERATION] = null;
+
+ if (null != operations[HMAC_SIGNER_OPERATION]) {
+ ((KMOperation) operations[HMAC_SIGNER_OPERATION]).abort();
+ }
+ operations[HMAC_SIGNER_OPERATION] = null;
+ }
+
+ public short compare(byte[] handle, short start, short len) {
+ return Util.arrayCompare(handle, start, opHandle, (short) 0, (short) opHandle.length);
+ }
+
+ public short getKeySize() {
+ return data[KEY_SIZE];
+ }
+
+ public void setKeySize(short keySize) {
+ data[KEY_SIZE] = keySize;
+ }
+
+ public short getHandle() {
+ return KMInteger.uint_64(opHandle, (short) 0);
+ }
+
+ public void setHandle(byte[] buf, short start, short len) {
+ Util.arrayCopyNonAtomic(buf, start, opHandle, (short) 0, (short) opHandle.length);
+ }
+
+ public short getPurpose() {
+ return data[PURPOSE];
+ }
+
+ public void setPurpose(short purpose) {
+ data[PURPOSE] = purpose;
+ }
+
+ public boolean isInputMsgProcessed() {
+ return (data[FLAGS] & PROCESSED_INPUT_MSG) != 0;
+ }
+
+ public KMOperation getOperation() {
+ return (KMOperation) operations[OPERATION];
+ }
+
+ public void setOperation(KMOperation op) {
+ operations[OPERATION] = op;
+ }
+
+ public boolean isAuthPerOperationReqd() {
+ return (data[FLAGS] & AUTH_PER_OP_REQD) != 0;
+ }
+
+ public void setAuthPerOperationReqd(boolean flag) {
+ if (flag) {
+ data[FLAGS] = (short) (data[FLAGS] | AUTH_PER_OP_REQD);
+ } else {
+ data[FLAGS] = (short) (data[FLAGS] & (~AUTH_PER_OP_REQD));
+ }
+ }
+
+ public boolean isAuthTimeoutValidated() {
+ return (data[FLAGS] & AUTH_TIMEOUT_VALIDATED) != 0;
+ }
+
+ public void setAuthTimeoutValidated(boolean flag) {
+ if (flag) {
+ data[FLAGS] = (byte) (data[FLAGS] | AUTH_TIMEOUT_VALIDATED);
+ } else {
+ data[FLAGS] = (byte) (data[FLAGS] & (~AUTH_TIMEOUT_VALIDATED));
+ }
+ }
+
+ public boolean isSecureUserIdReqd() {
+ return (data[FLAGS] & SECURE_USER_ID_REQD) != 0;
+ }
+
+ public short getAuthTime() {
+ return KMInteger.uint_64(authTime, (short) 0);
+ }
+
+ public void setAuthTime(byte[] timeBuf, short start) {
+ Util.arrayCopyNonAtomic(timeBuf, start, authTime, (short) 0, AUTH_TIME_SIZE);
+ }
+
+ public void setProcessedInputMsg(boolean flag) {
+ if (flag) {
+ data[FLAGS] = (byte) (data[FLAGS] | PROCESSED_INPUT_MSG);
+ } else {
+ data[FLAGS] = (byte) (data[FLAGS] & (~PROCESSED_INPUT_MSG));
+ }
+ }
+
+ public void setOneTimeAuthReqd(boolean flag) {
+ if (flag) {
+ data[FLAGS] = (short) (data[FLAGS] | SECURE_USER_ID_REQD);
+ } else {
+ data[FLAGS] = (short) (data[FLAGS] & (~SECURE_USER_ID_REQD));
+ }
+ }
+
+ public short getAuthType() {
+ return data[AUTH_TYPE];
+ }
+
+ public void setAuthType(byte authType) {
+ data[AUTH_TYPE] = authType;
+ }
+
+ public short getUserSecureId() {
+ short offset = 0;
+ short length = Util.getShort(userSecureIds, offset);
+ offset += 2;
+ if (length == 0) {
+ return KMType.INVALID_VALUE;
+ }
+ short arrObj = KMArray.instance(length);
+ short index = 0;
+ short obj;
+ while (index < length) {
+ obj = KMInteger.instance(userSecureIds, (short) (offset + index * 8), (short) 8);
+ KMArray.cast(arrObj).add(index, obj);
+ index++;
+ }
+ return KMIntegerArrayTag.instance(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, arrObj);
+ }
+
+ public void setUserSecureId(short integerArrayPtr) {
+ short length = KMIntegerArrayTag.cast(integerArrayPtr).length();
+ if (length > MAX_SECURE_USER_IDS) {
+ KMException.throwIt(KMError.INVALID_KEY_BLOB);
+ }
+ Util.arrayFillNonAtomic(userSecureIds, (short) 0, USER_SECURE_IDS_SIZE, (byte) 0);
+ short index = 0;
+ short obj;
+ short offset = 0;
+ offset = Util.setShort(userSecureIds, offset, length);
+ while (index < length) {
+ obj = KMIntegerArrayTag.cast(integerArrayPtr).get(index);
+ Util.arrayCopyNonAtomic(
+ KMInteger.cast(obj).getBuffer(),
+ KMInteger.cast(obj).getStartOff(),
+ userSecureIds,
+ (short) (8 - KMInteger.cast(obj).length() + offset + 8 * index),
+ KMInteger.cast(obj).length());
+ index++;
+ }
+ }
+
+ public short getAlgorithm() {
+ return data[ALG];
+ }
+
+ public void setAlgorithm(short algorithm) {
+ data[ALG] = algorithm;
+ }
+
+ public short getPadding() {
+ return data[PADDING];
+ }
+
+ public void setPadding(short padding) {
+ data[PADDING] = padding;
+ }
+
+ public short getBlockMode() {
+ return data[BLOCK_MODE];
+ }
+
+ public void setBlockMode(short blockMode) {
+ data[BLOCK_MODE] = blockMode;
+ }
+
+ public short getDigest() {
+ return data[DIGEST];
+ }
+
+ public void setDigest(byte digest) {
+ data[DIGEST] = digest;
+ }
+
+ public short getMgfDigest() {
+ return data[MGF_DIGEST];
+ }
+
+ public void setMgfDigest(byte mgfDigest) {
+ data[MGF_DIGEST] = mgfDigest;
+ }
+
+ public boolean isAesGcmUpdateAllowed() {
+ return (data[FLAGS] & AES_GCM_UPDATE_ALLOWED) != 0;
+ }
+
+ public void setAesGcmUpdateComplete() {
+ data[FLAGS] = (byte) (data[FLAGS] & (~AES_GCM_UPDATE_ALLOWED));
+ }
+
+ public void setAesGcmUpdateStart() {
+ data[FLAGS] = (byte) (data[FLAGS] | AES_GCM_UPDATE_ALLOWED);
+ }
+
+ public short getMinMacLength() {
+ return data[MIN_MAC_LENGTH];
+ }
+
+ public void setMinMacLength(short length) {
+ data[MIN_MAC_LENGTH] = length;
+ }
+
+ public short getMacLength() {
+ return data[MAC_LENGTH];
+ }
+
+ public void setMacLength(short length) {
+ data[MAC_LENGTH] = length;
+ }
+
+ public byte getBufferingMode() {
+ short alg = getAlgorithm();
+ short purpose = getPurpose();
+ short digest = getDigest();
+ short padding = getPadding();
+ short blockMode = getBlockMode();
+
+ if (alg == KMType.RSA
+ && ((digest == KMType.DIGEST_NONE && purpose == KMType.SIGN)
+ || purpose == KMType.DECRYPT)) {
+ return KMType.BUF_RSA_DECRYPT_OR_NO_DIGEST;
+ }
+
+ if (alg == KMType.EC && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN) {
+ return KMType.BUF_EC_NO_DIGEST;
+ }
+
+ switch (alg) {
+ case KMType.AES:
+ if (purpose == KMType.ENCRYPT && padding == KMType.PKCS7) {
+ return KMType.BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGN;
+ } else if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) {
+ return KMType.BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGN;
+ } else if (purpose == KMType.DECRYPT && blockMode == KMType.GCM) {
+ return KMType.BUF_AES_GCM_DECRYPT_BLOCK_ALIGN;
+ }
+ break;
+ case KMType.DES:
+ if (purpose == KMType.ENCRYPT && padding == KMType.PKCS7) {
+ return KMType.BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGN;
+ } else if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) {
+ return KMType.BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGN;
+ }
+ }
+ return KMType.BUF_NONE;
+ }
+
+ public KMOperation getTrustedConfirmationSigner() {
+ return (KMOperation) operations[HMAC_SIGNER_OPERATION];
+ }
+
+ public void setTrustedConfirmationSigner(KMOperation hmacSignerOp) {
+ operations[HMAC_SIGNER_OPERATION] = hmacSignerOp;
+ }
+
+ public boolean isTrustedConfirmationRequired() {
+ return operations[HMAC_SIGNER_OPERATION] != null;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMRemotelyProvisionedComponentDevice.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMRemotelyProvisionedComponentDevice.java
new file mode 100644
index 0000000..d69534d
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMRemotelyProvisionedComponentDevice.java
@@ -0,0 +1,1396 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import com.android.javacard.seprovider.KMKey;
+import com.android.javacard.seprovider.KMOperation;
+import com.android.javacard.seprovider.KMSEProvider;
+import javacard.framework.APDU;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * This class handles remote key provisioning. Generates an RKP key and generates a certificate
+ * signing request(CSR). The generation of CSR is divided among multiple functions to the save the
+ * memory inside the Applet. The set of functions to be called sequentially in the order to complete
+ * the process of generating the CSR are processBeginSendData, processUpdateKey,
+ * processUpdateEekChain, processUpdateChallenge, processFinishSendData, and getResponse.
+ * ProcessUpdateKey is called Ntimes, where N is the number of keys. Similarly, getResponse is
+ * called multiple times till the client receives the response completely.
+ */
+public class KMRemotelyProvisionedComponentDevice {
+
+ // Below are the device info labels
+ // The string "brand" in hex
+ public static final byte[] BRAND = {0x62, 0x72, 0x61, 0x6E, 0x64};
+ // The string "manufacturer" in hex
+ public static final byte[] MANUFACTURER = {
+ 0x6D, 0x61, 0x6E, 0x75, 0x66, 0x61, 0x63, 0x74, 0x75, 0x72, 0x65, 0x72
+ };
+ // The string "product" in hex
+ public static final byte[] PRODUCT = {0x70, 0x72, 0x6F, 0x64, 0x75, 0x63, 0x74};
+ // The string "model" in hex
+ public static final byte[] MODEL = {0x6D, 0x6F, 0x64, 0x65, 0x6C};
+ // // The string "device" in hex
+ public static final byte[] DEVICE = {0x64, 0x65, 0x76, 0x69, 0x63, 0x65};
+ // The string "vb_state" in hex
+ public static final byte[] VB_STATE = {0x76, 0x62, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x65};
+ // The string "bootloader_state" in hex.
+ public static final byte[] BOOTLOADER_STATE = {
+ 0x62, 0x6F, 0x6F, 0x74, 0x6C, 0x6F, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x65
+ };
+ // The string "vb_meta_digest" in hdex.
+ public static final byte[] VB_META_DIGEST = {
+ 0X76, 0X62, 0X6D, 0X65, 0X74, 0X61, 0X5F, 0X64, 0X69, 0X67, 0X65, 0X73, 0X74
+ };
+ // The string "os_version" in hex.
+ public static final byte[] OS_VERSION = {
+ 0x6F, 0x73, 0x5F, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E
+ };
+ // The string "system_patch_level" in hex.
+ public static final byte[] SYSTEM_PATCH_LEVEL = {
+ 0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, 0x76,
+ 0x65, 0x6C
+ };
+ // The string "boot_patch_level" in hex.
+ public static final byte[] BOOT_PATCH_LEVEL = {
+ 0x62, 0x6F, 0x6F, 0x74, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, 0x76, 0x65, 0x6C
+ };
+ // The string "vendor_patch_level" in hex.
+ public static final byte[] VENDOR_PATCH_LEVEL = {
+ 0x76, 0x65, 0x6E, 0x64, 0x6F, 0x72, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, 0x76,
+ 0x65, 0x6C
+ };
+ // The string "version" in hex.
+ public static final byte[] DEVICE_INFO_VERSION = {0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E};
+ // The string "security_level" in hex.
+ public static final byte[] SECURITY_LEVEL = {
+ 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5F, 0x6C, 0x65, 0x76, 0x65, 0x6C
+ };
+ // The string "fused" in hex.
+ public static final byte[] FUSED = {0x66, 0x75, 0x73, 0x65, 0x64};
+ // The string "cert_type" in hex
+ public static final byte[] CERT_TYPE = {0x63, 0x65, 0x72, 0x74, 0x5F, 0x74, 0x79, 0x70, 0x65};
+ // Below are the Verified boot state values
+ // The string "green" in hex.
+ public static final byte[] VB_STATE_GREEN = {0x67, 0x72, 0x65, 0x65, 0x6E};
+ // The string "yellow" in hex.
+ public static final byte[] VB_STATE_YELLOW = {0x79, 0x65, 0x6C, 0x6C, 0x6F, 0x77};
+ // The string "orange" in hex.
+ public static final byte[] VB_STATE_ORANGE = {0x6F, 0x72, 0x61, 0x6E, 0x67, 0x65};
+ // The string "red" in hex.
+ public static final byte[] VB_STATE_RED = {0x72, 0x65, 0x64};
+ // Below are the boot loader state values
+ // The string "unlocked" in hex.
+ public static final byte[] UNLOCKED = {0x75, 0x6E, 0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64};
+ // The string "locked" in hex.
+ public static final byte[] LOCKED = {0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64};
+ // Device info CDDL schema version
+ public static final byte DI_SCHEMA_VERSION = 2;
+ // The string "strongbox" in hex.
+ public static final byte[] DI_SECURITY_LEVEL = {
+ 0x73, 0x74, 0x72, 0x6F, 0x6E, 0x67, 0x62, 0x6F, 0x78
+ };
+ // The string "keymint" in hex.
+ public static final byte[] DI_CERT_TYPE = {0x6B, 0x65, 0x79, 0x6D, 0x69, 0x6E, 0x74};
+ // Represents each element size inside the data buffer. Each element has two entries
+ // 1) Length of the element and 2) offset of the element in the data buffer.
+ public static final byte DATA_INDEX_ENTRY_SIZE = 4;
+ // It is the offset, which represents the position where the element is present
+ // in the data buffer.
+ public static final byte DATA_INDEX_ENTRY_OFFSET = 2;
+ // Flag to denote TRUE
+ private static final byte TRUE = 0x01;
+ // Flag to denote FALSE
+ private static final byte FALSE = 0x00;
+ // RKP hardware info Version
+ private static final byte RKP_VERSION = 0x03;
+ // RKP supportedNumKeysInCsr
+ private static final byte MIN_SUPPORTED_NUM_KEYS_IN_CSR = 20;
+ // The CsrPayload CDDL Schema version.
+ private static final byte CSR_PAYLOAD_CDDL_SCHEMA_VERSION = 3;
+ // Boot params
+ // Below constants used to denote the type of the boot parameters. Note that these
+ // constants are only defined to be used internally.
+ private static final byte OS_VERSION_ID = 0x00;
+ private static final byte SYSTEM_PATCH_LEVEL_ID = 0x01;
+ private static final byte BOOT_PATCH_LEVEL_ID = 0x02;
+ private static final byte VENDOR_PATCH_LEVEL_ID = 0x03;
+ // Configurable flag to denote if UDS certificate chain is supported in the
+ // RKP server.
+ private static final boolean IS_UDS_SUPPORTED_IN_RKP_SERVER = true;
+ // Denotes COSE Integer lengths less than or equal to 23.
+ private static final byte TINY_PAYLOAD = 0x17;
+ // Denotes COSE Integer with short lengths.
+ private static final short SHORT_PAYLOAD = 0x100;
+ // The maximum possible output buffer.
+ private static final short MAX_SEND_DATA = 512;
+ // The string "Google Strongbox KeyMint 3" in hex.
+ private static final byte[] uniqueId = {
+ 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x53, 0x74, 0x72, 0x6f, 0x6e, 0x67, 0x62, 0x6f, 0x78,
+ 0x20, 0x4b, 0x65, 0x79, 0x4d, 0x69, 0x6e, 0x74, 0x20, 0x33
+ };
+ // Flag to denote more response is available to the clients.
+ private static final byte MORE_DATA = 0x01;
+ // Flag to denote no response is available to the clients.
+ private static final byte NO_DATA = 0x00;
+ // Below are the response processing states.
+ private static final byte START_PROCESSING = 0x00;
+ private static final byte PROCESSING_DICE_CERTS_IN_PROGRESS = 0x02;
+ private static final byte PROCESSING_DICE_CERTS_COMPLETE = 0x04;
+ private static final byte PROCESSING_UDS_CERTS_IN_PROGRESS = 0x08;
+ private static final byte PROCESSING_UDS_CERTS_COMPLETE = 0x0A;
+ // The data table size.
+ private static final short DATA_SIZE = 512;
+ // Number of entries in the data table.
+ private static final byte DATA_INDEX_SIZE = 6;
+ // Below are the data table offsets.
+ private static final byte TOTAL_KEYS_TO_SIGN = 0;
+ private static final byte KEYS_TO_SIGN_COUNT = 1;
+ private static final byte GENERATE_CSR_PHASE = 2;
+ private static final byte RESPONSE_PROCESSING_STATE = 3;
+ private static final byte UDS_PROCESSED_LENGTH = 4;
+ private static final byte DICE_PROCESSED_LENGTH = 5;
+
+ // Below are some of the sizes defined in the data table.
+ // The size of the Ephemeral Mac key used to sign the rkp public key.
+ private static final byte EPHEMERAL_MAC_KEY_SIZE = 32;
+ // The size of short types.
+ private static final byte SHORT_SIZE = 2;
+ // The size of byte types
+ private static final byte BYTE_SIZE = 1;
+ // Below are the different processing stages for generateCSR.
+ // BEGIN - It is the initial stage where the process is initialized and construction of
+ // MacedPublickeys are initiated.
+ // UPDATE - Challenge, EEK and RKP keys are sent to the applet for further process.
+ // FINISH - MacedPublicKeys are constructed and construction of protected data is initiated.
+ // GET_UDS_CERTS_RESPONSE - Constructed the UDSCerts in the response.
+ private static final byte BEGIN = 0x01;
+ private static final byte UPDATE = 0x02;
+ private static final byte FINISH = 0x04;
+ private static final byte GET_UDS_CERTS_RESPONSE = 0x06;
+
+ // RKP mac key size
+ private static final byte RKP_MAC_KEY_SIZE = 32;
+ // RKP CDDL Schema version
+ private static final byte RKP_AUTHENTICATE_CDDL_SCHEMA_VERSION = 1;
+ // The maximum size of the encoded buffer size.
+ private static final short MAX_ENCODED_BUF_SIZE = 1024;
+ // Used to hold the temporary results.
+ public short[] rkpTmpVariables;
+ // Data table to hold the entries at the initial stages of generateCSR and which are used
+ // at later stages to construct the response data.
+ private byte[] data;
+ // Instance of the CBOR encoder.
+ private KMEncoder encoder;
+ // Instance of the CBOR decoder.
+ private KMDecoder decoder;
+ // Instance of the KMRepository for memory management.
+ private KMRepository repository;
+ // Instance of the provider for cyrpto operations.
+ private KMSEProvider seProvider;
+ // Instance of the KMKeymintDataStore to save or retrieve the data.
+ private KMKeymintDataStore storeDataInst;
+ // Holds the KMOperation instance. This is used to do multi part update operations.
+ private Object[] operation;
+ // Holds the current index in the data table.
+ private short[] dataIndex;
+
+ public KMRemotelyProvisionedComponentDevice(
+ KMEncoder encoder,
+ KMDecoder decoder,
+ KMRepository repository,
+ KMSEProvider seProvider,
+ KMKeymintDataStore storeDInst) {
+ this.encoder = encoder;
+ this.decoder = decoder;
+ this.repository = repository;
+ this.seProvider = seProvider;
+ this.storeDataInst = storeDInst;
+ rkpTmpVariables = JCSystem.makeTransientShortArray((short) 32, JCSystem.CLEAR_ON_RESET);
+ data = JCSystem.makeTransientByteArray(DATA_SIZE, JCSystem.CLEAR_ON_RESET);
+ operation = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET);
+ dataIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET);
+ // Initialize RKP mac key
+ if (!seProvider.isUpgrading()) {
+ short offset = repository.allocReclaimableMemory((short) RKP_MAC_KEY_SIZE);
+ byte[] buffer = repository.getHeap();
+ seProvider.getTrueRandomNumber(buffer, offset, RKP_MAC_KEY_SIZE);
+ storeDataInst.createRkpMacKey(buffer, offset, RKP_MAC_KEY_SIZE);
+ repository.reclaimMemory(RKP_MAC_KEY_SIZE);
+ }
+ operation[0] = null;
+ }
+
+ private void initializeDataTable() {
+ clearDataTable();
+ releaseOperation();
+ dataIndex[0] = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE);
+ }
+
+ private short dataAlloc(short length) {
+ if ((short) (dataIndex[0] + length) > (short) data.length) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ dataIndex[0] += length;
+ return (short) (dataIndex[0] - length);
+ }
+
+ private void clearDataTable() {
+ Util.arrayFillNonAtomic(data, (short) 0, (short) data.length, (byte) 0x00);
+ dataIndex[0] = 0x00;
+ }
+
+ private void releaseOperation() {
+ if (operation[0] != null) {
+ ((KMOperation) operation[0]).abort();
+ operation[0] = null;
+ }
+ }
+
+ private short createEntry(short index, short length) {
+ index = (short) (index * DATA_INDEX_ENTRY_SIZE);
+ short ptr = dataAlloc(length);
+ Util.setShort(data, index, length);
+ Util.setShort(data, (short) (index + DATA_INDEX_ENTRY_OFFSET), ptr);
+ return ptr;
+ }
+
+ private short getEntry(short index) {
+ index = (short) (index * DATA_INDEX_ENTRY_SIZE);
+ return Util.getShort(data, (short) (index + DATA_INDEX_ENTRY_OFFSET));
+ }
+
+ private void processGetRkpHwInfoCmd(APDU apdu) {
+ // Make the response
+ // Author name - Google.
+ short respPtr = KMArray.instance((short) 6);
+ KMArray resp = KMArray.cast(respPtr);
+ resp.add((short) 0, KMInteger.uint_16(KMError.OK));
+ resp.add((short) 1, KMInteger.uint_16(RKP_VERSION));
+ resp.add(
+ (short) 2,
+ KMByteBlob.instance(
+ KMKeymasterApplet.Google, (short) 0, (short) KMKeymasterApplet.Google.length));
+ // This field is no longer used in version 3
+ resp.add((short) 3, KMInteger.uint_8(KMType.RKP_CURVE_NONE));
+ resp.add((short) 4, KMByteBlob.instance(uniqueId, (short) 0, (short) uniqueId.length));
+ resp.add((short) 5, KMInteger.uint_16(MIN_SUPPORTED_NUM_KEYS_IN_CSR));
+ KMKeymasterApplet.sendOutgoing(apdu, respPtr);
+ }
+
+ /**
+ * This function generates an EC key pair with attest key as purpose and creates an encrypted key
+ * blob. It then generates a COSEMac message which includes the ECDSA public key.
+ */
+ public void processGenerateRkpKey(APDU apdu) {
+ short arr = KMArray.instance((short) 1);
+ KMArray.cast(arr).add((short) 0, KMSimpleValue.exp());
+ arr = KMKeymasterApplet.receiveIncoming(apdu, arr);
+ // Re-purpose the apdu buffer as scratch pad.
+ byte[] scratchPad = apdu.getBuffer();
+ // test mode flag.
+ boolean testMode =
+ (KMSimpleValue.TRUE == KMSimpleValue.cast(KMArray.cast(arr).get((short) 0)).getValue());
+ KMKeymasterApplet.generateRkpKey(scratchPad, getEcAttestKeyParameters());
+ short pubKey = KMKeymasterApplet.getPubKey();
+ short coseMac0 = constructCoseMacForRkpKey(testMode, scratchPad, pubKey);
+ // Encode the COSE_MAC0 object
+ arr = KMArray.instance((short) 3);
+ KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(arr).add((short) 1, coseMac0);
+ KMArray.cast(arr).add((short) 2, KMKeymasterApplet.getPivateKey());
+ KMKeymasterApplet.sendOutgoing(apdu, arr);
+ }
+
+ public short getHeaderLen(short length) {
+ if (length <= TINY_PAYLOAD) {
+ return (short) 1;
+ } else if (length < SHORT_PAYLOAD) {
+ return (short) 2;
+ } else {
+ return (short) 3;
+ }
+ }
+
+ public void constructPartialSignedData(
+ byte[] scratchPad,
+ short coseKeysCount,
+ short totalCoseKeysLen,
+ short challengeByteBlob,
+ short deviceInfo,
+ short versionPtr,
+ short certTypePtr) {
+ // Initialize ECDSA operation
+ initECDSAOperation();
+
+ short versionLength = encoder.getEncodedLength(versionPtr);
+ short certTypeLen = encoder.getEncodedLength(certTypePtr);
+ short challengeLen = (short) KMByteBlob.cast(challengeByteBlob).length();
+ if (challengeLen < 16 || challengeLen > 64) {
+ KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+ }
+ short challengeHeaderLen = encoder.getEncodedBytesLength(challengeLen);
+ short deviceInfoLen = encoder.getEncodedLength(deviceInfo);
+
+ // Calculate the keysToSign length
+ // keysToSignLen = coseKeysArrayHeaderLen + totalCoseKeysLen
+ short coseKeysArrHeaderLen = getHeaderLen(coseKeysCount);
+ short keysToSignLen = (short) (coseKeysArrHeaderLen + totalCoseKeysLen);
+
+ // Calculate the payload array header len
+ /*
+ * paylaodArrHeaderLen is Array of 2 elements that occupies 1 byte.
+ * SignedData = [challenge, AuthenticatedRequest<CsrPayload>]
+ */
+ short paylaodArrHeaderLen = 1;
+ /*
+ * csrPaylaodArrHeaderLen is Array of 4 elements that occupies 1 byte.
+ * CsrPayload = [version: 3, CertificateType, DeviceInfo, KeysToSign]
+ */
+ short csrPaylaodArrHeaderLen = 1;
+ short csrPayloadLen =
+ (short)
+ (csrPaylaodArrHeaderLen + versionLength + certTypeLen + deviceInfoLen + keysToSignLen);
+ short csrPaylaodByteHeaderLen = encoder.getEncodedBytesLength(csrPayloadLen);
+ short payloadLen =
+ (short)
+ (paylaodArrHeaderLen
+ + challengeHeaderLen
+ + challengeLen
+ + csrPaylaodByteHeaderLen
+ + csrPaylaodArrHeaderLen
+ + versionLength
+ + certTypeLen
+ + deviceInfoLen
+ + keysToSignLen);
+
+ // Empty aad
+ short aad = KMByteBlob.instance(scratchPad, (short) 0, (short) 0);
+
+ /* construct protected header */
+ short protectedHeaders =
+ KMCose.constructHeaders(
+ rkpTmpVariables,
+ KMNInteger.uint_8(KMCose.COSE_ALG_ES256),
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE);
+ protectedHeaders =
+ KMKeymasterApplet.encodeToApduBuffer(
+ protectedHeaders, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ protectedHeaders = KMByteBlob.instance(scratchPad, (short) 0, protectedHeaders);
+
+ // Construct partial signature
+ short signStructure =
+ KMCose.constructCoseSignStructure(protectedHeaders, aad, KMType.INVALID_VALUE);
+ short partialSignStructureLen =
+ KMKeymasterApplet.encodeToApduBuffer(
+ signStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ ((KMOperation) operation[0]).update(scratchPad, (short) 0, partialSignStructureLen);
+
+ // Add payload Byte Header
+ short prevReclaimIndex = repository.getHeapReclaimIndex();
+ byte[] heap = repository.getHeap();
+ short heapIndex = repository.allocReclaimableMemory(MAX_ENCODED_BUF_SIZE);
+ short byteBlobHeaderLen = encoder.encodeByteBlobHeader(payloadLen, heap, heapIndex, (short) 3);
+ ((KMOperation) operation[0]).update(heap, heapIndex, byteBlobHeaderLen);
+
+ short arr = KMArray.instance((short) 2);
+ KMArray.cast(arr).add((short) 0, challengeByteBlob);
+ KMArray.cast(arr).add((short) 1, KMType.INVALID_VALUE);
+ short payloadArrayLen = encoder.encode(arr, heap, heapIndex, prevReclaimIndex);
+ ((KMOperation) operation[0]).update(heap, heapIndex, payloadArrayLen);
+
+ byteBlobHeaderLen = encoder.encodeByteBlobHeader(csrPayloadLen, heap, heapIndex, (short) 3);
+ ((KMOperation) operation[0]).update(heap, heapIndex, byteBlobHeaderLen);
+
+ // Construct partial csr payload array
+ arr = KMArray.instance((short) 4);
+ KMArray.cast(arr).add((short) 0, versionPtr);
+ KMArray.cast(arr).add((short) 1, certTypePtr);
+ KMArray.cast(arr).add((short) 2, deviceInfo);
+ KMArray.cast(arr).add((short) 3, KMType.INVALID_VALUE);
+ short partialCsrPayloadArrayLen = encoder.encode(arr, heap, heapIndex, prevReclaimIndex);
+ ((KMOperation) operation[0]).update(heap, heapIndex, partialCsrPayloadArrayLen);
+
+ // Encode keysToSign Array Header length
+ short keysToSignArrayHeaderLen =
+ encoder.encodeArrayHeader(coseKeysCount, heap, heapIndex, (short) 3);
+ ((KMOperation) operation[0]).update(heap, heapIndex, keysToSignArrayHeaderLen);
+ repository.reclaimMemory(MAX_ENCODED_BUF_SIZE);
+ }
+
+ /**
+ * This is the first command of the generateCSR.
+ * Input:
+ * 1) Number of RKP keys.
+ * 2) Total size of the encoded CoseKeys (Each RKP key is represented in CoseKey)
+ * 3) Challenge
+ * Process:
+ * 1) creates device info, initializes version and cert type.
+ * 2) Initialize the ECDSA operation with the deviceUniqueKeyPair and do partial sign of the
+ * CsrPayload with the initial input data received. A Multipart update on ECDSA is
+ * called on each updateKey command in the second stage.
+ * 3) Store the number of RKP keys in the temporary data buffer.
+ * 4) Update the phase of the generateCSR function to BEGIN.
+ * 5) generates RKP device info
+ * Response:
+ * 1) Send OK response.
+ * 2) Encoded device info
+ * 3) CSR payload CDDL schema version
+ * 4) Cert type
+ *
+ * @param apdu Input apdu
+ */
+ public void processBeginSendData(APDU apdu) throws Exception {
+ try {
+ initializeDataTable();
+ short arr = KMArray.instance((short) 3);
+ KMArray.cast(arr).add((short) 0, KMInteger.exp()); // Array length
+ KMArray.cast(arr).add((short) 1, KMInteger.exp()); // Total length of the encoded CoseKeys.
+ KMArray.cast(arr).add((short) 2, KMByteBlob.exp()); // challenge
+ arr = KMKeymasterApplet.receiveIncoming(apdu, arr);
+ // Re-purpose the apdu buffer as scratch pad.
+ byte[] scratchPad = apdu.getBuffer();
+ short deviceInfo = createDeviceInfo(scratchPad);
+ short versionPtr = KMInteger.uint_16(CSR_PAYLOAD_CDDL_SCHEMA_VERSION);
+ short certTypePtr =
+ KMTextString.instance(DI_CERT_TYPE, (short) 0, (short) DI_CERT_TYPE.length);
+
+ constructPartialSignedData(
+ scratchPad,
+ KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort(),
+ KMInteger.cast(KMArray.cast(arr).get((short) 1)).getShort(),
+ KMArray.cast(arr).get((short) 2),
+ deviceInfo,
+ versionPtr,
+ certTypePtr);
+ // Store the total keys in data table.
+ short dataEntryIndex = createEntry(TOTAL_KEYS_TO_SIGN, SHORT_SIZE);
+ Util.setShort(
+ data, dataEntryIndex, KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort());
+ // Store the current csr status, which is BEGIN.
+ createEntry(GENERATE_CSR_PHASE, BYTE_SIZE);
+ updateState(BEGIN);
+ if (0 == KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort()) {
+ updateState(UPDATE);
+ }
+ short prevReclaimIndex = repository.getHeapReclaimIndex();
+ short offset = repository.allocReclaimableMemory(MAX_ENCODED_BUF_SIZE);
+ short length =
+ encoder.encode(
+ deviceInfo, repository.getHeap(), offset, prevReclaimIndex, MAX_ENCODED_BUF_SIZE);
+ short encodedDeviceInfo = KMByteBlob.instance(repository.getHeap(), offset, length);
+ // release memory
+ repository.reclaimMemory(MAX_ENCODED_BUF_SIZE);
+ // Send response.
+ short array = KMArray.instance((short) 4);
+ KMArray.cast(array).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(array).add((short) 1, encodedDeviceInfo);
+ KMArray.cast(array).add((short) 2, versionPtr);
+ KMArray.cast(array).add((short) 3, certTypePtr);
+ KMKeymasterApplet.sendOutgoing(apdu, array);
+ } catch (Exception e) {
+ clearDataTable();
+ releaseOperation();
+ throw e;
+ }
+ }
+
+ /**
+ * This is the second command of the generateCSR. This command will be called in a loop
+ * for the number of keys.
+ * Input:
+ * CoseMac0 containing the RKP Key
+ * Process:
+ * 1) Validate the phase of generateCSR. Prior state should be either BEGIN or UPDATE.
+ * 2) Validate the number of RKP Keys received against the value received in the first command.
+ * 3) Validate the CoseMac0 structure and extract the RKP Key.
+ * 4) Do Multipart ECDSA update operation with the input as RKP key.
+ * 5) Update the number of keys received count into the data buffer.
+ * 6) Update the phase of the generateCSR function to UPDATE.
+ * Response:
+ * 1) Send OK response.
+ * 2) encoded Cose Key
+ * @param apdu Input apdu
+ */
+ public void processUpdateKey(APDU apdu) throws Exception {
+ try {
+ // The prior state can be BEGIN or UPDATE
+ validateState((byte) (BEGIN | UPDATE));
+ validateKeysToSignCount();
+ short headers = KMCoseHeaders.exp();
+ short arrInst = KMArray.instance((short) 4);
+ short byteBlobExp = KMByteBlob.exp();
+ KMArray.cast(arrInst).add((short) 0, byteBlobExp);
+ KMArray.cast(arrInst).add((short) 1, headers);
+ KMArray.cast(arrInst).add((short) 2, byteBlobExp);
+ KMArray.cast(arrInst).add((short) 3, byteBlobExp);
+ short arr = KMArray.exp(arrInst);
+ arr = KMKeymasterApplet.receiveIncoming(apdu, arr);
+ arrInst = KMArray.cast(arr).get((short) 0);
+ // Re-purpose the apdu buffer as scratch pad.
+ byte[] scratchPad = apdu.getBuffer();
+
+ // Validate and extract the CoseKey from CoseMac0 message.
+ short coseKey = validateAndExtractPublicKey(arrInst, scratchPad);
+ // Encode CoseKey
+ short length =
+ KMKeymasterApplet.encodeToApduBuffer(
+ coseKey, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ // Do ECDSA update with input as encoded CoseKey.
+ ((KMOperation) operation[0]).update(scratchPad, (short) 0, length);
+ short encodedCoseKey = KMByteBlob.instance(scratchPad, (short) 0, length);
+
+ // Increment the count each time this function gets executed.
+ // Store the count in data table.
+ short dataEntryIndex = getEntry(KEYS_TO_SIGN_COUNT);
+ if (dataEntryIndex == 0) {
+ dataEntryIndex = createEntry(KEYS_TO_SIGN_COUNT, SHORT_SIZE);
+ }
+ length = Util.getShort(data, dataEntryIndex);
+ Util.setShort(data, dataEntryIndex, ++length);
+ // Update the csr state
+ updateState(UPDATE);
+ // Send response.
+ short array = KMArray.instance((short) 2);
+ KMArray.cast(array).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(array).add((short) 1, encodedCoseKey);
+ KMKeymasterApplet.sendOutgoing(apdu, array);
+ } catch (Exception e) {
+ clearDataTable();
+ releaseOperation();
+ throw e;
+ }
+ }
+
+ /**
+ * This is the third command of generateCSR.
+ * Input:
+ * No input data.
+ * Process:
+ * 1) Validate the phase of generateCSR. Prior state should be UPDATE.
+ * 2) Check if all the RKP keys are received, if not, throw exception.
+ * 3) Finalize the ECDSA operation and get the signature, signature of SignedDataSigStruct
+ * where SignedDataSigStruct is
+ * [context: "Signature1",
+ * protected: bstr .cbor {1 : AlgorithmEdDSA / AlgorithmES256},
+ * external_aad: bstr .size 0,
+ * payload: bstr .cbor [challenge,
+ * bstr .cbor CsrPayload = [version, CertificateType, DeviceInfo, KeysToSign]
+ * ]
+ * ]
+ * 4) Construct protected header data.
+ * 5) Update the phase of the generateCSR function to FINISH.
+ * Response:
+ * OK
+ * protectedHeader - SignedData protected header
+ * Signature of SignedDataSigStruct
+ * Version - The AuthenticatedRequest CDDL Schema version.
+ * Flag to represent there is more data to retrieve.
+ * @param apdu Input apdu.
+ */
+ public void processFinishSendData(APDU apdu) throws Exception {
+ try {
+ // The prior state should be UPDATE.
+ validateState(UPDATE);
+ byte[] scratchPad = apdu.getBuffer();
+ if (data[getEntry(TOTAL_KEYS_TO_SIGN)] != data[getEntry(KEYS_TO_SIGN_COUNT)]) {
+ // Mismatch in the number of keys sent.
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ // PubKeysToSignMac
+ short empty = repository.alloc((short) 0);
+ short len =
+ ((KMOperation) operation[0])
+ .sign(repository.getHeap(), (short) empty, (short) 0, scratchPad, (short) 0);
+ releaseOperation();
+ short signatureData = KMByteBlob.instance(scratchPad, (short) 0, len);
+ len = KMAsn1Parser.instance().decodeEcdsa256Signature(signatureData, scratchPad, (short) 0);
+
+ signatureData = KMByteBlob.instance(scratchPad, (short) 0, len);
+
+ /* construct protected header */
+ short protectedHeaders =
+ KMCose.constructHeaders(
+ rkpTmpVariables,
+ KMNInteger.uint_8(KMCose.COSE_ALG_ES256),
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE);
+ protectedHeaders =
+ KMKeymasterApplet.encodeToApduBuffer(
+ protectedHeaders, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ protectedHeaders = KMByteBlob.instance(scratchPad, (short) 0, protectedHeaders);
+
+ updateState(FINISH);
+ short arr = KMArray.instance((short) 5);
+ KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(arr).add((short) 1, protectedHeaders);
+ KMArray.cast(arr).add((short) 2, signatureData);
+ KMArray.cast(arr).add((short) 3, KMInteger.uint_8(RKP_AUTHENTICATE_CDDL_SCHEMA_VERSION));
+ KMArray.cast(arr).add((short) 4, KMInteger.uint_8(MORE_DATA));
+ KMKeymasterApplet.sendOutgoing(apdu, arr);
+ } catch (Exception e) {
+ clearDataTable();
+ releaseOperation();
+ throw e;
+ }
+ }
+
+ /**
+ * This is the fourth command of generateCSR. This command is called multiple times by the
+ * HAL until complete UdsCerts are received. On each call, a chunk of 512 bytes data is sent.
+ * Input:
+ * No input data.
+ * Process:
+ * 1) Validate the phase of generateCSR. Prior state should be FINISH.
+ * 2) checks if Uds cert is present and sends the certs in chunks of 512 bytes.
+ * 3) Update the phase of the generateCSR function to GET_UDS_CERTS_RESPONSE. In-case of
+ * a) No Uds certs present and
+ * b) When last chunk of Uds cert is sent
+ * Response:
+ * OK
+ * Uds cert data
+ * Flag to represent there is more data to retrieve.
+ * @param apdu Input apdu.
+ */
+ public void processGetUdsCerts(APDU apdu) throws Exception {
+ try {
+ // The prior state should be FINISH.
+ validateState((byte) (FINISH));
+ short len;
+ byte moreData;
+ byte[] scratchPad = apdu.getBuffer();
+ if (!isUdsCertsChainPresent()) {
+ createEntry(RESPONSE_PROCESSING_STATE, BYTE_SIZE);
+ updateState(GET_UDS_CERTS_RESPONSE);
+ moreData = NO_DATA;
+ scratchPad[0] = (byte) 0xA0; // CBOR Encoded empty map is A0
+ len = 1;
+ } else {
+ len = processUdsCertificateChain(scratchPad);
+ moreData = MORE_DATA;
+ byte state = getCurrentOutputProcessingState();
+ switch (state) {
+ case PROCESSING_UDS_CERTS_IN_PROGRESS:
+ moreData = MORE_DATA;
+ break;
+ case PROCESSING_UDS_CERTS_COMPLETE:
+ updateState(GET_UDS_CERTS_RESPONSE);
+ moreData = NO_DATA;
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_STATE);
+ }
+ }
+ short data = KMByteBlob.instance(scratchPad, (short) 0, len);
+ short arr = KMArray.instance((short) 3);
+ KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(arr).add((short) 1, data);
+ // represents there is more output to retrieve
+ KMArray.cast(arr).add((short) 2, KMInteger.uint_8(moreData));
+ KMKeymasterApplet.sendOutgoing(apdu, arr);
+ } catch (Exception e) {
+ clearDataTable();
+ throw e;
+ }
+ }
+
+ /**
+ * This is the fifth command of generateCSR. This command is called multiple times by the
+ * HAL until complete Dice cert chain is received. On each call, a chunk of 512 bytes data is sent.
+ * Input:
+ * No input data.
+ * Process:
+ * 1) Validate the phase of generateCSR. Prior state should be GET_UDS_CERTS_RESPONSE.
+ * 2) Sends the Dice cert chain data in chunks of 512 bytes.
+ *
+ * After receiving a complete dice cert chain in HAL, Hal constructs the final CSR using the output data
+ * returned from all the 5 generateCSR commands in Applet.
+ *
+ * Response:
+ * OK
+ * Dice cert chain data
+ * Flag to represent there is more data to retrieve.
+ * @param apdu Input apdu.
+ */
+ public void processGetDiceCertChain(APDU apdu) throws Exception {
+ try {
+ // The prior state should be GET_UDS_CERTS_RESPONSE.
+ validateState((byte) (GET_UDS_CERTS_RESPONSE));
+ byte[] scratchPad = apdu.getBuffer();
+ short len = 0;
+ len = processDiceCertChain(scratchPad);
+ byte moreData = MORE_DATA;
+ byte state = getCurrentOutputProcessingState();
+ switch (state) {
+ case PROCESSING_DICE_CERTS_IN_PROGRESS:
+ moreData = MORE_DATA;
+ break;
+ case PROCESSING_DICE_CERTS_COMPLETE:
+ moreData = NO_DATA;
+ clearDataTable();
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_STATE);
+ }
+ short data = KMByteBlob.instance(scratchPad, (short) 0, len);
+ short arr = KMArray.instance((short) 3);
+ KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK));
+ KMArray.cast(arr).add((short) 1, data);
+ // represents there is more output to retrieve
+ KMArray.cast(arr).add((short) 2, KMInteger.uint_8(moreData));
+ KMKeymasterApplet.sendOutgoing(apdu, arr);
+ } catch (Exception e) {
+ clearDataTable();
+ throw e;
+ }
+ }
+
+ private boolean isUdsCertsChainPresent() {
+ if (!IS_UDS_SUPPORTED_IN_RKP_SERVER || (storeDataInst.getUdsCertChainLength() == 0)) {
+ return false;
+ }
+ return true;
+ }
+
+ public void process(short ins, APDU apdu) throws Exception {
+ switch (ins) {
+ case KMKeymasterApplet.INS_GET_RKP_HARDWARE_INFO:
+ processGetRkpHwInfoCmd(apdu);
+ break;
+ case KMKeymasterApplet.INS_GENERATE_RKP_KEY_CMD:
+ processGenerateRkpKey(apdu);
+ break;
+ case KMKeymasterApplet.INS_BEGIN_SEND_DATA_CMD:
+ processBeginSendData(apdu);
+ break;
+ case KMKeymasterApplet.INS_UPDATE_KEY_CMD:
+ processUpdateKey(apdu);
+ break;
+ case KMKeymasterApplet.INS_FINISH_SEND_DATA_CMD:
+ processFinishSendData(apdu);
+ break;
+ case KMKeymasterApplet.INS_GET_UDS_CERTS_CMD:
+ processGetUdsCerts(apdu);
+ break;
+ case KMKeymasterApplet.INS_GET_DICE_CERT_CHAIN_CMD:
+ processGetDiceCertChain(apdu);
+ break;
+ default:
+ ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
+ }
+ }
+
+ private byte getCurrentOutputProcessingState() {
+ short index = getEntry(RESPONSE_PROCESSING_STATE);
+ if (index == 0) {
+ return START_PROCESSING;
+ }
+ return data[index];
+ }
+
+ private void updateOutputProcessingState(byte state) {
+ short dataEntryIndex = getEntry(RESPONSE_PROCESSING_STATE);
+ data[dataEntryIndex] = state;
+ }
+
+ /**
+ * Validates the CoseMac message and extracts the CoseKey from it.
+ *
+ * @param coseMacPtr CoseMac instance to be validated.
+ * @param scratchPad Scratch buffer used to store temp results.
+ * @return CoseKey instance.
+ */
+ private short validateAndExtractPublicKey(short coseMacPtr, byte[] scratchPad) {
+ // Version 3 removes the need to have testMode in function calls
+ boolean testMode = false;
+ // Exp for KMCoseHeaders
+ short coseHeadersExp = KMCoseHeaders.exp();
+ // Exp for coseky
+ short coseKeyExp = KMCoseKey.exp();
+
+ // validate protected Headers
+ short ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET);
+ ptr =
+ decoder.decode(
+ coseHeadersExp,
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length());
+
+ if (!KMCoseHeaders.cast(ptr)
+ .isDataValid(rkpTmpVariables, KMCose.COSE_ALG_HMAC_256, KMType.INVALID_VALUE)) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+
+ // Validate payload.
+ ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET);
+ ptr =
+ decoder.decode(
+ coseKeyExp,
+ KMByteBlob.cast(ptr).getBuffer(),
+ KMByteBlob.cast(ptr).getStartOff(),
+ KMByteBlob.cast(ptr).length());
+
+ if (!KMCoseKey.cast(ptr)
+ .isDataValid(
+ rkpTmpVariables,
+ KMCose.COSE_KEY_TYPE_EC2,
+ KMType.INVALID_VALUE,
+ KMCose.COSE_ALG_ES256,
+ KMCose.COSE_ECCURVE_256)) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+
+ boolean isTestKey = KMCoseKey.cast(ptr).isTestKey();
+ if (isTestKey && !testMode) {
+ KMException.throwIt(KMError.STATUS_TEST_KEY_IN_PRODUCTION_REQUEST);
+ } else if (!isTestKey && testMode) {
+ KMException.throwIt(KMError.STATUS_PRODUCTION_KEY_IN_TEST_REQUEST);
+ }
+
+ // Compute CoseMac Structure and compare the macs.
+ short macStructure =
+ KMCose.constructCoseMacStructure(
+ KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET),
+ KMByteBlob.instance((short) 0),
+ KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET));
+ short encodedLen =
+ KMKeymasterApplet.encodeToApduBuffer(
+ macStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+
+ short hmacLen =
+ rkpHmacSign(testMode, scratchPad, (short) 0, encodedLen, scratchPad, encodedLen);
+
+ if (hmacLen
+ != KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).length()) {
+ KMException.throwIt(KMError.STATUS_INVALID_MAC);
+ }
+
+ if (0
+ != Util.arrayCompare(
+ scratchPad,
+ encodedLen,
+ KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getBuffer(),
+ KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET))
+ .getStartOff(),
+ hmacLen)) {
+ KMException.throwIt(KMError.STATUS_INVALID_MAC);
+ }
+ return ptr;
+ }
+
+ private void validateKeysToSignCount() {
+ short index = getEntry(KEYS_TO_SIGN_COUNT);
+ short keysToSignCount = 0;
+ if (index != 0) {
+ keysToSignCount = Util.getShort(data, index);
+ }
+ if (Util.getShort(data, getEntry(TOTAL_KEYS_TO_SIGN)) <= keysToSignCount) {
+ // Mismatch in the number of keys sent.
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ }
+
+ private void validateState(byte expectedState) {
+ short dataEntryIndex = getEntry(GENERATE_CSR_PHASE);
+ if (0 == (data[dataEntryIndex] & expectedState)) {
+ KMException.throwIt(KMError.INVALID_STATE);
+ }
+ }
+
+ private void updateState(byte state) {
+ short dataEntryIndex = getEntry(GENERATE_CSR_PHASE);
+ if (dataEntryIndex == 0) {
+ KMException.throwIt(KMError.INVALID_STATE);
+ }
+ data[dataEntryIndex] = state;
+ }
+
+ /*
+ * Create DeviceInfo structure as specified in the RKPV3.0 specification.
+ */
+ private short createDeviceInfo(byte[] scratchpad) {
+ // Device Info Key Value pairs.
+ for (short i = 0; i < 32; i++) {
+ rkpTmpVariables[i] = KMType.INVALID_VALUE;
+ }
+ short dataOffset = 2;
+ rkpTmpVariables[0] = dataOffset;
+ rkpTmpVariables[1] = 0;
+ short metaOffset = 0;
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ BRAND,
+ getAttestationId(KMType.ATTESTATION_ID_BRAND, scratchpad));
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ MANUFACTURER,
+ getAttestationId(KMType.ATTESTATION_ID_MANUFACTURER, scratchpad));
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ PRODUCT,
+ getAttestationId(KMType.ATTESTATION_ID_PRODUCT, scratchpad));
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ MODEL,
+ getAttestationId(KMType.ATTESTATION_ID_MODEL, scratchpad));
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ DEVICE,
+ getAttestationId(KMType.ATTESTATION_ID_DEVICE, scratchpad));
+ updateItem(rkpTmpVariables, metaOffset, VB_STATE, getVbState());
+ updateItem(rkpTmpVariables, metaOffset, BOOTLOADER_STATE, getBootloaderState());
+ updateItem(rkpTmpVariables, metaOffset, VB_META_DIGEST, getVerifiedBootHash(scratchpad));
+ updateItem(rkpTmpVariables, metaOffset, OS_VERSION, getBootParams(OS_VERSION_ID, scratchpad));
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ SYSTEM_PATCH_LEVEL,
+ getBootParams(SYSTEM_PATCH_LEVEL_ID, scratchpad));
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ BOOT_PATCH_LEVEL,
+ getBootParams(BOOT_PATCH_LEVEL_ID, scratchpad));
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ VENDOR_PATCH_LEVEL,
+ getBootParams(VENDOR_PATCH_LEVEL_ID, scratchpad));
+ updateItem(
+ rkpTmpVariables,
+ metaOffset,
+ SECURITY_LEVEL,
+ KMTextString.instance(DI_SECURITY_LEVEL, (short) 0, (short) DI_SECURITY_LEVEL.length));
+ updateItem(rkpTmpVariables, metaOffset, FUSED, KMInteger.uint_8(storeDataInst.secureBootMode));
+ // Create device info map.
+ short map = KMMap.instance(rkpTmpVariables[1]);
+ short mapIndex = 0;
+ short index = 2;
+ while (index < (short) 32) {
+ if (rkpTmpVariables[index] != KMType.INVALID_VALUE) {
+ KMMap.cast(map)
+ .add(mapIndex++, rkpTmpVariables[index], rkpTmpVariables[(short) (index + 1)]);
+ }
+ index += 2;
+ }
+ KMMap.cast(map).canonicalize();
+ return map;
+ }
+
+ // Below 6 methods are helper methods to create device info structure.
+ // ----------------------------------------------------------------------------
+
+ /**
+ * Update the item inside the device info structure.
+ *
+ * @param deviceIds Device Info structure to be updated.
+ * @param metaOffset Out parameter meta information. Offset 0 is index and Offset 1 is length.
+ * @param item Key info to be updated.
+ * @param value value to be updated.
+ */
+ private void updateItem(short[] deviceIds, short metaOffset, byte[] item, short value) {
+ if (KMType.INVALID_VALUE != value) {
+ deviceIds[deviceIds[metaOffset]++] =
+ KMTextString.instance(item, (short) 0, (short) item.length);
+ deviceIds[deviceIds[metaOffset]++] = value;
+ deviceIds[(short) (metaOffset + 1)]++;
+ }
+ }
+
+ private short getAttestationId(short attestId, byte[] scratchpad) {
+ short attIdTagLen = storeDataInst.getAttestationId(attestId, scratchpad, (short) 0);
+ if (attIdTagLen == 0) {
+ KMException.throwIt(KMError.INVALID_STATE);
+ }
+ return KMTextString.instance(scratchpad, (short) 0, attIdTagLen);
+ }
+
+ private short getVerifiedBootHash(byte[] scratchPad) {
+ short len = storeDataInst.getVerifiedBootHash(scratchPad, (short) 0);
+ if (len == 0) {
+ KMException.throwIt(KMError.INVALID_STATE);
+ }
+ return KMByteBlob.instance(scratchPad, (short) 0, len);
+ }
+
+ private short getBootloaderState() {
+ short bootloaderState;
+ if (storeDataInst.isDeviceBootLocked()) {
+ bootloaderState = KMTextString.instance(LOCKED, (short) 0, (short) LOCKED.length);
+ } else {
+ bootloaderState = KMTextString.instance(UNLOCKED, (short) 0, (short) UNLOCKED.length);
+ }
+ return bootloaderState;
+ }
+
+ private short getVbState() {
+ short state = storeDataInst.getBootState();
+ short vbState = KMType.INVALID_VALUE;
+ if (state == KMType.VERIFIED_BOOT) {
+ vbState = KMTextString.instance(VB_STATE_GREEN, (short) 0, (short) VB_STATE_GREEN.length);
+ } else if (state == KMType.SELF_SIGNED_BOOT) {
+ vbState = KMTextString.instance(VB_STATE_YELLOW, (short) 0, (short) VB_STATE_YELLOW.length);
+ } else if (state == KMType.UNVERIFIED_BOOT) {
+ vbState = KMTextString.instance(VB_STATE_ORANGE, (short) 0, (short) VB_STATE_ORANGE.length);
+ } else if (state == KMType.FAILED_BOOT) {
+ vbState = KMTextString.instance(VB_STATE_RED, (short) 0, (short) VB_STATE_RED.length);
+ }
+ return vbState;
+ }
+
+ private short converIntegerToTextString(short intPtr, byte[] scratchPad) {
+ // Prepare Hex Values
+ short index = 1;
+ scratchPad[0] = 0x30; // Ascii 0
+ while (index < 10) {
+ scratchPad[index] = (byte) (scratchPad[(short) (index - 1)] + 1);
+ index++;
+ }
+ scratchPad[index++] = 0x41; // Ascii 'A'
+ while (index < 16) {
+ scratchPad[index] = (byte) (scratchPad[(short) (index - 1)] + 1);
+ index++;
+ }
+
+ short intLen = KMInteger.cast(intPtr).length();
+ short intOffset = KMInteger.cast(intPtr).getStartOff();
+ byte[] buf = repository.getHeap();
+ short tsPtr = KMTextString.instance((short) (intLen * 2));
+ short tsStartOff = KMTextString.cast(tsPtr).getStartOff();
+ index = 0;
+ byte nibble;
+ while (index < intLen) {
+ nibble = (byte) ((byte) (buf[intOffset] >> 4) & (byte) 0x0F);
+ buf[tsStartOff] = scratchPad[nibble];
+ nibble = (byte) (buf[intOffset] & 0x0F);
+ buf[(short) (tsStartOff + 1)] = scratchPad[nibble];
+ index++;
+ intOffset++;
+ tsStartOff += 2;
+ }
+ return tsPtr;
+ }
+
+ private short getBootParams(byte bootParam, byte[] scratchPad) {
+ short value = KMType.INVALID_VALUE;
+ switch (bootParam) {
+ case OS_VERSION_ID:
+ value = storeDataInst.getOsVersion();
+ break;
+ case SYSTEM_PATCH_LEVEL_ID:
+ value = storeDataInst.getOsPatch();
+ break;
+ case BOOT_PATCH_LEVEL_ID:
+ value = storeDataInst.getBootPatchLevel();
+ break;
+ case VENDOR_PATCH_LEVEL_ID:
+ value = storeDataInst.getVendorPatchLevel();
+ break;
+ default:
+ KMException.throwIt(KMError.INVALID_ARGUMENT);
+ }
+ // Convert Integer to Text String for OS_VERSION.
+ if (bootParam == OS_VERSION_ID) {
+ value = converIntegerToTextString(value, scratchPad);
+ }
+ return value;
+ }
+ // ----------------------------------------------------------------------------
+
+ // ----------------------------------------------------------------------------
+ private void initECDSAOperation() {
+ KMKey deviceUniqueKeyPair = storeDataInst.getRkpDeviceUniqueKeyPair();
+ operation[0] =
+ seProvider.getRkpOperation(
+ KMType.SIGN,
+ KMType.EC,
+ KMType.SHA2_256,
+ KMType.PADDING_NONE,
+ (byte) 0,
+ deviceUniqueKeyPair,
+ null,
+ (short) 0,
+ (short) 0,
+ (short) 0);
+ if (operation[0] == null) {
+ KMException.throwIt(KMError.STATUS_FAILED);
+ }
+ }
+
+ private short getResponseProcessedLength(short index) {
+ short dataEntryIndex = getEntry(index);
+ if (dataEntryIndex == 0) {
+ dataEntryIndex = createEntry(index, SHORT_SIZE);
+ Util.setShort(data, dataEntryIndex, (short) 0);
+ return (short) 0;
+ }
+ return Util.getShort(data, dataEntryIndex);
+ }
+
+ private void updateResponseProcessedLength(short index, short processedLen) {
+ short dataEntryIndex = getEntry(index);
+ Util.setShort(data, dataEntryIndex, processedLen);
+ }
+
+ private short processUdsCertificateChain(byte[] scratchPad) {
+ byte[] persistedData = storeDataInst.getUdsCertChain();
+ short totalUccLen = Util.getShort(persistedData, (short) 0);
+ createEntry(RESPONSE_PROCESSING_STATE, BYTE_SIZE);
+ if (totalUccLen == 0) {
+ // No Uds certificate chain present.
+ updateOutputProcessingState(PROCESSING_UDS_CERTS_COMPLETE);
+ return 0;
+ }
+ short processedLen = getResponseProcessedLength(UDS_PROCESSED_LENGTH);
+ short lengthToSend = (short) (totalUccLen - processedLen);
+ if (lengthToSend > MAX_SEND_DATA) {
+ lengthToSend = MAX_SEND_DATA;
+ }
+ Util.arrayCopyNonAtomic(
+ persistedData, (short) (2 + processedLen), scratchPad, (short) 0, lengthToSend);
+
+ processedLen += lengthToSend;
+ updateResponseProcessedLength(UDS_PROCESSED_LENGTH, processedLen);
+ // Update the output processing state.
+ updateOutputProcessingState(
+ (processedLen == totalUccLen)
+ ? PROCESSING_UDS_CERTS_COMPLETE
+ : PROCESSING_UDS_CERTS_IN_PROGRESS);
+ return lengthToSend;
+ }
+
+ // Dice cert chain for STRONGBOX has chain length of 2. So it can be returned in a single go.
+ private short processDiceCertChain(byte[] scratchPad) {
+ byte[] diceCertChain = storeDataInst.getDiceCertificateChain();
+ short totalDccLen = Util.getShort(diceCertChain, (short) 0);
+ if (totalDccLen == 0) {
+ // No Uds certificate chain present.
+ updateOutputProcessingState(PROCESSING_DICE_CERTS_COMPLETE);
+ return 0;
+ }
+ short processedLen = getResponseProcessedLength(DICE_PROCESSED_LENGTH);
+ short lengthToSend = (short) (totalDccLen - processedLen);
+ if (lengthToSend > MAX_SEND_DATA) {
+ lengthToSend = MAX_SEND_DATA;
+ }
+ Util.arrayCopyNonAtomic(
+ diceCertChain, (short) (2 + processedLen), scratchPad, (short) 0, lengthToSend);
+
+ processedLen += lengthToSend;
+ updateResponseProcessedLength(DICE_PROCESSED_LENGTH, processedLen);
+ // Update the output processing state.
+ updateOutputProcessingState(
+ (processedLen == totalDccLen)
+ ? PROCESSING_DICE_CERTS_COMPLETE
+ : PROCESSING_DICE_CERTS_IN_PROGRESS);
+ return lengthToSend;
+ }
+
+ private short constructCoseMacForRkpKey(boolean testMode, byte[] scratchPad, short pubKey) {
+ // prepare cosekey
+ short coseKey =
+ KMCose.constructCoseKey(
+ rkpTmpVariables,
+ KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2),
+ KMType.INVALID_VALUE,
+ KMNInteger.uint_8(KMCose.COSE_ALG_ES256),
+ KMInteger.uint_8(KMCose.COSE_ECCURVE_256),
+ KMByteBlob.cast(pubKey).getBuffer(),
+ KMByteBlob.cast(pubKey).getStartOff(),
+ KMByteBlob.cast(pubKey).length(),
+ KMType.INVALID_VALUE,
+ testMode);
+ // Encode the cose key and make it as payload.
+ short len =
+ KMKeymasterApplet.encodeToApduBuffer(
+ coseKey, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ short payload = KMByteBlob.instance(scratchPad, (short) 0, len);
+ // Prepare protected header, which is required to construct the COSE_MAC0
+ short headerPtr =
+ KMCose.constructHeaders(
+ rkpTmpVariables,
+ KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256),
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE,
+ KMType.INVALID_VALUE);
+ // Encode the protected header as byte blob.
+ len =
+ KMKeymasterApplet.encodeToApduBuffer(
+ headerPtr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ short protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, len);
+ // create MAC_Structure
+ short macStructure =
+ KMCose.constructCoseMacStructure(protectedHeader, KMByteBlob.instance((short) 0), payload);
+ // Encode the Mac_structure and do HMAC_Sign to produce the tag for COSE_MAC0
+ len =
+ KMKeymasterApplet.encodeToApduBuffer(
+ macStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ // HMAC Sign.
+ short hmacLen = rkpHmacSign(testMode, scratchPad, (short) 0, len, scratchPad, len);
+ // Create COSE_MAC0 object
+ short coseMac0 =
+ KMCose.constructCoseMac0(
+ protectedHeader,
+ KMCoseHeaders.instance(KMArray.instance((short) 0)),
+ payload,
+ KMByteBlob.instance(scratchPad, len, hmacLen));
+ len =
+ KMKeymasterApplet.encodeToApduBuffer(
+ coseMac0, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+ return KMByteBlob.instance(scratchPad, (short) 0, len);
+ }
+
+ private short getEcAttestKeyParameters() {
+ short tagIndex = 0;
+ short arrPtr = KMArray.instance((short) 6);
+ // Key size - 256
+ short keySize =
+ KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 256));
+ // Digest - SHA256
+ short byteBlob = KMByteBlob.instance((short) 1);
+ KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256);
+ short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob);
+ // Purpose - Attest
+ byteBlob = KMByteBlob.instance((short) 1);
+ KMByteBlob.cast(byteBlob).add((short) 0, KMType.ATTEST_KEY);
+ short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob);
+
+ KMArray.cast(arrPtr).add(tagIndex++, purpose);
+ // Algorithm - EC
+ KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC));
+ KMArray.cast(arrPtr).add(tagIndex++, keySize);
+ KMArray.cast(arrPtr).add(tagIndex++, digest);
+ // Curve - P256
+ KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ECCURVE, KMType.P_256));
+ // No Authentication is required to use this key.
+ KMArray.cast(arrPtr).add(tagIndex, KMBoolTag.instance(KMType.NO_AUTH_REQUIRED));
+ return KMKeyParameters.instance(arrPtr);
+ }
+
+ private boolean isSignedByte(byte b) {
+ return ((b & 0x0080) != 0);
+ }
+
+ private short writeIntegerHeader(short valueLen, byte[] data, short offset) {
+ // write length
+ data[offset] = (byte) valueLen;
+ // write INTEGER tag
+ offset--;
+ data[offset] = 0x02;
+ return offset;
+ }
+
+ private short writeSequenceHeader(short valueLen, byte[] data, short offset) {
+ // write length
+ data[offset] = (byte) valueLen;
+ // write INTEGER tag
+ offset--;
+ data[offset] = 0x30;
+ return offset;
+ }
+
+ private short writeSignatureData(
+ byte[] input, short inputOff, short inputlen, byte[] output, short offset) {
+ Util.arrayCopyNonAtomic(input, inputOff, output, offset, inputlen);
+ if (isSignedByte(input[inputOff])) {
+ offset--;
+ output[offset] = (byte) 0;
+ }
+ return offset;
+ }
+
+ public short encodeES256CoseSignSignature(
+ byte[] input, short offset, short len, byte[] scratchPad, short scratchPadOff) {
+ // SEQ [ INTEGER(r), INTEGER(s)]
+ // write from bottom to the top
+ if (len != 64) {
+ KMException.throwIt(KMError.INVALID_DATA);
+ }
+ short maxTotalLen = 72;
+ short end = (short) (scratchPadOff + maxTotalLen);
+ // write s.
+ short start = (short) (end - 32);
+ start = writeSignatureData(input, (short) (offset + 32), (short) 32, scratchPad, start);
+ // write length and header
+ short length = (short) (end - start);
+ start--;
+ start = writeIntegerHeader(length, scratchPad, start);
+ // write r
+ short rEnd = start;
+ start = (short) (start - 32);
+ start = writeSignatureData(input, offset, (short) 32, scratchPad, start);
+ // write length and header
+ length = (short) (rEnd - start);
+ start--;
+ start = writeIntegerHeader(length, scratchPad, start);
+ // write length and sequence header
+ length = (short) (end - start);
+ start--;
+ start = writeSequenceHeader(length, scratchPad, start);
+ length = (short) (end - start);
+ if (start > scratchPadOff) {
+ // re adjust the buffer
+ Util.arrayCopyNonAtomic(scratchPad, start, scratchPad, scratchPadOff, length);
+ }
+ return length;
+ }
+
+ private short rkpHmacSign(
+ boolean testMode,
+ byte[] data,
+ short dataStart,
+ short dataLength,
+ byte[] signature,
+ short signatureStart) {
+ short result;
+ if (testMode) {
+ short macKey = KMByteBlob.instance(EPHEMERAL_MAC_KEY_SIZE);
+ Util.arrayFillNonAtomic(
+ KMByteBlob.cast(macKey).getBuffer(),
+ KMByteBlob.cast(macKey).getStartOff(),
+ EPHEMERAL_MAC_KEY_SIZE,
+ (byte) 0);
+ result =
+ seProvider.hmacSign(
+ KMByteBlob.cast(macKey).getBuffer(),
+ KMByteBlob.cast(macKey).getStartOff(),
+ EPHEMERAL_MAC_KEY_SIZE,
+ data,
+ dataStart,
+ dataLength,
+ signature,
+ signatureStart);
+ } else {
+ result =
+ seProvider.hmacSign(
+ storeDataInst.getRkpMacKey(), data, dataStart, dataLength, signature, signatureStart);
+ }
+ return result;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMRepository.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMRepository.java
new file mode 100644
index 0000000..9fd2406
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMRepository.java
@@ -0,0 +1,130 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * KMRepository class manages volatile memory usage by the applet. Note the repository is only used
+ * by applet and it is not intended to be used by seProvider.
+ */
+public class KMRepository {
+
+ // The maximum available heap memory.
+ public static final short HEAP_SIZE = 10000;
+ // Index pointing from the back of heap.
+ private static short[] reclaimIndex;
+ // Singleton instance
+ private static KMRepository repository;
+ // Heap buffer
+ private byte[] heap;
+ // Index to the heap buffer.
+ private short[] heapIndex;
+
+ public KMRepository(boolean isUpgrading) {
+ heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET);
+ heapIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET);
+ reclaimIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET);
+ reclaimIndex[0] = HEAP_SIZE;
+ repository = this;
+ }
+
+ public static KMRepository instance() {
+ return repository;
+ }
+
+ public void onUninstall() {
+ // Javacard Runtime environment cleans up the data.
+
+ }
+
+ public void onProcess() {}
+
+ public void clean() {
+ Util.arrayFillNonAtomic(heap, (short) 0, HEAP_SIZE, (byte) 0);
+ heapIndex[0] = 0;
+ reclaimIndex[0] = HEAP_SIZE;
+ }
+
+ public void onDeselect() {}
+
+ public void onSelect() {
+ // If write through caching is implemented then this method will restore the data into cache
+ }
+
+ // This function uses memory from the back of the heap(transient memory). Call
+ // reclaimMemory function immediately after the use.
+ public short allocReclaimableMemory(short length) {
+ if ((((short) (reclaimIndex[0] - length)) <= heapIndex[0]) || (length >= HEAP_SIZE / 2)) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ reclaimIndex[0] -= length;
+ return reclaimIndex[0];
+ }
+
+ // Reclaims the memory back.
+ public void reclaimMemory(short length) {
+ if (reclaimIndex[0] < heapIndex[0]) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ Util.arrayFillNonAtomic(heap, reclaimIndex[0], length, (byte) 0);
+ reclaimIndex[0] += length;
+ }
+
+ public short allocAvailableMemory() {
+ if (heapIndex[0] >= heap.length) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short index = heapIndex[0];
+ heapIndex[0] = reclaimIndex[0];
+ return index;
+ }
+
+ public short alloc(short length) {
+ if ((((short) (heapIndex[0] + length)) > heap.length)
+ || (((short) (heapIndex[0] + length)) > reclaimIndex[0])) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ heapIndex[0] += length;
+ return (short) (heapIndex[0] - length);
+ }
+
+ public byte[] getHeap() {
+ return heap;
+ }
+
+ public short getHeapIndex() {
+ return heapIndex[0];
+ }
+
+ // Use this function to reset the heapIndex to its previous state.
+ // Some of the data might be lost so use it carefully.
+ public void setHeapIndex(short offset) {
+ if (offset > heapIndex[0] || offset < 0) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ Util.arrayFillNonAtomic(heap, offset, (short) (heapIndex[0] - offset), (byte) 0);
+ heapIndex[0] = offset;
+ }
+
+ public short getHeapReclaimIndex() {
+ return reclaimIndex[0];
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMSemanticTag.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMSemanticTag.java
new file mode 100644
index 0000000..07b2675
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMSemanticTag.java
@@ -0,0 +1,80 @@
+package com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMSemanticTag corresponds to CBOR type of tagged item. The structure is defined as struct{byte
+ * SEMANTIC_TAG_TYPE; short length; tag, short ptr }. Tag is INTEGER_TYPE and the possible values
+ * are defined here https://www.rfc-editor.org/rfc/rfc7049#section-2.4
+ */
+public class KMSemanticTag extends KMType {
+
+ public static final short COSE_MAC_SEMANTIC_TAG = (short) 0x0011;
+ public static final short ROT_SEMANTIC_TAG = (short) 0x9C41;
+ private static KMSemanticTag prototype;
+
+ private KMSemanticTag() {}
+
+ private static KMSemanticTag proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMSemanticTag();
+ }
+ instanceTable[KM_SEMANTIC_TAG_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp(short valuePtr) {
+ short ptr = KMType.instance(SEMANTIC_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMInteger.exp());
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+ return ptr;
+ }
+
+ public static KMSemanticTag cast(short ptr) {
+ if (heap[ptr] != SEMANTIC_TAG_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static short instance(short tag, short value) {
+ if (!isSemanticTagSupported(tag)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ // The maximum tag size can be UINT32. Currently, we support
+ // only two tags which are short.
+ short ptr = KMType.instance(SEMANTIC_TAG_TYPE, (short) 6);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), tag);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), value);
+ return ptr;
+ }
+
+ private static boolean isSemanticTagSupported(short tag) {
+ tag = KMInteger.cast(tag).getShort();
+ switch (tag) {
+ case COSE_MAC_SEMANTIC_TAG:
+ case ROT_SEMANTIC_TAG:
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ public short length() {
+ return Util.getShort(heap, (short) (instanceTable[KM_SEMANTIC_TAG_OFFSET] + 1));
+ }
+
+ public short getKeyPtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_SEMANTIC_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+ }
+
+ public short getValuePtr() {
+ return Util.getShort(
+ heap, (short) (instanceTable[KM_SEMANTIC_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMSimpleValue.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMSimpleValue.java
new file mode 100644
index 0000000..6dffd73
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMSimpleValue.java
@@ -0,0 +1,71 @@
+package com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMSimpleValue corresponds to CBOR type of Simple value. It holds either true, false or NULL
+ * values. The structure is defined as struct{byte SIMPLE_VALUE_TYPE; short length; simple value }
+ */
+public class KMSimpleValue extends KMType {
+
+ public static final byte FALSE = (byte) 20;
+ public static final byte TRUE = (byte) 21;
+ public static final byte NULL = (byte) 22;
+ private static KMSimpleValue prototype;
+
+ private KMSimpleValue() {}
+
+ private static KMSimpleValue proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMSimpleValue();
+ }
+ instanceTable[KM_SIMPLE_VALUE_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ return KMType.exp(SIMPLE_VALUE_TYPE);
+ }
+
+ public static KMSimpleValue cast(short ptr) {
+ if (heap[ptr] != SIMPLE_VALUE_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (!isSimpleValueValid(heap[(short) (ptr + 3)])) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public static short instance(byte value) {
+ if (!isSimpleValueValid(value)) {
+ ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+ }
+ short ptr = KMType.instance(SIMPLE_VALUE_TYPE, (short) 1);
+ heap[(short) (ptr + 3)] = value;
+ return ptr;
+ }
+
+ private static boolean isSimpleValueValid(byte value) {
+ switch (value) {
+ case TRUE:
+ case FALSE:
+ case NULL:
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+
+ public short length() {
+ return Util.getShort(heap, (short) (instanceTable[KM_SIMPLE_VALUE_OFFSET] + 1));
+ }
+
+ public byte getValue() {
+ return heap[(short) (instanceTable[KM_SIMPLE_VALUE_OFFSET] + 3)];
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMTag.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMTag.java
new file mode 100644
index 0000000..3033a70
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMTag.java
@@ -0,0 +1,102 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.Util;
+
+/**
+ * This class represents a tag as defined by keymaster hal specifications. It is composed of key
+ * value pair. The key consists of short tag type e.g. KMType.ENUM and short tag key e.g.
+ * KMType.ALGORITHM. The key is encoded as uint CBOR type with 4 bytes. This is followed by value
+ * which can be any CBOR type based on key. struct{byte tag=KMType.TAG_TYPE, short length, value)
+ * where value is subtype of KMTag i.e. struct{short tagType=one of tag types declared in KMType ,
+ * short tagKey=one of the tag keys declared in KMType, value} where value is one of the sub-types
+ * of KMType.
+ */
+public class KMTag extends KMType {
+
+ public static short getTagType(short ptr) {
+ return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ }
+
+ public static short getKey(short ptr) {
+ return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2));
+ }
+
+ public static void assertPresence(short params, short tagType, short tagKey, short error) {
+ if (!isPresent(params, tagType, tagKey)) {
+ KMException.throwIt(error);
+ }
+ }
+
+ public static void assertAbsence(short params, short tagType, short tagKey, short error) {
+ if (isPresent(params, tagType, tagKey)) {
+ KMException.throwIt(error);
+ }
+ }
+
+ public static boolean isPresent(short params, short tagType, short tagKey) {
+ short tag = KMKeyParameters.findTag(tagType, tagKey, params);
+ return tag != KMType.INVALID_VALUE;
+ }
+
+ public static boolean isEqual(short params, short tagType, short tagKey, short value) {
+ switch (tagType) {
+ case KMType.ENUM_TAG:
+ return KMEnumTag.getValue(tagKey, params) == value;
+ case KMType.UINT_TAG:
+ case KMType.DATE_TAG:
+ case KMType.ULONG_TAG:
+ return KMIntegerTag.isEqual(params, tagType, tagKey, value);
+ case KMType.ENUM_ARRAY_TAG:
+ return KMEnumArrayTag.contains(tagKey, value, params);
+ case KMType.UINT_ARRAY_TAG:
+ case KMType.ULONG_ARRAY_TAG:
+ return KMIntegerArrayTag.contains(tagKey, value, params);
+ }
+ return false;
+ }
+
+ public static void assertTrue(boolean condition, short error) {
+ if (!condition) {
+ KMException.throwIt(error);
+ }
+ }
+
+ public static boolean isValidPublicExponent(short params) {
+ short pubExp = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, params);
+ if (pubExp == KMType.INVALID_VALUE) {
+ return false;
+ }
+ pubExp = KMIntegerTag.cast(pubExp).getValue();
+ if (!(KMInteger.cast(pubExp).getShort() == 0x01
+ && KMInteger.cast(pubExp).getSignificantShort() == 0x01)) {
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean isValidKeySize(short params) {
+ short keysize = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, params);
+ if (keysize == KMType.INVALID_VALUE) {
+ return false;
+ }
+ short alg = KMEnumTag.getValue(KMType.ALGORITHM, params);
+ return KMIntegerTag.cast(keysize).isValidKeySize((byte) alg);
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMTextString.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMTextString.java
new file mode 100644
index 0000000..80aebd2
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMTextString.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright(C) 2021 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMTextString represents contiguous block of bytes. It corresponds to CBOR type of Text String. It
+ * extends KMByteBlob by specifying value field as zero or more sequence of bytes. struct{ byte
+ * TEXT_STR_TYPE; short length; sequence of bytes}
+ */
+public class KMTextString extends KMByteBlob {
+
+ private static byte OFFSET_SIZE = 2;
+
+ private static KMTextString prototype;
+
+ private KMTextString() {}
+
+ private static KMTextString proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMTextString();
+ }
+ instanceTable[KM_TEXT_STRING_OFFSET] = ptr;
+ return prototype;
+ }
+
+ // pointer to an empty instance used as expression
+ public static short exp() {
+ return KMType.exp(TEXT_STRING_TYPE);
+ }
+
+ // return an empty byte blob instance
+ public static short instance(short length) {
+ short ptr = KMType.instance(TEXT_STRING_TYPE, (short) (length + OFFSET_SIZE));
+ Util.setShort(
+ heap, (short) (ptr + TLV_HEADER_SIZE), (short) (ptr + TLV_HEADER_SIZE + OFFSET_SIZE));
+ Util.setShort(heap, (short) (ptr + 1), length);
+ return ptr;
+ }
+
+ // byte blob from existing buf
+ public static short instance(byte[] buf, short startOff, short length) {
+ short ptr = instance(length);
+ Util.arrayCopyNonAtomic(
+ buf, startOff, heap, (short) (ptr + TLV_HEADER_SIZE + OFFSET_SIZE), length);
+ return ptr;
+ }
+
+ // cast the ptr to KMTextString
+ public static KMTextString cast(short ptr) {
+ if (heap[ptr] != TEXT_STRING_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ protected short getBaseOffset() {
+ return instanceTable[KM_TEXT_STRING_OFFSET];
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMType.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMType.java
new file mode 100644
index 0000000..dc89604
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMType.java
@@ -0,0 +1,409 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * This class declares all types, tag types, and tag keys. It also establishes basic structure of
+ * any KMType i.e. struct{byte type, short length, value} where value can any of the KMType. Also,
+ * KMType refers to transient memory heap in the repository. Finally KMType's subtypes are singleton
+ * prototype objects which just cast the structure over contiguous memory buffer.
+ */
+public abstract class KMType {
+
+ public static final short INVALID_VALUE = (short) 0x8000;
+ // Types
+ public static final byte BYTE_BLOB_TYPE = 0x01;
+ public static final byte INTEGER_TYPE = 0x02;
+ public static final byte ENUM_TYPE = 0x03;
+ public static final byte TAG_TYPE = 0x04;
+ public static final byte ARRAY_TYPE = 0x05;
+ public static final byte KEY_PARAM_TYPE = 0x06;
+ public static final byte KEY_CHAR_TYPE = 0x07;
+ public static final byte HW_AUTH_TOKEN_TYPE = 0x08;
+ public static final byte VERIFICATION_TOKEN_TYPE = 0x09;
+ public static final byte HMAC_SHARING_PARAM_TYPE = 0x0A;
+ public static final byte X509_CERT = 0x0B;
+ public static final byte NEG_INTEGER_TYPE = 0x0C;
+ public static final byte TEXT_STRING_TYPE = 0x0D;
+ public static final byte MAP_TYPE = 0x0E;
+ public static final byte COSE_KEY_TYPE = 0x0F;
+ public static final byte COSE_PAIR_TAG_TYPE = 0x10;
+ public static final byte COSE_PAIR_INT_TAG_TYPE = 0x20;
+ public static final byte COSE_PAIR_NEG_INT_TAG_TYPE = 0x30;
+ public static final byte COSE_PAIR_BYTE_BLOB_TAG_TYPE = 0x40;
+ public static final byte COSE_PAIR_COSE_KEY_TAG_TYPE = 0x60;
+ public static final byte COSE_PAIR_SIMPLE_VALUE_TAG_TYPE = 0x70;
+ public static final byte COSE_PAIR_TEXT_STR_TAG_TYPE = (byte) 0x80;
+ public static final byte SIMPLE_VALUE_TYPE = (byte) 0x90;
+ public static final byte COSE_HEADERS_TYPE = (byte) 0xA0;
+ public static final byte COSE_CERT_PAYLOAD_TYPE = (byte) 0xB0;
+ public static final byte SEMANTIC_TAG_TYPE = (byte) 0xC0;
+ // Tag Types
+ public static final short INVALID_TAG = 0x0000;
+ public static final short ENUM_TAG = 0x1000;
+ public static final short ENUM_ARRAY_TAG = 0x2000;
+ public static final short UINT_TAG = 0x3000;
+ public static final short UINT_ARRAY_TAG = 0x4000;
+ public static final short ULONG_TAG = 0x5000;
+ public static final short DATE_TAG = 0x6000;
+ public static final short BOOL_TAG = 0x7000;
+ public static final short BIGNUM_TAG = (short) 0x8000;
+ public static final short BYTES_TAG = (short) 0x9000;
+ public static final short ULONG_ARRAY_TAG = (short) 0xA000;
+ public static final short TAG_TYPE_MASK = (short) 0xF000;
+
+ // Enum Tag
+ // Internal tags
+ public static final short RULE = 0x7FFF;
+ public static final byte IGNORE_INVALID_TAGS = 0x00;
+ public static final byte FAIL_ON_INVALID_TAGS = 0x01;
+
+ // Algorithm Enum Tag key and values
+ public static final short ALGORITHM = 0x0002;
+ public static final byte RSA = 0x01;
+ public static final byte DES = 0x21;
+ public static final byte EC = 0x03;
+ public static final byte AES = 0x20;
+ public static final byte HMAC = (byte) 0x80;
+
+ // EcCurve Enum Tag key and values.
+ public static final short ECCURVE = 0x000A;
+ public static final byte P_224 = 0x00;
+ public static final byte P_256 = 0x01;
+ public static final byte P_384 = 0x02;
+ public static final byte P_521 = 0x03;
+ public static final byte CURVE_25519 = 0x04;
+
+ // KeyBlobUsageRequirements Enum Tag key and values.
+ public static final short BLOB_USAGE_REQ = 0x012D;
+ public static final byte STANDALONE = 0x00;
+ public static final byte REQUIRES_FILE_SYSTEM = 0x01;
+
+ // HardwareAuthenticatorType Enum Tag key and values.
+ public static final short USER_AUTH_TYPE = 0x01F8;
+ public static final byte USER_AUTH_NONE = 0x00;
+ public static final byte PASSWORD = 0x01;
+ public static final byte FINGERPRINT = 0x02;
+ public static final byte BOTH = 0x03;
+ // have to be power of 2
+ public static final byte ANY = (byte) 0xFF;
+
+ // Origin Enum Tag key and values.
+ public static final short ORIGIN = 0x02BE;
+ public static final byte GENERATED = 0x00;
+ public static final byte DERIVED = 0x01;
+ public static final byte IMPORTED = 0x02;
+ public static final byte UNKNOWN = 0x03;
+ public static final byte SECURELY_IMPORTED = 0x04;
+
+ // Hardware Type tag key and values
+ public static final short HARDWARE_TYPE = 0x0130;
+ public static final byte SOFTWARE = 0x00;
+ public static final byte TRUSTED_ENVIRONMENT = 0x01;
+ public static final byte STRONGBOX = 0x02;
+
+ // No Tag
+ // Derivation Function - No Tag defined
+ public static final short KEY_DERIVATION_FUNCTION = (short) 0xF001;
+ public static final byte DERIVATION_NONE = 0x00;
+ public static final byte RFC5869_SHA256 = 0x01;
+ public static final byte ISO18033_2_KDF1_SHA1 = 0x02;
+ public static final byte ISO18033_2_KDF1_SHA256 = 0x03;
+ public static final byte ISO18033_2_KDF2_SHA1 = 0x04;
+ public static final byte ISO18033_2_KDF2_SHA256 = 0x05;
+
+ // KeyFormat - No Tag defined.
+ public static final short KEY_FORMAT = (short) 0xF002;
+ public static final byte X509 = 0x00;
+ public static final byte PKCS8 = 0x01;
+ public static final byte RAW = 0x03;
+
+ // Verified Boot State
+ public static final short VERIFIED_BOOT_STATE = (short) 0xF003;
+ public static final byte VERIFIED_BOOT = 0x00;
+ public static final byte SELF_SIGNED_BOOT = 0x01;
+ public static final byte UNVERIFIED_BOOT = 0x02;
+ public static final byte FAILED_BOOT = 0x03;
+
+ // Device Locked
+ public static final short DEVICE_LOCKED = (short) 0xF006;
+ public static final byte DEVICE_LOCKED_TRUE = 0x01;
+ public static final byte DEVICE_LOCKED_FALSE = 0x00;
+
+ // Enum Array Tag
+ // Purpose
+ public static final short PURPOSE = 0x0001;
+ public static final byte ENCRYPT = 0x00;
+ public static final byte DECRYPT = 0x01;
+ public static final byte SIGN = 0x02;
+ public static final byte VERIFY = 0x03;
+ public static final byte DERIVE_KEY = 0x04;
+ public static final byte WRAP_KEY = 0x05;
+ public static final byte AGREE_KEY = 0x06;
+ public static final byte ATTEST_KEY = (byte) 0x07;
+ // Block mode
+ public static final short BLOCK_MODE = 0x0004;
+ public static final byte ECB = 0x01;
+ public static final byte CBC = 0x02;
+ public static final byte CTR = 0x03;
+ public static final byte GCM = 0x20;
+
+ // Digest
+ public static final short DIGEST = 0x0005;
+ public static final byte DIGEST_NONE = 0x00;
+ public static final byte MD5 = 0x01;
+ public static final byte SHA1 = 0x02;
+ public static final byte SHA2_224 = 0x03;
+ public static final byte SHA2_256 = 0x04;
+ public static final byte SHA2_384 = 0x05;
+ public static final byte SHA2_512 = 0x06;
+
+ // Padding mode
+ public static final short PADDING = 0x0006;
+ public static final byte PADDING_NONE = 0x01;
+ public static final byte RSA_OAEP = 0x02;
+ public static final byte RSA_PSS = 0x03;
+ public static final byte RSA_PKCS1_1_5_ENCRYPT = 0x04;
+ public static final byte RSA_PKCS1_1_5_SIGN = 0x05;
+ public static final byte PKCS7 = 0x40;
+
+ // OAEP MGF Digests - only SHA-1 is supported in Javacard
+ public static final short RSA_OAEP_MGF_DIGEST = 0xCB;
+
+ // Integer Tag - UINT, ULONG and DATE
+ // UINT tags
+ // Keysize
+ public static final short KEYSIZE = 0x0003;
+ // Min Mac Length
+ public static final short MIN_MAC_LENGTH = 0x0008;
+ // Min Seconds between OPS
+ public static final short MIN_SEC_BETWEEN_OPS = 0x0193;
+ // Max Uses per Boot
+ public static final short MAX_USES_PER_BOOT = 0x0194;
+ // UserId
+ public static final short USERID = 0x01F5;
+ // Auth Timeout
+ public static final short AUTH_TIMEOUT = 0x01F9;
+ // Auth Timeout in Milliseconds
+ public static final short AUTH_TIMEOUT_MILLIS = 0x7FFF;
+ // OS Version
+ public static final short OS_VERSION = 0x02C1;
+ // OS Patch Level
+ public static final short OS_PATCH_LEVEL = 0x02C2;
+ // Vendor Patch Level
+ public static final short VENDOR_PATCH_LEVEL = 0x02CE;
+ // Boot Patch Level
+ public static final short BOOT_PATCH_LEVEL = 0x02CF;
+ // Mac Length
+ public static final short MAC_LENGTH = 0x03EB;
+ // Usage Count Limit
+ public static final short USAGE_COUNT_LIMIT = 0x195;
+
+ // ULONG tags
+ // RSA Public Exponent
+ public static final short RSA_PUBLIC_EXPONENT = 0x00C8;
+
+ // DATE tags
+ public static final short ACTIVE_DATETIME = 0x0190;
+ public static final short ORIGINATION_EXPIRE_DATETIME = 0x0191;
+ public static final short USAGE_EXPIRE_DATETIME = 0x0192;
+ public static final short CREATION_DATETIME = 0x02BD;
+ ;
+ public static final short CERTIFICATE_NOT_BEFORE = 0x03F0;
+ public static final short CERTIFICATE_NOT_AFTER = 0x03F1;
+ // Integer Array Tags - ULONG_REP and UINT_REP.
+ // User Secure Id
+ public static final short USER_SECURE_ID = (short) 0x01F6;
+
+ // Boolean Tag
+ // Caller Nonce
+ public static final short CALLER_NONCE = (short) 0x0007;
+ // Include Unique Id
+ public static final short INCLUDE_UNIQUE_ID = (short) 0x00CA;
+ // Bootloader Only
+ public static final short BOOTLOADER_ONLY = (short) 0x012E;
+ // Rollback Resistance
+ public static final short ROLLBACK_RESISTANCE = (short) 0x012F;
+ // No Auth Required
+ public static final short NO_AUTH_REQUIRED = (short) 0x01F7;
+ // Allow While On Body
+ public static final short ALLOW_WHILE_ON_BODY = (short) 0x01FA;
+ // Max Boot Level
+ public static final short MAX_BOOT_LEVEL = (short) 0x03F2;
+ // Trusted User Presence Required
+ public static final short TRUSTED_USER_PRESENCE_REQUIRED = (short) 0x01FB;
+ // Trusted Confirmation Required
+ public static final short TRUSTED_CONFIRMATION_REQUIRED = (short) 0x01FC;
+ // Unlocked Device Required
+ public static final short UNLOCKED_DEVICE_REQUIRED = (short) 0x01FD;
+ // Reset Since Id Rotation
+ public static final short RESET_SINCE_ID_ROTATION = (short) 0x03EC;
+ // Early boot ended.
+ public static final short EARLY_BOOT_ONLY = (short) 0x0131;
+ // Device unique attestation.
+ public static final short DEVICE_UNIQUE_ATTESTATION = (short) 0x02D0;
+
+ // Byte Tag
+ // Application Id
+ public static final short APPLICATION_ID = (short) 0x0259;
+ // Application Data
+ public static final short APPLICATION_DATA = (short) 0x02BC;
+ // Root Of Trust
+ public static final short ROOT_OF_TRUST = (short) 0x02C0;
+ // Unique Id
+ public static final short UNIQUE_ID = (short) 0x02C3;
+ // Attestation Challenge
+ public static final short ATTESTATION_CHALLENGE = (short) 0x02C4;
+ // Attestation Application Id
+ public static final short ATTESTATION_APPLICATION_ID = (short) 0x02C5;
+ // Attestation Id Brand
+ public static final short ATTESTATION_ID_BRAND = (short) 0x02C6;
+ // Attestation Id Device
+ public static final short ATTESTATION_ID_DEVICE = (short) 0x02C7;
+ // Attestation Id Product
+ public static final short ATTESTATION_ID_PRODUCT = (short) 0x02C8;
+ // Attestation Id Serial
+ public static final short ATTESTATION_ID_SERIAL = (short) 0x02C9;
+ // Attestation Id IMEI
+ public static final short ATTESTATION_ID_IMEI = (short) 0x02CA;
+ // Attestation Id SECOND IMEI
+ public static final short ATTESTATION_ID_SECOND_IMEI = (short) 0x02D3;
+ // Attestation Id MEID
+ public static final short ATTESTATION_ID_MEID = (short) 0x02CB;
+ // Attestation Id Manufacturer
+ public static final short ATTESTATION_ID_MANUFACTURER = (short) 0x02CC;
+ // Attestation Id Model
+ public static final short ATTESTATION_ID_MODEL = (short) 0x02CD;
+ // Associated Data
+ public static final short ASSOCIATED_DATA = (short) 0x03E8;
+ // Nonce
+ public static final short NONCE = (short) 0x03E9;
+ // Confirmation Token
+ public static final short CONFIRMATION_TOKEN = (short) 0x03ED;
+ // Serial Number - this is a big num but in applet we handle it as byte blob
+ public static final short CERTIFICATE_SERIAL_NUM = (short) 0x03EE;
+ // Subject Name
+ public static final short CERTIFICATE_SUBJECT_NAME = (short) 0x03EF;
+
+ public static final short LENGTH_FROM_PDU = (short) 0xFFFF;
+
+ public static final byte NO_VALUE = (byte) 0xff;
+ // Support Curves for Eek Chain validation.
+ public static final byte RKP_CURVE_NONE = 0;
+ // Type offsets.
+ public static final byte KM_TYPE_BASE_OFFSET = 0;
+ public static final byte KM_ARRAY_OFFSET = KM_TYPE_BASE_OFFSET;
+ public static final byte KM_BOOL_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 1;
+ public static final byte KM_BYTE_BLOB_OFFSET = KM_TYPE_BASE_OFFSET + 2;
+ public static final byte KM_BYTE_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 3;
+ public static final byte KM_ENUM_OFFSET = KM_TYPE_BASE_OFFSET + 4;
+ public static final byte KM_ENUM_ARRAY_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 5;
+ public static final byte KM_ENUM_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 6;
+ public static final byte KM_HARDWARE_AUTH_TOKEN_OFFSET = KM_TYPE_BASE_OFFSET + 7;
+ public static final byte KM_HMAC_SHARING_PARAMETERS_OFFSET = KM_TYPE_BASE_OFFSET + 8;
+ public static final byte KM_INTEGER_OFFSET = KM_TYPE_BASE_OFFSET + 9;
+ public static final byte KM_INTEGER_ARRAY_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 10;
+ public static final byte KM_INTEGER_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 11;
+ public static final byte KM_KEY_CHARACTERISTICS_OFFSET = KM_TYPE_BASE_OFFSET + 12;
+ public static final byte KM_KEY_PARAMETERS_OFFSET = KM_TYPE_BASE_OFFSET + 13;
+ public static final byte KM_VERIFICATION_TOKEN_OFFSET = KM_TYPE_BASE_OFFSET + 14;
+ public static final byte KM_NEG_INTEGER_OFFSET = KM_TYPE_BASE_OFFSET + 15;
+ public static final byte KM_TEXT_STRING_OFFSET = KM_TYPE_BASE_OFFSET + 16;
+ public static final byte KM_MAP_OFFSET = KM_TYPE_BASE_OFFSET + 17;
+ public static final byte KM_COSE_KEY_OFFSET = KM_TYPE_BASE_OFFSET + 18;
+ public static final byte KM_COSE_KEY_INT_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 19;
+ public static final byte KM_COSE_KEY_NINT_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 20;
+ public static final byte KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 21;
+ public static final byte KM_COSE_KEY_COSE_KEY_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 22;
+ public static final byte KM_COSE_KEY_SIMPLE_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 23;
+ public static final byte KM_SIMPLE_VALUE_OFFSET = KM_TYPE_BASE_OFFSET + 24;
+ public static final byte KM_COSE_HEADERS_OFFSET = KM_TYPE_BASE_OFFSET + 25;
+ public static final byte KM_COSE_KEY_TXT_STR_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 26;
+ public static final byte KM_COSE_CERT_PAYLOAD_OFFSET = KM_TYPE_BASE_OFFSET + 27;
+ public static final byte KM_BIGNUM_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 28;
+ public static final byte KM_SEMANTIC_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 29;
+
+ // Attestation types
+ public static final byte NO_CERT = 0;
+ public static final byte ATTESTATION_CERT = 1;
+ public static final byte SELF_SIGNED_CERT = 2;
+ public static final byte FAKE_CERT = 3;
+ // Buffering Mode
+ public static final byte BUF_NONE = 0;
+ public static final byte BUF_RSA_DECRYPT_OR_NO_DIGEST = 1;
+ public static final byte BUF_EC_NO_DIGEST = 2;
+ public static final byte BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGN = 3;
+ public static final byte BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGN = 4;
+ public static final byte BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGN = 5;
+ public static final byte BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGN = 6;
+ public static final byte BUF_AES_GCM_DECRYPT_BLOCK_ALIGN = 7;
+
+ // MAX ApplicationID or Application Data size
+ public static final byte MAX_APP_ID_APP_DATA_SIZE = 64;
+ // Max attestation challenge size.
+ public static final short MAX_ATTESTATION_CHALLENGE_SIZE = 128;
+ // Max certificate serial size.
+ public static final byte MAX_CERTIFICATE_SERIAL_SIZE = 20;
+ // Attestation Application ID
+ public static final short MAX_ATTESTATION_APP_ID_SIZE = 1024;
+ // Instance table
+ public static final byte INSTANCE_TABLE_SIZE = 30;
+ protected static final byte TLV_HEADER_SIZE = 3;
+ protected static KMRepository repository;
+ protected static byte[] heap;
+ protected static short[] instanceTable;
+
+ public static void initialize() {
+ instanceTable = JCSystem.makeTransientShortArray(INSTANCE_TABLE_SIZE, JCSystem.CLEAR_ON_RESET);
+ KMType.repository = KMRepository.instance();
+ KMType.heap = repository.getHeap();
+ }
+
+ public static byte getType(short ptr) {
+ return heap[ptr];
+ }
+
+ public static short length(short ptr) {
+ return Util.getShort(heap, (short) (ptr + 1));
+ }
+
+ public static short getValue(short ptr) {
+ return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ }
+
+ protected static short instance(byte type, short length) {
+ if (length < 0) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ short ptr = repository.alloc((short) (length + TLV_HEADER_SIZE));
+ heap[ptr] = type;
+ Util.setShort(heap, (short) (ptr + 1), length);
+ return ptr;
+ }
+
+ protected static short exp(byte type) {
+ short ptr = repository.alloc(TLV_HEADER_SIZE);
+ heap[ptr] = type;
+ Util.setShort(heap, (short) (ptr + 1), INVALID_VALUE);
+ return ptr;
+ }
+}
diff --git a/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java
new file mode 100644
index 0000000..590e73a
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java
@@ -0,0 +1,129 @@
+/*
+ * 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" 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.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMVerificationToken represents VerificationToken structure from android keymaster hal
+ * specifications. It corresponds to CBOR array type. struct{byte type=VERIFICATION_TOKEN_TYPE;
+ * short length=2; short arrayPtr} where arrayPtr is a pointer to ordered array with following
+ * elements: {KMInteger Challenge; KMInteger Timestamp; KMByteBlob PARAMETERS_VERIFIED;
+ * SecurityLevel level; KMByteBlob Mac}.
+ */
+public class KMVerificationToken extends KMType {
+
+ public static final byte CHALLENGE = 0x00;
+ public static final byte TIMESTAMP = 0x01;
+ public static final byte MAC = 0x02;
+
+ private static KMVerificationToken prototype;
+
+ private KMVerificationToken() {}
+
+ public static short exp() {
+ short arrPtr = KMArray.instance((short) 3);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add(CHALLENGE, KMInteger.exp());
+ arr.add(TIMESTAMP, KMInteger.exp());
+ arr.add(MAC, KMByteBlob.exp());
+ return instance(arrPtr);
+ }
+
+ private static KMVerificationToken proto(short ptr) {
+ if (prototype == null) {
+ prototype = new KMVerificationToken();
+ }
+ KMType.instanceTable[KM_VERIFICATION_TOKEN_OFFSET] = ptr;
+ return prototype;
+ }
+
+ public static short instance() {
+ short arrPtr = KMArray.instance((short) 3);
+ KMArray arr = KMArray.cast(arrPtr);
+ arr.add(CHALLENGE, KMInteger.uint_16((short) 0));
+ arr.add(TIMESTAMP, KMInteger.uint_16((short) 0));
+ arr.add(MAC, KMByteBlob.instance((short) 0));
+ return instance(arrPtr);
+ }
+
+ public static short instance(short vals) {
+ KMArray arr = KMArray.cast(vals);
+ if (arr.length() != 3) {
+ ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+ }
+ short ptr = KMType.instance(VERIFICATION_TOKEN_TYPE, (short) 2);
+ Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+ return ptr;
+ }
+
+ public static KMVerificationToken cast(short ptr) {
+ if (heap[ptr] != VERIFICATION_TOKEN_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+ if (heap[arrPtr] != ARRAY_TYPE) {
+ ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+ }
+ return proto(ptr);
+ }
+
+ public short getVals() {
+ return Util.getShort(
+ heap, (short) (KMType.instanceTable[KM_VERIFICATION_TOKEN_OFFSET] + TLV_HEADER_SIZE));
+ }
+
+ public short length() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).length();
+ }
+
+ public short getChallenge() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(CHALLENGE);
+ }
+
+ public void setChallenge(short vals) {
+ KMInteger.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(CHALLENGE, vals);
+ }
+
+ public short getTimestamp() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(TIMESTAMP);
+ }
+
+ public void setTimestamp(short vals) {
+ KMInteger.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(TIMESTAMP, vals);
+ }
+
+ public short getMac() {
+ short arrPtr = getVals();
+ return KMArray.cast(arrPtr).get(MAC);
+ }
+
+ public void setMac(short vals) {
+ KMByteBlob.cast(vals);
+ short arrPtr = getVals();
+ KMArray.cast(arrPtr).add(MAC, vals);
+ }
+}
diff --git a/ready_se/google/keymint/KM300/HAL/.clang-format b/ready_se/google/keymint/KM300/HAL/.clang-format
new file mode 100644
index 0000000..b0dc94c
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/.clang-format
@@ -0,0 +1,10 @@
+BasedOnStyle: LLVM
+IndentWidth: 4
+UseTab: Never
+BreakBeforeBraces: Attach
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: true
+IndentCaseLabels: false
+ColumnLimit: 100
+PointerBindsToType: true
+SpacesBeforeTrailingComments: 2
diff --git a/ready_se/google/keymint/KM300/HAL/Android.bp b/ready_se/google/keymint/KM300/HAL/Android.bp
new file mode 100644
index 0000000..fd38d10
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/Android.bp
@@ -0,0 +1,117 @@
+// 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" 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 {
+ default_applicable_licenses: [
+ "external_libese_ready_se_google_keymint_KM300_HAL_license",
+ ],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+ name: "external_libese_ready_se_google_keymint_KM300_HAL_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ ],
+ license_text: [
+ "LICENSE",
+ ],
+}
+
+cc_library {
+ name: "libjc_keymint3",
+ defaults: [
+ "keymaster_defaults",
+ "keymint_use_latest_hal_aidl_ndk_shared",
+ ],
+ srcs: [
+ "CborConverter.cpp",
+ "JavacardKeyMintDevice.cpp",
+ "JavacardKeyMintOperation.cpp",
+ "JavacardRemotelyProvisionedComponentDevice.cpp",
+ "JavacardSecureElement.cpp",
+ "JavacardSharedSecret.cpp",
+ "keymint_utils.cpp",
+ ],
+ cflags: ["-O0"],
+ shared_libs: [
+ "android.hardware.security.secureclock-V1-ndk",
+ "android.hardware.security.sharedsecret-V1-ndk",
+ "android.hardware.security.rkp-V3-ndk",
+ "lib_android_keymaster_keymint_utils",
+ "libbase",
+ "libcppbor_external",
+ "libkeymaster_portable",
+ "libkeymaster_messages",
+ "libsoft_attestation_cert",
+ "liblog",
+ "libcrypto",
+ "libcutils",
+ "libjc_keymint_transport",
+ "libbinder_ndk",
+ ],
+ export_include_dirs: [
+ ".",
+ ],
+ vendor_available: true,
+}
+
+cc_binary {
+ name: "android.hardware.security.keymint3-service.strongbox",
+ relative_install_path: "hw",
+ init_rc: ["android.hardware.security.keymint3-service.strongbox.rc"],
+ vintf_fragments: [
+ "android.hardware.security.keymint3-service.strongbox.xml",
+ "android.hardware.security.sharedsecret3-service.strongbox.xml",
+ ],
+ vendor: true,
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ ],
+ defaults: [
+ "keymint_use_latest_hal_aidl_ndk_shared",
+ ],
+ shared_libs: [
+ "android.hardware.security.sharedsecret-V1-ndk",
+ "lib_android_keymaster_keymint_utils",
+ "android.hardware.security.rkp-V3-ndk",
+ "libbase",
+ "libbinder_ndk",
+ "libcppbor_external",
+ "libcrypto",
+ "libkeymaster_portable",
+ "libjc_keymint3",
+ "libjc_keymint_transport",
+ "liblog",
+ "libutils",
+ "android.se.omapi-V1-ndk",
+ ],
+ srcs: [
+ "service.cpp",
+ ],
+ required: [
+ "android.hardware.hardware_keystore.jc-strongbox-keymint3.xml",
+ ],
+}
+
+prebuilt_etc {
+ name: "android.hardware.hardware_keystore.jc-strongbox-keymint3.xml",
+ sub_dir: "permissions",
+ vendor: true,
+ src: "android.hardware.hardware_keystore.jc-strongbox-keymint3.xml",
+}
diff --git a/ready_se/google/keymint/KM300/HAL/CborConverter.cpp b/ready_se/google/keymint/KM300/HAL/CborConverter.cpp
new file mode 100644
index 0000000..7d0fc23
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/CborConverter.cpp
@@ -0,0 +1,521 @@
+/*
+ **
+ ** Copyright 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" 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.
+ */
+
+#include "CborConverter.h"
+
+#include <map>
+#include <string>
+
+#include <android-base/logging.h>
+
+#include <KeyMintUtils.h>
+
+namespace keymint::javacard {
+using ::aidl::android::hardware::security::keymint::KeyParameterValue;
+using ::aidl::android::hardware::security::keymint::SecurityLevel;
+using ::aidl::android::hardware::security::keymint::km_utils::kmParam2Aidl;
+using ::aidl::android::hardware::security::keymint::km_utils::legacy_enum_conversion;
+using ::aidl::android::hardware::security::keymint::km_utils::typeFromTag;
+
+constexpr int SB_ENFORCED = 0;
+constexpr int TEE_ENFORCED = 1;
+constexpr int SW_ENFORCED = 2;
+
+namespace {
+
+template <KeyParameterValue::Tag aidl_tag>
+std::optional<uint32_t> aidlEnumVal2Uint32(const KeyParameterValue& value) {
+ return (value.getTag() == aidl_tag)
+ ? std::optional(static_cast<uint32_t>(value.get<aidl_tag>()))
+ : std::nullopt;
+}
+
+std::optional<uint32_t> aidlEnumParam2Uint32(const KeyParameter& param) {
+ auto tag = legacy_enum_conversion(param.tag);
+ switch (tag) {
+ case KM_TAG_PURPOSE:
+ return aidlEnumVal2Uint32<KeyParameterValue::keyPurpose>(param.value);
+ case KM_TAG_ALGORITHM:
+ return aidlEnumVal2Uint32<KeyParameterValue::algorithm>(param.value);
+ case KM_TAG_BLOCK_MODE:
+ return aidlEnumVal2Uint32<KeyParameterValue::blockMode>(param.value);
+ case KM_TAG_DIGEST:
+ case KM_TAG_RSA_OAEP_MGF_DIGEST:
+ return aidlEnumVal2Uint32<KeyParameterValue::digest>(param.value);
+ case KM_TAG_PADDING:
+ return aidlEnumVal2Uint32<KeyParameterValue::paddingMode>(param.value);
+ case KM_TAG_EC_CURVE:
+ return aidlEnumVal2Uint32<KeyParameterValue::ecCurve>(param.value);
+ case KM_TAG_USER_AUTH_TYPE:
+ return aidlEnumVal2Uint32<KeyParameterValue::hardwareAuthenticatorType>(param.value);
+ case KM_TAG_ORIGIN:
+ return aidlEnumVal2Uint32<KeyParameterValue::origin>(param.value);
+ case KM_TAG_BLOB_USAGE_REQUIREMENTS:
+ case KM_TAG_KDF:
+ default:
+ CHECK(false) << "Unknown or unused enum tag: Something is broken";
+ return std::nullopt;
+ }
+}
+
+} // namespace
+
+bool CborConverter::addAttestationKey(Array& array,
+ const std::optional<AttestationKey>& attestationKey) {
+ if (attestationKey.has_value()) {
+ array.add(Bstr(attestationKey->keyBlob));
+ addKeyparameters(array, attestationKey->attestKeyParams);
+ array.add(Bstr(attestationKey->issuerSubjectName));
+ } else {
+ array.add(std::move(Bstr(vector<uint8_t>(0))));
+ array.add(std::move(Map()));
+ array.add(std::move(Bstr(vector<uint8_t>(0))));
+ }
+ return true;
+}
+
+bool CborConverter::addKeyparameters(Array& array, const vector<KeyParameter>& keyParams) {
+ Map map;
+ std::map<uint64_t, vector<uint8_t>> enum_repetition;
+ std::map<uint64_t, Array> uint_repetition;
+ for (auto& param : keyParams) {
+ auto tag = legacy_enum_conversion(param.tag);
+ switch (typeFromTag(tag)) {
+ case KM_ENUM: {
+ auto paramEnum = aidlEnumParam2Uint32(param);
+ if (paramEnum.has_value()) {
+ map.add(static_cast<uint64_t>(tag), *paramEnum);
+ }
+ break;
+ }
+ case KM_UINT:
+ if (param.value.getTag() == KeyParameterValue::integer) {
+ auto intVal = param.value.get<KeyParameterValue::integer>();
+ map.add(static_cast<uint64_t>(tag), intVal);
+ }
+ break;
+ case KM_UINT_REP:
+ if (param.value.getTag() == KeyParameterValue::integer) {
+ auto intVal = param.value.get<KeyParameterValue::integer>();
+ uint_repetition[static_cast<uint64_t>(tag)].add(intVal);
+ }
+ break;
+ case KM_ENUM_REP: {
+ auto paramEnumRep = aidlEnumParam2Uint32(param);
+ if (paramEnumRep.has_value()) {
+ enum_repetition[static_cast<uint64_t>(tag)].push_back(*paramEnumRep);
+ }
+ break;
+ }
+ case KM_ULONG:
+ if (param.value.getTag() == KeyParameterValue::longInteger) {
+ auto longVal = param.value.get<KeyParameterValue::longInteger>();
+ map.add(static_cast<uint64_t>(tag), longVal);
+ }
+ break;
+ case KM_ULONG_REP:
+ if (param.value.getTag() == KeyParameterValue::longInteger) {
+ auto longVal = param.value.get<KeyParameterValue::longInteger>();
+ uint_repetition[static_cast<uint64_t>(tag & 0x00000000ffffffff)].add(longVal);
+ }
+ break;
+ case KM_DATE:
+ if (param.value.getTag() == KeyParameterValue::dateTime) {
+ auto dateVal = param.value.get<KeyParameterValue::dateTime>();
+ map.add(static_cast<uint64_t>(tag), dateVal);
+ }
+ break;
+ case KM_BOOL:
+ map.add(static_cast<uint64_t>(tag), 1 /* true */);
+ break;
+ case KM_BIGNUM:
+ case KM_BYTES:
+ if (param.value.getTag() == KeyParameterValue::blob) {
+ const auto& value = param.value.get<KeyParameterValue::blob>();
+ map.add(static_cast<uint64_t>(tag & 0x00000000ffffffff), value);
+ }
+ break;
+ case KM_INVALID:
+ break;
+ }
+ }
+
+ for (auto const& [key, val] : enum_repetition) {
+ Bstr bstr(val);
+ map.add(key, std::move(bstr));
+ }
+
+ for (auto& [key, val] : uint_repetition) {
+ map.add(key, std::move(val));
+ }
+ array.add(std::move(map));
+ return true;
+}
+
+// Array of three maps
+std::optional<vector<KeyCharacteristics>>
+CborConverter::getKeyCharacteristics(const unique_ptr<Item>& item, const uint32_t pos) {
+ vector<KeyCharacteristics> keyCharacteristics;
+ auto arrayItem = getItemAtPos(item, pos);
+ if (!arrayItem || (MajorType::ARRAY != getType(arrayItem.value()))) {
+ return std::nullopt;
+ }
+ KeyCharacteristics swEnf{SecurityLevel::KEYSTORE, {}};
+ KeyCharacteristics teeEnf{SecurityLevel::TRUSTED_ENVIRONMENT, {}};
+ KeyCharacteristics sbEnf{SecurityLevel::STRONGBOX, {}};
+
+ auto optSbEnf = getKeyParameters(arrayItem.value(), SB_ENFORCED);
+ if (!optSbEnf) {
+ return std::nullopt;
+ }
+ sbEnf.authorizations = std::move(optSbEnf.value());
+ auto optTeeEnf = getKeyParameters(arrayItem.value(), TEE_ENFORCED);
+ if (!optTeeEnf) {
+ return std::nullopt;
+ }
+ teeEnf.authorizations = std::move(optTeeEnf.value());
+ auto optSwEnf = getKeyParameters(arrayItem.value(), SW_ENFORCED);
+ if (!optSwEnf) {
+ return std::nullopt;
+ }
+ swEnf.authorizations = std::move(optSwEnf.value());
+ // VTS will fail if the authorizations list is empty.
+ if (!sbEnf.authorizations.empty()) keyCharacteristics.push_back(std::move(sbEnf));
+ if (!teeEnf.authorizations.empty()) keyCharacteristics.push_back(std::move(teeEnf));
+ if (!swEnf.authorizations.empty()) keyCharacteristics.push_back(std::move(swEnf));
+ return keyCharacteristics;
+}
+
+std::optional<std::vector<KeyParameter>> CborConverter::getKeyParameter(
+ const std::pair<const std::unique_ptr<Item>&, const std::unique_ptr<Item>&> pair) {
+ std::vector<KeyParameter> keyParams;
+ keymaster_tag_t key;
+ auto optValue = getUint64(pair.first);
+ if (!optValue) {
+ return std::nullopt;
+ }
+ key = static_cast<keymaster_tag_t>(optValue.value());
+ switch (keymaster_tag_get_type(key)) {
+ case KM_ENUM_REP: {
+ /* ENUM_REP contains values encoded in a Byte string */
+ const Bstr* bstr = pair.second.get()->asBstr();
+ if (bstr == nullptr) {
+ return std::nullopt;
+ }
+ for (auto bchar : bstr->value()) {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ keyParam.enumerated = bchar;
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ }
+ return keyParams;
+ }
+ case KM_ENUM: {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ if (!(optValue = getUint64(pair.second))) {
+ return std::nullopt;
+ }
+ keyParam.enumerated = static_cast<uint32_t>(optValue.value());
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ return keyParams;
+ }
+ case KM_UINT: {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ if (!(optValue = getUint64(pair.second))) {
+ return std::nullopt;
+ }
+ keyParam.integer = static_cast<uint32_t>(optValue.value());
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ return keyParams;
+ }
+ case KM_ULONG: {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ if (!(optValue = getUint64(pair.second))) {
+ return std::nullopt;
+ }
+ keyParam.long_integer = optValue.value();
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ return keyParams;
+ }
+ case KM_UINT_REP: {
+ /* UINT_REP contains values encoded in a Array */
+ Array* array = const_cast<Array*>(pair.second.get()->asArray());
+ if (array == nullptr) return std::nullopt;
+ for (int i = 0; i < array->size(); i++) {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ const std::unique_ptr<Item>& item = array->get(i);
+ if (!(optValue = getUint64(item))) {
+ return std::nullopt;
+ }
+ keyParam.integer = static_cast<uint32_t>(optValue.value());
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ }
+ return keyParams;
+ }
+ case KM_ULONG_REP: {
+ /* ULONG_REP contains values encoded in a Array */
+ Array* array = const_cast<Array*>(pair.second.get()->asArray());
+ if (array == nullptr) return std::nullopt;
+ for (int i = 0; i < array->size(); i++) {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ const std::unique_ptr<Item>& item = array->get(i);
+ if (!(optValue = getUint64(item))) {
+ return std::nullopt;
+ }
+ keyParam.long_integer = optValue.value();
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ }
+ return keyParams;
+ }
+ case KM_DATE: {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ if (!(optValue = getUint64(pair.second))) {
+ return std::nullopt;
+ }
+ keyParam.date_time = optValue.value();
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ return keyParams;
+ }
+ case KM_BOOL: {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ if (!(optValue = getUint64(pair.second))) {
+ return std::nullopt;
+ }
+ // If a tag with this type is present, the value is true. If absent, false.
+ keyParam.boolean = true;
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ return keyParams;
+ }
+ case KM_BIGNUM:
+ case KM_BYTES: {
+ keymaster_key_param_t keyParam;
+ keyParam.tag = key;
+ const Bstr* bstr = pair.second.get()->asBstr();
+ if (bstr == nullptr) return std::nullopt;
+ keyParam.blob.data = bstr->value().data();
+ keyParam.blob.data_length = bstr->value().size();
+ keyParams.push_back(kmParam2Aidl(keyParam));
+ return keyParams;
+ }
+ case KM_INVALID:
+ break;
+ }
+ return std::nullopt;
+}
+
+// array of a blobs
+std::optional<vector<Certificate>>
+CborConverter::getCertificateChain(const std::unique_ptr<Item>& item, const uint32_t pos) {
+ vector<Certificate> certChain;
+ auto arrayItem = getItemAtPos(item, pos);
+ if (!arrayItem || (MajorType::ARRAY != getType(arrayItem.value()))) return std::nullopt;
+
+ const Array* arr = arrayItem.value().get()->asArray();
+ for (int i = 0; i < arr->size(); i++) {
+ Certificate cert;
+ auto optTemp = getByteArrayVec(arrayItem.value(), i);
+ if (!optTemp) return std::nullopt;
+ cert.encodedCertificate = std::move(optTemp.value());
+ certChain.push_back(std::move(cert));
+ }
+ return certChain;
+}
+
+std::optional<string> CborConverter::getTextStr(const unique_ptr<Item>& item, const uint32_t pos) {
+ auto textStrItem = getItemAtPos(item, pos);
+ if (!textStrItem || (MajorType::TSTR != getType(textStrItem.value()))) {
+ return std::nullopt;
+ }
+ const Tstr* tstr = textStrItem.value().get()->asTstr();
+ return tstr->value();
+}
+
+std::optional<string> CborConverter::getByteArrayStr(const unique_ptr<Item>& item,
+ const uint32_t pos) {
+ auto optTemp = getByteArrayVec(item, pos);
+ if (!optTemp) {
+ return std::nullopt;
+ }
+ std::string str(optTemp->begin(), optTemp->end());
+ return str;
+}
+
+std::optional<std::vector<uint8_t>> CborConverter::getByteArrayVec(const unique_ptr<Item>& item,
+ const uint32_t pos) {
+ auto strItem = getItemAtPos(item, pos);
+ if (!strItem || (MajorType::BSTR != getType(strItem.value()))) {
+ return std::nullopt;
+ }
+ const Bstr* bstr = strItem.value().get()->asBstr();
+ return bstr->value();
+}
+
+std::optional<SharedSecretParameters>
+CborConverter::getSharedSecretParameters(const unique_ptr<Item>& item, const uint32_t pos) {
+ SharedSecretParameters params;
+ // Array [seed, nonce]
+ auto arrayItem = getItemAtPos(item, pos);
+ if (!arrayItem || (MajorType::ARRAY != getType(arrayItem.value()))) {
+ return std::nullopt;
+ }
+ auto optSeed = getByteArrayVec(arrayItem.value(), 0);
+ auto optNonce = getByteArrayVec(arrayItem.value(), 1);
+ if (!optSeed || !optNonce) {
+ return std::nullopt;
+ }
+ params.seed = std::move(optSeed.value());
+ params.nonce = std::move(optNonce.value());
+ return params;
+}
+
+bool CborConverter::addSharedSecretParameters(Array& array,
+ const vector<SharedSecretParameters>& params) {
+ Array cborParamsVec;
+ for (auto param : params) {
+ Array cborParam;
+ cborParam.add(Bstr(param.seed));
+ cborParam.add(Bstr(param.nonce));
+ cborParamsVec.add(std::move(cborParam));
+ }
+ array.add(std::move(cborParamsVec));
+ return true;
+}
+
+bool CborConverter::addTimeStampToken(Array& array, const TimeStampToken& token) {
+ Array vToken;
+ vToken.add(static_cast<uint64_t>(token.challenge));
+ vToken.add(static_cast<uint64_t>(token.timestamp.milliSeconds));
+ vToken.add((std::vector<uint8_t>(token.mac)));
+ array.add(std::move(vToken));
+ return true;
+}
+
+bool CborConverter::addHardwareAuthToken(Array& array, const HardwareAuthToken& authToken) {
+
+ Array hwAuthToken;
+ hwAuthToken.add(static_cast<uint64_t>(authToken.challenge));
+ hwAuthToken.add(static_cast<uint64_t>(authToken.userId));
+ hwAuthToken.add(static_cast<uint64_t>(authToken.authenticatorId));
+ hwAuthToken.add(static_cast<uint64_t>(authToken.authenticatorType));
+ hwAuthToken.add(static_cast<uint64_t>(authToken.timestamp.milliSeconds));
+ hwAuthToken.add((std::vector<uint8_t>(authToken.mac)));
+ array.add(std::move(hwAuthToken));
+ return true;
+}
+
+std::optional<TimeStampToken> CborConverter::getTimeStampToken(const unique_ptr<Item>& item,
+ const uint32_t pos) {
+ TimeStampToken token;
+ // {challenge, timestamp, Mac}
+ auto optChallenge = getUint64(item, pos);
+ auto optTimestampMillis = getUint64(item, pos + 1);
+ auto optTemp = getByteArrayVec(item, pos + 2);
+ if (!optChallenge || !optTimestampMillis || !optTemp) {
+ return std::nullopt;
+ }
+ token.mac = std::move(optTemp.value());
+ token.challenge = static_cast<long>(std::move(optChallenge.value()));
+ token.timestamp.milliSeconds = static_cast<long>(std::move(optTimestampMillis.value()));
+ return token;
+}
+
+std::optional<Array> CborConverter::getArrayItem(const std::unique_ptr<Item>& item,
+ const uint32_t pos) {
+ Array array;
+ auto arrayItem = getItemAtPos(item, pos);
+ if (!arrayItem || (MajorType::ARRAY != getType(arrayItem.value()))) {
+ return std::nullopt;
+ }
+ array = std::move(*(arrayItem.value().get()->asArray()));
+ return array;
+}
+
+std::optional<Map> CborConverter::getMapItem(const std::unique_ptr<Item>& item,
+ const uint32_t pos) {
+ Map map;
+ auto mapItem = getItemAtPos(item, pos);
+ if (!mapItem || (MajorType::MAP != getType(mapItem.value()))) {
+ return std::nullopt;
+ }
+ map = std::move(*(mapItem.value().get()->asMap()));
+ return map;
+}
+
+std::optional<vector<KeyParameter>> CborConverter::getKeyParameters(const unique_ptr<Item>& item,
+ const uint32_t pos) {
+ vector<KeyParameter> params;
+ auto mapItem = getItemAtPos(item, pos);
+ if (!mapItem || (MajorType::MAP != getType(mapItem.value()))) return std::nullopt;
+ const Map* map = mapItem.value().get()->asMap();
+ size_t mapSize = map->size();
+ for (int i = 0; i < mapSize; i++) {
+ auto optKeyParams = getKeyParameter((*map)[i]);
+ if (optKeyParams) {
+ params.insert(params.end(), optKeyParams->begin(), optKeyParams->end());
+ } else {
+ return std::nullopt;
+ }
+ }
+ return params;
+}
+
+std::tuple<std::unique_ptr<Item>, keymaster_error_t>
+CborConverter::decodeData(const std::vector<uint8_t>& response) {
+ auto [item, pos, message] = cppbor::parse(response);
+ if (!item || MajorType::ARRAY != getType(item)) {
+ return {nullptr, KM_ERROR_UNKNOWN_ERROR};
+ }
+ auto optErrorCode = getErrorCode(item, 0);
+ if (!optErrorCode) {
+ return {nullptr, KM_ERROR_UNKNOWN_ERROR};
+ }
+ return {std::move(item), optErrorCode.value()};
+}
+
+std::optional<keymaster_error_t>
+CborConverter::getErrorCode(const std::unique_ptr<cppbor::Item>& item, const uint32_t pos) {
+ auto optErrorVal = getUint64(item, pos);
+ if (!optErrorVal) {
+ return std::nullopt;
+ }
+ return static_cast<keymaster_error_t>(0 - optErrorVal.value());
+}
+
+std::optional<uint64_t> CborConverter::getUint64(const unique_ptr<Item>& item) {
+ if ((item == nullptr) || (MajorType::UINT != getType(item))) {
+ return std::nullopt;
+ }
+ const Uint* uintVal = item.get()->asUint();
+ return uintVal->unsignedValue();
+}
+
+std::optional<uint64_t> CborConverter::getUint64(const unique_ptr<Item>& item, const uint32_t pos) {
+ auto intItem = getItemAtPos(item, pos);
+ if (!intItem) {
+ return std::nullopt;
+ }
+ return getUint64(intItem.value());
+}
+
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM300/HAL/CborConverter.h b/ready_se/google/keymint/KM300/HAL/CborConverter.h
new file mode 100644
index 0000000..d594350
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/CborConverter.h
@@ -0,0 +1,142 @@
+/*
+ **
+ ** Copyright 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" 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.
+ */
+#pragma once
+
+#include <iostream>
+#include <memory>
+#include <numeric>
+#include <vector>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+#include <aidl/android/hardware/security/keymint/Certificate.h>
+#include <aidl/android/hardware/security/keymint/IKeyMintDevice.h>
+#include <aidl/android/hardware/security/secureclock/TimeStampToken.h>
+#include <aidl/android/hardware/security/sharedsecret/ISharedSecret.h>
+
+#include <keymaster/android_keymaster_messages.h>
+
+namespace keymint::javacard {
+using aidl::android::hardware::security::keymint::AttestationKey;
+using aidl::android::hardware::security::keymint::Certificate;
+using aidl::android::hardware::security::keymint::HardwareAuthToken;
+using aidl::android::hardware::security::keymint::KeyCharacteristics;
+using aidl::android::hardware::security::keymint::KeyParameter;
+using aidl::android::hardware::security::secureclock::TimeStampToken;
+using aidl::android::hardware::security::sharedsecret::SharedSecretParameters;
+using cppbor::Array;
+using cppbor::Bstr;
+using cppbor::EncodedItem;
+using cppbor::Item;
+using cppbor::MajorType;
+using cppbor::Map;
+using cppbor::Nint;
+using cppbor::Tstr;
+using cppbor::Uint;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+class CborConverter {
+ public:
+ CborConverter() = default;
+
+ ~CborConverter() = default;
+
+ std::tuple<std::unique_ptr<Item>, keymaster_error_t>
+ decodeData(const std::vector<uint8_t>& response);
+
+ std::optional<uint64_t> getUint64(const unique_ptr<Item>& item);
+
+ std::optional<uint64_t> getUint64(const unique_ptr<Item>& item, const uint32_t pos);
+
+ std::optional<SharedSecretParameters>
+ getSharedSecretParameters(const std::unique_ptr<Item>& item, const uint32_t pos);
+
+ std::optional<string> getByteArrayStr(const unique_ptr<Item>& item, const uint32_t pos);
+
+ std::optional<string> getTextStr(const unique_ptr<Item>& item, const uint32_t pos);
+
+ std::optional<std::vector<uint8_t>> getByteArrayVec(const unique_ptr<Item>& item,
+ const uint32_t pos);
+
+ std::optional<vector<KeyParameter>> getKeyParameters(const unique_ptr<Item>& item,
+ const uint32_t pos);
+
+ bool addKeyparameters(Array& array, const vector<KeyParameter>& keyParams);
+
+ bool addAttestationKey(Array& array, const std::optional<AttestationKey>& attestationKey);
+
+ bool addHardwareAuthToken(Array& array, const HardwareAuthToken& authToken);
+
+ bool addSharedSecretParameters(Array& array, const vector<SharedSecretParameters>& params);
+
+ std::optional<TimeStampToken> getTimeStampToken(const std::unique_ptr<Item>& item,
+ const uint32_t pos);
+
+ std::optional<vector<KeyCharacteristics>>
+ getKeyCharacteristics(const std::unique_ptr<Item>& item, const uint32_t pos);
+
+ std::optional<vector<Certificate>> getCertificateChain(const std::unique_ptr<Item>& item,
+ const uint32_t pos);
+
+ std::optional<vector<vector<uint8_t>>> getMultiByteArray(const unique_ptr<Item>& item,
+ const uint32_t pos);
+
+ bool addTimeStampToken(Array& array, const TimeStampToken& token);
+
+ std::optional<Map> getMapItem(const std::unique_ptr<Item>& item, const uint32_t pos);
+
+ std::optional<Array> getArrayItem(const std::unique_ptr<Item>& item, const uint32_t pos);
+
+ std::optional<keymaster_error_t> getErrorCode(const std::unique_ptr<Item>& item,
+ const uint32_t pos);
+
+ private:
+ /**
+ * Get the type of the Item pointer.
+ */
+ inline MajorType getType(const unique_ptr<Item>& item) { return item.get()->type(); }
+
+ /**
+ * Construct Keyparameter structure from the pair of key and value. If TagType is ENUM_REP the
+ * value contains binary string. If TagType is UINT_REP or ULONG_REP the value contains Array of
+ * unsigned integers.
+ */
+ std::optional<std::vector<KeyParameter>> getKeyParameter(
+ const std::pair<const std::unique_ptr<Item>&, const std::unique_ptr<Item>&> pair);
+
+ /**
+ * Get the sub item pointer from the root item pointer at the given position.
+ */
+ inline std::optional<unique_ptr<Item>> getItemAtPos(const unique_ptr<Item>& item,
+ const uint32_t pos) {
+ Array* arr = nullptr;
+
+ if (MajorType::ARRAY != getType(item)) {
+ return std::nullopt;
+ }
+ arr = const_cast<Array*>(item.get()->asArray());
+ if (arr->size() < (pos + 1)) {
+ return std::nullopt;
+ }
+ return std::move((*arr)[pos]);
+ }
+};
+
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM300/HAL/JavacardKeyMintDevice.cpp b/ready_se/google/keymint/KM300/HAL/JavacardKeyMintDevice.cpp
new file mode 100644
index 0000000..bd68b48
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/JavacardKeyMintDevice.cpp
@@ -0,0 +1,454 @@
+/*
+ * Copyright 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" 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.
+ */
+
+#define LOG_TAG "javacard.keymint.device.strongbox-impl"
+
+#include "JavacardKeyMintDevice.h"
+
+#include <regex.h>
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <KeyMintUtils.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <hardware/hw_auth_token.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/wrapped_key.h>
+
+#include "JavacardKeyMintOperation.h"
+#include "JavacardSharedSecret.h"
+
+namespace aidl::android::hardware::security::keymint {
+using cppbor::Bstr;
+using cppbor::EncodedItem;
+using cppbor::Uint;
+using ::keymaster::AuthorizationSet;
+using ::keymaster::dup_buffer;
+using ::keymaster::KeymasterBlob;
+using ::keymaster::KeymasterKeyBlob;
+using ::keymint::javacard::Instruction;
+using std::string;
+
+ScopedAStatus JavacardKeyMintDevice::defaultHwInfo(KeyMintHardwareInfo* info) {
+ info->versionNumber = 2;
+ info->keyMintAuthorName = "Google";
+ info->keyMintName = "JavacardKeymintDevice";
+ info->securityLevel = securitylevel_;
+ info->timestampTokenRequired = true;
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) {
+ auto [item, err] = card_->sendRequest(Instruction::INS_GET_HW_INFO_CMD);
+ std::optional<string> optKeyMintName;
+ std::optional<string> optKeyMintAuthorName;
+ std::optional<uint64_t> optSecLevel;
+ std::optional<uint64_t> optVersion;
+ std::optional<uint64_t> optTsRequired;
+ if (err != KM_ERROR_OK || !(optVersion = cbor_.getUint64(item, 1)) ||
+ !(optSecLevel = cbor_.getUint64(item, 2)) ||
+ !(optKeyMintName = cbor_.getByteArrayStr(item, 3)) ||
+ !(optKeyMintAuthorName = cbor_.getByteArrayStr(item, 4)) ||
+ !(optTsRequired = cbor_.getUint64(item, 5))) {
+ LOG(ERROR) << "Error in response of getHardwareInfo.";
+ LOG(INFO) << "Returning defaultHwInfo in getHardwareInfo.";
+ return defaultHwInfo(info);
+ }
+ card_->initializeJavacard();
+ info->keyMintName = std::move(optKeyMintName.value());
+ info->keyMintAuthorName = std::move(optKeyMintAuthorName.value());
+ info->timestampTokenRequired = (optTsRequired.value() == 1);
+ info->securityLevel = static_cast<SecurityLevel>(std::move(optSecLevel.value()));
+ info->versionNumber = static_cast<int32_t>(std::move(optVersion.value()));
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::generateKey(const vector<KeyParameter>& keyParams,
+ const optional<AttestationKey>& attestationKey,
+ KeyCreationResult* creationResult) {
+ cppbor::Array array;
+ // add key params
+ cbor_.addKeyparameters(array, keyParams);
+ // add attestation key if any
+ cbor_.addAttestationKey(array, attestationKey);
+ auto [item, err] = card_->sendRequest(Instruction::INS_GENERATE_KEY_CMD, array);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending generateKey.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ auto optKeyBlob = cbor_.getByteArrayVec(item, 1);
+ auto optKeyChars = cbor_.getKeyCharacteristics(item, 2);
+ auto optCertChain = cbor_.getCertificateChain(item, 3);
+ if (!optKeyBlob || !optKeyChars || !optCertChain) {
+ LOG(ERROR) << "Error in decoding og response in generateKey.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ creationResult->keyCharacteristics = std::move(optKeyChars.value());
+ creationResult->certificateChain = std::move(optCertChain.value());
+ creationResult->keyBlob = std::move(optKeyBlob.value());
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::addRngEntropy(const vector<uint8_t>& data) {
+ cppbor::Array request;
+ // add key data
+ request.add(Bstr(data));
+ auto [item, err] = card_->sendRequest(Instruction::INS_ADD_RNG_ENTROPY_CMD, request);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending addRngEntropy.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::importKey(const vector<KeyParameter>& keyParams,
+ KeyFormat keyFormat, const vector<uint8_t>& keyData,
+ const optional<AttestationKey>& attestationKey,
+ KeyCreationResult* creationResult) {
+
+ cppbor::Array request;
+ // add key params
+ cbor_.addKeyparameters(request, keyParams);
+ // add key format
+ request.add(Uint(static_cast<uint8_t>(keyFormat)));
+ // add key data
+ request.add(Bstr(keyData));
+ // add attestation key if any
+ cbor_.addAttestationKey(request, attestationKey);
+
+ auto [item, err] = card_->sendRequest(Instruction::INS_IMPORT_KEY_CMD, request);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending data in importKey.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ auto optKeyBlob = cbor_.getByteArrayVec(item, 1);
+ auto optKeyChars = cbor_.getKeyCharacteristics(item, 2);
+ auto optCertChain = cbor_.getCertificateChain(item, 3);
+ if (!optKeyBlob || !optKeyChars || !optCertChain) {
+ LOG(ERROR) << "Error in decoding response in importKey.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ creationResult->keyCharacteristics = std::move(optKeyChars.value());
+ creationResult->certificateChain = std::move(optCertChain.value());
+ creationResult->keyBlob = std::move(optKeyBlob.value());
+ return ScopedAStatus::ok();
+}
+
+// import wrapped key is divided into 2 stage operation.
+ScopedAStatus JavacardKeyMintDevice::importWrappedKey(const vector<uint8_t>& wrappedKeyData,
+ const vector<uint8_t>& wrappingKeyBlob,
+ const vector<uint8_t>& maskingKey,
+ const vector<KeyParameter>& unwrappingParams,
+ int64_t passwordSid, int64_t biometricSid,
+ KeyCreationResult* creationResult) {
+ cppbor::Array request;
+ std::unique_ptr<Item> item;
+ vector<uint8_t> keyBlob;
+ std::vector<uint8_t> response;
+ vector<KeyCharacteristics> keyCharacteristics;
+ std::vector<uint8_t> iv;
+ std::vector<uint8_t> transitKey;
+ std::vector<uint8_t> secureKey;
+ std::vector<uint8_t> tag;
+ vector<KeyParameter> authList;
+ KeyFormat keyFormat;
+ std::vector<uint8_t> wrappedKeyDescription;
+ keymaster_error_t errorCode = parseWrappedKey(wrappedKeyData, iv, transitKey, secureKey, tag,
+ authList, keyFormat, wrappedKeyDescription);
+ if (errorCode != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in parse wrapped key in importWrappedKey.";
+ return km_utils::kmError2ScopedAStatus(errorCode);
+ }
+
+ // begin import
+ std::tie(item, errorCode) =
+ sendBeginImportWrappedKeyCmd(transitKey, wrappingKeyBlob, maskingKey, unwrappingParams);
+ if (errorCode != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in send begin import wrapped key in importWrappedKey.";
+ return km_utils::kmError2ScopedAStatus(errorCode);
+ }
+ // Finish the import
+ std::tie(item, errorCode) = sendFinishImportWrappedKeyCmd(
+ authList, keyFormat, secureKey, tag, iv, wrappedKeyDescription, passwordSid, biometricSid);
+ if (errorCode != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in send finish import wrapped key in importWrappedKey.";
+ return km_utils::kmError2ScopedAStatus(errorCode);
+ }
+ auto optKeyBlob = cbor_.getByteArrayVec(item, 1);
+ auto optKeyChars = cbor_.getKeyCharacteristics(item, 2);
+ auto optCertChain = cbor_.getCertificateChain(item, 3);
+ if (!optKeyBlob || !optKeyChars || !optCertChain) {
+ LOG(ERROR) << "Error in decoding the response in importWrappedKey.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ creationResult->keyCharacteristics = std::move(optKeyChars.value());
+ creationResult->certificateChain = std::move(optCertChain.value());
+ creationResult->keyBlob = std::move(optKeyBlob.value());
+ return ScopedAStatus::ok();
+}
+
+std::tuple<std::unique_ptr<Item>, keymaster_error_t>
+JavacardKeyMintDevice::sendBeginImportWrappedKeyCmd(const std::vector<uint8_t>& transitKey,
+ const std::vector<uint8_t>& wrappingKeyBlob,
+ const std::vector<uint8_t>& maskingKey,
+ const vector<KeyParameter>& unwrappingParams) {
+ Array request;
+ request.add(std::vector<uint8_t>(transitKey));
+ request.add(std::vector<uint8_t>(wrappingKeyBlob));
+ request.add(std::vector<uint8_t>(maskingKey));
+ cbor_.addKeyparameters(request, unwrappingParams);
+ return card_->sendRequest(Instruction::INS_BEGIN_IMPORT_WRAPPED_KEY_CMD, request);
+}
+
+std::tuple<std::unique_ptr<Item>, keymaster_error_t>
+JavacardKeyMintDevice::sendFinishImportWrappedKeyCmd(
+ const vector<KeyParameter>& keyParams, KeyFormat keyFormat,
+ const std::vector<uint8_t>& secureKey, const std::vector<uint8_t>& tag,
+ const std::vector<uint8_t>& iv, const std::vector<uint8_t>& wrappedKeyDescription,
+ int64_t passwordSid, int64_t biometricSid) {
+ Array request;
+ cbor_.addKeyparameters(request, keyParams);
+ request.add(static_cast<uint64_t>(keyFormat));
+ request.add(std::vector<uint8_t>(secureKey));
+ request.add(std::vector<uint8_t>(tag));
+ request.add(std::vector<uint8_t>(iv));
+ request.add(std::vector<uint8_t>(wrappedKeyDescription));
+ request.add(Uint(passwordSid));
+ request.add(Uint(biometricSid));
+ return card_->sendRequest(Instruction::INS_FINISH_IMPORT_WRAPPED_KEY_CMD, request);
+}
+
+ScopedAStatus JavacardKeyMintDevice::upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,
+ const vector<KeyParameter>& upgradeParams,
+ vector<uint8_t>* keyBlob) {
+ cppbor::Array request;
+ // add key blob
+ request.add(Bstr(keyBlobToUpgrade));
+ // add key params
+ cbor_.addKeyparameters(request, upgradeParams);
+ auto [item, err] = card_->sendRequest(Instruction::INS_UPGRADE_KEY_CMD, request);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in upgradeKey.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ auto optKeyBlob = cbor_.getByteArrayVec(item, 1);
+ if (!optKeyBlob) {
+ LOG(ERROR) << "Error in decoding the response in upgradeKey.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ *keyBlob = std::move(optKeyBlob.value());
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::deleteKey(const vector<uint8_t>& keyBlob) {
+ Array request;
+ request.add(Bstr(keyBlob));
+ auto [item, err] = card_->sendRequest(Instruction::INS_DELETE_KEY_CMD, request);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in deleteKey.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::deleteAllKeys() {
+ auto [item, err] = card_->sendRequest(Instruction::INS_DELETE_ALL_KEYS_CMD);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in deleteAllKeys.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::destroyAttestationIds() {
+ auto [item, err] = card_->sendRequest(Instruction::INS_DESTROY_ATT_IDS_CMD);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in destroyAttestationIds.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::begin(KeyPurpose purpose, const std::vector<uint8_t>& keyBlob,
+ const std::vector<KeyParameter>& params,
+ const std::optional<HardwareAuthToken>& authToken,
+ BeginResult* result) {
+
+ cppbor::Array array;
+ std::vector<uint8_t> response;
+ // make request
+ array.add(Uint(static_cast<uint64_t>(purpose)));
+ array.add(Bstr(keyBlob));
+ cbor_.addKeyparameters(array, params);
+ HardwareAuthToken token = authToken.value_or(HardwareAuthToken());
+ cbor_.addHardwareAuthToken(array, token);
+
+ // Send earlyBootEnded if there is any pending earlybootEnded event.
+ auto retErr = card_->sendEarlyBootEndedEvent(false);
+ if (retErr != KM_ERROR_OK) {
+ return km_utils::kmError2ScopedAStatus(retErr);
+ ;
+ }
+
+ auto [item, err] = card_->sendRequest(Instruction::INS_BEGIN_OPERATION_CMD, array);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in begin.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ // return the result
+ auto keyParams = cbor_.getKeyParameters(item, 1);
+ auto optOpHandle = cbor_.getUint64(item, 2);
+ auto optBufMode = cbor_.getUint64(item, 3);
+ auto optMacLength = cbor_.getUint64(item, 4);
+
+ if (!keyParams || !optOpHandle || !optBufMode || !optMacLength) {
+ LOG(ERROR) << "Error in decoding the response in begin.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ result->params = std::move(keyParams.value());
+ result->challenge = optOpHandle.value();
+ result->operation = ndk::SharedRefBase::make<JavacardKeyMintOperation>(
+ static_cast<keymaster_operation_handle_t>(optOpHandle.value()),
+ static_cast<BufferingMode>(optBufMode.value()), optMacLength.value(), card_);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus
+JavacardKeyMintDevice::deviceLocked(bool passwordOnly,
+ const std::optional<TimeStampToken>& timestampToken) {
+ Array request;
+ int8_t password = 1;
+ if (!passwordOnly) {
+ password = 0;
+ }
+ request.add(Uint(password));
+ cbor_.addTimeStampToken(request, timestampToken.value_or(TimeStampToken()));
+ auto [item, err] = card_->sendRequest(Instruction::INS_DEVICE_LOCKED_CMD, request);
+ if (err != KM_ERROR_OK) {
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::earlyBootEnded() {
+ auto err = card_->sendEarlyBootEndedEvent(true);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending earlyBootEndedEvent.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::getKeyCharacteristics(
+ const std::vector<uint8_t>& keyBlob, const std::vector<uint8_t>& appId,
+ const std::vector<uint8_t>& appData, std::vector<KeyCharacteristics>* result) {
+ cppbor::Array request;
+ request.add(vector<uint8_t>(keyBlob));
+ request.add(vector<uint8_t>(appId));
+ request.add(vector<uint8_t>(appData));
+ auto [item, err] = card_->sendRequest(Instruction::INS_GET_KEY_CHARACTERISTICS_CMD, request);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in getKeyCharacteristics.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ auto optKeyChars = cbor_.getKeyCharacteristics(item, 1);
+ if (!optKeyChars) {
+ LOG(ERROR) << "Error in sending in upgradeKey.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ *result = std::move(optKeyChars.value());
+ return ScopedAStatus::ok();
+}
+
+keymaster_error_t
+JavacardKeyMintDevice::parseWrappedKey(const vector<uint8_t>& wrappedKeyData,
+ std::vector<uint8_t>& iv, std::vector<uint8_t>& transitKey,
+ std::vector<uint8_t>& secureKey, std::vector<uint8_t>& tag,
+ vector<KeyParameter>& authList, KeyFormat& keyFormat,
+ std::vector<uint8_t>& wrappedKeyDescription) {
+ KeymasterBlob kmIv;
+ KeymasterKeyBlob kmTransitKey;
+ KeymasterKeyBlob kmSecureKey;
+ KeymasterBlob kmTag;
+ AuthorizationSet authSet;
+ keymaster_key_format_t kmKeyFormat;
+ KeymasterBlob kmWrappedKeyDescription;
+
+ size_t keyDataLen = wrappedKeyData.size();
+ uint8_t* keyData = dup_buffer(wrappedKeyData.data(), keyDataLen);
+ keymaster_key_blob_t keyMaterial = {keyData, keyDataLen};
+ keymaster_error_t error =
+ parse_wrapped_key(KeymasterKeyBlob(keyMaterial), &kmIv, &kmTransitKey, &kmSecureKey, &kmTag,
+ &authSet, &kmKeyFormat, &kmWrappedKeyDescription);
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "Error parsing wrapped key.";
+ return error;
+ }
+ iv = km_utils::kmBlob2vector(kmIv);
+ transitKey = km_utils::kmBlob2vector(kmTransitKey);
+ secureKey = km_utils::kmBlob2vector(kmSecureKey);
+ tag = km_utils::kmBlob2vector(kmTag);
+ authList = km_utils::kmParamSet2Aidl(authSet);
+ keyFormat = static_cast<KeyFormat>(kmKeyFormat);
+ wrappedKeyDescription = km_utils::kmBlob2vector(kmWrappedKeyDescription);
+ return KM_ERROR_OK;
+}
+
+ScopedAStatus JavacardKeyMintDevice::convertStorageKeyToEphemeral(
+ const std::vector<uint8_t>& /* storageKeyBlob */,
+ std::vector<uint8_t>* /* ephemeralKeyBlob */) {
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
+ScopedAStatus JavacardKeyMintDevice::getRootOfTrustChallenge(array<uint8_t, 16>* challenge) {
+ auto [item, err] = card_->sendRequest(Instruction::INS_GET_ROT_CHALLENGE_CMD);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in getRootOfTrustChallenge.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ auto optChallenge = cbor_.getByteArrayVec(item, 1);
+ if (!optChallenge) {
+ LOG(ERROR) << "Error in sending in upgradeKey.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ std::move(optChallenge->begin(), optChallenge->begin() + 16, challenge->begin());
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintDevice::getRootOfTrust(const array<uint8_t, 16>& /*challenge*/,
+ vector<uint8_t>* /*rootOfTrust*/) {
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
+ScopedAStatus JavacardKeyMintDevice::sendRootOfTrust(const vector<uint8_t>& rootOfTrust) {
+ cppbor::Array request;
+ request.add(EncodedItem(rootOfTrust)); // taggedItem.
+ auto [item, err] = card_->sendRequest(Instruction::INS_SEND_ROT_DATA_CMD, request);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in sendRootOfTrust.";
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ LOG(INFO) << "JavacardKeyMintDevice::sendRootOfTrust success";
+ return ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/ready_se/google/keymint/KM300/HAL/JavacardKeyMintDevice.h b/ready_se/google/keymint/KM300/HAL/JavacardKeyMintDevice.h
new file mode 100644
index 0000000..adf0f7d
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/JavacardKeyMintDevice.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 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" 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/security/keymint/BnKeyMintDevice.h>
+#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
+#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
+#include <aidl/android/hardware/security/sharedsecret/SharedSecretParameters.h>
+
+#include "CborConverter.h"
+#include "JavacardSecureElement.h"
+
+namespace aidl::android::hardware::security::keymint {
+using cppbor::Item;
+using ::keymint::javacard::CborConverter;
+using ::keymint::javacard::JavacardSecureElement;
+using ndk::ScopedAStatus;
+using secureclock::TimeStampToken;
+using std::array;
+using std::optional;
+using std::shared_ptr;
+using std::vector;
+
+class JavacardKeyMintDevice : public BnKeyMintDevice {
+ public:
+ explicit JavacardKeyMintDevice(shared_ptr<JavacardSecureElement> card)
+ : securitylevel_(SecurityLevel::STRONGBOX), card_(card) {
+ card_->initializeJavacard();
+ }
+ virtual ~JavacardKeyMintDevice() {}
+
+ ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* info) override;
+
+ ScopedAStatus addRngEntropy(const vector<uint8_t>& data) override;
+
+ ScopedAStatus generateKey(const vector<KeyParameter>& keyParams,
+ const optional<AttestationKey>& attestationKey,
+ KeyCreationResult* creationResult) override;
+
+ ScopedAStatus importKey(const vector<KeyParameter>& keyParams, KeyFormat keyFormat,
+ const vector<uint8_t>& keyData,
+ const optional<AttestationKey>& attestationKey,
+ KeyCreationResult* creationResult) override;
+
+ ScopedAStatus importWrappedKey(const vector<uint8_t>& wrappedKeyData,
+ const vector<uint8_t>& wrappingKeyBlob,
+ const vector<uint8_t>& maskingKey,
+ const vector<KeyParameter>& unwrappingParams,
+ int64_t passwordSid, int64_t biometricSid,
+ KeyCreationResult* creationResult) override;
+
+ ScopedAStatus upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,
+ const vector<KeyParameter>& upgradeParams,
+ vector<uint8_t>* keyBlob) override;
+
+ ScopedAStatus deleteKey(const vector<uint8_t>& keyBlob) override;
+ ScopedAStatus deleteAllKeys() override;
+ ScopedAStatus destroyAttestationIds() override;
+
+ virtual ScopedAStatus begin(KeyPurpose in_purpose, const std::vector<uint8_t>& in_keyBlob,
+ const std::vector<KeyParameter>& in_params,
+ const std::optional<HardwareAuthToken>& in_authToken,
+ BeginResult* _aidl_return) override;
+
+ ScopedAStatus deviceLocked(bool passwordOnly,
+ const optional<TimeStampToken>& timestampToken) override;
+
+ ScopedAStatus earlyBootEnded() override;
+
+ ScopedAStatus getKeyCharacteristics(const std::vector<uint8_t>& in_keyBlob,
+ const std::vector<uint8_t>& in_appId,
+ const std::vector<uint8_t>& in_appData,
+ std::vector<KeyCharacteristics>* _aidl_return) override;
+
+ ScopedAStatus convertStorageKeyToEphemeral(const std::vector<uint8_t>& storageKeyBlob,
+ std::vector<uint8_t>* ephemeralKeyBlob) override;
+
+ ScopedAStatus getRootOfTrustChallenge(array<uint8_t, 16>* challenge) override;
+
+ ScopedAStatus getRootOfTrust(const array<uint8_t, 16>& challenge,
+ vector<uint8_t>* rootOfTrust) override;
+
+ ScopedAStatus sendRootOfTrust(const vector<uint8_t>& rootOfTrust) override;
+
+ private:
+ keymaster_error_t parseWrappedKey(const vector<uint8_t>& wrappedKeyData,
+ std::vector<uint8_t>& iv, std::vector<uint8_t>& transitKey,
+ std::vector<uint8_t>& secureKey, std::vector<uint8_t>& tag,
+ vector<KeyParameter>& authList, KeyFormat& keyFormat,
+ std::vector<uint8_t>& wrappedKeyDescription);
+
+ std::tuple<std::unique_ptr<Item>, keymaster_error_t> sendBeginImportWrappedKeyCmd(
+ const std::vector<uint8_t>& transitKey, const std::vector<uint8_t>& wrappingKeyBlob,
+ const std::vector<uint8_t>& maskingKey, const vector<KeyParameter>& unwrappingParams);
+
+ std::tuple<std::unique_ptr<Item>, keymaster_error_t>
+ sendFinishImportWrappedKeyCmd(const vector<KeyParameter>& keyParams, KeyFormat keyFormat,
+ const std::vector<uint8_t>& secureKey,
+ const std::vector<uint8_t>& tag, const std::vector<uint8_t>& iv,
+ const std::vector<uint8_t>& wrappedKeyDescription,
+ int64_t passwordSid, int64_t biometricSid);
+
+ ScopedAStatus defaultHwInfo(KeyMintHardwareInfo* info);
+
+ const SecurityLevel securitylevel_;
+ const shared_ptr<JavacardSecureElement> card_;
+ CborConverter cbor_;
+};
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/ready_se/google/keymint/KM300/HAL/JavacardKeyMintOperation.cpp b/ready_se/google/keymint/KM300/HAL/JavacardKeyMintOperation.cpp
new file mode 100644
index 0000000..a46f066
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/JavacardKeyMintOperation.cpp
@@ -0,0 +1,300 @@
+/*
+ * Copyright 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" 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.
+ */
+
+#define LOG_TAG "javacard.strongbox.keymint.operation-impl"
+
+#include "JavacardKeyMintOperation.h"
+
+#include <KeyMintUtils.h>
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/secureclock/ISecureClock.h>
+#include <android-base/logging.h>
+
+#include "CborConverter.h"
+
+namespace aidl::android::hardware::security::keymint {
+using cppbor::Bstr;
+using cppbor::Uint;
+using secureclock::TimeStampToken;
+
+JavacardKeyMintOperation::~JavacardKeyMintOperation() {
+ if (opHandle_ != 0) {
+ JavacardKeyMintOperation::abort();
+ }
+}
+
+ScopedAStatus JavacardKeyMintOperation::updateAad(const vector<uint8_t>& input,
+ const optional<HardwareAuthToken>& authToken,
+ const optional<TimeStampToken>& timestampToken) {
+ cppbor::Array request;
+ request.add(Uint(opHandle_));
+ request.add(Bstr(input));
+ cbor_.addHardwareAuthToken(request, authToken.value_or(HardwareAuthToken()));
+ cbor_.addTimeStampToken(request, timestampToken.value_or(TimeStampToken()));
+ auto [item, err] = card_->sendRequest(Instruction::INS_UPDATE_AAD_OPERATION_CMD, request);
+ if (err != KM_ERROR_OK) {
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardKeyMintOperation::update(const vector<uint8_t>& input,
+ const optional<HardwareAuthToken>& authToken,
+ const optional<TimeStampToken>& timestampToken,
+ vector<uint8_t>* output) {
+ HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken());
+ TimeStampToken tToken = timestampToken.value_or(TimeStampToken());
+ DataView view = {.buffer = {}, .data = input, .start = 0, .length = input.size()};
+ keymaster_error_t err = bufferData(view);
+ if (err != KM_ERROR_OK) {
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ if (!(bufferingMode_ == BufferingMode::EC_NO_DIGEST ||
+ bufferingMode_ == BufferingMode::RSA_DECRYPT_OR_NO_DIGEST)) {
+ if (view.length > MAX_CHUNK_SIZE) {
+ err = updateInChunks(view, aToken, tToken, output);
+ if (err != KM_ERROR_OK) {
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ }
+ vector<uint8_t> remaining = popNextChunk(view, view.length);
+ err = sendUpdate(remaining, aToken, tToken, *output);
+ }
+ return km_utils::kmError2ScopedAStatus(err);
+}
+
+ScopedAStatus JavacardKeyMintOperation::finish(const optional<vector<uint8_t>>& input,
+ const optional<vector<uint8_t>>& signature,
+ const optional<HardwareAuthToken>& authToken,
+ const optional<TimeStampToken>& timestampToken,
+ const optional<vector<uint8_t>>& confirmationToken,
+ vector<uint8_t>* output) {
+ HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken());
+ TimeStampToken tToken = timestampToken.value_or(TimeStampToken());
+ const vector<uint8_t> confToken = confirmationToken.value_or(vector<uint8_t>());
+ const vector<uint8_t> inData = input.value_or(vector<uint8_t>());
+ DataView view = {.buffer = {}, .data = inData, .start = 0, .length = inData.size()};
+ const vector<uint8_t> sign = signature.value_or(vector<uint8_t>());
+ if (!(bufferingMode_ == BufferingMode::EC_NO_DIGEST ||
+ bufferingMode_ == BufferingMode::RSA_DECRYPT_OR_NO_DIGEST)) {
+ appendBufferedData(view);
+ if (view.length > MAX_CHUNK_SIZE) {
+ auto err = updateInChunks(view, aToken, tToken, output);
+ if (err != KM_ERROR_OK) {
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ }
+ } else {
+ keymaster_error_t err = bufferData(view);
+ if (err != KM_ERROR_OK) {
+ return km_utils::kmError2ScopedAStatus(err);
+ }
+ appendBufferedData(view);
+ }
+ vector<uint8_t> remaining = popNextChunk(view, view.length);
+ return km_utils::kmError2ScopedAStatus(
+ sendFinish(remaining, sign, aToken, tToken, confToken, *output));
+}
+
+ScopedAStatus JavacardKeyMintOperation::abort() {
+ Array request;
+ request.add(Uint(opHandle_));
+ auto [item, err] = card_->sendRequest(Instruction::INS_ABORT_OPERATION_CMD, request);
+ opHandle_ = 0;
+ buffer_.clear();
+ return km_utils::kmError2ScopedAStatus(err);
+}
+
+void JavacardKeyMintOperation::blockAlign(DataView& view, uint16_t blockSize) {
+ appendBufferedData(view);
+ uint16_t offset = getDataViewOffset(view, blockSize);
+ if (view.buffer.empty() && !view.data.empty()) {
+ buffer_.insert(buffer_.end(), view.data.begin() + offset, view.data.end());
+ } else if (view.data.empty() && !view.buffer.empty()) {
+ buffer_.insert(buffer_.end(), view.buffer.begin() + offset, view.buffer.end());
+ } else {
+ if (offset < view.buffer.size()) {
+ buffer_.insert(buffer_.end(), view.buffer.begin() + offset, view.buffer.end());
+ buffer_.insert(buffer_.end(), view.data.begin(), view.data.end());
+ } else {
+ offset = offset - view.buffer.size();
+ buffer_.insert(buffer_.end(), view.data.begin() + offset, view.data.end());
+ }
+ }
+ // adjust the view length by removing the buffered data size from it.
+ view.length = view.length - buffer_.size();
+}
+
+uint16_t JavacardKeyMintOperation::getDataViewOffset(DataView& view, uint16_t blockSize) {
+ uint16_t offset = 0;
+ uint16_t remaining = 0;
+ switch (bufferingMode_) {
+ case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED:
+ case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED:
+ offset = ((view.length / blockSize)) * blockSize;
+ remaining = (view.length % blockSize);
+ if (offset >= blockSize && remaining == 0) {
+ offset -= blockSize;
+ }
+ break;
+ case BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED:
+ case BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED:
+ offset = ((view.length / blockSize)) * blockSize;
+ break;
+ case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED:
+ if (view.length > macLength_) {
+ offset = (view.length - macLength_);
+ }
+ break;
+ default:
+ break;
+ }
+ return offset;
+}
+
+keymaster_error_t JavacardKeyMintOperation::bufferData(DataView& view) {
+ if (view.data.empty()) return KM_ERROR_OK; // nothing to buffer
+ switch (bufferingMode_) {
+ case BufferingMode::RSA_DECRYPT_OR_NO_DIGEST:
+ buffer_.insert(buffer_.end(), view.data.begin(), view.data.end());
+ if (buffer_.size() > RSA_BUFFER_SIZE) {
+ abort();
+ return KM_ERROR_INVALID_INPUT_LENGTH;
+ }
+ view.start = 0;
+ view.length = 0;
+ break;
+ case BufferingMode::EC_NO_DIGEST:
+ if (buffer_.size() < EC_BUFFER_SIZE) {
+ buffer_.insert(buffer_.end(), view.data.begin(), view.data.end());
+ // Truncate the buffered data if greater then allowed EC buffer size.
+ if (buffer_.size() > EC_BUFFER_SIZE) {
+ buffer_.erase(buffer_.begin() + EC_BUFFER_SIZE, buffer_.end());
+ }
+ }
+ view.start = 0;
+ view.length = 0;
+ break;
+ case BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED:
+ case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED:
+ blockAlign(view, AES_BLOCK_SIZE);
+ break;
+ case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED:
+ blockAlign(view, macLength_);
+ break;
+ case BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED:
+ case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED:
+ blockAlign(view, DES_BLOCK_SIZE);
+ break;
+ case BufferingMode::NONE:
+ break;
+ }
+ return KM_ERROR_OK;
+}
+
+// Incrementally send the request using multiple updates.
+keymaster_error_t JavacardKeyMintOperation::updateInChunks(DataView& view,
+ HardwareAuthToken& authToken,
+ TimeStampToken& timestampToken,
+ vector<uint8_t>* output) {
+ keymaster_error_t sendError = KM_ERROR_UNKNOWN_ERROR;
+ while (view.length > MAX_CHUNK_SIZE) {
+ vector<uint8_t> chunk = popNextChunk(view, MAX_CHUNK_SIZE);
+ sendError = sendUpdate(chunk, authToken, timestampToken, *output);
+ if (sendError != KM_ERROR_OK) {
+ return sendError;
+ }
+ // Clear tokens
+ if (!authToken.mac.empty()) authToken = HardwareAuthToken();
+ if (!timestampToken.mac.empty()) timestampToken = TimeStampToken();
+ }
+ return KM_ERROR_OK;
+}
+
+vector<uint8_t> JavacardKeyMintOperation::popNextChunk(DataView& view, uint32_t chunkSize) {
+ uint32_t start = view.start;
+ uint32_t end = start + ((view.length < chunkSize) ? view.length : chunkSize);
+ vector<uint8_t> chunk;
+ if (start < view.buffer.size()) {
+ if (end < view.buffer.size()) {
+ chunk = {view.buffer.begin() + start, view.buffer.begin() + end};
+ } else {
+ end = end - view.buffer.size();
+ chunk = {view.buffer.begin() + start, view.buffer.end()};
+ chunk.insert(chunk.end(), view.data.begin(), view.data.begin() + end);
+ }
+ } else {
+ start = start - view.buffer.size();
+ end = end - view.buffer.size();
+ chunk = {view.data.begin() + start, view.data.begin() + end};
+ }
+ view.start = view.start + chunk.size();
+ view.length = view.length - chunk.size();
+ return chunk;
+}
+
+keymaster_error_t JavacardKeyMintOperation::sendUpdate(const vector<uint8_t>& input,
+ const HardwareAuthToken& authToken,
+ const TimeStampToken& timestampToken,
+ vector<uint8_t>& output) {
+ if (input.empty()) {
+ return KM_ERROR_OK;
+ }
+ cppbor::Array request;
+ request.add(Uint(opHandle_));
+ request.add(Bstr(input));
+ cbor_.addHardwareAuthToken(request, authToken);
+ cbor_.addTimeStampToken(request, timestampToken);
+ auto [item, error] = card_->sendRequest(Instruction::INS_UPDATE_OPERATION_CMD, request);
+ if (error != KM_ERROR_OK) {
+ return error;
+ }
+ auto optTemp = cbor_.getByteArrayVec(item, 1);
+ if (!optTemp) {
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ output.insert(output.end(), optTemp.value().begin(), optTemp.value().end());
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t JavacardKeyMintOperation::sendFinish(const vector<uint8_t>& data,
+ const vector<uint8_t>& sign,
+ const HardwareAuthToken& authToken,
+ const TimeStampToken& timestampToken,
+ const vector<uint8_t>& confToken,
+ vector<uint8_t>& output) {
+ cppbor::Array request;
+ request.add(Uint(opHandle_));
+ request.add(Bstr(data));
+ request.add(Bstr(sign));
+ cbor_.addHardwareAuthToken(request, authToken);
+ cbor_.addTimeStampToken(request, timestampToken);
+ request.add(Bstr(confToken));
+
+ auto [item, err] = card_->sendRequest(Instruction::INS_FINISH_OPERATION_CMD, request);
+ if (err != KM_ERROR_OK) {
+ return err;
+ }
+ auto optTemp = cbor_.getByteArrayVec(item, 1);
+ if (!optTemp) {
+ return KM_ERROR_UNKNOWN_ERROR;
+ }
+ opHandle_ = 0;
+ output.insert(output.end(), optTemp.value().begin(), optTemp.value().end());
+ return KM_ERROR_OK;
+}
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/ready_se/google/keymint/KM300/HAL/JavacardKeyMintOperation.h b/ready_se/google/keymint/KM300/HAL/JavacardKeyMintOperation.h
new file mode 100644
index 0000000..c1d967a
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/JavacardKeyMintOperation.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright 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" 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.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
+#include <aidl/android/hardware/security/secureclock/ISecureClock.h>
+#include <hardware/keymaster_defs.h>
+
+#include "CborConverter.h"
+#include "JavacardSecureElement.h"
+
+#define AES_BLOCK_SIZE 16
+#define DES_BLOCK_SIZE 8
+#define RSA_BUFFER_SIZE 256
+#define EC_BUFFER_SIZE 32
+#define MAX_CHUNK_SIZE 256
+
+namespace aidl::android::hardware::security::keymint {
+using cppbor::Array;
+using cppbor::Item;
+using ::keymint::javacard::CborConverter;
+using ::keymint::javacard::Instruction;
+using ::keymint::javacard::JavacardSecureElement;
+using ::ndk::ScopedAStatus;
+using secureclock::TimeStampToken;
+using std::optional;
+using std::shared_ptr;
+using std::vector;
+
+// Bufferig modes for update
+enum class BufferingMode : int32_t {
+ NONE = 0, // Send everything to javacard - most of the assymteric operations
+ RSA_DECRYPT_OR_NO_DIGEST =
+ 1, // Buffer everything in update upto 256 bytes and send in finish. If
+ // input data is greater then 256 bytes then it is an error. Javacard
+ // will further check according to exact key size and crypto provider.
+ EC_NO_DIGEST = 2, // Buffer upto 65 bytes and then truncate. Javacard will further truncate
+ // upto exact keysize.
+ BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED = 3, // Buffer 16 bytes.
+ BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED = 4, // Buffer 16 bytes.
+ BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED = 5, // Buffer 8 bytes.
+ BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED = 6, // Buffer 8 bytes.
+ BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED = 7, // Buffer 16 bytes.
+
+};
+
+// The is the view in the input data being processed by update/finish funcion.
+
+struct DataView {
+ vector<uint8_t> buffer; // previously buffered data from cycle n-1
+ const vector<uint8_t>& data; // current data in cycle n.
+ uint32_t start; // start of the view
+ size_t length; // length of the view
+};
+
+class JavacardKeyMintOperation : public BnKeyMintOperation {
+ public:
+ explicit JavacardKeyMintOperation(keymaster_operation_handle_t opHandle,
+ BufferingMode bufferingMode, uint16_t macLength,
+ shared_ptr<JavacardSecureElement> card)
+ : buffer_(vector<uint8_t>()), bufferingMode_(bufferingMode), macLength_(macLength),
+ card_(card), opHandle_(opHandle) {}
+ virtual ~JavacardKeyMintOperation();
+
+ ScopedAStatus updateAad(const vector<uint8_t>& input,
+ const optional<HardwareAuthToken>& authToken,
+ const optional<TimeStampToken>& timestampToken) override;
+
+ ScopedAStatus update(const vector<uint8_t>& input, const optional<HardwareAuthToken>& authToken,
+ const optional<TimeStampToken>& timestampToken,
+ vector<uint8_t>* output) override;
+
+ ScopedAStatus finish(const optional<vector<uint8_t>>& input,
+ const optional<vector<uint8_t>>& signature,
+ const optional<HardwareAuthToken>& authToken,
+ const optional<TimeStampToken>& timestampToken,
+ const optional<vector<uint8_t>>& confirmationToken,
+ vector<uint8_t>* output) override;
+
+ ScopedAStatus abort() override;
+
+ private:
+ vector<uint8_t> popNextChunk(DataView& view, uint32_t chunkSize);
+
+ keymaster_error_t updateInChunks(DataView& data, HardwareAuthToken& authToken,
+ TimeStampToken& timestampToken, vector<uint8_t>* output);
+
+ keymaster_error_t sendFinish(const vector<uint8_t>& data, const vector<uint8_t>& signature,
+ const HardwareAuthToken& authToken,
+ const TimeStampToken& timestampToken,
+ const vector<uint8_t>& confToken, vector<uint8_t>& output);
+
+ keymaster_error_t sendUpdate(const vector<uint8_t>& data, const HardwareAuthToken& authToken,
+ const TimeStampToken& timestampToken, vector<uint8_t>& output);
+
+ inline void appendBufferedData(DataView& view) {
+ if (!buffer_.empty()) {
+ view.buffer = buffer_;
+ view.length = view.length + buffer_.size();
+ view.start = 0;
+ // view.buffer = insert(data.begin(), buffer_.begin(), buffer_.end());
+ buffer_.clear();
+ }
+ }
+
+ std::tuple<std::unique_ptr<Item>, keymaster_error_t> sendRequest(Instruction ins,
+ Array& request);
+ keymaster_error_t bufferData(DataView& data);
+ void blockAlign(DataView& data, uint16_t blockSize);
+ uint16_t getDataViewOffset(DataView& view, uint16_t blockSize);
+
+ vector<uint8_t> buffer_;
+ BufferingMode bufferingMode_;
+ uint16_t macLength_;
+ const shared_ptr<JavacardSecureElement> card_;
+ keymaster_operation_handle_t opHandle_;
+ CborConverter cbor_;
+};
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/ready_se/google/keymint/KM300/HAL/JavacardRemotelyProvisionedComponentDevice.cpp b/ready_se/google/keymint/KM300/HAL/JavacardRemotelyProvisionedComponentDevice.cpp
new file mode 100644
index 0000000..c79889f
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/JavacardRemotelyProvisionedComponentDevice.cpp
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2021, 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.
+ */
+
+#define LOG_TAG "javacard.keymint.device.rkp.strongbox-impl"
+
+#include "JavacardRemotelyProvisionedComponentDevice.h"
+
+#include <aidl/android/hardware/security/keymint/MacedPublicKey.h>
+
+#include <KeyMintUtils.h>
+#include <android-base/logging.h>
+#include <keymaster/cppcose/cppcose.h>
+#include <keymaster/remote_provisioning_utils.h>
+
+namespace aidl::android::hardware::security::keymint {
+using cppbor::Array;
+using cppbor::EncodedItem;
+using cppcose::kCoseMac0EntryCount;
+using cppcose::kCoseMac0Payload;
+using ::keymint::javacard::Instruction;
+using std::string;
+
+// RKP error codes defined in keymint applet.
+constexpr int32_t kStatusFailed = 32000;
+constexpr int32_t kStatusInvalidMac = 32001;
+constexpr int32_t kStatusProductionKeyInTestRequest = 32002;
+constexpr int32_t kStatusTestKeyInProductionRequest = 32003;
+constexpr int32_t kStatusInvalidEek = 32004;
+constexpr int32_t kStatusInvalidState = 32005;
+
+namespace {
+
+keymaster_error_t translateRkpErrorCode(keymaster_error_t error) {
+ switch (static_cast<int32_t>(-error)) {
+ case kStatusFailed:
+ case kStatusInvalidState:
+ return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_FAILED);
+ case kStatusInvalidMac:
+ return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_INVALID_MAC);
+ case kStatusProductionKeyInTestRequest:
+ return static_cast<keymaster_error_t>(
+ BnRemotelyProvisionedComponent::STATUS_PRODUCTION_KEY_IN_TEST_REQUEST);
+ case kStatusTestKeyInProductionRequest:
+ return static_cast<keymaster_error_t>(
+ BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST);
+ case kStatusInvalidEek:
+ return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
+ }
+ return error;
+}
+
+ScopedAStatus defaultHwInfo(RpcHardwareInfo* info) {
+ info->versionNumber = 3;
+ info->rpcAuthorName = "Google";
+ info->supportedEekCurve = RpcHardwareInfo::CURVE_NONE;
+ info->uniqueId = "Google Strongbox KeyMint 3";
+ info->supportedNumKeysInCsr = RpcHardwareInfo::MIN_SUPPORTED_NUM_KEYS_IN_CSR;
+ return ScopedAStatus::ok();
+}
+
+uint32_t coseKeyEncodedSize(const std::vector<MacedPublicKey>& keysToSign) {
+ uint32_t size = 0;
+ for (auto& macKey : keysToSign) {
+ auto [macedKeyItem, _, coseMacErrMsg] = cppbor::parse(macKey.macedKey);
+ if (!macedKeyItem || !macedKeyItem->asArray() ||
+ macedKeyItem->asArray()->size() != kCoseMac0EntryCount) {
+ LOG(ERROR) << "Invalid COSE_Mac0 structure";
+ return 0;
+ }
+ auto payload = macedKeyItem->asArray()->get(kCoseMac0Payload)->asBstr();
+ if (!payload) return 0;
+ size += payload->value().size();
+ }
+ return size;
+}
+
+} // namespace
+
+ScopedAStatus JavacardRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) {
+ auto [item, err] = card_->sendRequest(Instruction::INS_GET_RKP_HARDWARE_INFO);
+ std::optional<uint64_t> optVersionNumber;
+ std::optional<uint64_t> optSupportedEekCurve;
+ std::optional<string> optRpcAuthorName;
+ std::optional<string> optUniqueId;
+ std::optional<uint64_t> optMinSupportedKeysInCsr;
+ if (err != KM_ERROR_OK || !(optVersionNumber = cbor_.getUint64(item, 1)) ||
+ !(optRpcAuthorName = cbor_.getByteArrayStr(item, 2)) ||
+ !(optSupportedEekCurve = cbor_.getUint64(item, 3)) ||
+ !(optUniqueId = cbor_.getByteArrayStr(item, 4)) ||
+ !(optMinSupportedKeysInCsr = cbor_.getUint64(item, 5))) {
+ LOG(ERROR) << "Error in response of getHardwareInfo.";
+ LOG(INFO) << "Returning defaultHwInfo in getHardwareInfo.";
+ return defaultHwInfo(info);
+ }
+ info->rpcAuthorName = std::move(optRpcAuthorName.value());
+ info->versionNumber = static_cast<int32_t>(std::move(optVersionNumber.value()));
+ info->supportedEekCurve = static_cast<int32_t>(std::move(optSupportedEekCurve.value()));
+ info->uniqueId = std::move(optUniqueId.value());
+ info->supportedNumKeysInCsr = static_cast<int32_t>(std::move(optMinSupportedKeysInCsr.value()));
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardRemotelyProvisionedComponentDevice::generateEcdsaP256KeyPair(
+ bool testMode, MacedPublicKey* macedPublicKey, std::vector<uint8_t>* privateKeyHandle) {
+ cppbor::Array array;
+ array.add(testMode);
+ auto [item, err] = card_->sendRequest(Instruction::INS_GENERATE_RKP_KEY_CMD, array);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending generateEcdsaP256KeyPair.";
+ return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err));
+ }
+ std::optional<std::vector<uint8_t>> optMacedKey;
+ std::optional<std::vector<uint8_t>> optPKeyHandle;
+ if (!(optMacedKey = cbor_.getByteArrayVec(item, 1)) ||
+ !(optPKeyHandle = cbor_.getByteArrayVec(item, 2))) {
+ LOG(ERROR) << "Error in decoding the response in generateEcdsaP256KeyPair.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ *privateKeyHandle = std::move(optPKeyHandle.value());
+ macedPublicKey->macedKey = std::move(optMacedKey.value());
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardRemotelyProvisionedComponentDevice::beginSendData(
+ const std::vector<MacedPublicKey>& keysToSign, const std::vector<uint8_t>& challenge,
+ DeviceInfo* deviceInfo, uint32_t* version, std::string* certificateType) {
+ uint32_t totalEncodedSize = coseKeyEncodedSize(keysToSign);
+ cppbor::Array array;
+ array.add(keysToSign.size());
+ array.add(totalEncodedSize);
+ array.add(challenge);
+ auto [item, err] = card_->sendRequest(Instruction::INS_BEGIN_SEND_DATA_CMD, array);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in beginSendData.";
+ return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err));
+ }
+ auto optDecodedDeviceInfo = cbor_.getByteArrayVec(item, 1);
+ if (!optDecodedDeviceInfo) {
+ LOG(ERROR) << "Error in decoding deviceInfo response in beginSendData.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ deviceInfo->deviceInfo = std::move(optDecodedDeviceInfo.value());
+ auto optVersion = cbor_.getUint64(item, 2);
+ if (!optVersion) {
+ LOG(ERROR) << "Error in decoding version in beginSendData.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ *version = optVersion.value();
+ auto optCertType = cbor_.getTextStr(item, 3);
+ if (!optCertType) {
+ LOG(ERROR) << "Error in decoding cert type in beginSendData.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ *certificateType = std::move(optCertType.value());
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardRemotelyProvisionedComponentDevice::updateMacedKey(
+ const std::vector<MacedPublicKey>& keysToSign, Array& coseKeys) {
+ for (auto& macedPublicKey : keysToSign) {
+ cppbor::Array array;
+ array.add(EncodedItem(macedPublicKey.macedKey));
+ auto [item, err] = card_->sendRequest(Instruction::INS_UPDATE_KEY_CMD, array);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in updateMacedKey.";
+ return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err));
+ }
+ auto coseKeyData = cbor_.getByteArrayVec(item, 1);
+ coseKeys.add(EncodedItem(coseKeyData.value()));
+ }
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardRemotelyProvisionedComponentDevice::finishSendData(
+ std::vector<uint8_t>& coseEncryptProtectedHeader, std::vector<uint8_t>& signature,
+ uint32_t& version, uint32_t& respFlag) {
+ auto [item, err] = card_->sendRequest(Instruction::INS_FINISH_SEND_DATA_CMD);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in finishSendData.";
+ return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err));
+ }
+ auto optCEncryptProtectedHeader = cbor_.getByteArrayVec(item, 1);
+ auto optSignature = cbor_.getByteArrayVec(item, 2);
+ auto optVersion = cbor_.getUint64(item, 3);
+ auto optRespFlag = cbor_.getUint64(item, 4);
+ if (!optCEncryptProtectedHeader || !optSignature || !optVersion || !optRespFlag) {
+ LOG(ERROR) << "Error in decoding response in finishSendData.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+
+ coseEncryptProtectedHeader = std::move(optCEncryptProtectedHeader.value());
+ signature.insert(signature.end(), optSignature->begin(), optSignature->end());
+ version = std::move(optVersion.value());
+ respFlag = std::move(optRespFlag.value());
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus
+JavacardRemotelyProvisionedComponentDevice::getDiceCertChain(std::vector<uint8_t>& diceCertChain) {
+ uint32_t respFlag = 0;
+ do {
+ auto [item, err] = card_->sendRequest(Instruction::INS_GET_DICE_CERT_CHAIN_CMD);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in getDiceCertChain.";
+ return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err));
+ }
+ auto optDiceCertChain = cbor_.getByteArrayVec(item, 1);
+ auto optRespFlag = cbor_.getUint64(item, 2);
+ if (!optDiceCertChain || !optRespFlag) {
+ LOG(ERROR) << "Error in decoding response in getDiceCertChain.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ respFlag = optRespFlag.value();
+ diceCertChain.insert(diceCertChain.end(), optDiceCertChain->begin(),
+ optDiceCertChain->end());
+ } while (respFlag != 0);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus
+JavacardRemotelyProvisionedComponentDevice::getUdsCertsChain(std::vector<uint8_t>& udsCertsChain) {
+ uint32_t respFlag = 0;
+ do {
+ auto [item, err] = card_->sendRequest(Instruction::INS_GET_UDS_CERTS_CMD);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in getUdsCertsChain.";
+ return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err));
+ }
+ auto optUdsCertData = cbor_.getByteArrayVec(item, 1);
+ auto optRespFlag = cbor_.getUint64(item, 2);
+ if (!optUdsCertData || !optRespFlag) {
+ LOG(ERROR) << "Error in decoding og response in getUdsCertsChain.";
+ return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ respFlag = optRespFlag.value();
+ udsCertsChain.insert(udsCertsChain.end(), optUdsCertData->begin(), optUdsCertData->end());
+ } while (respFlag != 0);
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus JavacardRemotelyProvisionedComponentDevice::generateCertificateRequest(
+ bool, const std::vector<MacedPublicKey>&, const std::vector<uint8_t>&,
+ const std::vector<uint8_t>&, DeviceInfo*, ProtectedData*, std::vector<uint8_t>*) {
+ return km_utils::kmError2ScopedAStatus(static_cast<keymaster_error_t>(STATUS_REMOVED));
+}
+
+ScopedAStatus JavacardRemotelyProvisionedComponentDevice::generateCertificateRequestV2(
+ const std::vector<MacedPublicKey>& keysToSign, const std::vector<uint8_t>& challenge,
+ std::vector<uint8_t>* csr) {
+ uint32_t version;
+ uint32_t csrPayloadSchemaVersion;
+ std::string certificateType;
+ uint32_t respFlag;
+ DeviceInfo deviceInfo;
+ Array coseKeys;
+ std::vector<uint8_t> protectedHeader;
+ cppbor::Map coseEncryptUnProtectedHeader;
+ std::vector<uint8_t> signature;
+ std::vector<uint8_t> diceCertChain;
+ std::vector<uint8_t> udsCertChain;
+ cppbor::Array payLoad;
+
+ auto ret = beginSendData(keysToSign, challenge, &deviceInfo, &csrPayloadSchemaVersion,
+ &certificateType);
+ if (!ret.isOk()) return ret;
+
+ ret = updateMacedKey(keysToSign, coseKeys);
+ if (!ret.isOk()) return ret;
+
+ ret = finishSendData(protectedHeader, signature, version, respFlag);
+ if (!ret.isOk()) return ret;
+
+ ret = getUdsCertsChain(udsCertChain);
+ if (!ret.isOk()) return ret;
+
+ ret = getDiceCertChain(diceCertChain);
+ if (!ret.isOk()) return ret;
+
+ auto payload = cppbor::Array()
+ .add(csrPayloadSchemaVersion)
+ .add(certificateType)
+ .add(EncodedItem(deviceInfo.deviceInfo)) // deviceinfo
+ .add(std::move(coseKeys)) // KeysToSign
+ .encode();
+
+ auto signDataPayload = cppbor::Array()
+ .add(challenge) // Challenge
+ .add(std::move(payload))
+ .encode();
+
+ auto signedData = cppbor::Array()
+ .add(std::move(protectedHeader))
+ .add(cppbor::Map() /* unprotected parameters */)
+ .add(std::move(signDataPayload))
+ .add(std::move(signature));
+
+ *csr = cppbor::Array()
+ .add(version)
+ .add(EncodedItem(udsCertChain))
+ .add(EncodedItem(diceCertChain))
+ .add(std::move(signedData))
+ .encode();
+
+ return ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/ready_se/google/keymint/KM300/HAL/JavacardRemotelyProvisionedComponentDevice.h b/ready_se/google/keymint/KM300/HAL/JavacardRemotelyProvisionedComponentDevice.h
new file mode 100644
index 0000000..5ce8cd7
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/JavacardRemotelyProvisionedComponentDevice.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2021, 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.
+ */
+
+#pragma once
+
+#include <cppbor.h>
+
+#include <aidl/android/hardware/security/keymint/BnRemotelyProvisionedComponent.h>
+#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
+#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
+
+#include <keymaster/UniquePtr.h>
+#include <keymaster/android_keymaster.h>
+
+#include "CborConverter.h"
+#include "JavacardSecureElement.h"
+
+namespace aidl::android::hardware::security::keymint {
+using ::keymint::javacard::CborConverter;
+using ::keymint::javacard::JavacardSecureElement;
+using ndk::ScopedAStatus;
+using std::shared_ptr;
+
+class JavacardRemotelyProvisionedComponentDevice : public BnRemotelyProvisionedComponent {
+ public:
+ explicit JavacardRemotelyProvisionedComponentDevice(shared_ptr<JavacardSecureElement> card)
+ : card_(card) {}
+
+ virtual ~JavacardRemotelyProvisionedComponentDevice() = default;
+
+ ScopedAStatus getHardwareInfo(RpcHardwareInfo* info) override;
+
+ ScopedAStatus generateEcdsaP256KeyPair(bool testMode, MacedPublicKey* macedPublicKey,
+ std::vector<uint8_t>* privateKeyHandle) override;
+
+ ScopedAStatus generateCertificateRequest(bool testMode,
+ const std::vector<MacedPublicKey>& keysToSign,
+ const std::vector<uint8_t>& endpointEncCertChain,
+ const std::vector<uint8_t>& challenge,
+ DeviceInfo* deviceInfo, ProtectedData* protectedData,
+ std::vector<uint8_t>* keysToSignMac) override;
+
+ ScopedAStatus generateCertificateRequestV2(const std::vector<MacedPublicKey>& keysToSign,
+ const std::vector<uint8_t>& challenge,
+ std::vector<uint8_t>* csr) override;
+
+ private:
+ ScopedAStatus beginSendData(const std::vector<MacedPublicKey>& keysToSign,
+ const std::vector<uint8_t>& challenge, DeviceInfo* deviceInfo,
+ uint32_t* version, std::string* certificateType);
+
+ ScopedAStatus updateMacedKey(const std::vector<MacedPublicKey>& keysToSign,
+ cppbor::Array& coseKeys);
+
+ ScopedAStatus finishSendData(std::vector<uint8_t>& coseEncryptProtectedHeader,
+ std::vector<uint8_t>& signature, uint32_t& version,
+ uint32_t& respFlag);
+
+ ScopedAStatus getResponse(std::vector<uint8_t>& partialCipheredData,
+ cppbor::Array& recepientStructure, uint32_t& respFlag);
+ ScopedAStatus getDiceCertChain(std::vector<uint8_t>& diceCertChain);
+ ScopedAStatus getUdsCertsChain(std::vector<uint8_t>& udsCertsChain);
+ std::shared_ptr<JavacardSecureElement> card_;
+ CborConverter cbor_;
+};
+
+} // namespace aidl::android::hardware::security::keymint
diff --git a/ready_se/google/keymint/KM300/HAL/JavacardSecureElement.cpp b/ready_se/google/keymint/KM300/HAL/JavacardSecureElement.cpp
new file mode 100644
index 0000000..7c4f038
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/JavacardSecureElement.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright 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" 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.
+ */
+
+#define LOG_TAG "javacard.keymint.device.strongbox-impl"
+#include "JavacardSecureElement.h"
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <memory>
+#include <regex.h>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <keymaster/android_keymaster_messages.h>
+
+#include "keymint_utils.h"
+
+namespace keymint::javacard {
+
+keymaster_error_t JavacardSecureElement::initializeJavacard() {
+ Array request;
+ request.add(Uint(getOsVersion()));
+ request.add(Uint(getOsPatchlevel()));
+ request.add(Uint(getVendorPatchlevel()));
+ auto [item, err] = sendRequest(Instruction::INS_INIT_STRONGBOX_CMD, request);
+ return err;
+}
+
+keymaster_error_t JavacardSecureElement::sendEarlyBootEndedEvent(bool eventTriggered) {
+ isEarlyBootEventPending |= eventTriggered;
+ if (!isEarlyBootEventPending) {
+ return KM_ERROR_OK;
+ }
+ auto [item, err] = sendRequest(Instruction::INS_EARLY_BOOT_ENDED_CMD);
+ if (err != KM_ERROR_OK) {
+ // Incase of failure cache the event and send in the next immediate request to Applet.
+ isEarlyBootEventPending = true;
+ return err;
+ }
+ isEarlyBootEventPending = false;
+ return KM_ERROR_OK;
+}
+
+keymaster_error_t JavacardSecureElement::constructApduMessage(Instruction& ins,
+ std::vector<uint8_t>& inputData,
+ std::vector<uint8_t>& apduOut) {
+ apduOut.push_back(static_cast<uint8_t>(APDU_CLS)); // CLS
+ apduOut.push_back(static_cast<uint8_t>(ins)); // INS
+ apduOut.push_back(static_cast<uint8_t>(APDU_P1)); // P1
+ apduOut.push_back(static_cast<uint8_t>(APDU_P2)); // P2
+
+ if (USHRT_MAX >= inputData.size()) {
+ // Send extended length APDU always as response size is not known to HAL.
+ // Case 1: Lc > 0 CLS | INS | P1 | P2 | 00 | 2 bytes of Lc | CommandData | 2 bytes of Le
+ // all set to 00. Case 2: Lc = 0 CLS | INS | P1 | P2 | 3 bytes of Le all set to 00.
+ // Extended length 3 bytes, starts with 0x00
+ apduOut.push_back(static_cast<uint8_t>(0x00));
+ if (inputData.size() > 0) {
+ apduOut.push_back(static_cast<uint8_t>(inputData.size() >> 8));
+ apduOut.push_back(static_cast<uint8_t>(inputData.size() & 0xFF));
+ // Data
+ apduOut.insert(apduOut.end(), inputData.begin(), inputData.end());
+ }
+ // Expected length of output.
+ // Accepting complete length of output every time.
+ apduOut.push_back(static_cast<uint8_t>(0x00));
+ apduOut.push_back(static_cast<uint8_t>(0x00));
+ } else {
+ LOG(ERROR) << "Error in constructApduMessage.";
+ return (KM_ERROR_INVALID_INPUT_LENGTH);
+ }
+ return (KM_ERROR_OK); // success
+}
+
+keymaster_error_t JavacardSecureElement::sendData(Instruction ins, std::vector<uint8_t>& inData,
+ std::vector<uint8_t>& response) {
+ keymaster_error_t ret = KM_ERROR_UNKNOWN_ERROR;
+ std::vector<uint8_t> apdu;
+
+ ret = constructApduMessage(ins, inData, apdu);
+
+ if (ret != KM_ERROR_OK) {
+ return ret;
+ }
+
+ ret = transport_->sendData(apdu, response);
+ if (ret != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending data in sendData. " << static_cast<int>(ret);
+ return ret;
+ }
+
+ // Response size should be greater than 2. Cbor output data followed by two bytes of APDU
+ // status.
+ if ((response.size() <= 2) || (getApduStatus(response) != APDU_RESP_STATUS_OK)) {
+ LOG(ERROR) << "Response of the sendData is wrong: response size = " << response.size()
+ << " apdu status = " << getApduStatus(response);
+ return (KM_ERROR_UNKNOWN_ERROR);
+ }
+ // remove the status bytes
+ response.pop_back();
+ response.pop_back();
+ return (KM_ERROR_OK); // success
+}
+
+std::tuple<std::unique_ptr<Item>, keymaster_error_t>
+JavacardSecureElement::sendRequest(Instruction ins, Array& request) {
+ vector<uint8_t> response;
+ // encode request
+ std::vector<uint8_t> command = request.encode();
+ auto sendError = sendData(ins, command, response);
+ if (sendError != KM_ERROR_OK) {
+ return {unique_ptr<Item>(nullptr), sendError};
+ }
+ // decode the response and send that back
+ return cbor_.decodeData(response);
+}
+
+std::tuple<std::unique_ptr<Item>, keymaster_error_t>
+JavacardSecureElement::sendRequest(Instruction ins, std::vector<uint8_t>& command) {
+ vector<uint8_t> response;
+ auto sendError = sendData(ins, command, response);
+ if (sendError != KM_ERROR_OK) {
+ return {unique_ptr<Item>(nullptr), sendError};
+ }
+ // decode the response and send that back
+ return cbor_.decodeData(response);
+}
+
+std::tuple<std::unique_ptr<Item>, keymaster_error_t>
+JavacardSecureElement::sendRequest(Instruction ins) {
+ vector<uint8_t> response;
+ vector<uint8_t> emptyRequest;
+ auto sendError = sendData(ins, emptyRequest, response);
+ if (sendError != KM_ERROR_OK) {
+ return {unique_ptr<Item>(nullptr), sendError};
+ }
+ // decode the response and send that back
+ return cbor_.decodeData(response);
+}
+
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM300/HAL/JavacardSecureElement.h b/ready_se/google/keymint/KM300/HAL/JavacardSecureElement.h
new file mode 100644
index 0000000..8ba0a44
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/JavacardSecureElement.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 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" 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.
+ */
+
+#pragma once
+
+#include <ITransport.h>
+
+#include "CborConverter.h"
+
+#define APDU_CLS 0x80
+#define APDU_P1 0x60
+#define APDU_P2 0x00
+#define APDU_RESP_STATUS_OK 0x9000
+
+#define KEYMINT_CMD_APDU_START 0x20
+
+namespace keymint::javacard {
+using std::shared_ptr;
+using std::vector;
+
+enum class Instruction {
+ // Keymaster commands
+ INS_GENERATE_KEY_CMD = KEYMINT_CMD_APDU_START + 1,
+ INS_IMPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 2,
+ INS_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 3,
+ INS_EXPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 4,
+ INS_ATTEST_KEY_CMD = KEYMINT_CMD_APDU_START + 5,
+ INS_UPGRADE_KEY_CMD = KEYMINT_CMD_APDU_START + 6,
+ INS_DELETE_KEY_CMD = KEYMINT_CMD_APDU_START + 7,
+ INS_DELETE_ALL_KEYS_CMD = KEYMINT_CMD_APDU_START + 8,
+ INS_ADD_RNG_ENTROPY_CMD = KEYMINT_CMD_APDU_START + 9,
+ INS_COMPUTE_SHARED_SECRET_CMD = KEYMINT_CMD_APDU_START + 10,
+ INS_DESTROY_ATT_IDS_CMD = KEYMINT_CMD_APDU_START + 11,
+ INS_VERIFY_AUTHORIZATION_CMD = KEYMINT_CMD_APDU_START + 12,
+ INS_GET_SHARED_SECRET_PARAM_CMD = KEYMINT_CMD_APDU_START + 13,
+ INS_GET_KEY_CHARACTERISTICS_CMD = KEYMINT_CMD_APDU_START + 14,
+ INS_GET_HW_INFO_CMD = KEYMINT_CMD_APDU_START + 15,
+ INS_BEGIN_OPERATION_CMD = KEYMINT_CMD_APDU_START + 16,
+ INS_UPDATE_OPERATION_CMD = KEYMINT_CMD_APDU_START + 17,
+ INS_FINISH_OPERATION_CMD = KEYMINT_CMD_APDU_START + 18,
+ INS_ABORT_OPERATION_CMD = KEYMINT_CMD_APDU_START + 19,
+ INS_DEVICE_LOCKED_CMD = KEYMINT_CMD_APDU_START + 20,
+ INS_EARLY_BOOT_ENDED_CMD = KEYMINT_CMD_APDU_START + 21,
+ INS_GET_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 22,
+ INS_UPDATE_AAD_OPERATION_CMD = KEYMINT_CMD_APDU_START + 23,
+ INS_BEGIN_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 24,
+ INS_FINISH_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 25,
+ INS_INIT_STRONGBOX_CMD = KEYMINT_CMD_APDU_START + 26,
+ // RKP Commands
+ INS_GET_RKP_HARDWARE_INFO = KEYMINT_CMD_APDU_START + 27,
+ INS_GENERATE_RKP_KEY_CMD = KEYMINT_CMD_APDU_START + 28,
+ INS_BEGIN_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 29,
+ INS_UPDATE_KEY_CMD = KEYMINT_CMD_APDU_START + 30,
+ INS_UPDATE_EEK_CHAIN_CMD = KEYMINT_CMD_APDU_START + 31,
+ INS_UPDATE_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 32,
+ INS_FINISH_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 33,
+ INS_GET_RESPONSE_CMD = KEYMINT_CMD_APDU_START + 34,
+ INS_GET_UDS_CERTS_CMD = KEYMINT_CMD_APDU_START + 35,
+ INS_GET_DICE_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 36,
+ // SE ROT Commands
+ INS_GET_ROT_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 45,
+ INS_GET_ROT_DATA_CMD = KEYMINT_CMD_APDU_START + 46,
+ INS_SEND_ROT_DATA_CMD = KEYMINT_CMD_APDU_START + 47,
+};
+
+class JavacardSecureElement {
+ public:
+ explicit JavacardSecureElement(shared_ptr<ITransport> transport, uint32_t osVersion,
+ uint32_t osPatchLevel, uint32_t vendorPatchLevel)
+ : transport_(transport), osVersion_(osVersion), osPatchLevel_(osPatchLevel),
+ vendorPatchLevel_(vendorPatchLevel), isEarlyBootEventPending(false) {
+ transport_->openConnection();
+ }
+ virtual ~JavacardSecureElement() { transport_->closeConnection(); }
+
+ std::tuple<std::unique_ptr<Item>, keymaster_error_t> sendRequest(Instruction ins,
+ Array& request);
+ std::tuple<std::unique_ptr<Item>, keymaster_error_t> sendRequest(Instruction ins);
+ std::tuple<std::unique_ptr<Item>, keymaster_error_t> sendRequest(Instruction ins,
+ std::vector<uint8_t>& command);
+
+ keymaster_error_t sendData(Instruction ins, std::vector<uint8_t>& inData,
+ std::vector<uint8_t>& response);
+
+ keymaster_error_t constructApduMessage(Instruction& ins, std::vector<uint8_t>& inputData,
+ std::vector<uint8_t>& apduOut);
+ keymaster_error_t initializeJavacard();
+ keymaster_error_t sendEarlyBootEndedEvent(bool eventTriggered);
+ inline uint16_t getApduStatus(std::vector<uint8_t>& inputData) {
+ // Last two bytes are the status SW0SW1
+ uint8_t SW0 = inputData.at(inputData.size() - 2);
+ uint8_t SW1 = inputData.at(inputData.size() - 1);
+ return (SW0 << 8 | SW1);
+ }
+
+ shared_ptr<ITransport> transport_;
+ uint32_t osVersion_;
+ uint32_t osPatchLevel_;
+ uint32_t vendorPatchLevel_;
+ bool isEarlyBootEventPending;
+ CborConverter cbor_;
+};
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM300/HAL/JavacardSharedSecret.cpp b/ready_se/google/keymint/KM300/HAL/JavacardSharedSecret.cpp
new file mode 100644
index 0000000..c5cf9a2
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/JavacardSharedSecret.cpp
@@ -0,0 +1,61 @@
+#define LOG_TAG "javacard.strongbox.keymint.operation-impl"
+#include "JavacardSharedSecret.h"
+
+#include <android-base/logging.h>
+
+#include <KeyMintUtils.h>
+
+namespace aidl::android::hardware::security::sharedsecret {
+using ::keymint::javacard::Instruction;
+
+ScopedAStatus JavacardSharedSecret::getSharedSecretParameters(SharedSecretParameters* params) {
+ auto error = card_->initializeJavacard();
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in initializing javacard.";
+ return keymint::km_utils::kmError2ScopedAStatus(error);
+ }
+ auto [item, err] = card_->sendRequest(Instruction::INS_GET_SHARED_SECRET_PARAM_CMD);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in getSharedSecretParameters.";
+ return keymint::km_utils::kmError2ScopedAStatus(err);
+ }
+ auto optSSParams = cbor_.getSharedSecretParameters(item, 1);
+ if (!optSSParams) {
+ LOG(ERROR) << "Error in sending in getSharedSecretParameters.";
+ return keymint::km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ *params = std::move(optSSParams.value());
+ return ScopedAStatus::ok();
+}
+
+ScopedAStatus
+JavacardSharedSecret::computeSharedSecret(const std::vector<SharedSecretParameters>& params,
+ std::vector<uint8_t>* secret) {
+
+ auto error = card_->sendEarlyBootEndedEvent(false);
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending earlyBoot event javacard.";
+ return keymint::km_utils::kmError2ScopedAStatus(error);
+ }
+ error = card_->initializeJavacard();
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in initializing javacard.";
+ return keymint::km_utils::kmError2ScopedAStatus(error);
+ }
+ cppbor::Array request;
+ cbor_.addSharedSecretParameters(request, params);
+ auto [item, err] = card_->sendRequest(Instruction::INS_COMPUTE_SHARED_SECRET_CMD, request);
+ if (err != KM_ERROR_OK) {
+ LOG(ERROR) << "Error in sending in computeSharedSecret.";
+ return keymint::km_utils::kmError2ScopedAStatus(err);
+ }
+ auto optSecret = cbor_.getByteArrayVec(item, 1);
+ if (!optSecret) {
+ LOG(ERROR) << "Error in decoding the response in computeSharedSecret.";
+ return keymint::km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
+ }
+ *secret = std::move(optSecret.value());
+ return ScopedAStatus::ok();
+}
+
+} // namespace aidl::android::hardware::security::sharedsecret
diff --git a/ready_se/google/keymint/KM300/HAL/JavacardSharedSecret.h b/ready_se/google/keymint/KM300/HAL/JavacardSharedSecret.h
new file mode 100644
index 0000000..340853a
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/JavacardSharedSecret.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include <aidl/android/hardware/security/sharedsecret/BnSharedSecret.h>
+#include <aidl/android/hardware/security/sharedsecret/SharedSecretParameters.h>
+
+#include "CborConverter.h"
+#include "JavacardSecureElement.h"
+
+namespace aidl::android::hardware::security::sharedsecret {
+using ::keymint::javacard::CborConverter;
+using ::keymint::javacard::JavacardSecureElement;
+using ndk::ScopedAStatus;
+using std::shared_ptr;
+using std::vector;
+
+class JavacardSharedSecret : public BnSharedSecret {
+ public:
+ explicit JavacardSharedSecret(shared_ptr<JavacardSecureElement> card) : card_(card) {}
+ virtual ~JavacardSharedSecret() {}
+
+ ScopedAStatus getSharedSecretParameters(SharedSecretParameters* params) override;
+
+ ScopedAStatus computeSharedSecret(const std::vector<SharedSecretParameters>& params,
+ std::vector<uint8_t>* secret) override;
+
+ private:
+ shared_ptr<JavacardSecureElement> card_;
+ CborConverter cbor_;
+};
+
+} // namespace aidl::android::hardware::security::sharedsecret
diff --git a/ready_se/google/keymint/KM300/HAL/LICENSE b/ready_se/google/keymint/KM300/HAL/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/ready_se/google/keymint/KM300/HAL/METADATA b/ready_se/google/keymint/KM300/HAL/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/METADATA
@@ -0,0 +1,3 @@
+third_party {
+ license_type: NOTICE
+}
diff --git a/ready_se/google/keymint/KM300/HAL/OWNERS b/ready_se/google/keymint/KM300/HAL/OWNERS
new file mode 100644
index 0000000..0bd972b
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/OWNERS
@@ -0,0 +1,3 @@
+pathakc@google.com
+subrahmanyaman@google.com
+avinashh@google.com
diff --git a/ready_se/google/keymint/KM300/HAL/android.hardware.hardware_keystore.jc-strongbox-keymint3.xml b/ready_se/google/keymint/KM300/HAL/android.hardware.hardware_keystore.jc-strongbox-keymint3.xml
new file mode 100644
index 0000000..ca49e71
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/android.hardware.hardware_keystore.jc-strongbox-keymint3.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+<!-- Feature for devices with Keymaster in StrongBox. -->
+<permissions>
+ <feature name="android.hardware.strongbox_keystore" version="300"/>
+ <feature name="android.hardware.keystore.app_attest_key" />
+</permissions>
diff --git a/ready_se/google/keymint/KM300/HAL/android.hardware.security.keymint3-service.strongbox.rc b/ready_se/google/keymint/KM300/HAL/android.hardware.security.keymint3-service.strongbox.rc
new file mode 100644
index 0000000..e1c1494
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/android.hardware.security.keymint3-service.strongbox.rc
@@ -0,0 +1,3 @@
+service vendor.keymint-strongbox /vendor/bin/hw/android.hardware.security.keymint3-service.strongbox
+ class early_hal
+ user jc_strongbox
diff --git a/ready_se/google/keymint/KM300/HAL/android.hardware.security.keymint3-service.strongbox.xml b/ready_se/google/keymint/KM300/HAL/android.hardware.security.keymint3-service.strongbox.xml
new file mode 100644
index 0000000..481f028
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/android.hardware.security.keymint3-service.strongbox.xml
@@ -0,0 +1,12 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.security.keymint</name>
+ <version>3</version>
+ <fqname>IKeyMintDevice/strongbox</fqname>
+ </hal>
+ <hal format="aidl">
+ <name>android.hardware.security.keymint</name>
+ <version>3</version>
+ <fqname>IRemotelyProvisionedComponent/strongbox</fqname>
+ </hal>
+</manifest>
diff --git a/ready_se/google/keymint/KM300/HAL/android.hardware.security.sharedsecret3-service.strongbox.xml b/ready_se/google/keymint/KM300/HAL/android.hardware.security.sharedsecret3-service.strongbox.xml
new file mode 100644
index 0000000..5492100
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/android.hardware.security.sharedsecret3-service.strongbox.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+ <hal format="aidl">
+ <name>android.hardware.security.sharedsecret</name>
+ <fqname>ISharedSecret/strongbox</fqname>
+ </hal>
+</manifest>
diff --git a/ready_se/google/keymint/KM300/HAL/keymint_utils.cpp b/ready_se/google/keymint/KM300/HAL/keymint_utils.cpp
new file mode 100644
index 0000000..f613eda
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/keymint_utils.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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" 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.
+ */
+#include "keymint_utils.h"
+
+#include <regex.h>
+
+#include <android-base/properties.h>
+
+namespace keymint::javacard {
+
+namespace {
+
+constexpr char kPlatformVersionProp[] = "ro.build.version.release";
+constexpr char kPlatformVersionRegex[] = "^([0-9]{1,2})(\\.([0-9]{1,2}))?(\\.([0-9]{1,2}))?";
+constexpr size_t kMajorVersionMatch = 1;
+constexpr size_t kMinorVersionMatch = 3;
+constexpr size_t kSubminorVersionMatch = 5;
+constexpr size_t kPlatformVersionMatchCount = kSubminorVersionMatch + 1;
+
+constexpr char kPlatformPatchlevelProp[] = "ro.build.version.security_patch";
+constexpr char kVendorPatchlevelProp[] = "ro.vendor.build.security_patch";
+constexpr char kPatchlevelRegex[] = "^([0-9]{4})-([0-9]{2})-([0-9]{2})$";
+constexpr size_t kYearMatch = 1;
+constexpr size_t kMonthMatch = 2;
+constexpr size_t kDayMatch = 3;
+constexpr size_t kPatchlevelMatchCount = kDayMatch + 1;
+
+uint32_t match_to_uint32(const char* expression, const regmatch_t& match) {
+ if (match.rm_so == -1) return 0;
+
+ size_t len = match.rm_eo - match.rm_so;
+ std::string s(expression + match.rm_so, len);
+ return std::stoul(s);
+}
+
+std::string wait_and_get_property(const char* prop) {
+ std::string prop_value;
+ while (!::android::base::WaitForPropertyCreation(prop))
+ ;
+ prop_value = ::android::base::GetProperty(prop, "" /* default */);
+ return prop_value;
+}
+
+uint32_t getOsVersion(const char* version_str) {
+ regex_t regex;
+ if (regcomp(&regex, kPlatformVersionRegex, REG_EXTENDED)) {
+ return 0;
+ }
+
+ regmatch_t matches[kPlatformVersionMatchCount];
+ int not_match =
+ regexec(&regex, version_str, kPlatformVersionMatchCount, matches, 0 /* flags */);
+ regfree(&regex);
+ if (not_match) {
+ return 0;
+ }
+
+ uint32_t major = match_to_uint32(version_str, matches[kMajorVersionMatch]);
+ uint32_t minor = match_to_uint32(version_str, matches[kMinorVersionMatch]);
+ uint32_t subminor = match_to_uint32(version_str, matches[kSubminorVersionMatch]);
+
+ return (major * 100 + minor) * 100 + subminor;
+}
+
+enum class PatchlevelOutput { kYearMonthDay, kYearMonth };
+
+uint32_t getPatchlevel(const char* patchlevel_str, PatchlevelOutput detail) {
+ regex_t regex;
+ if (regcomp(&regex, kPatchlevelRegex, REG_EXTENDED) != 0) {
+ return 0;
+ }
+
+ regmatch_t matches[kPatchlevelMatchCount];
+ int not_match = regexec(&regex, patchlevel_str, kPatchlevelMatchCount, matches, 0 /* flags */);
+ regfree(&regex);
+ if (not_match) {
+ return 0;
+ }
+
+ uint32_t year = match_to_uint32(patchlevel_str, matches[kYearMatch]);
+ uint32_t month = match_to_uint32(patchlevel_str, matches[kMonthMatch]);
+
+ if (month < 1 || month > 12) {
+ return 0;
+ }
+
+ switch (detail) {
+ case PatchlevelOutput::kYearMonthDay: {
+ uint32_t day = match_to_uint32(patchlevel_str, matches[kDayMatch]);
+ if (day < 1 || day > 31) {
+ return 0;
+ }
+ return year * 10000 + month * 100 + day;
+ }
+ case PatchlevelOutput::kYearMonth:
+ return year * 100 + month;
+ }
+}
+
+} // anonymous namespace
+
+uint32_t getOsVersion() {
+ std::string version = wait_and_get_property(kPlatformVersionProp);
+ return getOsVersion(version.c_str());
+}
+
+uint32_t getOsPatchlevel() {
+ std::string patchlevel = wait_and_get_property(kPlatformPatchlevelProp);
+ return getPatchlevel(patchlevel.c_str(), PatchlevelOutput::kYearMonth);
+}
+
+uint32_t getVendorPatchlevel() {
+ std::string patchlevel = wait_and_get_property(kVendorPatchlevelProp);
+ return getPatchlevel(patchlevel.c_str(), PatchlevelOutput::kYearMonthDay);
+}
+
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM300/HAL/keymint_utils.h b/ready_se/google/keymint/KM300/HAL/keymint_utils.h
new file mode 100644
index 0000000..65cda63
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/keymint_utils.h
@@ -0,0 +1,47 @@
+/*
+ * 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" 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.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+// #include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
+
+// namespace aidl::android::hardware::security::keymint {
+namespace keymint::javacard {
+
+using std::vector;
+
+inline static std::vector<uint8_t> blob2vector(const uint8_t* data, const size_t length) {
+ std::vector<uint8_t> result(data, data + length);
+ return result;
+}
+
+inline static std::vector<uint8_t> blob2vector(const std::string& value) {
+ vector<uint8_t> result(reinterpret_cast<const uint8_t*>(value.data()),
+ reinterpret_cast<const uint8_t*>(value.data()) + value.size());
+ return result;
+}
+
+// HardwareAuthToken vector2AuthToken(const vector<uint8_t>& buffer);
+// vector<uint8_t> authToken2vector(const HardwareAuthToken& token);
+
+uint32_t getOsVersion();
+uint32_t getOsPatchlevel();
+uint32_t getVendorPatchlevel();
+
+} // namespace keymint::javacard
diff --git a/ready_se/google/keymint/KM300/HAL/service.cpp b/ready_se/google/keymint/KM300/HAL/service.cpp
new file mode 100644
index 0000000..e83ee3d
--- /dev/null
+++ b/ready_se/google/keymint/KM300/HAL/service.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 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" 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.
+ */
+
+#define LOG_TAG "javacard.strongbox-service"
+
+#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "JavacardKeyMintDevice.h"
+#include "JavacardRemotelyProvisionedComponentDevice.h"
+#include "JavacardSecureElement.h"
+#include "JavacardSharedSecret.h"
+#include "OmapiTransport.h"
+#include "SocketTransport.h"
+#include "keymint_utils.h"
+
+using aidl::android::hardware::security::keymint::JavacardKeyMintDevice;
+using aidl::android::hardware::security::keymint::JavacardRemotelyProvisionedComponentDevice;
+using aidl::android::hardware::security::keymint::SecurityLevel;
+using aidl::android::hardware::security::sharedsecret::JavacardSharedSecret;
+using keymint::javacard::getOsPatchlevel;
+using keymint::javacard::getOsVersion;
+using keymint::javacard::getVendorPatchlevel;
+using keymint::javacard::ITransport;
+using keymint::javacard::JavacardSecureElement;
+using keymint::javacard::OmapiTransport;
+using keymint::javacard::SocketTransport;
+
+#define PROP_BUILD_QEMU "ro.kernel.qemu"
+#define PROP_BUILD_FINGERPRINT "ro.build.fingerprint"
+// Cuttlefish build fingerprint substring.
+#define CUTTLEFISH_FINGERPRINT_SS "aosp_cf_"
+
+template <typename T, class... Args> std::shared_ptr<T> addService(Args&&... args) {
+ std::shared_ptr<T> ser = ndk::SharedRefBase::make<T>(std::forward<Args>(args)...);
+ auto instanceName = std::string(T::descriptor) + "/strongbox";
+ LOG(INFO) << "adding javacard strongbox service instance: " << instanceName;
+ binder_status_t status =
+ AServiceManager_addService(ser->asBinder().get(), instanceName.c_str());
+ CHECK(status == STATUS_OK);
+ return ser;
+}
+
+std::shared_ptr<ITransport> getTransportInstance() {
+ bool isEmulator = false;
+ // Check if the current build is for emulator or device.
+ isEmulator = android::base::GetBoolProperty(PROP_BUILD_QEMU, false);
+ if (!isEmulator) {
+ std::string fingerprint = android::base::GetProperty(PROP_BUILD_FINGERPRINT, "");
+ if (!fingerprint.empty()) {
+ if (fingerprint.find(CUTTLEFISH_FINGERPRINT_SS, 0) != std::string::npos) {
+ isEmulator = true;
+ }
+ }
+ }
+
+ if (!isEmulator) {
+ return std::make_shared<OmapiTransport>();
+ } else {
+ return std::make_shared<SocketTransport>();
+ }
+}
+
+int main() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+ // Javacard Secure Element
+ std::shared_ptr<JavacardSecureElement> card = std::make_shared<JavacardSecureElement>(
+ getTransportInstance(), getOsVersion(), getOsPatchlevel(), getVendorPatchlevel());
+ // Add Keymint Service
+ addService<JavacardKeyMintDevice>(card);
+ // Add Shared Secret Service
+ addService<JavacardSharedSecret>(card);
+ // Add Remotely Provisioned Component Service
+ addService<JavacardRemotelyProvisionedComponentDevice>(card);
+
+ ABinderProcess_joinThreadPool();
+ return EXIT_FAILURE; // should not reach
+}
diff --git a/tools/ese_relay/ese_relay_fake.c b/tools/ese_relay/ese_relay_fake.c
index f336709..99b359e 100644
--- a/tools/ese_relay/ese_relay_fake.c
+++ b/tools/ese_relay/ese_relay_fake.c
@@ -20,7 +20,8 @@
ESE_INCLUDE_HW(ESE_HW_FAKE);
/* Minimal ATR */
-const uint8_t kAtr[] = {0x00, 0x00};
+static const uint8_t kAtrBytes[] = {0x00, 0x00};
+const uint8_t *kAtr = &kAtrBytes[0];
const size_t kAtrLength = sizeof(kAtr);
const void *kEseOpenData = NULL;