pk11_x25519_unittest.cc (12937B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include <memory> 6 #include "nss.h" 7 8 #include "json_reader.h" 9 #include "nss_scoped_ptrs.h" 10 11 #include "cpputil.h" 12 #include "pk11_x25519_vectors.h" 13 #include "pk11_signature_test.h" 14 #include "pk11_keygen.h" 15 16 namespace nss_test { 17 18 // For test vectors. 19 struct Pkcs11X25519ImportParams { 20 const DataBuffer pkcs8_; 21 const DataBuffer spki_; 22 }; 23 24 static const Pkcs11X25519ImportParams kX25519Vectors[] = { 25 { 26 DataBuffer(kX25519Pkcs8_1, sizeof(kX25519Pkcs8_1)), 27 DataBuffer(kX25519Spki_1, sizeof(kX25519Spki_1)), 28 }, 29 }; 30 31 class Pkcs11X25519Test 32 : public ::testing::Test, 33 public ::testing::WithParamInterface<Pkcs11X25519ImportParams> { 34 protected: 35 ScopedSECKEYPrivateKey ImportPrivateKey(const DataBuffer& pkcs8) { 36 ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); 37 if (!slot) { 38 ADD_FAILURE() << "No slot"; 39 return nullptr; 40 } 41 42 SECItem pkcs8Item = {siBuffer, toUcharPtr(pkcs8.data()), 43 static_cast<unsigned int>(pkcs8.len())}; 44 45 SECKEYPrivateKey* key = nullptr; 46 SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( 47 slot.get(), &pkcs8Item, nullptr, nullptr, false, false, KU_ALL, &key, 48 nullptr); 49 50 if (rv != SECSuccess) { 51 return nullptr; 52 } 53 54 return ScopedSECKEYPrivateKey(key); 55 } 56 57 bool ExportPrivateKey(ScopedSECKEYPrivateKey* key, DataBuffer& pkcs8) { 58 ScopedSECItem pkcs8Item(PK11_ExportDERPrivateKeyInfo(key->get(), nullptr)); 59 if (!pkcs8Item) { 60 return false; 61 } 62 pkcs8.Assign(pkcs8Item->data, pkcs8Item->len); 63 return true; 64 } 65 66 ScopedSECKEYPublicKey ImportPublicKey(const DataBuffer& spki) { 67 SECItem spkiItem = {siBuffer, toUcharPtr(spki.data()), 68 static_cast<unsigned int>(spki.len())}; 69 70 ScopedCERTSubjectPublicKeyInfo certSpki( 71 SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem)); 72 if (!certSpki) { 73 return nullptr; 74 } 75 76 return ScopedSECKEYPublicKey(SECKEY_ExtractPublicKey(certSpki.get())); 77 } 78 79 bool CheckAlgIsX25519(SECItem* algorithm) { 80 SECOidTag tag = SECOID_FindOIDTag(algorithm); 81 if (tag != SEC_OID_X25519) { 82 return false; 83 } 84 85 return true; 86 } 87 }; 88 89 TEST_P(Pkcs11X25519Test, ImportExportPkcs8) { 90 DataBuffer exported; 91 ScopedSECKEYPrivateKey key = ImportPrivateKey(GetParam().pkcs8_); 92 EXPECT_EQ(key.get()->keyType, ecMontKey); 93 94 SECKEYPrivateKeyInfo* pkInfo = PK11_ExportPrivKeyInfo(key.get(), nullptr); 95 ASSERT_TRUE(pkInfo); 96 /* empty parameters for X25519*/ 97 ASSERT_EQ(pkInfo->algorithm.parameters.len, (unsigned int)0); 98 ASSERT_TRUE(CheckAlgIsX25519(&pkInfo->algorithm.algorithm)); 99 ExportPrivateKey(&key, exported); 100 EXPECT_EQ(GetParam().pkcs8_, exported); 101 102 SECKEY_DestroyPrivateKeyInfo(pkInfo, PR_TRUE); 103 } 104 105 TEST_P(Pkcs11X25519Test, ImportExportSpki) { 106 DataBuffer exported; 107 ScopedSECKEYPublicKey key = ImportPublicKey(GetParam().spki_); 108 109 ScopedSECItem spki(SECKEY_EncodeDERSubjectPublicKeyInfo(key.get())); 110 ASSERT_TRUE(spki); 111 ASSERT_EQ(spki->len, GetParam().spki_.len()); 112 ASSERT_EQ(0, memcmp(spki->data, GetParam().spki_.data(), spki->len)); 113 } 114 115 TEST_P(Pkcs11X25519Test, ImportConvertToPublicExport) { 116 ScopedSECKEYPrivateKey privKey(ImportPrivateKey(GetParam().pkcs8_)); 117 ASSERT_TRUE(privKey); 118 119 ScopedSECKEYPublicKey pubKey(SECKEY_ConvertToPublicKey(privKey.get())); 120 ASSERT_TRUE(pubKey); 121 122 ScopedSECItem der_spki(SECKEY_EncodeDERSubjectPublicKeyInfo(pubKey.get())); 123 ASSERT_TRUE(der_spki); 124 ASSERT_EQ(der_spki->len, GetParam().spki_.len()); 125 ASSERT_EQ(0, memcmp(der_spki->data, GetParam().spki_.data(), der_spki->len)); 126 } 127 128 TEST_P(Pkcs11X25519Test, GenImportExport) { 129 Pkcs11KeyPairGenerator generator(CKM_EC_MONTGOMERY_KEY_PAIR_GEN); 130 ScopedSECKEYPrivateKey priv; 131 ScopedSECKEYPublicKey pub; 132 133 generator.GenerateKey(&priv, &pub, false); 134 ASSERT_TRUE(priv); 135 ASSERT_TRUE(pub); 136 137 DataBuffer exportedPrivateKey, twiceExportedPrKey; 138 ExportPrivateKey(&priv, exportedPrivateKey); 139 ScopedSECKEYPrivateKey privExportedImported = 140 ImportPrivateKey(exportedPrivateKey); 141 ExportPrivateKey(&privExportedImported, twiceExportedPrKey); 142 EXPECT_EQ(exportedPrivateKey, twiceExportedPrKey); 143 144 ScopedSECItem spki(SECKEY_EncodeDERSubjectPublicKeyInfo(pub.get())); 145 ASSERT_TRUE(spki); 146 147 DataBuffer publicKeyDb(spki.get()->data, spki.get()->len); 148 ScopedSECKEYPublicKey exportedImportedPublicKey = 149 ImportPublicKey(publicKeyDb); 150 ScopedSECItem spkiTwice( 151 SECKEY_EncodeDERSubjectPublicKeyInfo(exportedImportedPublicKey.get())); 152 ASSERT_TRUE(spkiTwice); 153 154 ASSERT_EQ(spkiTwice->len, spki->len); 155 ASSERT_EQ(0, memcmp(spki->data, spkiTwice->data, spki->len)); 156 } 157 158 INSTANTIATE_TEST_SUITE_P(Pkcs11X25519Test, Pkcs11X25519Test, 159 ::testing::ValuesIn(kX25519Vectors)); 160 161 /* 162 RFC 8410 describes several scenarios with the potential errors during 163 exporting/encoding of the keys. See: 164 https://www.rfc-editor.org/rfc/rfc8410#appendix-A. */ 165 166 /* 167 PKCS8 (private) X25519 key explanation: 168 NB: NSS does not currently support PKCS8 keys with the public key as an 169 attribute. 170 171 const uint8_t kX25519Pkcs8_1[] = { 172 0x30, 0x2e, // where 0x2e is the length of the buffer 173 0x02, 0x01, 0x00, // EC key version 174 id-X25519 OBJECT IDENTIFIER ::= { 1 3 101 110 } 175 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, // algorithm identifier 176 0x04, 0x22, // Outer octet string of length 0x22 177 0x04, 0x20, // Inner octet string of length 0x20 178 179 0xc8, 0x83, 0x8e, 0x76, 0xd0, 0x57, 0xdf, 0xb7, // Raw key 180 0xd8, 0xc9, 0x5a, 0x69, 0xe1, 0x38, 0x16, 0x0a, 181 0xdd, 0x63, 0x73, 0xfd, 0x71, 0xa4, 0xd2, 0x76, 182 0xbb, 0x56, 0xe3, 0xa8, 0x1b, 0x64, 0xff, 0x61}; 183 184 */ 185 186 /* Private Key ASN.1 encoding errors */ 187 TEST_F(Pkcs11X25519Test, ImportPkcs8BitStringInsteadOfOctetString) { 188 const uint8_t kX25519BitString[] = { 189 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 190 0x04, 0x22, 0x03, 0x20, 0xc8, 0x83, 0x8e, 0x76, 0xd0, 0x57, 0xdf, 0xb7, 191 0xd8, 0xc9, 0x5a, 0x69, 0xe1, 0x38, 0x16, 0x0a, 0xdd, 0x63, 0x73, 0xfd, 192 0x71, 0xa4, 0xd2, 0x76, 0xbb, 0x56, 0xe3, 0xa8, 0x1b, 0x64, 0xff, 0x61}; 193 194 DataBuffer privateKeyPkcs8( 195 DataBuffer(kX25519BitString, sizeof(kX25519BitString))); 196 ScopedSECKEYPrivateKey key = ImportPrivateKey(privateKeyPkcs8); 197 ASSERT_FALSE(key); 198 } 199 200 TEST_F(Pkcs11X25519Test, ImportPkcs8WrongLen) { 201 /* The pkcs8 encoding has a wrong length (0x2d instead of 0x2e) */ 202 const uint8_t x25519_wrongLen[] = { 203 0x30, 0x2d, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 204 0x04, 0x22, 0x04, 0x20, 0xc8, 0x83, 0x8e, 0x76, 0xd0, 0x57, 0xdf, 0xb7, 205 0xd8, 0xc9, 0x5a, 0x69, 0xe1, 0x38, 0x16, 0x0a, 0xdd, 0x63, 0x73, 0xfd, 206 0x71, 0xa4, 0xd2, 0x76, 0xbb, 0x56, 0xe3, 0xa8, 0x1b, 0x64, 0xff, 0x61}; 207 208 DataBuffer privateKeyPkcs8( 209 DataBuffer(x25519_wrongLen, sizeof(x25519_wrongLen))); 210 ScopedSECKEYPrivateKey key = ImportPrivateKey(privateKeyPkcs8); 211 ASSERT_FALSE(key); 212 } 213 214 /* Key encoding errors */ 215 TEST_F(Pkcs11X25519Test, ImportPkcs8NotSupportedOID) { 216 /* The modified oid corresponds to not-supported x448: 217 id-X448 OBJECT IDENTIFIER ::= { 1 3 101 111 }. */ 218 const uint8_t x25519_wrongOID[] = { 219 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6f, 220 0x04, 0x22, 0x04, 0x20, 0xc8, 0x83, 0x8e, 0x76, 0xd0, 0x57, 0xdf, 0xb7, 221 0xd8, 0xc9, 0x5a, 0x69, 0xe1, 0x38, 0x16, 0x0a, 0xdd, 0x63, 0x73, 0xfd, 222 0x71, 0xa4, 0xd2, 0x76, 0xbb, 0x56, 0xe3, 0xa8, 0x1b, 0x64, 0xff, 0x61}; 223 224 DataBuffer privateKeyPkcs8( 225 DataBuffer(x25519_wrongOID, sizeof(x25519_wrongOID))); 226 ScopedSECKEYPrivateKey key = ImportPrivateKey(privateKeyPkcs8); 227 ASSERT_FALSE(key); 228 } 229 230 TEST_F(Pkcs11X25519Test, ImportPkcs8ShortLenPrivateKey) { 231 /* We change the length of the private key from 0x20 to 0x1f. 232 Such way all the lengths will be decreased by one */ 233 const uint8_t x25519_shortPrivateKey[] = { 234 0x30, 0x2d, // the length is decreased by one 235 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 0x04, 236 0x21, // the length is decreased by one 237 0x04, 0x1f, // the length is decreased by one 238 0xc8, 0x83, 0x8e, 0x76, 0xd0, 0x57, 0xdf, 0xb7, 0xd8, 0xc9, 0x5a, 0x69, 239 0xe1, 0x38, 0x16, 0x0a, 0xdd, 0x63, 0x73, 0xfd, 0x71, 0xa4, 0xd2, 0x76, 240 // removed the last byte of the key 241 0xbb, 0x56, 0xe3, 0xa8, 0x1b, 0x64, 0xff}; 242 243 DataBuffer privateKeyPkcs8( 244 DataBuffer(x25519_shortPrivateKey, sizeof(x25519_shortPrivateKey))); 245 ScopedSECKEYPrivateKey key = ImportPrivateKey(privateKeyPkcs8); 246 ASSERT_TRUE(key); 247 } 248 249 /* We allow importing all-zero keys*/ 250 TEST_F(Pkcs11X25519Test, ImportPkcs8ZeroKey) { 251 const uint8_t x25519_ZeroKey[] = { 252 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 253 0x04, 0x22, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 254 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 255 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 256 257 DataBuffer privateKeyPkcs8( 258 DataBuffer(x25519_ZeroKey, sizeof(x25519_ZeroKey))); 259 ScopedSECKEYPrivateKey key = ImportPrivateKey(privateKeyPkcs8); 260 ASSERT_TRUE(key); 261 } 262 263 TEST_P(Pkcs11X25519Test, KeyGeneration) { 264 Pkcs11KeyPairGenerator generator(CKM_EC_MONTGOMERY_KEY_PAIR_GEN); 265 ScopedSECKEYPrivateKey priv; 266 ScopedSECKEYPublicKey pub; 267 268 generator.GenerateKey(&priv, &pub, false); 269 ASSERT_TRUE(priv); 270 ASSERT_TRUE(pub); 271 272 SECKEYPrivateKeyInfo* pkInfo = PK11_ExportPrivKeyInfo(priv.get(), nullptr); 273 ASSERT_TRUE(pkInfo); 274 /* 0x04 + len + 32 bytes the key */ 275 ASSERT_EQ(pkInfo->privateKey.len, (unsigned int)34); 276 /* empty parameters for X25519*/ 277 ASSERT_EQ(pkInfo->algorithm.parameters.len, (unsigned int)0); 278 ASSERT_TRUE(CheckAlgIsX25519(&pkInfo->algorithm.algorithm)); 279 280 ScopedCERTSubjectPublicKeyInfo spki( 281 SECKEY_CreateSubjectPublicKeyInfo(pub.get())); 282 ASSERT_TRUE(CheckAlgIsX25519(&spki->algorithm.algorithm)); 283 /* empty parameters for X25519*/ 284 ASSERT_EQ(spki->algorithm.parameters.len, (unsigned int)0); 285 286 SECKEY_DestroyPrivateKeyInfo(pkInfo, PR_TRUE); 287 } 288 289 /* Public Key ASN.1 encoding errors */ 290 TEST_F(Pkcs11X25519Test, ImportExportSpkiWrongLen) { 291 const uint8_t pk[] = {0x30, 0x2b, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 292 0x03, 0x21, 0x00, 0x1c, 0xf2, 0xb1, 0xe6, 0x02, 0x2e, 293 0xc5, 0x37, 0x37, 0x1e, 0xd7, 0xf5, 0x3e, 0x54, 0xfa, 294 0x11, 0x54, 0xd8, 0x3e, 0x98, 0xeb, 0x64, 0xea, 0x51, 295 0xfa, 0xe5, 0xb3, 0x30, 0x7c, 0xfe, 0x97, 0x06}; 296 297 DataBuffer publicKey(DataBuffer(pk, sizeof(pk))); 298 299 ScopedSECKEYPublicKey key = ImportPublicKey(publicKey); 300 ASSERT_FALSE(key); 301 } 302 303 /* Key encoding errors */ 304 TEST_F(Pkcs11X25519Test, ImportExportSpkiWrongOID) { 305 /*0x2b, 0x65, 0x6d instead of 0x2b, 0x65, 0x6e */ 306 const uint8_t pk[] = {0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6d, 307 0x03, 0x21, 0x00, 0x1c, 0xf2, 0xb1, 0xe6, 0x02, 0x2e, 308 0xc5, 0x37, 0x37, 0x1e, 0xd7, 0xf5, 0x3e, 0x54, 0xfa, 309 0x11, 0x54, 0xd8, 0x3e, 0x98, 0xeb, 0x64, 0xea, 0x51, 310 0xfa, 0xe5, 0xb3, 0x30, 0x7c, 0xfe, 0x97, 0x06}; 311 312 DataBuffer publicKey(DataBuffer(pk, sizeof(pk))); 313 ScopedSECKEYPublicKey key = ImportPublicKey(publicKey); 314 ASSERT_FALSE(key); 315 } 316 317 /* Key encoding errors */ 318 TEST_F(Pkcs11X25519Test, ImportExportSpkiWrongKeyID) { 319 /*0x2b, 0x65, 0x6d instead of 0x2b, 0x65, 0x6e */ 320 const uint8_t pk[] = {0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6d, 321 0x04, // 0x04 instead of 0x03 322 0x21, 0x00, 0x1c, 0xf2, 0xb1, 0xe6, 0x02, 0x2e, 0xc5, 323 0x37, 0x37, 0x1e, 0xd7, 0xf5, 0x3e, 0x54, 0xfa, 0x11, 324 0x54, 0xd8, 0x3e, 0x98, 0xeb, 0x64, 0xea, 0x51, 0xfa, 325 0xe5, 0xb3, 0x30, 0x7c, 0xfe, 0x97, 0x06}; 326 327 DataBuffer publicKey(DataBuffer(pk, sizeof(pk))); 328 ScopedSECKEYPublicKey key = ImportPublicKey(publicKey); 329 ASSERT_FALSE(key); 330 } 331 332 /* We allow to import all-zero keys. */ 333 TEST_F(Pkcs11X25519Test, ImportExportSpkiZeroKey) { 334 const uint8_t pk[] = {0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 335 0x03, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 336 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 337 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 338 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 339 340 DataBuffer publicKey(DataBuffer(pk, sizeof(pk))); 341 ScopedSECKEYPublicKey key = ImportPublicKey(publicKey); 342 ASSERT_TRUE(key); 343 } 344 345 } // namespace nss_test