mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-01 16:50:18 +00:00
Detect if there is an IRemotelyProvisionedComponent for strongbox, and if so run the associated keymint tests. Else, allow strongbox to skip the test as it's not required to implement the IRPC HAL. Bug: 271948302 Test: VtsAidlKeyMintTargetTest Change-Id: Ibf98e594e725d6ad14c0ff189ab9fbcc25b51f80
8695 lines
382 KiB
C++
8695 lines
382 KiB
C++
/*
|
|
* 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.
|
|
*/
|
|
|
|
#define LOG_TAG "keymint_1_test"
|
|
#include <cutils/log.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
|
|
#include <openssl/curve25519.h>
|
|
#include <openssl/ec.h>
|
|
#include <openssl/evp.h>
|
|
#include <openssl/mem.h>
|
|
#include <openssl/x509v3.h>
|
|
|
|
#include <cutils/properties.h>
|
|
|
|
#include <android/binder_manager.h>
|
|
|
|
#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
|
|
#include <aidl/android/hardware/security/keymint/KeyFormat.h>
|
|
|
|
#include <keymint_support/key_param_output.h>
|
|
#include <keymint_support/openssl_utils.h>
|
|
|
|
#include "KeyMintAidlTestBase.h"
|
|
|
|
using aidl::android::hardware::security::keymint::AuthorizationSet;
|
|
using aidl::android::hardware::security::keymint::KeyCharacteristics;
|
|
using aidl::android::hardware::security::keymint::KeyFormat;
|
|
|
|
namespace std {
|
|
|
|
using namespace aidl::android::hardware::security::keymint;
|
|
|
|
template <>
|
|
struct std::equal_to<KeyCharacteristics> {
|
|
bool operator()(const KeyCharacteristics& a, const KeyCharacteristics& b) const {
|
|
if (a.securityLevel != b.securityLevel) return false;
|
|
|
|
// this isn't very efficient. Oh, well.
|
|
AuthorizationSet a_auths(a.authorizations);
|
|
AuthorizationSet b_auths(b.authorizations);
|
|
|
|
a_auths.Sort();
|
|
b_auths.Sort();
|
|
|
|
return a_auths == b_auths;
|
|
}
|
|
};
|
|
|
|
} // namespace std
|
|
|
|
namespace aidl::android::hardware::security::keymint::test {
|
|
|
|
namespace {
|
|
|
|
// Maximum supported Ed25519 message size.
|
|
const size_t MAX_ED25519_MSG_SIZE = 16 * 1024;
|
|
|
|
// Whether to check that BOOT_PATCHLEVEL is populated.
|
|
bool check_boot_pl = true;
|
|
|
|
// The maximum number of times we'll attempt to verify that corruption
|
|
// of an encrypted blob results in an error. Retries are necessary as there
|
|
// is a small (roughly 1/256) chance that corrupting ciphertext still results
|
|
// in valid PKCS7 padding.
|
|
constexpr size_t kMaxPaddingCorruptionRetries = 8;
|
|
|
|
template <TagType tag_type, Tag tag, typename ValueT>
|
|
bool contains(const vector<KeyParameter>& set, TypedTag<tag_type, tag> ttag,
|
|
ValueT expected_value) {
|
|
auto it = std::find_if(set.begin(), set.end(), [&](const KeyParameter& param) {
|
|
if (auto p = authorizationValue(ttag, param)) {
|
|
return *p == expected_value;
|
|
}
|
|
return false;
|
|
});
|
|
return (it != set.end());
|
|
}
|
|
|
|
template <TagType tag_type, Tag tag>
|
|
bool contains(const vector<KeyParameter>& set, TypedTag<tag_type, tag>) {
|
|
auto it = std::find_if(set.begin(), set.end(),
|
|
[&](const KeyParameter& param) { return param.tag == tag; });
|
|
return (it != set.end());
|
|
}
|
|
|
|
constexpr char hex_value[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // '0'..'9'
|
|
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'A'..'F'
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
|
|
0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'a'..'f'
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
string hex2str(string a) {
|
|
string b;
|
|
size_t num = a.size() / 2;
|
|
b.resize(num);
|
|
for (size_t i = 0; i < num; i++) {
|
|
b[i] = (hex_value[a[i * 2] & 0xFF] << 4) + (hex_value[a[i * 2 + 1] & 0xFF]);
|
|
}
|
|
return b;
|
|
}
|
|
|
|
string rsa_key = hex2str(
|
|
// RFC 5208 s5
|
|
"30820275" // SEQUENCE length 0x275 (PrivateKeyInfo) {
|
|
"020100" // INTEGER length 1 value 0x00 (version)
|
|
"300d" // SEQUENCE length 0x0d (AlgorithmIdentifier) {
|
|
"0609" // OBJECT IDENTIFIER length 9 (algorithm)
|
|
"2a864886f70d010101" // 1.2.840.113549.1.1.1 (rsaEncryption)
|
|
"0500" // NULL (parameters)
|
|
// } end SEQUENCE (AlgorithmIdentifier)
|
|
"0482025f" // OCTET STRING length 0x25f (privateKey) holding...
|
|
// RFC 8017 A.1.2
|
|
"3082025b" // SEQUENCE length 0x25b (RSAPrivateKey) {
|
|
"020100" // INTEGER length 1 value 0x00 (version)
|
|
"028181" // INTEGER length 0x81 value (modulus) ...
|
|
"00c6095409047d8634812d5a218176e4"
|
|
"5c41d60a75b13901f234226cffe77652"
|
|
"1c5a77b9e389417b71c0b6a44d13afe4"
|
|
"e4a2805d46c9da2935adb1ff0c1f24ea"
|
|
"06e62b20d776430a4d435157233c6f91"
|
|
"6783c30e310fcbd89b85c2d567711697"
|
|
"85ac12bca244abda72bfb19fc44d27c8"
|
|
"1e1d92de284f4061edfd99280745ea6d"
|
|
"25"
|
|
"0203010001" // INTEGER length 3 value 0x10001 (publicExponent)
|
|
"028180" // INTEGER length 0x80 (privateExponent) value...
|
|
"1be0f04d9cae3718691f035338308e91"
|
|
"564b55899ffb5084d2460e6630257e05"
|
|
"b3ceab02972dfabcd6ce5f6ee2589eb6"
|
|
"7911ed0fac16e43a444b8c861e544a05"
|
|
"93365772f8baf6b22fc9e3c5f1024b06"
|
|
"3ac080a7b2234cf8aee8f6c47bbf4fd3"
|
|
"ace7240290bef16c0b3f7f3cdd64ce3a"
|
|
"b5912cf6e32f39ab188358afcccd8081"
|
|
"0241" // INTEGER length 0x41 (prime1)
|
|
"00e4b49ef50f765d3b24dde01aceaaf1"
|
|
"30f2c76670a91a61ae08af497b4a82be"
|
|
"6dee8fcdd5e3f7ba1cfb1f0c926b88f8"
|
|
"8c92bfab137fba2285227b83c342ff7c"
|
|
"55"
|
|
"0241" // INTEGER length 0x41 (prime2)
|
|
"00ddabb5839c4c7f6bf3d4183231f005"
|
|
"b31aa58affdda5c79e4cce217f6bc930"
|
|
"dbe563d480706c24e9ebfcab28a6cdef"
|
|
"d324b77e1bf7251b709092c24ff501fd"
|
|
"91"
|
|
"0240" // INTEGER length 0x40 (exponent1)
|
|
"23d4340eda3445d8cd26c14411da6fdc"
|
|
"a63c1ccd4b80a98ad52b78cc8ad8beb2"
|
|
"842c1d280405bc2f6c1bea214a1d742a"
|
|
"b996b35b63a82a5e470fa88dbf823cdd"
|
|
"0240" // INTEGER length 0x40 (exponent2)
|
|
"1b7b57449ad30d1518249a5f56bb9829"
|
|
"4d4b6ac12ffc86940497a5a5837a6cf9"
|
|
"46262b494526d328c11e1126380fde04"
|
|
"c24f916dec250892db09a6d77cdba351"
|
|
"0240" // INTEGER length 0x40 (coefficient)
|
|
"7762cd8f4d050da56bd591adb515d24d"
|
|
"7ccd32cca0d05f866d583514bd7324d5"
|
|
"f33645e8ed8b4a1cb3cc4a1d67987399"
|
|
"f2a09f5b3fb68c88d5e5d90ac33492d6"
|
|
// } end SEQUENCE (PrivateKey)
|
|
// } end SEQUENCE (PrivateKeyInfo)
|
|
);
|
|
|
|
/*
|
|
* DER-encoded PKCS#8 format RSA key. Generated using:
|
|
*
|
|
* openssl genrsa 2048 | openssl pkcs8 -topk8 -nocrypt -outform der | hexdump -e '30/1 "%02X" "\n"'
|
|
*/
|
|
string rsa_2048_key = hex2str(
|
|
// RFC 5208 s5
|
|
"308204BD" // SEQUENCE length 0x4bd (PrivateKeyInfo) {
|
|
"020100" // INTEGER length 1 value 0x00 (version)
|
|
"300D" // SEQUENCE length 0x0d (AlgorithmIdentifier) {
|
|
"0609" // OBJECT IDENTIFIER length 9 (algorithm)
|
|
"2A864886F70D010101" // 1.2.840.113549.1.1.1 (rsaEncryption)
|
|
"0500" // NULL (parameters)
|
|
// } end SEQUENCE (AlgorithmIdentifier)
|
|
"048204A7" // OCTET STRING length 0x25f (privateKey) holding...
|
|
// RFC 8017 A.1.2
|
|
"308204A3" // SEQUENCE length 0x4a3 (RSAPrivateKey) {
|
|
"020100" // INTEGER length 1 value 0x00 (version)
|
|
"02820101" // INTEGER length 0x101 value (modulus) ...
|
|
"00BEBC342B56D443B1299F9A6A7056E8"
|
|
"0A897E318476A5A18029E63B2ED739A6"
|
|
"1791D339F58DC763D9D14911F2EDEC38"
|
|
"3DEE11F6319B44510E7A3ECD9B79B973"
|
|
"82E49500ACF8117DC89CAF0E621F7775"
|
|
"6554A2FD4664BFE7AB8B59AB48340DBF"
|
|
"A27B93B5A81F6ECDEB02D0759307128D"
|
|
"F3E3BAD4055C8B840216DFAA5700670E"
|
|
"6C5126F0962FCB70FF308F25049164CC"
|
|
"F76CC2DA66A7DD9A81A714C2809D6918"
|
|
"6133D29D84568E892B6FFBF3199BDB14"
|
|
"383EE224407F190358F111A949552ABA"
|
|
"6714227D1BD7F6B20DD0CB88F9467B71"
|
|
"9339F33BFF35B3870B3F62204E4286B0"
|
|
"948EA348B524544B5F9838F29EE643B0"
|
|
"79EEF8A713B220D7806924CDF7295070"
|
|
"C5"
|
|
"0203010001" // INTEGER length 3 value 0x10001 (publicExponent)
|
|
"02820100" // INTEGER length 0x100 (privateExponent) value...
|
|
"69F377F35F2F584EF075353CCD1CA997"
|
|
"38DB3DBC7C7FF35F9366CE176DFD1B13"
|
|
"5AB10030344ABF5FBECF1D4659FDEF1C"
|
|
"0FC430834BE1BE3911951377BB3D563A"
|
|
"2EA9CA8F4AD9C48A8CE6FD516A735C66"
|
|
"2686C7B4B3C09A7B8354133E6F93F790"
|
|
"D59EAEB92E84C9A4339302CCE28FDF04"
|
|
"CCCAFA7DE3F3A827D4F6F7D38E68B0EC"
|
|
"6AB706645BF074A4E4090D06FB163124"
|
|
"365FD5EE7A20D350E9958CC30D91326E"
|
|
"1B292E9EF5DB408EC42DAF737D201497"
|
|
"04D0A678A0FB5B5446863B099228A352"
|
|
"D604BA8091A164D01D5AB05397C71EAD"
|
|
"20BE2A08FC528FE442817809C787FEE4"
|
|
"AB97F97B9130D022153EDC6EB6CBE7B0"
|
|
"F8E3473F2E901209B5DB10F93604DB01"
|
|
"028181" // INTEGER length 0x81 (prime1)
|
|
"00E83C0998214941EA4F9293F1B77E2E"
|
|
"99E6CF305FAF358238E126124FEAF2EB"
|
|
"9724B2EA7B78E6032343821A80E55D1D"
|
|
"88FB12D220C3F41A56142FEC85796D19"
|
|
"17F1E8C774F142B67D3D6E7B7E6B4383"
|
|
"E94DB5929089DBB346D5BDAB40CC2D96"
|
|
"EE0409475E175C63BF78CFD744136740"
|
|
"838127EA723FF3FE7FA368C1311B4A4E"
|
|
"05"
|
|
"028181" // INTEGER length 0x81 (prime2)
|
|
"00D240FCC0F5D7715CDE21CB2DC86EA1"
|
|
"46132EA3B06F61FF2AF54BF38473F59D"
|
|
"ADCCE32B5F4CC32DD0BA6F509347B4B5"
|
|
"B1B58C39F95E4798CCBB43E83D0119AC"
|
|
"F532F359CA743C85199F0286610E2009"
|
|
"97D7312917179AC9B67558773212EC96"
|
|
"1E8BCE7A3CC809BC5486A96E4B0E6AF3"
|
|
"94D94E066A0900B7B70E82A44FB30053"
|
|
"C1"
|
|
"028181" // INTEGER length 0x81 (exponent1)
|
|
"00AD15DA1CBD6A492B66851BA8C316D3"
|
|
"8AB700E2CFDDD926A658003513C54BAA"
|
|
"152B30021D667D20078F500F8AD3E7F3"
|
|
"945D74A891ED1A28EAD0FEEAEC8C14A8"
|
|
"E834CF46A13D1378C99D18940823CFDD"
|
|
"27EC5810D59339E0C34198AC638E09C8"
|
|
"7CBB1B634A9864AE9F4D5EB2D53514F6"
|
|
"7B4CAEC048C8AB849A02E397618F3271"
|
|
"35"
|
|
"028180" // INTEGER length 0x80 (exponent2)
|
|
"1FA2C1A5331880A92D8F3E281C617108"
|
|
"BF38244F16E352E69ED417C7153F9EC3"
|
|
"18F211839C643DCF8B4DD67CE2AC312E"
|
|
"95178D5D952F06B1BF779F4916924B70"
|
|
"F582A23F11304E02A5E7565AE22A35E7"
|
|
"4FECC8B6FDC93F92A1A37703E4CF0E63"
|
|
"783BD02EB716A7ECBBFA606B10B74D01"
|
|
"579522E7EF84D91FC522292108D902C1"
|
|
"028180" // INTEGER length 0x80 (coefficient)
|
|
"796FE3825F9DCC85DF22D58690065D93"
|
|
"898ACD65C087BEA8DA3A63BF4549B795"
|
|
"E2CD0E3BE08CDEBD9FCF1720D9CDC507"
|
|
"0D74F40DED8E1102C52152A31B6165F8"
|
|
"3A6722AECFCC35A493D7634664B888A0"
|
|
"8D3EB034F12EA28BFEE346E205D33482"
|
|
"7F778B16ED40872BD29FCB36536B6E93"
|
|
"FFB06778696B4A9D81BB0A9423E63DE5"
|
|
// } end SEQUENCE (PrivateKey)
|
|
// } end SEQUENCE (PrivateKeyInfo)
|
|
);
|
|
|
|
string ec_256_key = hex2str(
|
|
// RFC 5208 s5
|
|
"308187" // SEQUENCE length 0x87 (PrivateKeyInfo) {
|
|
"020100" // INTEGER length 1 value 0 (version)
|
|
"3013" // SEQUENCE length 0x13 (AlgorithmIdentifier) {
|
|
"0607" // OBJECT IDENTIFIER length 7 (algorithm)
|
|
"2a8648ce3d0201" // 1.2.840.10045.2.1 (ecPublicKey)
|
|
"0608" // OBJECT IDENTIFIER length 8 (param)
|
|
"2a8648ce3d030107" // 1.2.840.10045.3.1.7 (secp256r1)
|
|
// } end SEQUENCE (AlgorithmIdentifier)
|
|
"046d" // OCTET STRING length 0x6d (privateKey) holding...
|
|
"306b" // SEQUENCE length 0x6b (ECPrivateKey)
|
|
"020101" // INTEGER length 1 value 1 (version)
|
|
"0420" // OCTET STRING length 0x20 (privateKey)
|
|
"737c2ecd7b8d1940bf2930aa9b4ed3ff"
|
|
"941eed09366bc03299986481f3a4d859"
|
|
"a144" // TAG [1] len 0x44 (publicKey) {
|
|
"03420004bf85d7720d07c25461683bc6"
|
|
"48b4778a9a14dd8a024e3bdd8c7ddd9a"
|
|
"b2b528bbc7aa1b51f14ebbbb0bd0ce21"
|
|
"bcc41c6eb00083cf3376d11fd44949e0"
|
|
"b2183bfe"
|
|
// } end SEQUENCE (ECPrivateKey)
|
|
// } end SEQUENCE (PrivateKeyInfo)
|
|
);
|
|
|
|
string ec_521_key = hex2str(
|
|
// RFC 5208 s5
|
|
"3081EE" // SEQUENCE length 0xee (PrivateKeyInfo) {
|
|
"020100" // INTEGER length 1 value 0 (version)
|
|
"3010" // SEQUENCE length 0x10 (AlgorithmIdentifier) {
|
|
"0607" // OBJECT IDENTIFIER length 7 (algorithm)
|
|
"2A8648CE3D0201" // 1.2.840.10045.2.1 (ecPublicKey)
|
|
"0605" // OBJECT IDENTIFIER length 5 (param)
|
|
"2B81040023" // 1.3.132.0.35 (secp521r1)
|
|
// } end SEQUENCE (AlgorithmIdentifier)
|
|
"0481D6" // OCTET STRING length 0xd6 (privateKey) holding...
|
|
"3081D3" // SEQUENCE length 0xd3 (ECPrivateKey)
|
|
"020101" // INTEGER length 1 value 1 (version)
|
|
"0442" // OCTET STRING length 0x42 (privateKey)
|
|
"0011458C586DB5DAA92AFAB03F4FE46A"
|
|
"A9D9C3CE9A9B7A006A8384BEC4C78E8E"
|
|
"9D18D7D08B5BCFA0E53C75B064AD51C4"
|
|
"49BAE0258D54B94B1E885DED08ED4FB2"
|
|
"5CE9"
|
|
"A18189" // TAG [1] len 0x89 (publicKey) {
|
|
"03818600040149EC11C6DF0FA122C6A9"
|
|
"AFD9754A4FA9513A627CA329E349535A"
|
|
"5629875A8ADFBE27DCB932C051986377"
|
|
"108D054C28C6F39B6F2C9AF81802F9F3"
|
|
"26B842FF2E5F3C00AB7635CFB36157FC"
|
|
"0882D574A10D839C1A0C049DC5E0D775"
|
|
"E2EE50671A208431BB45E78E70BEFE93"
|
|
"0DB34818EE4D5C26259F5C6B8E28A652"
|
|
"950F9F88D7B4B2C9D9"
|
|
// } end SEQUENCE (ECPrivateKey)
|
|
// } end SEQUENCE (PrivateKeyInfo)
|
|
);
|
|
|
|
string ec_256_key_rfc5915 = hex2str(
|
|
// RFC 5208 s5
|
|
"308193" // SEQUENCE length 0x93 (PrivateKeyInfo) {
|
|
"020100" // INTEGER length 1 value 0 (version)
|
|
"3013" // SEQUENCE length 0x13 (AlgorithmIdentifier) {
|
|
"0607" // OBJECT IDENTIFIER length 7 (algorithm)
|
|
"2a8648ce3d0201" // 1.2.840.10045.2.1 (ecPublicKey)
|
|
"0608" // OBJECT IDENTIFIER length 8 (param)
|
|
"2a8648ce3d030107" // 1.2.840.10045.3.1.7 (secp256r1)
|
|
// } end SEQUENCE (AlgorithmIdentifier)
|
|
"0479" // OCTET STRING length 0x79 (privateKey) holding...
|
|
// RFC 5915 s3
|
|
"3077" // SEQUENCE length 0x77 (ECPrivateKey)
|
|
"020101" // INTEGER length 1 value 1 (version)
|
|
"0420" // OCTET STRING length 0x42 (privateKey)
|
|
"782370a8c8ce5537baadd04dcff079c8"
|
|
"158cfa9c67b818b38e8d21c9fa750c1d"
|
|
"a00a" // TAG [0] length 0xa (parameters)
|
|
"0608" // OBJECT IDENTIFIER length 8
|
|
"2a8648ce3d030107" // 1.2.840.10045.3.1.7 (secp256r1)
|
|
// } end TAG [0]
|
|
"a144" // TAG [1] length 0x44 (publicKey) {
|
|
"0342" // BIT STRING length 0x42
|
|
"00" // no pad bits
|
|
"04e2cc561ee701da0ad0ef0d176bb0c9"
|
|
"19d42e79c393fdc1bd6c4010d85cf2cf"
|
|
"8e68c905464666f98dad4f01573ba810"
|
|
"78b3428570a439ba3229fbc026c55068"
|
|
"2f"
|
|
// } end SEQUENCE (ECPrivateKey)
|
|
// } end SEQUENCE (PrivateKeyInfo)
|
|
);
|
|
|
|
string ec_256_key_sec1 = hex2str(
|
|
// RFC 5208 s5
|
|
"308187" // SEQUENCE length 0x87 (PrivateKeyInfo) {
|
|
"020100" // INTEGER length 1 value 0 (version)
|
|
"3013" // SEQUENCE length 0x13 (AlgorithmIdentifier) {
|
|
"0607" // OBJECT IDENTIFIER length 7 (algorithm)
|
|
"2a8648ce3d0201" // 1.2.840.10045.2.1 (ecPublicKey)
|
|
"0608" // OBJECT IDENTIFIER length 8 (param)
|
|
"2a8648ce3d030107" // 1.2.840.10045.3.1.7 (secp256r1)
|
|
// } end SEQUENCE (AlgorithmIdentifier)
|
|
"046d" // OCTET STRING length 0x6d (privateKey) holding...
|
|
// SEC1-v2 C.4
|
|
"306b" // SEQUENCE length 0x6b (ECPrivateKey)
|
|
"020101" // INTEGER length 1 value 0x01 (version)
|
|
"0420" // OCTET STRING length 0x20 (privateKey)
|
|
"782370a8c8ce5537baadd04dcff079c8"
|
|
"158cfa9c67b818b38e8d21c9fa750c1d"
|
|
"a144" // TAG [1] length 0x44 (publicKey) {
|
|
"0342" // BIT STRING length 0x42
|
|
"00" // no pad bits
|
|
"04e2cc561ee701da0ad0ef0d176bb0c9"
|
|
"19d42e79c393fdc1bd6c4010d85cf2cf"
|
|
"8e68c905464666f98dad4f01573ba810"
|
|
"78b3428570a439ba3229fbc026c55068"
|
|
"2f"
|
|
// } end TAG [1] (publicKey)
|
|
// } end SEQUENCE (PrivateKeyInfo)
|
|
);
|
|
|
|
/**
|
|
* Ed25519 key pair generated as follows:
|
|
* ```
|
|
* % openssl req -x509 -newkey ED25519 -days 700 -nodes \
|
|
* -keyout ed25519_priv.key -out ed25519.pem * -subj "/CN=fake.ed25519.com"
|
|
* Generating a ED25519 private key writing new private key to
|
|
* 'ed25519_priv.key'
|
|
* -----
|
|
* % cat ed25519_priv.key
|
|
* -----BEGIN PRIVATE KEY-----
|
|
* MC4CAQAwBQYDK2VwBCIEIKl3A5quNywcj1P+0XI9SBalFPIvO52NxceMLRH6dVmR
|
|
* -----END PRIVATE KEY-----
|
|
* % der2ascii -pem -i ed25519_priv.key
|
|
* SEQUENCE {
|
|
* INTEGER { 0 }
|
|
* SEQUENCE {
|
|
* # ed25519
|
|
* OBJECT_IDENTIFIER { 1.3.101.112 }
|
|
* }
|
|
* OCTET_STRING {
|
|
* OCTET_STRING { `a977039aae372c1c8f53fed1723d4816a514f22f3b9d8dc5c78c2d11fa755991` }
|
|
* }
|
|
* }
|
|
* % cat ed25519.pem
|
|
* -----BEGIN CERTIFICATE-----
|
|
* MIIBSjCB/aADAgECAhR0Jron3eKcdgqyecv/eEfGWAzn8DAFBgMrZXAwGzEZMBcG
|
|
* A1UEAwwQZmFrZS5lZDI1NTE5LmNvbTAeFw0yMTEwMjAwODI3NDJaFw0yMzA5MjAw
|
|
* ODI3NDJaMBsxGTAXBgNVBAMMEGZha2UuZWQyNTUxOS5jb20wKjAFBgMrZXADIQDv
|
|
* uwHz+3TaQ69D2digxlz0fFfsZg0rPqgQae3jBPRWkaNTMFEwHQYDVR0OBBYEFN9O
|
|
* od30SY4JTs66ZR403UPya+iXMB8GA1UdIwQYMBaAFN9Ood30SY4JTs66ZR403UPy
|
|
* a+iXMA8GA1UdEwEB/wQFMAMBAf8wBQYDK2VwA0EAKjVrYQjuE/gEL2j/ABpDbFjV
|
|
* Ilg5tJ6MN/P3psAv3Cs7f0X1lFqdlt15nJ/6aj2cmGCwNRXt5wcyYDKNu+v2Dw==
|
|
* -----END CERTIFICATE-----
|
|
* % openssl x509 -in ed25519.pem -text -noout
|
|
* Certificate:
|
|
* Data:
|
|
* Version: 3 (0x2)
|
|
* Serial Number:
|
|
* 74:26:ba:27:dd:e2:9c:76:0a:b2:79:cb:ff:78:47:c6:58:0c:e7:f0
|
|
* Signature Algorithm: ED25519
|
|
* Issuer: CN = fake.ed25519.com
|
|
* Validity
|
|
* Not Before: Oct 20 08:27:42 2021 GMT
|
|
* Not After : Sep 20 08:27:42 2023 GMT
|
|
* Subject: CN = fake.ed25519.com
|
|
* Subject Public Key Info:
|
|
* Public Key Algorithm: ED25519
|
|
* ED25519 Public-Key:
|
|
* pub:
|
|
* ef:bb:01:f3:fb:74:da:43:af:43:d9:d8:a0:c6:5c:
|
|
* f4:7c:57:ec:66:0d:2b:3e:a8:10:69:ed:e3:04:f4:
|
|
* 56:91
|
|
* X509v3 extensions:
|
|
* X509v3 Subject Key Identifier:
|
|
* DF:4E:A1:DD:F4:49:8E:09:4E:CE:BA:65:1E:34:DD:43:F2:6B:E8:97
|
|
* X509v3 Authority Key Identifier:
|
|
* keyid:DF:4E:A1:DD:F4:49:8E:09:4E:CE:BA:65:1E:34:DD:43:F2:6B:E8:97
|
|
*
|
|
* X509v3 Basic Constraints: critical
|
|
* CA:TRUE
|
|
* Signature Algorithm: ED25519
|
|
* 2a:35:6b:61:08:ee:13:f8:04:2f:68:ff:00:1a:43:6c:58:d5:
|
|
* 22:58:39:b4:9e:8c:37:f3:f7:a6:c0:2f:dc:2b:3b:7f:45:f5:
|
|
* 94:5a:9d:96:dd:79:9c:9f:fa:6a:3d:9c:98:60:b0:35:15:ed:
|
|
* e7:07:32:60:32:8d:bb:eb:f6:0f
|
|
* ```
|
|
*/
|
|
string ed25519_key = hex2str("a977039aae372c1c8f53fed1723d4816a514f22f3b9d8dc5c78c2d11fa755991");
|
|
string ed25519_pkcs8_key = hex2str(
|
|
// RFC 5208 s5
|
|
"302e" // SEQUENCE length 0x2e (PrivateKeyInfo) {
|
|
"0201" // INTEGER length 1 (Version)
|
|
"00" // version 0
|
|
"3005" // SEQUENCE length 05 (AlgorithmIdentifier) {
|
|
"0603" // OBJECT IDENTIFIER length 3 (algorithm)
|
|
"2b6570" // 1.3.101.112 (id-Ed125519 RFC 8410 s3)
|
|
// } end SEQUENCE (AlgorithmIdentifier)
|
|
"0422" // OCTET STRING length 0x22 (PrivateKey)
|
|
"0420" // OCTET STRING length 0x20 (RFC 8410 s7)
|
|
"a977039aae372c1c8f53fed1723d4816a514f22f3b9d8dc5c78c2d11fa755991"
|
|
// } end SEQUENCE (PrivateKeyInfo)
|
|
);
|
|
string ed25519_pubkey = hex2str("efbb01f3fb74da43af43d9d8a0c65cf47c57ec660d2b3ea81069ede304f45691");
|
|
|
|
/**
|
|
* X25519 key pair generated as follows:
|
|
* ```
|
|
* % openssl genpkey -algorithm X25519 > x25519_priv.key
|
|
* % cat x25519_priv.key
|
|
* -----BEGIN PRIVATE KEY-----
|
|
* MC4CAQAwBQYDK2VuBCIEIGgPwF3NLwQx/Sfwr2nfJvXitwlDNh3Skzh+TISN/y1C
|
|
* -----END PRIVATE KEY-----
|
|
* % der2ascii -pem -i x25519_priv.key
|
|
* SEQUENCE {
|
|
* INTEGER { 0 }
|
|
* SEQUENCE {
|
|
* # x25519
|
|
* OBJECT_IDENTIFIER { 1.3.101.110 }
|
|
* }
|
|
* OCTET_STRING {
|
|
* OCTET_STRING { `680fc05dcd2f0431fd27f0af69df26f5e2b70943361dd293387e4c848dff2d42` }
|
|
* }
|
|
* }
|
|
* ```
|
|
*/
|
|
|
|
string x25519_key = hex2str("680fc05dcd2f0431fd27f0af69df26f5e2b70943361dd293387e4c848dff2d42");
|
|
string x25519_pkcs8_key = hex2str(
|
|
// RFC 5208 s5
|
|
"302e" // SEQUENCE length 0x2e (PrivateKeyInfo) {
|
|
"0201" // INTEGER length 1 (Version)
|
|
"00" // version 0
|
|
"3005" // SEQUENCE length 05 (AlgorithmIdentifier) {
|
|
"0603" // OBJECT IDENTIFIER length 3 (algorithm)
|
|
"2b656e" // 1.3.101.110 (id-X125519 RFC 8410 s3)
|
|
"0422" // OCTET STRING length 0x22 (PrivateKey)
|
|
"0420" // OCTET STRING length 0x20 (RFC 8410 s7)
|
|
"680fc05dcd2f0431fd27f0af69df26f5e2b70943361dd293387e4c848dff2d42");
|
|
string x25519_pubkey = hex2str("be46925a857f17831d6d454b9d3d36a4a30166edf80eb82b684661c3e258f768");
|
|
|
|
struct RSA_Delete {
|
|
void operator()(RSA* p) { RSA_free(p); }
|
|
};
|
|
|
|
std::string make_string(const uint8_t* data, size_t length) {
|
|
return std::string(reinterpret_cast<const char*>(data), length);
|
|
}
|
|
|
|
template <size_t N>
|
|
std::string make_string(const uint8_t (&a)[N]) {
|
|
return make_string(a, N);
|
|
}
|
|
|
|
class AidlBuf : public vector<uint8_t> {
|
|
typedef vector<uint8_t> super;
|
|
|
|
public:
|
|
AidlBuf() {}
|
|
AidlBuf(const super& other) : super(other) {}
|
|
AidlBuf(super&& other) : super(std::move(other)) {}
|
|
explicit AidlBuf(const std::string& other) : AidlBuf() { *this = other; }
|
|
|
|
AidlBuf& operator=(const super& other) {
|
|
super::operator=(other);
|
|
return *this;
|
|
}
|
|
|
|
AidlBuf& operator=(super&& other) {
|
|
super::operator=(std::move(other));
|
|
return *this;
|
|
}
|
|
|
|
AidlBuf& operator=(const string& other) {
|
|
resize(other.size());
|
|
for (size_t i = 0; i < other.size(); ++i) {
|
|
(*this)[i] = static_cast<uint8_t>(other[i]);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
string to_string() const { return string(reinterpret_cast<const char*>(data()), size()); }
|
|
};
|
|
|
|
string device_suffix(const string& name) {
|
|
size_t pos = name.find('/');
|
|
if (pos == string::npos) {
|
|
return name;
|
|
}
|
|
return name.substr(pos + 1);
|
|
}
|
|
|
|
std::shared_ptr<IRemotelyProvisionedComponent> matching_rp_instance(const std::string& km_name) {
|
|
string km_suffix = device_suffix(km_name);
|
|
|
|
vector<string> rp_names =
|
|
::android::getAidlHalInstanceNames(IRemotelyProvisionedComponent::descriptor);
|
|
for (const string& rp_name : rp_names) {
|
|
// If the suffix of the RemotelyProvisionedComponent instance equals the suffix of the
|
|
// KeyMint instance, assume they match.
|
|
if (device_suffix(rp_name) == km_suffix && AServiceManager_isDeclared(rp_name.c_str())) {
|
|
::ndk::SpAIBinder binder(AServiceManager_waitForService(rp_name.c_str()));
|
|
return IRemotelyProvisionedComponent::fromBinder(binder);
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
class NewKeyGenerationTest : public KeyMintAidlTestBase {
|
|
protected:
|
|
void CheckBaseParams(const vector<KeyCharacteristics>& keyCharacteristics) {
|
|
AuthorizationSet auths = CheckCommonParams(keyCharacteristics, KeyOrigin::GENERATED);
|
|
EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN));
|
|
|
|
// Check that some unexpected tags/values are NOT present.
|
|
EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::ENCRYPT));
|
|
EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::DECRYPT));
|
|
}
|
|
|
|
void CheckSymmetricParams(const vector<KeyCharacteristics>& keyCharacteristics) {
|
|
AuthorizationSet auths = CheckCommonParams(keyCharacteristics, KeyOrigin::GENERATED);
|
|
EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::ENCRYPT));
|
|
EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::DECRYPT));
|
|
|
|
EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN));
|
|
}
|
|
|
|
AuthorizationSet CheckCommonParams(const vector<KeyCharacteristics>& keyCharacteristics,
|
|
const KeyOrigin expectedKeyOrigin) {
|
|
// TODO(swillden): Distinguish which params should be in which auth list.
|
|
AuthorizationSet auths;
|
|
for (auto& entry : keyCharacteristics) {
|
|
auths.push_back(AuthorizationSet(entry.authorizations));
|
|
}
|
|
EXPECT_TRUE(auths.Contains(TAG_ORIGIN, expectedKeyOrigin));
|
|
|
|
// Verify that App data, ROT and auth timeout are NOT included.
|
|
EXPECT_FALSE(auths.Contains(TAG_ROOT_OF_TRUST));
|
|
EXPECT_FALSE(auths.Contains(TAG_APPLICATION_DATA));
|
|
EXPECT_FALSE(auths.Contains(TAG_AUTH_TIMEOUT, 301U));
|
|
|
|
// None of the tests specify CREATION_DATETIME so check that the KeyMint implementation
|
|
// never adds it.
|
|
EXPECT_FALSE(auths.Contains(TAG_CREATION_DATETIME));
|
|
|
|
// Check OS details match the original hardware info.
|
|
auto os_ver = auths.GetTagValue(TAG_OS_VERSION);
|
|
EXPECT_TRUE(os_ver);
|
|
EXPECT_EQ(*os_ver, os_version());
|
|
auto os_pl = auths.GetTagValue(TAG_OS_PATCHLEVEL);
|
|
EXPECT_TRUE(os_pl);
|
|
EXPECT_EQ(*os_pl, os_patch_level());
|
|
|
|
// Should include vendor patchlevel.
|
|
auto vendor_pl = auths.GetTagValue(TAG_VENDOR_PATCHLEVEL);
|
|
EXPECT_TRUE(vendor_pl);
|
|
EXPECT_EQ(*vendor_pl, vendor_patch_level());
|
|
|
|
// Should include boot patchlevel (but there are some test scenarios where this is not
|
|
// possible).
|
|
if (check_boot_pl) {
|
|
auto boot_pl = auths.GetTagValue(TAG_BOOT_PATCHLEVEL);
|
|
EXPECT_TRUE(boot_pl);
|
|
}
|
|
|
|
return auths;
|
|
}
|
|
};
|
|
|
|
/*
|
|
* NewKeyGenerationTest.Aes
|
|
*
|
|
* Verifies that keymint can generate all required AES key sizes, and that the resulting keys
|
|
* have correct characteristics.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, Aes) {
|
|
for (auto key_size : ValidKeySizes(Algorithm::AES)) {
|
|
for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
|
|
for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
|
|
SCOPED_TRACE(testing::Message()
|
|
<< "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
auto builder = AuthorizationSetBuilder()
|
|
.AesEncryptionKey(key_size)
|
|
.BlockMode(block_mode)
|
|
.Padding(padding_mode)
|
|
.SetDefaultValidity();
|
|
if (block_mode == BlockMode::GCM) {
|
|
builder.Authorization(TAG_MIN_MAC_LENGTH, 128);
|
|
}
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(builder, &key_blob, &key_characteristics));
|
|
|
|
EXPECT_GT(key_blob.size(), 0U);
|
|
CheckSymmetricParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::AES));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
|
|
<< "Key size " << key_size << "missing";
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.AesInvalidSize
|
|
*
|
|
* Verifies that specifying an invalid key size for AES key generation returns
|
|
* UNSUPPORTED_KEY_SIZE.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, AesInvalidSize) {
|
|
for (auto key_size : InvalidKeySizes(Algorithm::AES)) {
|
|
for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
|
|
for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
|
|
SCOPED_TRACE(testing::Message()
|
|
<< "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
auto builder = AuthorizationSetBuilder()
|
|
.AesEncryptionKey(key_size)
|
|
.BlockMode(block_mode)
|
|
.Padding(padding_mode)
|
|
.SetDefaultValidity();
|
|
if (block_mode == BlockMode::GCM) {
|
|
builder.Authorization(TAG_MIN_MAC_LENGTH, 128);
|
|
}
|
|
EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
|
|
GenerateKey(builder, &key_blob, &key_characteristics));
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
|
|
for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
|
|
SCOPED_TRACE(testing::Message() << "AES-unknown-" << block_mode << "-" << padding_mode);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
// No key size specified
|
|
auto builder = AuthorizationSetBuilder()
|
|
.Authorization(TAG_ALGORITHM, Algorithm::AES)
|
|
.BlockMode(block_mode)
|
|
.Padding(padding_mode)
|
|
.SetDefaultValidity();
|
|
if (block_mode == BlockMode::GCM) {
|
|
builder.Authorization(TAG_MIN_MAC_LENGTH, 128);
|
|
}
|
|
EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
|
|
GenerateKey(builder, &key_blob, &key_characteristics));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.AesInvalidPadding
|
|
*
|
|
* Verifies that specifying an invalid padding on AES keys gives a failure
|
|
* somewhere along the way.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, AesInvalidPadding) {
|
|
for (auto key_size : ValidKeySizes(Algorithm::AES)) {
|
|
for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
|
|
for (auto padding_mode : InvalidPaddingModes(Algorithm::AES, block_mode)) {
|
|
SCOPED_TRACE(testing::Message()
|
|
<< "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
|
|
auto builder = AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(key_size)
|
|
.BlockMode(block_mode)
|
|
.Padding(padding_mode)
|
|
.SetDefaultValidity();
|
|
if (block_mode == BlockMode::GCM) {
|
|
builder.Authorization(TAG_MIN_MAC_LENGTH, 128);
|
|
}
|
|
|
|
auto result = GenerateKey(builder);
|
|
if (result == ErrorCode::OK) {
|
|
// Key creation was OK but has generated a key that cannot be used.
|
|
auto params =
|
|
AuthorizationSetBuilder().BlockMode(block_mode).Padding(padding_mode);
|
|
if (block_mode == BlockMode::GCM) {
|
|
params.Authorization(TAG_MAC_LENGTH, 128);
|
|
}
|
|
auto result = Begin(KeyPurpose::ENCRYPT, params);
|
|
EXPECT_TRUE(result == ErrorCode::INCOMPATIBLE_PADDING_MODE ||
|
|
result == ErrorCode::INVALID_KEY_BLOB)
|
|
<< "unexpected result: " << result;
|
|
} else {
|
|
// The KeyMint implementation detected that the generated key
|
|
// is unusable.
|
|
EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.AesGcmMissingMinMac
|
|
*
|
|
* Verifies that specifying an invalid key size for AES key generation returns
|
|
* UNSUPPORTED_KEY_SIZE.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, AesGcmMissingMinMac) {
|
|
for (auto key_size : ValidKeySizes(Algorithm::AES)) {
|
|
BlockMode block_mode = BlockMode::GCM;
|
|
for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
|
|
SCOPED_TRACE(testing::Message()
|
|
<< "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
// No MIN_MAC_LENGTH provided.
|
|
auto builder = AuthorizationSetBuilder()
|
|
.AesEncryptionKey(key_size)
|
|
.BlockMode(block_mode)
|
|
.Padding(padding_mode)
|
|
.SetDefaultValidity();
|
|
EXPECT_EQ(ErrorCode::MISSING_MIN_MAC_LENGTH,
|
|
GenerateKey(builder, &key_blob, &key_characteristics));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.AesGcmMinMacOutOfRange
|
|
*
|
|
* Verifies that specifying an invalid min MAC size for AES key generation returns
|
|
* UNSUPPORTED_MIN_MAC_LENGTH.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, AesGcmMinMacOutOfRange) {
|
|
for (size_t min_mac_len : {88, 136}) {
|
|
for (auto key_size : ValidKeySizes(Algorithm::AES)) {
|
|
BlockMode block_mode = BlockMode::GCM;
|
|
for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
|
|
SCOPED_TRACE(testing::Message()
|
|
<< "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
auto builder = AuthorizationSetBuilder()
|
|
.AesEncryptionKey(key_size)
|
|
.BlockMode(block_mode)
|
|
.Padding(padding_mode)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, min_mac_len)
|
|
.SetDefaultValidity();
|
|
EXPECT_EQ(ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH,
|
|
GenerateKey(builder, &key_blob, &key_characteristics));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.TripleDes
|
|
*
|
|
* Verifies that keymint can generate all required 3DES key sizes, and that the resulting keys
|
|
* have correct characteristics.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, TripleDes) {
|
|
for (auto key_size : ValidKeySizes(Algorithm::TRIPLE_DES)) {
|
|
for (auto block_mode : ValidBlockModes(Algorithm::TRIPLE_DES)) {
|
|
for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
|
|
SCOPED_TRACE(testing::Message()
|
|
<< "3DES-" << key_size << "-" << block_mode << "-" << padding_mode);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(key_size)
|
|
.BlockMode(block_mode)
|
|
.Padding(padding_mode)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics));
|
|
|
|
EXPECT_GT(key_blob.size(), 0U);
|
|
CheckSymmetricParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::TRIPLE_DES));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
|
|
<< "Key size " << key_size << "missing";
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.TripleDesWithAttestation
|
|
*
|
|
* Verifies that keymint can generate all required 3DES key sizes, and that the resulting keys
|
|
* have correct characteristics.
|
|
*
|
|
* Request attestation, which doesn't help for symmetric keys (as there is no public key to
|
|
* put in a certificate) but which isn't an error.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, TripleDesWithAttestation) {
|
|
for (auto key_size : ValidKeySizes(Algorithm::TRIPLE_DES)) {
|
|
for (auto block_mode : ValidBlockModes(Algorithm::TRIPLE_DES)) {
|
|
for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
|
|
SCOPED_TRACE(testing::Message()
|
|
<< "3DES-" << key_size << "-" << block_mode << "-" << padding_mode);
|
|
|
|
auto challenge = "hello";
|
|
auto app_id = "foo";
|
|
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(key_size)
|
|
.BlockMode(block_mode)
|
|
.Padding(padding_mode)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AttestationChallenge(challenge)
|
|
.AttestationApplicationId(app_id)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics));
|
|
|
|
EXPECT_GT(key_blob.size(), 0U);
|
|
CheckSymmetricParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::TRIPLE_DES));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
|
|
<< "Key size " << key_size << "missing";
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.TripleDesInvalidSize
|
|
*
|
|
* Verifies that specifying an invalid key size for 3-DES key generation returns
|
|
* UNSUPPORTED_KEY_SIZE.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, TripleDesInvalidSize) {
|
|
for (auto key_size : InvalidKeySizes(Algorithm::TRIPLE_DES)) {
|
|
for (auto block_mode : ValidBlockModes(Algorithm::TRIPLE_DES)) {
|
|
for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
|
|
SCOPED_TRACE(testing::Message()
|
|
<< "3DES-" << key_size << "-" << block_mode << "-" << padding_mode);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(key_size)
|
|
.BlockMode(block_mode)
|
|
.Padding(padding_mode)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Omitting the key size fails.
|
|
for (auto block_mode : ValidBlockModes(Algorithm::TRIPLE_DES)) {
|
|
for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
|
|
SCOPED_TRACE(testing::Message()
|
|
<< "3DES-default-" << block_mode << "-" << padding_mode);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_ALGORITHM, Algorithm::TRIPLE_DES)
|
|
.BlockMode(block_mode)
|
|
.Padding(padding_mode)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.Rsa
|
|
*
|
|
* Verifies that keymint can generate all required RSA key sizes, and that the resulting keys
|
|
* have correct characteristics.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, Rsa) {
|
|
for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
|
|
SCOPED_TRACE(testing::Message() << "RSA-" << key_size);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(key_size, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics));
|
|
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
|
|
<< "Key size " << key_size << "missing";
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.RsaWithMissingValidity
|
|
*
|
|
* Verifies that keymint returns an error while generating asymmetric key
|
|
* without providing NOT_BEFORE and NOT_AFTER parameters.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, RsaWithMissingValidity) {
|
|
if (AidlVersion() < 3) {
|
|
/*
|
|
* The KeyMint V1 spec required that CERTIFICATE_NOT_{BEFORE,AFTER} be
|
|
* specified for asymmetric key generation. However, this was not
|
|
* checked at the time so we can only be strict about checking this for
|
|
* implementations of KeyMint version 2 and above.
|
|
*/
|
|
GTEST_SKIP() << "Validity strict since KeyMint v2";
|
|
}
|
|
// Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set to
|
|
// GeneralizedTime 999912312359559, which is 253402300799000 ms from Jan 1, 1970.
|
|
constexpr uint64_t kUndefinedExpirationDateTime = 253402300799000;
|
|
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::MISSING_NOT_BEFORE,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_CERTIFICATE_NOT_AFTER,
|
|
kUndefinedExpirationDateTime),
|
|
&key_blob, &key_characteristics));
|
|
|
|
ASSERT_EQ(ErrorCode::MISSING_NOT_AFTER,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_CERTIFICATE_NOT_BEFORE, 0),
|
|
&key_blob, &key_characteristics));
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.RsaWithAttestation
|
|
*
|
|
* Verifies that keymint can generate all required RSA key sizes with attestation, and that the
|
|
* resulting keys have correct characteristics.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, RsaWithAttestation) {
|
|
auto challenge = "hello";
|
|
auto app_id = "foo";
|
|
|
|
auto subject = "cert subj 2";
|
|
vector<uint8_t> subject_der(make_name_from_str(subject));
|
|
|
|
uint64_t serial_int = 66;
|
|
vector<uint8_t> serial_blob(build_serial_blob(serial_int));
|
|
|
|
for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
|
|
SCOPED_TRACE(testing::Message() << "RSA-" << key_size);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
auto builder = AuthorizationSetBuilder()
|
|
.RsaSigningKey(key_size, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.AttestationChallenge(challenge)
|
|
.AttestationApplicationId(app_id)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
|
|
.Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
|
|
.SetDefaultValidity();
|
|
|
|
auto result = GenerateKey(builder, &key_blob, &key_characteristics);
|
|
// Strongbox may not support factory provisioned attestation key.
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) {
|
|
result = GenerateKeyWithSelfSignedAttestKey(
|
|
AuthorizationSetBuilder()
|
|
.RsaKey(key_size, 65537)
|
|
.AttestKey()
|
|
.SetDefaultValidity(), /* attest key params */
|
|
builder, &key_blob, &key_characteristics);
|
|
}
|
|
}
|
|
ASSERT_EQ(ErrorCode::OK, result);
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
|
|
<< "Key size " << key_size << "missing";
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
|
|
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
|
|
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
|
|
|
|
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
|
|
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
|
|
EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, //
|
|
sw_enforced, hw_enforced, SecLevel(),
|
|
cert_chain_[0].encodedCertificate));
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.RsaWithRkpAttestation
|
|
*
|
|
* Verifies that keymint can generate all required RSA key sizes using an attestation key
|
|
* that has been generated using an associate IRemotelyProvisionedComponent.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, RsaWithRkpAttestation) {
|
|
if (!IsRkpSupportRequired()) {
|
|
GTEST_SKIP() << "RKP support is not required on this platform";
|
|
}
|
|
|
|
// Check for an IRemotelyProvisionedComponent instance associated with the
|
|
// KeyMint instance.
|
|
std::shared_ptr<IRemotelyProvisionedComponent> rp = matching_rp_instance(GetParam());
|
|
if (rp == nullptr && SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Encountered StrongBox implementation that does not support RKP";
|
|
}
|
|
ASSERT_NE(rp, nullptr) << "No IRemotelyProvisionedComponent found that matches KeyMint device "
|
|
<< GetParam();
|
|
|
|
// Generate a P-256 keypair to use as an attestation key.
|
|
MacedPublicKey macedPubKey;
|
|
std::vector<uint8_t> privateKeyBlob;
|
|
auto status =
|
|
rp->generateEcdsaP256KeyPair(/* testMode= */ false, &macedPubKey, &privateKeyBlob);
|
|
ASSERT_TRUE(status.isOk());
|
|
vector<uint8_t> coseKeyData;
|
|
check_maced_pubkey(macedPubKey, /* testMode= */ false, &coseKeyData);
|
|
|
|
AttestationKey attestation_key;
|
|
attestation_key.keyBlob = std::move(privateKeyBlob);
|
|
attestation_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
|
|
|
|
for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
|
|
SCOPED_TRACE(testing::Message() << "RSA-" << key_size);
|
|
auto challenge = "hello";
|
|
auto app_id = "foo";
|
|
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(key_size, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.AttestationChallenge(challenge)
|
|
.AttestationApplicationId(app_id)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.SetDefaultValidity(),
|
|
attestation_key, &key_blob, &key_characteristics, &cert_chain_));
|
|
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
|
|
<< "Key size " << key_size << "missing";
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
|
|
|
|
// Attestation by itself is not valid (last entry is not self-signed).
|
|
EXPECT_FALSE(ChainSignaturesAreValid(cert_chain_));
|
|
|
|
// The signature over the attested key should correspond to the P256 public key.
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
X509_Ptr key_cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
|
|
ASSERT_TRUE(key_cert.get());
|
|
EVP_PKEY_Ptr signing_pubkey;
|
|
p256_pub_key(coseKeyData, &signing_pubkey);
|
|
ASSERT_TRUE(signing_pubkey.get());
|
|
|
|
ASSERT_TRUE(X509_verify(key_cert.get(), signing_pubkey.get()))
|
|
<< "Verification of attested certificate failed "
|
|
<< "OpenSSL error string: " << ERR_error_string(ERR_get_error(), NULL);
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaWithRkpAttestation
|
|
*
|
|
* Verifies that keymint can generate all required ECDSA key sizes using an attestation key
|
|
* that has been generated using an associate IRemotelyProvisionedComponent.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaWithRkpAttestation) {
|
|
if (!IsRkpSupportRequired()) {
|
|
GTEST_SKIP() << "RKP support is not required on this platform";
|
|
}
|
|
|
|
// Check for an IRemotelyProvisionedComponent instance associated with the
|
|
// KeyMint instance.
|
|
std::shared_ptr<IRemotelyProvisionedComponent> rp = matching_rp_instance(GetParam());
|
|
if (rp == nullptr && SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Encountered StrongBox implementation that does not support RKP";
|
|
}
|
|
ASSERT_NE(rp, nullptr) << "No IRemotelyProvisionedComponent found that matches KeyMint device "
|
|
<< GetParam();
|
|
|
|
// Generate a P-256 keypair to use as an attestation key.
|
|
MacedPublicKey macedPubKey;
|
|
std::vector<uint8_t> privateKeyBlob;
|
|
auto status =
|
|
rp->generateEcdsaP256KeyPair(/* testMode= */ false, &macedPubKey, &privateKeyBlob);
|
|
ASSERT_TRUE(status.isOk());
|
|
vector<uint8_t> coseKeyData;
|
|
check_maced_pubkey(macedPubKey, /* testMode= */ false, &coseKeyData);
|
|
|
|
AttestationKey attestation_key;
|
|
attestation_key.keyBlob = std::move(privateKeyBlob);
|
|
attestation_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
|
|
|
|
for (auto curve : ValidCurves()) {
|
|
SCOPED_TRACE(testing::Message() << "Curve::" << curve);
|
|
auto challenge = "hello";
|
|
auto app_id = "foo";
|
|
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(curve)
|
|
.Digest(Digest::NONE)
|
|
.AttestationChallenge(challenge)
|
|
.AttestationApplicationId(app_id)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.SetDefaultValidity(),
|
|
attestation_key, &key_blob, &key_characteristics, &cert_chain_));
|
|
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
|
|
|
|
// Attestation by itself is not valid (last entry is not self-signed).
|
|
EXPECT_FALSE(ChainSignaturesAreValid(cert_chain_));
|
|
|
|
// The signature over the attested key should correspond to the P256 public key.
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
X509_Ptr key_cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
|
|
ASSERT_TRUE(key_cert.get());
|
|
EVP_PKEY_Ptr signing_pubkey;
|
|
p256_pub_key(coseKeyData, &signing_pubkey);
|
|
ASSERT_TRUE(signing_pubkey.get());
|
|
|
|
ASSERT_TRUE(X509_verify(key_cert.get(), signing_pubkey.get()))
|
|
<< "Verification of attested certificate failed "
|
|
<< "OpenSSL error string: " << ERR_error_string(ERR_get_error(), NULL);
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.RsaEncryptionWithAttestation
|
|
*
|
|
* Verifies that keymint attestation for RSA encryption keys with challenge and
|
|
* app id is also successful.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) {
|
|
auto key_size = 2048;
|
|
auto challenge = "hello";
|
|
auto app_id = "foo";
|
|
|
|
auto subject = "subj 2";
|
|
vector<uint8_t> subject_der(make_name_from_str(subject));
|
|
|
|
uint64_t serial_int = 111166;
|
|
vector<uint8_t> serial_blob(build_serial_blob(serial_int));
|
|
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
auto builder = AuthorizationSetBuilder()
|
|
.RsaEncryptionKey(key_size, 65537)
|
|
.Padding(PaddingMode::NONE)
|
|
.AttestationChallenge(challenge)
|
|
.AttestationApplicationId(app_id)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
|
|
.Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
|
|
.SetDefaultValidity();
|
|
|
|
auto result = GenerateKey(builder, &key_blob, &key_characteristics);
|
|
// Strongbox may not support factory provisioned attestation key.
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) {
|
|
result = GenerateKeyWithSelfSignedAttestKey(
|
|
AuthorizationSetBuilder()
|
|
.RsaKey(key_size, 65537)
|
|
.AttestKey()
|
|
.SetDefaultValidity(), /* attest key params */
|
|
builder, &key_blob, &key_characteristics);
|
|
}
|
|
}
|
|
ASSERT_EQ(ErrorCode::OK, result);
|
|
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
AuthorizationSet auths;
|
|
for (auto& entry : key_characteristics) {
|
|
auths.push_back(AuthorizationSet(entry.authorizations));
|
|
}
|
|
|
|
EXPECT_TRUE(auths.Contains(TAG_ORIGIN, KeyOrigin::GENERATED));
|
|
EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::DECRYPT));
|
|
|
|
// Verify that App data and ROT are NOT included.
|
|
EXPECT_FALSE(auths.Contains(TAG_ROOT_OF_TRUST));
|
|
EXPECT_FALSE(auths.Contains(TAG_APPLICATION_DATA));
|
|
|
|
// Check that some unexpected tags/values are NOT present.
|
|
EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN));
|
|
EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::VERIFY));
|
|
|
|
EXPECT_FALSE(auths.Contains(TAG_AUTH_TIMEOUT, 301U));
|
|
|
|
auto os_ver = auths.GetTagValue(TAG_OS_VERSION);
|
|
ASSERT_TRUE(os_ver);
|
|
EXPECT_EQ(*os_ver, os_version());
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
|
|
<< "Key size " << key_size << "missing";
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
|
|
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
|
|
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
|
|
|
|
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
|
|
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
|
|
EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, //
|
|
sw_enforced, hw_enforced, SecLevel(),
|
|
cert_chain_[0].encodedCertificate));
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.RsaWithSelfSign
|
|
*
|
|
* Verifies that attesting to RSA key generation is successful, and returns
|
|
* self signed certificate if no challenge is provided. And signing etc
|
|
* works as expected.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, RsaWithSelfSign) {
|
|
auto subject = "cert subj subj subj subj subj subj 22222222222222222222";
|
|
vector<uint8_t> subject_der(make_name_from_str(subject));
|
|
|
|
uint64_t serial_int = 0;
|
|
vector<uint8_t> serial_blob(build_serial_blob(serial_int));
|
|
|
|
for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
|
|
SCOPED_TRACE(testing::Message() << "RSA-" << key_size);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(key_size, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
|
|
.Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics));
|
|
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
|
|
<< "Key size " << key_size << "missing";
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
|
|
|
|
ASSERT_EQ(cert_chain_.size(), 1);
|
|
verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
|
|
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.RsaWithAttestationMissAppId
|
|
*
|
|
* Verifies that attesting to RSA checks for missing app ID.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, RsaWithAttestationMissAppId) {
|
|
auto challenge = "hello";
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
|
|
auto builder = AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.AttestationChallenge(challenge)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.SetDefaultValidity();
|
|
|
|
auto result = GenerateKey(builder, &key_blob, &key_characteristics);
|
|
// Strongbox may not support factory provisioned attestation key.
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) {
|
|
result = GenerateKeyWithSelfSignedAttestKey(
|
|
AuthorizationSetBuilder()
|
|
.RsaKey(2048, 65537)
|
|
.AttestKey()
|
|
.SetDefaultValidity(), /* attest key params */
|
|
builder, &key_blob, &key_characteristics);
|
|
}
|
|
}
|
|
ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, result);
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.RsaWithAttestationAppIdIgnored
|
|
*
|
|
* Verifies that attesting to RSA ignores app id if challenge is missing.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, RsaWithAttestationAppIdIgnored) {
|
|
auto key_size = 2048;
|
|
auto app_id = "foo";
|
|
|
|
auto subject = "cert subj 2";
|
|
vector<uint8_t> subject_der(make_name_from_str(subject));
|
|
|
|
uint64_t serial_int = 1;
|
|
vector<uint8_t> serial_blob(build_serial_blob(serial_int));
|
|
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(key_size, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.AttestationApplicationId(app_id)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
|
|
.Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics));
|
|
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
|
|
<< "Key size " << key_size << "missing";
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
|
|
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
|
|
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
|
|
ASSERT_EQ(cert_chain_.size(), 1);
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.LimitedUsageRsa
|
|
*
|
|
* Verifies that KeyMint can generate all required RSA key sizes with limited usage, and that the
|
|
* resulting keys have correct characteristics.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, LimitedUsageRsa) {
|
|
for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
|
|
SCOPED_TRACE(testing::Message() << "RSA-" << key_size);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(key_size, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_USAGE_COUNT_LIMIT, 1)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics));
|
|
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
|
|
<< "Key size " << key_size << "missing";
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
|
|
|
|
// Check the usage count limit tag appears in the authorizations.
|
|
AuthorizationSet auths;
|
|
for (auto& entry : key_characteristics) {
|
|
auths.push_back(AuthorizationSet(entry.authorizations));
|
|
}
|
|
EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U))
|
|
<< "key usage count limit " << 1U << " missing";
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.LimitedUsageRsaWithAttestation
|
|
*
|
|
* Verifies that KeyMint can generate all required RSA key sizes with limited usage, and that the
|
|
* resulting keys have correct characteristics and attestation.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) {
|
|
auto challenge = "hello";
|
|
auto app_id = "foo";
|
|
|
|
auto subject = "cert subj 2";
|
|
vector<uint8_t> subject_der(make_name_from_str(subject));
|
|
|
|
uint64_t serial_int = 66;
|
|
vector<uint8_t> serial_blob(build_serial_blob(serial_int));
|
|
|
|
for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
|
|
SCOPED_TRACE(testing::Message() << "RSA-" << key_size);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
auto builder = AuthorizationSetBuilder()
|
|
.RsaSigningKey(key_size, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.AttestationChallenge(challenge)
|
|
.AttestationApplicationId(app_id)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_USAGE_COUNT_LIMIT, 1)
|
|
.Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
|
|
.Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
|
|
.SetDefaultValidity();
|
|
|
|
auto result = GenerateKey(builder, &key_blob, &key_characteristics);
|
|
// Strongbox may not support factory provisioned attestation key.
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) {
|
|
result = GenerateKeyWithSelfSignedAttestKey(
|
|
AuthorizationSetBuilder()
|
|
.RsaKey(key_size, 65537)
|
|
.AttestKey()
|
|
.SetDefaultValidity(), /* attest key params */
|
|
builder, &key_blob, &key_characteristics);
|
|
}
|
|
}
|
|
ASSERT_EQ(ErrorCode::OK, result);
|
|
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
|
|
<< "Key size " << key_size << "missing";
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
|
|
|
|
// Check the usage count limit tag appears in the authorizations.
|
|
AuthorizationSet auths;
|
|
for (auto& entry : key_characteristics) {
|
|
auths.push_back(AuthorizationSet(entry.authorizations));
|
|
}
|
|
EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U))
|
|
<< "key usage count limit " << 1U << " missing";
|
|
|
|
// Check the usage count limit tag also appears in the attestation.
|
|
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
|
|
|
|
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
|
|
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
|
|
EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, //
|
|
sw_enforced, hw_enforced, SecLevel(),
|
|
cert_chain_[0].encodedCertificate));
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.NoInvalidRsaSizes
|
|
*
|
|
* Verifies that keymint cannot generate any RSA key sizes that are designated as invalid.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, NoInvalidRsaSizes) {
|
|
for (auto key_size : InvalidKeySizes(Algorithm::RSA)) {
|
|
SCOPED_TRACE(testing::Message() << "RSA-" << key_size);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(key_size, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.RsaNoDefaultSize
|
|
*
|
|
* Verifies that failing to specify a key size for RSA key generation returns
|
|
* UNSUPPORTED_KEY_SIZE.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, RsaNoDefaultSize) {
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_ALGORITHM, Algorithm::RSA)
|
|
.Authorization(TAG_RSA_PUBLIC_EXPONENT, 3U)
|
|
.SigningKey()
|
|
.SetDefaultValidity()));
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.RsaMissingParams
|
|
*
|
|
* Verifies that omitting optional tags works.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, RsaMissingParams) {
|
|
for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
|
|
SCOPED_TRACE(testing::Message() << "RSA-" << key_size);
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
GenerateKey(
|
|
AuthorizationSetBuilder().RsaKey(key_size, 65537).SetDefaultValidity()));
|
|
CheckedDeleteKey();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.Ecdsa
|
|
*
|
|
* Verifies that keymint can generate all required EC curves, and that the resulting keys
|
|
* have correct characteristics.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, Ecdsa) {
|
|
for (auto curve : ValidCurves()) {
|
|
SCOPED_TRACE(testing::Message() << "Curve::" << curve);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(curve)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics));
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaCurve25519
|
|
*
|
|
* Verifies that keymint can generate a curve25519 key, and that the resulting key
|
|
* has correct characteristics.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaCurve25519) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
EcCurve curve = EcCurve::CURVE_25519;
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ErrorCode result = GenerateKey(AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(curve)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics);
|
|
ASSERT_EQ(result, ErrorCode::OK);
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
|
|
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcCurve25519MultiPurposeFail
|
|
*
|
|
* Verifies that KeyMint rejects an attempt to generate a curve 25519 key for both
|
|
* SIGN and AGREE_KEY.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaCurve25519MultiPurposeFail) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
EcCurve curve = EcCurve::CURVE_25519;
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ErrorCode result = GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
|
|
.EcdsaSigningKey(curve)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics);
|
|
ASSERT_EQ(result, ErrorCode::INCOMPATIBLE_PURPOSE);
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaWithMissingValidity
|
|
*
|
|
* Verifies that keymint returns an error while generating asymmetric key
|
|
* without providing NOT_BEFORE and NOT_AFTER parameters.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaWithMissingValidity) {
|
|
if (AidlVersion() < 2) {
|
|
/*
|
|
* The KeyMint V1 spec required that CERTIFICATE_NOT_{BEFORE,AFTER} be
|
|
* specified for asymmetric key generation. However, this was not
|
|
* checked at the time so we can only be strict about checking this for
|
|
* implementations of KeyMint version 2 and above.
|
|
*/
|
|
GTEST_SKIP() << "Validity strict since KeyMint v2";
|
|
}
|
|
// Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set to
|
|
// GeneralizedTime 999912312359559, which is 253402300799000 ms from Jan 1, 1970.
|
|
constexpr uint64_t kUndefinedExpirationDateTime = 253402300799000;
|
|
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::MISSING_NOT_BEFORE,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::NONE)
|
|
.Authorization(TAG_CERTIFICATE_NOT_AFTER,
|
|
kUndefinedExpirationDateTime),
|
|
&key_blob, &key_characteristics));
|
|
|
|
ASSERT_EQ(ErrorCode::MISSING_NOT_AFTER,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::NONE)
|
|
.Authorization(TAG_CERTIFICATE_NOT_BEFORE, 0),
|
|
&key_blob, &key_characteristics));
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaAttestation
|
|
*
|
|
* Verifies that for all Ecdsa key sizes, if challenge and app id is provided,
|
|
* an attestation will be generated.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaAttestation) {
|
|
auto challenge = "hello";
|
|
auto app_id = "foo";
|
|
|
|
auto subject = "cert subj 2";
|
|
vector<uint8_t> subject_der(make_name_from_str(subject));
|
|
|
|
uint64_t serial_int = 0xFFFFFFFFFFFFFFFF;
|
|
vector<uint8_t> serial_blob(build_serial_blob(serial_int));
|
|
|
|
for (auto curve : ValidCurves()) {
|
|
SCOPED_TRACE(testing::Message() << "Curve::" << curve);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
auto builder = AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(curve)
|
|
.Digest(Digest::NONE)
|
|
.AttestationChallenge(challenge)
|
|
.AttestationApplicationId(app_id)
|
|
.Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
|
|
.Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
|
|
.SetDefaultValidity();
|
|
|
|
auto result = GenerateKey(builder, &key_blob, &key_characteristics);
|
|
// Strongbox may not support factory provisioned attestation key.
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) {
|
|
result = GenerateKeyWithSelfSignedAttestKey(
|
|
AuthorizationSetBuilder()
|
|
.EcdsaKey(curve)
|
|
.AttestKey()
|
|
.SetDefaultValidity(), /* attest key params */
|
|
builder, &key_blob, &key_characteristics);
|
|
}
|
|
}
|
|
ASSERT_EQ(ErrorCode::OK, result);
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
|
|
|
|
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
|
|
|
|
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
|
|
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
|
|
EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, //
|
|
sw_enforced, hw_enforced, SecLevel(),
|
|
cert_chain_[0].encodedCertificate));
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaAttestationCurve25519
|
|
*
|
|
* Verifies that for a curve 25519 key, if challenge and app id is provided,
|
|
* an attestation will be generated.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaAttestationCurve25519) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
EcCurve curve = EcCurve::CURVE_25519;
|
|
auto challenge = "hello";
|
|
auto app_id = "foo";
|
|
|
|
auto subject = "cert subj 2";
|
|
vector<uint8_t> subject_der(make_name_from_str(subject));
|
|
|
|
uint64_t serial_int = 0xFFFFFFFFFFFFFFFF;
|
|
vector<uint8_t> serial_blob(build_serial_blob(serial_int));
|
|
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ErrorCode result = GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(curve)
|
|
.Digest(Digest::NONE)
|
|
.AttestationChallenge(challenge)
|
|
.AttestationApplicationId(app_id)
|
|
.Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
|
|
.Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics);
|
|
ASSERT_EQ(ErrorCode::OK, result);
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
|
|
|
|
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
|
|
|
|
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
|
|
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
|
|
EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, //
|
|
sw_enforced, hw_enforced, SecLevel(),
|
|
cert_chain_[0].encodedCertificate));
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaAttestationTags
|
|
*
|
|
* Verifies that creation of an attested ECDSA key includes various tags in the
|
|
* attestation extension.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) {
|
|
auto challenge = "hello";
|
|
auto app_id = "foo";
|
|
auto subject = "cert subj 2";
|
|
vector<uint8_t> subject_der(make_name_from_str(subject));
|
|
uint64_t serial_int = 0x1010;
|
|
vector<uint8_t> serial_blob(build_serial_blob(serial_int));
|
|
const AuthorizationSetBuilder base_builder =
|
|
AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::NONE)
|
|
.AttestationChallenge(challenge)
|
|
.AttestationApplicationId(app_id)
|
|
.Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
|
|
.Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
|
|
.SetDefaultValidity();
|
|
|
|
// Various tags that map to fields in the attestation extension ASN.1 schema.
|
|
auto extra_tags = AuthorizationSetBuilder()
|
|
.Authorization(TAG_ROLLBACK_RESISTANCE)
|
|
.Authorization(TAG_EARLY_BOOT_ONLY)
|
|
.Authorization(TAG_ACTIVE_DATETIME, 1619621648000)
|
|
.Authorization(TAG_ORIGINATION_EXPIRE_DATETIME, 1619621648000)
|
|
.Authorization(TAG_USAGE_EXPIRE_DATETIME, 1619621999000)
|
|
.Authorization(TAG_USAGE_COUNT_LIMIT, 42)
|
|
.Authorization(TAG_AUTH_TIMEOUT, 100000)
|
|
.Authorization(TAG_ALLOW_WHILE_ON_BODY)
|
|
.Authorization(TAG_TRUSTED_USER_PRESENCE_REQUIRED)
|
|
.Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED)
|
|
.Authorization(TAG_UNLOCKED_DEVICE_REQUIRED)
|
|
.Authorization(TAG_CREATION_DATETIME, 1619621648000);
|
|
|
|
for (const KeyParameter& tag : extra_tags) {
|
|
SCOPED_TRACE(testing::Message() << "tag-" << tag);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
AuthorizationSetBuilder builder = base_builder;
|
|
builder.push_back(tag);
|
|
auto result = GenerateKey(builder, &key_blob, &key_characteristics);
|
|
if (result == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE &&
|
|
tag.tag == TAG_ROLLBACK_RESISTANCE) {
|
|
continue;
|
|
}
|
|
if (result == ErrorCode::UNSUPPORTED_TAG && tag.tag == TAG_TRUSTED_USER_PRESENCE_REQUIRED) {
|
|
// Tag not required to be supported by all KeyMint implementations.
|
|
continue;
|
|
}
|
|
// Strongbox may not support factory provisioned attestation key.
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) {
|
|
result = GenerateKeyWithSelfSignedAttestKey(
|
|
AuthorizationSetBuilder()
|
|
.EcdsaKey(EcCurve::P_256)
|
|
.AttestKey()
|
|
.SetDefaultValidity(), /* attest key params */
|
|
builder, &key_blob, &key_characteristics);
|
|
}
|
|
}
|
|
ASSERT_EQ(result, ErrorCode::OK);
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
|
|
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
verify_subject_and_serial(cert_chain_[0], serial_int, subject, /* self_signed = */ false);
|
|
|
|
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
|
|
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
|
|
// Some tags are optional, so don't require them to be in the enforcements.
|
|
if (tag.tag != TAG_ATTESTATION_APPLICATION_ID && tag.tag != TAG_ALLOW_WHILE_ON_BODY) {
|
|
EXPECT_TRUE(hw_enforced.Contains(tag.tag) || sw_enforced.Contains(tag.tag))
|
|
<< tag << " not in hw:" << hw_enforced << " nor sw:" << sw_enforced;
|
|
}
|
|
|
|
// Verifying the attestation record will check for the specific tag because
|
|
// it's included in the authorizations.
|
|
EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, sw_enforced,
|
|
hw_enforced, SecLevel(),
|
|
cert_chain_[0].encodedCertificate));
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
|
|
// Collection of invalid attestation ID tags.
|
|
auto invalid_tags =
|
|
AuthorizationSetBuilder()
|
|
.Authorization(TAG_ATTESTATION_ID_BRAND, "bogus-brand")
|
|
.Authorization(TAG_ATTESTATION_ID_DEVICE, "devious-device")
|
|
.Authorization(TAG_ATTESTATION_ID_PRODUCT, "punctured-product")
|
|
.Authorization(TAG_ATTESTATION_ID_SERIAL, "suspicious-serial")
|
|
.Authorization(TAG_ATTESTATION_ID_IMEI, "invalid-imei")
|
|
.Authorization(TAG_ATTESTATION_ID_MEID, "mismatching-meid")
|
|
.Authorization(TAG_ATTESTATION_ID_MANUFACTURER, "malformed-manufacturer")
|
|
.Authorization(TAG_ATTESTATION_ID_MODEL, "malicious-model");
|
|
for (const KeyParameter& tag : invalid_tags) {
|
|
SCOPED_TRACE(testing::Message() << "-incorrect-tag-" << tag);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
AuthorizationSetBuilder builder =
|
|
AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::NONE)
|
|
.AttestationChallenge(challenge)
|
|
.AttestationApplicationId(app_id)
|
|
.Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
|
|
.Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
|
|
.SetDefaultValidity();
|
|
builder.push_back(tag);
|
|
|
|
auto error = GenerateKey(builder, &key_blob, &key_characteristics);
|
|
// Strongbox may not support factory provisioned attestation key.
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
if (error == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) {
|
|
error = GenerateKeyWithSelfSignedAttestKey(
|
|
AuthorizationSetBuilder()
|
|
.EcdsaKey(EcCurve::P_256)
|
|
.AttestKey()
|
|
.SetDefaultValidity(), /* attest key params */
|
|
builder, &key_blob, &key_characteristics);
|
|
}
|
|
}
|
|
ASSERT_EQ(error, ErrorCode::CANNOT_ATTEST_IDS);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaAttestationIdTags
|
|
*
|
|
* Verifies that creation of an attested ECDSA key includes various ID tags in the
|
|
* attestation extension.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaAttestationIdTags) {
|
|
if (is_gsi_image()) {
|
|
// GSI sets up a standard set of device identifiers that may not match
|
|
// the device identifiers held by the device.
|
|
GTEST_SKIP() << "Test not applicable under GSI";
|
|
}
|
|
auto challenge = "hello";
|
|
auto app_id = "foo";
|
|
auto subject = "cert subj 2";
|
|
vector<uint8_t> subject_der(make_name_from_str(subject));
|
|
uint64_t serial_int = 0x1010;
|
|
vector<uint8_t> serial_blob(build_serial_blob(serial_int));
|
|
const AuthorizationSetBuilder base_builder =
|
|
AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::NONE)
|
|
.AttestationChallenge(challenge)
|
|
.AttestationApplicationId(app_id)
|
|
.Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
|
|
.Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
|
|
.SetDefaultValidity();
|
|
|
|
// Various ATTESTATION_ID_* tags that map to fields in the attestation extension ASN.1 schema.
|
|
auto extra_tags = AuthorizationSetBuilder();
|
|
// Use ro.product.brand_for_attestation property for attestation if it is present else fallback
|
|
// to ro.product.brand
|
|
std::string prop_value =
|
|
::android::base::GetProperty("ro.product.brand_for_attestation", /* default= */ "");
|
|
if (!prop_value.empty()) {
|
|
add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_BRAND,
|
|
"ro.product.brand_for_attestation");
|
|
} else {
|
|
add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_BRAND, "ro.product.brand");
|
|
}
|
|
add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_DEVICE, "ro.product.device");
|
|
// Use ro.product.name_for_attestation property for attestation if it is present else fallback
|
|
// to ro.product.name
|
|
prop_value = ::android::base::GetProperty("ro.product.name_for_attestation", /* default= */ "");
|
|
if (!prop_value.empty()) {
|
|
add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_PRODUCT,
|
|
"ro.product.name_for_attestation");
|
|
} else {
|
|
add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_PRODUCT, "ro.product.name");
|
|
}
|
|
add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serialno");
|
|
add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_MANUFACTURER, "ro.product.manufacturer");
|
|
// Use ro.product.model_for_attestation property for attestation if it is present else fallback
|
|
// to ro.product.model
|
|
prop_value =
|
|
::android::base::GetProperty("ro.product.model_for_attestation", /* default= */ "");
|
|
if (!prop_value.empty()) {
|
|
add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_MODEL,
|
|
"ro.product.model_for_attestation");
|
|
} else {
|
|
add_tag_from_prop(&extra_tags, TAG_ATTESTATION_ID_MODEL, "ro.product.model");
|
|
}
|
|
|
|
for (const KeyParameter& tag : extra_tags) {
|
|
SCOPED_TRACE(testing::Message() << "tag-" << tag);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
AuthorizationSetBuilder builder = base_builder;
|
|
builder.push_back(tag);
|
|
auto result = GenerateKey(builder, &key_blob, &key_characteristics);
|
|
// Strongbox may not support factory provisioned attestation key.
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) return;
|
|
}
|
|
if (result == ErrorCode::CANNOT_ATTEST_IDS && !isDeviceIdAttestationRequired()) {
|
|
// ID attestation was optional till api level 32, from api level 33 it is mandatory.
|
|
continue;
|
|
}
|
|
ASSERT_EQ(result, ErrorCode::OK);
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
|
|
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
verify_subject_and_serial(cert_chain_[0], serial_int, subject, /* self_signed = */ false);
|
|
|
|
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
|
|
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
|
|
|
|
// The attested key characteristics will not contain APPLICATION_ID_* fields (their
|
|
// spec definitions all have "Must never appear in KeyCharacteristics"), but the
|
|
// attestation extension should contain them, so make sure the extra tag is added.
|
|
hw_enforced.push_back(tag);
|
|
|
|
// Verifying the attestation record will check for the specific tag because
|
|
// it's included in the authorizations.
|
|
EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, sw_enforced,
|
|
hw_enforced, SecLevel(),
|
|
cert_chain_[0].encodedCertificate));
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaAttestationUniqueId
|
|
*
|
|
* Verifies that creation of an attested ECDSA key with a UNIQUE_ID included.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaAttestationUniqueId) {
|
|
auto get_unique_id = [this](const std::string& app_id, uint64_t datetime,
|
|
vector<uint8_t>* unique_id, bool reset = false) {
|
|
auto challenge = "hello";
|
|
auto subject = "cert subj 2";
|
|
vector<uint8_t> subject_der(make_name_from_str(subject));
|
|
uint64_t serial_int = 0x1010;
|
|
vector<uint8_t> serial_blob(build_serial_blob(serial_int));
|
|
AuthorizationSetBuilder builder =
|
|
AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_INCLUDE_UNIQUE_ID)
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::NONE)
|
|
.AttestationChallenge(challenge)
|
|
.Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
|
|
.Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
|
|
.AttestationApplicationId(app_id)
|
|
.Authorization(TAG_CREATION_DATETIME, datetime)
|
|
.SetDefaultValidity();
|
|
if (reset) {
|
|
builder.Authorization(TAG_RESET_SINCE_ID_ROTATION);
|
|
}
|
|
auto result = GenerateKey(builder);
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) {
|
|
result = GenerateKeyWithSelfSignedAttestKey(
|
|
AuthorizationSetBuilder()
|
|
.EcdsaKey(EcCurve::P_256)
|
|
.AttestKey()
|
|
.SetDefaultValidity(), /* attest key params */
|
|
builder, &key_blob_, &key_characteristics_, &cert_chain_);
|
|
}
|
|
}
|
|
ASSERT_EQ(ErrorCode::OK, result);
|
|
ASSERT_GT(key_blob_.size(), 0U);
|
|
|
|
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
verify_subject_and_serial(cert_chain_[0], serial_int, subject, /* self_signed = */ false);
|
|
|
|
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics_);
|
|
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics_);
|
|
|
|
// Check that the unique ID field in the extension is non-empty.
|
|
EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, sw_enforced,
|
|
hw_enforced, SecLevel(),
|
|
cert_chain_[0].encodedCertificate, unique_id));
|
|
EXPECT_GT(unique_id->size(), 0);
|
|
CheckedDeleteKey();
|
|
};
|
|
|
|
// Generate unique ID
|
|
auto app_id = "foo";
|
|
uint64_t cert_date = 1619621648000; // Wed Apr 28 14:54:08 2021 in ms since epoch
|
|
vector<uint8_t> unique_id;
|
|
get_unique_id(app_id, cert_date, &unique_id);
|
|
|
|
// Generating a new key with the same parameters should give the same unique ID.
|
|
vector<uint8_t> unique_id2;
|
|
get_unique_id(app_id, cert_date, &unique_id2);
|
|
EXPECT_EQ(unique_id, unique_id2);
|
|
|
|
// Generating a new key with a slightly different date should give the same unique ID.
|
|
uint64_t rounded_date = cert_date / 2592000000LLU;
|
|
uint64_t min_date = rounded_date * 2592000000LLU;
|
|
uint64_t max_date = ((rounded_date + 1) * 2592000000LLU) - 1;
|
|
|
|
vector<uint8_t> unique_id3;
|
|
get_unique_id(app_id, min_date, &unique_id3);
|
|
EXPECT_EQ(unique_id, unique_id3);
|
|
|
|
vector<uint8_t> unique_id4;
|
|
get_unique_id(app_id, max_date, &unique_id4);
|
|
EXPECT_EQ(unique_id, unique_id4);
|
|
|
|
// A different attestation application ID should yield a different unique ID.
|
|
auto app_id2 = "different_foo";
|
|
vector<uint8_t> unique_id5;
|
|
get_unique_id(app_id2, cert_date, &unique_id5);
|
|
EXPECT_NE(unique_id, unique_id5);
|
|
|
|
// A radically different date should yield a different unique ID.
|
|
vector<uint8_t> unique_id6;
|
|
get_unique_id(app_id, 1611621648000, &unique_id6);
|
|
EXPECT_NE(unique_id, unique_id6);
|
|
|
|
vector<uint8_t> unique_id7;
|
|
get_unique_id(app_id, max_date + 1, &unique_id7);
|
|
EXPECT_NE(unique_id, unique_id7);
|
|
|
|
vector<uint8_t> unique_id8;
|
|
get_unique_id(app_id, min_date - 1, &unique_id8);
|
|
EXPECT_NE(unique_id, unique_id8);
|
|
|
|
// Marking RESET_SINCE_ID_ROTATION should give a different unique ID.
|
|
vector<uint8_t> unique_id9;
|
|
get_unique_id(app_id, cert_date, &unique_id9, /* reset_id = */ true);
|
|
EXPECT_NE(unique_id, unique_id9);
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaAttestationTagNoApplicationId
|
|
*
|
|
* Verifies that creation of an attested ECDSA key does not include APPLICATION_ID.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaAttestationTagNoApplicationId) {
|
|
auto challenge = "hello";
|
|
auto attest_app_id = "foo";
|
|
auto subject = "cert subj 2";
|
|
vector<uint8_t> subject_der(make_name_from_str(subject));
|
|
uint64_t serial_int = 0x1010;
|
|
vector<uint8_t> serial_blob(build_serial_blob(serial_int));
|
|
|
|
// Earlier versions of the attestation extension schema included a slot:
|
|
// applicationId [601] EXPLICIT OCTET_STRING OPTIONAL,
|
|
// This should never have been included, and should never be filled in.
|
|
// Generate an attested key that include APPLICATION_ID and APPLICATION_DATA,
|
|
// to confirm that this field never makes it into the attestation extension.
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
auto builder = AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::NONE)
|
|
.AttestationChallenge(challenge)
|
|
.AttestationApplicationId(attest_app_id)
|
|
.Authorization(TAG_APPLICATION_ID, "client_id")
|
|
.Authorization(TAG_APPLICATION_DATA, "appdata")
|
|
.Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
|
|
.Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
|
|
.SetDefaultValidity();
|
|
|
|
auto result = GenerateKey(builder, &key_blob, &key_characteristics);
|
|
// Strongbox may not support factory provisioned attestation key.
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) {
|
|
result = GenerateKeyWithSelfSignedAttestKey(
|
|
AuthorizationSetBuilder()
|
|
.EcdsaKey(EcCurve::P_256)
|
|
.AttestKey()
|
|
.SetDefaultValidity(), /* attest key params */
|
|
builder, &key_blob, &key_characteristics);
|
|
}
|
|
}
|
|
ASSERT_EQ(result, ErrorCode::OK);
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
|
|
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
verify_subject_and_serial(cert_chain_[0], serial_int, subject, /* self_signed = */ false);
|
|
|
|
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
|
|
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
|
|
EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, attest_app_id, sw_enforced,
|
|
hw_enforced, SecLevel(),
|
|
cert_chain_[0].encodedCertificate));
|
|
|
|
// Check that the app id is not in the cert.
|
|
string app_id = "clientid";
|
|
std::vector<uint8_t> needle(reinterpret_cast<const uint8_t*>(app_id.data()),
|
|
reinterpret_cast<const uint8_t*>(app_id.data()) + app_id.size());
|
|
ASSERT_EQ(std::search(cert_chain_[0].encodedCertificate.begin(),
|
|
cert_chain_[0].encodedCertificate.end(), needle.begin(), needle.end()),
|
|
cert_chain_[0].encodedCertificate.end());
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaSelfSignAttestation
|
|
*
|
|
* Verifies that if no challenge is provided to an Ecdsa key generation, then
|
|
* the key will generate a self signed attestation.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaSelfSignAttestation) {
|
|
auto subject = "cert subj 2";
|
|
vector<uint8_t> subject_der(make_name_from_str(subject));
|
|
|
|
uint64_t serial_int = 0x123456FFF1234;
|
|
vector<uint8_t> serial_blob(build_serial_blob(serial_int));
|
|
|
|
for (auto curve : ValidCurves()) {
|
|
SCOPED_TRACE(testing::Message() << "Curve::" << curve);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(curve)
|
|
.Digest(Digest::NONE)
|
|
.Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
|
|
.Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics));
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
|
|
|
|
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
|
|
ASSERT_EQ(cert_chain_.size(), 1);
|
|
verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
|
|
|
|
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
|
|
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaAttestationRequireAppId
|
|
*
|
|
* Verifies that if attestation challenge is provided to Ecdsa key generation, then
|
|
* app id must also be provided or else it will fail.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaAttestationRequireAppId) {
|
|
auto challenge = "hello";
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
auto builder = AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::NONE)
|
|
.AttestationChallenge(challenge)
|
|
.SetDefaultValidity();
|
|
|
|
auto result = GenerateKey(builder, &key_blob, &key_characteristics);
|
|
// Strongbox may not support factory provisioned attestation key.
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) {
|
|
result = GenerateKeyWithSelfSignedAttestKey(
|
|
AuthorizationSetBuilder()
|
|
.EcdsaKey(EcCurve::P_256)
|
|
.AttestKey()
|
|
.SetDefaultValidity(), /* attest key params */
|
|
builder, &key_blob, &key_characteristics);
|
|
}
|
|
}
|
|
ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, result);
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaIgnoreAppId
|
|
*
|
|
* Verifies that if no challenge is provided to the Ecdsa key generation, then
|
|
* any appid will be ignored, and keymint will generate a self sign certificate.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaIgnoreAppId) {
|
|
auto app_id = "foo";
|
|
|
|
for (auto curve : ValidCurves()) {
|
|
SCOPED_TRACE(testing::Message() << "Curve::" << curve);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(curve)
|
|
.Digest(Digest::NONE)
|
|
.AttestationApplicationId(app_id)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics));
|
|
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
|
|
|
|
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
|
|
ASSERT_EQ(cert_chain_.size(), 1);
|
|
|
|
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
|
|
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.AttestationApplicationIDLengthProperlyEncoded
|
|
*
|
|
* Verifies that the Attestation Application ID software enforced tag has a proper length encoding.
|
|
* Some implementations break strict encoding rules by encoding a length between 127 and 256 in one
|
|
* byte. Proper DER encoding specifies that for lengths greater than 127, one byte should be used
|
|
* to specify how many following bytes will be used to encode the length.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, AttestationApplicationIDLengthProperlyEncoded) {
|
|
auto challenge = "hello";
|
|
std::vector<uint32_t> app_id_lengths{143, 258};
|
|
|
|
for (uint32_t length : app_id_lengths) {
|
|
SCOPED_TRACE(testing::Message() << "app_id_len=" << length);
|
|
const string app_id(length, 'a');
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
auto builder = AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::NONE)
|
|
.AttestationChallenge(challenge)
|
|
.AttestationApplicationId(app_id)
|
|
.SetDefaultValidity();
|
|
|
|
auto result = GenerateKey(builder, &key_blob, &key_characteristics);
|
|
// Strongbox may not support factory provisioned attestation key.
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) {
|
|
result = GenerateKeyWithSelfSignedAttestKey(
|
|
AuthorizationSetBuilder()
|
|
.EcdsaKey(EcCurve::P_256)
|
|
.AttestKey()
|
|
.SetDefaultValidity(), /* attest key params */
|
|
builder, &key_blob, &key_characteristics);
|
|
}
|
|
}
|
|
ASSERT_EQ(ErrorCode::OK, result);
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, EcCurve::P_256)) << "Curve P256 missing";
|
|
|
|
EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
|
|
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
|
|
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
|
|
EXPECT_TRUE(verify_attestation_record(AidlVersion(), challenge, app_id, //
|
|
sw_enforced, hw_enforced, SecLevel(),
|
|
cert_chain_[0].encodedCertificate));
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.LimitedUsageEcdsa
|
|
*
|
|
* Verifies that KeyMint can generate all required EC key sizes with limited usage, and that the
|
|
* resulting keys have correct characteristics.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, LimitedUsageEcdsa) {
|
|
for (auto curve : ValidCurves()) {
|
|
SCOPED_TRACE(testing::Message() << "Curve::" << curve);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(curve)
|
|
.Digest(Digest::NONE)
|
|
.Authorization(TAG_USAGE_COUNT_LIMIT, 1)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics));
|
|
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
|
|
|
|
// Check the usage count limit tag appears in the authorizations.
|
|
AuthorizationSet auths;
|
|
for (auto& entry : key_characteristics) {
|
|
auths.push_back(AuthorizationSet(entry.authorizations));
|
|
}
|
|
EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U))
|
|
<< "key usage count limit " << 1U << " missing";
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaDefaultSize
|
|
*
|
|
* Verifies that failing to specify a curve for EC key generation returns
|
|
* UNSUPPORTED_KEY_SIZE.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaDefaultSize) {
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_ALGORITHM, Algorithm::EC)
|
|
.SigningKey()
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity()));
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaInvalidCurve
|
|
*
|
|
* Verifies that specifying an invalid curve for EC key generation returns
|
|
* UNSUPPORTED_KEY_SIZE.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaInvalidCurve) {
|
|
for (auto curve : InvalidCurves()) {
|
|
SCOPED_TRACE(testing::Message() << "Curve::" << curve);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
auto result = GenerateKey(AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(curve)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity(),
|
|
&key_blob, &key_characteristics);
|
|
ASSERT_TRUE(result == ErrorCode::UNSUPPORTED_KEY_SIZE ||
|
|
result == ErrorCode::UNSUPPORTED_EC_CURVE);
|
|
}
|
|
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_ALGORITHM, Algorithm::EC)
|
|
.Authorization(TAG_KEY_SIZE, 190)
|
|
.SigningKey()
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity()));
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaMissingCurve
|
|
*
|
|
* Verifies that EC key generation fails if EC_CURVE not specified after KeyMint V2.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaMissingCurve) {
|
|
if (AidlVersion() < 2) {
|
|
/*
|
|
* The KeyMint V1 spec required that EC_CURVE be specified for EC keys.
|
|
* However, this was not checked at the time so we can only be strict about checking this
|
|
* for implementations of KeyMint version 2 and above.
|
|
*/
|
|
GTEST_SKIP() << "Requiring EC_CURVE only strict since KeyMint v2";
|
|
}
|
|
/* If EC_CURVE not provided, generateKey
|
|
* must return ErrorCode::UNSUPPORTED_KEY_SIZE or ErrorCode::UNSUPPORTED_EC_CURVE.
|
|
*/
|
|
auto result = GenerateKey(
|
|
AuthorizationSetBuilder().EcdsaKey(256).Digest(Digest::NONE).SetDefaultValidity());
|
|
ASSERT_TRUE(result == ErrorCode::UNSUPPORTED_KEY_SIZE ||
|
|
result == ErrorCode::UNSUPPORTED_EC_CURVE);
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaMismatchKeySize
|
|
*
|
|
* Verifies that specifying mismatched key size and curve for EC key generation returns
|
|
* INVALID_ARGUMENT.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaMismatchKeySize) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Test not applicable to StrongBox device";
|
|
}
|
|
|
|
auto result = GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_ALGORITHM, Algorithm::EC)
|
|
.Authorization(TAG_KEY_SIZE, 224)
|
|
.Authorization(TAG_EC_CURVE, EcCurve::P_256)
|
|
.SigningKey()
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity());
|
|
ASSERT_TRUE(result == ErrorCode::INVALID_ARGUMENT);
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.EcdsaAllValidCurves
|
|
*
|
|
* Verifies that keymint does not support any curve designated as unsupported.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, EcdsaAllValidCurves) {
|
|
Digest digest;
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
digest = Digest::SHA_2_256;
|
|
} else {
|
|
digest = Digest::SHA_2_512;
|
|
}
|
|
for (auto curve : ValidCurves()) {
|
|
SCOPED_TRACE(testing::Message() << "Curve::" << curve);
|
|
EXPECT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(curve)
|
|
.Digest(digest)
|
|
.SetDefaultValidity()))
|
|
<< "Failed to generate key on curve: " << curve;
|
|
CheckedDeleteKey();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.Hmac
|
|
*
|
|
* Verifies that keymint supports all required digests, and that the resulting keys have correct
|
|
* characteristics.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, Hmac) {
|
|
for (auto digest : ValidDigests(false /* withNone */, true /* withMD5 */)) {
|
|
SCOPED_TRACE(testing::Message() << "Digest::" << digest);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
constexpr size_t key_size = 128;
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
GenerateKey(
|
|
AuthorizationSetBuilder().HmacKey(key_size).Digest(digest).Authorization(
|
|
TAG_MIN_MAC_LENGTH, 128),
|
|
&key_blob, &key_characteristics));
|
|
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::HMAC));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
|
|
<< "Key size " << key_size << "missing";
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.HmacNoAttestation
|
|
*
|
|
* Verifies that for Hmac key generation, no attestation will be generated even if challenge
|
|
* and app id are provided.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, HmacNoAttestation) {
|
|
auto challenge = "hello";
|
|
auto app_id = "foo";
|
|
|
|
for (auto digest : ValidDigests(false /* withNone */, true /* withMD5 */)) {
|
|
SCOPED_TRACE(testing::Message() << "Digest::" << digest);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
constexpr size_t key_size = 128;
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.HmacKey(key_size)
|
|
.Digest(digest)
|
|
.AttestationChallenge(challenge)
|
|
.AttestationApplicationId(app_id)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128),
|
|
&key_blob, &key_characteristics));
|
|
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
ASSERT_EQ(cert_chain_.size(), 0);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::HMAC));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
|
|
<< "Key size " << key_size << "missing";
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.LimitedUsageHmac
|
|
*
|
|
* Verifies that KeyMint supports all required digests with limited usage Hmac, and that the
|
|
* resulting keys have correct characteristics.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, LimitedUsageHmac) {
|
|
for (auto digest : ValidDigests(false /* withNone */, true /* withMD5 */)) {
|
|
SCOPED_TRACE(testing::Message() << "Digest::" << digest);
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
constexpr size_t key_size = 128;
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.HmacKey(key_size)
|
|
.Digest(digest)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)
|
|
.Authorization(TAG_USAGE_COUNT_LIMIT, 1),
|
|
&key_blob, &key_characteristics));
|
|
|
|
ASSERT_GT(key_blob.size(), 0U);
|
|
CheckBaseParams(key_characteristics);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::HMAC));
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
|
|
<< "Key size " << key_size << "missing";
|
|
|
|
// Check the usage count limit tag appears in the authorizations.
|
|
AuthorizationSet auths;
|
|
for (auto& entry : key_characteristics) {
|
|
auths.push_back(AuthorizationSet(entry.authorizations));
|
|
}
|
|
EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U))
|
|
<< "key usage count limit " << 1U << " missing";
|
|
|
|
CheckedDeleteKey(&key_blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.HmacCheckKeySizes
|
|
*
|
|
* Verifies that keymint supports all key sizes, and rejects all invalid key sizes.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, HmacCheckKeySizes) {
|
|
for (size_t key_size = 0; key_size <= 512; ++key_size) {
|
|
SCOPED_TRACE(testing::Message() << "HMAC-" << key_size);
|
|
if (key_size < 64 || key_size % 8 != 0) {
|
|
// To keep this test from being very slow, we only test a random fraction of
|
|
// non-byte key sizes. We test only ~10% of such cases. Since there are 392 of
|
|
// them, we expect to run ~40 of them in each run.
|
|
if (key_size % 8 == 0 || random() % 10 == 0) {
|
|
EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.HmacKey(key_size)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 256)))
|
|
<< "HMAC key size " << key_size << " invalid";
|
|
}
|
|
} else {
|
|
EXPECT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.HmacKey(key_size)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 256)))
|
|
<< "Failed to generate HMAC key of size " << key_size;
|
|
CheckedDeleteKey();
|
|
}
|
|
}
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
// STRONGBOX devices must not support keys larger than 512 bits.
|
|
size_t key_size = 520;
|
|
EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.HmacKey(key_size)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 256)))
|
|
<< "HMAC key size " << key_size << " unexpectedly valid";
|
|
}
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.HmacCheckMinMacLengths
|
|
*
|
|
* Verifies that keymint supports all required MAC lengths and rejects all invalid lengths. This
|
|
* test is probabilistic in order to keep the runtime down, but any failure prints out the
|
|
* specific MAC length that failed, so reproducing a failed run will be easy.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, HmacCheckMinMacLengths) {
|
|
for (size_t min_mac_length = 0; min_mac_length <= 256; ++min_mac_length) {
|
|
SCOPED_TRACE(testing::Message() << "MIN_MAC_LENGTH=" << min_mac_length);
|
|
if (min_mac_length < 64 || min_mac_length % 8 != 0) {
|
|
// To keep this test from being very long, we only test a random fraction of
|
|
// non-byte lengths. We test only ~10% of such cases. Since there are 172 of them,
|
|
// we expect to run ~17 of them in each run.
|
|
if (min_mac_length % 8 == 0 || random() % 10 == 0) {
|
|
EXPECT_EQ(ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.HmacKey(128)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, min_mac_length)))
|
|
<< "HMAC min mac length " << min_mac_length << " invalid.";
|
|
}
|
|
} else {
|
|
EXPECT_EQ(ErrorCode::OK,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.HmacKey(128)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, min_mac_length)))
|
|
<< "Failed to generate HMAC key with min MAC length " << min_mac_length;
|
|
CheckedDeleteKey();
|
|
}
|
|
}
|
|
|
|
// Minimum MAC length must be no more than 512 bits.
|
|
size_t min_mac_length = 520;
|
|
EXPECT_EQ(ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.HmacKey(128)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, min_mac_length)))
|
|
<< "HMAC min mac length " << min_mac_length << " invalid.";
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.HmacMultipleDigests
|
|
*
|
|
* Verifies that keymint rejects HMAC key generation with multiple specified digest algorithms.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, HmacMultipleDigests) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Test not applicable to StrongBox device";
|
|
}
|
|
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.HmacKey(128)
|
|
.Digest(Digest::SHA1)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.HmacDigestNone
|
|
*
|
|
* Verifies that keymint rejects HMAC key generation with no digest or Digest::NONE
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, HmacDigestNone) {
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
|
|
GenerateKey(AuthorizationSetBuilder().HmacKey(128).Authorization(TAG_MIN_MAC_LENGTH,
|
|
128)));
|
|
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.HmacKey(128)
|
|
.Digest(Digest::NONE)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.AesNoAttestation
|
|
*
|
|
* Verifies that attestation parameters to AES keys are ignored and generateKey
|
|
* will succeed.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, AesNoAttestation) {
|
|
auto challenge = "hello";
|
|
auto app_id = "foo";
|
|
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.EcbMode()
|
|
.Padding(PaddingMode::PKCS7)
|
|
.AttestationChallenge(challenge)
|
|
.AttestationApplicationId(app_id)));
|
|
|
|
ASSERT_EQ(cert_chain_.size(), 0);
|
|
}
|
|
|
|
/*
|
|
* NewKeyGenerationTest.TripleDesNoAttestation
|
|
*
|
|
* Verifies that attesting parameters to 3DES keys are ignored and generate key
|
|
* will be successful. No attestation should be generated.
|
|
*/
|
|
TEST_P(NewKeyGenerationTest, TripleDesNoAttestation) {
|
|
auto challenge = "hello";
|
|
auto app_id = "foo";
|
|
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(168)
|
|
.BlockMode(BlockMode::ECB)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::NONE)
|
|
.AttestationChallenge(challenge)
|
|
.AttestationApplicationId(app_id)));
|
|
ASSERT_EQ(cert_chain_.size(), 0);
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(NewKeyGenerationTest);
|
|
|
|
typedef KeyMintAidlTestBase SigningOperationsTest;
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaSuccess
|
|
*
|
|
* Verifies that raw RSA signature operations succeed.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaSuccess) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.SetDefaultValidity()));
|
|
string message = "12345678901234567890123456789012";
|
|
string signature = SignMessage(
|
|
message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
|
|
LocalVerifyMessage(message, signature,
|
|
AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaAllPaddingsAndDigests
|
|
*
|
|
* Verifies RSA signature/verification for all padding modes and digests.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaAllPaddingsAndDigests) {
|
|
auto authorizations = AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(ValidDigests(true /* withNone */, true /* withMD5 */))
|
|
.Padding(PaddingMode::NONE)
|
|
.Padding(PaddingMode::RSA_PSS)
|
|
.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
|
|
.SetDefaultValidity();
|
|
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(authorizations));
|
|
|
|
string message(128, 'a');
|
|
string corrupt_message(message);
|
|
++corrupt_message[corrupt_message.size() / 2];
|
|
|
|
for (auto padding :
|
|
{PaddingMode::NONE, PaddingMode::RSA_PSS, PaddingMode::RSA_PKCS1_1_5_SIGN}) {
|
|
for (auto digest : ValidDigests(true /* withNone */, true /* withMD5 */)) {
|
|
SCOPED_TRACE(testing::Message() << "RSA padding=" << padding << " digest=" << digest);
|
|
if (padding == PaddingMode::NONE && digest != Digest::NONE) {
|
|
// Digesting only makes sense with padding.
|
|
continue;
|
|
}
|
|
|
|
if (padding == PaddingMode::RSA_PSS && digest == Digest::NONE) {
|
|
// PSS requires digesting.
|
|
continue;
|
|
}
|
|
|
|
string signature =
|
|
SignMessage(message, AuthorizationSetBuilder().Digest(digest).Padding(padding));
|
|
LocalVerifyMessage(message, signature,
|
|
AuthorizationSetBuilder().Digest(digest).Padding(padding));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaUseRequiresCorrectAppIdAppData
|
|
*
|
|
* Verifies that using an RSA key requires the correct app data.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaUseRequiresCorrectAppIdAppData) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_APPLICATION_ID, "clientid")
|
|
.Authorization(TAG_APPLICATION_DATA, "appdata")
|
|
.SetDefaultValidity()));
|
|
|
|
CheckAppIdCharacteristics(key_blob_, "clientid", "appdata", key_characteristics_);
|
|
|
|
EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
|
|
Begin(KeyPurpose::SIGN,
|
|
AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
|
|
AbortIfNeeded();
|
|
EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_APPLICATION_ID, "clientid")));
|
|
AbortIfNeeded();
|
|
EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_APPLICATION_DATA, "appdata")));
|
|
AbortIfNeeded();
|
|
EXPECT_EQ(ErrorCode::OK,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_APPLICATION_DATA, "appdata")
|
|
.Authorization(TAG_APPLICATION_ID, "clientid")));
|
|
AbortIfNeeded();
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaPssSha256Success
|
|
*
|
|
* Verifies that RSA-PSS signature operations succeed.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaPssSha256Success) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_PSS)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.SetDefaultValidity()));
|
|
// Use large message, which won't work without digesting.
|
|
string message(1024, 'a');
|
|
string signature = SignMessage(
|
|
message,
|
|
AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaPaddingNoneDoesNotAllowOther
|
|
*
|
|
* Verifies that keymint rejects signature operations that specify a padding mode when the key
|
|
* supports only unpadded operations.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaPaddingNoneDoesNotAllowOther) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::NONE)
|
|
.SetDefaultValidity()));
|
|
string message = "12345678901234567890123456789012";
|
|
string signature;
|
|
|
|
EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.NoUserConfirmation
|
|
*
|
|
* Verifies that keymint rejects signing operations for keys with
|
|
* TRUSTED_CONFIRMATION_REQUIRED and no valid confirmation token
|
|
* presented.
|
|
*/
|
|
TEST_P(SigningOperationsTest, NoUserConfirmation) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Test not applicable to StrongBox device";
|
|
}
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(1024, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED)
|
|
.SetDefaultValidity()));
|
|
|
|
const string message = "12345678901234567890123456789012";
|
|
EXPECT_EQ(ErrorCode::OK,
|
|
Begin(KeyPurpose::SIGN,
|
|
AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
|
|
string signature;
|
|
EXPECT_EQ(ErrorCode::NO_USER_CONFIRMATION, Finish(message, &signature));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaPkcs1Sha256Success
|
|
*
|
|
* Verifies that digested RSA-PKCS1 signature operations succeed.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaPkcs1Sha256Success) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
|
|
.SetDefaultValidity()));
|
|
string message(1024, 'a');
|
|
string signature = SignMessage(message, AuthorizationSetBuilder()
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaPkcs1NoDigestSuccess
|
|
*
|
|
* Verifies that undigested RSA-PKCS1 signature operations succeed.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaPkcs1NoDigestSuccess) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
|
|
.SetDefaultValidity()));
|
|
string message(53, 'a');
|
|
string signature = SignMessage(message, AuthorizationSetBuilder()
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaPkcs1NoDigestTooLarge
|
|
*
|
|
* Verifies that undigested RSA-PKCS1 signature operations fail with the correct error code when
|
|
* given a too-long message.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaPkcs1NoDigestTooLong) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
|
|
.SetDefaultValidity()));
|
|
string message(257, 'a');
|
|
|
|
EXPECT_EQ(ErrorCode::OK,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
|
|
string signature;
|
|
EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &signature));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaPssSha512TooSmallKey
|
|
*
|
|
* Verifies that undigested RSA-PSS signature operations fail with the correct error code when
|
|
* used with a key that is too small for the message.
|
|
*
|
|
* A PSS-padded message is of length salt_size + digest_size + 16 (sizes in bits), and the
|
|
* keymint specification requires that salt_size == digest_size, so the message will be
|
|
* digest_size * 2 +
|
|
* 16. Such a message can only be signed by a given key if the key is at least that size. This
|
|
* test uses SHA512, which has a digest_size == 512, so the message size is 1040 bits, too large
|
|
* for a 1024-bit key.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaPssSha512TooSmallKey) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Test not applicable to StrongBox device";
|
|
}
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(1024, 65537)
|
|
.Digest(Digest::SHA_2_512)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::RSA_PSS)
|
|
.SetDefaultValidity()));
|
|
EXPECT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
|
|
.Digest(Digest::SHA_2_512)
|
|
.Padding(PaddingMode::RSA_PSS)));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaNoPaddingTooLong
|
|
*
|
|
* Verifies that raw RSA signature operations fail with the correct error code when
|
|
* given a too-long message.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaNoPaddingTooLong) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
|
|
.SetDefaultValidity()));
|
|
// One byte too long
|
|
string message(2048 / 8 + 1, 'a');
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
|
|
string result;
|
|
ErrorCode finish_error_code = Finish(message, &result);
|
|
EXPECT_TRUE(finish_error_code == ErrorCode::INVALID_INPUT_LENGTH ||
|
|
finish_error_code == ErrorCode::INVALID_ARGUMENT);
|
|
|
|
// Very large message that should exceed the transfer buffer size of any reasonable TEE.
|
|
message = string(128 * 1024, 'a');
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
|
|
finish_error_code = Finish(message, &result);
|
|
EXPECT_TRUE(finish_error_code == ErrorCode::INVALID_INPUT_LENGTH ||
|
|
finish_error_code == ErrorCode::INVALID_ARGUMENT);
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaAbort
|
|
*
|
|
* Verifies that operations can be aborted correctly. Uses an RSA signing operation for the
|
|
* test, but the behavior should be algorithm and purpose-independent.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaAbort) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::NONE)
|
|
.SetDefaultValidity()));
|
|
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
Begin(KeyPurpose::SIGN,
|
|
AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
|
|
EXPECT_EQ(ErrorCode::OK, Abort());
|
|
|
|
// Another abort should fail
|
|
EXPECT_EQ(ErrorCode::INVALID_OPERATION_HANDLE, Abort());
|
|
|
|
// Set to sentinel, so TearDown() doesn't try to abort again.
|
|
op_.reset();
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaNonUniqueParams
|
|
*
|
|
* Verifies that an operation with multiple padding modes is rejected.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaNonUniqueParams) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Digest(Digest::SHA1)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::NONE)
|
|
.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
|
|
.SetDefaultValidity()));
|
|
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
|
|
|
|
auto result = Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
|
|
.Digest(Digest::NONE)
|
|
.Digest(Digest::SHA1)
|
|
.Padding(PaddingMode::RSA_PKCS1_1_5_SIGN));
|
|
ASSERT_TRUE(result == ErrorCode::UNSUPPORTED_DIGEST || result == ErrorCode::INVALID_ARGUMENT);
|
|
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
|
|
Begin(KeyPurpose::SIGN,
|
|
AuthorizationSetBuilder().Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaUnsupportedPadding
|
|
*
|
|
* Verifies that RSA operations fail with the correct error (but key gen succeeds) when used
|
|
* with a padding mode inappropriate for RSA.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaUnsupportedPadding) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Digest(Digest::SHA_2_256 /* supported digest */)
|
|
.Padding(PaddingMode::PKCS7)
|
|
.SetDefaultValidity()));
|
|
ASSERT_EQ(
|
|
ErrorCode::UNSUPPORTED_PADDING_MODE,
|
|
Begin(KeyPurpose::SIGN,
|
|
AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::PKCS7)));
|
|
CheckedDeleteKey();
|
|
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
GenerateKey(
|
|
AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Digest(Digest::SHA_2_256 /* supported digest */)
|
|
.Padding(PaddingMode::RSA_OAEP) /* padding mode for encryption only */
|
|
.SetDefaultValidity()));
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_OAEP)));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaPssNoDigest
|
|
*
|
|
* Verifies that RSA PSS operations fail when no digest is used. PSS requires a digest.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaNoDigest) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::RSA_PSS)
|
|
.SetDefaultValidity()));
|
|
ASSERT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
|
|
Begin(KeyPurpose::SIGN,
|
|
AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::RSA_PSS)));
|
|
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Padding(PaddingMode::RSA_PSS)));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaPssNoPadding
|
|
*
|
|
* Verifies that RSA operations fail when no padding mode is specified. PaddingMode::NONE is
|
|
* supported in some cases (as validated in other tests), but a mode must be specified.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaNoPadding) {
|
|
// Padding must be specified
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.RsaKey(2048, 65537)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.SigningKey()
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity()));
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Digest(Digest::NONE)));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaShortMessage
|
|
*
|
|
* Verifies that raw RSA signatures succeed with a message shorter than the key size.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaTooShortMessage) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.SetDefaultValidity()));
|
|
|
|
// Barely shorter
|
|
string message(2048 / 8 - 1, 'a');
|
|
SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
|
|
|
|
// Much shorter
|
|
message = "a";
|
|
SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaSignWithEncryptionKey
|
|
*
|
|
* Verifies that RSA encryption keys cannot be used to sign.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaSignWithEncryptionKey) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaEncryptionKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.SetDefaultValidity()));
|
|
ASSERT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE,
|
|
Begin(KeyPurpose::SIGN,
|
|
AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.RsaSignTooLargeMessage
|
|
*
|
|
* Verifies that attempting a raw signature of a message which is the same length as the key,
|
|
* but numerically larger than the public modulus, fails with the correct error.
|
|
*/
|
|
TEST_P(SigningOperationsTest, RsaSignTooLargeMessage) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.SetDefaultValidity()));
|
|
|
|
// Largest possible message will always be larger than the public modulus.
|
|
string message(2048 / 8, static_cast<char>(0xff));
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)));
|
|
string signature;
|
|
ASSERT_EQ(ErrorCode::INVALID_ARGUMENT, Finish(message, &signature));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.EcdsaAllDigestsAndCurves
|
|
*
|
|
* Verifies ECDSA signature/verification for all digests and required curves.
|
|
*/
|
|
TEST_P(SigningOperationsTest, EcdsaAllDigestsAndCurves) {
|
|
|
|
string message = "1234567890";
|
|
string corrupt_message = "2234567890";
|
|
for (auto curve : ValidCurves()) {
|
|
SCOPED_TRACE(testing::Message() << "Curve::" << curve);
|
|
// Ed25519 only allows Digest::NONE.
|
|
auto digests = (curve == EcCurve::CURVE_25519)
|
|
? std::vector<Digest>(1, Digest::NONE)
|
|
: ValidDigests(true /* withNone */, false /* withMD5 */);
|
|
|
|
ErrorCode error = GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(curve)
|
|
.Digest(digests)
|
|
.SetDefaultValidity());
|
|
EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate key for EC curve " << curve;
|
|
if (error != ErrorCode::OK) {
|
|
continue;
|
|
}
|
|
|
|
for (auto digest : digests) {
|
|
SCOPED_TRACE(testing::Message() << "Digest::" << digest);
|
|
string signature = SignMessage(message, AuthorizationSetBuilder().Digest(digest));
|
|
LocalVerifyMessage(message, signature, AuthorizationSetBuilder().Digest(digest));
|
|
}
|
|
|
|
auto rc = DeleteKey();
|
|
ASSERT_TRUE(rc == ErrorCode::OK || rc == ErrorCode::UNIMPLEMENTED);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.EcdsaAllCurves
|
|
*
|
|
* Verifies that ECDSA operations succeed with all required curves.
|
|
*/
|
|
TEST_P(SigningOperationsTest, EcdsaAllCurves) {
|
|
for (auto curve : ValidCurves()) {
|
|
Digest digest = (curve == EcCurve::CURVE_25519 ? Digest::NONE : Digest::SHA_2_256);
|
|
SCOPED_TRACE(testing::Message() << "Curve::" << curve);
|
|
ErrorCode error = GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(curve)
|
|
.Digest(digest)
|
|
.SetDefaultValidity());
|
|
EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve;
|
|
if (error != ErrorCode::OK) continue;
|
|
|
|
string message(1024, 'a');
|
|
SignMessage(message, AuthorizationSetBuilder().Digest(digest));
|
|
CheckedDeleteKey();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.EcdsaCurve25519
|
|
*
|
|
* Verifies that ECDSA operations succeed with curve25519.
|
|
*/
|
|
TEST_P(SigningOperationsTest, EcdsaCurve25519) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
EcCurve curve = EcCurve::CURVE_25519;
|
|
ErrorCode error = GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(curve)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity());
|
|
ASSERT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve;
|
|
|
|
string message(1024, 'a');
|
|
SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE));
|
|
CheckedDeleteKey();
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.EcdsaCurve25519MaxSize
|
|
*
|
|
* Verifies that EDDSA operations with curve25519 under the maximum message size succeed.
|
|
*/
|
|
TEST_P(SigningOperationsTest, EcdsaCurve25519MaxSize) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
EcCurve curve = EcCurve::CURVE_25519;
|
|
ErrorCode error = GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(curve)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity());
|
|
ASSERT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve;
|
|
|
|
auto params = AuthorizationSetBuilder().Digest(Digest::NONE);
|
|
|
|
for (size_t msg_size : {MAX_ED25519_MSG_SIZE - 1, MAX_ED25519_MSG_SIZE}) {
|
|
SCOPED_TRACE(testing::Message() << "-msg-size=" << msg_size);
|
|
string message(msg_size, 'a');
|
|
|
|
// Attempt to sign via Begin+Finish.
|
|
AuthorizationSet out_params;
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params));
|
|
EXPECT_TRUE(out_params.empty());
|
|
string signature;
|
|
auto result = Finish(message, &signature);
|
|
EXPECT_EQ(result, ErrorCode::OK);
|
|
LocalVerifyMessage(message, signature, params);
|
|
|
|
// Attempt to sign via Begin+Update+Finish
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params));
|
|
EXPECT_TRUE(out_params.empty());
|
|
string output;
|
|
result = Update(message, &output);
|
|
EXPECT_EQ(result, ErrorCode::OK);
|
|
EXPECT_EQ(output.size(), 0);
|
|
string signature2;
|
|
EXPECT_EQ(ErrorCode::OK, Finish({}, &signature2));
|
|
LocalVerifyMessage(message, signature2, params);
|
|
}
|
|
|
|
CheckedDeleteKey();
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.EcdsaCurve25519MaxSizeFail
|
|
*
|
|
* Verifies that EDDSA operations with curve25519 fail when message size is too large.
|
|
*/
|
|
TEST_P(SigningOperationsTest, EcdsaCurve25519MaxSizeFail) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
EcCurve curve = EcCurve::CURVE_25519;
|
|
ErrorCode error = GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(curve)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity());
|
|
ASSERT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve;
|
|
|
|
auto params = AuthorizationSetBuilder().Digest(Digest::NONE);
|
|
|
|
for (size_t msg_size : {MAX_ED25519_MSG_SIZE + 1, MAX_ED25519_MSG_SIZE * 2}) {
|
|
SCOPED_TRACE(testing::Message() << "-msg-size=" << msg_size);
|
|
string message(msg_size, 'a');
|
|
|
|
// Attempt to sign via Begin+Finish.
|
|
AuthorizationSet out_params;
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params));
|
|
EXPECT_TRUE(out_params.empty());
|
|
string signature;
|
|
auto result = Finish(message, &signature);
|
|
EXPECT_EQ(result, ErrorCode::INVALID_INPUT_LENGTH);
|
|
|
|
// Attempt to sign via Begin+Update (but never get to Finish)
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, key_blob_, params, &out_params));
|
|
EXPECT_TRUE(out_params.empty());
|
|
string output;
|
|
result = Update(message, &output);
|
|
EXPECT_EQ(result, ErrorCode::INVALID_INPUT_LENGTH);
|
|
}
|
|
|
|
CheckedDeleteKey();
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.EcdsaNoDigestHugeData
|
|
*
|
|
* Verifies that ECDSA operations support very large messages, even without digesting. This
|
|
* should work because ECDSA actually only signs the leftmost L_n bits of the message, however
|
|
* large it may be. Not using digesting is a bad idea, but in some cases digesting is done by
|
|
* the framework.
|
|
*/
|
|
TEST_P(SigningOperationsTest, EcdsaNoDigestHugeData) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity()));
|
|
string message(1 * 1024, 'a');
|
|
SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.EcUseRequiresCorrectAppIdAppData
|
|
*
|
|
* Verifies that using an EC key requires the correct app ID/data.
|
|
*/
|
|
TEST_P(SigningOperationsTest, EcUseRequiresCorrectAppIdAppData) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::NONE)
|
|
.Authorization(TAG_APPLICATION_ID, "clientid")
|
|
.Authorization(TAG_APPLICATION_DATA, "appdata")
|
|
.SetDefaultValidity()));
|
|
|
|
CheckAppIdCharacteristics(key_blob_, "clientid", "appdata", key_characteristics_);
|
|
|
|
EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Digest(Digest::NONE)));
|
|
AbortIfNeeded();
|
|
EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
|
|
.Digest(Digest::NONE)
|
|
.Authorization(TAG_APPLICATION_ID, "clientid")));
|
|
AbortIfNeeded();
|
|
EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
|
|
.Digest(Digest::NONE)
|
|
.Authorization(TAG_APPLICATION_DATA, "appdata")));
|
|
AbortIfNeeded();
|
|
EXPECT_EQ(ErrorCode::OK,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
|
|
.Digest(Digest::NONE)
|
|
.Authorization(TAG_APPLICATION_DATA, "appdata")
|
|
.Authorization(TAG_APPLICATION_ID, "clientid")));
|
|
AbortIfNeeded();
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.EcdsaIncompatibleDigest
|
|
*
|
|
* Verifies that using an EC key requires compatible digest.
|
|
*/
|
|
TEST_P(SigningOperationsTest, EcdsaIncompatibleDigest) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::NONE)
|
|
.Digest(Digest::SHA1)
|
|
.SetDefaultValidity()));
|
|
EXPECT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Digest(Digest::SHA_2_256)));
|
|
AbortIfNeeded();
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.AesEcbSign
|
|
*
|
|
* Verifies that attempts to use AES keys to sign fail in the correct way.
|
|
*/
|
|
TEST_P(SigningOperationsTest, AesEcbSign) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.SigningKey()
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::ECB)));
|
|
|
|
AuthorizationSet out_params;
|
|
EXPECT_EQ(ErrorCode::UNSUPPORTED_PURPOSE,
|
|
Begin(KeyPurpose::SIGN, AuthorizationSet() /* in_params */, &out_params));
|
|
EXPECT_EQ(ErrorCode::UNSUPPORTED_PURPOSE,
|
|
Begin(KeyPurpose::VERIFY, AuthorizationSet() /* in_params */, &out_params));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.HmacAllDigests
|
|
*
|
|
* Verifies that HMAC works with all digests.
|
|
*/
|
|
TEST_P(SigningOperationsTest, HmacAllDigests) {
|
|
for (auto digest : ValidDigests(false /* withNone */, false /* withMD5 */)) {
|
|
SCOPED_TRACE(testing::Message() << "Digest::" << digest);
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.HmacKey(128)
|
|
.Digest(digest)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 160)))
|
|
<< "Failed to create HMAC key with digest " << digest;
|
|
string message = "12345678901234567890123456789012";
|
|
string signature = MacMessage(message, digest, 160);
|
|
EXPECT_EQ(160U / 8U, signature.size())
|
|
<< "Failed to sign with HMAC key with digest " << digest;
|
|
CheckedDeleteKey();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.HmacSha256TooLargeMacLength
|
|
*
|
|
* Verifies that HMAC fails in the correct way when asked to generate a MAC larger than the
|
|
* digest size.
|
|
*/
|
|
TEST_P(SigningOperationsTest, HmacSha256TooLargeMacLength) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.HmacKey(128)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 256)));
|
|
AuthorizationSet output_params;
|
|
EXPECT_EQ(ErrorCode::UNSUPPORTED_MAC_LENGTH, Begin(KeyPurpose::SIGN, key_blob_,
|
|
AuthorizationSetBuilder()
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MAC_LENGTH, 264),
|
|
&output_params));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.HmacSha256InvalidMacLength
|
|
*
|
|
* Verifies that HMAC fails in the correct way when asked to generate a MAC whose length is
|
|
* not a multiple of 8.
|
|
*/
|
|
TEST_P(SigningOperationsTest, HmacSha256InvalidMacLength) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.HmacKey(128)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 160)));
|
|
AuthorizationSet output_params;
|
|
EXPECT_EQ(ErrorCode::UNSUPPORTED_MAC_LENGTH, Begin(KeyPurpose::SIGN, key_blob_,
|
|
AuthorizationSetBuilder()
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MAC_LENGTH, 161),
|
|
&output_params));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.HmacSha256TooSmallMacLength
|
|
*
|
|
* Verifies that HMAC fails in the correct way when asked to generate a MAC smaller than the
|
|
* specified minimum MAC length.
|
|
*/
|
|
TEST_P(SigningOperationsTest, HmacSha256TooSmallMacLength) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.HmacKey(128)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
|
|
AuthorizationSet output_params;
|
|
EXPECT_EQ(ErrorCode::INVALID_MAC_LENGTH, Begin(KeyPurpose::SIGN, key_blob_,
|
|
AuthorizationSetBuilder()
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MAC_LENGTH, 120),
|
|
&output_params));
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.HmacRfc4231TestCase3
|
|
*
|
|
* Validates against the test vectors from RFC 4231 test case 3.
|
|
*/
|
|
TEST_P(SigningOperationsTest, HmacRfc4231TestCase3) {
|
|
string key(20, 0xaa);
|
|
string message(50, 0xdd);
|
|
uint8_t sha_224_expected[] = {
|
|
0x7f, 0xb3, 0xcb, 0x35, 0x88, 0xc6, 0xc1, 0xf6, 0xff, 0xa9, 0x69, 0x4d, 0x7d, 0x6a,
|
|
0xd2, 0x64, 0x93, 0x65, 0xb0, 0xc1, 0xf6, 0x5d, 0x69, 0xd1, 0xec, 0x83, 0x33, 0xea,
|
|
};
|
|
uint8_t sha_256_expected[] = {
|
|
0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, 0x85, 0x4d, 0xb8,
|
|
0xeb, 0xd0, 0x91, 0x81, 0xa7, 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8,
|
|
0xc1, 0x22, 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe,
|
|
};
|
|
uint8_t sha_384_expected[] = {
|
|
0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a, 0x0a, 0xa2, 0xac, 0xe0,
|
|
0x14, 0xc8, 0xa8, 0x6f, 0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb,
|
|
0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b, 0x2a, 0x5a, 0xb3, 0x9d,
|
|
0xc1, 0x38, 0x14, 0xb9, 0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27,
|
|
};
|
|
uint8_t sha_512_expected[] = {
|
|
0xfa, 0x73, 0xb0, 0x08, 0x9d, 0x56, 0xa2, 0x84, 0xef, 0xb0, 0xf0, 0x75, 0x6c,
|
|
0x89, 0x0b, 0xe9, 0xb1, 0xb5, 0xdb, 0xdd, 0x8e, 0xe8, 0x1a, 0x36, 0x55, 0xf8,
|
|
0x3e, 0x33, 0xb2, 0x27, 0x9d, 0x39, 0xbf, 0x3e, 0x84, 0x82, 0x79, 0xa7, 0x22,
|
|
0xc8, 0x06, 0xb4, 0x85, 0xa4, 0x7e, 0x67, 0xc8, 0x07, 0xb9, 0x46, 0xa3, 0x37,
|
|
0xbe, 0xe8, 0x94, 0x26, 0x74, 0x27, 0x88, 0x59, 0xe1, 0x32, 0x92, 0xfb,
|
|
};
|
|
|
|
CheckHmacTestVector(key, message, Digest::SHA_2_256, make_string(sha_256_expected));
|
|
if (SecLevel() != SecurityLevel::STRONGBOX) {
|
|
CheckHmacTestVector(key, message, Digest::SHA_2_224, make_string(sha_224_expected));
|
|
CheckHmacTestVector(key, message, Digest::SHA_2_384, make_string(sha_384_expected));
|
|
CheckHmacTestVector(key, message, Digest::SHA_2_512, make_string(sha_512_expected));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* SigningOperationsTest.HmacRfc4231TestCase5
|
|
*
|
|
* Validates against the test vectors from RFC 4231 test case 5.
|
|
*/
|
|
TEST_P(SigningOperationsTest, HmacRfc4231TestCase5) {
|
|
string key(20, 0x0c);
|
|
string message = "Test With Truncation";
|
|
|
|
uint8_t sha_224_expected[] = {
|
|
0x0e, 0x2a, 0xea, 0x68, 0xa9, 0x0c, 0x8d, 0x37,
|
|
0xc9, 0x88, 0xbc, 0xdb, 0x9f, 0xca, 0x6f, 0xa8,
|
|
};
|
|
uint8_t sha_256_expected[] = {
|
|
0xa3, 0xb6, 0x16, 0x74, 0x73, 0x10, 0x0e, 0xe0,
|
|
0x6e, 0x0c, 0x79, 0x6c, 0x29, 0x55, 0x55, 0x2b,
|
|
};
|
|
uint8_t sha_384_expected[] = {
|
|
0x3a, 0xbf, 0x34, 0xc3, 0x50, 0x3b, 0x2a, 0x23,
|
|
0xa4, 0x6e, 0xfc, 0x61, 0x9b, 0xae, 0xf8, 0x97,
|
|
};
|
|
uint8_t sha_512_expected[] = {
|
|
0x41, 0x5f, 0xad, 0x62, 0x71, 0x58, 0x0a, 0x53,
|
|
0x1d, 0x41, 0x79, 0xbc, 0x89, 0x1d, 0x87, 0xa6,
|
|
};
|
|
|
|
CheckHmacTestVector(key, message, Digest::SHA_2_256, make_string(sha_256_expected));
|
|
if (SecLevel() != SecurityLevel::STRONGBOX) {
|
|
CheckHmacTestVector(key, message, Digest::SHA_2_224, make_string(sha_224_expected));
|
|
CheckHmacTestVector(key, message, Digest::SHA_2_384, make_string(sha_384_expected));
|
|
CheckHmacTestVector(key, message, Digest::SHA_2_512, make_string(sha_512_expected));
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(SigningOperationsTest);
|
|
|
|
typedef KeyMintAidlTestBase VerificationOperationsTest;
|
|
|
|
/*
|
|
* VerificationOperationsTest.HmacSigningKeyCannotVerify
|
|
*
|
|
* Verifies HMAC signing and verification, but that a signing key cannot be used to verify.
|
|
*/
|
|
TEST_P(VerificationOperationsTest, HmacSigningKeyCannotVerify) {
|
|
string key_material = "HelloThisIsAKey";
|
|
|
|
vector<uint8_t> signing_key, verification_key;
|
|
vector<KeyCharacteristics> signing_key_chars, verification_key_chars;
|
|
EXPECT_EQ(ErrorCode::OK,
|
|
ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_ALGORITHM, Algorithm::HMAC)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::SIGN)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 160),
|
|
KeyFormat::RAW, key_material, &signing_key, &signing_key_chars));
|
|
EXPECT_EQ(ErrorCode::OK,
|
|
ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_ALGORITHM, Algorithm::HMAC)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::VERIFY)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 160),
|
|
KeyFormat::RAW, key_material, &verification_key, &verification_key_chars));
|
|
|
|
string message = "This is a message.";
|
|
string signature = SignMessage(
|
|
signing_key, message,
|
|
AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Authorization(TAG_MAC_LENGTH, 160));
|
|
|
|
// Signing key should not work.
|
|
AuthorizationSet out_params;
|
|
EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE,
|
|
Begin(KeyPurpose::VERIFY, signing_key,
|
|
AuthorizationSetBuilder().Digest(Digest::SHA_2_256), &out_params));
|
|
|
|
// Verification key should work.
|
|
VerifyMessage(verification_key, message, signature,
|
|
AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
|
|
|
|
CheckedDeleteKey(&signing_key);
|
|
CheckedDeleteKey(&verification_key);
|
|
}
|
|
|
|
/*
|
|
* VerificationOperationsTest.HmacVerificationFailsForCorruptSignature
|
|
*
|
|
* Verifies HMAC signature verification should fails if message or signature is corrupted.
|
|
*/
|
|
TEST_P(VerificationOperationsTest, HmacVerificationFailsForCorruptSignature) {
|
|
string key_material = "HelloThisIsAKey";
|
|
|
|
vector<uint8_t> signing_key, verification_key;
|
|
vector<KeyCharacteristics> signing_key_chars, verification_key_chars;
|
|
EXPECT_EQ(ErrorCode::OK,
|
|
ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_ALGORITHM, Algorithm::HMAC)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::SIGN)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 160),
|
|
KeyFormat::RAW, key_material, &signing_key, &signing_key_chars));
|
|
EXPECT_EQ(ErrorCode::OK,
|
|
ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_ALGORITHM, Algorithm::HMAC)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::VERIFY)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 160),
|
|
KeyFormat::RAW, key_material, &verification_key, &verification_key_chars));
|
|
|
|
string message = "This is a message.";
|
|
string signature = SignMessage(
|
|
signing_key, message,
|
|
AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Authorization(TAG_MAC_LENGTH, 160));
|
|
|
|
AuthorizationSet begin_out_params;
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
Begin(KeyPurpose::VERIFY, verification_key,
|
|
AuthorizationSetBuilder().Digest(Digest::SHA_2_256), &begin_out_params));
|
|
|
|
string corruptMessage = "This is b message."; // Corrupted message
|
|
string output;
|
|
EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(corruptMessage, signature, &output));
|
|
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
Begin(KeyPurpose::VERIFY, verification_key,
|
|
AuthorizationSetBuilder().Digest(Digest::SHA_2_256), &begin_out_params));
|
|
|
|
signature[0] += 1; // Corrupt a signature
|
|
EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(message, signature, &output));
|
|
|
|
CheckedDeleteKey(&signing_key);
|
|
CheckedDeleteKey(&verification_key);
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(VerificationOperationsTest);
|
|
|
|
typedef KeyMintAidlTestBase ExportKeyTest;
|
|
|
|
/*
|
|
* ExportKeyTest.RsaUnsupportedKeyFormat
|
|
*
|
|
* Verifies that attempting to export RSA keys in PKCS#8 format fails with the correct error.
|
|
*/
|
|
// TODO(seleneh) add ExportKey to GenerateKey
|
|
// check result
|
|
|
|
class ImportKeyTest : public NewKeyGenerationTest {
|
|
public:
|
|
template <TagType tag_type, Tag tag, typename ValueT>
|
|
void CheckCryptoParam(TypedTag<tag_type, tag> ttag, ValueT expected) {
|
|
SCOPED_TRACE("CheckCryptoParam");
|
|
for (auto& entry : key_characteristics_) {
|
|
if (entry.securityLevel == SecLevel()) {
|
|
EXPECT_TRUE(contains(entry.authorizations, ttag, expected))
|
|
<< "Tag " << tag << " with value " << expected
|
|
<< " not found at security level" << entry.securityLevel;
|
|
} else {
|
|
EXPECT_FALSE(contains(entry.authorizations, ttag, expected))
|
|
<< "Tag " << tag << " found at security level " << entry.securityLevel;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CheckOrigin() {
|
|
SCOPED_TRACE("CheckOrigin");
|
|
// Origin isn't a crypto param, but it always lives with them.
|
|
return CheckCryptoParam(TAG_ORIGIN, KeyOrigin::IMPORTED);
|
|
}
|
|
};
|
|
|
|
/*
|
|
* ImportKeyTest.RsaSuccess
|
|
*
|
|
* Verifies that importing and using an RSA key pair works correctly.
|
|
*/
|
|
TEST_P(ImportKeyTest, RsaSuccess) {
|
|
uint32_t key_size;
|
|
string key;
|
|
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
key_size = 2048;
|
|
key = rsa_2048_key;
|
|
} else {
|
|
key_size = 1024;
|
|
key = rsa_key;
|
|
}
|
|
|
|
ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaSigningKey(key_size, 65537)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_PSS)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, key));
|
|
|
|
CheckCryptoParam(TAG_ALGORITHM, Algorithm::RSA);
|
|
CheckCryptoParam(TAG_KEY_SIZE, key_size);
|
|
CheckCryptoParam(TAG_RSA_PUBLIC_EXPONENT, 65537U);
|
|
CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
|
|
CheckCryptoParam(TAG_PADDING, PaddingMode::RSA_PSS);
|
|
CheckOrigin();
|
|
|
|
string message(1024 / 8, 'a');
|
|
auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS);
|
|
string signature = SignMessage(message, params);
|
|
LocalVerifyMessage(message, signature, params);
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.RsaSuccessWithoutParams
|
|
*
|
|
* Verifies that importing and using an RSA key pair without specifying parameters
|
|
* works correctly.
|
|
*/
|
|
TEST_P(ImportKeyTest, RsaSuccessWithoutParams) {
|
|
uint32_t key_size;
|
|
string key;
|
|
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
key_size = 2048;
|
|
key = rsa_2048_key;
|
|
} else {
|
|
key_size = 1024;
|
|
key = rsa_key;
|
|
}
|
|
|
|
ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.SigningKey()
|
|
.Authorization(TAG_ALGORITHM, Algorithm::RSA)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_PSS)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, key));
|
|
|
|
// Key size and public exponent are determined from the imported key material.
|
|
CheckCryptoParam(TAG_KEY_SIZE, key_size);
|
|
CheckCryptoParam(TAG_RSA_PUBLIC_EXPONENT, 65537U);
|
|
|
|
CheckCryptoParam(TAG_ALGORITHM, Algorithm::RSA);
|
|
CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
|
|
CheckCryptoParam(TAG_PADDING, PaddingMode::RSA_PSS);
|
|
CheckOrigin();
|
|
|
|
string message(1024 / 8, 'a');
|
|
auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS);
|
|
string signature = SignMessage(message, params);
|
|
LocalVerifyMessage(message, signature, params);
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.RsaKeySizeMismatch
|
|
*
|
|
* Verifies that importing an RSA key pair with a size that doesn't match the key fails in the
|
|
* correct way.
|
|
*/
|
|
TEST_P(ImportKeyTest, RsaKeySizeMismatch) {
|
|
ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH,
|
|
ImportKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048 /* Doesn't match key */, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, rsa_key));
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.RsaPublicExponentMismatch
|
|
*
|
|
* Verifies that importing an RSA key pair with a public exponent that doesn't match the key
|
|
* fails in the correct way.
|
|
*/
|
|
TEST_P(ImportKeyTest, RsaPublicExponentMismatch) {
|
|
ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH,
|
|
ImportKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(1024, 3 /* Doesn't match key */)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, rsa_key));
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.RsaAttestMultiPurposeFail
|
|
*
|
|
* Verifies that importing an RSA key pair with purpose ATTEST_KEY+SIGN fails.
|
|
*/
|
|
TEST_P(ImportKeyTest, RsaAttestMultiPurposeFail) {
|
|
if (AidlVersion() < 2) {
|
|
// The KeyMint v1 spec required that KeyPurpose::ATTEST_KEY not be combined
|
|
// with other key purposes. However, this was not checked at the time
|
|
// so we can only be strict about checking this for implementations of KeyMint
|
|
// version 2 and above.
|
|
GTEST_SKIP() << "Single-purpose for KeyPurpose::ATTEST_KEY only strict since KeyMint v2";
|
|
}
|
|
uint32_t key_size = 2048;
|
|
string key = rsa_2048_key;
|
|
|
|
ASSERT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE,
|
|
ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaSigningKey(key_size, 65537)
|
|
.AttestKey()
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_PSS)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, key));
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.EcdsaSuccess
|
|
*
|
|
* Verifies that importing and using an ECDSA P-256 key pair works correctly.
|
|
*/
|
|
TEST_P(ImportKeyTest, EcdsaSuccess) {
|
|
ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::SHA_2_256)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, ec_256_key));
|
|
|
|
CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
|
|
CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
|
|
CheckCryptoParam(TAG_EC_CURVE, EcCurve::P_256);
|
|
|
|
CheckOrigin();
|
|
|
|
string message(32, 'a');
|
|
auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
|
|
string signature = SignMessage(message, params);
|
|
LocalVerifyMessage(message, signature, params);
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.EcdsaP256RFC5915Success
|
|
*
|
|
* Verifies that importing and using an ECDSA P-256 key pair encoded using RFC5915 works
|
|
* correctly.
|
|
*/
|
|
TEST_P(ImportKeyTest, EcdsaP256RFC5915Success) {
|
|
ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::SHA_2_256)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, ec_256_key_rfc5915));
|
|
|
|
CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
|
|
CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
|
|
CheckCryptoParam(TAG_EC_CURVE, EcCurve::P_256);
|
|
|
|
CheckOrigin();
|
|
|
|
string message(32, 'a');
|
|
auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
|
|
string signature = SignMessage(message, params);
|
|
LocalVerifyMessage(message, signature, params);
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.EcdsaP256SEC1Success
|
|
*
|
|
* Verifies that importing and using an ECDSA P-256 key pair encoded using SEC1 works correctly.
|
|
*/
|
|
TEST_P(ImportKeyTest, EcdsaP256SEC1Success) {
|
|
ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::SHA_2_256)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, ec_256_key_sec1));
|
|
|
|
CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
|
|
CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
|
|
CheckCryptoParam(TAG_EC_CURVE, EcCurve::P_256);
|
|
|
|
CheckOrigin();
|
|
|
|
string message(32, 'a');
|
|
auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
|
|
string signature = SignMessage(message, params);
|
|
LocalVerifyMessage(message, signature, params);
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.Ecdsa521Success
|
|
*
|
|
* Verifies that importing and using an ECDSA P-521 key pair works correctly.
|
|
*/
|
|
TEST_P(ImportKeyTest, Ecdsa521Success) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Test not applicable to StrongBox device";
|
|
}
|
|
ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(EcCurve::P_521)
|
|
.Digest(Digest::SHA_2_256)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, ec_521_key));
|
|
|
|
CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
|
|
CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
|
|
CheckCryptoParam(TAG_EC_CURVE, EcCurve::P_521);
|
|
CheckOrigin();
|
|
|
|
string message(32, 'a');
|
|
auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
|
|
string signature = SignMessage(message, params);
|
|
LocalVerifyMessage(message, signature, params);
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.EcdsaCurveMismatch
|
|
*
|
|
* Verifies that importing an ECDSA key pair with a curve that doesn't match the key fails in
|
|
* the correct way.
|
|
*/
|
|
TEST_P(ImportKeyTest, EcdsaCurveMismatch) {
|
|
ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH,
|
|
ImportKey(AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(EcCurve::P_224 /* Doesn't match key */)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, ec_256_key));
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.EcdsaAttestMultiPurposeFail
|
|
*
|
|
* Verifies that importing and using an ECDSA P-256 key pair with purpose ATTEST_KEY+SIGN fails.
|
|
*/
|
|
TEST_P(ImportKeyTest, EcdsaAttestMultiPurposeFail) {
|
|
if (AidlVersion() < 2) {
|
|
// The KeyMint v1 spec required that KeyPurpose::ATTEST_KEY not be combined
|
|
// with other key purposes. However, this was not checked at the time
|
|
// so we can only be strict about checking this for implementations of KeyMint
|
|
// version 2 and above.
|
|
GTEST_SKIP() << "Single-purpose for KeyPurpose::ATTEST_KEY only strict since KeyMint v2";
|
|
}
|
|
ASSERT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE,
|
|
ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.AttestKey()
|
|
.Digest(Digest::SHA_2_256)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, ec_256_key));
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.Ed25519RawSuccess
|
|
*
|
|
* Verifies that importing and using a raw Ed25519 private key works correctly.
|
|
*/
|
|
TEST_P(ImportKeyTest, Ed25519RawSuccess) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(EcCurve::CURVE_25519)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::RAW, ed25519_key));
|
|
CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
|
|
CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519);
|
|
CheckOrigin();
|
|
|
|
// The returned cert should hold the correct public key.
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate));
|
|
ASSERT_NE(kmKeyCert, nullptr);
|
|
EVP_PKEY_Ptr kmPubKey(X509_get_pubkey(kmKeyCert.get()));
|
|
ASSERT_NE(kmPubKey.get(), nullptr);
|
|
size_t kmPubKeySize = 32;
|
|
uint8_t kmPubKeyData[32];
|
|
ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize));
|
|
ASSERT_EQ(kmPubKeySize, 32);
|
|
EXPECT_EQ(string(kmPubKeyData, kmPubKeyData + 32), ed25519_pubkey);
|
|
|
|
string message(32, 'a');
|
|
auto params = AuthorizationSetBuilder().Digest(Digest::NONE);
|
|
string signature = SignMessage(message, params);
|
|
LocalVerifyMessage(message, signature, params);
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.Ed25519Pkcs8Success
|
|
*
|
|
* Verifies that importing and using a PKCS#8-encoded Ed25519 private key works correctly.
|
|
*/
|
|
TEST_P(ImportKeyTest, Ed25519Pkcs8Success) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(EcCurve::CURVE_25519)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, ed25519_pkcs8_key));
|
|
CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
|
|
CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519);
|
|
CheckOrigin();
|
|
|
|
// The returned cert should hold the correct public key.
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate));
|
|
ASSERT_NE(kmKeyCert, nullptr);
|
|
EVP_PKEY_Ptr kmPubKey(X509_get_pubkey(kmKeyCert.get()));
|
|
ASSERT_NE(kmPubKey.get(), nullptr);
|
|
size_t kmPubKeySize = 32;
|
|
uint8_t kmPubKeyData[32];
|
|
ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize));
|
|
ASSERT_EQ(kmPubKeySize, 32);
|
|
EXPECT_EQ(string(kmPubKeyData, kmPubKeyData + 32), ed25519_pubkey);
|
|
|
|
string message(32, 'a');
|
|
auto params = AuthorizationSetBuilder().Digest(Digest::NONE);
|
|
string signature = SignMessage(message, params);
|
|
LocalVerifyMessage(message, signature, params);
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.Ed25519CurveMismatch
|
|
*
|
|
* Verifies that importing an Ed25519 key pair with a curve that doesn't match the key fails in
|
|
* the correct way.
|
|
*/
|
|
TEST_P(ImportKeyTest, Ed25519CurveMismatch) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
ASSERT_NE(ErrorCode::OK,
|
|
ImportKey(AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(EcCurve::P_224 /* Doesn't match key */)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::RAW, ed25519_key));
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.Ed25519FormatMismatch
|
|
*
|
|
* Verifies that importing an Ed25519 key pair with an invalid format fails.
|
|
*/
|
|
TEST_P(ImportKeyTest, Ed25519FormatMismatch) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(EcCurve::CURVE_25519)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, ed25519_key));
|
|
ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(EcCurve::CURVE_25519)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::RAW, ed25519_pkcs8_key));
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.Ed25519PurposeMismatch
|
|
*
|
|
* Verifies that importing an Ed25519 key pair with an invalid purpose fails.
|
|
*/
|
|
TEST_P(ImportKeyTest, Ed25519PurposeMismatch) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
// Can't have both SIGN and ATTEST_KEY
|
|
ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(EcCurve::CURVE_25519)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::ATTEST_KEY)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::RAW, ed25519_key));
|
|
// AGREE_KEY is for X25519 (but can only tell the difference if the import key is in
|
|
// PKCS#8 format and so includes an OID).
|
|
ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.EcdsaKey(EcCurve::CURVE_25519)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, ed25519_pkcs8_key));
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.X25519RawSuccess
|
|
*
|
|
* Verifies that importing and using a raw X25519 private key works correctly.
|
|
*/
|
|
TEST_P(ImportKeyTest, X25519RawSuccess) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaKey(EcCurve::CURVE_25519)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::RAW, x25519_key));
|
|
|
|
CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
|
|
CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519);
|
|
CheckOrigin();
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.X25519Pkcs8Success
|
|
*
|
|
* Verifies that importing and using a PKCS#8-encoded X25519 private key works correctly.
|
|
*/
|
|
TEST_P(ImportKeyTest, X25519Pkcs8Success) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaKey(EcCurve::CURVE_25519)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, x25519_pkcs8_key));
|
|
|
|
CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
|
|
CheckCryptoParam(TAG_EC_CURVE, EcCurve::CURVE_25519);
|
|
CheckOrigin();
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.X25519CurveMismatch
|
|
*
|
|
* Verifies that importing an X25519 key with a curve that doesn't match the key fails in
|
|
* the correct way.
|
|
*/
|
|
TEST_P(ImportKeyTest, X25519CurveMismatch) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.EcdsaKey(EcCurve::P_224 /* Doesn't match key */)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::RAW, x25519_key));
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.X25519FormatMismatch
|
|
*
|
|
* Verifies that importing an X25519 key with an invalid format fails.
|
|
*/
|
|
TEST_P(ImportKeyTest, X25519FormatMismatch) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.EcdsaKey(EcCurve::CURVE_25519)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, x25519_key));
|
|
ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.EcdsaKey(EcCurve::CURVE_25519)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::RAW, x25519_pkcs8_key));
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.X25519PurposeMismatch
|
|
*
|
|
* Verifies that importing an X25519 key pair with an invalid format fails.
|
|
*/
|
|
TEST_P(ImportKeyTest, X25519PurposeMismatch) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.EcdsaKey(EcCurve::CURVE_25519)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::ATTEST_KEY)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, x25519_pkcs8_key));
|
|
ASSERT_NE(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.EcdsaSigningKey(EcCurve::CURVE_25519)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, x25519_pkcs8_key));
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.AesSuccess
|
|
*
|
|
* Verifies that importing and using an AES key works.
|
|
*/
|
|
TEST_P(ImportKeyTest, AesSuccess) {
|
|
string key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(key.size() * 8)
|
|
.EcbMode()
|
|
.Padding(PaddingMode::PKCS7),
|
|
KeyFormat::RAW, key));
|
|
|
|
CheckCryptoParam(TAG_ALGORITHM, Algorithm::AES);
|
|
CheckCryptoParam(TAG_KEY_SIZE, 128U);
|
|
CheckCryptoParam(TAG_PADDING, PaddingMode::PKCS7);
|
|
CheckCryptoParam(TAG_BLOCK_MODE, BlockMode::ECB);
|
|
CheckOrigin();
|
|
|
|
string message = "Hello World!";
|
|
auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
|
|
string ciphertext = EncryptMessage(message, params);
|
|
string plaintext = DecryptMessage(ciphertext, params);
|
|
EXPECT_EQ(message, plaintext);
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.AesFailure
|
|
*
|
|
* Verifies that importing an invalid AES key fails.
|
|
*/
|
|
TEST_P(ImportKeyTest, AesFailure) {
|
|
string key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
uint32_t bitlen = key.size() * 8;
|
|
for (uint32_t key_size : {bitlen - 1, bitlen + 1, bitlen - 8, bitlen + 8}) {
|
|
SCOPED_TRACE(testing::Message() << "import-key-size=" << key_size);
|
|
// Explicit key size doesn't match that of the provided key.
|
|
auto result = ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(key_size)
|
|
.EcbMode()
|
|
.Padding(PaddingMode::PKCS7),
|
|
KeyFormat::RAW, key);
|
|
ASSERT_TRUE(result == ErrorCode::IMPORT_PARAMETER_MISMATCH ||
|
|
result == ErrorCode::UNSUPPORTED_KEY_SIZE)
|
|
<< "unexpected result: " << result;
|
|
}
|
|
|
|
// Explicit key size matches that of the provided key, but it's not a valid size.
|
|
string long_key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
|
|
ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(long_key.size() * 8)
|
|
.EcbMode()
|
|
.Padding(PaddingMode::PKCS7),
|
|
KeyFormat::RAW, long_key));
|
|
string short_key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
|
|
ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(short_key.size() * 8)
|
|
.EcbMode()
|
|
.Padding(PaddingMode::PKCS7),
|
|
KeyFormat::RAW, short_key));
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.TripleDesSuccess
|
|
*
|
|
* Verifies that importing and using a 3DES key works.
|
|
*/
|
|
TEST_P(ImportKeyTest, TripleDesSuccess) {
|
|
string key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358");
|
|
ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.TripleDesEncryptionKey(168)
|
|
.EcbMode()
|
|
.Padding(PaddingMode::PKCS7),
|
|
KeyFormat::RAW, key));
|
|
|
|
CheckCryptoParam(TAG_ALGORITHM, Algorithm::TRIPLE_DES);
|
|
CheckCryptoParam(TAG_KEY_SIZE, 168U);
|
|
CheckCryptoParam(TAG_PADDING, PaddingMode::PKCS7);
|
|
CheckCryptoParam(TAG_BLOCK_MODE, BlockMode::ECB);
|
|
CheckOrigin();
|
|
|
|
string message = "Hello World!";
|
|
auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
|
|
string ciphertext = EncryptMessage(message, params);
|
|
string plaintext = DecryptMessage(ciphertext, params);
|
|
EXPECT_EQ(message, plaintext);
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.TripleDesFailure
|
|
*
|
|
* Verifies that importing an invalid 3DES key fails.
|
|
*/
|
|
TEST_P(ImportKeyTest, TripleDesFailure) {
|
|
string key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358");
|
|
uint32_t bitlen = key.size() * 7;
|
|
for (uint32_t key_size : {bitlen - 1, bitlen + 1, bitlen - 8, bitlen + 8}) {
|
|
SCOPED_TRACE(testing::Message() << "import-key-size=" << key_size);
|
|
// Explicit key size doesn't match that of the provided key.
|
|
auto result = ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.TripleDesEncryptionKey(key_size)
|
|
.EcbMode()
|
|
.Padding(PaddingMode::PKCS7),
|
|
KeyFormat::RAW, key);
|
|
ASSERT_TRUE(result == ErrorCode::IMPORT_PARAMETER_MISMATCH ||
|
|
result == ErrorCode::UNSUPPORTED_KEY_SIZE)
|
|
<< "unexpected result: " << result;
|
|
}
|
|
// Explicit key size matches that of the provided key, but it's not a valid size.
|
|
string long_key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f735800");
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
|
|
ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.TripleDesEncryptionKey(long_key.size() * 7)
|
|
.EcbMode()
|
|
.Padding(PaddingMode::PKCS7),
|
|
KeyFormat::RAW, long_key));
|
|
string short_key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f73");
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
|
|
ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.TripleDesEncryptionKey(short_key.size() * 7)
|
|
.EcbMode()
|
|
.Padding(PaddingMode::PKCS7),
|
|
KeyFormat::RAW, short_key));
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.HmacKeySuccess
|
|
*
|
|
* Verifies that importing and using an HMAC key works.
|
|
*/
|
|
TEST_P(ImportKeyTest, HmacKeySuccess) {
|
|
string key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.HmacKey(key.size() * 8)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 256),
|
|
KeyFormat::RAW, key));
|
|
|
|
CheckCryptoParam(TAG_ALGORITHM, Algorithm::HMAC);
|
|
CheckCryptoParam(TAG_KEY_SIZE, 128U);
|
|
CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
|
|
CheckOrigin();
|
|
|
|
string message = "Hello World!";
|
|
string signature = MacMessage(message, Digest::SHA_2_256, 256);
|
|
VerifyMessage(message, signature, AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
|
|
}
|
|
|
|
/*
|
|
* ImportKeyTest.GetKeyCharacteristics
|
|
*
|
|
* Verifies that imported keys have the correct characteristics.
|
|
*/
|
|
TEST_P(ImportKeyTest, GetKeyCharacteristics) {
|
|
vector<uint8_t> key_blob;
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
auto base_builder = AuthorizationSetBuilder()
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.SetDefaultValidity();
|
|
vector<Algorithm> algorithms = {Algorithm::RSA, Algorithm::EC, Algorithm::HMAC, Algorithm::AES,
|
|
Algorithm::TRIPLE_DES};
|
|
ErrorCode result;
|
|
string symKey = hex2str("a49d7564199e97cb529d2c9d97bf2f98"); // 128 bits
|
|
string tdesKey = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358"); // 192 bits
|
|
for (auto alg : algorithms) {
|
|
SCOPED_TRACE(testing::Message() << "Algorithm-" << alg);
|
|
AuthorizationSetBuilder builder(base_builder);
|
|
switch (alg) {
|
|
case Algorithm::RSA:
|
|
builder.RsaSigningKey(2048, 65537).Digest(Digest::NONE);
|
|
|
|
result = ImportKey(builder, KeyFormat::PKCS8, rsa_2048_key, &key_blob,
|
|
&key_characteristics);
|
|
break;
|
|
case Algorithm::EC:
|
|
builder.EcdsaSigningKey(EcCurve::P_256).Digest(Digest::NONE);
|
|
result = ImportKey(builder, KeyFormat::PKCS8, ec_256_key, &key_blob,
|
|
&key_characteristics);
|
|
break;
|
|
case Algorithm::HMAC:
|
|
builder.HmacKey(128)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128);
|
|
result =
|
|
ImportKey(builder, KeyFormat::RAW, symKey, &key_blob, &key_characteristics);
|
|
break;
|
|
case Algorithm::AES:
|
|
builder.AesEncryptionKey(128).BlockMode(BlockMode::ECB);
|
|
result =
|
|
ImportKey(builder, KeyFormat::RAW, symKey, &key_blob, &key_characteristics);
|
|
break;
|
|
case Algorithm::TRIPLE_DES:
|
|
builder.TripleDesEncryptionKey(168).BlockMode(BlockMode::ECB);
|
|
result = ImportKey(builder, KeyFormat::RAW, tdesKey, &key_blob,
|
|
&key_characteristics);
|
|
break;
|
|
default:
|
|
ADD_FAILURE() << "Invalid Algorithm " << uint32_t(alg);
|
|
continue;
|
|
}
|
|
ASSERT_EQ(ErrorCode::OK, result);
|
|
CheckCharacteristics(key_blob, key_characteristics);
|
|
CheckCommonParams(key_characteristics, KeyOrigin::IMPORTED);
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(ImportKeyTest);
|
|
|
|
auto wrapped_key = hex2str(
|
|
// IKeyMintDevice.aidl
|
|
"30820179" // SEQUENCE length 0x179 (SecureKeyWrapper) {
|
|
"020100" // INTEGER length 1 value 0x00 (version)
|
|
"04820100" // OCTET STRING length 0x100 (encryptedTransportKey)
|
|
"934bf94e2aa28a3f83c9f79297250262"
|
|
"fbe3276b5a1c91159bbfa3ef8957aac8"
|
|
"4b59b30b455a79c2973480823d8b3863"
|
|
"c3deef4a8e243590268d80e18751a0e1"
|
|
"30f67ce6a1ace9f79b95e097474febc9"
|
|
"81195b1d13a69086c0863f66a7b7fdb4"
|
|
"8792227b1ac5e2489febdf087ab54864"
|
|
"83033a6f001ca5d1ec1e27f5c30f4cec"
|
|
"2642074a39ae68aee552e196627a8e3d"
|
|
"867e67a8c01b11e75f13cca0a97ab668"
|
|
"b50cda07a8ecb7cd8e3dd7009c963653"
|
|
"4f6f239cffe1fc8daa466f78b676c711"
|
|
"9efb96bce4e69ca2a25d0b34ed9c3ff9"
|
|
"99b801597d5220e307eaa5bee507fb94"
|
|
"d1fa69f9e519b2de315bac92c36f2ea1"
|
|
"fa1df4478c0ddedeae8c70e0233cd098"
|
|
"040c" // OCTET STRING length 0x0c (initializationVector)
|
|
"d796b02c370f1fa4cc0124f1"
|
|
"302e" // SEQUENCE length 0x2e (KeyDescription) {
|
|
"020103" // INTEGER length 1 value 0x03 (keyFormat = RAW)
|
|
"3029" // SEQUENCE length 0x29 (AuthorizationList) {
|
|
"a108" // [1] context-specific constructed tag=1 length 0x08 { (purpose)
|
|
"3106" // SET length 0x06
|
|
"020100" // INTEGER length 1 value 0x00 (Encrypt)
|
|
"020101" // INTEGER length 1 value 0x01 (Decrypt)
|
|
// } end SET
|
|
// } end [1]
|
|
"a203" // [2] context-specific constructed tag=2 length 0x02 { (algorithm)
|
|
"020120" // INTEGER length 1 value 0x20 (AES)
|
|
// } end [2]
|
|
"a304" // [3] context-specific constructed tag=3 length 0x04 { (keySize)
|
|
"02020100" // INTEGER length 2 value 0x100
|
|
// } end [3]
|
|
"a405" // [4] context-specific constructed tag=4 length 0x05 { (blockMode)
|
|
"3103" // SET length 0x03 {
|
|
"020101" // INTEGER length 1 value 0x01 (ECB)
|
|
// } end SET
|
|
// } end [4]
|
|
"a605" // [6] context-specific constructed tag=6 length 0x05 { (padding)
|
|
"3103" // SET length 0x03 {
|
|
"020140" // INTEGER length 1 value 0x40 (PKCS7)
|
|
// } end SET
|
|
// } end [5]
|
|
"bf837702" // [503] context-specific constructed tag=503=0x1F7 length 0x02 {
|
|
// (noAuthRequired)
|
|
"0500" // NULL
|
|
// } end [503]
|
|
// } end SEQUENCE (AuthorizationList)
|
|
// } end SEQUENCE (KeyDescription)
|
|
"0420" // OCTET STRING length 0x20 (encryptedKey)
|
|
"ccd540855f833a5e1480bfd2d36faf3a"
|
|
"eee15df5beabe2691bc82dde2a7aa910"
|
|
"0410" // OCTET STRING length 0x10 (tag)
|
|
"64c9f689c60ff6223ab6e6999e0eb6e5"
|
|
// } SEQUENCE (SecureKeyWrapper)
|
|
);
|
|
|
|
auto wrapped_key_masked = hex2str(
|
|
// IKeyMintDevice.aidl
|
|
"30820179" // SEQUENCE length 0x179 (SecureKeyWrapper) {
|
|
"020100" // INTEGER length 1 value 0x00 (version)
|
|
"04820100" // OCTET STRING length 0x100 (encryptedTransportKey)
|
|
"aad93ed5924f283b4bb5526fbe7a1412"
|
|
"f9d9749ec30db9062b29e574a8546f33"
|
|
"c88732452f5b8e6a391ee76c39ed1712"
|
|
"c61d8df6213dec1cffbc17a8c6d04c7b"
|
|
"30893d8daa9b2015213e219468215532"
|
|
"07f8f9931c4caba23ed3bee28b36947e"
|
|
"47f10e0a5c3dc51c988a628daad3e5e1"
|
|
"f4005e79c2d5a96c284b4b8d7e4948f3"
|
|
"31e5b85dd5a236f85579f3ea1d1b8484"
|
|
"87470bdb0ab4f81a12bee42c99fe0df4"
|
|
"bee3759453e69ad1d68a809ce06b949f"
|
|
"7694a990429b2fe81e066ff43e56a216"
|
|
"02db70757922a4bcc23ab89f1e35da77"
|
|
"586775f423e519c2ea394caf48a28d0c"
|
|
"8020f1dcf6b3a68ec246f615ae96dae9"
|
|
"a079b1f6eb959033c1af5c125fd94168"
|
|
"040c" // OCTET STRING length 0x0c (initializationVector)
|
|
"6d9721d08589581ab49204a3"
|
|
"302e" // SEQUENCE length 0x2e (KeyDescription) {
|
|
"020103" // INTEGER length 1 value 0x03 (keyFormat = RAW)
|
|
"3029" // SEQUENCE length 0x29 (AuthorizationList) {
|
|
"a108" // [1] context-specific constructed tag=1 length 0x08 { (purpose)
|
|
"3106" // SET length 0x06
|
|
"020100" // INTEGER length 1 value 0x00 (Encrypt)
|
|
"020101" // INTEGER length 1 value 0x01 (Decrypt)
|
|
// } end SET
|
|
// } end [1]
|
|
"a203" // [2] context-specific constructed tag=2 length 0x02 { (algorithm)
|
|
"020120" // INTEGER length 1 value 0x20 (AES)
|
|
// } end [2]
|
|
"a304" // [3] context-specific constructed tag=3 length 0x04 { (keySize)
|
|
"02020100" // INTEGER length 2 value 0x100
|
|
// } end [3]
|
|
"a405" // [4] context-specific constructed tag=4 length 0x05 { (blockMode
|
|
"3103" // SET length 0x03 {
|
|
"020101" // INTEGER length 1 value 0x01 (ECB)
|
|
// } end SET
|
|
// } end [4]
|
|
"a605" // [6] context-specific constructed tag=6 length 0x05 { (padding)
|
|
"3103" // SET length 0x03 {
|
|
"020140" // INTEGER length 1 value 0x40 (PKCS7)
|
|
// } end SET
|
|
// } end [5]
|
|
"bf837702" // [503] context-specific constructed tag=503=0x1F7 length 0x02 {
|
|
// (noAuthRequired)
|
|
"0500" // NULL
|
|
// } end [503]
|
|
// } end SEQUENCE (AuthorizationList)
|
|
// } end SEQUENCE (KeyDescription)
|
|
"0420" // OCTET STRING length 0x20 (encryptedKey)
|
|
"a61c6e247e25b3e6e69aa78eb03c2d4a"
|
|
"c20d1f99a9a024a76f35c8e2cab9b68d"
|
|
"0410" // OCTET STRING length 0x10 (tag)
|
|
"2560c70109ae67c030f00b98b512a670"
|
|
// } SEQUENCE (SecureKeyWrapper)
|
|
);
|
|
|
|
auto wrapping_key = hex2str(
|
|
// RFC 5208 s5
|
|
"308204be" // SEQUENCE length 0x4be (PrivateKeyInfo) {
|
|
"020100" // INTEGER length 1 value 0x00 (version)
|
|
"300d" // SEQUENCE length 0x0d (AlgorithmIdentifier) {
|
|
"0609" // OBJECT IDENTIFIER length 0x09 (algorithm)
|
|
"2a864886f70d010101" // 1.2.840.113549.1.1.1 (RSAES-PKCS1-v1_5 encryption scheme)
|
|
"0500" // NULL (parameters)
|
|
// } SEQUENCE (AlgorithmIdentifier)
|
|
"048204a8" // OCTET STRING len 0x4a8 (privateKey), which contains...
|
|
// RFC 8017 A.1.2
|
|
"308204a4" // SEQUENCE len 0x4a4 (RSAPrivateKey) {
|
|
"020100" // INTEGER length 1 value 0x00 (version)
|
|
"02820101" // INTEGER length 0x0101 (modulus) value...
|
|
"00aec367931d8900ce56b0067f7d70e1" // 0x10
|
|
"fc653f3f34d194c1fed50018fb43db93" // 0x20
|
|
"7b06e673a837313d56b1c725150a3fef" // 0x30
|
|
"86acbddc41bb759c2854eae32d35841e" // 0x40
|
|
"fb5c18d82bc90a1cb5c1d55adf245b02" // 0x50
|
|
"911f0b7cda88c421ff0ebafe7c0d23be" // 0x60
|
|
"312d7bd5921ffaea1347c157406fef71" // 0x70
|
|
"8f682643e4e5d33c6703d61c0cf7ac0b" // 0x80
|
|
"f4645c11f5c1374c3886427411c44979" // 0x90
|
|
"6792e0bef75dec858a2123c36753e02a" // 0xa0
|
|
"95a96d7c454b504de385a642e0dfc3e6" // 0xb0
|
|
"0ac3a7ee4991d0d48b0172a95f9536f0" // 0xc0
|
|
"2ba13cecccb92b727db5c27e5b2f5cec" // 0xd0
|
|
"09600b286af5cf14c42024c61ddfe71c" // 0xe0
|
|
"2a8d7458f185234cb00e01d282f10f8f" // 0xf0
|
|
"c6721d2aed3f4833cca2bd8fa62821dd" // 0x100
|
|
"55" // 0x101
|
|
"0203010001" // INTEGER length 3 value 0x10001 (publicExponent)
|
|
"02820100" // INTEGER length 0x100 (privateExponent) value...
|
|
"431447b6251908112b1ee76f99f3711a" // 0x10
|
|
"52b6630960046c2de70de188d833f8b8" // 0x20
|
|
"b91e4d785caeeeaf4f0f74414e2cda40" // 0x30
|
|
"641f7fe24f14c67a88959bdb27766df9" // 0x40
|
|
"e710b630a03adc683b5d2c43080e52be" // 0x50
|
|
"e71e9eaeb6de297a5fea1072070d181c" // 0x60
|
|
"822bccff087d63c940ba8a45f670feb2" // 0x70
|
|
"9fb4484d1c95e6d2579ba02aae0a0090" // 0x80
|
|
"0c3ebf490e3d2cd7ee8d0e20c536e4dc" // 0x90
|
|
"5a5097272888cddd7e91f228b1c4d747" // 0xa0
|
|
"4c55b8fcd618c4a957bbddd5ad7407cc" // 0xb0
|
|
"312d8d98a5caf7e08f4a0d6b45bb41c6" // 0xc0
|
|
"52659d5a5ba05b663737a8696281865b" // 0xd0
|
|
"a20fbdd7f851e6c56e8cbe0ddbbf24dc" // 0xe0
|
|
"03b2d2cb4c3d540fb0af52e034a2d066" // 0xf0
|
|
"98b128e5f101e3b51a34f8d8b4f86181" // 0x100
|
|
"028181" // INTEGER length 0x81 (prime1) value...
|
|
"00de392e18d682c829266cc3454e1d61" // 0x10
|
|
"66242f32d9a1d10577753e904ea7d08b" // 0x20
|
|
"ff841be5bac82a164c5970007047b8c5" // 0x30
|
|
"17db8f8f84e37bd5988561bdf503d4dc" // 0x40
|
|
"2bdb38f885434ae42c355f725c9a60f9" // 0x50
|
|
"1f0788e1f1a97223b524b5357fdf72e2" // 0x60
|
|
"f696bab7d78e32bf92ba8e1864eab122" // 0x70
|
|
"9e91346130748a6e3c124f9149d71c74" // 0x80
|
|
"35"
|
|
"028181" // INTEGER length 0x81 (prime2) value...
|
|
"00c95387c0f9d35f137b57d0d65c397c" // 0x10
|
|
"5e21cc251e47008ed62a542409c8b6b6" // 0x20
|
|
"ac7f8967b3863ca645fcce49582a9aa1" // 0x30
|
|
"7349db6c4a95affdae0dae612e1afac9" // 0x40
|
|
"9ed39a2d934c880440aed8832f984316" // 0x50
|
|
"3a47f27f392199dc1202f9a0f9bd0830" // 0x60
|
|
"8007cb1e4e7f58309366a7de25f7c3c9" // 0x70
|
|
"b880677c068e1be936e81288815252a8" // 0x80
|
|
"a1"
|
|
"028180" // INTEGER length 0x80 (exponent1) value...
|
|
"57ff8ca1895080b2cae486ef0adfd791" // 0x10
|
|
"fb0235c0b8b36cd6c136e52e4085f4ea" // 0x20
|
|
"5a063212a4f105a3764743e53281988a" // 0x30
|
|
"ba073f6e0027298e1c4378556e0efca0" // 0x40
|
|
"e14ece1af76ad0b030f27af6f0ab35fb" // 0x50
|
|
"73a060d8b1a0e142fa2647e93b32e36d" // 0x60
|
|
"8282ae0a4de50ab7afe85500a16f43a6" // 0x70
|
|
"4719d6e2b9439823719cd08bcd031781" // 0x80
|
|
"028181" // INTEGER length 0x81 (exponent2) value...
|
|
"00ba73b0bb28e3f81e9bd1c568713b10" // 0x10
|
|
"1241acc607976c4ddccc90e65b6556ca" // 0x20
|
|
"31516058f92b6e09f3b160ff0e374ec4" // 0x30
|
|
"0d78ae4d4979fde6ac06a1a400c61dd3" // 0x40
|
|
"1254186af30b22c10582a8a43e34fe94" // 0x50
|
|
"9c5f3b9755bae7baa7b7b7a6bd03b38c" // 0x60
|
|
"ef55c86885fc6c1978b9cee7ef33da50" // 0x70
|
|
"7c9df6b9277cff1e6aaa5d57aca52846" // 0x80
|
|
"61"
|
|
"028181" // INTEGER length 0x81 (coefficient) value...
|
|
"00c931617c77829dfb1270502be9195c" // 0x10
|
|
"8f2830885f57dba869536811e6864236" // 0x20
|
|
"d0c4736a0008a145af36b8357a7c3d13" // 0x30
|
|
"9966d04c4e00934ea1aede3bb6b8ec84" // 0x40
|
|
"1dc95e3f579751e2bfdfe27ae778983f" // 0x50
|
|
"959356210723287b0affcc9f727044d4" // 0x60
|
|
"8c373f1babde0724fa17a4fd4da0902c" // 0x70
|
|
"7c9b9bf27ba61be6ad02dfddda8f4e68" // 0x80
|
|
"22"
|
|
// } SEQUENCE
|
|
// } SEQUENCE ()
|
|
);
|
|
|
|
string zero_masking_key =
|
|
hex2str("0000000000000000000000000000000000000000000000000000000000000000");
|
|
string masking_key = hex2str("D796B02C370F1FA4CC0124F14EC8CBEBE987E825246265050F399A51FD477DFC");
|
|
|
|
class ImportWrappedKeyTest : public KeyMintAidlTestBase {};
|
|
|
|
TEST_P(ImportWrappedKeyTest, Success) {
|
|
auto wrapping_key_desc = AuthorizationSetBuilder()
|
|
.RsaEncryptionKey(2048, 65537)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
|
|
.SetDefaultValidity();
|
|
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
|
|
AuthorizationSetBuilder()
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_OAEP)));
|
|
|
|
string message = "Hello World!";
|
|
auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
|
|
string ciphertext = EncryptMessage(message, params);
|
|
string plaintext = DecryptMessage(ciphertext, params);
|
|
EXPECT_EQ(message, plaintext);
|
|
}
|
|
|
|
/*
|
|
* ImportWrappedKeyTest.SuccessSidsIgnored
|
|
*
|
|
* Verifies that password_sid and biometric_sid are ignored on import if the authorizations don't
|
|
* include Tag:USER_SECURE_ID.
|
|
*/
|
|
TEST_P(ImportWrappedKeyTest, SuccessSidsIgnored) {
|
|
auto wrapping_key_desc = AuthorizationSetBuilder()
|
|
.RsaEncryptionKey(2048, 65537)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
|
|
.SetDefaultValidity();
|
|
|
|
int64_t password_sid = 42;
|
|
int64_t biometric_sid = 24;
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
|
|
AuthorizationSetBuilder()
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_OAEP),
|
|
password_sid, biometric_sid));
|
|
|
|
string message = "Hello World!";
|
|
auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
|
|
string ciphertext = EncryptMessage(message, params);
|
|
string plaintext = DecryptMessage(ciphertext, params);
|
|
EXPECT_EQ(message, plaintext);
|
|
}
|
|
|
|
TEST_P(ImportWrappedKeyTest, SuccessMasked) {
|
|
auto wrapping_key_desc = AuthorizationSetBuilder()
|
|
.RsaEncryptionKey(2048, 65537)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
|
|
.SetDefaultValidity();
|
|
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
ImportWrappedKey(wrapped_key_masked, wrapping_key, wrapping_key_desc, masking_key,
|
|
AuthorizationSetBuilder()
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_OAEP)));
|
|
}
|
|
|
|
TEST_P(ImportWrappedKeyTest, WrongMask) {
|
|
auto wrapping_key_desc = AuthorizationSetBuilder()
|
|
.RsaEncryptionKey(2048, 65537)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
|
|
.SetDefaultValidity();
|
|
|
|
ASSERT_EQ(
|
|
ErrorCode::VERIFICATION_FAILED,
|
|
ImportWrappedKey(wrapped_key_masked, wrapping_key, wrapping_key_desc, zero_masking_key,
|
|
AuthorizationSetBuilder()
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_OAEP)));
|
|
}
|
|
|
|
TEST_P(ImportWrappedKeyTest, WrongPurpose) {
|
|
auto wrapping_key_desc = AuthorizationSetBuilder()
|
|
.RsaEncryptionKey(2048, 65537)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.SetDefaultValidity();
|
|
|
|
ASSERT_EQ(
|
|
ErrorCode::INCOMPATIBLE_PURPOSE,
|
|
ImportWrappedKey(wrapped_key_masked, wrapping_key, wrapping_key_desc, zero_masking_key,
|
|
AuthorizationSetBuilder()
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_OAEP)));
|
|
}
|
|
|
|
TEST_P(ImportWrappedKeyTest, WrongPaddingMode) {
|
|
auto wrapping_key_desc = AuthorizationSetBuilder()
|
|
.RsaEncryptionKey(2048, 65537)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_PSS)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
|
|
.SetDefaultValidity();
|
|
|
|
ASSERT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE,
|
|
ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
|
|
AuthorizationSetBuilder()
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_OAEP)));
|
|
}
|
|
|
|
TEST_P(ImportWrappedKeyTest, WrongDigest) {
|
|
auto wrapping_key_desc = AuthorizationSetBuilder()
|
|
.RsaEncryptionKey(2048, 65537)
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
|
|
.SetDefaultValidity();
|
|
|
|
ASSERT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
|
|
ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
|
|
AuthorizationSetBuilder()
|
|
.Digest(Digest::SHA_2_512)
|
|
.Padding(PaddingMode::RSA_OAEP)));
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(ImportWrappedKeyTest);
|
|
|
|
typedef KeyMintAidlTestBase EncryptionOperationsTest;
|
|
|
|
/*
|
|
* EncryptionOperationsTest.RsaNoPaddingSuccess
|
|
*
|
|
* Verifies that raw RSA decryption works.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, RsaNoPaddingSuccess) {
|
|
for (uint64_t exponent : ValidExponents()) {
|
|
SCOPED_TRACE(testing::Message() << "RSA exponent=" << exponent);
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaEncryptionKey(2048, exponent)
|
|
.Padding(PaddingMode::NONE)
|
|
.SetDefaultValidity()));
|
|
|
|
string message = string(2048 / 8, 'a');
|
|
auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
|
|
string ciphertext1 = LocalRsaEncryptMessage(message, params);
|
|
EXPECT_EQ(2048U / 8, ciphertext1.size());
|
|
|
|
string ciphertext2 = LocalRsaEncryptMessage(message, params);
|
|
EXPECT_EQ(2048U / 8, ciphertext2.size());
|
|
|
|
// Unpadded RSA is deterministic
|
|
EXPECT_EQ(ciphertext1, ciphertext2);
|
|
|
|
CheckedDeleteKey();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.RsaNoPaddingShortMessage
|
|
*
|
|
* Verifies that raw RSA decryption of short messages works.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, RsaNoPaddingShortMessage) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaEncryptionKey(2048, 65537)
|
|
.Padding(PaddingMode::NONE)
|
|
.SetDefaultValidity()));
|
|
|
|
string message = "1";
|
|
auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
|
|
|
|
string ciphertext = LocalRsaEncryptMessage(message, params);
|
|
EXPECT_EQ(2048U / 8, ciphertext.size());
|
|
|
|
string expected_plaintext = string(2048U / 8 - 1, 0) + message;
|
|
string plaintext = DecryptMessage(ciphertext, params);
|
|
|
|
EXPECT_EQ(expected_plaintext, plaintext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.RsaOaepSuccess
|
|
*
|
|
* Verifies that RSA-OAEP decryption operations work, with all digests.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, RsaOaepSuccess) {
|
|
auto digests = ValidDigests(false /* withNone */, true /* withMD5 */);
|
|
|
|
size_t key_size = 2048; // Need largish key for SHA-512 test.
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaEncryptionKey(key_size, 65537)
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.Digest(digests)
|
|
.Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA1)
|
|
.SetDefaultValidity()));
|
|
|
|
string message = "Hello";
|
|
|
|
for (auto digest : digests) {
|
|
SCOPED_TRACE(testing::Message() << "digest-" << digest);
|
|
|
|
auto params = AuthorizationSetBuilder()
|
|
.Digest(digest)
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA1);
|
|
string ciphertext1 = LocalRsaEncryptMessage(message, params);
|
|
if (HasNonfatalFailure()) std::cout << "-->" << digest << std::endl;
|
|
EXPECT_EQ(key_size / 8, ciphertext1.size());
|
|
|
|
string ciphertext2 = LocalRsaEncryptMessage(message, params);
|
|
EXPECT_EQ(key_size / 8, ciphertext2.size());
|
|
|
|
// OAEP randomizes padding so every result should be different (with astronomically high
|
|
// probability).
|
|
EXPECT_NE(ciphertext1, ciphertext2);
|
|
|
|
string plaintext1 = DecryptMessage(ciphertext1, params);
|
|
EXPECT_EQ(message, plaintext1) << "RSA-OAEP failed with digest " << digest;
|
|
string plaintext2 = DecryptMessage(ciphertext2, params);
|
|
EXPECT_EQ(message, plaintext2) << "RSA-OAEP failed with digest " << digest;
|
|
|
|
// Decrypting corrupted ciphertext should fail.
|
|
size_t offset_to_corrupt = random() % ciphertext1.size();
|
|
char corrupt_byte;
|
|
do {
|
|
corrupt_byte = static_cast<char>(random() % 256);
|
|
} while (corrupt_byte == ciphertext1[offset_to_corrupt]);
|
|
ciphertext1[offset_to_corrupt] = corrupt_byte;
|
|
|
|
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
|
|
string result;
|
|
EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext1, &result));
|
|
EXPECT_EQ(0U, result.size());
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.RsaOaepInvalidDigest
|
|
*
|
|
* Verifies that RSA-OAEP decryption operations fail in the correct way when asked to operate
|
|
* without a digest.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, RsaOaepInvalidDigest) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaEncryptionKey(2048, 65537)
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity()));
|
|
|
|
auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_OAEP).Digest(Digest::NONE);
|
|
EXPECT_EQ(ErrorCode::INCOMPATIBLE_DIGEST, Begin(KeyPurpose::DECRYPT, params));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.RsaOaepInvalidPadding
|
|
*
|
|
* Verifies that RSA-OAEP decryption operations fail in the correct way when asked to operate
|
|
* with a padding value that is only suitable for signing/verifying.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, RsaOaepInvalidPadding) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaEncryptionKey(2048, 65537)
|
|
.Padding(PaddingMode::RSA_PSS)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity()));
|
|
|
|
auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_PSS).Digest(Digest::NONE);
|
|
EXPECT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE, Begin(KeyPurpose::DECRYPT, params));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.RsaOaepDecryptWithWrongDigest
|
|
*
|
|
* Verifies that RSA-OAEP decryption operations fail in the correct way when asked to decrypt
|
|
* with a different digest than was used to encrypt.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, RsaOaepDecryptWithWrongDigest) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Test not applicable to StrongBox device";
|
|
}
|
|
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaEncryptionKey(1024, 65537)
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.Digest(Digest::SHA_2_224, Digest::SHA_2_256)
|
|
.SetDefaultValidity()));
|
|
string message = "Hello World!";
|
|
string ciphertext = LocalRsaEncryptMessage(
|
|
message,
|
|
AuthorizationSetBuilder().Digest(Digest::SHA_2_224).Padding(PaddingMode::RSA_OAEP));
|
|
|
|
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, AuthorizationSetBuilder()
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_OAEP)));
|
|
string result;
|
|
EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext, &result));
|
|
EXPECT_EQ(0U, result.size());
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.RsaOaepWithMGFDigestSuccess
|
|
*
|
|
* Verifies that RSA-OAEP decryption operations work, with all SHA 256 digests and all type of MGF1
|
|
* digests.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, RsaOaepWithMGFDigestSuccess) {
|
|
auto digests = ValidDigests(false /* withNone */, true /* withMD5 */);
|
|
|
|
size_t key_size = 2048; // Need largish key for SHA-512 test.
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.OaepMGFDigest(digests)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaEncryptionKey(key_size, 65537)
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.Digest(Digest::SHA_2_256)
|
|
.SetDefaultValidity()));
|
|
|
|
string message = "Hello";
|
|
|
|
for (auto digest : digests) {
|
|
SCOPED_TRACE(testing::Message() << "digest-" << digest);
|
|
auto params = AuthorizationSetBuilder()
|
|
.Authorization(TAG_RSA_OAEP_MGF_DIGEST, digest)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::RSA_OAEP);
|
|
string ciphertext1 = LocalRsaEncryptMessage(message, params);
|
|
if (HasNonfatalFailure()) std::cout << "-->" << digest << std::endl;
|
|
EXPECT_EQ(key_size / 8, ciphertext1.size());
|
|
|
|
string ciphertext2 = LocalRsaEncryptMessage(message, params);
|
|
EXPECT_EQ(key_size / 8, ciphertext2.size());
|
|
|
|
// OAEP randomizes padding so every result should be different (with astronomically high
|
|
// probability).
|
|
EXPECT_NE(ciphertext1, ciphertext2);
|
|
|
|
string plaintext1 = DecryptMessage(ciphertext1, params);
|
|
EXPECT_EQ(message, plaintext1) << "RSA-OAEP failed with digest " << digest;
|
|
string plaintext2 = DecryptMessage(ciphertext2, params);
|
|
EXPECT_EQ(message, plaintext2) << "RSA-OAEP failed with digest " << digest;
|
|
|
|
// Decrypting corrupted ciphertext should fail.
|
|
size_t offset_to_corrupt = random() % ciphertext1.size();
|
|
char corrupt_byte;
|
|
do {
|
|
corrupt_byte = static_cast<char>(random() % 256);
|
|
} while (corrupt_byte == ciphertext1[offset_to_corrupt]);
|
|
ciphertext1[offset_to_corrupt] = corrupt_byte;
|
|
|
|
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
|
|
string result;
|
|
EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext1, &result));
|
|
EXPECT_EQ(0U, result.size());
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.RsaOaepMGFDigestDefaultSuccess
|
|
*
|
|
* Verifies that RSA-OAEP decryption operations work when no MGF digest is
|
|
* specified, defaulting to SHA-1.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, RsaOaepMGFDigestDefaultSuccess) {
|
|
size_t key_size = 2048;
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaEncryptionKey(key_size, 65537)
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.Digest(Digest::SHA_2_256)
|
|
.SetDefaultValidity()));
|
|
|
|
// Do local RSA encryption using the default MGF digest of SHA-1.
|
|
string message = "Hello";
|
|
auto params =
|
|
AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_OAEP);
|
|
string ciphertext = LocalRsaEncryptMessage(message, params);
|
|
EXPECT_EQ(key_size / 8, ciphertext.size());
|
|
|
|
// Do KeyMint RSA decryption also using the default MGF digest of SHA-1.
|
|
string plaintext = DecryptMessage(ciphertext, params);
|
|
EXPECT_EQ(message, plaintext) << "RSA-OAEP failed with default digest";
|
|
|
|
// Decrypting corrupted ciphertext should fail.
|
|
size_t offset_to_corrupt = random() % ciphertext.size();
|
|
char corrupt_byte;
|
|
do {
|
|
corrupt_byte = static_cast<char>(random() % 256);
|
|
} while (corrupt_byte == ciphertext[offset_to_corrupt]);
|
|
ciphertext[offset_to_corrupt] = corrupt_byte;
|
|
|
|
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
|
|
string result;
|
|
EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext, &result));
|
|
EXPECT_EQ(0U, result.size());
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.RsaOaepMGFDigestDefaultFail
|
|
*
|
|
* Verifies that RSA-OAEP decryption operations fail when no MGF digest is
|
|
* specified on begin (thus defaulting to SHA-1), but the key characteristics
|
|
* has an explicit set of values for MGF_DIGEST that do not contain SHA-1.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, RsaOaepMGFDigestDefaultFail) {
|
|
size_t key_size = 2048;
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA_2_256)
|
|
.RsaEncryptionKey(key_size, 65537)
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.Digest(Digest::SHA_2_256)
|
|
.SetDefaultValidity()));
|
|
|
|
// Do local RSA encryption using the default MGF digest of SHA-1.
|
|
string message = "Hello";
|
|
auto params =
|
|
AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_OAEP);
|
|
string ciphertext = LocalRsaEncryptMessage(message, params);
|
|
EXPECT_EQ(key_size / 8, ciphertext.size());
|
|
|
|
// begin() params do not include MGF_DIGEST, so a default of SHA1 is assumed.
|
|
// Key characteristics *do* include values for MGF_DIGEST, so the SHA1 value
|
|
// is checked against those values, and found absent.
|
|
auto result = Begin(KeyPurpose::DECRYPT, params);
|
|
EXPECT_TRUE(result == ErrorCode::UNSUPPORTED_MGF_DIGEST ||
|
|
result == ErrorCode::INCOMPATIBLE_MGF_DIGEST);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.RsaOaepWithMGFIncompatibleDigest
|
|
*
|
|
* Verifies that RSA-OAEP decryption operations fail in the correct way when asked to operate
|
|
* with incompatible MGF digest.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, RsaOaepWithMGFIncompatibleDigest) {
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA_2_256)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaEncryptionKey(2048, 65537)
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.Digest(Digest::SHA_2_256)
|
|
.SetDefaultValidity()));
|
|
string message = "Hello World!";
|
|
|
|
auto params = AuthorizationSetBuilder()
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA_2_224);
|
|
EXPECT_EQ(ErrorCode::INCOMPATIBLE_MGF_DIGEST, Begin(KeyPurpose::DECRYPT, params));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.RsaOaepWithMGFUnsupportedDigest
|
|
*
|
|
* Verifies that RSA-OAEP encryption operations fail in the correct way when asked to operate
|
|
* with unsupported MGF digest.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, RsaOaepWithMGFUnsupportedDigest) {
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA_2_256)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaEncryptionKey(2048, 65537)
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.Digest(Digest::SHA_2_256)
|
|
.SetDefaultValidity()));
|
|
string message = "Hello World!";
|
|
|
|
auto params = AuthorizationSetBuilder()
|
|
.Padding(PaddingMode::RSA_OAEP)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::NONE);
|
|
EXPECT_EQ(ErrorCode::UNSUPPORTED_MGF_DIGEST, Begin(KeyPurpose::DECRYPT, params));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.RsaPkcs1Success
|
|
*
|
|
* Verifies that RSA PKCS encryption/decrypts works.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, RsaPkcs1Success) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaEncryptionKey(2048, 65537)
|
|
.Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT)
|
|
.SetDefaultValidity()));
|
|
|
|
string message = "Hello World!";
|
|
auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT);
|
|
string ciphertext1 = LocalRsaEncryptMessage(message, params);
|
|
EXPECT_EQ(2048U / 8, ciphertext1.size());
|
|
|
|
string ciphertext2 = LocalRsaEncryptMessage(message, params);
|
|
EXPECT_EQ(2048U / 8, ciphertext2.size());
|
|
|
|
// PKCS1 v1.5 randomizes padding so every result should be different.
|
|
EXPECT_NE(ciphertext1, ciphertext2);
|
|
|
|
string plaintext = DecryptMessage(ciphertext1, params);
|
|
EXPECT_EQ(message, plaintext);
|
|
|
|
// Decrypting corrupted ciphertext should fail.
|
|
size_t offset_to_corrupt = random() % ciphertext1.size();
|
|
char corrupt_byte;
|
|
do {
|
|
corrupt_byte = static_cast<char>(random() % 256);
|
|
} while (corrupt_byte == ciphertext1[offset_to_corrupt]);
|
|
ciphertext1[offset_to_corrupt] = corrupt_byte;
|
|
|
|
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
|
|
string result;
|
|
EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext1, &result));
|
|
EXPECT_EQ(0U, result.size());
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.EcdsaEncrypt
|
|
*
|
|
* Verifies that attempting to use ECDSA keys to encrypt fails in the correct way.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, EcdsaEncrypt) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::NONE)
|
|
.SetDefaultValidity()));
|
|
auto params = AuthorizationSetBuilder().Digest(Digest::NONE);
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::ENCRYPT, params));
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::DECRYPT, params));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.HmacEncrypt
|
|
*
|
|
* Verifies that attempting to use HMAC keys to encrypt fails in the correct way.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, HmacEncrypt) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.HmacKey(128)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
|
|
auto params = AuthorizationSetBuilder()
|
|
.Digest(Digest::SHA_2_256)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MAC_LENGTH, 128);
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::ENCRYPT, params));
|
|
ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::DECRYPT, params));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesEcbRoundTripSuccess
|
|
*
|
|
* Verifies that AES ECB mode works.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesEcbRoundTripSuccess) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
|
|
.Padding(PaddingMode::NONE)));
|
|
|
|
ASSERT_GT(key_blob_.size(), 0U);
|
|
auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
|
|
|
|
// Two-block message.
|
|
string message = "12345678901234567890123456789012";
|
|
string ciphertext1 = EncryptMessage(message, params);
|
|
EXPECT_EQ(message.size(), ciphertext1.size());
|
|
|
|
string ciphertext2 = EncryptMessage(string(message), params);
|
|
EXPECT_EQ(message.size(), ciphertext2.size());
|
|
|
|
// ECB is deterministic.
|
|
EXPECT_EQ(ciphertext1, ciphertext2);
|
|
|
|
string plaintext = DecryptMessage(ciphertext1, params);
|
|
EXPECT_EQ(message, plaintext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesEcbUnknownTag
|
|
*
|
|
* Verifies that AES ECB operations ignore unknown tags.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesEcbUnknownTag) {
|
|
int32_t unknown_tag_value = ((7 << 28) /* TagType:BOOL */ | 150);
|
|
Tag unknown_tag = static_cast<Tag>(unknown_tag_value);
|
|
KeyParameter unknown_param;
|
|
unknown_param.tag = unknown_tag;
|
|
|
|
vector<KeyCharacteristics> key_characteristics;
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(unknown_param),
|
|
&key_blob_, &key_characteristics));
|
|
ASSERT_GT(key_blob_.size(), 0U);
|
|
|
|
// Unknown tags should not be returned in key characteristics.
|
|
AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
|
|
AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
|
|
EXPECT_EQ(hw_enforced.find(unknown_tag), -1);
|
|
EXPECT_EQ(sw_enforced.find(unknown_tag), -1);
|
|
|
|
// Encrypt without mentioning the unknown parameter.
|
|
auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
|
|
string message = "12345678901234567890123456789012";
|
|
string ciphertext = EncryptMessage(message, params);
|
|
EXPECT_EQ(message.size(), ciphertext.size());
|
|
|
|
// Decrypt including the unknown parameter.
|
|
auto decrypt_params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::ECB)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(unknown_param);
|
|
string plaintext = DecryptMessage(ciphertext, decrypt_params);
|
|
EXPECT_EQ(message, plaintext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesWrongMode
|
|
*
|
|
* Verifies that AES encryption fails in the correct way when an unauthorized mode is specified.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesWrongMode) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
|
|
.Padding(PaddingMode::NONE)));
|
|
ASSERT_GT(key_blob_.size(), 0U);
|
|
|
|
EXPECT_EQ(
|
|
ErrorCode::INCOMPATIBLE_BLOCK_MODE,
|
|
Begin(KeyPurpose::ENCRYPT,
|
|
AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE)));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesWrongPadding
|
|
*
|
|
* Verifies that AES encryption fails in the correct way when an unauthorized padding is specified.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesWrongPadding) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
|
|
.Padding(PaddingMode::NONE)));
|
|
ASSERT_GT(key_blob_.size(), 0U);
|
|
|
|
EXPECT_EQ(
|
|
ErrorCode::INCOMPATIBLE_PADDING_MODE,
|
|
Begin(KeyPurpose::ENCRYPT,
|
|
AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::PKCS7)));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesInvalidParams
|
|
*
|
|
* Verifies that AES encryption fails in the correct way when an duplicate parameters are specified.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesInvalidParams) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
|
|
.Padding(PaddingMode::NONE)
|
|
.Padding(PaddingMode::PKCS7)));
|
|
ASSERT_GT(key_blob_.size(), 0U);
|
|
|
|
auto result = Begin(KeyPurpose::ENCRYPT, AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::CBC)
|
|
.BlockMode(BlockMode::ECB)
|
|
.Padding(PaddingMode::NONE));
|
|
EXPECT_TRUE(result == ErrorCode::INCOMPATIBLE_BLOCK_MODE ||
|
|
result == ErrorCode::UNSUPPORTED_BLOCK_MODE);
|
|
|
|
result = Begin(KeyPurpose::ENCRYPT, AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::ECB)
|
|
.Padding(PaddingMode::NONE)
|
|
.Padding(PaddingMode::PKCS7));
|
|
EXPECT_TRUE(result == ErrorCode::INCOMPATIBLE_PADDING_MODE ||
|
|
result == ErrorCode::UNSUPPORTED_PADDING_MODE);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesWrongPurpose
|
|
*
|
|
* Verifies that AES encryption fails in the correct way when an unauthorized purpose is
|
|
* specified.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesWrongPurpose) {
|
|
auto err = GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesKey(128)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::ENCRYPT)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)
|
|
.Padding(PaddingMode::NONE));
|
|
ASSERT_EQ(ErrorCode::OK, err) << "Got " << err;
|
|
ASSERT_GT(key_blob_.size(), 0U);
|
|
|
|
err = Begin(KeyPurpose::DECRYPT, AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MAC_LENGTH, 128));
|
|
EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE, err) << "Got " << err;
|
|
|
|
CheckedDeleteKey();
|
|
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesKey(128)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::DECRYPT)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)
|
|
.Padding(PaddingMode::NONE)));
|
|
|
|
err = Begin(KeyPurpose::ENCRYPT, AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MAC_LENGTH, 128));
|
|
EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE, err) << "Got " << err;
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesEcbCbcNoPaddingWrongInputSize
|
|
*
|
|
* Verifies that AES encryption fails in the correct way when provided an input that is not a
|
|
* multiple of the block size and no padding is specified.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesEcbCbcNoPaddingWrongInputSize) {
|
|
for (BlockMode blockMode : {BlockMode::ECB, BlockMode::CBC}) {
|
|
SCOPED_TRACE(testing::Message() << "AES-" << blockMode);
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, blockMode)
|
|
.Padding(PaddingMode::NONE)));
|
|
// Message is slightly shorter than two blocks.
|
|
string message(16 * 2 - 1, 'a');
|
|
|
|
auto params = AuthorizationSetBuilder().BlockMode(blockMode).Padding(PaddingMode::NONE);
|
|
AuthorizationSet out_params;
|
|
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &out_params));
|
|
string ciphertext;
|
|
EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &ciphertext));
|
|
EXPECT_EQ(0U, ciphertext.size());
|
|
|
|
CheckedDeleteKey();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesEcbPkcs7Padding
|
|
*
|
|
* Verifies that AES PKCS7 padding works for any message length.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesEcbPkcs7Padding) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
|
|
.Padding(PaddingMode::PKCS7)));
|
|
|
|
auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
|
|
|
|
// Try various message lengths; all should work.
|
|
for (size_t i = 0; i <= 48; i++) {
|
|
SCOPED_TRACE(testing::Message() << "i = " << i);
|
|
// Edge case: '\t' (0x09) is also a valid PKCS7 padding character.
|
|
string message(i, '\t');
|
|
string ciphertext = EncryptMessage(message, params);
|
|
EXPECT_EQ(i + 16 - (i % 16), ciphertext.size());
|
|
string plaintext = DecryptMessage(ciphertext, params);
|
|
EXPECT_EQ(message, plaintext);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesEcbWrongPadding
|
|
*
|
|
* Verifies that AES enryption fails in the correct way when an unauthorized padding mode is
|
|
* specified.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesEcbWrongPadding) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
|
|
.Padding(PaddingMode::NONE)));
|
|
|
|
auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
|
|
|
|
// Try various message lengths; all should fail
|
|
for (size_t i = 0; i <= 48; i++) {
|
|
string message(i, 'a');
|
|
EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, params));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesEcbPkcs7PaddingCorrupted
|
|
*
|
|
* Verifies that AES decryption fails in the correct way when the padding is corrupted.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesEcbPkcs7PaddingCorrupted) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
|
|
.Padding(PaddingMode::PKCS7)));
|
|
|
|
auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
|
|
|
|
string message = "a";
|
|
string ciphertext = EncryptMessage(message, params);
|
|
EXPECT_EQ(16U, ciphertext.size());
|
|
EXPECT_NE(ciphertext, message);
|
|
|
|
for (size_t i = 0; i < kMaxPaddingCorruptionRetries; ++i) {
|
|
++ciphertext[ciphertext.size() / 2];
|
|
|
|
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
|
|
string plaintext;
|
|
ErrorCode error = Finish(ciphertext, &plaintext);
|
|
if (error == ErrorCode::INVALID_ARGUMENT) {
|
|
// This is the expected error, we can exit the test now.
|
|
return;
|
|
} else {
|
|
// Very small chance we got valid decryption, so try again.
|
|
ASSERT_EQ(error, ErrorCode::OK)
|
|
<< "Expected INVALID_ARGUMENT or (rarely) OK, got " << error;
|
|
}
|
|
}
|
|
FAIL() << "Corrupt ciphertext should have failed to decrypt by now.";
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesEcbPkcs7CiphertextTooShort
|
|
*
|
|
* Verifies that AES decryption fails in the correct way when the padding is corrupted.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesEcbPkcs7CiphertextTooShort) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
|
|
.Padding(PaddingMode::PKCS7)));
|
|
|
|
auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
|
|
|
|
string message = "a";
|
|
string ciphertext = EncryptMessage(message, params);
|
|
EXPECT_EQ(16U, ciphertext.size());
|
|
EXPECT_NE(ciphertext, message);
|
|
|
|
// Shorten the ciphertext.
|
|
ciphertext.resize(ciphertext.size() - 1);
|
|
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
|
|
string plaintext;
|
|
EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(ciphertext, &plaintext));
|
|
}
|
|
|
|
vector<uint8_t> CopyIv(const AuthorizationSet& set) {
|
|
auto iv = set.GetTagValue(TAG_NONCE);
|
|
EXPECT_TRUE(iv);
|
|
return iv->get();
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesCtrRoundTripSuccess
|
|
*
|
|
* Verifies that AES CTR mode works.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesCtrRoundTripSuccess) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::CTR)
|
|
.Padding(PaddingMode::NONE)));
|
|
|
|
auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CTR).Padding(PaddingMode::NONE);
|
|
|
|
string message = "123";
|
|
AuthorizationSet out_params;
|
|
string ciphertext1 = EncryptMessage(message, params, &out_params);
|
|
vector<uint8_t> iv1 = CopyIv(out_params);
|
|
EXPECT_EQ(16U, iv1.size());
|
|
|
|
EXPECT_EQ(message.size(), ciphertext1.size());
|
|
|
|
out_params.Clear();
|
|
string ciphertext2 = EncryptMessage(message, params, &out_params);
|
|
vector<uint8_t> iv2 = CopyIv(out_params);
|
|
EXPECT_EQ(16U, iv2.size());
|
|
|
|
// IVs should be random, so ciphertexts should differ.
|
|
EXPECT_NE(ciphertext1, ciphertext2);
|
|
|
|
auto params_iv1 =
|
|
AuthorizationSetBuilder().Authorizations(params).Authorization(TAG_NONCE, iv1);
|
|
auto params_iv2 =
|
|
AuthorizationSetBuilder().Authorizations(params).Authorization(TAG_NONCE, iv2);
|
|
|
|
string plaintext = DecryptMessage(ciphertext1, params_iv1);
|
|
EXPECT_EQ(message, plaintext);
|
|
plaintext = DecryptMessage(ciphertext2, params_iv2);
|
|
EXPECT_EQ(message, plaintext);
|
|
|
|
// Using the wrong IV will result in a "valid" decryption, but the data will be garbage.
|
|
plaintext = DecryptMessage(ciphertext1, params_iv2);
|
|
EXPECT_NE(message, plaintext);
|
|
plaintext = DecryptMessage(ciphertext2, params_iv1);
|
|
EXPECT_NE(message, plaintext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesEcbIncremental
|
|
*
|
|
* Verifies that AES works for ECB block mode, when provided data in various size increments.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesEcbIncremental) {
|
|
CheckAesIncrementalEncryptOperation(BlockMode::ECB, 240);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesCbcIncremental
|
|
*
|
|
* Verifies that AES works for CBC block mode, when provided data in various size increments.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesCbcIncremental) {
|
|
CheckAesIncrementalEncryptOperation(BlockMode::CBC, 240);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesCtrIncremental
|
|
*
|
|
* Verifies that AES works for CTR block mode, when provided data in various size increments.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesCtrIncremental) {
|
|
CheckAesIncrementalEncryptOperation(BlockMode::CTR, 240);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesGcmIncremental
|
|
*
|
|
* Verifies that AES works for GCM block mode, when provided data in various size increments.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesGcmIncremental) {
|
|
CheckAesIncrementalEncryptOperation(BlockMode::GCM, 240);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes128CBCNoPaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/CBC/NoPadding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes128CBCNoPaddingOneByteAtATime) {
|
|
string kat_key = hex2str("7E3D723C09A9852B24F584F9D916F6A8");
|
|
string kat_iv = hex2str("944AE274D983892EADE422274858A96A");
|
|
string kat_plaintext =
|
|
hex2str("044E15899A080AADEB6778F64323B64D2CBCBADB338DF93B9AC459D4F41029"
|
|
"809FFF37081C22EF278F896AB213A2A631");
|
|
string kat_ciphertext =
|
|
hex2str("B419293FCBD686F2913D1CF947E510D42FAFEDE5593C98AFD6AEE272596A"
|
|
"56FE42C22F2A5E3B6A02BA9D8D0DE1E9A810");
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::CBC, PaddingMode::NONE, kat_iv, kat_plaintext,
|
|
kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes128CBCPKCS7PaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/CBC/PKCS7Padding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes128CBCPKCS7PaddingOneByteAtATime) {
|
|
string kat_key = hex2str("F16E698472578E919D92806262C5169F");
|
|
string kat_iv = hex2str("EF743540F8421ACA128A3247521F3E7D");
|
|
string kat_plaintext =
|
|
hex2str("5BEBF33569D90BF5E853814E12E7C7AA5758013F755773E29F4A25EC26EEB7"
|
|
"65F7F2DC251F7DC62AEFCA1E8A5A11A1DCD44F0BD8FB593A5AE3");
|
|
string kat_ciphertext =
|
|
hex2str("3197CF6DB9466188B5FED375329324EE7D6092A8C0E41DFAF49E3724271427"
|
|
"896D56A6243C0D59D6639722AF93CD53449BDDABF9C5F153EBDBFED9ED98C8CC37");
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::CBC, PaddingMode::PKCS7, kat_iv,
|
|
kat_plaintext, kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes128CTRNoPaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/CTR/NoPadding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes128CTRNoPaddingOneByteAtATime) {
|
|
string kat_key = hex2str("4713a7b2f93efe809b42ecc45213ef9f");
|
|
string kat_iv = hex2str("ebfa19b0ebf3d57feabd4c4bd04bea01");
|
|
string kat_plaintext =
|
|
hex2str("6d2c07e1fc86f99c6e2a8f6567828b4262a9c23d0f3ed8ab32482283c79796"
|
|
"f0adba1bcd3736084996452a917fae98005aebe61f9e91c3");
|
|
string kat_ciphertext =
|
|
hex2str("345deb1d67b95e600e05cad4c32ec381aadb3e2c1ec7e0fb956dc38e6860cf"
|
|
"0553535566e1b12fa9f87d29266ca26df427233df035df28");
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::CTR, PaddingMode::NONE, kat_iv, kat_plaintext,
|
|
kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes128ECBNoPaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/ECB/NoPadding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes128ECBNoPaddingOneByteAtATime) {
|
|
string kat_key = hex2str("7DA2467F068854B3CB36E5C333A16619");
|
|
string kat_plaintext =
|
|
hex2str("9A07C9575AD9CE209DF9F3953965CEBE8208587C7AE575A1904BF25048946D"
|
|
"7B6168A9A27BCE554BEA94EF26E6C742A0");
|
|
string kat_ciphertext =
|
|
hex2str("8C47E49420FC92AC4CA2C601BC3F8AC31D01B260B7B849F2B8EEDFFFED8F36"
|
|
"C31CBDA0D22F95C9C2A48C347E8C77AC82");
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::ECB, PaddingMode::NONE, "", kat_plaintext,
|
|
kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes128ECBPKCS7PaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/ECB/PKCS7Padding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes128ECBPKCS7PaddingOneByteAtATime) {
|
|
string kat_key = hex2str("C3BE04BCCB3D99B85290F113FE7AF194");
|
|
string kat_plaintext =
|
|
hex2str("348C213FD8DF3F990C20C5ACBF07B34B6264AE245784A5A6176DBFB1C2E7DD"
|
|
"27E52CC92B8EEE40614F05B507B355F6354A2705BD86");
|
|
string kat_ciphertext =
|
|
hex2str("07CD05C41FEDEDDC5DB4B3E35E676153184A119AA4DFDDC290616F1FA60093"
|
|
"1DE6BEA9BDB90D1D733899946F8C8E5C0C4383F99F5D88E27F3EBC0C6E52759ED3");
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::ECB, PaddingMode::PKCS7, "", kat_plaintext,
|
|
kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes128GCMNoPaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/GCM/NoPadding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes128GCMNoPaddingOneByteAtATime) {
|
|
string kat_key = hex2str("ba76354f0aed6e8d91f45c4ff5a062db");
|
|
string kat_iv = hex2str("b79437ae08ff355d7d8a4d0f");
|
|
string kat_plaintext =
|
|
hex2str("6d7596a8fd56ceaec61de7940984b7736fec44f572afc3c8952e4dc6541e2b"
|
|
"c6a702c440a37610989543f63fedb047ca2173bc18581944");
|
|
string kat_ciphertext =
|
|
hex2str("b3f6799e8f9326f2df1e80fcd2cb16d78c9dc7cc14bb677862dc6c639b3a63"
|
|
"38d24b312d3989e5920b5dbfc976765efbfe57bb385940a7a43bdf05bddae3c9d6a2fb"
|
|
"bdfcc0cba0");
|
|
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::GCM, PaddingMode::NONE, kat_iv, kat_plaintext,
|
|
kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes192CBCNoPaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/CBC/NoPadding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes192CBCNoPaddingOneByteAtATime) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Key size 192 is not supported by Strongbox.";
|
|
}
|
|
string kat_key = hex2str("be8cc4e25cce46e5d55725e2391f7d3cf59ed60062f5a43b");
|
|
string kat_iv = hex2str("80a199aab0eee77e7762ddf3b3a32f40");
|
|
string kat_plaintext =
|
|
hex2str("064f9200e0df37d4711af4a69d11addf9e1c345d9d8195f9f1f715019ce96a"
|
|
"167f2497c994bd496eb80bfb2ba2c9d5af");
|
|
string kat_ciphertext =
|
|
hex2str("859b90becaa85e95a71e104efbd7a3b723bcbf4eb39865544a05d9e90b6fe5"
|
|
"72c134552f3a138e726fbe493b3a839598");
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::CBC, PaddingMode::NONE, kat_iv, kat_plaintext,
|
|
kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes192CBCPKCS7PaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/CBC/PKCS7Padding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes192CBCPKCS7PaddingOneByteAtATime) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Key size 192 is not supported by Strongbox.";
|
|
}
|
|
string kat_key = hex2str("68969215ec41e4df7d23de0e806f458f52aff492bd7c5263");
|
|
string kat_iv = hex2str("e61d13dfbf0533289f0e7950209da418");
|
|
string kat_plaintext =
|
|
hex2str("8d4c1cac27511ee2d82409a7f378e7e402b0eb189c1eaa5c506eb72a9074"
|
|
"b170");
|
|
string kat_ciphertext =
|
|
hex2str("e70bcd62c595dc1b2b8c197bb91a7447e1be2cbcf3fdc69e7e991faf0f57cf"
|
|
"4e3884138ff403a41fd99818708ada301c");
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::CBC, PaddingMode::PKCS7, kat_iv,
|
|
kat_plaintext, kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes192CTRNoPaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/CTR/NoPadding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes192CTRNoPaddingOneByteAtATime) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Key size 192 is not supported by Strongbox.";
|
|
}
|
|
string kat_key = hex2str("5e2036e790d38815c90beb67a1c9e5aa0e167ef082927317");
|
|
string kat_iv = hex2str("df0694959b89054156962d68a226965c");
|
|
string kat_plaintext =
|
|
hex2str("6ed2781c99e03e45314d6019932220c2c98130c53f9f67ad10ac519adf50e9"
|
|
"28091e09cdbbd3b42b");
|
|
string kat_ciphertext =
|
|
hex2str("e427b6666502e05b82d0b20ae50e862b1936d71266fc49178ac984e71571f2"
|
|
"2ae0f90f0c19f42b4a");
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::CTR, PaddingMode::NONE, kat_iv, kat_plaintext,
|
|
kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes192ECBNoPaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/ECB/NoPadding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes192ECBNoPaddingOneByteAtATime) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Key size 192 is not supported by Strongbox.";
|
|
}
|
|
string kat_key = hex2str("3cab83fb338ba985fbfe74c5e9d2e900adb570b1d67faf92");
|
|
string kat_plaintext =
|
|
hex2str("2cc64c335a13fb838f3c6aad0a6b47297ca90bb886ddb059200f0b41740c"
|
|
"44ab");
|
|
string kat_ciphertext =
|
|
hex2str("9c5c825328f5ee0aa24947e374d3f9165f484b39dd808c790d7a12964810"
|
|
"2453");
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::ECB, PaddingMode::NONE, "", kat_plaintext,
|
|
kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes192ECBPKCS7PaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/ECB/PKCS7Padding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes192ECBPKCS7PaddingOneByteAtATime) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Key size 192 is not supported by Strongbox.";
|
|
}
|
|
string kat_key = hex2str("d57f4e5446f736c16476ec4db5decc7b1bf3936e4f7e4618");
|
|
string kat_plaintext =
|
|
hex2str("b115777f1ee7a43a07daa6401e59c46b7a98213a8747eabfbe3ca4ec93524d"
|
|
"e2c7");
|
|
string kat_ciphertext =
|
|
hex2str("1e92cd20da08bb5fa174a7a69879d4fc25a155e6af06d75b26c5b450d273c8"
|
|
"bb7e3a889dd4a9589098b44acf1056e7aa");
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::ECB, PaddingMode::PKCS7, "", kat_plaintext,
|
|
kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes192GCMNoPaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/GCM/NoPadding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes192GCMNoPaddingOneByteAtATime) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Key size 192 is not supported by Strongbox.";
|
|
}
|
|
string kat_key = hex2str("21339fc1d011abca65d50ce2365230603fd47d07e8830f6e");
|
|
string kat_iv = hex2str("d5fb1469a8d81dd75286a418");
|
|
string kat_plaintext =
|
|
hex2str("cf776dedf53a828d51a0073db3ef0dd1ee19e2e9e243ce97e95841bb9ad4e3"
|
|
"ff52");
|
|
string kat_ciphertext =
|
|
hex2str("3a0d48278111d3296bc663df8a5dbeb2474ea47fd85b608f8d9375d9dcf7de"
|
|
"1413ad70fb0e1970669095ad77ebb5974ae8");
|
|
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::GCM, PaddingMode::NONE, kat_iv, kat_plaintext,
|
|
kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes256CBCNoPaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/CBC/NoPadding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes256CBCNoPaddingOneByteAtATime) {
|
|
string kat_key = hex2str("dd2f20dc6b98c100bac919120ff95eb5d96003f8229987b283a1e777b0cd5c30");
|
|
string kat_iv = hex2str("23b4d85239fb90db93b07a981e90a170");
|
|
string kat_plaintext =
|
|
hex2str("2fbe5d46dca5cea433e550d8b291740ab9551c2a2d37680d7fb7b993225f58"
|
|
"494cb53caca353e4b637ba05687be20f8d");
|
|
string kat_ciphertext =
|
|
hex2str("5aba24fc316936c8369061ee8fe463e4faed04288e204456626b988c0e376b"
|
|
"6047da1e4fd7c4e1cf2656097f75ae8685");
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::CBC, PaddingMode::NONE, kat_iv, kat_plaintext,
|
|
kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes256CBCPKCS7PaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/CBC/PKCS7Padding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes256CBCPKCS7PaddingOneByteAtATime) {
|
|
string kat_key = hex2str("03ab2510520f5cfebfab0a17a7f8324c9634911f6fc59e586f85346bb38ac88a");
|
|
string kat_iv = hex2str("9af96967195bb0184f129beffa8241ae");
|
|
string kat_plaintext =
|
|
hex2str("2d6944653ac14988a772a2730b7c5bfa99a21732ae26f40cdc5b3a2874c794"
|
|
"2545a82b73c48078b9dae62261c65909");
|
|
string kat_ciphertext =
|
|
hex2str("26b308f7e1668b55705a79c8b3ad10e244655f705f027f390a5c34e4536f51"
|
|
"9403a71987b95124073d69f2a3cb95b0ab");
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::CBC, PaddingMode::PKCS7, kat_iv,
|
|
kat_plaintext, kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes256CTRNoPaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/CTR/NoPadding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes256CTRNoPaddingOneByteAtATime) {
|
|
string kat_key = hex2str("928b380a8fed4b4b4cfeb56e0c66a4cb0f9ff58d61ac68bcfd0e3fbd910a684f");
|
|
string kat_iv = hex2str("0b678a5249e6eeda461dfb4776b6c58e");
|
|
string kat_plaintext =
|
|
hex2str("f358de57543b297e997cba46fb9100553d6abd65377e55b9aac3006400ead1"
|
|
"1f6db3c884");
|
|
string kat_ciphertext =
|
|
hex2str("a07a35fbd1776ad81462e1935f542337add60962bf289249476817b6ddd532"
|
|
"a7be30d4c3");
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::CTR, PaddingMode::NONE, kat_iv, kat_plaintext,
|
|
kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes256ECBNoPaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/ECB/NoPadding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes256ECBNoPaddingOneByteAtATime) {
|
|
string kat_key = hex2str("fa4622d9cf6485075daedd33d2c4fffdf859e2edb7f7df4f04603f7e647fae90");
|
|
string kat_plaintext =
|
|
hex2str("96ccabbe0c68970d8cdee2b30ab43c2d61cc50ee68271e77571e72478d713a"
|
|
"31a476d6806b8116089c6ec50bb543200f");
|
|
string kat_ciphertext =
|
|
hex2str("0e81839e9dfbfe3b503d619e676abe5ac80fac3f245d8f09b9134b1b32a67d"
|
|
"c83e377faf246288931136bef2a07c0be4");
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::ECB, PaddingMode::NONE, "", kat_plaintext,
|
|
kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes256ECBPKCS7PaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/ECB/PKCS7Padding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes256ECBPKCS7PaddingOneByteAtATime) {
|
|
string kat_key = hex2str("bf3f07c68467fead0ca8e2754500ab514258abf02eb7e615a493bcaaa45d5ee1");
|
|
string kat_plaintext =
|
|
hex2str("af0757e49018dad628f16998628a407db5f28291bef3bc2e4d8a5a31fb238e"
|
|
"6f");
|
|
string kat_ciphertext =
|
|
hex2str("21ec3011074bf1ef140643d47130326c5e183f61237c69bc77551ca207d71f"
|
|
"c2b90cfac6c8d2d125e5cd9ff353dee0df");
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::ECB, PaddingMode::PKCS7, "", kat_plaintext,
|
|
kat_ciphertext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.Aes256GCMNoPaddingOneByteAtATime
|
|
* Verifies input and output sizes of AES/GCM/NoPadding Algorithm.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, Aes256GCMNoPaddingOneByteAtATime) {
|
|
string kat_key = hex2str("7972140d831eedac75d5ea515c9a4c3bb124499a90b5f317ac1a685e88fae395");
|
|
string kat_iv = hex2str("a66c5252808d823dd4151fed");
|
|
string kat_plaintext =
|
|
hex2str("c2b9dabf3a55adaa94e8c0d1e77a84a3435aee23b2c3c4abb587b09a9c2afb"
|
|
"f0");
|
|
string kat_ciphertext =
|
|
hex2str("a960619314657b2afb96b93bebb372bffd09e19d53e351f17d1ba2611f9dc3"
|
|
"3c9c92d563e8fd381254ac262aa2a4ea0d");
|
|
|
|
AesCheckEncryptOneByteAtATime(kat_key, BlockMode::GCM, PaddingMode::NONE, kat_iv, kat_plaintext,
|
|
kat_ciphertext);
|
|
}
|
|
|
|
struct AesCtrSp80038aTestVector {
|
|
const char* key;
|
|
const char* nonce;
|
|
const char* plaintext;
|
|
const char* ciphertext;
|
|
};
|
|
|
|
// These test vectors are taken from
|
|
// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf, section F.5.
|
|
static const AesCtrSp80038aTestVector kAesCtrSp80038aTestVectors[] = {
|
|
// AES-128
|
|
{
|
|
"2b7e151628aed2a6abf7158809cf4f3c",
|
|
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
|
|
"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"
|
|
"30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
|
|
"874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff"
|
|
"5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee",
|
|
},
|
|
// AES-192
|
|
{
|
|
"8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b",
|
|
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
|
|
"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"
|
|
"30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
|
|
"1abc932417521ca24f2b0459fe7e6e0b090339ec0aa6faefd5ccc2c6f4ce8e94"
|
|
"1e36b26bd1ebc670d1bd1d665620abf74f78a7f6d29809585a97daec58c6b050",
|
|
},
|
|
// AES-256
|
|
{
|
|
"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
|
|
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
|
|
"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"
|
|
"30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
|
|
"601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5"
|
|
"2b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6",
|
|
},
|
|
};
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesCtrSp80038aTestVector
|
|
*
|
|
* Verifies AES CTR implementation against SP800-38A test vectors.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesCtrSp80038aTestVector) {
|
|
std::vector<uint32_t> InvalidSizes = InvalidKeySizes(Algorithm::AES);
|
|
for (size_t i = 0; i < 3; i++) {
|
|
const AesCtrSp80038aTestVector& test(kAesCtrSp80038aTestVectors[i]);
|
|
const string key = hex2str(test.key);
|
|
if (std::find(InvalidSizes.begin(), InvalidSizes.end(), (key.size() * 8)) !=
|
|
InvalidSizes.end())
|
|
continue;
|
|
const string nonce = hex2str(test.nonce);
|
|
const string plaintext = hex2str(test.plaintext);
|
|
const string ciphertext = hex2str(test.ciphertext);
|
|
CheckAesCtrTestVector(key, nonce, plaintext, ciphertext);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesCtrIncompatiblePaddingMode
|
|
*
|
|
* Verifies that keymint rejects use of CTR mode with PKCS7 padding in the correct way.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesCtrIncompatiblePaddingMode) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::CTR)
|
|
.Padding(PaddingMode::PKCS7)));
|
|
auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CTR).Padding(PaddingMode::NONE);
|
|
EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, params));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesCtrInvalidCallerNonce
|
|
*
|
|
* Verifies that keymint fails correctly when the user supplies an incorrect-size nonce.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesCtrInvalidCallerNonce) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::CTR)
|
|
.Authorization(TAG_CALLER_NONCE)
|
|
.Padding(PaddingMode::NONE)));
|
|
|
|
auto params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::CTR)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_NONCE, AidlBuf(string(1, 'a')));
|
|
EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params));
|
|
|
|
params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::CTR)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_NONCE, AidlBuf(string(15, 'a')));
|
|
EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params));
|
|
|
|
params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::CTR)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_NONCE, AidlBuf(string(17, 'a')));
|
|
EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesCbcRoundTripSuccess
|
|
*
|
|
* Verifies that keymint fails correctly when the user supplies an incorrect-size nonce.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesCbcRoundTripSuccess) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
|
|
.Padding(PaddingMode::NONE)));
|
|
// Two-block message.
|
|
string message = "12345678901234567890123456789012";
|
|
auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
|
|
AuthorizationSet out_params;
|
|
string ciphertext1 = EncryptMessage(message, params, &out_params);
|
|
vector<uint8_t> iv1 = CopyIv(out_params);
|
|
EXPECT_EQ(message.size(), ciphertext1.size());
|
|
|
|
out_params.Clear();
|
|
|
|
string ciphertext2 = EncryptMessage(message, params, &out_params);
|
|
vector<uint8_t> iv2 = CopyIv(out_params);
|
|
EXPECT_EQ(message.size(), ciphertext2.size());
|
|
|
|
// IVs should be random, so ciphertexts should differ.
|
|
EXPECT_NE(ciphertext1, ciphertext2);
|
|
|
|
params.push_back(TAG_NONCE, iv1);
|
|
string plaintext = DecryptMessage(ciphertext1, params);
|
|
EXPECT_EQ(message, plaintext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesCbcZeroInputSuccessb
|
|
*
|
|
* Verifies that keymaster generates correct output on zero-input with
|
|
* NonePadding mode
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesCbcZeroInputSuccess) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.BlockMode(BlockMode::CBC)
|
|
.Padding(PaddingMode::NONE, PaddingMode::PKCS7)));
|
|
|
|
// Zero input message
|
|
string message = "";
|
|
for (auto padding : {PaddingMode::NONE, PaddingMode::PKCS7}) {
|
|
SCOPED_TRACE(testing::Message() << "AES padding=" << padding);
|
|
auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(padding);
|
|
AuthorizationSet out_params;
|
|
string ciphertext1 = EncryptMessage(message, params, &out_params);
|
|
vector<uint8_t> iv1 = CopyIv(out_params);
|
|
if (padding == PaddingMode::NONE)
|
|
EXPECT_EQ(message.size(), ciphertext1.size()) << "PaddingMode: " << padding;
|
|
else
|
|
EXPECT_EQ(message.size(), ciphertext1.size() - 16) << "PaddingMode: " << padding;
|
|
|
|
out_params.Clear();
|
|
|
|
string ciphertext2 = EncryptMessage(message, params, &out_params);
|
|
vector<uint8_t> iv2 = CopyIv(out_params);
|
|
if (padding == PaddingMode::NONE)
|
|
EXPECT_EQ(message.size(), ciphertext2.size()) << "PaddingMode: " << padding;
|
|
else
|
|
EXPECT_EQ(message.size(), ciphertext2.size() - 16) << "PaddingMode: " << padding;
|
|
|
|
// IVs should be random
|
|
EXPECT_NE(iv1, iv2) << "PaddingMode: " << padding;
|
|
|
|
params.push_back(TAG_NONCE, iv1);
|
|
string plaintext = DecryptMessage(ciphertext1, params);
|
|
EXPECT_EQ(message, plaintext) << "PaddingMode: " << padding;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesCallerNonce
|
|
*
|
|
* Verifies that AES caller-provided nonces work correctly.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesCallerNonce) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
|
|
.Authorization(TAG_CALLER_NONCE)
|
|
.Padding(PaddingMode::NONE)));
|
|
|
|
string message = "12345678901234567890123456789012";
|
|
|
|
// Don't specify nonce, should get a random one.
|
|
AuthorizationSetBuilder params =
|
|
AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
|
|
AuthorizationSet out_params;
|
|
string ciphertext = EncryptMessage(message, params, &out_params);
|
|
EXPECT_EQ(message.size(), ciphertext.size());
|
|
EXPECT_EQ(16U, out_params.GetTagValue(TAG_NONCE)->get().size());
|
|
|
|
params.push_back(TAG_NONCE, out_params.GetTagValue(TAG_NONCE)->get());
|
|
string plaintext = DecryptMessage(ciphertext, params);
|
|
EXPECT_EQ(message, plaintext);
|
|
|
|
// Now specify a nonce, should also work.
|
|
params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::CBC)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_NONCE, AidlBuf("abcdefghijklmnop"));
|
|
out_params.Clear();
|
|
ciphertext = EncryptMessage(message, params, &out_params);
|
|
|
|
// Decrypt with correct nonce.
|
|
plaintext = DecryptMessage(ciphertext, params);
|
|
EXPECT_EQ(message, plaintext);
|
|
|
|
// Try with wrong nonce.
|
|
params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::CBC)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_NONCE, AidlBuf("aaaaaaaaaaaaaaaa"));
|
|
plaintext = DecryptMessage(ciphertext, params);
|
|
EXPECT_NE(message, plaintext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesCallerNonceProhibited
|
|
*
|
|
* Verifies that caller-provided nonces are not permitted when not specified in the key
|
|
* authorizations.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesCallerNonceProhibited) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
|
|
.Padding(PaddingMode::NONE)));
|
|
|
|
string message = "12345678901234567890123456789012";
|
|
|
|
// Don't specify nonce, should get a random one.
|
|
AuthorizationSetBuilder params =
|
|
AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
|
|
AuthorizationSet out_params;
|
|
string ciphertext = EncryptMessage(message, params, &out_params);
|
|
EXPECT_EQ(message.size(), ciphertext.size());
|
|
EXPECT_EQ(16U, out_params.GetTagValue(TAG_NONCE)->get().size());
|
|
|
|
params.push_back(TAG_NONCE, out_params.GetTagValue(TAG_NONCE)->get());
|
|
string plaintext = DecryptMessage(ciphertext, params);
|
|
EXPECT_EQ(message, plaintext);
|
|
|
|
// Now specify a nonce, should fail
|
|
params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::CBC)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_NONCE, AidlBuf("abcdefghijklmnop"));
|
|
out_params.Clear();
|
|
EXPECT_EQ(ErrorCode::CALLER_NONCE_PROHIBITED, Begin(KeyPurpose::ENCRYPT, params, &out_params));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesGcmRoundTripSuccess
|
|
*
|
|
* Verifies that AES GCM mode works.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesGcmRoundTripSuccess) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
|
|
|
|
string aad = "foobar";
|
|
string message = "123456789012345678901234567890123456";
|
|
|
|
auto begin_params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MAC_LENGTH, 128);
|
|
|
|
// Encrypt
|
|
AuthorizationSet begin_out_params;
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params))
|
|
<< "Begin encrypt";
|
|
string ciphertext;
|
|
ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
|
|
ASSERT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
|
|
ASSERT_EQ(ciphertext.length(), message.length() + 16);
|
|
|
|
// Grab nonce
|
|
begin_params.push_back(begin_out_params);
|
|
|
|
// Decrypt.
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params)) << "Begin decrypt";
|
|
ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
|
|
string plaintext;
|
|
EXPECT_EQ(ErrorCode::OK, Finish(ciphertext, &plaintext));
|
|
EXPECT_EQ(message.length(), plaintext.length());
|
|
EXPECT_EQ(message, plaintext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesGcmRoundTripWithDelaySuccess
|
|
*
|
|
* Verifies that AES GCM mode works, even when there's a long delay
|
|
* between operations.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesGcmRoundTripWithDelaySuccess) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
|
|
|
|
string aad = "foobar";
|
|
string message = "123456789012345678901234567890123456";
|
|
|
|
auto begin_params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MAC_LENGTH, 128);
|
|
|
|
// Encrypt
|
|
AuthorizationSet begin_out_params;
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params))
|
|
<< "Begin encrypt";
|
|
string ciphertext;
|
|
AuthorizationSet update_out_params;
|
|
ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
|
|
sleep(5);
|
|
ASSERT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
|
|
|
|
ASSERT_EQ(ciphertext.length(), message.length() + 16);
|
|
|
|
// Grab nonce
|
|
begin_params.push_back(begin_out_params);
|
|
|
|
// Decrypt.
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params)) << "Begin decrypt";
|
|
string plaintext;
|
|
ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
|
|
sleep(5);
|
|
ASSERT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext));
|
|
sleep(5);
|
|
EXPECT_EQ(ErrorCode::OK, Finish("", &plaintext));
|
|
EXPECT_EQ(message.length(), plaintext.length());
|
|
EXPECT_EQ(message, plaintext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesGcmDifferentNonces
|
|
*
|
|
* Verifies that encrypting the same data with different nonces produces different outputs.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesGcmDifferentNonces) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)
|
|
.Authorization(TAG_CALLER_NONCE)));
|
|
|
|
string aad = "foobar";
|
|
string message = "123456789012345678901234567890123456";
|
|
string nonce1 = "000000000000";
|
|
string nonce2 = "111111111111";
|
|
string nonce3 = "222222222222";
|
|
|
|
string ciphertext1 =
|
|
EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128, AidlBuf(nonce1));
|
|
string ciphertext2 =
|
|
EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128, AidlBuf(nonce2));
|
|
string ciphertext3 =
|
|
EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128, AidlBuf(nonce3));
|
|
|
|
ASSERT_NE(ciphertext1, ciphertext2);
|
|
ASSERT_NE(ciphertext1, ciphertext3);
|
|
ASSERT_NE(ciphertext2, ciphertext3);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesGcmDifferentAutoNonces
|
|
*
|
|
* Verifies that encrypting the same data with KeyMint generated nonces produces different outputs.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesGcmDifferentAutoNonces) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
|
|
|
|
string aad = "foobar";
|
|
string message = "123456789012345678901234567890123456";
|
|
|
|
string ciphertext1 = EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128);
|
|
string ciphertext2 = EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128);
|
|
string ciphertext3 = EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128);
|
|
|
|
ASSERT_NE(ciphertext1, ciphertext2);
|
|
ASSERT_NE(ciphertext1, ciphertext3);
|
|
ASSERT_NE(ciphertext2, ciphertext3);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesGcmTooShortTag
|
|
*
|
|
* Verifies that AES GCM mode fails correctly when a too-short tag length is specified.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesGcmTooShortTag) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
|
|
string message = "123456789012345678901234567890123456";
|
|
auto params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MAC_LENGTH, 96);
|
|
|
|
EXPECT_EQ(ErrorCode::INVALID_MAC_LENGTH, Begin(KeyPurpose::ENCRYPT, params));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesGcmTooShortTagOnDecrypt
|
|
*
|
|
* Verifies that AES GCM mode fails correctly when a too-short tag is provided to decryption.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesGcmTooShortTagOnDecrypt) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
|
|
string aad = "foobar";
|
|
string message = "123456789012345678901234567890123456";
|
|
auto params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MAC_LENGTH, 128);
|
|
|
|
// Encrypt
|
|
AuthorizationSet begin_out_params;
|
|
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params));
|
|
EXPECT_EQ(1U, begin_out_params.size());
|
|
ASSERT_TRUE(begin_out_params.GetTagValue(TAG_NONCE));
|
|
|
|
AuthorizationSet finish_out_params;
|
|
string ciphertext;
|
|
ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
|
|
EXPECT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
|
|
|
|
params = AuthorizationSetBuilder()
|
|
.Authorizations(begin_out_params)
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MAC_LENGTH, 96);
|
|
|
|
// Decrypt.
|
|
EXPECT_EQ(ErrorCode::INVALID_MAC_LENGTH, Begin(KeyPurpose::DECRYPT, params));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesGcmCorruptKey
|
|
*
|
|
* Verifies that AES GCM mode fails correctly when the decryption key is incorrect.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesGcmCorruptKey) {
|
|
const uint8_t nonce_bytes[] = {
|
|
0xb7, 0x94, 0x37, 0xae, 0x08, 0xff, 0x35, 0x5d, 0x7d, 0x8a, 0x4d, 0x0f,
|
|
};
|
|
string nonce = make_string(nonce_bytes);
|
|
const uint8_t ciphertext_bytes[] = {
|
|
0xb3, 0xf6, 0x79, 0x9e, 0x8f, 0x93, 0x26, 0xf2, 0xdf, 0x1e, 0x80, 0xfc,
|
|
0xd2, 0xcb, 0x16, 0xd7, 0x8c, 0x9d, 0xc7, 0xcc, 0x14, 0xbb, 0x67, 0x78,
|
|
0x62, 0xdc, 0x6c, 0x63, 0x9b, 0x3a, 0x63, 0x38, 0xd2, 0x4b, 0x31, 0x2d,
|
|
0x39, 0x89, 0xe5, 0x92, 0x0b, 0x5d, 0xbf, 0xc9, 0x76, 0x76, 0x5e, 0xfb,
|
|
0xfe, 0x57, 0xbb, 0x38, 0x59, 0x40, 0xa7, 0xa4, 0x3b, 0xdf, 0x05, 0xbd,
|
|
0xda, 0xe3, 0xc9, 0xd6, 0xa2, 0xfb, 0xbd, 0xfc, 0xc0, 0xcb, 0xa0,
|
|
};
|
|
string ciphertext = make_string(ciphertext_bytes);
|
|
|
|
auto params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MAC_LENGTH, 128)
|
|
.Authorization(TAG_NONCE, nonce.data(), nonce.size());
|
|
|
|
auto import_params = AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_CALLER_NONCE)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128);
|
|
|
|
// Import correct key and decrypt
|
|
const uint8_t key_bytes[] = {
|
|
0xba, 0x76, 0x35, 0x4f, 0x0a, 0xed, 0x6e, 0x8d,
|
|
0x91, 0xf4, 0x5c, 0x4f, 0xf5, 0xa0, 0x62, 0xdb,
|
|
};
|
|
string key = make_string(key_bytes);
|
|
ASSERT_EQ(ErrorCode::OK, ImportKey(import_params, KeyFormat::RAW, key));
|
|
string plaintext = DecryptMessage(ciphertext, params);
|
|
CheckedDeleteKey();
|
|
|
|
// Corrupt key and attempt to decrypt
|
|
key[0] = 0;
|
|
ASSERT_EQ(ErrorCode::OK, ImportKey(import_params, KeyFormat::RAW, key));
|
|
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
|
|
EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(ciphertext, &plaintext));
|
|
CheckedDeleteKey();
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesGcmAadNoData
|
|
*
|
|
* Verifies that AES GCM mode works when provided additional authenticated data, but no data to
|
|
* encrypt.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesGcmAadNoData) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
|
|
|
|
string aad = "1234567890123456";
|
|
auto params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MAC_LENGTH, 128);
|
|
|
|
// Encrypt
|
|
AuthorizationSet begin_out_params;
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params));
|
|
string ciphertext;
|
|
AuthorizationSet finish_out_params;
|
|
ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
|
|
EXPECT_EQ(ErrorCode::OK, Finish(&ciphertext));
|
|
EXPECT_TRUE(finish_out_params.empty());
|
|
|
|
// Grab nonce
|
|
params.push_back(begin_out_params);
|
|
|
|
// Decrypt.
|
|
EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
|
|
ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
|
|
string plaintext;
|
|
EXPECT_EQ(ErrorCode::OK, Finish(ciphertext, &plaintext));
|
|
|
|
EXPECT_TRUE(finish_out_params.empty());
|
|
|
|
EXPECT_EQ("", plaintext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesGcmMultiPartAad
|
|
*
|
|
* Verifies that AES GCM mode works when provided additional authenticated data in multiple
|
|
* chunks.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesGcmMultiPartAad) {
|
|
const size_t tag_bits = 128;
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
|
|
|
|
string message = "123456789012345678901234567890123456";
|
|
auto begin_params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MAC_LENGTH, tag_bits);
|
|
AuthorizationSet begin_out_params;
|
|
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
|
|
|
|
// No data, AAD only.
|
|
EXPECT_EQ(ErrorCode::OK, UpdateAad("foo"));
|
|
EXPECT_EQ(ErrorCode::OK, UpdateAad("foo"));
|
|
string ciphertext;
|
|
EXPECT_EQ(ErrorCode::OK, Update(message, &ciphertext));
|
|
EXPECT_EQ(ErrorCode::OK, Finish(&ciphertext));
|
|
|
|
// Expect 128-bit (16-byte) tag appended to ciphertext.
|
|
EXPECT_EQ(message.size() + (tag_bits / 8), ciphertext.size());
|
|
|
|
// Grab nonce.
|
|
begin_params.push_back(begin_out_params);
|
|
|
|
// Decrypt
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
|
|
EXPECT_EQ(ErrorCode::OK, UpdateAad("foofoo"));
|
|
string plaintext;
|
|
EXPECT_EQ(ErrorCode::OK, Finish(ciphertext, &plaintext));
|
|
EXPECT_EQ(message, plaintext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesGcmAadOutOfOrder
|
|
*
|
|
* Verifies that AES GCM mode fails correctly when given AAD after data to encipher.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesGcmAadOutOfOrder) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
|
|
|
|
string message = "123456789012345678901234567890123456";
|
|
auto begin_params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MAC_LENGTH, 128);
|
|
AuthorizationSet begin_out_params;
|
|
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
|
|
|
|
EXPECT_EQ(ErrorCode::OK, UpdateAad("foo"));
|
|
string ciphertext;
|
|
EXPECT_EQ(ErrorCode::OK, Update(message, &ciphertext));
|
|
EXPECT_EQ(ErrorCode::INVALID_TAG, UpdateAad("foo"));
|
|
|
|
// The failure should have already cancelled the operation.
|
|
EXPECT_EQ(ErrorCode::INVALID_OPERATION_HANDLE, Abort());
|
|
|
|
op_ = {};
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesGcmBadAad
|
|
*
|
|
* Verifies that AES GCM decryption fails correctly when additional authenticated date is wrong.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesGcmBadAad) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
|
|
|
|
string message = "12345678901234567890123456789012";
|
|
auto begin_params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MAC_LENGTH, 128);
|
|
|
|
// Encrypt
|
|
AuthorizationSet begin_out_params;
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
|
|
EXPECT_EQ(ErrorCode::OK, UpdateAad("foobar"));
|
|
string ciphertext;
|
|
EXPECT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
|
|
|
|
// Grab nonce
|
|
begin_params.push_back(begin_out_params);
|
|
|
|
// Decrypt.
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params, &begin_out_params));
|
|
EXPECT_EQ(ErrorCode::OK, UpdateAad("barfoo"));
|
|
string plaintext;
|
|
EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(ciphertext, &plaintext));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesGcmWrongNonce
|
|
*
|
|
* Verifies that AES GCM decryption fails correctly when the nonce is incorrect.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesGcmWrongNonce) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
|
|
|
|
string message = "12345678901234567890123456789012";
|
|
auto begin_params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MAC_LENGTH, 128);
|
|
|
|
// Encrypt
|
|
AuthorizationSet begin_out_params;
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
|
|
EXPECT_EQ(ErrorCode::OK, UpdateAad("foobar"));
|
|
string ciphertext;
|
|
AuthorizationSet finish_out_params;
|
|
EXPECT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
|
|
|
|
// Wrong nonce
|
|
begin_params.push_back(TAG_NONCE, AidlBuf("123456789012"));
|
|
|
|
// Decrypt.
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params, &begin_out_params));
|
|
EXPECT_EQ(ErrorCode::OK, UpdateAad("foobar"));
|
|
string plaintext;
|
|
EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(ciphertext, &plaintext));
|
|
|
|
// With wrong nonce, should have gotten garbage plaintext (or none).
|
|
EXPECT_NE(message, plaintext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.AesGcmCorruptTag
|
|
*
|
|
* Verifies that AES GCM decryption fails correctly when the tag is wrong.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, AesGcmCorruptTag) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 128)));
|
|
|
|
string aad = "1234567890123456";
|
|
string message = "123456789012345678901234567890123456";
|
|
|
|
auto params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::GCM)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MAC_LENGTH, 128);
|
|
|
|
// Encrypt
|
|
AuthorizationSet begin_out_params;
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params));
|
|
EXPECT_EQ(ErrorCode::OK, UpdateAad(aad));
|
|
string ciphertext;
|
|
EXPECT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
|
|
|
|
// Corrupt tag
|
|
++(*ciphertext.rbegin());
|
|
|
|
// Grab nonce
|
|
params.push_back(begin_out_params);
|
|
|
|
// Decrypt.
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
|
|
EXPECT_EQ(ErrorCode::OK, UpdateAad(aad));
|
|
string plaintext;
|
|
EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(ciphertext, &plaintext));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.TripleDesEcbRoundTripSuccess
|
|
*
|
|
* Verifies that 3DES is basically functional.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, TripleDesEcbRoundTripSuccess) {
|
|
auto auths = AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(168)
|
|
.BlockMode(BlockMode::ECB)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::NONE);
|
|
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(auths));
|
|
// Two-block message.
|
|
string message = "1234567890123456";
|
|
auto inParams = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
|
|
string ciphertext1 = EncryptMessage(message, inParams);
|
|
EXPECT_EQ(message.size(), ciphertext1.size());
|
|
|
|
string ciphertext2 = EncryptMessage(string(message), inParams);
|
|
EXPECT_EQ(message.size(), ciphertext2.size());
|
|
|
|
// ECB is deterministic.
|
|
EXPECT_EQ(ciphertext1, ciphertext2);
|
|
|
|
string plaintext = DecryptMessage(ciphertext1, inParams);
|
|
EXPECT_EQ(message, plaintext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.TripleDesEcbNotAuthorized
|
|
*
|
|
* Verifies that CBC keys reject ECB usage.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, TripleDesEcbNotAuthorized) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(168)
|
|
.BlockMode(BlockMode::CBC)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::NONE)));
|
|
|
|
auto inParams = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
|
|
EXPECT_EQ(ErrorCode::INCOMPATIBLE_BLOCK_MODE, Begin(KeyPurpose::ENCRYPT, inParams));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.TripleDesEcbPkcs7Padding
|
|
*
|
|
* Tests ECB mode with PKCS#7 padding, various message sizes.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, TripleDesEcbPkcs7Padding) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(168)
|
|
.BlockMode(BlockMode::ECB)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::PKCS7)));
|
|
|
|
for (size_t i = 0; i < 32; ++i) {
|
|
SCOPED_TRACE(testing::Message() << "msg size=" << i);
|
|
string message(i, 'a');
|
|
auto inParams =
|
|
AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
|
|
string ciphertext = EncryptMessage(message, inParams);
|
|
EXPECT_EQ(i + 8 - (i % 8), ciphertext.size());
|
|
string plaintext = DecryptMessage(ciphertext, inParams);
|
|
EXPECT_EQ(message, plaintext);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.TripleDesEcbNoPaddingKeyWithPkcs7Padding
|
|
*
|
|
* Verifies that keys configured for no padding reject PKCS7 padding
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, TripleDesEcbNoPaddingKeyWithPkcs7Padding) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(168)
|
|
.BlockMode(BlockMode::ECB)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::NONE)));
|
|
auto inParams = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
|
|
EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, inParams));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.TripleDesEcbPkcs7PaddingCorrupted
|
|
*
|
|
* Verifies that corrupted padding is detected.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, TripleDesEcbPkcs7PaddingCorrupted) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(168)
|
|
.BlockMode(BlockMode::ECB)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::PKCS7)));
|
|
|
|
string message = "a";
|
|
string ciphertext = EncryptMessage(message, BlockMode::ECB, PaddingMode::PKCS7);
|
|
EXPECT_EQ(8U, ciphertext.size());
|
|
EXPECT_NE(ciphertext, message);
|
|
|
|
AuthorizationSetBuilder begin_params;
|
|
begin_params.push_back(TAG_BLOCK_MODE, BlockMode::ECB);
|
|
begin_params.push_back(TAG_PADDING, PaddingMode::PKCS7);
|
|
|
|
for (size_t i = 0; i < kMaxPaddingCorruptionRetries; ++i) {
|
|
++ciphertext[ciphertext.size() / 2];
|
|
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
|
|
string plaintext;
|
|
EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext));
|
|
ErrorCode error = Finish(&plaintext);
|
|
if (error == ErrorCode::INVALID_ARGUMENT) {
|
|
// This is the expected error, we can exit the test now.
|
|
return;
|
|
} else {
|
|
// Very small chance we got valid decryption, so try again.
|
|
ASSERT_EQ(error, ErrorCode::OK);
|
|
}
|
|
}
|
|
FAIL() << "Corrupt ciphertext should have failed to decrypt by now.";
|
|
}
|
|
|
|
struct TripleDesTestVector {
|
|
const char* name;
|
|
const KeyPurpose purpose;
|
|
const BlockMode block_mode;
|
|
const PaddingMode padding_mode;
|
|
const char* key;
|
|
const char* iv;
|
|
const char* input;
|
|
const char* output;
|
|
};
|
|
|
|
// These test vectors are from NIST CAVP, plus a few custom variants to test padding, since all
|
|
// of the NIST vectors are multiples of the block size.
|
|
static const TripleDesTestVector kTripleDesTestVectors[] = {
|
|
{
|
|
"TECBMMT3 Encrypt 0", KeyPurpose::ENCRYPT, BlockMode::ECB, PaddingMode::NONE,
|
|
"a2b5bc67da13dc92cd9d344aa238544a0e1fa79ef76810cd", // key
|
|
"", // IV
|
|
"329d86bdf1bc5af4", // input
|
|
"d946c2756d78633f", // output
|
|
},
|
|
{
|
|
"TECBMMT3 Encrypt 1", KeyPurpose::ENCRYPT, BlockMode::ECB, PaddingMode::NONE,
|
|
"49e692290d2a5e46bace79b9648a4c5d491004c262dc9d49", // key
|
|
"", // IV
|
|
"6b1540781b01ce1997adae102dbf3c5b", // input
|
|
"4d0dc182d6e481ac4a3dc6ab6976ccae", // output
|
|
},
|
|
{
|
|
"TECBMMT3 Decrypt 0", KeyPurpose::DECRYPT, BlockMode::ECB, PaddingMode::NONE,
|
|
"52daec2ac7dc1958377392682f37860b2cc1ea2304bab0e9", // key
|
|
"", // IV
|
|
"6daad94ce08acfe7", // input
|
|
"660e7d32dcc90e79", // output
|
|
},
|
|
{
|
|
"TECBMMT3 Decrypt 1", KeyPurpose::DECRYPT, BlockMode::ECB, PaddingMode::NONE,
|
|
"7f8fe3d3f4a48394fb682c2919926d6ddfce8932529229ce", // key
|
|
"", // IV
|
|
"e9653a0a1f05d31b9acd12d73aa9879d", // input
|
|
"9b2ae9d998efe62f1b592e7e1df8ff38", // output
|
|
},
|
|
{
|
|
"TCBCMMT3 Encrypt 0", KeyPurpose::ENCRYPT, BlockMode::CBC, PaddingMode::NONE,
|
|
"b5cb1504802326c73df186e3e352a20de643b0d63ee30e37", // key
|
|
"43f791134c5647ba", // IV
|
|
"dcc153cef81d6f24", // input
|
|
"92538bd8af18d3ba", // output
|
|
},
|
|
{
|
|
"TCBCMMT3 Encrypt 1", KeyPurpose::ENCRYPT, BlockMode::CBC, PaddingMode::NONE,
|
|
"a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358", // key
|
|
"c2e999cb6249023c", // IV
|
|
"c689aee38a301bb316da75db36f110b5", // input
|
|
"e9afaba5ec75ea1bbe65506655bb4ecb", // output
|
|
},
|
|
{
|
|
"TCBCMMT3 Encrypt 1 PKCS7 variant", KeyPurpose::ENCRYPT, BlockMode::CBC,
|
|
PaddingMode::PKCS7,
|
|
"a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358", // key
|
|
"c2e999cb6249023c", // IV
|
|
"c689aee38a301bb316da75db36f110b500", // input
|
|
"e9afaba5ec75ea1bbe65506655bb4ecb825aa27ec0656156", // output
|
|
},
|
|
{
|
|
"TCBCMMT3 Encrypt 1 PKCS7 decrypted", KeyPurpose::DECRYPT, BlockMode::CBC,
|
|
PaddingMode::PKCS7,
|
|
"a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358", // key
|
|
"c2e999cb6249023c", // IV
|
|
"e9afaba5ec75ea1bbe65506655bb4ecb825aa27ec0656156", // input
|
|
"c689aee38a301bb316da75db36f110b500", // output
|
|
},
|
|
{
|
|
"TCBCMMT3 Decrypt 0", KeyPurpose::DECRYPT, BlockMode::CBC, PaddingMode::NONE,
|
|
"5eb6040d46082c7aa7d06dfd08dfeac8c18364c1548c3ba1", // key
|
|
"41746c7e442d3681", // IV
|
|
"c53a7b0ec40600fe", // input
|
|
"d4f00eb455de1034", // output
|
|
},
|
|
{
|
|
"TCBCMMT3 Decrypt 1", KeyPurpose::DECRYPT, BlockMode::CBC, PaddingMode::NONE,
|
|
"5b1cce7c0dc1ec49130dfb4af45785ab9179e567f2c7d549", // key
|
|
"3982bc02c3727d45", // IV
|
|
"6006f10adef52991fcc777a1238bbb65", // input
|
|
"edae09288e9e3bc05746d872b48e3b29", // output
|
|
},
|
|
};
|
|
|
|
/*
|
|
* EncryptionOperationsTest.TripleDesTestVector
|
|
*
|
|
* Verifies that NIST (plus a few extra) test vectors produce the correct results.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, TripleDesTestVector) {
|
|
constexpr size_t num_tests = sizeof(kTripleDesTestVectors) / sizeof(TripleDesTestVector);
|
|
for (auto* test = kTripleDesTestVectors; test < kTripleDesTestVectors + num_tests; ++test) {
|
|
SCOPED_TRACE(test->name);
|
|
CheckTripleDesTestVector(test->purpose, test->block_mode, test->padding_mode,
|
|
hex2str(test->key), hex2str(test->iv), hex2str(test->input),
|
|
hex2str(test->output));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.TripleDesCbcRoundTripSuccess
|
|
*
|
|
* Validates CBC mode functionality.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, TripleDesCbcRoundTripSuccess) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(168)
|
|
.BlockMode(BlockMode::CBC)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::NONE)));
|
|
|
|
ASSERT_GT(key_blob_.size(), 0U);
|
|
|
|
// Four-block message.
|
|
string message = "12345678901234561234567890123456";
|
|
vector<uint8_t> iv1;
|
|
string ciphertext1 = EncryptMessage(message, BlockMode::CBC, PaddingMode::NONE, &iv1);
|
|
EXPECT_EQ(message.size(), ciphertext1.size());
|
|
|
|
vector<uint8_t> iv2;
|
|
string ciphertext2 = EncryptMessage(message, BlockMode::CBC, PaddingMode::NONE, &iv2);
|
|
EXPECT_EQ(message.size(), ciphertext2.size());
|
|
|
|
// IVs should be random, so ciphertexts should differ.
|
|
EXPECT_NE(iv1, iv2);
|
|
EXPECT_NE(ciphertext1, ciphertext2);
|
|
|
|
string plaintext = DecryptMessage(ciphertext1, BlockMode::CBC, PaddingMode::NONE, iv1);
|
|
EXPECT_EQ(message, plaintext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.TripleDesInvalidCallerIv
|
|
*
|
|
* Validates that keymint fails correctly when the user supplies an incorrect-size IV.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, TripleDesInvalidCallerIv) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(168)
|
|
.BlockMode(BlockMode::CBC)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_CALLER_NONCE)
|
|
.Padding(PaddingMode::NONE)));
|
|
auto params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::CBC)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_NONCE, AidlBuf("abcdefg"));
|
|
EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.TripleDesCallerIv
|
|
*
|
|
* Validates that 3DES keys can allow caller-specified IVs, and use them correctly.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, TripleDesCallerIv) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(168)
|
|
.BlockMode(BlockMode::CBC)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_CALLER_NONCE)
|
|
.Padding(PaddingMode::NONE)));
|
|
string message = "1234567890123456";
|
|
vector<uint8_t> iv;
|
|
// Don't specify IV, should get a random one.
|
|
string ciphertext1 = EncryptMessage(message, BlockMode::CBC, PaddingMode::NONE, &iv);
|
|
EXPECT_EQ(message.size(), ciphertext1.size());
|
|
EXPECT_EQ(8U, iv.size());
|
|
|
|
string plaintext = DecryptMessage(ciphertext1, BlockMode::CBC, PaddingMode::NONE, iv);
|
|
EXPECT_EQ(message, plaintext);
|
|
|
|
// Now specify an IV, should also work.
|
|
iv = AidlBuf("abcdefgh");
|
|
string ciphertext2 = EncryptMessage(message, BlockMode::CBC, PaddingMode::NONE, iv);
|
|
|
|
// Decrypt with correct IV.
|
|
plaintext = DecryptMessage(ciphertext2, BlockMode::CBC, PaddingMode::NONE, iv);
|
|
EXPECT_EQ(message, plaintext);
|
|
|
|
// Now try with wrong IV.
|
|
plaintext = DecryptMessage(ciphertext2, BlockMode::CBC, PaddingMode::NONE, AidlBuf("aaaaaaaa"));
|
|
EXPECT_NE(message, plaintext);
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest, TripleDesCallerNonceProhibited.
|
|
*
|
|
* Verifies that 3DES keys without TAG_CALLER_NONCE do not allow caller-specified IVs.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, TripleDesCallerNonceProhibited) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(168)
|
|
.BlockMode(BlockMode::CBC)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::NONE)));
|
|
|
|
string message = "12345678901234567890123456789012";
|
|
vector<uint8_t> iv;
|
|
// Don't specify nonce, should get a random one.
|
|
string ciphertext1 = EncryptMessage(message, BlockMode::CBC, PaddingMode::NONE, &iv);
|
|
EXPECT_EQ(message.size(), ciphertext1.size());
|
|
EXPECT_EQ(8U, iv.size());
|
|
|
|
string plaintext = DecryptMessage(ciphertext1, BlockMode::CBC, PaddingMode::NONE, iv);
|
|
EXPECT_EQ(message, plaintext);
|
|
|
|
// Now specify a nonce, should fail.
|
|
auto input_params = AuthorizationSetBuilder()
|
|
.Authorization(TAG_NONCE, AidlBuf("abcdefgh"))
|
|
.BlockMode(BlockMode::CBC)
|
|
.Padding(PaddingMode::NONE);
|
|
AuthorizationSet output_params;
|
|
EXPECT_EQ(ErrorCode::CALLER_NONCE_PROHIBITED,
|
|
Begin(KeyPurpose::ENCRYPT, input_params, &output_params));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.TripleDesCbcNotAuthorized
|
|
*
|
|
* Verifies that 3DES ECB-only keys do not allow CBC usage.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, TripleDesCbcNotAuthorized) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(168)
|
|
.BlockMode(BlockMode::ECB)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::NONE)));
|
|
// Two-block message.
|
|
string message = "1234567890123456";
|
|
auto begin_params =
|
|
AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
|
|
EXPECT_EQ(ErrorCode::INCOMPATIBLE_BLOCK_MODE, Begin(KeyPurpose::ENCRYPT, begin_params));
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.TripleDesEcbCbcNoPaddingWrongInputSize
|
|
*
|
|
* Verifies that unpadded CBC operations reject inputs that are not a multiple of block size.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, TripleDesEcbCbcNoPaddingWrongInputSize) {
|
|
for (BlockMode blockMode : {BlockMode::ECB, BlockMode::CBC}) {
|
|
SCOPED_TRACE(testing::Message() << "BlockMode::" << blockMode);
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(168)
|
|
.BlockMode(blockMode)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::NONE)));
|
|
// Message is slightly shorter than two blocks.
|
|
string message = "123456789012345";
|
|
|
|
auto begin_params =
|
|
AuthorizationSetBuilder().BlockMode(blockMode).Padding(PaddingMode::NONE);
|
|
AuthorizationSet output_params;
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &output_params));
|
|
string ciphertext;
|
|
EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, "", &ciphertext));
|
|
|
|
CheckedDeleteKey();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest, TripleDesCbcPkcs7Padding.
|
|
*
|
|
* Verifies that PKCS7 padding works correctly in CBC mode.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, TripleDesCbcPkcs7Padding) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(168)
|
|
.BlockMode(BlockMode::CBC)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::PKCS7)));
|
|
|
|
// Try various message lengths; all should work.
|
|
for (size_t i = 0; i <= 32; i++) {
|
|
SCOPED_TRACE(testing::Message() << "i = " << i);
|
|
// Edge case: '\t' (0x09) is also a valid PKCS7 padding character, albeit not for 3DES.
|
|
string message(i, '\t');
|
|
vector<uint8_t> iv;
|
|
string ciphertext = EncryptMessage(message, BlockMode::CBC, PaddingMode::PKCS7, &iv);
|
|
EXPECT_EQ(i + 8 - (i % 8), ciphertext.size());
|
|
string plaintext = DecryptMessage(ciphertext, BlockMode::CBC, PaddingMode::PKCS7, iv);
|
|
EXPECT_EQ(message, plaintext);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.TripleDesCbcNoPaddingKeyWithPkcs7Padding
|
|
*
|
|
* Verifies that a key that requires PKCS7 padding cannot be used in unpadded mode.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, TripleDesCbcNoPaddingKeyWithPkcs7Padding) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(168)
|
|
.BlockMode(BlockMode::CBC)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::NONE)));
|
|
|
|
// Try various message lengths; all should fail.
|
|
for (size_t i = 0; i <= 32; i++) {
|
|
SCOPED_TRACE(testing::Message() << "i = " << i);
|
|
auto begin_params =
|
|
AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::PKCS7);
|
|
EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, begin_params));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest.TripleDesCbcPkcs7PaddingCorrupted
|
|
*
|
|
* Verifies that corrupted PKCS7 padding is rejected during decryption.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, TripleDesCbcPkcs7PaddingCorrupted) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(168)
|
|
.BlockMode(BlockMode::CBC)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::PKCS7)));
|
|
|
|
string message = "a";
|
|
vector<uint8_t> iv;
|
|
string ciphertext = EncryptMessage(message, BlockMode::CBC, PaddingMode::PKCS7, &iv);
|
|
EXPECT_EQ(8U, ciphertext.size());
|
|
EXPECT_NE(ciphertext, message);
|
|
|
|
auto begin_params = AuthorizationSetBuilder()
|
|
.BlockMode(BlockMode::CBC)
|
|
.Padding(PaddingMode::PKCS7)
|
|
.Authorization(TAG_NONCE, iv);
|
|
|
|
for (size_t i = 0; i < kMaxPaddingCorruptionRetries; ++i) {
|
|
SCOPED_TRACE(testing::Message() << "i = " << i);
|
|
++ciphertext[ciphertext.size() / 2];
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
|
|
string plaintext;
|
|
EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext));
|
|
ErrorCode error = Finish(&plaintext);
|
|
if (error == ErrorCode::INVALID_ARGUMENT) {
|
|
// This is the expected error, we can exit the test now.
|
|
return;
|
|
} else {
|
|
// Very small chance we got valid decryption, so try again.
|
|
ASSERT_EQ(error, ErrorCode::OK);
|
|
}
|
|
}
|
|
FAIL() << "Corrupt ciphertext should have failed to decrypt by now.";
|
|
}
|
|
|
|
/*
|
|
* EncryptionOperationsTest, TripleDesCbcIncrementalNoPadding.
|
|
*
|
|
* Verifies that 3DES CBC works with many different input sizes.
|
|
*/
|
|
TEST_P(EncryptionOperationsTest, TripleDesCbcIncrementalNoPadding) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.TripleDesEncryptionKey(168)
|
|
.BlockMode(BlockMode::CBC)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Padding(PaddingMode::NONE)));
|
|
|
|
int increment = 7;
|
|
string message(240, 'a');
|
|
AuthorizationSet input_params =
|
|
AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
|
|
AuthorizationSet output_params;
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, input_params, &output_params));
|
|
|
|
string ciphertext;
|
|
for (size_t i = 0; i < message.size(); i += increment)
|
|
EXPECT_EQ(ErrorCode::OK, Update(message.substr(i, increment), &ciphertext));
|
|
EXPECT_EQ(ErrorCode::OK, Finish(&ciphertext));
|
|
EXPECT_EQ(message.size(), ciphertext.size());
|
|
|
|
// Move TAG_NONCE into input_params
|
|
input_params = output_params;
|
|
input_params.push_back(TAG_BLOCK_MODE, BlockMode::CBC);
|
|
input_params.push_back(TAG_PADDING, PaddingMode::NONE);
|
|
output_params.Clear();
|
|
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, input_params, &output_params));
|
|
string plaintext;
|
|
for (size_t i = 0; i < ciphertext.size(); i += increment)
|
|
EXPECT_EQ(ErrorCode::OK, Update(ciphertext.substr(i, increment), &plaintext));
|
|
EXPECT_EQ(ErrorCode::OK, Finish(&plaintext));
|
|
EXPECT_EQ(ciphertext.size(), plaintext.size());
|
|
EXPECT_EQ(message, plaintext);
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(EncryptionOperationsTest);
|
|
|
|
typedef KeyMintAidlTestBase MaxOperationsTest;
|
|
|
|
/*
|
|
* MaxOperationsTest.TestLimitAes
|
|
*
|
|
* Verifies that the max uses per boot tag works correctly with AES keys.
|
|
*/
|
|
TEST_P(MaxOperationsTest, TestLimitAes) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Test not applicable to StrongBox device";
|
|
}
|
|
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.EcbMode()
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_MAX_USES_PER_BOOT, 3)));
|
|
|
|
string message = "1234567890123456";
|
|
|
|
auto params = AuthorizationSetBuilder().EcbMode().Padding(PaddingMode::NONE);
|
|
|
|
EncryptMessage(message, params);
|
|
EncryptMessage(message, params);
|
|
EncryptMessage(message, params);
|
|
|
|
// Fourth time should fail.
|
|
EXPECT_EQ(ErrorCode::KEY_MAX_OPS_EXCEEDED, Begin(KeyPurpose::ENCRYPT, params));
|
|
}
|
|
|
|
/*
|
|
* MaxOperationsTest.TestLimitRsa
|
|
*
|
|
* Verifies that the max uses per boot tag works correctly with RSA keys.
|
|
*/
|
|
TEST_P(MaxOperationsTest, TestLimitRsa) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Test not applicable to StrongBox device";
|
|
}
|
|
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaSigningKey(1024, 65537)
|
|
.NoDigestOrPadding()
|
|
.Authorization(TAG_MAX_USES_PER_BOOT, 3)
|
|
.SetDefaultValidity()));
|
|
|
|
string message = "1234567890123456";
|
|
|
|
auto params = AuthorizationSetBuilder().NoDigestOrPadding();
|
|
|
|
SignMessage(message, params);
|
|
SignMessage(message, params);
|
|
SignMessage(message, params);
|
|
|
|
// Fourth time should fail.
|
|
EXPECT_EQ(ErrorCode::KEY_MAX_OPS_EXCEEDED, Begin(KeyPurpose::SIGN, params));
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(MaxOperationsTest);
|
|
|
|
typedef KeyMintAidlTestBase UsageCountLimitTest;
|
|
|
|
/*
|
|
* UsageCountLimitTest.TestSingleUseAes
|
|
*
|
|
* Verifies that the usage count limit tag = 1 works correctly with AES keys.
|
|
*/
|
|
TEST_P(UsageCountLimitTest, TestSingleUseAes) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Test not applicable to StrongBox device";
|
|
}
|
|
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.EcbMode()
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_USAGE_COUNT_LIMIT, 1)));
|
|
|
|
// Check the usage count limit tag appears in the authorizations.
|
|
AuthorizationSet auths;
|
|
for (auto& entry : key_characteristics_) {
|
|
auths.push_back(AuthorizationSet(entry.authorizations));
|
|
}
|
|
EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U))
|
|
<< "key usage count limit " << 1U << " missing";
|
|
|
|
string message = "1234567890123456";
|
|
auto params = AuthorizationSetBuilder().EcbMode().Padding(PaddingMode::NONE);
|
|
|
|
AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_);
|
|
AuthorizationSet keystore_auths =
|
|
SecLevelAuthorizations(key_characteristics_, SecurityLevel::KEYSTORE);
|
|
|
|
// First usage of AES key should work.
|
|
EncryptMessage(message, params);
|
|
|
|
if (hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U)) {
|
|
// Usage count limit tag is enforced by hardware. After using the key, the key blob
|
|
// must be invalidated from secure storage (such as RPMB partition).
|
|
EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::ENCRYPT, params));
|
|
} else {
|
|
// Usage count limit tag is enforced by keystore, keymint does nothing.
|
|
EXPECT_TRUE(keystore_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U));
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* UsageCountLimitTest.TestLimitedUseAes
|
|
*
|
|
* Verifies that the usage count limit tag > 1 works correctly with AES keys.
|
|
*/
|
|
TEST_P(UsageCountLimitTest, TestLimitedUseAes) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Test not applicable to StrongBox device";
|
|
}
|
|
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.EcbMode()
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_USAGE_COUNT_LIMIT, 3)));
|
|
|
|
// Check the usage count limit tag appears in the authorizations.
|
|
AuthorizationSet auths;
|
|
for (auto& entry : key_characteristics_) {
|
|
auths.push_back(AuthorizationSet(entry.authorizations));
|
|
}
|
|
EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U))
|
|
<< "key usage count limit " << 3U << " missing";
|
|
|
|
string message = "1234567890123456";
|
|
auto params = AuthorizationSetBuilder().EcbMode().Padding(PaddingMode::NONE);
|
|
|
|
AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_);
|
|
AuthorizationSet keystore_auths =
|
|
SecLevelAuthorizations(key_characteristics_, SecurityLevel::KEYSTORE);
|
|
|
|
EncryptMessage(message, params);
|
|
EncryptMessage(message, params);
|
|
EncryptMessage(message, params);
|
|
|
|
if (hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U)) {
|
|
// Usage count limit tag is enforced by hardware. After using the key, the key blob
|
|
// must be invalidated from secure storage (such as RPMB partition).
|
|
EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::ENCRYPT, params));
|
|
} else {
|
|
// Usage count limit tag is enforced by keystore, keymint does nothing.
|
|
EXPECT_TRUE(keystore_auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U));
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* UsageCountLimitTest.TestSingleUseRsa
|
|
*
|
|
* Verifies that the usage count limit tag = 1 works correctly with RSA keys.
|
|
*/
|
|
TEST_P(UsageCountLimitTest, TestSingleUseRsa) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Test not applicable to StrongBox device";
|
|
}
|
|
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaSigningKey(1024, 65537)
|
|
.NoDigestOrPadding()
|
|
.Authorization(TAG_USAGE_COUNT_LIMIT, 1)
|
|
.SetDefaultValidity()));
|
|
|
|
// Check the usage count limit tag appears in the authorizations.
|
|
AuthorizationSet auths;
|
|
for (auto& entry : key_characteristics_) {
|
|
auths.push_back(AuthorizationSet(entry.authorizations));
|
|
}
|
|
EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U))
|
|
<< "key usage count limit " << 1U << " missing";
|
|
|
|
string message = "1234567890123456";
|
|
auto params = AuthorizationSetBuilder().NoDigestOrPadding();
|
|
|
|
AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_);
|
|
AuthorizationSet keystore_auths =
|
|
SecLevelAuthorizations(key_characteristics_, SecurityLevel::KEYSTORE);
|
|
|
|
// First usage of RSA key should work.
|
|
SignMessage(message, params);
|
|
|
|
if (hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U)) {
|
|
// Usage count limit tag is enforced by hardware. After using the key, the key blob
|
|
// must be invalidated from secure storage (such as RPMB partition).
|
|
EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::SIGN, params));
|
|
} else {
|
|
// Usage count limit tag is enforced by keystore, keymint does nothing.
|
|
EXPECT_TRUE(keystore_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U));
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, params));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* UsageCountLimitTest.TestLimitUseRsa
|
|
*
|
|
* Verifies that the usage count limit tag > 1 works correctly with RSA keys.
|
|
*/
|
|
TEST_P(UsageCountLimitTest, TestLimitUseRsa) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Test not applicable to StrongBox device";
|
|
}
|
|
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaSigningKey(1024, 65537)
|
|
.NoDigestOrPadding()
|
|
.Authorization(TAG_USAGE_COUNT_LIMIT, 3)
|
|
.SetDefaultValidity()));
|
|
|
|
// Check the usage count limit tag appears in the authorizations.
|
|
AuthorizationSet auths;
|
|
for (auto& entry : key_characteristics_) {
|
|
auths.push_back(AuthorizationSet(entry.authorizations));
|
|
}
|
|
EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U))
|
|
<< "key usage count limit " << 3U << " missing";
|
|
|
|
string message = "1234567890123456";
|
|
auto params = AuthorizationSetBuilder().NoDigestOrPadding();
|
|
|
|
AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_);
|
|
AuthorizationSet keystore_auths =
|
|
SecLevelAuthorizations(key_characteristics_, SecurityLevel::KEYSTORE);
|
|
|
|
SignMessage(message, params);
|
|
SignMessage(message, params);
|
|
SignMessage(message, params);
|
|
|
|
if (hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U)) {
|
|
// Usage count limit tag is enforced by hardware. After using the key, the key blob
|
|
// must be invalidated from secure storage (such as RPMB partition).
|
|
EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::SIGN, params));
|
|
} else {
|
|
// Usage count limit tag is enforced by keystore, keymint does nothing.
|
|
EXPECT_TRUE(keystore_auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U));
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, params));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* UsageCountLimitTest.TestSingleUseKeyAndRollbackResistance
|
|
*
|
|
* Verifies that when rollback resistance is supported by the KeyMint implementation with
|
|
* the secure hardware, the single use key with usage count limit tag = 1 must also be enforced
|
|
* in hardware.
|
|
*/
|
|
TEST_P(UsageCountLimitTest, TestSingleUseKeyAndRollbackResistance) {
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
GTEST_SKIP() << "Test not applicable to StrongBox device";
|
|
}
|
|
|
|
auto error = GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_ROLLBACK_RESISTANCE)
|
|
.SetDefaultValidity());
|
|
if (error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE) {
|
|
GTEST_SKIP() << "Rollback resistance not supported";
|
|
}
|
|
|
|
// Rollback resistance is supported by KeyMint, verify it is enforced in hardware.
|
|
ASSERT_EQ(ErrorCode::OK, error);
|
|
AuthorizationSet hardwareEnforced(SecLevelAuthorizations());
|
|
ASSERT_TRUE(hardwareEnforced.Contains(TAG_ROLLBACK_RESISTANCE));
|
|
ASSERT_EQ(ErrorCode::OK, DeleteKey());
|
|
|
|
// The KeyMint should also enforce single use key in hardware when it supports rollback
|
|
// resistance.
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaSigningKey(1024, 65537)
|
|
.NoDigestOrPadding()
|
|
.Authorization(TAG_USAGE_COUNT_LIMIT, 1)
|
|
.SetDefaultValidity()));
|
|
|
|
// Check the usage count limit tag appears in the hardware authorizations.
|
|
AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_);
|
|
EXPECT_TRUE(hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U))
|
|
<< "key usage count limit " << 1U << " missing";
|
|
|
|
string message = "1234567890123456";
|
|
auto params = AuthorizationSetBuilder().NoDigestOrPadding();
|
|
|
|
// First usage of RSA key should work.
|
|
SignMessage(message, params);
|
|
|
|
// Usage count limit tag is enforced by hardware. After using the key, the key blob
|
|
// must be invalidated from secure storage (such as RPMB partition).
|
|
EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::SIGN, params));
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(UsageCountLimitTest);
|
|
|
|
typedef KeyMintAidlTestBase GetHardwareInfoTest;
|
|
|
|
TEST_P(GetHardwareInfoTest, GetHardwareInfo) {
|
|
// Retrieving hardware info should give the same result each time.
|
|
KeyMintHardwareInfo info;
|
|
ASSERT_TRUE(keyMint().getHardwareInfo(&info).isOk());
|
|
KeyMintHardwareInfo info2;
|
|
ASSERT_TRUE(keyMint().getHardwareInfo(&info2).isOk());
|
|
EXPECT_EQ(info, info2);
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(GetHardwareInfoTest);
|
|
|
|
typedef KeyMintAidlTestBase AddEntropyTest;
|
|
|
|
/*
|
|
* AddEntropyTest.AddEntropy
|
|
*
|
|
* Verifies that the addRngEntropy method doesn't blow up. There's no way to test that entropy
|
|
* is actually added.
|
|
*/
|
|
TEST_P(AddEntropyTest, AddEntropy) {
|
|
string data = "foo";
|
|
EXPECT_TRUE(keyMint().addRngEntropy(vector<uint8_t>(data.begin(), data.end())).isOk());
|
|
}
|
|
|
|
/*
|
|
* AddEntropyTest.AddEmptyEntropy
|
|
*
|
|
* Verifies that the addRngEntropy method doesn't blow up when given an empty buffer.
|
|
*/
|
|
TEST_P(AddEntropyTest, AddEmptyEntropy) {
|
|
EXPECT_TRUE(keyMint().addRngEntropy(AidlBuf()).isOk());
|
|
}
|
|
|
|
/*
|
|
* AddEntropyTest.AddLargeEntropy
|
|
*
|
|
* Verifies that the addRngEntropy method doesn't blow up when given a largish amount of data.
|
|
*/
|
|
TEST_P(AddEntropyTest, AddLargeEntropy) {
|
|
EXPECT_TRUE(keyMint().addRngEntropy(AidlBuf(string(2 * 1024, 'a'))).isOk());
|
|
}
|
|
|
|
/*
|
|
* AddEntropyTest.AddTooLargeEntropy
|
|
*
|
|
* Verifies that the addRngEntropy method rejects more than 2KiB of data.
|
|
*/
|
|
TEST_P(AddEntropyTest, AddTooLargeEntropy) {
|
|
ErrorCode rc = GetReturnErrorCode(keyMint().addRngEntropy(AidlBuf(string(2 * 1024 + 1, 'a'))));
|
|
EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, rc);
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(AddEntropyTest);
|
|
|
|
typedef KeyMintAidlTestBase KeyDeletionTest;
|
|
|
|
/**
|
|
* KeyDeletionTest.DeleteKey
|
|
*
|
|
* This test checks that if rollback protection is implemented, DeleteKey invalidates a formerly
|
|
* valid key blob.
|
|
*/
|
|
TEST_P(KeyDeletionTest, DeleteKey) {
|
|
auto error = GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_ROLLBACK_RESISTANCE)
|
|
.SetDefaultValidity());
|
|
if (error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE) {
|
|
GTEST_SKIP() << "Rollback resistance not supported";
|
|
}
|
|
|
|
// Delete must work if rollback protection is implemented
|
|
ASSERT_EQ(ErrorCode::OK, error);
|
|
AuthorizationSet hardwareEnforced(SecLevelAuthorizations());
|
|
ASSERT_TRUE(hardwareEnforced.Contains(TAG_ROLLBACK_RESISTANCE));
|
|
|
|
ASSERT_EQ(ErrorCode::OK, DeleteKey(true /* keep key blob */));
|
|
|
|
string message = "12345678901234567890123456789012";
|
|
AuthorizationSet begin_out_params;
|
|
EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
|
|
Begin(KeyPurpose::SIGN, key_blob_,
|
|
AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE),
|
|
&begin_out_params));
|
|
AbortIfNeeded();
|
|
key_blob_ = AidlBuf();
|
|
}
|
|
|
|
/**
|
|
* KeyDeletionTest.DeleteInvalidKey
|
|
*
|
|
* This test checks that the HAL excepts invalid key blobs..
|
|
*/
|
|
TEST_P(KeyDeletionTest, DeleteInvalidKey) {
|
|
// Generate key just to check if rollback protection is implemented
|
|
auto error = GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_ROLLBACK_RESISTANCE)
|
|
.SetDefaultValidity());
|
|
if (error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE) {
|
|
GTEST_SKIP() << "Rollback resistance not supported";
|
|
}
|
|
|
|
// Delete must work if rollback protection is implemented
|
|
ASSERT_EQ(ErrorCode::OK, error);
|
|
AuthorizationSet enforced(SecLevelAuthorizations());
|
|
ASSERT_TRUE(enforced.Contains(TAG_ROLLBACK_RESISTANCE));
|
|
|
|
// Delete the key we don't care about the result at this point.
|
|
DeleteKey();
|
|
|
|
// Now create an invalid key blob and delete it.
|
|
key_blob_ = AidlBuf("just some garbage data which is not a valid key blob");
|
|
|
|
ASSERT_EQ(ErrorCode::OK, DeleteKey());
|
|
}
|
|
|
|
/**
|
|
* KeyDeletionTest.DeleteAllKeys
|
|
*
|
|
* This test is disarmed by default. To arm it use --arm_deleteAllKeys.
|
|
*
|
|
* BEWARE: This test has serious side effects. All user keys will be lost! This includes
|
|
* FBE/FDE encryption keys, which means that the device will not even boot until after the
|
|
* device has been wiped manually (e.g., fastboot flashall -w), and new FBE/FDE keys have
|
|
* been provisioned. Use this test only on dedicated testing devices that have no valuable
|
|
* credentials stored in Keystore/Keymint.
|
|
*/
|
|
TEST_P(KeyDeletionTest, DeleteAllKeys) {
|
|
if (!arm_deleteAllKeys) {
|
|
GTEST_SKIP() << "Option --arm_deleteAllKeys not set";
|
|
return;
|
|
}
|
|
auto error = GenerateKey(AuthorizationSetBuilder()
|
|
.RsaSigningKey(2048, 65537)
|
|
.Digest(Digest::NONE)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_ROLLBACK_RESISTANCE)
|
|
.SetDefaultValidity());
|
|
if (error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE) {
|
|
GTEST_SKIP() << "Rollback resistance not supported";
|
|
}
|
|
|
|
// Delete must work if rollback protection is implemented
|
|
ASSERT_EQ(ErrorCode::OK, error);
|
|
AuthorizationSet hardwareEnforced(SecLevelAuthorizations());
|
|
ASSERT_TRUE(hardwareEnforced.Contains(TAG_ROLLBACK_RESISTANCE));
|
|
|
|
ASSERT_EQ(ErrorCode::OK, DeleteAllKeys());
|
|
|
|
string message = "12345678901234567890123456789012";
|
|
AuthorizationSet begin_out_params;
|
|
|
|
EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
|
|
Begin(KeyPurpose::SIGN, key_blob_,
|
|
AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE),
|
|
&begin_out_params));
|
|
AbortIfNeeded();
|
|
key_blob_ = AidlBuf();
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(KeyDeletionTest);
|
|
|
|
typedef KeyMintAidlTestBase KeyUpgradeTest;
|
|
|
|
/**
|
|
* KeyUpgradeTest.UpgradeInvalidKey
|
|
*
|
|
* This test checks that the HAL excepts invalid key blobs..
|
|
*/
|
|
TEST_P(KeyUpgradeTest, UpgradeInvalidKey) {
|
|
AidlBuf key_blob = AidlBuf("just some garbage data which is not a valid key blob");
|
|
|
|
std::vector<uint8_t> new_blob;
|
|
Status result = keymint_->upgradeKey(key_blob,
|
|
AuthorizationSetBuilder()
|
|
.Authorization(TAG_APPLICATION_ID, "clientid")
|
|
.Authorization(TAG_APPLICATION_DATA, "appdata")
|
|
.vector_data(),
|
|
&new_blob);
|
|
ASSERT_EQ(ErrorCode::INVALID_KEY_BLOB, GetReturnErrorCode(result));
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(KeyUpgradeTest);
|
|
|
|
using UpgradeKeyTest = KeyMintAidlTestBase;
|
|
|
|
/*
|
|
* UpgradeKeyTest.UpgradeKey
|
|
*
|
|
* Verifies that calling upgrade key on an up-to-date key works (i.e. does nothing).
|
|
*/
|
|
TEST_P(UpgradeKeyTest, UpgradeKey) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.AesEncryptionKey(128)
|
|
.Padding(PaddingMode::NONE)
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)));
|
|
|
|
auto result = UpgradeKey(key_blob_);
|
|
|
|
// Key doesn't need upgrading. Should get okay, but no new key blob.
|
|
EXPECT_EQ(result, std::make_pair(ErrorCode::OK, vector<uint8_t>()));
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(UpgradeKeyTest);
|
|
|
|
using ClearOperationsTest = KeyMintAidlTestBase;
|
|
|
|
/*
|
|
* ClearSlotsTest.TooManyOperations
|
|
*
|
|
* Verifies that TOO_MANY_OPERATIONS is returned after the max number of
|
|
* operations are started without being finished or aborted. Also verifies
|
|
* that aborting the operations clears the operations.
|
|
*
|
|
*/
|
|
TEST_P(ClearOperationsTest, TooManyOperations) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.RsaEncryptionKey(2048, 65537)
|
|
.Padding(PaddingMode::NONE)
|
|
.SetDefaultValidity()));
|
|
|
|
auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
|
|
constexpr size_t max_operations = 100; // set to arbituary large number
|
|
std::shared_ptr<IKeyMintOperation> op_handles[max_operations];
|
|
AuthorizationSet out_params;
|
|
ErrorCode result;
|
|
size_t i;
|
|
|
|
for (i = 0; i < max_operations; i++) {
|
|
result = Begin(KeyPurpose::DECRYPT, key_blob_, params, &out_params, op_handles[i]);
|
|
if (ErrorCode::OK != result) {
|
|
break;
|
|
}
|
|
}
|
|
EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, result);
|
|
// Try again just in case there's a weird overflow bug
|
|
EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS,
|
|
Begin(KeyPurpose::DECRYPT, key_blob_, params, &out_params));
|
|
for (size_t j = 0; j < i; j++) {
|
|
EXPECT_EQ(ErrorCode::OK, Abort(op_handles[j]))
|
|
<< "Aboort failed for i = " << j << std::endl;
|
|
}
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, key_blob_, params, &out_params));
|
|
AbortIfNeeded();
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(ClearOperationsTest);
|
|
|
|
typedef KeyMintAidlTestBase TransportLimitTest;
|
|
|
|
/*
|
|
* TransportLimitTest.LargeFinishInput
|
|
*
|
|
* Verifies that passing input data to finish succeeds as expected.
|
|
*/
|
|
TEST_P(TransportLimitTest, LargeFinishInput) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.AesEncryptionKey(128)
|
|
.BlockMode(BlockMode::ECB)
|
|
.Padding(PaddingMode::NONE)));
|
|
|
|
for (int msg_size = 8 /* 256 bytes */; msg_size <= 11 /* 2 KiB */; msg_size++) {
|
|
SCOPED_TRACE(testing::Message() << "msg_size = " << msg_size);
|
|
auto cipher_params =
|
|
AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
|
|
|
|
AuthorizationSet out_params;
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, cipher_params, &out_params));
|
|
|
|
string plain_message = std::string(1 << msg_size, 'x');
|
|
string encrypted_message;
|
|
auto rc = Finish(plain_message, &encrypted_message);
|
|
|
|
EXPECT_EQ(ErrorCode::OK, rc);
|
|
EXPECT_EQ(plain_message.size(), encrypted_message.size())
|
|
<< "Encrypt finish returned OK, but did not consume all of the given input";
|
|
cipher_params.push_back(out_params);
|
|
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, cipher_params));
|
|
|
|
string decrypted_message;
|
|
rc = Finish(encrypted_message, &decrypted_message);
|
|
EXPECT_EQ(ErrorCode::OK, rc);
|
|
EXPECT_EQ(plain_message.size(), decrypted_message.size())
|
|
<< "Decrypt finish returned OK, did not consume all of the given input";
|
|
}
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(TransportLimitTest);
|
|
|
|
static int EcdhCurveToOpenSslCurveName(EcCurve curve) {
|
|
switch (curve) {
|
|
case EcCurve::P_224:
|
|
return NID_secp224r1;
|
|
case EcCurve::P_256:
|
|
return NID_X9_62_prime256v1;
|
|
case EcCurve::P_384:
|
|
return NID_secp384r1;
|
|
case EcCurve::P_521:
|
|
return NID_secp521r1;
|
|
case EcCurve::CURVE_25519:
|
|
return NID_X25519;
|
|
}
|
|
}
|
|
|
|
class KeyAgreementTest : public KeyMintAidlTestBase {
|
|
protected:
|
|
void GenerateLocalEcKey(EcCurve localCurve, EVP_PKEY_Ptr* localPrivKey,
|
|
std::vector<uint8_t>* localPublicKey) {
|
|
// Generate EC key locally (with access to private key material)
|
|
if (localCurve == EcCurve::CURVE_25519) {
|
|
uint8_t privKeyData[32];
|
|
uint8_t pubKeyData[32];
|
|
X25519_keypair(pubKeyData, privKeyData);
|
|
*localPrivKey = EVP_PKEY_Ptr(EVP_PKEY_new_raw_private_key(
|
|
EVP_PKEY_X25519, nullptr, privKeyData, sizeof(privKeyData)));
|
|
} else {
|
|
auto ecKey = EC_KEY_Ptr(EC_KEY_new());
|
|
int curveName = EcdhCurveToOpenSslCurveName(localCurve);
|
|
auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(curveName));
|
|
ASSERT_NE(group, nullptr);
|
|
ASSERT_EQ(EC_KEY_set_group(ecKey.get(), group.get()), 1);
|
|
ASSERT_EQ(EC_KEY_generate_key(ecKey.get()), 1);
|
|
*localPrivKey = EVP_PKEY_Ptr(EVP_PKEY_new());
|
|
ASSERT_EQ(EVP_PKEY_set1_EC_KEY(localPrivKey->get(), ecKey.get()), 1);
|
|
}
|
|
|
|
// Get encoded form of the public part of the locally generated key...
|
|
unsigned char* p = nullptr;
|
|
int localPublicKeySize = i2d_PUBKEY(localPrivKey->get(), &p);
|
|
ASSERT_GT(localPublicKeySize, 0);
|
|
*localPublicKey = vector<uint8_t>(reinterpret_cast<const uint8_t*>(p),
|
|
reinterpret_cast<const uint8_t*>(p + localPublicKeySize));
|
|
OPENSSL_free(p);
|
|
}
|
|
|
|
void GenerateKeyMintEcKey(EcCurve curve, EVP_PKEY_Ptr* kmPubKey) {
|
|
vector<uint8_t> challenge = {0x41, 0x42};
|
|
auto builder = AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_EC_CURVE, curve)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
|
|
.Authorization(TAG_ALGORITHM, Algorithm::EC)
|
|
.Authorization(TAG_ATTESTATION_APPLICATION_ID, {0x61, 0x62})
|
|
.Authorization(TAG_ATTESTATION_CHALLENGE, challenge)
|
|
.SetDefaultValidity();
|
|
ErrorCode result = GenerateKey(builder);
|
|
|
|
if (SecLevel() == SecurityLevel::STRONGBOX) {
|
|
if (result == ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED) {
|
|
result = GenerateKeyWithSelfSignedAttestKey(
|
|
AuthorizationSetBuilder()
|
|
.EcdsaKey(EcCurve::P_256)
|
|
.AttestKey()
|
|
.SetDefaultValidity(), /* attest key params */
|
|
builder, &key_blob_, &key_characteristics_, &cert_chain_);
|
|
}
|
|
}
|
|
ASSERT_EQ(ErrorCode::OK, result) << "Failed to generate key";
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate));
|
|
ASSERT_NE(kmKeyCert, nullptr);
|
|
// Check that keyAgreement (bit 4) is set in KeyUsage
|
|
EXPECT_TRUE((X509_get_key_usage(kmKeyCert.get()) & X509v3_KU_KEY_AGREEMENT) != 0);
|
|
*kmPubKey = EVP_PKEY_Ptr(X509_get_pubkey(kmKeyCert.get()));
|
|
ASSERT_NE(*kmPubKey, nullptr);
|
|
if (dump_Attestations) {
|
|
for (size_t n = 0; n < cert_chain_.size(); n++) {
|
|
std::cout << bin2hex(cert_chain_[n].encodedCertificate) << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CheckAgreement(EVP_PKEY_Ptr kmPubKey, EVP_PKEY_Ptr localPrivKey,
|
|
const std::vector<uint8_t>& localPublicKey) {
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
|
|
string ZabFromKeyMintStr;
|
|
ASSERT_EQ(ErrorCode::OK,
|
|
Finish(string(localPublicKey.begin(), localPublicKey.end()), &ZabFromKeyMintStr));
|
|
vector<uint8_t> ZabFromKeyMint(ZabFromKeyMintStr.begin(), ZabFromKeyMintStr.end());
|
|
vector<uint8_t> ZabFromTest;
|
|
|
|
if (EVP_PKEY_id(kmPubKey.get()) == EVP_PKEY_X25519) {
|
|
size_t kmPubKeySize = 32;
|
|
uint8_t kmPubKeyData[32];
|
|
ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize));
|
|
ASSERT_EQ(kmPubKeySize, 32);
|
|
|
|
uint8_t localPrivKeyData[32];
|
|
size_t localPrivKeySize = 32;
|
|
ASSERT_EQ(1, EVP_PKEY_get_raw_private_key(localPrivKey.get(), localPrivKeyData,
|
|
&localPrivKeySize));
|
|
ASSERT_EQ(localPrivKeySize, 32);
|
|
|
|
uint8_t sharedKey[32];
|
|
ASSERT_EQ(1, X25519(sharedKey, localPrivKeyData, kmPubKeyData));
|
|
ZabFromTest = std::vector<uint8_t>(sharedKey, sharedKey + 32);
|
|
} else {
|
|
// Perform local ECDH between the two keys so we can check if we get the same Zab..
|
|
auto ctx = EVP_PKEY_CTX_Ptr(EVP_PKEY_CTX_new(localPrivKey.get(), nullptr));
|
|
ASSERT_NE(ctx, nullptr);
|
|
ASSERT_EQ(EVP_PKEY_derive_init(ctx.get()), 1);
|
|
ASSERT_EQ(EVP_PKEY_derive_set_peer(ctx.get(), kmPubKey.get()), 1);
|
|
size_t ZabFromTestLen = 0;
|
|
ASSERT_EQ(EVP_PKEY_derive(ctx.get(), nullptr, &ZabFromTestLen), 1);
|
|
ZabFromTest.resize(ZabFromTestLen);
|
|
ASSERT_EQ(EVP_PKEY_derive(ctx.get(), ZabFromTest.data(), &ZabFromTestLen), 1);
|
|
}
|
|
EXPECT_EQ(ZabFromKeyMint, ZabFromTest);
|
|
}
|
|
};
|
|
|
|
/*
|
|
* KeyAgreementTest.Ecdh
|
|
*
|
|
* Verifies that ECDH works for all required curves
|
|
*/
|
|
TEST_P(KeyAgreementTest, Ecdh) {
|
|
// Because it's possible to use this API with keys on different curves, we
|
|
// check all N^2 combinations where N is the number of supported
|
|
// curves.
|
|
//
|
|
// This is not a big deal as N is 4 so we only do 16 runs. If we end up with a
|
|
// lot more curves we can be smart about things and just pick |otherCurve| so
|
|
// it's not |curve| and that way we end up with only 2*N runs
|
|
//
|
|
for (auto curve : ValidCurves()) {
|
|
for (auto localCurve : ValidCurves()) {
|
|
SCOPED_TRACE(testing::Message()
|
|
<< "local-curve-" << localCurve << "-keymint-curve-" << curve);
|
|
|
|
// Generate EC key locally (with access to private key material)
|
|
EVP_PKEY_Ptr localPrivKey;
|
|
vector<uint8_t> localPublicKey;
|
|
GenerateLocalEcKey(localCurve, &localPrivKey, &localPublicKey);
|
|
|
|
// Generate EC key in KeyMint (only access to public key material)
|
|
EVP_PKEY_Ptr kmPubKey;
|
|
GenerateKeyMintEcKey(curve, &kmPubKey);
|
|
|
|
// Now that we have the two keys, we ask KeyMint to perform ECDH...
|
|
if (curve != localCurve) {
|
|
// If the keys are using different curves KeyMint should fail with
|
|
// ErrorCode:INVALID_ARGUMENT. Check that.
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
|
|
string ZabFromKeyMintStr;
|
|
EXPECT_EQ(ErrorCode::INVALID_ARGUMENT,
|
|
Finish(string(localPublicKey.begin(), localPublicKey.end()),
|
|
&ZabFromKeyMintStr));
|
|
|
|
} else {
|
|
// Otherwise if the keys are using the same curve, it should work.
|
|
CheckAgreement(std::move(kmPubKey), std::move(localPrivKey), localPublicKey);
|
|
}
|
|
|
|
CheckedDeleteKey();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* KeyAgreementTest.EcdhCurve25519
|
|
*
|
|
* Verifies that ECDH works for curve25519. This is also covered by the general
|
|
* KeyAgreementTest.Ecdh case, but is pulled out separately here because this curve was added after
|
|
* KeyMint 1.0.
|
|
*/
|
|
TEST_P(KeyAgreementTest, EcdhCurve25519) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
// Generate EC key in KeyMint (only access to public key material)
|
|
EcCurve curve = EcCurve::CURVE_25519;
|
|
EVP_PKEY_Ptr kmPubKey = nullptr;
|
|
GenerateKeyMintEcKey(curve, &kmPubKey);
|
|
|
|
// Generate EC key on same curve locally (with access to private key material).
|
|
EVP_PKEY_Ptr privKey;
|
|
vector<uint8_t> encodedPublicKey;
|
|
GenerateLocalEcKey(curve, &privKey, &encodedPublicKey);
|
|
|
|
// Agree on a key between local and KeyMint and check it.
|
|
CheckAgreement(std::move(kmPubKey), std::move(privKey), encodedPublicKey);
|
|
|
|
CheckedDeleteKey();
|
|
}
|
|
|
|
/*
|
|
* KeyAgreementTest.EcdhCurve25519Imported
|
|
*
|
|
* Verifies that ECDH works for an imported curve25519 key.
|
|
*/
|
|
TEST_P(KeyAgreementTest, EcdhCurve25519Imported) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
// Import x25519 key into KeyMint.
|
|
EcCurve curve = EcCurve::CURVE_25519;
|
|
ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.EcdsaKey(EcCurve::CURVE_25519)
|
|
.Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, x25519_pkcs8_key));
|
|
ASSERT_GT(cert_chain_.size(), 0);
|
|
X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate));
|
|
ASSERT_NE(kmKeyCert, nullptr);
|
|
EVP_PKEY_Ptr kmPubKey(X509_get_pubkey(kmKeyCert.get()));
|
|
ASSERT_NE(kmPubKey.get(), nullptr);
|
|
|
|
// Expect the import to emit corresponding public key data.
|
|
size_t kmPubKeySize = 32;
|
|
uint8_t kmPubKeyData[32];
|
|
ASSERT_EQ(1, EVP_PKEY_get_raw_public_key(kmPubKey.get(), kmPubKeyData, &kmPubKeySize));
|
|
ASSERT_EQ(kmPubKeySize, 32);
|
|
EXPECT_EQ(bin2hex(std::vector<uint8_t>(kmPubKeyData, kmPubKeyData + 32)),
|
|
bin2hex(std::vector<uint8_t>(x25519_pubkey.begin(), x25519_pubkey.end())));
|
|
|
|
// Generate EC key on same curve locally (with access to private key material).
|
|
EVP_PKEY_Ptr privKey;
|
|
vector<uint8_t> encodedPublicKey;
|
|
GenerateLocalEcKey(curve, &privKey, &encodedPublicKey);
|
|
|
|
// Agree on a key between local and KeyMint and check it.
|
|
CheckAgreement(std::move(kmPubKey), std::move(privKey), encodedPublicKey);
|
|
|
|
CheckedDeleteKey();
|
|
}
|
|
|
|
/*
|
|
* KeyAgreementTest.EcdhCurve25519InvalidSize
|
|
*
|
|
* Verifies that ECDH fails for curve25519 if the wrong size of public key is provided.
|
|
*/
|
|
TEST_P(KeyAgreementTest, EcdhCurve25519InvalidSize) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
// Generate EC key in KeyMint (only access to public key material)
|
|
EcCurve curve = EcCurve::CURVE_25519;
|
|
EVP_PKEY_Ptr kmPubKey = nullptr;
|
|
GenerateKeyMintEcKey(curve, &kmPubKey);
|
|
|
|
// Generate EC key on same curve locally (with access to private key material).
|
|
EVP_PKEY_Ptr privKey;
|
|
vector<uint8_t> encodedPublicKey;
|
|
GenerateLocalEcKey(curve, &privKey, &encodedPublicKey);
|
|
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
|
|
string ZabFromKeyMintStr;
|
|
// Send in an incomplete public key.
|
|
ASSERT_NE(ErrorCode::OK, Finish(string(encodedPublicKey.begin(), encodedPublicKey.end() - 1),
|
|
&ZabFromKeyMintStr));
|
|
|
|
CheckedDeleteKey();
|
|
}
|
|
|
|
/*
|
|
* KeyAgreementTest.EcdhCurve25519Mismatch
|
|
*
|
|
* Verifies that ECDH fails between curve25519 and other curves.
|
|
*/
|
|
TEST_P(KeyAgreementTest, EcdhCurve25519Mismatch) {
|
|
if (!Curve25519Supported()) {
|
|
GTEST_SKIP() << "Test not applicable to device that is not expected to support curve 25519";
|
|
}
|
|
|
|
// Generate EC key in KeyMint (only access to public key material)
|
|
EcCurve curve = EcCurve::CURVE_25519;
|
|
EVP_PKEY_Ptr kmPubKey = nullptr;
|
|
GenerateKeyMintEcKey(curve, &kmPubKey);
|
|
|
|
for (auto localCurve : ValidCurves()) {
|
|
SCOPED_TRACE(testing::Message() << "local-curve-" << localCurve);
|
|
if (localCurve == curve) {
|
|
continue;
|
|
}
|
|
// Generate EC key on a different curve locally (with access to private key material).
|
|
EVP_PKEY_Ptr privKey;
|
|
vector<uint8_t> encodedPublicKey;
|
|
GenerateLocalEcKey(localCurve, &privKey, &encodedPublicKey);
|
|
|
|
ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
|
|
string ZabFromKeyMintStr;
|
|
EXPECT_EQ(ErrorCode::INVALID_ARGUMENT,
|
|
Finish(string(encodedPublicKey.begin(), encodedPublicKey.end()),
|
|
&ZabFromKeyMintStr));
|
|
}
|
|
|
|
CheckedDeleteKey();
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(KeyAgreementTest);
|
|
|
|
using DestroyAttestationIdsTest = KeyMintAidlTestBase;
|
|
|
|
// This is a problematic test, as it can render the device under test permanently unusable.
|
|
// Re-enable and run at your own risk.
|
|
TEST_P(DestroyAttestationIdsTest, DISABLED_DestroyTest) {
|
|
auto result = DestroyAttestationIds();
|
|
EXPECT_TRUE(result == ErrorCode::OK || result == ErrorCode::UNIMPLEMENTED);
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(DestroyAttestationIdsTest);
|
|
|
|
using EarlyBootKeyTest = KeyMintAidlTestBase;
|
|
|
|
/*
|
|
* EarlyBootKeyTest.CreateEarlyBootKeys
|
|
*
|
|
* Verifies that creating early boot keys succeeds, even at a later stage (after boot).
|
|
*/
|
|
TEST_P(EarlyBootKeyTest, CreateEarlyBootKeys) {
|
|
// Early boot keys can be created after early boot.
|
|
auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
|
|
CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK);
|
|
|
|
for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) {
|
|
ASSERT_GT(keyData.blob.size(), 0U);
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(keyData.characteristics);
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_EARLY_BOOT_ONLY)) << crypto_params;
|
|
}
|
|
CheckedDeleteKey(&aesKeyData.blob);
|
|
CheckedDeleteKey(&hmacKeyData.blob);
|
|
CheckedDeleteKey(&rsaKeyData.blob);
|
|
CheckedDeleteKey(&ecdsaKeyData.blob);
|
|
}
|
|
|
|
/*
|
|
* EarlyBootKeyTest.CreateAttestedEarlyBootKey
|
|
*
|
|
* Verifies that creating an early boot key with attestation succeeds.
|
|
*/
|
|
TEST_P(EarlyBootKeyTest, CreateAttestedEarlyBootKey) {
|
|
auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] = CreateTestKeys(
|
|
TAG_EARLY_BOOT_ONLY, ErrorCode::OK, [](AuthorizationSetBuilder* builder) {
|
|
builder->AttestationChallenge("challenge");
|
|
builder->AttestationApplicationId("app_id");
|
|
});
|
|
|
|
for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) {
|
|
// Strongbox may not support factory attestation. Key creation might fail with
|
|
// ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED
|
|
if (SecLevel() == SecurityLevel::STRONGBOX && keyData.blob.size() == 0U) {
|
|
continue;
|
|
}
|
|
ASSERT_GT(keyData.blob.size(), 0U);
|
|
AuthorizationSet crypto_params = SecLevelAuthorizations(keyData.characteristics);
|
|
EXPECT_TRUE(crypto_params.Contains(TAG_EARLY_BOOT_ONLY)) << crypto_params;
|
|
}
|
|
CheckedDeleteKey(&aesKeyData.blob);
|
|
CheckedDeleteKey(&hmacKeyData.blob);
|
|
if (rsaKeyData.blob.size() != 0U) {
|
|
CheckedDeleteKey(&rsaKeyData.blob);
|
|
}
|
|
if (ecdsaKeyData.blob.size() != 0U) {
|
|
CheckedDeleteKey(&ecdsaKeyData.blob);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EarlyBootKeyTest.UseEarlyBootKeyFailure
|
|
*
|
|
* Verifies that using early boot keys at a later stage fails.
|
|
*/
|
|
TEST_P(EarlyBootKeyTest, UseEarlyBootKeyFailure) {
|
|
ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_EARLY_BOOT_ONLY)
|
|
.HmacKey(128)
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MIN_MAC_LENGTH, 256)));
|
|
AuthorizationSet output_params;
|
|
EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, Begin(KeyPurpose::SIGN, key_blob_,
|
|
AuthorizationSetBuilder()
|
|
.Digest(Digest::SHA_2_256)
|
|
.Authorization(TAG_MAC_LENGTH, 256),
|
|
&output_params));
|
|
}
|
|
|
|
/*
|
|
* EarlyBootKeyTest.ImportEarlyBootKeyFailure
|
|
*
|
|
* Verifies that importing early boot keys fails.
|
|
*/
|
|
TEST_P(EarlyBootKeyTest, ImportEarlyBootKeyFailure) {
|
|
ASSERT_EQ(ErrorCode::EARLY_BOOT_ENDED, ImportKey(AuthorizationSetBuilder()
|
|
.Authorization(TAG_NO_AUTH_REQUIRED)
|
|
.Authorization(TAG_EARLY_BOOT_ONLY)
|
|
.EcdsaSigningKey(EcCurve::P_256)
|
|
.Digest(Digest::SHA_2_256)
|
|
.SetDefaultValidity(),
|
|
KeyFormat::PKCS8, ec_256_key));
|
|
}
|
|
|
|
// This is a more comprehensive test, but it can only be run on a machine which is still in early
|
|
// boot stage, which no proper Android device is by the time we can run VTS. To use this,
|
|
// un-disable it and modify vold to remove the call to earlyBootEnded(). Running the test will end
|
|
// early boot, so you'll have to reboot between runs.
|
|
TEST_P(EarlyBootKeyTest, DISABLED_FullTest) {
|
|
auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
|
|
CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK);
|
|
// TAG_EARLY_BOOT_ONLY should be in hw-enforced.
|
|
EXPECT_TRUE(HwEnforcedAuthorizations(aesKeyData.characteristics).Contains(TAG_EARLY_BOOT_ONLY));
|
|
EXPECT_TRUE(
|
|
HwEnforcedAuthorizations(hmacKeyData.characteristics).Contains(TAG_EARLY_BOOT_ONLY));
|
|
EXPECT_TRUE(HwEnforcedAuthorizations(rsaKeyData.characteristics).Contains(TAG_EARLY_BOOT_ONLY));
|
|
EXPECT_TRUE(
|
|
HwEnforcedAuthorizations(ecdsaKeyData.characteristics).Contains(TAG_EARLY_BOOT_ONLY));
|
|
|
|
// Should be able to use keys, since early boot has not ended
|
|
EXPECT_EQ(ErrorCode::OK, UseAesKey(aesKeyData.blob));
|
|
EXPECT_EQ(ErrorCode::OK, UseHmacKey(hmacKeyData.blob));
|
|
EXPECT_EQ(ErrorCode::OK, UseRsaKey(rsaKeyData.blob));
|
|
EXPECT_EQ(ErrorCode::OK, UseEcdsaKey(ecdsaKeyData.blob));
|
|
|
|
// End early boot
|
|
ErrorCode earlyBootResult = GetReturnErrorCode(keyMint().earlyBootEnded());
|
|
EXPECT_EQ(earlyBootResult, ErrorCode::OK);
|
|
|
|
// Should not be able to use already-created keys.
|
|
EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, UseAesKey(aesKeyData.blob));
|
|
EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, UseHmacKey(hmacKeyData.blob));
|
|
EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, UseRsaKey(rsaKeyData.blob));
|
|
EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, UseEcdsaKey(ecdsaKeyData.blob));
|
|
|
|
CheckedDeleteKey(&aesKeyData.blob);
|
|
CheckedDeleteKey(&hmacKeyData.blob);
|
|
CheckedDeleteKey(&rsaKeyData.blob);
|
|
CheckedDeleteKey(&ecdsaKeyData.blob);
|
|
|
|
// Should not be able to create new keys
|
|
std::tie(aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData) =
|
|
CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::EARLY_BOOT_ENDED);
|
|
|
|
CheckedDeleteKey(&aesKeyData.blob);
|
|
CheckedDeleteKey(&hmacKeyData.blob);
|
|
CheckedDeleteKey(&rsaKeyData.blob);
|
|
CheckedDeleteKey(&ecdsaKeyData.blob);
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(EarlyBootKeyTest);
|
|
|
|
using UnlockedDeviceRequiredTest = KeyMintAidlTestBase;
|
|
|
|
// This may be a problematic test. It can't be run repeatedly without unlocking the device in
|
|
// between runs... and on most test devices there are no enrolled credentials so it can't be
|
|
// unlocked at all, meaning the only way to get the test to pass again on a properly-functioning
|
|
// device is to reboot it. For that reason, this is disabled by default. It can be used as part of
|
|
// a manual test process, which includes unlocking between runs, which is why it's included here.
|
|
// Well, that and the fact that it's the only test we can do without also making calls into the
|
|
// Gatekeeper HAL. We haven't written any cross-HAL tests, and don't know what all of the
|
|
// implications might be, so that may or may not be a solution.
|
|
TEST_P(UnlockedDeviceRequiredTest, DISABLED_KeysBecomeUnusable) {
|
|
auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
|
|
CreateTestKeys(TAG_UNLOCKED_DEVICE_REQUIRED, ErrorCode::OK);
|
|
|
|
EXPECT_EQ(ErrorCode::OK, UseAesKey(aesKeyData.blob));
|
|
EXPECT_EQ(ErrorCode::OK, UseHmacKey(hmacKeyData.blob));
|
|
EXPECT_EQ(ErrorCode::OK, UseRsaKey(rsaKeyData.blob));
|
|
EXPECT_EQ(ErrorCode::OK, UseEcdsaKey(ecdsaKeyData.blob));
|
|
|
|
ErrorCode rc = GetReturnErrorCode(
|
|
keyMint().deviceLocked(false /* passwordOnly */, {} /* timestampToken */));
|
|
ASSERT_EQ(ErrorCode::OK, rc);
|
|
EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseAesKey(aesKeyData.blob));
|
|
EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseHmacKey(hmacKeyData.blob));
|
|
EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseRsaKey(rsaKeyData.blob));
|
|
EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseEcdsaKey(ecdsaKeyData.blob));
|
|
|
|
CheckedDeleteKey(&aesKeyData.blob);
|
|
CheckedDeleteKey(&hmacKeyData.blob);
|
|
CheckedDeleteKey(&rsaKeyData.blob);
|
|
CheckedDeleteKey(&ecdsaKeyData.blob);
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(UnlockedDeviceRequiredTest);
|
|
|
|
using VsrRequirementTest = KeyMintAidlTestBase;
|
|
|
|
TEST_P(VsrRequirementTest, Vsr13Test) {
|
|
int vsr_api_level = get_vsr_api_level();
|
|
if (vsr_api_level < __ANDROID_API_T__) {
|
|
GTEST_SKIP() << "Applies only to VSR API level 33, this device is: " << vsr_api_level;
|
|
}
|
|
EXPECT_GE(AidlVersion(), 2) << "VSR 13+ requires KeyMint version 2";
|
|
}
|
|
|
|
TEST_P(VsrRequirementTest, Vsr14Test) {
|
|
int vsr_api_level = get_vsr_api_level();
|
|
if (vsr_api_level < __ANDROID_API_U__) {
|
|
GTEST_SKIP() << "Applies only to VSR API level 34, this device is: " << vsr_api_level;
|
|
}
|
|
EXPECT_GE(AidlVersion(), 3) << "VSR 14+ requires KeyMint version 3";
|
|
}
|
|
|
|
INSTANTIATE_KEYMINT_AIDL_TEST(VsrRequirementTest);
|
|
|
|
} // namespace aidl::android::hardware::security::keymint::test
|
|
|
|
int main(int argc, char** argv) {
|
|
std::cout << "Testing ";
|
|
auto halInstances =
|
|
aidl::android::hardware::security::keymint::test::KeyMintAidlTestBase::build_params();
|
|
std::cout << "HAL instances:\n";
|
|
for (auto& entry : halInstances) {
|
|
std::cout << " " << entry << '\n';
|
|
}
|
|
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
for (int i = 1; i < argc; ++i) {
|
|
if (argv[i][0] == '-') {
|
|
if (std::string(argv[i]) == "--arm_deleteAllKeys") {
|
|
aidl::android::hardware::security::keymint::test::KeyMintAidlTestBase::
|
|
arm_deleteAllKeys = true;
|
|
}
|
|
if (std::string(argv[i]) == "--dump_attestations") {
|
|
aidl::android::hardware::security::keymint::test::KeyMintAidlTestBase::
|
|
dump_Attestations = true;
|
|
} else {
|
|
std::cout << "NOT dumping attestations" << std::endl;
|
|
}
|
|
if (std::string(argv[i]) == "--skip_boot_pl_check") {
|
|
// Allow checks of BOOT_PATCHLEVEL to be disabled, so that the tests can
|
|
// be run in emulated environments that don't have the normal bootloader
|
|
// interactions.
|
|
aidl::android::hardware::security::keymint::test::check_boot_pl = false;
|
|
}
|
|
if (std::string(argv[i]) == "--keyblob_dir") {
|
|
if (i + 1 >= argc) {
|
|
std::cerr << "Missing argument for --keyblob_dir\n";
|
|
return 1;
|
|
}
|
|
aidl::android::hardware::security::keymint::test::KeyMintAidlTestBase::keyblob_dir =
|
|
std::string(argv[i + 1]);
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
return RUN_ALL_TESTS();
|
|
}
|