/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_WebCryptoCommon_h #define mozilla_dom_WebCryptoCommon_h #include "js/StructuredClone.h" #include "mozilla/ArrayUtils.h" #include "mozilla/dom/CryptoBuffer.h" #include "nsContentUtils.h" #include "nsString.h" #include "pk11pub.h" // WebCrypto algorithm names #define WEBCRYPTO_ALG_AES_CBC "AES-CBC" #define WEBCRYPTO_ALG_AES_CTR "AES-CTR" #define WEBCRYPTO_ALG_AES_GCM "AES-GCM" #define WEBCRYPTO_ALG_AES_KW "AES-KW" #define WEBCRYPTO_ALG_SHA1 "SHA-1" #define WEBCRYPTO_ALG_SHA256 "SHA-256" #define WEBCRYPTO_ALG_SHA384 "SHA-384" #define WEBCRYPTO_ALG_SHA512 "SHA-512" #define WEBCRYPTO_ALG_HMAC "HMAC" #define WEBCRYPTO_ALG_HKDF "HKDF" #define WEBCRYPTO_ALG_PBKDF2 "PBKDF2" #define WEBCRYPTO_ALG_RSASSA_PKCS1 "RSASSA-PKCS1-v1_5" #define WEBCRYPTO_ALG_RSA_OAEP "RSA-OAEP" #define WEBCRYPTO_ALG_RSA_PSS "RSA-PSS" #define WEBCRYPTO_ALG_ECDH "ECDH" #define WEBCRYPTO_ALG_ECDSA "ECDSA" #define WEBCRYPTO_ALG_DH "DH" // WebCrypto key formats #define WEBCRYPTO_KEY_FORMAT_RAW "raw" #define WEBCRYPTO_KEY_FORMAT_PKCS8 "pkcs8" #define WEBCRYPTO_KEY_FORMAT_SPKI "spki" #define WEBCRYPTO_KEY_FORMAT_JWK "jwk" // WebCrypto key types #define WEBCRYPTO_KEY_TYPE_PUBLIC "public" #define WEBCRYPTO_KEY_TYPE_PRIVATE "private" #define WEBCRYPTO_KEY_TYPE_SECRET "secret" // WebCrypto key usages #define WEBCRYPTO_KEY_USAGE_ENCRYPT "encrypt" #define WEBCRYPTO_KEY_USAGE_DECRYPT "decrypt" #define WEBCRYPTO_KEY_USAGE_SIGN "sign" #define WEBCRYPTO_KEY_USAGE_VERIFY "verify" #define WEBCRYPTO_KEY_USAGE_DERIVEKEY "deriveKey" #define WEBCRYPTO_KEY_USAGE_DERIVEBITS "deriveBits" #define WEBCRYPTO_KEY_USAGE_WRAPKEY "wrapKey" #define WEBCRYPTO_KEY_USAGE_UNWRAPKEY "unwrapKey" // WebCrypto named curves #define WEBCRYPTO_NAMED_CURVE_P256 "P-256" #define WEBCRYPTO_NAMED_CURVE_P384 "P-384" #define WEBCRYPTO_NAMED_CURVE_P521 "P-521" // JWK key types #define JWK_TYPE_SYMMETRIC "oct" #define JWK_TYPE_RSA "RSA" #define JWK_TYPE_EC "EC" // JWK algorithms #define JWK_ALG_A128CBC "A128CBC" // CBC #define JWK_ALG_A192CBC "A192CBC" #define JWK_ALG_A256CBC "A256CBC" #define JWK_ALG_A128CTR "A128CTR" // CTR #define JWK_ALG_A192CTR "A192CTR" #define JWK_ALG_A256CTR "A256CTR" #define JWK_ALG_A128GCM "A128GCM" // GCM #define JWK_ALG_A192GCM "A192GCM" #define JWK_ALG_A256GCM "A256GCM" #define JWK_ALG_A128KW "A128KW" // KW #define JWK_ALG_A192KW "A192KW" #define JWK_ALG_A256KW "A256KW" #define JWK_ALG_HS1 "HS1" // HMAC #define JWK_ALG_HS256 "HS256" #define JWK_ALG_HS384 "HS384" #define JWK_ALG_HS512 "HS512" #define JWK_ALG_RS1 "RS1" // RSASSA-PKCS1 #define JWK_ALG_RS256 "RS256" #define JWK_ALG_RS384 "RS384" #define JWK_ALG_RS512 "RS512" #define JWK_ALG_RSA_OAEP "RSA-OAEP" // RSA-OAEP #define JWK_ALG_RSA_OAEP_256 "RSA-OAEP-256" #define JWK_ALG_RSA_OAEP_384 "RSA-OAEP-384" #define JWK_ALG_RSA_OAEP_512 "RSA-OAEP-512" #define JWK_ALG_PS1 "PS1" // RSA-PSS #define JWK_ALG_PS256 "PS256" #define JWK_ALG_PS384 "PS384" #define JWK_ALG_PS512 "PS512" #define JWK_ALG_ECDSA_P_256 "ES256" #define JWK_ALG_ECDSA_P_384 "ES384" #define JWK_ALG_ECDSA_P_521 "ES521" // JWK usages #define JWK_USE_ENC "enc" #define JWK_USE_SIG "sig" // Define an unknown mechanism type #define UNKNOWN_CK_MECHANISM CKM_VENDOR_DEFINED+1 // python security/pkix/tools/DottedOIDToCode.py id-ecDH 1.3.132.112 static const uint8_t id_ecDH[] = { 0x2b, 0x81, 0x04, 0x70 }; const SECItem SEC_OID_DATA_EC_DH = { siBuffer, (unsigned char*)id_ecDH, static_cast<unsigned int>( mozilla::ArrayLength(id_ecDH)) }; // python security/pkix/tools/DottedOIDToCode.py dhKeyAgreement 1.2.840.113549.1.3.1 static const uint8_t dhKeyAgreement[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x03, 0x01 }; const SECItem SEC_OID_DATA_DH_KEY_AGREEMENT = { siBuffer, (unsigned char*)dhKeyAgreement, static_cast<unsigned int>( mozilla::ArrayLength(dhKeyAgreement)) }; namespace mozilla { namespace dom { // Helper functions for structured cloning inline bool ReadString(JSStructuredCloneReader* aReader, nsString& aString) { bool read; uint32_t nameLength, zero; read = JS_ReadUint32Pair(aReader, &nameLength, &zero); if (!read) { return false; } aString.SetLength(nameLength); size_t charSize = sizeof(nsString::char_type); read = JS_ReadBytes(aReader, (void*) aString.BeginWriting(), nameLength * charSize); if (!read) { return false; } return true; } inline bool WriteString(JSStructuredCloneWriter* aWriter, const nsString& aString) { size_t charSize = sizeof(nsString::char_type); return JS_WriteUint32Pair(aWriter, aString.Length(), 0) && JS_WriteBytes(aWriter, aString.get(), aString.Length() * charSize); } inline bool ReadBuffer(JSStructuredCloneReader* aReader, CryptoBuffer& aBuffer) { uint32_t length, zero; bool ret = JS_ReadUint32Pair(aReader, &length, &zero); if (!ret) { return false; } if (length > 0) { if (!aBuffer.SetLength(length, fallible)) { return false; } ret = JS_ReadBytes(aReader, aBuffer.Elements(), aBuffer.Length()); } return ret; } inline bool WriteBuffer(JSStructuredCloneWriter* aWriter, const uint8_t* aBuffer, size_t aLength) { bool ret = JS_WriteUint32Pair(aWriter, aLength, 0); if (ret && aLength > 0) { ret = JS_WriteBytes(aWriter, aBuffer, aLength); } return ret; } inline bool WriteBuffer(JSStructuredCloneWriter* aWriter, const CryptoBuffer& aBuffer) { return WriteBuffer(aWriter, aBuffer.Elements(), aBuffer.Length()); } inline CK_MECHANISM_TYPE MapAlgorithmNameToMechanism(const nsString& aName) { CK_MECHANISM_TYPE mechanism(UNKNOWN_CK_MECHANISM); // Set mechanism based on algorithm name if (aName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) { mechanism = CKM_AES_CBC_PAD; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) { mechanism = CKM_AES_CTR; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) { mechanism = CKM_AES_GCM; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_AES_KW)) { mechanism = CKM_NSS_AES_KEY_WRAP; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) { mechanism = CKM_SHA_1; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) { mechanism = CKM_SHA256; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) { mechanism = CKM_SHA384; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) { mechanism = CKM_SHA512; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) { mechanism = CKM_PKCS5_PBKD2; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) { mechanism = CKM_RSA_PKCS; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSA_OAEP)) { mechanism = CKM_RSA_PKCS_OAEP; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSA_PSS)) { mechanism = CKM_RSA_PKCS_PSS; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_ECDH)) { mechanism = CKM_ECDH1_DERIVE; } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_DH)) { mechanism = CKM_DH_PKCS_DERIVE; } return mechanism; } #define NORMALIZED_EQUALS(aTest, aConst) \ nsContentUtils::EqualsIgnoreASCIICase(aTest, NS_LITERAL_STRING(aConst)) inline bool NormalizeToken(const nsString& aName, nsString& aDest) { // Algorithm names if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_AES_CBC)) { aDest.AssignLiteral(WEBCRYPTO_ALG_AES_CBC); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_AES_CTR)) { aDest.AssignLiteral(WEBCRYPTO_ALG_AES_CTR); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_AES_GCM)) { aDest.AssignLiteral(WEBCRYPTO_ALG_AES_GCM); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_AES_KW)) { aDest.AssignLiteral(WEBCRYPTO_ALG_AES_KW); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_SHA1)) { aDest.AssignLiteral(WEBCRYPTO_ALG_SHA1); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_SHA256)) { aDest.AssignLiteral(WEBCRYPTO_ALG_SHA256); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_SHA384)) { aDest.AssignLiteral(WEBCRYPTO_ALG_SHA384); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_SHA512)) { aDest.AssignLiteral(WEBCRYPTO_ALG_SHA512); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_HMAC)) { aDest.AssignLiteral(WEBCRYPTO_ALG_HMAC); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_HKDF)) { aDest.AssignLiteral(WEBCRYPTO_ALG_HKDF); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_PBKDF2)) { aDest.AssignLiteral(WEBCRYPTO_ALG_PBKDF2); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_RSASSA_PKCS1)) { aDest.AssignLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_RSA_OAEP)) { aDest.AssignLiteral(WEBCRYPTO_ALG_RSA_OAEP); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_RSA_PSS)) { aDest.AssignLiteral(WEBCRYPTO_ALG_RSA_PSS); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_ECDH)) { aDest.AssignLiteral(WEBCRYPTO_ALG_ECDH); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_ECDSA)) { aDest.AssignLiteral(WEBCRYPTO_ALG_ECDSA); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_ALG_DH)) { aDest.AssignLiteral(WEBCRYPTO_ALG_DH); // Named curve values } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P256)) { aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P256); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P384)) { aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P384); } else if (NORMALIZED_EQUALS(aName, WEBCRYPTO_NAMED_CURVE_P521)) { aDest.AssignLiteral(WEBCRYPTO_NAMED_CURVE_P521); } else { return false; } return true; } inline bool CheckEncodedECParameters(const SECItem* aEcParams) { // Need at least two bytes for a valid ASN.1 encoding. if (aEcParams->len < 2) { return false; } // Check the ASN.1 tag. if (aEcParams->data[0] != SEC_ASN1_OBJECT_ID) { return false; } // OID tags are short, we never need more than one length byte. if (aEcParams->data[1] >= 128) { return false; } // Check that the SECItem's length is correct. if (aEcParams->len != (unsigned)aEcParams->data[1] + 2) { return false; } return true; } inline SECItem* CreateECParamsForCurve(const nsString& aNamedCurve, PLArenaPool* aArena) { MOZ_ASSERT(aArena); SECOidTag curveOIDTag; if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P256)) { curveOIDTag = SEC_OID_SECG_EC_SECP256R1; } else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P384)) { curveOIDTag = SEC_OID_SECG_EC_SECP384R1; } else if (aNamedCurve.EqualsLiteral(WEBCRYPTO_NAMED_CURVE_P521)) { curveOIDTag = SEC_OID_SECG_EC_SECP521R1; } else { return nullptr; } // Retrieve curve data by OID tag. SECOidData* oidData = SECOID_FindOIDByTag(curveOIDTag); if (!oidData) { return nullptr; } // Create parameters. SECItem* params = ::SECITEM_AllocItem(aArena, nullptr, 2 + oidData->oid.len); if (!params) { return nullptr; } // Set parameters. params->data[0] = SEC_ASN1_OBJECT_ID; params->data[1] = oidData->oid.len; memcpy(params->data + 2, oidData->oid.data, oidData->oid.len); // Sanity check the params we just created. if (!CheckEncodedECParameters(params)) { return nullptr; } return params; } } // namespace dom } // namespace mozilla #endif // mozilla_dom_WebCryptoCommon_h