TACK_RETVAL tackNssVerifyFunc(uint8_t publicKeyBytes[TACK_PUBKEY_LENGTH], uint8_t signature[TACK_SIG_LENGTH], uint8_t *data, uint32_t dataLength) { SECItem signatureItem; SECItem hashItem; uint8_t hashBuffer[TACK_HASH_LENGTH]; SECKEYPublicKey *publicKey = getPublicKeyFromBytes(publicKeyBytes); PK11_HashBuf(SEC_OID_SHA256, hashBuffer, data, dataLength); signatureItem.data = signature; signatureItem.len = TACK_SIG_LENGTH; hashItem.data = hashBuffer; hashItem.len = sizeof(hashBuffer); uint32_t result = PK11_Verify(publicKey, &signatureItem, &hashItem, NULL); SECKEY_DestroyPublicKey(publicKey); if (result == SECSuccess) return TACK_OK; else return TACK_ERR_BAD_SIGNATURE; }
/* * c a l c u l a t e _ M D 5 _ r a n g e * * Calculate the MD5 digest on a range of bytes in * the specified fopen'd file. Returns base64. * */ static int calculate_MD5_range (FILE *fp, long r1, long r2, JAR_Digest *dig) { int num; int range; unsigned char *buf; SECStatus rv; range = r2 - r1; /* position to the beginning of range */ fseek (fp, r1, SEEK_SET); buf = (unsigned char *) PORT_ZAlloc (range); if (buf == NULL) out_of_memory(); if ((num = fread (buf, 1, range, fp)) != range) { PR_fprintf(errorFD, "%s: expected %d bytes, got %d\n", PROGRAM_NAME, range, num); errorCount++; exit (ERRX); } rv = PK11_HashBuf(SEC_OID_MD5, dig->md5, buf, range); if (rv == SECSuccess) { rv =PK11_HashBuf(SEC_OID_SHA1, dig->sha1, buf, range); } if (rv != SECSuccess) { PR_fprintf(errorFD, "%s: can't generate digest context\n", PROGRAM_NAME); errorCount++; exit (ERRX); } PORT_Free (buf); return 0; }
int do_hash(char *src, int src_size, char *dst, int algo) { SECStatus err; NSS_NoDB_Init(NULL); err = PK11_HashBuf(algo, (unsigned char *)dst, (unsigned char *)src, src_size); if (err == SECFailure) { syslog(LOG_ERR, "%s: PK11_HashBuf() error; SECFailure = [%d]; " "PORT_GetError() = [%d]\n", __FUNCTION__, SECFailure, PORT_GetError()); err = -EINVAL; goto out; } out: return (int)err; }
SECStatus DigestBuf(const SECItem& item, /*out*/ uint8_t* digestBuf, size_t digestBufLen) { static_assert(TrustDomain::DIGEST_LENGTH == SHA1_LENGTH, "TrustDomain::DIGEST_LENGTH must be 20 (SHA-1 digest length)"); if (digestBufLen != TrustDomain::DIGEST_LENGTH) { PR_NOT_REACHED("invalid hash length"); PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } if (item.len > static_cast<decltype(item.len)>(std::numeric_limits<int32_t>::max())) { PR_NOT_REACHED("large OCSP responses should have already been rejected"); PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } return PK11_HashBuf(SEC_OID_SHA1, digestBuf, item.data, static_cast<int32_t>(item.len)); }
static nsresult GetCertFingerprintByOidTag(CERTCertificate* nsscert, SECOidTag aOidTag, nsCString &fp) { unsigned int hash_len = HASH_ResultLenByOidTag(aOidTag); nsRefPtr<nsStringBuffer> fingerprint = nsStringBuffer::Alloc(hash_len); if (!fingerprint) return NS_ERROR_OUT_OF_MEMORY; PK11_HashBuf(aOidTag, (unsigned char*)fingerprint->Data(), nsscert->derCert.data, nsscert->derCert.len); SECItem fpItem; fpItem.data = (unsigned char*)fingerprint->Data(); fpItem.len = hash_len; fp.Adopt(CERT_Hexify(&fpItem, 1)); return NS_OK; }
/* compute the thumbprint of the DER cert and create a digest info * to store it in and return the digest info. * a return of NULL indicates an error. */ SGNDigestInfo * sec_pkcs12_compute_thumbprint(SECItem *der_cert) { SGNDigestInfo *thumb = NULL; SECItem digest; PRArenaPool *temparena = NULL; SECStatus rv = SECFailure; if(der_cert == NULL) return NULL; temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); if(temparena == NULL) { return NULL; } digest.data = (unsigned char *)PORT_ArenaZAlloc(temparena, sizeof(unsigned char) * SHA1_LENGTH); /* digest data and create digest info */ if(digest.data != NULL) { digest.len = SHA1_LENGTH; rv = PK11_HashBuf(SEC_OID_SHA1, digest.data, der_cert->data, der_cert->len); if(rv == SECSuccess) { thumb = SGN_CreateDigestInfo(SEC_OID_SHA1, digest.data, digest.len); } else { PORT_SetError(SEC_ERROR_NO_MEMORY); } } else { PORT_SetError(SEC_ERROR_NO_MEMORY); } PORT_FreeArena(temparena, PR_TRUE); return thumb; }
int do_hash(char *src, int src_size, char *dst, int algo) { #ifdef ENABLE_NSS SECStatus err; #else gcry_md_hd_t hd; gcry_error_t err = 0; unsigned char * hash; unsigned int mdlen; #endif /* #ifdef ENABLE_NSS */ #ifdef ENABLE_NSS NSS_NoDB_Init(NULL); err = PK11_HashBuf(algo, (unsigned char *)dst, (unsigned char *)src, src_size); if (err == SECFailure) { syslog(LOG_ERR, "%s: PK11_HashBuf() error; SECFailure = [%d]; " "PORT_GetError() = [%d]\n", __FUNCTION__, SECFailure, PORT_GetError()); err = -EINVAL; goto out; } #else err = gcry_md_open(&hd, algo, 0); mdlen = gcry_md_get_algo_dlen(algo); if (err) { syslog(LOG_ERR, "Failed to open hash algo [%d]: " "[%d]\n", algo, err); goto out; } gcry_md_write(hd, src, src_size); hash = gcry_md_read(hd, algo); memcpy(dst, hash, mdlen); gcry_md_close(hd); #endif /* #ifdef ENABLE_NSS */ out: return (int)err; }
Result DigestBuf(Input item, /*out*/ uint8_t* digestBuf, size_t digestBufLen) { static_assert(TrustDomain::DIGEST_LENGTH == SHA1_LENGTH, "TrustDomain::DIGEST_LENGTH must be 20 (SHA-1 digest length)"); if (digestBufLen != TrustDomain::DIGEST_LENGTH) { PR_NOT_REACHED("invalid hash length"); 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(SEC_OID_SHA1, digestBuf, itemSECItem.data, static_cast<int32_t>(itemSECItem.len)); if (srv != SECSuccess) { return MapPRErrorCodeToResult(PR_GetError()); } return Success; }
NS_IMETHODIMP nsNSSCertificate::GetMd5Fingerprint(nsAString &_md5Fingerprint) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; _md5Fingerprint.Truncate(); unsigned char fingerprint[20]; SECItem fpItem; memset(fingerprint, 0, sizeof fingerprint); PK11_HashBuf(SEC_OID_MD5, fingerprint, mCert->derCert.data, mCert->derCert.len); fpItem.data = fingerprint; fpItem.len = MD5_LENGTH; char *fpStr = CERT_Hexify(&fpItem, 1); if (fpStr) { _md5Fingerprint = NS_ConvertASCIItoUTF16(fpStr); PORT_Free(fpStr); return NS_OK; } return NS_ERROR_FAILURE; }
// 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." static der::Result MatchIssuerKey(const SECItem& issuerKeyHash, const CERTCertificate& issuer, /*out*/ bool& match) { if (issuerKeyHash.len != SHA1_LENGTH) { return der::Fail(SEC_ERROR_OCSP_MALFORMED_RESPONSE); } // TODO(bug 966856): support SHA-2 hashes // Copy just the length and data pointer (nothing needs to be freed) of the // subject public key so we can convert the length from bits to bytes, which // is what the digest function expects. SECItem spk = issuer.subjectPublicKeyInfo.subjectPublicKey; DER_ConvertBitString(&spk); static uint8_t hashBuf[SHA1_LENGTH]; if (PK11_HashBuf(SEC_OID_SHA1, hashBuf, spk.data, spk.len) != SECSuccess) { return der::Failure; } match = !memcmp(hashBuf, issuerKeyHash.data, issuerKeyHash.len); return der::Success; }
SECItem* CreateEncodedOCSPRequest(PLArenaPool* arena, const CERTCertificate* cert, const CERTCertificate* issuerCert) { if (!arena || !cert || !issuerCert) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return nullptr; } // 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 = SHA1_LENGTH; static const unsigned int totalLenWithoutSerialNumberData = 2 // OCSPRequest + 2 // tbsRequest + 2 // requestList + 2 // Request + 2 // reqCert (CertID) + PR_ARRAY_SIZE(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. if (issuerCert->serialNumber.len > 127u - totalLenWithoutSerialNumberData) { PR_SetError(SEC_ERROR_BAD_DATA, 0); return nullptr; } uint8_t totalLen = static_cast<uint8_t>(totalLenWithoutSerialNumberData + cert->serialNumber.len); SECItem* encodedRequest = SECITEM_AllocItem(arena, nullptr, totalLen); if (!encodedRequest) { return nullptr; } uint8_t* d = encodedRequest->data; *d++ = 0x30; *d++ = totalLen - 2; // OCSPRequest (SEQUENCE) *d++ = 0x30; *d++ = totalLen - 4; // tbsRequest (SEQUENCE) *d++ = 0x30; *d++ = totalLen - 6; // requestList (SEQUENCE OF) *d++ = 0x30; *d++ = totalLen - 8; // Request (SEQUENCE) *d++ = 0x30; *d++ = totalLen - 10; // reqCert (CertID SEQUENCE) // reqCert.hashAlgorithm for (size_t i = 0; i < PR_ARRAY_SIZE(hashAlgorithm); ++i) { *d++ = hashAlgorithm[i]; } // reqCert.issuerNameHash (OCTET STRING) *d++ = 0x04; *d++ = hashLen; if (PK11_HashBuf(SEC_OID_SHA1, d, issuerCert->derSubject.data, issuerCert->derSubject.len) != SECSuccess) { return nullptr; } d += hashLen; // reqCert.issuerKeyHash (OCTET STRING) *d++ = 0x04; *d++ = hashLen; SECItem key = issuerCert->subjectPublicKeyInfo.subjectPublicKey; DER_ConvertBitString(&key); if (PK11_HashBuf(SEC_OID_SHA1, d, key.data, key.len) != SECSuccess) { return nullptr; } d += hashLen; // reqCert.serialNumber (INTEGER) *d++ = 0x02; // INTEGER *d++ = static_cast<uint8_t>(cert->serialNumber.len); for (size_t i = 0; i < cert->serialNumber.len; ++i) { *d++ = cert->serialNumber.data[i]; } PR_ASSERT(d == encodedRequest->data + totalLen); return encodedRequest; }
NSS_IMPLEMENT NSSTrust * nssTrust_Create ( nssPKIObject *object, NSSItem *certData ) { PRStatus status; PRUint32 i; PRUint32 lastTrustOrder, myTrustOrder; unsigned char sha1_hashcmp[SHA1_LENGTH]; unsigned char sha1_hashin[SHA1_LENGTH]; NSSItem sha1_hash; NSSTrust *rvt; nssCryptokiObject *instance; nssTrustLevel serverAuth, clientAuth, codeSigning, emailProtection; SECStatus rv; /* Should be stan flavor */ PRBool stepUp; lastTrustOrder = 1<<16; /* just make it big */ PR_ASSERT(object->instances != NULL && object->numInstances > 0); rvt = nss_ZNEW(object->arena, NSSTrust); if (!rvt) { return (NSSTrust *)NULL; } rvt->object = *object; /* should be stan flavor of Hashbuf */ rv = PK11_HashBuf(SEC_OID_SHA1,sha1_hashcmp,certData->data,certData->size); if (rv != SECSuccess) { return (NSSTrust *)NULL; } sha1_hash.data = sha1_hashin; sha1_hash.size = sizeof (sha1_hashin); /* trust has to peek into the base object members */ nssPKIObject_Lock(object); for (i=0; i<object->numInstances; i++) { instance = object->instances[i]; myTrustOrder = nssToken_GetTrustOrder(instance->token); status = nssCryptokiTrust_GetAttributes(instance, NULL, &sha1_hash, &serverAuth, &clientAuth, &codeSigning, &emailProtection, &stepUp); if (status != PR_SUCCESS) { nssPKIObject_Unlock(object); return (NSSTrust *)NULL; } if (PORT_Memcmp(sha1_hashin,sha1_hashcmp,SHA1_LENGTH) != 0) { nssPKIObject_Unlock(object); return (NSSTrust *)NULL; } if (rvt->serverAuth == nssTrustLevel_Unknown || myTrustOrder < lastTrustOrder) { rvt->serverAuth = serverAuth; } if (rvt->clientAuth == nssTrustLevel_Unknown || myTrustOrder < lastTrustOrder) { rvt->clientAuth = clientAuth; } if (rvt->emailProtection == nssTrustLevel_Unknown || myTrustOrder < lastTrustOrder) { rvt->emailProtection = emailProtection; } if (rvt->codeSigning == nssTrustLevel_Unknown || myTrustOrder < lastTrustOrder) { rvt->codeSigning = codeSigning; } rvt->stepUpApproved = stepUp; lastTrustOrder = myTrustOrder; } nssPKIObject_Unlock(object); return rvt; }
// 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 der::Result CertID(der::Input& input, const Context& context, /*out*/ bool& match) { match = false; SECAlgorithmID hashAlgorithm; if (der::Nested(input, der::SEQUENCE, bind(der::AlgorithmIdentifier, _1, ref(hashAlgorithm))) != der::Success) { return der::Failure; } SECItem issuerNameHash; if (der::Skip(input, der::OCTET_STRING, issuerNameHash) != der::Success) { return der::Failure; } SECItem issuerKeyHash; if (der::Skip(input, der::OCTET_STRING, issuerKeyHash) != der::Success) { return der::Failure; } SECItem serialNumber; if (der::CertificateSerialNumber(input, serialNumber) != der::Success) { return der::Failure; } const CERTCertificate& cert = context.cert; const CERTCertificate& issuerCert = context.issuerCert; if (!SECITEM_ItemsAreEqual(&serialNumber, &cert.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 der::Success; } // TODO: support SHA-2 hashes. SECOidTag hashAlg = SECOID_GetAlgorithmTag(&hashAlgorithm); if (hashAlg != SEC_OID_SHA1) { // Again, not interested in this response. Consume input, return success. input.SkipToEnd(); return der::Success; } if (issuerNameHash.len != SHA1_LENGTH) { return der::Fail(SEC_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_LENGTH]; if (PK11_HashBuf(SEC_OID_SHA1, hashBuf, cert.derIssuer.data, cert.derIssuer.len) != SECSuccess) { return der::Failure; } if (memcmp(hashBuf, issuerNameHash.data, issuerNameHash.len)) { // Again, not interested in this response. Consume input, return success. input.SkipToEnd(); return der::Success; } return MatchIssuerKey(issuerKeyHash, issuerCert, match); }
static SECStatus ConvertCertificate(SECItem *sdder, char *nickname, CERTCertTrust *trust, PRBool excludeCert, PRBool excludeHash) { SECStatus rv = SECSuccess; CERTCertificate *cert; unsigned char sha1_hash[SHA1_LENGTH]; unsigned char md5_hash[MD5_LENGTH]; SECItem *serial = NULL; PRBool step_up = PR_FALSE; const char *trust_info; cert = CERT_DecodeDERCertificate(sdder, PR_FALSE, nickname); if (!cert) { return SECFailure; } serial = SEC_ASN1EncodeItem(NULL,NULL,cert,serialTemplate); if (!serial) { return SECFailure; } if (!excludeCert) { printf("\n#\n# Certificate \"%s\"\n#\n",nickname); print_info(sdder, cert); printf("CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE\n"); printf("CKA_TOKEN CK_BBOOL CK_TRUE\n"); printf("CKA_PRIVATE CK_BBOOL CK_FALSE\n"); printf("CKA_MODIFIABLE CK_BBOOL CK_FALSE\n"); printf("CKA_LABEL UTF8 \"%s\"\n",nickname); printf("CKA_CERTIFICATE_TYPE CK_CERTIFICATE_TYPE CKC_X_509\n"); printf("CKA_SUBJECT MULTILINE_OCTAL\n"); dumpbytes(cert->derSubject.data,cert->derSubject.len); printf("END\n"); printf("CKA_ID UTF8 \"0\"\n"); printf("CKA_ISSUER MULTILINE_OCTAL\n"); dumpbytes(cert->derIssuer.data,cert->derIssuer.len); printf("END\n"); printf("CKA_SERIAL_NUMBER MULTILINE_OCTAL\n"); dumpbytes(serial->data,serial->len); printf("END\n"); printf("CKA_VALUE MULTILINE_OCTAL\n"); dumpbytes(sdder->data,sdder->len); printf("END\n"); } if ((trust->sslFlags | trust->emailFlags | trust->objectSigningFlags) == CERTDB_TERMINAL_RECORD) trust_info = "Distrust"; else trust_info = "Trust for"; printf("\n# %s \"%s\"\n", trust_info, nickname); print_info(sdder, cert); printf("CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST\n"); printf("CKA_TOKEN CK_BBOOL CK_TRUE\n"); printf("CKA_PRIVATE CK_BBOOL CK_FALSE\n"); printf("CKA_MODIFIABLE CK_BBOOL CK_FALSE\n"); printf("CKA_LABEL UTF8 \"%s\"\n",nickname); if (!excludeHash) { PK11_HashBuf(SEC_OID_SHA1, sha1_hash, sdder->data, sdder->len); printf("CKA_CERT_SHA1_HASH MULTILINE_OCTAL\n"); dumpbytes(sha1_hash,SHA1_LENGTH); printf("END\n"); PK11_HashBuf(SEC_OID_MD5, md5_hash, sdder->data, sdder->len); printf("CKA_CERT_MD5_HASH MULTILINE_OCTAL\n"); dumpbytes(md5_hash,MD5_LENGTH); printf("END\n"); } printf("CKA_ISSUER MULTILINE_OCTAL\n"); dumpbytes(cert->derIssuer.data,cert->derIssuer.len); printf("END\n"); printf("CKA_SERIAL_NUMBER MULTILINE_OCTAL\n"); dumpbytes(serial->data,serial->len); printf("END\n"); printf("CKA_TRUST_SERVER_AUTH CK_TRUST %s\n", getTrustString(trust->sslFlags)); printf("CKA_TRUST_EMAIL_PROTECTION CK_TRUST %s\n", getTrustString(trust->emailFlags)); printf("CKA_TRUST_CODE_SIGNING CK_TRUST %s\n", getTrustString(trust->objectSigningFlags)); #ifdef notdef printf("CKA_TRUST_CLIENT_AUTH CK_TRUST CKT_NSS_TRUSTED\n"); printf("CKA_TRUST_DIGITAL_SIGNATURE CK_TRUST CKT_NSS_TRUSTED_DELEGATOR\n"); printf("CKA_TRUST_NON_REPUDIATION CK_TRUST CKT_NSS_TRUSTED_DELEGATOR\n"); printf("CKA_TRUST_KEY_ENCIPHERMENT CK_TRUST CKT_NSS_TRUSTED_DELEGATOR\n"); printf("CKA_TRUST_DATA_ENCIPHERMENT CK_TRUST CKT_NSS_TRUSTED_DELEGATOR\n"); printf("CKA_TRUST_KEY_AGREEMENT CK_TRUST CKT_NSS_TRUSTED_DELEGATOR\n"); printf("CKA_TRUST_KEY_CERT_SIGN CK_TRUST CKT_NSS_TRUSTED_DELEGATOR\n"); #endif step_up = (trust->sslFlags & CERTDB_GOVT_APPROVED_CA); printf("CKA_TRUST_STEP_UP_APPROVED CK_BBOOL %s\n", step_up ? "CK_TRUE" : "CK_FALSE"); PORT_Free(sdder->data); return(rv); }
PRBool pk11_HandleTrustObject(PK11SlotInfo *slot, CERTCertificate *cert, CERTCertTrust *trust) { PRArenaPool *arena; CK_ATTRIBUTE tobjTemplate[] = { { CKA_CLASS, NULL, 0 }, { CKA_CERT_SHA1_HASH, NULL, 0 }, }; CK_OBJECT_CLASS tobjc = CKO_NETSCAPE_TRUST; CK_OBJECT_HANDLE tobjID; unsigned char sha1_hash[SHA1_LENGTH]; CK_TRUST serverAuth, codeSigning, emailProtection, clientAuth; PK11_HashBuf(SEC_OID_SHA1, sha1_hash, cert->derCert.data, cert->derCert.len); PK11_SETATTRS(&tobjTemplate[0], CKA_CLASS, &tobjc, sizeof(tobjc)); PK11_SETATTRS(&tobjTemplate[1], CKA_CERT_SHA1_HASH, sha1_hash, SHA1_LENGTH); tobjID = pk11_FindObjectByTemplate(slot, tobjTemplate, sizeof(tobjTemplate)/sizeof(tobjTemplate[0])); if( CK_INVALID_HANDLE == tobjID ) { return PR_FALSE; } arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if( NULL == arena ) return PR_FALSE; /* Unfortunately, it seems that PK11_GetAttributes doesn't deal * well with nonexistent attributes. I guess we have to check * the trust info fields one at a time. */ /* We could verify CKA_CERT_HASH here */ /* We could verify CKA_EXPIRES here */ /* "Purpose" trust information */ serverAuth = pk11_GetTrustField(slot, arena, tobjID, CKA_TRUST_SERVER_AUTH); clientAuth = pk11_GetTrustField(slot, arena, tobjID, CKA_TRUST_CLIENT_AUTH); codeSigning = pk11_GetTrustField(slot, arena, tobjID, CKA_TRUST_CODE_SIGNING); emailProtection = pk11_GetTrustField(slot, arena, tobjID, CKA_TRUST_EMAIL_PROTECTION); /* Here's where the fun logic happens. We have to map back from the * key usage, extended key usage, purpose, and possibly other trust values * into the old trust-flags bits. */ /* First implementation: keep it simple for testing. We can study what other * mappings would be appropriate and add them later.. fgmr 20000724 */ if ( serverAuth == CKT_NSS_TRUSTED ) { trust->sslFlags |= CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED; } if ( serverAuth == CKT_NSS_TRUSTED_DELEGATOR ) { trust->sslFlags |= CERTDB_VALID_CA | CERTDB_TRUSTED_CA | CERTDB_NS_TRUSTED_CA; } if ( clientAuth == CKT_NSS_TRUSTED_DELEGATOR ) { trust->sslFlags |= CERTDB_TRUSTED_CLIENT_CA ; } if ( emailProtection == CKT_NSS_TRUSTED ) { trust->emailFlags |= CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED; } if ( emailProtection == CKT_NSS_TRUSTED_DELEGATOR ) { trust->emailFlags |= CERTDB_VALID_CA | CERTDB_TRUSTED_CA | CERTDB_NS_TRUSTED_CA; } if( codeSigning == CKT_NSS_TRUSTED ) { trust->objectSigningFlags |= CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED; } if( codeSigning == CKT_NSS_TRUSTED_DELEGATOR ) { trust->objectSigningFlags |= CERTDB_VALID_CA | CERTDB_TRUSTED_CA | CERTDB_NS_TRUSTED_CA; } /* There's certainly a lot more logic that can go here.. */ PORT_FreeArena(arena, PR_FALSE); return PR_TRUE; }
static PRStatus IdentityInfoInit() { for (size_t iEV = 0; iEV < PR_ARRAY_SIZE(myTrustedEVInfos); ++iEV) { nsMyTrustedEVInfo& entry = myTrustedEVInfos[iEV]; SECStatus rv; CERTIssuerAndSN ias; rv = ATOB_ConvertAsciiToItem(&ias.derIssuer, const_cast<char*>(entry.issuer_base64)); PR_ASSERT(rv == SECSuccess); if (rv != SECSuccess) { return PR_FAILURE; } rv = ATOB_ConvertAsciiToItem(&ias.serialNumber, const_cast<char*>(entry.serial_base64)); PR_ASSERT(rv == SECSuccess); if (rv != SECSuccess) { SECITEM_FreeItem(&ias.derIssuer, false); return PR_FAILURE; } ias.serialNumber.type = siUnsignedInteger; entry.cert = CERT_FindCertByIssuerAndSN(nullptr, &ias); SECITEM_FreeItem(&ias.derIssuer, false); SECITEM_FreeItem(&ias.serialNumber, false); // If an entry is missing in the NSS root database, it may be because the // root database is out of sync with what we expect (e.g. a different // version of system NSS is installed). We will just silently avoid // treating that root cert as EV. if (!entry.cert) { #ifdef DEBUG // The debug CA info is at position 0, and is NOT on the NSS root db if (iEV == 0) { continue; } #endif PR_NOT_REACHED("Could not find EV root in NSS storage"); continue; } unsigned char certFingerprint[20]; rv = PK11_HashBuf(SEC_OID_SHA1, certFingerprint, entry.cert->derCert.data, entry.cert->derCert.len); PR_ASSERT(rv == SECSuccess); if (rv == SECSuccess) { bool same = !memcmp(certFingerprint, entry.ev_root_sha1_fingerprint, 20); PR_ASSERT(same); if (same) { SECItem ev_oid_item; ev_oid_item.data = nullptr; ev_oid_item.len = 0; rv = SEC_StringToOID(nullptr, &ev_oid_item, entry.dotted_oid, 0); PR_ASSERT(rv == SECSuccess); if (rv == SECSuccess) { entry.oid_tag = register_oid(&ev_oid_item, entry.oid_name); if (entry.oid_tag == SEC_OID_UNKNOWN) { rv = SECFailure; } SECITEM_FreeItem(&ev_oid_item, false); } } else { PR_SetError(SEC_ERROR_BAD_DATA, 0); rv = SECFailure; } } if (rv != SECSuccess) { CERT_DestroyCertificate(entry.cert); entry.cert = nullptr; entry.oid_tag = SEC_OID_UNKNOWN; return PR_FAILURE; } } return PR_SUCCESS; }
static int cmmf_create_witness_and_challenge(PRArenaPool *poolp, CMMFChallenge *challenge, long inRandom, SECItem *senderDER, SECKEYPublicKey *inPubKey, void *passwdArg) { SECItem *encodedRandNum; SECItem encodedRandStr = {siBuffer, NULL, 0}; SECItem *dummy; unsigned char *randHash, *senderHash, *encChal=NULL; unsigned modulusLen = 0; SECStatus rv = SECFailure; CMMFRand randStr= { {siBuffer, NULL, 0}, {siBuffer, NULL, 0}}; PK11SlotInfo *slot; PK11SymKey *symKey = NULL; CK_OBJECT_HANDLE id; CERTSubjectPublicKeyInfo *spki = NULL; encodedRandNum = SEC_ASN1EncodeInteger(poolp, &challenge->randomNumber, inRandom); encodedRandNum = &challenge->randomNumber; randHash = PORT_ArenaNewArray(poolp, unsigned char, SHA1_LENGTH); senderHash = PORT_ArenaNewArray(poolp, unsigned char, SHA1_LENGTH); if (randHash == NULL) { goto loser; } rv = PK11_HashBuf(SEC_OID_SHA1, randHash, encodedRandNum->data, (PRUint32)encodedRandNum->len); if (rv != SECSuccess) { goto loser; } rv = PK11_HashBuf(SEC_OID_SHA1, senderHash, senderDER->data, (PRUint32)senderDER->len); if (rv != SECSuccess) { goto loser; } challenge->witness.data = randHash; challenge->witness.len = SHA1_LENGTH; randStr.integer = *encodedRandNum; randStr.senderHash.data = senderHash; randStr.senderHash.len = SHA1_LENGTH; dummy = SEC_ASN1EncodeItem(NULL, &encodedRandStr, &randStr, CMMFRandTemplate); if (dummy != &encodedRandStr) { rv = SECFailure; goto loser; } /* XXXX Now I have to encrypt encodedRandStr and stash it away. */ modulusLen = SECKEY_PublicKeyStrength(inPubKey); encChal = PORT_ArenaNewArray(poolp, unsigned char, modulusLen); if (encChal == NULL) { rv = SECFailure; goto loser; } slot =PK11_GetBestSlotWithAttributes(CKM_RSA_PKCS, CKF_WRAP, 0, passwdArg); if (slot == NULL) { rv = SECFailure; goto loser; } id = PK11_ImportPublicKey(slot, inPubKey, PR_FALSE); /* In order to properly encrypt the data, we import as a symmetric * key, and then wrap that key. That in essence encrypts the data. * This is the method recommended in the PK11 world in order * to prevent threading issues as well as breaking any other semantics * the PK11 libraries depend on. */ symKey = PK11_ImportSymKey(slot, CKM_RSA_PKCS, PK11_OriginGenerated, CKA_VALUE, &encodedRandStr, passwdArg); if (symKey == NULL) { rv = SECFailure; goto loser; } challenge->challenge.data = encChal; challenge->challenge.len = modulusLen; rv = PK11_PubWrapSymKey(CKM_RSA_PKCS, inPubKey, symKey, &challenge->challenge); PK11_FreeSlot(slot); if (rv != SECSuccess) { goto loser; } rv = SECITEM_CopyItem(poolp, &challenge->senderDER, senderDER); crmf_get_public_value(inPubKey, &challenge->key); /* Fall through */ loser: if (spki != NULL) { SECKEY_DestroySubjectPublicKeyInfo(spki); } if (encodedRandStr.data != NULL) { PORT_Free(encodedRandStr.data); } if (encodedRandNum != NULL) { SECITEM_FreeItem(encodedRandNum, PR_TRUE); } if (symKey != NULL) { PK11_FreeSymKey(symKey); } return rv; }
/* generate KEYS -- as per PKCS12 section 7. * only used for MAC */ SECItem * sec_pkcs12_generate_key_from_password(SECOidTag algorithm, SECItem *salt, SECItem *password) { unsigned char *pre_hash=NULL; unsigned char *hash_dest=NULL; SECStatus res; PRArenaPool *poolp; SECItem *key = NULL; int key_len = 0; if((salt == NULL) || (password == NULL)) { return NULL; } poolp = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if(poolp == NULL) { PORT_SetError(SEC_ERROR_NO_MEMORY); return NULL; } pre_hash = (unsigned char *)PORT_ArenaZAlloc(poolp, sizeof(char) * (salt->len+password->len)); if(pre_hash == NULL) { PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser; } hash_dest = (unsigned char *)PORT_ArenaZAlloc(poolp, sizeof(unsigned char) * SHA1_LENGTH); if(hash_dest == NULL) { PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser; } PORT_Memcpy(pre_hash, salt->data, salt->len); /* handle password of 0 length case */ if(password->len > 0) { PORT_Memcpy(&(pre_hash[salt->len]), password->data, password->len); } res = PK11_HashBuf(SEC_OID_SHA1, hash_dest, pre_hash, (salt->len+password->len)); if(res == SECFailure) { PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser; } switch(algorithm) { case SEC_OID_SHA1: if(key_len == 0) key_len = 16; key = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); if(key == NULL) { PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser; } key->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) * key_len); if(key->data == NULL) { PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser; } key->len = key_len; PORT_Memcpy(key->data, &hash_dest[SHA1_LENGTH-key->len], key->len); break; default: goto loser; break; } PORT_FreeArena(poolp, PR_TRUE); return key; loser: PORT_FreeArena(poolp, PR_TRUE); if(key != NULL) { SECITEM_ZfreeItem(key, PR_TRUE); } return NULL; }
/* MAC is generated per PKCS 12 section 6. It is expected that key, msg * and mac_dest are pre allocated, non-NULL arrays. msg_len is passed in * because it is not known how long the message actually is. String * manipulation routines will not necessarily work because msg may have * imbedded NULLs */ static SECItem * sec_pkcs12_generate_old_mac(SECItem *key, SECItem *msg) { SECStatus res; PRArenaPool *temparena = NULL; unsigned char *hash_dest=NULL, *hash_src1=NULL, *hash_src2 = NULL; int i; SECItem *mac = NULL; if((key == NULL) || (msg == NULL)) goto loser; /* allocate return item */ mac = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); if(mac == NULL) return NULL; mac->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) * SHA1_LENGTH); mac->len = SHA1_LENGTH; if(mac->data == NULL) goto loser; /* allocate temporary items */ temparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if(temparena == NULL) goto loser; hash_src1 = (unsigned char *)PORT_ArenaZAlloc(temparena, sizeof(unsigned char) * (16+msg->len)); if(hash_src1 == NULL) goto loser; hash_src2 = (unsigned char *)PORT_ArenaZAlloc(temparena, sizeof(unsigned char) * (SHA1_LENGTH+16)); if(hash_src2 == NULL) goto loser; hash_dest = (unsigned char *)PORT_ArenaZAlloc(temparena, sizeof(unsigned char) * SHA1_LENGTH); if(hash_dest == NULL) goto loser; /* perform mac'ing as per PKCS 12 */ /* first round of hashing */ for(i = 0; i < 16; i++) hash_src1[i] = key->data[i] ^ 0x36; PORT_Memcpy(&(hash_src1[16]), msg->data, msg->len); res = PK11_HashBuf(SEC_OID_SHA1, hash_dest, hash_src1, (16+msg->len)); if(res == SECFailure) goto loser; /* second round of hashing */ for(i = 0; i < 16; i++) hash_src2[i] = key->data[i] ^ 0x5c; PORT_Memcpy(&(hash_src2[16]), hash_dest, SHA1_LENGTH); res = PK11_HashBuf(SEC_OID_SHA1, mac->data, hash_src2, SHA1_LENGTH+16); if(res == SECFailure) goto loser; PORT_FreeArena(temparena, PR_TRUE); return mac; loser: if(temparena != NULL) PORT_FreeArena(temparena, PR_TRUE); if(mac != NULL) SECITEM_ZfreeItem(mac, PR_TRUE); return NULL; }
NSS_IMPLEMENT NSSTrust * nssTrust_Create ( nssPKIObject *object, NSSItem *certData ) { PRStatus status; PRUint32 i; PRUint32 lastTrustOrder, myTrustOrder; unsigned char sha1_hashcmp[SHA1_LENGTH]; unsigned char sha1_hashin[SHA1_LENGTH]; NSSItem sha1_hash; NSSTrust *rvt; nssCryptokiObject *instance; nssTrustLevel serverAuth, clientAuth, codeSigning, emailProtection; SECStatus rv; /* Should be stan flavor */ PRBool stepUp; lastTrustOrder = 1<<16; /* just make it big */ PR_ASSERT(object->instances != NULL && object->numInstances > 0); rvt = nss_ZNEW(object->arena, NSSTrust); if (!rvt) { return (NSSTrust *)NULL; } rvt->object = *object; /* should be stan flavor of Hashbuf */ rv = PK11_HashBuf(SEC_OID_SHA1,sha1_hashcmp,certData->data,certData->size); if (rv != SECSuccess) { return (NSSTrust *)NULL; } sha1_hash.data = sha1_hashin; sha1_hash.size = sizeof (sha1_hashin); /* trust has to peek into the base object members */ nssPKIObject_Lock(object); for (i=0; i<object->numInstances; i++) { instance = object->instances[i]; myTrustOrder = nssToken_GetTrustOrder(instance->token); status = nssCryptokiTrust_GetAttributes(instance, NULL, &sha1_hash, &serverAuth, &clientAuth, &codeSigning, &emailProtection, &stepUp); if (status != PR_SUCCESS) { nssPKIObject_Unlock(object); return (NSSTrust *)NULL; } /* if no hash is specified, then trust applies to all certs with * this issuer/SN. NOTE: This is only true for entries that * have distrust and unknown record */ if (!( /* we continue if there is no hash, and the trust type is * safe to accept without a hash ... or ... */ ((sha1_hash.size == 0) && nssTrust_IsSafeToIgnoreCertHash(serverAuth,clientAuth, codeSigning, emailProtection,stepUp)) || /* we have a hash of the correct size, and it matches */ ((sha1_hash.size == SHA1_LENGTH) && (PORT_Memcmp(sha1_hashin, sha1_hashcmp,SHA1_LENGTH) == 0)) )) { nssPKIObject_Unlock(object); return (NSSTrust *)NULL; } if (rvt->serverAuth == nssTrustLevel_Unknown || myTrustOrder < lastTrustOrder) { rvt->serverAuth = serverAuth; } if (rvt->clientAuth == nssTrustLevel_Unknown || myTrustOrder < lastTrustOrder) { rvt->clientAuth = clientAuth; } if (rvt->emailProtection == nssTrustLevel_Unknown || myTrustOrder < lastTrustOrder) { rvt->emailProtection = emailProtection; } if (rvt->codeSigning == nssTrustLevel_Unknown || myTrustOrder < lastTrustOrder) { rvt->codeSigning = codeSigning; } rvt->stepUpApproved = stepUp; lastTrustOrder = myTrustOrder; } nssPKIObject_Unlock(object); return rvt; }