diff options
author | wolfbeast <mcwerewolf@gmail.com> | 2018-12-15 01:42:53 +0100 |
---|---|---|
committer | wolfbeast <mcwerewolf@gmail.com> | 2018-12-15 01:42:53 +0100 |
commit | 74cabf7948b2597f5b6a67d6910c844fd1a88ff6 (patch) | |
tree | db1f30ada487c3831ea8e4e98b2d39edc9e88eea /security/nss/lib | |
parent | 09ef48bd005a7f9e97a3fe797a079fcf2b5e58d3 (diff) | |
download | UXP-74cabf7948b2597f5b6a67d6910c844fd1a88ff6.tar UXP-74cabf7948b2597f5b6a67d6910c844fd1a88ff6.tar.gz UXP-74cabf7948b2597f5b6a67d6910c844fd1a88ff6.tar.lz UXP-74cabf7948b2597f5b6a67d6910c844fd1a88ff6.tar.xz UXP-74cabf7948b2597f5b6a67d6910c844fd1a88ff6.zip |
Update NSS to 3.41
Diffstat (limited to 'security/nss/lib')
140 files changed, 15450 insertions, 2272 deletions
diff --git a/security/nss/lib/base/error.c b/security/nss/lib/base/error.c index 95a76cf79..2ef032933 100644 --- a/security/nss/lib/base/error.c +++ b/security/nss/lib/base/error.c @@ -15,6 +15,10 @@ #include <limits.h> /* for UINT_MAX */ #include <string.h> /* for memmove */ +#if defined(__MINGW32__) +#include <windows.h> +#endif + #define NSS_MAX_ERROR_STACK_COUNT 16 /* error codes */ /* @@ -65,7 +69,32 @@ static const PRCallOnceType error_call_again; static PRStatus error_once_function(void) { + +/* + * This #ifdef function is redundant. It performs the same thing as the + * else case. + * + * However, the MinGW version looks up the function from nss3's export + * table, and on MinGW _that_ behaves differently than passing a + * function pointer in a different module because MinGW has + * -mnop-fun-dllimport specified, which generates function thunks for + * cross-module calls. And when a module (like nssckbi) gets unloaded, + * and you try to call into that thunk (which is now missing) you'll + * crash. So we do this bit of ugly to avoid that crash. Fortunately + * this is the only place we've had to do this. + */ +#if defined(__MINGW32__) + HMODULE nss3 = GetModuleHandleW(L"nss3"); + if (nss3) { + FARPROC freePtr = GetProcAddress(nss3, "PR_Free"); + if (freePtr) { + return PR_NewThreadPrivateIndex(&error_stack_index, freePtr); + } + } + return PR_NewThreadPrivateIndex(&error_stack_index, PR_Free); +#else return PR_NewThreadPrivateIndex(&error_stack_index, PR_Free); +#endif } /* diff --git a/security/nss/lib/certdb/cert.h b/security/nss/lib/certdb/cert.h index c76a5a9b0..333ba4c9d 100644 --- a/security/nss/lib/certdb/cert.h +++ b/security/nss/lib/certdb/cert.h @@ -18,7 +18,7 @@ #include "seccomon.h" #include "secdert.h" #include "secoidt.h" -#include "keyt.h" +#include "keythi.h" #include "certt.h" SEC_BEGIN_PROTOS diff --git a/security/nss/lib/certdb/certdb.c b/security/nss/lib/certdb/certdb.c index 1a676a720..85b5f2917 100644 --- a/security/nss/lib/certdb/certdb.c +++ b/security/nss/lib/certdb/certdb.c @@ -446,6 +446,74 @@ cert_GetCertType(CERTCertificate *cert) return SECSuccess; } +PRBool +cert_EKUAllowsIPsecIKE(CERTCertificate *cert, PRBool *isCritical) +{ + SECStatus rv; + SECItem encodedExtKeyUsage; + CERTOidSequence *extKeyUsage = NULL; + PRBool result = PR_FALSE; + + rv = CERT_GetExtenCriticality(cert->extensions, + SEC_OID_X509_EXT_KEY_USAGE, + isCritical); + if (rv != SECSuccess) { + *isCritical = PR_FALSE; + } + + encodedExtKeyUsage.data = NULL; + rv = CERT_FindCertExtension(cert, SEC_OID_X509_EXT_KEY_USAGE, + &encodedExtKeyUsage); + if (rv != SECSuccess) { + /* EKU not present, allowed. */ + result = PR_TRUE; + goto done; + } + + extKeyUsage = CERT_DecodeOidSequence(&encodedExtKeyUsage); + if (!extKeyUsage) { + /* failure */ + goto done; + } + + if (findOIDinOIDSeqByTagNum(extKeyUsage, + SEC_OID_X509_ANY_EXT_KEY_USAGE) == + SECSuccess) { + result = PR_TRUE; + goto done; + } + + if (findOIDinOIDSeqByTagNum(extKeyUsage, + SEC_OID_EXT_KEY_USAGE_IPSEC_IKE) == + SECSuccess) { + result = PR_TRUE; + goto done; + } + + if (findOIDinOIDSeqByTagNum(extKeyUsage, + SEC_OID_IPSEC_IKE_END) == + SECSuccess) { + result = PR_TRUE; + goto done; + } + + if (findOIDinOIDSeqByTagNum(extKeyUsage, + SEC_OID_IPSEC_IKE_INTERMEDIATE) == + SECSuccess) { + result = PR_TRUE; + goto done; + } + +done: + if (encodedExtKeyUsage.data != NULL) { + PORT_Free(encodedExtKeyUsage.data); + } + if (extKeyUsage != NULL) { + CERT_DestroyOidSequence(extKeyUsage); + } + return result; +} + PRUint32 cert_ComputeCertType(CERTCertificate *cert) { @@ -1083,6 +1151,10 @@ CERT_KeyUsageAndTypeForCertUsage(SECCertUsage usage, PRBool ca, requiredKeyUsage = KU_KEY_CERT_SIGN; requiredCertType = NS_CERT_TYPE_SSL_CA; break; + case certUsageIPsec: + requiredKeyUsage = KU_KEY_CERT_SIGN; + requiredCertType = NS_CERT_TYPE_SSL_CA; + break; case certUsageSSLCA: requiredKeyUsage = KU_KEY_CERT_SIGN; requiredCertType = NS_CERT_TYPE_SSL_CA; @@ -1125,6 +1197,11 @@ CERT_KeyUsageAndTypeForCertUsage(SECCertUsage usage, PRBool ca, requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT; requiredCertType = NS_CERT_TYPE_SSL_SERVER; break; + case certUsageIPsec: + /* RFC 4945 Section 5.1.3.2 */ + requiredKeyUsage = KU_DIGITAL_SIGNATURE_OR_NON_REPUDIATION; + requiredCertType = 0; + break; case certUsageSSLServerWithStepUp: requiredKeyUsage = KU_KEY_AGREEMENT_OR_ENCIPHERMENT | KU_NS_GOVT_APPROVED; diff --git a/security/nss/lib/certdb/certi.h b/security/nss/lib/certdb/certi.h index 456f2fc4e..2a8ae2758 100644 --- a/security/nss/lib/certdb/certi.h +++ b/security/nss/lib/certdb/certi.h @@ -294,6 +294,9 @@ extern SECStatus cert_GetCertType(CERTCertificate* cert); */ extern PRUint32 cert_ComputeCertType(CERTCertificate* cert); +extern PRBool cert_EKUAllowsIPsecIKE(CERTCertificate* cert, + PRBool* isCritical); + void cert_AddToVerifyLog(CERTVerifyLog* log, CERTCertificate* cert, long errorCode, unsigned int depth, void* arg); diff --git a/security/nss/lib/certdb/certt.h b/security/nss/lib/certdb/certt.h index 797f9f585..9cac70ca6 100644 --- a/security/nss/lib/certdb/certt.h +++ b/security/nss/lib/certdb/certt.h @@ -447,7 +447,8 @@ typedef enum SECCertUsageEnum { certUsageVerifyCA = 8, certUsageProtectedObjectSigner = 9, certUsageStatusResponder = 10, - certUsageAnyCA = 11 + certUsageAnyCA = 11, + certUsageIPsec = 12 } SECCertUsage; typedef PRInt64 SECCertificateUsage; @@ -465,8 +466,9 @@ typedef PRInt64 SECCertificateUsage; #define certificateUsageProtectedObjectSigner (0x0200) #define certificateUsageStatusResponder (0x0400) #define certificateUsageAnyCA (0x0800) +#define certificateUsageIPsec (0x1000) -#define certificateUsageHighest certificateUsageAnyCA +#define certificateUsageHighest certificateUsageIPsec /* * Does the cert belong to the user, a peer, or a CA. diff --git a/security/nss/lib/certhigh/certreq.c b/security/nss/lib/certhigh/certreq.c index 4087bc978..2ab4f1ab7 100644 --- a/security/nss/lib/certhigh/certreq.c +++ b/security/nss/lib/certhigh/certreq.c @@ -5,7 +5,7 @@ #include "cert.h" #include "certt.h" #include "secder.h" -#include "key.h" +#include "keyhi.h" #include "secitem.h" #include "secasn1.h" #include "secerr.h" diff --git a/security/nss/lib/certhigh/certvfy.c b/security/nss/lib/certhigh/certvfy.c index ccd38e660..3a94a4150 100644 --- a/security/nss/lib/certhigh/certvfy.c +++ b/security/nss/lib/certhigh/certvfy.c @@ -25,7 +25,7 @@ #include "pkim.h" #include "pki3hack.h" #include "base.h" -#include "keyhi.h" +#include "keyi.h" /* * Check the validity times of a certificate @@ -73,12 +73,38 @@ checkKeyParams(const SECAlgorithmID *sigAlgorithm, const SECKEYPublicKey *key) return SECFailure; } return SECSuccess; + + case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: { + PORTCheapArenaPool tmpArena; + SECOidTag hashAlg; + SECOidTag maskHashAlg; + + PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); + rv = sec_DecodeRSAPSSParams(&tmpArena.arena, + &sigAlgorithm->parameters, + &hashAlg, &maskHashAlg, NULL); + PORT_DestroyCheapArena(&tmpArena); + if (rv != SECSuccess) { + return SECFailure; + } + + if (NSS_GetAlgorithmPolicy(hashAlg, &policyFlags) == SECSuccess && + !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) { + PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); + return SECFailure; + } + if (NSS_GetAlgorithmPolicy(maskHashAlg, &policyFlags) == SECSuccess && + !(policyFlags & NSS_USE_ALG_IN_CERT_SIGNATURE)) { + PORT_SetError(SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED); + return SECFailure; + } + } + /* fall through to RSA key checking */ case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION: case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION: case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION: case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION: case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION: - case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE: case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE: if (key->keyType != rsaKey && key->keyType != rsaPssKey) { @@ -289,6 +315,10 @@ CERT_TrustFlagsForCACertUsage(SECCertUsage usage, requiredFlags = CERTDB_TRUSTED_CA; trustType = trustSSL; break; + case certUsageIPsec: + requiredFlags = CERTDB_TRUSTED_CA; + trustType = trustSSL; + break; case certUsageSSLServerWithStepUp: requiredFlags = CERTDB_TRUSTED_CA | CERTDB_GOVT_APPROVED_CA; trustType = trustSSL; @@ -579,6 +609,7 @@ cert_VerifyCertChainOld(CERTCertDBHandle *handle, CERTCertificate *cert, switch (certUsage) { case certUsageSSLClient: case certUsageSSLServer: + case certUsageIPsec: case certUsageSSLCA: case certUsageSSLServerWithStepUp: case certUsageEmailSigner: @@ -645,7 +676,8 @@ cert_VerifyCertChainOld(CERTCertDBHandle *handle, CERTCertificate *cert, CERTGeneralName *subjectNameList; int subjectNameListLen; int i; - PRBool getSubjectCN = (!count && certUsage == certUsageSSLServer); + PRBool getSubjectCN = (!count && + (certUsage == certUsageSSLServer || certUsage == certUsageIPsec)); subjectNameList = CERT_GetConstrainedCertificateNames(subjectCert, arena, getSubjectCN); @@ -986,6 +1018,7 @@ CERT_VerifyCACertForUsage(CERTCertDBHandle *handle, CERTCertificate *cert, switch (certUsage) { case certUsageSSLClient: case certUsageSSLServer: + case certUsageIPsec: case certUsageSSLCA: case certUsageSSLServerWithStepUp: case certUsageEmailSigner: @@ -1171,6 +1204,7 @@ cert_CheckLeafTrust(CERTCertificate *cert, SECCertUsage certUsage, switch (certUsage) { case certUsageSSLClient: case certUsageSSLServer: + case certUsageIPsec: flags = trust.sslFlags; /* is the cert directly trusted or not trusted ? */ @@ -1347,7 +1381,8 @@ CERT_VerifyCertificate(CERTCertDBHandle *handle, CERTCertificate *cert, /* make sure that the cert is valid at time t */ allowOverride = (PRBool)((requiredUsages & certificateUsageSSLServer) || - (requiredUsages & certificateUsageSSLServerWithStepUp)); + (requiredUsages & certificateUsageSSLServerWithStepUp) || + (requiredUsages & certificateUsageIPsec)); validity = CERT_CheckCertValidTimes(cert, t, allowOverride); if (validity != secCertTimeValid) { valid = SECFailure; @@ -1360,6 +1395,7 @@ CERT_VerifyCertificate(CERTCertDBHandle *handle, CERTCertificate *cert, for (i = 1; i <= certificateUsageHighest && (SECSuccess == valid || returnedUsages || log);) { + PRBool typeAndEKUAllowed = PR_TRUE; PRBool requiredUsage = (i & requiredUsages) ? PR_TRUE : PR_FALSE; if (PR_FALSE == requiredUsage && PR_FALSE == checkAllUsages) { NEXT_USAGE(); @@ -1376,6 +1412,7 @@ CERT_VerifyCertificate(CERTCertDBHandle *handle, CERTCertificate *cert, case certUsageEmailRecipient: case certUsageObjectSigner: case certUsageStatusResponder: + case certUsageIPsec: rv = CERT_KeyUsageAndTypeForCertUsage(certUsage, PR_FALSE, &requiredKeyUsage, &requiredCertType); @@ -1408,7 +1445,19 @@ CERT_VerifyCertificate(CERTCertDBHandle *handle, CERTCertificate *cert, LOG_ERROR(log, cert, 0, requiredKeyUsage); INVALID_USAGE(); } - if (!(certType & requiredCertType)) { + if (certUsage != certUsageIPsec) { + if (!(certType & requiredCertType)) { + typeAndEKUAllowed = PR_FALSE; + } + } else { + PRBool isCritical; + PRBool allowed = cert_EKUAllowsIPsecIKE(cert, &isCritical); + /* If the extension isn't critical, we allow any EKU value. */ + if (isCritical && !allowed) { + typeAndEKUAllowed = PR_FALSE; + } + } + if (!typeAndEKUAllowed) { if (PR_TRUE == requiredUsage) { PORT_SetError(SEC_ERROR_INADEQUATE_CERT_TYPE); } @@ -1508,7 +1557,8 @@ cert_VerifyCertWithFlags(CERTCertDBHandle *handle, CERTCertificate *cert, /* make sure that the cert is valid at time t */ allowOverride = (PRBool)((certUsage == certUsageSSLServer) || - (certUsage == certUsageSSLServerWithStepUp)); + (certUsage == certUsageSSLServerWithStepUp) || + (certUsage == certUsageIPsec)); validity = CERT_CheckCertValidTimes(cert, t, allowOverride); if (validity != secCertTimeValid) { LOG_ERROR_OR_EXIT(log, cert, 0, validity); @@ -1521,6 +1571,7 @@ cert_VerifyCertWithFlags(CERTCertDBHandle *handle, CERTCertificate *cert, case certUsageSSLClient: case certUsageSSLServer: case certUsageSSLServerWithStepUp: + case certUsageIPsec: case certUsageSSLCA: case certUsageEmailSigner: case certUsageEmailRecipient: @@ -1633,6 +1684,7 @@ CERT_VerifyCertNow(CERTCertDBHandle *handle, CERTCertificate *cert, * certUsageSSLClient * certUsageSSLServer * certUsageSSLServerWithStepUp + * certUsageIPsec * certUsageEmailSigner * certUsageEmailRecipient * certUsageObjectSigner diff --git a/security/nss/lib/certhigh/ocsp.h b/security/nss/lib/certhigh/ocsp.h index ac9dd6465..1b94aec2e 100644 --- a/security/nss/lib/certhigh/ocsp.h +++ b/security/nss/lib/certhigh/ocsp.h @@ -12,7 +12,7 @@ #include "plarena.h" #include "seccomon.h" #include "secoidt.h" -#include "keyt.h" +#include "keythi.h" #include "certt.h" #include "ocspt.h" diff --git a/security/nss/lib/ckfw/builtins/certdata.txt b/security/nss/lib/ckfw/builtins/certdata.txt index d291f28a5..182dda65e 100644 --- a/security/nss/lib/ckfw/builtins/certdata.txt +++ b/security/nss/lib/ckfw/builtins/certdata.txt @@ -2145,146 +2145,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE # -# Certificate "Visa eCommerce Root" -# -# Issuer: CN=Visa eCommerce Root,OU=Visa International Service Association,O=VISA,C=US -# Serial Number:13:86:35:4d:1d:3f:06:f2:c1:f9:65:05:d5:90:1c:62 -# Subject: CN=Visa eCommerce Root,OU=Visa International Service Association,O=VISA,C=US -# Not Valid Before: Wed Jun 26 02:18:36 2002 -# Not Valid After : Fri Jun 24 00:16:12 2022 -# Fingerprint (MD5): FC:11:B8:D8:08:93:30:00:6D:23:F9:7E:EB:52:1E:02 -# Fingerprint (SHA1): 70:17:9B:86:8C:00:A4:FA:60:91:52:22:3F:9F:3E:32:BD:E0:05:62 -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Visa eCommerce Root" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\153\061\013\060\011\006\003\125\004\006\023\002\125\123\061 -\015\060\013\006\003\125\004\012\023\004\126\111\123\101\061\057 -\060\055\006\003\125\004\013\023\046\126\151\163\141\040\111\156 -\164\145\162\156\141\164\151\157\156\141\154\040\123\145\162\166 -\151\143\145\040\101\163\163\157\143\151\141\164\151\157\156\061 -\034\060\032\006\003\125\004\003\023\023\126\151\163\141\040\145 -\103\157\155\155\145\162\143\145\040\122\157\157\164 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\153\061\013\060\011\006\003\125\004\006\023\002\125\123\061 -\015\060\013\006\003\125\004\012\023\004\126\111\123\101\061\057 -\060\055\006\003\125\004\013\023\046\126\151\163\141\040\111\156 -\164\145\162\156\141\164\151\157\156\141\154\040\123\145\162\166 -\151\143\145\040\101\163\163\157\143\151\141\164\151\157\156\061 -\034\060\032\006\003\125\004\003\023\023\126\151\163\141\040\145 -\103\157\155\155\145\162\143\145\040\122\157\157\164 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\020\023\206\065\115\035\077\006\362\301\371\145\005\325\220 -\034\142 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\003\242\060\202\002\212\240\003\002\001\002\002\020\023 -\206\065\115\035\077\006\362\301\371\145\005\325\220\034\142\060 -\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060\153 -\061\013\060\011\006\003\125\004\006\023\002\125\123\061\015\060 -\013\006\003\125\004\012\023\004\126\111\123\101\061\057\060\055 -\006\003\125\004\013\023\046\126\151\163\141\040\111\156\164\145 -\162\156\141\164\151\157\156\141\154\040\123\145\162\166\151\143 -\145\040\101\163\163\157\143\151\141\164\151\157\156\061\034\060 -\032\006\003\125\004\003\023\023\126\151\163\141\040\145\103\157 -\155\155\145\162\143\145\040\122\157\157\164\060\036\027\015\060 -\062\060\066\062\066\060\062\061\070\063\066\132\027\015\062\062 -\060\066\062\064\060\060\061\066\061\062\132\060\153\061\013\060 -\011\006\003\125\004\006\023\002\125\123\061\015\060\013\006\003 -\125\004\012\023\004\126\111\123\101\061\057\060\055\006\003\125 -\004\013\023\046\126\151\163\141\040\111\156\164\145\162\156\141 -\164\151\157\156\141\154\040\123\145\162\166\151\143\145\040\101 -\163\163\157\143\151\141\164\151\157\156\061\034\060\032\006\003 -\125\004\003\023\023\126\151\163\141\040\145\103\157\155\155\145 -\162\143\145\040\122\157\157\164\060\202\001\042\060\015\006\011 -\052\206\110\206\367\015\001\001\001\005\000\003\202\001\017\000 -\060\202\001\012\002\202\001\001\000\257\127\336\126\036\156\241 -\332\140\261\224\047\313\027\333\007\077\200\205\117\310\234\266 -\320\364\157\117\317\231\330\341\333\302\110\134\072\254\071\063 -\307\037\152\213\046\075\053\065\365\110\261\221\301\002\116\004 -\226\221\173\260\063\360\261\024\116\021\157\265\100\257\033\105 -\245\112\357\176\266\254\362\240\037\130\077\022\106\140\074\215 -\241\340\175\317\127\076\063\036\373\107\361\252\025\227\007\125 -\146\245\265\055\056\330\200\131\262\247\015\267\106\354\041\143 -\377\065\253\245\002\317\052\364\114\376\173\365\224\135\204\115 -\250\362\140\217\333\016\045\074\237\163\161\317\224\337\112\352 -\333\337\162\070\214\363\226\275\361\027\274\322\272\073\105\132 -\306\247\366\306\027\213\001\235\374\031\250\052\203\026\270\072 -\110\376\116\076\240\253\006\031\351\123\363\200\023\007\355\055 -\277\077\012\074\125\040\071\054\054\000\151\164\225\112\274\040 -\262\251\171\345\030\211\221\250\334\034\115\357\273\176\067\013 -\135\376\071\245\210\122\214\000\154\354\030\174\101\275\366\213 -\165\167\272\140\235\204\347\376\055\002\003\001\000\001\243\102 -\060\100\060\017\006\003\125\035\023\001\001\377\004\005\060\003 -\001\001\377\060\016\006\003\125\035\017\001\001\377\004\004\003 -\002\001\006\060\035\006\003\125\035\016\004\026\004\024\025\070 -\203\017\077\054\077\160\063\036\315\106\376\007\214\040\340\327 -\303\267\060\015\006\011\052\206\110\206\367\015\001\001\005\005 -\000\003\202\001\001\000\137\361\101\175\174\134\010\271\053\340 -\325\222\107\372\147\134\245\023\303\003\041\233\053\114\211\106 -\317\131\115\311\376\245\100\266\143\315\335\161\050\225\147\021 -\314\044\254\323\104\154\161\256\001\040\153\003\242\217\030\267 -\051\072\175\345\026\140\123\170\074\300\257\025\203\367\217\122 -\063\044\275\144\223\227\356\213\367\333\030\250\155\161\263\367 -\054\027\320\164\045\151\367\376\153\074\224\276\115\113\101\214 -\116\342\163\320\343\220\042\163\103\315\363\357\352\163\316\105 -\212\260\246\111\377\114\175\235\161\210\304\166\035\220\133\035 -\356\375\314\367\356\375\140\245\261\172\026\161\321\026\320\174 -\022\074\154\151\227\333\256\137\071\232\160\057\005\074\031\106 -\004\231\040\066\320\140\156\141\006\273\026\102\214\160\367\060 -\373\340\333\146\243\000\001\275\346\054\332\221\137\240\106\213 -\115\152\234\075\075\335\005\106\376\166\277\240\012\074\344\000 -\346\047\267\377\204\055\336\272\042\047\226\020\161\353\042\355 -\337\337\063\234\317\343\255\256\216\324\216\346\117\121\257\026 -\222\340\134\366\007\017 -END -CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE - -# Trust for Certificate "Visa eCommerce Root" -# Issuer: CN=Visa eCommerce Root,OU=Visa International Service Association,O=VISA,C=US -# Serial Number:13:86:35:4d:1d:3f:06:f2:c1:f9:65:05:d5:90:1c:62 -# Subject: CN=Visa eCommerce Root,OU=Visa International Service Association,O=VISA,C=US -# Not Valid Before: Wed Jun 26 02:18:36 2002 -# Not Valid After : Fri Jun 24 00:16:12 2022 -# Fingerprint (MD5): FC:11:B8:D8:08:93:30:00:6D:23:F9:7E:EB:52:1E:02 -# Fingerprint (SHA1): 70:17:9B:86:8C:00:A4:FA:60:91:52:22:3F:9F:3E:32:BD:E0:05:62 -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Visa eCommerce Root" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\160\027\233\206\214\000\244\372\140\221\122\042\077\237\076\062 -\275\340\005\142 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\374\021\270\330\010\223\060\000\155\043\371\176\353\122\036\002 -END -CKA_ISSUER MULTILINE_OCTAL -\060\153\061\013\060\011\006\003\125\004\006\023\002\125\123\061 -\015\060\013\006\003\125\004\012\023\004\126\111\123\101\061\057 -\060\055\006\003\125\004\013\023\046\126\151\163\141\040\111\156 -\164\145\162\156\141\164\151\157\156\141\154\040\123\145\162\166 -\151\143\145\040\101\163\163\157\143\151\141\164\151\157\156\061 -\034\060\032\006\003\125\004\003\023\023\126\151\163\141\040\145 -\103\157\155\155\145\162\143\145\040\122\157\157\164 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\020\023\206\065\115\035\077\006\362\301\371\145\005\325\220 -\034\142 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - -# # Certificate "Certum Root CA" # # Issuer: CN=Certum CA,O=Unizeto Sp. z o.o.,C=PL @@ -7054,193 +6914,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE # -# Certificate "AC Raiz Certicamara S.A." -# -# Issuer: CN=AC Ra..z Certic..mara S.A.,O=Sociedad Cameral de Certificaci..n Digital - Certic..mara S.A.,C=CO -# Serial Number:07:7e:52:93:7b:e0:15:e3:57:f0:69:8c:cb:ec:0c -# Subject: CN=AC Ra..z Certic..mara S.A.,O=Sociedad Cameral de Certificaci..n Digital - Certic..mara S.A.,C=CO -# Not Valid Before: Mon Nov 27 20:46:29 2006 -# Not Valid After : Tue Apr 02 21:42:02 2030 -# Fingerprint (MD5): 93:2A:3E:F6:FD:23:69:0D:71:20:D4:2B:47:99:2B:A6 -# Fingerprint (SHA1): CB:A1:C5:F8:B0:E3:5E:B8:B9:45:12:D3:F9:34:A2:E9:06:10:D3:36 -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "AC Ra\xC3\xADz Certic\xC3\xA1mara S.A." -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\173\061\013\060\011\006\003\125\004\006\023\002\103\117\061 -\107\060\105\006\003\125\004\012\014\076\123\157\143\151\145\144 -\141\144\040\103\141\155\145\162\141\154\040\144\145\040\103\145 -\162\164\151\146\151\143\141\143\151\303\263\156\040\104\151\147 -\151\164\141\154\040\055\040\103\145\162\164\151\143\303\241\155 -\141\162\141\040\123\056\101\056\061\043\060\041\006\003\125\004 -\003\014\032\101\103\040\122\141\303\255\172\040\103\145\162\164 -\151\143\303\241\155\141\162\141\040\123\056\101\056 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\173\061\013\060\011\006\003\125\004\006\023\002\103\117\061 -\107\060\105\006\003\125\004\012\014\076\123\157\143\151\145\144 -\141\144\040\103\141\155\145\162\141\154\040\144\145\040\103\145 -\162\164\151\146\151\143\141\143\151\303\263\156\040\104\151\147 -\151\164\141\154\040\055\040\103\145\162\164\151\143\303\241\155 -\141\162\141\040\123\056\101\056\061\043\060\041\006\003\125\004 -\003\014\032\101\103\040\122\141\303\255\172\040\103\145\162\164 -\151\143\303\241\155\141\162\141\040\123\056\101\056 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\017\007\176\122\223\173\340\025\343\127\360\151\214\313\354 -\014 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\006\146\060\202\004\116\240\003\002\001\002\002\017\007 -\176\122\223\173\340\025\343\127\360\151\214\313\354\014\060\015 -\006\011\052\206\110\206\367\015\001\001\005\005\000\060\173\061 -\013\060\011\006\003\125\004\006\023\002\103\117\061\107\060\105 -\006\003\125\004\012\014\076\123\157\143\151\145\144\141\144\040 -\103\141\155\145\162\141\154\040\144\145\040\103\145\162\164\151 -\146\151\143\141\143\151\303\263\156\040\104\151\147\151\164\141 -\154\040\055\040\103\145\162\164\151\143\303\241\155\141\162\141 -\040\123\056\101\056\061\043\060\041\006\003\125\004\003\014\032 -\101\103\040\122\141\303\255\172\040\103\145\162\164\151\143\303 -\241\155\141\162\141\040\123\056\101\056\060\036\027\015\060\066 -\061\061\062\067\062\060\064\066\062\071\132\027\015\063\060\060 -\064\060\062\062\061\064\062\060\062\132\060\173\061\013\060\011 -\006\003\125\004\006\023\002\103\117\061\107\060\105\006\003\125 -\004\012\014\076\123\157\143\151\145\144\141\144\040\103\141\155 -\145\162\141\154\040\144\145\040\103\145\162\164\151\146\151\143 -\141\143\151\303\263\156\040\104\151\147\151\164\141\154\040\055 -\040\103\145\162\164\151\143\303\241\155\141\162\141\040\123\056 -\101\056\061\043\060\041\006\003\125\004\003\014\032\101\103\040 -\122\141\303\255\172\040\103\145\162\164\151\143\303\241\155\141 -\162\141\040\123\056\101\056\060\202\002\042\060\015\006\011\052 -\206\110\206\367\015\001\001\001\005\000\003\202\002\017\000\060 -\202\002\012\002\202\002\001\000\253\153\211\243\123\314\110\043 -\010\373\303\317\121\226\010\056\270\010\172\155\074\220\027\206 -\251\351\355\056\023\064\107\262\320\160\334\311\074\320\215\312 -\356\113\027\253\320\205\260\247\043\004\313\250\242\374\345\165 -\333\100\312\142\211\217\120\236\001\075\046\133\030\204\034\313 -\174\067\267\175\354\323\177\163\031\260\152\262\330\210\212\055 -\105\164\250\367\263\270\300\324\332\315\042\211\164\115\132\025 -\071\163\030\164\117\265\353\231\247\301\036\210\264\302\223\220 -\143\227\363\247\247\022\262\011\042\007\063\331\221\315\016\234 -\037\016\040\307\356\273\063\215\217\302\322\130\247\137\375\145 -\067\342\210\302\330\217\206\165\136\371\055\247\207\063\362\170 -\067\057\213\274\035\206\067\071\261\224\362\330\274\112\234\203 -\030\132\006\374\363\324\324\272\214\025\011\045\360\371\266\215 -\004\176\027\022\063\153\127\110\114\117\333\046\036\353\314\220 -\347\213\371\150\174\160\017\243\052\320\072\070\337\067\227\342 -\133\336\200\141\323\200\330\221\203\102\132\114\004\211\150\021 -\074\254\137\150\200\101\314\140\102\316\015\132\052\014\017\233 -\060\300\246\360\206\333\253\111\327\227\155\110\213\371\003\300 -\122\147\233\022\367\302\362\056\230\145\102\331\326\232\343\320 -\031\061\014\255\207\325\127\002\172\060\350\206\046\373\217\043 -\212\124\207\344\277\074\356\353\303\165\110\137\036\071\157\201 -\142\154\305\055\304\027\124\031\267\067\215\234\067\221\310\366 -\013\325\352\143\157\203\254\070\302\363\077\336\232\373\341\043 -\141\360\310\046\313\066\310\241\363\060\217\244\243\242\241\335 -\123\263\336\360\232\062\037\203\221\171\060\301\251\037\123\233 -\123\242\025\123\077\335\235\263\020\073\110\175\211\017\374\355 -\003\365\373\045\144\165\016\027\031\015\217\000\026\147\171\172 -\100\374\055\131\007\331\220\372\232\255\075\334\200\212\346\134 -\065\242\147\114\021\153\261\370\200\144\000\055\157\042\141\305 -\254\113\046\345\132\020\202\233\244\203\173\064\367\236\211\221 -\040\227\216\267\102\307\146\303\320\351\244\326\365\040\215\304 -\303\225\254\104\012\235\133\163\074\046\075\057\112\276\247\311 -\247\020\036\373\237\120\151\363\002\003\001\000\001\243\201\346 -\060\201\343\060\017\006\003\125\035\023\001\001\377\004\005\060 -\003\001\001\377\060\016\006\003\125\035\017\001\001\377\004\004 -\003\002\001\006\060\035\006\003\125\035\016\004\026\004\024\321 -\011\320\351\327\316\171\164\124\371\072\060\263\364\155\054\003 -\003\033\150\060\201\240\006\003\125\035\040\004\201\230\060\201 -\225\060\201\222\006\004\125\035\040\000\060\201\211\060\053\006 -\010\053\006\001\005\005\007\002\001\026\037\150\164\164\160\072 -\057\057\167\167\167\056\143\145\162\164\151\143\141\155\141\162 -\141\056\143\157\155\057\144\160\143\057\060\132\006\010\053\006 -\001\005\005\007\002\002\060\116\032\114\114\151\155\151\164\141 -\143\151\157\156\145\163\040\144\145\040\147\141\162\141\156\164 -\355\141\163\040\144\145\040\145\163\164\145\040\143\145\162\164 -\151\146\151\143\141\144\157\040\163\145\040\160\165\145\144\145 -\156\040\145\156\143\157\156\164\162\141\162\040\145\156\040\154 -\141\040\104\120\103\056\060\015\006\011\052\206\110\206\367\015 -\001\001\005\005\000\003\202\002\001\000\134\224\265\270\105\221 -\115\216\141\037\003\050\017\123\174\346\244\131\251\263\212\172 -\305\260\377\010\174\054\243\161\034\041\023\147\241\225\022\100 -\065\203\203\217\164\333\063\134\360\111\166\012\201\122\335\111 -\324\232\062\063\357\233\247\313\165\345\172\313\227\022\220\134 -\272\173\305\233\337\273\071\043\310\377\230\316\012\115\042\001 -\110\007\176\212\300\325\040\102\224\104\357\277\167\242\211\147 -\110\033\100\003\005\241\211\354\317\142\343\075\045\166\146\277 -\046\267\273\042\276\157\377\071\127\164\272\172\311\001\225\301 -\225\121\350\253\054\370\261\206\040\351\077\313\065\133\322\027 -\351\052\376\203\023\027\100\356\210\142\145\133\325\073\140\351 -\173\074\270\311\325\177\066\002\045\252\150\302\061\025\267\060 -\145\353\177\035\110\171\261\317\071\342\102\200\026\323\365\223 -\043\374\114\227\311\132\067\154\174\042\330\112\315\322\216\066 -\203\071\221\220\020\310\361\311\065\176\077\270\323\201\306\040 -\144\032\266\120\302\041\244\170\334\320\057\073\144\223\164\360 -\226\220\361\357\373\011\132\064\100\226\360\066\022\301\243\164 -\214\223\176\101\336\167\213\354\206\331\322\017\077\055\321\314 -\100\242\211\146\110\036\040\263\234\043\131\163\251\104\163\274 -\044\171\220\126\067\263\306\051\176\243\017\361\051\071\357\176 -\134\050\062\160\065\254\332\270\310\165\146\374\233\114\071\107 -\216\033\157\233\115\002\124\042\063\357\141\272\236\051\204\357 -\116\113\063\107\166\227\152\313\176\137\375\025\246\236\102\103 -\133\146\132\212\210\015\367\026\271\077\121\145\053\146\152\213 -\321\070\122\242\326\106\021\372\374\232\034\164\236\217\227\013 -\002\117\144\306\365\150\323\113\055\377\244\067\036\213\077\277 -\104\276\141\106\241\204\075\010\047\114\201\040\167\211\010\352 -\147\100\136\154\010\121\137\064\132\214\226\150\315\327\367\211 -\302\034\323\062\000\257\122\313\323\140\133\052\072\107\176\153 -\060\063\241\142\051\177\112\271\341\055\347\024\043\016\016\030 -\107\341\171\374\025\125\320\261\374\045\161\143\165\063\034\043 -\053\257\134\331\355\107\167\140\016\073\017\036\322\300\334\144 -\005\211\374\170\326\134\054\046\103\251 -END -CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE - -# Trust for Certificate "AC Raiz Certicamara S.A." -# Issuer: CN=AC Ra..z Certic..mara S.A.,O=Sociedad Cameral de Certificaci..n Digital - Certic..mara S.A.,C=CO -# Serial Number:07:7e:52:93:7b:e0:15:e3:57:f0:69:8c:cb:ec:0c -# Subject: CN=AC Ra..z Certic..mara S.A.,O=Sociedad Cameral de Certificaci..n Digital - Certic..mara S.A.,C=CO -# Not Valid Before: Mon Nov 27 20:46:29 2006 -# Not Valid After : Tue Apr 02 21:42:02 2030 -# Fingerprint (MD5): 93:2A:3E:F6:FD:23:69:0D:71:20:D4:2B:47:99:2B:A6 -# Fingerprint (SHA1): CB:A1:C5:F8:B0:E3:5E:B8:B9:45:12:D3:F9:34:A2:E9:06:10:D3:36 -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "AC Ra\xC3\xADz Certic\xC3\xA1mara S.A." -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\313\241\305\370\260\343\136\270\271\105\022\323\371\064\242\351 -\006\020\323\066 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\223\052\076\366\375\043\151\015\161\040\324\053\107\231\053\246 -END -CKA_ISSUER MULTILINE_OCTAL -\060\173\061\013\060\011\006\003\125\004\006\023\002\103\117\061 -\107\060\105\006\003\125\004\012\014\076\123\157\143\151\145\144 -\141\144\040\103\141\155\145\162\141\154\040\144\145\040\103\145 -\162\164\151\146\151\143\141\143\151\303\263\156\040\104\151\147 -\151\164\141\154\040\055\040\103\145\162\164\151\143\303\241\155 -\141\162\141\040\123\056\101\056\061\043\060\041\006\003\125\004 -\003\014\032\101\103\040\122\141\303\255\172\040\103\145\162\164 -\151\143\303\241\155\141\162\141\040\123\056\101\056 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\017\007\176\122\223\173\340\025\343\127\360\151\214\313\354 -\014 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - -# # Certificate "Deutsche Telekom Root CA 2" # # Issuer: CN=Deutsche Telekom Root CA 2,OU=T-TeleSec Trust Center,O=Deutsche Telekom AG,C=DE @@ -7382,136 +7055,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE # -# Certificate "ComSign CA" -# -# Issuer: C=IL,O=ComSign,CN=ComSign CA -# Serial Number:14:13:96:83:14:55:8c:ea:7b:63:e5:fc:34:87:77:44 -# Subject: C=IL,O=ComSign,CN=ComSign CA -# Not Valid Before: Wed Mar 24 11:32:18 2004 -# Not Valid After : Mon Mar 19 15:02:18 2029 -# Fingerprint (MD5): CD:F4:39:F3:B5:18:50:D7:3E:A4:C5:91:A0:3E:21:4B -# Fingerprint (SHA1): E1:A4:5B:14:1A:21:DA:1A:79:F4:1A:42:A9:61:D6:69:CD:06:34:C1 -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "ComSign CA" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\064\061\023\060\021\006\003\125\004\003\023\012\103\157\155 -\123\151\147\156\040\103\101\061\020\060\016\006\003\125\004\012 -\023\007\103\157\155\123\151\147\156\061\013\060\011\006\003\125 -\004\006\023\002\111\114 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\064\061\023\060\021\006\003\125\004\003\023\012\103\157\155 -\123\151\147\156\040\103\101\061\020\060\016\006\003\125\004\012 -\023\007\103\157\155\123\151\147\156\061\013\060\011\006\003\125 -\004\006\023\002\111\114 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\020\024\023\226\203\024\125\214\352\173\143\345\374\064\207 -\167\104 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\003\223\060\202\002\173\240\003\002\001\002\002\020\024 -\023\226\203\024\125\214\352\173\143\345\374\064\207\167\104\060 -\015\006\011\052\206\110\206\367\015\001\001\005\005\000\060\064 -\061\023\060\021\006\003\125\004\003\023\012\103\157\155\123\151 -\147\156\040\103\101\061\020\060\016\006\003\125\004\012\023\007 -\103\157\155\123\151\147\156\061\013\060\011\006\003\125\004\006 -\023\002\111\114\060\036\027\015\060\064\060\063\062\064\061\061 -\063\062\061\070\132\027\015\062\071\060\063\061\071\061\065\060 -\062\061\070\132\060\064\061\023\060\021\006\003\125\004\003\023 -\012\103\157\155\123\151\147\156\040\103\101\061\020\060\016\006 -\003\125\004\012\023\007\103\157\155\123\151\147\156\061\013\060 -\011\006\003\125\004\006\023\002\111\114\060\202\001\042\060\015 -\006\011\052\206\110\206\367\015\001\001\001\005\000\003\202\001 -\017\000\060\202\001\012\002\202\001\001\000\360\344\124\151\053 -\323\307\217\152\104\344\176\130\047\370\013\320\344\224\022\212 -\361\033\070\070\057\037\061\234\006\324\054\247\336\013\052\256 -\032\240\343\236\152\277\237\074\307\156\242\371\213\144\154\072 -\255\205\125\121\124\245\070\125\270\253\203\004\362\077\144\066 -\367\300\215\103\103\152\146\321\367\027\052\325\357\066\372\060 -\020\102\327\123\315\371\372\063\163\114\263\351\204\040\212\326 -\101\047\065\344\070\372\224\233\270\172\344\171\037\063\373\033 -\330\041\011\050\174\115\030\151\136\144\212\172\031\223\312\176 -\354\363\162\347\067\007\130\131\050\254\102\371\305\377\315\077 -\347\245\372\070\261\320\014\307\331\122\032\123\326\201\314\102 -\172\065\133\355\113\072\172\366\265\216\314\377\017\174\344\140 -\066\207\057\255\360\241\045\175\377\322\113\021\210\160\124\246 -\101\250\147\123\122\102\136\344\064\236\344\276\243\354\252\142 -\135\335\303\114\246\202\101\344\063\013\254\311\063\017\144\202 -\127\052\375\014\255\066\341\014\256\113\305\357\073\231\331\043 -\263\133\135\264\127\354\164\160\014\052\117\002\003\001\000\001 -\243\201\240\060\201\235\060\014\006\003\125\035\023\004\005\060 -\003\001\001\377\060\075\006\003\125\035\037\004\066\060\064\060 -\062\240\060\240\056\206\054\150\164\164\160\072\057\057\146\145 -\144\151\162\056\143\157\155\163\151\147\156\056\143\157\056\151 -\154\057\143\162\154\057\103\157\155\123\151\147\156\103\101\056 -\143\162\154\060\016\006\003\125\035\017\001\001\377\004\004\003 -\002\001\206\060\037\006\003\125\035\043\004\030\060\026\200\024 -\113\001\233\076\126\032\145\066\166\313\173\227\252\222\005\356 -\062\347\050\061\060\035\006\003\125\035\016\004\026\004\024\113 -\001\233\076\126\032\145\066\166\313\173\227\252\222\005\356\062 -\347\050\061\060\015\006\011\052\206\110\206\367\015\001\001\005 -\005\000\003\202\001\001\000\320\331\245\176\376\051\140\105\235 -\176\203\317\156\274\107\156\365\032\236\124\166\102\161\264\074 -\130\077\055\100\045\102\366\201\234\361\211\020\310\016\252\170 -\117\070\011\127\260\074\300\010\374\065\216\361\110\121\215\014 -\161\164\272\204\304\327\162\233\204\174\070\116\144\006\047\052 -\341\247\265\354\010\231\264\012\015\324\205\163\310\022\341\065 -\355\361\005\061\035\163\231\014\353\226\312\335\323\346\205\252 -\360\212\373\165\301\362\011\074\145\145\144\363\114\330\255\313 -\210\151\363\344\203\267\014\275\027\132\226\027\312\133\377\255 -\273\034\351\055\204\200\330\041\276\205\122\331\324\164\271\151 -\205\272\115\355\050\062\353\371\141\112\344\304\066\036\031\334 -\157\204\021\037\225\365\203\050\030\250\063\222\103\047\335\135 -\023\004\105\117\207\325\106\315\075\250\272\360\363\270\126\044 -\105\353\067\307\341\166\117\162\071\030\337\176\164\162\307\163 -\055\071\352\140\346\255\021\242\126\207\173\303\150\232\376\370 -\214\160\250\337\145\062\364\244\100\214\241\302\104\003\016\224 -\000\147\240\161\000\202\110 -END -CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE - -# Trust for Certificate "ComSign CA" -# Issuer: C=IL,O=ComSign,CN=ComSign CA -# Serial Number:14:13:96:83:14:55:8c:ea:7b:63:e5:fc:34:87:77:44 -# Subject: C=IL,O=ComSign,CN=ComSign CA -# Not Valid Before: Wed Mar 24 11:32:18 2004 -# Not Valid After : Mon Mar 19 15:02:18 2029 -# Fingerprint (MD5): CD:F4:39:F3:B5:18:50:D7:3E:A4:C5:91:A0:3E:21:4B -# Fingerprint (SHA1): E1:A4:5B:14:1A:21:DA:1A:79:F4:1A:42:A9:61:D6:69:CD:06:34:C1 -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "ComSign CA" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\341\244\133\024\032\041\332\032\171\364\032\102\251\141\326\151 -\315\006\064\301 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\315\364\071\363\265\030\120\327\076\244\305\221\240\076\041\113 -END -CKA_ISSUER MULTILINE_OCTAL -\060\064\061\023\060\021\006\003\125\004\003\023\012\103\157\155 -\123\151\147\156\040\103\101\061\020\060\016\006\003\125\004\012 -\023\007\103\157\155\123\151\147\156\061\013\060\011\006\003\125 -\004\006\023\002\111\114 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\020\024\023\226\203\024\125\214\352\173\143\345\374\064\207 -\167\104 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - -# # Certificate "Cybertrust Global Root" # # Issuer: CN=Cybertrust Global Root,O="Cybertrust, Inc" @@ -19149,707 +18692,6 @@ CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE # -# Certificate "Certplus Root CA G1" -# -# Issuer: CN=Certplus Root CA G1,O=Certplus,C=FR -# Serial Number:11:20:55:83:e4:2d:3e:54:56:85:2d:83:37:b7:2c:dc:46:11 -# Subject: CN=Certplus Root CA G1,O=Certplus,C=FR -# Not Valid Before: Mon May 26 00:00:00 2014 -# Not Valid After : Fri Jan 15 00:00:00 2038 -# Fingerprint (SHA-256): 15:2A:40:2B:FC:DF:2C:D5:48:05:4D:22:75:B3:9C:7F:CA:3E:C0:97:80:78:B0:F0:EA:76:E5:61:A6:C7:43:3E -# Fingerprint (SHA1): 22:FD:D0:B7:FD:A2:4E:0D:AC:49:2C:A0:AC:A6:7B:6A:1F:E3:F7:66 -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Certplus Root CA G1" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\076\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\021\060\017\006\003\125\004\012\014\010\103\145\162\164\160\154 -\165\163\061\034\060\032\006\003\125\004\003\014\023\103\145\162 -\164\160\154\165\163\040\122\157\157\164\040\103\101\040\107\061 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\076\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\021\060\017\006\003\125\004\012\014\010\103\145\162\164\160\154 -\165\163\061\034\060\032\006\003\125\004\003\014\023\103\145\162 -\164\160\154\165\163\040\122\157\157\164\040\103\101\040\107\061 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\022\021\040\125\203\344\055\076\124\126\205\055\203\067\267 -\054\334\106\021 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\005\153\060\202\003\123\240\003\002\001\002\002\022\021 -\040\125\203\344\055\076\124\126\205\055\203\067\267\054\334\106 -\021\060\015\006\011\052\206\110\206\367\015\001\001\015\005\000 -\060\076\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\021\060\017\006\003\125\004\012\014\010\103\145\162\164\160\154 -\165\163\061\034\060\032\006\003\125\004\003\014\023\103\145\162 -\164\160\154\165\163\040\122\157\157\164\040\103\101\040\107\061 -\060\036\027\015\061\064\060\065\062\066\060\060\060\060\060\060 -\132\027\015\063\070\060\061\061\065\060\060\060\060\060\060\132 -\060\076\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\021\060\017\006\003\125\004\012\014\010\103\145\162\164\160\154 -\165\163\061\034\060\032\006\003\125\004\003\014\023\103\145\162 -\164\160\154\165\163\040\122\157\157\164\040\103\101\040\107\061 -\060\202\002\042\060\015\006\011\052\206\110\206\367\015\001\001 -\001\005\000\003\202\002\017\000\060\202\002\012\002\202\002\001 -\000\332\120\207\266\332\270\251\076\235\144\372\126\063\232\126 -\075\026\345\003\225\262\064\034\232\155\142\005\324\330\217\347 -\211\144\237\272\333\144\213\144\346\171\052\141\315\257\217\132 -\211\221\145\271\130\374\264\003\137\221\077\055\020\025\340\176 -\317\274\374\177\103\147\250\255\136\066\043\330\230\263\115\363 -\103\236\071\174\052\374\354\210\325\210\356\160\275\205\026\055 -\352\113\211\074\243\161\102\376\034\375\323\034\055\020\270\206 -\124\352\103\270\333\306\207\332\250\256\200\045\317\172\046\035 -\252\221\260\110\157\256\265\336\236\330\327\372\000\375\306\217 -\320\121\273\142\175\244\261\214\262\377\040\021\272\065\143\005 -\206\107\140\103\063\220\366\107\242\003\117\226\115\235\117\301 -\352\352\234\242\376\064\056\336\267\312\033\166\244\267\255\237 -\351\250\324\170\077\170\376\362\070\011\066\035\322\026\002\310 -\354\052\150\257\365\216\224\357\055\023\172\036\102\112\035\025 -\061\256\014\004\127\374\141\163\363\061\126\206\061\200\240\304 -\021\156\060\166\343\224\360\137\004\304\254\207\162\211\230\305 -\235\314\127\010\232\364\014\374\175\172\005\072\372\107\200\071 -\266\317\204\023\167\157\047\352\377\226\147\027\010\155\351\015 -\326\043\120\060\260\025\164\023\076\345\057\377\016\315\304\013 -\112\135\360\330\000\063\111\146\353\241\030\174\131\056\075\050 -\271\141\161\313\265\245\272\270\352\334\342\160\157\010\152\334 -\207\147\064\357\337\060\162\335\363\311\077\043\377\065\341\276 -\041\051\040\060\201\344\031\245\040\351\045\312\163\061\164\051 -\276\342\102\325\363\262\046\146\307\150\375\031\263\347\040\223 -\231\350\135\340\136\207\347\106\350\045\234\012\051\044\324\315 -\130\206\122\100\044\262\173\017\230\022\040\044\366\220\154\107 -\310\015\273\030\040\056\331\375\374\213\362\051\352\207\164\225 -\340\102\120\170\204\004\101\141\260\364\041\043\217\055\313\050 -\041\362\152\154\364\032\246\305\024\264\067\145\117\225\375\200 -\310\370\162\345\045\153\304\140\261\173\155\216\112\212\163\316 -\131\373\160\172\163\006\023\331\323\164\067\044\101\012\021\157 -\227\334\347\344\176\241\275\025\362\272\207\017\075\150\212\026 -\007\002\003\001\000\001\243\143\060\141\060\016\006\003\125\035 -\017\001\001\377\004\004\003\002\001\006\060\017\006\003\125\035 -\023\001\001\377\004\005\060\003\001\001\377\060\035\006\003\125 -\035\016\004\026\004\024\250\301\300\233\221\250\103\025\174\135 -\006\047\264\052\121\330\227\013\201\261\060\037\006\003\125\035 -\043\004\030\060\026\200\024\250\301\300\233\221\250\103\025\174 -\135\006\047\264\052\121\330\227\013\201\261\060\015\006\011\052 -\206\110\206\367\015\001\001\015\005\000\003\202\002\001\000\234 -\126\157\001\176\321\275\114\365\212\306\360\046\037\344\340\070 -\030\314\062\303\051\073\235\101\051\064\141\306\327\360\000\241 -\353\244\162\217\224\027\274\023\054\165\264\127\356\012\174\011 -\172\334\325\312\241\320\064\023\370\167\253\237\345\376\330\036 -\164\212\205\007\217\177\314\171\172\312\226\315\315\375\117\373 -\375\043\015\220\365\364\136\323\306\141\175\236\021\340\002\356 -\011\004\331\007\335\246\212\267\014\203\044\273\203\120\222\376 -\140\165\021\076\330\235\260\212\172\265\340\235\233\313\220\122 -\113\260\223\052\324\076\026\063\345\236\306\145\025\076\144\073 -\004\077\333\014\217\137\134\035\151\037\257\363\351\041\214\363 -\357\227\366\232\267\031\266\204\164\234\243\124\265\160\116\143 -\330\127\135\123\041\233\100\222\103\372\326\167\125\063\117\144 -\325\373\320\054\152\216\155\045\246\357\205\350\002\304\123\076 -\271\236\207\274\314\065\032\336\241\351\212\143\207\145\036\021 -\052\333\143\167\227\024\276\232\024\231\021\262\300\356\260\117 -\370\024\041\062\103\117\237\253\242\313\250\017\252\073\006\125 -\306\022\051\127\010\324\067\327\207\047\255\111\131\247\221\253 -\104\172\136\215\160\333\227\316\110\120\261\163\223\366\360\203 -\140\371\315\361\341\061\375\133\174\161\041\143\024\024\252\257 -\305\336\223\176\150\261\354\042\242\252\220\165\236\265\103\162 -\352\144\243\204\113\375\014\250\046\153\161\227\356\126\143\146 -\350\102\124\371\307\035\337\320\217\133\337\310\060\157\210\376 -\015\304\063\034\123\250\243\375\110\020\362\344\012\116\341\025 -\127\374\156\144\060\302\125\021\334\352\251\315\112\124\254\051 -\143\104\317\112\100\240\326\150\131\033\063\371\357\072\213\333 -\040\222\334\102\204\277\001\253\207\300\325\040\202\333\306\271 -\203\205\102\134\017\103\073\152\111\065\325\230\364\025\277\372 -\141\201\014\011\040\030\322\320\027\014\313\110\000\120\351\166 -\202\214\144\327\072\240\007\125\314\036\061\300\357\072\264\145 -\373\343\277\102\153\236\017\250\275\153\230\334\330\333\313\213 -\244\335\327\131\364\156\335\376\252\303\221\320\056\102\007\300 -\014\115\123\315\044\261\114\133\036\121\364\337\351\222\372 -END -CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE - -# Trust for "Certplus Root CA G1" -# Issuer: CN=Certplus Root CA G1,O=Certplus,C=FR -# Serial Number:11:20:55:83:e4:2d:3e:54:56:85:2d:83:37:b7:2c:dc:46:11 -# Subject: CN=Certplus Root CA G1,O=Certplus,C=FR -# Not Valid Before: Mon May 26 00:00:00 2014 -# Not Valid After : Fri Jan 15 00:00:00 2038 -# Fingerprint (SHA-256): 15:2A:40:2B:FC:DF:2C:D5:48:05:4D:22:75:B3:9C:7F:CA:3E:C0:97:80:78:B0:F0:EA:76:E5:61:A6:C7:43:3E -# Fingerprint (SHA1): 22:FD:D0:B7:FD:A2:4E:0D:AC:49:2C:A0:AC:A6:7B:6A:1F:E3:F7:66 -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Certplus Root CA G1" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\042\375\320\267\375\242\116\015\254\111\054\240\254\246\173\152 -\037\343\367\146 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\177\011\234\367\331\271\134\151\151\126\325\067\076\024\015\102 -END -CKA_ISSUER MULTILINE_OCTAL -\060\076\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\021\060\017\006\003\125\004\012\014\010\103\145\162\164\160\154 -\165\163\061\034\060\032\006\003\125\004\003\014\023\103\145\162 -\164\160\154\165\163\040\122\157\157\164\040\103\101\040\107\061 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\022\021\040\125\203\344\055\076\124\126\205\055\203\067\267 -\054\334\106\021 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - -# -# Certificate "Certplus Root CA G2" -# -# Issuer: CN=Certplus Root CA G2,O=Certplus,C=FR -# Serial Number:11:20:d9:91:ce:ae:a3:e8:c5:e7:ff:e9:02:af:cf:73:bc:55 -# Subject: CN=Certplus Root CA G2,O=Certplus,C=FR -# Not Valid Before: Mon May 26 00:00:00 2014 -# Not Valid After : Fri Jan 15 00:00:00 2038 -# Fingerprint (SHA-256): 6C:C0:50:41:E6:44:5E:74:69:6C:4C:FB:C9:F8:0F:54:3B:7E:AB:BB:44:B4:CE:6F:78:7C:6A:99:71:C4:2F:17 -# Fingerprint (SHA1): 4F:65:8E:1F:E9:06:D8:28:02:E9:54:47:41:C9:54:25:5D:69:CC:1A -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Certplus Root CA G2" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\076\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\021\060\017\006\003\125\004\012\014\010\103\145\162\164\160\154 -\165\163\061\034\060\032\006\003\125\004\003\014\023\103\145\162 -\164\160\154\165\163\040\122\157\157\164\040\103\101\040\107\062 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\076\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\021\060\017\006\003\125\004\012\014\010\103\145\162\164\160\154 -\165\163\061\034\060\032\006\003\125\004\003\014\023\103\145\162 -\164\160\154\165\163\040\122\157\157\164\040\103\101\040\107\062 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\022\021\040\331\221\316\256\243\350\305\347\377\351\002\257 -\317\163\274\125 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\002\034\060\202\001\242\240\003\002\001\002\002\022\021 -\040\331\221\316\256\243\350\305\347\377\351\002\257\317\163\274 -\125\060\012\006\010\052\206\110\316\075\004\003\003\060\076\061 -\013\060\011\006\003\125\004\006\023\002\106\122\061\021\060\017 -\006\003\125\004\012\014\010\103\145\162\164\160\154\165\163\061 -\034\060\032\006\003\125\004\003\014\023\103\145\162\164\160\154 -\165\163\040\122\157\157\164\040\103\101\040\107\062\060\036\027 -\015\061\064\060\065\062\066\060\060\060\060\060\060\132\027\015 -\063\070\060\061\061\065\060\060\060\060\060\060\132\060\076\061 -\013\060\011\006\003\125\004\006\023\002\106\122\061\021\060\017 -\006\003\125\004\012\014\010\103\145\162\164\160\154\165\163\061 -\034\060\032\006\003\125\004\003\014\023\103\145\162\164\160\154 -\165\163\040\122\157\157\164\040\103\101\040\107\062\060\166\060 -\020\006\007\052\206\110\316\075\002\001\006\005\053\201\004\000 -\042\003\142\000\004\315\017\133\126\202\337\360\105\032\326\255 -\367\171\360\035\311\254\226\326\236\116\234\037\264\102\021\312 -\206\277\155\373\205\243\305\345\031\134\327\356\246\077\151\147 -\330\170\342\246\311\304\333\055\171\056\347\213\215\002\157\061 -\042\115\006\343\140\162\105\235\016\102\167\236\316\317\345\177 -\205\233\030\344\374\314\056\162\323\026\223\116\312\231\143\134 -\241\005\052\154\006\243\143\060\141\060\016\006\003\125\035\017 -\001\001\377\004\004\003\002\001\006\060\017\006\003\125\035\023 -\001\001\377\004\005\060\003\001\001\377\060\035\006\003\125\035 -\016\004\026\004\024\332\203\143\002\171\216\332\114\306\074\043 -\024\330\217\303\040\253\050\140\131\060\037\006\003\125\035\043 -\004\030\060\026\200\024\332\203\143\002\171\216\332\114\306\074 -\043\024\330\217\303\040\253\050\140\131\060\012\006\010\052\206 -\110\316\075\004\003\003\003\150\000\060\145\002\060\160\376\260 -\013\331\367\203\227\354\363\125\035\324\334\263\006\016\376\063 -\230\235\213\071\220\153\224\041\355\266\327\135\326\114\327\041 -\247\347\277\041\017\053\315\367\052\334\205\007\235\002\061\000 -\206\024\026\345\334\260\145\302\300\216\024\237\277\044\026\150 -\345\274\371\171\151\334\255\105\053\367\266\061\163\314\006\245 -\123\223\221\032\223\256\160\152\147\272\327\236\345\141\032\137 -END -CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE - -# Trust for "Certplus Root CA G2" -# Issuer: CN=Certplus Root CA G2,O=Certplus,C=FR -# Serial Number:11:20:d9:91:ce:ae:a3:e8:c5:e7:ff:e9:02:af:cf:73:bc:55 -# Subject: CN=Certplus Root CA G2,O=Certplus,C=FR -# Not Valid Before: Mon May 26 00:00:00 2014 -# Not Valid After : Fri Jan 15 00:00:00 2038 -# Fingerprint (SHA-256): 6C:C0:50:41:E6:44:5E:74:69:6C:4C:FB:C9:F8:0F:54:3B:7E:AB:BB:44:B4:CE:6F:78:7C:6A:99:71:C4:2F:17 -# Fingerprint (SHA1): 4F:65:8E:1F:E9:06:D8:28:02:E9:54:47:41:C9:54:25:5D:69:CC:1A -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "Certplus Root CA G2" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\117\145\216\037\351\006\330\050\002\351\124\107\101\311\124\045 -\135\151\314\032 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\247\356\304\170\055\033\356\055\271\051\316\326\247\226\062\061 -END -CKA_ISSUER MULTILINE_OCTAL -\060\076\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\021\060\017\006\003\125\004\012\014\010\103\145\162\164\160\154 -\165\163\061\034\060\032\006\003\125\004\003\014\023\103\145\162 -\164\160\154\165\163\040\122\157\157\164\040\103\101\040\107\062 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\022\021\040\331\221\316\256\243\350\305\347\377\351\002\257 -\317\163\274\125 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - -# -# Certificate "OpenTrust Root CA G1" -# -# Issuer: CN=OpenTrust Root CA G1,O=OpenTrust,C=FR -# Serial Number:11:20:b3:90:55:39:7d:7f:36:6d:64:c2:a7:9f:6b:63:8e:67 -# Subject: CN=OpenTrust Root CA G1,O=OpenTrust,C=FR -# Not Valid Before: Mon May 26 08:45:50 2014 -# Not Valid After : Fri Jan 15 00:00:00 2038 -# Fingerprint (SHA-256): 56:C7:71:28:D9:8C:18:D9:1B:4C:FD:FF:BC:25:EE:91:03:D4:75:8E:A2:AB:AD:82:6A:90:F3:45:7D:46:0E:B4 -# Fingerprint (SHA1): 79:91:E8:34:F7:E2:EE:DD:08:95:01:52:E9:55:2D:14:E9:58:D5:7E -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "OpenTrust Root CA G1" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\100\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\022\060\020\006\003\125\004\012\014\011\117\160\145\156\124\162 -\165\163\164\061\035\060\033\006\003\125\004\003\014\024\117\160 -\145\156\124\162\165\163\164\040\122\157\157\164\040\103\101\040 -\107\061 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\100\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\022\060\020\006\003\125\004\012\014\011\117\160\145\156\124\162 -\165\163\164\061\035\060\033\006\003\125\004\003\014\024\117\160 -\145\156\124\162\165\163\164\040\122\157\157\164\040\103\101\040 -\107\061 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\022\021\040\263\220\125\071\175\177\066\155\144\302\247\237 -\153\143\216\147 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\005\157\060\202\003\127\240\003\002\001\002\002\022\021 -\040\263\220\125\071\175\177\066\155\144\302\247\237\153\143\216 -\147\060\015\006\011\052\206\110\206\367\015\001\001\013\005\000 -\060\100\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\022\060\020\006\003\125\004\012\014\011\117\160\145\156\124\162 -\165\163\164\061\035\060\033\006\003\125\004\003\014\024\117\160 -\145\156\124\162\165\163\164\040\122\157\157\164\040\103\101\040 -\107\061\060\036\027\015\061\064\060\065\062\066\060\070\064\065 -\065\060\132\027\015\063\070\060\061\061\065\060\060\060\060\060 -\060\132\060\100\061\013\060\011\006\003\125\004\006\023\002\106 -\122\061\022\060\020\006\003\125\004\012\014\011\117\160\145\156 -\124\162\165\163\164\061\035\060\033\006\003\125\004\003\014\024 -\117\160\145\156\124\162\165\163\164\040\122\157\157\164\040\103 -\101\040\107\061\060\202\002\042\060\015\006\011\052\206\110\206 -\367\015\001\001\001\005\000\003\202\002\017\000\060\202\002\012 -\002\202\002\001\000\370\171\106\332\226\305\060\136\212\161\003 -\055\160\244\273\260\305\010\334\315\346\065\300\200\244\021\055 -\335\346\207\256\135\075\221\322\207\154\067\267\332\142\236\233 -\302\044\327\217\361\333\246\246\337\106\157\121\246\161\313\076 -\033\061\147\142\367\021\133\064\047\325\171\116\214\233\130\275 -\042\020\015\134\047\014\335\060\345\250\323\135\041\070\164\027 -\376\343\037\266\117\073\153\055\333\175\140\037\214\175\114\005 -\302\353\001\026\025\230\024\216\321\220\167\042\077\354\302\071 -\270\171\072\360\111\044\342\225\221\334\141\064\222\214\124\164 -\357\261\175\214\001\342\070\175\301\137\152\137\044\262\216\142 -\027\255\171\040\255\253\035\267\340\264\226\110\117\146\103\020 -\006\026\044\003\341\340\234\216\306\106\117\216\032\231\341\217 -\271\216\063\154\151\336\130\255\240\016\247\144\124\021\151\104 -\146\117\114\022\247\216\054\175\304\324\133\305\000\064\060\301 -\331\231\376\062\316\007\204\264\116\315\012\377\066\115\142\361 -\247\143\127\344\333\152\247\256\277\053\271\311\346\262\047\211 -\345\176\232\034\115\150\306\301\030\336\063\053\121\106\113\034 -\216\367\075\014\371\212\064\024\304\373\063\065\043\361\314\361 -\052\307\245\273\260\242\316\376\123\153\115\101\033\146\050\262 -\226\372\247\256\012\116\271\071\063\104\234\164\301\223\034\370 -\340\236\044\045\103\361\233\043\202\252\337\054\040\260\334\066 -\116\003\263\174\002\324\346\173\032\252\207\023\277\076\241\164 -\273\233\016\341\300\223\237\327\244\146\312\273\033\073\343\060 -\364\063\131\212\007\162\003\125\347\163\152\003\061\156\157\226 -\033\343\242\237\257\222\307\355\365\102\267\045\114\073\023\004 -\317\034\226\257\034\042\243\320\253\005\262\114\022\043\122\334 -\375\031\133\047\234\036\073\172\375\102\043\333\043\200\023\360 -\274\121\025\124\224\246\167\076\320\164\121\275\121\024\010\071 -\067\313\037\064\251\060\235\122\204\056\125\220\261\272\337\125 -\000\013\330\126\055\261\111\111\162\200\251\142\327\300\366\030 -\021\004\125\315\164\173\317\141\160\171\364\173\054\134\134\222 -\374\345\270\132\253\114\223\225\241\047\356\245\276\317\161\043 -\102\272\233\166\055\002\003\001\000\001\243\143\060\141\060\016 -\006\003\125\035\017\001\001\377\004\004\003\002\001\006\060\017 -\006\003\125\035\023\001\001\377\004\005\060\003\001\001\377\060 -\035\006\003\125\035\016\004\026\004\024\227\106\041\127\041\065 -\332\066\125\307\363\361\067\160\345\010\366\223\051\266\060\037 -\006\003\125\035\043\004\030\060\026\200\024\227\106\041\127\041 -\065\332\066\125\307\363\361\067\160\345\010\366\223\051\266\060 -\015\006\011\052\206\110\206\367\015\001\001\013\005\000\003\202 -\002\001\000\035\335\002\140\174\340\065\247\346\230\173\352\104 -\316\147\100\117\362\223\156\146\324\071\211\046\254\323\115\004 -\074\273\207\041\077\067\364\161\045\332\113\272\253\226\202\201 -\221\266\355\331\261\244\145\227\342\157\144\131\244\226\356\140 -\312\037\043\373\105\272\377\217\044\360\312\251\061\177\171\037 -\200\263\055\062\272\144\147\140\257\271\131\315\337\232\111\323 -\250\202\261\371\230\224\212\314\340\273\340\004\033\231\140\261 -\106\145\334\010\242\262\106\236\104\210\352\223\176\127\026\322 -\025\162\137\056\113\253\324\235\143\270\343\110\345\376\204\056 -\130\012\237\103\035\376\267\030\222\206\103\113\016\234\062\206 -\054\140\365\351\110\352\225\355\160\051\361\325\057\375\065\264 -\127\317\333\205\110\231\271\302\157\154\217\315\170\225\254\144 -\050\375\126\260\303\157\303\276\131\122\341\137\204\217\200\362 -\364\015\066\255\166\263\243\265\341\144\166\072\130\334\175\117 -\136\126\154\345\125\131\127\245\337\361\212\146\060\214\324\122 -\142\070\167\264\276\050\327\312\066\304\233\005\360\370\025\333 -\333\361\357\064\235\035\170\112\210\126\147\156\140\377\217\310 -\213\341\216\275\102\251\063\012\131\102\022\022\052\372\261\235 -\103\216\005\233\231\332\142\255\127\066\263\035\266\015\171\055 -\226\270\353\362\014\113\014\245\224\306\060\247\046\031\055\355 -\114\006\120\060\361\375\130\075\271\113\027\137\031\264\152\204 -\124\264\070\117\071\242\015\226\150\303\050\224\375\355\055\037 -\112\153\103\226\056\220\001\020\373\070\246\201\013\320\277\165 -\323\324\271\316\361\077\157\016\034\036\067\161\345\030\207\165 -\031\077\120\271\136\244\105\064\255\260\312\346\345\023\166\017 -\061\024\251\216\055\224\326\325\205\115\163\025\117\113\362\262 -\076\355\154\275\375\016\235\146\163\260\075\264\367\277\250\340 -\021\244\304\256\165\011\112\143\000\110\040\246\306\235\013\011 -\212\264\340\346\316\076\307\076\046\070\351\053\336\246\010\111 -\003\004\220\212\351\217\277\350\266\264\052\243\043\215\034\034 -\262\071\222\250\217\002\134\100\071\165\324\163\101\002\167\336 -\315\340\103\207\326\344\272\112\303\154\022\177\376\052\346\043 -\326\214\161 -END -CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE - -# Trust for "OpenTrust Root CA G1" -# Issuer: CN=OpenTrust Root CA G1,O=OpenTrust,C=FR -# Serial Number:11:20:b3:90:55:39:7d:7f:36:6d:64:c2:a7:9f:6b:63:8e:67 -# Subject: CN=OpenTrust Root CA G1,O=OpenTrust,C=FR -# Not Valid Before: Mon May 26 08:45:50 2014 -# Not Valid After : Fri Jan 15 00:00:00 2038 -# Fingerprint (SHA-256): 56:C7:71:28:D9:8C:18:D9:1B:4C:FD:FF:BC:25:EE:91:03:D4:75:8E:A2:AB:AD:82:6A:90:F3:45:7D:46:0E:B4 -# Fingerprint (SHA1): 79:91:E8:34:F7:E2:EE:DD:08:95:01:52:E9:55:2D:14:E9:58:D5:7E -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "OpenTrust Root CA G1" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\171\221\350\064\367\342\356\335\010\225\001\122\351\125\055\024 -\351\130\325\176 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\166\000\314\201\051\315\125\136\210\152\172\056\367\115\071\332 -END -CKA_ISSUER MULTILINE_OCTAL -\060\100\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\022\060\020\006\003\125\004\012\014\011\117\160\145\156\124\162 -\165\163\164\061\035\060\033\006\003\125\004\003\014\024\117\160 -\145\156\124\162\165\163\164\040\122\157\157\164\040\103\101\040 -\107\061 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\022\021\040\263\220\125\071\175\177\066\155\144\302\247\237 -\153\143\216\147 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - -# -# Certificate "OpenTrust Root CA G2" -# -# Issuer: CN=OpenTrust Root CA G2,O=OpenTrust,C=FR -# Serial Number:11:20:a1:69:1b:bf:bd:b9:bd:52:96:8f:23:e8:48:bf:26:11 -# Subject: CN=OpenTrust Root CA G2,O=OpenTrust,C=FR -# Not Valid Before: Mon May 26 00:00:00 2014 -# Not Valid After : Fri Jan 15 00:00:00 2038 -# Fingerprint (SHA-256): 27:99:58:29:FE:6A:75:15:C1:BF:E8:48:F9:C4:76:1D:B1:6C:22:59:29:25:7B:F4:0D:08:94:F2:9E:A8:BA:F2 -# Fingerprint (SHA1): 79:5F:88:60:C5:AB:7C:3D:92:E6:CB:F4:8D:E1:45:CD:11:EF:60:0B -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "OpenTrust Root CA G2" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\100\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\022\060\020\006\003\125\004\012\014\011\117\160\145\156\124\162 -\165\163\164\061\035\060\033\006\003\125\004\003\014\024\117\160 -\145\156\124\162\165\163\164\040\122\157\157\164\040\103\101\040 -\107\062 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\100\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\022\060\020\006\003\125\004\012\014\011\117\160\145\156\124\162 -\165\163\164\061\035\060\033\006\003\125\004\003\014\024\117\160 -\145\156\124\162\165\163\164\040\122\157\157\164\040\103\101\040 -\107\062 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\022\021\040\241\151\033\277\275\271\275\122\226\217\043\350 -\110\277\046\021 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\005\157\060\202\003\127\240\003\002\001\002\002\022\021 -\040\241\151\033\277\275\271\275\122\226\217\043\350\110\277\046 -\021\060\015\006\011\052\206\110\206\367\015\001\001\015\005\000 -\060\100\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\022\060\020\006\003\125\004\012\014\011\117\160\145\156\124\162 -\165\163\164\061\035\060\033\006\003\125\004\003\014\024\117\160 -\145\156\124\162\165\163\164\040\122\157\157\164\040\103\101\040 -\107\062\060\036\027\015\061\064\060\065\062\066\060\060\060\060 -\060\060\132\027\015\063\070\060\061\061\065\060\060\060\060\060 -\060\132\060\100\061\013\060\011\006\003\125\004\006\023\002\106 -\122\061\022\060\020\006\003\125\004\012\014\011\117\160\145\156 -\124\162\165\163\164\061\035\060\033\006\003\125\004\003\014\024 -\117\160\145\156\124\162\165\163\164\040\122\157\157\164\040\103 -\101\040\107\062\060\202\002\042\060\015\006\011\052\206\110\206 -\367\015\001\001\001\005\000\003\202\002\017\000\060\202\002\012 -\002\202\002\001\000\314\266\127\245\063\224\020\201\062\123\337 -\141\176\017\166\071\317\134\302\123\165\035\111\172\226\070\335 -\242\163\152\361\157\336\136\242\132\271\161\041\276\066\331\241 -\374\274\356\154\250\174\064\032\161\032\350\032\330\137\016\104 -\006\355\247\340\363\322\141\013\340\062\242\226\321\070\360\302 -\332\001\027\374\344\254\117\350\356\211\036\164\253\117\277\036 -\011\266\066\152\126\363\341\356\226\211\146\044\006\344\315\102 -\072\112\335\340\232\260\304\202\105\263\376\311\253\134\174\076 -\311\353\027\057\014\175\156\256\245\217\310\254\045\012\157\372 -\325\105\230\322\065\011\366\003\103\224\376\331\277\040\225\171 -\200\230\212\331\211\065\273\121\033\244\067\175\374\231\073\253 -\377\277\254\015\217\103\261\231\173\026\020\176\035\157\107\304 -\025\217\004\226\010\006\102\004\370\204\326\035\274\221\246\102 -\276\111\325\152\210\077\274\055\121\321\236\215\340\122\314\127 -\335\065\065\130\333\264\217\044\210\344\213\337\334\153\124\322 -\201\053\262\316\222\113\034\037\106\372\035\330\222\313\166\147 -\265\011\231\011\345\254\027\024\125\160\306\074\240\126\012\003 -\263\334\142\031\337\310\265\060\177\365\074\046\165\021\275\327 -\033\263\207\236\007\257\145\161\345\240\317\032\247\011\020\035 -\223\211\146\133\350\074\142\062\265\265\072\156\351\205\001\213 -\236\103\214\147\163\050\131\133\353\343\334\054\314\245\046\162 -\142\022\264\346\234\203\104\366\121\244\342\300\172\044\127\312 -\016\245\077\072\265\073\213\345\166\356\160\346\222\336\026\134 -\050\133\227\031\047\222\376\172\222\124\316\223\071\012\026\207 -\274\143\263\365\261\223\134\340\156\267\320\352\371\142\062\210 -\104\373\277\047\050\266\060\225\135\022\050\271\225\276\217\123 -\030\345\242\030\026\342\126\244\262\054\020\365\035\067\246\370 -\267\366\320\131\134\211\367\302\325\265\224\164\321\325\376\033 -\266\360\346\326\036\173\322\074\313\250\343\365\030\363\041\037 -\156\357\115\150\006\173\055\135\156\103\211\246\300\371\240\277 -\202\036\317\123\177\264\353\054\333\135\366\152\175\100\044\005 -\162\211\070\001\223\313\161\302\071\135\006\021\366\157\170\370 -\067\015\071\204\047\002\003\001\000\001\243\143\060\141\060\016 -\006\003\125\035\017\001\001\377\004\004\003\002\001\006\060\017 -\006\003\125\035\023\001\001\377\004\005\060\003\001\001\377\060 -\035\006\003\125\035\016\004\026\004\024\152\071\372\102\042\367 -\346\211\000\115\136\175\063\203\313\270\156\167\206\257\060\037 -\006\003\125\035\043\004\030\060\026\200\024\152\071\372\102\042 -\367\346\211\000\115\136\175\063\203\313\270\156\167\206\257\060 -\015\006\011\052\206\110\206\367\015\001\001\015\005\000\003\202 -\002\001\000\230\313\253\100\074\345\063\002\227\177\055\207\246 -\217\324\136\112\257\270\036\347\273\161\373\200\144\045\251\263 -\032\076\150\135\047\046\247\272\052\341\360\127\203\012\144\117 -\036\042\164\033\351\220\137\360\254\317\377\117\150\172\070\244 -\020\154\015\261\307\244\167\200\030\266\242\050\104\166\247\064 -\235\161\204\057\312\131\322\107\210\231\101\042\311\060\230\141 -\156\075\250\250\005\155\321\037\300\121\104\126\177\047\065\002 -\335\136\230\012\102\353\060\277\215\241\233\121\252\073\352\223 -\106\144\305\000\171\336\041\153\366\127\240\206\327\006\162\354 -\160\106\113\213\163\335\240\041\165\076\334\035\300\217\323\117 -\163\034\205\331\376\177\142\310\225\157\266\323\173\214\272\123 -\302\157\233\104\114\171\320\035\160\263\327\237\002\364\262\007 -\260\307\345\370\255\043\016\246\126\311\051\022\167\110\331\057 -\106\375\073\360\374\164\160\222\245\216\070\010\037\144\060\266 -\267\113\373\066\254\020\216\240\122\063\143\235\003\065\126\305 -\151\275\306\043\132\047\224\366\244\022\370\055\063\074\241\126 -\245\137\326\031\351\355\174\010\275\167\315\047\144\314\224\332 -\116\106\120\207\340\371\301\123\200\036\273\255\373\107\122\213 -\033\375\242\371\336\016\042\267\075\063\131\154\324\336\365\225 -\006\062\015\121\031\101\134\076\117\006\367\271\053\200\047\366 -\243\252\172\174\006\341\103\303\023\071\142\032\066\275\340\050 -\056\224\002\344\051\056\140\125\256\100\075\260\164\222\136\360 -\040\144\226\077\137\105\135\210\265\212\332\002\240\133\105\124 -\336\070\075\011\300\250\112\145\106\026\374\252\277\124\116\115 -\133\276\070\103\267\050\312\213\063\252\032\045\272\045\134\051 -\057\133\112\156\214\352\055\234\052\366\005\166\340\167\227\200 -\210\335\147\023\157\035\150\044\213\117\267\164\201\345\364\140 -\237\172\125\327\076\067\332\026\153\076\167\254\256\030\160\225 -\010\171\051\003\212\376\301\073\263\077\032\017\244\073\136\037 -\130\241\225\311\253\057\163\112\320\055\156\232\131\017\125\030 -\170\055\074\121\246\227\213\346\273\262\160\252\114\021\336\377 -\174\053\067\324\172\321\167\064\217\347\371\102\367\074\201\014 -\113\122\012 -END -CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE - -# Trust for "OpenTrust Root CA G2" -# Issuer: CN=OpenTrust Root CA G2,O=OpenTrust,C=FR -# Serial Number:11:20:a1:69:1b:bf:bd:b9:bd:52:96:8f:23:e8:48:bf:26:11 -# Subject: CN=OpenTrust Root CA G2,O=OpenTrust,C=FR -# Not Valid Before: Mon May 26 00:00:00 2014 -# Not Valid After : Fri Jan 15 00:00:00 2038 -# Fingerprint (SHA-256): 27:99:58:29:FE:6A:75:15:C1:BF:E8:48:F9:C4:76:1D:B1:6C:22:59:29:25:7B:F4:0D:08:94:F2:9E:A8:BA:F2 -# Fingerprint (SHA1): 79:5F:88:60:C5:AB:7C:3D:92:E6:CB:F4:8D:E1:45:CD:11:EF:60:0B -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "OpenTrust Root CA G2" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\171\137\210\140\305\253\174\075\222\346\313\364\215\341\105\315 -\021\357\140\013 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\127\044\266\131\044\153\256\310\376\034\014\040\362\300\116\353 -END -CKA_ISSUER MULTILINE_OCTAL -\060\100\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\022\060\020\006\003\125\004\012\014\011\117\160\145\156\124\162 -\165\163\164\061\035\060\033\006\003\125\004\003\014\024\117\160 -\145\156\124\162\165\163\164\040\122\157\157\164\040\103\101\040 -\107\062 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\022\021\040\241\151\033\277\275\271\275\122\226\217\043\350 -\110\277\046\021 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - -# -# Certificate "OpenTrust Root CA G3" -# -# Issuer: CN=OpenTrust Root CA G3,O=OpenTrust,C=FR -# Serial Number:11:20:e6:f8:4c:fc:24:b0:be:05:40:ac:da:83:1b:34:60:3f -# Subject: CN=OpenTrust Root CA G3,O=OpenTrust,C=FR -# Not Valid Before: Mon May 26 00:00:00 2014 -# Not Valid After : Fri Jan 15 00:00:00 2038 -# Fingerprint (SHA-256): B7:C3:62:31:70:6E:81:07:8C:36:7C:B8:96:19:8F:1E:32:08:DD:92:69:49:DD:8F:57:09:A4:10:F7:5B:62:92 -# Fingerprint (SHA1): 6E:26:64:F3:56:BF:34:55:BF:D1:93:3F:7C:01:DE:D8:13:DA:8A:A6 -CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "OpenTrust Root CA G3" -CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 -CKA_SUBJECT MULTILINE_OCTAL -\060\100\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\022\060\020\006\003\125\004\012\014\011\117\160\145\156\124\162 -\165\163\164\061\035\060\033\006\003\125\004\003\014\024\117\160 -\145\156\124\162\165\163\164\040\122\157\157\164\040\103\101\040 -\107\063 -END -CKA_ID UTF8 "0" -CKA_ISSUER MULTILINE_OCTAL -\060\100\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\022\060\020\006\003\125\004\012\014\011\117\160\145\156\124\162 -\165\163\164\061\035\060\033\006\003\125\004\003\014\024\117\160 -\145\156\124\162\165\163\164\040\122\157\157\164\040\103\101\040 -\107\063 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\022\021\040\346\370\114\374\044\260\276\005\100\254\332\203 -\033\064\140\077 -END -CKA_VALUE MULTILINE_OCTAL -\060\202\002\041\060\202\001\246\240\003\002\001\002\002\022\021 -\040\346\370\114\374\044\260\276\005\100\254\332\203\033\064\140 -\077\060\012\006\010\052\206\110\316\075\004\003\003\060\100\061 -\013\060\011\006\003\125\004\006\023\002\106\122\061\022\060\020 -\006\003\125\004\012\014\011\117\160\145\156\124\162\165\163\164 -\061\035\060\033\006\003\125\004\003\014\024\117\160\145\156\124 -\162\165\163\164\040\122\157\157\164\040\103\101\040\107\063\060 -\036\027\015\061\064\060\065\062\066\060\060\060\060\060\060\132 -\027\015\063\070\060\061\061\065\060\060\060\060\060\060\132\060 -\100\061\013\060\011\006\003\125\004\006\023\002\106\122\061\022 -\060\020\006\003\125\004\012\014\011\117\160\145\156\124\162\165 -\163\164\061\035\060\033\006\003\125\004\003\014\024\117\160\145 -\156\124\162\165\163\164\040\122\157\157\164\040\103\101\040\107 -\063\060\166\060\020\006\007\052\206\110\316\075\002\001\006\005 -\053\201\004\000\042\003\142\000\004\112\356\130\256\115\312\146 -\336\006\072\243\021\374\340\030\360\156\034\272\055\060\014\211 -\331\326\356\233\163\203\251\043\025\214\057\131\212\132\335\024 -\352\235\131\053\103\267\006\354\062\266\272\356\101\265\255\135 -\241\205\314\352\035\024\146\243\147\176\106\342\224\363\347\266 -\126\241\025\131\241\117\067\227\271\042\036\275\021\353\364\262 -\037\136\303\024\232\345\331\227\231\243\143\060\141\060\016\006 -\003\125\035\017\001\001\377\004\004\003\002\001\006\060\017\006 -\003\125\035\023\001\001\377\004\005\060\003\001\001\377\060\035 -\006\003\125\035\016\004\026\004\024\107\167\303\024\213\142\071 -\014\311\157\341\120\115\320\020\130\334\225\210\155\060\037\006 -\003\125\035\043\004\030\060\026\200\024\107\167\303\024\213\142 -\071\014\311\157\341\120\115\320\020\130\334\225\210\155\060\012 -\006\010\052\206\110\316\075\004\003\003\003\151\000\060\146\002 -\061\000\217\250\334\235\272\014\004\027\372\025\351\075\057\051 -\001\227\277\201\026\063\100\223\154\374\371\355\200\160\157\252 -\217\333\204\302\213\365\065\312\006\334\144\157\150\026\341\217 -\221\271\002\061\000\330\113\245\313\302\320\010\154\351\030\373 -\132\335\115\137\044\013\260\000\041\045\357\217\247\004\046\161 -\342\174\151\345\135\232\370\101\037\073\071\223\223\235\125\352 -\315\215\361\373\301 -END -CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE - -# Trust for "OpenTrust Root CA G3" -# Issuer: CN=OpenTrust Root CA G3,O=OpenTrust,C=FR -# Serial Number:11:20:e6:f8:4c:fc:24:b0:be:05:40:ac:da:83:1b:34:60:3f -# Subject: CN=OpenTrust Root CA G3,O=OpenTrust,C=FR -# Not Valid Before: Mon May 26 00:00:00 2014 -# Not Valid After : Fri Jan 15 00:00:00 2038 -# Fingerprint (SHA-256): B7:C3:62:31:70:6E:81:07:8C:36:7C:B8:96:19:8F:1E:32:08:DD:92:69:49:DD:8F:57:09:A4:10:F7:5B:62:92 -# Fingerprint (SHA1): 6E:26:64:F3:56:BF:34:55:BF:D1:93:3F:7C:01:DE:D8:13:DA:8A:A6 -CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST -CKA_TOKEN CK_BBOOL CK_TRUE -CKA_PRIVATE CK_BBOOL CK_FALSE -CKA_MODIFIABLE CK_BBOOL CK_FALSE -CKA_LABEL UTF8 "OpenTrust Root CA G3" -CKA_CERT_SHA1_HASH MULTILINE_OCTAL -\156\046\144\363\126\277\064\125\277\321\223\077\174\001\336\330 -\023\332\212\246 -END -CKA_CERT_MD5_HASH MULTILINE_OCTAL -\041\067\264\027\026\222\173\147\106\160\251\226\327\250\023\044 -END -CKA_ISSUER MULTILINE_OCTAL -\060\100\061\013\060\011\006\003\125\004\006\023\002\106\122\061 -\022\060\020\006\003\125\004\012\014\011\117\160\145\156\124\162 -\165\163\164\061\035\060\033\006\003\125\004\003\014\024\117\160 -\145\156\124\162\165\163\164\040\122\157\157\164\040\103\101\040 -\107\063 -END -CKA_SERIAL_NUMBER MULTILINE_OCTAL -\002\022\021\040\346\370\114\374\044\260\276\005\100\254\332\203 -\033\064\140\077 -END -CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR -CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST -CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE - -# # Certificate "ISRG Root X1" # # Issuer: CN=ISRG Root X1,O=Internet Security Research Group,C=US @@ -22993,3 +21835,1321 @@ CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE + +# +# Certificate "GlobalSign Root CA - R6" +# +# Issuer: CN=GlobalSign,O=GlobalSign,OU=GlobalSign Root CA - R6 +# Serial Number:45:e6:bb:03:83:33:c3:85:65:48:e6:ff:45:51 +# Subject: CN=GlobalSign,O=GlobalSign,OU=GlobalSign Root CA - R6 +# Not Valid Before: Wed Dec 10 00:00:00 2014 +# Not Valid After : Sun Dec 10 00:00:00 2034 +# Fingerprint (SHA-256): 2C:AB:EA:FE:37:D0:6C:A2:2A:BA:73:91:C0:03:3D:25:98:29:52:C4:53:64:73:49:76:3A:3A:B5:AD:6C:CF:69 +# Fingerprint (SHA1): 80:94:64:0E:B5:A7:A1:CA:11:9C:1F:DD:D5:9F:81:02:63:A7:FB:D1 +CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "GlobalSign Root CA - R6" +CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 +CKA_SUBJECT MULTILINE_OCTAL +\060\114\061\040\060\036\006\003\125\004\013\023\027\107\154\157 +\142\141\154\123\151\147\156\040\122\157\157\164\040\103\101\040 +\055\040\122\066\061\023\060\021\006\003\125\004\012\023\012\107 +\154\157\142\141\154\123\151\147\156\061\023\060\021\006\003\125 +\004\003\023\012\107\154\157\142\141\154\123\151\147\156 +END +CKA_ID UTF8 "0" +CKA_ISSUER MULTILINE_OCTAL +\060\114\061\040\060\036\006\003\125\004\013\023\027\107\154\157 +\142\141\154\123\151\147\156\040\122\157\157\164\040\103\101\040 +\055\040\122\066\061\023\060\021\006\003\125\004\012\023\012\107 +\154\157\142\141\154\123\151\147\156\061\023\060\021\006\003\125 +\004\003\023\012\107\154\157\142\141\154\123\151\147\156 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\016\105\346\273\003\203\063\303\205\145\110\346\377\105\121 +END +CKA_VALUE MULTILINE_OCTAL +\060\202\005\203\060\202\003\153\240\003\002\001\002\002\016\105 +\346\273\003\203\063\303\205\145\110\346\377\105\121\060\015\006 +\011\052\206\110\206\367\015\001\001\014\005\000\060\114\061\040 +\060\036\006\003\125\004\013\023\027\107\154\157\142\141\154\123 +\151\147\156\040\122\157\157\164\040\103\101\040\055\040\122\066 +\061\023\060\021\006\003\125\004\012\023\012\107\154\157\142\141 +\154\123\151\147\156\061\023\060\021\006\003\125\004\003\023\012 +\107\154\157\142\141\154\123\151\147\156\060\036\027\015\061\064 +\061\062\061\060\060\060\060\060\060\060\132\027\015\063\064\061 +\062\061\060\060\060\060\060\060\060\132\060\114\061\040\060\036 +\006\003\125\004\013\023\027\107\154\157\142\141\154\123\151\147 +\156\040\122\157\157\164\040\103\101\040\055\040\122\066\061\023 +\060\021\006\003\125\004\012\023\012\107\154\157\142\141\154\123 +\151\147\156\061\023\060\021\006\003\125\004\003\023\012\107\154 +\157\142\141\154\123\151\147\156\060\202\002\042\060\015\006\011 +\052\206\110\206\367\015\001\001\001\005\000\003\202\002\017\000 +\060\202\002\012\002\202\002\001\000\225\007\350\163\312\146\371 +\354\024\312\173\074\367\015\010\361\264\105\013\054\202\264\110 +\306\353\133\074\256\203\270\101\222\063\024\244\157\177\351\052 +\314\306\260\210\153\305\266\211\321\306\262\377\024\316\121\024 +\041\354\112\335\033\132\306\326\207\356\115\072\025\006\355\144 +\146\013\222\200\312\104\336\163\224\116\363\247\211\177\117\170 +\143\010\310\022\120\155\102\146\057\115\271\171\050\115\122\032 +\212\032\200\267\031\201\016\176\304\212\274\144\114\041\034\103 +\150\327\075\074\212\305\262\146\325\220\232\267\061\006\305\276 +\342\155\062\006\246\036\371\271\353\252\243\270\277\276\202\143 +\120\320\360\030\211\337\344\017\171\365\352\242\037\052\322\160 +\056\173\347\274\223\273\155\123\342\110\174\214\020\007\070\377 +\146\262\167\141\176\340\352\214\074\252\264\244\366\363\225\112 +\022\007\155\375\214\262\211\317\320\240\141\167\310\130\164\260 +\324\043\072\367\135\072\312\242\333\235\011\336\135\104\055\220 +\361\201\315\127\222\372\176\274\120\004\143\064\337\153\223\030 +\276\153\066\262\071\344\254\044\066\267\360\357\266\034\023\127 +\223\266\336\262\370\342\205\267\163\242\270\065\252\105\362\340 +\235\066\241\157\124\212\361\162\126\156\056\210\305\121\102\104 +\025\224\356\243\305\070\226\233\116\116\132\013\107\363\006\066 +\111\167\060\274\161\067\345\246\354\041\010\165\374\346\141\026 +\077\167\325\331\221\227\204\012\154\324\002\115\164\300\024\355 +\375\071\373\203\362\136\024\241\004\260\013\351\376\356\217\341 +\156\013\262\010\263\141\146\011\152\261\006\072\145\226\131\300 +\360\065\375\311\332\050\215\032\021\207\160\201\012\250\232\165 +\035\236\072\206\005\000\236\333\200\326\045\371\334\005\236\047 +\131\114\166\071\133\352\371\245\241\330\203\017\321\377\337\060 +\021\371\205\317\063\110\365\312\155\144\024\054\172\130\117\323 +\113\010\111\305\225\144\032\143\016\171\075\365\263\214\312\130 +\255\234\102\105\171\156\016\207\031\134\124\261\145\266\277\214 +\233\334\023\351\015\157\270\056\334\147\156\311\213\021\265\204 +\024\212\000\031\160\203\171\221\227\221\324\032\047\277\067\036 +\062\007\330\024\143\074\050\114\257\002\003\001\000\001\243\143 +\060\141\060\016\006\003\125\035\017\001\001\377\004\004\003\002 +\001\006\060\017\006\003\125\035\023\001\001\377\004\005\060\003 +\001\001\377\060\035\006\003\125\035\016\004\026\004\024\256\154 +\005\243\223\023\342\242\347\342\327\034\326\307\360\177\310\147 +\123\240\060\037\006\003\125\035\043\004\030\060\026\200\024\256 +\154\005\243\223\023\342\242\347\342\327\034\326\307\360\177\310 +\147\123\240\060\015\006\011\052\206\110\206\367\015\001\001\014 +\005\000\003\202\002\001\000\203\045\355\350\321\375\225\122\315 +\236\300\004\240\221\151\346\134\320\204\336\334\255\242\117\350 +\107\170\326\145\230\251\133\250\074\207\174\002\212\321\156\267 +\026\163\346\137\300\124\230\325\164\276\301\315\342\021\221\255 +\043\030\075\335\341\162\104\226\264\225\136\300\173\216\231\170 +\026\103\023\126\127\263\242\263\073\265\167\334\100\162\254\243 +\353\233\065\076\261\010\041\241\347\304\103\067\171\062\276\265 +\347\234\054\114\274\103\051\231\216\060\323\254\041\340\343\035 +\372\330\007\063\166\124\000\042\052\271\115\040\056\160\150\332 +\345\123\374\203\134\323\235\362\377\104\014\104\146\362\322\343 +\275\106\000\032\155\002\272\045\135\215\241\061\121\335\124\106 +\034\115\333\231\226\357\032\034\004\134\246\025\357\170\340\171 +\376\135\333\076\252\114\125\375\232\025\251\157\341\246\373\337 +\160\060\351\303\356\102\106\355\302\223\005\211\372\175\143\173 +\077\320\161\201\174\000\350\230\256\016\170\064\303\045\373\257 +\012\237\040\153\335\073\023\217\022\214\342\101\032\110\172\163 +\240\167\151\307\266\134\177\202\310\036\376\130\033\050\053\250 +\154\255\136\155\300\005\322\173\267\353\200\376\045\067\376\002 +\233\150\254\102\135\303\356\365\314\334\360\120\165\322\066\151 +\234\346\173\004\337\156\006\151\266\336\012\011\110\131\207\353 +\173\024\140\172\144\252\151\103\357\221\307\114\354\030\335\154 +\357\123\055\214\231\341\136\362\162\076\317\124\310\275\147\354 +\244\017\114\105\377\323\271\060\043\007\114\217\020\277\206\226 +\331\231\132\264\231\127\034\244\314\273\025\211\123\272\054\005 +\017\344\304\236\031\261\030\064\325\114\235\272\355\367\037\257 +\044\225\004\170\250\003\273\356\201\345\332\137\174\213\112\241 +\220\164\045\247\263\076\113\310\054\126\275\307\310\357\070\342 +\134\222\360\171\367\234\204\272\164\055\141\001\040\176\176\321 +\362\117\007\131\137\213\055\103\122\353\106\014\224\341\365\146 +\107\171\167\325\124\133\037\255\044\067\313\105\132\116\240\104 +\110\310\330\260\231\305\025\204\011\366\326\111\111\300\145\270 +\346\032\161\156\240\250\361\202\350\105\076\154\326\002\327\012 +\147\203\005\132\311\244\020 +END +CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE + +# Trust for "GlobalSign Root CA - R6" +# Issuer: CN=GlobalSign,O=GlobalSign,OU=GlobalSign Root CA - R6 +# Serial Number:45:e6:bb:03:83:33:c3:85:65:48:e6:ff:45:51 +# Subject: CN=GlobalSign,O=GlobalSign,OU=GlobalSign Root CA - R6 +# Not Valid Before: Wed Dec 10 00:00:00 2014 +# Not Valid After : Sun Dec 10 00:00:00 2034 +# Fingerprint (SHA-256): 2C:AB:EA:FE:37:D0:6C:A2:2A:BA:73:91:C0:03:3D:25:98:29:52:C4:53:64:73:49:76:3A:3A:B5:AD:6C:CF:69 +# Fingerprint (SHA1): 80:94:64:0E:B5:A7:A1:CA:11:9C:1F:DD:D5:9F:81:02:63:A7:FB:D1 +CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "GlobalSign Root CA - R6" +CKA_CERT_SHA1_HASH MULTILINE_OCTAL +\200\224\144\016\265\247\241\312\021\234\037\335\325\237\201\002 +\143\247\373\321 +END +CKA_CERT_MD5_HASH MULTILINE_OCTAL +\117\335\007\344\324\042\144\071\036\014\067\102\352\321\306\256 +END +CKA_ISSUER MULTILINE_OCTAL +\060\114\061\040\060\036\006\003\125\004\013\023\027\107\154\157 +\142\141\154\123\151\147\156\040\122\157\157\164\040\103\101\040 +\055\040\122\066\061\023\060\021\006\003\125\004\012\023\012\107 +\154\157\142\141\154\123\151\147\156\061\023\060\021\006\003\125 +\004\003\023\012\107\154\157\142\141\154\123\151\147\156 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\016\105\346\273\003\203\063\303\205\145\110\346\377\105\121 +END +CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE + +# +# Certificate "OISTE WISeKey Global Root GC CA" +# +# Issuer: CN=OISTE WISeKey Global Root GC CA,OU=OISTE Foundation Endorsed,O=WISeKey,C=CH +# Serial Number:21:2a:56:0c:ae:da:0c:ab:40:45:bf:2b:a2:2d:3a:ea +# Subject: CN=OISTE WISeKey Global Root GC CA,OU=OISTE Foundation Endorsed,O=WISeKey,C=CH +# Not Valid Before: Tue May 09 09:48:34 2017 +# Not Valid After : Fri May 09 09:58:33 2042 +# Fingerprint (SHA-256): 85:60:F9:1C:36:24:DA:BA:95:70:B5:FE:A0:DB:E3:6F:F1:1A:83:23:BE:94:86:85:4F:B3:F3:4A:55:71:19:8D +# Fingerprint (SHA1): E0:11:84:5E:34:DE:BE:88:81:B9:9C:F6:16:26:D1:96:1F:C3:B9:31 +CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "OISTE WISeKey Global Root GC CA" +CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 +CKA_SUBJECT MULTILINE_OCTAL +\060\155\061\013\060\011\006\003\125\004\006\023\002\103\110\061 +\020\060\016\006\003\125\004\012\023\007\127\111\123\145\113\145 +\171\061\042\060\040\006\003\125\004\013\023\031\117\111\123\124 +\105\040\106\157\165\156\144\141\164\151\157\156\040\105\156\144 +\157\162\163\145\144\061\050\060\046\006\003\125\004\003\023\037 +\117\111\123\124\105\040\127\111\123\145\113\145\171\040\107\154 +\157\142\141\154\040\122\157\157\164\040\107\103\040\103\101 +END +CKA_ID UTF8 "0" +CKA_ISSUER MULTILINE_OCTAL +\060\155\061\013\060\011\006\003\125\004\006\023\002\103\110\061 +\020\060\016\006\003\125\004\012\023\007\127\111\123\145\113\145 +\171\061\042\060\040\006\003\125\004\013\023\031\117\111\123\124 +\105\040\106\157\165\156\144\141\164\151\157\156\040\105\156\144 +\157\162\163\145\144\061\050\060\046\006\003\125\004\003\023\037 +\117\111\123\124\105\040\127\111\123\145\113\145\171\040\107\154 +\157\142\141\154\040\122\157\157\164\040\107\103\040\103\101 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\020\041\052\126\014\256\332\014\253\100\105\277\053\242\055 +\072\352 +END +CKA_VALUE MULTILINE_OCTAL +\060\202\002\151\060\202\001\357\240\003\002\001\002\002\020\041 +\052\126\014\256\332\014\253\100\105\277\053\242\055\072\352\060 +\012\006\010\052\206\110\316\075\004\003\003\060\155\061\013\060 +\011\006\003\125\004\006\023\002\103\110\061\020\060\016\006\003 +\125\004\012\023\007\127\111\123\145\113\145\171\061\042\060\040 +\006\003\125\004\013\023\031\117\111\123\124\105\040\106\157\165 +\156\144\141\164\151\157\156\040\105\156\144\157\162\163\145\144 +\061\050\060\046\006\003\125\004\003\023\037\117\111\123\124\105 +\040\127\111\123\145\113\145\171\040\107\154\157\142\141\154\040 +\122\157\157\164\040\107\103\040\103\101\060\036\027\015\061\067 +\060\065\060\071\060\071\064\070\063\064\132\027\015\064\062\060 +\065\060\071\060\071\065\070\063\063\132\060\155\061\013\060\011 +\006\003\125\004\006\023\002\103\110\061\020\060\016\006\003\125 +\004\012\023\007\127\111\123\145\113\145\171\061\042\060\040\006 +\003\125\004\013\023\031\117\111\123\124\105\040\106\157\165\156 +\144\141\164\151\157\156\040\105\156\144\157\162\163\145\144\061 +\050\060\046\006\003\125\004\003\023\037\117\111\123\124\105\040 +\127\111\123\145\113\145\171\040\107\154\157\142\141\154\040\122 +\157\157\164\040\107\103\040\103\101\060\166\060\020\006\007\052 +\206\110\316\075\002\001\006\005\053\201\004\000\042\003\142\000 +\004\114\351\120\300\306\017\162\030\274\330\361\272\263\211\342 +\171\112\243\026\247\153\124\044\333\121\377\352\364\011\044\303 +\013\042\237\313\152\047\202\201\015\322\300\257\061\344\164\202 +\156\312\045\331\214\165\235\361\333\320\232\242\113\041\176\026 +\247\143\220\322\071\324\261\207\170\137\030\226\017\120\033\065 +\067\017\152\306\334\331\023\115\244\216\220\067\346\275\133\061 +\221\243\124\060\122\060\016\006\003\125\035\017\001\001\377\004 +\004\003\002\001\006\060\017\006\003\125\035\023\001\001\377\004 +\005\060\003\001\001\377\060\035\006\003\125\035\016\004\026\004 +\024\110\207\024\254\343\303\236\220\140\072\327\312\211\356\323 +\255\214\264\120\146\060\020\006\011\053\006\001\004\001\202\067 +\025\001\004\003\002\001\000\060\012\006\010\052\206\110\316\075 +\004\003\003\003\150\000\060\145\002\060\046\307\151\133\334\325 +\347\262\347\310\014\214\214\303\335\171\214\033\143\325\311\122 +\224\116\115\202\112\163\036\262\200\204\251\045\300\114\132\155 +\111\051\140\170\023\342\176\110\353\144\002\061\000\333\064\040 +\062\010\377\232\111\002\266\210\336\024\257\135\154\231\161\215 +\032\077\213\327\340\242\066\206\034\007\202\072\166\123\375\302 +\242\355\357\173\260\200\117\130\017\113\123\071\275 +END +CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE + +# Trust for "OISTE WISeKey Global Root GC CA" +# Issuer: CN=OISTE WISeKey Global Root GC CA,OU=OISTE Foundation Endorsed,O=WISeKey,C=CH +# Serial Number:21:2a:56:0c:ae:da:0c:ab:40:45:bf:2b:a2:2d:3a:ea +# Subject: CN=OISTE WISeKey Global Root GC CA,OU=OISTE Foundation Endorsed,O=WISeKey,C=CH +# Not Valid Before: Tue May 09 09:48:34 2017 +# Not Valid After : Fri May 09 09:58:33 2042 +# Fingerprint (SHA-256): 85:60:F9:1C:36:24:DA:BA:95:70:B5:FE:A0:DB:E3:6F:F1:1A:83:23:BE:94:86:85:4F:B3:F3:4A:55:71:19:8D +# Fingerprint (SHA1): E0:11:84:5E:34:DE:BE:88:81:B9:9C:F6:16:26:D1:96:1F:C3:B9:31 +CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "OISTE WISeKey Global Root GC CA" +CKA_CERT_SHA1_HASH MULTILINE_OCTAL +\340\021\204\136\064\336\276\210\201\271\234\366\026\046\321\226 +\037\303\271\061 +END +CKA_CERT_MD5_HASH MULTILINE_OCTAL +\251\326\271\055\057\223\144\370\245\151\312\221\351\150\007\043 +END +CKA_ISSUER MULTILINE_OCTAL +\060\155\061\013\060\011\006\003\125\004\006\023\002\103\110\061 +\020\060\016\006\003\125\004\012\023\007\127\111\123\145\113\145 +\171\061\042\060\040\006\003\125\004\013\023\031\117\111\123\124 +\105\040\106\157\165\156\144\141\164\151\157\156\040\105\156\144 +\157\162\163\145\144\061\050\060\046\006\003\125\004\003\023\037 +\117\111\123\124\105\040\127\111\123\145\113\145\171\040\107\154 +\157\142\141\154\040\122\157\157\164\040\107\103\040\103\101 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\020\041\052\126\014\256\332\014\253\100\105\277\053\242\055 +\072\352 +END +CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE + +# +# Certificate "GTS Root R1" +# +# Issuer: CN=GTS Root R1,O=Google Trust Services LLC,C=US +# Serial Number:6e:47:a9:c5:4b:47:0c:0d:ec:33:d0:89:b9:1c:f4:e1 +# Subject: CN=GTS Root R1,O=Google Trust Services LLC,C=US +# Not Valid Before: Wed Jun 22 00:00:00 2016 +# Not Valid After : Sun Jun 22 00:00:00 2036 +# Fingerprint (SHA-256): 2A:57:54:71:E3:13:40:BC:21:58:1C:BD:2C:F1:3E:15:84:63:20:3E:CE:94:BC:F9:D3:CC:19:6B:F0:9A:54:72 +# Fingerprint (SHA1): E1:C9:50:E6:EF:22:F8:4C:56:45:72:8B:92:20:60:D7:D5:A7:A3:E8 +CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "GTS Root R1" +CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 +CKA_SUBJECT MULTILINE_OCTAL +\060\107\061\013\060\011\006\003\125\004\006\023\002\125\123\061 +\042\060\040\006\003\125\004\012\023\031\107\157\157\147\154\145 +\040\124\162\165\163\164\040\123\145\162\166\151\143\145\163\040 +\114\114\103\061\024\060\022\006\003\125\004\003\023\013\107\124 +\123\040\122\157\157\164\040\122\061 +END +CKA_ID UTF8 "0" +CKA_ISSUER MULTILINE_OCTAL +\060\107\061\013\060\011\006\003\125\004\006\023\002\125\123\061 +\042\060\040\006\003\125\004\012\023\031\107\157\157\147\154\145 +\040\124\162\165\163\164\040\123\145\162\166\151\143\145\163\040 +\114\114\103\061\024\060\022\006\003\125\004\003\023\013\107\124 +\123\040\122\157\157\164\040\122\061 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\020\156\107\251\305\113\107\014\015\354\063\320\211\271\034 +\364\341 +END +CKA_VALUE MULTILINE_OCTAL +\060\202\005\132\060\202\003\102\240\003\002\001\002\002\020\156 +\107\251\305\113\107\014\015\354\063\320\211\271\034\364\341\060 +\015\006\011\052\206\110\206\367\015\001\001\014\005\000\060\107 +\061\013\060\011\006\003\125\004\006\023\002\125\123\061\042\060 +\040\006\003\125\004\012\023\031\107\157\157\147\154\145\040\124 +\162\165\163\164\040\123\145\162\166\151\143\145\163\040\114\114 +\103\061\024\060\022\006\003\125\004\003\023\013\107\124\123\040 +\122\157\157\164\040\122\061\060\036\027\015\061\066\060\066\062 +\062\060\060\060\060\060\060\132\027\015\063\066\060\066\062\062 +\060\060\060\060\060\060\132\060\107\061\013\060\011\006\003\125 +\004\006\023\002\125\123\061\042\060\040\006\003\125\004\012\023 +\031\107\157\157\147\154\145\040\124\162\165\163\164\040\123\145 +\162\166\151\143\145\163\040\114\114\103\061\024\060\022\006\003 +\125\004\003\023\013\107\124\123\040\122\157\157\164\040\122\061 +\060\202\002\042\060\015\006\011\052\206\110\206\367\015\001\001 +\001\005\000\003\202\002\017\000\060\202\002\012\002\202\002\001 +\000\266\021\002\213\036\343\241\167\233\073\334\277\224\076\267 +\225\247\100\074\241\375\202\371\175\062\006\202\161\366\366\214 +\177\373\350\333\274\152\056\227\227\243\214\113\371\053\366\261 +\371\316\204\035\261\371\305\227\336\357\271\362\243\351\274\022 +\211\136\247\252\122\253\370\043\047\313\244\261\234\143\333\327 +\231\176\360\012\136\353\150\246\364\306\132\107\015\115\020\063 +\343\116\261\023\243\310\030\154\113\354\374\011\220\337\235\144 +\051\045\043\007\241\264\322\075\056\140\340\317\322\011\207\273 +\315\110\360\115\302\302\172\210\212\273\272\317\131\031\326\257 +\217\260\007\260\236\061\361\202\301\300\337\056\246\155\154\031 +\016\265\330\176\046\032\105\003\075\260\171\244\224\050\255\017 +\177\046\345\250\010\376\226\350\074\150\224\123\356\203\072\210 +\053\025\226\011\262\340\172\214\056\165\326\234\353\247\126\144 +\217\226\117\150\256\075\227\302\204\217\300\274\100\300\013\134 +\275\366\207\263\065\154\254\030\120\177\204\340\114\315\222\323 +\040\351\063\274\122\231\257\062\265\051\263\045\052\264\110\371 +\162\341\312\144\367\346\202\020\215\350\235\302\212\210\372\070 +\146\212\374\143\371\001\371\170\375\173\134\167\372\166\207\372 +\354\337\261\016\171\225\127\264\275\046\357\326\001\321\353\026 +\012\273\216\013\265\305\305\212\125\253\323\254\352\221\113\051 +\314\031\244\062\045\116\052\361\145\104\320\002\316\252\316\111 +\264\352\237\174\203\260\100\173\347\103\253\247\154\243\217\175 +\211\201\372\114\245\377\325\216\303\316\113\340\265\330\263\216 +\105\317\166\300\355\100\053\375\123\017\260\247\325\073\015\261 +\212\242\003\336\061\255\314\167\352\157\173\076\326\337\221\042 +\022\346\276\372\330\062\374\020\143\024\121\162\336\135\326\026 +\223\275\051\150\063\357\072\146\354\007\212\046\337\023\327\127 +\145\170\047\336\136\111\024\000\242\000\177\232\250\041\266\251 +\261\225\260\245\271\015\026\021\332\307\154\110\074\100\340\176 +\015\132\315\126\074\321\227\005\271\313\113\355\071\113\234\304 +\077\322\125\023\156\044\260\326\161\372\364\301\272\314\355\033 +\365\376\201\101\330\000\230\075\072\310\256\172\230\067\030\005 +\225\002\003\001\000\001\243\102\060\100\060\016\006\003\125\035 +\017\001\001\377\004\004\003\002\001\006\060\017\006\003\125\035 +\023\001\001\377\004\005\060\003\001\001\377\060\035\006\003\125 +\035\016\004\026\004\024\344\257\053\046\161\032\053\110\047\205 +\057\122\146\054\357\360\211\023\161\076\060\015\006\011\052\206 +\110\206\367\015\001\001\014\005\000\003\202\002\001\000\070\226 +\012\356\075\264\226\036\137\357\235\234\013\063\237\053\340\312 +\375\322\216\012\037\101\164\245\174\252\204\324\345\362\036\346 +\067\122\062\234\013\321\141\035\277\050\301\266\104\051\065\165 +\167\230\262\174\331\275\164\254\212\150\343\251\061\011\051\001 +\140\163\343\107\174\123\250\220\112\047\357\113\327\237\223\347 +\202\066\316\232\150\014\202\347\317\324\020\026\157\137\016\231 +\134\366\037\161\175\357\357\173\057\176\352\066\326\227\160\013 +\025\356\327\134\126\152\063\245\343\111\070\014\270\175\373\215 +\205\244\261\131\136\364\152\341\335\241\366\144\104\256\346\121 +\203\041\146\306\021\076\363\316\107\356\234\050\037\045\332\377 +\254\146\225\335\065\017\134\357\040\054\142\375\221\272\251\314 +\374\132\234\223\201\203\051\227\112\174\132\162\264\071\320\267 +\167\313\171\375\151\072\222\067\355\156\070\145\106\176\351\140 +\275\171\210\227\137\070\022\364\356\257\133\202\310\206\325\341 +\231\155\214\004\362\166\272\111\366\156\351\155\036\137\240\357 +\047\202\166\100\370\246\323\130\134\017\054\102\332\102\306\173 +\210\064\307\301\330\105\233\301\076\305\141\035\331\143\120\111 +\366\064\205\152\340\030\305\156\107\253\101\102\051\233\366\140 +\015\322\061\323\143\230\043\223\132\000\201\110\264\357\315\212 +\315\311\317\231\356\331\236\252\066\341\150\113\161\111\024\066 +\050\072\075\035\316\232\217\045\346\200\161\141\053\265\173\314 +\371\045\026\201\341\061\137\241\243\176\026\244\234\026\152\227 +\030\275\166\162\245\013\236\035\066\346\057\241\057\276\160\221 +\017\250\346\332\370\304\222\100\154\045\176\173\263\011\334\262 +\027\255\200\104\360\150\245\217\224\165\377\164\132\350\250\002 +\174\014\011\342\251\113\013\240\205\013\142\271\357\241\061\222 +\373\357\366\121\004\211\154\350\251\164\241\273\027\263\265\375 +\111\017\174\074\354\203\030\040\103\116\325\223\272\264\064\261 +\037\026\066\037\014\346\144\071\026\114\334\340\376\035\310\251 +\142\075\100\352\312\305\064\002\264\256\211\210\063\065\334\054 +\023\163\330\047\361\320\162\356\165\073\042\336\230\150\146\133 +\361\306\143\107\125\034\272\245\010\121\165\246\110\045 +END +CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE + +# Trust for "GTS Root R1" +# Issuer: CN=GTS Root R1,O=Google Trust Services LLC,C=US +# Serial Number:6e:47:a9:c5:4b:47:0c:0d:ec:33:d0:89:b9:1c:f4:e1 +# Subject: CN=GTS Root R1,O=Google Trust Services LLC,C=US +# Not Valid Before: Wed Jun 22 00:00:00 2016 +# Not Valid After : Sun Jun 22 00:00:00 2036 +# Fingerprint (SHA-256): 2A:57:54:71:E3:13:40:BC:21:58:1C:BD:2C:F1:3E:15:84:63:20:3E:CE:94:BC:F9:D3:CC:19:6B:F0:9A:54:72 +# Fingerprint (SHA1): E1:C9:50:E6:EF:22:F8:4C:56:45:72:8B:92:20:60:D7:D5:A7:A3:E8 +CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "GTS Root R1" +CKA_CERT_SHA1_HASH MULTILINE_OCTAL +\341\311\120\346\357\042\370\114\126\105\162\213\222\040\140\327 +\325\247\243\350 +END +CKA_CERT_MD5_HASH MULTILINE_OCTAL +\202\032\357\324\322\112\362\237\342\075\227\006\024\160\162\205 +END +CKA_ISSUER MULTILINE_OCTAL +\060\107\061\013\060\011\006\003\125\004\006\023\002\125\123\061 +\042\060\040\006\003\125\004\012\023\031\107\157\157\147\154\145 +\040\124\162\165\163\164\040\123\145\162\166\151\143\145\163\040 +\114\114\103\061\024\060\022\006\003\125\004\003\023\013\107\124 +\123\040\122\157\157\164\040\122\061 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\020\156\107\251\305\113\107\014\015\354\063\320\211\271\034 +\364\341 +END +CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE + +# +# Certificate "GTS Root R2" +# +# Issuer: CN=GTS Root R2,O=Google Trust Services LLC,C=US +# Serial Number:6e:47:a9:c6:5a:b3:e7:20:c5:30:9a:3f:68:52:f2:6f +# Subject: CN=GTS Root R2,O=Google Trust Services LLC,C=US +# Not Valid Before: Wed Jun 22 00:00:00 2016 +# Not Valid After : Sun Jun 22 00:00:00 2036 +# Fingerprint (SHA-256): C4:5D:7B:B0:8E:6D:67:E6:2E:42:35:11:0B:56:4E:5F:78:FD:92:EF:05:8C:84:0A:EA:4E:64:55:D7:58:5C:60 +# Fingerprint (SHA1): D2:73:96:2A:2A:5E:39:9F:73:3F:E1:C7:1E:64:3F:03:38:34:FC:4D +CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "GTS Root R2" +CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 +CKA_SUBJECT MULTILINE_OCTAL +\060\107\061\013\060\011\006\003\125\004\006\023\002\125\123\061 +\042\060\040\006\003\125\004\012\023\031\107\157\157\147\154\145 +\040\124\162\165\163\164\040\123\145\162\166\151\143\145\163\040 +\114\114\103\061\024\060\022\006\003\125\004\003\023\013\107\124 +\123\040\122\157\157\164\040\122\062 +END +CKA_ID UTF8 "0" +CKA_ISSUER MULTILINE_OCTAL +\060\107\061\013\060\011\006\003\125\004\006\023\002\125\123\061 +\042\060\040\006\003\125\004\012\023\031\107\157\157\147\154\145 +\040\124\162\165\163\164\040\123\145\162\166\151\143\145\163\040 +\114\114\103\061\024\060\022\006\003\125\004\003\023\013\107\124 +\123\040\122\157\157\164\040\122\062 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\020\156\107\251\306\132\263\347\040\305\060\232\077\150\122 +\362\157 +END +CKA_VALUE MULTILINE_OCTAL +\060\202\005\132\060\202\003\102\240\003\002\001\002\002\020\156 +\107\251\306\132\263\347\040\305\060\232\077\150\122\362\157\060 +\015\006\011\052\206\110\206\367\015\001\001\014\005\000\060\107 +\061\013\060\011\006\003\125\004\006\023\002\125\123\061\042\060 +\040\006\003\125\004\012\023\031\107\157\157\147\154\145\040\124 +\162\165\163\164\040\123\145\162\166\151\143\145\163\040\114\114 +\103\061\024\060\022\006\003\125\004\003\023\013\107\124\123\040 +\122\157\157\164\040\122\062\060\036\027\015\061\066\060\066\062 +\062\060\060\060\060\060\060\132\027\015\063\066\060\066\062\062 +\060\060\060\060\060\060\132\060\107\061\013\060\011\006\003\125 +\004\006\023\002\125\123\061\042\060\040\006\003\125\004\012\023 +\031\107\157\157\147\154\145\040\124\162\165\163\164\040\123\145 +\162\166\151\143\145\163\040\114\114\103\061\024\060\022\006\003 +\125\004\003\023\013\107\124\123\040\122\157\157\164\040\122\062 +\060\202\002\042\060\015\006\011\052\206\110\206\367\015\001\001 +\001\005\000\003\202\002\017\000\060\202\002\012\002\202\002\001 +\000\316\336\375\246\373\354\354\024\064\074\007\006\132\154\131 +\367\031\065\335\367\301\235\125\252\323\315\073\244\223\162\357 +\012\372\155\235\366\360\205\200\133\241\110\122\237\071\305\267 +\356\050\254\357\313\166\150\024\271\337\255\001\154\231\037\304 +\042\035\237\376\162\167\340\054\133\257\344\004\277\117\162\240 +\032\064\230\350\071\150\354\225\045\173\166\241\346\151\271\205 +\031\275\211\214\376\255\355\066\352\163\274\377\203\342\313\175 +\301\322\316\112\263\215\005\236\213\111\223\337\301\133\320\156 +\136\360\056\060\056\202\374\372\274\264\027\012\110\345\210\233 +\305\233\153\336\260\312\264\003\360\332\364\220\270\145\144\367 +\134\114\255\350\176\146\136\231\327\270\302\076\310\320\023\235 +\255\356\344\105\173\211\125\367\212\037\142\122\204\022\263\302 +\100\227\343\212\037\107\221\246\164\132\322\370\261\143\050\020 +\270\263\011\270\126\167\100\242\046\230\171\306\376\337\045\356 +\076\345\240\177\324\141\017\121\113\074\077\214\332\341\160\164 +\330\302\150\241\371\301\014\351\241\342\177\273\125\074\166\006 +\356\152\116\314\222\210\060\115\232\275\117\013\110\232\204\265 +\230\243\325\373\163\301\127\141\335\050\126\165\023\256\207\216 +\347\014\121\011\020\165\210\114\274\215\371\173\074\324\042\110 +\037\052\334\353\153\273\104\261\313\063\161\062\106\257\255\112 +\361\214\350\164\072\254\347\032\042\163\200\322\060\367\045\102 +\307\042\073\073\022\255\226\056\306\303\166\007\252\040\267\065 +\111\127\351\222\111\350\166\026\162\061\147\053\226\176\212\243 +\307\224\126\042\277\152\113\176\001\041\262\043\062\337\344\232 +\104\155\131\133\135\365\000\240\034\233\306\170\227\215\220\377 +\233\310\252\264\257\021\121\071\136\331\373\147\255\325\133\021 +\235\062\232\033\275\325\272\133\245\311\313\045\151\123\125\047 +\134\340\312\066\313\210\141\373\036\267\320\313\356\026\373\323 +\246\114\336\222\245\324\342\337\365\006\124\336\056\235\113\264 +\223\060\252\201\316\335\032\334\121\163\015\117\160\351\345\266 +\026\041\031\171\262\346\211\013\165\144\312\325\253\274\011\301 +\030\241\377\324\124\241\205\074\375\024\044\003\262\207\323\244 +\267\002\003\001\000\001\243\102\060\100\060\016\006\003\125\035 +\017\001\001\377\004\004\003\002\001\006\060\017\006\003\125\035 +\023\001\001\377\004\005\060\003\001\001\377\060\035\006\003\125 +\035\016\004\026\004\024\273\377\312\216\043\237\117\231\312\333 +\342\150\246\245\025\047\027\036\331\016\060\015\006\011\052\206 +\110\206\367\015\001\001\014\005\000\003\202\002\001\000\266\151 +\360\246\167\376\236\356\013\201\255\341\300\251\307\371\065\035 +\100\202\253\346\004\264\337\313\367\035\017\203\360\176\023\115 +\215\214\356\343\063\042\303\071\374\100\337\156\101\113\102\123 +\276\026\210\361\322\070\136\304\150\231\034\230\122\223\214\347 +\150\355\033\152\163\172\005\100\115\177\145\073\326\130\361\316 +\203\107\140\343\377\227\251\234\140\167\030\125\265\176\010\223 +\317\320\366\074\147\003\025\141\011\371\201\171\365\354\123\244 +\237\311\217\001\213\163\304\167\166\334\203\242\365\014\111\032 +\250\166\336\222\233\144\370\263\054\305\047\323\007\300\010\200 +\244\230\222\343\001\226\002\252\002\356\217\073\305\321\155\012 +\063\060\163\170\271\117\124\026\277\013\007\241\244\134\346\313 +\311\134\204\217\017\340\025\167\054\176\046\176\332\304\113\333 +\247\026\167\007\260\315\165\350\162\102\326\225\204\235\206\203 +\362\344\220\315\011\107\324\213\003\160\332\132\306\003\102\364 +\355\067\242\360\033\120\124\113\016\330\204\336\031\050\231\201 +\107\256\011\033\077\110\321\303\157\342\260\140\027\365\356\043 +\002\245\332\000\133\155\220\253\356\242\351\033\073\351\307\104 +\047\105\216\153\237\365\244\204\274\167\371\153\227\254\076\121 +\105\242\021\246\314\205\356\012\150\362\076\120\070\172\044\142 +\036\027\040\067\155\152\115\267\011\233\311\374\244\130\365\266 +\373\234\116\030\273\225\002\347\241\255\233\007\356\066\153\044 +\322\071\206\301\223\203\120\322\201\106\250\137\142\127\054\273 +\154\144\210\010\156\357\023\124\137\335\055\304\147\143\323\317 +\211\067\277\235\040\364\373\172\203\233\240\036\201\000\120\302 +\344\014\042\131\122\020\355\103\126\207\000\370\024\122\247\035 +\213\223\214\242\115\106\177\047\306\161\233\044\336\344\332\206 +\213\015\176\153\040\301\300\236\341\145\330\152\243\246\350\205 +\213\072\007\010\034\272\365\217\125\232\030\165\176\345\354\201 +\146\321\041\163\241\065\104\013\200\075\133\234\136\157\052\027 +\226\321\203\043\210\146\155\346\206\342\160\062\057\122\042\347 +\310\347\177\304\054\140\135\057\303\257\236\105\005\303\204\002 +\267\375\054\010\122\117\202\335\243\360\324\206\011\002 +END +CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE + +# Trust for "GTS Root R2" +# Issuer: CN=GTS Root R2,O=Google Trust Services LLC,C=US +# Serial Number:6e:47:a9:c6:5a:b3:e7:20:c5:30:9a:3f:68:52:f2:6f +# Subject: CN=GTS Root R2,O=Google Trust Services LLC,C=US +# Not Valid Before: Wed Jun 22 00:00:00 2016 +# Not Valid After : Sun Jun 22 00:00:00 2036 +# Fingerprint (SHA-256): C4:5D:7B:B0:8E:6D:67:E6:2E:42:35:11:0B:56:4E:5F:78:FD:92:EF:05:8C:84:0A:EA:4E:64:55:D7:58:5C:60 +# Fingerprint (SHA1): D2:73:96:2A:2A:5E:39:9F:73:3F:E1:C7:1E:64:3F:03:38:34:FC:4D +CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "GTS Root R2" +CKA_CERT_SHA1_HASH MULTILINE_OCTAL +\322\163\226\052\052\136\071\237\163\077\341\307\036\144\077\003 +\070\064\374\115 +END +CKA_CERT_MD5_HASH MULTILINE_OCTAL +\104\355\232\016\244\011\073\000\362\256\114\243\306\141\260\213 +END +CKA_ISSUER MULTILINE_OCTAL +\060\107\061\013\060\011\006\003\125\004\006\023\002\125\123\061 +\042\060\040\006\003\125\004\012\023\031\107\157\157\147\154\145 +\040\124\162\165\163\164\040\123\145\162\166\151\143\145\163\040 +\114\114\103\061\024\060\022\006\003\125\004\003\023\013\107\124 +\123\040\122\157\157\164\040\122\062 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\020\156\107\251\306\132\263\347\040\305\060\232\077\150\122 +\362\157 +END +CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE + +# +# Certificate "GTS Root R3" +# +# Issuer: CN=GTS Root R3,O=Google Trust Services LLC,C=US +# Serial Number:6e:47:a9:c7:6c:a9:73:24:40:89:0f:03:55:dd:8d:1d +# Subject: CN=GTS Root R3,O=Google Trust Services LLC,C=US +# Not Valid Before: Wed Jun 22 00:00:00 2016 +# Not Valid After : Sun Jun 22 00:00:00 2036 +# Fingerprint (SHA-256): 15:D5:B8:77:46:19:EA:7D:54:CE:1C:A6:D0:B0:C4:03:E0:37:A9:17:F1:31:E8:A0:4E:1E:6B:7A:71:BA:BC:E5 +# Fingerprint (SHA1): 30:D4:24:6F:07:FF:DB:91:89:8A:0B:E9:49:66:11:EB:8C:5E:46:E5 +CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "GTS Root R3" +CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 +CKA_SUBJECT MULTILINE_OCTAL +\060\107\061\013\060\011\006\003\125\004\006\023\002\125\123\061 +\042\060\040\006\003\125\004\012\023\031\107\157\157\147\154\145 +\040\124\162\165\163\164\040\123\145\162\166\151\143\145\163\040 +\114\114\103\061\024\060\022\006\003\125\004\003\023\013\107\124 +\123\040\122\157\157\164\040\122\063 +END +CKA_ID UTF8 "0" +CKA_ISSUER MULTILINE_OCTAL +\060\107\061\013\060\011\006\003\125\004\006\023\002\125\123\061 +\042\060\040\006\003\125\004\012\023\031\107\157\157\147\154\145 +\040\124\162\165\163\164\040\123\145\162\166\151\143\145\163\040 +\114\114\103\061\024\060\022\006\003\125\004\003\023\013\107\124 +\123\040\122\157\157\164\040\122\063 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\020\156\107\251\307\154\251\163\044\100\211\017\003\125\335 +\215\035 +END +CKA_VALUE MULTILINE_OCTAL +\060\202\002\014\060\202\001\221\240\003\002\001\002\002\020\156 +\107\251\307\154\251\163\044\100\211\017\003\125\335\215\035\060 +\012\006\010\052\206\110\316\075\004\003\003\060\107\061\013\060 +\011\006\003\125\004\006\023\002\125\123\061\042\060\040\006\003 +\125\004\012\023\031\107\157\157\147\154\145\040\124\162\165\163 +\164\040\123\145\162\166\151\143\145\163\040\114\114\103\061\024 +\060\022\006\003\125\004\003\023\013\107\124\123\040\122\157\157 +\164\040\122\063\060\036\027\015\061\066\060\066\062\062\060\060 +\060\060\060\060\132\027\015\063\066\060\066\062\062\060\060\060 +\060\060\060\132\060\107\061\013\060\011\006\003\125\004\006\023 +\002\125\123\061\042\060\040\006\003\125\004\012\023\031\107\157 +\157\147\154\145\040\124\162\165\163\164\040\123\145\162\166\151 +\143\145\163\040\114\114\103\061\024\060\022\006\003\125\004\003 +\023\013\107\124\123\040\122\157\157\164\040\122\063\060\166\060 +\020\006\007\052\206\110\316\075\002\001\006\005\053\201\004\000 +\042\003\142\000\004\037\117\063\207\063\051\212\241\204\336\313 +\307\041\130\101\211\352\126\235\053\113\205\306\035\114\047\274 +\177\046\121\162\157\342\237\326\243\312\314\105\024\106\213\255 +\357\176\206\214\354\261\176\057\377\251\161\235\030\204\105\004 +\101\125\156\053\352\046\177\273\220\001\343\113\031\272\344\124 +\226\105\011\261\325\154\221\104\255\204\023\216\232\214\015\200 +\014\062\366\340\047\243\102\060\100\060\016\006\003\125\035\017 +\001\001\377\004\004\003\002\001\006\060\017\006\003\125\035\023 +\001\001\377\004\005\060\003\001\001\377\060\035\006\003\125\035 +\016\004\026\004\024\301\361\046\272\240\055\256\205\201\317\323 +\361\052\022\275\270\012\147\375\274\060\012\006\010\052\206\110 +\316\075\004\003\003\003\151\000\060\146\002\061\000\200\133\244 +\174\043\300\225\245\054\334\276\211\157\043\271\243\335\145\000 +\122\136\221\254\310\235\162\164\202\123\013\175\251\100\275\150 +\140\305\341\270\124\073\301\066\027\045\330\301\275\002\061\000 +\236\065\222\164\205\045\121\365\044\354\144\122\044\120\245\037 +\333\350\313\311\166\354\354\202\156\365\205\030\123\350\270\343 +\232\051\252\226\323\203\043\311\244\173\141\263\314\002\350\135 +END +CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE + +# Trust for "GTS Root R3" +# Issuer: CN=GTS Root R3,O=Google Trust Services LLC,C=US +# Serial Number:6e:47:a9:c7:6c:a9:73:24:40:89:0f:03:55:dd:8d:1d +# Subject: CN=GTS Root R3,O=Google Trust Services LLC,C=US +# Not Valid Before: Wed Jun 22 00:00:00 2016 +# Not Valid After : Sun Jun 22 00:00:00 2036 +# Fingerprint (SHA-256): 15:D5:B8:77:46:19:EA:7D:54:CE:1C:A6:D0:B0:C4:03:E0:37:A9:17:F1:31:E8:A0:4E:1E:6B:7A:71:BA:BC:E5 +# Fingerprint (SHA1): 30:D4:24:6F:07:FF:DB:91:89:8A:0B:E9:49:66:11:EB:8C:5E:46:E5 +CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "GTS Root R3" +CKA_CERT_SHA1_HASH MULTILINE_OCTAL +\060\324\044\157\007\377\333\221\211\212\013\351\111\146\021\353 +\214\136\106\345 +END +CKA_CERT_MD5_HASH MULTILINE_OCTAL +\032\171\133\153\004\122\234\135\307\164\063\033\045\232\371\045 +END +CKA_ISSUER MULTILINE_OCTAL +\060\107\061\013\060\011\006\003\125\004\006\023\002\125\123\061 +\042\060\040\006\003\125\004\012\023\031\107\157\157\147\154\145 +\040\124\162\165\163\164\040\123\145\162\166\151\143\145\163\040 +\114\114\103\061\024\060\022\006\003\125\004\003\023\013\107\124 +\123\040\122\157\157\164\040\122\063 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\020\156\107\251\307\154\251\163\044\100\211\017\003\125\335 +\215\035 +END +CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE + +# +# Certificate "GTS Root R4" +# +# Issuer: CN=GTS Root R4,O=Google Trust Services LLC,C=US +# Serial Number:6e:47:a9:c8:8b:94:b6:e8:bb:3b:2a:d8:a2:b2:c1:99 +# Subject: CN=GTS Root R4,O=Google Trust Services LLC,C=US +# Not Valid Before: Wed Jun 22 00:00:00 2016 +# Not Valid After : Sun Jun 22 00:00:00 2036 +# Fingerprint (SHA-256): 71:CC:A5:39:1F:9E:79:4B:04:80:25:30:B3:63:E1:21:DA:8A:30:43:BB:26:66:2F:EA:4D:CA:7F:C9:51:A4:BD +# Fingerprint (SHA1): 2A:1D:60:27:D9:4A:B1:0A:1C:4D:91:5C:CD:33:A0:CB:3E:2D:54:CB +CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "GTS Root R4" +CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 +CKA_SUBJECT MULTILINE_OCTAL +\060\107\061\013\060\011\006\003\125\004\006\023\002\125\123\061 +\042\060\040\006\003\125\004\012\023\031\107\157\157\147\154\145 +\040\124\162\165\163\164\040\123\145\162\166\151\143\145\163\040 +\114\114\103\061\024\060\022\006\003\125\004\003\023\013\107\124 +\123\040\122\157\157\164\040\122\064 +END +CKA_ID UTF8 "0" +CKA_ISSUER MULTILINE_OCTAL +\060\107\061\013\060\011\006\003\125\004\006\023\002\125\123\061 +\042\060\040\006\003\125\004\012\023\031\107\157\157\147\154\145 +\040\124\162\165\163\164\040\123\145\162\166\151\143\145\163\040 +\114\114\103\061\024\060\022\006\003\125\004\003\023\013\107\124 +\123\040\122\157\157\164\040\122\064 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\020\156\107\251\310\213\224\266\350\273\073\052\330\242\262 +\301\231 +END +CKA_VALUE MULTILINE_OCTAL +\060\202\002\012\060\202\001\221\240\003\002\001\002\002\020\156 +\107\251\310\213\224\266\350\273\073\052\330\242\262\301\231\060 +\012\006\010\052\206\110\316\075\004\003\003\060\107\061\013\060 +\011\006\003\125\004\006\023\002\125\123\061\042\060\040\006\003 +\125\004\012\023\031\107\157\157\147\154\145\040\124\162\165\163 +\164\040\123\145\162\166\151\143\145\163\040\114\114\103\061\024 +\060\022\006\003\125\004\003\023\013\107\124\123\040\122\157\157 +\164\040\122\064\060\036\027\015\061\066\060\066\062\062\060\060 +\060\060\060\060\132\027\015\063\066\060\066\062\062\060\060\060 +\060\060\060\132\060\107\061\013\060\011\006\003\125\004\006\023 +\002\125\123\061\042\060\040\006\003\125\004\012\023\031\107\157 +\157\147\154\145\040\124\162\165\163\164\040\123\145\162\166\151 +\143\145\163\040\114\114\103\061\024\060\022\006\003\125\004\003 +\023\013\107\124\123\040\122\157\157\164\040\122\064\060\166\060 +\020\006\007\052\206\110\316\075\002\001\006\005\053\201\004\000 +\042\003\142\000\004\363\164\163\247\150\213\140\256\103\270\065 +\305\201\060\173\113\111\235\373\301\141\316\346\336\106\275\153 +\325\141\030\065\256\100\335\163\367\211\221\060\132\353\074\356 +\205\174\242\100\166\073\251\306\270\107\330\052\347\222\221\152 +\163\351\261\162\071\237\051\237\242\230\323\137\136\130\206\145 +\017\241\204\145\006\321\334\213\311\307\163\310\214\152\057\345 +\304\253\321\035\212\243\102\060\100\060\016\006\003\125\035\017 +\001\001\377\004\004\003\002\001\006\060\017\006\003\125\035\023 +\001\001\377\004\005\060\003\001\001\377\060\035\006\003\125\035 +\016\004\026\004\024\200\114\326\353\164\377\111\066\243\325\330 +\374\265\076\305\152\360\224\035\214\060\012\006\010\052\206\110 +\316\075\004\003\003\003\147\000\060\144\002\060\152\120\122\164 +\010\304\160\334\236\120\164\041\350\215\172\041\303\117\226\156 +\025\321\042\065\141\055\372\010\067\356\031\155\255\333\262\314 +\175\007\064\365\140\031\054\265\064\331\157\040\002\060\003\161 +\261\272\243\140\013\206\355\232\010\152\225\150\237\342\263\341 +\223\144\174\136\223\246\337\171\055\215\205\343\224\317\043\135 +\161\314\362\260\115\326\376\231\310\224\251\165\242\343 +END +CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE + +# Trust for "GTS Root R4" +# Issuer: CN=GTS Root R4,O=Google Trust Services LLC,C=US +# Serial Number:6e:47:a9:c8:8b:94:b6:e8:bb:3b:2a:d8:a2:b2:c1:99 +# Subject: CN=GTS Root R4,O=Google Trust Services LLC,C=US +# Not Valid Before: Wed Jun 22 00:00:00 2016 +# Not Valid After : Sun Jun 22 00:00:00 2036 +# Fingerprint (SHA-256): 71:CC:A5:39:1F:9E:79:4B:04:80:25:30:B3:63:E1:21:DA:8A:30:43:BB:26:66:2F:EA:4D:CA:7F:C9:51:A4:BD +# Fingerprint (SHA1): 2A:1D:60:27:D9:4A:B1:0A:1C:4D:91:5C:CD:33:A0:CB:3E:2D:54:CB +CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "GTS Root R4" +CKA_CERT_SHA1_HASH MULTILINE_OCTAL +\052\035\140\047\331\112\261\012\034\115\221\134\315\063\240\313 +\076\055\124\313 +END +CKA_CERT_MD5_HASH MULTILINE_OCTAL +\135\266\152\304\140\027\044\152\032\231\250\113\356\136\264\046 +END +CKA_ISSUER MULTILINE_OCTAL +\060\107\061\013\060\011\006\003\125\004\006\023\002\125\123\061 +\042\060\040\006\003\125\004\012\023\031\107\157\157\147\154\145 +\040\124\162\165\163\164\040\123\145\162\166\151\143\145\163\040 +\114\114\103\061\024\060\022\006\003\125\004\003\023\013\107\124 +\123\040\122\157\157\164\040\122\064 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\020\156\107\251\310\213\224\266\350\273\073\052\330\242\262 +\301\231 +END +CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE + +# +# Certificate "UCA Global G2 Root" +# +# Issuer: CN=UCA Global G2 Root,O=UniTrust,C=CN +# Serial Number:5d:df:b1:da:5a:a3:ed:5d:be:5a:65:20:65:03:90:ef +# Subject: CN=UCA Global G2 Root,O=UniTrust,C=CN +# Not Valid Before: Fri Mar 11 00:00:00 2016 +# Not Valid After : Mon Dec 31 00:00:00 2040 +# Fingerprint (SHA-256): 9B:EA:11:C9:76:FE:01:47:64:C1:BE:56:A6:F9:14:B5:A5:60:31:7A:BD:99:88:39:33:82:E5:16:1A:A0:49:3C +# Fingerprint (SHA1): 28:F9:78:16:19:7A:FF:18:25:18:AA:44:FE:C1:A0:CE:5C:B6:4C:8A +CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "UCA Global G2 Root" +CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 +CKA_SUBJECT MULTILINE_OCTAL +\060\075\061\013\060\011\006\003\125\004\006\023\002\103\116\061 +\021\060\017\006\003\125\004\012\014\010\125\156\151\124\162\165 +\163\164\061\033\060\031\006\003\125\004\003\014\022\125\103\101 +\040\107\154\157\142\141\154\040\107\062\040\122\157\157\164 +END +CKA_ID UTF8 "0" +CKA_ISSUER MULTILINE_OCTAL +\060\075\061\013\060\011\006\003\125\004\006\023\002\103\116\061 +\021\060\017\006\003\125\004\012\014\010\125\156\151\124\162\165 +\163\164\061\033\060\031\006\003\125\004\003\014\022\125\103\101 +\040\107\154\157\142\141\154\040\107\062\040\122\157\157\164 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\020\135\337\261\332\132\243\355\135\276\132\145\040\145\003 +\220\357 +END +CKA_VALUE MULTILINE_OCTAL +\060\202\005\106\060\202\003\056\240\003\002\001\002\002\020\135 +\337\261\332\132\243\355\135\276\132\145\040\145\003\220\357\060 +\015\006\011\052\206\110\206\367\015\001\001\013\005\000\060\075 +\061\013\060\011\006\003\125\004\006\023\002\103\116\061\021\060 +\017\006\003\125\004\012\014\010\125\156\151\124\162\165\163\164 +\061\033\060\031\006\003\125\004\003\014\022\125\103\101\040\107 +\154\157\142\141\154\040\107\062\040\122\157\157\164\060\036\027 +\015\061\066\060\063\061\061\060\060\060\060\060\060\132\027\015 +\064\060\061\062\063\061\060\060\060\060\060\060\132\060\075\061 +\013\060\011\006\003\125\004\006\023\002\103\116\061\021\060\017 +\006\003\125\004\012\014\010\125\156\151\124\162\165\163\164\061 +\033\060\031\006\003\125\004\003\014\022\125\103\101\040\107\154 +\157\142\141\154\040\107\062\040\122\157\157\164\060\202\002\042 +\060\015\006\011\052\206\110\206\367\015\001\001\001\005\000\003 +\202\002\017\000\060\202\002\012\002\202\002\001\000\305\346\053 +\157\174\357\046\005\047\243\201\044\332\157\313\001\371\231\232 +\251\062\302\042\207\141\101\221\073\313\303\150\033\006\305\114 +\251\053\301\147\027\042\035\053\355\371\051\211\223\242\170\275 +\222\153\240\243\015\242\176\312\223\263\246\321\214\065\325\165 +\371\027\366\317\105\305\345\172\354\167\223\240\217\043\256\016 +\032\003\177\276\324\320\355\056\173\253\106\043\133\377\054\346 +\124\172\224\300\052\025\360\311\215\260\172\073\044\341\327\150 +\342\061\074\006\063\106\266\124\021\246\245\057\042\124\052\130 +\015\001\002\361\372\025\121\147\154\300\372\327\266\033\177\321 +\126\210\057\032\072\215\073\273\202\021\340\107\000\320\122\207 +\253\373\206\176\017\044\153\100\235\064\147\274\215\307\055\206 +\157\171\076\216\251\074\027\113\177\260\231\343\260\161\140\334 +\013\365\144\303\316\103\274\155\161\271\322\336\047\133\212\350 +\330\306\256\341\131\175\317\050\055\065\270\225\126\032\361\262 +\130\113\267\022\067\310\174\263\355\113\200\341\215\372\062\043 +\266\157\267\110\225\010\261\104\116\205\214\072\002\124\040\057 +\337\277\127\117\073\072\220\041\327\301\046\065\124\040\354\307 +\077\107\354\357\132\277\113\172\301\255\073\027\120\134\142\330 +\017\113\112\334\053\372\156\274\163\222\315\354\307\120\350\101 +\226\327\251\176\155\330\351\035\217\212\265\271\130\222\272\112 +\222\053\014\126\375\200\353\010\360\136\051\156\033\034\014\257 +\217\223\211\255\333\275\243\236\041\312\211\031\354\337\265\303 +\032\353\026\376\170\066\114\326\156\320\076\027\034\220\027\153 +\046\272\373\172\057\277\021\034\030\016\055\163\003\217\240\345 +\065\240\132\342\114\165\035\161\341\071\070\123\170\100\314\203 +\223\327\012\236\235\133\217\212\344\345\340\110\344\110\262\107 +\315\116\052\165\052\173\362\042\366\311\276\011\221\226\127\172 +\210\210\254\356\160\254\371\334\051\343\014\034\073\022\116\104 +\326\247\116\260\046\310\363\331\032\227\221\150\352\357\215\106 +\006\322\126\105\130\232\074\014\017\203\270\005\045\303\071\317 +\073\244\064\211\267\171\022\057\107\305\347\251\227\151\374\246 +\167\147\265\337\173\361\172\145\025\344\141\126\145\002\003\001 +\000\001\243\102\060\100\060\016\006\003\125\035\017\001\001\377 +\004\004\003\002\001\006\060\017\006\003\125\035\023\001\001\377 +\004\005\060\003\001\001\377\060\035\006\003\125\035\016\004\026 +\004\024\201\304\214\314\365\344\060\377\245\014\010\137\214\025 +\147\041\164\001\337\337\060\015\006\011\052\206\110\206\367\015 +\001\001\013\005\000\003\202\002\001\000\023\145\042\365\216\053 +\255\104\344\313\377\271\150\346\303\200\110\075\004\173\372\043 +\057\172\355\066\332\262\316\155\366\346\236\345\137\130\217\313 +\067\062\241\310\145\266\256\070\075\065\033\076\274\073\266\004 +\320\274\371\111\365\233\367\205\305\066\266\313\274\370\310\071 +\325\344\137\007\275\025\124\227\164\312\312\355\117\272\272\144 +\166\237\201\270\204\105\111\114\215\157\242\353\261\314\321\303 +\224\332\104\302\346\342\352\030\350\242\037\047\005\272\327\345 +\326\251\315\335\357\166\230\215\000\016\315\033\372\003\267\216 +\200\130\016\047\077\122\373\224\242\312\136\145\311\326\204\332 +\271\065\161\363\046\300\117\167\346\201\047\322\167\073\232\024 +\157\171\364\366\320\341\323\224\272\320\127\121\275\047\005\015 +\301\375\310\022\060\356\157\215\021\053\010\235\324\324\277\200 +\105\024\232\210\104\332\060\352\264\247\343\356\357\133\202\325 +\076\326\255\170\222\333\134\074\363\330\255\372\270\153\177\304 +\066\050\266\002\025\212\124\054\234\260\027\163\216\320\067\243 +\024\074\230\225\000\014\051\005\133\236\111\111\261\137\307\343 +\313\317\047\145\216\065\027\267\127\310\060\331\101\133\271\024 +\266\350\302\017\224\061\247\224\230\314\152\353\265\341\047\365 +\020\250\001\350\216\022\142\350\210\314\265\177\106\227\300\233 +\020\146\070\032\066\106\137\042\150\075\337\311\306\023\047\253 +\123\006\254\242\074\206\006\145\157\261\176\261\051\104\232\243 +\272\111\151\050\151\217\327\345\137\255\004\206\144\157\032\240 +\014\305\010\142\316\200\243\320\363\354\150\336\276\063\307\027 +\133\177\200\304\114\114\261\246\204\212\303\073\270\011\315\024 +\201\272\030\343\124\127\066\376\333\057\174\107\241\072\063\310 +\371\130\073\104\117\261\312\002\211\004\226\050\150\305\113\270 +\046\211\273\326\063\057\120\325\376\232\211\272\030\062\222\124 +\306\133\340\235\371\136\345\015\042\233\366\332\342\310\041\262 +\142\041\252\206\100\262\056\144\323\137\310\343\176\021\147\105 +\037\005\376\343\242\357\263\250\263\363\175\217\370\014\037\042 +\037\055\160\264\270\001\064\166\060\000\345\043\170\247\126\327 +\120\037\212\373\006\365\302\031\360\320 +END +CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE + +# Trust for "UCA Global G2 Root" +# Issuer: CN=UCA Global G2 Root,O=UniTrust,C=CN +# Serial Number:5d:df:b1:da:5a:a3:ed:5d:be:5a:65:20:65:03:90:ef +# Subject: CN=UCA Global G2 Root,O=UniTrust,C=CN +# Not Valid Before: Fri Mar 11 00:00:00 2016 +# Not Valid After : Mon Dec 31 00:00:00 2040 +# Fingerprint (SHA-256): 9B:EA:11:C9:76:FE:01:47:64:C1:BE:56:A6:F9:14:B5:A5:60:31:7A:BD:99:88:39:33:82:E5:16:1A:A0:49:3C +# Fingerprint (SHA1): 28:F9:78:16:19:7A:FF:18:25:18:AA:44:FE:C1:A0:CE:5C:B6:4C:8A +CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "UCA Global G2 Root" +CKA_CERT_SHA1_HASH MULTILINE_OCTAL +\050\371\170\026\031\172\377\030\045\030\252\104\376\301\240\316 +\134\266\114\212 +END +CKA_CERT_MD5_HASH MULTILINE_OCTAL +\200\376\360\304\112\360\134\142\062\237\034\272\170\251\120\370 +END +CKA_ISSUER MULTILINE_OCTAL +\060\075\061\013\060\011\006\003\125\004\006\023\002\103\116\061 +\021\060\017\006\003\125\004\012\014\010\125\156\151\124\162\165 +\163\164\061\033\060\031\006\003\125\004\003\014\022\125\103\101 +\040\107\154\157\142\141\154\040\107\062\040\122\157\157\164 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\020\135\337\261\332\132\243\355\135\276\132\145\040\145\003 +\220\357 +END +CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE + +# +# Certificate "UCA Extended Validation Root" +# +# Issuer: CN=UCA Extended Validation Root,O=UniTrust,C=CN +# Serial Number:4f:d2:2b:8f:f5:64:c8:33:9e:4f:34:58:66:23:70:60 +# Subject: CN=UCA Extended Validation Root,O=UniTrust,C=CN +# Not Valid Before: Fri Mar 13 00:00:00 2015 +# Not Valid After : Fri Dec 31 00:00:00 2038 +# Fingerprint (SHA-256): D4:3A:F9:B3:54:73:75:5C:96:84:FC:06:D7:D8:CB:70:EE:5C:28:E7:73:FB:29:4E:B4:1E:E7:17:22:92:4D:24 +# Fingerprint (SHA1): A3:A1:B0:6F:24:61:23:4A:E3:36:A5:C2:37:FC:A6:FF:DD:F0:D7:3A +CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "UCA Extended Validation Root" +CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 +CKA_SUBJECT MULTILINE_OCTAL +\060\107\061\013\060\011\006\003\125\004\006\023\002\103\116\061 +\021\060\017\006\003\125\004\012\014\010\125\156\151\124\162\165 +\163\164\061\045\060\043\006\003\125\004\003\014\034\125\103\101 +\040\105\170\164\145\156\144\145\144\040\126\141\154\151\144\141 +\164\151\157\156\040\122\157\157\164 +END +CKA_ID UTF8 "0" +CKA_ISSUER MULTILINE_OCTAL +\060\107\061\013\060\011\006\003\125\004\006\023\002\103\116\061 +\021\060\017\006\003\125\004\012\014\010\125\156\151\124\162\165 +\163\164\061\045\060\043\006\003\125\004\003\014\034\125\103\101 +\040\105\170\164\145\156\144\145\144\040\126\141\154\151\144\141 +\164\151\157\156\040\122\157\157\164 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\020\117\322\053\217\365\144\310\063\236\117\064\130\146\043 +\160\140 +END +CKA_VALUE MULTILINE_OCTAL +\060\202\005\132\060\202\003\102\240\003\002\001\002\002\020\117 +\322\053\217\365\144\310\063\236\117\064\130\146\043\160\140\060 +\015\006\011\052\206\110\206\367\015\001\001\013\005\000\060\107 +\061\013\060\011\006\003\125\004\006\023\002\103\116\061\021\060 +\017\006\003\125\004\012\014\010\125\156\151\124\162\165\163\164 +\061\045\060\043\006\003\125\004\003\014\034\125\103\101\040\105 +\170\164\145\156\144\145\144\040\126\141\154\151\144\141\164\151 +\157\156\040\122\157\157\164\060\036\027\015\061\065\060\063\061 +\063\060\060\060\060\060\060\132\027\015\063\070\061\062\063\061 +\060\060\060\060\060\060\132\060\107\061\013\060\011\006\003\125 +\004\006\023\002\103\116\061\021\060\017\006\003\125\004\012\014 +\010\125\156\151\124\162\165\163\164\061\045\060\043\006\003\125 +\004\003\014\034\125\103\101\040\105\170\164\145\156\144\145\144 +\040\126\141\154\151\144\141\164\151\157\156\040\122\157\157\164 +\060\202\002\042\060\015\006\011\052\206\110\206\367\015\001\001 +\001\005\000\003\202\002\017\000\060\202\002\012\002\202\002\001 +\000\251\011\007\050\023\002\260\231\340\144\252\036\103\026\172 +\163\261\221\240\165\076\250\372\343\070\000\172\354\211\152\040 +\017\213\305\260\233\063\003\132\206\306\130\206\325\301\205\273 +\117\306\234\100\115\312\276\356\151\226\270\255\201\060\232\174 +\222\005\353\005\053\232\110\320\270\166\076\226\310\040\273\322 +\260\361\217\330\254\105\106\377\252\147\140\264\167\176\152\037 +\074\032\122\172\004\075\007\074\205\015\204\320\037\166\012\367 +\152\024\337\162\343\064\174\127\116\126\001\076\171\361\252\051 +\073\154\372\370\217\155\115\310\065\337\256\353\334\044\356\171 +\105\247\205\266\005\210\336\210\135\045\174\227\144\147\011\331 +\277\132\025\005\206\363\011\036\354\130\062\063\021\363\167\144 +\260\166\037\344\020\065\027\033\362\016\261\154\244\052\243\163 +\374\011\037\036\062\031\123\021\347\331\263\054\056\166\056\241 +\243\336\176\152\210\011\350\362\007\212\370\262\315\020\347\342 +\163\100\223\273\010\321\077\341\374\013\224\263\045\357\174\246 +\327\321\257\237\377\226\232\365\221\173\230\013\167\324\176\350 +\007\322\142\265\225\071\343\363\361\155\017\016\145\204\212\143 +\124\305\200\266\340\236\113\175\107\046\247\001\010\135\321\210 +\236\327\303\062\104\372\202\112\012\150\124\177\070\123\003\314 +\244\000\063\144\121\131\013\243\202\221\172\136\354\026\302\363 +\052\346\142\332\052\333\131\142\020\045\112\052\201\013\107\007 +\103\006\160\207\322\372\223\021\051\172\110\115\353\224\307\160 +\115\257\147\325\121\261\200\040\001\001\264\172\010\246\220\177 +\116\340\357\007\101\207\257\152\245\136\213\373\317\120\262\232 +\124\257\303\211\272\130\055\365\060\230\261\066\162\071\176\111 +\004\375\051\247\114\171\344\005\127\333\224\271\026\123\215\106 +\263\035\225\141\127\126\177\257\360\026\133\141\130\157\066\120 +\021\013\330\254\053\225\026\032\016\037\010\315\066\064\145\020 +\142\146\325\200\137\024\040\137\055\014\240\170\012\150\326\054 +\327\351\157\053\322\112\005\223\374\236\157\153\147\377\210\361 +\116\245\151\112\122\067\005\352\306\026\215\322\304\231\321\202 +\053\073\272\065\165\367\121\121\130\363\310\007\335\344\264\003 +\177\002\003\001\000\001\243\102\060\100\060\035\006\003\125\035 +\016\004\026\004\024\331\164\072\344\060\075\015\367\022\334\176 +\132\005\237\036\064\232\367\341\024\060\017\006\003\125\035\023 +\001\001\377\004\005\060\003\001\001\377\060\016\006\003\125\035 +\017\001\001\377\004\004\003\002\001\206\060\015\006\011\052\206 +\110\206\367\015\001\001\013\005\000\003\202\002\001\000\066\215 +\227\314\102\025\144\051\067\233\046\054\326\373\256\025\151\054 +\153\032\032\367\137\266\371\007\114\131\352\363\311\310\271\256 +\314\272\056\172\334\300\365\260\055\300\073\257\237\160\005\021 +\152\237\045\117\001\051\160\343\345\014\341\352\132\174\334\111 +\273\301\036\052\201\365\026\113\162\221\310\242\061\271\252\332 +\374\235\037\363\135\100\002\023\374\116\034\006\312\263\024\220 +\124\027\031\022\032\361\037\327\014\151\132\366\161\170\364\224 +\175\221\013\216\354\220\124\216\274\157\241\114\253\374\164\144 +\375\161\232\370\101\007\241\315\221\344\074\232\340\233\062\071 +\163\253\052\325\151\310\170\221\046\061\175\342\307\060\361\374 +\024\170\167\022\016\023\364\335\026\224\277\113\147\173\160\123 +\205\312\260\273\363\070\115\054\220\071\300\015\302\135\153\351 +\342\345\325\210\215\326\054\277\253\033\276\265\050\207\022\027 +\164\156\374\175\374\217\320\207\046\260\033\373\271\154\253\342 +\236\075\025\301\073\056\147\002\130\221\237\357\370\102\037\054 +\267\150\365\165\255\317\265\366\377\021\175\302\360\044\245\255 +\323\372\240\074\251\372\135\334\245\240\357\104\244\276\326\350 +\345\344\023\226\027\173\006\076\062\355\307\267\102\274\166\243 +\330\145\070\053\070\065\121\041\016\016\157\056\064\023\100\341 +\053\147\014\155\112\101\060\030\043\132\062\125\231\311\027\340 +\074\336\366\354\171\255\053\130\031\242\255\054\042\032\225\216 +\276\226\220\135\102\127\304\371\024\003\065\053\034\055\121\127 +\010\247\072\336\077\344\310\264\003\163\302\301\046\200\273\013 +\102\037\255\015\257\046\162\332\314\276\263\243\203\130\015\202 +\305\037\106\121\343\234\030\314\215\233\215\354\111\353\165\120 +\325\214\050\131\312\164\064\332\214\013\041\253\036\352\033\345 +\307\375\025\076\300\027\252\373\043\156\046\106\313\372\371\261 +\162\153\151\317\042\204\013\142\017\254\331\031\000\224\242\166 +\074\324\055\232\355\004\236\055\006\142\020\067\122\034\205\162 +\033\047\345\314\306\061\354\067\354\143\131\233\013\035\166\314 +\176\062\232\210\225\010\066\122\273\336\166\137\166\111\111\255 +\177\275\145\040\262\311\301\053\166\030\166\237\126\261 +END +CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE + +# Trust for "UCA Extended Validation Root" +# Issuer: CN=UCA Extended Validation Root,O=UniTrust,C=CN +# Serial Number:4f:d2:2b:8f:f5:64:c8:33:9e:4f:34:58:66:23:70:60 +# Subject: CN=UCA Extended Validation Root,O=UniTrust,C=CN +# Not Valid Before: Fri Mar 13 00:00:00 2015 +# Not Valid After : Fri Dec 31 00:00:00 2038 +# Fingerprint (SHA-256): D4:3A:F9:B3:54:73:75:5C:96:84:FC:06:D7:D8:CB:70:EE:5C:28:E7:73:FB:29:4E:B4:1E:E7:17:22:92:4D:24 +# Fingerprint (SHA1): A3:A1:B0:6F:24:61:23:4A:E3:36:A5:C2:37:FC:A6:FF:DD:F0:D7:3A +CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "UCA Extended Validation Root" +CKA_CERT_SHA1_HASH MULTILINE_OCTAL +\243\241\260\157\044\141\043\112\343\066\245\302\067\374\246\377 +\335\360\327\072 +END +CKA_CERT_MD5_HASH MULTILINE_OCTAL +\241\363\137\103\306\064\233\332\277\214\176\005\123\255\226\342 +END +CKA_ISSUER MULTILINE_OCTAL +\060\107\061\013\060\011\006\003\125\004\006\023\002\103\116\061 +\021\060\017\006\003\125\004\012\014\010\125\156\151\124\162\165 +\163\164\061\045\060\043\006\003\125\004\003\014\034\125\103\101 +\040\105\170\164\145\156\144\145\144\040\126\141\154\151\144\141 +\164\151\157\156\040\122\157\157\164 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\020\117\322\053\217\365\144\310\063\236\117\064\130\146\043 +\160\140 +END +CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE + +# +# Certificate "Certigna Root CA" +# +# Issuer: CN=Certigna Root CA,OU=0002 48146308100036,O=Dhimyotis,C=FR +# Serial Number:00:ca:e9:1b:89:f1:55:03:0d:a3:e6:41:6d:c4:e3:a6:e1 +# Subject: CN=Certigna Root CA,OU=0002 48146308100036,O=Dhimyotis,C=FR +# Not Valid Before: Tue Oct 01 08:32:27 2013 +# Not Valid After : Sat Oct 01 08:32:27 2033 +# Fingerprint (SHA-256): D4:8D:3D:23:EE:DB:50:A4:59:E5:51:97:60:1C:27:77:4B:9D:7B:18:C9:4D:5A:05:95:11:A1:02:50:B9:31:68 +# Fingerprint (SHA1): 2D:0D:52:14:FF:9E:AD:99:24:01:74:20:47:6E:6C:85:27:27:F5:43 +CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "Certigna Root CA" +CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509 +CKA_SUBJECT MULTILINE_OCTAL +\060\132\061\013\060\011\006\003\125\004\006\023\002\106\122\061 +\022\060\020\006\003\125\004\012\014\011\104\150\151\155\171\157 +\164\151\163\061\034\060\032\006\003\125\004\013\014\023\060\060 +\060\062\040\064\070\061\064\066\063\060\070\061\060\060\060\063 +\066\061\031\060\027\006\003\125\004\003\014\020\103\145\162\164 +\151\147\156\141\040\122\157\157\164\040\103\101 +END +CKA_ID UTF8 "0" +CKA_ISSUER MULTILINE_OCTAL +\060\132\061\013\060\011\006\003\125\004\006\023\002\106\122\061 +\022\060\020\006\003\125\004\012\014\011\104\150\151\155\171\157 +\164\151\163\061\034\060\032\006\003\125\004\013\014\023\060\060 +\060\062\040\064\070\061\064\066\063\060\070\061\060\060\060\063 +\066\061\031\060\027\006\003\125\004\003\014\020\103\145\162\164 +\151\147\156\141\040\122\157\157\164\040\103\101 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\021\000\312\351\033\211\361\125\003\015\243\346\101\155\304 +\343\246\341 +END +CKA_VALUE MULTILINE_OCTAL +\060\202\006\133\060\202\004\103\240\003\002\001\002\002\021\000 +\312\351\033\211\361\125\003\015\243\346\101\155\304\343\246\341 +\060\015\006\011\052\206\110\206\367\015\001\001\013\005\000\060 +\132\061\013\060\011\006\003\125\004\006\023\002\106\122\061\022 +\060\020\006\003\125\004\012\014\011\104\150\151\155\171\157\164 +\151\163\061\034\060\032\006\003\125\004\013\014\023\060\060\060 +\062\040\064\070\061\064\066\063\060\070\061\060\060\060\063\066 +\061\031\060\027\006\003\125\004\003\014\020\103\145\162\164\151 +\147\156\141\040\122\157\157\164\040\103\101\060\036\027\015\061 +\063\061\060\060\061\060\070\063\062\062\067\132\027\015\063\063 +\061\060\060\061\060\070\063\062\062\067\132\060\132\061\013\060 +\011\006\003\125\004\006\023\002\106\122\061\022\060\020\006\003 +\125\004\012\014\011\104\150\151\155\171\157\164\151\163\061\034 +\060\032\006\003\125\004\013\014\023\060\060\060\062\040\064\070 +\061\064\066\063\060\070\061\060\060\060\063\066\061\031\060\027 +\006\003\125\004\003\014\020\103\145\162\164\151\147\156\141\040 +\122\157\157\164\040\103\101\060\202\002\042\060\015\006\011\052 +\206\110\206\367\015\001\001\001\005\000\003\202\002\017\000\060 +\202\002\012\002\202\002\001\000\315\030\071\145\032\131\261\352 +\144\026\016\214\224\044\225\174\203\323\305\071\046\334\014\357 +\026\127\215\327\330\254\243\102\177\202\312\355\315\133\333\016 +\267\055\355\105\010\027\262\331\263\313\326\027\122\162\050\333 +\216\116\236\212\266\013\371\236\204\232\115\166\336\042\051\134 +\322\263\322\006\076\060\071\251\164\243\222\126\034\241\157\114 +\012\040\155\237\043\172\264\306\332\054\344\035\054\334\263\050 +\320\023\362\114\116\002\111\241\124\100\236\346\345\005\240\055 +\204\310\377\230\154\320\353\212\032\204\010\036\267\150\043\356 +\043\325\160\316\155\121\151\020\356\241\172\302\321\042\061\302 +\202\205\322\362\125\166\120\174\045\172\311\204\134\013\254\335 +\102\116\053\347\202\242\044\211\313\220\262\320\356\043\272\146 +\114\273\142\244\371\123\132\144\173\174\230\372\243\110\236\017 +\225\256\247\030\364\152\354\056\003\105\257\360\164\370\052\315 +\172\135\321\276\104\046\062\051\361\361\365\154\314\176\002\041 +\013\237\157\244\077\276\235\123\342\317\175\251\054\174\130\032 +\227\341\075\067\067\030\146\050\322\100\305\121\212\214\303\055 +\316\123\210\044\130\144\060\026\305\252\340\326\012\246\100\337 +\170\366\365\004\174\151\023\204\274\321\321\247\006\317\001\367 +\150\300\250\127\273\072\141\255\004\214\223\343\255\374\360\333 +\104\155\131\334\111\131\256\254\232\231\066\060\101\173\166\063 +\042\207\243\302\222\206\156\371\160\356\256\207\207\225\033\304 +\172\275\061\363\324\322\345\231\377\276\110\354\165\365\170\026 +\035\246\160\301\177\074\033\241\222\373\317\310\074\326\305\223 +\012\217\365\125\072\166\225\316\131\230\212\011\225\167\062\232 +\203\272\054\004\072\227\275\324\057\276\327\154\233\242\312\175 +\155\046\311\125\325\317\303\171\122\010\011\231\007\044\055\144 +\045\153\246\041\151\233\152\335\164\115\153\227\172\101\275\253 +\027\371\220\027\110\217\066\371\055\325\305\333\356\252\205\105 +\101\372\315\072\105\261\150\346\066\114\233\220\127\354\043\271 +\207\010\302\304\011\361\227\206\052\050\115\342\164\300\332\304 +\214\333\337\342\241\027\131\316\044\131\164\061\332\177\375\060 +\155\331\334\341\152\341\374\137\002\003\001\000\001\243\202\001 +\032\060\202\001\026\060\017\006\003\125\035\023\001\001\377\004 +\005\060\003\001\001\377\060\016\006\003\125\035\017\001\001\377 +\004\004\003\002\001\006\060\035\006\003\125\035\016\004\026\004 +\024\030\207\126\340\156\167\356\044\065\074\116\163\232\037\326 +\341\342\171\176\053\060\037\006\003\125\035\043\004\030\060\026 +\200\024\030\207\126\340\156\167\356\044\065\074\116\163\232\037 +\326\341\342\171\176\053\060\104\006\003\125\035\040\004\075\060 +\073\060\071\006\004\125\035\040\000\060\061\060\057\006\010\053 +\006\001\005\005\007\002\001\026\043\150\164\164\160\163\072\057 +\057\167\167\167\167\056\143\145\162\164\151\147\156\141\056\146 +\162\057\141\165\164\157\162\151\164\145\163\057\060\155\006\003 +\125\035\037\004\146\060\144\060\057\240\055\240\053\206\051\150 +\164\164\160\072\057\057\143\162\154\056\143\145\162\164\151\147 +\156\141\056\146\162\057\143\145\162\164\151\147\156\141\162\157 +\157\164\143\141\056\143\162\154\060\061\240\057\240\055\206\053 +\150\164\164\160\072\057\057\143\162\154\056\144\150\151\155\171 +\157\164\151\163\056\143\157\155\057\143\145\162\164\151\147\156 +\141\162\157\157\164\143\141\056\143\162\154\060\015\006\011\052 +\206\110\206\367\015\001\001\013\005\000\003\202\002\001\000\224 +\270\236\117\360\343\225\010\042\347\315\150\101\367\034\125\325 +\174\000\342\055\072\211\135\150\070\057\121\042\013\112\215\313 +\351\273\135\076\273\134\075\261\050\376\344\123\125\023\317\241 +\220\033\002\035\137\146\106\011\063\050\341\015\044\227\160\323 +\020\037\352\144\127\226\273\135\332\347\304\214\117\114\144\106 +\035\134\207\343\131\336\102\321\233\250\176\246\211\335\217\034 +\311\060\202\355\073\234\315\300\351\031\340\152\330\002\165\067 +\253\367\064\050\050\221\362\004\012\117\065\343\140\046\001\372 +\320\021\214\371\021\152\356\257\075\303\120\323\217\137\063\171 +\074\206\250\163\105\220\214\040\266\162\163\027\043\276\007\145 +\345\170\222\015\272\001\300\353\214\034\146\277\254\206\167\001 +\224\015\234\346\351\071\215\037\246\121\214\231\014\071\167\341 +\264\233\372\034\147\127\157\152\152\216\251\053\114\127\171\172 +\127\042\317\315\137\143\106\215\134\131\072\206\370\062\107\142 +\243\147\015\030\221\334\373\246\153\365\110\141\163\043\131\216 +\002\247\274\104\352\364\111\235\361\124\130\371\140\257\332\030 +\244\057\050\105\334\172\240\210\206\135\363\073\347\377\051\065 +\200\374\144\103\224\346\343\034\157\276\255\016\052\143\231\053 +\311\176\205\366\161\350\006\003\225\376\336\217\110\034\132\324 +\222\350\053\356\347\061\333\272\004\152\207\230\347\305\137\357 +\175\247\042\367\001\330\115\371\211\320\016\232\005\131\244\236 +\230\331\157\053\312\160\276\144\302\125\243\364\351\257\303\222 +\051\334\210\026\044\231\074\215\046\230\266\133\267\314\316\267 +\067\007\375\046\331\230\205\044\377\131\043\003\232\355\235\235 +\250\344\136\070\316\327\122\015\157\322\077\155\261\005\153\111 +\316\212\221\106\163\364\366\057\360\250\163\167\016\145\254\241 +\215\146\122\151\176\113\150\014\307\036\067\047\203\245\214\307 +\002\344\024\315\111\001\260\163\263\375\306\220\072\157\322\154 +\355\073\356\354\221\276\242\103\135\213\000\112\146\045\104\160 +\336\100\017\370\174\025\367\242\316\074\327\136\023\214\201\027 +\030\027\321\275\361\167\020\072\324\145\071\301\047\254\127\054 +\045\124\377\242\332\117\212\141\071\136\256\075\112\214\275 +END +CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_TRUE + +# Trust for "Certigna Root CA" +# Issuer: CN=Certigna Root CA,OU=0002 48146308100036,O=Dhimyotis,C=FR +# Serial Number:00:ca:e9:1b:89:f1:55:03:0d:a3:e6:41:6d:c4:e3:a6:e1 +# Subject: CN=Certigna Root CA,OU=0002 48146308100036,O=Dhimyotis,C=FR +# Not Valid Before: Tue Oct 01 08:32:27 2013 +# Not Valid After : Sat Oct 01 08:32:27 2033 +# Fingerprint (SHA-256): D4:8D:3D:23:EE:DB:50:A4:59:E5:51:97:60:1C:27:77:4B:9D:7B:18:C9:4D:5A:05:95:11:A1:02:50:B9:31:68 +# Fingerprint (SHA1): 2D:0D:52:14:FF:9E:AD:99:24:01:74:20:47:6E:6C:85:27:27:F5:43 +CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST +CKA_TOKEN CK_BBOOL CK_TRUE +CKA_PRIVATE CK_BBOOL CK_FALSE +CKA_MODIFIABLE CK_BBOOL CK_FALSE +CKA_LABEL UTF8 "Certigna Root CA" +CKA_CERT_SHA1_HASH MULTILINE_OCTAL +\055\015\122\024\377\236\255\231\044\001\164\040\107\156\154\205 +\047\047\365\103 +END +CKA_CERT_MD5_HASH MULTILINE_OCTAL +\016\134\060\142\047\353\133\274\327\256\142\272\351\325\337\167 +END +CKA_ISSUER MULTILINE_OCTAL +\060\132\061\013\060\011\006\003\125\004\006\023\002\106\122\061 +\022\060\020\006\003\125\004\012\014\011\104\150\151\155\171\157 +\164\151\163\061\034\060\032\006\003\125\004\013\014\023\060\060 +\060\062\040\064\070\061\064\066\063\060\070\061\060\060\060\063 +\066\061\031\060\027\006\003\125\004\003\014\020\103\145\162\164 +\151\147\156\141\040\122\157\157\164\040\103\101 +END +CKA_SERIAL_NUMBER MULTILINE_OCTAL +\002\021\000\312\351\033\211\361\125\003\015\243\346\101\155\304 +\343\246\341 +END +CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_EMAIL_PROTECTION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR +CKA_TRUST_CODE_SIGNING CK_TRUST CKT_NSS_MUST_VERIFY_TRUST +CKA_TRUST_STEP_UP_APPROVED CK_BBOOL CK_FALSE diff --git a/security/nss/lib/ckfw/builtins/nssckbi.h b/security/nss/lib/ckfw/builtins/nssckbi.h index d40c8080e..157d9c40d 100644 --- a/security/nss/lib/ckfw/builtins/nssckbi.h +++ b/security/nss/lib/ckfw/builtins/nssckbi.h @@ -46,8 +46,8 @@ * It's recommend to switch back to 0 after having reached version 98/99. */ #define NSS_BUILTINS_LIBRARY_VERSION_MAJOR 2 -#define NSS_BUILTINS_LIBRARY_VERSION_MINOR 24 -#define NSS_BUILTINS_LIBRARY_VERSION "2.24" +#define NSS_BUILTINS_LIBRARY_VERSION_MINOR 30 +#define NSS_BUILTINS_LIBRARY_VERSION "2.30" /* These version numbers detail the semantic changes to the ckfw engine. */ #define NSS_BUILTINS_HARDWARE_VERSION_MAJOR 1 diff --git a/security/nss/lib/ckfw/ckfw.h b/security/nss/lib/ckfw/ckfw.h index d4a2ead99..9e7e17e36 100644 --- a/security/nss/lib/ckfw/ckfw.h +++ b/security/nss/lib/ckfw/ckfw.h @@ -1604,8 +1604,8 @@ nssCKFWSession_InitPIN( NSS_EXTERN CK_RV nssCKFWSession_SetPIN( NSSCKFWSession *fwSession, - NSSItem *newPin, - NSSItem *oldPin); + const NSSItem *oldPin, + NSSItem *newPin); /* * nssCKFWSession_GetOperationStateLen diff --git a/security/nss/lib/ckfw/session.c b/security/nss/lib/ckfw/session.c index 7efedf403..e2613089b 100644 --- a/security/nss/lib/ckfw/session.c +++ b/security/nss/lib/ckfw/session.c @@ -871,7 +871,7 @@ nssCKFWSession_InitPIN( NSS_IMPLEMENT CK_RV nssCKFWSession_SetPIN( NSSCKFWSession *fwSession, - NSSItem *oldPin, + const NSSItem *oldPin, NSSItem *newPin) { CK_RV error = CKR_OK; @@ -907,7 +907,7 @@ nssCKFWSession_SetPIN( error = fwSession->mdSession->SetPIN(fwSession->mdSession, fwSession, fwSession->mdToken, fwSession->fwToken, fwSession->mdInstance, - fwSession->fwInstance, oldPin, newPin); + fwSession->fwInstance, (NSSItem *)oldPin, newPin); return error; } diff --git a/security/nss/lib/cryptohi/cryptohi.h b/security/nss/lib/cryptohi/cryptohi.h index e529fa34f..7b66f0b0b 100644 --- a/security/nss/lib/cryptohi/cryptohi.h +++ b/security/nss/lib/cryptohi/cryptohi.h @@ -14,7 +14,7 @@ #include "secoidt.h" #include "secdert.h" #include "cryptoht.h" -#include "keyt.h" +#include "keythi.h" #include "certt.h" SEC_BEGIN_PROTOS diff --git a/security/nss/lib/cryptohi/key.h b/security/nss/lib/cryptohi/key.h index 3e89b74cb..8392031c5 100644 --- a/security/nss/lib/cryptohi/key.h +++ b/security/nss/lib/cryptohi/key.h @@ -2,11 +2,13 @@ * 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/. */ -/* This header is deprecated. Please include keyhi.h instead. */ - #ifndef _KEY_H_ #define _KEY_H_ +#if defined(_MSC_VER) || defined(__GNUC__) || defined(__clang__) +#pragma message("key.h is deprecated. Please include keyhi.h instead.") +#endif + #include "keyhi.h" #endif /* _KEY_H_ */ diff --git a/security/nss/lib/cryptohi/keyi.h b/security/nss/lib/cryptohi/keyi.h index ee11fc905..b746d3c8d 100644 --- a/security/nss/lib/cryptohi/keyi.h +++ b/security/nss/lib/cryptohi/keyi.h @@ -17,8 +17,21 @@ KeyType seckey_GetKeyType(SECOidTag pubKeyOid); SECStatus sec_DecodeSigAlg(const SECKEYPublicKey *key, SECOidTag sigAlg, const SECItem *param, SECOidTag *encalg, SECOidTag *hashalg); -SECStatus sec_RSAPSSParamsToMechanism(CK_RSA_PKCS_PSS_PARAMS *mech, - const SECKEYRSAPSSParams *params); +/* extract the RSA-PSS hash algorithms and salt length from + * parameters, taking into account of the default implications. + * + * (parameters is the parameters field of a algorithm ID structure + * (SECAlgorithmID)*/ +SECStatus sec_DecodeRSAPSSParams(PLArenaPool *arena, + const SECItem *params, + SECOidTag *hashAlg, + SECOidTag *maskHashAlg, + unsigned long *saltLength); + +/* convert the encoded RSA-PSS parameters into PKCS #11 mechanism parameters */ +SECStatus sec_DecodeRSAPSSParamsToMechanism(PLArenaPool *arena, + const SECItem *params, + CK_RSA_PKCS_PSS_PARAMS *mech); SEC_END_PROTOS diff --git a/security/nss/lib/cryptohi/keyt.h b/security/nss/lib/cryptohi/keyt.h index 99da312f6..5a0d2c2e7 100644 --- a/security/nss/lib/cryptohi/keyt.h +++ b/security/nss/lib/cryptohi/keyt.h @@ -5,6 +5,10 @@ #ifndef _KEYT_H_ #define _KEYT_H_ +#if defined(_MSC_VER) || defined(__GNUC__) || defined(__clang__) +#pragma message("keyt.h is deprecated. Please include keythi.h instead.") +#endif + #include "keythi.h" #endif /* _KEYT_H_ */ diff --git a/security/nss/lib/cryptohi/seckey.c b/security/nss/lib/cryptohi/seckey.c index 0f9353f3b..080909772 100644 --- a/security/nss/lib/cryptohi/seckey.c +++ b/security/nss/lib/cryptohi/seckey.c @@ -2015,66 +2015,63 @@ sec_GetMgfTypeByOidTag(SECOidTag tag) } SECStatus -sec_RSAPSSParamsToMechanism(CK_RSA_PKCS_PSS_PARAMS *mech, - const SECKEYRSAPSSParams *params) +sec_DecodeRSAPSSParams(PLArenaPool *arena, + const SECItem *params, + SECOidTag *retHashAlg, SECOidTag *retMaskHashAlg, + unsigned long *retSaltLength) { - SECStatus rv = SECSuccess; - SECOidTag hashAlgTag; + SECKEYRSAPSSParams pssParams; + SECOidTag hashAlg; + SECOidTag maskHashAlg; unsigned long saltLength; unsigned long trailerField; + SECStatus rv; - PORT_Memset(mech, 0, sizeof(CK_RSA_PKCS_PSS_PARAMS)); + PORT_Memset(&pssParams, 0, sizeof(pssParams)); + rv = SEC_QuickDERDecodeItem(arena, &pssParams, + SECKEY_RSAPSSParamsTemplate, + params); + if (rv != SECSuccess) { + return rv; + } - if (params->hashAlg) { - hashAlgTag = SECOID_GetAlgorithmTag(params->hashAlg); + if (pssParams.hashAlg) { + hashAlg = SECOID_GetAlgorithmTag(pssParams.hashAlg); } else { - hashAlgTag = SEC_OID_SHA1; /* default, SHA-1 */ - } - mech->hashAlg = sec_GetHashMechanismByOidTag(hashAlgTag); - if (mech->hashAlg == CKM_INVALID_MECHANISM) { - return SECFailure; + hashAlg = SEC_OID_SHA1; /* default, SHA-1 */ } - if (params->maskAlg) { - SECAlgorithmID maskHashAlg; - SECOidTag maskHashAlgTag; - PORTCheapArenaPool tmpArena; + if (pssParams.maskAlg) { + SECAlgorithmID algId; - if (SECOID_GetAlgorithmTag(params->maskAlg) != SEC_OID_PKCS1_MGF1) { + if (SECOID_GetAlgorithmTag(pssParams.maskAlg) != SEC_OID_PKCS1_MGF1) { /* only MGF1 is known to PKCS#11 */ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); return SECFailure; } - PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); - rv = SEC_QuickDERDecodeItem(&tmpArena.arena, &maskHashAlg, + rv = SEC_QuickDERDecodeItem(arena, &algId, SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), - ¶ms->maskAlg->parameters); - PORT_DestroyCheapArena(&tmpArena); + &pssParams.maskAlg->parameters); if (rv != SECSuccess) { return rv; } - maskHashAlgTag = SECOID_GetAlgorithmTag(&maskHashAlg); - mech->mgf = sec_GetMgfTypeByOidTag(maskHashAlgTag); - if (mech->mgf == 0) { - return SECFailure; - } + maskHashAlg = SECOID_GetAlgorithmTag(&algId); } else { - mech->mgf = CKG_MGF1_SHA1; /* default, MGF1 with SHA-1 */ + maskHashAlg = SEC_OID_SHA1; /* default, MGF1 with SHA-1 */ } - if (params->saltLength.data) { - rv = SEC_ASN1DecodeInteger((SECItem *)¶ms->saltLength, &saltLength); + if (pssParams.saltLength.data) { + rv = SEC_ASN1DecodeInteger((SECItem *)&pssParams.saltLength, &saltLength); if (rv != SECSuccess) { return rv; } } else { saltLength = 20; /* default, 20 */ } - mech->sLen = saltLength; - if (params->trailerField.data) { - rv = SEC_ASN1DecodeInteger((SECItem *)¶ms->trailerField, &trailerField); + if (pssParams.trailerField.data) { + rv = SEC_ASN1DecodeInteger((SECItem *)&pssParams.trailerField, &trailerField); if (rv != SECSuccess) { return rv; } @@ -2086,5 +2083,46 @@ sec_RSAPSSParamsToMechanism(CK_RSA_PKCS_PSS_PARAMS *mech, } } - return rv; + if (retHashAlg) { + *retHashAlg = hashAlg; + } + if (retMaskHashAlg) { + *retMaskHashAlg = maskHashAlg; + } + if (retSaltLength) { + *retSaltLength = saltLength; + } + + return SECSuccess; +} + +SECStatus +sec_DecodeRSAPSSParamsToMechanism(PLArenaPool *arena, + const SECItem *params, + CK_RSA_PKCS_PSS_PARAMS *mech) +{ + SECOidTag hashAlg; + SECOidTag maskHashAlg; + unsigned long saltLength; + SECStatus rv; + + rv = sec_DecodeRSAPSSParams(arena, params, + &hashAlg, &maskHashAlg, &saltLength); + if (rv != SECSuccess) { + return SECFailure; + } + + mech->hashAlg = sec_GetHashMechanismByOidTag(hashAlg); + if (mech->hashAlg == CKM_INVALID_MECHANISM) { + return SECFailure; + } + + mech->mgf = sec_GetMgfTypeByOidTag(maskHashAlg); + if (mech->mgf == 0) { + return SECFailure; + } + + mech->sLen = saltLength; + + return SECSuccess; } diff --git a/security/nss/lib/cryptohi/secsign.c b/security/nss/lib/cryptohi/secsign.c index dc10f2fa6..8a8d0f664 100644 --- a/security/nss/lib/cryptohi/secsign.c +++ b/security/nss/lib/cryptohi/secsign.c @@ -225,22 +225,13 @@ SGN_End(SGNContext *cx, SECItem *result) PORT_Memset(&mech, 0, sizeof(mech)); if (cx->params && cx->params->data) { - SECKEYRSAPSSParams params; - arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) { rv = SECFailure; goto loser; } - PORT_Memset(¶ms, 0, sizeof(params)); - rv = SEC_QuickDERDecodeItem(arena, ¶ms, - SECKEY_RSAPSSParamsTemplate, - cx->params); - if (rv != SECSuccess) { - goto loser; - } - rv = sec_RSAPSSParamsToMechanism(&mech, ¶ms); + rv = sec_DecodeRSAPSSParamsToMechanism(arena, cx->params, &mech); if (rv != SECSuccess) { goto loser; } diff --git a/security/nss/lib/cryptohi/secvfy.c b/security/nss/lib/cryptohi/secvfy.c index 83c9c579d..aa3d6778c 100644 --- a/security/nss/lib/cryptohi/secvfy.c +++ b/security/nss/lib/cryptohi/secvfy.c @@ -161,7 +161,7 @@ verifyPKCS1DigestInfo(const VFYContext *cx, const SECItem *digest) pkcs1DigestInfo.len = cx->pkcs1RSADigestInfoLen; return _SGN_VerifyPKCS1DigestInfo( cx->hashAlg, digest, &pkcs1DigestInfo, - PR_TRUE /*XXX: unsafeAllowMissingParameters*/); + PR_FALSE /*XXX: unsafeAllowMissingParameters*/); } /* @@ -257,25 +257,13 @@ sec_DecodeSigAlg(const SECKEYPublicKey *key, SECOidTag sigAlg, break; case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: if (param && param->data) { - SECKEYRSAPSSParams pssParam; - arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); - if (arena == NULL) { - return SECFailure; - } - PORT_Memset(&pssParam, 0, sizeof pssParam); - rv = SEC_QuickDERDecodeItem(arena, &pssParam, - SECKEY_RSAPSSParamsTemplate, - param); - if (rv != SECSuccess) { - PORT_FreeArena(arena, PR_FALSE); - return rv; - } - if (pssParam.hashAlg) { - *hashalg = SECOID_GetAlgorithmTag(pssParam.hashAlg); - } else { - *hashalg = SEC_OID_SHA1; /* default, SHA-1 */ - } - PORT_FreeArena(arena, PR_FALSE); + PORTCheapArenaPool tmpArena; + + PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); + rv = sec_DecodeRSAPSSParams(&tmpArena.arena, param, + hashalg, NULL, NULL); + PORT_DestroyCheapArena(&tmpArena); + /* only accept hash algorithms */ if (HASH_GetHashTypeByOidTag(*hashalg) == HASH_AlgNULL) { /* error set by HASH_GetHashTypeByOidTag */ @@ -658,27 +646,17 @@ VFY_EndWithSignature(VFYContext *cx, SECItem *sig) if (cx->encAlg == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { CK_RSA_PKCS_PSS_PARAMS mech; SECItem mechItem = { siBuffer, (unsigned char *)&mech, sizeof(mech) }; - SECKEYRSAPSSParams params; - PLArenaPool *arena; + PORTCheapArenaPool tmpArena; - arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); - if (arena == NULL) { - return SECFailure; - } - - PORT_Memset(¶ms, 0, sizeof(params)); - rv = SEC_QuickDERDecodeItem(arena, ¶ms, - SECKEY_RSAPSSParamsTemplate, - cx->params); - if (rv != SECSuccess) { - PORT_FreeArena(arena, PR_FALSE); - return SECFailure; - } - rv = sec_RSAPSSParamsToMechanism(&mech, ¶ms); - PORT_FreeArena(arena, PR_FALSE); + PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE); + rv = sec_DecodeRSAPSSParamsToMechanism(&tmpArena.arena, + cx->params, + &mech); + PORT_DestroyCheapArena(&tmpArena); if (rv != SECSuccess) { return SECFailure; } + rsasig.data = cx->u.buffer; rsasig.len = SECKEY_SignatureLen(cx->key); if (rsasig.len == 0) { diff --git a/security/nss/lib/freebl/ctr.c b/security/nss/lib/freebl/ctr.c index b7167d4c4..d7652c060 100644 --- a/security/nss/lib/freebl/ctr.c +++ b/security/nss/lib/freebl/ctr.c @@ -219,15 +219,18 @@ CTR_Update_HW_AES(CTRContext *ctr, unsigned char *outbuf, PORT_Assert(ctr->bufPtr == blocksize); } - intel_aes_ctr_worker(((AESContext *)(ctr->context))->Nr)( - ctr, outbuf, outlen, maxout, inbuf, inlen, blocksize); - /* XXX intel_aes_ctr_worker should set *outlen. */ - PORT_Assert(*outlen == 0); - fullblocks = (inlen / blocksize) * blocksize; - *outlen += fullblocks; - outbuf += fullblocks; - inbuf += fullblocks; - inlen -= fullblocks; + if (inlen >= blocksize) { + rv = intel_aes_ctr_worker(((AESContext *)(ctr->context))->Nr)( + ctr, outbuf, outlen, maxout, inbuf, inlen, blocksize); + if (rv != SECSuccess) { + return SECFailure; + } + fullblocks = (inlen / blocksize) * blocksize; + *outlen += fullblocks; + outbuf += fullblocks; + inbuf += fullblocks; + inlen -= fullblocks; + } if (inlen == 0) { return SECSuccess; diff --git a/security/nss/lib/freebl/freebl.gyp b/security/nss/lib/freebl/freebl.gyp index 004807483..288ff07a3 100644 --- a/security/nss/lib/freebl/freebl.gyp +++ b/security/nss/lib/freebl/freebl.gyp @@ -7,6 +7,30 @@ ], 'targets': [ { + 'target_name': 'intel-gcm-s_lib', + 'type': 'static_library', + 'sources': [ + 'intel-aes.s', + 'intel-gcm.s', + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports' + ], + 'conditions': [ + [ 'cc_is_clang==1', { + 'cflags': [ + '-no-integrated-as', + ], + 'cflags_mozilla': [ + '-no-integrated-as', + ], + 'asflags_mozilla': [ + '-no-integrated-as', + ], + }], + ], + }, + { 'target_name': 'intel-gcm-wrap_c_lib', 'type': 'static_library', 'sources': [ @@ -15,12 +39,19 @@ 'dependencies': [ '<(DEPTH)/exports.gyp:nss_exports' ], + 'conditions': [ + [ '(OS=="linux" or OS=="android") and target_arch=="x64"', { + 'dependencies': [ + 'intel-gcm-s_lib', + ], + }], + ], 'cflags': [ - '-mssse3' + '-mssse3', ], 'cflags_mozilla': [ '-mssse3' - ] + ], }, { # TODO: make this so that all hardware accelerated code is in here. diff --git a/security/nss/lib/freebl/freebl_base.gypi b/security/nss/lib/freebl/freebl_base.gypi index 1372994f4..76df71497 100644 --- a/security/nss/lib/freebl/freebl_base.gypi +++ b/security/nss/lib/freebl/freebl_base.gypi @@ -67,14 +67,12 @@ [ 'target_arch=="x64"', { 'sources': [ 'arcfour-amd64-gas.s', - 'intel-aes.s', - 'intel-gcm.s', 'mpi/mpi_amd64.c', 'mpi/mpi_amd64_gas.s', 'mpi/mp_comba.c', ], 'conditions': [ - [ 'cc_is_clang==1', { + [ 'cc_is_clang==1 and fuzz!=1', { 'cflags': [ '-no-integrated-as', ], @@ -114,8 +112,7 @@ 'intel-gcm-x64-masm.asm', ], }], - [ 'cc_use_gnu_ld!=1 and target_arch!="x64"', { - # not x64 + [ 'cc_use_gnu_ld!=1 and target_arch=="ia32"', { 'sources': [ 'mpi/mpi_x86_asm.c', 'intel-aes-x86-masm.asm', diff --git a/security/nss/lib/freebl/mpi/mpi.c b/security/nss/lib/freebl/mpi/mpi.c index 8c893fb5f..401eac51d 100644 --- a/security/nss/lib/freebl/mpi/mpi.c +++ b/security/nss/lib/freebl/mpi/mpi.c @@ -4775,38 +4775,61 @@ mp_to_signed_octets(const mp_int *mp, unsigned char *str, mp_size maxlen) /* }}} */ /* {{{ mp_to_fixlen_octets(mp, str) */ -/* output a buffer of big endian octets exactly as long as requested. */ +/* output a buffer of big endian octets exactly as long as requested. + constant time on the value of mp. */ mp_err mp_to_fixlen_octets(const mp_int *mp, unsigned char *str, mp_size length) { - int ix, pos = 0; + int ix, jx; unsigned int bytes; - ARGCHK(mp != NULL && str != NULL && !SIGN(mp), MP_BADARG); - - bytes = mp_unsigned_octet_size(mp); - ARGCHK(bytes <= length, MP_BADARG); + ARGCHK(mp != NULL, MP_BADARG); + ARGCHK(str != NULL, MP_BADARG); + ARGCHK(!SIGN(mp), MP_BADARG); + ARGCHK(length > 0, MP_BADARG); + + /* Constant time on the value of mp. Don't use mp_unsigned_octet_size. */ + bytes = USED(mp) * MP_DIGIT_SIZE; + + /* If the output is shorter than the native size of mp, then check that any + * bytes not written have zero values. This check isn't constant time on + * the assumption that timing-sensitive callers can guarantee that mp fits + * in the allocated space. */ + ix = USED(mp) - 1; + if (bytes > length) { + unsigned int zeros = bytes - length; + + while (zeros >= MP_DIGIT_SIZE) { + ARGCHK(DIGIT(mp, ix) == 0, MP_BADARG); + zeros -= MP_DIGIT_SIZE; + ix--; + } - /* place any needed leading zeros */ - for (; length > bytes; --length) { - *str++ = 0; + if (zeros > 0) { + mp_digit d = DIGIT(mp, ix); + mp_digit m = ~0ULL << ((MP_DIGIT_SIZE - zeros) * CHAR_BIT); + ARGCHK((d & m) == 0, MP_BADARG); + for (jx = MP_DIGIT_SIZE - zeros - 1; jx >= 0; jx--) { + *str++ = d >> (jx * CHAR_BIT); + } + ix--; + } + } else if (bytes < length) { + /* Place any needed leading zeros. */ + unsigned int zeros = length - bytes; + memset(str, 0, zeros); + str += zeros; } - /* Iterate over each digit... */ - for (ix = USED(mp) - 1; ix >= 0; ix--) { + /* Iterate over each whole digit... */ + for (; ix >= 0; ix--) { mp_digit d = DIGIT(mp, ix); - int jx; /* Unpack digit bytes, high order first */ - for (jx = sizeof(mp_digit) - 1; jx >= 0; jx--) { - unsigned char x = (unsigned char)(d >> (jx * CHAR_BIT)); - if (!pos && !x) /* suppress leading zeros */ - continue; - str[pos++] = x; + for (jx = MP_DIGIT_SIZE - 1; jx >= 0; jx--) { + *str++ = d >> (jx * CHAR_BIT); } } - if (!pos) - str[pos++] = 0; return MP_OKAY; } /* end mp_to_fixlen_octets() */ /* }}} */ diff --git a/security/nss/lib/freebl/mpi/mpi.h b/security/nss/lib/freebl/mpi/mpi.h index 97af0f069..d5aef46d7 100644 --- a/security/nss/lib/freebl/mpi/mpi.h +++ b/security/nss/lib/freebl/mpi/mpi.h @@ -128,7 +128,8 @@ typedef int mp_sword; #define MP_WORD_MAX UINT_MAX #endif -#define MP_DIGIT_BIT (CHAR_BIT * sizeof(mp_digit)) +#define MP_DIGIT_SIZE sizeof(mp_digit) +#define MP_DIGIT_BIT (CHAR_BIT * MP_DIGIT_SIZE) #define MP_WORD_BIT (CHAR_BIT * sizeof(mp_word)) #define MP_RADIX (1 + (mp_word)MP_DIGIT_MAX) diff --git a/security/nss/lib/freebl/mpi/mpi_arm.c b/security/nss/lib/freebl/mpi/mpi_arm.c index b5139f28d..27e4efdad 100644 --- a/security/nss/lib/freebl/mpi/mpi_arm.c +++ b/security/nss/lib/freebl/mpi/mpi_arm.c @@ -29,17 +29,17 @@ s_mpv_mul_d(const mp_digit *a, mp_size a_len, mp_digit b, mp_digit *c) "1:\n" "mov r4, #0\n" "ldr r6, [%0], #4\n" - "umlal r5, r4, r6, %2\n" - "str r5, [%3], #4\n" + "umlal r5, r4, r6, %3\n" + "str r5, [%2], #4\n" "mov r5, r4\n" "subs %1, #1\n" "bne 1b\n" "2:\n" - "str r5, [%3]\n" - : - : "r"(a), "r"(a_len), "r"(b), "r"(c) + "str r5, [%2]\n" + : "+r"(a), "+l"(a_len), "+r"(c) + : "r"(b) : "memory", "cc", "%r4", "%r5", "%r6"); } @@ -57,22 +57,22 @@ s_mpv_mul_d_add(const mp_digit *a, mp_size a_len, mp_digit b, mp_digit *c) "1:\n" "mov r4, #0\n" - "ldr r6, [%3]\n" + "ldr r6, [%2]\n" "adds r5, r6\n" "adc r4, r4, #0\n" "ldr r6, [%0], #4\n" - "umlal r5, r4, r6, %2\n" - "str r5, [%3], #4\n" + "umlal r5, r4, r6, %3\n" + "str r5, [%2], #4\n" "mov r5, r4\n" "subs %1, #1\n" "bne 1b\n" "2:\n" - "str r5, [%3]\n" - : - : "r"(a), "r"(a_len), "r"(b), "r"(c) + "str r5, [%2]\n" + : "+r"(a), "+l"(a_len), "+r"(c) + : "r"(b) : "memory", "cc", "%r4", "%r5", "%r6"); } @@ -87,12 +87,12 @@ s_mpv_mul_d_add_prop(const mp_digit *a, mp_size a_len, mp_digit b, mp_digit *c) "1:\n" "mov r4, #0\n" - "ldr r6, [%3]\n" + "ldr r6, [%2]\n" "adds r5, r6\n" "adc r4, r4, #0\n" "ldr r6, [%0], #4\n" - "umlal r5, r4, r6, %2\n" - "str r5, [%3], #4\n" + "umlal r5, r4, r6, %3\n" + "str r5, [%2], #4\n" "mov r5, r4\n" "subs %1, #1\n" @@ -107,16 +107,16 @@ s_mpv_mul_d_add_prop(const mp_digit *a, mp_size a_len, mp_digit b, mp_digit *c) "2:\n" "mov r4, #0\n" - "ldr r6, [%3]\n" + "ldr r6, [%2]\n" "adds r5, r6\n" "adc r4, r4, #0\n" - "str r5, [%3], #4\n" + "str r5, [%2], #4\n" "movs r5, r4\n" "bne 2b\n" "3:\n" - : - : "r"(a), "r"(a_len), "r"(b), "r"(c) + : "+r"(a), "+l"(a_len), "+r"(c) + : "r"(b) : "memory", "cc", "%r4", "%r5", "%r6"); } #endif @@ -167,8 +167,8 @@ s_mpv_sqr_add_prop(const mp_digit *pa, mp_size a_len, mp_digit *ps) "bne 2b\n" "3:" + : "+r"(pa), "+r"(a_len), "+r"(ps) : - : "r"(pa), "r"(a_len), "r"(ps) : "memory", "cc", "%r3", "%r4", "%r5", "%r6"); } #endif diff --git a/security/nss/lib/freebl/rsapkcs.c b/security/nss/lib/freebl/rsapkcs.c index ad18c8b73..875e4e28d 100644 --- a/security/nss/lib/freebl/rsapkcs.c +++ b/security/nss/lib/freebl/rsapkcs.c @@ -938,48 +938,56 @@ RSA_DecryptBlock(RSAPrivateKey *key, const unsigned char *input, unsigned int inputLen) { - SECStatus rv; + PRInt8 rv; unsigned int modulusLen = rsa_modulusLen(&key->modulus); unsigned int i; - unsigned char *buffer; + unsigned char *buffer = NULL; + unsigned int outLen = 0; + unsigned int copyOutLen = modulusLen - 11; - if (inputLen != modulusLen) - goto failure; + if (inputLen != modulusLen || modulusLen < 10) { + return SECFailure; + } - buffer = (unsigned char *)PORT_Alloc(modulusLen + 1); - if (!buffer) - goto failure; + if (copyOutLen > maxOutputLen) { + copyOutLen = maxOutputLen; + } - rv = RSA_PrivateKeyOp(key, buffer, input); - if (rv != SECSuccess) - goto loser; + // Allocate enough space to decrypt + copyOutLen to allow copying outLen later. + buffer = PORT_ZAlloc(modulusLen + 1 + copyOutLen); + if (!buffer) { + return SECFailure; + } - /* XXX(rsleevi): Constant time */ - if (buffer[0] != RSA_BLOCK_FIRST_OCTET || - buffer[1] != (unsigned char)RSA_BlockPublic) { - goto loser; + // rv is 0 if everything is going well and 1 if an error occurs. + rv = RSA_PrivateKeyOp(key, buffer, input) != SECSuccess; + rv |= (buffer[0] != RSA_BLOCK_FIRST_OCTET) | + (buffer[1] != (unsigned char)RSA_BlockPublic); + + // There have to be at least 8 bytes of padding. + for (i = 2; i < 10; i++) { + rv |= buffer[i] == RSA_BLOCK_AFTER_PAD_OCTET; } - *outputLen = 0; - for (i = 2; i < modulusLen; i++) { - if (buffer[i] == RSA_BLOCK_AFTER_PAD_OCTET) { - *outputLen = modulusLen - i - 1; - break; - } + + for (i = 10; i < modulusLen; i++) { + unsigned int newLen = modulusLen - i - 1; + unsigned int c = (buffer[i] == RSA_BLOCK_AFTER_PAD_OCTET) & (outLen == 0); + outLen = constantTimeCondition(c, newLen, outLen); } - if (*outputLen == 0) - goto loser; - if (*outputLen > maxOutputLen) - goto loser; + rv |= outLen == 0; + rv |= outLen > maxOutputLen; - PORT_Memcpy(output, buffer + modulusLen - *outputLen, *outputLen); + // Note that output is set even if SECFailure is returned. + PORT_Memcpy(output, buffer + modulusLen - outLen, copyOutLen); + *outputLen = constantTimeCondition(outLen > maxOutputLen, maxOutputLen, + outLen); PORT_Free(buffer); - return SECSuccess; -loser: - PORT_Free(buffer); -failure: - return SECFailure; + for (i = 1; i < sizeof(rv) * 8; i <<= 1) { + rv |= rv << i; + } + return (SECStatus)rv; } /* diff --git a/security/nss/lib/jar/jarint.h b/security/nss/lib/jar/jarint.h index 21aecef89..0f40f931f 100644 --- a/security/nss/lib/jar/jarint.h +++ b/security/nss/lib/jar/jarint.h @@ -5,7 +5,7 @@ /* JAR internal routines */ #include "nspr.h" -#include "key.h" +#include "keyhi.h" #include "base64.h" extern CERTCertDBHandle *JAR_open_database(void); diff --git a/security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.c b/security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.c index fa8f1851e..145dcff9a 100644 --- a/security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.c +++ b/security/nss/lib/libpkix/pkix_pl_nss/pki/pkix_pl_cert.c @@ -2914,7 +2914,8 @@ PKIX_PL_Cert_CheckValidity( requiredUsages = ((PKIX_PL_NssContext*)plContext)->certificateUsage; allowOverride = (PRBool)((requiredUsages & certificateUsageSSLServer) || - (requiredUsages & certificateUsageSSLServerWithStepUp)); + (requiredUsages & certificateUsageSSLServerWithStepUp) || + (requiredUsages & certificateUsageIPsec)); val = CERT_CheckCertValidTimes(cert->nssCert, timeToCheck, allowOverride); if (val != secCertTimeValid){ PKIX_ERROR(PKIX_CERTCHECKCERTVALIDTIMESFAILED); @@ -3001,8 +3002,17 @@ PKIX_PL_Cert_VerifyCertAndKeyType( if (CERT_CheckKeyUsage(cert->nssCert, requiredKeyUsage) != SECSuccess) { PKIX_ERROR(PKIX_CERTCHECKKEYUSAGEFAILED); } - if (!(certType & requiredCertType)) { - PKIX_ERROR(PKIX_CERTCHECKCERTTYPEFAILED); + if (certUsage != certUsageIPsec) { + if (!(certType & requiredCertType)) { + PKIX_ERROR(PKIX_CERTCHECKCERTTYPEFAILED); + } + } else { + PRBool isCritical; + PRBool allowed = cert_EKUAllowsIPsecIKE(cert->nssCert, &isCritical); + /* If the extension isn't critical, we allow any EKU value. */ + if (isCritical && !allowed) { + PKIX_ERROR(PKIX_CERTCHECKCERTTYPEFAILED); + } } cleanup: PKIX_DECREF(basicConstraints); diff --git a/security/nss/lib/mozpkix/.clang-format b/security/nss/lib/mozpkix/.clang-format new file mode 100644 index 000000000..06e3c5115 --- /dev/null +++ b/security/nss/lib/mozpkix/.clang-format @@ -0,0 +1,4 @@ +--- +Language: Cpp +BasedOnStyle: Google +... diff --git a/security/nss/lib/mozpkix/exports.gyp b/security/nss/lib/mozpkix/exports.gyp new file mode 100644 index 000000000..248efc910 --- /dev/null +++ b/security/nss/lib/mozpkix/exports.gyp @@ -0,0 +1,47 @@ +# 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/. +{ + 'includes': [ + '../../coreconf/config.gypi' + ], + 'targets': [ + { + 'target_name': 'lib_mozpkix_exports', + 'type': 'none', + 'copies': [ + { + 'files': [ + '<(DEPTH)/cpputil/nss_scoped_ptrs.h', + 'include/pkix/Input.h', + 'include/pkix/Time.h', + 'include/pkix/Result.h', + 'include/pkix/pkix.h', + 'include/pkix/pkixnss.h', + 'include/pkix/pkixtypes.h', + 'include/pkix/pkixutil.h', + 'include/pkix/pkixcheck.h', + 'include/pkix/pkixder.h', + ], + 'destination': '<(nss_public_dist_dir)/<(module)/mozpkix' + }, + ], + }, + { + 'target_name': 'lib_mozpkix_test_exports', + 'type': 'none', + 'copies': [ + { + 'files': [ + 'include/pkix-test/pkixtestutil.h', + 'include/pkix-test/pkixtestnss.h', + ], + 'destination': '<(nss_public_dist_dir)/<(module)/mozpkix/test' + }, + ], + } + ], + 'variables': { + 'module': 'nss' + } +}
\ No newline at end of file diff --git a/security/nss/lib/mozpkix/include/pkix-test/pkixtestnss.h b/security/nss/lib/mozpkix/include/pkix-test/pkixtestnss.h new file mode 100644 index 000000000..5ae776f6a --- /dev/null +++ b/security/nss/lib/mozpkix/include/pkix-test/pkixtestnss.h @@ -0,0 +1,48 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2018 Mozilla Contributors + * + * 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. + */ + +// This file provides some implementation-specific test utilities. This is only +// necessary because some PSM xpcshell test utilities overlap in functionality +// with these test utilities, so the underlying implementation is shared. + +#ifndef mozilla_pkix_test_pkixtestnss_h +#define mozilla_pkix_test_pkixtestnss_h + +#include <keyhi.h> +#include <keythi.h> +#include "mozpkix/nss_scoped_ptrs.h" +#include "mozpkix/test/pkixtestutil.h" + +namespace mozilla { +namespace pkix { +namespace test { + +TestKeyPair* CreateTestKeyPair(const TestPublicKeyAlgorithm publicKeyAlg, + const ScopedSECKEYPublicKey& publicKey, + const ScopedSECKEYPrivateKey& privateKey); +} +} +} // namespace mozilla::pkix::test + +#endif // mozilla_pkix_test_pkixtestnss_h diff --git a/security/nss/lib/mozpkix/include/pkix-test/pkixtestutil.h b/security/nss/lib/mozpkix/include/pkix-test/pkixtestutil.h new file mode 100644 index 000000000..55c435419 --- /dev/null +++ b/security/nss/lib/mozpkix/include/pkix-test/pkixtestutil.h @@ -0,0 +1,406 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * 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. + */ + +#ifndef mozilla_pkix_test_pkixtestutil_h +#define mozilla_pkix_test_pkixtestutil_h + +#include <cstdint> +#include <cstring> +#include <ctime> +#include <string> + +#include "mozpkix/pkixtypes.h" + +namespace mozilla { +namespace pkix { +namespace test { + +typedef std::basic_string<uint8_t> ByteString; + +inline bool ENCODING_FAILED(const ByteString& bs) { return bs.empty(); } + +template <size_t L> +inline ByteString BytesToByteString(const uint8_t (&bytes)[L]) { + return ByteString(bytes, L); +} + +// XXX: Ideally, we should define this instead: +// +// template <typename T, std::size_t N> +// constexpr inline std::size_t +// ArrayLength(T (&)[N]) +// { +// return N; +// } +// +// However, we don't because not all supported compilers support constexpr, +// and we need to calculate array lengths in static_assert sometimes. +// +// XXX: Evaluates its argument twice +#define MOZILLA_PKIX_ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0])) + +bool InputEqualsByteString(Input input, const ByteString& bs); +ByteString InputToByteString(Input input); + +// python DottedOIDToCode.py --tlv id-kp-OCSPSigning 1.3.6.1.5.5.7.3.9 +static const uint8_t tlv_id_kp_OCSPSigning[] = {0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x03, 0x09}; + +// python DottedOIDToCode.py --tlv id-kp-serverAuth 1.3.6.1.5.5.7.3.1 +static const uint8_t tlv_id_kp_serverAuth[] = {0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x03, 0x01}; + +enum class TestDigestAlgorithmID { + MD2, + MD5, + SHA1, + SHA224, + SHA256, + SHA384, + SHA512, +}; + +struct TestPublicKeyAlgorithm { + explicit TestPublicKeyAlgorithm(const ByteString& aAlgorithmIdentifier) + : algorithmIdentifier(aAlgorithmIdentifier) {} + bool operator==(const TestPublicKeyAlgorithm& other) const { + return algorithmIdentifier == other.algorithmIdentifier; + } + ByteString algorithmIdentifier; +}; + +ByteString DSS_P(); +ByteString DSS_Q(); +ByteString DSS_G(); + +TestPublicKeyAlgorithm DSS(); +TestPublicKeyAlgorithm RSA_PKCS1(); + +struct TestSignatureAlgorithm { + TestSignatureAlgorithm(const TestPublicKeyAlgorithm& publicKeyAlg, + TestDigestAlgorithmID digestAlg, + const ByteString& algorithmIdentifier, bool accepted); + + TestPublicKeyAlgorithm publicKeyAlg; + TestDigestAlgorithmID digestAlg; + ByteString algorithmIdentifier; + bool accepted; +}; + +TestSignatureAlgorithm md2WithRSAEncryption(); +TestSignatureAlgorithm md5WithRSAEncryption(); +TestSignatureAlgorithm sha1WithRSAEncryption(); +TestSignatureAlgorithm sha256WithRSAEncryption(); + +// e.g. YMDHMS(2016, 12, 31, 1, 23, 45) => 2016-12-31:01:23:45 (GMT) +mozilla::pkix::Time YMDHMS(uint16_t year, uint16_t month, uint16_t day, + uint16_t hour, uint16_t minutes, uint16_t seconds); + +ByteString TLV(uint8_t tag, size_t length, const ByteString& value); + +inline ByteString TLV(uint8_t tag, const ByteString& value) { + return TLV(tag, value.length(), value); +} + +// Although we can't enforce it without relying on Cuser-defined literals, +// which aren't supported by all of our compilers yet, you should only pass +// string literals as the last parameter to the following two functions. + +template <size_t N> +inline ByteString TLV(uint8_t tag, const char (&value)[N]) { + static_assert(N > 0, "cannot have string literal of size 0"); + assert(value[N - 1] == 0); + return TLV(tag, ByteString(reinterpret_cast<const uint8_t*>(&value), N - 1)); +} + +template <size_t N> +inline ByteString TLV(uint8_t tag, size_t length, const char (&value)[N]) { + static_assert(N > 0, "cannot have string literal of size 0"); + assert(value[N - 1] == 0); + return TLV(tag, length, + ByteString(reinterpret_cast<const uint8_t*>(&value), N - 1)); +} + +ByteString Boolean(bool value); +ByteString Integer(long value); + +ByteString CN(const ByteString&, uint8_t encodingTag = 0x0c /*UTF8String*/); + +inline ByteString CN(const char* value, + uint8_t encodingTag = 0x0c /*UTF8String*/) { + return CN( + ByteString(reinterpret_cast<const uint8_t*>(value), std::strlen(value)), + encodingTag); +} + +ByteString OU(const ByteString&, uint8_t encodingTag = 0x0c /*UTF8String*/); + +inline ByteString OU(const char* value, + uint8_t encodingTag = 0x0c /*UTF8String*/) { + return OU( + ByteString(reinterpret_cast<const uint8_t*>(value), std::strlen(value)), + encodingTag); +} + +ByteString emailAddress(const ByteString&); + +inline ByteString emailAddress(const char* value) { + return emailAddress( + ByteString(reinterpret_cast<const uint8_t*>(value), std::strlen(value))); +} + +// RelativeDistinguishedName ::= +// SET SIZE (1..MAX) OF AttributeTypeAndValue +// +ByteString RDN(const ByteString& avas); + +// Name ::= CHOICE { -- only one possibility for now -- +// rdnSequence RDNSequence } +// +// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName +// +ByteString Name(const ByteString& rdns); + +inline ByteString CNToDERName(const ByteString& cn) { + return Name(RDN(CN(cn))); +} + +inline ByteString CNToDERName(const char* cn) { return Name(RDN(CN(cn))); } + +// GeneralName ::= CHOICE { +// otherName [0] OtherName, +// rfc822Name [1] IA5String, +// dNSName [2] IA5String, +// x400Address [3] ORAddress, +// directoryName [4] Name, +// ediPartyName [5] EDIPartyName, +// uniformResourceIdentifier [6] IA5String, +// iPAddress [7] OCTET STRING, +// registeredID [8] OBJECT IDENTIFIER } + +inline ByteString RFC822Name(const ByteString& name) { + // (2 << 6) means "context-specific", 1 is the GeneralName tag. + return TLV((2 << 6) | 1, name); +} + +template <size_t L> +inline ByteString RFC822Name(const char (&bytes)[L]) { + return RFC822Name( + ByteString(reinterpret_cast<const uint8_t*>(&bytes), L - 1)); +} + +inline ByteString DNSName(const ByteString& name) { + // (2 << 6) means "context-specific", 2 is the GeneralName tag. + return TLV((2 << 6) | 2, name); +} + +template <size_t L> +inline ByteString DNSName(const char (&bytes)[L]) { + return DNSName(ByteString(reinterpret_cast<const uint8_t*>(&bytes), L - 1)); +} + +inline ByteString DirectoryName(const ByteString& name) { + // (2 << 6) means "context-specific", (1 << 5) means "constructed", and 4 is + // the DirectoryName tag. + return TLV((2 << 6) | (1 << 5) | 4, name); +} + +inline ByteString IPAddress() { + // (2 << 6) means "context-specific", 7 is the GeneralName tag. + return TLV((2 << 6) | 7, ByteString()); +} + +template <size_t L> +inline ByteString IPAddress(const uint8_t (&bytes)[L]) { + // (2 << 6) means "context-specific", 7 is the GeneralName tag. + return TLV((2 << 6) | 7, ByteString(bytes, L)); +} + +// Names should be zero or more GeneralNames, like DNSName and IPAddress return, +// concatenated together. +// +// CreatedEncodedSubjectAltName(ByteString()) results in a SAN with an empty +// sequence. CreateEmptyEncodedSubjectName() results in a SAN without any +// sequence. +ByteString CreateEncodedSubjectAltName(const ByteString& names); +ByteString CreateEncodedEmptySubjectAltName(); + +class TestKeyPair { + public: + virtual ~TestKeyPair() {} + + const TestPublicKeyAlgorithm publicKeyAlg; + + // The DER encoding of the entire SubjectPublicKeyInfo structure. This is + // what is encoded in certificates. + const ByteString subjectPublicKeyInfo; + + // The DER encoding of subjectPublicKeyInfo.subjectPublicKey. This is what is + // hashed to create CertIDs for OCSP. + const ByteString subjectPublicKey; + + virtual Result SignData(const ByteString& tbs, + const TestSignatureAlgorithm& signatureAlgorithm, + /*out*/ ByteString& signature) const = 0; + + virtual TestKeyPair* Clone() const = 0; + + protected: + TestKeyPair(const TestPublicKeyAlgorithm& publicKeyAlg, + const ByteString& spk); + TestKeyPair(const TestKeyPair&) = delete; + void operator=(const TestKeyPair&) = delete; +}; + +TestKeyPair* CloneReusedKeyPair(); +TestKeyPair* GenerateKeyPair(); +TestKeyPair* GenerateDSSKeyPair(); +inline void DeleteTestKeyPair(TestKeyPair* keyPair) { delete keyPair; } +typedef std::unique_ptr<TestKeyPair> ScopedTestKeyPair; + +Result TestVerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo); +Result TestVerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo); +Result TestDigestBuf(Input item, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestBufLen); + +// Replace one substring in item with another of the same length, but only if +// the substring was found exactly once. The "same length" restriction is +// useful for avoiding invalidating lengths encoded within the item. The +// "only once" restriction is helpful for avoiding making accidental changes. +// +// The string to search for must be 8 or more bytes long so that it is +// extremely unlikely that there will ever be any false positive matches +// in digital signatures, keys, hashes, etc. +Result TamperOnce(/*in/out*/ ByteString& item, const ByteString& from, + const ByteString& to); + +/////////////////////////////////////////////////////////////////////////////// +// Encode Certificates + +enum Version { v1 = 0, v2 = 1, v3 = 2 }; + +// signature is assumed to be the DER encoding of an AlgorithmIdentifer. It is +// put into the signature field of the TBSCertificate. In most cases, it will +// be the same as signatureAlgorithm, which is the algorithm actually used +// to sign the certificate. +// serialNumber is assumed to be the DER encoding of an INTEGER. +// +// If extensions is null, then no extensions will be encoded. Otherwise, +// extensions must point to an array of ByteStrings, terminated with an empty +// ByteString. (If the first item of the array is empty then an empty +// Extensions sequence will be encoded.) +ByteString CreateEncodedCertificate( + long version, const TestSignatureAlgorithm& signature, + const ByteString& serialNumber, const ByteString& issuerNameDER, + time_t notBefore, time_t notAfter, const ByteString& subjectNameDER, + const TestKeyPair& subjectKeyPair, + /*optional*/ const ByteString* extensions, const TestKeyPair& issuerKeyPair, + const TestSignatureAlgorithm& signatureAlgorithm); + +ByteString CreateEncodedSerialNumber(long value); + +enum class Critical { No = 0, Yes = 1 }; + +ByteString CreateEncodedBasicConstraints( + bool isCA, + /*optional in*/ const long* pathLenConstraint, Critical critical); + +// Creates a DER-encoded extKeyUsage extension with one EKU OID. +ByteString CreateEncodedEKUExtension(Input eku, Critical critical); + +/////////////////////////////////////////////////////////////////////////////// +// Encode OCSP responses + +class OCSPResponseExtension final { + public: + OCSPResponseExtension(); + + ByteString id; + bool critical; + ByteString value; + OCSPResponseExtension* next; +}; + +class OCSPResponseContext final { + public: + OCSPResponseContext(const CertID& certID, std::time_t time); + + const CertID& certID; + // TODO(bug 980538): add a way to specify what certificates are included. + + // The fields below are in the order that they appear in an OCSP response. + + enum OCSPResponseStatus { + successful = 0, + malformedRequest = 1, + internalError = 2, + tryLater = 3, + // 4 is not used + sigRequired = 5, + unauthorized = 6, + }; + uint8_t responseStatus; // an OCSPResponseStatus or an invalid value + bool skipResponseBytes; // If true, don't include responseBytes + + // responderID + ByteString signerNameDER; // If set, responderID will use the byName + // form; otherwise responderID will use the + // byKeyHash form. + + std::time_t producedAt; + + // SingleResponse extensions (for the certID given in the constructor). + OCSPResponseExtension* singleExtensions; + // ResponseData extensions. + OCSPResponseExtension* responseExtensions; + bool includeEmptyExtensions; // If true, include the extension wrapper + // regardless of if there are any actual + // extensions. + ScopedTestKeyPair signerKeyPair; + TestSignatureAlgorithm signatureAlgorithm; + bool badSignature; // If true, alter the signature to fail verification + const ByteString* certs; // optional; array terminated by an empty string + + // The following fields are on a per-SingleResponse basis. In the future we + // may support including multiple SingleResponses per response. + enum CertStatus { + good = 0, + revoked = 1, + unknown = 2, + }; + uint8_t certStatus; // CertStatus or an invalid value + std::time_t revocationTime; // For certStatus == revoked + std::time_t thisUpdate; + std::time_t nextUpdate; + bool includeNextUpdate; +}; + +ByteString CreateEncodedOCSPResponse(OCSPResponseContext& context); +} +} +} // namespace mozilla::pkix::test + +#endif // mozilla_pkix_test_pkixtestutil_h diff --git a/security/nss/lib/mozpkix/include/pkix/Input.h b/security/nss/lib/mozpkix/include/pkix/Input.h new file mode 100644 index 000000000..11b2a0f7e --- /dev/null +++ b/security/nss/lib/mozpkix/include/pkix/Input.h @@ -0,0 +1,310 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * 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. + */ + +#ifndef mozilla_pkix_Input_h +#define mozilla_pkix_Input_h + +#include <algorithm> + +#include "mozpkix/Result.h" +#include "stdint.h" + +namespace mozilla { +namespace pkix { + +class Reader; + +// An Input is a safety-oriented immutable weak reference to a array of bytes +// of a known size. The data can only be legally accessed by constructing a +// Reader object, which guarantees all accesses to the data are memory safe. +// Neither Input not Reader provide any facilities for modifying the data +// they reference. +// +// Inputs are small and should usually be passed by value, not by reference, +// though for inline functions the distinction doesn't matter: +// +// Result GoodExample(Input input); +// Result BadExample(const Input& input); +// Result WorseExample(const uint8_t* input, size_t len); +// +// Note that in the example, GoodExample has the same performance +// characteristics as WorseExample, but with much better safety guarantees. +class Input final { + public: + typedef uint16_t size_type; + + // This constructor is useful for inputs that are statically known to be of a + // fixed size, e.g.: + // + // static const uint8_t EXPECTED_BYTES[] = { 0x00, 0x01, 0x02 }; + // const Input expected(EXPECTED_BYTES); + // + // This is equivalent to (and preferred over): + // + // static const uint8_t EXPECTED_BYTES[] = { 0x00, 0x01, 0x02 }; + // Input expected; + // Result rv = expected.Init(EXPECTED_BYTES, sizeof EXPECTED_BYTES); + template <size_type N> + explicit Input(const uint8_t (&aData)[N]) : data(aData), len(N) {} + + // Construct a valid, empty, Init-able Input. + Input() : data(nullptr), len(0u) {} + + // This is intentionally not explicit in order to allow value semantics. + Input(const Input&) = default; + + // Initialize the input. data must be non-null and len must be less than + // 65536. Init may not be called more than once. + Result Init(const uint8_t* aData, size_t aLen) { + if (this->data) { + // already initialized + return Result::FATAL_ERROR_INVALID_ARGS; + } + if (!aData || aLen > 0xffffu) { + // input too large + return Result::ERROR_BAD_DER; + } + + this->data = aData; + this->len = aLen; + + return Success; + } + + // Initialize the input to be equivalent to the given input. Init may not be + // called more than once. + // + // This is basically operator=, but it wasn't given that name because + // normally callers do not check the result of operator=, and normally + // operator= can be used multiple times. + Result Init(Input other) { return Init(other.data, other.len); } + + // Returns the length of the input. + // + // Having the return type be size_type instead of size_t avoids the need for + // callers to ensure that the result is small enough. + size_type GetLength() const { return static_cast<size_type>(len); } + + // Don't use this. It is here because we have some "friend" functions that we + // don't want to declare in this header file. + const uint8_t* UnsafeGetData() const { return data; } + + private: + const uint8_t* data; + size_t len; + + void operator=(const Input&) = delete; // Use Init instead. +}; + +inline bool InputsAreEqual(const Input& a, const Input& b) { + return a.GetLength() == b.GetLength() && + std::equal(a.UnsafeGetData(), a.UnsafeGetData() + a.GetLength(), + b.UnsafeGetData()); +} + +// An Reader is a cursor/iterator through the contents of an Input, designed to +// maximize safety during parsing while minimizing the performance cost of that +// safety. In particular, all methods do strict bounds checking to ensure +// buffer overflows are impossible, and they are all inline so that the +// compiler can coalesce as many of those checks together as possible. +// +// In general, Reader allows for one byte of lookahead and no backtracking. +// However, the Match* functions internally may have more lookahead. +class Reader final { + public: + Reader() : input(nullptr), end(nullptr) {} + + explicit Reader(Input aInput) + : input(aInput.UnsafeGetData()), + end(aInput.UnsafeGetData() + aInput.GetLength()) {} + + Result Init(Input aInput) { + if (this->input) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + this->input = aInput.UnsafeGetData(); + this->end = aInput.UnsafeGetData() + aInput.GetLength(); + return Success; + } + + bool Peek(uint8_t expectedByte) const { + return input < end && *input == expectedByte; + } + + Result Read(uint8_t& out) { + Result rv = EnsureLength(1); + if (rv != Success) { + return rv; + } + out = *input++; + return Success; + } + + Result Read(uint16_t& out) { + Result rv = EnsureLength(2); + if (rv != Success) { + return rv; + } + out = *input++; + out <<= 8u; + out |= *input++; + return Success; + } + + template <Input::size_type N> + bool MatchRest(const uint8_t (&toMatch)[N]) { + // Normally we use EnsureLength which compares (input + len < end), but + // here we want to be sure that there is nothing following the matched + // bytes + if (static_cast<size_t>(end - input) != N) { + return false; + } + if (!std::equal(input, end, toMatch)) { + return false; + } + input = end; + return true; + } + + bool MatchRest(Input toMatch) { + // Normally we use EnsureLength which compares (input + len < end), but + // here we want to be sure that there is nothing following the matched + // bytes + size_t remaining = static_cast<size_t>(end - input); + if (toMatch.GetLength() != remaining) { + return false; + } + if (!std::equal(input, end, toMatch.UnsafeGetData())) { + return false; + } + input = end; + return true; + } + + Result Skip(Input::size_type len) { + Result rv = EnsureLength(len); + if (rv != Success) { + return rv; + } + input += len; + return Success; + } + + Result Skip(Input::size_type len, Reader& skipped) { + Result rv = EnsureLength(len); + if (rv != Success) { + return rv; + } + rv = skipped.Init(input, len); + if (rv != Success) { + return rv; + } + input += len; + return Success; + } + + Result Skip(Input::size_type len, /*out*/ Input& skipped) { + Result rv = EnsureLength(len); + if (rv != Success) { + return rv; + } + rv = skipped.Init(input, len); + if (rv != Success) { + return rv; + } + input += len; + return Success; + } + + void SkipToEnd() { input = end; } + + Result SkipToEnd(/*out*/ Input& skipped) { + return Skip(static_cast<Input::size_type>(end - input), skipped); + } + + Result EnsureLength(Input::size_type len) { + if (static_cast<size_t>(end - input) < len) { + return Result::ERROR_BAD_DER; + } + return Success; + } + + bool AtEnd() const { return input == end; } + + class Mark final { + public: + Mark(const Mark&) = default; // Intentionally not explicit. + private: + friend class Reader; + Mark(const Reader& aInput, const uint8_t* aMark) + : input(aInput), mark(aMark) {} + const Reader& input; + const uint8_t* const mark; + void operator=(const Mark&) = delete; + }; + + Mark GetMark() const { return Mark(*this, input); } + + Result GetInput(const Mark& mark, /*out*/ Input& item) { + if (&mark.input != this || mark.mark > input) { + return NotReached("invalid mark", Result::FATAL_ERROR_INVALID_ARGS); + } + return item.Init(mark.mark, + static_cast<Input::size_type>(input - mark.mark)); + } + + private: + Result Init(const uint8_t* data, Input::size_type len) { + if (input) { + // already initialized + return Result::FATAL_ERROR_INVALID_ARGS; + } + input = data; + end = data + len; + return Success; + } + + const uint8_t* input; + const uint8_t* end; + + Reader(const Reader&) = delete; + void operator=(const Reader&) = delete; +}; + +inline bool InputContains(const Input& input, uint8_t toFind) { + Reader reader(input); + for (;;) { + uint8_t b; + if (reader.Read(b) != Success) { + return false; + } + if (b == toFind) { + return true; + } + } +} +} +} // namespace mozilla::pkix + +#endif // mozilla_pkix_Input_h diff --git a/security/nss/lib/mozpkix/include/pkix/Result.h b/security/nss/lib/mozpkix/include/pkix/Result.h new file mode 100644 index 000000000..29461dc1a --- /dev/null +++ b/security/nss/lib/mozpkix/include/pkix/Result.h @@ -0,0 +1,219 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * 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. + */ + +#ifndef mozilla_pkix_Result_h +#define mozilla_pkix_Result_h + +#include <cassert> + +namespace mozilla { +namespace pkix { + +static const unsigned int FATAL_ERROR_FLAG = 0x800; + +// ---------------------------------------------------------------------------- +// SELECTED ERROR CODE EXPLANATIONS +// +// Result::ERROR_UNTRUSTED_CERT +// means that the end-entity certificate was actively distrusted. +// Result::ERROR_UNTRUSTED_ISSUER +// means that path building failed because of active distrust. +// Result::ERROR_INVALID_DER_TIME +// means the DER-encoded time was unexpected, such as being before the +// UNIX epoch (allowed by X500, but not valid here). +// Result::ERROR_EXPIRED_CERTIFICATE +// means the end entity certificate expired. +// Result::ERROR_EXPIRED_ISSUER_CERTIFICATE +// means the CA certificate expired. +// Result::ERROR_UNKNOWN_ISSUER +// means that the CA could not be found in the root store. +// Result::ERROR_POLICY_VALIDATION_FAILED +// means that an encoded policy could not be applied or wasn't present +// when expected. Usually this is in the context of Extended Validation. +// Result::ERROR_BAD_CERT_DOMAIN +// means that the certificate's name couldn't be matched to the +// reference identifier. +// Result::ERROR_CERT_NOT_IN_NAME_SPACE +// typically means the certificate violates name constraints applied +// by the issuer. +// Result::ERROR_BAD_DER +// means the input was improperly encoded. +// Result::ERROR_UNKNOWN_ERROR +// means that an external library (NSS) provided an error we didn't +// anticipate. See the map below in Result.h to add new ones. +// Result::FATAL_ERROR_LIBRARY_FAILURE +// is an unexpected fatal error indicating a library had an unexpected +// failure, and we can't proceed. +// Result::FATAL_ERROR_INVALID_ARGS +// means that we violated our own expectations on inputs and there's a +// bug somewhere. +// Result::FATAL_ERROR_INVALID_STATE +// means that we violated our own expectations on state and there's a +// bug somewhere. +// Result::FATAL_ERROR_NO_MEMORY +// means a memory allocation failed, prohibiting validation. +// ---------------------------------------------------------------------------- + +// The first argument to MOZILLA_PKIX_MAP() is used for building the mapping +// from error code to error name in MapResultToName. +// +// The second argument is for defining the value for the enum literal in the +// Result enum class. +// +// The third argument to MOZILLA_PKIX_MAP() is used, along with the first +// argument, for maintaining the mapping of mozilla::pkix error codes to +// NSS/NSPR error codes in pkixnss.cpp. +#define MOZILLA_PKIX_MAP_LIST \ + MOZILLA_PKIX_MAP(Success, 0, 0) \ + MOZILLA_PKIX_MAP(ERROR_BAD_DER, 1, SEC_ERROR_BAD_DER) \ + MOZILLA_PKIX_MAP(ERROR_CA_CERT_INVALID, 2, SEC_ERROR_CA_CERT_INVALID) \ + MOZILLA_PKIX_MAP(ERROR_BAD_SIGNATURE, 3, SEC_ERROR_BAD_SIGNATURE) \ + MOZILLA_PKIX_MAP(ERROR_CERT_BAD_ACCESS_LOCATION, 4, \ + SEC_ERROR_CERT_BAD_ACCESS_LOCATION) \ + MOZILLA_PKIX_MAP(ERROR_CERT_NOT_IN_NAME_SPACE, 5, \ + SEC_ERROR_CERT_NOT_IN_NAME_SPACE) \ + MOZILLA_PKIX_MAP(ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, 6, \ + SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED) \ + MOZILLA_PKIX_MAP(ERROR_CONNECT_REFUSED, 7, PR_CONNECT_REFUSED_ERROR) \ + MOZILLA_PKIX_MAP(ERROR_EXPIRED_CERTIFICATE, 8, \ + SEC_ERROR_EXPIRED_CERTIFICATE) \ + MOZILLA_PKIX_MAP(ERROR_EXTENSION_VALUE_INVALID, 9, \ + SEC_ERROR_EXTENSION_VALUE_INVALID) \ + MOZILLA_PKIX_MAP(ERROR_INADEQUATE_CERT_TYPE, 10, \ + SEC_ERROR_INADEQUATE_CERT_TYPE) \ + MOZILLA_PKIX_MAP(ERROR_INADEQUATE_KEY_USAGE, 11, \ + SEC_ERROR_INADEQUATE_KEY_USAGE) \ + MOZILLA_PKIX_MAP(ERROR_INVALID_ALGORITHM, 12, SEC_ERROR_INVALID_ALGORITHM) \ + MOZILLA_PKIX_MAP(ERROR_INVALID_DER_TIME, 13, SEC_ERROR_INVALID_TIME) \ + MOZILLA_PKIX_MAP(ERROR_KEY_PINNING_FAILURE, 14, \ + MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE) \ + MOZILLA_PKIX_MAP(ERROR_PATH_LEN_CONSTRAINT_INVALID, 15, \ + SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID) \ + MOZILLA_PKIX_MAP(ERROR_POLICY_VALIDATION_FAILED, 16, \ + SEC_ERROR_POLICY_VALIDATION_FAILED) \ + MOZILLA_PKIX_MAP(ERROR_REVOKED_CERTIFICATE, 17, \ + SEC_ERROR_REVOKED_CERTIFICATE) \ + MOZILLA_PKIX_MAP(ERROR_UNKNOWN_CRITICAL_EXTENSION, 18, \ + SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION) \ + MOZILLA_PKIX_MAP(ERROR_UNKNOWN_ERROR, 19, PR_UNKNOWN_ERROR) \ + MOZILLA_PKIX_MAP(ERROR_UNKNOWN_ISSUER, 20, SEC_ERROR_UNKNOWN_ISSUER) \ + MOZILLA_PKIX_MAP(ERROR_UNTRUSTED_CERT, 21, SEC_ERROR_UNTRUSTED_CERT) \ + MOZILLA_PKIX_MAP(ERROR_UNTRUSTED_ISSUER, 22, SEC_ERROR_UNTRUSTED_ISSUER) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_BAD_SIGNATURE, 23, SEC_ERROR_OCSP_BAD_SIGNATURE) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_INVALID_SIGNING_CERT, 24, \ + SEC_ERROR_OCSP_INVALID_SIGNING_CERT) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_MALFORMED_REQUEST, 25, \ + SEC_ERROR_OCSP_MALFORMED_REQUEST) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_MALFORMED_RESPONSE, 26, \ + SEC_ERROR_OCSP_MALFORMED_RESPONSE) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_OLD_RESPONSE, 27, SEC_ERROR_OCSP_OLD_RESPONSE) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_REQUEST_NEEDS_SIG, 28, \ + SEC_ERROR_OCSP_REQUEST_NEEDS_SIG) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_RESPONDER_CERT_INVALID, 29, \ + SEC_ERROR_OCSP_RESPONDER_CERT_INVALID) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_SERVER_ERROR, 30, SEC_ERROR_OCSP_SERVER_ERROR) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_TRY_SERVER_LATER, 31, \ + SEC_ERROR_OCSP_TRY_SERVER_LATER) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_UNAUTHORIZED_REQUEST, 32, \ + SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_UNKNOWN_RESPONSE_STATUS, 33, \ + SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_UNKNOWN_CERT, 34, SEC_ERROR_OCSP_UNKNOWN_CERT) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_FUTURE_RESPONSE, 35, \ + SEC_ERROR_OCSP_FUTURE_RESPONSE) \ + MOZILLA_PKIX_MAP(ERROR_INVALID_KEY, 36, SEC_ERROR_INVALID_KEY) \ + MOZILLA_PKIX_MAP(ERROR_UNSUPPORTED_KEYALG, 37, SEC_ERROR_UNSUPPORTED_KEYALG) \ + MOZILLA_PKIX_MAP(ERROR_EXPIRED_ISSUER_CERTIFICATE, 38, \ + SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE) \ + MOZILLA_PKIX_MAP(ERROR_CA_CERT_USED_AS_END_ENTITY, 39, \ + MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY) \ + MOZILLA_PKIX_MAP(ERROR_INADEQUATE_KEY_SIZE, 40, \ + MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE) \ + MOZILLA_PKIX_MAP(ERROR_V1_CERT_USED_AS_CA, 41, \ + MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA) \ + MOZILLA_PKIX_MAP(ERROR_BAD_CERT_DOMAIN, 42, SSL_ERROR_BAD_CERT_DOMAIN) \ + MOZILLA_PKIX_MAP(ERROR_NO_RFC822NAME_MATCH, 43, \ + MOZILLA_PKIX_ERROR_NO_RFC822NAME_MATCH) \ + MOZILLA_PKIX_MAP(ERROR_UNSUPPORTED_ELLIPTIC_CURVE, 44, \ + SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE) \ + MOZILLA_PKIX_MAP(ERROR_NOT_YET_VALID_CERTIFICATE, 45, \ + MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE) \ + MOZILLA_PKIX_MAP(ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE, 46, \ + MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE) \ + MOZILLA_PKIX_MAP(ERROR_UNSUPPORTED_EC_POINT_FORM, 47, \ + SEC_ERROR_UNSUPPORTED_EC_POINT_FORM) \ + MOZILLA_PKIX_MAP(ERROR_SIGNATURE_ALGORITHM_MISMATCH, 48, \ + MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH) \ + MOZILLA_PKIX_MAP(ERROR_OCSP_RESPONSE_FOR_CERT_MISSING, 49, \ + MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING) \ + MOZILLA_PKIX_MAP(ERROR_VALIDITY_TOO_LONG, 50, \ + MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG) \ + MOZILLA_PKIX_MAP(ERROR_REQUIRED_TLS_FEATURE_MISSING, 51, \ + MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING) \ + MOZILLA_PKIX_MAP(ERROR_INVALID_INTEGER_ENCODING, 52, \ + MOZILLA_PKIX_ERROR_INVALID_INTEGER_ENCODING) \ + MOZILLA_PKIX_MAP(ERROR_EMPTY_ISSUER_NAME, 53, \ + MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME) \ + MOZILLA_PKIX_MAP(ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED, 54, \ + MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED) \ + MOZILLA_PKIX_MAP(ERROR_SELF_SIGNED_CERT, 55, \ + MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT) \ + MOZILLA_PKIX_MAP(ERROR_MITM_DETECTED, 56, MOZILLA_PKIX_ERROR_MITM_DETECTED) \ + MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_ARGS, FATAL_ERROR_FLAG | 1, \ + SEC_ERROR_INVALID_ARGS) \ + MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_STATE, FATAL_ERROR_FLAG | 2, \ + PR_INVALID_STATE_ERROR) \ + MOZILLA_PKIX_MAP(FATAL_ERROR_LIBRARY_FAILURE, FATAL_ERROR_FLAG | 3, \ + SEC_ERROR_LIBRARY_FAILURE) \ + MOZILLA_PKIX_MAP(FATAL_ERROR_NO_MEMORY, FATAL_ERROR_FLAG | 4, \ + SEC_ERROR_NO_MEMORY) \ +/* nothing here */ + +enum class Result { +#define MOZILLA_PKIX_MAP(name, value, nss_name) name = value, + MOZILLA_PKIX_MAP_LIST +#undef MOZILLA_PKIX_MAP +}; + +// Returns the stringified name of the given result, e.g. "Result::Success", +// or nullptr if result is unknown (invalid). +const char* MapResultToName(Result result); + +// We write many comparisons as (x != Success), and this shortened name makes +// those comparisons clearer, especially because the shortened name often +// results in less line wrapping. +static const Result Success = Result::Success; + +inline bool IsFatalError(Result rv) { + return (static_cast<unsigned int>(rv) & FATAL_ERROR_FLAG) != 0; +} + +inline Result NotReached(const char* /*explanation*/, Result result) { + assert(false); + return result; +} +} +} // namespace mozilla::pkix + +#endif // mozilla_pkix_Result_h diff --git a/security/nss/lib/mozpkix/include/pkix/Time.h b/security/nss/lib/mozpkix/include/pkix/Time.h new file mode 100644 index 000000000..8aea5479b --- /dev/null +++ b/security/nss/lib/mozpkix/include/pkix/Time.h @@ -0,0 +1,137 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2014 Mozilla Contributors + * + * 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. + */ + +#ifndef mozilla_pkix_Time_h +#define mozilla_pkix_Time_h + +#include <stdint.h> +#include <ctime> +#include <limits> + +#include "mozpkix/Result.h" + +namespace mozilla { +namespace pkix { + +// Time with a range from the first second of year 0 (AD) through at least the +// last second of year 9999, which is the range of legal times in X.509 and +// OCSP. This type has second-level precision. The time zone is always UTC. +// +// Pass by value, not by reference. +class Time final { + public: + // Construct an uninitialized instance. + // + // This will fail to compile because there is no default constructor: + // Time x; + // + // This will succeed, leaving the time uninitialized: + // Time x(Time::uninitialized); + enum Uninitialized { uninitialized }; + explicit Time(Uninitialized) {} + + bool operator==(const Time& other) const { + return elapsedSecondsAD == other.elapsedSecondsAD; + } + bool operator>(const Time& other) const { + return elapsedSecondsAD > other.elapsedSecondsAD; + } + bool operator>=(const Time& other) const { + return elapsedSecondsAD >= other.elapsedSecondsAD; + } + bool operator<(const Time& other) const { + return elapsedSecondsAD < other.elapsedSecondsAD; + } + bool operator<=(const Time& other) const { + return elapsedSecondsAD <= other.elapsedSecondsAD; + } + + Result AddSeconds(uint64_t seconds) { + if (std::numeric_limits<uint64_t>::max() - elapsedSecondsAD < seconds) { + return Result::FATAL_ERROR_INVALID_ARGS; // integer overflow + } + elapsedSecondsAD += seconds; + return Success; + } + + Result SubtractSeconds(uint64_t seconds) { + if (seconds > elapsedSecondsAD) { + return Result::FATAL_ERROR_INVALID_ARGS; // integer overflow + } + elapsedSecondsAD -= seconds; + return Success; + } + + static const uint64_t ONE_DAY_IN_SECONDS = + UINT64_C(24) * UINT64_C(60) * UINT64_C(60); + + private: + // This constructor is hidden to prevent accidents like this: + // + // Time foo(time_t t) + // { + // // WRONG! 1970-01-01-00:00:00 == time_t(0), but not Time(0)! + // return Time(t); + // } + explicit Time(uint64_t aElapsedSecondsAD) + : elapsedSecondsAD(aElapsedSecondsAD) {} + friend Time TimeFromElapsedSecondsAD(uint64_t); + friend class Duration; + + uint64_t elapsedSecondsAD; +}; + +inline Time TimeFromElapsedSecondsAD(uint64_t aElapsedSecondsAD) { + return Time(aElapsedSecondsAD); +} + +Time Now(); + +// Note the epoch is the unix epoch (ie 00:00:00 UTC, 1 January 1970) +Time TimeFromEpochInSeconds(uint64_t secondsSinceEpoch); + +class Duration final { + public: + Duration(Time timeA, Time timeB) + : durationInSeconds( + timeA < timeB ? timeB.elapsedSecondsAD - timeA.elapsedSecondsAD + : timeA.elapsedSecondsAD - timeB.elapsedSecondsAD) {} + + explicit Duration(uint64_t aDurationInSeconds) + : durationInSeconds(aDurationInSeconds) {} + + bool operator>(const Duration& other) const { + return durationInSeconds > other.durationInSeconds; + } + bool operator<(const Duration& other) const { + return durationInSeconds < other.durationInSeconds; + } + + private: + uint64_t durationInSeconds; +}; +} +} // namespace mozilla::pkix + +#endif // mozilla_pkix_Time_h diff --git a/security/nss/lib/mozpkix/include/pkix/pkix.h b/security/nss/lib/mozpkix/include/pkix/pkix.h new file mode 100644 index 000000000..1cd6548e4 --- /dev/null +++ b/security/nss/lib/mozpkix/include/pkix/pkix.h @@ -0,0 +1,160 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * 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. + */ + +#ifndef mozilla_pkix_pkix_h +#define mozilla_pkix_pkix_h + +#include "mozpkix/pkixtypes.h" + +namespace mozilla { +namespace pkix { + +// ---------------------------------------------------------------------------- +// LIMITED SUPPORT FOR CERTIFICATE POLICIES +// +// If SEC_OID_X509_ANY_POLICY is passed as the value of the requiredPolicy +// parameter then all policy validation will be skipped. Otherwise, path +// building and validation will be done for the given policy. +// +// In RFC 5280 terms: +// +// * user-initial-policy-set = { requiredPolicy }. +// * initial-explicit-policy = true +// * initial-any-policy-inhibit = false +// +// We allow intermediate cerificates to use this extension but since +// we do not process the inhibit anyPolicy extesion we will fail if this +// extension is present. TODO(bug 989051) +// Because we force explicit policy and because we prohibit policy mapping, we +// do not bother processing the policy mapping, or policy constraint. +// +// ---------------------------------------------------------------------------- +// ERROR RANKING +// +// BuildCertChain prioritizes certain checks ahead of others so that when a +// certificate chain has multiple errors, the "most serious" error is +// returned. In practice, this ranking of seriousness is tied directly to how +// Firefox's certificate error override mechanism. +// +// The ranking is: +// +// 1. Active distrust (Result::ERROR_UNTRUSTED_CERT). +// 2. Problems with issuer-independent properties for CA certificates. +// 3. Unknown issuer (Result::ERROR_UNKNOWN_ISSUER). +// 4. Problems with issuer-independent properties for EE certificates. +// 5. Revocation. +// +// In particular, if BuildCertChain returns Result::ERROR_UNKNOWN_ISSUER then +// the caller can call CERT_CheckCertValidTimes to determine if the certificate +// is ALSO expired. +// +// It would be better if revocation were prioritized above expiration and +// unknown issuer. However, it is impossible to do revocation checking without +// knowing the issuer, since the issuer information is needed to validate the +// revocation information. Also, generally revocation checking only works +// during the validity period of the certificate. +// +// In general, when path building fails, BuildCertChain will return +// Result::ERROR_UNKNOWN_ISSUER. However, if all attempted paths resulted in +// the same error (which is trivially true when there is only one potential +// path), more specific errors will be returned. +// +// ---------------------------------------------------------------------------- +// Meanings of specific error codes can be found in Result.h + +// This function attempts to find a trustworthy path from the supplied +// certificate to a trust anchor. In the event that no trusted path is found, +// the method returns an error result; the error ranking is described above. +// +// Parameters: +// time: +// Timestamp for which the chain should be valid; this is useful to +// analyze whether a record was trustworthy when it was made. +// requiredKeyUsageIfPresent: +// What key usage bits must be set, if the extension is present at all, +// to be considered a valid chain. Multiple values should be OR'd +// together. If you don't want to specify anything, use +// KeyUsage::noParticularKeyUsageRequired. +// requiredEKUIfPresent: +// What extended key usage bits must be set, if the EKU extension +// exists, to be considered a valid chain. Multiple values should be +// OR'd together. If you don't want to specify anything, use +// KeyPurposeId::anyExtendedKeyUsage. +// requiredPolicy: +// This is the policy to apply; typically included in EV certificates. +// If there is no policy, pass in CertPolicyId::anyPolicy. +Result BuildCertChain(TrustDomain& trustDomain, Input cert, Time time, + EndEntityOrCA endEntityOrCA, + KeyUsage requiredKeyUsageIfPresent, + KeyPurposeId requiredEKUIfPresent, + const CertPolicyId& requiredPolicy, + /*optional*/ const Input* stapledOCSPResponse); + +// Verify that the given end-entity cert, which is assumed to have been already +// validated with BuildCertChain, is valid for the given hostname. The matching +// function attempts to implement RFC 6125 with a couple of differences: +// - IP addresses are out of scope of RFC 6125, but this method accepts them for +// backward compatibility (see SearchNames in pkixnames.cpp) +// - A wildcard in a DNS-ID may only appear as the entirety of the first label. +Result CheckCertHostname(Input cert, Input hostname, + NameMatchingPolicy& nameMatchingPolicy); + +// Construct an RFC-6960-encoded OCSP request, ready for submission to a +// responder, for the provided CertID. The request has no extensions. +static const size_t OCSP_REQUEST_MAX_LENGTH = 127; +Result CreateEncodedOCSPRequest(TrustDomain& trustDomain, const CertID& certID, + /*out*/ uint8_t (&out)[OCSP_REQUEST_MAX_LENGTH], + /*out*/ size_t& outLen); + +// The out parameter expired will be true if the response has expired. If the +// response also indicates a revoked or unknown certificate, that error +// will be returned. Otherwise, Result::ERROR_OCSP_OLD_RESPONSE will be +// returned for an expired response. +// +// The optional parameter thisUpdate will be the thisUpdate value of +// the encoded response if it is considered trustworthy. Only +// good, unknown, or revoked responses that verify correctly are considered +// trustworthy. If the response is not trustworthy, thisUpdate will be 0. +// Similarly, the optional parameter validThrough will be the time through +// which the encoded response is considered trustworthy (that is, as long as +// the given time at which to validate is less than or equal to validThrough, +// the response will be considered trustworthy). +Result VerifyEncodedOCSPResponse( + TrustDomain& trustDomain, const CertID& certID, Time time, + uint16_t maxLifetimeInDays, Input encodedResponse, + /* out */ bool& expired, + /* optional out */ Time* thisUpdate = nullptr, + /* optional out */ Time* validThrough = nullptr); + +// Check that the TLSFeature extensions in a given end-entity cert (which is +// assumed to have been already validated with BuildCertChain) are satisfied. +// The only feature which we cancurrently process a requirement for is +// status_request (OCSP stapling) so we reject any extension that specifies a +// requirement for another value. Empty extensions are also rejected. +Result CheckTLSFeaturesAreSatisfied(Input& cert, + const Input* stapledOCSPResponse); +} +} // namespace mozilla::pkix + +#endif // mozilla_pkix_pkix_h diff --git a/security/nss/lib/mozpkix/include/pkix/pkixcheck.h b/security/nss/lib/mozpkix/include/pkix/pkixcheck.h new file mode 100644 index 000000000..e04780e57 --- /dev/null +++ b/security/nss/lib/mozpkix/include/pkix/pkixcheck.h @@ -0,0 +1,65 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * 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. + */ + +#ifndef mozilla_pkix_pkixcheck_h +#define mozilla_pkix_pkixcheck_h + +#include "mozpkix/pkixtypes.h" + +namespace mozilla { +namespace pkix { + +class BackCert; + +Result CheckIssuerIndependentProperties(TrustDomain& trustDomain, + const BackCert& cert, Time time, + KeyUsage requiredKeyUsageIfPresent, + KeyPurposeId requiredEKUIfPresent, + const CertPolicyId& requiredPolicy, + unsigned int subCACount, + /*out*/ TrustLevel& trustLevel); + +Result CheckNameConstraints(Input encodedNameConstraints, + const BackCert& firstChild, + KeyPurposeId requiredEKUIfPresent); + +Result CheckIssuer(Input encodedIssuer); + +// ParseValidity and CheckValidity are usually used together. First you parse +// the dates from the DER Validity sequence, then you compare them to the time +// at which you are validating. They are separate so that the notBefore and +// notAfter times can be used for other things before they are checked against +// the time of validation. +Result ParseValidity(Input encodedValidity, + /*optional out*/ Time* notBeforeOut = nullptr, + /*optional out*/ Time* notAfterOut = nullptr); +Result CheckValidity(Time time, Time notBefore, Time notAfter); + +// Check that a subject has TLS Feature (rfc7633) requirements that match its +// potential issuer +Result CheckTLSFeatures(const BackCert& subject, BackCert& potentialIssuer); +} +} // namespace mozilla::pkix + +#endif // mozilla_pkix_pkixcheck_h diff --git a/security/nss/lib/mozpkix/include/pkix/pkixder.h b/security/nss/lib/mozpkix/include/pkix/pkixder.h new file mode 100644 index 000000000..3aae0ecf6 --- /dev/null +++ b/security/nss/lib/mozpkix/include/pkix/pkixder.h @@ -0,0 +1,520 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * 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. + */ + +#ifndef mozilla_pkix_pkixder_h +#define mozilla_pkix_pkixder_h + +// Expect* functions advance the input mark and return Success if the input +// matches the given criteria; they fail with the input mark in an undefined +// state if the input does not match the criteria. +// +// Match* functions advance the input mark and return true if the input matches +// the given criteria; they return false without changing the input mark if the +// input does not match the criteria. +// +// Skip* functions unconditionally advance the input mark and return Success if +// they are able to do so; otherwise they fail with the input mark in an +// undefined state. + +#include "mozpkix/Input.h" +#include "mozpkix/pkixtypes.h" + +namespace mozilla { +namespace pkix { +namespace der { + +enum Class : uint8_t { + UNIVERSAL = 0 << 6, + // APPLICATION = 1 << 6, // unused + CONTEXT_SPECIFIC = 2 << 6, + // PRIVATE = 3 << 6 // unused +}; + +enum Constructed { CONSTRUCTED = 1 << 5 }; + +enum Tag : uint8_t { + BOOLEAN = UNIVERSAL | 0x01, + INTEGER = UNIVERSAL | 0x02, + BIT_STRING = UNIVERSAL | 0x03, + OCTET_STRING = UNIVERSAL | 0x04, + NULLTag = UNIVERSAL | 0x05, + OIDTag = UNIVERSAL | 0x06, + ENUMERATED = UNIVERSAL | 0x0a, + UTF8String = UNIVERSAL | 0x0c, + SEQUENCE = UNIVERSAL | CONSTRUCTED | 0x10, // 0x30 + SET = UNIVERSAL | CONSTRUCTED | 0x11, // 0x31 + PrintableString = UNIVERSAL | 0x13, + TeletexString = UNIVERSAL | 0x14, + IA5String = UNIVERSAL | 0x16, + UTCTime = UNIVERSAL | 0x17, + GENERALIZED_TIME = UNIVERSAL | 0x18, +}; + +enum class EmptyAllowed { No = 0, Yes = 1 }; + +Result ReadTagAndGetValue(Reader& input, /*out*/ uint8_t& tag, + /*out*/ Input& value); +Result End(Reader& input); + +inline Result ExpectTagAndGetValue(Reader& input, uint8_t tag, + /*out*/ Input& value) { + uint8_t actualTag; + Result rv = ReadTagAndGetValue(input, actualTag, value); + if (rv != Success) { + return rv; + } + if (tag != actualTag) { + return Result::ERROR_BAD_DER; + } + return Success; +} + +inline Result ExpectTagAndGetValue(Reader& input, uint8_t tag, + /*out*/ Reader& value) { + Input valueInput; + Result rv = ExpectTagAndGetValue(input, tag, valueInput); + if (rv != Success) { + return rv; + } + return value.Init(valueInput); +} + +inline Result ExpectTagAndEmptyValue(Reader& input, uint8_t tag) { + Reader value; + Result rv = ExpectTagAndGetValue(input, tag, value); + if (rv != Success) { + return rv; + } + return End(value); +} + +inline Result ExpectTagAndSkipValue(Reader& input, uint8_t tag) { + Input ignoredValue; + return ExpectTagAndGetValue(input, tag, ignoredValue); +} + +// Like ExpectTagAndGetValue, except the output Input will contain the +// encoded tag and length along with the value. +inline Result ExpectTagAndGetTLV(Reader& input, uint8_t tag, + /*out*/ Input& tlv) { + Reader::Mark mark(input.GetMark()); + Result rv = ExpectTagAndSkipValue(input, tag); + if (rv != Success) { + return rv; + } + return input.GetInput(mark, tlv); +} + +inline Result End(Reader& input) { + if (!input.AtEnd()) { + return Result::ERROR_BAD_DER; + } + + return Success; +} + +template <typename Decoder> +inline Result Nested(Reader& input, uint8_t tag, Decoder decoder) { + Reader nested; + Result rv = ExpectTagAndGetValue(input, tag, nested); + if (rv != Success) { + return rv; + } + rv = decoder(nested); + if (rv != Success) { + return rv; + } + return End(nested); +} + +template <typename Decoder> +inline Result Nested(Reader& input, uint8_t outerTag, uint8_t innerTag, + Decoder decoder) { + Reader nestedInput; + Result rv = ExpectTagAndGetValue(input, outerTag, nestedInput); + if (rv != Success) { + return rv; + } + rv = Nested(nestedInput, innerTag, decoder); + if (rv != Success) { + return rv; + } + return End(nestedInput); +} + +// This can be used to decode constructs like this: +// +// ... +// foos SEQUENCE OF Foo, +// ... +// Foo ::= SEQUENCE { +// } +// +// using code like this: +// +// Result Foo(Reader& r) { /*...*/ } +// +// rv = der::NestedOf(input, der::SEQEUENCE, der::SEQUENCE, Foo); +// +// or: +// +// Result Bar(Reader& r, int value) { /*...*/ } +// +// int value = /*...*/; +// +// rv = der::NestedOf(input, der::SEQUENCE, [value](Reader& r) { +// return Bar(r, value); +// }); +// +// In these examples the function will get called once for each element of +// foos. +// +template <typename Decoder> +inline Result NestedOf(Reader& input, uint8_t outerTag, uint8_t innerTag, + EmptyAllowed mayBeEmpty, Decoder decoder) { + Reader inner; + Result rv = ExpectTagAndGetValue(input, outerTag, inner); + if (rv != Success) { + return rv; + } + + if (inner.AtEnd()) { + if (mayBeEmpty != EmptyAllowed::Yes) { + return Result::ERROR_BAD_DER; + } + return Success; + } + + do { + rv = Nested(inner, innerTag, decoder); + if (rv != Success) { + return rv; + } + } while (!inner.AtEnd()); + + return Success; +} + +// Often, a function will need to decode an Input or Reader that contains +// DER-encoded data wrapped in a SEQUENCE (or similar) with nothing after it. +// This function reduces the boilerplate necessary for stripping the outermost +// SEQUENCE (or similar) and ensuring that nothing follows it. +inline Result ExpectTagAndGetValueAtEnd(Reader& outer, uint8_t expectedTag, + /*out*/ Reader& inner) { + Result rv = der::ExpectTagAndGetValue(outer, expectedTag, inner); + if (rv != Success) { + return rv; + } + return der::End(outer); +} + +// Similar to the above, but takes an Input instead of a Reader&. +inline Result ExpectTagAndGetValueAtEnd(Input outer, uint8_t expectedTag, + /*out*/ Reader& inner) { + Reader outerReader(outer); + return ExpectTagAndGetValueAtEnd(outerReader, expectedTag, inner); +} + +// Universal types + +namespace internal { + +enum class IntegralValueRestriction { + NoRestriction, + MustBePositive, + MustBe0To127, +}; + +Result IntegralBytes( + Reader& input, uint8_t tag, IntegralValueRestriction valueRestriction, + /*out*/ Input& value, + /*optional out*/ Input::size_type* significantBytes = nullptr); + +// This parser will only parse values between 0..127. If this range is +// increased then callers will need to be changed. +Result IntegralValue(Reader& input, uint8_t tag, /*out*/ uint8_t& value); + +} // namespace internal + +Result BitStringWithNoUnusedBits(Reader& input, /*out*/ Input& value); + +inline Result Boolean(Reader& input, /*out*/ bool& value) { + Reader valueReader; + Result rv = ExpectTagAndGetValue(input, BOOLEAN, valueReader); + if (rv != Success) { + return rv; + } + + uint8_t intValue; + rv = valueReader.Read(intValue); + if (rv != Success) { + return rv; + } + rv = End(valueReader); + if (rv != Success) { + return rv; + } + switch (intValue) { + case 0: + value = false; + return Success; + case 0xFF: + value = true; + return Success; + default: + return Result::ERROR_BAD_DER; + } +} + +// This is for BOOLEAN DEFAULT FALSE. +// The standard stipulates that "The encoding of a set value or sequence value +// shall not include an encoding for any component value which is equal to its +// default value." However, it appears to be common that other libraries +// incorrectly include the value of a BOOLEAN even when it's equal to the +// default value, so we allow invalid explicit encodings here. +inline Result OptionalBoolean(Reader& input, /*out*/ bool& value) { + value = false; + if (input.Peek(BOOLEAN)) { + Result rv = Boolean(input, value); + if (rv != Success) { + return rv; + } + } + return Success; +} + +// This parser will only parse values between 0..127. If this range is +// increased then callers will need to be changed. +inline Result Enumerated(Reader& input, uint8_t& value) { + return internal::IntegralValue(input, ENUMERATED | 0, value); +} + +namespace internal { + +// internal::TimeChoice implements the shared functionality of GeneralizedTime +// and TimeChoice. tag must be either UTCTime or GENERALIZED_TIME. +// +// Only times from 1970-01-01-00:00:00 onward are accepted, in order to +// eliminate the chance for complications in converting times to traditional +// time formats that start at 1970. +Result TimeChoice(Reader& input, uint8_t tag, /*out*/ Time& time); + +} // namespace internal + +// Only times from 1970-01-01-00:00:00 onward are accepted, in order to +// eliminate the chance for complications in converting times to traditional +// time formats that start at 1970. +inline Result GeneralizedTime(Reader& input, /*out*/ Time& time) { + return internal::TimeChoice(input, GENERALIZED_TIME, time); +} + +// Only times from 1970-01-01-00:00:00 onward are accepted, in order to +// eliminate the chance for complications in converting times to traditional +// time formats that start at 1970. +inline Result TimeChoice(Reader& input, /*out*/ Time& time) { + uint8_t expectedTag = input.Peek(UTCTime) ? UTCTime : GENERALIZED_TIME; + return internal::TimeChoice(input, expectedTag, time); +} + +// Parse a DER integer value into value. Empty values, negative values, and +// zero are rejected. If significantBytes is not null, then it will be set to +// the number of significant bytes in the value (the length of the value, less +// the length of any leading padding), which is useful for key size checks. +inline Result PositiveInteger( + Reader& input, /*out*/ Input& value, + /*optional out*/ Input::size_type* significantBytes = nullptr) { + return internal::IntegralBytes( + input, INTEGER, internal::IntegralValueRestriction::MustBePositive, value, + significantBytes); +} + +// This parser will only parse values between 0..127. If this range is +// increased then callers will need to be changed. +inline Result Integer(Reader& input, /*out*/ uint8_t& value) { + return internal::IntegralValue(input, INTEGER, value); +} + +// This parser will only parse values between 0..127. If this range is +// increased then callers will need to be changed. The default value must be +// -1; defaultValue is only a parameter to make it clear in the calling code +// what the default value is. +inline Result OptionalInteger(Reader& input, long defaultValue, + /*out*/ long& value) { + // If we need to support a different default value in the future, we need to + // test that parsedValue != defaultValue. + if (defaultValue != -1) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + + if (!input.Peek(INTEGER)) { + value = defaultValue; + return Success; + } + + uint8_t parsedValue; + Result rv = Integer(input, parsedValue); + if (rv != Success) { + return rv; + } + value = parsedValue; + return Success; +} + +inline Result Null(Reader& input) { + return ExpectTagAndEmptyValue(input, NULLTag); +} + +template <uint8_t Len> +Result OID(Reader& input, const uint8_t (&expectedOid)[Len]) { + Reader value; + Result rv = ExpectTagAndGetValue(input, OIDTag, value); + if (rv != Success) { + return rv; + } + if (!value.MatchRest(expectedOid)) { + return Result::ERROR_BAD_DER; + } + return Success; +} + +// PKI-specific types + +inline Result CertificateSerialNumber(Reader& input, /*out*/ Input& value) { + // http://tools.ietf.org/html/rfc5280#section-4.1.2.2: + // + // * "The serial number MUST be a positive integer assigned by the CA to + // each certificate." + // * "Certificate users MUST be able to handle serialNumber values up to 20 + // octets. Conforming CAs MUST NOT use serialNumber values longer than 20 + // octets." + // * "Note: Non-conforming CAs may issue certificates with serial numbers + // that are negative or zero. Certificate users SHOULD be prepared to + // gracefully handle such certificates." + return internal::IntegralBytes( + input, INTEGER, internal::IntegralValueRestriction::NoRestriction, value); +} + +// x.509 and OCSP both use this same version numbering scheme, though OCSP +// only supports v1. +enum class Version { v1 = 0, v2 = 1, v3 = 2, v4 = 3, Uninitialized = 255 }; + +// X.509 Certificate and OCSP ResponseData both use +// "[0] EXPLICIT Version DEFAULT v1". Although an explicit encoding of v1 is +// illegal, we support it because some real-world OCSP responses explicitly +// encode it. +Result OptionalVersion(Reader& input, /*out*/ Version& version); + +template <typename ExtensionHandler> +inline Result OptionalExtensions(Reader& input, uint8_t tag, + ExtensionHandler extensionHandler) { + if (!input.Peek(tag)) { + return Success; + } + + return Nested(input, tag, [extensionHandler](Reader& tagged) { + // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + // + // TODO(bug 997994): According to the specification, there should never be + // an empty sequence of extensions but we've found OCSP responses that have + // that (see bug 991898). + return NestedOf( + tagged, SEQUENCE, SEQUENCE, EmptyAllowed::Yes, + [extensionHandler](Reader& extension) -> Result { + // Extension ::= SEQUENCE { + // extnID OBJECT IDENTIFIER, + // critical BOOLEAN DEFAULT FALSE, + // extnValue OCTET STRING + // } + Reader extnID; + Result rv = ExpectTagAndGetValue(extension, OIDTag, extnID); + if (rv != Success) { + return rv; + } + bool critical; + rv = OptionalBoolean(extension, critical); + if (rv != Success) { + return rv; + } + Input extnValue; + rv = ExpectTagAndGetValue(extension, OCTET_STRING, extnValue); + if (rv != Success) { + return rv; + } + bool understood = false; + rv = extensionHandler(extnID, extnValue, critical, understood); + if (rv != Success) { + return rv; + } + if (critical && !understood) { + return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION; + } + return Success; + }); + }); +} + +Result DigestAlgorithmIdentifier(Reader& input, + /*out*/ DigestAlgorithm& algorithm); + +enum class PublicKeyAlgorithm { RSA_PKCS1, ECDSA, Uninitialized }; + +Result SignatureAlgorithmIdentifierValue( + Reader& input, + /*out*/ PublicKeyAlgorithm& publicKeyAlgorithm, + /*out*/ DigestAlgorithm& digestAlgorithm); + +struct SignedDataWithSignature final { + public: + Input data; + Input algorithm; + Input signature; + + void operator=(const SignedDataWithSignature&) = delete; +}; + +// Parses a SEQUENCE into tbs and then parses an AlgorithmIdentifier followed +// by a BIT STRING into signedData. This handles the commonality between +// parsing the signed/signature fields of certificates and OCSP responses. In +// the case of an OCSP response, the caller needs to parse the certs +// separately. +// +// Note that signatureAlgorithm is NOT parsed or validated. +// +// Certificate ::= SEQUENCE { +// tbsCertificate TBSCertificate, +// signatureAlgorithm AlgorithmIdentifier, +// signatureValue BIT STRING } +// +// BasicOCSPResponse ::= SEQUENCE { +// tbsResponseData ResponseData, +// signatureAlgorithm AlgorithmIdentifier, +// signature BIT STRING, +// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } +Result SignedData(Reader& input, /*out*/ Reader& tbs, + /*out*/ SignedDataWithSignature& signedDataWithSignature); +} +} +} // namespace mozilla::pkix::der + +#endif // mozilla_pkix_pkixder_h diff --git a/security/nss/lib/mozpkix/include/pkix/pkixnss.h b/security/nss/lib/mozpkix/include/pkix/pkixnss.h new file mode 100644 index 000000000..b181ca541 --- /dev/null +++ b/security/nss/lib/mozpkix/include/pkix/pkixnss.h @@ -0,0 +1,106 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * 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. + */ + +#ifndef mozilla_pkix_pkixnss_h +#define mozilla_pkix_pkixnss_h + +#include <seccomon.h> +#include "mozpkix/pkixtypes.h" +#include "prerror.h" + +namespace mozilla { +namespace pkix { + +// Verifies the PKCS#1.5 signature on the given data using the given RSA public +// key. +Result VerifyRSAPKCS1SignedDigestNSS(const SignedDigest& sd, + Input subjectPublicKeyInfo, + void* pkcs11PinArg); + +// Verifies the ECDSA signature on the given data using the given ECC public +// key. +Result VerifyECDSASignedDigestNSS(const SignedDigest& sd, + Input subjectPublicKeyInfo, + void* pkcs11PinArg); + +// Computes the digest of the given data using the given digest algorithm. +// +// item contains the data to hash. +// digestBuf must point to a buffer to where the digest will be written. +// digestBufLen must be the size of the buffer, which must be exactly equal +// to the size of the digest output (20 for SHA-1, 32 for SHA-256, +// etc.) +// +// TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our +// other, extensive, memory safety efforts in mozilla::pkix, and we should find +// a way to provide a more-obviously-safe interface. +Result DigestBufNSS(Input item, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestBufLen); + +Result MapPRErrorCodeToResult(PRErrorCode errorCode); +PRErrorCode MapResultToPRErrorCode(Result result); + +// The error codes within each module must fit in 16 bits. We want these +// errors to fit in the same module as the NSS errors but not overlap with +// any of them. Converting an NSS SEC, NSS SSL, or PSM error to an NS error +// involves negating the value of the error and then synthesizing an error +// in the NS_ERROR_MODULE_SECURITY module. Hence, PSM errors will start at +// a negative value that both doesn't overlap with the current value +// ranges for NSS errors and that will fit in 16 bits when negated. +static const PRErrorCode ERROR_BASE = -0x4000; +static const PRErrorCode ERROR_LIMIT = ERROR_BASE + 1000; + +enum ErrorCode { + MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE = ERROR_BASE + 0, + MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY = ERROR_BASE + 1, + MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE = ERROR_BASE + 2, + MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA = ERROR_BASE + 3, + MOZILLA_PKIX_ERROR_NO_RFC822NAME_MATCH = ERROR_BASE + 4, + MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE = ERROR_BASE + 5, + MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE = ERROR_BASE + 6, + MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH = ERROR_BASE + 7, + MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING = ERROR_BASE + 8, + MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG = ERROR_BASE + 9, + MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING = ERROR_BASE + 10, + MOZILLA_PKIX_ERROR_INVALID_INTEGER_ENCODING = ERROR_BASE + 11, + MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME = ERROR_BASE + 12, + MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED = ERROR_BASE + 13, + MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT = ERROR_BASE + 14, + MOZILLA_PKIX_ERROR_MITM_DETECTED = ERROR_BASE + 15, + END_OF_LIST +}; + +void RegisterErrorTable(); + +inline SECItem UnsafeMapInputToSECItem(Input input) { + SECItem result = {siBuffer, const_cast<uint8_t*>(input.UnsafeGetData()), + input.GetLength()}; + static_assert(sizeof(decltype(input.GetLength())) <= sizeof(result.len), + "input.GetLength() must fit in a SECItem"); + return result; +} +} +} // namespace mozilla::pkix + +#endif // mozilla_pkix_pkixnss_h diff --git a/security/nss/lib/mozpkix/include/pkix/pkixtypes.h b/security/nss/lib/mozpkix/include/pkix/pkixtypes.h new file mode 100644 index 000000000..6b12edbb1 --- /dev/null +++ b/security/nss/lib/mozpkix/include/pkix/pkixtypes.h @@ -0,0 +1,400 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * 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. + */ + +#ifndef mozilla_pkix_pkixtypes_h +#define mozilla_pkix_pkixtypes_h + +#include <memory> + +#include "mozpkix/Input.h" +#include "mozpkix/Time.h" +#include "stdint.h" + +namespace mozilla { +namespace pkix { + +enum class DigestAlgorithm { + sha512 = 1, + sha384 = 2, + sha256 = 3, + sha1 = 4, +}; + +enum class NamedCurve { + // secp521r1 (OID 1.3.132.0.35, RFC 5480) + secp521r1 = 1, + + // secp384r1 (OID 1.3.132.0.34, RFC 5480) + secp384r1 = 2, + + // secp256r1 (OID 1.2.840.10045.3.1.7, RFC 5480) + secp256r1 = 3, +}; + +struct SignedDigest final { + Input digest; + DigestAlgorithm digestAlgorithm; + Input signature; + + void operator=(const SignedDigest&) = delete; +}; + +enum class EndEntityOrCA { MustBeEndEntity = 0, MustBeCA = 1 }; + +enum class KeyUsage : uint8_t { + digitalSignature = 0, + nonRepudiation = 1, + keyEncipherment = 2, + dataEncipherment = 3, + keyAgreement = 4, + keyCertSign = 5, + // cRLSign = 6, + // encipherOnly = 7, + // decipherOnly = 8, + noParticularKeyUsageRequired = 0xff, +}; + +enum class KeyPurposeId { + anyExtendedKeyUsage = 0, + id_kp_serverAuth = 1, // id-kp-serverAuth + id_kp_clientAuth = 2, // id-kp-clientAuth + id_kp_codeSigning = 3, // id-kp-codeSigning + id_kp_emailProtection = 4, // id-kp-emailProtection + id_kp_OCSPSigning = 9, // id-kp-OCSPSigning +}; + +struct CertPolicyId final { + uint16_t numBytes; + static const uint16_t MAX_BYTES = 24; + uint8_t bytes[MAX_BYTES]; + + bool IsAnyPolicy() const; + bool operator==(const CertPolicyId& other) const; + + static const CertPolicyId anyPolicy; +}; + +enum class TrustLevel { + TrustAnchor = 1, // certificate is a trusted root CA certificate or + // equivalent *for the given policy*. + ActivelyDistrusted = 2, // certificate is known to be bad + InheritsTrust = 3 // certificate must chain to a trust anchor +}; + +// Extensions extracted during the verification flow. +// See TrustDomain::NoteAuxiliaryExtension. +enum class AuxiliaryExtension { + // Certificate Transparency data, specifically Signed Certificate + // Timestamps (SCTs). See RFC 6962. + + // SCT list embedded in the end entity certificate. Called by BuildCertChain + // after the certificate containing the SCTs has passed the revocation checks. + EmbeddedSCTList = 1, + // SCT list from OCSP response. Called by VerifyEncodedOCSPResponse + // when its result is a success and the SCT list is present. + SCTListFromOCSPResponse = 2 +}; + +// CertID references the information needed to do revocation checking for the +// certificate issued by the given issuer with the given serial number. +// +// issuer must be the DER-encoded issuer field from the certificate for which +// revocation checking is being done, **NOT** the subject field of the issuer +// certificate. (Those two fields must be equal to each other, but they may not +// be encoded exactly the same, and the encoding matters for OCSP.) +// issuerSubjectPublicKeyInfo is the entire DER-encoded subjectPublicKeyInfo +// field from the issuer's certificate. serialNumber is the entire DER-encoded +// serial number from the subject certificate (the certificate for which we are +// checking the revocation status). +struct CertID final { + public: + CertID(Input aIssuer, Input aIssuerSubjectPublicKeyInfo, Input aSerialNumber) + : issuer(aIssuer), + issuerSubjectPublicKeyInfo(aIssuerSubjectPublicKeyInfo), + serialNumber(aSerialNumber) {} + const Input issuer; + const Input issuerSubjectPublicKeyInfo; + const Input serialNumber; + + void operator=(const CertID&) = delete; +}; +typedef std::unique_ptr<CertID> ScopedCertID; + +class DERArray { + public: + // Returns the number of DER-encoded items in the array. + virtual size_t GetLength() const = 0; + + // Returns a weak (non-owning) pointer the ith DER-encoded item in the array + // (0-indexed). The result is guaranteed to be non-null if i < GetLength(), + // and the result is guaranteed to be nullptr if i >= GetLength(). + virtual const Input* GetDER(size_t i) const = 0; + + protected: + DERArray() {} + virtual ~DERArray() {} +}; + +// Applications control the behavior of path building and verification by +// implementing the TrustDomain interface. The TrustDomain is used for all +// cryptography and for determining which certificates are trusted or +// distrusted. +class TrustDomain { + public: + virtual ~TrustDomain() {} + + // Determine the level of trust in the given certificate for the given role. + // This will be called for every certificate encountered during path + // building. + // + // When policy.IsAnyPolicy(), then no policy-related checking should be done. + // When !policy.IsAnyPolicy(), then GetCertTrust MUST NOT return with + // trustLevel == TrustAnchor unless the given cert is considered a trust + // anchor *for that policy*. In particular, if the user has marked an + // intermediate certificate as trusted, but that intermediate isn't in the + // list of EV roots, then GetCertTrust must result in + // trustLevel == InheritsTrust instead of trustLevel == TrustAnchor + // (assuming the candidate cert is not actively distrusted). + virtual Result GetCertTrust(EndEntityOrCA endEntityOrCA, + const CertPolicyId& policy, + Input candidateCertDER, + /*out*/ TrustLevel& trustLevel) = 0; + + class IssuerChecker { + public: + // potentialIssuerDER is the complete DER encoding of the certificate to + // be checked as a potential issuer. + // + // If additionalNameConstraints is not nullptr then it must point to an + // encoded NameConstraints extension value; in that case, those name + // constraints will be checked in addition to any any name constraints + // contained in potentialIssuerDER. + virtual Result Check(Input potentialIssuerDER, + /*optional*/ const Input* additionalNameConstraints, + /*out*/ bool& keepGoing) = 0; + + protected: + IssuerChecker(); + virtual ~IssuerChecker(); + + IssuerChecker(const IssuerChecker&) = delete; + void operator=(const IssuerChecker&) = delete; + }; + + // Search for a CA certificate with the given name. The implementation must + // call checker.Check with the DER encoding of the potential issuer + // certificate. The implementation must follow these rules: + // + // * The implementation must be reentrant and must limit the amount of stack + // space it uses; see the note on reentrancy and stack usage below. + // * When checker.Check does not return Success then immediately return its + // return value. + // * When checker.Check returns Success and sets keepGoing = false, then + // immediately return Success. + // * When checker.Check returns Success and sets keepGoing = true, then + // call checker.Check again with a different potential issuer certificate, + // if any more are available. + // * When no more potential issuer certificates are available, return + // Success. + // * Don't call checker.Check with the same potential issuer certificate more + // than once in a given call of FindIssuer. + // * The given time parameter may be used to filter out certificates that are + // not valid at the given time, or it may be ignored. + // + // Note on reentrancy and stack usage: checker.Check will attempt to + // recursively build a certificate path from the potential issuer it is given + // to a trusted root, as determined by this TrustDomain. That means that + // checker.Check may call any/all of the methods on this TrustDomain. In + // particular, there will be call stacks that look like this: + // + // BuildCertChain + // [...] + // TrustDomain::FindIssuer + // [...] + // IssuerChecker::Check + // [...] + // TrustDomain::FindIssuer + // [...] + // IssuerChecker::Check + // [...] + // + // checker.Check is responsible for limiting the recursion to a reasonable + // limit. + // + // checker.Check will verify that the subject's issuer field matches the + // potential issuer's subject field. It will also check that the potential + // issuer is valid at the given time. However, if the FindIssuer + // implementation has an efficient way of filtering potential issuers by name + // and/or validity period itself, then it is probably better for performance + // for it to do so. + virtual Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker, + Time time) = 0; + + // Called as soon as we think we have a valid chain but before revocation + // checks are done. This function can be used to compute additional checks, + // especially checks that require the entire certificate chain. This callback + // can also be used to save a copy of the built certificate chain for later + // use. + // + // This function may be called multiple times, regardless of whether it + // returns success or failure. It is guaranteed that BuildCertChain will not + // return Success unless the last call to IsChainValid returns Success. + // Further, + // it is guaranteed that when BuildCertChain returns Success the last chain + // passed to IsChainValid is the valid chain that should be used for further + // operations that require the whole chain. + // + // Keep in mind, in particular, that if the application saves a copy of the + // certificate chain the last invocation of IsChainValid during a validation, + // it is still possible for BuildCertChain to fail, in which case the + // application must not assume anything about the validity of the last + // certificate chain passed to IsChainValid; especially, it would be very + // wrong to assume that the certificate chain is valid. + // + // certChain.GetDER(0) is the trust anchor. + virtual Result IsChainValid(const DERArray& certChain, Time time, + const CertPolicyId& requiredPolicy) = 0; + + virtual Result CheckRevocation(EndEntityOrCA endEntityOrCA, + const CertID& certID, Time time, + Duration validityDuration, + /*optional*/ const Input* stapledOCSPresponse, + /*optional*/ const Input* aiaExtension) = 0; + + // Check that the given digest algorithm is acceptable for use in signatures. + // + // Return Success if the algorithm is acceptable, + // Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED if the algorithm is not + // acceptable, or another error code if another error occurred. + virtual Result CheckSignatureDigestAlgorithm(DigestAlgorithm digestAlg, + EndEntityOrCA endEntityOrCA, + Time notBefore) = 0; + + // Check that the RSA public key size is acceptable. + // + // Return Success if the key size is acceptable, + // Result::ERROR_INADEQUATE_KEY_SIZE if the key size is not acceptable, + // or another error code if another error occurred. + virtual Result CheckRSAPublicKeyModulusSizeInBits( + EndEntityOrCA endEntityOrCA, unsigned int modulusSizeInBits) = 0; + + // Verify the given RSA PKCS#1.5 signature on the given digest using the + // given RSA public key. + // + // CheckRSAPublicKeyModulusSizeInBits will be called before calling this + // function, so it is not necessary to repeat those checks here. However, + // VerifyRSAPKCS1SignedDigest *is* responsible for doing the mathematical + // verification of the public key validity as specified in NIST SP 800-56A. + virtual Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) = 0; + + // Check that the given named ECC curve is acceptable for ECDSA signatures. + // + // Return Success if the curve is acceptable, + // Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE if the curve is not acceptable, + // or another error code if another error occurred. + virtual Result CheckECDSACurveIsAcceptable(EndEntityOrCA endEntityOrCA, + NamedCurve curve) = 0; + + // Verify the given ECDSA signature on the given digest using the given ECC + // public key. + // + // CheckECDSACurveIsAcceptable will be called before calling this function, + // so it is not necessary to repeat that check here. However, + // VerifyECDSASignedDigest *is* responsible for doing the mathematical + // verification of the public key validity as specified in NIST SP 800-56A. + virtual Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) = 0; + + // Check that the validity duration is acceptable. + // + // Return Success if the validity duration is acceptable, + // Result::ERROR_VALIDITY_TOO_LONG if the validity duration is not acceptable, + // or another error code if another error occurred. + virtual Result CheckValidityIsAcceptable(Time notBefore, Time notAfter, + EndEntityOrCA endEntityOrCA, + KeyPurposeId keyPurpose) = 0; + + // For compatibility, a CA certificate with an extended key usage that + // contains the id-Netscape-stepUp OID but does not contain the + // id-kp-serverAuth OID may be considered valid for issuing server auth + // certificates. This function allows TrustDomain implementations to control + // this setting based on the start of the validity period of the certificate + // in question. + virtual Result NetscapeStepUpMatchesServerAuth(Time notBefore, + /*out*/ bool& matches) = 0; + + // Some certificate or OCSP response extensions do not directly participate + // in the verification flow, but might still be of interest to the clients + // (notably Certificate Transparency data, RFC 6962). Such extensions are + // extracted and passed to this function for further processing. + virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension, + Input extensionData) = 0; + + // Compute a digest of the data in item using the given digest algorithm. + // + // item contains the data to hash. + // digestBuf points to a buffer to where the digest will be written. + // digestBufLen will be the size of the digest output (20 for SHA-1, + // 32 for SHA-256, etc.). + // + // TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our + // other, extensive, memory safety efforts in mozilla::pkix, and we should + // find a way to provide a more-obviously-safe interface. + virtual Result DigestBuf(Input item, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestBufLen) = 0; + + protected: + TrustDomain() {} + + TrustDomain(const TrustDomain&) = delete; + void operator=(const TrustDomain&) = delete; +}; + +enum class FallBackToSearchWithinSubject { No = 0, Yes = 1 }; + +// Applications control the behavior of matching presented name information from +// a certificate against a reference hostname by implementing the +// NameMatchingPolicy interface. Used in concert with CheckCertHostname. +class NameMatchingPolicy { + public: + virtual ~NameMatchingPolicy() {} + + // Given that the certificate in question has a notBefore field with the given + // value, should name matching fall back to searching within the subject + // common name field? + virtual Result FallBackToCommonName( + Time notBefore, + /*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) = 0; + + protected: + NameMatchingPolicy() {} + + NameMatchingPolicy(const NameMatchingPolicy&) = delete; + void operator=(const NameMatchingPolicy&) = delete; +}; +} +} // namespace mozilla::pkix + +#endif // mozilla_pkix_pkixtypes_h diff --git a/security/nss/lib/mozpkix/include/pkix/pkixutil.h b/security/nss/lib/mozpkix/include/pkix/pkixutil.h new file mode 100644 index 000000000..ca5b5a2d7 --- /dev/null +++ b/security/nss/lib/mozpkix/include/pkix/pkixutil.h @@ -0,0 +1,265 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * 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. + */ + +#ifndef mozilla_pkix_pkixutil_h +#define mozilla_pkix_pkixutil_h + +#include "mozpkix/pkixder.h" + +namespace mozilla { +namespace pkix { + +// During path building and verification, we build a linked list of BackCerts +// from the current cert toward the end-entity certificate. The linked list +// is used to verify properties that aren't local to the current certificate +// and/or the direct link between the current certificate and its issuer, +// such as name constraints. +// +// Each BackCert contains pointers to all the given certificate's extensions +// so that we can parse the extension block once and then process the +// extensions in an order that may be different than they appear in the cert. +class BackCert final { + public: + // certDER and childCert must be valid for the lifetime of BackCert. + BackCert(Input aCertDER, EndEntityOrCA aEndEntityOrCA, + const BackCert* aChildCert) + : der(aCertDER), + endEntityOrCA(aEndEntityOrCA), + childCert(aChildCert), + version(der::Version::Uninitialized) {} + + Result Init(); + + const Input GetDER() const { return der; } + const der::SignedDataWithSignature& GetSignedData() const { + return signedData; + } + + der::Version GetVersion() const { return version; } + const Input GetSerialNumber() const { return serialNumber; } + const Input GetSignature() const { return signature; } + const Input GetIssuer() const { return issuer; } + // XXX: "validity" is a horrible name for the structure that holds + // notBefore & notAfter, but that is the name used in RFC 5280 and we use the + // RFC 5280 names for everything. + const Input GetValidity() const { return validity; } + const Input GetSubject() const { return subject; } + const Input GetSubjectPublicKeyInfo() const { return subjectPublicKeyInfo; } + const Input* GetAuthorityInfoAccess() const { + return MaybeInput(authorityInfoAccess); + } + const Input* GetBasicConstraints() const { + return MaybeInput(basicConstraints); + } + const Input* GetCertificatePolicies() const { + return MaybeInput(certificatePolicies); + } + const Input* GetExtKeyUsage() const { return MaybeInput(extKeyUsage); } + const Input* GetKeyUsage() const { return MaybeInput(keyUsage); } + const Input* GetInhibitAnyPolicy() const { + return MaybeInput(inhibitAnyPolicy); + } + const Input* GetNameConstraints() const { + return MaybeInput(nameConstraints); + } + const Input* GetSubjectAltName() const { return MaybeInput(subjectAltName); } + const Input* GetRequiredTLSFeatures() const { + return MaybeInput(requiredTLSFeatures); + } + const Input* GetSignedCertificateTimestamps() const { + return MaybeInput(signedCertificateTimestamps); + } + + private: + const Input der; + + public: + const EndEntityOrCA endEntityOrCA; + BackCert const* const childCert; + + private: + // When parsing certificates in BackCert::Init, we don't accept empty + // extensions. Consequently, we don't have to store a distinction between + // empty extensions and extensions that weren't included. However, when + // *processing* extensions, we distinguish between whether an extension was + // included or not based on whetehr the GetXXX function for the extension + // returns nullptr. + static inline const Input* MaybeInput(const Input& item) { + return item.GetLength() > 0 ? &item : nullptr; + } + + der::SignedDataWithSignature signedData; + + der::Version version; + Input serialNumber; + Input signature; + Input issuer; + // XXX: "validity" is a horrible name for the structure that holds + // notBefore & notAfter, but that is the name used in RFC 5280 and we use the + // RFC 5280 names for everything. + Input validity; + Input subject; + Input subjectPublicKeyInfo; + + Input authorityInfoAccess; + Input basicConstraints; + Input certificatePolicies; + Input extKeyUsage; + Input inhibitAnyPolicy; + Input keyUsage; + Input nameConstraints; + Input subjectAltName; + Input criticalNetscapeCertificateType; + Input requiredTLSFeatures; + Input signedCertificateTimestamps; // RFC 6962 (Certificate Transparency) + + Result RememberExtension(Reader& extnID, Input extnValue, bool critical, + /*out*/ bool& understood); + + BackCert(const BackCert&) = delete; + void operator=(const BackCert&) = delete; +}; + +class NonOwningDERArray final : public DERArray { + public: + NonOwningDERArray() : numItems(0) { + // we don't need to initialize the items array because we always check + // numItems before accessing i. + } + + size_t GetLength() const override { return numItems; } + + const Input* GetDER(size_t i) const override { + return i < numItems ? &items[i] : nullptr; + } + + Result Append(Input der) { + if (numItems >= MAX_LENGTH) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + Result rv = items[numItems].Init(der); // structure assignment + if (rv != Success) { + return rv; + } + ++numItems; + return Success; + } + + // Public so we can static_assert on this. Keep in sync with MAX_SUBCA_COUNT. + static const size_t MAX_LENGTH = 8; + + private: + Input items[MAX_LENGTH]; // avoids any heap allocations + size_t numItems; + + NonOwningDERArray(const NonOwningDERArray&) = delete; + void operator=(const NonOwningDERArray&) = delete; +}; + +// Extracts the SignedCertificateTimestampList structure which is encoded as an +// OCTET STRING within the X.509v3 / OCSP extensions (see RFC 6962 section 3.3). +Result ExtractSignedCertificateTimestampListFromExtension(Input extnValue, + Input& sctList); + +inline unsigned int DaysBeforeYear(unsigned int year) { + assert(year <= 9999); + return ((year - 1u) * 365u) + + ((year - 1u) / 4u) // leap years are every 4 years, + - ((year - 1u) / 100u) // except years divisible by 100, + + ((year - 1u) / 400u); // except years divisible by 400. +} + +static const size_t MAX_DIGEST_SIZE_IN_BYTES = 512 / 8; // sha-512 + +Result DigestSignedData(TrustDomain& trustDomain, + const der::SignedDataWithSignature& signedData, + /*out*/ uint8_t (&digestBuf)[MAX_DIGEST_SIZE_IN_BYTES], + /*out*/ der::PublicKeyAlgorithm& publicKeyAlg, + /*out*/ SignedDigest& signedDigest); + +Result VerifySignedDigest(TrustDomain& trustDomain, + der::PublicKeyAlgorithm publicKeyAlg, + const SignedDigest& signedDigest, + Input signerSubjectPublicKeyInfo); + +// Combines DigestSignedData and VerifySignedDigest +Result VerifySignedData(TrustDomain& trustDomain, + const der::SignedDataWithSignature& signedData, + Input signerSubjectPublicKeyInfo); + +// Extracts the key parameters from |subjectPublicKeyInfo|, invoking +// the relevant methods of |trustDomain|. +Result CheckSubjectPublicKeyInfo(Input subjectPublicKeyInfo, + TrustDomain& trustDomain, + EndEntityOrCA endEntityOrCA); + +// In a switch over an enum, sometimes some compilers are not satisfied that +// all control flow paths have been considered unless there is a default case. +// However, in our code, such a default case is almost always unreachable dead +// code. That can be particularly problematic when the compiler wants the code +// to choose a value, such as a return value, for the default case, but there's +// no appropriate "impossible case" value to choose. +// +// MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM accounts for this. Example: +// +// // In xy.cpp +// #include "xt.h" +// +// enum class XY { X, Y }; +// +// int func(XY xy) { +// switch (xy) { +// case XY::X: return 1; +// case XY::Y; return 2; +// MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM +// } +// } +#if defined(__clang__) +// Clang will warn if not all cases are covered (-Wswitch-enum) AND it will +// warn if a switch statement that covers every enum label has a default case +// (-W-covered-switch-default). Versions prior to 3.5 warned about unreachable +// code in such default cases (-Wunreachable-code) even when +// -W-covered-switch-default was disabled, but that changed in Clang 3.5. +#define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM // empty +#elif defined(__GNUC__) +// GCC will warn if not all cases are covered (-Wswitch-enum). It does not +// assume that the default case is unreachable. +#define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM \ + default: \ + assert(false); \ + __builtin_unreachable(); +#elif defined(_MSC_VER) +// MSVC will warn if not all cases are covered (C4061, level 4). It does not +// assume that the default case is unreachable. +#define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM \ + default: \ + assert(false); \ + __assume(0); +#else +#error Unsupported compiler for MOZILLA_PKIX_UNREACHABLE_DEFAULT. +#endif +} +} // namespace mozilla::pkix + +#endif // mozilla_pkix_pkixutil_h diff --git a/security/nss/lib/mozpkix/lib/pkixbuild.cpp b/security/nss/lib/mozpkix/lib/pkixbuild.cpp new file mode 100644 index 000000000..0ac2cb883 --- /dev/null +++ b/security/nss/lib/mozpkix/lib/pkixbuild.cpp @@ -0,0 +1,418 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mozpkix/pkix.h" + +#include "mozpkix/pkixcheck.h" +#include "mozpkix/pkixutil.h" + +namespace mozilla { namespace pkix { + +static Result BuildForward(TrustDomain& trustDomain, + const BackCert& subject, + Time time, + KeyUsage requiredKeyUsageIfPresent, + KeyPurposeId requiredEKUIfPresent, + const CertPolicyId& requiredPolicy, + /*optional*/ const Input* stapledOCSPResponse, + unsigned int subCACount, + unsigned int& buildForwardCallBudget); + +TrustDomain::IssuerChecker::IssuerChecker() { } +TrustDomain::IssuerChecker::~IssuerChecker() { } + +// The implementation of TrustDomain::IssuerTracker is in a subclass only to +// hide the implementation from external users. +class PathBuildingStep final : public TrustDomain::IssuerChecker +{ +public: + PathBuildingStep(TrustDomain& aTrustDomain, const BackCert& aSubject, + Time aTime, KeyPurposeId aRequiredEKUIfPresent, + const CertPolicyId& aRequiredPolicy, + /*optional*/ const Input* aStapledOCSPResponse, + unsigned int aSubCACount, Result aDeferredSubjectError, + unsigned int& aBuildForwardCallBudget) + : trustDomain(aTrustDomain) + , subject(aSubject) + , time(aTime) + , requiredEKUIfPresent(aRequiredEKUIfPresent) + , requiredPolicy(aRequiredPolicy) + , stapledOCSPResponse(aStapledOCSPResponse) + , subCACount(aSubCACount) + , deferredSubjectError(aDeferredSubjectError) + , subjectSignaturePublicKeyAlg(der::PublicKeyAlgorithm::Uninitialized) + , result(Result::FATAL_ERROR_LIBRARY_FAILURE) + , resultWasSet(false) + , buildForwardCallBudget(aBuildForwardCallBudget) + { + } + + Result Check(Input potentialIssuerDER, + /*optional*/ const Input* additionalNameConstraints, + /*out*/ bool& keepGoing) override; + + Result CheckResult() const; + +private: + TrustDomain& trustDomain; + const BackCert& subject; + const Time time; + const KeyPurposeId requiredEKUIfPresent; + const CertPolicyId& requiredPolicy; + /*optional*/ Input const* const stapledOCSPResponse; + const unsigned int subCACount; + const Result deferredSubjectError; + + // Initialized lazily. + uint8_t subjectSignatureDigestBuf[MAX_DIGEST_SIZE_IN_BYTES]; + der::PublicKeyAlgorithm subjectSignaturePublicKeyAlg; + SignedDigest subjectSignature; + + Result RecordResult(Result currentResult, /*out*/ bool& keepGoing); + Result result; + bool resultWasSet; + unsigned int& buildForwardCallBudget; + + PathBuildingStep(const PathBuildingStep&) = delete; + void operator=(const PathBuildingStep&) = delete; +}; + +Result +PathBuildingStep::RecordResult(Result newResult, /*out*/ bool& keepGoing) +{ + if (newResult == Result::ERROR_UNTRUSTED_CERT) { + newResult = Result::ERROR_UNTRUSTED_ISSUER; + } else if (newResult == Result::ERROR_EXPIRED_CERTIFICATE) { + newResult = Result::ERROR_EXPIRED_ISSUER_CERTIFICATE; + } else if (newResult == Result::ERROR_NOT_YET_VALID_CERTIFICATE) { + newResult = Result::ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE; + } + + if (resultWasSet) { + if (result == Success) { + return NotReached("RecordResult called after finding a chain", + Result::FATAL_ERROR_INVALID_STATE); + } + // If every potential issuer has the same problem (e.g. expired) and/or if + // there is only one bad potential issuer, then return a more specific + // error. Otherwise, punt on trying to decide which error should be + // returned by returning the generic Result::ERROR_UNKNOWN_ISSUER error. + if (newResult != Success && newResult != result) { + newResult = Result::ERROR_UNKNOWN_ISSUER; + } + } + + result = newResult; + resultWasSet = true; + keepGoing = result != Success; + return Success; +} + +Result +PathBuildingStep::CheckResult() const +{ + if (!resultWasSet) { + return Result::ERROR_UNKNOWN_ISSUER; + } + return result; +} + +// The code that executes in the inner loop of BuildForward +Result +PathBuildingStep::Check(Input potentialIssuerDER, + /*optional*/ const Input* additionalNameConstraints, + /*out*/ bool& keepGoing) +{ + BackCert potentialIssuer(potentialIssuerDER, EndEntityOrCA::MustBeCA, + &subject); + Result rv = potentialIssuer.Init(); + if (rv != Success) { + return RecordResult(rv, keepGoing); + } + + // Simple TrustDomain::FindIssuers implementations may pass in all possible + // CA certificates without any filtering. Because of this, we don't consider + // a mismatched name to be an error. Instead, we just pretend that any + // certificate without a matching name was never passed to us. In particular, + // we treat the case where the TrustDomain only asks us to check CA + // certificates with mismatched names as equivalent to the case where the + // TrustDomain never called Check() at all. + if (!InputsAreEqual(potentialIssuer.GetSubject(), subject.GetIssuer())) { + keepGoing = true; + return Success; + } + + // Loop prevention, done as recommended by RFC4158 Section 5.2 + // TODO: this doesn't account for subjectAltNames! + // TODO(perf): This probably can and should be optimized in some way. + for (const BackCert* prev = potentialIssuer.childCert; prev; + prev = prev->childCert) { + if (InputsAreEqual(potentialIssuer.GetSubjectPublicKeyInfo(), + prev->GetSubjectPublicKeyInfo()) && + InputsAreEqual(potentialIssuer.GetSubject(), prev->GetSubject())) { + // XXX: error code + return RecordResult(Result::ERROR_UNKNOWN_ISSUER, keepGoing); + } + } + + if (potentialIssuer.GetNameConstraints()) { + rv = CheckNameConstraints(*potentialIssuer.GetNameConstraints(), + subject, requiredEKUIfPresent); + if (rv != Success) { + return RecordResult(rv, keepGoing); + } + } + + if (additionalNameConstraints) { + rv = CheckNameConstraints(*additionalNameConstraints, subject, + requiredEKUIfPresent); + if (rv != Success) { + return RecordResult(rv, keepGoing); + } + } + + rv = CheckTLSFeatures(subject, potentialIssuer); + if (rv != Success) { + return RecordResult(rv, keepGoing); + } + + // If we've ran out of budget, stop searching. + if (buildForwardCallBudget == 0) { + Result savedRv = RecordResult(Result::ERROR_UNKNOWN_ISSUER, keepGoing); + keepGoing = false; + return savedRv; + } + buildForwardCallBudget--; + + // RFC 5280, Section 4.2.1.3: "If the keyUsage extension is present, then the + // subject public key MUST NOT be used to verify signatures on certificates + // or CRLs unless the corresponding keyCertSign or cRLSign bit is set." + rv = BuildForward(trustDomain, potentialIssuer, time, KeyUsage::keyCertSign, + requiredEKUIfPresent, requiredPolicy, nullptr, subCACount, + buildForwardCallBudget); + if (rv != Success) { + return RecordResult(rv, keepGoing); + } + + // Calculate the digest of the subject's signed data if we haven't already + // done so. We do this lazily to avoid doing it at all if we backtrack before + // getting to this point. We cache the result to avoid recalculating it if we + // backtrack after getting to this point. + if (subjectSignature.digest.GetLength() == 0) { + rv = DigestSignedData(trustDomain, subject.GetSignedData(), + subjectSignatureDigestBuf, + subjectSignaturePublicKeyAlg, subjectSignature); + if (rv != Success) { + return rv; + } + } + + rv = VerifySignedDigest(trustDomain, subjectSignaturePublicKeyAlg, + subjectSignature, + potentialIssuer.GetSubjectPublicKeyInfo()); + if (rv != Success) { + return RecordResult(rv, keepGoing); + } + + // We avoid doing revocation checking for expired certificates because OCSP + // responders are allowed to forget about expired certificates, and many OCSP + // responders return an error when asked for the status of an expired + // certificate. + if (deferredSubjectError != Result::ERROR_EXPIRED_CERTIFICATE) { + CertID certID(subject.GetIssuer(), potentialIssuer.GetSubjectPublicKeyInfo(), + subject.GetSerialNumber()); + Time notBefore(Time::uninitialized); + Time notAfter(Time::uninitialized); + // This should never fail. If we're here, we've already parsed the validity + // and checked that the given time is in the certificate's validity period. + rv = ParseValidity(subject.GetValidity(), ¬Before, ¬After); + if (rv != Success) { + return rv; + } + Duration validityDuration(notAfter, notBefore); + rv = trustDomain.CheckRevocation(subject.endEntityOrCA, certID, time, + validityDuration, stapledOCSPResponse, + subject.GetAuthorityInfoAccess()); + if (rv != Success) { + // Since this is actually a problem with the current subject certificate + // (rather than the issuer), it doesn't make sense to keep going; all + // paths through this certificate will fail. + Result savedRv = RecordResult(rv, keepGoing); + keepGoing = false; + return savedRv; + } + + if (subject.endEntityOrCA == EndEntityOrCA::MustBeEndEntity) { + const Input* sctExtension = subject.GetSignedCertificateTimestamps(); + if (sctExtension) { + Input sctList; + rv = ExtractSignedCertificateTimestampListFromExtension(*sctExtension, + sctList); + if (rv != Success) { + // Again, the problem is with this certificate, and all paths through + // it will fail. + Result savedRv = RecordResult(rv, keepGoing); + keepGoing = false; + return savedRv; + } + trustDomain.NoteAuxiliaryExtension(AuxiliaryExtension::EmbeddedSCTList, + sctList); + } + } + } + + return RecordResult(Success, keepGoing); +} + +// Recursively build the path from the given subject certificate to the root. +// +// Be very careful about changing the order of checks. The order is significant +// because it affects which error we return when a certificate or certificate +// chain has multiple problems. See the error ranking documentation in +// pkix/pkix.h. +static Result +BuildForward(TrustDomain& trustDomain, + const BackCert& subject, + Time time, + KeyUsage requiredKeyUsageIfPresent, + KeyPurposeId requiredEKUIfPresent, + const CertPolicyId& requiredPolicy, + /*optional*/ const Input* stapledOCSPResponse, + unsigned int subCACount, + unsigned int& buildForwardCallBudget) +{ + Result rv; + + TrustLevel trustLevel; + // If this is an end-entity and not a trust anchor, we defer reporting + // any error found here until after attempting to find a valid chain. + // See the explanation of error prioritization in pkix.h. + rv = CheckIssuerIndependentProperties(trustDomain, subject, time, + requiredKeyUsageIfPresent, + requiredEKUIfPresent, requiredPolicy, + subCACount, trustLevel); + Result deferredEndEntityError = Success; + if (rv != Success) { + if (subject.endEntityOrCA == EndEntityOrCA::MustBeEndEntity && + trustLevel != TrustLevel::TrustAnchor) { + deferredEndEntityError = rv; + } else { + return rv; + } + } + + if (trustLevel == TrustLevel::TrustAnchor) { + // End of the recursion. + + NonOwningDERArray chain; + for (const BackCert* cert = &subject; cert; cert = cert->childCert) { + rv = chain.Append(cert->GetDER()); + if (rv != Success) { + return NotReached("NonOwningDERArray::SetItem failed.", rv); + } + } + + // This must be done here, after the chain is built but before any + // revocation checks have been done. + return trustDomain.IsChainValid(chain, time, requiredPolicy); + } + + if (subject.endEntityOrCA == EndEntityOrCA::MustBeCA) { + // Avoid stack overflows and poor performance by limiting cert chain + // length. + static const unsigned int MAX_SUBCA_COUNT = 6; + static_assert(1/*end-entity*/ + MAX_SUBCA_COUNT + 1/*root*/ == + NonOwningDERArray::MAX_LENGTH, + "MAX_SUBCA_COUNT and NonOwningDERArray::MAX_LENGTH mismatch."); + if (subCACount >= MAX_SUBCA_COUNT) { + return Result::ERROR_UNKNOWN_ISSUER; + } + ++subCACount; + } else { + assert(subCACount == 0); + } + + // Find a trusted issuer. + + PathBuildingStep pathBuilder(trustDomain, subject, time, + requiredEKUIfPresent, requiredPolicy, + stapledOCSPResponse, subCACount, + deferredEndEntityError, buildForwardCallBudget); + + // TODO(bug 965136): Add SKI/AKI matching optimizations + rv = trustDomain.FindIssuer(subject.GetIssuer(), pathBuilder, time); + if (rv != Success) { + return rv; + } + + rv = pathBuilder.CheckResult(); + if (rv != Success) { + return rv; + } + + // If we found a valid chain but deferred reporting an error with the + // end-entity certificate, report it now. + if (deferredEndEntityError != Success) { + return deferredEndEntityError; + } + + // We've built a valid chain from the subject cert up to a trusted root. + return Success; +} + +Result +BuildCertChain(TrustDomain& trustDomain, Input certDER, + Time time, EndEntityOrCA endEntityOrCA, + KeyUsage requiredKeyUsageIfPresent, + KeyPurposeId requiredEKUIfPresent, + const CertPolicyId& requiredPolicy, + /*optional*/ const Input* stapledOCSPResponse) +{ + // XXX: Support the legacy use of the subject CN field for indicating the + // domain name the certificate is valid for. + BackCert cert(certDER, endEntityOrCA, nullptr); + Result rv = cert.Init(); + if (rv != Success) { + return rv; + } + + // See bug 1056341 for context. If mozilla::pkix is being used in an + // environment where there are many certificates that all have the same + // distinguished name as their subject and issuer (but different SPKIs - see + // the loop prevention as per RFC4158 Section 5.2 in PathBuildingStep::Check), + // the space to search becomes exponential. Because it would be prohibitively + // expensive to explore the entire space, we introduce a budget here that, + // when exhausted, terminates the search with the result + // Result::ERROR_UNKNOWN_ISSUER. Essentially, we limit the total number of + // times `BuildForward` can be called. The current value appears to be a good + // balance between finding a path when one exists (when the space isn't too + // large) and timing out quickly enough when the space is too large or there + // is no valid path to a trust anchor. + unsigned int buildForwardCallBudget = 200000; + return BuildForward(trustDomain, cert, time, requiredKeyUsageIfPresent, + requiredEKUIfPresent, requiredPolicy, stapledOCSPResponse, + 0/*subCACount*/, buildForwardCallBudget); +} + +} } // namespace mozilla::pkix diff --git a/security/nss/lib/mozpkix/lib/pkixcert.cpp b/security/nss/lib/mozpkix/lib/pkixcert.cpp new file mode 100644 index 000000000..a30483738 --- /dev/null +++ b/security/nss/lib/mozpkix/lib/pkixcert.cpp @@ -0,0 +1,323 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2014 Mozilla Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mozpkix/pkixutil.h" + +namespace mozilla { namespace pkix { + +Result +BackCert::Init() +{ + Result rv; + + // Certificate ::= SEQUENCE { + // tbsCertificate TBSCertificate, + // signatureAlgorithm AlgorithmIdentifier, + // signatureValue BIT STRING } + + Reader tbsCertificate; + + // The scope of |input| and |certificate| are limited to this block so we + // don't accidentally confuse them for tbsCertificate later. + { + Reader certificate; + rv = der::ExpectTagAndGetValueAtEnd(der, der::SEQUENCE, certificate); + if (rv != Success) { + return rv; + } + rv = der::SignedData(certificate, tbsCertificate, signedData); + if (rv != Success) { + return rv; + } + rv = der::End(certificate); + if (rv != Success) { + return rv; + } + } + + // TBSCertificate ::= SEQUENCE { + // version [0] EXPLICIT Version DEFAULT v1, + // serialNumber CertificateSerialNumber, + // signature AlgorithmIdentifier, + // issuer Name, + // validity Validity, + // subject Name, + // subjectPublicKeyInfo SubjectPublicKeyInfo, + // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + // -- If present, version MUST be v2 or v3 + // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + // -- If present, version MUST be v2 or v3 + // extensions [3] EXPLICIT Extensions OPTIONAL + // -- If present, version MUST be v3 + // } + rv = der::OptionalVersion(tbsCertificate, version); + if (rv != Success) { + return rv; + } + rv = der::CertificateSerialNumber(tbsCertificate, serialNumber); + if (rv != Success) { + return rv; + } + rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, signature); + if (rv != Success) { + return rv; + } + rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, issuer); + if (rv != Success) { + return rv; + } + rv = der::ExpectTagAndGetValue(tbsCertificate, der::SEQUENCE, validity); + if (rv != Success) { + return rv; + } + // TODO(bug XXXXXXX): We rely on the the caller of mozilla::pkix to validate + // that the name is syntactically valid, if they care. In Gecko we do this + // implicitly by parsing the certificate into a CERTCertificate object. + // Instead of relying on the caller to do this, we should do it ourselves. + rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, subject); + if (rv != Success) { + return rv; + } + rv = der::ExpectTagAndGetTLV(tbsCertificate, der::SEQUENCE, + subjectPublicKeyInfo); + if (rv != Success) { + return rv; + } + + static const uint8_t CSC = der::CONTEXT_SPECIFIC | der::CONSTRUCTED; + + // According to RFC 5280, all fields below this line are forbidden for + // certificate versions less than v3. However, for compatibility reasons, + // we parse v1/v2 certificates in the same way as v3 certificates. So if + // these fields appear in a v1 certificate, they will be used. + + // Ignore issuerUniqueID if present. + if (tbsCertificate.Peek(CSC | 1)) { + rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 1); + if (rv != Success) { + return rv; + } + } + + // Ignore subjectUniqueID if present. + if (tbsCertificate.Peek(CSC | 2)) { + rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 2); + if (rv != Success) { + return rv; + } + } + + rv = der::OptionalExtensions( + tbsCertificate, CSC | 3, + [this](Reader& extnID, const Input& extnValue, bool critical, + /*out*/ bool& understood) { + return RememberExtension(extnID, extnValue, critical, understood); + }); + if (rv != Success) { + return rv; + } + + // The Netscape Certificate Type extension is an obsolete + // Netscape-proprietary mechanism that we ignore in favor of the standard + // extensions. However, some CAs have issued certificates with the Netscape + // Cert Type extension marked critical. Thus, for compatibility reasons, we + // "understand" this extension by ignoring it when it is not critical, and + // by ensuring that the equivalent standardized extensions are present when + // it is marked critical, based on the assumption that the information in + // the Netscape Cert Type extension is consistent with the information in + // the standard extensions. + // + // Here is a mapping between the Netscape Cert Type extension and the + // standard extensions: + // + // Netscape Cert Type | BasicConstraints.cA | Extended Key Usage + // --------------------+-----------------------+---------------------- + // SSL Server | false | id_kp_serverAuth + // SSL Client | false | id_kp_clientAuth + // S/MIME Client | false | id_kp_emailProtection + // Object Signing | false | id_kp_codeSigning + // SSL Server CA | true | id_kp_serverAuth + // SSL Client CA | true | id_kp_clientAuth + // S/MIME CA | true | id_kp_emailProtection + // Object Signing CA | true | id_kp_codeSigning + if (criticalNetscapeCertificateType.GetLength() > 0 && + (basicConstraints.GetLength() == 0 || extKeyUsage.GetLength() == 0)) { + return Result::ERROR_UNKNOWN_CRITICAL_EXTENSION; + } + + return der::End(tbsCertificate); +} + +Result +BackCert::RememberExtension(Reader& extnID, Input extnValue, + bool critical, /*out*/ bool& understood) +{ + understood = false; + + // python DottedOIDToCode.py id-ce-keyUsage 2.5.29.15 + static const uint8_t id_ce_keyUsage[] = { + 0x55, 0x1d, 0x0f + }; + // python DottedOIDToCode.py id-ce-subjectAltName 2.5.29.17 + static const uint8_t id_ce_subjectAltName[] = { + 0x55, 0x1d, 0x11 + }; + // python DottedOIDToCode.py id-ce-basicConstraints 2.5.29.19 + static const uint8_t id_ce_basicConstraints[] = { + 0x55, 0x1d, 0x13 + }; + // python DottedOIDToCode.py id-ce-nameConstraints 2.5.29.30 + static const uint8_t id_ce_nameConstraints[] = { + 0x55, 0x1d, 0x1e + }; + // python DottedOIDToCode.py id-ce-certificatePolicies 2.5.29.32 + static const uint8_t id_ce_certificatePolicies[] = { + 0x55, 0x1d, 0x20 + }; + // python DottedOIDToCode.py id-ce-policyConstraints 2.5.29.36 + static const uint8_t id_ce_policyConstraints[] = { + 0x55, 0x1d, 0x24 + }; + // python DottedOIDToCode.py id-ce-extKeyUsage 2.5.29.37 + static const uint8_t id_ce_extKeyUsage[] = { + 0x55, 0x1d, 0x25 + }; + // python DottedOIDToCode.py id-ce-inhibitAnyPolicy 2.5.29.54 + static const uint8_t id_ce_inhibitAnyPolicy[] = { + 0x55, 0x1d, 0x36 + }; + // python DottedOIDToCode.py id-pe-authorityInfoAccess 1.3.6.1.5.5.7.1.1 + static const uint8_t id_pe_authorityInfoAccess[] = { + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01 + }; + // python DottedOIDToCode.py id-pkix-ocsp-nocheck 1.3.6.1.5.5.7.48.1.5 + static const uint8_t id_pkix_ocsp_nocheck[] = { + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x05 + }; + // python DottedOIDToCode.py Netscape-certificate-type 2.16.840.1.113730.1.1 + static const uint8_t Netscape_certificate_type[] = { + 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01 + }; + // python DottedOIDToCode.py id-pe-tlsfeature 1.3.6.1.5.5.7.1.24 + static const uint8_t id_pe_tlsfeature[] = { + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x18 + }; + // python DottedOIDToCode.py id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2 + // See Section 3.3 of RFC 6962. + static const uint8_t id_embeddedSctList[] = { + 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02 + }; + + Input* out = nullptr; + + // We already enforce the maximum possible constraints for policies so we + // can safely ignore even critical policy constraint extensions. + // + // XXX: Doing it this way won't allow us to detect duplicate + // policyConstraints extensions, but that's OK because (and only because) we + // ignore the extension. + Input dummyPolicyConstraints; + + // We don't need to save the contents of this extension if it is present. We + // just need to handle its presence (it is essentially ignored right now). + Input dummyOCSPNocheck; + + // For compatibility reasons, for some extensions we have to allow empty + // extension values. This would normally interfere with our duplicate + // extension checking code. However, as long as the extensions we allow to + // have empty values are also the ones we implicitly allow duplicates of, + // this will work fine. + bool emptyValueAllowed = false; + + // RFC says "Conforming CAs MUST mark this extension as non-critical" for + // both authorityKeyIdentifier and subjectKeyIdentifier, and we do not use + // them for anything, so we totally ignore them here. + + if (extnID.MatchRest(id_ce_keyUsage)) { + out = &keyUsage; + } else if (extnID.MatchRest(id_ce_subjectAltName)) { + out = &subjectAltName; + } else if (extnID.MatchRest(id_ce_basicConstraints)) { + out = &basicConstraints; + } else if (extnID.MatchRest(id_ce_nameConstraints)) { + out = &nameConstraints; + } else if (extnID.MatchRest(id_ce_certificatePolicies)) { + out = &certificatePolicies; + } else if (extnID.MatchRest(id_ce_policyConstraints)) { + out = &dummyPolicyConstraints; + } else if (extnID.MatchRest(id_ce_extKeyUsage)) { + out = &extKeyUsage; + } else if (extnID.MatchRest(id_ce_inhibitAnyPolicy)) { + out = &inhibitAnyPolicy; + } else if (extnID.MatchRest(id_pe_authorityInfoAccess)) { + out = &authorityInfoAccess; + } else if (extnID.MatchRest(id_pe_tlsfeature)) { + out = &requiredTLSFeatures; + } else if (extnID.MatchRest(id_embeddedSctList)) { + out = &signedCertificateTimestamps; + } else if (extnID.MatchRest(id_pkix_ocsp_nocheck) && critical) { + // We need to make sure we don't reject delegated OCSP response signing + // certificates that contain the id-pkix-ocsp-nocheck extension marked as + // critical when validating OCSP responses. Without this, an application + // that implements soft-fail OCSP might ignore a valid Revoked or Unknown + // response, and an application that implements hard-fail OCSP might fail + // to connect to a server given a valid Good response. + out = &dummyOCSPNocheck; + // We allow this extension to have an empty value. + // See http://comments.gmane.org/gmane.ietf.x509/30947 + emptyValueAllowed = true; + } else if (extnID.MatchRest(Netscape_certificate_type) && critical) { + out = &criticalNetscapeCertificateType; + } + + if (out) { + // Don't allow an empty value for any extension we understand. This way, we + // can test out->GetLength() != 0 or out->Init() to check for duplicates. + if (extnValue.GetLength() == 0 && !emptyValueAllowed) { + return Result::ERROR_EXTENSION_VALUE_INVALID; + } + if (out->Init(extnValue) != Success) { + // Duplicate extension + return Result::ERROR_EXTENSION_VALUE_INVALID; + } + understood = true; + } + + return Success; +} + +Result +ExtractSignedCertificateTimestampListFromExtension(Input extnValue, + Input& sctList) +{ + Reader decodedValue; + Result rv = der::ExpectTagAndGetValueAtEnd(extnValue, der::OCTET_STRING, + decodedValue); + if (rv != Success) { + return rv; + } + return decodedValue.SkipToEnd(sctList); +} + +} } // namespace mozilla::pkix diff --git a/security/nss/lib/mozpkix/lib/pkixcheck.cpp b/security/nss/lib/mozpkix/lib/pkixcheck.cpp new file mode 100644 index 000000000..317db01e2 --- /dev/null +++ b/security/nss/lib/mozpkix/lib/pkixcheck.cpp @@ -0,0 +1,1100 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mozpkix/pkixcheck.h" + +#include "mozpkix/pkixder.h" +#include "mozpkix/pkixutil.h" + +namespace mozilla { namespace pkix { + +// 4.1.1.2 signatureAlgorithm +// 4.1.2.3 signature + +Result +CheckSignatureAlgorithm(TrustDomain& trustDomain, + EndEntityOrCA endEntityOrCA, + Time notBefore, + const der::SignedDataWithSignature& signedData, + Input signatureValue) +{ + // 4.1.1.2. signatureAlgorithm + der::PublicKeyAlgorithm publicKeyAlg; + DigestAlgorithm digestAlg; + Reader signatureAlgorithmReader(signedData.algorithm); + Result rv = der::SignatureAlgorithmIdentifierValue(signatureAlgorithmReader, + publicKeyAlg, digestAlg); + if (rv != Success) { + return rv; + } + rv = der::End(signatureAlgorithmReader); + if (rv != Success) { + return rv; + } + + // 4.1.2.3. Signature + der::PublicKeyAlgorithm signedPublicKeyAlg; + DigestAlgorithm signedDigestAlg; + Reader signedSignatureAlgorithmReader(signatureValue); + rv = der::SignatureAlgorithmIdentifierValue(signedSignatureAlgorithmReader, + signedPublicKeyAlg, + signedDigestAlg); + if (rv != Success) { + return rv; + } + rv = der::End(signedSignatureAlgorithmReader); + if (rv != Success) { + return rv; + } + + // "This field MUST contain the same algorithm identifier as the + // signatureAlgorithm field in the sequence Certificate." However, it may + // be encoded differently. In particular, one of the fields may have a NULL + // parameter while the other one may omit the parameter field altogether, and + // these are considered equivalent. Some certificates generation software + // actually generates certificates like that, so we compare the parsed values + // instead of comparing the encoded values byte-for-byte. + // + // Along the same lines, we accept two different OIDs for RSA-with-SHA1, and + // we consider those OIDs to be equivalent here. + if (publicKeyAlg != signedPublicKeyAlg || digestAlg != signedDigestAlg) { + return Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH; + } + + // During the time of the deprecation of SHA-1 and the deprecation of RSA + // keys of less than 2048 bits, we will encounter many certs signed using + // SHA-1 and/or too-small RSA keys. With this in mind, we ask the trust + // domain early on if it knows it will reject the signature purely based on + // the digest algorithm and/or the RSA key size (if an RSA signature). This + // is a good optimization because it completely avoids calling + // trustDomain.FindIssuers (which may be slow) for such rejected certs, and + // more generally it short-circuits any path building with them (which, of + // course, is even slower). + + rv = trustDomain.CheckSignatureDigestAlgorithm(digestAlg, endEntityOrCA, + notBefore); + if (rv != Success) { + return rv; + } + + switch (publicKeyAlg) { + case der::PublicKeyAlgorithm::RSA_PKCS1: + { + // The RSA computation may give a result that requires fewer bytes to + // encode than the public key (since it is modular arithmetic). However, + // the last step of generating a PKCS#1.5 signature is the I2OSP + // procedure, which pads any such shorter result with zeros so that it + // is exactly the same length as the public key. + unsigned int signatureSizeInBits = signedData.signature.GetLength() * 8u; + return trustDomain.CheckRSAPublicKeyModulusSizeInBits( + endEntityOrCA, signatureSizeInBits); + } + + case der::PublicKeyAlgorithm::ECDSA: + // In theory, we could implement a similar early-pruning optimization for + // ECDSA curves. However, since there has been no similar deprecation for + // for any curve that we support, the chances of us encountering a curve + // during path building is too low to be worth bothering with. + break; + case der::PublicKeyAlgorithm::Uninitialized: + { + assert(false); + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM + } + + return Success; +} + +// 4.1.2.4 Issuer + +Result +CheckIssuer(Input encodedIssuer) +{ + // "The issuer field MUST contain a non-empty distinguished name (DN)." + Reader issuer(encodedIssuer); + Input encodedRDNs; + ExpectTagAndGetValue(issuer, der::SEQUENCE, encodedRDNs); + Reader rdns(encodedRDNs); + // Check that the issuer name contains at least one RDN + // (Note: this does not check related grammar rules, such as there being one + // or more AVAs in each RDN, or the values in AVAs not being empty strings) + if (rdns.AtEnd()) { + return Result::ERROR_EMPTY_ISSUER_NAME; + } + return Success; +} + +// 4.1.2.5 Validity + +Result +ParseValidity(Input encodedValidity, + /*optional out*/ Time* notBeforeOut, + /*optional out*/ Time* notAfterOut) +{ + Reader validity(encodedValidity); + Time notBefore(Time::uninitialized); + if (der::TimeChoice(validity, notBefore) != Success) { + return Result::ERROR_INVALID_DER_TIME; + } + + Time notAfter(Time::uninitialized); + if (der::TimeChoice(validity, notAfter) != Success) { + return Result::ERROR_INVALID_DER_TIME; + } + + if (der::End(validity) != Success) { + return Result::ERROR_INVALID_DER_TIME; + } + + if (notBefore > notAfter) { + return Result::ERROR_INVALID_DER_TIME; + } + + if (notBeforeOut) { + *notBeforeOut = notBefore; + } + if (notAfterOut) { + *notAfterOut = notAfter; + } + + return Success; +} + +Result +CheckValidity(Time time, Time notBefore, Time notAfter) +{ + if (time < notBefore) { + return Result::ERROR_NOT_YET_VALID_CERTIFICATE; + } + + if (time > notAfter) { + return Result::ERROR_EXPIRED_CERTIFICATE; + } + + return Success; +} + +// 4.1.2.7 Subject Public Key Info + +Result +CheckSubjectPublicKeyInfoContents(Reader& input, TrustDomain& trustDomain, + EndEntityOrCA endEntityOrCA) +{ + // Here, we validate the syntax and do very basic semantic validation of the + // public key of the certificate. The intention here is to filter out the + // types of bad inputs that are most likely to trigger non-mathematical + // security vulnerabilities in the TrustDomain, like buffer overflows or the + // use of unsafe elliptic curves. + // + // We don't check (all of) the mathematical properties of the public key here + // because it is more efficient for the TrustDomain to do it during signature + // verification and/or other use of the public key. In particular, we + // delegate the arithmetic validation of the public key, as specified in + // NIST SP800-56A section 5.6.2, to the TrustDomain, at least for now. + + Reader algorithm; + Input subjectPublicKey; + Result rv = der::ExpectTagAndGetValue(input, der::SEQUENCE, algorithm); + if (rv != Success) { + return rv; + } + rv = der::BitStringWithNoUnusedBits(input, subjectPublicKey); + if (rv != Success) { + return rv; + } + rv = der::End(input); + if (rv != Success) { + return rv; + } + + Reader subjectPublicKeyReader(subjectPublicKey); + + Reader algorithmOID; + rv = der::ExpectTagAndGetValue(algorithm, der::OIDTag, algorithmOID); + if (rv != Success) { + return rv; + } + + // RFC 3279 Section 2.3.1 + // python DottedOIDToCode.py rsaEncryption 1.2.840.113549.1.1.1 + static const uint8_t rsaEncryption[] = { + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 + }; + + // RFC 3279 Section 2.3.5 and RFC 5480 Section 2.1.1 + // python DottedOIDToCode.py id-ecPublicKey 1.2.840.10045.2.1 + static const uint8_t id_ecPublicKey[] = { + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 + }; + + if (algorithmOID.MatchRest(id_ecPublicKey)) { + // An id-ecPublicKey AlgorithmIdentifier has a parameter that identifes + // the curve being used. Although RFC 5480 specifies multiple forms, we + // only supported the NamedCurve form, where the curve is identified by an + // OID. + + Reader namedCurveOIDValue; + rv = der::ExpectTagAndGetValue(algorithm, der::OIDTag, + namedCurveOIDValue); + if (rv != Success) { + return rv; + } + + // RFC 5480 + // python DottedOIDToCode.py secp256r1 1.2.840.10045.3.1.7 + static const uint8_t secp256r1[] = { + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 + }; + + // RFC 5480 + // python DottedOIDToCode.py secp384r1 1.3.132.0.34 + static const uint8_t secp384r1[] = { + 0x2b, 0x81, 0x04, 0x00, 0x22 + }; + + // RFC 5480 + // python DottedOIDToCode.py secp521r1 1.3.132.0.35 + static const uint8_t secp521r1[] = { + 0x2b, 0x81, 0x04, 0x00, 0x23 + }; + + // Matching is attempted based on a rough estimate of the commonality of the + // elliptic curve, to minimize the number of MatchRest calls. + NamedCurve curve; + unsigned int bits; + if (namedCurveOIDValue.MatchRest(secp256r1)) { + curve = NamedCurve::secp256r1; + bits = 256; + } else if (namedCurveOIDValue.MatchRest(secp384r1)) { + curve = NamedCurve::secp384r1; + bits = 384; + } else if (namedCurveOIDValue.MatchRest(secp521r1)) { + curve = NamedCurve::secp521r1; + bits = 521; + } else { + return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE; + } + + rv = trustDomain.CheckECDSACurveIsAcceptable(endEntityOrCA, curve); + if (rv != Success) { + return rv; + } + + // RFC 5480 Section 2.2 says that the first octet will be 0x04 to indicate + // an uncompressed point, which is the only encoding we support. + uint8_t compressedOrUncompressed; + rv = subjectPublicKeyReader.Read(compressedOrUncompressed); + if (rv != Success) { + return rv; + } + if (compressedOrUncompressed != 0x04) { + return Result::ERROR_UNSUPPORTED_EC_POINT_FORM; + } + + // The point is encoded as two raw (not DER-encoded) integers, each padded + // to the bit length (rounded up to the nearest byte). + Input point; + rv = subjectPublicKeyReader.SkipToEnd(point); + if (rv != Success) { + return rv; + } + if (point.GetLength() != ((bits + 7) / 8u) * 2u) { + return Result::ERROR_BAD_DER; + } + + // XXX: We defer the mathematical verification of the validity of the point + // until signature verification. This means that if we never verify a + // signature, we'll never fully check whether the public key is valid. + } else if (algorithmOID.MatchRest(rsaEncryption)) { + // RFC 3279 Section 2.3.1 says "The parameters field MUST have ASN.1 type + // NULL for this algorithm identifier." + rv = der::ExpectTagAndEmptyValue(algorithm, der::NULLTag); + if (rv != Success) { + return rv; + } + + // RSAPublicKey :: = SEQUENCE{ + // modulus INTEGER, --n + // publicExponent INTEGER } --e + rv = der::Nested(subjectPublicKeyReader, der::SEQUENCE, + [&trustDomain, endEntityOrCA](Reader& r) { + Input modulus; + Input::size_type modulusSignificantBytes; + Result nestedRv = + der::PositiveInteger(r, modulus, &modulusSignificantBytes); + if (nestedRv != Success) { + return nestedRv; + } + // XXX: Should we do additional checks of the modulus? + nestedRv = trustDomain.CheckRSAPublicKeyModulusSizeInBits( + endEntityOrCA, modulusSignificantBytes * 8u); + if (nestedRv != Success) { + return nestedRv; + } + + // XXX: We don't allow the TrustDomain to validate the exponent. + // XXX: We don't do our own sanity checking of the exponent. + Input exponent; + return der::PositiveInteger(r, exponent); + }); + if (rv != Success) { + return rv; + } + } else { + return Result::ERROR_UNSUPPORTED_KEYALG; + } + + rv = der::End(algorithm); + if (rv != Success) { + return rv; + } + rv = der::End(subjectPublicKeyReader); + if (rv != Success) { + return rv; + } + + return Success; +} + +Result +CheckSubjectPublicKeyInfo(Input subjectPublicKeyInfo, TrustDomain& trustDomain, + EndEntityOrCA endEntityOrCA) +{ + Reader spkiReader(subjectPublicKeyInfo); + Result rv = der::Nested(spkiReader, der::SEQUENCE, [&](Reader& r) { + return CheckSubjectPublicKeyInfoContents(r, trustDomain, endEntityOrCA); + }); + if (rv != Success) { + return rv; + } + return der::End(spkiReader); +} + +// 4.2.1.3. Key Usage (id-ce-keyUsage) + +// As explained in the comment in CheckKeyUsage, bit 0 is the most significant +// bit and bit 7 is the least significant bit. +inline uint8_t KeyUsageToBitMask(KeyUsage keyUsage) +{ + assert(keyUsage != KeyUsage::noParticularKeyUsageRequired); + return 0x80u >> static_cast<uint8_t>(keyUsage); +} + +Result +CheckKeyUsage(EndEntityOrCA endEntityOrCA, const Input* encodedKeyUsage, + KeyUsage requiredKeyUsageIfPresent) +{ + if (!encodedKeyUsage) { + // TODO(bug 970196): Reject certificates that are being used to verify + // certificate signatures unless the certificate is a trust anchor, to + // reduce the chances of an end-entity certificate being abused as a CA + // certificate. + // if (endEntityOrCA == EndEntityOrCA::MustBeCA && !isTrustAnchor) { + // return Result::ERROR_INADEQUATE_KEY_USAGE; + // } + // + // TODO: Users may configure arbitrary certificates as trust anchors, not + // just roots. We should only allow a certificate without a key usage to be + // used as a CA when it is self-issued and self-signed. + return Success; + } + + Reader input(*encodedKeyUsage); + Reader value; + if (der::ExpectTagAndGetValue(input, der::BIT_STRING, value) != Success) { + return Result::ERROR_INADEQUATE_KEY_USAGE; + } + + uint8_t numberOfPaddingBits; + if (value.Read(numberOfPaddingBits) != Success) { + return Result::ERROR_INADEQUATE_KEY_USAGE; + } + if (numberOfPaddingBits > 7) { + return Result::ERROR_INADEQUATE_KEY_USAGE; + } + + uint8_t bits; + if (value.Read(bits) != Success) { + // Reject empty bit masks. + return Result::ERROR_INADEQUATE_KEY_USAGE; + } + + // The most significant bit is numbered 0 (digitalSignature) and the least + // significant bit is numbered 7 (encipherOnly), and the padding is in the + // least significant bits of the last byte. The numbering of bits in a byte + // is backwards from how we usually interpret them. + // + // For example, let's say bits is encoded in one byte with of value 0xB0 and + // numberOfPaddingBits == 4. Then, bits is 10110000 in binary: + // + // bit 0 bit 3 + // | | + // v v + // 10110000 + // ^^^^ + // | + // 4 padding bits + // + // Since bits is the last byte, we have to consider the padding by ensuring + // that the least significant 4 bits are all zero, since DER rules require + // all padding bits to be zero. Then we have to look at the bit N bits to the + // right of the most significant bit, where N is a value from the KeyUsage + // enumeration. + // + // Let's say we're interested in the keyCertSign (5) bit. We'd need to look + // at bit 5, which is zero, so keyCertSign is not asserted. (Since we check + // that the padding is all zeros, it is OK to read from the padding bits.) + // + // Let's say we're interested in the digitalSignature (0) bit. We'd need to + // look at the bit 0 (the most significant bit), which is set, so that means + // digitalSignature is asserted. Similarly, keyEncipherment (2) and + // dataEncipherment (3) are asserted. + // + // Note that since the KeyUsage enumeration is limited to values 0-7, we + // only ever need to examine the first byte test for + // requiredKeyUsageIfPresent. + + if (requiredKeyUsageIfPresent != KeyUsage::noParticularKeyUsageRequired) { + // Check that the required key usage bit is set. + if ((bits & KeyUsageToBitMask(requiredKeyUsageIfPresent)) == 0) { + return Result::ERROR_INADEQUATE_KEY_USAGE; + } + } + + // RFC 5280 says "The keyCertSign bit is asserted when the subject public + // key is used for verifying signatures on public key certificates. If the + // keyCertSign bit is asserted, then the cA bit in the basic constraints + // extension (Section 4.2.1.9) MUST also be asserted." + // However, we allow end-entity certificates (i.e. certificates without + // basicConstraints.cA set to TRUE) to claim keyCertSign for compatibility + // reasons. This does not compromise security because we only allow + // certificates with basicConstraints.cA set to TRUE to act as CAs. + if (requiredKeyUsageIfPresent == KeyUsage::keyCertSign && + endEntityOrCA != EndEntityOrCA::MustBeCA) { + return Result::ERROR_INADEQUATE_KEY_USAGE; + } + + // The padding applies to the last byte, so skip to the last byte. + while (!value.AtEnd()) { + if (value.Read(bits) != Success) { + return Result::ERROR_INADEQUATE_KEY_USAGE; + } + } + + // All of the padding bits must be zero, according to DER rules. + uint8_t paddingMask = static_cast<uint8_t>((1 << numberOfPaddingBits) - 1); + if ((bits & paddingMask) != 0) { + return Result::ERROR_INADEQUATE_KEY_USAGE; + } + + return Success; +} + +// RFC5820 4.2.1.4. Certificate Policies + +// "The user-initial-policy-set contains the special value any-policy if the +// user is not concerned about certificate policy." +// +// python DottedOIDToCode.py anyPolicy 2.5.29.32.0 + +static const uint8_t anyPolicy[] = { + 0x55, 0x1d, 0x20, 0x00 +}; + +/*static*/ const CertPolicyId CertPolicyId::anyPolicy = { + 4, { 0x55, 0x1d, 0x20, 0x00 } +}; + +bool +CertPolicyId::IsAnyPolicy() const { + if (this == &CertPolicyId::anyPolicy) { + return true; + } + return numBytes == sizeof(::mozilla::pkix::anyPolicy) && + std::equal(bytes, bytes + numBytes, ::mozilla::pkix::anyPolicy); +} + +bool +CertPolicyId::operator==(const CertPolicyId& other) const +{ + return numBytes == other.numBytes && + std::equal(bytes, bytes + numBytes, other.bytes); +} + +// certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation +Result +CheckCertificatePolicies(EndEntityOrCA endEntityOrCA, + const Input* encodedCertificatePolicies, + const Input* encodedInhibitAnyPolicy, + TrustLevel trustLevel, + const CertPolicyId& requiredPolicy) +{ + if (requiredPolicy.numBytes == 0 || + requiredPolicy.numBytes > sizeof requiredPolicy.bytes) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + + bool requiredPolicyFound = requiredPolicy.IsAnyPolicy(); + if (requiredPolicyFound) { + return Success; + } + + // Bug 989051. Until we handle inhibitAnyPolicy we will fail close when + // inhibitAnyPolicy extension is present and we are validating for a policy. + if (!requiredPolicyFound && encodedInhibitAnyPolicy) { + return Result::ERROR_POLICY_VALIDATION_FAILED; + } + + // The root CA certificate may omit the policies that it has been + // trusted for, so we cannot require the policies to be present in those + // certificates. Instead, the determination of which roots are trusted for + // which policies is made by the TrustDomain's GetCertTrust method. + if (trustLevel == TrustLevel::TrustAnchor && + endEntityOrCA == EndEntityOrCA::MustBeCA) { + requiredPolicyFound = true; + } + + Input requiredPolicyDER; + if (requiredPolicyDER.Init(requiredPolicy.bytes, requiredPolicy.numBytes) + != Success) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + + if (encodedCertificatePolicies) { + Reader extension(*encodedCertificatePolicies); + Reader certificatePolicies; + Result rv = der::ExpectTagAndGetValue(extension, der::SEQUENCE, + certificatePolicies); + if (rv != Success) { + return Result::ERROR_POLICY_VALIDATION_FAILED; + } + if (!extension.AtEnd()) { + return Result::ERROR_POLICY_VALIDATION_FAILED; + } + + do { + // PolicyInformation ::= SEQUENCE { + // policyIdentifier CertPolicyId, + // policyQualifiers SEQUENCE SIZE (1..MAX) OF + // PolicyQualifierInfo OPTIONAL } + Reader policyInformation; + rv = der::ExpectTagAndGetValue(certificatePolicies, der::SEQUENCE, + policyInformation); + if (rv != Success) { + return Result::ERROR_POLICY_VALIDATION_FAILED; + } + + Reader policyIdentifier; + rv = der::ExpectTagAndGetValue(policyInformation, der::OIDTag, + policyIdentifier); + if (rv != Success) { + return rv; + } + + if (policyIdentifier.MatchRest(requiredPolicyDER)) { + requiredPolicyFound = true; + } else if (endEntityOrCA == EndEntityOrCA::MustBeCA && + policyIdentifier.MatchRest(anyPolicy)) { + requiredPolicyFound = true; + } + + // RFC 5280 Section 4.2.1.4 says "Optional qualifiers, which MAY be + // present, are not expected to change the definition of the policy." Also, + // it seems that Section 6, which defines validation, does not require any + // matching of qualifiers. Thus, doing anything with the policy qualifiers + // would be a waste of time and a source of potential incompatibilities, so + // we just ignore them. + } while (!requiredPolicyFound && !certificatePolicies.AtEnd()); + } + + if (!requiredPolicyFound) { + return Result::ERROR_POLICY_VALIDATION_FAILED; + } + + return Success; +} + +static const long UNLIMITED_PATH_LEN = -1; // must be less than zero + +// BasicConstraints ::= SEQUENCE { +// cA BOOLEAN DEFAULT FALSE, +// pathLenConstraint INTEGER (0..MAX) OPTIONAL } + +// RFC5280 4.2.1.9. Basic Constraints (id-ce-basicConstraints) +Result +CheckBasicConstraints(EndEntityOrCA endEntityOrCA, + const Input* encodedBasicConstraints, + const der::Version version, TrustLevel trustLevel, + unsigned int subCACount) +{ + bool isCA = false; + long pathLenConstraint = UNLIMITED_PATH_LEN; + + if (encodedBasicConstraints) { + Reader input(*encodedBasicConstraints); + Result rv = der::Nested(input, der::SEQUENCE, + [&isCA, &pathLenConstraint](Reader& r) { + Result nestedRv = der::OptionalBoolean(r, isCA); + if (nestedRv != Success) { + return nestedRv; + } + // TODO(bug 985025): If isCA is false, pathLenConstraint + // MUST NOT be included (as per RFC 5280 section + // 4.2.1.9), but for compatibility reasons, we don't + // check this. + return der::OptionalInteger(r, UNLIMITED_PATH_LEN, pathLenConstraint); + }); + if (rv != Success) { + return Result::ERROR_EXTENSION_VALUE_INVALID; + } + if (der::End(input) != Success) { + return Result::ERROR_EXTENSION_VALUE_INVALID; + } + } else { + // "If the basic constraints extension is not present in a version 3 + // certificate, or the extension is present but the cA boolean is not + // asserted, then the certified public key MUST NOT be used to verify + // certificate signatures." + // + // For compatibility, we must accept v1 trust anchors without basic + // constraints as CAs. + // + // There are devices with v1 certificates that are unlikely to be trust + // anchors. In order to allow applications to treat this case differently + // from other basic constraints violations (e.g. allowing certificate error + // overrides for only this case), we return a different error code. + // + // TODO: add check for self-signedness? + if (endEntityOrCA == EndEntityOrCA::MustBeCA && version == der::Version::v1) { + if (trustLevel == TrustLevel::TrustAnchor) { + isCA = true; + } else { + return Result::ERROR_V1_CERT_USED_AS_CA; + } + } + } + + if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) { + // CA certificates are not trusted as EE certs. + + if (isCA) { + // Note that this check prevents a delegated OCSP response signing + // certificate with the CA bit from successfully validating when we check + // it from pkixocsp.cpp, which is a good thing. + return Result::ERROR_CA_CERT_USED_AS_END_ENTITY; + } + + return Success; + } + + assert(endEntityOrCA == EndEntityOrCA::MustBeCA); + + // End-entity certificates are not allowed to act as CA certs. + if (!isCA) { + return Result::ERROR_CA_CERT_INVALID; + } + + if (pathLenConstraint >= 0 && + static_cast<long>(subCACount) > pathLenConstraint) { + return Result::ERROR_PATH_LEN_CONSTRAINT_INVALID; + } + + return Success; +} + +// 4.2.1.12. Extended Key Usage (id-ce-extKeyUsage) + +static Result +MatchEKU(Reader& value, KeyPurposeId requiredEKU, + EndEntityOrCA endEntityOrCA, TrustDomain& trustDomain, + Time notBefore, /*in/out*/ bool& found, + /*in/out*/ bool& foundOCSPSigning) +{ + // See Section 5.9 of "A Layman's Guide to a Subset of ASN.1, BER, and DER" + // for a description of ASN.1 DER encoding of OIDs. + + // id-pkix OBJECT IDENTIFIER ::= + // { iso(1) identified-organization(3) dod(6) internet(1) + // security(5) mechanisms(5) pkix(7) } + // id-kp OBJECT IDENTIFIER ::= { id-pkix 3 } + // id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 } + // id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 } + // id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 } + // id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 } + // id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 } + static const uint8_t server[] = { (40*1)+3, 6, 1, 5, 5, 7, 3, 1 }; + static const uint8_t client[] = { (40*1)+3, 6, 1, 5, 5, 7, 3, 2 }; + static const uint8_t code [] = { (40*1)+3, 6, 1, 5, 5, 7, 3, 3 }; + static const uint8_t email [] = { (40*1)+3, 6, 1, 5, 5, 7, 3, 4 }; + static const uint8_t ocsp [] = { (40*1)+3, 6, 1, 5, 5, 7, 3, 9 }; + + // id-Netscape OBJECT IDENTIFIER ::= { 2 16 840 1 113730 } + // id-Netscape-policy OBJECT IDENTIFIER ::= { id-Netscape 4 } + // id-Netscape-stepUp OBJECT IDENTIFIER ::= { id-Netscape-policy 1 } + static const uint8_t serverStepUp[] = + { (40*2)+16, 128+6,72, 1, 128+6,128+120,66, 4, 1 }; + + bool match = false; + + if (!found) { + switch (requiredEKU) { + case KeyPurposeId::id_kp_serverAuth: { + if (value.MatchRest(server)) { + match = true; + break; + } + // Potentially treat CA certs with step-up OID as also having SSL server + // type. Comodo has issued certificates that require this behavior that + // don't expire until June 2020! + if (endEntityOrCA == EndEntityOrCA::MustBeCA && + value.MatchRest(serverStepUp)) { + Result rv = trustDomain.NetscapeStepUpMatchesServerAuth(notBefore, + match); + if (rv != Success) { + return rv; + } + } + break; + } + + case KeyPurposeId::id_kp_clientAuth: + match = value.MatchRest(client); + break; + + case KeyPurposeId::id_kp_codeSigning: + match = value.MatchRest(code); + break; + + case KeyPurposeId::id_kp_emailProtection: + match = value.MatchRest(email); + break; + + case KeyPurposeId::id_kp_OCSPSigning: + match = value.MatchRest(ocsp); + break; + + case KeyPurposeId::anyExtendedKeyUsage: + return NotReached("anyExtendedKeyUsage should start with found==true", + Result::FATAL_ERROR_LIBRARY_FAILURE); + } + } + + if (match) { + found = true; + if (requiredEKU == KeyPurposeId::id_kp_OCSPSigning) { + foundOCSPSigning = true; + } + } else if (value.MatchRest(ocsp)) { + foundOCSPSigning = true; + } + + value.SkipToEnd(); // ignore unmatched OIDs. + + return Success; +} + +Result +CheckExtendedKeyUsage(EndEntityOrCA endEntityOrCA, + const Input* encodedExtendedKeyUsage, + KeyPurposeId requiredEKU, TrustDomain& trustDomain, + Time notBefore) +{ + // XXX: We're using Result::ERROR_INADEQUATE_CERT_TYPE here so that callers + // can distinguish EKU mismatch from KU mismatch from basic constraints + // mismatch. We should probably add a new error code that is more clear for + // this type of problem. + + bool foundOCSPSigning = false; + + if (encodedExtendedKeyUsage) { + bool found = requiredEKU == KeyPurposeId::anyExtendedKeyUsage; + + Reader input(*encodedExtendedKeyUsage); + Result rv = der::NestedOf(input, der::SEQUENCE, der::OIDTag, + der::EmptyAllowed::No, [&](Reader& r) { + return MatchEKU(r, requiredEKU, endEntityOrCA, trustDomain, notBefore, + found, foundOCSPSigning); + }); + if (rv != Success) { + return Result::ERROR_INADEQUATE_CERT_TYPE; + } + if (der::End(input) != Success) { + return Result::ERROR_INADEQUATE_CERT_TYPE; + } + + // If the EKU extension was included, then the required EKU must be in the + // list. + if (!found) { + return Result::ERROR_INADEQUATE_CERT_TYPE; + } + } + + // pkixocsp.cpp depends on the following additional checks. + + if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) { + // When validating anything other than an delegated OCSP signing cert, + // reject any cert that also claims to be an OCSP responder, because such + // a cert does not make sense. For example, if an SSL certificate were to + // assert id-kp-OCSPSigning then it could sign OCSP responses for itself, + // if not for this check. + // That said, we accept CA certificates with id-kp-OCSPSigning because + // some CAs in Mozilla's CA program have issued such intermediate + // certificates, and because some CAs have reported some Microsoft server + // software wrongly requires CA certificates to have id-kp-OCSPSigning. + // Allowing this exception does not cause any security issues because we + // require delegated OCSP response signing certificates to be end-entity + // certificates. + if (foundOCSPSigning && requiredEKU != KeyPurposeId::id_kp_OCSPSigning) { + return Result::ERROR_INADEQUATE_CERT_TYPE; + } + // http://tools.ietf.org/html/rfc6960#section-4.2.2.2: + // "OCSP signing delegation SHALL be designated by the inclusion of + // id-kp-OCSPSigning in an extended key usage certificate extension + // included in the OCSP response signer's certificate." + // + // id-kp-OCSPSigning is the only EKU that isn't implicitly assumed when the + // EKU extension is missing from an end-entity certificate. However, any CA + // certificate can issue a delegated OCSP response signing certificate, so + // we can't require the EKU be explicitly included for CA certificates. + if (!foundOCSPSigning && requiredEKU == KeyPurposeId::id_kp_OCSPSigning) { + return Result::ERROR_INADEQUATE_CERT_TYPE; + } + } + + return Success; +} + +Result +CheckTLSFeatures(const BackCert& subject, BackCert& potentialIssuer) +{ + const Input* issuerTLSFeatures = potentialIssuer.GetRequiredTLSFeatures(); + if (!issuerTLSFeatures) { + return Success; + } + + const Input* subjectTLSFeatures = subject.GetRequiredTLSFeatures(); + if (issuerTLSFeatures->GetLength() == 0 || + !subjectTLSFeatures || + !InputsAreEqual(*issuerTLSFeatures, *subjectTLSFeatures)) { + return Result::ERROR_REQUIRED_TLS_FEATURE_MISSING; + } + + return Success; +} + +Result +TLSFeaturesSatisfiedInternal(const Input* requiredTLSFeatures, + const Input* stapledOCSPResponse) +{ + if (!requiredTLSFeatures) { + return Success; + } + + // RFC 6066 10.2: ExtensionType status_request + const static uint8_t status_request = 5; + const static uint8_t status_request_bytes[] = { status_request }; + + Reader input(*requiredTLSFeatures); + return der::NestedOf(input, der::SEQUENCE, der::INTEGER, + der::EmptyAllowed::No, [&](Reader& r) { + if (!r.MatchRest(status_request_bytes)) { + return Result::ERROR_REQUIRED_TLS_FEATURE_MISSING; + } + + if (!stapledOCSPResponse) { + return Result::ERROR_REQUIRED_TLS_FEATURE_MISSING; + } + + return Result::Success; + }); +} + +Result +CheckTLSFeaturesAreSatisfied(Input& cert, + const Input* stapledOCSPResponse) +{ + BackCert backCert(cert, EndEntityOrCA::MustBeEndEntity, nullptr); + Result rv = backCert.Init(); + if (rv != Success) { + return rv; + } + + return TLSFeaturesSatisfiedInternal(backCert.GetRequiredTLSFeatures(), + stapledOCSPResponse); +} + +Result +CheckIssuerIndependentProperties(TrustDomain& trustDomain, + const BackCert& cert, + Time time, + KeyUsage requiredKeyUsageIfPresent, + KeyPurposeId requiredEKUIfPresent, + const CertPolicyId& requiredPolicy, + unsigned int subCACount, + /*out*/ TrustLevel& trustLevel) +{ + Result rv; + + const EndEntityOrCA endEntityOrCA = cert.endEntityOrCA; + + // Check the cert's trust first, because we want to minimize the amount of + // processing we do on a distrusted cert, in case it is trying to exploit + // some bug in our processing. + rv = trustDomain.GetCertTrust(endEntityOrCA, requiredPolicy, cert.GetDER(), + trustLevel); + if (rv != Success) { + return rv; + } + + // IMPORTANT: We parse the validity interval here, so that we can use the + // notBefore and notAfter values in checks for things that might be deprecated + // over time. However, we must not fail for semantic errors until the end of + // this method, in order to preserve error ranking. + Time notBefore(Time::uninitialized); + Time notAfter(Time::uninitialized); + rv = ParseValidity(cert.GetValidity(), ¬Before, ¬After); + if (rv != Success) { + return rv; + } + + if (trustLevel == TrustLevel::TrustAnchor && + endEntityOrCA == EndEntityOrCA::MustBeEndEntity && + requiredEKUIfPresent == KeyPurposeId::id_kp_OCSPSigning) { + // OCSP signer certificates can never be trust anchors, especially + // since we don't support designated OCSP responders. All of the checks + // below that are dependent on trustLevel rely on this overriding of the + // trust level for OCSP signers. + trustLevel = TrustLevel::InheritsTrust; + } + + switch (trustLevel) { + case TrustLevel::InheritsTrust: + rv = CheckSignatureAlgorithm(trustDomain, endEntityOrCA, notBefore, + cert.GetSignedData(), cert.GetSignature()); + if (rv != Success) { + return rv; + } + break; + + case TrustLevel::TrustAnchor: + // We don't even bother checking signatureAlgorithm or signature for + // syntactic validity for trust anchors, because we don't use those + // fields for anything, and because the trust anchor might be signed + // with a signature algorithm we don't actually support. + break; + + case TrustLevel::ActivelyDistrusted: + return Result::ERROR_UNTRUSTED_CERT; + } + + // Check the SPKI early, because it is one of the most selective properties + // of the certificate due to SHA-1 deprecation and the deprecation of + // certificates with keys weaker than RSA 2048. + rv = CheckSubjectPublicKeyInfo(cert.GetSubjectPublicKeyInfo(), trustDomain, + endEntityOrCA); + if (rv != Success) { + return rv; + } + + // 4.1.2.4. Issuer + rv = CheckIssuer(cert.GetIssuer()); + if (rv != Success) { + return rv; + } + + // 4.2.1.1. Authority Key Identifier is ignored (see bug 965136). + + // 4.2.1.2. Subject Key Identifier is ignored (see bug 965136). + + // 4.2.1.3. Key Usage + rv = CheckKeyUsage(endEntityOrCA, cert.GetKeyUsage(), + requiredKeyUsageIfPresent); + if (rv != Success) { + return rv; + } + + // 4.2.1.4. Certificate Policies + rv = CheckCertificatePolicies(endEntityOrCA, cert.GetCertificatePolicies(), + cert.GetInhibitAnyPolicy(), trustLevel, + requiredPolicy); + if (rv != Success) { + return rv; + } + + // 4.2.1.5. Policy Mappings are not supported; see the documentation about + // policy enforcement in pkix.h. + + // 4.2.1.6. Subject Alternative Name dealt with during name constraint + // checking and during name verification (CERT_VerifyCertName). + + // 4.2.1.7. Issuer Alternative Name is not something that needs checking. + + // 4.2.1.8. Subject Directory Attributes is not something that needs + // checking. + + // 4.2.1.9. Basic Constraints. + rv = CheckBasicConstraints(endEntityOrCA, cert.GetBasicConstraints(), + cert.GetVersion(), trustLevel, subCACount); + if (rv != Success) { + return rv; + } + + // 4.2.1.10. Name Constraints is dealt with in during path building. + + // 4.2.1.11. Policy Constraints are implicitly supported; see the + // documentation about policy enforcement in pkix.h. + + // 4.2.1.12. Extended Key Usage + rv = CheckExtendedKeyUsage(endEntityOrCA, cert.GetExtKeyUsage(), + requiredEKUIfPresent, trustDomain, notBefore); + if (rv != Success) { + return rv; + } + + // 4.2.1.13. CRL Distribution Points is not supported, though the + // TrustDomain's CheckRevocation method may parse it and process it + // on its own. + + // 4.2.1.14. Inhibit anyPolicy is implicitly supported; see the documentation + // about policy enforcement in pkix.h. + + // IMPORTANT: Even though we parse validity above, we wait until this point to + // check it, so that error ranking works correctly. + rv = CheckValidity(time, notBefore, notAfter); + if (rv != Success) { + return rv; + } + + rv = trustDomain.CheckValidityIsAcceptable(notBefore, notAfter, endEntityOrCA, + requiredEKUIfPresent); + if (rv != Success) { + return rv; + } + + return Success; +} + +} } // namespace mozilla::pkix diff --git a/security/nss/lib/mozpkix/lib/pkixder.cpp b/security/nss/lib/mozpkix/lib/pkixder.cpp new file mode 100644 index 000000000..152d11a23 --- /dev/null +++ b/security/nss/lib/mozpkix/lib/pkixder.cpp @@ -0,0 +1,611 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mozpkix/pkixder.h" + +#include "mozpkix/pkixutil.h" + +namespace mozilla { namespace pkix { namespace der { + +// Too complicated to be inline +Result +ReadTagAndGetValue(Reader& input, /*out*/ uint8_t& tag, /*out*/ Input& value) +{ + Result rv; + + rv = input.Read(tag); + if (rv != Success) { + return rv; + } + if ((tag & 0x1F) == 0x1F) { + return Result::ERROR_BAD_DER; // high tag number form not allowed + } + + uint16_t length; + + // The short form of length is a single byte with the high order bit set + // to zero. The long form of length is one byte with the high order bit + // set, followed by N bytes, where N is encoded in the lowest 7 bits of + // the first byte. + uint8_t length1; + rv = input.Read(length1); + if (rv != Success) { + return rv; + } + if (!(length1 & 0x80)) { + length = length1; + } else if (length1 == 0x81) { + uint8_t length2; + rv = input.Read(length2); + if (rv != Success) { + return rv; + } + if (length2 < 128) { + // Not shortest possible encoding + return Result::ERROR_BAD_DER; + } + length = length2; + } else if (length1 == 0x82) { + rv = input.Read(length); + if (rv != Success) { + return rv; + } + if (length < 256) { + // Not shortest possible encoding + return Result::ERROR_BAD_DER; + } + } else { + // We don't support lengths larger than 2^16 - 1. + return Result::ERROR_BAD_DER; + } + + return input.Skip(length, value); +} + +static Result +OptionalNull(Reader& input) +{ + if (input.Peek(NULLTag)) { + return Null(input); + } + return Success; +} + +namespace { + +Result +AlgorithmIdentifierValue(Reader& input, /*out*/ Reader& algorithmOIDValue) +{ + Result rv = ExpectTagAndGetValue(input, der::OIDTag, algorithmOIDValue); + if (rv != Success) { + return rv; + } + return OptionalNull(input); +} + +} // namespace + +Result +SignatureAlgorithmIdentifierValue(Reader& input, + /*out*/ PublicKeyAlgorithm& publicKeyAlgorithm, + /*out*/ DigestAlgorithm& digestAlgorithm) +{ + // RFC 5758 Section 3.2 (ECDSA with SHA-2), and RFC 3279 Section 2.2.3 + // (ECDSA with SHA-1) say that parameters must be omitted. + // + // RFC 4055 Section 5 and RFC 3279 Section 2.2.1 both say that parameters for + // RSA must be encoded as NULL; we relax that requirement by allowing the + // NULL to be omitted, to match all the other signature algorithms we support + // and for compatibility. + Reader algorithmID; + Result rv = AlgorithmIdentifierValue(input, algorithmID); + if (rv != Success) { + return rv; + } + + // RFC 5758 Section 3.2 (ecdsa-with-SHA224 is intentionally excluded) + // python DottedOIDToCode.py ecdsa-with-SHA256 1.2.840.10045.4.3.2 + static const uint8_t ecdsa_with_SHA256[] = { + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02 + }; + // python DottedOIDToCode.py ecdsa-with-SHA384 1.2.840.10045.4.3.3 + static const uint8_t ecdsa_with_SHA384[] = { + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03 + }; + // python DottedOIDToCode.py ecdsa-with-SHA512 1.2.840.10045.4.3.4 + static const uint8_t ecdsa_with_SHA512[] = { + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 + }; + + // RFC 4055 Section 5 (sha224WithRSAEncryption is intentionally excluded) + // python DottedOIDToCode.py sha256WithRSAEncryption 1.2.840.113549.1.1.11 + static const uint8_t sha256WithRSAEncryption[] = { + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b + }; + // python DottedOIDToCode.py sha384WithRSAEncryption 1.2.840.113549.1.1.12 + static const uint8_t sha384WithRSAEncryption[] = { + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c + }; + // python DottedOIDToCode.py sha512WithRSAEncryption 1.2.840.113549.1.1.13 + static const uint8_t sha512WithRSAEncryption[] = { + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d + }; + + // RFC 3279 Section 2.2.1 + // python DottedOIDToCode.py sha-1WithRSAEncryption 1.2.840.113549.1.1.5 + static const uint8_t sha_1WithRSAEncryption[] = { + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05 + }; + + // NIST Open Systems Environment (OSE) Implementor's Workshop (OIW) + // http://www.oiw.org/agreements/stable/12s-9412.txt (no longer works). + // http://www.imc.org/ietf-pkix/old-archive-97/msg01166.html + // We need to support this this non-PKIX OID for compatibility. + // python DottedOIDToCode.py sha1WithRSASignature 1.3.14.3.2.29 + static const uint8_t sha1WithRSASignature[] = { + 0x2b, 0x0e, 0x03, 0x02, 0x1d + }; + + // RFC 3279 Section 2.2.3 + // python DottedOIDToCode.py ecdsa-with-SHA1 1.2.840.10045.4.1 + static const uint8_t ecdsa_with_SHA1[] = { + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01 + }; + + // Matching is attempted based on a rough estimate of the commonality of the + // algorithm, to minimize the number of MatchRest calls. + if (algorithmID.MatchRest(sha256WithRSAEncryption)) { + publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; + digestAlgorithm = DigestAlgorithm::sha256; + } else if (algorithmID.MatchRest(ecdsa_with_SHA256)) { + publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA; + digestAlgorithm = DigestAlgorithm::sha256; + } else if (algorithmID.MatchRest(sha_1WithRSAEncryption)) { + publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; + digestAlgorithm = DigestAlgorithm::sha1; + } else if (algorithmID.MatchRest(ecdsa_with_SHA1)) { + publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA; + digestAlgorithm = DigestAlgorithm::sha1; + } else if (algorithmID.MatchRest(ecdsa_with_SHA384)) { + publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA; + digestAlgorithm = DigestAlgorithm::sha384; + } else if (algorithmID.MatchRest(ecdsa_with_SHA512)) { + publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA; + digestAlgorithm = DigestAlgorithm::sha512; + } else if (algorithmID.MatchRest(sha384WithRSAEncryption)) { + publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; + digestAlgorithm = DigestAlgorithm::sha384; + } else if (algorithmID.MatchRest(sha512WithRSAEncryption)) { + publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; + digestAlgorithm = DigestAlgorithm::sha512; + } else if (algorithmID.MatchRest(sha1WithRSASignature)) { + // XXX(bug 1042479): recognize this old OID for compatibility. + publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; + digestAlgorithm = DigestAlgorithm::sha1; + } else { + return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; + } + + return Success; +} + +Result +DigestAlgorithmIdentifier(Reader& input, /*out*/ DigestAlgorithm& algorithm) +{ + return der::Nested(input, SEQUENCE, [&algorithm](Reader& r) -> Result { + Reader algorithmID; + Result rv = AlgorithmIdentifierValue(r, algorithmID); + if (rv != Success) { + return rv; + } + + // RFC 4055 Section 2.1 + // python DottedOIDToCode.py id-sha1 1.3.14.3.2.26 + static const uint8_t id_sha1[] = { + 0x2b, 0x0e, 0x03, 0x02, 0x1a + }; + // python DottedOIDToCode.py id-sha256 2.16.840.1.101.3.4.2.1 + static const uint8_t id_sha256[] = { + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 + }; + // python DottedOIDToCode.py id-sha384 2.16.840.1.101.3.4.2.2 + static const uint8_t id_sha384[] = { + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 + }; + // python DottedOIDToCode.py id-sha512 2.16.840.1.101.3.4.2.3 + static const uint8_t id_sha512[] = { + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 + }; + + // Matching is attempted based on a rough estimate of the commonality of the + // algorithm, to minimize the number of MatchRest calls. + if (algorithmID.MatchRest(id_sha1)) { + algorithm = DigestAlgorithm::sha1; + } else if (algorithmID.MatchRest(id_sha256)) { + algorithm = DigestAlgorithm::sha256; + } else if (algorithmID.MatchRest(id_sha384)) { + algorithm = DigestAlgorithm::sha384; + } else if (algorithmID.MatchRest(id_sha512)) { + algorithm = DigestAlgorithm::sha512; + } else { + return Result::ERROR_INVALID_ALGORITHM; + } + + return Success; + }); +} + +Result +SignedData(Reader& input, /*out*/ Reader& tbs, + /*out*/ SignedDataWithSignature& signedData) +{ + Reader::Mark mark(input.GetMark()); + + Result rv; + rv = ExpectTagAndGetValue(input, SEQUENCE, tbs); + if (rv != Success) { + return rv; + } + + rv = input.GetInput(mark, signedData.data); + if (rv != Success) { + return rv; + } + + rv = ExpectTagAndGetValue(input, der::SEQUENCE, signedData.algorithm); + if (rv != Success) { + return rv; + } + + rv = BitStringWithNoUnusedBits(input, signedData.signature); + if (rv == Result::ERROR_BAD_DER) { + rv = Result::ERROR_BAD_SIGNATURE; + } + return rv; +} + +Result +BitStringWithNoUnusedBits(Reader& input, /*out*/ Input& value) +{ + Reader valueWithUnusedBits; + Result rv = ExpectTagAndGetValue(input, BIT_STRING, valueWithUnusedBits); + if (rv != Success) { + return rv; + } + + uint8_t unusedBitsAtEnd; + if (valueWithUnusedBits.Read(unusedBitsAtEnd) != Success) { + return Result::ERROR_BAD_DER; + } + // XXX: Really the constraint should be that unusedBitsAtEnd must be less + // than 7. But, we suspect there are no real-world values in OCSP responses + // or certificates with non-zero unused bits. It seems like NSS assumes this + // in various places, so we enforce it too in order to simplify this code. If + // we find compatibility issues, we'll know we're wrong and we'll have to + // figure out how to shift the bits around. + if (unusedBitsAtEnd != 0) { + return Result::ERROR_BAD_DER; + } + return valueWithUnusedBits.SkipToEnd(value); +} + +static inline Result +ReadDigit(Reader& input, /*out*/ unsigned int& value) +{ + uint8_t b; + if (input.Read(b) != Success) { + return Result::ERROR_INVALID_DER_TIME; + } + if (b < '0' || b > '9') { + return Result::ERROR_INVALID_DER_TIME; + } + value = static_cast<unsigned int>(b - static_cast<uint8_t>('0')); + return Success; +} + +static inline Result +ReadTwoDigits(Reader& input, unsigned int minValue, unsigned int maxValue, + /*out*/ unsigned int& value) +{ + unsigned int hi; + Result rv = ReadDigit(input, hi); + if (rv != Success) { + return rv; + } + unsigned int lo; + rv = ReadDigit(input, lo); + if (rv != Success) { + return rv; + } + value = (hi * 10) + lo; + if (value < minValue || value > maxValue) { + return Result::ERROR_INVALID_DER_TIME; + } + return Success; +} + +namespace internal { + +// We parse GeneralizedTime and UTCTime according to RFC 5280 and we do not +// accept all time formats allowed in the ASN.1 spec. That is, +// GeneralizedTime must always be in the format YYYYMMDDHHMMSSZ and UTCTime +// must always be in the format YYMMDDHHMMSSZ. Timezone formats of the form +// +HH:MM or -HH:MM or NOT accepted. +Result +TimeChoice(Reader& tagged, uint8_t expectedTag, /*out*/ Time& time) +{ + unsigned int days; + + Reader input; + Result rv = ExpectTagAndGetValue(tagged, expectedTag, input); + if (rv != Success) { + return rv; + } + + unsigned int yearHi; + unsigned int yearLo; + if (expectedTag == GENERALIZED_TIME) { + rv = ReadTwoDigits(input, 0, 99, yearHi); + if (rv != Success) { + return rv; + } + rv = ReadTwoDigits(input, 0, 99, yearLo); + if (rv != Success) { + return rv; + } + } else if (expectedTag == UTCTime) { + rv = ReadTwoDigits(input, 0, 99, yearLo); + if (rv != Success) { + return rv; + } + yearHi = yearLo >= 50u ? 19u : 20u; + } else { + return NotReached("invalid tag given to TimeChoice", + Result::ERROR_INVALID_DER_TIME); + } + unsigned int year = (yearHi * 100u) + yearLo; + if (year < 1970u) { + // We don't support dates before January 1, 1970 because that is the epoch. + return Result::ERROR_INVALID_DER_TIME; + } + days = DaysBeforeYear(year); + + unsigned int month; + rv = ReadTwoDigits(input, 1u, 12u, month); + if (rv != Success) { + return rv; + } + unsigned int daysInMonth; + static const unsigned int jan = 31u; + const unsigned int feb = ((year % 4u == 0u) && + ((year % 100u != 0u) || (year % 400u == 0u))) + ? 29u + : 28u; + static const unsigned int mar = 31u; + static const unsigned int apr = 30u; + static const unsigned int may = 31u; + static const unsigned int jun = 30u; + static const unsigned int jul = 31u; + static const unsigned int aug = 31u; + static const unsigned int sep = 30u; + static const unsigned int oct = 31u; + static const unsigned int nov = 30u; + static const unsigned int dec = 31u; + switch (month) { + case 1: daysInMonth = jan; break; + case 2: daysInMonth = feb; days += jan; break; + case 3: daysInMonth = mar; days += jan + feb; break; + case 4: daysInMonth = apr; days += jan + feb + mar; break; + case 5: daysInMonth = may; days += jan + feb + mar + apr; break; + case 6: daysInMonth = jun; days += jan + feb + mar + apr + may; break; + case 7: daysInMonth = jul; days += jan + feb + mar + apr + may + jun; + break; + case 8: daysInMonth = aug; days += jan + feb + mar + apr + may + jun + + jul; + break; + case 9: daysInMonth = sep; days += jan + feb + mar + apr + may + jun + + jul + aug; + break; + case 10: daysInMonth = oct; days += jan + feb + mar + apr + may + jun + + jul + aug + sep; + break; + case 11: daysInMonth = nov; days += jan + feb + mar + apr + may + jun + + jul + aug + sep + oct; + break; + case 12: daysInMonth = dec; days += jan + feb + mar + apr + may + jun + + jul + aug + sep + oct + nov; + break; + default: + return NotReached("month already bounds-checked by ReadTwoDigits", + Result::FATAL_ERROR_INVALID_STATE); + } + + unsigned int dayOfMonth; + rv = ReadTwoDigits(input, 1u, daysInMonth, dayOfMonth); + if (rv != Success) { + return rv; + } + days += dayOfMonth - 1; + + unsigned int hours; + rv = ReadTwoDigits(input, 0u, 23u, hours); + if (rv != Success) { + return rv; + } + unsigned int minutes; + rv = ReadTwoDigits(input, 0u, 59u, minutes); + if (rv != Success) { + return rv; + } + unsigned int seconds; + rv = ReadTwoDigits(input, 0u, 59u, seconds); + if (rv != Success) { + return rv; + } + + uint8_t b; + if (input.Read(b) != Success) { + return Result::ERROR_INVALID_DER_TIME; + } + if (b != 'Z') { + return Result::ERROR_INVALID_DER_TIME; + } + if (End(input) != Success) { + return Result::ERROR_INVALID_DER_TIME; + } + + uint64_t totalSeconds = (static_cast<uint64_t>(days) * 24u * 60u * 60u) + + (static_cast<uint64_t>(hours) * 60u * 60u) + + (static_cast<uint64_t>(minutes) * 60u) + + seconds; + + time = TimeFromElapsedSecondsAD(totalSeconds); + return Success; +} + +Result +IntegralBytes(Reader& input, uint8_t tag, + IntegralValueRestriction valueRestriction, + /*out*/ Input& value, + /*optional out*/ Input::size_type* significantBytes) +{ + Result rv = ExpectTagAndGetValue(input, tag, value); + if (rv != Success) { + return rv; + } + Reader reader(value); + + // There must be at least one byte in the value. (Zero is encoded with a + // single 0x00 value byte.) + uint8_t firstByte; + rv = reader.Read(firstByte); + if (rv != Success) { + if (rv == Result::ERROR_BAD_DER) { + return Result::ERROR_INVALID_INTEGER_ENCODING; + } + + return rv; + } + + // If there is a byte after an initial 0x00/0xFF, then the initial byte + // indicates a positive/negative integer value with its high bit set/unset. + bool prefixed = !reader.AtEnd() && (firstByte == 0 || firstByte == 0xff); + + if (prefixed) { + uint8_t nextByte; + if (reader.Read(nextByte) != Success) { + return NotReached("Read of one byte failed but not at end.", + Result::FATAL_ERROR_LIBRARY_FAILURE); + } + if ((firstByte & 0x80) == (nextByte & 0x80)) { + return Result::ERROR_INVALID_INTEGER_ENCODING; + } + } + + switch (valueRestriction) { + case IntegralValueRestriction::MustBe0To127: + if (value.GetLength() != 1 || (firstByte & 0x80) != 0) { + return Result::ERROR_INVALID_INTEGER_ENCODING; + } + break; + + case IntegralValueRestriction::MustBePositive: + if ((value.GetLength() == 1 && firstByte == 0) || + (firstByte & 0x80) != 0) { + return Result::ERROR_INVALID_INTEGER_ENCODING; + } + break; + + case IntegralValueRestriction::NoRestriction: + break; + } + + if (significantBytes) { + *significantBytes = value.GetLength(); + if (prefixed) { + assert(*significantBytes > 1); + --*significantBytes; + } + + assert(*significantBytes > 0); + } + + return Success; +} + +// This parser will only parse values between 0..127. If this range is +// increased then callers will need to be changed. +Result +IntegralValue(Reader& input, uint8_t tag, /*out*/ uint8_t& value) +{ + // Conveniently, all the Integers that we actually have to be able to parse + // are positive and very small. Consequently, this parser is *much* simpler + // than a general Integer parser would need to be. + Input valueBytes; + Result rv = IntegralBytes(input, tag, IntegralValueRestriction::MustBe0To127, + valueBytes, nullptr); + if (rv != Success) { + return rv; + } + Reader valueReader(valueBytes); + rv = valueReader.Read(value); + if (rv != Success) { + return NotReached("IntegralBytes already validated the value.", rv); + } + rv = End(valueReader); + assert(rv == Success); // guaranteed by IntegralBytes's range checks. + return rv; +} + +} // namespace internal + +Result +OptionalVersion(Reader& input, /*out*/ Version& version) +{ + static const uint8_t TAG = CONTEXT_SPECIFIC | CONSTRUCTED | 0; + if (!input.Peek(TAG)) { + version = Version::v1; + return Success; + } + return Nested(input, TAG, [&version](Reader& value) -> Result { + uint8_t integerValue; + Result rv = Integer(value, integerValue); + if (rv != Success) { + return rv; + } + // XXX(bug 1031093): We shouldn't accept an explicit encoding of v1, + // but we do here for compatibility reasons. + switch (integerValue) { + case static_cast<uint8_t>(Version::v3): version = Version::v3; break; + case static_cast<uint8_t>(Version::v2): version = Version::v2; break; + case static_cast<uint8_t>(Version::v1): version = Version::v1; break; + case static_cast<uint8_t>(Version::v4): version = Version::v4; break; + default: + return Result::ERROR_BAD_DER; + } + return Success; + }); +} + +} } } // namespace mozilla::pkix::der diff --git a/security/nss/lib/mozpkix/lib/pkixnames.cpp b/security/nss/lib/mozpkix/lib/pkixnames.cpp new file mode 100644 index 000000000..6f40800d7 --- /dev/null +++ b/security/nss/lib/mozpkix/lib/pkixnames.cpp @@ -0,0 +1,2050 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2014 Mozilla Contributors + * + * 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. + */ + +// This code implements RFC6125-ish name matching, RFC5280-ish name constraint +// checking, and related things. +// +// In this code, identifiers are classified as either "presented" or +// "reference" identifiers are defined in +// http://tools.ietf.org/html/rfc6125#section-1.8. A "presented identifier" is +// one in the subjectAltName of the certificate, or sometimes within a CN of +// the certificate's subject. The "reference identifier" is the one we are +// being asked to match the certificate against. When checking name +// constraints, the reference identifier is the entire encoded name constraint +// extension value. + +#include <algorithm> + +#include "mozpkix/pkixcheck.h" +#include "mozpkix/pkixutil.h" + +namespace mozilla { namespace pkix { + +namespace { + +// GeneralName ::= CHOICE { +// otherName [0] OtherName, +// rfc822Name [1] IA5String, +// dNSName [2] IA5String, +// x400Address [3] ORAddress, +// directoryName [4] Name, +// ediPartyName [5] EDIPartyName, +// uniformResourceIdentifier [6] IA5String, +// iPAddress [7] OCTET STRING, +// registeredID [8] OBJECT IDENTIFIER } +enum class GeneralNameType : uint8_t +{ + // Note that these values are NOT contiguous. Some values have the + // der::CONSTRUCTED bit set while others do not. + // (The der::CONSTRUCTED bit is for types where the value is a SEQUENCE.) + otherName = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0, + rfc822Name = der::CONTEXT_SPECIFIC | 1, + dNSName = der::CONTEXT_SPECIFIC | 2, + x400Address = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 3, + directoryName = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 4, + ediPartyName = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 5, + uniformResourceIdentifier = der::CONTEXT_SPECIFIC | 6, + iPAddress = der::CONTEXT_SPECIFIC | 7, + registeredID = der::CONTEXT_SPECIFIC | 8, + // nameConstraints is a pseudo-GeneralName used to signify that a + // reference ID is actually the entire name constraint extension. + nameConstraints = 0xff +}; + +inline Result +ReadGeneralName(Reader& reader, + /*out*/ GeneralNameType& generalNameType, + /*out*/ Input& value) +{ + uint8_t tag; + Result rv = der::ReadTagAndGetValue(reader, tag, value); + if (rv != Success) { + return rv; + } + switch (tag) { + case static_cast<uint8_t>(GeneralNameType::otherName): + generalNameType = GeneralNameType::otherName; + break; + case static_cast<uint8_t>(GeneralNameType::rfc822Name): + generalNameType = GeneralNameType::rfc822Name; + break; + case static_cast<uint8_t>(GeneralNameType::dNSName): + generalNameType = GeneralNameType::dNSName; + break; + case static_cast<uint8_t>(GeneralNameType::x400Address): + generalNameType = GeneralNameType::x400Address; + break; + case static_cast<uint8_t>(GeneralNameType::directoryName): + generalNameType = GeneralNameType::directoryName; + break; + case static_cast<uint8_t>(GeneralNameType::ediPartyName): + generalNameType = GeneralNameType::ediPartyName; + break; + case static_cast<uint8_t>(GeneralNameType::uniformResourceIdentifier): + generalNameType = GeneralNameType::uniformResourceIdentifier; + break; + case static_cast<uint8_t>(GeneralNameType::iPAddress): + generalNameType = GeneralNameType::iPAddress; + break; + case static_cast<uint8_t>(GeneralNameType::registeredID): + generalNameType = GeneralNameType::registeredID; + break; + default: + return Result::ERROR_BAD_DER; + } + return Success; +} + +enum class MatchResult +{ + NoNamesOfGivenType = 0, + Mismatch = 1, + Match = 2 +}; + +Result SearchNames(const Input* subjectAltName, Input subject, + GeneralNameType referenceIDType, + Input referenceID, + FallBackToSearchWithinSubject fallBackToCommonName, + /*out*/ MatchResult& match); +Result SearchWithinRDN(Reader& rdn, + GeneralNameType referenceIDType, + Input referenceID, + FallBackToSearchWithinSubject fallBackToEmailAddress, + FallBackToSearchWithinSubject fallBackToCommonName, + /*in/out*/ MatchResult& match); +Result MatchAVA(Input type, + uint8_t valueEncodingTag, + Input presentedID, + GeneralNameType referenceIDType, + Input referenceID, + FallBackToSearchWithinSubject fallBackToEmailAddress, + FallBackToSearchWithinSubject fallBackToCommonName, + /*in/out*/ MatchResult& match); +Result ReadAVA(Reader& rdn, + /*out*/ Input& type, + /*out*/ uint8_t& valueTag, + /*out*/ Input& value); +void MatchSubjectPresentedIDWithReferenceID(GeneralNameType presentedIDType, + Input presentedID, + GeneralNameType referenceIDType, + Input referenceID, + /*in/out*/ MatchResult& match); + +Result MatchPresentedIDWithReferenceID(GeneralNameType presentedIDType, + Input presentedID, + GeneralNameType referenceIDType, + Input referenceID, + /*in/out*/ MatchResult& matchResult); +Result CheckPresentedIDConformsToConstraints(GeneralNameType referenceIDType, + Input presentedID, + Input nameConstraints); + +uint8_t LocaleInsensitveToLower(uint8_t a); +bool StartsWithIDNALabel(Input id); + +enum class IDRole +{ + ReferenceID = 0, + PresentedID = 1, + NameConstraint = 2, +}; + +enum class AllowWildcards { No = 0, Yes = 1 }; + +// DNSName constraints implicitly allow subdomain matching when there is no +// leading dot ("foo.example.com" matches a constraint of "example.com"), but +// RFC822Name constraints only allow subdomain matching when there is a leading +// dot ("foo.example.com" does not match "example.com" but does match +// ".example.com"). +enum class AllowDotlessSubdomainMatches { No = 0, Yes = 1 }; + +bool IsValidDNSID(Input hostname, IDRole idRole, + AllowWildcards allowWildcards); + +Result MatchPresentedDNSIDWithReferenceDNSID( + Input presentedDNSID, + AllowWildcards allowWildcards, + AllowDotlessSubdomainMatches allowDotlessSubdomainMatches, + IDRole referenceDNSIDRole, + Input referenceDNSID, + /*out*/ bool& matches); + +Result MatchPresentedRFC822NameWithReferenceRFC822Name( + Input presentedRFC822Name, IDRole referenceRFC822NameRole, + Input referenceRFC822Name, /*out*/ bool& matches); + +} // namespace + +bool IsValidReferenceDNSID(Input hostname); +bool IsValidPresentedDNSID(Input hostname); +bool ParseIPv4Address(Input hostname, /*out*/ uint8_t (&out)[4]); +bool ParseIPv6Address(Input hostname, /*out*/ uint8_t (&out)[16]); + +// This is used by the pkixnames_tests.cpp tests. +Result +MatchPresentedDNSIDWithReferenceDNSID(Input presentedDNSID, + Input referenceDNSID, + /*out*/ bool& matches) +{ + return MatchPresentedDNSIDWithReferenceDNSID( + presentedDNSID, AllowWildcards::Yes, + AllowDotlessSubdomainMatches::Yes, IDRole::ReferenceID, + referenceDNSID, matches); +} + +// Verify that the given end-entity cert, which is assumed to have been already +// validated with BuildCertChain, is valid for the given hostname. hostname is +// assumed to be a string representation of an IPv4 address, an IPv6 addresss, +// or a normalized ASCII (possibly punycode) DNS name. +Result +CheckCertHostname(Input endEntityCertDER, Input hostname, + NameMatchingPolicy& nameMatchingPolicy) +{ + BackCert cert(endEntityCertDER, EndEntityOrCA::MustBeEndEntity, nullptr); + Result rv = cert.Init(); + if (rv != Success) { + return rv; + } + + Time notBefore(Time::uninitialized); + rv = ParseValidity(cert.GetValidity(), ¬Before); + if (rv != Success) { + return rv; + } + FallBackToSearchWithinSubject fallBackToSearchWithinSubject; + rv = nameMatchingPolicy.FallBackToCommonName(notBefore, + fallBackToSearchWithinSubject); + if (rv != Success) { + return rv; + } + + const Input* subjectAltName(cert.GetSubjectAltName()); + Input subject(cert.GetSubject()); + + // For backward compatibility with legacy certificates, we may fall back to + // searching for a name match in the subject common name for DNS names and + // IPv4 addresses. We don't do so for IPv6 addresses because we do not think + // there are many certificates that would need such fallback, and because + // comparisons of string representations of IPv6 addresses are particularly + // error prone due to the syntactic flexibility that IPv6 addresses have. + // + // IPv4 and IPv6 addresses are represented using the same type of GeneralName + // (iPAddress); they are differentiated by the lengths of the values. + MatchResult match; + uint8_t ipv6[16]; + uint8_t ipv4[4]; + if (IsValidReferenceDNSID(hostname)) { + rv = SearchNames(subjectAltName, subject, GeneralNameType::dNSName, + hostname, fallBackToSearchWithinSubject, match); + } else if (ParseIPv6Address(hostname, ipv6)) { + rv = SearchNames(subjectAltName, subject, GeneralNameType::iPAddress, + Input(ipv6), FallBackToSearchWithinSubject::No, match); + } else if (ParseIPv4Address(hostname, ipv4)) { + rv = SearchNames(subjectAltName, subject, GeneralNameType::iPAddress, + Input(ipv4), fallBackToSearchWithinSubject, match); + } else { + return Result::ERROR_BAD_CERT_DOMAIN; + } + if (rv != Success) { + return rv; + } + switch (match) { + case MatchResult::NoNamesOfGivenType: // fall through + case MatchResult::Mismatch: + return Result::ERROR_BAD_CERT_DOMAIN; + case MatchResult::Match: + return Success; + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM + } +} + +// 4.2.1.10. Name Constraints +Result +CheckNameConstraints(Input encodedNameConstraints, + const BackCert& firstChild, + KeyPurposeId requiredEKUIfPresent) +{ + for (const BackCert* child = &firstChild; child; child = child->childCert) { + FallBackToSearchWithinSubject fallBackToCommonName + = (child->endEntityOrCA == EndEntityOrCA::MustBeEndEntity && + requiredEKUIfPresent == KeyPurposeId::id_kp_serverAuth) + ? FallBackToSearchWithinSubject::Yes + : FallBackToSearchWithinSubject::No; + + MatchResult match; + Result rv = SearchNames(child->GetSubjectAltName(), child->GetSubject(), + GeneralNameType::nameConstraints, + encodedNameConstraints, fallBackToCommonName, + match); + if (rv != Success) { + return rv; + } + switch (match) { + case MatchResult::Match: // fall through + case MatchResult::NoNamesOfGivenType: + break; + case MatchResult::Mismatch: + return Result::ERROR_CERT_NOT_IN_NAME_SPACE; + } + } + + return Success; +} + +namespace { + +// SearchNames is used by CheckCertHostname and CheckNameConstraints. +// +// When called during name constraint checking, referenceIDType is +// GeneralNameType::nameConstraints and referenceID is the entire encoded name +// constraints extension value. +// +// The main benefit of using the exact same code paths for both is that we +// ensure consistency between name validation and name constraint enforcement +// regarding thing like "Which CN attributes should be considered as potential +// CN-IDs" and "Which character sets are acceptable for CN-IDs?" If the name +// matching and the name constraint enforcement logic were out of sync on these +// issues (e.g. if name matching were to consider all subject CN attributes, +// but name constraints were only enforced on the most specific subject CN), +// trivial name constraint bypasses could result. + +Result +SearchNames(/*optional*/ const Input* subjectAltName, + Input subject, + GeneralNameType referenceIDType, + Input referenceID, + FallBackToSearchWithinSubject fallBackToCommonName, + /*out*/ MatchResult& match) +{ + Result rv; + + match = MatchResult::NoNamesOfGivenType; + + // RFC 6125 says "A client MUST NOT seek a match for a reference identifier + // of CN-ID if the presented identifiers include a DNS-ID, SRV-ID, URI-ID, or + // any application-specific identifier types supported by the client." + // Accordingly, we only consider CN-IDs if there are no DNS-IDs in the + // subjectAltName. + // + // RFC 6125 says that IP addresses are out of scope, but for backward + // compatibility we accept them, by considering IP addresses to be an + // "application-specific identifier type supported by the client." + // + // TODO(bug XXXXXXX): Consider strengthening this check to "A client MUST NOT + // seek a match for a reference identifier of CN-ID if the certificate + // contains a subjectAltName extension." + // + // TODO(bug XXXXXXX): Consider dropping support for IP addresses as + // identifiers completely. + + if (subjectAltName) { + Reader altNames; + rv = der::ExpectTagAndGetValueAtEnd(*subjectAltName, der::SEQUENCE, + altNames); + if (rv != Success) { + return rv; + } + + // According to RFC 5280, "If the subjectAltName extension is present, the + // sequence MUST contain at least one entry." For compatibility reasons, we + // do not enforce this. See bug 1143085. + while (!altNames.AtEnd()) { + GeneralNameType presentedIDType; + Input presentedID; + rv = ReadGeneralName(altNames, presentedIDType, presentedID); + if (rv != Success) { + return rv; + } + + rv = MatchPresentedIDWithReferenceID(presentedIDType, presentedID, + referenceIDType, referenceID, + match); + if (rv != Success) { + return rv; + } + if (referenceIDType != GeneralNameType::nameConstraints && + match == MatchResult::Match) { + return Success; + } + if (presentedIDType == GeneralNameType::dNSName || + presentedIDType == GeneralNameType::iPAddress) { + fallBackToCommonName = FallBackToSearchWithinSubject::No; + } + } + } + + if (referenceIDType == GeneralNameType::nameConstraints) { + rv = CheckPresentedIDConformsToConstraints(GeneralNameType::directoryName, + subject, referenceID); + if (rv != Success) { + return rv; + } + } + + FallBackToSearchWithinSubject fallBackToEmailAddress; + if (!subjectAltName && + (referenceIDType == GeneralNameType::rfc822Name || + referenceIDType == GeneralNameType::nameConstraints)) { + fallBackToEmailAddress = FallBackToSearchWithinSubject::Yes; + } else { + fallBackToEmailAddress = FallBackToSearchWithinSubject::No; + } + + // Short-circuit the parsing of the subject name if we're not going to match + // any names in it + if (fallBackToEmailAddress == FallBackToSearchWithinSubject::No && + fallBackToCommonName == FallBackToSearchWithinSubject::No) { + return Success; + } + + // Attempt to match the reference ID against the CN-ID, which we consider to + // be the most-specific CN AVA in the subject field. + // + // https://tools.ietf.org/html/rfc6125#section-2.3.1 says: + // + // To reduce confusion, in this specification we avoid such terms and + // instead use the terms provided under Section 1.8; in particular, we + // do not use the term "(most specific) Common Name field in the subject + // field" from [HTTP-TLS] and instead state that a CN-ID is a Relative + // Distinguished Name (RDN) in the certificate subject containing one + // and only one attribute-type-and-value pair of type Common Name (thus + // removing the possibility that an RDN might contain multiple AVAs + // (Attribute Value Assertions) of type CN, one of which could be + // considered "most specific"). + // + // https://tools.ietf.org/html/rfc6125#section-7.4 says: + // + // [...] Although it would be preferable to + // forbid multiple CN-IDs entirely, there are several reasons at this + // time why this specification states that they SHOULD NOT (instead of + // MUST NOT) be included [...] + // + // Consequently, it is unclear what to do when there are multiple CNs in the + // subject, regardless of whether there "SHOULD NOT" be. + // + // NSS's CERT_VerifyCertName mostly follows RFC2818 in this instance, which + // says: + // + // If a subjectAltName extension of type dNSName is present, that MUST + // be used as the identity. Otherwise, the (most specific) Common Name + // field in the Subject field of the certificate MUST be used. + // + // [...] + // + // In some cases, the URI is specified as an IP address rather than a + // hostname. In this case, the iPAddress subjectAltName must be present + // in the certificate and must exactly match the IP in the URI. + // + // (The main difference from RFC2818 is that NSS's CERT_VerifyCertName also + // matches IP addresses in the most-specific CN.) + // + // NSS's CERT_VerifyCertName finds the most specific CN via + // CERT_GetCommoName, which uses CERT_GetLastNameElement. Note that many + // NSS-based applications, including Gecko, also use CERT_GetCommonName. It + // is likely that other, non-NSS-based, applications also expect only the + // most specific CN to be matched against the reference ID. + // + // "A Layman's Guide to a Subset of ASN.1, BER, and DER" and other sources + // agree that an RDNSequence is ordered from most significant (least + // specific) to least significant (most specific), as do other references. + // + // However, Chromium appears to use the least-specific (first) CN instead of + // the most-specific; see https://crbug.com/366957. Also, MSIE and some other + // popular implementations apparently attempt to match the reference ID + // against any/all CNs in the subject. Since we're trying to phase out the + // use of CN-IDs, we intentionally avoid trying to match MSIE's more liberal + // behavior. + + // Name ::= CHOICE { -- only one possibility for now -- + // rdnSequence RDNSequence } + // + // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + // + // RelativeDistinguishedName ::= + // SET SIZE (1..MAX) OF AttributeTypeAndValue + Reader subjectReader(subject); + return der::NestedOf(subjectReader, der::SEQUENCE, der::SET, + der::EmptyAllowed::Yes, [&](Reader& r) { + return SearchWithinRDN(r, referenceIDType, referenceID, + fallBackToEmailAddress, fallBackToCommonName, match); + }); +} + +// RelativeDistinguishedName ::= +// SET SIZE (1..MAX) OF AttributeTypeAndValue +// +// AttributeTypeAndValue ::= SEQUENCE { +// type AttributeType, +// value AttributeValue } +Result +SearchWithinRDN(Reader& rdn, + GeneralNameType referenceIDType, + Input referenceID, + FallBackToSearchWithinSubject fallBackToEmailAddress, + FallBackToSearchWithinSubject fallBackToCommonName, + /*in/out*/ MatchResult& match) +{ + do { + Input type; + uint8_t valueTag; + Input value; + Result rv = ReadAVA(rdn, type, valueTag, value); + if (rv != Success) { + return rv; + } + rv = MatchAVA(type, valueTag, value, referenceIDType, referenceID, + fallBackToEmailAddress, fallBackToCommonName, match); + if (rv != Success) { + return rv; + } + } while (!rdn.AtEnd()); + + return Success; +} + +// AttributeTypeAndValue ::= SEQUENCE { +// type AttributeType, +// value AttributeValue } +// +// AttributeType ::= OBJECT IDENTIFIER +// +// AttributeValue ::= ANY -- DEFINED BY AttributeType +// +// DirectoryString ::= CHOICE { +// teletexString TeletexString (SIZE (1..MAX)), +// printableString PrintableString (SIZE (1..MAX)), +// universalString UniversalString (SIZE (1..MAX)), +// utf8String UTF8String (SIZE (1..MAX)), +// bmpString BMPString (SIZE (1..MAX)) } +Result +MatchAVA(Input type, uint8_t valueEncodingTag, Input presentedID, + GeneralNameType referenceIDType, + Input referenceID, + FallBackToSearchWithinSubject fallBackToEmailAddress, + FallBackToSearchWithinSubject fallBackToCommonName, + /*in/out*/ MatchResult& match) +{ + // Try to match the CN as a DNSName or an IPAddress. + // + // id-at-commonName AttributeType ::= { id-at 3 } + // + // -- Naming attributes of type X520CommonName: + // -- X520CommonName ::= DirectoryName (SIZE (1..ub-common-name)) + // -- + // -- Expanded to avoid parameterized type: + // X520CommonName ::= CHOICE { + // teletexString TeletexString (SIZE (1..ub-common-name)), + // printableString PrintableString (SIZE (1..ub-common-name)), + // universalString UniversalString (SIZE (1..ub-common-name)), + // utf8String UTF8String (SIZE (1..ub-common-name)), + // bmpString BMPString (SIZE (1..ub-common-name)) } + // + // python DottedOIDToCode.py id-at-commonName 2.5.4.3 + static const uint8_t id_at_commonName[] = { + 0x55, 0x04, 0x03 + }; + if (fallBackToCommonName == FallBackToSearchWithinSubject::Yes && + InputsAreEqual(type, Input(id_at_commonName))) { + // We might have previously found a match. Now that we've found another CN, + // we no longer consider that previous match to be a match, so "forget" about + // it. + match = MatchResult::NoNamesOfGivenType; + + // PrintableString is a subset of ASCII that contains all the characters + // allowed in CN-IDs except '*'. Although '*' is illegal, there are many + // real-world certificates that are encoded this way, so we accept it. + // + // In the case of UTF8String, we rely on the fact that in UTF-8 the octets in + // a multi-byte encoding of a code point are always distinct from ASCII. Any + // non-ASCII byte in a UTF-8 string causes us to fail to match. We make no + // attempt to detect or report malformed UTF-8 (e.g. incomplete or overlong + // encodings of code points, or encodings of invalid code points). + // + // TeletexString is supported as long as it does not contain any escape + // sequences, which are not supported. We'll reject escape sequences as + // invalid characters in names, which means we only accept strings that are + // in the default character set, which is a superset of ASCII. Note that NSS + // actually treats TeletexString as ISO-8859-1. Many certificates that have + // wildcard CN-IDs (e.g. "*.example.com") use TeletexString because + // PrintableString is defined to not allow '*' and because, at one point in + // history, UTF8String was too new to use for compatibility reasons. + // + // UniversalString and BMPString are also deprecated, and they are a little + // harder to support because they are not single-byte ASCII superset + // encodings, so we don't bother. + if (valueEncodingTag != der::PrintableString && + valueEncodingTag != der::UTF8String && + valueEncodingTag != der::TeletexString) { + return Success; + } + + if (IsValidPresentedDNSID(presentedID)) { + MatchSubjectPresentedIDWithReferenceID(GeneralNameType::dNSName, + presentedID, referenceIDType, + referenceID, match); + } else { + // We don't match CN-IDs for IPv6 addresses. + // MatchSubjectPresentedIDWithReferenceID ensures that it won't match an + // IPv4 address with an IPv6 address, so we don't need to check that + // referenceID is an IPv4 address here. + uint8_t ipv4[4]; + if (ParseIPv4Address(presentedID, ipv4)) { + MatchSubjectPresentedIDWithReferenceID(GeneralNameType::iPAddress, + Input(ipv4), referenceIDType, + referenceID, match); + } + } + + // Regardless of whether there was a match, we keep going in case we find + // another CN later. If we do find another one, then this match/mismatch + // will be ignored, because we only care about the most specific CN. + + return Success; + } + + // Match an email address against an emailAddress attribute in the + // subject. + // + // id-emailAddress AttributeType ::= { pkcs-9 1 } + // + // EmailAddress ::= IA5String (SIZE (1..ub-emailaddress-length)) + // + // python DottedOIDToCode.py id-emailAddress 1.2.840.113549.1.9.1 + static const uint8_t id_emailAddress[] = { + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01 + }; + if (fallBackToEmailAddress == FallBackToSearchWithinSubject::Yes && + InputsAreEqual(type, Input(id_emailAddress))) { + if (referenceIDType == GeneralNameType::rfc822Name && + match == MatchResult::Match) { + // We already found a match; we don't need to match another one + return Success; + } + if (valueEncodingTag != der::IA5String) { + return Result::ERROR_BAD_DER; + } + return MatchPresentedIDWithReferenceID(GeneralNameType::rfc822Name, + presentedID, referenceIDType, + referenceID, match); + } + + return Success; +} + +void +MatchSubjectPresentedIDWithReferenceID(GeneralNameType presentedIDType, + Input presentedID, + GeneralNameType referenceIDType, + Input referenceID, + /*in/out*/ MatchResult& match) +{ + Result rv = MatchPresentedIDWithReferenceID(presentedIDType, presentedID, + referenceIDType, referenceID, + match); + if (rv != Success) { + match = MatchResult::Mismatch; + } +} + +Result +MatchPresentedIDWithReferenceID(GeneralNameType presentedIDType, + Input presentedID, + GeneralNameType referenceIDType, + Input referenceID, + /*out*/ MatchResult& matchResult) +{ + if (referenceIDType == GeneralNameType::nameConstraints) { + // matchResult is irrelevant when checking name constraints; only the + // pass/fail result of CheckPresentedIDConformsToConstraints matters. + return CheckPresentedIDConformsToConstraints(presentedIDType, presentedID, + referenceID); + } + + if (presentedIDType != referenceIDType) { + matchResult = MatchResult::Mismatch; + return Success; + } + + Result rv; + bool foundMatch; + + switch (referenceIDType) { + case GeneralNameType::dNSName: + rv = MatchPresentedDNSIDWithReferenceDNSID( + presentedID, AllowWildcards::Yes, + AllowDotlessSubdomainMatches::Yes, IDRole::ReferenceID, + referenceID, foundMatch); + break; + + case GeneralNameType::iPAddress: + foundMatch = InputsAreEqual(presentedID, referenceID); + rv = Success; + break; + + case GeneralNameType::rfc822Name: + rv = MatchPresentedRFC822NameWithReferenceRFC822Name( + presentedID, IDRole::ReferenceID, referenceID, foundMatch); + break; + + case GeneralNameType::directoryName: + // TODO: At some point, we may add APIs for matching DirectoryNames. + // fall through + + case GeneralNameType::otherName: // fall through + case GeneralNameType::x400Address: // fall through + case GeneralNameType::ediPartyName: // fall through + case GeneralNameType::uniformResourceIdentifier: // fall through + case GeneralNameType::registeredID: // fall through + case GeneralNameType::nameConstraints: + return NotReached("unexpected nameType for SearchType::Match", + Result::FATAL_ERROR_INVALID_ARGS); + + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM + } + + if (rv != Success) { + return rv; + } + matchResult = foundMatch ? MatchResult::Match : MatchResult::Mismatch; + return Success; +} + +enum class NameConstraintsSubtrees : uint8_t +{ + permittedSubtrees = der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0, + excludedSubtrees = der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 1 +}; + +Result CheckPresentedIDConformsToNameConstraintsSubtrees( + GeneralNameType presentedIDType, + Input presentedID, + Reader& nameConstraints, + NameConstraintsSubtrees subtreesType); +Result MatchPresentedIPAddressWithConstraint(Input presentedID, + Input iPAddressConstraint, + /*out*/ bool& foundMatch); +Result MatchPresentedDirectoryNameWithConstraint( + NameConstraintsSubtrees subtreesType, Input presentedID, + Input directoryNameConstraint, /*out*/ bool& matches); + +Result +CheckPresentedIDConformsToConstraints( + GeneralNameType presentedIDType, + Input presentedID, + Input encodedNameConstraints) +{ + // NameConstraints ::= SEQUENCE { + // permittedSubtrees [0] GeneralSubtrees OPTIONAL, + // excludedSubtrees [1] GeneralSubtrees OPTIONAL } + Reader nameConstraints; + Result rv = der::ExpectTagAndGetValueAtEnd(encodedNameConstraints, + der::SEQUENCE, nameConstraints); + if (rv != Success) { + return rv; + } + + // RFC 5280 says "Conforming CAs MUST NOT issue certificates where name + // constraints is an empty sequence. That is, either the permittedSubtrees + // field or the excludedSubtrees MUST be present." + if (nameConstraints.AtEnd()) { + return Result::ERROR_BAD_DER; + } + + rv = CheckPresentedIDConformsToNameConstraintsSubtrees( + presentedIDType, presentedID, nameConstraints, + NameConstraintsSubtrees::permittedSubtrees); + if (rv != Success) { + return rv; + } + + rv = CheckPresentedIDConformsToNameConstraintsSubtrees( + presentedIDType, presentedID, nameConstraints, + NameConstraintsSubtrees::excludedSubtrees); + if (rv != Success) { + return rv; + } + + return der::End(nameConstraints); +} + +Result +CheckPresentedIDConformsToNameConstraintsSubtrees( + GeneralNameType presentedIDType, + Input presentedID, + Reader& nameConstraints, + NameConstraintsSubtrees subtreesType) +{ + if (!nameConstraints.Peek(static_cast<uint8_t>(subtreesType))) { + return Success; + } + + Reader subtrees; + Result rv = der::ExpectTagAndGetValue(nameConstraints, + static_cast<uint8_t>(subtreesType), + subtrees); + if (rv != Success) { + return rv; + } + + bool hasPermittedSubtreesMatch = false; + bool hasPermittedSubtreesMismatch = false; + + // GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree + // + // do { ... } while(...) because subtrees isn't allowed to be empty. + do { + // GeneralSubtree ::= SEQUENCE { + // base GeneralName, + // minimum [0] BaseDistance DEFAULT 0, + // maximum [1] BaseDistance OPTIONAL } + Reader subtree; + rv = ExpectTagAndGetValue(subtrees, der::SEQUENCE, subtree); + if (rv != Success) { + return rv; + } + GeneralNameType nameConstraintType; + Input base; + rv = ReadGeneralName(subtree, nameConstraintType, base); + if (rv != Success) { + return rv; + } + // http://tools.ietf.org/html/rfc5280#section-4.2.1.10: "Within this + // profile, the minimum and maximum fields are not used with any name + // forms, thus, the minimum MUST be zero, and maximum MUST be absent." + // + // Since the default value isn't allowed to be encoded according to the DER + // encoding rules for DEFAULT, this is equivalent to saying that neither + // minimum or maximum must be encoded. + rv = der::End(subtree); + if (rv != Success) { + return rv; + } + + if (presentedIDType == nameConstraintType) { + bool matches; + + switch (presentedIDType) { + case GeneralNameType::dNSName: + rv = MatchPresentedDNSIDWithReferenceDNSID( + presentedID, AllowWildcards::Yes, + AllowDotlessSubdomainMatches::Yes, IDRole::NameConstraint, + base, matches); + if (rv != Success) { + return rv; + } + break; + + case GeneralNameType::iPAddress: + rv = MatchPresentedIPAddressWithConstraint(presentedID, base, + matches); + if (rv != Success) { + return rv; + } + break; + + case GeneralNameType::directoryName: + rv = MatchPresentedDirectoryNameWithConstraint(subtreesType, + presentedID, base, + matches); + if (rv != Success) { + return rv; + } + break; + + case GeneralNameType::rfc822Name: + rv = MatchPresentedRFC822NameWithReferenceRFC822Name( + presentedID, IDRole::NameConstraint, base, matches); + if (rv != Success) { + return rv; + } + break; + + // RFC 5280 says "Conforming CAs [...] SHOULD NOT impose name + // constraints on the x400Address, ediPartyName, or registeredID + // name forms. It also says "Applications conforming to this profile + // [...] SHOULD be able to process name constraints that are imposed + // on [...] uniformResourceIdentifier [...]", but we don't bother. + // + // TODO: Ask to have spec updated to say ""Conforming CAs [...] SHOULD + // NOT impose name constraints on the otherName, x400Address, + // ediPartyName, uniformResourceIdentifier, or registeredID name + // forms." + case GeneralNameType::otherName: // fall through + case GeneralNameType::x400Address: // fall through + case GeneralNameType::ediPartyName: // fall through + case GeneralNameType::uniformResourceIdentifier: // fall through + case GeneralNameType::registeredID: // fall through + return Result::ERROR_CERT_NOT_IN_NAME_SPACE; + + case GeneralNameType::nameConstraints: + return NotReached("invalid presentedIDType", + Result::FATAL_ERROR_LIBRARY_FAILURE); + + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM + } + + switch (subtreesType) { + case NameConstraintsSubtrees::permittedSubtrees: + if (matches) { + hasPermittedSubtreesMatch = true; + } else { + hasPermittedSubtreesMismatch = true; + } + break; + case NameConstraintsSubtrees::excludedSubtrees: + if (matches) { + return Result::ERROR_CERT_NOT_IN_NAME_SPACE; + } + break; + } + } + } while (!subtrees.AtEnd()); + + if (hasPermittedSubtreesMismatch && !hasPermittedSubtreesMatch) { + // If there was any entry of the given type in permittedSubtrees, then it + // required that at least one of them must match. Since none of them did, + // we have a failure. + return Result::ERROR_CERT_NOT_IN_NAME_SPACE; + } + + return Success; +} + +// We do not distinguish between a syntactically-invalid presentedDNSID and one +// that is syntactically valid but does not match referenceDNSID; in both +// cases, the result is false. +// +// We assume that both presentedDNSID and referenceDNSID are encoded in such a +// way that US-ASCII (7-bit) characters are encoded in one byte and no encoding +// of a non-US-ASCII character contains a code point in the range 0-127. For +// example, UTF-8 is OK but UTF-16 is not. +// +// RFC6125 says that a wildcard label may be of the form <x>*<y>.<DNSID>, where +// <x> and/or <y> may be empty. However, NSS requires <y> to be empty, and we +// follow NSS's stricter policy by accepting wildcards only of the form +// <x>*.<DNSID>, where <x> may be empty. +// +// An relative presented DNS ID matches both an absolute reference ID and a +// relative reference ID. Absolute presented DNS IDs are not supported: +// +// Presented ID Reference ID Result +// ------------------------------------- +// example.com example.com Match +// example.com. example.com Mismatch +// example.com example.com. Match +// example.com. example.com. Mismatch +// +// There are more subtleties documented inline in the code. +// +// Name constraints /////////////////////////////////////////////////////////// +// +// This is all RFC 5280 has to say about DNSName constraints: +// +// DNS name restrictions are expressed as host.example.com. Any DNS +// name that can be constructed by simply adding zero or more labels to +// the left-hand side of the name satisfies the name constraint. For +// example, www.host.example.com would satisfy the constraint but +// host1.example.com would not. +// +// This lack of specificity has lead to a lot of uncertainty regarding +// subdomain matching. In particular, the following questions have been +// raised and answered: +// +// Q: Does a presented identifier equal (case insensitive) to the name +// constraint match the constraint? For example, does the presented +// ID "host.example.com" match a "host.example.com" constraint? +// A: Yes. RFC5280 says "by simply adding zero or more labels" and this +// is the case of adding zero labels. +// +// Q: When the name constraint does not start with ".", do subdomain +// presented identifiers match it? For example, does the presented +// ID "www.host.example.com" match a "host.example.com" constraint? +// A: Yes. RFC5280 says "by simply adding zero or more labels" and this +// is the case of adding more than zero labels. The example is the +// one from RFC 5280. +// +// Q: When the name constraint does not start with ".", does a +// non-subdomain prefix match it? For example, does "bigfoo.bar.com" +// match "foo.bar.com"? [4] +// A: No. We interpret RFC 5280's language of "adding zero or more labels" +// to mean that whole labels must be prefixed. +// +// (Note that the above three scenarios are the same as the RFC 6265 +// domain matching rules [0].) +// +// Q: Is a name constraint that starts with "." valid, and if so, what +// semantics does it have? For example, does a presented ID of +// "www.example.com" match a constraint of ".example.com"? Does a +// presented ID of "example.com" match a constraint of ".example.com"? +// A: This implementation, NSS[1], and SChannel[2] all support a +// leading ".", but OpenSSL[3] does not yet. Amongst the +// implementations that support it, a leading "." is legal and means +// the same thing as when the "." is omitted, EXCEPT that a +// presented identifier equal (case insensitive) to the name +// constraint is not matched; i.e. presented DNSName identifiers +// must be subdomains. Some CAs in Mozilla's CA program (e.g. HARICA) +// have name constraints with the leading "." in their root +// certificates. The name constraints imposed on DCISS by Mozilla also +// have the it, so supporting this is a requirement for backward +// compatibility, even if it is not yet standardized. So, for example, a +// presented ID of "www.example.com" matches a constraint of +// ".example.com" but a presented ID of "example.com" does not. +// +// Q: Is there a way to prevent subdomain matches? +// A: Yes. +// +// Some people have proposed that dNSName constraints that do not +// start with a "." should be restricted to exact (case insensitive) +// matches. However, such a change of semantics from what RFC5280 +// specifies would be a non-backward-compatible change in the case of +// permittedSubtrees constraints, and it would be a security issue for +// excludedSubtrees constraints. +// +// However, it can be done with a combination of permittedSubtrees and +// excludedSubtrees, e.g. "example.com" in permittedSubtrees and +// ".example.com" in excudedSubtrees. +// +// Q: Are name constraints allowed to be specified as absolute names? +// For example, does a presented ID of "example.com" match a name +// constraint of "example.com." and vice versa. +// A: Absolute names are not supported as presented IDs or name +// constraints. Only reference IDs may be absolute. +// +// Q: Is "" a valid DNSName constraints? If so, what does it mean? +// A: Yes. Any valid presented DNSName can be formed "by simply adding zero +// or more labels to the left-hand side" of "". In particular, an +// excludedSubtrees DNSName constraint of "" forbids all DNSNames. +// +// Q: Is "." a valid DNSName constraints? If so, what does it mean? +// A: No, because absolute names are not allowed (see above). +// +// [0] RFC 6265 (Cookies) Domain Matching rules: +// http://tools.ietf.org/html/rfc6265#section-5.1.3 +// [1] NSS source code: +// https://mxr.mozilla.org/nss/source/lib/certdb/genname.c?rev=2a7348f013cb#1209 +// [2] Description of SChannel's behavior from Microsoft: +// http://www.imc.org/ietf-pkix/mail-archive/msg04668.html +// [3] Proposal to add such support to OpenSSL: +// http://www.mail-archive.com/openssl-dev%40openssl.org/msg36204.html +// https://rt.openssl.org/Ticket/Display.html?id=3562 +// [4] Feedback on the lack of clarify in the definition that never got +// incorporated into the spec: +// https://www.ietf.org/mail-archive/web/pkix/current/msg21192.html +Result +MatchPresentedDNSIDWithReferenceDNSID( + Input presentedDNSID, + AllowWildcards allowWildcards, + AllowDotlessSubdomainMatches allowDotlessSubdomainMatches, + IDRole referenceDNSIDRole, + Input referenceDNSID, + /*out*/ bool& matches) +{ + if (!IsValidDNSID(presentedDNSID, IDRole::PresentedID, allowWildcards)) { + return Result::ERROR_BAD_DER; + } + + if (!IsValidDNSID(referenceDNSID, referenceDNSIDRole, AllowWildcards::No)) { + return Result::ERROR_BAD_DER; + } + + Reader presented(presentedDNSID); + Reader reference(referenceDNSID); + + switch (referenceDNSIDRole) + { + case IDRole::ReferenceID: + break; + + case IDRole::NameConstraint: + { + if (presentedDNSID.GetLength() > referenceDNSID.GetLength()) { + if (referenceDNSID.GetLength() == 0) { + // An empty constraint matches everything. + matches = true; + return Success; + } + // If the reference ID starts with a dot then skip the prefix of + // of the presented ID and start the comparison at the position of that + // dot. Examples: + // + // Matches Doesn't Match + // ----------------------------------------------------------- + // original presented ID: www.example.com badexample.com + // skipped: www ba + // presented ID w/o prefix: .example.com dexample.com + // reference ID: .example.com .example.com + // + // If the reference ID does not start with a dot then we skip the + // prefix of the presented ID but also verify that the prefix ends with + // a dot. Examples: + // + // Matches Doesn't Match + // ----------------------------------------------------------- + // original presented ID: www.example.com badexample.com + // skipped: www ba + // must be '.': . d + // presented ID w/o prefix: example.com example.com + // reference ID: example.com example.com + // + if (reference.Peek('.')) { + if (presented.Skip(static_cast<Input::size_type>( + presentedDNSID.GetLength() - + referenceDNSID.GetLength())) != Success) { + return NotReached("skipping subdomain failed", + Result::FATAL_ERROR_LIBRARY_FAILURE); + } + } else if (allowDotlessSubdomainMatches == + AllowDotlessSubdomainMatches::Yes) { + if (presented.Skip(static_cast<Input::size_type>( + presentedDNSID.GetLength() - + referenceDNSID.GetLength() - 1)) != Success) { + return NotReached("skipping subdomains failed", + Result::FATAL_ERROR_LIBRARY_FAILURE); + } + uint8_t b; + if (presented.Read(b) != Success) { + return NotReached("reading from presentedDNSID failed", + Result::FATAL_ERROR_LIBRARY_FAILURE); + } + if (b != '.') { + matches = false; + return Success; + } + } + } + break; + } + + case IDRole::PresentedID: // fall through + return NotReached("IDRole::PresentedID is not a valid referenceDNSIDRole", + Result::FATAL_ERROR_INVALID_ARGS); + } + + // We only allow wildcard labels that consist only of '*'. + if (presented.Peek('*')) { + if (presented.Skip(1) != Success) { + return NotReached("Skipping '*' failed", + Result::FATAL_ERROR_LIBRARY_FAILURE); + } + do { + // This will happen if reference is a single, relative label + if (reference.AtEnd()) { + matches = false; + return Success; + } + uint8_t referenceByte; + if (reference.Read(referenceByte) != Success) { + return NotReached("invalid reference ID", + Result::FATAL_ERROR_INVALID_ARGS); + } + } while (!reference.Peek('.')); + } + + for (;;) { + uint8_t presentedByte; + if (presented.Read(presentedByte) != Success) { + matches = false; + return Success; + } + uint8_t referenceByte; + if (reference.Read(referenceByte) != Success) { + matches = false; + return Success; + } + if (LocaleInsensitveToLower(presentedByte) != + LocaleInsensitveToLower(referenceByte)) { + matches = false; + return Success; + } + if (presented.AtEnd()) { + // Don't allow presented IDs to be absolute. + if (presentedByte == '.') { + return Result::ERROR_BAD_DER; + } + break; + } + } + + // Allow a relative presented DNS ID to match an absolute reference DNS ID, + // unless we're matching a name constraint. + if (!reference.AtEnd()) { + if (referenceDNSIDRole != IDRole::NameConstraint) { + uint8_t referenceByte; + if (reference.Read(referenceByte) != Success) { + return NotReached("read failed but not at end", + Result::FATAL_ERROR_LIBRARY_FAILURE); + } + if (referenceByte != '.') { + matches = false; + return Success; + } + } + if (!reference.AtEnd()) { + matches = false; + return Success; + } + } + + matches = true; + return Success; +} + +// https://tools.ietf.org/html/rfc5280#section-4.2.1.10 says: +// +// For IPv4 addresses, the iPAddress field of GeneralName MUST contain +// eight (8) octets, encoded in the style of RFC 4632 (CIDR) to represent +// an address range [RFC4632]. For IPv6 addresses, the iPAddress field +// MUST contain 32 octets similarly encoded. For example, a name +// constraint for "class C" subnet 192.0.2.0 is represented as the +// octets C0 00 02 00 FF FF FF 00, representing the CIDR notation +// 192.0.2.0/24 (mask 255.255.255.0). +Result +MatchPresentedIPAddressWithConstraint(Input presentedID, + Input iPAddressConstraint, + /*out*/ bool& foundMatch) +{ + if (presentedID.GetLength() != 4 && presentedID.GetLength() != 16) { + return Result::ERROR_BAD_DER; + } + if (iPAddressConstraint.GetLength() != 8 && + iPAddressConstraint.GetLength() != 32) { + return Result::ERROR_BAD_DER; + } + + // an IPv4 address never matches an IPv6 constraint, and vice versa. + if (presentedID.GetLength() * 2 != iPAddressConstraint.GetLength()) { + foundMatch = false; + return Success; + } + + Reader constraint(iPAddressConstraint); + Reader constraintAddress; + Result rv = constraint.Skip(iPAddressConstraint.GetLength() / 2u, + constraintAddress); + if (rv != Success) { + return rv; + } + Reader constraintMask; + rv = constraint.Skip(iPAddressConstraint.GetLength() / 2u, constraintMask); + if (rv != Success) { + return rv; + } + rv = der::End(constraint); + if (rv != Success) { + return rv; + } + + Reader presented(presentedID); + do { + uint8_t presentedByte; + rv = presented.Read(presentedByte); + if (rv != Success) { + return rv; + } + uint8_t constraintAddressByte; + rv = constraintAddress.Read(constraintAddressByte); + if (rv != Success) { + return rv; + } + uint8_t constraintMaskByte; + rv = constraintMask.Read(constraintMaskByte); + if (rv != Success) { + return rv; + } + foundMatch = + ((presentedByte ^ constraintAddressByte) & constraintMaskByte) == 0; + } while (foundMatch && !presented.AtEnd()); + + return Success; +} + +// AttributeTypeAndValue ::= SEQUENCE { +// type AttributeType, +// value AttributeValue } +// +// AttributeType ::= OBJECT IDENTIFIER +// +// AttributeValue ::= ANY -- DEFINED BY AttributeType +Result +ReadAVA(Reader& rdn, + /*out*/ Input& type, + /*out*/ uint8_t& valueTag, + /*out*/ Input& value) +{ + return der::Nested(rdn, der::SEQUENCE, [&](Reader& ava) -> Result { + Result rv = der::ExpectTagAndGetValue(ava, der::OIDTag, type); + if (rv != Success) { + return rv; + } + rv = der::ReadTagAndGetValue(ava, valueTag, value); + if (rv != Success) { + return rv; + } + return Success; + }); +} + +// Names are sequences of RDNs. RDNS are sets of AVAs. That means that RDNs are +// unordered, so in theory we should match RDNs with equivalent AVAs that are +// in different orders. Within the AVAs are DirectoryNames that are supposed to +// be compared according to LDAP stringprep normalization rules (e.g. +// normalizing whitespace), consideration of different character encodings, +// etc. Indeed, RFC 5280 says we MUST deal with all of that. +// +// In practice, many implementations, including NSS, only match Names in a way +// that only meets a subset of the requirements of RFC 5280. Those +// normalization and character encoding conversion steps appear to be +// unnecessary for processing real-world certificates, based on experience from +// having used NSS in Firefox for many years. +// +// RFC 5280 also says "CAs issuing certificates with a restriction of the form +// directoryName SHOULD NOT rely on implementation of the full +// ISO DN name comparison algorithm. This implies name restrictions MUST +// be stated identically to the encoding used in the subject field or +// subjectAltName extension." It goes on to say, in the security +// considerations: +// +// In addition, name constraints for distinguished names MUST be stated +// identically to the encoding used in the subject field or +// subjectAltName extension. If not, then name constraints stated as +// excludedSubtrees will not match and invalid paths will be accepted +// and name constraints expressed as permittedSubtrees will not match +// and valid paths will be rejected. To avoid acceptance of invalid +// paths, CAs SHOULD state name constraints for distinguished names as +// permittedSubtrees wherever possible. +// +// For permittedSubtrees, the MUST-level requirement is relaxed for +// compatibility in the case of PrintableString and UTF8String. That is, if a +// name constraint has been encoded using UTF8String and the presented ID has +// been encoded with a PrintableString (or vice-versa), they are considered to +// match if they are equal everywhere except for the tag identifying the +// encoding. See bug 1150114. +// +// For excludedSubtrees, we simply prohibit any non-empty directoryName +// constraint to ensure we are not being too lenient. We support empty +// DirectoryName constraints in excludedSubtrees so that a CA can say "Do not +// allow any DirectoryNames in issued certificates." +Result +MatchPresentedDirectoryNameWithConstraint(NameConstraintsSubtrees subtreesType, + Input presentedID, + Input directoryNameConstraint, + /*out*/ bool& matches) +{ + Reader constraintRDNs; + Result rv = der::ExpectTagAndGetValueAtEnd(directoryNameConstraint, + der::SEQUENCE, constraintRDNs); + if (rv != Success) { + return rv; + } + Reader presentedRDNs; + rv = der::ExpectTagAndGetValueAtEnd(presentedID, der::SEQUENCE, + presentedRDNs); + if (rv != Success) { + return rv; + } + + switch (subtreesType) { + case NameConstraintsSubtrees::permittedSubtrees: + break; // dealt with below + case NameConstraintsSubtrees::excludedSubtrees: + if (!constraintRDNs.AtEnd() || !presentedRDNs.AtEnd()) { + return Result::ERROR_CERT_NOT_IN_NAME_SPACE; + } + matches = true; + return Success; + } + + for (;;) { + // The AVAs have to be fully equal, but the constraint RDNs just need to be + // a prefix of the presented RDNs. + if (constraintRDNs.AtEnd()) { + matches = true; + return Success; + } + if (presentedRDNs.AtEnd()) { + matches = false; + return Success; + } + Reader constraintRDN; + rv = der::ExpectTagAndGetValue(constraintRDNs, der::SET, constraintRDN); + if (rv != Success) { + return rv; + } + Reader presentedRDN; + rv = der::ExpectTagAndGetValue(presentedRDNs, der::SET, presentedRDN); + if (rv != Success) { + return rv; + } + while (!constraintRDN.AtEnd() && !presentedRDN.AtEnd()) { + Input constraintType; + uint8_t constraintValueTag; + Input constraintValue; + rv = ReadAVA(constraintRDN, constraintType, constraintValueTag, + constraintValue); + if (rv != Success) { + return rv; + } + Input presentedType; + uint8_t presentedValueTag; + Input presentedValue; + rv = ReadAVA(presentedRDN, presentedType, presentedValueTag, + presentedValue); + if (rv != Success) { + return rv; + } + // TODO (bug 1155767): verify that if an AVA is a PrintableString it + // consists only of characters valid for PrintableStrings. + bool avasMatch = + InputsAreEqual(constraintType, presentedType) && + InputsAreEqual(constraintValue, presentedValue) && + (constraintValueTag == presentedValueTag || + (constraintValueTag == der::Tag::UTF8String && + presentedValueTag == der::Tag::PrintableString) || + (constraintValueTag == der::Tag::PrintableString && + presentedValueTag == der::Tag::UTF8String)); + if (!avasMatch) { + matches = false; + return Success; + } + } + if (!constraintRDN.AtEnd() || !presentedRDN.AtEnd()) { + matches = false; + return Success; + } + } +} + +// RFC 5280 says: +// +// The format of an rfc822Name is a "Mailbox" as defined in Section 4.1.2 +// of [RFC2821]. A Mailbox has the form "Local-part@Domain". Note that a +// Mailbox has no phrase (such as a common name) before it, has no comment +// (text surrounded in parentheses) after it, and is not surrounded by "<" +// and ">". Rules for encoding Internet mail addresses that include +// internationalized domain names are specified in Section 7.5. +// +// and: +// +// A name constraint for Internet mail addresses MAY specify a +// particular mailbox, all addresses at a particular host, or all +// mailboxes in a domain. To indicate a particular mailbox, the +// constraint is the complete mail address. For example, +// "root@example.com" indicates the root mailbox on the host +// "example.com". To indicate all Internet mail addresses on a +// particular host, the constraint is specified as the host name. For +// example, the constraint "example.com" is satisfied by any mail +// address at the host "example.com". To specify any address within a +// domain, the constraint is specified with a leading period (as with +// URIs). For example, ".example.com" indicates all the Internet mail +// addresses in the domain "example.com", but not Internet mail +// addresses on the host "example.com". + +bool +IsValidRFC822Name(Input input) +{ + Reader reader(input); + + // Local-part@. + bool startOfAtom = true; + for (;;) { + uint8_t presentedByte; + if (reader.Read(presentedByte) != Success) { + return false; + } + switch (presentedByte) { + // atext is defined in https://tools.ietf.org/html/rfc2822#section-3.2.4 + case 'A': case 'a': case 'N': case 'n': case '0': case '!': case '#': + case 'B': case 'b': case 'O': case 'o': case '1': case '$': case '%': + case 'C': case 'c': case 'P': case 'p': case '2': case '&': case '\'': + case 'D': case 'd': case 'Q': case 'q': case '3': case '*': case '+': + case 'E': case 'e': case 'R': case 'r': case '4': case '-': case '/': + case 'F': case 'f': case 'S': case 's': case '5': case '=': case '?': + case 'G': case 'g': case 'T': case 't': case '6': case '^': case '_': + case 'H': case 'h': case 'U': case 'u': case '7': case '`': case '{': + case 'I': case 'i': case 'V': case 'v': case '8': case '|': case '}': + case 'J': case 'j': case 'W': case 'w': case '9': case '~': + case 'K': case 'k': case 'X': case 'x': + case 'L': case 'l': case 'Y': case 'y': + case 'M': case 'm': case 'Z': case 'z': + startOfAtom = false; + break; + + case '.': + if (startOfAtom) { + return false; + } + startOfAtom = true; + break; + + case '@': + { + if (startOfAtom) { + return false; + } + Input domain; + if (reader.SkipToEnd(domain) != Success) { + return false; + } + return IsValidDNSID(domain, IDRole::PresentedID, AllowWildcards::No); + } + + default: + return false; + } + } +} + +Result +MatchPresentedRFC822NameWithReferenceRFC822Name(Input presentedRFC822Name, + IDRole referenceRFC822NameRole, + Input referenceRFC822Name, + /*out*/ bool& matches) +{ + if (!IsValidRFC822Name(presentedRFC822Name)) { + return Result::ERROR_BAD_DER; + } + Reader presented(presentedRFC822Name); + + switch (referenceRFC822NameRole) + { + case IDRole::PresentedID: + return Result::FATAL_ERROR_INVALID_ARGS; + + case IDRole::ReferenceID: + break; + + case IDRole::NameConstraint: + { + if (InputContains(referenceRFC822Name, '@')) { + // The constraint is of the form "Local-part@Domain". + break; + } + + // The constraint is of the form "example.com" or ".example.com". + + // Skip past the '@' in the presented ID. + for (;;) { + uint8_t presentedByte; + if (presented.Read(presentedByte) != Success) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + if (presentedByte == '@') { + break; + } + } + + Input presentedDNSID; + if (presented.SkipToEnd(presentedDNSID) != Success) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + return MatchPresentedDNSIDWithReferenceDNSID( + presentedDNSID, AllowWildcards::No, + AllowDotlessSubdomainMatches::No, IDRole::NameConstraint, + referenceRFC822Name, matches); + } + } + + if (!IsValidRFC822Name(referenceRFC822Name)) { + return Result::ERROR_BAD_DER; + } + + Reader reference(referenceRFC822Name); + + for (;;) { + uint8_t presentedByte; + if (presented.Read(presentedByte) != Success) { + matches = reference.AtEnd(); + return Success; + } + uint8_t referenceByte; + if (reference.Read(referenceByte) != Success) { + matches = false; + return Success; + } + if (LocaleInsensitveToLower(presentedByte) != + LocaleInsensitveToLower(referenceByte)) { + matches = false; + return Success; + } + } +} + +// We avoid isdigit because it is locale-sensitive. See +// http://pubs.opengroup.org/onlinepubs/009695399/functions/tolower.html. +inline uint8_t +LocaleInsensitveToLower(uint8_t a) +{ + if (a >= 'A' && a <= 'Z') { // unlikely + return static_cast<uint8_t>( + static_cast<uint8_t>(a - static_cast<uint8_t>('A')) + + static_cast<uint8_t>('a')); + } + return a; +} + +bool +StartsWithIDNALabel(Input id) +{ + static const uint8_t IDN_ALABEL_PREFIX[4] = { 'x', 'n', '-', '-' }; + Reader input(id); + for (const uint8_t prefixByte : IDN_ALABEL_PREFIX) { + uint8_t b; + if (input.Read(b) != Success) { + return false; + } + if (b != prefixByte) { + return false; + } + } + return true; +} + +bool +ReadIPv4AddressComponent(Reader& input, bool lastComponent, + /*out*/ uint8_t& valueOut) +{ + size_t length = 0; + unsigned int value = 0; // Must be larger than uint8_t. + + for (;;) { + if (input.AtEnd() && lastComponent) { + break; + } + + uint8_t b; + if (input.Read(b) != Success) { + return false; + } + + if (b >= '0' && b <= '9') { + if (value == 0 && length > 0) { + return false; // Leading zeros are not allowed. + } + value = (value * 10) + (b - '0'); + if (value > 255) { + return false; // Component's value is too large. + } + ++length; + } else if (!lastComponent && b == '.') { + break; + } else { + return false; // Invalid character. + } + } + + if (length == 0) { + return false; // empty components not allowed + } + + valueOut = static_cast<uint8_t>(value); + return true; +} + +} // namespace + +// On Windows and maybe other platforms, OS-provided IP address parsing +// functions might fail if the protocol (IPv4 or IPv6) has been disabled, so we +// can't rely on them. +bool +ParseIPv4Address(Input hostname, /*out*/ uint8_t (&out)[4]) +{ + Reader input(hostname); + return ReadIPv4AddressComponent(input, false, out[0]) && + ReadIPv4AddressComponent(input, false, out[1]) && + ReadIPv4AddressComponent(input, false, out[2]) && + ReadIPv4AddressComponent(input, true, out[3]); +} + +namespace { + +bool +FinishIPv6Address(/*in/out*/ uint8_t (&address)[16], int numComponents, + int contractionIndex) +{ + assert(numComponents >= 0); + assert(numComponents <= 8); + assert(contractionIndex >= -1); + assert(contractionIndex <= 8); + assert(contractionIndex <= numComponents); + if (!(numComponents >= 0 && + numComponents <= 8 && + contractionIndex >= -1 && + contractionIndex <= 8 && + contractionIndex <= numComponents)) { + return false; + } + + if (contractionIndex == -1) { + // no contraction + return numComponents == 8; + } + + if (numComponents >= 8) { + return false; // no room left to expand the contraction. + } + + // Shift components that occur after the contraction over. + std::copy_backward(address + (2u * static_cast<size_t>(contractionIndex)), + address + (2u * static_cast<size_t>(numComponents)), + address + (2u * 8u)); + // Fill in the contracted area with zeros. + std::fill_n(address + 2u * static_cast<size_t>(contractionIndex), + (8u - static_cast<size_t>(numComponents)) * 2u, static_cast<uint8_t>(0u)); + + return true; +} + +} // namespace + +// On Windows and maybe other platforms, OS-provided IP address parsing +// functions might fail if the protocol (IPv4 or IPv6) has been disabled, so we +// can't rely on them. +bool +ParseIPv6Address(Input hostname, /*out*/ uint8_t (&out)[16]) +{ + Reader input(hostname); + + int currentComponentIndex = 0; + int contractionIndex = -1; + + if (input.Peek(':')) { + // A valid input can only start with ':' if there is a contraction at the + // beginning. + uint8_t b; + if (input.Read(b) != Success || b != ':') { + assert(false); + return false; + } + if (input.Read(b) != Success) { + return false; + } + if (b != ':') { + return false; + } + contractionIndex = 0; + } + + for (;;) { + // If we encounter a '.' then we'll have to backtrack to parse the input + // from startOfComponent to the end of the input as an IPv4 address. + Reader::Mark startOfComponent(input.GetMark()); + uint16_t componentValue = 0; + size_t componentLength = 0; + while (!input.AtEnd() && !input.Peek(':')) { + uint8_t value; + uint8_t b; + if (input.Read(b) != Success) { + assert(false); + return false; + } + switch (b) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + value = static_cast<uint8_t>(b - static_cast<uint8_t>('0')); + break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + value = static_cast<uint8_t>(b - static_cast<uint8_t>('a') + + UINT8_C(10)); + break; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + value = static_cast<uint8_t>(b - static_cast<uint8_t>('A') + + UINT8_C(10)); + break; + case '.': + { + // A dot indicates we hit a IPv4-syntax component. Backtrack, parsing + // the input from startOfComponent to the end of the input as an IPv4 + // address, and then combine it with the other components. + + if (currentComponentIndex > 6) { + return false; // Too many components before the IPv4 component + } + + input.SkipToEnd(); + Input ipv4Component; + if (input.GetInput(startOfComponent, ipv4Component) != Success) { + return false; + } + uint8_t (*ipv4)[4] = + reinterpret_cast<uint8_t(*)[4]>(&out[2 * currentComponentIndex]); + if (!ParseIPv4Address(ipv4Component, *ipv4)) { + return false; + } + assert(input.AtEnd()); + currentComponentIndex += 2; + + return FinishIPv6Address(out, currentComponentIndex, + contractionIndex); + } + default: + return false; + } + if (componentLength >= 4) { + // component too long + return false; + } + ++componentLength; + componentValue = (componentValue * 0x10u) + value; + } + + if (currentComponentIndex >= 8) { + return false; // too many components + } + + if (componentLength == 0) { + if (input.AtEnd() && currentComponentIndex == contractionIndex) { + if (contractionIndex == 0) { + // don't accept "::" + return false; + } + return FinishIPv6Address(out, currentComponentIndex, + contractionIndex); + } + return false; + } + + out[2 * currentComponentIndex] = + static_cast<uint8_t>(componentValue / 0x100); + out[(2 * currentComponentIndex) + 1] = + static_cast<uint8_t>(componentValue % 0x100); + + ++currentComponentIndex; + + if (input.AtEnd()) { + return FinishIPv6Address(out, currentComponentIndex, + contractionIndex); + } + + uint8_t b; + if (input.Read(b) != Success || b != ':') { + assert(false); + return false; + } + + if (input.Peek(':')) { + // Contraction + if (contractionIndex != -1) { + return false; // multiple contractions are not allowed. + } + if (input.Read(b) != Success || b != ':') { + assert(false); + return false; + } + contractionIndex = currentComponentIndex; + if (input.AtEnd()) { + // "::" at the end of the input. + return FinishIPv6Address(out, currentComponentIndex, + contractionIndex); + } + } + } +} + +bool +IsValidReferenceDNSID(Input hostname) +{ + return IsValidDNSID(hostname, IDRole::ReferenceID, AllowWildcards::No); +} + +bool +IsValidPresentedDNSID(Input hostname) +{ + return IsValidDNSID(hostname, IDRole::PresentedID, AllowWildcards::Yes); +} + +namespace { + +// RFC 5280 Section 4.2.1.6 says that a dNSName "MUST be in the 'preferred name +// syntax', as specified by Section 3.5 of [RFC1034] and as modified by Section +// 2.1 of [RFC1123]" except "a dNSName of ' ' MUST NOT be used." Additionally, +// we allow underscores for compatibility with existing practice. +bool +IsValidDNSID(Input hostname, IDRole idRole, AllowWildcards allowWildcards) +{ + if (hostname.GetLength() > 253) { + return false; + } + + Reader input(hostname); + + if (idRole == IDRole::NameConstraint && input.AtEnd()) { + return true; + } + + size_t dotCount = 0; + size_t labelLength = 0; + bool labelIsAllNumeric = false; + bool labelEndsWithHyphen = false; + + // Only presented IDs are allowed to have wildcard labels. And, like + // Chromium, be stricter than RFC 6125 requires by insisting that a + // wildcard label consist only of '*'. + bool isWildcard = allowWildcards == AllowWildcards::Yes && input.Peek('*'); + bool isFirstByte = !isWildcard; + if (isWildcard) { + Result rv = input.Skip(1); + if (rv != Success) { + assert(false); + return false; + } + + uint8_t b; + rv = input.Read(b); + if (rv != Success) { + return false; + } + if (b != '.') { + return false; + } + ++dotCount; + } + + do { + static const size_t MAX_LABEL_LENGTH = 63; + + uint8_t b; + if (input.Read(b) != Success) { + return false; + } + switch (b) { + case '-': + if (labelLength == 0) { + return false; // Labels must not start with a hyphen. + } + labelIsAllNumeric = false; + labelEndsWithHyphen = true; + ++labelLength; + if (labelLength > MAX_LABEL_LENGTH) { + return false; + } + break; + + // We avoid isdigit because it is locale-sensitive. See + // http://pubs.opengroup.org/onlinepubs/009695399/functions/isdigit.html + case '0': case '5': + case '1': case '6': + case '2': case '7': + case '3': case '8': + case '4': case '9': + if (labelLength == 0) { + labelIsAllNumeric = true; + } + labelEndsWithHyphen = false; + ++labelLength; + if (labelLength > MAX_LABEL_LENGTH) { + return false; + } + break; + + // We avoid using islower/isupper/tolower/toupper or similar things, to + // avoid any possibility of this code being locale-sensitive. See + // http://pubs.opengroup.org/onlinepubs/009695399/functions/isupper.html + case 'a': case 'A': case 'n': case 'N': + case 'b': case 'B': case 'o': case 'O': + case 'c': case 'C': case 'p': case 'P': + case 'd': case 'D': case 'q': case 'Q': + case 'e': case 'E': case 'r': case 'R': + case 'f': case 'F': case 's': case 'S': + case 'g': case 'G': case 't': case 'T': + case 'h': case 'H': case 'u': case 'U': + case 'i': case 'I': case 'v': case 'V': + case 'j': case 'J': case 'w': case 'W': + case 'k': case 'K': case 'x': case 'X': + case 'l': case 'L': case 'y': case 'Y': + case 'm': case 'M': case 'z': case 'Z': + // We allow underscores for compatibility with existing practices. + // See bug 1136616. + case '_': + labelIsAllNumeric = false; + labelEndsWithHyphen = false; + ++labelLength; + if (labelLength > MAX_LABEL_LENGTH) { + return false; + } + break; + + case '.': + ++dotCount; + if (labelLength == 0 && + (idRole != IDRole::NameConstraint || !isFirstByte)) { + return false; + } + if (labelEndsWithHyphen) { + return false; // Labels must not end with a hyphen. + } + labelLength = 0; + break; + + default: + return false; // Invalid character. + } + isFirstByte = false; + } while (!input.AtEnd()); + + // Only reference IDs, not presented IDs or name constraints, may be + // absolute. + if (labelLength == 0 && idRole != IDRole::ReferenceID) { + return false; + } + + if (labelEndsWithHyphen) { + return false; // Labels must not end with a hyphen. + } + + if (labelIsAllNumeric) { + return false; // Last label must not be all numeric. + } + + if (isWildcard) { + // If the DNS ID ends with a dot, the last dot signifies an absolute ID. + size_t labelCount = (labelLength == 0) ? dotCount : (dotCount + 1); + + // Like NSS, require at least two labels to follow the wildcard label. + // + // TODO(bug XXXXXXX): Allow the TrustDomain to control this on a + // per-eTLD+1 basis, similar to Chromium. Even then, it might be better to + // still enforce that there are at least two labels after the wildcard. + if (labelCount < 3) { + return false; + } + // XXX: RFC6125 says that we shouldn't accept wildcards within an IDN + // A-Label. The consequence of this is that we effectively discriminate + // against users of languages that cannot be encoded with ASCII. + if (StartsWithIDNALabel(hostname)) { + return false; + } + + // TODO(bug XXXXXXX): Wildcards are not allowed for EV certificates. + // Provide an option to indicate whether wildcards should be matched, for + // the purpose of helping the application enforce this. + } + + return true; +} + +} // namespace + +} } // namespace mozilla::pkix diff --git a/security/nss/lib/mozpkix/lib/pkixnss.cpp b/security/nss/lib/mozpkix/lib/pkixnss.cpp new file mode 100644 index 000000000..9b293d5fd --- /dev/null +++ b/security/nss/lib/mozpkix/lib/pkixnss.cpp @@ -0,0 +1,236 @@ +/*- *- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mozpkix/pkixnss.h" + +#include <limits> + +#include "cryptohi.h" +#include "keyhi.h" +#include "pk11pub.h" +#include "mozpkix/nss_scoped_ptrs.h" +#include "mozpkix/pkix.h" +#include "mozpkix/pkixutil.h" +#include "secerr.h" +#include "sslerr.h" + +namespace mozilla { namespace pkix { + +namespace { + +Result +VerifySignedDigest(const SignedDigest& sd, + Input subjectPublicKeyInfo, + SECOidTag pubKeyAlg, + void* pkcs11PinArg) +{ + SECOidTag digestAlg; + switch (sd.digestAlgorithm) { + case DigestAlgorithm::sha512: digestAlg = SEC_OID_SHA512; break; + case DigestAlgorithm::sha384: digestAlg = SEC_OID_SHA384; break; + case DigestAlgorithm::sha256: digestAlg = SEC_OID_SHA256; break; + case DigestAlgorithm::sha1: digestAlg = SEC_OID_SHA1; break; + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM + } + + SECItem subjectPublicKeyInfoSECItem = + UnsafeMapInputToSECItem(subjectPublicKeyInfo); + ScopedCERTSubjectPublicKeyInfo + spki(SECKEY_DecodeDERSubjectPublicKeyInfo(&subjectPublicKeyInfoSECItem)); + if (!spki) { + return MapPRErrorCodeToResult(PR_GetError()); + } + ScopedSECKEYPublicKey + pubKey(SECKEY_ExtractPublicKey(spki.get())); + if (!pubKey) { + return MapPRErrorCodeToResult(PR_GetError()); + } + + SECItem digestSECItem(UnsafeMapInputToSECItem(sd.digest)); + SECItem signatureSECItem(UnsafeMapInputToSECItem(sd.signature)); + SECStatus srv = VFY_VerifyDigestDirect(&digestSECItem, pubKey.get(), + &signatureSECItem, pubKeyAlg, + digestAlg, pkcs11PinArg); + if (srv != SECSuccess) { + return MapPRErrorCodeToResult(PR_GetError()); + } + + return Success; +} + +} // namespace + +Result +VerifyRSAPKCS1SignedDigestNSS(const SignedDigest& sd, + Input subjectPublicKeyInfo, + void* pkcs11PinArg) +{ + return VerifySignedDigest(sd, subjectPublicKeyInfo, + SEC_OID_PKCS1_RSA_ENCRYPTION, pkcs11PinArg); +} + +Result +VerifyECDSASignedDigestNSS(const SignedDigest& sd, + Input subjectPublicKeyInfo, + void* pkcs11PinArg) +{ + return VerifySignedDigest(sd, subjectPublicKeyInfo, + SEC_OID_ANSIX962_EC_PUBLIC_KEY, pkcs11PinArg); +} + +Result +DigestBufNSS(Input item, + DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, + size_t digestBufLen) +{ + SECOidTag oid; + size_t bits; + switch (digestAlg) { + case DigestAlgorithm::sha512: oid = SEC_OID_SHA512; bits = 512; break; + case DigestAlgorithm::sha384: oid = SEC_OID_SHA384; bits = 384; break; + case DigestAlgorithm::sha256: oid = SEC_OID_SHA256; bits = 256; break; + case DigestAlgorithm::sha1: oid = SEC_OID_SHA1; bits = 160; break; + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM + } + if (digestBufLen != bits / 8) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + + SECItem itemSECItem = UnsafeMapInputToSECItem(item); + if (itemSECItem.len > + static_cast<decltype(itemSECItem.len)>( + std::numeric_limits<int32_t>::max())) { + PR_NOT_REACHED("large items should not be possible here"); + return Result::FATAL_ERROR_INVALID_ARGS; + } + SECStatus srv = PK11_HashBuf(oid, digestBuf, itemSECItem.data, + static_cast<int32_t>(itemSECItem.len)); + if (srv != SECSuccess) { + return MapPRErrorCodeToResult(PR_GetError()); + } + return Success; +} + +Result +MapPRErrorCodeToResult(PRErrorCode error) +{ + switch (error) + { +#define MOZILLA_PKIX_MAP(mozilla_pkix_result, value, nss_result) \ + case nss_result: return Result::mozilla_pkix_result; + + MOZILLA_PKIX_MAP_LIST + +#undef MOZILLA_PKIX_MAP + + default: + return Result::ERROR_UNKNOWN_ERROR; + } +} + +PRErrorCode +MapResultToPRErrorCode(Result result) +{ + switch (result) + { +#define MOZILLA_PKIX_MAP(mozilla_pkix_result, value, nss_result) \ + case Result::mozilla_pkix_result: return nss_result; + + MOZILLA_PKIX_MAP_LIST + +#undef MOZILLA_PKIX_MAP + + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM + } +} + +void +RegisterErrorTable() +{ + // Note that these error strings are not localizable. + // When these strings change, update the localization information too. + static const PRErrorMessage ErrorTableText[] = { + { "MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE", + "The server uses key pinning (HPKP) but no trusted certificate chain " + "could be constructed that matches the pinset. Key pinning violations " + "cannot be overridden." }, + { "MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY", + "The server uses a certificate with a basic constraints extension " + "identifying it as a certificate authority. For a properly-issued " + "certificate, this should not be the case." }, + { "MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE", + "The server presented a certificate with a key size that is too small " + "to establish a secure connection." }, + { "MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA", + "An X.509 version 1 certificate that is not a trust anchor was used to " + "issue the server's certificate. X.509 version 1 certificates are " + "deprecated and should not be used to sign other certificates." }, + { "MOZILLA_PKIX_ERROR_NO_RFC822NAME_MATCH", + "The certificate is not valid for the given email address." }, + { "MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE", + "The server presented a certificate that is not yet valid." }, + { "MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE", + "A certificate that is not yet valid was used to issue the server's " + "certificate." }, + { "MOZILLA_PKIX_ERROR_SIGNATURE_ALGORITHM_MISMATCH", + "The signature algorithm in the signature field of the certificate does " + "not match the algorithm in its signatureAlgorithm field." }, + { "MOZILLA_PKIX_ERROR_OCSP_RESPONSE_FOR_CERT_MISSING", + "The OCSP response does not include a status for the certificate being " + "verified." }, + { "MOZILLA_PKIX_ERROR_VALIDITY_TOO_LONG", + "The server presented a certificate that is valid for too long." }, + { "MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING", + "A required TLS feature is missing." }, + { "MOZILLA_PKIX_ERROR_INVALID_INTEGER_ENCODING", + "The server presented a certificate that contains an invalid encoding of " + "an integer. Common causes include negative serial numbers, negative RSA " + "moduli, and encodings that are longer than necessary." }, + { "MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME", + "The server presented a certificate with an empty issuer distinguished " + "name." }, + { "MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED", + "An additional policy constraint failed when validating this " + "certificate." }, + { "MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT", + "The certificate is not trusted because it is self-signed." }, + { "MOZILLA_PKIX_ERROR_MITM_DETECTED", + "Your connection is being intercepted by a TLS proxy. Uninstall it if " + "possible or configure your device to trust its root certificate." }, + }; + // Note that these error strings are not localizable. + // When these strings change, update the localization information too. + + static const PRErrorTable ErrorTable = { + ErrorTableText, + "pkixerrors", + ERROR_BASE, + PR_ARRAY_SIZE(ErrorTableText) + }; + + (void) PR_ErrorInstallTable(&ErrorTable); +} + +} } // namespace mozilla::pkix diff --git a/security/nss/lib/mozpkix/lib/pkixocsp.cpp b/security/nss/lib/mozpkix/lib/pkixocsp.cpp new file mode 100644 index 000000000..a81154417 --- /dev/null +++ b/security/nss/lib/mozpkix/lib/pkixocsp.cpp @@ -0,0 +1,1012 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <limits> + +#include "mozpkix/pkix.h" +#include "mozpkix/pkixcheck.h" +#include "mozpkix/pkixutil.h" + +namespace { + +const size_t SHA1_DIGEST_LENGTH = 160 / 8; + +} // namespace + +namespace mozilla { namespace pkix { + +// These values correspond to the tag values in the ASN.1 CertStatus +enum class CertStatus : uint8_t { + Good = der::CONTEXT_SPECIFIC | 0, + Revoked = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1, + Unknown = der::CONTEXT_SPECIFIC | 2 +}; + +class Context final +{ +public: + Context(TrustDomain& aTrustDomain, const CertID& aCertID, Time aTime, + uint16_t aMaxLifetimeInDays, /*optional out*/ Time* aThisUpdate, + /*optional out*/ Time* aValidThrough) + : trustDomain(aTrustDomain) + , certID(aCertID) + , time(aTime) + , maxLifetimeInDays(aMaxLifetimeInDays) + , certStatus(CertStatus::Unknown) + , thisUpdate(aThisUpdate) + , validThrough(aValidThrough) + , expired(false) + , matchFound(false) + { + if (thisUpdate) { + *thisUpdate = TimeFromElapsedSecondsAD(0); + } + if (validThrough) { + *validThrough = TimeFromElapsedSecondsAD(0); + } + } + + TrustDomain& trustDomain; + const CertID& certID; + const Time time; + const uint16_t maxLifetimeInDays; + CertStatus certStatus; + Time* thisUpdate; + Time* validThrough; + bool expired; + + Input signedCertificateTimestamps; + + // Keep track of whether the OCSP response contains the status of the + // certificate we're interested in. Responders might reply without + // including the status of any of the requested certs, we should + // indicate a server failure in those cases. + bool matchFound; + + Context(const Context&) = delete; + void operator=(const Context&) = delete; +}; + +// Verify that potentialSigner is a valid delegated OCSP response signing cert +// according to RFC 6960 section 4.2.2.2. +static Result +CheckOCSPResponseSignerCert(TrustDomain& trustDomain, + BackCert& potentialSigner, + Input issuerSubject, + Input issuerSubjectPublicKeyInfo, + Time time) +{ + Result rv; + + // We don't need to do a complete verification of the signer (i.e. we don't + // have to call BuildCertChain to verify the entire chain) because we + // already know that the issuer is valid, since revocation checking is done + // from the root to the parent after we've built a complete chain that we + // know is otherwise valid. Rather, we just need to do a one-step validation + // from potentialSigner to the issuer. + // + // It seems reasonable to require the KU_DIGITAL_SIGNATURE key usage on the + // OCSP responder certificate if the OCSP responder certificate has a + // key usage extension. However, according to bug 240456, some OCSP responder + // certificates may have only the nonRepudiation bit set. Also, the OCSP + // specification (RFC 6960) does not mandate any particular key usage to be + // asserted for OCSP responde signers. Oddly, the CABForum Baseline + // Requirements v.1.1.5 do say "If the Root CA Private Key is used for + // signing OCSP responses, then the digitalSignature bit MUST be set." + // + // Note that CheckIssuerIndependentProperties processes + // SEC_OID_OCSP_RESPONDER in the way that the OCSP specification requires us + // to--in particular, it doesn't allow SEC_OID_OCSP_RESPONDER to be implied + // by a missing EKU extension, unlike other EKUs. + // + // TODO(bug 926261): If we're validating for a policy then the policy OID we + // are validating for should be passed to CheckIssuerIndependentProperties. + TrustLevel unusedTrustLevel; + rv = CheckIssuerIndependentProperties(trustDomain, potentialSigner, time, + KeyUsage::noParticularKeyUsageRequired, + KeyPurposeId::id_kp_OCSPSigning, + CertPolicyId::anyPolicy, 0, + unusedTrustLevel); + if (rv != Success) { + return rv; + } + + // It is possible that there exists a certificate with the same key as the + // issuer but with a different name, so we need to compare names + // XXX(bug 926270) XXX(bug 1008133) XXX(bug 980163): Improve name + // comparison. + // TODO: needs test + if (!InputsAreEqual(potentialSigner.GetIssuer(), issuerSubject)) { + return Result::ERROR_OCSP_RESPONDER_CERT_INVALID; + } + + // TODO(bug 926260): check name constraints + + rv = VerifySignedData(trustDomain, potentialSigner.GetSignedData(), + issuerSubjectPublicKeyInfo); + + // TODO: check for revocation of the OCSP responder certificate unless no-check + // or the caller forcing no-check. To properly support the no-check policy, we'd + // need to enforce policy constraints from the issuerChain. + + return rv; +} + +enum class ResponderIDType : uint8_t +{ + byName = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1, + byKey = der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 2 +}; + +static inline Result OCSPResponse(Reader&, Context&); +static inline Result ResponseBytes(Reader&, Context&); +static inline Result BasicResponse(Reader&, Context&); +static inline Result ResponseData( + Reader& tbsResponseData, + Context& context, + const der::SignedDataWithSignature& signedResponseData, + const DERArray& certs); +static inline Result SingleResponse(Reader& input, Context& context); +static Result ExtensionNotUnderstood(Reader& extnID, Input extnValue, + bool critical, /*out*/ bool& understood); +static Result RememberSingleExtension(Context& context, Reader& extnID, + Input extnValue, bool critical, + /*out*/ bool& understood); +// It is convention to name the function after the part of the data structure +// we're parsing from the RFC (e.g. OCSPResponse, ResponseBytes). +// But since we also have a C++ type called CertID, this function doesn't +// follow the convention to prevent shadowing. +static inline Result MatchCertID(Reader& input, + const Context& context, + /*out*/ bool& match); +static Result MatchKeyHash(TrustDomain& trustDomain, + Input issuerKeyHash, + Input issuerSubjectPublicKeyInfo, + /*out*/ bool& match); +static Result KeyHash(TrustDomain& trustDomain, + Input subjectPublicKeyInfo, + /*out*/ uint8_t* hashBuf, size_t hashBufSize); + +static Result +MatchResponderID(TrustDomain& trustDomain, + ResponderIDType responderIDType, + Input responderID, + Input potentialSignerSubject, + Input potentialSignerSubjectPublicKeyInfo, + /*out*/ bool& match) +{ + match = false; + + switch (responderIDType) { + case ResponderIDType::byName: + // XXX(bug 926270) XXX(bug 1008133) XXX(bug 980163): Improve name + // comparison. + match = InputsAreEqual(responderID, potentialSignerSubject); + return Success; + + case ResponderIDType::byKey: + { + Reader input(responderID); + Input keyHash; + Result rv = der::ExpectTagAndGetValue(input, der::OCTET_STRING, keyHash); + if (rv != Success) { + return rv; + } + return MatchKeyHash(trustDomain, keyHash, + potentialSignerSubjectPublicKeyInfo, match); + } + + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM + } +} + +static Result +VerifyOCSPSignedData(TrustDomain& trustDomain, + const der::SignedDataWithSignature& signedResponseData, + Input spki) +{ + Result rv = VerifySignedData(trustDomain, signedResponseData, spki); + if (rv == Result::ERROR_BAD_SIGNATURE) { + rv = Result::ERROR_OCSP_BAD_SIGNATURE; + } + return rv; +} + +// RFC 6960 section 4.2.2.2: The OCSP responder must either be the issuer of +// the cert or it must be a delegated OCSP response signing cert directly +// issued by the issuer. If the OCSP responder is a delegated OCSP response +// signer, then its certificate is (probably) embedded within the OCSP +// response and we'll need to verify that it is a valid certificate that chains +// *directly* to issuerCert. +static Result +VerifySignature(Context& context, ResponderIDType responderIDType, + Input responderID, const DERArray& certs, + const der::SignedDataWithSignature& signedResponseData) +{ + bool match; + Result rv = MatchResponderID(context.trustDomain, responderIDType, + responderID, context.certID.issuer, + context.certID.issuerSubjectPublicKeyInfo, + match); + if (rv != Success) { + return rv; + } + if (match) { + return VerifyOCSPSignedData(context.trustDomain, signedResponseData, + context.certID.issuerSubjectPublicKeyInfo); + } + + size_t numCerts = certs.GetLength(); + for (size_t i = 0; i < numCerts; ++i) { + BackCert cert(*certs.GetDER(i), EndEntityOrCA::MustBeEndEntity, nullptr); + rv = cert.Init(); + if (rv != Success) { + return rv; + } + rv = MatchResponderID(context.trustDomain, responderIDType, responderID, + cert.GetSubject(), cert.GetSubjectPublicKeyInfo(), + match); + if (rv != Success) { + if (IsFatalError(rv)) { + return rv; + } + continue; + } + + if (match) { + rv = CheckOCSPResponseSignerCert(context.trustDomain, cert, + context.certID.issuer, + context.certID.issuerSubjectPublicKeyInfo, + context.time); + if (rv != Success) { + if (IsFatalError(rv)) { + return rv; + } + continue; + } + + return VerifyOCSPSignedData(context.trustDomain, signedResponseData, + cert.GetSubjectPublicKeyInfo()); + } + } + + return Result::ERROR_OCSP_INVALID_SIGNING_CERT; +} + +static inline Result +MapBadDERToMalformedOCSPResponse(Result rv) +{ + if (rv == Result::ERROR_BAD_DER) { + return Result::ERROR_OCSP_MALFORMED_RESPONSE; + } + return rv; +} + +Result +VerifyEncodedOCSPResponse(TrustDomain& trustDomain, const struct CertID& certID, + Time time, uint16_t maxOCSPLifetimeInDays, + Input encodedResponse, + /*out*/ bool& expired, + /*optional out*/ Time* thisUpdate, + /*optional out*/ Time* validThrough) +{ + // Always initialize this to something reasonable. + expired = false; + + Context context(trustDomain, certID, time, maxOCSPLifetimeInDays, + thisUpdate, validThrough); + + Reader input(encodedResponse); + Result rv = der::Nested(input, der::SEQUENCE, [&context](Reader& r) { + return OCSPResponse(r, context); + }); + if (rv != Success) { + return MapBadDERToMalformedOCSPResponse(rv); + } + rv = der::End(input); + if (rv != Success) { + return MapBadDERToMalformedOCSPResponse(rv); + } + if (!context.matchFound) { + return Result::ERROR_OCSP_RESPONSE_FOR_CERT_MISSING; + } + + expired = context.expired; + + switch (context.certStatus) { + case CertStatus::Good: + if (expired) { + return Result::ERROR_OCSP_OLD_RESPONSE; + } + if (context.signedCertificateTimestamps.GetLength()) { + Input sctList; + rv = ExtractSignedCertificateTimestampListFromExtension( + context.signedCertificateTimestamps, sctList); + if (rv != Success) { + return MapBadDERToMalformedOCSPResponse(rv); + } + context.trustDomain.NoteAuxiliaryExtension( + AuxiliaryExtension::SCTListFromOCSPResponse, sctList); + } + return Success; + case CertStatus::Revoked: + return Result::ERROR_REVOKED_CERTIFICATE; + case CertStatus::Unknown: + return Result::ERROR_OCSP_UNKNOWN_CERT; + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM + } +} + +// OCSPResponse ::= SEQUENCE { +// responseStatus OCSPResponseStatus, +// responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } +// +static inline Result +OCSPResponse(Reader& input, Context& context) +{ + // OCSPResponseStatus ::= ENUMERATED { + // successful (0), -- Response has valid confirmations + // malformedRequest (1), -- Illegal confirmation request + // internalError (2), -- Internal error in issuer + // tryLater (3), -- Try again later + // -- (4) is not used + // sigRequired (5), -- Must sign the request + // unauthorized (6) -- Request unauthorized + // } + uint8_t responseStatus; + + Result rv = der::Enumerated(input, responseStatus); + if (rv != Success) { + return rv; + } + switch (responseStatus) { + case 0: break; // successful + case 1: return Result::ERROR_OCSP_MALFORMED_REQUEST; + case 2: return Result::ERROR_OCSP_SERVER_ERROR; + case 3: return Result::ERROR_OCSP_TRY_SERVER_LATER; + case 5: return Result::ERROR_OCSP_REQUEST_NEEDS_SIG; + case 6: return Result::ERROR_OCSP_UNAUTHORIZED_REQUEST; + default: return Result::ERROR_OCSP_UNKNOWN_RESPONSE_STATUS; + } + + return der::Nested(input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0, + der::SEQUENCE, [&context](Reader& r) { + return ResponseBytes(r, context); + }); +} + +// ResponseBytes ::= SEQUENCE { +// responseType OBJECT IDENTIFIER, +// response OCTET STRING } +static inline Result +ResponseBytes(Reader& input, Context& context) +{ + static const uint8_t id_pkix_ocsp_basic[] = { + 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01 + }; + + Result rv = der::OID(input, id_pkix_ocsp_basic); + if (rv != Success) { + return rv; + } + + return der::Nested(input, der::OCTET_STRING, der::SEQUENCE, + [&context](Reader& r) { + return BasicResponse(r, context); + }); +} + +// BasicOCSPResponse ::= SEQUENCE { +// tbsResponseData ResponseData, +// signatureAlgorithm AlgorithmIdentifier, +// signature BIT STRING, +// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } +Result +BasicResponse(Reader& input, Context& context) +{ + Reader tbsResponseData; + der::SignedDataWithSignature signedData; + Result rv = der::SignedData(input, tbsResponseData, signedData); + if (rv != Success) { + if (rv == Result::ERROR_BAD_SIGNATURE) { + return Result::ERROR_OCSP_BAD_SIGNATURE; + } + return rv; + } + + // Parse certificates, if any + NonOwningDERArray certs; + if (!input.AtEnd()) { + rv = der::Nested(input, der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0, + der::SEQUENCE, [&certs](Reader& certsDER) -> Result { + while (!certsDER.AtEnd()) { + Input cert; + Result nestedRv = + der::ExpectTagAndGetTLV(certsDER, der::SEQUENCE, cert); + if (nestedRv != Success) { + return nestedRv; + } + nestedRv = certs.Append(cert); + if (nestedRv != Success) { + return Result::ERROR_BAD_DER; // Too many certs + } + } + return Success; + }); + if (rv != Success) { + return rv; + } + } + + return ResponseData(tbsResponseData, context, signedData, certs); +} + +// ResponseData ::= SEQUENCE { +// version [0] EXPLICIT Version DEFAULT v1, +// responderID ResponderID, +// producedAt GeneralizedTime, +// responses SEQUENCE OF SingleResponse, +// responseExtensions [1] EXPLICIT Extensions OPTIONAL } +static inline Result +ResponseData(Reader& input, Context& context, + const der::SignedDataWithSignature& signedResponseData, + const DERArray& certs) +{ + der::Version version; + Result rv = der::OptionalVersion(input, version); + if (rv != Success) { + return rv; + } + if (version != der::Version::v1) { + // TODO: more specific error code for bad version? + return Result::ERROR_BAD_DER; + } + + // ResponderID ::= CHOICE { + // byName [1] Name, + // byKey [2] KeyHash } + Input responderID; + ResponderIDType responderIDType + = input.Peek(static_cast<uint8_t>(ResponderIDType::byName)) + ? ResponderIDType::byName + : ResponderIDType::byKey; + rv = der::ExpectTagAndGetValue(input, static_cast<uint8_t>(responderIDType), + responderID); + if (rv != Success) { + return rv; + } + + // This is the soonest we can verify the signature. We verify the signature + // right away to follow the principal of minimizing the processing of data + // before verifying its signature. + rv = VerifySignature(context, responderIDType, responderID, certs, + signedResponseData); + if (rv != Success) { + return rv; + } + + // TODO: Do we even need to parse this? Should we just skip it? + Time producedAt(Time::uninitialized); + rv = der::GeneralizedTime(input, producedAt); + if (rv != Success) { + return rv; + } + + // We don't accept an empty sequence of responses. In practice, a legit OCSP + // responder will never return an empty response, and handling the case of an + // empty response makes things unnecessarily complicated. + rv = der::NestedOf(input, der::SEQUENCE, der::SEQUENCE, + der::EmptyAllowed::No, [&context](Reader& r) { + return SingleResponse(r, context); + }); + if (rv != Success) { + return rv; + } + + return der::OptionalExtensions(input, + der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1, + ExtensionNotUnderstood); +} + +// SingleResponse ::= SEQUENCE { +// certID CertID, +// certStatus CertStatus, +// thisUpdate GeneralizedTime, +// nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, +// singleExtensions [1] EXPLICIT Extensions{{re-ocsp-crl | +// re-ocsp-archive-cutoff | +// CrlEntryExtensions, ...} +// } OPTIONAL } +static inline Result +SingleResponse(Reader& input, Context& context) +{ + bool match = false; + Result rv = der::Nested(input, der::SEQUENCE, [&context, &match](Reader& r) { + return MatchCertID(r, context, match); + }); + if (rv != Success) { + return rv; + } + + if (!match) { + // This response does not reference the certificate we're interested in. + // By consuming the rest of our input and returning successfully, we can + // continue processing and examine another response that might have what + // we want. + input.SkipToEnd(); + return Success; + } + + // We found a response for the cert we're interested in. + context.matchFound = true; + + // CertStatus ::= CHOICE { + // good [0] IMPLICIT NULL, + // revoked [1] IMPLICIT RevokedInfo, + // unknown [2] IMPLICIT UnknownInfo } + // + // In the event of multiple SingleResponses for a cert that have conflicting + // statuses, we use the following precedence rules: + // + // * revoked overrides good and unknown + // * good overrides unknown + if (input.Peek(static_cast<uint8_t>(CertStatus::Good))) { + rv = der::ExpectTagAndEmptyValue(input, + static_cast<uint8_t>(CertStatus::Good)); + if (rv != Success) { + return rv; + } + if (context.certStatus != CertStatus::Revoked) { + context.certStatus = CertStatus::Good; + } + } else if (input.Peek(static_cast<uint8_t>(CertStatus::Revoked))) { + // We don't need any info from the RevokedInfo structure, so we don't even + // parse it. TODO: We should mention issues like this in the explanation of + // why we treat invalid OCSP responses equivalently to revoked for OCSP + // stapling. + rv = der::ExpectTagAndSkipValue(input, + static_cast<uint8_t>(CertStatus::Revoked)); + if (rv != Success) { + return rv; + } + context.certStatus = CertStatus::Revoked; + } else { + rv = der::ExpectTagAndEmptyValue(input, + static_cast<uint8_t>(CertStatus::Unknown)); + if (rv != Success) { + return rv; + } + } + + // http://tools.ietf.org/html/rfc6960#section-3.2 + // 5. The time at which the status being indicated is known to be + // correct (thisUpdate) is sufficiently recent; + // 6. When available, the time at or before which newer information will + // be available about the status of the certificate (nextUpdate) is + // greater than the current time. + + Time thisUpdate(Time::uninitialized); + rv = der::GeneralizedTime(input, thisUpdate); + if (rv != Success) { + return rv; + } + + static const uint64_t SLOP_SECONDS = Time::ONE_DAY_IN_SECONDS; + + Time timePlusSlop(context.time); + rv = timePlusSlop.AddSeconds(SLOP_SECONDS); + if (rv != Success) { + return rv; + } + if (thisUpdate > timePlusSlop) { + return Result::ERROR_OCSP_FUTURE_RESPONSE; + } + + Time notAfter(Time::uninitialized); + static const uint8_t NEXT_UPDATE_TAG = + der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0; + if (input.Peek(NEXT_UPDATE_TAG)) { + Time nextUpdate(Time::uninitialized); + rv = der::Nested(input, NEXT_UPDATE_TAG, [&nextUpdate](Reader& r) { + return der::GeneralizedTime(r, nextUpdate); + }); + if (rv != Success) { + return rv; + } + + if (nextUpdate < thisUpdate) { + return Result::ERROR_OCSP_MALFORMED_RESPONSE; + } + notAfter = thisUpdate; + if (notAfter.AddSeconds(context.maxLifetimeInDays * + Time::ONE_DAY_IN_SECONDS) != Success) { + // This could only happen if we're dealing with times beyond the year + // 10,000AD. + return Result::ERROR_OCSP_FUTURE_RESPONSE; + } + if (nextUpdate <= notAfter) { + notAfter = nextUpdate; + } + } else { + // NSS requires all OCSP responses without a nextUpdate to be recent. + // Match that stricter behavior. + notAfter = thisUpdate; + if (notAfter.AddSeconds(Time::ONE_DAY_IN_SECONDS) != Success) { + // This could only happen if we're dealing with times beyond the year + // 10,000AD. + return Result::ERROR_OCSP_FUTURE_RESPONSE; + } + } + + // Add some slop to hopefully handle clock-skew. + Time notAfterPlusSlop(notAfter); + rv = notAfterPlusSlop.AddSeconds(SLOP_SECONDS); + if (rv != Success) { + // This could only happen if we're dealing with times beyond the year + // 10,000AD. + return Result::ERROR_OCSP_FUTURE_RESPONSE; + } + if (context.time > notAfterPlusSlop) { + context.expired = true; + } + + rv = der::OptionalExtensions( + input, + der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1, + [&context](Reader& extnID, const Input& extnValue, bool critical, + /*out*/ bool& understood) { + return RememberSingleExtension(context, extnID, extnValue, critical, + understood); + }); + + if (rv != Success) { + return rv; + } + + if (context.thisUpdate) { + *context.thisUpdate = thisUpdate; + } + if (context.validThrough) { + *context.validThrough = notAfterPlusSlop; + } + + return Success; +} + +// CertID ::= SEQUENCE { +// hashAlgorithm AlgorithmIdentifier, +// issuerNameHash OCTET STRING, -- Hash of issuer's DN +// issuerKeyHash OCTET STRING, -- Hash of issuer's public key +// serialNumber CertificateSerialNumber } +static inline Result +MatchCertID(Reader& input, const Context& context, /*out*/ bool& match) +{ + match = false; + + DigestAlgorithm hashAlgorithm; + Result rv = der::DigestAlgorithmIdentifier(input, hashAlgorithm); + if (rv != Success) { + if (rv == Result::ERROR_INVALID_ALGORITHM) { + // Skip entries that are hashed with algorithms we don't support. + input.SkipToEnd(); + return Success; + } + return rv; + } + + Input issuerNameHash; + rv = der::ExpectTagAndGetValue(input, der::OCTET_STRING, issuerNameHash); + if (rv != Success) { + return rv; + } + + Input issuerKeyHash; + rv = der::ExpectTagAndGetValue(input, der::OCTET_STRING, issuerKeyHash); + if (rv != Success) { + return rv; + } + + Input serialNumber; + rv = der::CertificateSerialNumber(input, serialNumber); + if (rv != Success) { + return rv; + } + + if (!InputsAreEqual(serialNumber, context.certID.serialNumber)) { + // This does not reference the certificate we're interested in. + // Consume the rest of the input and return successfully to + // potentially continue processing other responses. + input.SkipToEnd(); + return Success; + } + + // TODO: support SHA-2 hashes. + + if (hashAlgorithm != DigestAlgorithm::sha1) { + // Again, not interested in this response. Consume input, return success. + input.SkipToEnd(); + return Success; + } + + if (issuerNameHash.GetLength() != SHA1_DIGEST_LENGTH) { + return Result::ERROR_OCSP_MALFORMED_RESPONSE; + } + + // From http://tools.ietf.org/html/rfc6960#section-4.1.1: + // "The hash shall be calculated over the DER encoding of the + // issuer's name field in the certificate being checked." + uint8_t hashBuf[SHA1_DIGEST_LENGTH]; + rv = context.trustDomain.DigestBuf(context.certID.issuer, + DigestAlgorithm::sha1, hashBuf, + sizeof(hashBuf)); + if (rv != Success) { + return rv; + } + Input computed(hashBuf); + if (!InputsAreEqual(computed, issuerNameHash)) { + // Again, not interested in this response. Consume input, return success. + input.SkipToEnd(); + return Success; + } + + return MatchKeyHash(context.trustDomain, issuerKeyHash, + context.certID.issuerSubjectPublicKeyInfo, match); +} + +// From http://tools.ietf.org/html/rfc6960#section-4.1.1: +// "The hash shall be calculated over the value (excluding tag and length) of +// the subject public key field in the issuer's certificate." +// +// From http://tools.ietf.org/html/rfc6960#appendix-B.1: +// KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key +// -- (i.e., the SHA-1 hash of the value of the +// -- BIT STRING subjectPublicKey [excluding +// -- the tag, length, and number of unused +// -- bits] in the responder's certificate) +static Result +MatchKeyHash(TrustDomain& trustDomain, Input keyHash, + const Input subjectPublicKeyInfo, /*out*/ bool& match) +{ + if (keyHash.GetLength() != SHA1_DIGEST_LENGTH) { + return Result::ERROR_OCSP_MALFORMED_RESPONSE; + } + uint8_t hashBuf[SHA1_DIGEST_LENGTH]; + Result rv = KeyHash(trustDomain, subjectPublicKeyInfo, hashBuf, + sizeof hashBuf); + if (rv != Success) { + return rv; + } + Input computed(hashBuf); + match = InputsAreEqual(computed, keyHash); + return Success; +} + +// TODO(bug 966856): support SHA-2 hashes +Result +KeyHash(TrustDomain& trustDomain, const Input subjectPublicKeyInfo, + /*out*/ uint8_t* hashBuf, size_t hashBufSize) +{ + if (!hashBuf || hashBufSize != SHA1_DIGEST_LENGTH) { + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + // RFC 5280 Section 4.1 + // + // SubjectPublicKeyInfo ::= SEQUENCE { + // algorithm AlgorithmIdentifier, + // subjectPublicKey BIT STRING } + + Reader spki; + Result rv = der::ExpectTagAndGetValueAtEnd(subjectPublicKeyInfo, + der::SEQUENCE, spki); + if (rv != Success) { + return rv; + } + + // Skip AlgorithmIdentifier + rv = der::ExpectTagAndSkipValue(spki, der::SEQUENCE); + if (rv != Success) { + return rv; + } + + Input subjectPublicKey; + rv = der::BitStringWithNoUnusedBits(spki, subjectPublicKey); + if (rv != Success) { + return rv; + } + rv = der::End(spki); + if (rv != Success) { + return rv; + } + + return trustDomain.DigestBuf(subjectPublicKey, DigestAlgorithm::sha1, + hashBuf, hashBufSize); +} + +Result +ExtensionNotUnderstood(Reader& /*extnID*/, Input /*extnValue*/, + bool /*critical*/, /*out*/ bool& understood) +{ + understood = false; + return Success; +} + +Result +RememberSingleExtension(Context& context, Reader& extnID, Input extnValue, + bool /*critical*/, /*out*/ bool& understood) +{ + understood = false; + + // SingleExtension for Signed Certificate Timestamp List. + // See Section 3.3 of RFC 6962. + // python DottedOIDToCode.py + // id_ocsp_singleExtensionSctList 1.3.6.1.4.1.11129.2.4.5 + static const uint8_t id_ocsp_singleExtensionSctList[] = { + 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x05 + }; + + if (extnID.MatchRest(id_ocsp_singleExtensionSctList)) { + // Empty values are not allowed for this extension. Note that + // we assume this later, when checking if the extension was present. + if (extnValue.GetLength() == 0) { + return Result::ERROR_EXTENSION_VALUE_INVALID; + } + if (context.signedCertificateTimestamps.Init(extnValue) != Success) { + // Duplicate extension. + return Result::ERROR_EXTENSION_VALUE_INVALID; + } + understood = true; + } + + return Success; +} + +// 1. The certificate identified in a received response corresponds to +// the certificate that was identified in the corresponding request; +// 2. The signature on the response is valid; +// 3. The identity of the signer matches the intended recipient of the +// request; +// 4. The signer is currently authorized to provide a response for the +// certificate in question; +// 5. The time at which the status being indicated is known to be +// correct (thisUpdate) is sufficiently recent; +// 6. When available, the time at or before which newer information will +// be available about the status of the certificate (nextUpdate) is +// greater than the current time. +// +// Responses whose nextUpdate value is earlier than +// the local system time value SHOULD be considered unreliable. +// Responses whose thisUpdate time is later than the local system time +// SHOULD be considered unreliable. +// +// If nextUpdate is not set, the responder is indicating that newer +// revocation information is available all the time. +// +// http://tools.ietf.org/html/rfc5019#section-4 + +Result +CreateEncodedOCSPRequest(TrustDomain& trustDomain, const struct CertID& certID, + /*out*/ uint8_t (&out)[OCSP_REQUEST_MAX_LENGTH], + /*out*/ size_t& outLen) +{ + // We do not add any extensions to the request. + + // RFC 6960 says "An OCSP client MAY wish to specify the kinds of response + // types it understands. To do so, it SHOULD use an extension with the OID + // id-pkix-ocsp-response." This use of MAY and SHOULD is unclear. MSIE11 + // on Windows 8.1 does not include any extensions, whereas NSS has always + // included the id-pkix-ocsp-response extension. Avoiding the sending the + // extension is better for OCSP GET because it makes the request smaller, + // and thus more likely to fit within the 255 byte limit for OCSP GET that + // is specified in RFC 5019 Section 5. + + // Bug 966856: Add the id-pkix-ocsp-pref-sig-algs extension. + + // Since we don't know whether the OCSP responder supports anything other + // than SHA-1, we have no choice but to use SHA-1 for issuerNameHash and + // issuerKeyHash. + static const uint8_t hashAlgorithm[11] = { + 0x30, 0x09, // SEQUENCE + 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, // OBJECT IDENTIFIER id-sha1 + 0x05, 0x00, // NULL + }; + static const uint8_t hashLen = 160 / 8; + + static const unsigned int totalLenWithoutSerialNumberData + = 2 // OCSPRequest + + 2 // tbsRequest + + 2 // requestList + + 2 // Request + + 2 // reqCert (CertID) + + sizeof(hashAlgorithm) // hashAlgorithm + + 2 + hashLen // issuerNameHash + + 2 + hashLen // issuerKeyHash + + 2; // serialNumber (header) + + // The only way we could have a request this large is if the serialNumber was + // ridiculously and unreasonably large. RFC 5280 says "Conforming CAs MUST + // NOT use serialNumber values longer than 20 octets." With this restriction, + // we allow for some amount of non-conformance with that requirement while + // still ensuring we can encode the length values in the ASN.1 TLV structures + // in a single byte. + static_assert(totalLenWithoutSerialNumberData < OCSP_REQUEST_MAX_LENGTH, + "totalLenWithoutSerialNumberData too big"); + if (certID.serialNumber.GetLength() > + OCSP_REQUEST_MAX_LENGTH - totalLenWithoutSerialNumberData) { + return Result::ERROR_BAD_DER; + } + + outLen = totalLenWithoutSerialNumberData + certID.serialNumber.GetLength(); + + uint8_t totalLen = static_cast<uint8_t>(outLen); + + uint8_t* d = out; + *d++ = 0x30; *d++ = totalLen - 2u; // OCSPRequest (SEQUENCE) + *d++ = 0x30; *d++ = totalLen - 4u; // tbsRequest (SEQUENCE) + *d++ = 0x30; *d++ = totalLen - 6u; // requestList (SEQUENCE OF) + *d++ = 0x30; *d++ = totalLen - 8u; // Request (SEQUENCE) + *d++ = 0x30; *d++ = totalLen - 10u; // reqCert (CertID SEQUENCE) + + // reqCert.hashAlgorithm + for (const uint8_t hashAlgorithmByte : hashAlgorithm) { + *d++ = hashAlgorithmByte; + } + + // reqCert.issuerNameHash (OCTET STRING) + *d++ = 0x04; + *d++ = hashLen; + Result rv = trustDomain.DigestBuf(certID.issuer, DigestAlgorithm::sha1, d, + hashLen); + if (rv != Success) { + return rv; + } + d += hashLen; + + // reqCert.issuerKeyHash (OCTET STRING) + *d++ = 0x04; + *d++ = hashLen; + rv = KeyHash(trustDomain, certID.issuerSubjectPublicKeyInfo, d, hashLen); + if (rv != Success) { + return rv; + } + d += hashLen; + + // reqCert.serialNumber (INTEGER) + *d++ = 0x02; // INTEGER + *d++ = static_cast<uint8_t>(certID.serialNumber.GetLength()); + Reader serialNumber(certID.serialNumber); + do { + rv = serialNumber.Read(*d); + if (rv != Success) { + return rv; + } + ++d; + } while (!serialNumber.AtEnd()); + + assert(d == out + totalLen); + + return Success; +} + +} } // namespace mozilla::pkix diff --git a/security/nss/lib/mozpkix/lib/pkixresult.cpp b/security/nss/lib/mozpkix/lib/pkixresult.cpp new file mode 100644 index 000000000..871d9a0fe --- /dev/null +++ b/security/nss/lib/mozpkix/lib/pkixresult.cpp @@ -0,0 +1,46 @@ +/*- *- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mozpkix/Result.h" +#include "mozpkix/pkixutil.h" + +namespace mozilla { namespace pkix { + +const char* +MapResultToName(Result result) +{ + switch (result) + { +#define MOZILLA_PKIX_MAP(mozilla_pkix_result, value, nss_result) \ + case Result::mozilla_pkix_result: return "Result::" #mozilla_pkix_result; + + MOZILLA_PKIX_MAP_LIST + +#undef MOZILLA_PKIX_MAP + + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM + } +} + +} } // namespace mozilla::pkix diff --git a/security/nss/lib/mozpkix/lib/pkixtime.cpp b/security/nss/lib/mozpkix/lib/pkixtime.cpp new file mode 100644 index 000000000..38e063804 --- /dev/null +++ b/security/nss/lib/mozpkix/lib/pkixtime.cpp @@ -0,0 +1,78 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2014 Mozilla Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mozpkix/Time.h" +#include "mozpkix/pkixutil.h" + +#ifdef _WINDOWS +#ifdef _MSC_VER +#pragma warning(push, 3) +#endif +#include "windows.h" +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#else +#include "sys/time.h" +#endif + +namespace mozilla { namespace pkix { + +Time +Now() +{ + uint64_t seconds; + +#ifdef _WINDOWS + // "Contains a 64-bit value representing the number of 100-nanosecond + // intervals since January 1, 1601 (UTC)." + // - http://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).aspx + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + uint64_t ft64 = (static_cast<uint64_t>(ft.dwHighDateTime) << 32) | + ft.dwLowDateTime; + seconds = (DaysBeforeYear(1601) * Time::ONE_DAY_IN_SECONDS) + + ft64 / (1000u * 1000u * 1000u / 100u); +#else + // "The gettimeofday() function shall obtain the current time, expressed as + // seconds and microseconds since the Epoch." + // - http://pubs.opengroup.org/onlinepubs/009695399/functions/gettimeofday.html + timeval tv; + (void) gettimeofday(&tv, nullptr); + seconds = (DaysBeforeYear(1970) * Time::ONE_DAY_IN_SECONDS) + + static_cast<uint64_t>(tv.tv_sec); +#endif + + return TimeFromElapsedSecondsAD(seconds); +} + +Time +TimeFromEpochInSeconds(uint64_t secondsSinceEpoch) +{ + uint64_t seconds = (DaysBeforeYear(1970) * Time::ONE_DAY_IN_SECONDS) + + secondsSinceEpoch; + return TimeFromElapsedSecondsAD(seconds); +} + +} } // namespace mozilla::pkix diff --git a/security/nss/lib/mozpkix/lib/pkixverify.cpp b/security/nss/lib/mozpkix/lib/pkixverify.cpp new file mode 100644 index 000000000..8ceb2c184 --- /dev/null +++ b/security/nss/lib/mozpkix/lib/pkixverify.cpp @@ -0,0 +1,106 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2015 Mozilla Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mozpkix/pkixutil.h" + +namespace mozilla { namespace pkix { + +Result +DigestSignedData(TrustDomain& trustDomain, + const der::SignedDataWithSignature& signedData, + /*out*/ uint8_t(&digestBuf)[MAX_DIGEST_SIZE_IN_BYTES], + /*out*/ der::PublicKeyAlgorithm& publicKeyAlg, + /*out*/ SignedDigest& signedDigest) +{ + Reader signatureAlg(signedData.algorithm); + Result rv = der::SignatureAlgorithmIdentifierValue( + signatureAlg, publicKeyAlg, signedDigest.digestAlgorithm); + if (rv != Success) { + return rv; + } + if (!signatureAlg.AtEnd()) { + return Result::ERROR_BAD_DER; + } + + size_t digestLen; + switch (signedDigest.digestAlgorithm) { + case DigestAlgorithm::sha512: digestLen = 512 / 8; break; + case DigestAlgorithm::sha384: digestLen = 384 / 8; break; + case DigestAlgorithm::sha256: digestLen = 256 / 8; break; + case DigestAlgorithm::sha1: digestLen = 160 / 8; break; + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM + } + assert(digestLen <= sizeof(digestBuf)); + + rv = trustDomain.DigestBuf(signedData.data, signedDigest.digestAlgorithm, + digestBuf, digestLen); + if (rv != Success) { + return rv; + } + rv = signedDigest.digest.Init(digestBuf, digestLen); + if (rv != Success) { + return rv; + } + + return signedDigest.signature.Init(signedData.signature); +} + +Result +VerifySignedDigest(TrustDomain& trustDomain, + der::PublicKeyAlgorithm publicKeyAlg, + const SignedDigest& signedDigest, + Input signerSubjectPublicKeyInfo) +{ + switch (publicKeyAlg) { + case der::PublicKeyAlgorithm::ECDSA: + return trustDomain.VerifyECDSASignedDigest(signedDigest, + signerSubjectPublicKeyInfo); + case der::PublicKeyAlgorithm::RSA_PKCS1: + return trustDomain.VerifyRSAPKCS1SignedDigest(signedDigest, + signerSubjectPublicKeyInfo); + case der::PublicKeyAlgorithm::Uninitialized: + assert(false); + return Result::FATAL_ERROR_LIBRARY_FAILURE; + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM + } +} + +Result +VerifySignedData(TrustDomain& trustDomain, + const der::SignedDataWithSignature& signedData, + Input signerSubjectPublicKeyInfo) +{ + uint8_t digestBuf[MAX_DIGEST_SIZE_IN_BYTES]; + der::PublicKeyAlgorithm publicKeyAlg; + SignedDigest signedDigest; + Result rv = DigestSignedData(trustDomain, signedData, digestBuf, + publicKeyAlg, signedDigest); + if (rv != Success) { + return rv; + } + return VerifySignedDigest(trustDomain, publicKeyAlg, signedDigest, + signerSubjectPublicKeyInfo); +} + +} } // namespace mozilla::pkix diff --git a/security/nss/lib/mozpkix/mozpkix.gyp b/security/nss/lib/mozpkix/mozpkix.gyp new file mode 100644 index 000000000..1c552ba5f --- /dev/null +++ b/security/nss/lib/mozpkix/mozpkix.gyp @@ -0,0 +1,60 @@ +# 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/. +{ + 'includes': [ + '../../coreconf/config.gypi' + ], + 'targets': [ + { + 'target_name': 'mozpkix', + 'type': 'static_library', + 'standalone_static_library': 1, + 'sources': [ + 'lib/pkixbuild.cpp', + 'lib/pkixcert.cpp', + 'lib/pkixcheck.cpp', + 'lib/pkixder.cpp', + 'lib/pkixnames.cpp', + 'lib/pkixnss.cpp', + 'lib/pkixocsp.cpp', + 'lib/pkixresult.cpp', + 'lib/pkixtime.cpp', + 'lib/pkixverify.cpp', + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_mozpkix_exports', + ], + 'conditions': [ + [ 'mozpkix_only==0', { + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports' + ], + }], + ], + }, + { + 'target_name': 'mozpkix-testlib', + 'type': 'static_library', + 'standalone_static_library': 1, + 'sources': [ + 'test-lib/pkixtestalg.cpp', + 'test-lib/pkixtestnss.cpp', + 'test-lib/pkixtestutil.cpp', + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_mozpkix_exports', + ], + 'conditions': [ + [ 'mozpkix_only==0', { + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports' + ], + }], + ], + }, + ], + 'variables': { + 'module': 'nss', + } +} diff --git a/security/nss/lib/mozpkix/test-lib/pkixtestalg.cpp b/security/nss/lib/mozpkix/test-lib/pkixtestalg.cpp new file mode 100644 index 000000000..304641e2f --- /dev/null +++ b/security/nss/lib/mozpkix/test-lib/pkixtestalg.cpp @@ -0,0 +1,211 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2015 Mozilla Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mozpkix/test/pkixtestutil.h" + +#include "mozpkix/pkixder.h" +#include "mozpkix/nss_scoped_ptrs.h" + +// python DottedOIDToCode.py --prefixdefine PREFIX_1_2_840_10040 1.2.840.10040 +#define PREFIX_1_2_840_10040 0x2a, 0x86, 0x48, 0xce, 0x38 + +// python DottedOIDToCode.py --prefixdefine PREFIX_1_2_840_10045 1.2.840.10045 +#define PREFIX_1_2_840_10045 0x2a, 0x86, 0x48, 0xce, 0x3d + +// python DottedOIDToCode.py --prefixdefine PREFIX_1_2_840_113549 1.2.840.113549 +#define PREFIX_1_2_840_113549 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d + +namespace mozilla { namespace pkix { namespace test { + +namespace { + +enum class NULLParam { NO, YES }; + +template <size_t SIZE> +ByteString +OID(const uint8_t (&rawValue)[SIZE]) +{ + return TLV(der::OIDTag, ByteString(rawValue, SIZE)); +} + +template <size_t SIZE> +ByteString +SimpleAlgID(const uint8_t (&rawValue)[SIZE], + NULLParam nullParam = NULLParam::NO) +{ + ByteString sequenceValue(OID(rawValue)); + if (nullParam == NULLParam::YES) { + sequenceValue.append(TLV(der::NULLTag, ByteString())); + } + return TLV(der::SEQUENCE, sequenceValue); +} + +template <size_t SIZE> +ByteString +DERInteger(const uint8_t (&rawValue)[SIZE]) +{ + ByteString value(rawValue, SIZE); + if (value[0] & 0x80u) { + // Prefix with a leading zero to disambiguate this from a negative value. + value.insert(value.begin(), 0x00); + } + return TLV(der::INTEGER, value); +} + +// Generated with "openssl dsaparam -C -noout 2048" and reformatted. +// openssl 1.0 or later must be used so that a 256-bit Q value is +// generated. +static const uint8_t DSS_P_RAW[] = +{ + 0xB3,0xCD,0x29,0x44,0xF0,0x25,0xA7,0x73,0xFC,0x86,0x70,0xA2, + 0x69,0x5A,0x97,0x3F,0xBD,0x1C,0x6F,0xAA,0x4A,0x40,0x42,0x8E, + 0xCF,0xAE,0x62,0x12,0xED,0xB4,0xFD,0x05,0xC2,0xAE,0xB1,0x8C, + 0xFC,0xBE,0x38,0x90,0xBB,0x7C,0xFF,0x16,0xF4,0xED,0xCE,0x72, + 0x12,0x93,0x83,0xF0,0xA4,0xA1,0x71,0xDC,0x4B,0xF0,0x4E,0x3A, + 0x2B,0xFA,0x17,0xB7,0xB3,0x2A,0xCC,0x2C,0xD3,0xC8,0x21,0x49, + 0x7A,0x83,0x71,0x8B,0x3D,0x62,0x96,0xDC,0xAD,0xA8,0x03,0xBE, + 0x1D,0x33,0x11,0xF3,0xEB,0xD8,0x1B,0x8D,0xDB,0x62,0x79,0x83, + 0xF8,0x67,0x4E,0x62,0x21,0x2C,0x81,0x59,0xE8,0x73,0xD7,0xAF, + 0xB9,0x63,0x60,0xEA,0xAE,0xEC,0x68,0x6A,0xB4,0xB0,0x65,0xBA, + 0xA3,0x4C,0x09,0x99,0x29,0x6A,0x2E,0x2B,0xFC,0x6D,0x51,0xCA, + 0x30,0xA2,0x2F,0x7A,0x65,0x76,0xA7,0x55,0x13,0x11,0xA0,0x02, + 0xA2,0x59,0x4B,0xCE,0xA7,0x05,0xF6,0x07,0x35,0x9B,0x41,0xD7, + 0x11,0x5A,0x18,0x57,0xA7,0x78,0x88,0xC3,0xA8,0xE3,0x39,0xF5, + 0x47,0x3D,0x2E,0x18,0x54,0xB0,0xF0,0xBF,0x65,0x3F,0x77,0xC7, + 0x11,0xB8,0x0D,0x52,0xAD,0xC8,0xE8,0x6D,0xF6,0x7E,0x88,0x65, + 0x84,0x2B,0xF7,0xEF,0x8E,0xB5,0x7C,0xBD,0x2E,0x0D,0xF3,0xC6, + 0xDD,0x0B,0xB4,0xF2,0x23,0x1F,0xDA,0x55,0x05,0xF5,0xDC,0x53, + 0xA6,0x83,0xDA,0x5C,0xEF,0x29,0x02,0x78,0x68,0xD0,0xA4,0x39, + 0x09,0x7F,0xFA,0x49,0x18,0xD0,0xB5,0x19,0x35,0x31,0x8E,0xDE, + 0x43,0x35,0xA3,0xB9,0x6D,0xC1,0x70,0xC6,0x0D,0x18,0x24,0xEB, + 0x1E,0x4D,0x52,0xB7, +}; + +static const uint8_t DSS_Q_RAW[] = +{ + 0x8D,0x6B,0x86,0x89,0x9C,0x8D,0x30,0x91,0xCC,0x6E,0x34,0xF1, + 0xE8,0x9C,0x8A,0x5C,0xD6,0xAB,0x01,0x1E,0xC4,0xDB,0xFD,0x07, + 0xEB,0x5F,0x4E,0xE8,0xFA,0xFC,0x98,0x2D, +}; + +static const uint8_t DSS_G_RAW[] = +{ + 0x0E,0x2C,0x34,0xB2,0xE1,0x66,0x49,0xB6,0x9A,0x7D,0x67,0x3E, + 0xEE,0x98,0x35,0x18,0x28,0x35,0xFC,0x05,0x36,0x3B,0x94,0xE6, + 0x1E,0x1C,0x5B,0x05,0x3E,0x86,0x1B,0xE3,0xED,0xD2,0xE1,0xF3, + 0xF7,0xF7,0x60,0x6D,0x7D,0xA1,0xAF,0x9A,0xD1,0xDF,0xA2,0x9C, + 0xFC,0xA2,0xEB,0x90,0x8B,0x1C,0x82,0x92,0x45,0x7B,0x30,0x2A, + 0xFD,0x7A,0xE6,0x68,0x8F,0xEC,0x89,0x3A,0x9A,0xAD,0xFE,0x25, + 0x5E,0x51,0xC5,0x29,0x45,0x7F,0xAC,0xDE,0xFC,0xB4,0x1B,0x3A, + 0xDA,0xC7,0x21,0x68,0x87,0x27,0x8D,0x7B,0xB2,0xBB,0x41,0x60, + 0x46,0x42,0x5B,0x6B,0xE8,0x80,0xD2,0xE4,0xA3,0x30,0x8F,0xD5, + 0x71,0x07,0x8A,0x7B,0x32,0x56,0x84,0x41,0x1C,0xDF,0x69,0xE9, + 0xFD,0xBA,0x48,0xE0,0x43,0xA0,0x38,0x92,0x12,0xF3,0x52,0xA5, + 0x40,0x87,0xCB,0x34,0xBB,0x3E,0x25,0x29,0x3C,0xC6,0xA5,0x17, + 0xFD,0x58,0x47,0x89,0xDB,0x9B,0xB9,0xCF,0xE9,0xA8,0xF2,0xEC, + 0x55,0x76,0xF5,0xF1,0x9C,0x6E,0x0A,0x3F,0x16,0x5F,0x49,0x31, + 0x31,0x1C,0x43,0xA2,0x83,0xDA,0xDD,0x7F,0x1C,0xEA,0x05,0x36, + 0x7B,0xED,0x09,0xFB,0x6F,0x8A,0x2B,0x55,0xB9,0xBC,0x4A,0x8C, + 0x28,0xC1,0x4D,0x13,0x6E,0x47,0xF4,0xAD,0x79,0x00,0xE9,0x5A, + 0xB6,0xC7,0x73,0x28,0xA9,0x89,0xAD,0xE8,0x6E,0xC6,0x54,0xA5, + 0x56,0x2D,0xAA,0x81,0x83,0x9E,0xC1,0x13,0x79,0xA4,0x12,0xE0, + 0x76,0x1F,0x25,0x43,0xB6,0xDE,0x56,0xF7,0x52,0xCC,0x07,0xB8, + 0x37,0xE2,0x8C,0xC5,0x56,0x8C,0xDD,0x63,0xF5,0xB6,0xA3,0x46, + 0x62,0xF6,0x35,0x76, +}; + +} // namespace + +TestSignatureAlgorithm::TestSignatureAlgorithm( + const TestPublicKeyAlgorithm& aPublicKeyAlg, + TestDigestAlgorithmID aDigestAlg, + const ByteString& aAlgorithmIdentifier, + bool aAccepted) + : publicKeyAlg(aPublicKeyAlg) + , digestAlg(aDigestAlg) + , algorithmIdentifier(aAlgorithmIdentifier) + , accepted(aAccepted) +{ +} + +ByteString DSS_P() { return ByteString(DSS_P_RAW, sizeof(DSS_P_RAW)); } +ByteString DSS_Q() { return ByteString(DSS_Q_RAW, sizeof(DSS_Q_RAW)); } +ByteString DSS_G() { return ByteString(DSS_G_RAW, sizeof(DSS_G_RAW)); } + +TestPublicKeyAlgorithm +DSS() +{ + static const uint8_t oidValue[] = { PREFIX_1_2_840_10040, 4, 1 }; + + // RFC 3279 Section-2.3.2 + return TestPublicKeyAlgorithm( + TLV(der::SEQUENCE, + OID(oidValue) + + TLV(der::SEQUENCE, + DERInteger(DSS_P_RAW) + + DERInteger(DSS_Q_RAW) + + DERInteger(DSS_G_RAW)))); +} + +// RFC 3279 Section 2.3.1 +TestPublicKeyAlgorithm +RSA_PKCS1() +{ + static const uint8_t rsaEncryption[] = { PREFIX_1_2_840_113549, 1, 1, 1 }; + return TestPublicKeyAlgorithm(SimpleAlgID(rsaEncryption, NULLParam::YES)); +} + +// RFC 3279 Section 2.2.1 +TestSignatureAlgorithm md2WithRSAEncryption() +{ + static const uint8_t oidValue[] = { PREFIX_1_2_840_113549, 1, 1, 2 }; + return TestSignatureAlgorithm(RSA_PKCS1(), TestDigestAlgorithmID::MD2, + SimpleAlgID(oidValue), false); +} + +// RFC 3279 Section 2.2.1 +TestSignatureAlgorithm md5WithRSAEncryption() +{ + static const uint8_t oidValue[] = { PREFIX_1_2_840_113549, 1, 1, 4 }; + return TestSignatureAlgorithm(RSA_PKCS1(), TestDigestAlgorithmID::MD5, + SimpleAlgID(oidValue), false); +} + +// RFC 3279 Section 2.2.1 +TestSignatureAlgorithm sha1WithRSAEncryption() +{ + static const uint8_t oidValue[] = { PREFIX_1_2_840_113549, 1, 1, 5 }; + return TestSignatureAlgorithm(RSA_PKCS1(), TestDigestAlgorithmID::SHA1, + SimpleAlgID(oidValue), true); +} + +// RFC 4055 Section 5 +TestSignatureAlgorithm sha256WithRSAEncryption() +{ + static const uint8_t oidValue[] = { PREFIX_1_2_840_113549, 1, 1, 11 }; + return TestSignatureAlgorithm(RSA_PKCS1(), TestDigestAlgorithmID::SHA256, + SimpleAlgID(oidValue), true); +} + +} } } // namespace mozilla::pkix diff --git a/security/nss/lib/mozpkix/test-lib/pkixtestnss.cpp b/security/nss/lib/mozpkix/test-lib/pkixtestnss.cpp new file mode 100644 index 000000000..ee59b1d97 --- /dev/null +++ b/security/nss/lib/mozpkix/test-lib/pkixtestnss.cpp @@ -0,0 +1,364 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mozpkix/test/pkixtestutil.h" +#include "mozpkix/test/pkixtestnss.h" + +#include <limits> + +#include "cryptohi.h" +#include "keyhi.h" +#include "nss.h" +#include "pk11pqg.h" +#include "pk11pub.h" +#include "mozpkix/nss_scoped_ptrs.h" +#include "mozpkix/pkixnss.h" +#include "mozpkix/pkixder.h" +#include "mozpkix/pkixutil.h" +#include "prinit.h" +#include "secerr.h" +#include "secitem.h" + +namespace mozilla { namespace pkix { namespace test { + +namespace { + +TestKeyPair* GenerateKeyPairInner(); + +void +InitNSSIfNeeded() +{ + if (NSS_NoDB_Init(nullptr) != SECSuccess) { + abort(); + } +} + +static ScopedTestKeyPair reusedKeyPair; + +PRStatus +InitReusedKeyPair() +{ + InitNSSIfNeeded(); + reusedKeyPair.reset(GenerateKeyPairInner()); + return reusedKeyPair ? PR_SUCCESS : PR_FAILURE; +} + +class NSSTestKeyPair final : public TestKeyPair +{ +public: + NSSTestKeyPair(const TestPublicKeyAlgorithm& aPublicKeyAlg, + const ByteString& spk, + const ByteString& aEncryptedPrivateKey, + const ByteString& aEncryptionAlgorithm, + const ByteString& aEncryptionParams) + : TestKeyPair(aPublicKeyAlg, spk) + , encryptedPrivateKey(aEncryptedPrivateKey) + , encryptionAlgorithm(aEncryptionAlgorithm) + , encryptionParams(aEncryptionParams) + { + } + + Result SignData(const ByteString& tbs, + const TestSignatureAlgorithm& signatureAlgorithm, + /*out*/ ByteString& signature) const override + { + SECOidTag oidTag; + if (signatureAlgorithm.publicKeyAlg == RSA_PKCS1()) { + switch (signatureAlgorithm.digestAlg) { + case TestDigestAlgorithmID::MD2: + oidTag = SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION; + break; + case TestDigestAlgorithmID::MD5: + oidTag = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION; + break; + case TestDigestAlgorithmID::SHA1: + oidTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; + break; + case TestDigestAlgorithmID::SHA224: + oidTag = SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION; + break; + case TestDigestAlgorithmID::SHA256: + oidTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; + break; + case TestDigestAlgorithmID::SHA384: + oidTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; + break; + case TestDigestAlgorithmID::SHA512: + oidTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; + break; + MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM + } + } else { + abort(); + } + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) { + return MapPRErrorCodeToResult(PR_GetError()); + } + SECItem encryptedPrivateKeyInfoItem = { + siBuffer, + const_cast<uint8_t*>(encryptedPrivateKey.data()), + static_cast<unsigned int>(encryptedPrivateKey.length()) + }; + SECItem encryptionAlgorithmItem = { + siBuffer, + const_cast<uint8_t*>(encryptionAlgorithm.data()), + static_cast<unsigned int>(encryptionAlgorithm.length()) + }; + SECItem encryptionParamsItem = { + siBuffer, + const_cast<uint8_t*>(encryptionParams.data()), + static_cast<unsigned int>(encryptionParams.length()) + }; + SECKEYEncryptedPrivateKeyInfo encryptedPrivateKeyInfo = { + nullptr, + { encryptionAlgorithmItem, encryptionParamsItem }, + encryptedPrivateKeyInfoItem + }; + SECItem passwordItem = { siBuffer, nullptr, 0 }; + SECItem publicValueItem = { + siBuffer, + const_cast<uint8_t*>(subjectPublicKey.data()), + static_cast<unsigned int>(subjectPublicKey.length()) + }; + SECKEYPrivateKey* privateKey; + // This should always be an RSA key (we'll have aborted above if we're not + // doing an RSA signature). + if (PK11_ImportEncryptedPrivateKeyInfoAndReturnKey( + slot.get(), &encryptedPrivateKeyInfo, &passwordItem, nullptr, + &publicValueItem, false, false, rsaKey, KU_ALL, &privateKey, + nullptr) != SECSuccess) { + return MapPRErrorCodeToResult(PR_GetError()); + } + ScopedSECKEYPrivateKey scopedPrivateKey(privateKey); + SECItem signatureItem; + if (SEC_SignData(&signatureItem, tbs.data(), + static_cast<int>(tbs.length()), + scopedPrivateKey.get(), oidTag) != SECSuccess) { + return MapPRErrorCodeToResult(PR_GetError()); + } + signature.assign(signatureItem.data, signatureItem.len); + SECITEM_FreeItem(&signatureItem, false); + return Success; + } + + TestKeyPair* Clone() const override + { + return new (std::nothrow) NSSTestKeyPair(publicKeyAlg, + subjectPublicKey, + encryptedPrivateKey, + encryptionAlgorithm, + encryptionParams); + } + +private: + const ByteString encryptedPrivateKey; + const ByteString encryptionAlgorithm; + const ByteString encryptionParams; +}; + +} // namespace + +// This private function is also used by Gecko's PSM test framework +// (OCSPCommon.cpp). +TestKeyPair* CreateTestKeyPair(const TestPublicKeyAlgorithm publicKeyAlg, + const ScopedSECKEYPublicKey& publicKey, + const ScopedSECKEYPrivateKey& privateKey) +{ + ScopedCERTSubjectPublicKeyInfo + spki(SECKEY_CreateSubjectPublicKeyInfo(publicKey.get())); + if (!spki) { + return nullptr; + } + SECItem spkDER = spki->subjectPublicKey; + DER_ConvertBitString(&spkDER); // bits to bytes + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) { + return nullptr; + } + // Because NSSTestKeyPair isn't tracked by XPCOM and won't otherwise be aware + // of shutdown, we don't have a way to release NSS resources at the + // appropriate time. To work around this, NSSTestKeyPair doesn't hold on to + // NSS resources. Instead, we export the generated private key part as an + // encrypted blob (with an empty password and fairly lame encryption). When we + // need to use it (e.g. to sign something), we decrypt it and create a + // temporary key object. + SECItem passwordItem = { siBuffer, nullptr, 0 }; + ScopedSECKEYEncryptedPrivateKeyInfo encryptedPrivateKey( + PK11_ExportEncryptedPrivKeyInfo( + slot.get(), SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC, + &passwordItem, privateKey.get(), 1, nullptr)); + if (!encryptedPrivateKey) { + return nullptr; + } + + return new (std::nothrow) NSSTestKeyPair( + publicKeyAlg, + ByteString(spkDER.data, spkDER.len), + ByteString(encryptedPrivateKey->encryptedData.data, + encryptedPrivateKey->encryptedData.len), + ByteString(encryptedPrivateKey->algorithm.algorithm.data, + encryptedPrivateKey->algorithm.algorithm.len), + ByteString(encryptedPrivateKey->algorithm.parameters.data, + encryptedPrivateKey->algorithm.parameters.len)); +} + +namespace { + +TestKeyPair* +GenerateKeyPairInner() +{ + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) { + abort(); + } + + // Bug 1012786: PK11_GenerateKeyPair can fail if there is insufficient + // entropy to generate a random key. Attempting to add some entropy and + // retrying appears to solve this issue. + for (uint32_t retries = 0; retries < 10; retries++) { + PK11RSAGenParams params; + params.keySizeInBits = 2048; + params.pe = 3; + SECKEYPublicKey* publicKeyTemp = nullptr; + ScopedSECKEYPrivateKey + privateKey(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, + ¶ms, &publicKeyTemp, false, true, + nullptr)); + ScopedSECKEYPublicKey publicKey(publicKeyTemp); + if (privateKey) { + return CreateTestKeyPair(RSA_PKCS1(), publicKey, privateKey); + } + + assert(!publicKeyTemp); + + if (PR_GetError() != SEC_ERROR_PKCS11_FUNCTION_FAILED) { + break; + } + + // Since these keys are only for testing, we don't need them to be good, + // random keys. + // https://xkcd.com/221/ + static const uint8_t RANDOM_NUMBER[] = { 4, 4, 4, 4, 4, 4, 4, 4 }; + if (PK11_RandomUpdate((void*) &RANDOM_NUMBER, + sizeof(RANDOM_NUMBER)) != SECSuccess) { + break; + } + } + + abort(); +} + +} // namespace + +TestKeyPair* +GenerateKeyPair() +{ + InitNSSIfNeeded(); + return GenerateKeyPairInner(); +} + +TestKeyPair* +CloneReusedKeyPair() +{ + static PRCallOnceType initCallOnce; + if (PR_CallOnce(&initCallOnce, InitReusedKeyPair) != PR_SUCCESS) { + abort(); + } + assert(reusedKeyPair); + return reusedKeyPair->Clone(); +} + +TestKeyPair* +GenerateDSSKeyPair() +{ + InitNSSIfNeeded(); + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) { + return nullptr; + } + + ByteString p(DSS_P()); + ByteString q(DSS_Q()); + ByteString g(DSS_G()); + + static const PQGParams PARAMS = { + nullptr, + { siBuffer, + const_cast<uint8_t*>(p.data()), + static_cast<unsigned int>(p.length()) + }, + { siBuffer, + const_cast<uint8_t*>(q.data()), + static_cast<unsigned int>(q.length()) + }, + { siBuffer, + const_cast<uint8_t*>(g.data()), + static_cast<unsigned int>(g.length()) + } + }; + + SECKEYPublicKey* publicKeyTemp = nullptr; + ScopedSECKEYPrivateKey + privateKey(PK11_GenerateKeyPair(slot.get(), CKM_DSA_KEY_PAIR_GEN, + const_cast<PQGParams*>(&PARAMS), + &publicKeyTemp, false, true, nullptr)); + if (!privateKey) { + return nullptr; + } + ScopedSECKEYPublicKey publicKey(publicKeyTemp); + return CreateTestKeyPair(DSS(), publicKey, privateKey); +} + +Result +TestVerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) +{ + InitNSSIfNeeded(); + return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo, + nullptr); +} + +Result +TestVerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) +{ + InitNSSIfNeeded(); + return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo, + nullptr); +} + +Result +TestDigestBuf(Input item, + DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, + size_t digestBufLen) +{ + InitNSSIfNeeded(); + return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen); +} + +} } } // namespace mozilla::pkix::test diff --git a/security/nss/lib/mozpkix/test-lib/pkixtestutil.cpp b/security/nss/lib/mozpkix/test-lib/pkixtestutil.cpp new file mode 100644 index 000000000..b1b89c07e --- /dev/null +++ b/security/nss/lib/mozpkix/test-lib/pkixtestutil.cpp @@ -0,0 +1,1155 @@ +/* -*- 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 code is made available to you under your choice of the following sets + * of licensing terms: + */ +/* 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/. + */ +/* Copyright 2013 Mozilla Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mozpkix/test/pkixtestutil.h" + +#include <cerrno> +#include <cstdio> +#include <limits> +#include <new> +#include <sstream> +#include <cstdlib> + +#include "mozpkix/pkixder.h" +#include "mozpkix/pkixutil.h" + +using namespace std; + +namespace mozilla { namespace pkix { namespace test { + +namespace { + +struct ScopedMaybeDeleteFile { + void operator()(FILE* f) { + if (f) { + (void)fclose(f); + } + } +}; +typedef std::unique_ptr<FILE, ScopedMaybeDeleteFile> ScopedFILE; + +FILE* +OpenFile(const string& dir, const string& filename, const string& mode) +{ + string path = dir + '/' + filename; + + ScopedFILE file; +#ifdef _MSC_VER + { + FILE* rawFile; + errno_t error = fopen_s(&rawFile, path.c_str(), mode.c_str()); + if (error) { + // TODO: map error to NSPR error code + rawFile = nullptr; + } + file.reset(rawFile); + } +#else + file.reset(fopen(path.c_str(), mode.c_str())); +#endif + return file.release(); +} + +} // namespace + +bool +InputEqualsByteString(Input input, const ByteString& bs) +{ + Input bsInput; + if (bsInput.Init(bs.data(), bs.length()) != Success) { + // Init can only fail if it is given a bad pointer or if the input is too + // long, which won't ever happen. Plus, if it does, it is ok to call abort + // since this is only test code. + abort(); + } + return InputsAreEqual(input, bsInput); +} + +ByteString +InputToByteString(Input input) +{ + ByteString result; + Reader reader(input); + for (;;) { + uint8_t b; + if (reader.Read(b) != Success) { + return result; + } + result.push_back(b); + } +} + +Result +TamperOnce(/*in/out*/ ByteString& item, const ByteString& from, + const ByteString& to) +{ + if (from.length() < 8) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + if (from.length() != to.length()) { + return Result::FATAL_ERROR_INVALID_ARGS; + } + size_t pos = item.find(from); + if (pos == string::npos) { + return Result::FATAL_ERROR_INVALID_ARGS; // No matches. + } + if (item.find(from, pos + from.length()) != string::npos) { + return Result::FATAL_ERROR_INVALID_ARGS; // More than once match. + } + item.replace(pos, from.length(), to); + return Success; +} + +// Given a tag and a value, generates a DER-encoded tag-length-value item. +ByteString +TLV(uint8_t tag, size_t length, const ByteString& value) +{ + ByteString result; + result.push_back(tag); + + if (value.length() < 128) { + result.push_back(static_cast<uint8_t>(length)); + } else if (value.length() < 256) { + result.push_back(0x81u); + result.push_back(static_cast<uint8_t>(length)); + } else if (value.length() < 65536) { + result.push_back(0x82u); + result.push_back(static_cast<uint8_t>(length / 256)); + result.push_back(static_cast<uint8_t>(length % 256)); + } else { + // It is MUCH more convenient for TLV to be infallible than for it to have + // "proper" error handling. + abort(); + } + result.append(value); + return result; +} + +OCSPResponseExtension::OCSPResponseExtension() + : id() + , critical(false) + , value() + , next(nullptr) +{ +} + +OCSPResponseContext::OCSPResponseContext(const CertID& aCertID, time_t time) + : certID(aCertID) + , responseStatus(successful) + , skipResponseBytes(false) + , producedAt(time) + , singleExtensions(nullptr) + , responseExtensions(nullptr) + , includeEmptyExtensions(false) + , signatureAlgorithm(sha256WithRSAEncryption()) + , badSignature(false) + , certs(nullptr) + + , certStatus(good) + , revocationTime(0) + , thisUpdate(time) + , nextUpdate(time + static_cast<time_t>(Time::ONE_DAY_IN_SECONDS)) + , includeNextUpdate(true) +{ +} + +static ByteString ResponseBytes(OCSPResponseContext& context); +static ByteString BasicOCSPResponse(OCSPResponseContext& context); +static ByteString ResponseData(OCSPResponseContext& context); +static ByteString ResponderID(OCSPResponseContext& context); +static ByteString KeyHash(const ByteString& subjectPublicKeyInfo); +static ByteString SingleResponse(OCSPResponseContext& context); +static ByteString CertID(OCSPResponseContext& context); +static ByteString CertStatus(OCSPResponseContext& context); + +static ByteString +SHA1(const ByteString& toHash) +{ + uint8_t digestBuf[20]; + Input input; + if (input.Init(toHash.data(), toHash.length()) != Success) { + abort(); + } + Result rv = TestDigestBuf(input, DigestAlgorithm::sha1, digestBuf, + sizeof(digestBuf)); + if (rv != Success) { + abort(); + } + return ByteString(digestBuf, sizeof(digestBuf)); +} + +static ByteString +HashedOctetString(const ByteString& bytes) +{ + ByteString digest(SHA1(bytes)); + if (ENCODING_FAILED(digest)) { + return ByteString(); + } + return TLV(der::OCTET_STRING, digest); +} + +static ByteString +BitString(const ByteString& rawBytes, bool corrupt) +{ + ByteString prefixed; + // We have to add a byte at the beginning indicating no unused bits. + // TODO: add ability to have bit strings of bit length not divisible by 8, + // resulting in unused bits in the bitstring encoding + prefixed.push_back(0); + prefixed.append(rawBytes); + if (corrupt) { + assert(prefixed.length() > 8); + prefixed[8]++; + } + return TLV(der::BIT_STRING, prefixed); +} + +ByteString +Boolean(bool value) +{ + ByteString encodedValue; + encodedValue.push_back(value ? 0xffu : 0x00u); + return TLV(der::BOOLEAN, encodedValue); +} + +ByteString +Integer(long value) +{ + if (value < 0 || value > 127) { + // TODO: add encoding of larger values + // It is MUCH more convenient for Integer to be infallible than for it to + // have "proper" error handling. + abort(); + } + + ByteString encodedValue; + encodedValue.push_back(static_cast<uint8_t>(value)); + return TLV(der::INTEGER, encodedValue); +} + +enum TimeEncoding { UTCTime = 0, GeneralizedTime = 1 }; + +// Windows doesn't provide gmtime_r, but it provides something very similar. +#if defined(_WINDOWS) && (!defined(_POSIX_C_SOURCE) || !defined(_POSIX_THREAD_SAFE_FUNCTIONS)) +static tm* +gmtime_r(const time_t* t, /*out*/ tm* exploded) +{ + if (gmtime_s(exploded, t) != 0) { + return nullptr; + } + return exploded; +} +#endif + +// http://tools.ietf.org/html/rfc5280#section-4.1.2.5 +// UTCTime: YYMMDDHHMMSSZ (years 1950-2049 only) +// GeneralizedTime: YYYYMMDDHHMMSSZ +// +// This assumes that time/time_t are POSIX-compliant in that time() returns +// the number of seconds since the Unix epoch. +static ByteString +TimeToEncodedTime(time_t time, TimeEncoding encoding) +{ + assert(encoding == UTCTime || encoding == GeneralizedTime); + + tm exploded; + if (!gmtime_r(&time, &exploded)) { + return ByteString(); + } + + if (exploded.tm_sec >= 60) { + // round down for leap seconds + exploded.tm_sec = 59; + } + + // exploded.tm_year is the year offset by 1900. + int year = exploded.tm_year + 1900; + + if (encoding == UTCTime && (year < 1950 || year >= 2050)) { + return ByteString(); + } + + ByteString value; + + if (encoding == GeneralizedTime) { + value.push_back(static_cast<uint8_t>('0' + (year / 1000))); + value.push_back(static_cast<uint8_t>('0' + ((year % 1000) / 100))); + } + + value.push_back(static_cast<uint8_t>('0' + ((year % 100) / 10))); + value.push_back(static_cast<uint8_t>('0' + (year % 10))); + value.push_back(static_cast<uint8_t>('0' + ((exploded.tm_mon + 1) / 10))); + value.push_back(static_cast<uint8_t>('0' + ((exploded.tm_mon + 1) % 10))); + value.push_back(static_cast<uint8_t>('0' + (exploded.tm_mday / 10))); + value.push_back(static_cast<uint8_t>('0' + (exploded.tm_mday % 10))); + value.push_back(static_cast<uint8_t>('0' + (exploded.tm_hour / 10))); + value.push_back(static_cast<uint8_t>('0' + (exploded.tm_hour % 10))); + value.push_back(static_cast<uint8_t>('0' + (exploded.tm_min / 10))); + value.push_back(static_cast<uint8_t>('0' + (exploded.tm_min % 10))); + value.push_back(static_cast<uint8_t>('0' + (exploded.tm_sec / 10))); + value.push_back(static_cast<uint8_t>('0' + (exploded.tm_sec % 10))); + value.push_back('Z'); + + return TLV(encoding == GeneralizedTime ? der::GENERALIZED_TIME : der::UTCTime, + value); +} + +static ByteString +TimeToGeneralizedTime(time_t time) +{ + return TimeToEncodedTime(time, GeneralizedTime); +} + +// http://tools.ietf.org/html/rfc5280#section-4.1.2.5: "CAs conforming to this +// profile MUST always encode certificate validity dates through the year 2049 +// as UTCTime; certificate validity dates in 2050 or later MUST be encoded as +// GeneralizedTime." (This is a special case of the rule that we must always +// use the shortest possible encoding.) +static ByteString +TimeToTimeChoice(time_t time) +{ + tm exploded; + if (!gmtime_r(&time, &exploded)) { + return ByteString(); + } + TimeEncoding encoding = (exploded.tm_year + 1900 >= 1950 && + exploded.tm_year + 1900 < 2050) + ? UTCTime + : GeneralizedTime; + + return TimeToEncodedTime(time, encoding); +} + +Time +YMDHMS(uint16_t year, uint16_t month, uint16_t day, + uint16_t hour, uint16_t minutes, uint16_t seconds) +{ + assert(year <= 9999); + assert(month >= 1); + assert(month <= 12); + assert(day >= 1); + assert(hour < 24); + assert(minutes < 60); + assert(seconds < 60); + + uint64_t days = DaysBeforeYear(year); + + { + static const int16_t DAYS_IN_MONTH[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + + int16_t i = 1; + for (;;) { + int16_t daysInMonth = DAYS_IN_MONTH[i - 1]; + if (i == 2 && + ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)))) { + // Add leap day + ++daysInMonth; + } + if (i == month) { + assert(day <= daysInMonth); + break; + } + days += daysInMonth; + ++i; + } + } + + days += (day - 1); + + uint64_t totalSeconds = days * Time::ONE_DAY_IN_SECONDS; + totalSeconds += hour * 60 * 60; + totalSeconds += minutes * 60; + totalSeconds += seconds; + return TimeFromElapsedSecondsAD(totalSeconds); +} + +static ByteString +SignedData(const ByteString& tbsData, + const TestKeyPair& keyPair, + const TestSignatureAlgorithm& signatureAlgorithm, + bool corrupt, /*optional*/ const ByteString* certs) +{ + ByteString signature; + if (keyPair.SignData(tbsData, signatureAlgorithm, signature) != Success) { + return ByteString(); + } + + // TODO: add ability to have signatures of bit length not divisible by 8, + // resulting in unused bits in the bitstring encoding + ByteString signatureNested(BitString(signature, corrupt)); + if (ENCODING_FAILED(signatureNested)) { + return ByteString(); + } + + ByteString certsNested; + if (certs) { + ByteString certsSequenceValue; + while (!(*certs).empty()) { + certsSequenceValue.append(*certs); + ++certs; + } + ByteString certsSequence(TLV(der::SEQUENCE, certsSequenceValue)); + certsNested = TLV(der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0, + certsSequence); + } + + ByteString value; + value.append(tbsData); + value.append(signatureAlgorithm.algorithmIdentifier); + value.append(signatureNested); + value.append(certsNested); + return TLV(der::SEQUENCE, value); +} + +// Extension ::= SEQUENCE { +// extnID OBJECT IDENTIFIER, +// critical BOOLEAN DEFAULT FALSE, +// extnValue OCTET STRING +// -- contains the DER encoding of an ASN.1 value +// -- corresponding to the extension type identified +// -- by extnID +// } +static ByteString +Extension(Input extnID, Critical critical, const ByteString& extnValueBytes) +{ + ByteString encoded; + + encoded.append(ByteString(extnID.UnsafeGetData(), extnID.GetLength())); + + if (critical == Critical::Yes) { + encoded.append(Boolean(true)); + } + + ByteString extnValueSequence(TLV(der::SEQUENCE, extnValueBytes)); + ByteString extnValue(TLV(der::OCTET_STRING, extnValueSequence)); + encoded.append(extnValue); + return TLV(der::SEQUENCE, encoded); +} + +static ByteString +EmptyExtension(Input extnID, Critical critical) +{ + ByteString encoded(extnID.UnsafeGetData(), extnID.GetLength()); + + if (critical == Critical::Yes) { + encoded.append(Boolean(true)); + } + + ByteString extnValue(TLV(der::OCTET_STRING, ByteString())); + encoded.append(extnValue); + return TLV(der::SEQUENCE, encoded); +} + +std::string +GetEnv(const char* name) +{ + std::string result; + +#ifndef _MSC_VER + // XXX: Not thread safe. + const char* value = getenv(name); + if (value) { + result = value; + } +#else + char* value = nullptr; + size_t valueLength = 0; + if (_dupenv_s(&value, &valueLength, name) != 0) { + abort(); + } + if (value) { + result = value; + free(value); + } +#endif + return result; +} + +void +MaybeLogOutput(const ByteString& result, const char* suffix) +{ + assert(suffix); + + // This allows us to more easily debug the generated output, by creating a + // file in the directory given by MOZILLA_PKIX_TEST_LOG_DIR for each + // NOT THREAD-SAFE!!! + std::string logPath(GetEnv("MOZILLA_PKIX_TEST_LOG_DIR")); + if (!logPath.empty()) { + static int counter = 0; + + std::ostringstream counterStream; + counterStream << counter; + if (!counterStream) { + assert(false); + return; + } + string filename = counterStream.str() + '-' + suffix + ".der"; + + ++counter; + ScopedFILE file(OpenFile(logPath, filename, "wb")); + if (file) { + (void) fwrite(result.data(), result.length(), 1, file.get()); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Certificates + +static ByteString TBSCertificate(long version, const ByteString& serialNumber, + const ByteString& signature, + const ByteString& issuer, + time_t notBefore, time_t notAfter, + const ByteString& subject, + const ByteString& subjectPublicKeyInfo, + /*optional*/ const ByteString* extensions); + +// Certificate ::= SEQUENCE { +// tbsCertificate TBSCertificate, +// signatureAlgorithm AlgorithmIdentifier, +// signatureValue BIT STRING } +ByteString +CreateEncodedCertificate(long version, + const TestSignatureAlgorithm& signature, + const ByteString& serialNumber, + const ByteString& issuerNameDER, + time_t notBefore, time_t notAfter, + const ByteString& subjectNameDER, + const TestKeyPair& subjectKeyPair, + /*optional*/ const ByteString* extensions, + const TestKeyPair& issuerKeyPair, + const TestSignatureAlgorithm& signatureAlgorithm) +{ + ByteString tbsCertificate(TBSCertificate(version, serialNumber, + signature.algorithmIdentifier, + issuerNameDER, notBefore, + notAfter, subjectNameDER, + subjectKeyPair.subjectPublicKeyInfo, + extensions)); + if (ENCODING_FAILED(tbsCertificate)) { + return ByteString(); + } + + ByteString result(SignedData(tbsCertificate, issuerKeyPair, + signatureAlgorithm, false, nullptr)); + if (ENCODING_FAILED(result)) { + return ByteString(); + } + + MaybeLogOutput(result, "cert"); + + return result; +} + +// TBSCertificate ::= SEQUENCE { +// version [0] Version DEFAULT v1, +// serialNumber CertificateSerialNumber, +// signature AlgorithmIdentifier, +// issuer Name, +// validity Validity, +// subject Name, +// subjectPublicKeyInfo SubjectPublicKeyInfo, +// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, +// -- If present, version MUST be v2 or v3 +// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, +// -- If present, version MUST be v2 or v3 +// extensions [3] Extensions OPTIONAL +// -- If present, version MUST be v3 -- } +static ByteString +TBSCertificate(long versionValue, + const ByteString& serialNumber, const ByteString& signature, + const ByteString& issuer, time_t notBeforeTime, + time_t notAfterTime, const ByteString& subject, + const ByteString& subjectPublicKeyInfo, + /*optional*/ const ByteString* extensions) +{ + ByteString value; + + if (versionValue != static_cast<long>(der::Version::v1)) { + ByteString versionInteger(Integer(versionValue)); + ByteString version(TLV(der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0, + versionInteger)); + value.append(version); + } + + value.append(serialNumber); + value.append(signature); + value.append(issuer); + + // Validity ::= SEQUENCE { + // notBefore Time, + // notAfter Time } + ByteString validity; + { + ByteString notBefore(TimeToTimeChoice(notBeforeTime)); + if (ENCODING_FAILED(notBefore)) { + return ByteString(); + } + ByteString notAfter(TimeToTimeChoice(notAfterTime)); + if (ENCODING_FAILED(notAfter)) { + return ByteString(); + } + ByteString validityValue; + validityValue.append(notBefore); + validityValue.append(notAfter); + validity = TLV(der::SEQUENCE, validityValue); + if (ENCODING_FAILED(validity)) { + return ByteString(); + } + } + value.append(validity); + + value.append(subject); + + value.append(subjectPublicKeyInfo); + + if (extensions) { + ByteString extensionsValue; + while (!(*extensions).empty()) { + extensionsValue.append(*extensions); + ++extensions; + } + ByteString extensionsSequence(TLV(der::SEQUENCE, extensionsValue)); + if (ENCODING_FAILED(extensionsSequence)) { + return ByteString(); + } + ByteString extensionsWrapped( + TLV(der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 3, extensionsSequence)); + if (ENCODING_FAILED(extensionsWrapped)) { + return ByteString(); + } + value.append(extensionsWrapped); + } + + return TLV(der::SEQUENCE, value); +} + +// AttributeTypeAndValue ::= SEQUENCE { +// type AttributeType, +// value AttributeValue } +// +// AttributeType ::= OBJECT IDENTIFIER +// +// AttributeValue ::= ANY -- DEFINED BY AttributeType +// +// DirectoryString ::= CHOICE { +// teletexString TeletexString (SIZE (1..MAX)), +// printableString PrintableString (SIZE (1..MAX)), +// universalString UniversalString (SIZE (1..MAX)), +// utf8String UTF8String (SIZE (1..MAX)), +// bmpString BMPString (SIZE (1..MAX)) } +template <size_t N> +static ByteString +AVA(const uint8_t (&type)[N], uint8_t directoryStringType, + const ByteString& value) +{ + ByteString wrappedValue(TLV(directoryStringType, value)); + ByteString ava; + ava.append(type, N); + ava.append(wrappedValue); + return TLV(der::SEQUENCE, ava); +} + +ByteString +CN(const ByteString& value, uint8_t encodingTag) +{ + // id-at OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) ds(5) 4 } + // id-at-commonName AttributeType ::= { id-at 3 } + // python DottedOIDToCode.py --tlv id-at-commonName 2.5.4.3 + static const uint8_t tlv_id_at_commonName[] = { + 0x06, 0x03, 0x55, 0x04, 0x03 + }; + return AVA(tlv_id_at_commonName, encodingTag, value); +} + +ByteString +OU(const ByteString& value, uint8_t encodingTag) +{ + // id-at OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) ds(5) 4 } + // id-at-organizationalUnitName AttributeType ::= { id-at 11 } + // python DottedOIDToCode.py --tlv id-at-organizationalUnitName 2.5.4.11 + static const uint8_t tlv_id_at_organizationalUnitName[] = { + 0x06, 0x03, 0x55, 0x04, 0x0b + }; + + return AVA(tlv_id_at_organizationalUnitName, encodingTag, value); +} + +ByteString +emailAddress(const ByteString& value) +{ + // id-emailAddress AttributeType ::= { pkcs-9 1 } + // python DottedOIDToCode.py --tlv id-emailAddress 1.2.840.113549.1.9.1 + static const uint8_t tlv_id_emailAddress[] = { + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01 + }; + + return AVA(tlv_id_emailAddress, der::IA5String, value); +} + +// RelativeDistinguishedName ::= +// SET SIZE (1..MAX) OF AttributeTypeAndValue +// +ByteString +RDN(const ByteString& avas) +{ + return TLV(der::SET, avas); +} + +// Name ::= CHOICE { -- only one possibility for now -- +// rdnSequence RDNSequence } +// +// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName +// +ByteString +Name(const ByteString& rdns) +{ + return TLV(der::SEQUENCE, rdns); +} + +ByteString +CreateEncodedSerialNumber(long serialNumberValue) +{ + return Integer(serialNumberValue); +} + +// BasicConstraints ::= SEQUENCE { +// cA BOOLEAN DEFAULT FALSE, +// pathLenConstraint INTEGER (0..MAX) OPTIONAL } +ByteString +CreateEncodedBasicConstraints(bool isCA, + /*optional in*/ const long* pathLenConstraintValue, + Critical critical) +{ + ByteString value; + + if (isCA) { + ByteString cA(Boolean(true)); + value.append(cA); + } + + if (pathLenConstraintValue) { + ByteString pathLenConstraint(Integer(*pathLenConstraintValue)); + value.append(pathLenConstraint); + } + + // python DottedOIDToCode.py --tlv id-ce-basicConstraints 2.5.29.19 + static const uint8_t tlv_id_ce_basicConstraints[] = { + 0x06, 0x03, 0x55, 0x1d, 0x13 + }; + return Extension(Input(tlv_id_ce_basicConstraints), critical, value); +} + +// ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId +// KeyPurposeId ::= OBJECT IDENTIFIER +ByteString +CreateEncodedEKUExtension(Input ekuOID, Critical critical) +{ + ByteString value(ekuOID.UnsafeGetData(), ekuOID.GetLength()); + + // python DottedOIDToCode.py --tlv id-ce-extKeyUsage 2.5.29.37 + static const uint8_t tlv_id_ce_extKeyUsage[] = { + 0x06, 0x03, 0x55, 0x1d, 0x25 + }; + + return Extension(Input(tlv_id_ce_extKeyUsage), critical, value); +} + +// python DottedOIDToCode.py --tlv id-ce-subjectAltName 2.5.29.17 +static const uint8_t tlv_id_ce_subjectAltName[] = { + 0x06, 0x03, 0x55, 0x1d, 0x11 +}; + +ByteString +CreateEncodedSubjectAltName(const ByteString& names) +{ + return Extension(Input(tlv_id_ce_subjectAltName), Critical::No, names); +} + +ByteString +CreateEncodedEmptySubjectAltName() +{ + return EmptyExtension(Input(tlv_id_ce_subjectAltName), Critical::No); +} + +/////////////////////////////////////////////////////////////////////////////// +// OCSP responses + +ByteString +CreateEncodedOCSPResponse(OCSPResponseContext& context) +{ + if (!context.skipResponseBytes) { + if (!context.signerKeyPair) { + return ByteString(); + } + } + + // OCSPResponse ::= SEQUENCE { + // responseStatus OCSPResponseStatus, + // responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } + + // OCSPResponseStatus ::= ENUMERATED { + // successful (0), -- Response has valid confirmations + // malformedRequest (1), -- Illegal confirmation request + // internalError (2), -- Internal error in issuer + // tryLater (3), -- Try again later + // -- (4) is not used + // sigRequired (5), -- Must sign the request + // unauthorized (6) -- Request unauthorized + // } + ByteString reponseStatusValue; + reponseStatusValue.push_back(context.responseStatus); + ByteString responseStatus(TLV(der::ENUMERATED, reponseStatusValue)); + + ByteString responseBytesNested; + if (!context.skipResponseBytes) { + ByteString responseBytes(ResponseBytes(context)); + if (ENCODING_FAILED(responseBytes)) { + return ByteString(); + } + + responseBytesNested = TLV(der::CONSTRUCTED | der::CONTEXT_SPECIFIC, + responseBytes); + } + + ByteString value; + value.append(responseStatus); + value.append(responseBytesNested); + ByteString result(TLV(der::SEQUENCE, value)); + + MaybeLogOutput(result, "ocsp"); + + return result; +} + +// ResponseBytes ::= SEQUENCE { +// responseType OBJECT IDENTIFIER, +// response OCTET STRING } +ByteString +ResponseBytes(OCSPResponseContext& context) +{ + // Includes tag and length + static const uint8_t id_pkix_ocsp_basic_encoded[] = { + 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01 + }; + ByteString response(BasicOCSPResponse(context)); + if (ENCODING_FAILED(response)) { + return ByteString(); + } + ByteString responseNested = TLV(der::OCTET_STRING, response); + + ByteString value; + value.append(id_pkix_ocsp_basic_encoded, + sizeof(id_pkix_ocsp_basic_encoded)); + value.append(responseNested); + return TLV(der::SEQUENCE, value); +} + +// BasicOCSPResponse ::= SEQUENCE { +// tbsResponseData ResponseData, +// signatureAlgorithm AlgorithmIdentifier, +// signature BIT STRING, +// certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } +ByteString +BasicOCSPResponse(OCSPResponseContext& context) +{ + ByteString tbsResponseData(ResponseData(context)); + if (ENCODING_FAILED(tbsResponseData)) { + return ByteString(); + } + + return SignedData(tbsResponseData, *context.signerKeyPair, + context.signatureAlgorithm, context.badSignature, + context.certs); +} + +// Extension ::= SEQUENCE { +// id OBJECT IDENTIFIER, +// critical BOOLEAN DEFAULT FALSE +// value OCTET STRING +// } +static ByteString +OCSPExtension(OCSPResponseExtension& extension) +{ + ByteString encoded; + encoded.append(extension.id); + if (extension.critical) { + encoded.append(Boolean(true)); + } + ByteString value(TLV(der::OCTET_STRING, extension.value)); + encoded.append(value); + return TLV(der::SEQUENCE, encoded); +} + +// Extensions ::= [1] { +// SEQUENCE OF Extension +// } +static ByteString +OCSPExtensions(OCSPResponseExtension* extensions) +{ + ByteString value; + for (OCSPResponseExtension* extension = extensions; + extension; extension = extension->next) { + ByteString extensionEncoded(OCSPExtension(*extension)); + if (ENCODING_FAILED(extensionEncoded)) { + return ByteString(); + } + value.append(extensionEncoded); + } + ByteString sequence(TLV(der::SEQUENCE, value)); + return TLV(der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 1, sequence); +} + +// ResponseData ::= SEQUENCE { +// version [0] EXPLICIT Version DEFAULT v1, +// responderID ResponderID, +// producedAt GeneralizedTime, +// responses SEQUENCE OF SingleResponse, +// responseExtensions [1] EXPLICIT Extensions OPTIONAL } +ByteString +ResponseData(OCSPResponseContext& context) +{ + ByteString responderID(ResponderID(context)); + if (ENCODING_FAILED(responderID)) { + return ByteString(); + } + ByteString producedAtEncoded(TimeToGeneralizedTime(context.producedAt)); + if (ENCODING_FAILED(producedAtEncoded)) { + return ByteString(); + } + ByteString response(SingleResponse(context)); + if (ENCODING_FAILED(response)) { + return ByteString(); + } + ByteString responses(TLV(der::SEQUENCE, response)); + ByteString responseExtensions; + if (context.responseExtensions || context.includeEmptyExtensions) { + responseExtensions = OCSPExtensions(context.responseExtensions); + } + + ByteString value; + value.append(responderID); + value.append(producedAtEncoded); + value.append(responses); + value.append(responseExtensions); + return TLV(der::SEQUENCE, value); +} + +// ResponderID ::= CHOICE { +// byName [1] Name, +// byKey [2] KeyHash } +// } +ByteString +ResponderID(OCSPResponseContext& context) +{ + ByteString contents; + uint8_t responderIDType; + if (!context.signerNameDER.empty()) { + contents = context.signerNameDER; + responderIDType = 1; // byName + } else { + contents = KeyHash(context.signerKeyPair->subjectPublicKey); + if (ENCODING_FAILED(contents)) { + return ByteString(); + } + responderIDType = 2; // byKey + } + + // XXX: MSVC 2015 wrongly warns about signed/unsigned conversion without the + // static_cast. + uint8_t tag = static_cast<uint8_t>(der::CONSTRUCTED | der::CONTEXT_SPECIFIC | + responderIDType); + return TLV(tag, contents); +} + +// KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key +// -- (i.e., the SHA-1 hash of the value of the +// -- BIT STRING subjectPublicKey [excluding +// -- the tag, length, and number of unused +// -- bits] in the responder's certificate) +ByteString +KeyHash(const ByteString& subjectPublicKey) +{ + return HashedOctetString(subjectPublicKey); +} + +// SingleResponse ::= SEQUENCE { +// certID CertID, +// certStatus CertStatus, +// thisUpdate GeneralizedTime, +// nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, +// singleExtensions [1] EXPLICIT Extensions OPTIONAL } +ByteString +SingleResponse(OCSPResponseContext& context) +{ + ByteString certID(CertID(context)); + if (ENCODING_FAILED(certID)) { + return ByteString(); + } + ByteString certStatus(CertStatus(context)); + if (ENCODING_FAILED(certStatus)) { + return ByteString(); + } + ByteString thisUpdateEncoded(TimeToGeneralizedTime(context.thisUpdate)); + if (ENCODING_FAILED(thisUpdateEncoded)) { + return ByteString(); + } + ByteString nextUpdateEncodedNested; + if (context.includeNextUpdate) { + ByteString nextUpdateEncoded(TimeToGeneralizedTime(context.nextUpdate)); + if (ENCODING_FAILED(nextUpdateEncoded)) { + return ByteString(); + } + nextUpdateEncodedNested = TLV(der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0, + nextUpdateEncoded); + } + ByteString singleExtensions; + if (context.singleExtensions || context.includeEmptyExtensions) { + singleExtensions = OCSPExtensions(context.singleExtensions); + } + + ByteString value; + value.append(certID); + value.append(certStatus); + value.append(thisUpdateEncoded); + value.append(nextUpdateEncodedNested); + value.append(singleExtensions); + return TLV(der::SEQUENCE, value); +} + +// CertID ::= SEQUENCE { +// hashAlgorithm AlgorithmIdentifier, +// issuerNameHash OCTET STRING, -- Hash of issuer's DN +// issuerKeyHash OCTET STRING, -- Hash of issuer's public key +// serialNumber CertificateSerialNumber } +ByteString +CertID(OCSPResponseContext& context) +{ + ByteString issuerName(context.certID.issuer.UnsafeGetData(), + context.certID.issuer.GetLength()); + ByteString issuerNameHash(HashedOctetString(issuerName)); + if (ENCODING_FAILED(issuerNameHash)) { + return ByteString(); + } + + ByteString issuerKeyHash; + { + // context.certID.issuerSubjectPublicKeyInfo is the entire + // SubjectPublicKeyInfo structure, but we need just the subjectPublicKey + // part. + Reader input(context.certID.issuerSubjectPublicKeyInfo); + Reader contents; + if (der::ExpectTagAndGetValue(input, der::SEQUENCE, contents) != Success) { + return ByteString(); + } + // Skip AlgorithmIdentifier + if (der::ExpectTagAndSkipValue(contents, der::SEQUENCE) != Success) { + return ByteString(); + } + Input subjectPublicKey; + if (der::BitStringWithNoUnusedBits(contents, subjectPublicKey) + != Success) { + return ByteString(); + } + issuerKeyHash = KeyHash(ByteString(subjectPublicKey.UnsafeGetData(), + subjectPublicKey.GetLength())); + if (ENCODING_FAILED(issuerKeyHash)) { + return ByteString(); + } + } + + ByteString serialNumberValue(context.certID.serialNumber.UnsafeGetData(), + context.certID.serialNumber.GetLength()); + ByteString serialNumber(TLV(der::INTEGER, serialNumberValue)); + + // python DottedOIDToCode.py --alg id-sha1 1.3.14.3.2.26 + static const uint8_t alg_id_sha1[] = { + 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a + }; + + ByteString value; + value.append(alg_id_sha1, sizeof(alg_id_sha1)); + value.append(issuerNameHash); + value.append(issuerKeyHash); + value.append(serialNumber); + return TLV(der::SEQUENCE, value); +} + +// CertStatus ::= CHOICE { +// good [0] IMPLICIT NULL, +// revoked [1] IMPLICIT RevokedInfo, +// unknown [2] IMPLICIT UnknownInfo } +// +// RevokedInfo ::= SEQUENCE { +// revocationTime GeneralizedTime, +// revocationReason [0] EXPLICIT CRLReason OPTIONAL } +// +// UnknownInfo ::= NULL +// +ByteString +CertStatus(OCSPResponseContext& context) +{ + switch (context.certStatus) { + // Both good and unknown are ultimately represented as NULL - the only + // difference is in the tag that identifies them. + case 0: + case 2: + { + // XXX: MSVC 2015 wrongly warns about signed/unsigned conversion without + // the static cast. + return TLV(static_cast<uint8_t>(der::CONTEXT_SPECIFIC | + context.certStatus), ByteString()); + } + case 1: + { + ByteString revocationTime(TimeToGeneralizedTime(context.revocationTime)); + if (ENCODING_FAILED(revocationTime)) { + return ByteString(); + } + // TODO(bug 980536): add support for revocationReason + return TLV(der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1, revocationTime); + } + default: + assert(false); + // fall through + } + return ByteString(); +} + +static const ByteString NO_UNUSED_BITS(1, 0x00); + +// The SubjectPublicKeyInfo syntax is specified in RFC 5280 Section 4.1. +TestKeyPair::TestKeyPair(const TestPublicKeyAlgorithm& aPublicKeyAlg, + const ByteString& spk) + : publicKeyAlg(aPublicKeyAlg) + , subjectPublicKeyInfo(TLV(der::SEQUENCE, + aPublicKeyAlg.algorithmIdentifier + + TLV(der::BIT_STRING, NO_UNUSED_BITS + spk))) + , subjectPublicKey(spk) +{ +} + +} } } // namespace mozilla::pkix::test diff --git a/security/nss/lib/mozpkix/tools/DottedOIDToCode.py b/security/nss/lib/mozpkix/tools/DottedOIDToCode.py new file mode 100644 index 000000000..dfd4ade07 --- /dev/null +++ b/security/nss/lib/mozpkix/tools/DottedOIDToCode.py @@ -0,0 +1,216 @@ +# This code is made available to you under your choice of the following sets +# of licensing terms: +############################################################################### +# 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/. +############################################################################### +# Copyright 2013 Mozilla Contributors +# +# 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. + +from __future__ import print_function +import argparse +import itertools +import sys + + +def base128(value): + """ + Given an integral value, returns an array of the base-128 representation + of that value, with all but the last byte having the high bit set as + required by the DER rules for the nodes of an OID after the first two + bytes. + + >>> base128(1) + [1] + >>> base128(10045) + [206, 61] + """ + + if value < 0: + raise ValueError("An OID must have only positive-value nodes.") + + # least significant byte has highest bit unset + result = [value % 0x80] + value /= 0x80 + + while value != 0: + result = [0x80 | (value % 0x80)] + result + value /= 0x80 + + return result + + +def dottedOIDToEncodedArray(dottedOID): + """ + Takes a dotted OID string (e.g. '1.2.840.10045.4.3.4') as input, and + returns an array that contains the DER encoding of its value, without + the tag and length (e.g. [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04]). + """ + nodes = [int(x) for x in dottedOID.strip().split(".")] + if len(nodes) < 2: + raise ValueError("An OID must have at least two nodes.") + if not (0 <= nodes[0] <= 2): + raise ValueError("The first node of an OID must be 0, 1, or 2.") + if not (0 <= nodes[1] <= 39): + # XXX: Does this restriction apply when the first part is 2? + raise ValueError("The second node of an OID must be 0-39.") + firstByte = (40 * nodes[0]) + nodes[1] + restBase128 = [base128(x) for x in nodes[2:]] + return [firstByte] + list(itertools.chain.from_iterable(restBase128)) + + +def dottedOIDToCArray(dottedOID, mode): + """ + Takes a dotted OID string (e.g. '1.2.840.10045.4.3.4') as input, and + returns a string that contains the hex encoding of the OID in C++ literal + notation, e.g. '0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04'. + """ + bytes = dottedOIDToEncodedArray(dottedOID) + + if mode != "value" and mode != "prefixdefine": + bytes = [0x06, len(bytes)] + bytes + + if mode == "alg": + # Wrap the DER-encoded OID in a SEQUENCE to create an + # AlgorithmIdentifier with no parameters. + bytes = [0x30, len(bytes)] + bytes + + return ", ".join(["0x%.2x" % b for b in bytes]) + + +def specNameToCName(specName): + """ + Given an string containing an ASN.1 name, returns a string that is a valid + C++ identifier that is as similar to that name as possible. Since most + ASN.1 identifiers used in PKIX specifications are legal C++ names except + for containing hyphens, this function just converts the hyphens to + underscores. This may need to be improved in the future if we encounter + names with other funny characters. + """ + return specName.replace("-", "_") + + +def toCode(programName, specName, dottedOID, mode): + """ + Given an ASN.1 name and a string containing the dotted representation of an + OID, returns a string that contains a C++ declaration for a named constant + that contains that OID value. If mode is "value" then only the value of + the OID (without the tag or length) will be included in the output. If mode + is "tlv" then the value will be prefixed with the tag and length. If mode + is "alg" then the value will be a complete der-encoded AlgorithmIdentifier + with no parameters. + + This: + + toCode("DottedOIDToCode.py", "ecdsa-with-SHA512", "1.2.840.10045.4.3.4", + "value") + + would result in a string like: + + // python DottedOIDToCode.py ecdsa-with-SHA512 1.2.840.10045.4.3.4 + static const uint8_t ecdsa_with_SHA512[] = { + 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 + }; + + This: + + toCode("DottedOIDToCode.py", "ecdsa-with-SHA512", "1.2.840.10045.4.3.4", + "tlv") + + would result in a string like: + + // python DottedOIDToCode.py --tlv ecdsa-with-SHA512 1.2.840.10045.4.3.4 + static const uint8_t tlv_ecdsa_with_SHA512[] = { + 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 + }; + + This: + + toCode("DottedOIDToCode.py", "ecdsa-with-SHA512", "1.2.840.10045.4.3.4", + "alg") + + would result in a string like: + + // python DottedOIDToCode.py --alg ecdsa-with-SHA512 1.2.840.10045.4.3.4 + static const uint8_t alg_ecdsa_with_SHA512[] = { + 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 + }; + + This: + + toCode("DottedOIDToCode.py", "PREFIX_1_2_840_10045", "1.2.840.10045", + "prefixdefine") + + would result in a string like this (note the lack of indention): + + // python DottedOIDToCode.py --prefixdefine PREFIX_1_2_840_10045 1.2.840.10045 + #define PREFIX_1_2_840_10045 0x2a, 0x86, 0x48, 0xce, 0x3d + """ + programNameWithOptions = programName + + if mode == "prefixdefine": + programNameWithOptions += " --prefixdefine" + varName = specName + return ("// python %s %s %s\n" + + "#define %s %s\n") % (programNameWithOptions, specName, + dottedOID, varName, + dottedOIDToCArray(dottedOID, mode)) + + varName = specNameToCName(specName) + if mode == "tlv": + programNameWithOptions += " --tlv" + varName = "tlv_" + varName + elif mode == "alg": + programNameWithOptions += " --alg" + varName = "alg_" + varName + elif mode == "prefixdefine": + programNameWithOptions += " --alg" + varName = varName + + return (" // python %s %s %s\n" + + " static const uint8_t %s[] = {\n" + + " %s\n" + + " };\n") % (programNameWithOptions, specName, dottedOID, varName, + dottedOIDToCArray(dottedOID, mode)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Generate code snippets to handle OIDs in C++", + epilog="example: python %s ecdsa-with-SHA1 1.2.840.10045.4.1" + % sys.argv[0]) + group = parser.add_mutually_exclusive_group() + group.add_argument("--tlv", action='store_true', + help="Wrap the encoded OID value with the tag and length") + group.add_argument("--alg", action='store_true', + help="Wrap the encoded OID value in an encoded SignatureAlgorithm") + group.add_argument("--prefixdefine", action='store_true', + help="generate a OID prefix #define") + parser.add_argument("name", + help="The name given to the OID in the specification") + parser.add_argument("dottedOID", metavar="dotted-oid", + help="The OID value, in dotted notation") + + args = parser.parse_args() + if args.alg: + mode = 'alg' + elif args.tlv: + mode = 'tlv' + elif args.prefixdefine: + mode = 'prefixdefine' + else: + mode = 'value' + + print(toCode(sys.argv[0], args.name, args.dottedOID, mode)) diff --git a/security/nss/lib/nss/nss.def b/security/nss/lib/nss/nss.def index 4f0ade4d0..8a9b3b030 100644 --- a/security/nss/lib/nss/nss.def +++ b/security/nss/lib/nss/nss.def @@ -1133,3 +1133,9 @@ SEC_CreateSignatureAlgorithmParameters; ;+ local: ;+ *; ;+}; +;+NSS_3.39 { # NSS 3.39 release +;+ global: +CERT_GetCertKeyType; +;+ local: +;+ *; +;+}; diff --git a/security/nss/lib/nss/nss.h b/security/nss/lib/nss/nss.h index efb0827c5..49c545ecc 100644 --- a/security/nss/lib/nss/nss.h +++ b/security/nss/lib/nss/nss.h @@ -22,9 +22,9 @@ * The format of the version string should be * "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]" */ -#define NSS_VERSION "3.38" _NSS_CUSTOMIZED +#define NSS_VERSION "3.41" _NSS_CUSTOMIZED #define NSS_VMAJOR 3 -#define NSS_VMINOR 38 +#define NSS_VMINOR 41 #define NSS_VPATCH 0 #define NSS_VBUILD 0 #define NSS_BETA PR_FALSE diff --git a/security/nss/lib/nss/nssinit.c b/security/nss/lib/nss/nssinit.c index 5d62d479c..9b6012771 100644 --- a/security/nss/lib/nss/nssinit.c +++ b/security/nss/lib/nss/nssinit.c @@ -12,7 +12,7 @@ #include "prprf.h" #include "prmem.h" #include "cert.h" -#include "key.h" +#include "keyhi.h" #include "secmod.h" #include "secoid.h" #include "nss.h" @@ -54,7 +54,7 @@ nss_mktemp(char *path) #define NSS_MAX_FLAG_SIZE sizeof("readOnly") + sizeof("noCertDB") + \ sizeof("noModDB") + sizeof("forceOpen") + sizeof("passwordRequired") + \ - sizeof("optimizeSpace") + sizeof("optimizeSpace") + sizeof("printPolicyFeedback") #define NSS_DEFAULT_MOD_NAME "NSS Internal Module" static char * @@ -702,6 +702,30 @@ nss_Init(const char *configdir, const char *certPrefix, const char *keyPrefix, if (SECOID_Init() != SECSuccess) { goto loser; } +#ifdef POLICY_FILE + /* Load the system crypto policy file if it exists, + * unless the NSS_IGNORE_SYSTEM_POLICY environment + * variable has been set to 1. */ + ignoreVar = PR_GetEnvSecure("NSS_IGNORE_SYSTEM_POLICY"); + if (ignoreVar == NULL || strncmp(ignoreVar, "1", sizeof("1")) != 0) { + if (PR_Access(POLICY_PATH "/" POLICY_FILE, PR_ACCESS_READ_OK) == PR_SUCCESS) { + SECMODModule *module = SECMOD_LoadModule( + "name=\"Policy File\" " + "parameters=\"configdir='sql:" POLICY_PATH "' " + "secmod='" POLICY_FILE "' " + "flags=readOnly,noCertDB,forceSecmodChoice,forceOpen\" " + "NSS=\"flags=internal,moduleDB,skipFirst,moduleDBOnly,critical\"", + parent, PR_TRUE); + if (module) { + PRBool isLoaded = module->loaded; + SECMOD_DestroyModule(module); + if (!isLoaded) { + goto loser; + } + } + } + } +#endif if (STAN_LoadDefaultNSS3TrustDomain() != PR_SUCCESS) { goto loser; } @@ -730,30 +754,6 @@ nss_Init(const char *configdir, const char *certPrefix, const char *keyPrefix, } } } -#ifdef POLICY_FILE - /* Load the system crypto policy file if it exists, - * unless the NSS_IGNORE_SYSTEM_POLICY environment - * variable has been set to 1. */ - ignoreVar = PR_GetEnvSecure("NSS_IGNORE_SYSTEM_POLICY"); - if (ignoreVar == NULL || strncmp(ignoreVar, "1", sizeof("1")) != 0) { - if (PR_Access(POLICY_PATH "/" POLICY_FILE, PR_ACCESS_READ_OK) == PR_SUCCESS) { - SECMODModule *module = SECMOD_LoadModule( - "name=\"Policy File\" " - "parameters=\"configdir='sql:" POLICY_PATH "' " - "secmod='" POLICY_FILE "' " - "flags=readOnly,noCertDB,forceSecmodChoice,forceOpen\" " - "NSS=\"flags=internal,moduleDB,skipFirst,moduleDBOnly,critical\"", - parent, PR_TRUE); - if (module) { - PRBool isLoaded = module->loaded; - SECMOD_DestroyModule(module); - if (!isLoaded) { - goto loser; - } - } - } - } -#endif pk11sdr_Init(); cert_CreateSubjectKeyIDHashTable(); diff --git a/security/nss/lib/pk11wrap/pk11akey.c b/security/nss/lib/pk11wrap/pk11akey.c index 346e473a9..c6070e264 100644 --- a/security/nss/lib/pk11wrap/pk11akey.c +++ b/security/nss/lib/pk11wrap/pk11akey.c @@ -13,7 +13,7 @@ #include "pkcs11t.h" #include "pk11func.h" #include "cert.h" -#include "key.h" +#include "keyhi.h" #include "keyi.h" #include "secitem.h" #include "secasn1.h" @@ -804,30 +804,12 @@ PK11_MakePrivKey(PK11SlotInfo *slot, KeyType keyType, /* don't know? look it up */ if (keyType == nullKey) { CK_KEY_TYPE pk11Type = CKK_RSA; - SECItem info; pk11Type = PK11_ReadULongAttribute(slot, privID, CKA_KEY_TYPE); isTemp = (PRBool)!PK11_HasAttributeSet(slot, privID, CKA_TOKEN, PR_FALSE); switch (pk11Type) { case CKK_RSA: keyType = rsaKey; - /* determine RSA key type from the CKA_PUBLIC_KEY_INFO if present */ - rv = PK11_ReadAttribute(slot, privID, CKA_PUBLIC_KEY_INFO, NULL, &info); - if (rv == SECSuccess) { - CERTSubjectPublicKeyInfo *spki; - - spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&info); - if (spki) { - SECOidTag tag; - - tag = SECOID_GetAlgorithmTag(&spki->algorithm); - if (tag == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) - keyType = rsaPssKey; - SECKEY_DestroySubjectPublicKeyInfo(spki); - } - SECITEM_FreeItem(&info, PR_FALSE); - } - break; case CKK_DSA: keyType = dsaKey; diff --git a/security/nss/lib/pk11wrap/pk11cert.c b/security/nss/lib/pk11wrap/pk11cert.c index c1caf5e60..819769643 100644 --- a/security/nss/lib/pk11wrap/pk11cert.c +++ b/security/nss/lib/pk11wrap/pk11cert.c @@ -15,7 +15,7 @@ #include "cert.h" #include "certi.h" #include "secitem.h" -#include "key.h" +#include "keyhi.h" #include "secoid.h" #include "pkcs7t.h" #include "cmsreclist.h" @@ -741,7 +741,7 @@ find_certs_from_nickname(const char *nickname, void *wincx) char *delimit = NULL; char *tokenName; - if (!strncmp(nickname, "pkcs11:", strlen("pkcs11:"))) { + if (!PORT_Strncasecmp(nickname, "pkcs11:", strlen("pkcs11:"))) { certs = find_certs_from_uri(nickname, wincx); if (certs) return certs; diff --git a/security/nss/lib/pk11wrap/pk11kea.c b/security/nss/lib/pk11wrap/pk11kea.c index 331a19c16..1f228cfaf 100644 --- a/security/nss/lib/pk11wrap/pk11kea.c +++ b/security/nss/lib/pk11wrap/pk11kea.c @@ -14,7 +14,7 @@ #include "pkcs11.h" #include "pk11func.h" #include "secitem.h" -#include "key.h" +#include "keyhi.h" #include "secasn1.h" #include "sechash.h" #include "cert.h" diff --git a/security/nss/lib/pk11wrap/pk11obj.c b/security/nss/lib/pk11wrap/pk11obj.c index b97caddd4..937ac654a 100644 --- a/security/nss/lib/pk11wrap/pk11obj.c +++ b/security/nss/lib/pk11wrap/pk11obj.c @@ -11,7 +11,7 @@ #include "pkcs11.h" #include "pkcs11t.h" #include "pk11func.h" -#include "key.h" +#include "keyhi.h" #include "secitem.h" #include "secerr.h" #include "sslerr.h" diff --git a/security/nss/lib/pk11wrap/pk11pars.c b/security/nss/lib/pk11wrap/pk11pars.c index c165e1ef2..db60f7c9d 100644 --- a/security/nss/lib/pk11wrap/pk11pars.c +++ b/security/nss/lib/pk11wrap/pk11pars.c @@ -109,6 +109,7 @@ secmod_NewModule(void) *other flags are set */ #define SECMOD_FLAG_MODULE_DB_SKIP_FIRST 0x02 #define SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB 0x04 +#define SECMOD_FLAG_MODULE_DB_POLICY_ONLY 0x08 /* private flags for internal (field in SECMODModule). */ /* The meaing of these flags is as follows: @@ -193,7 +194,7 @@ typedef struct { * This table should be merged with the SECOID table. */ #define CIPHER_NAME(x) x, (sizeof(x) - 1) -static const oidValDef algOptList[] = { +static const oidValDef curveOptList[] = { /* Curves */ { CIPHER_NAME("PRIME192V1"), SEC_OID_ANSIX962_EC_PRIME192V1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, @@ -315,7 +316,9 @@ static const oidValDef algOptList[] = { NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SECT571R1"), SEC_OID_SECG_EC_SECT571R1, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, +}; +static const oidValDef hashOptList[] = { /* Hashes */ { CIPHER_NAME("MD2"), SEC_OID_MD2, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, @@ -333,7 +336,9 @@ static const oidValDef algOptList[] = { NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, { CIPHER_NAME("SHA512"), SEC_OID_SHA512, NSS_USE_ALG_IN_SSL_KX | NSS_USE_ALG_IN_CERT_SIGNATURE }, +}; +static const oidValDef macOptList[] = { /* MACs */ { CIPHER_NAME("HMAC-SHA1"), SEC_OID_HMAC_SHA1, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("HMAC-SHA224"), SEC_OID_HMAC_SHA224, NSS_USE_ALG_IN_SSL }, @@ -341,7 +346,9 @@ static const oidValDef algOptList[] = { { CIPHER_NAME("HMAC-SHA384"), SEC_OID_HMAC_SHA384, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("HMAC-SHA512"), SEC_OID_HMAC_SHA512, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("HMAC-MD5"), SEC_OID_HMAC_MD5, NSS_USE_ALG_IN_SSL }, +}; +static const oidValDef cipherOptList[] = { /* Ciphers */ { CIPHER_NAME("AES128-CBC"), SEC_OID_AES_128_CBC, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("AES192-CBC"), SEC_OID_AES_192_CBC, NSS_USE_ALG_IN_SSL }, @@ -361,7 +368,9 @@ static const oidValDef algOptList[] = { { CIPHER_NAME("RC2"), SEC_OID_RC2_CBC, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("RC4"), SEC_OID_RC4, NSS_USE_ALG_IN_SSL }, { CIPHER_NAME("IDEA"), SEC_OID_IDEA_CBC, NSS_USE_ALG_IN_SSL }, +}; +static const oidValDef kxOptList[] = { /* Key exchange */ { CIPHER_NAME("RSA"), SEC_OID_TLS_RSA, NSS_USE_ALG_IN_SSL_KX }, { CIPHER_NAME("RSA-EXPORT"), SEC_OID_TLS_RSA_EXPORT, NSS_USE_ALG_IN_SSL_KX }, @@ -375,6 +384,20 @@ static const oidValDef algOptList[] = { { CIPHER_NAME("ECDH-RSA"), SEC_OID_TLS_ECDH_RSA, NSS_USE_ALG_IN_SSL_KX }, }; +typedef struct { + const oidValDef *list; + PRUint32 entries; + const char *description; +} algListsDef; + +static const algListsDef algOptLists[] = { + { curveOptList, PR_ARRAY_SIZE(curveOptList), "ECC" }, + { hashOptList, PR_ARRAY_SIZE(hashOptList), "HASH" }, + { macOptList, PR_ARRAY_SIZE(macOptList), "MAC" }, + { cipherOptList, PR_ARRAY_SIZE(cipherOptList), "CIPHER" }, + { kxOptList, PR_ARRAY_SIZE(kxOptList), "OTHER-KX" }, +}; + static const optionFreeDef sslOptList[] = { /* Versions */ { CIPHER_NAME("SSL2.0"), 0x002 }, @@ -401,7 +424,7 @@ static const optionFreeDef freeOptList[] = { { CIPHER_NAME("TLS-VERSION-MAX"), NSS_TLS_VERSION_MAX_POLICY }, /* constraints on DTLS Protocols */ { CIPHER_NAME("DTLS-VERSION-MIN"), NSS_DTLS_VERSION_MIN_POLICY }, - { CIPHER_NAME("DTLS-VERSION-MAX"), NSS_DTLS_VERSION_MIN_POLICY } + { CIPHER_NAME("DTLS-VERSION-MAX"), NSS_DTLS_VERSION_MAX_POLICY } }; static const policyFlagDef policyFlagList[] = { @@ -446,7 +469,8 @@ secmod_ArgGetSubValue(const char *cipher, char sep1, char sep2, } static PRUint32 -secmod_parsePolicyValue(const char *policyFlags, int policyLength) +secmod_parsePolicyValue(const char *policyFlags, int policyLength, + PRBool printPolicyFeedback) { const char *flag, *currentString; PRUint32 flags = 0; @@ -455,6 +479,7 @@ secmod_parsePolicyValue(const char *policyFlags, int policyLength) for (currentString = policyFlags; currentString && currentString < policyFlags + policyLength;) { int length; + PRBool unknown = PR_TRUE; flag = secmod_ArgGetSubValue(currentString, ',', ':', &length, ¤tString); if (length == 0) { @@ -466,41 +491,49 @@ secmod_parsePolicyValue(const char *policyFlags, int policyLength) if ((policy->name_size == length) && PORT_Strncasecmp(policy->name, flag, name_size) == 0) { flags |= policy->flag; + unknown = PR_FALSE; break; } } + if (unknown && printPolicyFeedback) { + PR_SetEnv("NSS_POLICY_FAIL=1"); + fprintf(stderr, "NSS-POLICY-FAIL %.*s: unknown value: %.*s\n", + policyLength, policyFlags, length, flag); + } } return flags; } /* allow symbolic names for values. The only ones currently defines or * SSL protocol versions. */ -static PRInt32 -secmod_getPolicyOptValue(const char *policyValue, int policyValueLength) +static SECStatus +secmod_getPolicyOptValue(const char *policyValue, int policyValueLength, + PRInt32 *result) { PRInt32 val = atoi(policyValue); int i; if ((val != 0) || (*policyValue == '0')) { - return val; + *result = val; + return SECSuccess; } for (i = 0; i < PR_ARRAY_SIZE(sslOptList); i++) { if (policyValueLength == sslOptList[i].name_size && PORT_Strncasecmp(sslOptList[i].name, policyValue, sslOptList[i].name_size) == 0) { - val = sslOptList[i].option; - break; + *result = sslOptList[i].option; + return SECSuccess; } } - return val; + return SECFailure; } static SECStatus -secmod_applyCryptoPolicy(const char *policyString, - PRBool allow) +secmod_applyCryptoPolicy(const char *policyString, PRBool allow, + PRBool printPolicyFeedback) { const char *cipher, *currentString; - unsigned i; + unsigned i, j; SECStatus rv = SECSuccess; PRBool unknown; @@ -525,56 +558,63 @@ secmod_applyCryptoPolicy(const char *policyString, /* disable or enable all options by default */ PRUint32 value = 0; if (newValue) { - value = secmod_parsePolicyValue(&cipher[3] + 1, length - 3 - 1); + value = secmod_parsePolicyValue(&cipher[3] + 1, length - 3 - 1, printPolicyFeedback); } - for (i = 0; i < PR_ARRAY_SIZE(algOptList); i++) { - PRUint32 enable, disable; - if (!newValue) { - value = algOptList[i].val; - } - if (allow) { - enable = value; - disable = 0; - } else { - enable = 0; - disable = value; + for (i = 0; i < PR_ARRAY_SIZE(algOptLists); i++) { + const algListsDef *algOptList = &algOptLists[i]; + for (j = 0; j < algOptList->entries; j++) { + PRUint32 enable, disable; + if (!newValue) { + value = algOptList->list[j].val; + } + if (allow) { + enable = value; + disable = 0; + } else { + enable = 0; + disable = value; + } + NSS_SetAlgorithmPolicy(algOptList->list[j].oid, enable, disable); } - NSS_SetAlgorithmPolicy(algOptList[i].oid, enable, disable); } continue; } - for (i = 0; i < PR_ARRAY_SIZE(algOptList); i++) { - const oidValDef *algOpt = &algOptList[i]; - unsigned name_size = algOpt->name_size; - PRBool newOption = PR_FALSE; + for (i = 0; i < PR_ARRAY_SIZE(algOptLists); i++) { + const algListsDef *algOptList = &algOptLists[i]; + for (j = 0; j < algOptList->entries; j++) { + const oidValDef *algOpt = &algOptList->list[j]; + unsigned name_size = algOpt->name_size; + PRBool newOption = PR_FALSE; - if ((length >= name_size) && (cipher[name_size] == '/')) { - newOption = PR_TRUE; - } - if ((newOption || algOpt->name_size == length) && - PORT_Strncasecmp(algOpt->name, cipher, name_size) == 0) { - PRUint32 value = algOpt->val; - PRUint32 enable, disable; - if (newOption) { - value = secmod_parsePolicyValue(&cipher[name_size] + 1, - length - name_size - 1); + if ((length >= name_size) && (cipher[name_size] == '/')) { + newOption = PR_TRUE; } - if (allow) { - enable = value; - disable = 0; - } else { - enable = 0; - disable = value; - } - rv = NSS_SetAlgorithmPolicy(algOpt->oid, enable, disable); - if (rv != SECSuccess) { - /* could not enable option */ - /* NSS_SetAlgorithPolicy should have set the error code */ - return SECFailure; + if ((newOption || algOpt->name_size == length) && + PORT_Strncasecmp(algOpt->name, cipher, name_size) == 0) { + PRUint32 value = algOpt->val; + PRUint32 enable, disable; + if (newOption) { + value = secmod_parsePolicyValue(&cipher[name_size] + 1, + length - name_size - 1, + printPolicyFeedback); + } + if (allow) { + enable = value; + disable = 0; + } else { + enable = 0; + disable = value; + } + rv = NSS_SetAlgorithmPolicy(algOpt->oid, enable, disable); + if (rv != SECSuccess) { + /* could not enable option */ + /* NSS_SetAlgorithPolicy should have set the error code */ + return SECFailure; + } + unknown = PR_FALSE; + break; } - unknown = PR_FALSE; - break; } } if (!unknown) { @@ -587,9 +627,19 @@ secmod_applyCryptoPolicy(const char *policyString, if ((length > name_size) && cipher[name_size] == '=' && PORT_Strncasecmp(freeOpt->name, cipher, name_size) == 0) { - PRInt32 val = secmod_getPolicyOptValue(&cipher[name_size + 1], - length - name_size - 1); - + PRInt32 val; + const char *policyValue = &cipher[name_size + 1]; + int policyValueLength = length - name_size - 1; + rv = secmod_getPolicyOptValue(policyValue, policyValueLength, + &val); + if (rv != SECSuccess) { + if (printPolicyFeedback) { + PR_SetEnv("NSS_POLICY_FAIL=1"); + fprintf(stderr, "NSS-POLICY-FAIL %.*s: unknown value: %.*s\n", + length, cipher, policyValueLength, policyValue); + } + return SECFailure; + } rv = NSS_OptionSet(freeOpt->option, val); if (rv != SECSuccess) { /* could not enable option */ @@ -602,12 +652,83 @@ secmod_applyCryptoPolicy(const char *policyString, break; } } + + if (unknown && printPolicyFeedback) { + PR_SetEnv("NSS_POLICY_FAIL=1"); + fprintf(stderr, "NSS-POLICY-FAIL %s: unknown identifier: %.*s\n", + allow ? "allow" : "disallow", length, cipher); + } } return rv; } +static void +secmod_sanityCheckCryptoPolicy(void) +{ + unsigned i, j; + SECStatus rv = SECSuccess; + unsigned num_kx_enabled = 0; + unsigned num_ssl_enabled = 0; + unsigned num_sig_enabled = 0; + unsigned enabledCount[PR_ARRAY_SIZE(algOptLists)]; + const char *sWarn = "WARN"; + const char *sInfo = "INFO"; + PRBool haveWarning = PR_FALSE; + + for (i = 0; i < PR_ARRAY_SIZE(algOptLists); i++) { + const algListsDef *algOptList = &algOptLists[i]; + enabledCount[i] = 0; + for (j = 0; j < algOptList->entries; j++) { + const oidValDef *algOpt = &algOptList->list[j]; + PRUint32 value; + PRBool anyEnabled = PR_FALSE; + rv = NSS_GetAlgorithmPolicy(algOpt->oid, &value); + if (rv != SECSuccess) { + PR_SetEnv("NSS_POLICY_FAIL=1"); + fprintf(stderr, "NSS-POLICY-FAIL: internal failure with NSS_GetAlgorithmPolicy at %u\n", i); + return; + } + + if ((algOpt->val & NSS_USE_ALG_IN_SSL_KX) && (value & NSS_USE_ALG_IN_SSL_KX)) { + ++num_kx_enabled; + anyEnabled = PR_TRUE; + fprintf(stderr, "NSS-POLICY-INFO: %s is enabled for KX\n", algOpt->name); + } + if ((algOpt->val & NSS_USE_ALG_IN_SSL) && (value & NSS_USE_ALG_IN_SSL)) { + ++num_ssl_enabled; + anyEnabled = PR_TRUE; + fprintf(stderr, "NSS-POLICY-INFO: %s is enabled for SSL\n", algOpt->name); + } + if ((algOpt->val & NSS_USE_ALG_IN_CERT_SIGNATURE) && (value & NSS_USE_ALG_IN_CERT_SIGNATURE)) { + ++num_sig_enabled; + anyEnabled = PR_TRUE; + fprintf(stderr, "NSS-POLICY-INFO: %s is enabled for CERT-SIGNATURE\n", algOpt->name); + } + if (anyEnabled) { + ++enabledCount[i]; + } + } + } + fprintf(stderr, "NSS-POLICY-%s: NUMBER-OF-SSL-ALG-KX: %u\n", num_kx_enabled ? sInfo : sWarn, num_kx_enabled); + fprintf(stderr, "NSS-POLICY-%s: NUMBER-OF-SSL-ALG: %u\n", num_ssl_enabled ? sInfo : sWarn, num_ssl_enabled); + fprintf(stderr, "NSS-POLICY-%s: NUMBER-OF-CERT-SIG: %u\n", num_sig_enabled ? sInfo : sWarn, num_sig_enabled); + if (!num_kx_enabled || !num_ssl_enabled || !num_sig_enabled) { + haveWarning = PR_TRUE; + } + for (i = 0; i < PR_ARRAY_SIZE(algOptLists); i++) { + const algListsDef *algOptList = &algOptLists[i]; + fprintf(stderr, "NSS-POLICY-%s: NUMBER-OF-%s: %u\n", enabledCount[i] ? sInfo : sWarn, algOptList->description, enabledCount[i]); + if (!enabledCount[i]) { + haveWarning = PR_TRUE; + } + } + if (haveWarning) { + PR_SetEnv("NSS_POLICY_WARN=1"); + } +} + static SECStatus -secmod_parseCryptoPolicy(const char *policyConfig) +secmod_parseCryptoPolicy(const char *policyConfig, PRBool printPolicyFeedback) { char *disallow, *allow; SECStatus rv; @@ -622,16 +743,26 @@ secmod_parseCryptoPolicy(const char *policyConfig) return rv; } disallow = NSSUTIL_ArgGetParamValue("disallow", policyConfig); - rv = secmod_applyCryptoPolicy(disallow, PR_FALSE); + rv = secmod_applyCryptoPolicy(disallow, PR_FALSE, printPolicyFeedback); if (disallow) PORT_Free(disallow); if (rv != SECSuccess) { return rv; } allow = NSSUTIL_ArgGetParamValue("allow", policyConfig); - rv = secmod_applyCryptoPolicy(allow, PR_TRUE); + rv = secmod_applyCryptoPolicy(allow, PR_TRUE, printPolicyFeedback); if (allow) PORT_Free(allow); + if (rv != SECSuccess) { + return rv; + } + if (printPolicyFeedback) { + /* This helps to distinguish configurations that don't contain any + * policy config= statement. */ + PR_SetEnv("NSS_POLICY_LOADED=1"); + fprintf(stderr, "NSS-POLICY-INFO: LOADED-SUCCESSFULLY\n"); + secmod_sanityCheckCryptoPolicy(); + } return rv; } @@ -648,11 +779,16 @@ SECMOD_CreateModuleEx(const char *library, const char *moduleName, char *slotParams, *ciphers; /* pk11pars.h still does not have const char * interfaces */ char *nssc = (char *)nss; + PRBool printPolicyFeedback = NSSUTIL_ArgHasFlag("flags", "printPolicyFeedback", nssc); - rv = secmod_parseCryptoPolicy(config); + rv = secmod_parseCryptoPolicy(config, printPolicyFeedback); /* do not load the module if policy parsing fails */ if (rv != SECSuccess) { + if (printPolicyFeedback) { + PR_SetEnv("NSS_POLICY_FAIL=1"); + fprintf(stderr, "NSS-POLICY-FAIL: policy config parsing failed, not loading module %s\n", moduleName); + } return NULL; } @@ -703,6 +839,9 @@ SECMOD_CreateModuleEx(const char *library, const char *moduleName, if (NSSUTIL_ArgHasFlag("flags", "defaultModDB", nssc)) { flags |= SECMOD_FLAG_MODULE_DB_DEFAULT_MODDB; } + if (NSSUTIL_ArgHasFlag("flags", "policyOnly", nssc)) { + flags |= SECMOD_FLAG_MODULE_DB_POLICY_ONLY; + } /* additional moduleDB flags could be added here in the future */ mod->isModuleDB = (PRBool)flags; } @@ -743,6 +882,14 @@ SECMOD_GetDefaultModDBFlag(SECMODModule *mod) } PRBool +secmod_PolicyOnly(SECMODModule *mod) +{ + char flags = (char)mod->isModuleDB; + + return (flags & SECMOD_FLAG_MODULE_DB_POLICY_ONLY) ? PR_TRUE : PR_FALSE; +} + +PRBool secmod_IsInternalKeySlot(SECMODModule *mod) { char flags = (char)mod->internal; @@ -1635,6 +1782,7 @@ SECMOD_LoadModule(char *modulespec, SECMODModule *parent, PRBool recurse) SECMODModule *module = NULL; SECMODModule *oldModule = NULL; SECStatus rv; + PRBool forwardPolicyFeedback = PR_FALSE; /* initialize the underlying module structures */ SECMOD_Init(); @@ -1647,6 +1795,7 @@ SECMOD_LoadModule(char *modulespec, SECMODModule *parent, PRBool recurse) } module = SECMOD_CreateModuleEx(library, moduleName, parameters, nss, config); + forwardPolicyFeedback = NSSUTIL_ArgHasFlag("flags", "printPolicyFeedback", nss); if (library) PORT_Free(library); if (moduleName) @@ -1660,6 +1809,12 @@ SECMOD_LoadModule(char *modulespec, SECMODModule *parent, PRBool recurse) if (!module) { goto loser; } + + /* a policy only stanza doesn't actually get 'loaded'. policy has already + * been parsed as a side effect of the CreateModuleEx call */ + if (secmod_PolicyOnly(module)) { + return module; + } if (parent) { module->parent = SECMOD_ReferenceModule(parent); if (module->internal && secmod_IsInternalKeySlot(parent)) { @@ -1703,7 +1858,15 @@ SECMOD_LoadModule(char *modulespec, SECMODModule *parent, PRBool recurse) rv = SECFailure; break; } - child = SECMOD_LoadModule(*index, module, PR_TRUE); + if (!forwardPolicyFeedback) { + child = SECMOD_LoadModule(*index, module, PR_TRUE); + } else { + /* Add printPolicyFeedback to the nss flags */ + char *specWithForwards = + NSSUTIL_AddNSSFlagToModuleSpec(*index, "printPolicyFeedback"); + child = SECMOD_LoadModule(specWithForwards, module, PR_TRUE); + PORT_Free(specWithForwards); + } if (!child) break; if (child->isCritical && !child->loaded) { diff --git a/security/nss/lib/pk11wrap/pk11pbe.c b/security/nss/lib/pk11wrap/pk11pbe.c index 5f68f399e..4b6645578 100644 --- a/security/nss/lib/pk11wrap/pk11pbe.c +++ b/security/nss/lib/pk11wrap/pk11pbe.c @@ -23,7 +23,7 @@ #include "pkcs11.h" #include "pk11func.h" #include "secitem.h" -#include "key.h" +#include "keyhi.h" typedef struct SEC_PKCS5PBEParameterStr SEC_PKCS5PBEParameter; struct SEC_PKCS5PBEParameterStr { diff --git a/security/nss/lib/pk11wrap/pk11pk12.c b/security/nss/lib/pk11wrap/pk11pk12.c index 035143af8..47b6702c6 100644 --- a/security/nss/lib/pk11wrap/pk11pk12.c +++ b/security/nss/lib/pk11wrap/pk11pk12.c @@ -14,7 +14,7 @@ #include "pkcs11.h" #include "pk11func.h" #include "secitem.h" -#include "key.h" +#include "keyhi.h" #include "secoid.h" #include "secasn1.h" #include "secerr.h" diff --git a/security/nss/lib/pk11wrap/pk11priv.h b/security/nss/lib/pk11wrap/pk11priv.h index 9281923fa..8848c81ec 100644 --- a/security/nss/lib/pk11wrap/pk11priv.h +++ b/security/nss/lib/pk11wrap/pk11priv.h @@ -7,7 +7,7 @@ #include "seccomon.h" #include "secoidt.h" #include "secdert.h" -#include "keyt.h" +#include "keythi.h" #include "certt.h" #include "pkcs11t.h" #include "secmodt.h" diff --git a/security/nss/lib/pk11wrap/pk11pub.h b/security/nss/lib/pk11wrap/pk11pub.h index dbd8da092..8db969e4c 100644 --- a/security/nss/lib/pk11wrap/pk11pub.h +++ b/security/nss/lib/pk11wrap/pk11pub.h @@ -7,7 +7,7 @@ #include "seccomon.h" #include "secoidt.h" #include "secdert.h" -#include "keyt.h" +#include "keythi.h" #include "certt.h" #include "pkcs11t.h" #include "secmodt.h" diff --git a/security/nss/lib/pk11wrap/pk11slot.c b/security/nss/lib/pk11wrap/pk11slot.c index c39abe17e..ebe54d495 100644 --- a/security/nss/lib/pk11wrap/pk11slot.c +++ b/security/nss/lib/pk11wrap/pk11slot.c @@ -607,12 +607,32 @@ PK11_FindSlotsByNames(const char *dllName, const char *slotName, return slotList; } -PK11SlotInfo * -PK11_FindSlotByName(const char *name) +typedef PRBool (*PK11SlotMatchFunc)(PK11SlotInfo *slot, const void *arg); + +static PRBool +pk11_MatchSlotByTokenName(PK11SlotInfo *slot, const void *arg) +{ + return PORT_Strcmp(slot->token_name, arg) == 0; +} + +static PRBool +pk11_MatchSlotBySerial(PK11SlotInfo *slot, const void *arg) { + return PORT_Memcmp(slot->serial, arg, sizeof(slot->serial)) == 0; +} + +static PRBool +pk11_MatchSlotByTokenURI(PK11SlotInfo *slot, const void *arg) +{ + return pk11_MatchUriTokenInfo(slot, (PK11URI *)arg); +} + +static PK11SlotInfo * +pk11_FindSlot(const void *arg, PK11SlotMatchFunc func) +{ + SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); SECMODModuleList *mlp; SECMODModuleList *modules; - SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); int i; PK11SlotInfo *slot = NULL; @@ -620,10 +640,6 @@ PK11_FindSlotByName(const char *name) PORT_SetError(SEC_ERROR_NOT_INITIALIZED); return slot; } - if ((name == NULL) || (*name == 0)) { - return PK11_GetInternalKeySlot(); - } - /* work through all the slots */ SECMOD_GetReadLock(moduleLock); modules = SECMOD_GetDefaultModuleList(); @@ -631,7 +647,7 @@ PK11_FindSlotByName(const char *name) for (i = 0; i < mlp->module->slotCount; i++) { PK11SlotInfo *tmpSlot = mlp->module->slots[i]; if (PK11_IsPresent(tmpSlot)) { - if (PORT_Strcmp(tmpSlot->token_name, name) == 0) { + if (func(tmpSlot, arg)) { slot = PK11_ReferenceSlot(tmpSlot); break; } @@ -649,43 +665,41 @@ PK11_FindSlotByName(const char *name) return slot; } -PK11SlotInfo * -PK11_FindSlotBySerial(char *serial) +static PK11SlotInfo * +pk11_FindSlotByTokenURI(const char *uriString) { - SECMODModuleList *mlp; - SECMODModuleList *modules; - SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); - int i; PK11SlotInfo *slot = NULL; + PK11URI *uri; - if (!moduleLock) { - PORT_SetError(SEC_ERROR_NOT_INITIALIZED); + uri = PK11URI_ParseURI(uriString); + if (!uri) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); return slot; } - /* work through all the slots */ - SECMOD_GetReadLock(moduleLock); - modules = SECMOD_GetDefaultModuleList(); - for (mlp = modules; mlp != NULL; mlp = mlp->next) { - for (i = 0; i < mlp->module->slotCount; i++) { - PK11SlotInfo *tmpSlot = mlp->module->slots[i]; - if (PK11_IsPresent(tmpSlot)) { - if (PORT_Memcmp(tmpSlot->serial, serial, - sizeof(tmpSlot->serial)) == 0) { - slot = PK11_ReferenceSlot(tmpSlot); - break; - } - } - } - if (slot != NULL) - break; + + slot = pk11_FindSlot(uri, pk11_MatchSlotByTokenURI); + PK11URI_DestroyURI(uri); + return slot; +} + +PK11SlotInfo * +PK11_FindSlotByName(const char *name) +{ + if ((name == NULL) || (*name == 0)) { + return PK11_GetInternalKeySlot(); } - SECMOD_ReleaseReadLock(moduleLock); - if (slot == NULL) { - PORT_SetError(SEC_ERROR_NO_TOKEN); + if (!PORT_Strncasecmp(name, "pkcs11:", strlen("pkcs11:"))) { + return pk11_FindSlotByTokenURI(name); } - return slot; + return pk11_FindSlot(name, pk11_MatchSlotByTokenName); +} + +PK11SlotInfo * +PK11_FindSlotBySerial(char *serial) +{ + return pk11_FindSlot(serial, pk11_MatchSlotBySerial); } /* diff --git a/security/nss/lib/pk11wrap/secmodi.h b/security/nss/lib/pk11wrap/secmodi.h index 84f5f2a30..7ec77ced6 100644 --- a/security/nss/lib/pk11wrap/secmodi.h +++ b/security/nss/lib/pk11wrap/secmodi.h @@ -13,7 +13,7 @@ #include "secdert.h" #include "certt.h" #include "secmodt.h" -#include "keyt.h" +#include "keythi.h" SEC_BEGIN_PROTOS diff --git a/security/nss/lib/pkcs12/p12.h b/security/nss/lib/pkcs12/p12.h index 118db6efa..495bbf6c4 100644 --- a/security/nss/lib/pkcs12/p12.h +++ b/security/nss/lib/pkcs12/p12.h @@ -6,7 +6,7 @@ #define _P12_H_ #include "secoid.h" -#include "key.h" +#include "keyhi.h" #include "secpkcs7.h" #include "p12t.h" diff --git a/security/nss/lib/pkcs12/p12t.h b/security/nss/lib/pkcs12/p12t.h index 62c2b502e..b22f0dd82 100644 --- a/security/nss/lib/pkcs12/p12t.h +++ b/security/nss/lib/pkcs12/p12t.h @@ -6,7 +6,7 @@ #define _P12T_H_ #include "secoid.h" -#include "key.h" +#include "keythi.h" #include "pkcs11.h" #include "secpkcs7.h" #include "secdig.h" /* for SGNDigestInfo */ diff --git a/security/nss/lib/pkcs12/pkcs12t.h b/security/nss/lib/pkcs12/pkcs12t.h index ad00d7b5b..db10d28af 100644 --- a/security/nss/lib/pkcs12/pkcs12t.h +++ b/security/nss/lib/pkcs12/pkcs12t.h @@ -8,7 +8,7 @@ #include "seccomon.h" #include "secoid.h" #include "cert.h" -#include "key.h" +#include "keythi.h" #include "plarena.h" #include "secpkcs7.h" #include "secdig.h" /* for SGNDigestInfo */ diff --git a/security/nss/lib/pkcs7/p7decode.c b/security/nss/lib/pkcs7/p7decode.c index ba51955ab..641d201e5 100644 --- a/security/nss/lib/pkcs7/p7decode.c +++ b/security/nss/lib/pkcs7/p7decode.c @@ -16,7 +16,7 @@ /* include should be removed! */ /*#include "cdbhdl.h" */ #include "cryptohi.h" -#include "key.h" +#include "keyhi.h" #include "secasn1.h" #include "secitem.h" #include "secoid.h" diff --git a/security/nss/lib/pkcs7/secmime.c b/security/nss/lib/pkcs7/secmime.c index ca1046aa5..8a4afe45b 100644 --- a/security/nss/lib/pkcs7/secmime.c +++ b/security/nss/lib/pkcs7/secmime.c @@ -14,7 +14,7 @@ #include "secasn1.h" #include "secitem.h" #include "cert.h" -#include "key.h" +#include "keyhi.h" #include "secerr.h" typedef struct smime_cipher_map_struct { diff --git a/security/nss/lib/pkcs7/secpkcs7.h b/security/nss/lib/pkcs7/secpkcs7.h index 78270bd15..4a88df1df 100644 --- a/security/nss/lib/pkcs7/secpkcs7.h +++ b/security/nss/lib/pkcs7/secpkcs7.h @@ -13,7 +13,7 @@ #include "secoidt.h" #include "certt.h" -#include "keyt.h" +#include "keythi.h" #include "hasht.h" #include "pkcs7t.h" diff --git a/security/nss/lib/smime/cms.h b/security/nss/lib/smime/cms.h index 244df4879..f4a8a39e9 100644 --- a/security/nss/lib/smime/cms.h +++ b/security/nss/lib/smime/cms.h @@ -13,7 +13,7 @@ #include "secoidt.h" #include "certt.h" -#include "keyt.h" +#include "keythi.h" #include "hasht.h" #include "cmst.h" diff --git a/security/nss/lib/smime/cmsasn1.c b/security/nss/lib/smime/cmsasn1.c index 15cf08fcc..8ba95d044 100644 --- a/security/nss/lib/smime/cmsasn1.c +++ b/security/nss/lib/smime/cmsasn1.c @@ -9,7 +9,7 @@ #include "cmslocal.h" #include "cert.h" -#include "key.h" +#include "keyhi.h" #include "secasn1.h" #include "secitem.h" #include "secoid.h" diff --git a/security/nss/lib/smime/cmsdecode.c b/security/nss/lib/smime/cmsdecode.c index 62b4ebfe5..69965bdd7 100644 --- a/security/nss/lib/smime/cmsdecode.c +++ b/security/nss/lib/smime/cmsdecode.c @@ -9,7 +9,7 @@ #include "cmslocal.h" #include "cert.h" -#include "key.h" +#include "keyhi.h" #include "secasn1.h" #include "secitem.h" #include "secoid.h" diff --git a/security/nss/lib/smime/cmsdigest.c b/security/nss/lib/smime/cmsdigest.c index 64b64a0f8..bd1474068 100644 --- a/security/nss/lib/smime/cmsdigest.c +++ b/security/nss/lib/smime/cmsdigest.c @@ -9,7 +9,7 @@ #include "cmslocal.h" #include "cert.h" -#include "key.h" +#include "keyhi.h" #include "secitem.h" #include "secoid.h" #include "pk11func.h" diff --git a/security/nss/lib/smime/cmsencdata.c b/security/nss/lib/smime/cmsencdata.c index c3a4549ad..d2fc3358b 100644 --- a/security/nss/lib/smime/cmsencdata.c +++ b/security/nss/lib/smime/cmsencdata.c @@ -8,7 +8,7 @@ #include "cmslocal.h" -#include "key.h" +#include "keyhi.h" #include "secasn1.h" #include "secitem.h" #include "secoid.h" diff --git a/security/nss/lib/smime/cmsencode.c b/security/nss/lib/smime/cmsencode.c index 0d723e865..703492b5e 100644 --- a/security/nss/lib/smime/cmsencode.c +++ b/security/nss/lib/smime/cmsencode.c @@ -9,7 +9,7 @@ #include "cmslocal.h" #include "cert.h" -#include "key.h" +#include "keyhi.h" #include "secasn1.h" #include "secoid.h" #include "secitem.h" diff --git a/security/nss/lib/smime/cmsenvdata.c b/security/nss/lib/smime/cmsenvdata.c index f2c8e171d..d5d5c4123 100644 --- a/security/nss/lib/smime/cmsenvdata.c +++ b/security/nss/lib/smime/cmsenvdata.c @@ -9,7 +9,7 @@ #include "cmslocal.h" #include "cert.h" -#include "key.h" +#include "keyhi.h" #include "secasn1.h" #include "secitem.h" #include "secoid.h" diff --git a/security/nss/lib/smime/cmspubkey.c b/security/nss/lib/smime/cmspubkey.c index bc3cd993e..8f18f60de 100644 --- a/security/nss/lib/smime/cmspubkey.c +++ b/security/nss/lib/smime/cmspubkey.c @@ -9,7 +9,7 @@ #include "cmslocal.h" #include "cert.h" -#include "key.h" +#include "keyhi.h" #include "secasn1.h" #include "secitem.h" #include "secoid.h" diff --git a/security/nss/lib/smime/cmsrecinfo.c b/security/nss/lib/smime/cmsrecinfo.c index 8cab288d2..20dd698e8 100644 --- a/security/nss/lib/smime/cmsrecinfo.c +++ b/security/nss/lib/smime/cmsrecinfo.c @@ -9,7 +9,7 @@ #include "cmslocal.h" #include "cert.h" -#include "key.h" +#include "keyhi.h" #include "secasn1.h" #include "secitem.h" #include "secoid.h" diff --git a/security/nss/lib/smime/cmsreclist.c b/security/nss/lib/smime/cmsreclist.c index 99d7e9087..f75347407 100644 --- a/security/nss/lib/smime/cmsreclist.c +++ b/security/nss/lib/smime/cmsreclist.c @@ -9,7 +9,7 @@ #include "cmslocal.h" #include "cert.h" -#include "key.h" +#include "keyhi.h" #include "secasn1.h" #include "secitem.h" #include "secoid.h" diff --git a/security/nss/lib/smime/cmssiginfo.c b/security/nss/lib/smime/cmssiginfo.c index ce4f87c0a..79aaf8f0a 100644 --- a/security/nss/lib/smime/cmssiginfo.c +++ b/security/nss/lib/smime/cmssiginfo.c @@ -9,7 +9,7 @@ #include "cmslocal.h" #include "cert.h" -#include "key.h" +#include "keyhi.h" #include "secasn1.h" #include "secitem.h" #include "secoid.h" diff --git a/security/nss/lib/smime/cmsutil.c b/security/nss/lib/smime/cmsutil.c index cd12603fa..713b94aac 100644 --- a/security/nss/lib/smime/cmsutil.c +++ b/security/nss/lib/smime/cmsutil.c @@ -9,7 +9,7 @@ #include "cmslocal.h" #include "cert.h" -#include "key.h" +#include "keyhi.h" #include "secasn1.h" #include "secitem.h" #include "secoid.h" diff --git a/security/nss/lib/smime/smimemessage.c b/security/nss/lib/smime/smimemessage.c index 774b9f3fd..3073ab245 100644 --- a/security/nss/lib/smime/smimemessage.c +++ b/security/nss/lib/smime/smimemessage.c @@ -10,7 +10,7 @@ #include "smime.h" #include "cert.h" -#include "key.h" +#include "keyhi.h" #include "secasn1.h" #include "secitem.h" #include "secoid.h" diff --git a/security/nss/lib/smime/smimeutil.c b/security/nss/lib/smime/smimeutil.c index 7674a65fd..0e6bd32fd 100644 --- a/security/nss/lib/smime/smimeutil.c +++ b/security/nss/lib/smime/smimeutil.c @@ -13,7 +13,7 @@ #include "secasn1.h" #include "secitem.h" #include "cert.h" -#include "key.h" +#include "keyhi.h" #include "secerr.h" #include "cms.h" #include "nss.h" diff --git a/security/nss/lib/softoken/pkcs11c.c b/security/nss/lib/softoken/pkcs11c.c index 385d3c144..7eec3d7ee 100644 --- a/security/nss/lib/softoken/pkcs11c.c +++ b/security/nss/lib/softoken/pkcs11c.c @@ -3106,7 +3106,7 @@ RSA_HashCheckSign(SECOidTag digestOid, NSSLOWKEYPublicKey *key, digest.len = digestLen; rv = _SGN_VerifyPKCS1DigestInfo( digestOid, &digest, &pkcs1DigestInfo, - PR_TRUE /*XXX: unsafeAllowMissingParameters*/); + PR_FALSE /*XXX: unsafeAllowMissingParameters*/); } PORT_Free(pkcs1DigestInfoData); diff --git a/security/nss/lib/softoken/softkver.h b/security/nss/lib/softoken/softkver.h index 827bf2e22..c1f63d769 100644 --- a/security/nss/lib/softoken/softkver.h +++ b/security/nss/lib/softoken/softkver.h @@ -17,9 +17,9 @@ * The format of the version string should be * "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]" */ -#define SOFTOKEN_VERSION "3.38" SOFTOKEN_ECC_STRING +#define SOFTOKEN_VERSION "3.41" SOFTOKEN_ECC_STRING #define SOFTOKEN_VMAJOR 3 -#define SOFTOKEN_VMINOR 38 +#define SOFTOKEN_VMINOR 41 #define SOFTOKEN_VPATCH 0 #define SOFTOKEN_VBUILD 0 #define SOFTOKEN_BETA PR_FALSE diff --git a/security/nss/lib/ssl/SSLerrs.h b/security/nss/lib/ssl/SSLerrs.h index f01d16583..9be219494 100644 --- a/security/nss/lib/ssl/SSLerrs.h +++ b/security/nss/lib/ssl/SSLerrs.h @@ -552,3 +552,15 @@ ER3(SSL_ERROR_RX_MALFORMED_DTLS_ACK, (SSL_ERROR_BASE + 174), ER3(SSL_ERROR_DH_KEY_TOO_LONG, (SSL_ERROR_BASE + 175), "SSL received a DH key share that's too long (>8192 bit).") + +ER3(SSL_ERROR_RX_MALFORMED_ESNI_KEYS, (SSL_ERROR_BASE + 176), + "SSL received a malformed ESNI keys structure") + +ER3(SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION, (SSL_ERROR_BASE + 177), + "SSL received a malformed ESNI extension") + +ER3(SSL_ERROR_MISSING_ESNI_EXTENSION, (SSL_ERROR_BASE + 178), + "SSL did not receive an ESNI extension") + +ER3(SSL_ERROR_RX_UNEXPECTED_RECORD_TYPE, (SSL_ERROR_BASE + 179), + "SSL received an unexpected record type.") diff --git a/security/nss/lib/ssl/authcert.c b/security/nss/lib/ssl/authcert.c index 2765c8342..d05b30a72 100644 --- a/security/nss/lib/ssl/authcert.c +++ b/security/nss/lib/ssl/authcert.c @@ -13,7 +13,7 @@ #include "cert.h" #include "nspr.h" #include "secder.h" -#include "key.h" +#include "keyhi.h" #include "nss.h" #include "ssl.h" #include "pk11func.h" /* for PK11_ function calls */ diff --git a/security/nss/lib/ssl/cmpcert.c b/security/nss/lib/ssl/cmpcert.c index e6edbee83..8ab4a7f8d 100644 --- a/security/nss/lib/ssl/cmpcert.c +++ b/security/nss/lib/ssl/cmpcert.c @@ -13,7 +13,7 @@ #include "cert.h" #include "nspr.h" #include "secder.h" -#include "key.h" +#include "keyhi.h" #include "nss.h" /* @@ -27,13 +27,9 @@ NSS_CmpCertChainWCANames(CERTCertificate *cert, CERTDistNames *caNames) SECItem *caname; CERTCertificate *curcert; CERTCertificate *oldcert; - PRInt32 contentlen; int j; - int headerlen; int depth; - SECStatus rv; SECItem issuerName; - SECItem compatIssuerName; if (!cert || !caNames || !caNames->nnames || !caNames->names || !caNames->names->data) @@ -44,29 +40,11 @@ NSS_CmpCertChainWCANames(CERTCertificate *cert, CERTDistNames *caNames) while (curcert) { issuerName = curcert->derIssuer; - /* compute an alternate issuer name for compatibility with 2.0 - * enterprise server, which send the CA names without - * the outer layer of DER header - */ - rv = DER_Lengths(&issuerName, &headerlen, (PRUint32 *)&contentlen); - if (rv == SECSuccess) { - compatIssuerName.data = &issuerName.data[headerlen]; - compatIssuerName.len = issuerName.len - headerlen; - } else { - compatIssuerName.data = NULL; - compatIssuerName.len = 0; - } - for (j = 0; j < caNames->nnames; j++) { caname = &caNames->names[j]; if (SECITEM_CompareItem(&issuerName, caname) == SECEqual) { - rv = SECSuccess; CERT_DestroyCertificate(curcert); - goto done; - } else if (SECITEM_CompareItem(&compatIssuerName, caname) == SECEqual) { - rv = SECSuccess; - CERT_DestroyCertificate(curcert); - goto done; + return SECSuccess; } } if ((depth <= 20) && @@ -82,8 +60,5 @@ NSS_CmpCertChainWCANames(CERTCertificate *cert, CERTDistNames *caNames) curcert = NULL; } } - rv = SECFailure; - -done: - return rv; + return SECFailure; } diff --git a/security/nss/lib/ssl/config.mk b/security/nss/lib/ssl/config.mk index d13613f78..b901a8830 100644 --- a/security/nss/lib/ssl/config.mk +++ b/security/nss/lib/ssl/config.mk @@ -60,3 +60,7 @@ endif ifdef NSS_DISABLE_TLS_1_3 DEFINES += -DNSS_DISABLE_TLS_1_3 endif + +ifeq (,$(filter-out DragonFly FreeBSD Linux NetBSD OpenBSD, $(OS_TARGET))) +CFLAGS += -std=gnu99 +endif diff --git a/security/nss/lib/ssl/dtls13con.c b/security/nss/lib/ssl/dtls13con.c index de6cb47ca..81d196dee 100644 --- a/security/nss/lib/ssl/dtls13con.c +++ b/security/nss/lib/ssl/dtls13con.c @@ -32,7 +32,7 @@ dtls13_InsertCipherTextHeader(const sslSocket *ss, ssl3CipherSpec *cwSpec, return sslBuffer_AppendNumber(wrBuf, seq, 2); } - rv = sslBuffer_AppendNumber(wrBuf, content_application_data, 1); + rv = sslBuffer_AppendNumber(wrBuf, ssl_ct_application_data, 1); if (rv != SECSuccess) { return SECFailure; } @@ -147,7 +147,7 @@ dtls13_SendAck(sslSocket *ss) } ssl_GetXmitBufLock(ss); - sent = ssl3_SendRecord(ss, NULL, content_ack, + sent = ssl3_SendRecord(ss, NULL, ssl_ct_ack, buf.buf, buf.len, 0); ssl_ReleaseXmitBufLock(ss); if (sent != buf.len) { @@ -343,7 +343,7 @@ dtls13_SetupAcks(sslSocket *ss) */ SECStatus dtls13_HandleOutOfEpochRecord(sslSocket *ss, const ssl3CipherSpec *spec, - SSL3ContentType rType, + SSLContentType rType, sslBuffer *databuf) { SECStatus rv; @@ -360,7 +360,7 @@ dtls13_HandleOutOfEpochRecord(sslSocket *ss, const ssl3CipherSpec *spec, SSL_TRC(10, ("%d: DTLS13[%d]: handle out of epoch record: type=%d", SSL_GETPID(), ss->fd, rType)); - if (rType == content_ack) { + if (rType == ssl_ct_ack) { ssl_GetSSL3HandshakeLock(ss); rv = dtls13_HandleAck(ss, &buf); ssl_ReleaseSSL3HandshakeLock(ss); @@ -380,7 +380,7 @@ dtls13_HandleOutOfEpochRecord(sslSocket *ss, const ssl3CipherSpec *spec, * retransmitted Finished (e.g., because our ACK got lost.) * We just retransmit the previous Finished to let the client * complete. */ - if (rType == content_handshake) { + if (rType == ssl_ct_handshake) { if ((ss->sec.isServer) && (ss->ssl3.hs.ws == idle_handshake)) { PORT_Assert(dtls_TimerActive(ss, ss->ssl3.hs.hdTimer)); diff --git a/security/nss/lib/ssl/dtls13con.h b/security/nss/lib/ssl/dtls13con.h index ca48ef363..ce92a8a55 100644 --- a/security/nss/lib/ssl/dtls13con.h +++ b/security/nss/lib/ssl/dtls13con.h @@ -21,7 +21,7 @@ PRBool dtls_NextUnackedRange(sslSocket *ss, PRUint16 msgSeq, PRUint32 offset, PRUint32 len, PRUint32 *startOut, PRUint32 *endOut); SECStatus dtls13_SetupAcks(sslSocket *ss); SECStatus dtls13_HandleOutOfEpochRecord(sslSocket *ss, const ssl3CipherSpec *spec, - SSL3ContentType rType, + SSLContentType rType, sslBuffer *databuf); SECStatus dtls13_HandleAck(sslSocket *ss, sslBuffer *databuf); diff --git a/security/nss/lib/ssl/dtlscon.c b/security/nss/lib/ssl/dtlscon.c index a82295c66..a5c604bca 100644 --- a/security/nss/lib/ssl/dtlscon.c +++ b/security/nss/lib/ssl/dtlscon.c @@ -120,7 +120,7 @@ ssl3_DisableNonDTLSSuites(sslSocket *ss) * Called from dtls_QueueMessage() */ static DTLSQueuedMessage * -dtls_AllocQueuedMessage(ssl3CipherSpec *cwSpec, SSL3ContentType type, +dtls_AllocQueuedMessage(ssl3CipherSpec *cwSpec, SSLContentType ct, const unsigned char *data, PRUint32 len) { DTLSQueuedMessage *msg; @@ -138,7 +138,7 @@ dtls_AllocQueuedMessage(ssl3CipherSpec *cwSpec, SSL3ContentType type, msg->len = len; msg->cwSpec = cwSpec; - msg->type = type; + msg->type = ct; /* Safe if we are < 1.3, since the refct is * already very high. */ ssl_CipherSpecAddRef(cwSpec); @@ -517,7 +517,7 @@ loser: * ssl3_SendChangeCipherSpecs() */ SECStatus -dtls_QueueMessage(sslSocket *ss, SSL3ContentType type, +dtls_QueueMessage(sslSocket *ss, SSLContentType ct, const PRUint8 *pIn, PRInt32 nIn) { SECStatus rv = SECSuccess; @@ -528,7 +528,7 @@ dtls_QueueMessage(sslSocket *ss, SSL3ContentType type, PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); spec = ss->ssl3.cwSpec; - msg = dtls_AllocQueuedMessage(spec, type, pIn, nIn); + msg = dtls_AllocQueuedMessage(spec, ct, pIn, nIn); if (!msg) { PORT_SetError(SEC_ERROR_NO_MEMORY); @@ -562,7 +562,7 @@ dtls_StageHandshakeMessage(sslSocket *ss) if (!ss->sec.ci.sendBuf.buf || !ss->sec.ci.sendBuf.len) return rv; - rv = dtls_QueueMessage(ss, content_handshake, + rv = dtls_QueueMessage(ss, ssl_ct_handshake, ss->sec.ci.sendBuf.buf, ss->sec.ci.sendBuf.len); /* Whether we succeeded or failed, toss the old handshake data. */ @@ -696,7 +696,7 @@ dtls_FragmentHandshake(sslSocket *ss, DTLSQueuedMessage *msg) PORT_Assert(msg->len >= DTLS_HS_HDR_LEN); /* DTLS only supports fragmenting handshaking messages. */ - PORT_Assert(msg->type == content_handshake); + PORT_Assert(msg->type == ssl_ct_handshake); msgSeq = (msg->data[4] << 8) | msg->data[5]; @@ -848,7 +848,7 @@ dtls_TransmitMessageFlight(sslSocket *ss) * be quite fragmented. Adding an extra flush here would push new * messages into new records and reduce fragmentation. */ - if (msg->type == content_handshake) { + if (msg->type == ssl_ct_handshake) { rv = dtls_FragmentHandshake(ss, msg); } else { PORT_Assert(!tls13_MaybeTls13(ss)); @@ -1327,9 +1327,9 @@ dtls_IsLongHeader(SSL3ProtocolVersion version, PRUint8 firstOctet) { #ifndef UNSAFE_FUZZER_MODE return version < SSL_LIBRARY_VERSION_TLS_1_3 || - firstOctet == content_handshake || - firstOctet == content_ack || - firstOctet == content_alert; + firstOctet == ssl_ct_handshake || + firstOctet == ssl_ct_ack || + firstOctet == ssl_ct_alert; #else return PR_TRUE; #endif @@ -1359,7 +1359,7 @@ dtls_ReadEpoch(const ssl3CipherSpec *crSpec, const PRUint8 *hdr) } /* dtls_GatherData should ensure that this works. */ - PORT_Assert(hdr[0] == content_application_data); + PORT_Assert(hdr[0] == ssl_ct_application_data); /* This uses the same method as is used to recover the sequence number in * dtls_ReadSequenceNumber, except that the maximum value is set to the diff --git a/security/nss/lib/ssl/dtlscon.h b/security/nss/lib/ssl/dtlscon.h index 45fc069b9..4ede3c2ca 100644 --- a/security/nss/lib/ssl/dtlscon.h +++ b/security/nss/lib/ssl/dtlscon.h @@ -23,7 +23,7 @@ extern SECStatus dtls_HandleHandshake(sslSocket *ss, DTLSEpoch epoch, extern SECStatus dtls_HandleHelloVerifyRequest(sslSocket *ss, PRUint8 *b, PRUint32 length); extern SECStatus dtls_StageHandshakeMessage(sslSocket *ss); -extern SECStatus dtls_QueueMessage(sslSocket *ss, SSL3ContentType type, +extern SECStatus dtls_QueueMessage(sslSocket *ss, SSLContentType type, const PRUint8 *pIn, PRInt32 nIn); extern SECStatus dtls_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags); SECStatus ssl3_DisableNonDTLSSuites(sslSocket *ss); diff --git a/security/nss/lib/ssl/manifest.mn b/security/nss/lib/ssl/manifest.mn index ca9b9ee7b..fe9470bd0 100644 --- a/security/nss/lib/ssl/manifest.mn +++ b/security/nss/lib/ssl/manifest.mn @@ -1,4 +1,3 @@ -# # 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/. @@ -56,6 +55,7 @@ CSRCS = \ tls13replay.c \ sslcert.c \ sslgrp.c \ + tls13esni.c \ $(NULL) LIBRARY_NAME = ssl diff --git a/security/nss/lib/ssl/ssl.gyp b/security/nss/lib/ssl/ssl.gyp index 3694ab91a..2e28f6775 100644 --- a/security/nss/lib/ssl/ssl.gyp +++ b/security/nss/lib/ssl/ssl.gyp @@ -43,6 +43,7 @@ 'ssltrace.c', 'sslver.c', 'tls13con.c', + 'tls13esni.c', 'tls13exthandle.c', 'tls13hashstate.c', 'tls13hkdf.c', @@ -67,6 +68,11 @@ 'UNSAFE_FUZZER_MODE', ], }], + [ 'OS=="dragonfly" or OS=="freebsd" or OS=="netbsd" or OS=="openbsd" or OS=="linux"', { + 'cflags': [ + '-std=gnu99', + ], + }], ], 'dependencies': [ '<(DEPTH)/exports.gyp:nss_exports', diff --git a/security/nss/lib/ssl/ssl.h b/security/nss/lib/ssl/ssl.h index ecc4f9506..fc4a4a70c 100644 --- a/security/nss/lib/ssl/ssl.h +++ b/security/nss/lib/ssl/ssl.h @@ -13,7 +13,7 @@ #include "prio.h" #include "seccomon.h" #include "cert.h" -#include "keyt.h" +#include "keythi.h" #include "sslt.h" /* public ssl data types */ @@ -282,6 +282,23 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRFileDesc *model, PRFileDesc *fd); */ #define SSL_ENABLE_DTLS_SHORT_HEADER 36 +/* + * Enables the processing of the downgrade sentinel that can be added to the + * ServerHello.random by a server that supports Section 4.1.3 of TLS 1.3 + * [RFC8446]. This sentinel will always be generated by a server that + * negotiates a version lower than its maximum, this only controls whether a + * client will treat receipt of a value that indicates a downgrade as an error. + */ +#define SSL_ENABLE_HELLO_DOWNGRADE_CHECK 37 + +/* Enables the SSLv2-compatible ClientHello for servers. NSS does not support + * SSLv2 and will never send an SSLv2-compatible ClientHello as a client. An + * NSS server with this option enabled will accept a ClientHello that is + * v2-compatible as defined in Appendix E.1 of RFC 6101. + * + * This is disabled by default and will be removed in a future version. */ +#define SSL_ENABLE_V2_COMPATIBLE_HELLO 38 + #ifdef SSL_DEPRECATED_FUNCTION /* Old deprecated function names */ SSL_IMPORT SECStatus SSL_Enable(PRFileDesc *fd, int option, PRIntn on); diff --git a/security/nss/lib/ssl/ssl3con.c b/security/nss/lib/ssl/ssl3con.c index 466fc296f..3b5c69b11 100644 --- a/security/nss/lib/ssl/ssl3con.c +++ b/security/nss/lib/ssl/ssl3con.c @@ -93,8 +93,8 @@ static ssl3CipherSuiteCfg cipherSuites[ssl_V3_SUITES_IMPLEMENTED] = { { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE}, { TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE}, { TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE}, - { TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, SSL_ALLOWED, PR_FALSE, PR_FALSE}, - { TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, SSL_ALLOWED, PR_FALSE, PR_FALSE}, + { TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, SSL_ALLOWED, PR_TRUE, PR_FALSE}, + { TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, SSL_ALLOWED, PR_TRUE, PR_FALSE}, /* TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA is out of order to work around * bug 946147. */ @@ -114,7 +114,7 @@ static ssl3CipherSuiteCfg cipherSuites[ssl_V3_SUITES_IMPLEMENTED] = { { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE}, { TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,SSL_ALLOWED,PR_TRUE, PR_FALSE}, { TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_FALSE, PR_FALSE}, - { TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, SSL_ALLOWED, PR_FALSE, PR_FALSE}, + { TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, SSL_ALLOWED, PR_TRUE, PR_FALSE}, { TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, SSL_ALLOWED, PR_FALSE, PR_FALSE}, { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE}, { TLS_DHE_DSS_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE}, @@ -143,7 +143,7 @@ static ssl3CipherSuiteCfg cipherSuites[ssl_V3_SUITES_IMPLEMENTED] = { /* RSA */ { TLS_RSA_WITH_AES_128_GCM_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE}, - { TLS_RSA_WITH_AES_256_GCM_SHA384, SSL_ALLOWED, PR_FALSE, PR_FALSE}, + { TLS_RSA_WITH_AES_256_GCM_SHA384, SSL_ALLOWED, PR_TRUE, PR_FALSE}, { TLS_RSA_WITH_AES_128_CBC_SHA, SSL_ALLOWED, PR_TRUE, PR_FALSE}, { TLS_RSA_WITH_AES_128_CBC_SHA256, SSL_ALLOWED, PR_TRUE, PR_FALSE}, { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, SSL_ALLOWED, PR_FALSE, PR_FALSE}, @@ -501,19 +501,19 @@ ssl3_DecodeContentType(int msgType) static char line[40]; switch (msgType) { - case content_change_cipher_spec: + case ssl_ct_change_cipher_spec: rv = "change_cipher_spec (20)"; break; - case content_alert: + case ssl_ct_alert: rv = "alert (21)"; break; - case content_handshake: + case ssl_ct_handshake: rv = "handshake (22)"; break; - case content_application_data: + case ssl_ct_application_data: rv = "application_data (23)"; break; - case content_ack: + case ssl_ct_ack: rv = "ack (25)"; break; default: @@ -656,7 +656,7 @@ ssl_LookupCipherSuiteCfgMutable(ssl3CipherSuite suite, return NULL; } -const static ssl3CipherSuiteCfg * +const ssl3CipherSuiteCfg * ssl_LookupCipherSuiteCfg(ssl3CipherSuite suite, const ssl3CipherSuiteCfg *suites) { return ssl_LookupCipherSuiteCfgMutable(suite, @@ -765,6 +765,9 @@ ssl_HasCert(const sslSocket *ss, SSLAuthType authType) } return PR_TRUE; } + if (authType == ssl_auth_rsa_sign) { + return ssl_HasCert(ss, ssl_auth_rsa_pss); + } return PR_FALSE; } @@ -851,9 +854,9 @@ ssl3_config_match_init(sslSocket *ss) /* Return PR_TRUE if suite is usable. This if the suite is permitted by policy, * enabled, has a certificate (as needed), has a viable key agreement method, is * usable with the negotiated TLS version, and is otherwise usable. */ -static PRBool -config_match(const ssl3CipherSuiteCfg *suite, PRUint8 policy, - const SSLVersionRange *vrange, const sslSocket *ss) +PRBool +ssl3_config_match(const ssl3CipherSuiteCfg *suite, PRUint8 policy, + const SSLVersionRange *vrange, const sslSocket *ss) { const ssl3CipherSuiteDef *cipher_def; const ssl3KEADef *kea_def; @@ -896,7 +899,7 @@ count_cipher_suites(sslSocket *ss, PRUint8 policy) return 0; } for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) { - if (config_match(&ss->cipherSuites[i], policy, &ss->vrange, ss)) + if (ssl3_config_match(&ss->cipherSuites[i], policy, &ss->vrange, ss)) count++; } if (count == 0) { @@ -1120,6 +1123,8 @@ ssl3_SignHashes(sslSocket *ss, SSL3Hashes *hash, SECKEYPrivateKey *key, if (ss->sec.isServer) { ss->sec.signatureScheme = ss->ssl3.hs.signatureScheme; + ss->sec.authType = + ssl_SignatureSchemeToAuthType(ss->ssl3.hs.signatureScheme); } PRINT_BUF(60, (NULL, "signed hashes", (unsigned char *)buf->data, buf->len)); done: @@ -1255,6 +1260,7 @@ ssl3_VerifySignedHashes(sslSocket *ss, SSLSignatureScheme scheme, SSL3Hashes *ha } if (!ss->sec.isServer) { ss->sec.signatureScheme = scheme; + ss->sec.authType = ssl_SignatureSchemeToAuthType(scheme); } loser: @@ -1506,7 +1512,7 @@ loser: static SECStatus ssl3_BuildRecordPseudoHeader(DTLSEpoch epoch, sslSequenceNumber seqNum, - SSL3ContentType type, + SSLContentType ct, PRBool includesVersion, SSL3ProtocolVersion version, PRBool isDTLS, @@ -1526,7 +1532,7 @@ ssl3_BuildRecordPseudoHeader(DTLSEpoch epoch, if (rv != SECSuccess) { return SECFailure; } - rv = sslBuffer_AppendNumber(buf, type, 1); + rv = sslBuffer_AppendNumber(buf, ct, 1); if (rv != SECSuccess) { return SECFailure; } @@ -1994,7 +2000,7 @@ SECStatus ssl3_MACEncryptRecord(ssl3CipherSpec *cwSpec, PRBool isServer, PRBool isDTLS, - SSL3ContentType type, + SSLContentType ct, const PRUint8 *pIn, PRUint32 contentLen, sslBuffer *wrBuf) @@ -2041,7 +2047,7 @@ ssl3_MACEncryptRecord(ssl3CipherSpec *cwSpec, } rv = ssl3_BuildRecordPseudoHeader( - cwSpec->epoch, cwSpec->nextSeqNum, type, + cwSpec->epoch, cwSpec->nextSeqNum, ct, cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_0, cwSpec->recordVersion, isDTLS, contentLen, &pseudoHeader); PORT_Assert(rv == SECSuccess); @@ -2163,7 +2169,7 @@ ssl3_MACEncryptRecord(ssl3CipherSpec *cwSpec, /* Note: though this can report failure, it shouldn't. */ SECStatus ssl_InsertRecordHeader(const sslSocket *ss, ssl3CipherSpec *cwSpec, - SSL3ContentType contentType, sslBuffer *wrBuf, + SSLContentType contentType, sslBuffer *wrBuf, PRBool *needsLength) { SECStatus rv; @@ -2175,7 +2181,7 @@ ssl_InsertRecordHeader(const sslSocket *ss, ssl3CipherSpec *cwSpec, return dtls13_InsertCipherTextHeader(ss, cwSpec, wrBuf, needsLength); } - contentType = content_application_data; + contentType = ssl_ct_application_data; } #endif rv = sslBuffer_AppendNumber(wrBuf, contentType, 1); @@ -2202,7 +2208,7 @@ ssl_InsertRecordHeader(const sslSocket *ss, ssl3CipherSpec *cwSpec, } SECStatus -ssl_ProtectRecord(sslSocket *ss, ssl3CipherSpec *cwSpec, SSL3ContentType type, +ssl_ProtectRecord(sslSocket *ss, ssl3CipherSpec *cwSpec, SSLContentType ct, const PRUint8 *pIn, PRUint32 contentLen, sslBuffer *wrBuf) { PRBool needsLength; @@ -2222,7 +2228,7 @@ ssl_ProtectRecord(sslSocket *ss, ssl3CipherSpec *cwSpec, SSL3ContentType type, return SECFailure; } - rv = ssl_InsertRecordHeader(ss, cwSpec, type, wrBuf, &needsLength); + rv = ssl_InsertRecordHeader(ss, cwSpec, ct, wrBuf, &needsLength); if (rv != SECSuccess) { return SECFailure; } @@ -2246,9 +2252,9 @@ ssl_ProtectRecord(sslSocket *ss, ssl3CipherSpec *cwSpec, SSL3ContentType type, } #else if (cwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_3) { - rv = tls13_ProtectRecord(ss, cwSpec, type, pIn, contentLen, wrBuf); + rv = tls13_ProtectRecord(ss, cwSpec, ct, pIn, contentLen, wrBuf); } else { - rv = ssl3_MACEncryptRecord(cwSpec, ss->sec.isServer, IS_DTLS(ss), type, + rv = ssl3_MACEncryptRecord(cwSpec, ss->sec.isServer, IS_DTLS(ss), ct, pIn, contentLen, wrBuf); } #endif @@ -2270,7 +2276,7 @@ ssl_ProtectRecord(sslSocket *ss, ssl3CipherSpec *cwSpec, SSL3ContentType type, } SECStatus -ssl_ProtectNextRecord(sslSocket *ss, ssl3CipherSpec *spec, SSL3ContentType type, +ssl_ProtectNextRecord(sslSocket *ss, ssl3CipherSpec *spec, SSLContentType ct, const PRUint8 *pIn, unsigned int nIn, unsigned int *written) { @@ -2294,7 +2300,7 @@ ssl_ProtectNextRecord(sslSocket *ss, ssl3CipherSpec *spec, SSL3ContentType type, } } - rv = ssl_ProtectRecord(ss, spec, type, pIn, contentLen, wrBuf); + rv = ssl_ProtectRecord(ss, spec, ct, pIn, contentLen, wrBuf); if (rv != SECSuccess) { return SECFailure; } @@ -2328,7 +2334,7 @@ ssl_ProtectNextRecord(sslSocket *ss, ssl3CipherSpec *spec, SSL3ContentType type, PRInt32 ssl3_SendRecord(sslSocket *ss, ssl3CipherSpec *cwSpec, /* non-NULL for DTLS retransmits */ - SSL3ContentType type, + SSLContentType ct, const PRUint8 *pIn, /* input buffer */ PRInt32 nIn, /* bytes of input */ PRInt32 flags) @@ -2339,7 +2345,7 @@ ssl3_SendRecord(sslSocket *ss, PRInt32 totalSent = 0; SSL_TRC(3, ("%d: SSL3[%d] SendRecord type: %s nIn=%d", - SSL_GETPID(), ss->fd, ssl3_DecodeContentType(type), + SSL_GETPID(), ss->fd, ssl3_DecodeContentType(ct), nIn)); PRINT_BUF(50, (ss, "Send record (plain text)", pIn, nIn)); @@ -2349,7 +2355,7 @@ ssl3_SendRecord(sslSocket *ss, if (ss->ssl3.fatalAlertSent) { SSL_TRC(3, ("%d: SSL3[%d] Suppress write, fatal alert already sent", SSL_GETPID(), ss->fd)); - if (type != content_alert) { + if (ct != ssl_ct_alert) { /* If we are sending an alert, then we already have an * error, so don't overwrite. */ PORT_SetError(SSL_ERROR_HANDSHAKE_FAILED); @@ -2366,8 +2372,8 @@ ssl3_SendRecord(sslSocket *ss, if (cwSpec) { /* cwSpec can only be set for retransmissions of the DTLS handshake. */ PORT_Assert(IS_DTLS(ss) && - (type == content_handshake || - type == content_change_cipher_spec)); + (ct == ssl_ct_handshake || + ct == ssl_ct_change_cipher_spec)); spec = cwSpec; } else { spec = ss->ssl3.cwSpec; @@ -2378,7 +2384,7 @@ ssl3_SendRecord(sslSocket *ss, PRInt32 sent; ssl_GetSpecReadLock(ss); - rv = ssl_ProtectNextRecord(ss, spec, type, pIn, nIn, &written); + rv = ssl_ProtectNextRecord(ss, spec, ct, pIn, nIn, &written); ssl_ReleaseSpecReadLock(ss); if (rv != SECSuccess) { goto loser; @@ -2386,7 +2392,7 @@ ssl3_SendRecord(sslSocket *ss, PORT_Assert(written > 0); /* DTLS should not fragment non-application data here. */ - if (IS_DTLS(ss) && type != content_application_data) { + if (IS_DTLS(ss) && ct != ssl_ct_application_data) { PORT_Assert(written == nIn); } @@ -2535,7 +2541,7 @@ ssl3_SendApplicationData(sslSocket *ss, const unsigned char *in, * Note that the 0 epoch is OK because flags will never require * its use, as guaranteed by the PORT_Assert above. */ - sent = ssl3_SendRecord(ss, NULL, content_application_data, + sent = ssl3_SendRecord(ss, NULL, ssl_ct_application_data, in + totalSent, toSend, flags); if (sent < 0) { if (totalSent > 0 && PR_GetError() == PR_WOULD_BLOCK_ERROR) { @@ -2618,7 +2624,7 @@ ssl3_FlushHandshakeMessages(sslSocket *ss, PRInt32 flags) PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } - count = ssl3_SendRecord(ss, NULL, content_handshake, + count = ssl3_SendRecord(ss, NULL, ssl_ct_handshake, ss->sec.ci.sendBuf.buf, ss->sec.ci.sendBuf.len, flags); if (count < 0) { @@ -2744,7 +2750,7 @@ SSL3_SendAlert(sslSocket *ss, SSL3AlertLevel level, SSL3AlertDescription desc) rv = ssl3_FlushHandshake(ss, ssl_SEND_FLAG_FORCE_INTO_BUFFER); if (rv == SECSuccess) { PRInt32 sent; - sent = ssl3_SendRecord(ss, NULL, content_alert, bytes, 2, + sent = ssl3_SendRecord(ss, NULL, ssl_ct_alert, bytes, 2, (desc == no_certificate) ? ssl_SEND_FLAG_FORCE_INTO_BUFFER : 0); rv = (sent >= 0) ? SECSuccess : (SECStatus)sent; } @@ -3041,13 +3047,13 @@ ssl3_SendChangeCipherSpecsInt(sslSocket *ss) if (!IS_DTLS(ss)) { PRInt32 sent; - sent = ssl3_SendRecord(ss, NULL, content_change_cipher_spec, + sent = ssl3_SendRecord(ss, NULL, ssl_ct_change_cipher_spec, &change, 1, ssl_SEND_FLAG_FORCE_INTO_BUFFER); if (sent < 0) { return SECFailure; /* error code set by ssl3_SendRecord */ } } else { - rv = dtls_QueueMessage(ss, content_change_cipher_spec, &change, 1); + rv = dtls_QueueMessage(ss, ssl_ct_change_cipher_spec, &change, 1); if (rv != SECSuccess) { return SECFailure; } @@ -4002,8 +4008,8 @@ ssl_SignatureSchemeToHashType(SSLSignatureScheme scheme) return ssl_hash_none; } -KeyType -ssl_SignatureSchemeToKeyType(SSLSignatureScheme scheme) +static PRBool +ssl_SignatureSchemeMatchesSpkiOid(SSLSignatureScheme scheme, SECOidTag spkiOid) { switch (scheme) { case ssl_sig_rsa_pkcs1_sha256: @@ -4013,133 +4019,243 @@ ssl_SignatureSchemeToKeyType(SSLSignatureScheme scheme) case ssl_sig_rsa_pss_rsae_sha256: case ssl_sig_rsa_pss_rsae_sha384: case ssl_sig_rsa_pss_rsae_sha512: + case ssl_sig_rsa_pkcs1_sha1md5: + return (spkiOid == SEC_OID_X500_RSA_ENCRYPTION) || + (spkiOid == SEC_OID_PKCS1_RSA_ENCRYPTION); case ssl_sig_rsa_pss_pss_sha256: case ssl_sig_rsa_pss_pss_sha384: case ssl_sig_rsa_pss_pss_sha512: - case ssl_sig_rsa_pkcs1_sha1md5: - return rsaKey; + return spkiOid == SEC_OID_PKCS1_RSA_PSS_SIGNATURE; case ssl_sig_ecdsa_secp256r1_sha256: case ssl_sig_ecdsa_secp384r1_sha384: case ssl_sig_ecdsa_secp521r1_sha512: case ssl_sig_ecdsa_sha1: - return ecKey; + return spkiOid == SEC_OID_ANSIX962_EC_PUBLIC_KEY; case ssl_sig_dsa_sha256: case ssl_sig_dsa_sha384: case ssl_sig_dsa_sha512: case ssl_sig_dsa_sha1: - return dsaKey; + return spkiOid == SEC_OID_ANSIX9_DSA_SIGNATURE; case ssl_sig_none: case ssl_sig_ed25519: case ssl_sig_ed448: break; } PORT_Assert(0); - return nullKey; + return PR_FALSE; } -static SSLNamedGroup -ssl_NamedGroupForSignatureScheme(SSLSignatureScheme scheme) +/* Validate that the signature scheme works for the given key type. */ +static PRBool +ssl_SignatureSchemeValid(SSLSignatureScheme scheme, SECOidTag spkiOid, + PRBool isTls13) { - switch (scheme) { - case ssl_sig_ecdsa_secp256r1_sha256: - return ssl_grp_ec_secp256r1; - case ssl_sig_ecdsa_secp384r1_sha384: - return ssl_grp_ec_secp384r1; - case ssl_sig_ecdsa_secp521r1_sha512: - return ssl_grp_ec_secp521r1; - default: + if (!ssl_IsSupportedSignatureScheme(scheme)) { + return PR_FALSE; + } + if (!ssl_SignatureSchemeMatchesSpkiOid(scheme, spkiOid)) { + return PR_FALSE; + } + if (isTls13) { + if (ssl_SignatureSchemeToHashType(scheme) == ssl_hash_sha1) { + return PR_FALSE; + } + /* With TLS 1.3, EC keys should have been selected based on calling + * ssl_SignatureSchemeFromSpki(), reject them otherwise. */ + return spkiOid != SEC_OID_ANSIX962_EC_PUBLIC_KEY; + } + return PR_TRUE; +} + +static SECStatus +ssl_SignatureSchemeFromPssSpki(CERTSubjectPublicKeyInfo *spki, + SSLSignatureScheme *scheme) +{ + SECKEYRSAPSSParams pssParam = { 0 }; + PORTCheapArenaPool arena; + SECStatus rv; + + /* The key doesn't have parameters, boo. */ + if (!spki->algorithm.parameters.len) { + *scheme = ssl_sig_none; + return SECSuccess; + } + + PORT_InitCheapArena(&arena, DER_DEFAULT_CHUNKSIZE); + rv = SEC_QuickDERDecodeItem(&arena.arena, &pssParam, + SEC_ASN1_GET(SECKEY_RSAPSSParamsTemplate), + &spki->algorithm.parameters); + if (rv != SECSuccess) { + goto loser; + } + /* Not having hashAlg means SHA-1 and we don't accept that. */ + if (!pssParam.hashAlg) { + goto loser; + } + switch (SECOID_GetAlgorithmTag(pssParam.hashAlg)) { + case SEC_OID_SHA256: + *scheme = ssl_sig_rsa_pss_pss_sha256; break; + case SEC_OID_SHA384: + *scheme = ssl_sig_rsa_pss_pss_sha384; + break; + case SEC_OID_SHA512: + *scheme = ssl_sig_rsa_pss_pss_sha512; + break; + default: + goto loser; } - PORT_Assert(0); - return 0; + + PORT_DestroyCheapArena(&arena); + return SECSuccess; + +loser: + PORT_DestroyCheapArena(&arena); + PORT_SetError(SSL_ERROR_BAD_CERTIFICATE); + return SECFailure; } -/* Validate that the signature scheme works for the given key. - * If |allowSha1| is set, we allow the use of SHA-1. - * If |matchGroup| is set, we also check that the group and hash match. */ -static PRBool -ssl_SignatureSchemeValidForKey(PRBool allowSha1, PRBool matchGroup, - KeyType keyType, - const sslNamedGroupDef *ecGroup, - SSLSignatureScheme scheme) +static SECStatus +ssl_SignatureSchemeFromEcSpki(CERTSubjectPublicKeyInfo *spki, + SSLSignatureScheme *scheme) { - if (!ssl_IsSupportedSignatureScheme(scheme)) { - return PR_FALSE; + const sslNamedGroupDef *group; + SECKEYPublicKey *key; + + key = SECKEY_ExtractPublicKey(spki); + if (!key) { + PORT_SetError(SSL_ERROR_BAD_CERTIFICATE); + return SECFailure; } - if (keyType != ssl_SignatureSchemeToKeyType(scheme)) { - return PR_FALSE; + group = ssl_ECPubKey2NamedGroup(key); + SECKEY_DestroyPublicKey(key); + if (!group) { + PORT_SetError(SSL_ERROR_BAD_CERTIFICATE); + return SECFailure; } - if (!allowSha1 && ssl_SignatureSchemeToHashType(scheme) == ssl_hash_sha1) { - return PR_FALSE; + switch (group->name) { + case ssl_grp_ec_secp256r1: + *scheme = ssl_sig_ecdsa_secp256r1_sha256; + return SECSuccess; + case ssl_grp_ec_secp384r1: + *scheme = ssl_sig_ecdsa_secp384r1_sha384; + return SECSuccess; + case ssl_grp_ec_secp521r1: + *scheme = ssl_sig_ecdsa_secp521r1_sha512; + return SECSuccess; + default: + break; } - if (keyType != ecKey) { - return PR_TRUE; + PORT_SetError(SSL_ERROR_BAD_CERTIFICATE); + return SECFailure; +} + +/* Newer signature schemes are designed so that a single SPKI can be used with + * that scheme. This determines that scheme from the SPKI. If the SPKI doesn't + * have a single scheme, |*scheme| is set to ssl_sig_none. */ +static SECStatus +ssl_SignatureSchemeFromSpki(CERTSubjectPublicKeyInfo *spki, + PRBool isTls13, SSLSignatureScheme *scheme) +{ + SECOidTag spkiOid = SECOID_GetAlgorithmTag(&spki->algorithm); + + if (spkiOid == SEC_OID_PKCS1_RSA_PSS_SIGNATURE) { + return ssl_SignatureSchemeFromPssSpki(spki, scheme); } - if (!ecGroup) { - return PR_FALSE; + + /* Only do this lookup for TLS 1.3, where the scheme can be determined from + * the SPKI alone because the ECDSA key size determines the hash. Earlier + * TLS versions allow the same EC key to be used with different hashes. */ + if (isTls13 && spkiOid == SEC_OID_ANSIX962_EC_PUBLIC_KEY) { + return ssl_SignatureSchemeFromEcSpki(spki, scheme); } - /* If |allowSha1| is present and the scheme is ssl_sig_ecdsa_sha1, it's OK. - * This scheme isn't bound to a specific group. */ - if (allowSha1 && (scheme == ssl_sig_ecdsa_sha1)) { - return PR_TRUE; + + *scheme = ssl_sig_none; + return SECSuccess; +} + +static PRBool +ssl_SignatureSchemeEnabled(sslSocket *ss, SSLSignatureScheme scheme) +{ + unsigned int i; + for (i = 0; i < ss->ssl3.signatureSchemeCount; ++i) { + if (scheme == ss->ssl3.signatureSchemes[i]) { + return PR_TRUE; + } } - if (!matchGroup) { - return PR_TRUE; + return PR_FALSE; +} + +static PRBool +ssl_SignatureKeyMatchesSpkiOid(const ssl3KEADef *keaDef, SECOidTag spkiOid) +{ + switch (spkiOid) { + case SEC_OID_X500_RSA_ENCRYPTION: + case SEC_OID_PKCS1_RSA_ENCRYPTION: + case SEC_OID_PKCS1_RSA_PSS_SIGNATURE: + return keaDef->signKeyType == rsaKey; + case SEC_OID_ANSIX9_DSA_SIGNATURE: + return keaDef->signKeyType == dsaKey; + case SEC_OID_ANSIX962_EC_PUBLIC_KEY: + return keaDef->signKeyType == ecKey; + default: + break; } - return ecGroup->name == ssl_NamedGroupForSignatureScheme(scheme); + return PR_FALSE; } -/* ssl3_CheckSignatureSchemeConsistency checks that the signature - * algorithm identifier in |sigAndHash| is consistent with the public key in - * |cert|. It also checks the hash algorithm against the configured signature - * algorithms. If all the tests pass, SECSuccess is returned. Otherwise, - * PORT_SetError is called and SECFailure is returned. */ +/* ssl3_CheckSignatureSchemeConsistency checks that the signature algorithm + * identifier in |scheme| is consistent with the public key in |cert|. It also + * checks the hash algorithm against the configured signature algorithms. If + * all the tests pass, SECSuccess is returned. Otherwise, PORT_SetError is + * called and SECFailure is returned. */ SECStatus -ssl_CheckSignatureSchemeConsistency( - sslSocket *ss, SSLSignatureScheme scheme, CERTCertificate *cert) +ssl_CheckSignatureSchemeConsistency(sslSocket *ss, SSLSignatureScheme scheme, + CERTCertificate *cert) { - unsigned int i; - const sslNamedGroupDef *group = NULL; - SECKEYPublicKey *key; - KeyType keyType; + SSLSignatureScheme spkiScheme; PRBool isTLS13 = ss->version == SSL_LIBRARY_VERSION_TLS_1_3; + SECOidTag spkiOid; + SECStatus rv; - key = CERT_ExtractPublicKey(cert); - if (key == NULL) { - ssl_MapLowLevelError(SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE); + rv = ssl_SignatureSchemeFromSpki(&cert->subjectPublicKeyInfo, isTLS13, + &spkiScheme); + if (rv != SECSuccess) { return SECFailure; } - - keyType = SECKEY_GetPublicKeyType(key); - if (keyType == ecKey) { - group = ssl_ECPubKey2NamedGroup(key); + if (spkiScheme != ssl_sig_none) { + /* The SPKI in the certificate can only be used for a single scheme. */ + if (spkiScheme != scheme || + !ssl_SignatureSchemeEnabled(ss, scheme)) { + PORT_SetError(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM); + return SECFailure; + } + return SECSuccess; } - SECKEY_DestroyPublicKey(key); + + spkiOid = SECOID_GetAlgorithmTag(&cert->subjectPublicKeyInfo.algorithm); /* If we're a client, check that the signature algorithm matches the signing * key type of the cipher suite. */ - if (!isTLS13 && - !ss->sec.isServer && - ss->ssl3.hs.kea_def->signKeyType != keyType) { - PORT_SetError(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM); - return SECFailure; + if (!isTLS13 && !ss->sec.isServer) { + if (!ssl_SignatureKeyMatchesSpkiOid(ss->ssl3.hs.kea_def, spkiOid)) { + PORT_SetError(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM); + return SECFailure; + } } /* Verify that the signature scheme matches the signing key. */ - if (!ssl_SignatureSchemeValidForKey(!isTLS13 /* allowSha1 */, - isTLS13 /* matchGroup */, - keyType, group, scheme)) { + if (!ssl_SignatureSchemeValid(scheme, spkiOid, isTLS13)) { PORT_SetError(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM); return SECFailure; } - for (i = 0; i < ss->ssl3.signatureSchemeCount; ++i) { - if (scheme == ss->ssl3.signatureSchemes[i]) { - return SECSuccess; - } + if (!ssl_SignatureSchemeEnabled(ss, scheme)) { + PORT_SetError(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM); + return SECFailure; } - PORT_SetError(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM); - return SECFailure; + + return SECSuccess; } PRBool @@ -4153,6 +4269,9 @@ ssl_IsSupportedSignatureScheme(SSLSignatureScheme scheme) case ssl_sig_rsa_pss_rsae_sha256: case ssl_sig_rsa_pss_rsae_sha384: case ssl_sig_rsa_pss_rsae_sha512: + case ssl_sig_rsa_pss_pss_sha256: + case ssl_sig_rsa_pss_pss_sha384: + case ssl_sig_rsa_pss_pss_sha512: case ssl_sig_ecdsa_secp256r1_sha256: case ssl_sig_ecdsa_secp384r1_sha384: case ssl_sig_ecdsa_secp521r1_sha512: @@ -4164,9 +4283,6 @@ ssl_IsSupportedSignatureScheme(SSLSignatureScheme scheme) return PR_TRUE; case ssl_sig_rsa_pkcs1_sha1md5: - case ssl_sig_rsa_pss_pss_sha256: - case ssl_sig_rsa_pss_pss_sha384: - case ssl_sig_rsa_pss_pss_sha512: case ssl_sig_none: case ssl_sig_ed25519: case ssl_sig_ed448: @@ -4182,6 +4298,9 @@ ssl_IsRsaPssSignatureScheme(SSLSignatureScheme scheme) case ssl_sig_rsa_pss_rsae_sha256: case ssl_sig_rsa_pss_rsae_sha384: case ssl_sig_rsa_pss_rsae_sha512: + case ssl_sig_rsa_pss_pss_sha256: + case ssl_sig_rsa_pss_pss_sha384: + case ssl_sig_rsa_pss_pss_sha512: return PR_TRUE; default: @@ -4190,6 +4309,41 @@ ssl_IsRsaPssSignatureScheme(SSLSignatureScheme scheme) return PR_FALSE; } +SSLAuthType +ssl_SignatureSchemeToAuthType(SSLSignatureScheme scheme) +{ + switch (scheme) { + case ssl_sig_rsa_pkcs1_sha1: + case ssl_sig_rsa_pkcs1_sha1md5: + case ssl_sig_rsa_pkcs1_sha256: + case ssl_sig_rsa_pkcs1_sha384: + case ssl_sig_rsa_pkcs1_sha512: + /* We report based on the key type for PSS signatures. */ + case ssl_sig_rsa_pss_rsae_sha256: + case ssl_sig_rsa_pss_rsae_sha384: + case ssl_sig_rsa_pss_rsae_sha512: + return ssl_auth_rsa_sign; + case ssl_sig_rsa_pss_pss_sha256: + case ssl_sig_rsa_pss_pss_sha384: + case ssl_sig_rsa_pss_pss_sha512: + return ssl_auth_rsa_pss; + case ssl_sig_ecdsa_secp256r1_sha256: + case ssl_sig_ecdsa_secp384r1_sha384: + case ssl_sig_ecdsa_secp521r1_sha512: + case ssl_sig_ecdsa_sha1: + return ssl_auth_ecdsa; + case ssl_sig_dsa_sha1: + case ssl_sig_dsa_sha256: + case ssl_sig_dsa_sha384: + case ssl_sig_dsa_sha512: + return ssl_auth_dsa; + + default: + PORT_Assert(0); + } + return ssl_auth_null; +} + /* ssl_ConsumeSignatureScheme reads a SSLSignatureScheme (formerly * SignatureAndHashAlgorithm) structure from |b| and puts the resulting value * into |out|. |b| and |length| are updated accordingly. @@ -4617,9 +4771,13 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type) * If we have an sid and it comes from an external cache, we use it. */ if (ss->sec.ci.sid && ss->sec.ci.sid->cached == in_external_cache) { PORT_Assert(!ss->sec.isServer); - sid = ss->sec.ci.sid; + sid = ssl_ReferenceSID(ss->sec.ci.sid); SSL_TRC(3, ("%d: SSL3[%d]: using external resumption token in ClientHello", SSL_GETPID(), ss->fd)); + } else if (ss->sec.ci.sid && ss->statelessResume && type == client_hello_retry) { + /* If we are sending a second ClientHello, reuse the same SID + * as the original one. */ + sid = ssl_ReferenceSID(ss->sec.ci.sid); } else if (!ss->opt.noCache) { /* We ignore ss->sec.ci.sid here, and use ssl_Lookup because Lookup * handles expired entries and other details. @@ -4644,7 +4802,7 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type) suite = ssl_LookupCipherSuiteCfg(sid->u.ssl3.cipherSuite, ss->cipherSuites); PORT_Assert(suite); - if (!suite || !config_match(suite, ss->ssl3.policy, &ss->vrange, ss)) { + if (!suite || !ssl3_config_match(suite, ss->ssl3.policy, &ss->vrange, ss)) { sidOK = PR_FALSE; } @@ -4765,9 +4923,7 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type) } ssl_ReleaseSpecWriteLock(ss); - if (ss->sec.ci.sid != NULL) { - ssl_FreeSID(ss->sec.ci.sid); /* decrement ref count, free if zero */ - } + ssl_FreeSID(ss->sec.ci.sid); /* release the old sid */ ss->sec.ci.sid = sid; /* HACK for SCSV in SSL 3.0. On initial handshake, prepend SCSV, @@ -4792,6 +4948,14 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type) PR_RWLock_Rlock(sid->u.ssl3.lock); } + /* Generate a new random if this is the first attempt. */ + if (type == client_hello_initial) { + rv = ssl3_GetNewRandom(ss->ssl3.hs.client_random); + if (rv != SECSuccess) { + goto loser; /* err set by GetNewRandom. */ + } + } + if (ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3 && type == client_hello_initial) { rv = tls13_SetupClientHello(ss); @@ -4799,6 +4963,7 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type) goto loser; } } + if (isTLS || (ss->firstHsDone && ss->peerRequestedProtection)) { rv = ssl_ConstructExtensions(ss, &extensionBuf, ssl_hs_client_hello); if (rv != SECSuccess) { @@ -4870,13 +5035,6 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type) goto loser; /* err set by ssl3_AppendHandshake* */ } - /* Generate a new random if this is the first attempt. */ - if (type == client_hello_initial) { - rv = ssl3_GetNewRandom(ss->ssl3.hs.client_random); - if (rv != SECSuccess) { - goto loser; /* err set by GetNewRandom. */ - } - } rv = ssl3_AppendHandshake(ss, ss->ssl3.hs.client_random, SSL3_RANDOM_LENGTH); if (rv != SECSuccess) { @@ -4931,7 +5089,7 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type) } for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) { ssl3CipherSuiteCfg *suite = &ss->cipherSuites[i]; - if (config_match(suite, ss->ssl3.policy, &ss->vrange, ss)) { + if (ssl3_config_match(suite, ss->ssl3.policy, &ss->vrange, ss)) { actual_count++; if (actual_count > num_suites) { /* set error card removal/insertion error */ @@ -5394,6 +5552,7 @@ ssl3_GetWrappingKey(sslSocket *ss, switch (authType) { case ssl_auth_rsa_decrypt: case ssl_auth_rsa_sign: /* bad: see Bug 1248320 */ + case ssl_auth_rsa_pss: asymWrapMechanism = CKM_RSA_PKCS; rv = PK11_PubWrapSymKey(asymWrapMechanism, svrPubKey, unwrappedWrappingKey, &wrappedKey); @@ -5843,20 +6002,59 @@ ssl3_SendClientKeyExchange(sslSocket *ss) return rv; /* err code already set. */ } +/* Used by ssl_PickSignatureScheme(). */ +static PRBool +ssl_CanUseSignatureScheme(SSLSignatureScheme scheme, + const SSLSignatureScheme *peerSchemes, + unsigned int peerSchemeCount, + PRBool requireSha1, + PRBool slotDoesPss) +{ + SSLHashType hashType; + SECOidTag hashOID; + PRUint32 policy; + unsigned int i; + + /* Skip RSA-PSS schemes when the certificate's private key slot does + * not support this signature mechanism. */ + if (ssl_IsRsaPssSignatureScheme(scheme) && !slotDoesPss) { + return PR_FALSE; + } + + hashType = ssl_SignatureSchemeToHashType(scheme); + if (requireSha1 && (hashType != ssl_hash_sha1)) { + return PR_FALSE; + } + hashOID = ssl3_HashTypeToOID(hashType); + if ((NSS_GetAlgorithmPolicy(hashOID, &policy) == SECSuccess) && + !(policy & NSS_USE_ALG_IN_SSL_KX)) { + return PR_FALSE; + } + + for (i = 0; i < peerSchemeCount; i++) { + if (peerSchemes[i] == scheme) { + return PR_TRUE; + } + } + return PR_FALSE; +} + SECStatus ssl_PickSignatureScheme(sslSocket *ss, + CERTCertificate *cert, SECKEYPublicKey *pubKey, SECKEYPrivateKey *privKey, const SSLSignatureScheme *peerSchemes, unsigned int peerSchemeCount, PRBool requireSha1) { - unsigned int i, j; - const sslNamedGroupDef *group = NULL; - KeyType keyType; + unsigned int i; PK11SlotInfo *slot; PRBool slotDoesPss; PRBool isTLS13 = ss->version >= SSL_LIBRARY_VERSION_TLS_1_3; + SECStatus rv; + SSLSignatureScheme scheme; + SECOidTag spkiOid; /* We can't require SHA-1 in TLS 1.3. */ PORT_Assert(!(requireSha1 && isTLS13)); @@ -5874,47 +6072,35 @@ ssl_PickSignatureScheme(sslSocket *ss, slotDoesPss = PK11_DoesMechanism(slot, auth_alg_defs[ssl_auth_rsa_pss]); PK11_FreeSlot(slot); - keyType = SECKEY_GetPublicKeyType(pubKey); - if (keyType == ecKey) { - group = ssl_ECPubKey2NamedGroup(pubKey); + /* If the certificate SPKI indicates a single scheme, don't search. */ + rv = ssl_SignatureSchemeFromSpki(&cert->subjectPublicKeyInfo, + isTLS13, &scheme); + if (rv != SECSuccess) { + return SECFailure; } - - /* Here we look for the first local preference that the client has - * indicated support for in their signature_algorithms extension. */ - for (i = 0; i < ss->ssl3.signatureSchemeCount; ++i) { - SSLHashType hashType; - SECOidTag hashOID; - SSLSignatureScheme preferred = ss->ssl3.signatureSchemes[i]; - PRUint32 policy; - - if (!ssl_SignatureSchemeValidForKey(!isTLS13 /* allowSha1 */, - isTLS13 /* matchGroup */, - keyType, group, preferred)) { - continue; + if (scheme != ssl_sig_none) { + if (!ssl_SignatureSchemeEnabled(ss, scheme) || + !ssl_CanUseSignatureScheme(scheme, peerSchemes, peerSchemeCount, + requireSha1, slotDoesPss)) { + PORT_SetError(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM); + return SECFailure; } + ss->ssl3.hs.signatureScheme = scheme; + return SECSuccess; + } - /* Skip RSA-PSS schemes when the certificate's private key slot does - * not support this signature mechanism. */ - if (ssl_IsRsaPssSignatureScheme(preferred) && !slotDoesPss) { - continue; - } + spkiOid = SECOID_GetAlgorithmTag(&cert->subjectPublicKeyInfo.algorithm); - hashType = ssl_SignatureSchemeToHashType(preferred); - if (requireSha1 && (hashType != ssl_hash_sha1)) { - continue; - } - hashOID = ssl3_HashTypeToOID(hashType); - if ((NSS_GetAlgorithmPolicy(hashOID, &policy) == SECSuccess) && - !(policy & NSS_USE_ALG_IN_SSL_KX)) { - /* we ignore hashes we don't support */ - continue; - } + /* Now we have to search based on the key type. Go through our preferred + * schemes in order and find the first that can be used. */ + for (i = 0; i < ss->ssl3.signatureSchemeCount; ++i) { + scheme = ss->ssl3.signatureSchemes[i]; - for (j = 0; j < peerSchemeCount; j++) { - if (peerSchemes[j] == preferred) { - ss->ssl3.hs.signatureScheme = preferred; - return SECSuccess; - } + if (ssl_SignatureSchemeValid(scheme, spkiOid, isTLS13) && + ssl_CanUseSignatureScheme(scheme, peerSchemes, peerSchemeCount, + requireSha1, slotDoesPss)) { + ss->ssl3.hs.signatureScheme = scheme; + return SECSuccess; } } @@ -5956,17 +6142,19 @@ ssl_PickFallbackSignatureScheme(sslSocket *ss, SECKEYPublicKey *pubKey) static SECStatus ssl3_PickServerSignatureScheme(sslSocket *ss) { - sslKeyPair *keyPair = ss->sec.serverCert->serverKeyPair; + const sslServerCert *cert = ss->sec.serverCert; PRBool isTLS12 = ss->version >= SSL_LIBRARY_VERSION_TLS_1_2; if (!isTLS12 || !ssl3_ExtensionNegotiated(ss, ssl_signature_algorithms_xtn)) { /* If the client didn't provide any signature_algorithms extension then * we can assume that they support SHA-1: RFC5246, Section 7.4.1.4.1. */ - return ssl_PickFallbackSignatureScheme(ss, keyPair->pubKey); + return ssl_PickFallbackSignatureScheme(ss, cert->serverKeyPair->pubKey); } /* Sets error code, if needed. */ - return ssl_PickSignatureScheme(ss, keyPair->pubKey, keyPair->privKey, + return ssl_PickSignatureScheme(ss, cert->serverCert, + cert->serverKeyPair->pubKey, + cert->serverKeyPair->privKey, ss->xtnData.sigSchemes, ss->xtnData.numSigSchemes, PR_FALSE /* requireSha1 */); @@ -5977,23 +6165,18 @@ ssl_PickClientSignatureScheme(sslSocket *ss, const SSLSignatureScheme *schemes, unsigned int numSchemes) { SECKEYPrivateKey *privKey = ss->ssl3.clientPrivateKey; - SECKEYPublicKey *pubKey; SECStatus rv; - PRBool isTLS13 = (PRBool)ss->version >= SSL_LIBRARY_VERSION_TLS_1_3; - pubKey = CERT_ExtractPublicKey(ss->ssl3.clientCertificate); + SECKEYPublicKey *pubKey = CERT_ExtractPublicKey(ss->ssl3.clientCertificate); + PORT_Assert(pubKey); - if (!isTLS13 && numSchemes == 0) { - /* If the server didn't provide any signature algorithms - * then let's assume they support SHA-1. */ - rv = ssl_PickFallbackSignatureScheme(ss, pubKey); - SECKEY_DestroyPublicKey(pubKey); - return rv; + if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_2) { + /* We should have already checked that a signature scheme was + * listed in the request. */ + PORT_Assert(schemes && numSchemes > 0); } - PORT_Assert(schemes && numSchemes > 0); - if (!isTLS13 && (SECKEY_GetPublicKeyType(pubKey) == rsaKey || SECKEY_GetPublicKeyType(pubKey) == dsaKey) && @@ -6004,7 +6187,8 @@ ssl_PickClientSignatureScheme(sslSocket *ss, const SSLSignatureScheme *schemes, * older, DSA key size is at most 1024 bits and the hash function must * be SHA-1. */ - rv = ssl_PickSignatureScheme(ss, pubKey, privKey, schemes, numSchemes, + rv = ssl_PickSignatureScheme(ss, ss->ssl3.clientCertificate, + pubKey, privKey, schemes, numSchemes, PR_TRUE /* requireSha1 */); if (rv == SECSuccess) { SECKEY_DestroyPublicKey(pubKey); @@ -6013,7 +6197,8 @@ ssl_PickClientSignatureScheme(sslSocket *ss, const SSLSignatureScheme *schemes, /* If this fails, that's because the peer doesn't advertise SHA-1, * so fall back to the full negotiation. */ } - rv = ssl_PickSignatureScheme(ss, pubKey, privKey, schemes, numSchemes, + rv = ssl_PickSignatureScheme(ss, ss->ssl3.clientCertificate, + pubKey, privKey, schemes, numSchemes, PR_FALSE /* requireSha1 */); SECKEY_DestroyPublicKey(pubKey); return rv; @@ -6141,7 +6326,7 @@ ssl_ClientSetCipherSuite(sslSocket *ss, SSL3ProtocolVersion version, ssl3CipherSuiteCfg *suiteCfg = &ss->cipherSuites[i]; if (suite == suiteCfg->cipher_suite) { SSLVersionRange vrange = { version, version }; - if (!config_match(suiteCfg, ss->ssl3.policy, &vrange, ss)) { + if (!ssl3_config_match(suiteCfg, ss->ssl3.policy, &vrange, ss)) { /* config_match already checks whether the cipher suite is * acceptable for the version, but the check is repeated here * in order to give a more precise error code. */ @@ -6201,18 +6386,56 @@ ssl_CheckServerSessionIdCorrectness(sslSocket *ss, SECItem *sidBytes) /* TLS 1.2: Session ID shouldn't match if we sent a fake. */ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { - return !sentFakeSid || !sidMatch; + if (sentFakeSid) { + return !sidMatch; + } + return PR_TRUE; } /* TLS 1.3: We sent a session ID. The server's should match. */ - if (sentRealSid || sentFakeSid) { + if (!IS_DTLS(ss) && (sentRealSid || sentFakeSid)) { return sidMatch; } - /* TLS 1.3: The server shouldn't send a session ID. */ + /* TLS 1.3 (no SID)/DTLS 1.3: The server shouldn't send a session ID. */ return sidBytes->len == 0; } +static SECStatus +ssl_CheckServerRandom(sslSocket *ss) +{ + /* Check the ServerHello.random per [RFC 8446 Section 4.1.3]. + * + * TLS 1.3 clients receiving a ServerHello indicating TLS 1.2 or below + * MUST check that the last 8 bytes are not equal to either of these + * values. TLS 1.2 clients SHOULD also check that the last 8 bytes are + * not equal to the second value if the ServerHello indicates TLS 1.1 or + * below. If a match is found, the client MUST abort the handshake with + * an "illegal_parameter" alert. + */ + SSL3ProtocolVersion checkVersion = + ss->ssl3.downgradeCheckVersion ? ss->ssl3.downgradeCheckVersion + : ss->vrange.max; + + if (checkVersion >= SSL_LIBRARY_VERSION_TLS_1_2 && + checkVersion > ss->version) { + /* Both sections use the same sentinel region. */ + PRUint8 *downgrade_sentinel = + ss->ssl3.hs.server_random + + SSL3_RANDOM_LENGTH - sizeof(tls13_downgrade_random); + if (!PORT_Memcmp(downgrade_sentinel, + tls13_downgrade_random, + sizeof(tls13_downgrade_random)) || + !PORT_Memcmp(downgrade_sentinel, + tls12_downgrade_random, + sizeof(tls12_downgrade_random))) { + return SECFailure; + } + } + + return SECSuccess; +} + /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete * ssl3 ServerHello message. * Caller must hold Handshake and RecvBuf locks. @@ -6229,9 +6452,6 @@ ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length) SSL3AlertDescription desc = illegal_parameter; const PRUint8 *savedMsg = b; const PRUint32 savedLength = length; -#ifndef TLS_1_3_DRAFT_VERSION - SSL3ProtocolVersion downgradeCheckVersion; -#endif SSL_TRC(3, ("%d: SSL3[%d]: handle server_hello handshake", SSL_GETPID(), ss->fd)); @@ -6341,9 +6561,20 @@ ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length) goto alert_loser; } - /* The server didn't pick 1.3 although we either received a - * HelloRetryRequest, or we prepared to send early app data. */ + /* There are three situations in which the server must pick + * TLS 1.3. + * + * 1. We offered ESNI. + * 2. We received HRR + * 3. We sent early app data. + * + */ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { + if (ss->xtnData.esniPrivateKey) { + desc = protocol_version; + errCode = SSL_ERROR_UNSUPPORTED_VERSION; + goto alert_loser; + } if (isHelloRetry || ss->ssl3.hs.helloRetry) { /* SSL3_SendAlert() will uncache the SID. */ desc = illegal_parameter; @@ -6368,39 +6599,19 @@ ssl3_HandleServerHello(sslSocket *ss, PRUint8 *b, PRUint32 length) goto alert_loser; } -#ifndef TLS_1_3_DRAFT_VERSION - /* Check the ServerHello.random per - * [draft-ietf-tls-tls13-11 Section 6.3.1.1]. - * - * TLS 1.3 clients receiving a TLS 1.2 or below ServerHello MUST check - * that the top eight octets are not equal to either of these values. - * TLS 1.2 clients SHOULD also perform this check if the ServerHello - * indicates TLS 1.1 or below. If a match is found the client MUST - * abort the handshake with a fatal "illegal_parameter" alert. - * - * Disable this test during the TLS 1.3 draft version period. - */ - downgradeCheckVersion = ss->ssl3.downgradeCheckVersion ? ss->ssl3.downgradeCheckVersion - : ss->vrange.max; - - if (downgradeCheckVersion >= SSL_LIBRARY_VERSION_TLS_1_2 && - downgradeCheckVersion > ss->version) { - /* Both sections use the same sentinel region. */ - PRUint8 *downgrade_sentinel = - ss->ssl3.hs.server_random + - SSL3_RANDOM_LENGTH - sizeof(tls13_downgrade_random); - if (!PORT_Memcmp(downgrade_sentinel, - tls13_downgrade_random, - sizeof(tls13_downgrade_random)) || - !PORT_Memcmp(downgrade_sentinel, - tls12_downgrade_random, - sizeof(tls12_downgrade_random))) { + if (ss->opt.enableHelloDowngradeCheck +#ifdef DTLS_1_3_DRAFT_VERSION + /* Disable this check while we are on draft DTLS 1.3 versions. */ + && !IS_DTLS(ss) +#endif + ) { + rv = ssl_CheckServerRandom(ss); + if (rv != SECSuccess) { desc = illegal_parameter; errCode = SSL_ERROR_RX_MALFORMED_SERVER_HELLO; goto alert_loser; } } -#endif /* Finally, now all the version-related checks have passed. */ ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_version; @@ -6776,12 +6987,12 @@ ssl_HandleDHServerKeyExchange(sslSocket *ss, PRUint8 *b, PRUint32 length) if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_2) { rv = ssl_ConsumeSignatureScheme(ss, &b, &length, &sigScheme); if (rv != SECSuccess) { - goto loser; /* malformed or unsupported. */ + goto alert_loser; /* malformed or unsupported. */ } rv = ssl_CheckSignatureSchemeConsistency(ss, sigScheme, ss->sec.peerCert); if (rv != SECSuccess) { - goto loser; + goto alert_loser; } hashAlg = ssl_SignatureSchemeToHashType(sigScheme); } else { @@ -7005,7 +7216,8 @@ ssl_ParseSignatureSchemes(const sslSocket *ss, PLArenaPool *arena, SECStatus rv; SECItem buf; SSLSignatureScheme *schemes = NULL; - unsigned int numSchemes = 0; + unsigned int numSupported = 0; + unsigned int numRemaining = 0; unsigned int max; rv = ssl3_ExtConsumeHandshakeVariable(ss, &buf, 2, b, len); @@ -7024,7 +7236,8 @@ ssl_ParseSignatureSchemes(const sslSocket *ss, PLArenaPool *arena, } /* Limit the number of schemes we read. */ - max = PR_MIN(buf.len / 2, MAX_SIGNATURE_SCHEMES); + numRemaining = buf.len / 2; + max = PR_MIN(numRemaining, MAX_SIGNATURE_SCHEMES); if (arena) { schemes = PORT_ArenaZNewArray(arena, SSLSignatureScheme, max); @@ -7036,7 +7249,7 @@ ssl_ParseSignatureSchemes(const sslSocket *ss, PLArenaPool *arena, return SECFailure; } - for (; max; --max) { + for (; numRemaining && numSupported < MAX_SIGNATURE_SCHEMES; --numRemaining) { PRUint32 tmp; rv = ssl3_ExtConsumeHandshakeNumber(ss, &tmp, 2, &buf.data, &buf.len); if (rv != SECSuccess) { @@ -7045,11 +7258,11 @@ ssl_ParseSignatureSchemes(const sslSocket *ss, PLArenaPool *arena, return SECFailure; } if (ssl_IsSupportedSignatureScheme((SSLSignatureScheme)tmp)) { - schemes[numSchemes++] = (SSLSignatureScheme)tmp; + schemes[numSupported++] = (SSLSignatureScheme)tmp; } } - if (!numSchemes) { + if (!numSupported) { if (!arena) { PORT_Free(schemes); } @@ -7058,7 +7271,7 @@ ssl_ParseSignatureSchemes(const sslSocket *ss, PLArenaPool *arena, done: *schemesOut = schemes; - *numSchemesOut = numSchemes; + *numSchemesOut = numSupported; return SECSuccess; } @@ -7114,6 +7327,11 @@ ssl3_HandleCertificateRequest(sslSocket *ss, PRUint8 *b, PRUint32 length) PORT_SetError(SSL_ERROR_RX_MALFORMED_CERT_REQUEST); goto loser; /* malformed, alert has been sent */ } + if (signatureSchemeCount == 0) { + errCode = SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM; + desc = handshake_failure; + goto alert_loser; + } } rv = ssl3_ParseCertificateRequestCAs(ss, &b, &length, &ca_list); @@ -7239,16 +7457,25 @@ ssl3_CheckFalseStart(sslSocket *ss) SSL_TRC(3, ("%d: SSL[%d]: no false start callback so no false start", SSL_GETPID(), ss->fd)); } else { - PRBool maybeFalseStart; + PRBool maybeFalseStart = PR_TRUE; SECStatus rv; + rv = ssl_CheckServerRandom(ss); + if (rv != SECSuccess) { + SSL_TRC(3, ("%d: SSL[%d]: no false start due to possible downgrade", + SSL_GETPID(), ss->fd)); + maybeFalseStart = PR_FALSE; + } + /* An attacker can control the selected ciphersuite so we only wish to * do False Start in the case that the selected ciphersuite is * sufficiently strong that the attack can gain no advantage. * Therefore we always require an 80-bit cipher. */ - ssl_GetSpecReadLock(ss); - maybeFalseStart = ss->ssl3.cwSpec->cipherDef->secret_key_size >= 10; - ssl_ReleaseSpecReadLock(ss); + if (maybeFalseStart) { + ssl_GetSpecReadLock(ss); + maybeFalseStart = ss->ssl3.cwSpec->cipherDef->secret_key_size >= 10; + ssl_ReleaseSpecReadLock(ss); + } if (!maybeFalseStart) { SSL_TRC(3, ("%d: SSL[%d]: no false start due to weak cipher", @@ -7647,6 +7874,30 @@ ssl3_KEASupportsTickets(const ssl3KEADef *kea_def) return PR_TRUE; } +SECStatus +ssl3_NegotiateCipherSuiteInner(sslSocket *ss, const SECItem *suites, + PRUint16 version, PRUint16 *suitep) +{ + unsigned int j; + unsigned int i; + + for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) { + ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j]; + SSLVersionRange vrange = { version, version }; + if (!ssl3_config_match(suite, ss->ssl3.policy, &vrange, ss)) { + continue; + } + for (i = 0; i + 1 < suites->len; i += 2) { + PRUint16 suite_i = (suites->data[i] << 8) | suites->data[i + 1]; + if (suite_i == suite->cipher_suite) { + *suitep = suite_i; + return SECSuccess; + } + } + } + return SECFailure; +} + /* Select a cipher suite. ** ** NOTE: This suite selection algorithm should be the same as the one in @@ -7665,24 +7916,16 @@ SECStatus ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites, PRBool initHashes) { - unsigned int j; - unsigned int i; + PRUint16 selected; + SECStatus rv; - for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) { - ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j]; - SSLVersionRange vrange = { ss->version, ss->version }; - if (!config_match(suite, ss->ssl3.policy, &vrange, ss)) { - continue; - } - for (i = 0; i + 1 < suites->len; i += 2) { - PRUint16 suite_i = (suites->data[i] << 8) | suites->data[i + 1]; - if (suite_i == suite->cipher_suite) { - ss->ssl3.hs.cipher_suite = suite_i; - return ssl3_SetupCipherSuite(ss, initHashes); - } - } + rv = ssl3_NegotiateCipherSuiteInner(ss, suites, ss->version, &selected); + if (rv != SECSuccess) { + return SECFailure; } - return SECFailure; + + ss->ssl3.hs.cipher_suite = selected; + return ssl3_SetupCipherSuite(ss, initHashes); } /* @@ -7814,9 +8057,12 @@ ssl3_ServerCallSNICallback(sslSocket *ss) } /* Need to tell the client that application has picked * the name from the offered list and reconfigured the socket. + * Don't do this if we negotiated ESNI. */ - ssl3_RegisterExtensionSender(ss, &ss->xtnData, ssl_server_name_xtn, - ssl_SendEmptyExtension); + if (!ssl3_ExtensionNegotiated(ss, ssl_tls13_encrypted_sni_xtn)) { + ssl3_RegisterExtensionSender(ss, &ss->xtnData, ssl_server_name_xtn, + ssl_SendEmptyExtension); + } } else { /* Callback returned index outside of the boundary. */ PORT_Assert((unsigned int)ret < ss->xtnData.sniNameArrSize); @@ -7845,6 +8091,7 @@ ssl3_SelectServerCert(sslSocket *ss) { const ssl3KEADef *kea_def = ss->ssl3.hs.kea_def; PRCList *cursor; + SECStatus rv; /* If the client didn't include the supported groups extension, assume just * P-256 support and disable all the other ECDHE groups. This also affects @@ -7870,30 +8117,102 @@ ssl3_SelectServerCert(sslSocket *ss) cursor != &ss->serverCerts; cursor = PR_NEXT_LINK(cursor)) { sslServerCert *cert = (sslServerCert *)cursor; - if (!SSL_CERT_IS(cert, kea_def->authKeyType)) { - continue; - } - if (SSL_CERT_IS_EC(cert) && - !ssl_NamedGroupEnabled(ss, cert->namedCurve)) { - continue; + if (kea_def->authKeyType == ssl_auth_rsa_sign) { + /* We consider PSS certificates here as well for TLS 1.2. */ + if (!SSL_CERT_IS(cert, ssl_auth_rsa_sign) && + (!SSL_CERT_IS(cert, ssl_auth_rsa_pss) || + ss->version < SSL_LIBRARY_VERSION_TLS_1_2)) { + continue; + } + } else { + if (!SSL_CERT_IS(cert, kea_def->authKeyType)) { + continue; + } + if (SSL_CERT_IS_EC(cert) && + !ssl_NamedGroupEnabled(ss, cert->namedCurve)) { + continue; + } } /* Found one. */ ss->sec.serverCert = cert; - ss->sec.authType = kea_def->authKeyType; ss->sec.authKeyBits = cert->serverKeyBits; /* Don't pick a signature scheme if we aren't going to use it. */ if (kea_def->signKeyType == nullKey) { + ss->sec.authType = kea_def->authKeyType; return SECSuccess; } - return ssl3_PickServerSignatureScheme(ss); + + rv = ssl3_PickServerSignatureScheme(ss); + if (rv != SECSuccess) { + return SECFailure; + } + ss->sec.authType = + ssl_SignatureSchemeToAuthType(ss->ssl3.hs.signatureScheme); + return SECSuccess; } PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP); return SECFailure; } +static SECStatus +ssl_GenerateServerRandom(sslSocket *ss) +{ + SECStatus rv = ssl3_GetNewRandom(ss->ssl3.hs.server_random); + if (rv != SECSuccess) { + return SECFailure; + } + + if (ss->version == ss->vrange.max) { + return SECSuccess; + } +#ifdef DTLS_1_3_DRAFT_VERSION + if (IS_DTLS(ss)) { + return SECSuccess; + } +#endif + + /* + * [RFC 8446 Section 4.1.3]. + * + * TLS 1.3 servers which negotiate TLS 1.2 or below in response to a + * ClientHello MUST set the last 8 bytes of their Random value specially in + * their ServerHello. + * + * If negotiating TLS 1.2, TLS 1.3 servers MUST set the last 8 bytes of + * their Random value to the bytes: + * + * 44 4F 57 4E 47 52 44 01 + * + * If negotiating TLS 1.1 or below, TLS 1.3 servers MUST, and TLS 1.2 + * servers SHOULD, set the last 8 bytes of their ServerHello.Random value to + * the bytes: + * + * 44 4F 57 4E 47 52 44 00 + */ + PRUint8 *downgradeSentinel = + ss->ssl3.hs.server_random + + SSL3_RANDOM_LENGTH - sizeof(tls13_downgrade_random); + + switch (ss->vrange.max) { + case SSL_LIBRARY_VERSION_TLS_1_3: + PORT_Memcpy(downgradeSentinel, + tls13_downgrade_random, sizeof(tls13_downgrade_random)); + break; + case SSL_LIBRARY_VERSION_TLS_1_2: + PORT_Memcpy(downgradeSentinel, + tls12_downgrade_random, sizeof(tls12_downgrade_random)); + break; + default: + /* Do not change random. */ + break; + } + + return SECSuccess; +} + /* Called from ssl3_HandleHandshakeMessage() when it has deciphered a complete * ssl3 Client Hello message. * Caller must hold Handshake and RecvBuf locks. @@ -8088,56 +8407,6 @@ ssl3_HandleClientHello(sslSocket *ss, PRUint8 *b, PRUint32 length) } } - /* Generate the Server Random now so it is available - * when we process the ClientKeyShare in TLS 1.3 */ - rv = ssl3_GetNewRandom(ss->ssl3.hs.server_random); - if (rv != SECSuccess) { - errCode = SSL_ERROR_GENERATE_RANDOM_FAILURE; - goto loser; - } - -#ifndef TLS_1_3_DRAFT_VERSION - /* - * [draft-ietf-tls-tls13-11 Section 6.3.1.1]. - * TLS 1.3 server implementations which respond to a ClientHello with a - * client_version indicating TLS 1.2 or below MUST set the last eight - * bytes of their Random value to the bytes: - * - * 44 4F 57 4E 47 52 44 01 - * - * TLS 1.2 server implementations which respond to a ClientHello with a - * client_version indicating TLS 1.1 or below SHOULD set the last eight - * bytes of their Random value to the bytes: - * - * 44 4F 57 4E 47 52 44 00 - * - * TODO(ekr@rtfm.com): Note this change was not added in the SSLv2 - * compat processing code since that will most likely be removed before - * we ship the final version of TLS 1.3. Bug 1306672. - */ - if (ss->vrange.max > ss->version) { - PRUint8 *downgrade_sentinel = - ss->ssl3.hs.server_random + - SSL3_RANDOM_LENGTH - sizeof(tls13_downgrade_random); - - switch (ss->vrange.max) { - case SSL_LIBRARY_VERSION_TLS_1_3: - PORT_Memcpy(downgrade_sentinel, - tls13_downgrade_random, - sizeof(tls13_downgrade_random)); - break; - case SSL_LIBRARY_VERSION_TLS_1_2: - PORT_Memcpy(downgrade_sentinel, - tls12_downgrade_random, - sizeof(tls12_downgrade_random)); - break; - default: - /* Do not change random. */ - break; - } - } -#endif - /* If there is a cookie, then this is a second ClientHello (TLS 1.3). */ if (ssl3_FindExtension(ss, ssl_tls13_cookie_xtn)) { ss->ssl3.hs.helloRetry = PR_TRUE; @@ -8397,7 +8666,7 @@ ssl3_HandleClientHelloPart2(sslSocket *ss, * The product policy won't change during the process lifetime. * Implemented ("isPresent") shouldn't change for servers. */ - if (!config_match(suite, ss->ssl3.policy, &vrange, ss)) + if (!ssl3_config_match(suite, ss->ssl3.policy, &vrange, ss)) break; #else if (!suite->enabled) @@ -8779,7 +9048,7 @@ ssl3_HandleV2ClientHello(sslSocket *ss, unsigned char *buffer, unsigned int leng for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) { ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j]; SSLVersionRange vrange = { ss->version, ss->version }; - if (!config_match(suite, ss->ssl3.policy, &vrange, ss)) { + if (!ssl3_config_match(suite, ss->ssl3.policy, &vrange, ss)) { continue; } for (i = 0; i + 2 < suite_length; i += 3) { @@ -8884,6 +9153,7 @@ ssl_ConstructServerHello(sslSocket *ss, PRBool helloRetry, SECStatus rv; SSL3ProtocolVersion version; sslSessionID *sid = ss->sec.ci.sid; + const PRUint8 *random; version = PR_MIN(ss->version, SSL_LIBRARY_VERSION_TLS_1_2); if (IS_DTLS(ss)) { @@ -8893,9 +9163,17 @@ ssl_ConstructServerHello(sslSocket *ss, PRBool helloRetry, if (rv != SECSuccess) { return SECFailure; } - /* Random already generated in ssl3_HandleClientHello */ - rv = sslBuffer_Append(messageBuf, helloRetry ? ssl_hello_retry_random : ss->ssl3.hs.server_random, - SSL3_RANDOM_LENGTH); + + if (helloRetry) { + random = ssl_hello_retry_random; + } else { + rv = ssl_GenerateServerRandom(ss); + if (rv != SECSuccess) { + return SECFailure; + } + random = ss->ssl3.hs.server_random; + } + rv = sslBuffer_Append(messageBuf, random, SSL3_RANDOM_LENGTH); if (rv != SECSuccess) { return SECFailure; } @@ -9368,7 +9646,7 @@ ssl3_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length) ss->sec.peerCert); if (rv != SECSuccess) { errCode = PORT_GetError(); - desc = decrypt_error; + desc = illegal_parameter; goto alert_loser; } @@ -9501,6 +9779,23 @@ ssl3_GenerateRSAPMS(sslSocket *ss, ssl3CipherSpec *spec, return pms; } +static void +ssl3_CSwapPK11SymKey(PK11SymKey **x, PK11SymKey **y, PRBool c) +{ + uintptr_t mask = (uintptr_t)c; + unsigned int i; + for (i = 1; i < sizeof(uintptr_t) * 8; i <<= 1) { + mask |= mask << i; + } + uintptr_t x_ptr = (uintptr_t)*x; + uintptr_t y_ptr = (uintptr_t)*y; + uintptr_t tmp = (x_ptr ^ y_ptr) & mask; + x_ptr = x_ptr ^ tmp; + y_ptr = y_ptr ^ tmp; + *x = (PK11SymKey *)x_ptr; + *y = (PK11SymKey *)y_ptr; +} + /* Note: The Bleichenbacher attack on PKCS#1 necessitates that we NEVER * return any indication of failure of the Client Key Exchange message, * where that failure is caused by the content of the client's message. @@ -9521,9 +9816,9 @@ ssl3_HandleRSAClientKeyExchange(sslSocket *ss, { SECStatus rv; SECItem enc_pms; - PK11SymKey *tmpPms[2] = { NULL, NULL }; - PK11SlotInfo *slot; - int useFauxPms = 0; + PK11SymKey *pms = NULL; + PK11SymKey *fauxPms = NULL; + PK11SlotInfo *slot = NULL; PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); @@ -9544,11 +9839,6 @@ ssl3_HandleRSAClientKeyExchange(sslSocket *ss, } } -#define currentPms tmpPms[!useFauxPms] -#define unusedPms tmpPms[useFauxPms] -#define realPms tmpPms[1] -#define fauxPms tmpPms[0] - /* * Get as close to algorithm 2 from RFC 5246; Section 7.4.7.1 * as we can within the constraints of the PKCS#11 interface. @@ -9603,40 +9893,33 @@ ssl3_HandleRSAClientKeyExchange(sslSocket *ss, * the unwrap. Rather, it is the mechanism with which the * unwrapped pms will be used. */ - realPms = PK11_PubUnwrapSymKey(serverKeyPair->privKey, &enc_pms, - CKM_SSL3_MASTER_KEY_DERIVE, CKA_DERIVE, 0); + pms = PK11_PubUnwrapSymKey(serverKeyPair->privKey, &enc_pms, + CKM_SSL3_MASTER_KEY_DERIVE, CKA_DERIVE, 0); /* Temporarily use the PMS if unwrapping the real PMS fails. */ - useFauxPms |= (realPms == NULL); + ssl3_CSwapPK11SymKey(&pms, &fauxPms, pms == NULL); /* Attempt to derive the MS from the PMS. This is the only way to * check the version field in the RSA PMS. If this fails, we * then use the faux PMS in place of the PMS. Note that this * operation should never fail if we are using the faux PMS * since it is correctly formatted. */ - rv = ssl3_ComputeMasterSecret(ss, currentPms, NULL); - - /* If we succeeded, then select the true PMS and discard the - * FPMS. Else, select the FPMS and select the true PMS */ - useFauxPms |= (rv != SECSuccess); + rv = ssl3_ComputeMasterSecret(ss, pms, NULL); - if (unusedPms) { - PK11_FreeSymKey(unusedPms); - } + /* If we succeeded, then select the true PMS, else select the FPMS. */ + ssl3_CSwapPK11SymKey(&pms, &fauxPms, (rv != SECSuccess) & (fauxPms != NULL)); /* This step will derive the MS from the PMS, among other things. */ - rv = ssl3_InitPendingCipherSpecs(ss, currentPms, PR_TRUE); - PK11_FreeSymKey(currentPms); + rv = ssl3_InitPendingCipherSpecs(ss, pms, PR_TRUE); + + /* Clear both PMS. */ + PK11_FreeSymKey(pms); + PK11_FreeSymKey(fauxPms); if (rv != SECSuccess) { (void)SSL3_SendAlert(ss, alert_fatal, handshake_failure); return SECFailure; /* error code set by ssl3_InitPendingCipherSpec */ } -#undef currentPms -#undef unusedPms -#undef realPms -#undef fauxPms - return SECSuccess; } @@ -10429,6 +10712,9 @@ ssl3_AuthCertificate(sslSocket *ss) PR_TRUE, isServer); if (rv != SECSuccess) { errCode = PORT_GetError(); + if (errCode == 0) { + errCode = SSL_ERROR_BAD_CERTIFICATE; + } if (rv != SECWouldBlock) { if (ss->handleBadCert) { rv = (*ss->handleBadCert)(ss->badCertArg, ss->fd); @@ -11252,7 +11538,7 @@ ssl3_FinishHandshake(sslSocket *ss) } SECStatus -ssl_HashHandshakeMessageInt(sslSocket *ss, SSLHandshakeType type, +ssl_HashHandshakeMessageInt(sslSocket *ss, SSLHandshakeType ct, PRUint32 dtlsSeq, const PRUint8 *b, PRUint32 length) { @@ -11262,7 +11548,7 @@ ssl_HashHandshakeMessageInt(sslSocket *ss, SSLHandshakeType type, PRINT_BUF(50, (ss, "Hash handshake message:", b, length)); - hdr[0] = (PRUint8)type; + hdr[0] = (PRUint8)ct; hdr[1] = (PRUint8)(length >> 16); hdr[2] = (PRUint8)(length >> 8); hdr[3] = (PRUint8)(length); @@ -11302,10 +11588,10 @@ ssl_HashHandshakeMessageInt(sslSocket *ss, SSLHandshakeType type, } SECStatus -ssl_HashHandshakeMessage(sslSocket *ss, SSLHandshakeType type, +ssl_HashHandshakeMessage(sslSocket *ss, SSLHandshakeType ct, const PRUint8 *b, PRUint32 length) { - return ssl_HashHandshakeMessageInt(ss, type, ss->ssl3.hs.recvMessageSeq, + return ssl_HashHandshakeMessageInt(ss, ct, ss->ssl3.hs.recvMessageSeq, b, length); } @@ -11885,7 +12171,7 @@ ssl3_UnprotectRecord(sslSocket *ss, PRBool isTLS; unsigned int good; unsigned int ivLen = 0; - SSL3ContentType rType; + SSLContentType rType; SSL3ProtocolVersion rVersion; unsigned int minLength; unsigned int originalLen = 0; @@ -11959,7 +12245,7 @@ ssl3_UnprotectRecord(sslSocket *ss, return SECFailure; } - rType = (SSL3ContentType)cText->hdr[0]; + rType = (SSLContentType)cText->hdr[0]; rVersion = ((SSL3ProtocolVersion)cText->hdr[1] << 8) | (SSL3ProtocolVersion)cText->hdr[2]; if (cipher_def->type == type_aead) { @@ -12071,7 +12357,7 @@ ssl3_UnprotectRecord(sslSocket *ss, } SECStatus -ssl3_HandleNonApplicationData(sslSocket *ss, SSL3ContentType rType, +ssl3_HandleNonApplicationData(sslSocket *ss, SSLContentType rType, DTLSEpoch epoch, sslSequenceNumber seqNum, sslBuffer *databuf) { @@ -12089,20 +12375,20 @@ ssl3_HandleNonApplicationData(sslSocket *ss, SSL3ContentType rType, ** they return SECFailure or SECWouldBlock. */ switch (rType) { - case content_change_cipher_spec: + case ssl_ct_change_cipher_spec: rv = ssl3_HandleChangeCipherSpecs(ss, databuf); break; - case content_alert: + case ssl_ct_alert: rv = ssl3_HandleAlert(ss, databuf); break; - case content_handshake: + case ssl_ct_handshake: if (!IS_DTLS(ss)) { rv = ssl3_HandleHandshake(ss, databuf); } else { rv = dtls_HandleHandshake(ss, epoch, seqNum, databuf); } break; - case content_ack: + case ssl_ct_ack: if (IS_DTLS(ss) && tls13_MaybeTls13(ss)) { rv = dtls13_HandleAck(ss, databuf); break; @@ -12190,7 +12476,7 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText) ssl3CipherSpec *spec = NULL; PRUint16 recordSizeLimit; PRBool outOfOrderSpec = PR_FALSE; - SSL3ContentType rType; + SSLContentType rType; sslBuffer *plaintext = &ss->gs.buf; SSL3AlertDescription alert = internal_error; PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); @@ -12208,7 +12494,7 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText) /* We're waiting for another ClientHello, which will appear unencrypted. * Use the content type to tell whether this should be discarded. */ if (ss->ssl3.hs.zeroRttIgnore == ssl_0rtt_ignore_hrr && - cText->hdr[0] == content_application_data) { + cText->hdr[0] == ssl_ct_application_data) { PORT_Assert(ss->ssl3.hs.ws == wait_client_hello); return SECSuccess; } @@ -12269,7 +12555,7 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText) /* Encrypted application data records could arrive before the handshake * completes in DTLS 1.3. These can look like valid TLS 1.2 application_data * records in epoch 0, which is never valid. Pretend they didn't decrypt. */ - if (spec->epoch == 0 && rType == content_application_data) { + if (spec->epoch == 0 && rType == ssl_ct_application_data) { PORT_SetError(SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA); alert = unexpected_message; rv = SECFailure; @@ -12304,7 +12590,7 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText) * 0-RTT session that is resumed from a session that did negotiate it. * We don't care about that corner case right now. */ if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 && - cText->hdr[0] == content_change_cipher_spec && + cText->hdr[0] == ssl_ct_change_cipher_spec && ss->ssl3.hs.ws != idle_handshake && cText->buf->len == 1 && cText->buf->buf[0] == change_cipher_spec_choice) { @@ -12364,7 +12650,7 @@ ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cText) /* Application data records are processed by the caller of this ** function, not by this function. */ - if (rType == content_application_data) { + if (rType == ssl_ct_application_data) { if (ss->firstHsDone) return SECSuccess; if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 && diff --git a/security/nss/lib/ssl/ssl3ecc.c b/security/nss/lib/ssl/ssl3ecc.c index f8b9a9400..52d5bb515 100644 --- a/security/nss/lib/ssl/ssl3ecc.c +++ b/security/nss/lib/ssl/ssl3ecc.c @@ -327,16 +327,13 @@ ssl3_HandleECDHClientKeyExchange(sslSocket *ss, PRUint8 *b, ** Take an encoded key share and make a public key out of it. */ SECStatus -ssl_ImportECDHKeyShare(sslSocket *ss, SECKEYPublicKey *peerKey, +ssl_ImportECDHKeyShare(SECKEYPublicKey *peerKey, PRUint8 *b, PRUint32 length, const sslNamedGroupDef *ecGroup) { SECStatus rv; SECItem ecPoint = { siBuffer, NULL, 0 }; - PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); - PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); - if (!length) { PORT_SetError(SSL_ERROR_RX_MALFORMED_ECDHE_KEY_SHARE); return SECFailure; @@ -616,7 +613,7 @@ ssl3_HandleECDHServerKeyExchange(sslSocket *ss, PRUint8 *b, PRUint32 length) peerKey->arena = arena; /* create public key from point data */ - rv = ssl_ImportECDHKeyShare(ss, peerKey, ec_point.data, ec_point.len, + rv = ssl_ImportECDHKeyShare(peerKey, ec_point.data, ec_point.len, ecGroup); if (rv != SECSuccess) { /* error code is set */ diff --git a/security/nss/lib/ssl/ssl3ext.c b/security/nss/lib/ssl/ssl3ext.c index 9b6c719f8..60b5889e7 100644 --- a/security/nss/lib/ssl/ssl3ext.c +++ b/security/nss/lib/ssl/ssl3ext.c @@ -50,6 +50,7 @@ static const ssl3ExtensionHandler clientHelloHandlers[] = { { ssl_tls13_early_data_xtn, &tls13_ServerHandleEarlyDataXtn }, { ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ServerHandlePskModesXtn }, { ssl_tls13_cookie_xtn, &tls13_ServerHandleCookieXtn }, + { ssl_tls13_encrypted_sni_xtn, &tls13_ServerHandleEsniXtn }, { ssl_record_size_limit_xtn, &ssl_HandleRecordSizeLimitXtn }, { 0, NULL } }; @@ -136,6 +137,7 @@ static const sslExtensionBuilder clientHelloSendersTLS[] = { ssl_signature_algorithms_xtn, &ssl3_SendSigAlgsXtn }, { ssl_tls13_cookie_xtn, &tls13_ClientSendHrrCookieXtn }, { ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ClientSendPskModesXtn }, + { ssl_tls13_encrypted_sni_xtn, &tls13_ClientSendEsniXtn }, { ssl_record_size_limit_xtn, &ssl_SendRecordSizeLimitXtn }, /* The pre_shared_key extension MUST be last. */ { ssl_tls13_pre_shared_key_xtn, &tls13_ClientSendPreSharedKeyXtn }, @@ -338,8 +340,6 @@ ssl3_ParseExtensions(sslSocket *ss, PRUint8 **b, PRUint32 *length) return SECFailure; /* alert already sent */ } - SSL_TRC(10, ("%d: SSL3[%d]: parsing extension %d", - SSL_GETPID(), ss->fd, extension_type)); /* Check whether an extension has been sent multiple times. */ for (cursor = PR_NEXT_LINK(&ss->ssl3.hs.remoteExtensions); cursor != &ss->ssl3.hs.remoteExtensions; @@ -357,6 +357,9 @@ ssl3_ParseExtensions(sslSocket *ss, PRUint8 **b, PRUint32 *length) return rv; /* alert already sent */ } + SSL_TRC(10, ("%d: SSL3[%d]: parsed extension %d len=%u", + SSL_GETPID(), ss->fd, extension_type, extension_data.len)); + extension = PORT_ZNew(TLSExtension); if (!extension) { return SECFailure; @@ -409,7 +412,9 @@ ssl_CallExtensionHandler(sslSocket *ss, SSLHandshakeType handshakeMessage, /* Find extension_type in table of Hello Extension Handlers. */ for (; handler->ex_handler != NULL; ++handler) { if (handler->ex_type == extension->type) { - rv = (*handler->ex_handler)(ss, &ss->xtnData, &extension->data); + SECItem tmp = extension->data; + + rv = (*handler->ex_handler)(ss, &ss->xtnData, &tmp); break; } } @@ -960,6 +965,8 @@ ssl3_DestroyExtensionData(TLSExtensionData *xtnData) xtnData->certReqAuthorities.arena = NULL; } PORT_Free(xtnData->advertised); + ssl_FreeEphemeralKeyPair(xtnData->esniPrivateKey); + SECITEM_FreeItem(&xtnData->keyShareExtension, PR_FALSE); } /* Free everything that has been allocated and then reset back to diff --git a/security/nss/lib/ssl/ssl3ext.h b/security/nss/lib/ssl/ssl3ext.h index 6d77c7459..d96b4cffe 100644 --- a/security/nss/lib/ssl/ssl3ext.h +++ b/security/nss/lib/ssl/ssl3ext.h @@ -11,6 +11,8 @@ #include "sslencode.h" +#define TLS13_ESNI_NONCE_SIZE 16 + typedef enum { sni_nametype_hostname } SNINameType; @@ -101,6 +103,14 @@ struct TLSExtensionDataStr { /* The record size limit set by the peer. Our value is kept in ss->opt. */ PRUint16 recordSizeLimit; + + /* ESNI working state */ + SECItem keyShareExtension; + ssl3CipherSuite esniSuite; + sslEphemeralKeyPair *esniPrivateKey; + /* Pointer into |ss->esniKeys->keyShares| */ + TLS13KeyShareEntry *peerEsniShare; + PRUint8 esniNonce[TLS13_ESNI_NONCE_SIZE]; }; typedef struct TLSExtensionStr { diff --git a/security/nss/lib/ssl/ssl3exthandle.c b/security/nss/lib/ssl/ssl3exthandle.c index d1f286dc3..a2d83fa97 100644 --- a/security/nss/lib/ssl/ssl3exthandle.c +++ b/security/nss/lib/ssl/ssl3exthandle.c @@ -15,30 +15,40 @@ #include "selfencrypt.h" #include "ssl3ext.h" #include "ssl3exthandle.h" +#include "tls13esni.h" #include "tls13exthandle.h" /* For tls13_ServerSendStatusRequestXtn. */ +PRBool +ssl_ShouldSendSNIExtension(const sslSocket *ss, const char *url) +{ + PRNetAddr netAddr; + + /* must have a hostname */ + if (!url || !url[0]) { + return PR_FALSE; + } + /* must not be an IPv4 or IPv6 address */ + if (PR_SUCCESS == PR_StringToNetAddr(url, &netAddr)) { + /* is an IP address (v4 or v6) */ + return PR_FALSE; + } + + return PR_TRUE; +} + /* Format an SNI extension, using the name from the socket's URL, * unless that name is a dotted decimal string. * Used by client and server. */ SECStatus -ssl3_ClientSendServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData, - sslBuffer *buf, PRBool *added) +ssl3_ClientFormatServerNameXtn(const sslSocket *ss, const char *url, + TLSExtensionData *xtnData, + sslBuffer *buf) { unsigned int len; - PRNetAddr netAddr; SECStatus rv; - /* must have a hostname */ - if (!ss->url || !ss->url[0]) { - return SECSuccess; - } - /* must not be an IPv4 or IPv6 address */ - if (PR_SUCCESS == PR_StringToNetAddr(ss->url, &netAddr)) { - /* is an IP address (v4 or v6) */ - return SECSuccess; - } - len = PORT_Strlen(ss->url); + len = PORT_Strlen(url); /* length of server_name_list */ rv = sslBuffer_AppendNumber(buf, len + 3, 2); if (rv != SECSuccess) { @@ -50,7 +60,33 @@ ssl3_ClientSendServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData, return SECFailure; } /* HostName (length and value) */ - rv = sslBuffer_AppendVariable(buf, (const PRUint8 *)ss->url, len, 2); + rv = sslBuffer_AppendVariable(buf, (const PRUint8 *)url, len, 2); + if (rv != SECSuccess) { + return SECFailure; + } + + return SECSuccess; +} + +SECStatus +ssl3_ClientSendServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData, + sslBuffer *buf, PRBool *added) +{ + SECStatus rv; + + const char *url = ss->url; + + /* We only make an ESNI private key if we are going to + * send ESNI. */ + if (ss->xtnData.esniPrivateKey != NULL) { + url = ss->esniKeys->dummySni; + } + + if (!ssl_ShouldSendSNIExtension(ss, url)) { + return SECSuccess; + } + + rv = ssl3_ClientFormatServerNameXtn(ss, url, xtnData, buf); if (rv != SECSuccess) { return SECFailure; } @@ -59,7 +95,6 @@ ssl3_ClientSendServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData, return SECSuccess; } -/* Handle an incoming SNI extension. */ SECStatus ssl3_HandleServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData, SECItem *data) @@ -72,6 +107,13 @@ ssl3_HandleServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData, return SECSuccess; /* ignore extension */ } + if (ssl3_ExtensionNegotiated(ss, ssl_tls13_encrypted_sni_xtn)) { + /* If we already have ESNI, make sure we don't overwrite + * the value. */ + PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); + return SECSuccess; + } + /* Server side - consume client data and register server sender. */ /* do not parse the data if don't have user extension handling function. */ if (!ss->sniSocketConfig) { @@ -1174,17 +1216,18 @@ ssl3_ProcessSessionTicketCommon(sslSocket *ss, const SECItem *ticket, &decryptedTicket.len, decryptedTicket.len); if (rv != SECSuccess) { - SECITEM_ZfreeItem(&decryptedTicket, PR_FALSE); - - /* Fail with no ticket if we're not a recipient. Otherwise - * it's a hard failure. */ - if (PORT_GetError() != SEC_ERROR_NOT_A_RECIPIENT) { - SSL3_SendAlert(ss, alert_fatal, illegal_parameter); - return SECFailure; + /* Ignore decryption failure if we are doing TLS 1.3; that + * means the server rejects the client's resumption + * attempt. In TLS 1.2, however, it's a hard failure, unless + * it's just because we're not the recipient of the ticket. */ + if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 || + PORT_GetError() == SEC_ERROR_NOT_A_RECIPIENT) { + SECITEM_ZfreeItem(&decryptedTicket, PR_FALSE); + return SECSuccess; } - /* We didn't have the right key, so pretend we don't have a - * ticket. */ + SSL3_SendAlert(ss, alert_fatal, illegal_parameter); + goto loser; } rv = ssl_ParseSessionTicket(ss, &decryptedTicket, &parsedTicket); diff --git a/security/nss/lib/ssl/ssl3exthandle.h b/security/nss/lib/ssl/ssl3exthandle.h index eaf7f0081..3e9b418cf 100644 --- a/security/nss/lib/ssl/ssl3exthandle.h +++ b/security/nss/lib/ssl/ssl3exthandle.h @@ -91,6 +91,10 @@ SECStatus ssl3_HandleExtendedMasterSecretXtn(const sslSocket *ss, SECItem *data); SECStatus ssl3_ProcessSessionTicketCommon(sslSocket *ss, const SECItem *ticket, /* out */ SECItem *appToken); +PRBool ssl_ShouldSendSNIExtension(const sslSocket *ss, const char *url); +SECStatus ssl3_ClientFormatServerNameXtn(const sslSocket *ss, const char *url, + TLSExtensionData *xtnData, + sslBuffer *buf); SECStatus ssl3_ClientSendServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData, sslBuffer *buf, PRBool *added); diff --git a/security/nss/lib/ssl/ssl3gthr.c b/security/nss/lib/ssl/ssl3gthr.c index 5ea7cc249..64a1878f7 100644 --- a/security/nss/lib/ssl/ssl3gthr.c +++ b/security/nss/lib/ssl/ssl3gthr.c @@ -60,8 +60,8 @@ ssl3_isLikelyV3Hello(const unsigned char *buf) } /* Check for a typical V3 record header. */ - return (PRBool)(buf[0] >= content_change_cipher_spec && - buf[0] <= content_application_data && + return (PRBool)(buf[0] >= ssl_ct_change_cipher_spec && + buf[0] <= ssl_ct_application_data && buf[1] == MSB(SSL_LIBRARY_VERSION_3_0)); } @@ -314,7 +314,7 @@ dtls_GatherData(sslSocket *ss, sslGather *gs, int flags) contentType = gs->dtlsPacket.buf[gs->dtlsPacketOffset]; if (dtls_IsLongHeader(ss->version, contentType)) { headerLen = 13; - } else if (contentType == content_application_data) { + } else if (contentType == ssl_ct_application_data) { headerLen = 7; } else if ((contentType & 0xe0) == 0x20) { headerLen = 2; @@ -463,15 +463,15 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags) SSL_DBG(("%d: SSL3[%d]: resuming handshake", SSL_GETPID(), ss->fd)); PORT_Assert(!IS_DTLS(ss)); - rv = ssl3_HandleNonApplicationData(ss, content_handshake, + rv = ssl3_HandleNonApplicationData(ss, ssl_ct_handshake, 0, 0, &ss->gs.buf); } else { /* State for SSLv2 client hello support. */ ssl2Gather ssl2gs = { PR_FALSE, 0 }; ssl2Gather *ssl2gs_ptr = NULL; - /* If we're a server and waiting for a client hello, accept v2. */ - if (ss->sec.isServer && ss->ssl3.hs.ws == wait_client_hello) { + if (ss->sec.isServer && ss->opt.enableV2CompatibleHello && + ss->ssl3.hs.ws == wait_client_hello) { ssl2gs_ptr = &ssl2gs; } @@ -484,8 +484,8 @@ ssl3_GatherCompleteHandshake(sslSocket *ss, int flags) } if (!IS_DTLS(ss)) { - /* If we're a server waiting for a ClientHello then pass - * ssl2gs to support SSLv2 ClientHello messages. */ + /* Passing a non-NULL ssl2gs here enables detection of + * SSLv2-compatible ClientHello messages. */ rv = ssl3_GatherData(ss, &ss->gs, flags, ssl2gs_ptr); } else { rv = dtls_GatherData(ss, &ss->gs, flags); diff --git a/security/nss/lib/ssl/ssl3prot.h b/security/nss/lib/ssl/ssl3prot.h index 8e6cf2745..bfaa10d3f 100644 --- a/security/nss/lib/ssl/ssl3prot.h +++ b/security/nss/lib/ssl/ssl3prot.h @@ -13,10 +13,8 @@ typedef PRUint16 SSL3ProtocolVersion; /* version numbers are defined in sslproto.h */ -/* The TLS 1.3 draft version. Used to avoid negotiating - * between incompatible pre-standard TLS 1.3 drafts. - * TODO(ekr@rtfm.com): Remove when TLS 1.3 is published. */ -#define TLS_1_3_DRAFT_VERSION 28 +/* DTLS 1.3 is still a draft. */ +#define DTLS_1_3_DRAFT_VERSION 28 typedef PRUint16 ssl3CipherSuite; /* The cipher suites are defined in sslproto.h */ @@ -35,15 +33,6 @@ typedef PRUint16 ssl3CipherSuite; #define MAX_FRAGMENT_LENGTH 16384 -typedef enum { - content_change_cipher_spec = 20, - content_alert = 21, - content_handshake = 22, - content_application_data = 23, - content_alt_handshake = 24, - content_ack = 25 -} SSL3ContentType; - typedef enum { change_cipher_spec_choice = 1 } SSL3ChangeCipherSpecChoice; typedef enum { alert_warning = 1, diff --git a/security/nss/lib/ssl/sslcert.c b/security/nss/lib/ssl/sslcert.c index 1c3ddb0e7..878df761e 100644 --- a/security/nss/lib/ssl/sslcert.c +++ b/security/nss/lib/ssl/sslcert.c @@ -436,8 +436,6 @@ ssl_GetCertificateAuthTypes(CERTCertificate *cert, SSLAuthType targetAuthType) case SEC_OID_PKCS1_RSA_ENCRYPTION: if (cert->keyUsage & KU_DIGITAL_SIGNATURE) { authTypes |= 1 << ssl_auth_rsa_sign; - /* This certificate is RSA, assume that it's also PSS. */ - authTypes |= 1 << ssl_auth_rsa_pss; } if (cert->keyUsage & KU_KEY_ENCIPHERMENT) { diff --git a/security/nss/lib/ssl/sslerr.h b/security/nss/lib/ssl/sslerr.h index 518a2b887..a4aa27657 100644 --- a/security/nss/lib/ssl/sslerr.h +++ b/security/nss/lib/ssl/sslerr.h @@ -264,6 +264,10 @@ typedef enum { SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR = (SSL_ERROR_BASE + 173), SSL_ERROR_RX_MALFORMED_DTLS_ACK = (SSL_ERROR_BASE + 174), SSL_ERROR_DH_KEY_TOO_LONG = (SSL_ERROR_BASE + 175), + SSL_ERROR_RX_MALFORMED_ESNI_KEYS = (SSL_ERROR_BASE + 176), + SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION = (SSL_ERROR_BASE + 177), + SSL_ERROR_MISSING_ESNI_EXTENSION = (SSL_ERROR_BASE + 178), + SSL_ERROR_RX_UNEXPECTED_RECORD_TYPE = (SSL_ERROR_BASE + 179), SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */ } SSLErrorCodes; #endif /* NO_SECURITY_ERROR_ENUM */ diff --git a/security/nss/lib/ssl/sslexp.h b/security/nss/lib/ssl/sslexp.h index 08654f885..f450e528d 100644 --- a/security/nss/lib/ssl/sslexp.h +++ b/security/nss/lib/ssl/sslexp.h @@ -367,6 +367,7 @@ typedef struct SSLResumptionTokenInfoStr { PRUint8 *alpnSelection; PRUint32 alpnSelectionLen; PRUint32 maxEarlyDataSize; + PRTime expirationTime; /* added in NSS 3.41 */ } SSLResumptionTokenInfo; /* @@ -452,8 +453,65 @@ typedef SECStatus(PR_CALLBACK *SSLResumptionTokenCallback)( (PRFileDesc * _fd, PRUint32 _size), \ (fd, size)) -/* Deprecated experimental APIs */ +/* Set the ESNI key pair on a socket (server side) + * + * fd -- the socket + * record/recordLen -- the encoded DNS record (not base64) + * + * Important: the suites that are advertised in the record must + * be configured on, or this call will fail. + */ +#define SSL_SetESNIKeyPair(fd, \ + privKey, record, recordLen) \ + SSL_EXPERIMENTAL_API("SSL_SetESNIKeyPair", \ + (PRFileDesc * _fd, \ + SECKEYPrivateKey * _privKey, \ + const PRUint8 *_record, unsigned int _recordLen), \ + (fd, privKey, \ + record, recordLen)) +/* Set the ESNI keys on a client + * + * fd -- the socket + * ensikeys/esniKeysLen -- the ESNI key structure (not base64) + * dummyESNI -- the dummy ESNI to use (if any) + */ +#define SSL_EnableESNI(fd, esniKeys, esniKeysLen, dummySNI) \ + SSL_EXPERIMENTAL_API("SSL_EnableESNI", \ + (PRFileDesc * _fd, \ + const PRUint8 *_esniKeys, \ + unsigned int _esniKeysLen, \ + const char *_dummySNI), \ + (fd, esniKeys, esniKeysLen, dummySNI)) + +/* + * Generate an encoded ESNIKeys structure (presumably server side). + * + * cipherSuites -- the cipher suites that can be used + * cipherSuitesCount -- the number of suites in cipherSuites + * group -- the named group this key corresponds to + * pubKey -- the public key for the key pair + * pad -- the length to pad to + * notBefore/notAfter -- validity range + * out/outlen/maxlen -- where to output the data + */ +#define SSL_EncodeESNIKeys(cipherSuites, cipherSuiteCount, \ + group, pubKey, pad, notBefore, notAfter, \ + out, outlen, maxlen) \ + SSL_EXPERIMENTAL_API("SSL_EncodeESNIKeys", \ + (PRUint16 * _cipherSuites, \ + unsigned int _cipherSuiteCount, \ + SSLNamedGroup _group, \ + SECKEYPublicKey *_pubKey, \ + PRUint16 _pad, \ + PRUint64 _notBefore, PRUint64 _notAfter, \ + PRUint8 *_out, unsigned int *_outlen, \ + unsigned int _maxlen), \ + (cipherSuites, cipherSuiteCount, \ + group, pubKey, pad, notBefore, notAfter, \ + out, outlen, maxlen)) + +/* Deprecated experimental APIs */ #define SSL_UseAltServerHelloType(fd, enable) SSL_DEPRECATED_EXPERIMENTAL_API SEC_END_PROTOS diff --git a/security/nss/lib/ssl/sslimpl.h b/security/nss/lib/ssl/sslimpl.h index a2209e90a..35240d2fb 100644 --- a/security/nss/lib/ssl/sslimpl.h +++ b/security/nss/lib/ssl/sslimpl.h @@ -36,6 +36,10 @@ typedef struct sslSocketStr sslSocket; typedef struct sslNamedGroupDefStr sslNamedGroupDef; +typedef struct sslEsniKeysStr sslEsniKeys; +typedef struct sslEphemeralKeyPairStr sslEphemeralKeyPair; +typedef struct TLS13KeyShareEntryStr TLS13KeyShareEntry; + #include "sslencode.h" #include "sslexp.h" #include "ssl3ext.h" @@ -230,7 +234,7 @@ typedef struct { #define MAX_DTLS_SRTP_CIPHER_SUITES 4 /* MAX_SIGNATURE_SCHEMES allows for all the values we support. */ -#define MAX_SIGNATURE_SCHEMES 15 +#define MAX_SIGNATURE_SCHEMES 18 typedef struct sslOptionsStr { /* If SSL_SetNextProtoNego has been called, then this contains the @@ -266,6 +270,8 @@ typedef struct sslOptionsStr { unsigned int enable0RttData : 1; unsigned int enableTls13CompatMode : 1; unsigned int enableDtlsShortHeader : 1; + unsigned int enableHelloDowngradeCheck : 1; + unsigned int enableV2CompatibleHello : 1; } sslOptions; typedef enum { sslHandshakingUndetermined = 0, @@ -552,16 +558,16 @@ typedef SECStatus (*sslRestartTarget)(sslSocket *); typedef struct DTLSQueuedMessageStr { PRCList link; /* The linked list link */ ssl3CipherSpec *cwSpec; /* The cipher spec to use, null for none */ - SSL3ContentType type; /* The message type */ + SSLContentType type; /* The message type */ unsigned char *data; /* The data */ PRUint16 len; /* The data length */ } DTLSQueuedMessage; -typedef struct TLS13KeyShareEntryStr { +struct TLS13KeyShareEntryStr { PRCList link; /* The linked list link */ const sslNamedGroupDef *group; /* The group for the entry */ SECItem key_exchange; /* The share itself */ -} TLS13KeyShareEntry; +}; typedef struct TLS13EarlyDataStr { PRCList link; /* The linked list link */ @@ -803,11 +809,11 @@ struct sslKeyPairStr { PRInt32 refCount; /* use PR_Atomic calls for this. */ }; -typedef struct { +struct sslEphemeralKeyPairStr { PRCList link; const sslNamedGroupDef *group; sslKeyPair *keys; -} sslEphemeralKeyPair; +}; struct ssl3DHParamsStr { SSLNamedGroup name; @@ -1064,6 +1070,10 @@ struct sslSocketStr { /* Whether we are doing stream or datagram mode */ SSLProtocolVariant protocolVariant; + + /* The information from the ESNI keys record + * (also the private key for the server). */ + sslEsniKeys *esniKeys; }; struct sslSelfEncryptKeysStr { @@ -1168,11 +1178,13 @@ extern int ssl_Do1stHandshake(sslSocket *ss); extern SECStatus ssl3_InitPendingCipherSpecs(sslSocket *ss, PK11SymKey *secret, PRBool derive); +extern void ssl_DestroyKeyMaterial(ssl3KeyMaterial *keyMaterial); extern sslSessionID *ssl3_NewSessionID(sslSocket *ss, PRBool is_server); extern sslSessionID *ssl_LookupSID(const PRIPv6Addr *addr, PRUint16 port, const char *peerID, const char *urlSvrName); extern void ssl_FreeSID(sslSessionID *sid); extern void ssl_DestroySID(sslSessionID *sid, PRBool freeIt); +extern sslSessionID *ssl_ReferenceSID(sslSessionID *sid); extern int ssl3_SendApplicationData(sslSocket *ss, const PRUint8 *in, int len, int flags); @@ -1215,7 +1227,7 @@ SECStatus ssl_HashHandshakeMessage(sslSocket *ss, SSLHandshakeType type, extern PRBool ssl3_WaitingForServerSecondRound(sslSocket *ss); extern PRInt32 ssl3_SendRecord(sslSocket *ss, ssl3CipherSpec *cwSpec, - SSL3ContentType type, + SSLContentType type, const PRUint8 *pIn, PRInt32 nIn, PRInt32 flags); @@ -1387,7 +1399,7 @@ SECStatus ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type); * input into the SSL3 machinery from the actualy network reading code */ SECStatus ssl3_HandleRecord(sslSocket *ss, SSL3Ciphertext *cipher); -SECStatus ssl3_HandleNonApplicationData(sslSocket *ss, SSL3ContentType rType, +SECStatus ssl3_HandleNonApplicationData(sslSocket *ss, SSLContentType rType, DTLSEpoch epoch, sslSequenceNumber seqNum, sslBuffer *databuf); @@ -1497,7 +1509,7 @@ extern SECStatus ssl3_HandleECDHClientKeyExchange(sslSocket *ss, sslKeyPair *serverKeys); extern SECStatus ssl3_SendECDHServerKeyExchange(sslSocket *ss); extern SECStatus ssl_ImportECDHKeyShare( - sslSocket *ss, SECKEYPublicKey *peerKey, + SECKEYPublicKey *peerKey, PRUint8 *b, PRUint32 length, const sslNamedGroupDef *curve); extern SECStatus ssl3_ComputeCommonKeyHash(SSLHashType hashAlg, @@ -1562,6 +1574,12 @@ extern void ssl_FreePRSocket(PRFileDesc *fd); * various ciphers */ extern unsigned int ssl3_config_match_init(sslSocket *); +/* Return PR_TRUE if suite is usable. This if the suite is permitted by policy, + * enabled, has a certificate (as needed), has a viable key agreement method, is + * usable with the negotiated TLS version, and is otherwise usable. */ +PRBool ssl3_config_match(const ssl3CipherSuiteCfg *suite, PRUint8 policy, + const SSLVersionRange *vrange, const sslSocket *ss); + /* calls for accessing wrapping keys across processes. */ extern SECStatus ssl_GetWrappingKey(unsigned int symWrapMechIndex, unsigned int wrapKeyIndex, @@ -1591,6 +1609,8 @@ extern SECStatus ssl_InitSessionCacheLocks(PRBool lazyInit); extern SECStatus ssl_FreeSessionCacheLocks(void); CK_MECHANISM_TYPE ssl3_Alg2Mech(SSLCipherAlgorithm calg); +SECStatus ssl3_NegotiateCipherSuiteInner(sslSocket *ss, const SECItem *suites, + PRUint16 version, PRUint16 *suitep); SECStatus ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites, PRBool initHashes); SECStatus ssl3_InitHandshakeHashes(sslSocket *ss); @@ -1638,8 +1658,12 @@ PK11SymKey *ssl3_GetWrappingKey(sslSocket *ss, SECStatus ssl3_FillInCachedSID(sslSocket *ss, sslSessionID *sid, PK11SymKey *secret); const ssl3CipherSuiteDef *ssl_LookupCipherSuiteDef(ssl3CipherSuite suite); +const ssl3CipherSuiteCfg *ssl_LookupCipherSuiteCfg(ssl3CipherSuite suite, + const ssl3CipherSuiteCfg *suites); + SECStatus ssl3_SelectServerCert(sslSocket *ss); SECStatus ssl_PickSignatureScheme(sslSocket *ss, + CERTCertificate *cert, SECKEYPublicKey *pubKey, SECKEYPrivateKey *privKey, const SSLSignatureScheme *peerSchemes, @@ -1647,11 +1671,11 @@ SECStatus ssl_PickSignatureScheme(sslSocket *ss, PRBool requireSha1); SECOidTag ssl3_HashTypeToOID(SSLHashType hashType); SSLHashType ssl_SignatureSchemeToHashType(SSLSignatureScheme scheme); -KeyType ssl_SignatureSchemeToKeyType(SSLSignatureScheme scheme); +SSLAuthType ssl_SignatureSchemeToAuthType(SSLSignatureScheme scheme); SECStatus ssl3_SetupCipherSuite(sslSocket *ss, PRBool initHashes); SECStatus ssl_InsertRecordHeader(const sslSocket *ss, ssl3CipherSpec *cwSpec, - SSL3ContentType contentType, sslBuffer *wrBuf, + SSLContentType contentType, sslBuffer *wrBuf, PRBool *needsLength); /* Pull in DTLS functions */ @@ -1703,7 +1727,7 @@ void ssl_Trace(const char *format, ...); void ssl_CacheExternalToken(sslSocket *ss); SECStatus ssl_DecodeResumptionToken(sslSessionID *sid, const PRUint8 *encodedTicket, PRUint32 encodedTicketLen); -PRBool ssl_IsResumptionTokenValid(sslSocket *ss); +PRBool ssl_IsResumptionTokenUsable(sslSocket *ss, sslSessionID *sid); /* Remove when stable. */ diff --git a/security/nss/lib/ssl/sslnonce.c b/security/nss/lib/ssl/sslnonce.c index f79c23fc7..f8fb5d50f 100644 --- a/security/nss/lib/ssl/sslnonce.c +++ b/security/nss/lib/ssl/sslnonce.c @@ -234,9 +234,20 @@ ssl_FreeLockedSID(sslSessionID *sid) void ssl_FreeSID(sslSessionID *sid) { + if (sid) { + LOCK_CACHE; + ssl_FreeLockedSID(sid); + UNLOCK_CACHE; + } +} + +sslSessionID * +ssl_ReferenceSID(sslSessionID *sid) +{ LOCK_CACHE; - ssl_FreeLockedSID(sid); + sid->references++; UNLOCK_CACHE; + return sid; } /************************************************************************/ @@ -704,10 +715,9 @@ ssl_DecodeResumptionToken(sslSessionID *sid, const PRUint8 *encodedToken, } PRBool -ssl_IsResumptionTokenValid(sslSocket *ss) +ssl_IsResumptionTokenUsable(sslSocket *ss, sslSessionID *sid) { PORT_Assert(ss); - sslSessionID *sid = ss->sec.ci.sid; PORT_Assert(sid); // Check that the ticket didn't expire. @@ -1093,10 +1103,12 @@ ssl_CacheExternalToken(sslSocket *ss) PRINT_BUF(40, (ss, "SSL: encoded resumption token", SSL_BUFFER_BASE(&encodedToken), SSL_BUFFER_LEN(&encodedToken))); - ss->resumptionTokenCallback(ss->fd, SSL_BUFFER_BASE(&encodedToken), - SSL_BUFFER_LEN(&encodedToken), - ss->resumptionTokenContext); - + SECStatus rv = ss->resumptionTokenCallback( + ss->fd, SSL_BUFFER_BASE(&encodedToken), SSL_BUFFER_LEN(&encodedToken), + ss->resumptionTokenContext); + if (rv == SECSuccess) { + sid->cached = in_external_cache; + } sslBuffer_Clear(&encodedToken); } @@ -1200,17 +1212,23 @@ ssl3_SetSIDSessionTicket(sslSessionID *sid, PORT_Assert(newSessionTicket->ticket.data); PORT_Assert(newSessionTicket->ticket.len != 0); - /* if sid->u.ssl3.lock, we are updating an existing entry that is already - * cached or was once cached, so we need to acquire and release the write - * lock. Otherwise, this is a new session that isn't shared with anything - * yet, so no locking is needed. + /* If this is in the client cache, we are updating an existing entry that is + * already cached or was once cached, so we need to acquire and release the + * write lock. Otherwise, this is a new session that isn't shared with + * anything yet, so no locking is needed. */ if (sid->u.ssl3.lock) { + PORT_Assert(sid->cached == in_client_cache); PR_RWLock_Wlock(sid->u.ssl3.lock); - if (sid->u.ssl3.locked.sessionTicket.ticket.data) { - SECITEM_FreeItem(&sid->u.ssl3.locked.sessionTicket.ticket, - PR_FALSE); - } + } + /* If this was in the client cache, then we might have to free the old + * ticket. In TLS 1.3, we might get a replacement ticket if the server + * sends more than one ticket. */ + if (sid->u.ssl3.locked.sessionTicket.ticket.data) { + PORT_Assert(sid->cached == in_client_cache || + sid->version >= SSL_LIBRARY_VERSION_TLS_1_3); + SECITEM_FreeItem(&sid->u.ssl3.locked.sessionTicket.ticket, + PR_FALSE); } PORT_Assert(!sid->u.ssl3.locked.sessionTicket.ticket.data); diff --git a/security/nss/lib/ssl/sslsecur.c b/security/nss/lib/ssl/sslsecur.c index a1d389214..c011b66a1 100644 --- a/security/nss/lib/ssl/sslsecur.c +++ b/security/nss/lib/ssl/sslsecur.c @@ -685,23 +685,6 @@ ssl_SecureConnect(sslSocket *ss, const PRNetAddr *sa) } /* - * The TLS 1.2 RFC 5246, Section 7.2.1 says: - * - * Unless some other fatal alert has been transmitted, each party is - * required to send a close_notify alert before closing the write side - * of the connection. The other party MUST respond with a close_notify - * alert of its own and close down the connection immediately, - * discarding any pending writes. It is not required for the initiator - * of the close to wait for the responding close_notify alert before - * closing the read side of the connection. - * - * The second sentence requires that we send a close_notify alert when we - * have received a close_notify alert. In practice, all SSL implementations - * close the socket immediately after sending a close_notify alert (which is - * allowed by the third sentence), so responding with a close_notify alert - * would result in a write failure with the ECONNRESET error. This is why - * we don't respond with a close_notify alert. - * * Also, in the unlikely event that the TCP pipe is full and the peer stops * reading, the SSL3_SendAlert call in ssl_SecureClose and ssl_SecureShutdown * may block indefinitely in blocking mode, and may fail (without retrying) @@ -714,8 +697,7 @@ ssl_SecureClose(sslSocket *ss) int rv; if (!(ss->shutdownHow & ssl_SHUTDOWN_SEND) && - ss->firstHsDone && - !ss->recvdCloseNotify) { + ss->firstHsDone) { /* We don't want the final alert to be Nagle delayed. */ if (!ss->delayDisabled) { @@ -744,8 +726,7 @@ ssl_SecureShutdown(sslSocket *ss, int nsprHow) if ((sslHow & ssl_SHUTDOWN_SEND) != 0 && !(ss->shutdownHow & ssl_SHUTDOWN_SEND) && - ss->firstHsDone && - !ss->recvdCloseNotify) { + ss->firstHsDone) { (void)SSL3_SendAlert(ss, alert_warning, close_notify); } @@ -936,6 +917,25 @@ ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags) firstClientWrite = ss->ssl3.hs.ws == idle_handshake; ssl_ReleaseSSL3HandshakeLock(ss); } + /* Allow the server to send 0.5 RTT data in TLS 1.3. Requesting a + * certificate implies that the server might condition its sending on + * client authentication, so force servers that do that to wait. + * + * What might not be obvious here is that this allows 0.5 RTT when doing + * PSK-based resumption. As a result, 0.5 RTT is always enabled when + * early data is accepted. + * + * This check might be more conservative than absolutely necessary. + * It's possible that allowing 0.5 RTT data when the server requests, + * but does not require client authentication is safe because we can + * expect the server to check for a client certificate properly. */ + if (ss->sec.isServer && + ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 && + !tls13_ShouldRequestClientAuth(ss)) { + ssl_GetSSL3HandshakeLock(ss); + allowEarlySend = TLS13_IN_HS_STATE(ss, wait_finished); + ssl_ReleaseSSL3HandshakeLock(ss); + } if (!allowEarlySend && ss->handshake) { rv = ssl_Do1stHandshake(ss); } @@ -971,7 +971,7 @@ ssl_SecureSend(sslSocket *ss, const unsigned char *buf, int len, int flags) * 1-RTT later. */ ssl_GetSpecReadLock(ss); - len = tls13_LimitEarlyData(ss, content_application_data, len); + len = tls13_LimitEarlyData(ss, ssl_ct_application_data, len); ssl_ReleaseSpecReadLock(ss); } diff --git a/security/nss/lib/ssl/sslsock.c b/security/nss/lib/ssl/sslsock.c index 33595ffae..ae904e29b 100644 --- a/security/nss/lib/ssl/sslsock.c +++ b/security/nss/lib/ssl/sslsock.c @@ -18,6 +18,8 @@ #include "private/pprio.h" #include "nss.h" #include "pk11pqg.h" +#include "pk11pub.h" +#include "tls13esni.h" static const sslSocketOps ssl_default_ops = { /* No SSL. */ ssl_DefConnect, @@ -82,7 +84,9 @@ static sslOptions ssl_defaults = { .requireDHENamedGroups = PR_FALSE, .enable0RttData = PR_FALSE, .enableTls13CompatMode = PR_FALSE, - .enableDtlsShortHeader = PR_FALSE + .enableDtlsShortHeader = PR_FALSE, + .enableHelloDowngradeCheck = PR_FALSE, + .enableV2CompatibleHello = PR_FALSE }; /* @@ -359,6 +363,13 @@ ssl_DupSocket(sslSocket *os) ss->resumptionTokenCallback = os->resumptionTokenCallback; ss->resumptionTokenContext = os->resumptionTokenContext; + if (os->esniKeys) { + ss->esniKeys = tls13_CopyESNIKeys(os->esniKeys); + if (!ss->esniKeys) { + goto loser; + } + } + /* Create security data */ rv = ssl_CopySecurityInfo(ss, os); if (rv != SECSuccess) { @@ -444,6 +455,8 @@ ssl_DestroySocketContents(sslSocket *ss) ssl_ClearPRCList(&ss->ssl3.hs.dtlsSentHandshake, NULL); ssl_ClearPRCList(&ss->ssl3.hs.dtlsRcvdHandshake, NULL); + + tls13_DestroyESNIKeys(ss->esniKeys); } /* @@ -821,6 +834,14 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 which, PRIntn val) ss->opt.enableDtlsShortHeader = val; break; + case SSL_ENABLE_HELLO_DOWNGRADE_CHECK: + ss->opt.enableHelloDowngradeCheck = val; + break; + + case SSL_ENABLE_V2_COMPATIBLE_HELLO: + ss->opt.enableV2CompatibleHello = val; + break; + default: PORT_SetError(SEC_ERROR_INVALID_ARGS); rv = SECFailure; @@ -963,6 +984,12 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 which, PRIntn *pVal) case SSL_ENABLE_DTLS_SHORT_HEADER: val = ss->opt.enableDtlsShortHeader; break; + case SSL_ENABLE_HELLO_DOWNGRADE_CHECK: + val = ss->opt.enableHelloDowngradeCheck; + break; + case SSL_ENABLE_V2_COMPATIBLE_HELLO: + val = ss->opt.enableV2CompatibleHello; + break; default: PORT_SetError(SEC_ERROR_INVALID_ARGS); rv = SECFailure; @@ -1089,6 +1116,12 @@ SSL_OptionGetDefault(PRInt32 which, PRIntn *pVal) case SSL_ENABLE_DTLS_SHORT_HEADER: val = ssl_defaults.enableDtlsShortHeader; break; + case SSL_ENABLE_HELLO_DOWNGRADE_CHECK: + val = ssl_defaults.enableHelloDowngradeCheck; + break; + case SSL_ENABLE_V2_COMPATIBLE_HELLO: + val = ssl_defaults.enableV2CompatibleHello; + break; default: PORT_SetError(SEC_ERROR_INVALID_ARGS); rv = SECFailure; @@ -1284,6 +1317,14 @@ SSL_OptionSetDefault(PRInt32 which, PRIntn val) ssl_defaults.enableDtlsShortHeader = val; break; + case SSL_ENABLE_HELLO_DOWNGRADE_CHECK: + ssl_defaults.enableHelloDowngradeCheck = val; + break; + + case SSL_ENABLE_V2_COMPATIBLE_HELLO: + ssl_defaults.enableV2CompatibleHello = val; + break; + default: PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; @@ -3742,6 +3783,10 @@ ssl_GetKeyPairRef(sslKeyPair *keyPair) void ssl_FreeKeyPair(sslKeyPair *keyPair) { + if (!keyPair) { + return; + } + PRInt32 newCount = PR_ATOMIC_DECREMENT(&keyPair->refCount); if (!newCount) { SECKEY_DestroyPrivateKey(keyPair->privKey); @@ -3801,6 +3846,10 @@ ssl_CopyEphemeralKeyPair(sslEphemeralKeyPair *keyPair) void ssl_FreeEphemeralKeyPair(sslEphemeralKeyPair *keyPair) { + if (!keyPair) { + return; + } + ssl_FreeKeyPair(keyPair->keys); PR_REMOVE_LINK(&keyPair->link); PORT_Free(keyPair); @@ -3908,6 +3957,8 @@ ssl_NewSocket(PRBool makeLocks, SSLProtocolVariant protocolVariant) PR_INIT_CLIST(&ss->ssl3.hs.dtlsRcvdHandshake); dtls_InitTimers(ss); + ss->esniKeys = NULL; + if (makeLocks) { rv = ssl_MakeLocks(ss); if (rv != SECSuccess) @@ -3984,6 +4035,9 @@ struct { EXP(SetResumptionToken), EXP(GetResumptionTokenInfo), EXP(DestroyResumptionTokenInfo), + EXP(SetESNIKeyPair), + EXP(EncodeESNIKeys), + EXP(EnableESNI), #endif { "", NULL } }; @@ -4049,6 +4103,7 @@ SSLExp_SetResumptionToken(PRFileDesc *fd, const PRUint8 *token, unsigned int len) { sslSocket *ss = ssl_FindSocket(fd); + sslSessionID *sid = NULL; if (!ss) { SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SetResumptionToken", @@ -4062,7 +4117,7 @@ SSLExp_SetResumptionToken(PRFileDesc *fd, const PRUint8 *token, if (ss->firstHsDone || ss->ssl3.hs.ws != idle_handshake || ss->sec.isServer || len == 0 || !token) { PORT_SetError(SEC_ERROR_INVALID_ARGS); - goto done; + goto loser; } // We override any previously set session. @@ -4073,41 +4128,44 @@ SSLExp_SetResumptionToken(PRFileDesc *fd, const PRUint8 *token, PRINT_BUF(50, (ss, "incoming resumption token", token, len)); - ss->sec.ci.sid = ssl3_NewSessionID(ss, PR_FALSE); - if (!ss->sec.ci.sid) { - goto done; + sid = ssl3_NewSessionID(ss, PR_FALSE); + if (!sid) { + goto loser; } /* Populate NewSessionTicket values */ - SECStatus rv = ssl_DecodeResumptionToken(ss->sec.ci.sid, token, len); + SECStatus rv = ssl_DecodeResumptionToken(sid, token, len); if (rv != SECSuccess) { // If decoding fails, we assume the token is bad. PORT_SetError(SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR); - ssl_FreeSID(ss->sec.ci.sid); - ss->sec.ci.sid = NULL; - goto done; + goto loser; } - // Make sure that the token is valid. - if (!ssl_IsResumptionTokenValid(ss)) { - ssl_FreeSID(ss->sec.ci.sid); - ss->sec.ci.sid = NULL; + // Make sure that the token is currently usable. + if (!ssl_IsResumptionTokenUsable(ss, sid)) { PORT_SetError(SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR); - goto done; + goto loser; } + // Generate a new random session ID for this ticket. + rv = PK11_GenerateRandom(sid->u.ssl3.sessionID, SSL3_SESSIONID_BYTES); + if (rv != SECSuccess) { + goto loser; // Code set by PK11_GenerateRandom. + } + sid->u.ssl3.sessionIDLength = SSL3_SESSIONID_BYTES; /* Use the sid->cached as marker that this is from an external cache and * we don't have to look up anything in the NSS internal cache. */ - ss->sec.ci.sid->cached = in_external_cache; - // This has to be 2 to not free this in sendClientHello. - ss->sec.ci.sid->references = 2; - ss->sec.ci.sid->lastAccessTime = ssl_TimeSec(); + sid->cached = in_external_cache; + sid->lastAccessTime = ssl_TimeSec(); + + ss->sec.ci.sid = sid; ssl_ReleaseSSL3HandshakeLock(ss); ssl_Release1stHandshakeLock(ss); return SECSuccess; -done: +loser: + ssl_FreeSID(sid); ssl_ReleaseSSL3HandshakeLock(ss); ssl_Release1stHandshakeLock(ss); @@ -4164,6 +4222,7 @@ SSLExp_GetResumptionTokenInfo(const PRUint8 *tokenData, unsigned int tokenLen, } else { token.maxEarlyDataSize = 0; } + token.expirationTime = sid.expirationTime; token.length = PR_MIN(sizeof(SSLResumptionTokenInfo), len); PORT_Memcpy(tokenOut, &token, token.length); diff --git a/security/nss/lib/ssl/sslspec.c b/security/nss/lib/ssl/sslspec.c index 7833eeab6..f2e72a4ec 100644 --- a/security/nss/lib/ssl/sslspec.c +++ b/security/nss/lib/ssl/sslspec.c @@ -203,7 +203,7 @@ ssl_CipherSpecAddRef(ssl3CipherSpec *spec) SSL_GETPID(), SPEC_DIR(spec), spec, spec->refCt)); } -static void +void ssl_DestroyKeyMaterial(ssl3KeyMaterial *keyMaterial) { PK11_FreeSymKey(keyMaterial->key); diff --git a/security/nss/lib/ssl/sslt.h b/security/nss/lib/ssl/sslt.h index bb1bec7a3..bd32a6e18 100644 --- a/security/nss/lib/ssl/sslt.h +++ b/security/nss/lib/ssl/sslt.h @@ -35,6 +35,14 @@ typedef enum { ssl_hs_message_hash = 254, /* Not a real message. */ } SSLHandshakeType; +typedef enum { + ssl_ct_change_cipher_spec = 20, + ssl_ct_alert = 21, + ssl_ct_handshake = 22, + ssl_ct_application_data = 23, + ssl_ct_ack = 25 +} SSLContentType; + typedef struct SSL3StatisticsStr { /* statistics from ssl3_SendClientHello (sch) */ long sch_sid_cache_hits; @@ -446,7 +454,8 @@ typedef enum { ssl_tls13_key_share_xtn = 51, ssl_next_proto_nego_xtn = 13172, /* Deprecated. */ ssl_renegotiation_info_xtn = 0xff01, - ssl_tls13_short_header_xtn = 0xff03 /* Deprecated. */ + ssl_tls13_short_header_xtn = 0xff03, /* Deprecated. */ + ssl_tls13_encrypted_sni_xtn = 0xffce, } SSLExtensionType; /* This is the old name for the supported_groups extensions. */ diff --git a/security/nss/lib/ssl/tls13con.c b/security/nss/lib/ssl/tls13con.c index 4d9170fb0..461cd2eb9 100644 --- a/security/nss/lib/ssl/tls13con.c +++ b/security/nss/lib/ssl/tls13con.c @@ -21,6 +21,7 @@ #include "tls13hkdf.h" #include "tls13con.h" #include "tls13err.h" +#include "tls13esni.h" #include "tls13exthandle.h" #include "tls13hashstate.h" @@ -117,6 +118,7 @@ const char kHkdfLabelFinishedSecret[] = "finished"; const char kHkdfLabelResumptionMasterSecret[] = "res master"; const char kHkdfLabelExporterMasterSecret[] = "exp master"; const char kHkdfLabelResumption[] = "resumption"; +const char kHkdfLabelTrafficUpdate[] = "traffic upd"; const char kHkdfPurposeKey[] = "key"; const char kHkdfPurposeIv[] = "iv"; @@ -132,21 +134,6 @@ const char keylogLabelExporterSecret[] = "EXPORTER_SECRET"; PR_STATIC_ASSERT(SSL_LIBRARY_VERSION_MAX_SUPPORTED <= SSL_LIBRARY_VERSION_TLS_1_3); -/* Use this instead of FATAL_ERROR when no alert shall be sent. */ -#define LOG_ERROR(ss, prError) \ - do { \ - SSL_TRC(3, ("%d: TLS13[%d]: fatal error %d in %s (%s:%d)", \ - SSL_GETPID(), ss->fd, prError, __func__, __FILE__, __LINE__)); \ - PORT_SetError(prError); \ - } while (0) - -/* Log an error and generate an alert because something is irreparably wrong. */ -#define FATAL_ERROR(ss, prError, desc) \ - do { \ - LOG_ERROR(ss, prError); \ - tls13_FatalError(ss, prError, desc); \ - } while (0) - void tls13_FatalError(sslSocket *ss, PRErrorCode prError, SSL3AlertDescription desc) { @@ -354,16 +341,16 @@ tls13_ComputeHash(sslSocket *ss, SSL3Hashes *hashes, } SECStatus -tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef) +tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef, + sslEphemeralKeyPair **keyPair) { SECStatus rv; - sslEphemeralKeyPair *keyPair = NULL; const ssl3DHParams *params; PORT_Assert(groupDef); switch (groupDef->keaType) { case ssl_kea_ecdh: - rv = ssl_CreateECDHEphemeralKeyPair(ss, groupDef, &keyPair); + rv = ssl_CreateECDHEphemeralKeyPair(ss, groupDef, keyPair); if (rv != SECSuccess) { return SECFailure; } @@ -371,7 +358,7 @@ tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef) case ssl_kea_dh: params = ssl_GetDHEParams(groupDef); PORT_Assert(params->name != ssl_grp_ffdhe_custom); - rv = ssl_CreateDHEKeyPair(groupDef, params, &keyPair); + rv = ssl_CreateDHEKeyPair(groupDef, params, keyPair); if (rv != SECSuccess) { return SECFailure; } @@ -382,11 +369,24 @@ tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef) return SECFailure; } - PR_APPEND_LINK(&keyPair->link, &ss->ephemeralKeyPairs); return rv; } SECStatus +tls13_AddKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef) +{ + sslEphemeralKeyPair *keyPair = NULL; + SECStatus rv; + + rv = tls13_CreateKeyShare(ss, groupDef, &keyPair); + if (rv != SECSuccess) { + return SECFailure; + } + PR_APPEND_LINK(&keyPair->link, &ss->ephemeralKeyPairs); + return SECSuccess; +} + +SECStatus SSL_SendAdditionalKeyShares(PRFileDesc *fd, unsigned int count) { sslSocket *ss = ssl_FindSocket(fd); @@ -413,20 +413,26 @@ tls13_SetupClientHello(sslSocket *ss) NewSessionTicket *session_ticket = NULL; sslSessionID *sid = ss->sec.ci.sid; unsigned int numShares = 0; + SECStatus rv; PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); PORT_Assert(PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs)); + /* Do encrypted SNI. This may create a key share as a side effect. */ + rv = tls13_ClientSetupESNI(ss); + if (rv != SECSuccess) { + return SECFailure; + } + /* Select the first enabled group. * TODO(ekr@rtfm.com): be smarter about offering the group * that the other side negotiated if we are resuming. */ for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) { - SECStatus rv; if (!ss->namedGroupPreferences[i]) { continue; } - rv = tls13_CreateKeyShare(ss, ss->namedGroupPreferences[i]); + rv = tls13_AddKeyShare(ss, ss->namedGroupPreferences[i]); if (rv != SECSuccess) { return SECFailure; } @@ -455,8 +461,6 @@ tls13_SetupClientHello(sslSocket *ss) } if (ss->statelessResume) { - SECStatus rv; - PORT_Assert(ss->sec.ci.sid); rv = tls13_RecoverWrappedSharedSecret(ss, ss->sec.ci.sid); if (rv != SECSuccess) { @@ -486,7 +490,7 @@ tls13_SetupClientHello(sslSocket *ss) } static SECStatus -tls13_ImportDHEKeyShare(sslSocket *ss, SECKEYPublicKey *peerKey, +tls13_ImportDHEKeyShare(SECKEYPublicKey *peerKey, PRUint8 *b, PRUint32 length, SECKEYPublicKey *pubKey) { @@ -517,16 +521,20 @@ tls13_ImportDHEKeyShare(sslSocket *ss, SECKEYPublicKey *peerKey, return SECSuccess; } -static SECStatus +SECStatus tls13_HandleKeyShare(sslSocket *ss, TLS13KeyShareEntry *entry, - sslKeyPair *keyPair) + sslKeyPair *keyPair, + SSLHashType hash, + PK11SymKey **out) { PORTCheapArenaPool arena; SECKEYPublicKey *peerKey; CK_MECHANISM_TYPE mechanism; PRErrorCode errorCode; + PK11SymKey *key; SECStatus rv; + int keySize = 0; PORT_InitCheapArena(&arena, DER_DEFAULT_CHUNKSIZE); peerKey = PORT_ArenaZNew(&arena.arena, SECKEYPublicKey); @@ -539,18 +547,19 @@ tls13_HandleKeyShare(sslSocket *ss, switch (entry->group->keaType) { case ssl_kea_ecdh: - rv = ssl_ImportECDHKeyShare(ss, peerKey, + rv = ssl_ImportECDHKeyShare(peerKey, entry->key_exchange.data, entry->key_exchange.len, entry->group); mechanism = CKM_ECDH1_DERIVE; break; case ssl_kea_dh: - rv = tls13_ImportDHEKeyShare(ss, peerKey, + rv = tls13_ImportDHEKeyShare(peerKey, entry->key_exchange.data, entry->key_exchange.len, keyPair->pubKey); mechanism = CKM_DH_PKCS_DERIVE; + keySize = peerKey->u.dh.publicValue.len; break; default: PORT_Assert(0); @@ -560,13 +569,14 @@ tls13_HandleKeyShare(sslSocket *ss, goto loser; } - ss->ssl3.hs.dheSecret = PK11_PubDeriveWithKDF( + key = PK11_PubDeriveWithKDF( keyPair->privKey, peerKey, PR_FALSE, NULL, NULL, mechanism, - tls13_GetHkdfMechanism(ss), CKA_DERIVE, 0, CKD_NULL, NULL, NULL); - if (!ss->ssl3.hs.dheSecret) { + tls13_GetHkdfMechanismForHash(hash), CKA_DERIVE, keySize, CKD_NULL, NULL, NULL); + if (!key) { ssl_MapLowLevelError(SSL_ERROR_KEY_EXCHANGE_FAILURE); goto loser; } + *out = key; PORT_DestroyCheapArena(&arena); return SECSuccess; @@ -603,8 +613,8 @@ tls13_UpdateTrafficKeys(sslSocket *ss, CipherSpecDirection direction) secret = tls13_TrafficSecretRef(ss, direction); rv = tls13_HkdfExpandLabel(*secret, tls13_GetHash(ss), NULL, 0, - kHkdfLabelApplicationTrafficSecret, - strlen(kHkdfLabelApplicationTrafficSecret), + kHkdfLabelTrafficUpdate, + strlen(kHkdfLabelTrafficUpdate), tls13_GetHmacMechanism(ss), tls13_GetHashSize(ss), &updatedSecret); @@ -1417,30 +1427,6 @@ tls13_NegotiateKeyExchange(sslSocket *ss, return SECSuccess; } -SSLAuthType -ssl_SignatureSchemeToAuthType(SSLSignatureScheme scheme) -{ - switch (scheme) { - case ssl_sig_rsa_pkcs1_sha1: - case ssl_sig_rsa_pkcs1_sha256: - case ssl_sig_rsa_pkcs1_sha384: - case ssl_sig_rsa_pkcs1_sha512: - /* We report PSS signatures as being just RSA signatures. */ - case ssl_sig_rsa_pss_rsae_sha256: - case ssl_sig_rsa_pss_rsae_sha384: - case ssl_sig_rsa_pss_rsae_sha512: - return ssl_auth_rsa_sign; - case ssl_sig_ecdsa_secp256r1_sha256: - case ssl_sig_ecdsa_secp384r1_sha384: - case ssl_sig_ecdsa_secp521r1_sha512: - case ssl_sig_ecdsa_sha1: - return ssl_auth_ecdsa; - default: - PORT_Assert(0); - } - return ssl_auth_null; -} - SECStatus tls13_SelectServerCert(sslSocket *ss) { @@ -1469,6 +1455,7 @@ tls13_SelectServerCert(sslSocket *ss) } rv = ssl_PickSignatureScheme(ss, + cert->serverCert, cert->serverKeyPair->pubKey, cert->serverKeyPair->privKey, ss->xtnData.sigSchemes, @@ -2047,7 +2034,7 @@ tls13_HandleClientKeyShare(sslSocket *ss, TLS13KeyShareEntry *peerShare) tls13_SetKeyExchangeType(ss, peerShare->group); /* Generate our key */ - rv = tls13_CreateKeyShare(ss, peerShare->group); + rv = tls13_AddKeyShare(ss, peerShare->group); if (rv != SECSuccess) { return rv; } @@ -2067,7 +2054,9 @@ tls13_HandleClientKeyShare(sslSocket *ss, TLS13KeyShareEntry *peerShare) return SECFailure; /* Error code set already. */ } - rv = tls13_HandleKeyShare(ss, peerShare, keyPair->keys); + rv = tls13_HandleKeyShare(ss, peerShare, keyPair->keys, + tls13_GetHash(ss), + &ss->ssl3.hs.dheSecret); return rv; /* Error code set already. */ } @@ -2334,6 +2323,13 @@ tls13_HandleCertificateRequest(sslSocket *ss, PRUint8 *b, PRUint32 length) return SECSuccess; } +PRBool +tls13_ShouldRequestClientAuth(sslSocket *ss) +{ + return ss->opt.requestCertificate && + ss->ssl3.hs.kea_def->authKeyType != ssl_auth_psk; +} + static SECStatus tls13_SendEncryptedServerSequence(sslSocket *ss) { @@ -2365,7 +2361,7 @@ tls13_SendEncryptedServerSequence(sslSocket *ss) return SECFailure; /* error code is set. */ } - if (ss->opt.requestCertificate) { + if (tls13_ShouldRequestClientAuth(ss)) { rv = tls13_SendCertificateRequest(ss); if (rv != SECSuccess) { return SECFailure; /* error code is set. */ @@ -2484,9 +2480,11 @@ tls13_SendServerHelloSequence(sslSocket *ss) LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); return SECFailure; } - TLS13_SET_HS_STATE(ss, - ss->opt.requestCertificate ? wait_client_cert - : wait_finished); + if (tls13_ShouldRequestClientAuth(ss)) { + TLS13_SET_HS_STATE(ss, wait_client_cert); + } else { + TLS13_SET_HS_STATE(ss, wait_finished); + } } ss->ssl3.hs.serverHelloTime = ssl_TimeUsec(); @@ -2512,6 +2510,7 @@ tls13_HandleServerHelloPart2(sslSocket *ss) } if (ss->statelessResume) { + PORT_Assert(sid->version >= SSL_LIBRARY_VERSION_TLS_1_3); if (tls13_GetHash(ss) != tls13_GetHashForCipherSuite(sid->u.ssl3.cipherSuite)) { FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_SERVER_HELLO, @@ -2657,7 +2656,9 @@ tls13_HandleServerKeyShare(sslSocket *ss) PORT_Assert(ssl_NamedGroupEnabled(ss, entry->group)); - rv = tls13_HandleKeyShare(ss, entry, keyPair->keys); + rv = tls13_HandleKeyShare(ss, entry, keyPair->keys, + tls13_GetHash(ss), + &ss->ssl3.hs.dheSecret); if (rv != SECSuccess) return SECFailure; /* Error code set by caller. */ @@ -3213,6 +3214,21 @@ tls13_SetSpecRecordVersion(sslSocket *ss, ssl3CipherSpec *spec) SSL_GETPID(), ss->fd, spec, spec->recordVersion)); } +SSLAEADCipher +tls13_GetAead(const ssl3BulkCipherDef *cipherDef) +{ + switch (cipherDef->calg) { + case ssl_calg_aes_gcm: + return tls13_AESGCM; + case ssl_calg_chacha20: + return tls13_ChaCha20Poly1305; + default: + PORT_Assert(PR_FALSE); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return NULL; + } +} + static SECStatus tls13_SetupPendingCipherSpec(sslSocket *ss, ssl3CipherSpec *spec) { @@ -3236,16 +3252,9 @@ tls13_SetupPendingCipherSpec(sslSocket *ss, ssl3CipherSpec *spec) SSL_GETPID(), ss->fd, suite)); spec->cipherDef = ssl_GetBulkCipherDef(ssl_LookupCipherSuiteDef(suite)); - switch (spec->cipherDef->calg) { - case ssl_calg_aes_gcm: - spec->aead = tls13_AESGCM; - break; - case ssl_calg_chacha20: - spec->aead = tls13_ChaCha20Poly1305; - break; - default: - PORT_Assert(0); - return SECFailure; + spec->aead = tls13_GetAead(spec->cipherDef); + if (!spec->aead) { + return SECFailure; } if (spec->epoch == TrafficKeyEarlyApplicationData) { @@ -3427,9 +3436,31 @@ loser: return SECFailure; } +TLS13KeyShareEntry * +tls13_CopyKeyShareEntry(TLS13KeyShareEntry *o) +{ + TLS13KeyShareEntry *n; + + PORT_Assert(o); + n = PORT_ZNew(TLS13KeyShareEntry); + if (!n) { + return NULL; + } + + if (SECSuccess != SECITEM_CopyItem(NULL, &n->key_exchange, &o->key_exchange)) { + PORT_Free(n); + return NULL; + } + n->group = o->group; + return n; +} + void tls13_DestroyKeyShareEntry(TLS13KeyShareEntry *offer) { + if (!offer) { + return; + } SECITEM_ZfreeItem(&offer->key_exchange, PR_FALSE); PORT_ZFree(offer, sizeof(*offer)); } @@ -3550,7 +3581,7 @@ tls13_AESGCM(ssl3KeyMaterial *keys, CK_GCM_PARAMS gcmParams; unsigned char nonce[12]; - PORT_Assert(additionalDataLen > 8); + PORT_Assert(additionalDataLen >= 8); memset(&gcmParams, 0, sizeof(gcmParams)); gcmParams.pIv = nonce; gcmParams.ulIvLen = sizeof(nonce); @@ -3627,7 +3658,23 @@ tls13_HandleEncryptedExtensions(sslSocket *ss, PRUint8 *b, PRUint32 length) ss->xtnData.nextProto.data = NULL; ss->xtnData.nextProtoState = SSL_NEXT_PROTO_NO_SUPPORT; } - rv = ssl3_HandleExtensions(ss, &b, &length, ssl_hs_encrypted_extensions); + + rv = ssl3_ParseExtensions(ss, &b, &length); + if (rv != SECSuccess) { + return SECFailure; /* Error code set below */ + } + + /* If we sent ESNI, check the nonce. */ + if (ss->xtnData.esniPrivateKey) { + PORT_Assert(ssl3_ExtensionAdvertised(ss, ssl_tls13_encrypted_sni_xtn)); + rv = tls13_ClientCheckEsniXtn(ss); + if (rv != SECSuccess) { + return SECFailure; + } + } + + /* Handle the rest of the extensions. */ + rv = ssl3_HandleParsedExtensions(ss, ssl_hs_encrypted_extensions); if (rv != SECSuccess) { return SECFailure; /* Error code set below */ } @@ -4025,6 +4072,7 @@ tls13_ComputeFinished(sslSocket *ss, PK11SymKey *baseKey, PK11_FreeSymKey(secret); PK11_DestroyContext(hmacCtx, PR_TRUE); + PRINT_BUF(50, (ss, "finished value", output, outputLenUint)); return SECSuccess; abort: @@ -4189,7 +4237,7 @@ tls13_ServerHandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length) return SECFailure; } - if (!ss->opt.requestCertificate && + if (!tls13_ShouldRequestClientAuth(ss) && (ss->ssl3.hs.zeroRttState != ssl_0rtt_done)) { dtls_ReceivedFirstMessageInFlight(ss); } @@ -4679,7 +4727,8 @@ tls13_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b, PRUint32 length) /* Replace a previous session ticket when * we receive a second NewSessionTicket message. */ - if (ss->sec.ci.sid->cached == in_client_cache) { + if (ss->sec.ci.sid->cached == in_client_cache || + ss->sec.ci.sid->cached == in_external_cache) { /* Create a new session ID. */ sslSessionID *sid = ssl3_NewSessionID(ss, PR_FALSE); if (!sid) { @@ -4758,7 +4807,8 @@ static const struct { { ssl_tls13_certificate_authorities_xtn, _M1(certificate_request) }, { ssl_tls13_supported_versions_xtn, _M3(client_hello, server_hello, hello_retry_request) }, - { ssl_record_size_limit_xtn, _M2(client_hello, encrypted_extensions) } + { ssl_record_size_limit_xtn, _M2(client_hello, encrypted_extensions) }, + { ssl_tls13_encrypted_sni_xtn, _M2(client_hello, encrypted_extensions) } }; tls13ExtensionStatus @@ -4834,11 +4884,11 @@ tls13_FormatAdditionalData( } PRInt32 -tls13_LimitEarlyData(sslSocket *ss, SSL3ContentType type, PRInt32 toSend) +tls13_LimitEarlyData(sslSocket *ss, SSLContentType type, PRInt32 toSend) { PRInt32 reduced; - PORT_Assert(type == content_application_data); + PORT_Assert(type == ssl_ct_application_data); PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3); PORT_Assert(!ss->firstHsDone); if (ss->ssl3.cwSpec->epoch != TrafficKeyEarlyApplicationData) { @@ -4858,7 +4908,7 @@ tls13_LimitEarlyData(sslSocket *ss, SSL3ContentType type, PRInt32 toSend) SECStatus tls13_ProtectRecord(sslSocket *ss, ssl3CipherSpec *cwSpec, - SSL3ContentType type, + SSLContentType type, const PRUint8 *pIn, PRUint32 contentLen, sslBuffer *wrBuf) @@ -4899,7 +4949,7 @@ tls13_ProtectRecord(sslSocket *ss, *(SSL_BUFFER_NEXT(wrBuf) + contentLen) = type; /* Create the header (ugly that we have to do it twice). */ - rv = ssl_InsertRecordHeader(ss, cwSpec, content_application_data, + rv = ssl_InsertRecordHeader(ss, cwSpec, ssl_ct_application_data, &buf, &needsLength); if (rv != SECSuccess) { return SECFailure; @@ -4951,7 +5001,7 @@ tls13_UnprotectRecord(sslSocket *ss, ssl3CipherSpec *spec, SSL3Ciphertext *cText, sslBuffer *plaintext, - SSL3ContentType *innerType, + SSLContentType *innerType, SSL3AlertDescription *alert) { const ssl3BulkCipherDef *cipher_def = spec->cipherDef; @@ -4966,26 +5016,26 @@ tls13_UnprotectRecord(sslSocket *ss, SSL_GETPID(), ss->fd, spec, spec->epoch, spec->phase, cText->seqNum, cText->buf->len)); - /* We can perform this test in variable time because the record's total - * length and the ciphersuite are both public knowledge. */ - if (cText->buf->len < cipher_def->tag_size) { - SSL_TRC(3, - ("%d: TLS13[%d]: record too short to contain valid AEAD data", - SSL_GETPID(), ss->fd)); - PORT_SetError(SSL_ERROR_BAD_MAC_READ); - return SECFailure; - } - /* Verify that the content type is right, even though we overwrite it. * Also allow the DTLS short header in TLS 1.3. */ - if (!(cText->hdr[0] == content_application_data || + if (!(cText->hdr[0] == ssl_ct_application_data || (IS_DTLS(ss) && ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 && (cText->hdr[0] & 0xe0) == 0x20))) { SSL_TRC(3, ("%d: TLS13[%d]: record has invalid exterior type=%2.2x", SSL_GETPID(), ss->fd, cText->hdr[0])); - /* Do we need a better error here? */ + PORT_SetError(SSL_ERROR_RX_UNEXPECTED_RECORD_TYPE); + *alert = unexpected_message; + return SECFailure; + } + + /* We can perform this test in variable time because the record's total + * length and the ciphersuite are both public knowledge. */ + if (cText->buf->len < cipher_def->tag_size) { + SSL_TRC(3, + ("%d: TLS13[%d]: record too short to contain valid AEAD data", + SSL_GETPID(), ss->fd)); PORT_SetError(SSL_ERROR_BAD_MAC_READ); return SECFailure; } @@ -5054,12 +5104,12 @@ tls13_UnprotectRecord(sslSocket *ss, } /* Record the type. */ - *innerType = (SSL3ContentType)plaintext->buf[plaintext->len - 1]; + *innerType = (SSLContentType)plaintext->buf[plaintext->len - 1]; --plaintext->len; /* Check that we haven't received too much 0-RTT data. */ if (spec->epoch == TrafficKeyEarlyApplicationData && - *innerType == content_application_data) { + *innerType == ssl_ct_application_data) { if (plaintext->len > spec->earlyDataRemaining) { *alert = unexpected_message; PORT_SetError(SSL_ERROR_TOO_MUCH_EARLY_DATA); @@ -5242,9 +5292,11 @@ tls13_HandleEndOfEarlyData(sslSocket *ss, PRUint8 *b, PRUint32 length) } ss->ssl3.hs.zeroRttState = ssl_0rtt_done; - TLS13_SET_HS_STATE(ss, - ss->opt.requestCertificate ? wait_client_cert - : wait_finished); + if (tls13_ShouldRequestClientAuth(ss)) { + TLS13_SET_HS_STATE(ss, wait_client_cert); + } else { + TLS13_SET_HS_STATE(ss, wait_finished); + } return SECSuccess; } @@ -5283,11 +5335,12 @@ tls13_HandleEarlyApplicationData(sslSocket *ss, sslBuffer *origBuf) } PRUint16 -tls13_EncodeDraftVersion(SSL3ProtocolVersion version) +tls13_EncodeDraftVersion(SSL3ProtocolVersion version, SSLProtocolVariant variant) { -#ifdef TLS_1_3_DRAFT_VERSION - if (version == SSL_LIBRARY_VERSION_TLS_1_3) { - return 0x7f00 | TLS_1_3_DRAFT_VERSION; +#ifdef DTLS_1_3_DRAFT_VERSION + if (version == SSL_LIBRARY_VERSION_TLS_1_3 && + variant == ssl_variant_datagram) { + return 0x7f00 | DTLS_1_3_DRAFT_VERSION; } #endif return (PRUint16)version; @@ -5297,7 +5350,6 @@ SECStatus tls13_ClientReadSupportedVersion(sslSocket *ss) { PRUint32 temp; - SSL3ProtocolVersion v; TLSExtension *versionExtension; SECItem it; SECStatus rv; @@ -5319,29 +5371,15 @@ tls13_ClientReadSupportedVersion(sslSocket *ss) FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_SERVER_HELLO, illegal_parameter); return SECFailure; } - v = (SSL3ProtocolVersion)temp; - /* You cannot negotiate < TLS 1.3 with supported_versions. */ - if (v < SSL_LIBRARY_VERSION_TLS_1_3) { + if (temp != tls13_EncodeDraftVersion(SSL_LIBRARY_VERSION_TLS_1_3, + ss->protocolVariant)) { + /* You cannot negotiate < TLS 1.3 with supported_versions. */ FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_SERVER_HELLO, illegal_parameter); return SECFailure; } -#ifdef TLS_1_3_DRAFT_VERSION - if (temp == SSL_LIBRARY_VERSION_TLS_1_3) { - FATAL_ERROR(ss, SSL_ERROR_UNSUPPORTED_VERSION, protocol_version); - return SECFailure; - } - if (temp == tls13_EncodeDraftVersion(SSL_LIBRARY_VERSION_TLS_1_3)) { - v = SSL_LIBRARY_VERSION_TLS_1_3; - } else { - v = (SSL3ProtocolVersion)temp; - } -#else - v = (SSL3ProtocolVersion)temp; -#endif - - ss->version = v; + ss->version = SSL_LIBRARY_VERSION_TLS_1_3; return SECSuccess; } @@ -5365,7 +5403,7 @@ tls13_NegotiateVersion(sslSocket *ss, const TLSExtension *supportedVersions) return SECFailure; } for (version = ss->vrange.max; version >= ss->vrange.min; --version) { - PRUint16 wire = tls13_EncodeDraftVersion(version); + PRUint16 wire = tls13_EncodeDraftVersion(version, ss->protocolVariant); unsigned long offset; for (offset = 0; offset < versions.len; offset += 2) { diff --git a/security/nss/lib/ssl/tls13con.h b/security/nss/lib/ssl/tls13con.h index f35b20023..f3b2cb390 100644 --- a/security/nss/lib/ssl/tls13con.h +++ b/security/nss/lib/ssl/tls13con.h @@ -28,7 +28,7 @@ typedef enum { SECStatus tls13_UnprotectRecord( sslSocket *ss, ssl3CipherSpec *spec, SSL3Ciphertext *cText, sslBuffer *plaintext, - SSL3ContentType *innerType, + SSLContentType *innerType, SSL3AlertDescription *alert); #if defined(WIN32) @@ -64,7 +64,7 @@ void tls13_FatalError(sslSocket *ss, PRErrorCode prError, SSL3AlertDescription desc); SECStatus tls13_SetupClientHello(sslSocket *ss); SECStatus tls13_MaybeDo0RTTHandshake(sslSocket *ss); -PRInt32 tls13_LimitEarlyData(sslSocket *ss, SSL3ContentType type, PRInt32 toSend); +PRInt32 tls13_LimitEarlyData(sslSocket *ss, SSLContentType type, PRInt32 toSend); PRBool tls13_AllowPskCipher(const sslSocket *ss, const ssl3CipherSuiteDef *cipher_def); PRBool tls13_PskSuiteEnabled(sslSocket *ss); @@ -85,26 +85,36 @@ SECStatus tls13_ConstructHelloRetryRequest(sslSocket *ss, sslBuffer *buffer); SECStatus tls13_HandleHelloRetryRequest(sslSocket *ss, const PRUint8 *b, PRUint32 length); +SECStatus tls13_HandleKeyShare(sslSocket *ss, + TLS13KeyShareEntry *entry, + sslKeyPair *keyPair, + SSLHashType hash, + PK11SymKey **out); +TLS13KeyShareEntry *tls13_CopyKeyShareEntry(TLS13KeyShareEntry *o); void tls13_DestroyKeyShareEntry(TLS13KeyShareEntry *entry); void tls13_DestroyKeyShares(PRCList *list); -SECStatus tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef); +SECStatus tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef, + sslEphemeralKeyPair **keyPair); +SECStatus tls13_AddKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef); void tls13_DestroyEarlyData(PRCList *list); SECStatus tls13_SetAlertCipherSpec(sslSocket *ss); tls13ExtensionStatus tls13_ExtensionStatus(PRUint16 extension, SSLHandshakeType message); SECStatus tls13_ProtectRecord(sslSocket *ss, ssl3CipherSpec *cwSpec, - SSL3ContentType type, + SSLContentType type, const PRUint8 *pIn, PRUint32 contentLen, sslBuffer *wrBuf); PRInt32 tls13_Read0RttData(sslSocket *ss, void *buf, PRInt32 len); SECStatus tls13_HandleEarlyApplicationData(sslSocket *ss, sslBuffer *origBuf); PRBool tls13_ClientAllow0Rtt(const sslSocket *ss, const sslSessionID *sid); -PRUint16 tls13_EncodeDraftVersion(SSL3ProtocolVersion version); +PRUint16 tls13_EncodeDraftVersion(SSL3ProtocolVersion version, + SSLProtocolVariant variant); SECStatus tls13_ClientReadSupportedVersion(sslSocket *ss); SECStatus tls13_NegotiateVersion(sslSocket *ss, const TLSExtension *supported_versions); +PRBool tls13_ShouldRequestClientAuth(sslSocket *ss); PRBool tls13_IsReplay(const sslSocket *ss, const sslSessionID *sid); void tls13_AntiReplayRollover(PRTime now); @@ -119,6 +129,22 @@ SECStatus tls13_SendKeyUpdate(sslSocket *ss, tls13KeyUpdateRequest request, PRBool buffer); SECStatus SSLExp_KeyUpdate(PRFileDesc *fd, PRBool requestUpdate); PRBool tls13_MaybeTls13(sslSocket *ss); +SSLAEADCipher tls13_GetAead(const ssl3BulkCipherDef *cipherDef); void tls13_SetSpecRecordVersion(sslSocket *ss, ssl3CipherSpec *spec); +/* Use this instead of FATAL_ERROR when no alert shall be sent. */ +#define LOG_ERROR(ss, prError) \ + do { \ + SSL_TRC(3, ("%d: TLS13[%d]: fatal error %d in %s (%s:%d)", \ + SSL_GETPID(), ss->fd, prError, __func__, __FILE__, __LINE__)); \ + PORT_SetError(prError); \ + } while (0) + +/* Log an error and generate an alert because something is irreparably wrong. */ +#define FATAL_ERROR(ss, prError, desc) \ + do { \ + LOG_ERROR(ss, prError); \ + tls13_FatalError(ss, prError, desc); \ + } while (0) + #endif /* __tls13con_h_ */ diff --git a/security/nss/lib/ssl/tls13esni.c b/security/nss/lib/ssl/tls13esni.c new file mode 100644 index 000000000..e2328769b --- /dev/null +++ b/security/nss/lib/ssl/tls13esni.c @@ -0,0 +1,844 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * 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/. */ + +#define TLS13_ESNI_VERSION 0xff01 + +/* + * struct { + * uint16 version; + * uint8 checksum[4]; + * KeyShareEntry keys<4..2^16-1>; + * CipherSuite cipher_suites<2..2^16-2>; + * uint16 padded_length; + * uint64 not_before; + * uint64 not_after; + * Extension extensions<0..2^16-1>; + * } ESNIKeys; + */ +#include "nss.h" +#include "pk11func.h" +#include "ssl.h" +#include "sslproto.h" +#include "sslimpl.h" +#include "ssl3exthandle.h" +#include "tls13esni.h" +#include "tls13exthandle.h" +#include "tls13hkdf.h" + +const char kHkdfPurposeEsniKey[] = "esni key"; +const char kHkdfPurposeEsniIv[] = "esni iv"; + +void +tls13_DestroyESNIKeys(sslEsniKeys *keys) +{ + if (!keys) { + return; + } + SECITEM_FreeItem(&keys->data, PR_FALSE); + PORT_Free((void *)keys->dummySni); + tls13_DestroyKeyShares(&keys->keyShares); + ssl_FreeEphemeralKeyPair(keys->privKey); + SECITEM_FreeItem(&keys->suites, PR_FALSE); + PORT_ZFree(keys, sizeof(sslEsniKeys)); +} + +sslEsniKeys * +tls13_CopyESNIKeys(sslEsniKeys *okeys) +{ + sslEsniKeys *nkeys; + SECStatus rv; + + PORT_Assert(okeys); + + nkeys = PORT_ZNew(sslEsniKeys); + if (!nkeys) { + return NULL; + } + PR_INIT_CLIST(&nkeys->keyShares); + rv = SECITEM_CopyItem(NULL, &nkeys->data, &okeys->data); + if (rv != SECSuccess) { + goto loser; + } + if (okeys->dummySni) { + nkeys->dummySni = PORT_Strdup(okeys->dummySni); + if (!nkeys->dummySni) { + goto loser; + } + } + for (PRCList *cur_p = PR_LIST_HEAD(&okeys->keyShares); + cur_p != &okeys->keyShares; + cur_p = PR_NEXT_LINK(cur_p)) { + TLS13KeyShareEntry *copy = tls13_CopyKeyShareEntry( + (TLS13KeyShareEntry *)cur_p); + if (!copy) { + goto loser; + } + PR_APPEND_LINK(©->link, &nkeys->keyShares); + } + if (okeys->privKey) { + nkeys->privKey = ssl_CopyEphemeralKeyPair(okeys->privKey); + if (!nkeys->privKey) { + goto loser; + } + } + rv = SECITEM_CopyItem(NULL, &nkeys->suites, &okeys->suites); + if (rv != SECSuccess) { + goto loser; + } + nkeys->paddedLength = okeys->paddedLength; + nkeys->notBefore = okeys->notBefore; + nkeys->notAfter = okeys->notAfter; + return nkeys; + +loser: + tls13_DestroyESNIKeys(nkeys); + return NULL; +} + +/* Checksum is a 4-byte array. */ +static SECStatus +tls13_ComputeESNIKeysChecksum(const PRUint8 *buf, unsigned int len, + PRUint8 *checksum) +{ + SECItem copy; + SECStatus rv; + PRUint8 sha256[32]; + + rv = SECITEM_MakeItem(NULL, ©, buf, len); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Stomp the checksum. */ + PORT_Memset(copy.data + 2, 0, 4); + + rv = PK11_HashBuf(ssl3_HashTypeToOID(ssl_hash_sha256), + sha256, + copy.data, copy.len); + SECITEM_FreeItem(©, PR_FALSE); + if (rv != SECSuccess) { + return SECFailure; + } + PORT_Memcpy(checksum, sha256, 4); + return SECSuccess; +} + +static SECStatus +tls13_DecodeESNIKeys(SECItem *data, sslEsniKeys **keysp) +{ + SECStatus rv; + sslReadBuffer tmp; + PRUint64 tmpn; + sslEsniKeys *keys; + PRUint8 checksum[4]; + sslReader rdr = SSL_READER(data->data, data->len); + + rv = sslRead_ReadNumber(&rdr, 2, &tmpn); + if (rv != SECSuccess) { + return SECFailure; + } + if (tmpn != TLS13_ESNI_VERSION) { + PORT_SetError(SSL_ERROR_UNSUPPORTED_VERSION); + return SECFailure; + } + keys = PORT_ZNew(sslEsniKeys); + if (!keys) { + return SECFailure; + } + PR_INIT_CLIST(&keys->keyShares); + + /* Make a copy. */ + rv = SECITEM_CopyItem(NULL, &keys->data, data); + if (rv != SECSuccess) { + goto loser; + } + + rv = tls13_ComputeESNIKeysChecksum(data->data, data->len, checksum); + if (rv != SECSuccess) { + goto loser; + } + + /* Read and check checksum. */ + rv = sslRead_Read(&rdr, 4, &tmp); + if (rv != SECSuccess) { + goto loser; + } + + if (0 != NSS_SecureMemcmp(tmp.buf, checksum, 4)) { + goto loser; + } + + /* Parse the key shares. */ + rv = sslRead_ReadVariable(&rdr, 2, &tmp); + if (rv != SECSuccess) { + goto loser; + } + + sslReader rdr2 = SSL_READER(tmp.buf, tmp.len); + while (SSL_READER_REMAINING(&rdr2)) { + TLS13KeyShareEntry *ks = NULL; + + rv = tls13_DecodeKeyShareEntry(&rdr2, &ks); + if (rv != SECSuccess) { + goto loser; + } + + if (ks) { + PR_APPEND_LINK(&ks->link, &keys->keyShares); + } + } + + /* Parse cipher suites. */ + rv = sslRead_ReadVariable(&rdr, 2, &tmp); + if (rv != SECSuccess) { + goto loser; + } + /* This can't be odd. */ + if (tmp.len & 1) { + goto loser; + } + rv = SECITEM_MakeItem(NULL, &keys->suites, (PRUint8 *)tmp.buf, tmp.len); + if (rv != SECSuccess) { + goto loser; + } + + /* Padded Length */ + rv = sslRead_ReadNumber(&rdr, 2, &tmpn); + if (rv != SECSuccess) { + goto loser; + } + keys->paddedLength = (PRUint16)tmpn; + + /* Not Before */ + rv = sslRead_ReadNumber(&rdr, 8, &keys->notBefore); + if (rv != SECSuccess) { + goto loser; + } + + /* Not After */ + rv = sslRead_ReadNumber(&rdr, 8, &keys->notAfter); + if (rv != SECSuccess) { + goto loser; + } + + /* Extensions, which we ignore. */ + rv = sslRead_ReadVariable(&rdr, 2, &tmp); + if (rv != SECSuccess) { + goto loser; + } + + /* Check that this is empty. */ + if (SSL_READER_REMAINING(&rdr) > 0) { + goto loser; + } + + *keysp = keys; + return SECSuccess; + +loser: + tls13_DestroyESNIKeys(keys); + PORT_SetError(SSL_ERROR_RX_MALFORMED_ESNI_KEYS); + + return SECFailure; +} + +/* Encode an ESNI keys structure. We only allow one key + * share. */ +SECStatus +SSLExp_EncodeESNIKeys(PRUint16 *cipherSuites, unsigned int cipherSuiteCount, + SSLNamedGroup group, SECKEYPublicKey *pubKey, + PRUint16 pad, PRUint64 notBefore, PRUint64 notAfter, + PRUint8 *out, unsigned int *outlen, unsigned int maxlen) +{ + unsigned int savedOffset; + SECStatus rv; + sslBuffer b = SSL_BUFFER_EMPTY; + + rv = sslBuffer_AppendNumber(&b, TLS13_ESNI_VERSION, 2); + if (rv != SECSuccess) { + goto loser; + } + + rv = sslBuffer_Skip(&b, 4, &savedOffset); + if (rv != SECSuccess) { + goto loser; + } + + /* Length of vector. */ + rv = sslBuffer_AppendNumber( + &b, tls13_SizeOfKeyShareEntry(pubKey), 2); + if (rv != SECSuccess) { + goto loser; + } + + /* Our one key share. */ + rv = tls13_EncodeKeyShareEntry(&b, group, pubKey); + if (rv != SECSuccess) { + goto loser; + } + + /* Cipher suites. */ + rv = sslBuffer_AppendNumber(&b, cipherSuiteCount * 2, 2); + if (rv != SECSuccess) { + goto loser; + } + for (unsigned int i = 0; i < cipherSuiteCount; i++) { + rv = sslBuffer_AppendNumber(&b, cipherSuites[i], 2); + if (rv != SECSuccess) { + goto loser; + } + } + + /* Padding Length. Fixed for now. */ + rv = sslBuffer_AppendNumber(&b, pad, 2); + if (rv != SECSuccess) { + goto loser; + } + + /* Start time. */ + rv = sslBuffer_AppendNumber(&b, notBefore, 8); + if (rv != SECSuccess) { + goto loser; + } + + /* End time. */ + rv = sslBuffer_AppendNumber(&b, notAfter, 8); + if (rv != SECSuccess) { + goto loser; + } + + /* No extensions. */ + rv = sslBuffer_AppendNumber(&b, 0, 2); + if (rv != SECSuccess) { + goto loser; + } + + rv = tls13_ComputeESNIKeysChecksum(SSL_BUFFER_BASE(&b), + SSL_BUFFER_LEN(&b), + SSL_BUFFER_BASE(&b) + 2); + if (rv != SECSuccess) { + PORT_Assert(PR_FALSE); + goto loser; + } + + if (SSL_BUFFER_LEN(&b) > maxlen) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto loser; + } + PORT_Memcpy(out, SSL_BUFFER_BASE(&b), SSL_BUFFER_LEN(&b)); + *outlen = SSL_BUFFER_LEN(&b); + + sslBuffer_Clear(&b); + return SECSuccess; +loser: + sslBuffer_Clear(&b); + return SECFailure; +} + +SECStatus +SSLExp_SetESNIKeyPair(PRFileDesc *fd, + SECKEYPrivateKey *privKey, + const PRUint8 *record, unsigned int recordLen) +{ + sslSocket *ss; + SECStatus rv; + sslEsniKeys *keys = NULL; + SECKEYPublicKey *pubKey = NULL; + SECItem data = { siBuffer, CONST_CAST(PRUint8, record), recordLen }; + PLArenaPool *arena = NULL; + + ss = ssl_FindSocket(fd); + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in %s", + SSL_GETPID(), fd, __FUNCTION__)); + return SECFailure; + } + + rv = tls13_DecodeESNIKeys(&data, &keys); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Check the cipher suites. */ + (void)ssl3_config_match_init(ss); + /* Make sure the cipher suite is OK. */ + SSLVersionRange vrange = { SSL_LIBRARY_VERSION_TLS_1_3, + SSL_LIBRARY_VERSION_TLS_1_3 }; + + sslReader csrdr = SSL_READER(keys->suites.data, + keys->suites.len); + while (SSL_READER_REMAINING(&csrdr)) { + PRUint64 asuite; + + rv = sslRead_ReadNumber(&csrdr, 2, &asuite); + if (rv != SECSuccess) { + goto loser; + } + const ssl3CipherSuiteCfg *suiteCfg = + ssl_LookupCipherSuiteCfg(asuite, ss->cipherSuites); + if (!ssl3_config_match(suiteCfg, ss->ssl3.policy, &vrange, ss)) { + /* Illegal suite. */ + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto loser; + } + } + + if (PR_CLIST_IS_EMPTY(&keys->keyShares)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto loser; + } + if (PR_PREV_LINK(&keys->keyShares) != PR_NEXT_LINK(&keys->keyShares)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto loser; + } + TLS13KeyShareEntry *entry = (TLS13KeyShareEntry *)PR_LIST_HEAD( + &keys->keyShares); + if (entry->group->keaType != ssl_kea_ecdh) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + goto loser; + } + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + goto loser; + } + pubKey = PORT_ArenaZNew(arena, SECKEYPublicKey); + if (!pubKey) { + goto loser; + } + pubKey->arena = arena; + arena = NULL; /* From here, this will be destroyed with the pubkey. */ + /* Dummy PKCS11 values because this key isn't on a slot. */ + pubKey->pkcs11Slot = NULL; + pubKey->pkcs11ID = CK_INVALID_HANDLE; + rv = ssl_ImportECDHKeyShare(pubKey, + entry->key_exchange.data, + entry->key_exchange.len, + entry->group); + if (rv != SECSuccess) { + goto loser; + } + + privKey = SECKEY_CopyPrivateKey(privKey); + if (!privKey) { + goto loser; + } + keys->privKey = ssl_NewEphemeralKeyPair(entry->group, privKey, pubKey); + if (!keys->privKey) { + goto loser; + } + pubKey = NULL; + ss->esniKeys = keys; + return SECSuccess; + +loser: + PORT_FreeArena(arena, PR_FALSE); + SECKEY_DestroyPublicKey(pubKey); + tls13_DestroyESNIKeys(keys); + return SECFailure; +} + +SECStatus +SSLExp_EnableESNI(PRFileDesc *fd, + const PRUint8 *esniKeys, + unsigned int esniKeysLen, + const char *dummySNI) +{ + sslSocket *ss; + sslEsniKeys *keys = NULL; + SECItem data = { siBuffer, CONST_CAST(PRUint8, esniKeys), esniKeysLen }; + SECStatus rv; + + ss = ssl_FindSocket(fd); + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in %s", + SSL_GETPID(), fd, __FUNCTION__)); + return SECFailure; + } + + rv = tls13_DecodeESNIKeys(&data, &keys); + if (rv != SECSuccess) { + return SECFailure; + } + + if (dummySNI) { + keys->dummySni = PORT_Strdup(dummySNI); + if (!keys->dummySni) { + tls13_DestroyESNIKeys(keys); + return SECFailure; + } + } + + /* Delete in case it was set before. */ + tls13_DestroyESNIKeys(ss->esniKeys); + ss->esniKeys = keys; + + return SECSuccess; +} + +/* + * struct { + * opaque record_digest<0..2^16-1>; + * KeyShareEntry esni_key_share; + * Random client_hello_random; + * } ESNIContents; + */ +SECStatus +tls13_ComputeESNIKeys(const sslSocket *ss, + TLS13KeyShareEntry *entry, + sslKeyPair *keyPair, + const ssl3CipherSuiteDef *suite, + const PRUint8 *esniKeysHash, + const PRUint8 *keyShareBuf, + unsigned int keyShareBufLen, + const PRUint8 *clientRandom, + ssl3KeyMaterial *keyMat) +{ + PK11SymKey *Z = NULL; + PK11SymKey *Zx = NULL; + SECStatus ret = SECFailure; + PRUint8 esniContentsBuf[256]; /* Just big enough. */ + sslBuffer esniContents = SSL_BUFFER(esniContentsBuf); + PRUint8 hash[64]; + const ssl3BulkCipherDef *cipherDef = ssl_GetBulkCipherDef(suite); + size_t keySize = cipherDef->key_size; + size_t ivSize = cipherDef->iv_size + + cipherDef->explicit_nonce_size; /* This isn't always going to + * work, but it does for + * AES-GCM */ + unsigned int hashSize = tls13_GetHashSizeForHash(suite->prf_hash); + SECStatus rv; + + rv = tls13_HandleKeyShare(CONST_CAST(sslSocket, ss), entry, keyPair, + suite->prf_hash, &Z); + if (rv != SECSuccess) { + goto loser; + } + rv = tls13_HkdfExtract(NULL, Z, suite->prf_hash, &Zx); + if (rv != SECSuccess) { + goto loser; + } + + /* Encode ESNIContents. */ + rv = sslBuffer_AppendVariable(&esniContents, + esniKeysHash, hashSize, 2); + if (rv != SECSuccess) { + goto loser; + } + rv = sslBuffer_Append(&esniContents, keyShareBuf, keyShareBufLen); + if (rv != SECSuccess) { + goto loser; + } + rv = sslBuffer_Append(&esniContents, clientRandom, SSL3_RANDOM_LENGTH); + if (rv != SECSuccess) { + goto loser; + } + + PORT_Assert(hashSize <= sizeof(hash)); + rv = PK11_HashBuf(ssl3_HashTypeToOID(suite->prf_hash), + hash, + SSL_BUFFER_BASE(&esniContents), + SSL_BUFFER_LEN(&esniContents)); + ; + if (rv != SECSuccess) { + goto loser; + } + + rv = tls13_HkdfExpandLabel(Zx, suite->prf_hash, + hash, hashSize, + kHkdfPurposeEsniKey, strlen(kHkdfPurposeEsniKey), + ssl3_Alg2Mech(cipherDef->calg), + keySize, + &keyMat->key); + if (rv != SECSuccess) { + goto loser; + } + rv = tls13_HkdfExpandLabelRaw(Zx, suite->prf_hash, + hash, hashSize, + kHkdfPurposeEsniIv, strlen(kHkdfPurposeEsniIv), + keyMat->iv, ivSize); + if (rv != SECSuccess) { + goto loser; + } + + ret = SECSuccess; + +loser: + PK11_FreeSymKey(Z); + PK11_FreeSymKey(Zx); + return ret; +} + +/* Set up ESNI. This generates a private key as a side effect. */ +SECStatus +tls13_ClientSetupESNI(sslSocket *ss) +{ + ssl3CipherSuite suite; + sslEphemeralKeyPair *keyPair; + size_t i; + PRCList *cur; + SECStatus rv; + TLS13KeyShareEntry *share; + const sslNamedGroupDef *group = NULL; + PRTime now = PR_Now() / PR_USEC_PER_SEC; + + if (!ss->esniKeys) { + return SECSuccess; + } + + if ((ss->esniKeys->notBefore > now) || (ss->esniKeys->notAfter < now)) { + return SECSuccess; + } + + /* If we're not sending SNI, don't send ESNI. */ + if (!ssl_ShouldSendSNIExtension(ss, ss->url)) { + return SECSuccess; + } + + /* Pick the group. */ + for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) { + for (cur = PR_NEXT_LINK(&ss->esniKeys->keyShares); + cur != &ss->esniKeys->keyShares; + cur = PR_NEXT_LINK(cur)) { + if (!ss->namedGroupPreferences[i]) { + continue; + } + share = (TLS13KeyShareEntry *)cur; + if (share->group->name == ss->namedGroupPreferences[i]->name) { + group = ss->namedGroupPreferences[i]; + break; + } + } + } + + if (!group) { + /* No compatible group. */ + return SECSuccess; + } + + rv = ssl3_NegotiateCipherSuiteInner(ss, &ss->esniKeys->suites, + SSL_LIBRARY_VERSION_TLS_1_3, &suite); + if (rv != SECSuccess) { + return SECSuccess; + } + + rv = tls13_CreateKeyShare(ss, group, &keyPair); + if (rv != SECSuccess) { + return SECFailure; + } + + ss->xtnData.esniPrivateKey = keyPair; + ss->xtnData.esniSuite = suite; + ss->xtnData.peerEsniShare = share; + + return SECSuccess; +} + +/* + * struct { + * CipherSuite suite; + * KeyShareEntry key_share; + * opaque record_digest<0..2^16-1>; + * opaque encrypted_sni<0..2^16-1>; + * } ClientEncryptedSNI; + * + * struct { + * ServerNameList sni; + * opaque zeros[ESNIKeys.padded_length - length(sni)]; + * } PaddedServerNameList; + * + * struct { + * uint8 nonce[16]; + * PaddedServerNameList realSNI; + * } ClientESNIInner; + */ +SECStatus +tls13_FormatEsniAADInput(sslBuffer *aadInput, + PRUint8 *keyShare, unsigned int keyShareLen) +{ + SECStatus rv; + + /* 8 bytes of 0 for the sequence number. */ + rv = sslBuffer_AppendNumber(aadInput, 0, 8); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Key share. */ + PORT_Assert(keyShareLen > 0); + rv = sslBuffer_Append(aadInput, keyShare, keyShareLen); + if (rv != SECSuccess) { + return SECFailure; + } + + return SECSuccess; +} + +static SECStatus +tls13_ServerGetEsniAEAD(const sslSocket *ss, PRUint64 suite, + const ssl3CipherSuiteDef **suiteDefp, + SSLAEADCipher *aeadp) +{ + SECStatus rv; + const ssl3CipherSuiteDef *suiteDef; + SSLAEADCipher aead; + + /* Check against the suite list for ESNI */ + PRBool csMatch = PR_FALSE; + sslReader csrdr = SSL_READER(ss->esniKeys->suites.data, + ss->esniKeys->suites.len); + while (SSL_READER_REMAINING(&csrdr)) { + PRUint64 asuite; + + rv = sslRead_ReadNumber(&csrdr, 2, &asuite); + if (rv != SECSuccess) { + return SECFailure; + } + if (asuite == suite) { + csMatch = PR_TRUE; + break; + } + } + if (!csMatch) { + return SECFailure; + } + + suiteDef = ssl_LookupCipherSuiteDef(suite); + PORT_Assert(suiteDef); + if (!suiteDef) { + return SECFailure; + } + aead = tls13_GetAead(ssl_GetBulkCipherDef(suiteDef)); + if (!aead) { + return SECFailure; + } + + *suiteDefp = suiteDef; + *aeadp = aead; + return SECSuccess; +} + +SECStatus +tls13_ServerDecryptEsniXtn(const sslSocket *ss, PRUint8 *in, unsigned int inLen, + PRUint8 *out, int *outLen, int maxLen) +{ + sslReader rdr = SSL_READER(in, inLen); + PRUint64 suite; + const ssl3CipherSuiteDef *suiteDef; + SSLAEADCipher aead = NULL; + TLSExtension *keyShareExtension; + TLS13KeyShareEntry *entry = NULL; + ssl3KeyMaterial keyMat = { NULL }; + + sslBuffer aadInput = SSL_BUFFER_EMPTY; + const PRUint8 *keyShareBuf; + sslReadBuffer buf; + unsigned int keyShareBufLen; + PRUint8 hash[64]; + SECStatus rv; + + /* Read the cipher suite. */ + rv = sslRead_ReadNumber(&rdr, 2, &suite); + if (rv != SECSuccess) { + goto loser; + } + + /* Find the AEAD */ + rv = tls13_ServerGetEsniAEAD(ss, suite, &suiteDef, &aead); + if (rv != SECSuccess) { + goto loser; + } + + /* Note where the KeyShare starts. */ + keyShareBuf = SSL_READER_CURRENT(&rdr); + rv = tls13_DecodeKeyShareEntry(&rdr, &entry); + if (rv != SECSuccess) { + goto loser; + } + keyShareBufLen = SSL_READER_CURRENT(&rdr) - keyShareBuf; + if (!entry || entry->group->name != ss->esniKeys->privKey->group->name) { + goto loser; + } + + /* The hash of the ESNIKeys structure. */ + rv = sslRead_ReadVariable(&rdr, 2, &buf); + if (rv != SECSuccess) { + goto loser; + } + + /* Check that the hash matches. */ + unsigned int hashLen = tls13_GetHashSizeForHash(suiteDef->prf_hash); + PORT_Assert(hashLen <= sizeof(hash)); + rv = PK11_HashBuf(ssl3_HashTypeToOID(suiteDef->prf_hash), + hash, + ss->esniKeys->data.data, ss->esniKeys->data.len); + if (rv != SECSuccess) { + goto loser; + } + + if (buf.len != hashLen) { + /* This is malformed. */ + goto loser; + } + if (0 != NSS_SecureMemcmp(hash, buf.buf, hashLen)) { + goto loser; + } + + rv = tls13_ComputeESNIKeys(ss, entry, + ss->esniKeys->privKey->keys, + suiteDef, + hash, keyShareBuf, keyShareBufLen, + ((sslSocket *)ss)->ssl3.hs.client_random, + &keyMat); + if (rv != SECSuccess) { + goto loser; + } + + /* Read the ciphertext. */ + rv = sslRead_ReadVariable(&rdr, 2, &buf); + if (rv != SECSuccess) { + goto loser; + } + + /* Check that this is empty. */ + if (SSL_READER_REMAINING(&rdr) > 0) { + goto loser; + } + + /* Find the key share extension. */ + keyShareExtension = ssl3_FindExtension(CONST_CAST(sslSocket, ss), + ssl_tls13_key_share_xtn); + if (!keyShareExtension) { + goto loser; + } + rv = tls13_FormatEsniAADInput(&aadInput, + keyShareExtension->data.data, + keyShareExtension->data.len); + if (rv != SECSuccess) { + goto loser; + } + + rv = aead(&keyMat, PR_TRUE /* Decrypt */, + out, outLen, maxLen, + buf.buf, buf.len, + SSL_BUFFER_BASE(&aadInput), + SSL_BUFFER_LEN(&aadInput)); + sslBuffer_Clear(&aadInput); + if (rv != SECSuccess) { + goto loser; + } + + ssl_DestroyKeyMaterial(&keyMat); + tls13_DestroyKeyShareEntry(entry); + return SECSuccess; + +loser: + FATAL_ERROR(CONST_CAST(sslSocket, ss), SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION, illegal_parameter); + ssl_DestroyKeyMaterial(&keyMat); /* Safe because zeroed. */ + if (entry) { + tls13_DestroyKeyShareEntry(entry); + } + return SECFailure; +} diff --git a/security/nss/lib/ssl/tls13esni.h b/security/nss/lib/ssl/tls13esni.h new file mode 100644 index 000000000..6c52c9952 --- /dev/null +++ b/security/nss/lib/ssl/tls13esni.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is PRIVATE to SSL. + * + * 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 __tls13esni_h_ +#define __tls13esni_h_ + +struct sslEsniKeysStr { + SECItem data; /* The encoded record. */ + sslEphemeralKeyPair *privKey; + const char *dummySni; + PRCList keyShares; /* List of TLS13KeyShareEntry */ + SECItem suites; + PRUint16 paddedLength; + PRUint64 notBefore; + PRUint64 notAfter; +}; + +SECStatus SSLExp_SetESNIKeyPair(PRFileDesc *fd, + SECKEYPrivateKey *privKey, + const PRUint8 *record, unsigned int recordLen); + +SECStatus SSLExp_EnableESNI(PRFileDesc *fd, const PRUint8 *esniKeys, + unsigned int esniKeysLen, const char *dummySNI); +SECStatus SSLExp_EncodeESNIKeys(PRUint16 *cipherSuites, unsigned int cipherSuiteCount, + SSLNamedGroup group, SECKEYPublicKey *pubKey, + PRUint16 pad, PRUint64 notBefore, PRUint64 notAfter, + PRUint8 *out, unsigned int *outlen, unsigned int maxlen); +sslEsniKeys *tls13_CopyESNIKeys(sslEsniKeys *okeys); +void tls13_DestroyESNIKeys(sslEsniKeys *keys); +SECStatus tls13_ClientSetupESNI(sslSocket *ss); +SECStatus tls13_ComputeESNIKeys(const sslSocket *ss, + TLS13KeyShareEntry *entry, + sslKeyPair *keyPair, + const ssl3CipherSuiteDef *suite, + const PRUint8 *esniKeysHash, + const PRUint8 *keyShareBuf, + unsigned int keyShareBufLen, + const PRUint8 *clientRandom, + ssl3KeyMaterial *keyMat); +SECStatus tls13_FormatEsniAADInput(sslBuffer *aadInput, + PRUint8 *keyShare, unsigned int keyShareLen); + +SECStatus tls13_ServerDecryptEsniXtn(const sslSocket *ss, PRUint8 *in, unsigned int inLen, + PRUint8 *out, int *outLen, int maxLen); + +#endif diff --git a/security/nss/lib/ssl/tls13exthandle.c b/security/nss/lib/ssl/tls13exthandle.c index 1ab8a8e59..8ed18f69c 100644 --- a/security/nss/lib/ssl/tls13exthandle.c +++ b/security/nss/lib/ssl/tls13exthandle.c @@ -12,6 +12,7 @@ #include "pk11pub.h" #include "ssl3ext.h" #include "ssl3exthandle.h" +#include "tls13esni.h" #include "tls13exthandle.h" SECStatus @@ -71,7 +72,7 @@ tls13_ServerSendStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData, * * opaque point <1..2^8-1>; */ -static PRUint32 +PRUint32 tls13_SizeOfKeyShareEntry(const SECKEYPublicKey *pubKey) { /* Size = NamedGroup(2) + length(2) + opaque<?> share */ @@ -86,14 +87,14 @@ tls13_SizeOfKeyShareEntry(const SECKEYPublicKey *pubKey) return 0; } -static SECStatus -tls13_EncodeKeyShareEntry(sslBuffer *buf, const sslEphemeralKeyPair *keyPair) +SECStatus +tls13_EncodeKeyShareEntry(sslBuffer *buf, SSLNamedGroup group, + SECKEYPublicKey *pubKey) { SECStatus rv; - SECKEYPublicKey *pubKey = keyPair->keys->pubKey; unsigned int size = tls13_SizeOfKeyShareEntry(pubKey); - rv = sslBuffer_AppendNumber(buf, keyPair->group->name, 2); + rv = sslBuffer_AppendNumber(buf, group, 2); if (rv != SECSuccess) return rv; rv = sslBuffer_AppendNumber(buf, size - 4, 2); @@ -123,6 +124,7 @@ tls13_ClientSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, { SECStatus rv; PRCList *cursor; + unsigned int extStart; unsigned int lengthOffset; if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) { @@ -134,6 +136,8 @@ tls13_ClientSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, SSL_TRC(3, ("%d: TLS13[%d]: send client key share xtn", SSL_GETPID(), ss->fd)); + extStart = SSL_BUFFER_LEN(buf); + /* Save the offset to the length. */ rv = sslBuffer_Skip(buf, 2, &lengthOffset); if (rv != SECSuccess) { @@ -144,7 +148,9 @@ tls13_ClientSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, cursor != &ss->ephemeralKeyPairs; cursor = PR_NEXT_LINK(cursor)) { sslEphemeralKeyPair *keyPair = (sslEphemeralKeyPair *)cursor; - rv = tls13_EncodeKeyShareEntry(buf, keyPair); + rv = tls13_EncodeKeyShareEntry(buf, + keyPair->group->name, + keyPair->keys->pubKey); if (rv != SECSuccess) { return SECFailure; } @@ -154,50 +160,62 @@ tls13_ClientSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, return SECFailure; } + rv = SECITEM_MakeItem(NULL, &xtnData->keyShareExtension, + SSL_BUFFER_BASE(buf) + extStart, + SSL_BUFFER_LEN(buf) - extStart); + if (rv != SECSuccess) { + return SECFailure; + } + *added = PR_TRUE; return SECSuccess; } -static SECStatus -tls13_HandleKeyShareEntry(const sslSocket *ss, TLSExtensionData *xtnData, SECItem *data) +SECStatus +tls13_DecodeKeyShareEntry(sslReader *rdr, TLS13KeyShareEntry **ksp) { SECStatus rv; - PRUint32 group; + PRUint64 group; const sslNamedGroupDef *groupDef; TLS13KeyShareEntry *ks = NULL; - SECItem share = { siBuffer, NULL, 0 }; + sslReadBuffer share; - rv = ssl3_ExtConsumeHandshakeNumber(ss, &group, 2, &data->data, &data->len); + rv = sslRead_ReadNumber(rdr, 2, &group); if (rv != SECSuccess) { - PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE); goto loser; } groupDef = ssl_LookupNamedGroup(group); - rv = ssl3_ExtConsumeHandshakeVariable(ss, &share, 2, &data->data, - &data->len); + rv = sslRead_ReadVariable(rdr, 2, &share); if (rv != SECSuccess) { goto loser; } + + /* This has to happen here because we want to consume + * the entire entry even if the group is unknown + * or disabled. */ /* If the group is disabled, continue. */ if (!groupDef) { return SECSuccess; } ks = PORT_ZNew(TLS13KeyShareEntry); - if (!ks) + if (!ks) { goto loser; + } ks->group = groupDef; - rv = SECITEM_CopyItem(NULL, &ks->key_exchange, &share); - if (rv != SECSuccess) + rv = SECITEM_MakeItem(NULL, &ks->key_exchange, + share.buf, share.len); + if (rv != SECSuccess) { goto loser; + } - PR_APPEND_LINK(&ks->link, &xtnData->remoteKeyShares); + *ksp = ks; return SECSuccess; loser: - if (ks) - tls13_DestroyKeyShareEntry(ks); + tls13_DestroyKeyShareEntry(ks); + return SECFailure; } /* Handle an incoming KeyShare extension at the client and copy to @@ -209,6 +227,7 @@ tls13_ClientHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, { SECStatus rv; PORT_Assert(PR_CLIST_IS_EMPTY(&xtnData->remoteKeyShares)); + TLS13KeyShareEntry *ks = NULL; PORT_Assert(!ss->sec.isServer); @@ -221,16 +240,20 @@ tls13_ClientHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension", SSL_GETPID(), ss->fd)); - rv = tls13_HandleKeyShareEntry(ss, xtnData, data); - if (rv != SECSuccess) { + sslReader rdr = SSL_READER(data->data, data->len); + rv = tls13_DecodeKeyShareEntry(&rdr, &ks); + if ((rv != SECSuccess) || !ks) { + ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter); PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE); return SECFailure; } - if (data->len) { + if (SSL_READER_REMAINING(&rdr)) { + tls13_DestroyKeyShareEntry(ks); PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE); return SECFailure; } + PR_APPEND_LINK(&ks->link, &xtnData->remoteKeyShares); return SECSuccess; } @@ -273,7 +296,7 @@ tls13_ClientHandleKeyShareXtnHrr(const sslSocket *ss, TLSExtensionData *xtnData, ssl_FreeEphemeralKeyPairs(CONST_CAST(sslSocket, ss)); /* And replace with our new share. */ - rv = tls13_CreateKeyShare(CONST_CAST(sslSocket, ss), group); + rv = tls13_AddKeyShare(CONST_CAST(sslSocket, ss), group); if (rv != SECSuccess) { ssl3_ExtSendAlert(ss, alert_fatal, internal_error); PORT_SetError(SEC_ERROR_KEYGEN_FAIL); @@ -315,12 +338,24 @@ tls13_ServerHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, goto loser; } - while (data->len) { - rv = tls13_HandleKeyShareEntry(ss, xtnData, data); - if (rv != SECSuccess) + sslReader rdr = SSL_READER(data->data, data->len); + while (SSL_READER_REMAINING(&rdr)) { + TLS13KeyShareEntry *ks = NULL; + rv = tls13_DecodeKeyShareEntry(&rdr, &ks); + if (rv != SECSuccess) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE); goto loser; + } + if (ks) { + /* |ks| == NULL if this is an unknown group. */ + PR_APPEND_LINK(&ks->link, &xtnData->remoteKeyShares); + } } + /* Keep track of negotiated extensions. */ + xtnData->negotiated[xtnData->numNegotiated++] = + ssl_tls13_key_share_xtn; + return SECSuccess; loser: @@ -342,7 +377,8 @@ tls13_ServerSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData, keyPair = (sslEphemeralKeyPair *)PR_NEXT_LINK(&ss->ephemeralKeyPairs); - rv = tls13_EncodeKeyShareEntry(buf, keyPair); + rv = tls13_EncodeKeyShareEntry(buf, keyPair->group->name, + keyPair->keys->pubKey); if (rv != SECSuccess) { return SECFailure; } @@ -396,6 +432,7 @@ tls13_ClientSendPreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData, xtnData->lastXtnOffset = buf->len - 4; PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3); + PORT_Assert(ss->sec.ci.sid->version >= SSL_LIBRARY_VERSION_TLS_1_3); /* Send a single ticket identity. */ session_ticket = &ss->sec.ci.sid->u.ssl3.locked.sessionTicket; @@ -751,7 +788,9 @@ tls13_ClientSendSupportedVersionsXtn(const sslSocket *ss, TLSExtensionData *xtnD } for (version = ss->vrange.max; version >= ss->vrange.min; --version) { - rv = sslBuffer_AppendNumber(buf, tls13_EncodeDraftVersion(version), 2); + PRUint16 wire = tls13_EncodeDraftVersion(version, + ss->protocolVariant); + rv = sslBuffer_AppendNumber(buf, wire, 2); if (rv != SECSuccess) { return SECFailure; } @@ -779,8 +818,9 @@ tls13_ServerSendSupportedVersionsXtn(const sslSocket *ss, TLSExtensionData *xtnD SSL_TRC(3, ("%d: TLS13[%d]: server send supported_versions extension", SSL_GETPID(), ss->fd)); - rv = sslBuffer_AppendNumber( - buf, tls13_EncodeDraftVersion(SSL_LIBRARY_VERSION_TLS_1_3), 2); + PRUint16 ver = tls13_EncodeDraftVersion(SSL_LIBRARY_VERSION_TLS_1_3, + ss->protocolVariant); + rv = sslBuffer_AppendNumber(buf, ver, 2); if (rv != SECSuccess) { return SECFailure; } @@ -1056,3 +1096,276 @@ tls13_ServerSendHrrCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData, *added = PR_TRUE; return SECSuccess; } + +SECStatus +tls13_ClientSendEsniXtn(const sslSocket *ss, TLSExtensionData *xtnData, + sslBuffer *buf, PRBool *added) +{ + SECStatus rv; + PRUint8 sniBuf[1024]; + PRUint8 hash[64]; + sslBuffer sni = SSL_BUFFER(sniBuf); + const ssl3CipherSuiteDef *suiteDef; + ssl3KeyMaterial keyMat; + SSLAEADCipher aead; + PRUint8 outBuf[1024]; + int outLen; + unsigned int sniStart; + unsigned int sniLen; + sslBuffer aadInput = SSL_BUFFER_EMPTY; + unsigned int keyShareBufStart; + unsigned int keyShareBufLen; + + PORT_Memset(&keyMat, 0, sizeof(keyMat)); + + if (!ss->xtnData.esniPrivateKey) { + return SECSuccess; + } + + /* nonce */ + rv = PK11_GenerateRandom( + (unsigned char *)xtnData->esniNonce, sizeof(xtnData->esniNonce)); + if (rv != SECSuccess) { + return SECFailure; + } + rv = sslBuffer_Append(&sni, xtnData->esniNonce, sizeof(xtnData->esniNonce)); + if (rv != SECSuccess) { + return SECFailure; + } + + /* sni */ + sniStart = SSL_BUFFER_LEN(&sni); + rv = ssl3_ClientFormatServerNameXtn(ss, ss->url, xtnData, &sni); + if (rv != SECSuccess) { + return SECFailure; + } + + sniLen = SSL_BUFFER_LEN(&sni) - sniStart; + /* Padding. */ + if (ss->esniKeys->paddedLength > sniLen) { + unsigned int paddingRequired = ss->esniKeys->paddedLength - sniLen; + while (paddingRequired--) { + rv = sslBuffer_AppendNumber(&sni, 0, 1); + if (rv != SECSuccess) { + return SECFailure; + } + } + } + + suiteDef = ssl_LookupCipherSuiteDef(xtnData->esniSuite); + PORT_Assert(suiteDef); + if (!suiteDef) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + aead = tls13_GetAead(ssl_GetBulkCipherDef(suiteDef)); + if (!aead) { + return SECFailure; + } + + /* Format the first part of the extension so we have the + * encoded KeyShareEntry. */ + rv = sslBuffer_AppendNumber(buf, xtnData->esniSuite, 2); + if (rv != SECSuccess) { + return SECFailure; + } + keyShareBufStart = SSL_BUFFER_LEN(buf); + rv = tls13_EncodeKeyShareEntry(buf, + xtnData->esniPrivateKey->group->name, + xtnData->esniPrivateKey->keys->pubKey); + if (rv != SECSuccess) { + return SECFailure; + } + keyShareBufLen = SSL_BUFFER_LEN(buf) - keyShareBufStart; + + if (tls13_GetHashSizeForHash(suiteDef->prf_hash) > sizeof(hash)) { + PORT_Assert(PR_FALSE); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + rv = PK11_HashBuf(ssl3_HashTypeToOID(suiteDef->prf_hash), + hash, + ss->esniKeys->data.data, + ss->esniKeys->data.len); + if (rv != SECSuccess) { + PORT_Assert(PR_FALSE); + return SECFailure; + } + + rv = sslBuffer_AppendVariable(buf, hash, + tls13_GetHashSizeForHash(suiteDef->prf_hash), 2); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Compute the ESNI keys. */ + rv = tls13_ComputeESNIKeys(ss, xtnData->peerEsniShare, + xtnData->esniPrivateKey->keys, + suiteDef, + hash, + SSL_BUFFER_BASE(buf) + keyShareBufStart, + keyShareBufLen, + CONST_CAST(PRUint8, ss->ssl3.hs.client_random), + &keyMat); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = tls13_FormatEsniAADInput(&aadInput, + xtnData->keyShareExtension.data, + xtnData->keyShareExtension.len); + if (rv != SECSuccess) { + ssl_DestroyKeyMaterial(&keyMat); + return SECFailure; + } + /* Now encrypt. */ + rv = aead(&keyMat, PR_FALSE /* Encrypt */, + outBuf, &outLen, sizeof(outBuf), + SSL_BUFFER_BASE(&sni), + SSL_BUFFER_LEN(&sni), + SSL_BUFFER_BASE(&aadInput), + SSL_BUFFER_LEN(&aadInput)); + ssl_DestroyKeyMaterial(&keyMat); + sslBuffer_Clear(&aadInput); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Encode the rest. */ + rv = sslBuffer_AppendVariable(buf, outBuf, outLen, 2); + if (rv != SECSuccess) { + return SECFailure; + } + + *added = PR_TRUE; + return SECSuccess; +} + +static SECStatus +tls13_ServerSendEsniXtn(const sslSocket *ss, TLSExtensionData *xtnData, + sslBuffer *buf, PRBool *added) +{ + SECStatus rv; + + rv = sslBuffer_Append(buf, xtnData->esniNonce, sizeof(xtnData->esniNonce)); + if (rv != SECSuccess) { + return SECFailure; + } + + *added = PR_TRUE; + return SECSuccess; +} + +SECStatus +tls13_ServerHandleEsniXtn(const sslSocket *ss, TLSExtensionData *xtnData, + SECItem *data) +{ + sslReadBuffer buf; + PRUint8 *plainText = NULL; + int ptLen; + SECStatus rv; + + /* If we are doing < TLS 1.3, then ignore this. */ + if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { + return SECSuccess; + } + + if (!ss->esniKeys) { + /* Apparently we used to be configured for ESNI, but + * no longer. This violates the spec, or the client is + * broken. */ + return SECFailure; + } + + plainText = PORT_ZAlloc(data->len); + if (!plainText) { + return SECFailure; + } + rv = tls13_ServerDecryptEsniXtn(ss, data->data, data->len, + plainText, &ptLen, data->len); + if (rv) { + goto loser; + } + + /* Read out the interior extension. */ + sslReader sniRdr = SSL_READER(plainText, ptLen); + + rv = sslRead_Read(&sniRdr, sizeof(xtnData->esniNonce), &buf); + if (rv != SECSuccess) { + goto loser; + } + PORT_Memcpy(xtnData->esniNonce, buf.buf, sizeof(xtnData->esniNonce)); + + /* We need to capture the whole block with the length. */ + SECItem sniItem = { siBuffer, (unsigned char *)SSL_READER_CURRENT(&sniRdr), 0 }; + rv = sslRead_ReadVariable(&sniRdr, 2, &buf); + if (rv != SECSuccess) { + goto loser; + } + sniItem.len = buf.len + 2; + + /* Check the padding. Note we don't need to do this in constant time + * because it's inside the AEAD boundary. */ + /* TODO(ekr@rtfm.com): check that the padding is the right length. */ + PRUint64 tmp; + while (SSL_READER_REMAINING(&sniRdr)) { + rv = sslRead_ReadNumber(&sniRdr, 1, &tmp); + if (rv != SECSuccess) { + goto loser; + } + if (tmp != 0) { + goto loser; + } + } + + rv = ssl3_HandleServerNameXtn(ss, xtnData, &sniItem); + if (rv != SECSuccess) { + goto loser; + } + + rv = ssl3_RegisterExtensionSender(ss, xtnData, + ssl_tls13_encrypted_sni_xtn, + tls13_ServerSendEsniXtn); + if (rv != SECSuccess) { + goto loser; + } + + /* Keep track of negotiated extensions. */ + xtnData->negotiated[xtnData->numNegotiated++] = + ssl_tls13_encrypted_sni_xtn; + + PORT_ZFree(plainText, data->len); + return SECSuccess; +loser: + PORT_ZFree(plainText, data->len); + return SECFailure; +} + +/* Function to check the extension. We don't install a handler here + * because we need to check for the presence of the extension as + * well and it's easier to do it in one place. */ +SECStatus +tls13_ClientCheckEsniXtn(sslSocket *ss) +{ + TLSExtension *esniExtension = + ssl3_FindExtension(ss, ssl_tls13_encrypted_sni_xtn); + if (!esniExtension) { + FATAL_ERROR(ss, SSL_ERROR_MISSING_ESNI_EXTENSION, missing_extension); + return SECFailure; + } + + if (esniExtension->data.len != sizeof(ss->xtnData.esniNonce)) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION, illegal_parameter); + return SECFailure; + } + + if (0 != NSS_SecureMemcmp(esniExtension->data.data, + ss->xtnData.esniNonce, + sizeof(ss->xtnData.esniNonce))) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION, illegal_parameter); + return SECFailure; + } + + return SECSuccess; +} diff --git a/security/nss/lib/ssl/tls13exthandle.h b/security/nss/lib/ssl/tls13exthandle.h index edce94d83..dd64b44ff 100644 --- a/security/nss/lib/ssl/tls13exthandle.h +++ b/security/nss/lib/ssl/tls13exthandle.h @@ -84,5 +84,14 @@ SECStatus tls13_ServerSendHrrKeyShareXtn(const sslSocket *ss, SECStatus tls13_ServerSendHrrCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData, sslBuffer *buf, PRBool *added); +SECStatus tls13_DecodeKeyShareEntry(sslReader *rdr, TLS13KeyShareEntry **ksp); +PRUint32 tls13_SizeOfKeyShareEntry(const SECKEYPublicKey *pubKey); +SECStatus tls13_EncodeKeyShareEntry(sslBuffer *buf, SSLNamedGroup group, + SECKEYPublicKey *pubKey); +SECStatus tls13_ClientSendEsniXtn(const sslSocket *ss, TLSExtensionData *xtnData, + sslBuffer *buf, PRBool *added); +SECStatus tls13_ServerHandleEsniXtn(const sslSocket *ss, TLSExtensionData *xtnData, + SECItem *data); +SECStatus tls13_ClientCheckEsniXtn(sslSocket *ss); #endif diff --git a/security/nss/lib/util/nssutil.def b/security/nss/lib/util/nssutil.def index 26e438ba6..8c233f7d3 100644 --- a/security/nss/lib/util/nssutil.def +++ b/security/nss/lib/util/nssutil.def @@ -328,3 +328,9 @@ SECITEM_MakeItem; ;+ local: ;+ *; ;+}; +;+NSSUTIL_3.39 { # NSS Utilities 3.39 release +;+ global: +NSSUTIL_AddNSSFlagToModuleSpec; +;+ local: +;+ *; +;+}; diff --git a/security/nss/lib/util/nssutil.h b/security/nss/lib/util/nssutil.h index 2749abaa1..62511eafe 100644 --- a/security/nss/lib/util/nssutil.h +++ b/security/nss/lib/util/nssutil.h @@ -19,9 +19,9 @@ * The format of the version string should be * "<major version>.<minor version>[.<patch level>[.<build number>]][ <Beta>]" */ -#define NSSUTIL_VERSION "3.38" +#define NSSUTIL_VERSION "3.41" #define NSSUTIL_VMAJOR 3 -#define NSSUTIL_VMINOR 38 +#define NSSUTIL_VMINOR 41 #define NSSUTIL_VPATCH 0 #define NSSUTIL_VBUILD 0 #define NSSUTIL_BETA PR_FALSE diff --git a/security/nss/lib/util/pkcs11p.h b/security/nss/lib/util/pkcs11p.h index 2e904ee50..1c9201350 100644 --- a/security/nss/lib/util/pkcs11p.h +++ b/security/nss/lib/util/pkcs11p.h @@ -13,7 +13,10 @@ * though it's still needed. put in a central file to help merging.. */ -#if defined(_WIN32) +#if defined(_WIN32) || defined(_WINDOWS) +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wpragma-pack" +#endif #ifdef _MSC_VER #pragma warning(disable : 4103) #endif diff --git a/security/nss/lib/util/pkcs11u.h b/security/nss/lib/util/pkcs11u.h index be949bcd4..64ec2fdb5 100644 --- a/security/nss/lib/util/pkcs11u.h +++ b/security/nss/lib/util/pkcs11u.h @@ -11,7 +11,10 @@ * reset any packing set by pkcs11p.h */ -#if defined(_WIN32) +#if defined(_WIN32) || defined(_WINDOWS) +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wpragma-pack" +#endif #ifdef _MSC_VER #pragma warning(disable : 4103) #endif diff --git a/security/nss/lib/util/pkcs11uri.c b/security/nss/lib/util/pkcs11uri.c index 94b00171e..c29521080 100644 --- a/security/nss/lib/util/pkcs11uri.c +++ b/security/nss/lib/util/pkcs11uri.c @@ -674,7 +674,7 @@ PK11URI_ParseURI(const char *string) const char *p = string; SECStatus ret; - if (strncmp("pkcs11:", p, 7) != 0) { + if (PORT_Strncasecmp("pkcs11:", p, 7) != 0) { return NULL; } p += 7; diff --git a/security/nss/lib/util/pkcs1sig.c b/security/nss/lib/util/pkcs1sig.c index 502119aa5..68588c7f8 100644 --- a/security/nss/lib/util/pkcs1sig.c +++ b/security/nss/lib/util/pkcs1sig.c @@ -15,13 +15,6 @@ struct pkcs1PrefixStr { PRUint8 *data; }; -typedef struct pkcs1PrefixesStr pkcs1Prefixes; -struct pkcs1PrefixesStr { - unsigned int digestLen; - pkcs1Prefix prefixWithParams; - pkcs1Prefix prefixWithoutParams; -}; - /* The value for SGN_PKCS1_DIGESTINFO_MAX_PREFIX_LEN_EXCLUDING_OID is based on * the possible prefix encodings as explained below. */ @@ -101,9 +94,8 @@ _SGN_VerifyPKCS1DigestInfo(SECOidTag digestAlg, PRBool unsafeAllowMissingParameters) { SECOidData *hashOid; - pkcs1Prefixes pp; - const pkcs1Prefix *expectedPrefix; - SECStatus rv, rv2, rv3; + pkcs1Prefix prefix; + SECStatus rv; if (!digest || !digest->data || !dataRecoveredFromSignature || !dataRecoveredFromSignature->data) { @@ -117,17 +109,9 @@ _SGN_VerifyPKCS1DigestInfo(SECOidTag digestAlg, return SECFailure; } - pp.digestLen = digest->len; - pp.prefixWithParams.data = NULL; - pp.prefixWithoutParams.data = NULL; + prefix.data = NULL; - rv2 = encodePrefix(hashOid, pp.digestLen, &pp.prefixWithParams, PR_TRUE); - rv3 = encodePrefix(hashOid, pp.digestLen, &pp.prefixWithoutParams, PR_FALSE); - - rv = SECSuccess; - if (rv2 != SECSuccess || rv3 != SECSuccess) { - rv = SECFailure; - } + rv = encodePrefix(hashOid, digest->len, &prefix, PR_TRUE); if (rv == SECSuccess) { /* We don't attempt to avoid timing attacks on these comparisons because @@ -135,34 +119,39 @@ _SGN_VerifyPKCS1DigestInfo(SECOidTag digestAlg, * operation. */ - if (dataRecoveredFromSignature->len == - pp.prefixWithParams.len + pp.digestLen) { - expectedPrefix = &pp.prefixWithParams; - } else if (unsafeAllowMissingParameters && - dataRecoveredFromSignature->len == - pp.prefixWithoutParams.len + pp.digestLen) { - expectedPrefix = &pp.prefixWithoutParams; - } else { - PORT_SetError(SEC_ERROR_BAD_SIGNATURE); - rv = SECFailure; + if (dataRecoveredFromSignature->len != prefix.len + digest->len) { + PRBool lengthMismatch = PR_TRUE; +#ifdef NSS_PKCS1_AllowMissingParameters + if (unsafeAllowMissingParameters) { + if (prefix.data) { + PORT_Free(prefix.data); + prefix.data = NULL; + } + rv = encodePrefix(hashOid, digest->len, &prefix, PR_FALSE); + if (rv != SECSuccess || + dataRecoveredFromSignature->len == prefix.len + digest->len) { + lengthMismatch = PR_FALSE; + } + } +#endif + if (lengthMismatch) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + rv = SECFailure; + } } } if (rv == SECSuccess) { - if (memcmp(dataRecoveredFromSignature->data, expectedPrefix->data, - expectedPrefix->len) || - memcmp(dataRecoveredFromSignature->data + expectedPrefix->len, - digest->data, digest->len)) { + if (memcmp(dataRecoveredFromSignature->data, prefix.data, prefix.len) || + memcmp(dataRecoveredFromSignature->data + prefix.len, digest->data, + digest->len)) { PORT_SetError(SEC_ERROR_BAD_SIGNATURE); rv = SECFailure; } } - if (pp.prefixWithParams.data) { - PORT_Free(pp.prefixWithParams.data); - } - if (pp.prefixWithoutParams.data) { - PORT_Free(pp.prefixWithoutParams.data); + if (prefix.data) { + PORT_Free(prefix.data); } return rv; diff --git a/security/nss/lib/util/secder.h b/security/nss/lib/util/secder.h index dbc35807d..1b487d193 100644 --- a/security/nss/lib/util/secder.h +++ b/security/nss/lib/util/secder.h @@ -34,6 +34,9 @@ SEC_BEGIN_PROTOS extern SECStatus DER_Encode(PLArenaPool *arena, SECItem *dest, DERTemplate *t, void *src); +/* +** This function is deprecated. +*/ extern SECStatus DER_Lengths(SECItem *item, int *header_len_p, PRUint32 *contents_len_p); diff --git a/security/nss/lib/util/secitem.c b/security/nss/lib/util/secitem.c index 1e505a9af..cd6996178 100644 --- a/security/nss/lib/util/secitem.c +++ b/security/nss/lib/util/secitem.c @@ -76,10 +76,10 @@ loser: } SECStatus -SECITEM_MakeItem(PLArenaPool *arena, SECItem *dest, unsigned char *data, +SECITEM_MakeItem(PLArenaPool *arena, SECItem *dest, const unsigned char *data, unsigned int len) { - SECItem it = { siBuffer, data, len }; + SECItem it = { siBuffer, (unsigned char *)data, len }; return SECITEM_CopyItem(arena, dest, &it); } diff --git a/security/nss/lib/util/secitem.h b/security/nss/lib/util/secitem.h index 4fb123938..f7a8241b5 100644 --- a/security/nss/lib/util/secitem.h +++ b/security/nss/lib/util/secitem.h @@ -41,7 +41,7 @@ extern SECItem *SECITEM_AllocItem(PLArenaPool *arena, SECItem *item, * always siBuffer. */ extern SECStatus SECITEM_MakeItem(PLArenaPool *arena, SECItem *dest, - unsigned char *data, unsigned int len); + const unsigned char *data, unsigned int len); /* ** This is a legacy function containing bugs. It doesn't update item->len, diff --git a/security/nss/lib/util/secoid.c b/security/nss/lib/util/secoid.c index a05621c59..06b0cbcc4 100644 --- a/security/nss/lib/util/secoid.c +++ b/security/nss/lib/util/secoid.c @@ -122,7 +122,9 @@ const char __nss_util_version[] = "Version: NSS " NSSUTIL_VERSION _DEBUG_STRING; #define VERISIGN 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x45 -#define PKIX 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07 +#define INTERNET_SECURITY_MECH 0x2b, 0x06, 0x01, 0x05, 0x05 + +#define PKIX INTERNET_SECURITY_MECH, 0x07 #define PKIX_CERT_EXTENSIONS PKIX, 1 #define PKIX_POLICY_QUALIFIERS PKIX, 2 #define PKIX_KEY_USAGE PKIX, 3 @@ -360,6 +362,7 @@ CONST_OID x509FreshestCRL[] = { ID_CE_OID, 46 }; CONST_OID x509InhibitAnyPolicy[] = { ID_CE_OID, 54 }; CONST_OID x509CertificatePoliciesAnyPolicy[] = { ID_CE_OID, 32, 0 }; +CONST_OID x509ExtKeyUsageAnyUsage[] = { ID_CE_OID, 37, 0 }; CONST_OID x509AuthInfoAccess[] = { PKIX_CERT_EXTENSIONS, 1 }; CONST_OID x509SubjectInfoAccess[] = { PKIX_CERT_EXTENSIONS, 11 }; @@ -454,8 +457,13 @@ CONST_OID pkixExtendedKeyUsageCodeSign[] = { PKIX_KEY_USAGE, 3 }; CONST_OID pkixExtendedKeyUsageEMailProtect[] = { PKIX_KEY_USAGE, 4 }; CONST_OID pkixExtendedKeyUsageTimeStamp[] = { PKIX_KEY_USAGE, 8 }; CONST_OID pkixOCSPResponderExtendedKeyUsage[] = { PKIX_KEY_USAGE, 9 }; +/* 17 replaces 5 + 6 + 7 (declared obsolete in RFC 4945) */ +CONST_OID pkixExtendedKeyUsageIPsecIKE[] = { PKIX_KEY_USAGE, 17 }; CONST_OID msExtendedKeyUsageTrustListSigning[] = { MS_CRYPTO_EKU, 1 }; +CONST_OID ipsecIKEEnd[] = { INTERNET_SECURITY_MECH, 0x08, 0x02, 0x01 }; +CONST_OID ipsecIKEIntermediate[] = { INTERNET_SECURITY_MECH, 0x08, 0x02, 0x02 }; + /* OIDs for Netscape defined algorithms */ CONST_OID netscapeSMimeKEA[] = { NETSCAPE_ALGS, 0x01 }; @@ -1754,6 +1762,22 @@ const static SECOidData oids[SEC_OID_TOTAL] = { "Curve25519", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION), ODE(SEC_OID_TLS13_KEA_ANY, "TLS 1.3 fake key exchange", CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION), + + OD(x509ExtKeyUsageAnyUsage, SEC_OID_X509_ANY_EXT_KEY_USAGE, + "Any Extended Key Usage", + CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION), + OD(pkixExtendedKeyUsageIPsecIKE, + SEC_OID_EXT_KEY_USAGE_IPSEC_IKE, + "IPsec IKE Certificate", + CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION), + OD(ipsecIKEEnd, + SEC_OID_IPSEC_IKE_END, + "IPsec IKE End", + CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION), + OD(ipsecIKEIntermediate, + SEC_OID_IPSEC_IKE_INTERMEDIATE, + "IPsec IKE Intermediate", + CKM_INVALID_MECHANISM, INVALID_CERT_EXTENSION), }; /* PRIVATE EXTENDED SECOID Table diff --git a/security/nss/lib/util/secoidt.h b/security/nss/lib/util/secoidt.h index 0a40f29fd..c77aeb19f 100644 --- a/security/nss/lib/util/secoidt.h +++ b/security/nss/lib/util/secoidt.h @@ -494,6 +494,11 @@ typedef enum { SEC_OID_TLS13_KEA_ANY = 356, + SEC_OID_X509_ANY_EXT_KEY_USAGE = 357, + SEC_OID_EXT_KEY_USAGE_IPSEC_IKE = 358, + SEC_OID_IPSEC_IKE_END = 359, + SEC_OID_IPSEC_IKE_INTERMEDIATE = 360, + SEC_OID_TOTAL } SECOidTag; diff --git a/security/nss/lib/util/secport.c b/security/nss/lib/util/secport.c index e5bd4c1bb..ae979ebad 100644 --- a/security/nss/lib/util/secport.c +++ b/security/nss/lib/util/secport.c @@ -199,9 +199,6 @@ PORT_Strdup(const char *str) void PORT_SetError(int value) { -#ifdef DEBUG_jp96085 - PORT_Assert(value != SEC_ERROR_REUSED_ISSUER_AND_SERIAL); -#endif PR_SetError(value, 0); return; } diff --git a/security/nss/lib/util/utilpars.c b/security/nss/lib/util/utilpars.c index e7435bfcc..f9b807f7e 100644 --- a/security/nss/lib/util/utilpars.c +++ b/security/nss/lib/util/utilpars.c @@ -913,6 +913,92 @@ NSSUTIL_MkModuleSpec(char *dllName, char *commonName, char *parameters, return NSSUTIL_MkModuleSpecEx(dllName, commonName, parameters, NSS, NULL); } +/************************************************************************ + * add a single flag to the Flags= section inside the spec's NSS= section */ +char * +NSSUTIL_AddNSSFlagToModuleSpec(char *spec, char *addFlag) +{ + const char *prefix = "flags="; + const size_t prefixLen = strlen(prefix); + char *lib = NULL, *name = NULL, *param = NULL, *nss = NULL, *conf = NULL; + char *nss2 = NULL, *result = NULL; + SECStatus rv; + + rv = NSSUTIL_ArgParseModuleSpecEx(spec, &lib, &name, ¶m, &nss, &conf); + if (rv != SECSuccess) { + return NULL; + } + + if (nss && NSSUTIL_ArgHasFlag("flags", addFlag, nss)) { + /* It's already there, nothing to do! */ + PORT_Free(lib); + PORT_Free(name); + PORT_Free(param); + PORT_Free(nss); + PORT_Free(conf); + return PORT_Strdup(spec); + } + + if (!nss || !strlen(nss)) { + nss2 = PORT_Alloc(prefixLen + strlen(addFlag) + 1); + PORT_Strcpy(nss2, prefix); + PORT_Strcat(nss2, addFlag); + } else { + const char *iNss = nss; + PRBool alreadyAdded = PR_FALSE; + size_t maxSize = strlen(nss) + strlen(addFlag) + prefixLen + 2; /* space and null terminator */ + nss2 = PORT_Alloc(maxSize); + *nss2 = 0; + while (*iNss) { + iNss = NSSUTIL_ArgStrip(iNss); + if (PORT_Strncasecmp(iNss, prefix, prefixLen) == 0) { + /* We found an existing Flags= section. */ + char *oldFlags; + const char *valPtr; + int valSize; + valPtr = iNss + prefixLen; + oldFlags = NSSUTIL_ArgFetchValue(valPtr, &valSize); + iNss = valPtr + valSize; + PORT_Strcat(nss2, prefix); + PORT_Strcat(nss2, oldFlags); + PORT_Strcat(nss2, ","); + PORT_Strcat(nss2, addFlag); + PORT_Strcat(nss2, " "); + PORT_Free(oldFlags); + alreadyAdded = PR_TRUE; + iNss = NSSUTIL_ArgStrip(iNss); + PORT_Strcat(nss2, iNss); /* remainder of input */ + break; + } else { + /* Append this other name=value pair and continue. */ + const char *startOfNext = NSSUTIL_ArgSkipParameter(iNss); + PORT_Strncat(nss2, iNss, (startOfNext - iNss)); + if (nss2[strlen(nss2) - 1] != ' ') { + PORT_Strcat(nss2, " "); + } + iNss = startOfNext; + } + iNss = NSSUTIL_ArgStrip(iNss); + } + if (!alreadyAdded) { + /* nss wasn't empty, and it didn't contain a Flags section. We can + * assume that other content from nss has already been added to + * nss2, which means we already have a trailing space separator. */ + PORT_Strcat(nss2, prefix); + PORT_Strcat(nss2, addFlag); + } + } + + result = NSSUTIL_MkModuleSpecEx(lib, name, param, nss2, conf); + PORT_Free(lib); + PORT_Free(name); + PORT_Free(param); + PORT_Free(nss); + PORT_Free(nss2); + PORT_Free(conf); + return result; +} + #define NSSUTIL_ARG_FORTEZZA_FLAG "FORTEZZA" /****************************************************************************** * Parse the cipher flags from the NSS parameter diff --git a/security/nss/lib/util/utilpars.h b/security/nss/lib/util/utilpars.h index 1b0b1ff1c..289fdca97 100644 --- a/security/nss/lib/util/utilpars.h +++ b/security/nss/lib/util/utilpars.h @@ -46,6 +46,7 @@ char *NSSUTIL_MkModuleSpec(char *dllName, char *commonName, char *parameters, char *NSS); char *NSSUTIL_MkModuleSpecEx(char *dllName, char *commonName, char *parameters, char *NSS, char *config); +char *NSSUTIL_AddNSSFlagToModuleSpec(char *spec, char *addFlag); void NSSUTIL_ArgParseCipherFlags(unsigned long *newCiphers, const char *cipherList); char *NSSUTIL_MkNSSString(char **slotStrings, int slotCount, PRBool internal, |