// IsRegistered determines if the provided key handle is usable by this token. nsresult U2FSoftTokenManager::IsRegistered(nsTArray<uint8_t>& aKeyHandle, nsTArray<uint8_t>& aAppParam, bool& aResult) { nsNSSShutDownPreventionLock locker; if (NS_WARN_IF(isAlreadyShutDown())) { return NS_ERROR_FAILURE; } if (!mInitialized) { nsresult rv = Init(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } UniquePK11SlotInfo slot(PK11_GetInternalSlot()); MOZ_ASSERT(slot.get()); // Decode the key handle UniqueSECKEYPrivateKey privKey = PrivateKeyFromKeyHandle(slot, mWrappingKey, aKeyHandle.Elements(), aKeyHandle.Length(), aAppParam.Elements(), aAppParam.Length(), locker); aResult = privKey.get() != nullptr; return NS_OK; }
SECKEYPrivateKey* PrivateKeyFromPrivateKeyTemplate(SECItem* aObjID, CK_ATTRIBUTE* aTemplate, CK_ULONG aTemplateSize) { // Create a generic object with the contents of the key ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); if (!slot.get()) { return nullptr; } ScopedPK11GenericObject obj(PK11_CreateGenericObject(slot.get(), aTemplate, aTemplateSize, PR_FALSE)); if (!obj.get()) { return nullptr; } // Have NSS translate the object to a private key by inspection // and make a copy we can own ScopedSECKEYPrivateKey privKey(PK11_FindKeyByKeyID(slot.get(), aObjID, nullptr)); if (!privKey.get()) { return nullptr; } return SECKEY_CopyPrivateKey(privKey.get()); }
SECKEYPrivateKey* CryptoKey::PrivateKeyFromPkcs8(CryptoBuffer& aKeyData, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { SECKEYPrivateKey* privKey; ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return nullptr; } SECItem pkcs8Item = { siBuffer, nullptr, 0 }; if (!aKeyData.ToSECItem(arena, &pkcs8Item)) { return nullptr; } // Allow everything, we enforce usage ourselves unsigned int usage = KU_ALL; SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( slot.get(), &pkcs8Item, nullptr, nullptr, false, false, usage, &privKey, nullptr); if (rv == SECFailure) { return nullptr; } return privKey; }
static SECStatus tls13_AntiReplayKeyGen() { PRUint8 buf[32]; SECItem keyItem = { siBuffer, buf, sizeof(buf) }; PK11SlotInfo *slot; SECStatus rv; slot = PK11_GetInternalSlot(); if (!slot) { PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return SECFailure; } rv = PK11_GenerateRandomOnSlot(slot, buf, sizeof(buf)); if (rv != SECSuccess) { goto loser; } ssl_anti_replay.key = PK11_ImportSymKey(slot, CKM_NSS_HKDF_SHA256, PK11_OriginUnwrap, CKA_DERIVE, &keyItem, NULL); if (!ssl_anti_replay.key) { goto loser; } PK11_FreeSlot(slot); return SECSuccess; loser: PK11_FreeSlot(slot); return SECFailure; }
PRBool sslint_DamageTrafficSecret(PRFileDesc *fd, size_t offset) { unsigned char data[32] = {0}; PK11SymKey **keyPtr; PK11SlotInfo *slot = PK11_GetInternalSlot(); SECItem key_item = {siBuffer, data, sizeof(data)}; sslSocket *ss = ssl_FindSocket(fd); if (!ss) { return PR_FALSE; } if (!slot) { return PR_FALSE; } keyPtr = (PK11SymKey **)((char *)&ss->ssl3.hs + offset); if (!*keyPtr) { return PR_FALSE; } PK11_FreeSymKey(*keyPtr); *keyPtr = PK11_ImportSymKey(slot, CKM_NSS_HKDF_SHA256, PK11_OriginUnwrap, CKA_DERIVE, &key_item, NULL); PK11_FreeSlot(slot); if (!*keyPtr) { return PR_FALSE; } return PR_TRUE; }
NS_IMETHODIMP nsRandomGenerator::GenerateRandomBytes(uint32_t aLength, uint8_t** aBuffer) { NS_ENSURE_ARG_POINTER(aBuffer); *aBuffer = nullptr; nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } mozilla::UniquePK11SlotInfo slot(PK11_GetInternalSlot()); if (!slot) { return NS_ERROR_FAILURE; } auto buf = static_cast<uint8_t*>(moz_xmalloc(aLength)); if (!buf) { return NS_ERROR_OUT_OF_MEMORY; } SECStatus srv = PK11_GenerateRandomOnSlot(slot.get(), buf, aLength); if (srv != SECSuccess) { free(buf); return NS_ERROR_FAILURE; } *aBuffer = buf; return NS_OK; }
nsresult NSSToken::Init() { MOZ_ASSERT(!mInitialized); if (mInitialized) { return NS_OK; } nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } MutexAutoLock lock(mMutex); if (!EnsureNSSInitializedChromeOrContent()) { return NS_ERROR_FAILURE; } mSlot = PK11_GetInternalSlot(); if (!mSlot.get()) { return NS_ERROR_FAILURE; } mInitialized = true; return NS_OK; }
static void md5_hash(NSSItem *input, NSSItem *output) { NSSAlgorithmAndParameters *ap; PK11SlotInfo *internal = PK11_GetInternalSlot(); NSSToken *token = PK11Slot_GetNSSToken(internal); ap = NSSAlgorithmAndParameters_CreateMD5Digest(NULL); (void)nssToken_Digest(token, NULL, ap, input, output, NULL); PK11_FreeSlot(token->pk11slot); nss_ZFreeIf(ap); }
// Implement NSPR-based crypto algorithms static int nr_crypto_nss_random_bytes(UCHAR *buf, int len) { ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); if (!slot) return R_INTERNAL; SECStatus rv = PK11_GenerateRandomOnSlot(slot, buf, len); if (rv != SECSuccess) return R_INTERNAL; return 0; }
int main(int argc, char **argv) { if (NSS_NoDB_Init(NULL) != SECSuccess) { printf(" >>> NSS_NoDB_Init() failed.\n"); return 1; } if (argc < 3) { printf(" >>> I need a DER encoded file to read and have to know what to do with it [decode, private]!\n"); return 1; } PRFileDesc* file = PR_Open(argv[1], PR_RDONLY, 0); SECItem data = {0, NULL, 0}; if (SECU_ReadDERFromFile(&data, file, PR_FALSE, PR_FALSE) != SECSuccess) { printf(" >>> SECU_ReadDERFromFile() failed.\n"); return 1; } PR_Close(file); if (strcmp(argv[2], "decode") == 0) { CERTCertificate *cert = CERT_DecodeCertFromPackage((char*)data.data, data.len); if (cert){ printf(" >>> read cert!\n"); printf(" >>> SN: %s\n", cert->subjectName); printf(" >>> IN: %s\n", cert->issuerName); CERT_DestroyCertificate(cert); } else { printf(" >>> CERT_DecodeCertFromPackage failed.\n"); SECITEM_FreeItem(&data, PR_FALSE); return 1; } } if (argv[2] == "private") { PK11SlotInfo* slot = PK11_GetInternalSlot(); if (!slot) { printf(" >>> PK11_GetInternalSlot() failed.\n"); SECITEM_FreeItem(&data, PR_FALSE); return 1; } SECKEYPrivateKey* privKey; if (PK11_ImportDERPrivateKeyInfoAndReturnKey(slot, &data, NULL, NULL, PR_FALSE, PR_FALSE, KU_ALL, &privKey, NULL) != SECSuccess) { printf(" >>> PK11_ImportDERPrivateKeyInfoAndReturnKey() failed.\n"); SECITEM_FreeItem(&data, PR_FALSE); return 1; } } printf(" !!! Done.\n"); return 0; }
static SECStatus aes_encrypt_buf( const unsigned char *key, unsigned int keysize, const unsigned char *iv, unsigned int ivsize, unsigned char *output, unsigned int *outputlen, unsigned int maxoutputlen, const unsigned char *input, unsigned int inputlen, const unsigned char *aad, unsigned int aadlen, unsigned int tagsize) { SECStatus rv = SECFailure; SECItem key_item; PK11SlotInfo* slot = NULL; PK11SymKey *symKey = NULL; CK_GCM_PARAMS gcm_params; SECItem param; /* Import key into NSS. */ key_item.type = siBuffer; key_item.data = (unsigned char *) key; /* const cast */ key_item.len = keysize; slot = PK11_GetInternalSlot(); symKey = PK11_ImportSymKey(slot, CKM_AES_GCM, PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL); PK11_FreeSlot(slot); slot = NULL; if (!symKey) { fprintf(stderr, "PK11_ImportSymKey failed\n"); goto loser; } gcm_params.pIv = (unsigned char *) iv; /* const cast */ gcm_params.ulIvLen = ivsize; gcm_params.pAAD = (unsigned char *) aad; /* const cast */ gcm_params.ulAADLen = aadlen; gcm_params.ulTagBits = tagsize * 8; param.type = siBuffer; param.data = (unsigned char *) &gcm_params; param.len = sizeof(gcm_params); if (PK11_Encrypt(symKey, CKM_AES_GCM, ¶m, output, outputlen, maxoutputlen, input, inputlen) != SECSuccess) { fprintf(stderr, "PK11_Encrypt failed\n"); goto loser; } rv = SECSuccess; loser: if (symKey != NULL) { PK11_FreeSymKey(symKey); } return rv; }
/*********************************************************************** * * J S S _ P K 1 1 _ w r a p P K 1 1 T o k e n * * Create a PK11Token object from a PKCS #11 slot. * * slot is a pointer to a PKCS #11 slot, which must not be NULL. It will * be eaten by the wrapper, so you can't use it after you call this. * * Returns a new PK11Token object, or NULL if an exception was thrown. */ jobject JSS_PK11_wrapPK11Token(JNIEnv *env, PK11SlotInfo **slot) { jclass tokenClass; jmethodID constructor; jbyteArray byteArray; jobject Token=NULL; jboolean internal; jboolean keyStorage; PR_ASSERT(env!=NULL && slot!=NULL && *slot!=NULL); internal = (*slot == PK11_GetInternalSlot()); keyStorage = (*slot == PK11_GetInternalKeySlot()); byteArray = JSS_ptrToByteArray(env, (void*)*slot); /* * Lookup the class and constructor */ tokenClass = (*env)->FindClass(env, PK11TOKEN_CLASS_NAME); if(tokenClass == NULL) { ASSERT_OUTOFMEM(env); goto finish; } constructor = (*env)->GetMethodID( env, tokenClass, PK11TOKEN_CONSTRUCTOR_NAME, PK11TOKEN_CONSTRUCTOR_SIG); if(constructor == NULL) { ASSERT_OUTOFMEM(env); goto finish; } /* Call the constructor */ Token = (*env)->NewObject(env, tokenClass, constructor, byteArray, internal, keyStorage); finish: if(Token==NULL) { PK11_FreeSlot(*slot); } *slot = NULL; return Token; }
bool CryptoKey::PublicKeyValid(SECKEYPublicKey* aPubKey) { ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); if (!slot.get()) { return false; } // This assumes that NSS checks the validity of a public key when // it is imported into a PKCS#11 module, and returns CK_INVALID_HANDLE // if it is invalid. CK_OBJECT_HANDLE id = PK11_ImportPublicKey(slot, aPubKey, PR_FALSE); if (id == CK_INVALID_HANDLE) { return false; } SECStatus rv = PK11_DestroyObject(slot, id); return (rv == SECSuccess); }
SECKEYPrivateKey* CryptoKey::PrivateKeyFromPkcs8(CryptoBuffer& aKeyData, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { SECKEYPrivateKey* privKey; ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); ScopedSECItem pkcs8Item(aKeyData.ToSECItem()); if (!pkcs8Item) { return nullptr; } // Allow everything, we enforce usage ourselves unsigned int usage = KU_ALL; nsresult rv = MapSECStatus(PK11_ImportDERPrivateKeyInfoAndReturnKey( slot.get(), pkcs8Item.get(), nullptr, nullptr, false, false, usage, &privKey, nullptr)); if (NS_FAILED(rv)) { return nullptr; } return privKey; }
// NSS exports private EC keys without the CKA_EC_POINT attribute, i.e. the // public value. To properly export the private key to JWK or PKCS #8 we need // the public key data though and so we use this method to augment a private // key with data from the given public key. nsresult CryptoKey::AddPublicKeyData(SECKEYPublicKey* aPublicKey) { // This should be a private key. MOZ_ASSERT(GetKeyType() == PRIVATE); // There should be a private NSS key with type 'EC'. MOZ_ASSERT(mPrivateKey && mPrivateKey->keyType == ecKey); // The given public key should have the same key type. MOZ_ASSERT(aPublicKey->keyType == mPrivateKey->keyType); nsNSSShutDownPreventionLock locker; ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); if (!slot) { return NS_ERROR_DOM_OPERATION_ERR; } // Generate a random 160-bit object ID. ScopedSECItem objID(::SECITEM_AllocItem(nullptr, nullptr, 20)); SECStatus rv = PK11_GenerateRandomOnSlot(slot, objID->data, objID->len); if (rv != SECSuccess) { return NS_ERROR_DOM_OPERATION_ERR; } // Read EC params. ScopedSECItem params(::SECITEM_AllocItem(nullptr, nullptr, 0)); rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey, CKA_EC_PARAMS, params); if (rv != SECSuccess) { return NS_ERROR_DOM_OPERATION_ERR; } // Read private value. ScopedSECItem value(::SECITEM_AllocItem(nullptr, nullptr, 0)); rv = PK11_ReadRawAttribute(PK11_TypePrivKey, mPrivateKey, CKA_VALUE, value); if (rv != SECSuccess) { return NS_ERROR_DOM_OPERATION_ERR; } SECItem* point = &aPublicKey->u.ec.publicValue; CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY; CK_BBOOL falseValue = CK_FALSE; CK_KEY_TYPE ecValue = CKK_EC; CK_ATTRIBUTE keyTemplate[9] = { { CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) }, { CKA_KEY_TYPE, &ecValue, sizeof(ecValue) }, { CKA_TOKEN, &falseValue, sizeof(falseValue) }, { CKA_SENSITIVE, &falseValue, sizeof(falseValue) }, { CKA_PRIVATE, &falseValue, sizeof(falseValue) }, { CKA_ID, objID->data, objID->len }, { CKA_EC_PARAMS, params->data, params->len }, { CKA_EC_POINT, point->data, point->len }, { CKA_VALUE, value->data, value->len }, }; mPrivateKey = PrivateKeyFromPrivateKeyTemplate(objID, keyTemplate, PR_ARRAY_SIZE(keyTemplate)); NS_ENSURE_TRUE(mPrivateKey, NS_ERROR_DOM_OPERATION_ERR); return NS_OK; }
// A U2F Register operation causes a new key pair to be generated by the token. // The token then returns the public key of the key pair, and a handle to the // private key, which is a fancy way of saying "key wrapped private key", as // well as the generated attestation certificate and a signature using that // certificate's private key. // // The KeyHandleFromPrivateKey and PrivateKeyFromKeyHandle methods perform // the actual key wrap/unwrap operations. // // The format of the return registration data is as follows: // // Bytes Value // 1 0x05 // 65 public key // 1 key handle length // * key handle // ASN.1 attestation certificate // * attestation signature // nsresult U2FSoftTokenManager::Register(nsTArray<uint8_t>& aApplication, nsTArray<uint8_t>& aChallenge, /* out */ nsTArray<uint8_t>& aRegistration, /* out */ nsTArray<uint8_t>& aSignature) { nsNSSShutDownPreventionLock locker; if (NS_WARN_IF(isAlreadyShutDown())) { return NS_ERROR_NOT_AVAILABLE; } if (!mInitialized) { nsresult rv = Init(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } // We should already have a wrapping key MOZ_ASSERT(mWrappingKey); UniquePK11SlotInfo slot(PK11_GetInternalSlot()); MOZ_ASSERT(slot.get()); // Construct a one-time-use Attestation Certificate UniqueSECKEYPrivateKey attestPrivKey; UniqueCERTCertificate attestCert; nsresult rv = GetAttestationCertificate(slot, attestPrivKey, attestCert, locker); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; } MOZ_ASSERT(attestCert); MOZ_ASSERT(attestPrivKey); // Generate a new keypair; the private will be wrapped into a Key Handle UniqueSECKEYPrivateKey privKey; UniqueSECKEYPublicKey pubKey; rv = GenEcKeypair(slot, privKey, pubKey, locker); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; } // The key handle will be the result of keywrap(privKey, key=mWrappingKey) UniqueSECItem keyHandleItem = KeyHandleFromPrivateKey(slot, mWrappingKey, aApplication.Elements(), aApplication.Length(), privKey, locker); if (NS_WARN_IF(!keyHandleItem.get())) { return NS_ERROR_FAILURE; } // Sign the challenge using the Attestation privkey (from attestCert) mozilla::dom::CryptoBuffer signedDataBuf; if (NS_WARN_IF(!signedDataBuf.SetCapacity(1 + aApplication.Length() + aChallenge.Length() + keyHandleItem->len + kPublicKeyLen, mozilla::fallible))) { return NS_ERROR_OUT_OF_MEMORY; } // // It's OK to ignore the return values here because we're writing into // // pre-allocated space signedDataBuf.AppendElement(0x00, mozilla::fallible); signedDataBuf.AppendElements(aApplication, mozilla::fallible); signedDataBuf.AppendElements(aChallenge, mozilla::fallible); signedDataBuf.AppendSECItem(keyHandleItem.get()); signedDataBuf.AppendSECItem(pubKey->u.ec.publicValue); ScopedAutoSECItem signatureItem; SECStatus srv = SEC_SignData(&signatureItem, signedDataBuf.Elements(), signedDataBuf.Length(), attestPrivKey.get(), SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE); if (NS_WARN_IF(srv != SECSuccess)) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Signature failure: %d", PORT_GetError())); return NS_ERROR_FAILURE; } // Serialize the registration data mozilla::dom::CryptoBuffer registrationBuf; if (NS_WARN_IF(!registrationBuf.SetCapacity(1 + kPublicKeyLen + 1 + keyHandleItem->len + attestCert.get()->derCert.len + signatureItem.len, mozilla::fallible))) { return NS_ERROR_OUT_OF_MEMORY; } registrationBuf.AppendElement(0x05, mozilla::fallible); registrationBuf.AppendSECItem(pubKey->u.ec.publicValue); registrationBuf.AppendElement(keyHandleItem->len, mozilla::fallible); registrationBuf.AppendSECItem(keyHandleItem.get()); registrationBuf.AppendSECItem(attestCert.get()->derCert); registrationBuf.AppendSECItem(signatureItem); aRegistration = registrationBuf; return NS_OK; }
CK_RV PK11_MapPBEMechanismToCryptoMechanism(CK_MECHANISM_PTR pPBEMechanism, CK_MECHANISM_PTR pCryptoMechanism, SECItem *pbe_pwd, PRBool faulty3DES) { int iv_len = 0; CK_PBE_PARAMS_PTR pPBEparams; CK_RC2_CBC_PARAMS_PTR rc2_params; CK_ULONG rc2_key_len; if((pPBEMechanism == CK_NULL_PTR) || (pCryptoMechanism == CK_NULL_PTR)) { return CKR_HOST_MEMORY; } /* pkcs5 v2 cannot be supported by this interface. * use PK11_GetPBECryptoMechanism instead. */ if ((pPBEMechanism->mechanism == CKM_INVALID_MECHANISM) || (pPBEMechanism->mechanism == CKM_PKCS5_PBKD2)) { return CKR_MECHANISM_INVALID; } pPBEparams = (CK_PBE_PARAMS_PTR)pPBEMechanism->pParameter; iv_len = PK11_GetIVLength(pPBEMechanism->mechanism); if (iv_len) { if (pk11_isAllZero(pPBEparams->pInitVector,iv_len)) { SECItem param; PK11SymKey *symKey; PK11SlotInfo *intSlot = PK11_GetInternalSlot(); if (intSlot == NULL) { return CKR_DEVICE_ERROR; } param.data = pPBEMechanism->pParameter; param.len = pPBEMechanism->ulParameterLen; symKey = PK11_RawPBEKeyGen(intSlot, pPBEMechanism->mechanism, ¶m, pbe_pwd, faulty3DES, NULL); PK11_FreeSlot(intSlot); if (symKey== NULL) { return CKR_DEVICE_ERROR; /* sigh */ } PK11_FreeSymKey(symKey); } } switch(pPBEMechanism->mechanism) { case CKM_PBE_MD2_DES_CBC: case CKM_PBE_MD5_DES_CBC: case CKM_NETSCAPE_PBE_SHA1_DES_CBC: pCryptoMechanism->mechanism = CKM_DES_CBC; goto have_crypto_mechanism; case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC: case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC: case CKM_PBE_SHA1_DES3_EDE_CBC: case CKM_PBE_SHA1_DES2_EDE_CBC: pCryptoMechanism->mechanism = CKM_DES3_CBC; have_crypto_mechanism: pCryptoMechanism->pParameter = PORT_Alloc(iv_len); pCryptoMechanism->ulParameterLen = (CK_ULONG)iv_len; if(pCryptoMechanism->pParameter == NULL) { return CKR_HOST_MEMORY; } PORT_Memcpy((unsigned char *)(pCryptoMechanism->pParameter), (unsigned char *)(pPBEparams->pInitVector), iv_len); break; case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4: case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4: case CKM_PBE_SHA1_RC4_40: case CKM_PBE_SHA1_RC4_128: pCryptoMechanism->mechanism = CKM_RC4; pCryptoMechanism->ulParameterLen = 0; pCryptoMechanism->pParameter = CK_NULL_PTR; break; case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC: case CKM_PBE_SHA1_RC2_40_CBC: rc2_key_len = 40; goto have_key_len; case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC: rc2_key_len = 128; have_key_len: pCryptoMechanism->mechanism = CKM_RC2_CBC; pCryptoMechanism->ulParameterLen = (CK_ULONG) sizeof(CK_RC2_CBC_PARAMS); pCryptoMechanism->pParameter = (CK_RC2_CBC_PARAMS_PTR) PORT_ZAlloc(sizeof(CK_RC2_CBC_PARAMS)); if(pCryptoMechanism->pParameter == NULL) { return CKR_HOST_MEMORY; } rc2_params = (CK_RC2_CBC_PARAMS_PTR)pCryptoMechanism->pParameter; PORT_Memcpy((unsigned char *)rc2_params->iv, (unsigned char *)pPBEparams->pInitVector, iv_len); rc2_params->ulEffectiveBits = rc2_key_len; break; default: return CKR_MECHANISM_INVALID; } return CKR_OK; }
UtlBoolean SmimeBody::nssSmimeDecrypt(const char* derPkcs12, int derPkcs12Length, const char* pkcs12Password, UtlBoolean dataIsInBase64Format, const char* dataToDecrypt, int dataToDecryptLength, UtlString& decryptedData) { UtlBoolean decryptSucceeded = FALSE; decryptedData.remove(0); #ifdef ENABLE_NSS_SMIME Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SmimeBody::nssSmimeDecrypt not implemented"); ////// BEGIN WARNING: THIS CODE HAS NOT BEEN TESTED AT ALL /////// // allocate a temporaty slot in the database PK11SlotInfo *slot = PK11_GetInternalKeySlot(); PRBool swapUnicode = PR_FALSE; SEC_PKCS12DecoderContext *p12Decoder = NULL; // Need to put the pkcs12 password into a SECItem SECItem passwordItem; passwordItem.data = (unsigned char*) pkcs12Password; passwordItem.len = strlen(pkcs12Password); SECItem uniPasswordItem; uniPasswordItem.data = NULL; uniPasswordItem.len = 0; #ifdef IS_LITTLE_ENDIAN swapUnicode = PR_TRUE; #endif // Allocate a temporary internal slot slot = PK11_GetInternalSlot(); if(slot == NULL) { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "unable to use slot in NSS dataqbase for S/MIME decryption"); } else { // Do UNICODE conversion of password based upon the platform // (not sure this is even neccessary in our application). I do not // know how we would get unicode passwords if(0) //P12U_UnicodeConversion(NULL, &uniPasswordItem, passwordItem, PR_TRUE, // swapUnicode) != SECSuccess) { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "NSS Unicode conversion failed for PKCS12 object for S/MIME decryption"); } else { // Initialze the decoder for the PKCS12 container for the private key p12Decoder = SEC_PKCS12DecoderStart(&passwordItem, slot, NULL, NULL, NULL, NULL, NULL, NULL); if(!p12Decoder) { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "failed to initialize PKCS12 decoder to extract private key for S/MIME decryption"); } else { // Add the PKCS12 data to the decoder if(SEC_PKCS12DecoderUpdate(p12Decoder, (unsigned char *) derPkcs12, derPkcs12Length) != SECSuccess || // Validate the decoded PKCS12 SEC_PKCS12DecoderVerify(p12Decoder) != SECSuccess) { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "unable to decrypt PKCS12 for S/MIME decryption. Perhaps invalid PKCS12 or PKCS12 password"); } else { // Import the private key and certificate from the // decoded PKCS12 into the database if(SEC_PKCS12DecoderImportBags(p12Decoder) != SECSuccess) { Os::Logger::instance().log(FAC_SIP, PRI_ERR, "failed to import private key and certificate into NSS database"); } else { // Put the S/MIME data in a SECItem SECItem dataToDecodeItem; dataToDecodeItem.data = (unsigned char *) dataToDecrypt; dataToDecodeItem.len = dataToDecryptLength; if(dataIsInBase64Format) { // TODO: // Use some NSS util. to convert base64 to binary Os::Logger::instance().log(FAC_SIP, PRI_ERR, "NSS decrypt of base64 S/MIME message not implemented"); } else { // Decode the S/MIME blob NSSCMSMessage *cmsMessage = NSS_CMSMessage_CreateFromDER(&dataToDecodeItem, nssOutToUtlString, &decryptedData, NULL, NULL, NULL, NULL); if(cmsMessage && decryptedData.length() > 0) { decryptSucceeded = TRUE; } // TODO: // Remove the temporary private key from the // database using the slot handle } } } } } } // Clean up if(p12Decoder) { SEC_PKCS12DecoderFinish(p12Decoder); } if(uniPasswordItem.data) { SECITEM_ZfreeItem(&uniPasswordItem, PR_FALSE); } ////// END WARNING ///// #else Os::Logger::instance().log(FAC_SIP, PRI_ERR, "SmimeBody::nssSmimeDecrypt invoked with ENABLE_NSS_SMIME not defined"); #endif return(decryptSucceeded); }
RefPtr<DtlsIdentity> DtlsIdentity::Generate() { UniquePK11SlotInfo slot(PK11_GetInternalSlot()); if (!slot) { return nullptr; } uint8_t random_name[16]; SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), random_name, sizeof(random_name)); if (rv != SECSuccess) return nullptr; std::string name; char chunk[3]; for (size_t i = 0; i < sizeof(random_name); ++i) { SprintfLiteral(chunk, "%.2x", random_name[i]); name += chunk; } std::string subject_name_string = "CN=" + name; UniqueCERTName subject_name(CERT_AsciiToName(subject_name_string.c_str())); if (!subject_name) { return nullptr; } unsigned char paramBuf[12]; // OIDs are small SECItem ecdsaParams = { siBuffer, paramBuf, sizeof(paramBuf) }; SECOidData* oidData = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); if (!oidData || (oidData->oid.len > (sizeof(paramBuf) - 2))) { return nullptr; } ecdsaParams.data[0] = SEC_ASN1_OBJECT_ID; ecdsaParams.data[1] = oidData->oid.len; memcpy(ecdsaParams.data + 2, oidData->oid.data, oidData->oid.len); ecdsaParams.len = oidData->oid.len + 2; SECKEYPublicKey *pubkey; UniqueSECKEYPrivateKey private_key( PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &ecdsaParams, &pubkey, PR_FALSE, PR_TRUE, nullptr)); if (private_key == nullptr) return nullptr; UniqueSECKEYPublicKey public_key(pubkey); pubkey = nullptr; UniqueCERTSubjectPublicKeyInfo spki( SECKEY_CreateSubjectPublicKeyInfo(public_key.get())); if (!spki) { return nullptr; } UniqueCERTCertificateRequest certreq( CERT_CreateCertificateRequest(subject_name.get(), spki.get(), nullptr)); if (!certreq) { return nullptr; } // From 1 day before todayto 30 days after. // This is a sort of arbitrary range designed to be valid // now with some slack in case the other side expects // some before expiry. // // Note: explicit casts necessary to avoid // warning C4307: '*' : integral constant overflow static const PRTime oneDay = PRTime(PR_USEC_PER_SEC) * PRTime(60) // sec * PRTime(60) // min * PRTime(24); // hours PRTime now = PR_Now(); PRTime notBefore = now - oneDay; PRTime notAfter = now + (PRTime(30) * oneDay); UniqueCERTValidity validity(CERT_CreateValidity(notBefore, notAfter)); if (!validity) { return nullptr; } unsigned long serial; // Note: This serial in principle could collide, but it's unlikely rv = PK11_GenerateRandomOnSlot(slot.get(), reinterpret_cast<unsigned char *>(&serial), sizeof(serial)); if (rv != SECSuccess) { return nullptr; } UniqueCERTCertificate certificate( CERT_CreateCertificate(serial, subject_name.get(), validity.get(), certreq.get())); if (!certificate) { return nullptr; } PLArenaPool *arena = certificate->arena; rv = SECOID_SetAlgorithmID(arena, &certificate->signature, SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, 0); if (rv != SECSuccess) return nullptr; // Set version to X509v3. *(certificate->version.data) = SEC_CERTIFICATE_VERSION_3; certificate->version.len = 1; SECItem innerDER; innerDER.len = 0; innerDER.data = nullptr; if (!SEC_ASN1EncodeItem(arena, &innerDER, certificate.get(), SEC_ASN1_GET(CERT_CertificateTemplate))) { return nullptr; } SECItem *signedCert = PORT_ArenaZNew(arena, SECItem); if (!signedCert) { return nullptr; } rv = SEC_DerSignData(arena, signedCert, innerDER.data, innerDER.len, private_key.get(), SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE); if (rv != SECSuccess) { return nullptr; } certificate->derCert = *signedCert; RefPtr<DtlsIdentity> identity = new DtlsIdentity(Move(private_key), Move(certificate), ssl_kea_ecdh); return identity.forget(); }
SECKEYPrivateKey* CryptoKey::PrivateKeyFromJwk(const JsonWebKey& aJwk, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { if (!aJwk.mKty.WasPassed() || !aJwk.mKty.Value().EqualsLiteral(JWK_TYPE_RSA)) { return nullptr; } // Verify that all of the required parameters are present CryptoBuffer n, e, d, p, q, dp, dq, qi; if (!aJwk.mN.WasPassed() || NS_FAILED(n.FromJwkBase64(aJwk.mN.Value())) || !aJwk.mE.WasPassed() || NS_FAILED(e.FromJwkBase64(aJwk.mE.Value())) || !aJwk.mD.WasPassed() || NS_FAILED(d.FromJwkBase64(aJwk.mD.Value())) || !aJwk.mP.WasPassed() || NS_FAILED(p.FromJwkBase64(aJwk.mP.Value())) || !aJwk.mQ.WasPassed() || NS_FAILED(q.FromJwkBase64(aJwk.mQ.Value())) || !aJwk.mDp.WasPassed() || NS_FAILED(dp.FromJwkBase64(aJwk.mDp.Value())) || !aJwk.mDq.WasPassed() || NS_FAILED(dq.FromJwkBase64(aJwk.mDq.Value())) || !aJwk.mQi.WasPassed() || NS_FAILED(qi.FromJwkBase64(aJwk.mQi.Value()))) { return nullptr; } // Compute the ID for this key // This is generated with a SHA-1 hash, so unlikely to collide ScopedSECItem nItem(n.ToSECItem()); ScopedSECItem objID(PK11_MakeIDFromPubKey(nItem.get())); if (!nItem.get() || !objID.get()) { return nullptr; } // Populate template from parameters CK_OBJECT_CLASS privateKeyValue = CKO_PRIVATE_KEY; CK_KEY_TYPE rsaValue = CKK_RSA; CK_BBOOL falseValue = CK_FALSE; CK_ATTRIBUTE keyTemplate[14] = { { CKA_CLASS, &privateKeyValue, sizeof(privateKeyValue) }, { CKA_KEY_TYPE, &rsaValue, sizeof(rsaValue) }, { CKA_TOKEN, &falseValue, sizeof(falseValue) }, { CKA_SENSITIVE, &falseValue, sizeof(falseValue) }, { CKA_PRIVATE, &falseValue, sizeof(falseValue) }, { CKA_ID, objID->data, objID->len }, { CKA_MODULUS, (void*) n.Elements(), n.Length() }, { CKA_PUBLIC_EXPONENT, (void*) e.Elements(), e.Length() }, { CKA_PRIVATE_EXPONENT, (void*) d.Elements(), d.Length() }, { CKA_PRIME_1, (void*) p.Elements(), p.Length() }, { CKA_PRIME_2, (void*) q.Elements(), q.Length() }, { CKA_EXPONENT_1, (void*) dp.Elements(), dp.Length() }, { CKA_EXPONENT_2, (void*) dq.Elements(), dq.Length() }, { CKA_COEFFICIENT, (void*) qi.Elements(), qi.Length() }, }; // Create a generic object with the contents of the key ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); if (!slot.get()) { return nullptr; } ScopedPK11GenericObject obj(PK11_CreateGenericObject(slot.get(), keyTemplate, PR_ARRAY_SIZE(keyTemplate), PR_FALSE)); if (!obj.get()) { return nullptr; } // Have NSS translate the object to a private key by inspection // and make a copy we can own ScopedSECKEYPrivateKey privKey(PK11_FindKeyByKeyID(slot.get(), objID.get(), nullptr)); if (!privKey.get()) { return nullptr; } return SECKEY_CopyPrivateKey(privKey.get()); }
SECStatus tls13_HkdfExtract(PK11SymKey *ikm1, PK11SymKey *ikm2in, SSLHashType baseHash, PK11SymKey **prkp) { CK_NSS_HKDFParams params; SECItem paramsi; SECStatus rv; SECItem *salt; PK11SymKey *prk; static const PRUint8 zeroKeyBuf[HASH_LENGTH_MAX]; PK11SymKey *zeroKey = NULL; PK11SlotInfo *slot = NULL; PK11SymKey *ikm2; params.bExtract = CK_TRUE; params.bExpand = CK_FALSE; params.pInfo = NULL; params.ulInfoLen = 0UL; if (ikm1) { /* TODO([email protected]): This violates the PKCS#11 key boundary * but is imposed on us by the present HKDF interface. */ rv = PK11_ExtractKeyValue(ikm1); if (rv != SECSuccess) return rv; salt = PK11_GetKeyData(ikm1); if (!salt) return SECFailure; params.pSalt = salt->data; params.ulSaltLen = salt->len; PORT_Assert(salt->len > 0); } else { /* Per documentation for CKM_NSS_HKDF_*: * * If the optional salt is given, it is used; otherwise, the salt is * set to a sequence of zeros equal in length to the HMAC output. */ params.pSalt = NULL; params.ulSaltLen = 0UL; } paramsi.data = (unsigned char *)¶ms; paramsi.len = sizeof(params); PORT_Assert(kTlsHkdfInfo[baseHash].pkcs11Mech); PORT_Assert(kTlsHkdfInfo[baseHash].hashSize); PORT_Assert(kTlsHkdfInfo[baseHash].hash == baseHash); /* A zero ikm2 is a key of hash-length 0s. */ if (!ikm2in) { SECItem zeroItem = { siBuffer, (unsigned char *)zeroKeyBuf, kTlsHkdfInfo[baseHash].hashSize }; slot = PK11_GetInternalSlot(); if (!slot) { return SECFailure; } zeroKey = PK11_ImportSymKey(slot, kTlsHkdfInfo[baseHash].pkcs11Mech, PK11_OriginUnwrap, CKA_DERIVE, &zeroItem, NULL); if (!zeroKey) return SECFailure; ikm2 = zeroKey; } else { ikm2 = ikm2in; } PORT_Assert(ikm2); PRINT_BUF(50, (NULL, "HKDF Extract: IKM1/Salt", params.pSalt, params.ulSaltLen)); PRINT_KEY(50, (NULL, "HKDF Extract: IKM2", ikm2)); prk = PK11_Derive(ikm2, kTlsHkdfInfo[baseHash].pkcs11Mech, ¶msi, kTlsHkdfInfo[baseHash].pkcs11Mech, CKA_DERIVE, kTlsHkdfInfo[baseHash].hashSize); if (zeroKey) PK11_FreeSymKey(zeroKey); if (slot) PK11_FreeSlot(slot); if (!prk) return SECFailure; PRINT_KEY(50, (NULL, "HKDF Extract", prk)); *prkp = prk; return SECSuccess; }
static SECStatus aes_decrypt_buf( const unsigned char *key, unsigned int keysize, const unsigned char *iv, unsigned int ivsize, unsigned char *output, unsigned int *outputlen, unsigned int maxoutputlen, const unsigned char *input, unsigned int inputlen, const unsigned char *aad, unsigned int aadlen, const unsigned char *tag, unsigned int tagsize) { SECStatus rv = SECFailure; unsigned char concatenated[11*16]; /* 1 to 11 blocks */ SECItem key_item; PK11SlotInfo *slot = NULL; PK11SymKey *symKey = NULL; CK_GCM_PARAMS gcm_params; SECItem param; if (inputlen + tagsize > sizeof(concatenated)) { fprintf(stderr, "aes_decrypt_buf: local buffer too small\n"); goto loser; } memcpy(concatenated, input, inputlen); memcpy(concatenated + inputlen, tag, tagsize); /* Import key into NSS. */ key_item.type = siBuffer; key_item.data = (unsigned char *) key; /* const cast */ key_item.len = keysize; slot = PK11_GetInternalSlot(); symKey = PK11_ImportSymKey(slot, CKM_AES_GCM, PK11_OriginUnwrap, CKA_DECRYPT, &key_item, NULL); PK11_FreeSlot(slot); slot = NULL; if (!symKey) { fprintf(stderr, "PK11_ImportSymKey failed\n"); goto loser; } gcm_params.pIv = (unsigned char *) iv; gcm_params.ulIvLen = ivsize; gcm_params.pAAD = (unsigned char *) aad; gcm_params.ulAADLen = aadlen; gcm_params.ulTagBits = tagsize * 8; param.type = siBuffer; param.data = (unsigned char *) &gcm_params; param.len = sizeof(gcm_params); if (PK11_Decrypt(symKey, CKM_AES_GCM, ¶m, output, outputlen, maxoutputlen, concatenated, inputlen + tagsize) != SECSuccess) { goto loser; } rv = SECSuccess; loser: if (symKey != NULL) { PK11_FreeSymKey(symKey); } return rv; }
// A U2F Sign operation creates a signature over the "param" arguments (plus // some other stuff) using the private key indicated in the key handle argument. // // The format of the signed data is as follows: // // 32 Application parameter // 1 User presence (0x01) // 4 Counter // 32 Challenge parameter // // The format of the signature data is as follows: // // 1 User presence // 4 Counter // * Signature // nsresult U2FSoftTokenManager::Sign(nsTArray<uint8_t>& aApplication, nsTArray<uint8_t>& aChallenge, nsTArray<uint8_t>& aKeyHandle, nsTArray<uint8_t>& aSignature) { nsNSSShutDownPreventionLock locker; if (NS_WARN_IF(isAlreadyShutDown())) { return NS_ERROR_NOT_AVAILABLE; } MOZ_ASSERT(mInitialized); if (NS_WARN_IF(!mInitialized)) { nsresult rv = Init(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } MOZ_ASSERT(mWrappingKey); UniquePK11SlotInfo slot(PK11_GetInternalSlot()); MOZ_ASSERT(slot.get()); if (NS_WARN_IF((aChallenge.Length() != kParamLen) || (aApplication.Length() != kParamLen))) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Parameter lengths are wrong! challenge=%d app=%d expected=%d", (uint32_t)aChallenge.Length(), (uint32_t)aApplication.Length(), kParamLen)); return NS_ERROR_ILLEGAL_VALUE; } // Decode the key handle UniqueSECKEYPrivateKey privKey = PrivateKeyFromKeyHandle(slot, mWrappingKey, aKeyHandle.Elements(), aKeyHandle.Length(), aApplication.Elements(), aApplication.Length(), locker); if (NS_WARN_IF(!privKey.get())) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Couldn't get the priv key!")); return NS_ERROR_FAILURE; } // Increment the counter and turn it into a SECItem mCounter += 1; ScopedAutoSECItem counterItem(4); counterItem.data[0] = (mCounter >> 24) & 0xFF; counterItem.data[1] = (mCounter >> 16) & 0xFF; counterItem.data[2] = (mCounter >> 8) & 0xFF; counterItem.data[3] = (mCounter >> 0) & 0xFF; uint32_t counter = mCounter; AbstractThread::MainThread()->Dispatch(NS_NewRunnableFunction( [counter] () { MOZ_ASSERT(NS_IsMainThread()); Preferences::SetUint(PREF_U2F_NSSTOKEN_COUNTER, counter); })); // Compute the signature mozilla::dom::CryptoBuffer signedDataBuf; if (NS_WARN_IF(!signedDataBuf.SetCapacity(1 + 4 + (2 * kParamLen), mozilla::fallible))) { return NS_ERROR_OUT_OF_MEMORY; } // It's OK to ignore the return values here because we're writing into // pre-allocated space signedDataBuf.AppendElements(aApplication.Elements(), aApplication.Length(), mozilla::fallible); signedDataBuf.AppendElement(0x01, mozilla::fallible); signedDataBuf.AppendSECItem(counterItem); signedDataBuf.AppendElements(aChallenge.Elements(), aChallenge.Length(), mozilla::fallible); if (MOZ_LOG_TEST(gNSSTokenLog, LogLevel::Debug)) { nsAutoCString base64; nsresult rv = Base64URLEncode(signedDataBuf.Length(), signedDataBuf.Elements(), Base64URLEncodePaddingPolicy::Omit, base64); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; } MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("U2F Token signing bytes (base64): %s", base64.get())); } ScopedAutoSECItem signatureItem; SECStatus srv = SEC_SignData(&signatureItem, signedDataBuf.Elements(), signedDataBuf.Length(), privKey.get(), SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE); if (NS_WARN_IF(srv != SECSuccess)) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Signature failure: %d", PORT_GetError())); return NS_ERROR_FAILURE; } // Assemble the signature data into a buffer for return mozilla::dom::CryptoBuffer signatureBuf; if (NS_WARN_IF(!signatureBuf.SetCapacity(1 + counterItem.len + signatureItem.len, mozilla::fallible))) { return NS_ERROR_OUT_OF_MEMORY; } // It's OK to ignore the return values here because we're writing into // pre-allocated space signatureBuf.AppendElement(0x01, mozilla::fallible); signatureBuf.AppendSECItem(counterItem); signatureBuf.AppendSECItem(signatureItem); aSignature = signatureBuf; return NS_OK; }