/* * read an old style ascii or binary certificate chain */ SECStatus CERT_DecodeCertPackage(char *certbuf, int certlen, CERTImportCertificateFunc f, void *arg) { unsigned char *cp; unsigned char *bincert = NULL; char * ascCert = NULL; SECStatus rv; if ( certbuf == NULL ) { return(SECFailure); } cp = (unsigned char *)certbuf; /* is a DER encoded certificate of some type? */ if ( ( *cp & 0x1f ) == SEC_ASN1_SEQUENCE ) { SECItem certitem; SECItem *pcertitem = &certitem; int seqLen, seqLenLen; cp++; if ( *cp & 0x80) { /* Multibyte length */ seqLenLen = cp[0] & 0x7f; switch (seqLenLen) { case 4: seqLen = ((unsigned long)cp[1]<<24) | ((unsigned long)cp[2]<<16) | (cp[3]<<8) | cp[4]; break; case 3: seqLen = ((unsigned long)cp[1]<<16) | (cp[2]<<8) | cp[3]; break; case 2: seqLen = (cp[1]<<8) | cp[2]; break; case 1: seqLen = cp[1]; break; default: /* indefinite length */ seqLen = 0; } cp += ( seqLenLen + 1 ); } else { seqLenLen = 0; seqLen = *cp; cp++; } /* check entire length if definite length */ if ( seqLen || seqLenLen ) { if ( certlen != ( seqLen + seqLenLen + 2 ) ) { if (certlen > ( seqLen + seqLenLen + 2 )) PORT_SetError(SEC_ERROR_EXTRA_INPUT); else PORT_SetError(SEC_ERROR_INPUT_LEN); goto notder; } } /* check the type string */ /* netscape wrapped DER cert */ if ( ( cp[0] == SEC_ASN1_OCTET_STRING ) && ( cp[1] == CERTIFICATE_TYPE_LEN ) && ( PORT_Strcmp((char *)&cp[2], CERTIFICATE_TYPE_STRING) ) ) { cp += ( CERTIFICATE_TYPE_LEN + 2 ); /* it had better be a certificate by now!! */ certitem.data = cp; certitem.len = certlen - ( cp - (unsigned char *)certbuf ); rv = (* f)(arg, &pcertitem, 1); return(rv); } else if ( cp[0] == SEC_ASN1_OBJECT_ID ) { SECOidData *oiddata; SECItem oiditem; /* XXX - assume DER encoding of OID len!! */ oiditem.len = cp[1]; oiditem.data = (unsigned char *)&cp[2]; oiddata = SECOID_FindOID(&oiditem); if ( oiddata == NULL ) { return(SECFailure); } certitem.data = (unsigned char*)certbuf; certitem.len = certlen; switch ( oiddata->offset ) { case SEC_OID_PKCS7_SIGNED_DATA: return(SEC_ReadPKCS7Certs(&certitem, f, arg)); break; case SEC_OID_NS_TYPE_CERT_SEQUENCE: return(SEC_ReadCertSequence(&certitem, f, arg)); break; default: break; } } else { /* it had better be a certificate by now!! */ certitem.data = (unsigned char*)certbuf; certitem.len = certlen; rv = (* f)(arg, &pcertitem, 1); return(rv); } } /* now look for a netscape base64 ascii encoded cert */ notder: { unsigned char *certbegin = NULL; unsigned char *certend = NULL; char *pc; int cl; /* Convert the ASCII data into a nul-terminated string */ ascCert = (char *)PORT_Alloc(certlen + 1); if (!ascCert) { rv = SECFailure; goto loser; } PORT_Memcpy(ascCert, certbuf, certlen); ascCert[certlen] = '\0'; pc = PORT_Strchr(ascCert, '\n'); /* find an EOL */ if (!pc) { /* maybe this is a MAC file */ pc = ascCert; while (*pc && NULL != (pc = PORT_Strchr(pc, '\r'))) { *pc++ = '\n'; } } cp = (unsigned char *)ascCert; cl = certlen; /* find the beginning marker */ while ( cl > NS_CERT_HEADER_LEN ) { if ( !PORT_Strncasecmp((char *)cp, NS_CERT_HEADER, NS_CERT_HEADER_LEN) ) { cl -= NS_CERT_HEADER_LEN; cp += NS_CERT_HEADER_LEN; certbegin = cp; break; } /* skip to next eol */ do { cp++; cl--; } while ( ( *cp != '\n') && cl ); /* skip all blank lines */ while ( ( *cp == '\n') && cl ) { cp++; cl--; } } if ( certbegin ) { /* find the ending marker */ while ( cl > NS_CERT_TRAILER_LEN ) { if ( !PORT_Strncasecmp((char *)cp, NS_CERT_TRAILER, NS_CERT_TRAILER_LEN) ) { certend = (unsigned char *)cp; break; } /* skip to next eol */ do { cp++; cl--; } while ( ( *cp != '\n') && cl ); /* skip all blank lines */ while ( ( *cp == '\n') && cl ) { cp++; cl--; } } } if ( certbegin && certend ) { unsigned int binLen; *certend = 0; /* convert to binary */ bincert = ATOB_AsciiToData(certbegin, &binLen); if (!bincert) { rv = SECFailure; goto loser; } /* now recurse to decode the binary */ rv = CERT_DecodeCertPackage((char *)bincert, binLen, f, arg); } else { rv = SECFailure; } } loser: if ( bincert ) { PORT_Free(bincert); } if ( ascCert ) { PORT_Free(ascCert); } return(rv); }
/* Validates an EC public key as described in Section 5.2.2 of * X9.62. The ECDH primitive when used without the cofactor does * not address small subgroup attacks, which may occur when the * public key is not valid. These attacks can be prevented by * validating the public key before using ECDH. */ SECStatus EC_ValidatePublicKey(ECParams *ecParams, SECItem *publicValue) { #ifdef NSS_ENABLE_ECC mp_int Px, Py; ECGroup *group = NULL; SECStatus rv = SECFailure; mp_err err = MP_OKAY; int len; if (!ecParams || !publicValue) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } /* NOTE: We only support uncompressed points for now */ len = (ecParams->fieldID.size + 7) >> 3; if (publicValue->data[0] != EC_POINT_FORM_UNCOMPRESSED) { PORT_SetError(SEC_ERROR_UNSUPPORTED_EC_POINT_FORM); return SECFailure; } else if (publicValue->len != (2 * len + 1)) { PORT_SetError(SEC_ERROR_BAD_KEY); return SECFailure; } MP_DIGITS(&Px) = 0; MP_DIGITS(&Py) = 0; CHECK_MPI_OK( mp_init(&Px) ); CHECK_MPI_OK( mp_init(&Py) ); /* Initialize Px and Py */ CHECK_MPI_OK( mp_read_unsigned_octets(&Px, publicValue->data + 1, (mp_size) len) ); CHECK_MPI_OK( mp_read_unsigned_octets(&Py, publicValue->data + 1 + len, (mp_size) len) ); /* construct from named params */ group = ECGroup_fromName(ecParams->name); if (group == NULL) { /* * ECGroup_fromName fails if ecParams->name is not a valid * ECCurveName value, or if we run out of memory, or perhaps * for other reasons. Unfortunately if ecParams->name is a * valid ECCurveName value, we don't know what the right error * code should be because ECGroup_fromName doesn't return an * error code to the caller. Set err to MP_UNDEF because * that's what ECGroup_fromName uses internally. */ if ((ecParams->name <= ECCurve_noName) || (ecParams->name >= ECCurve_pastLastCurve)) { err = MP_BADARG; } else { err = MP_UNDEF; } goto cleanup; } /* validate public point */ if ((err = ECPoint_validate(group, &Px, &Py)) < MP_YES) { if (err == MP_NO) { PORT_SetError(SEC_ERROR_BAD_KEY); rv = SECFailure; err = MP_OKAY; /* don't change the error code */ } goto cleanup; } rv = SECSuccess; cleanup: ECGroup_free(group); mp_clear(&Px); mp_clear(&Py); if (err) { MP_TO_SEC_ERROR(err); rv = SECFailure; } return rv; #else PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); return SECFailure; #endif /* NSS_ENABLE_ECC */ }
static SECStatus dsa_NewKeyExtended(const PQGParams *params, const SECItem * seed, DSAPrivateKey **privKey) { mp_int p, g; mp_int x, y; mp_err err; PRArenaPool *arena; DSAPrivateKey *key; /* Check args. */ if (!params || !privKey || !seed || !seed->data) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } /* Initialize an arena for the DSA key. */ arena = PORT_NewArena(NSS_FREEBL_DSA_DEFAULT_CHUNKSIZE); if (!arena) { PORT_SetError(SEC_ERROR_NO_MEMORY); return SECFailure; } key = (DSAPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(DSAPrivateKey)); if (!key) { PORT_SetError(SEC_ERROR_NO_MEMORY); PORT_FreeArena(arena, PR_TRUE); return SECFailure; } key->params.arena = arena; /* Initialize MPI integers. */ MP_DIGITS(&p) = 0; MP_DIGITS(&g) = 0; MP_DIGITS(&x) = 0; MP_DIGITS(&y) = 0; CHECK_MPI_OK( mp_init(&p) ); CHECK_MPI_OK( mp_init(&g) ); CHECK_MPI_OK( mp_init(&x) ); CHECK_MPI_OK( mp_init(&y) ); /* Copy over the PQG params */ CHECK_MPI_OK( SECITEM_CopyItem(arena, &key->params.prime, ¶ms->prime) ); CHECK_MPI_OK( SECITEM_CopyItem(arena, &key->params.subPrime, ¶ms->subPrime) ); CHECK_MPI_OK( SECITEM_CopyItem(arena, &key->params.base, ¶ms->base) ); /* Convert stored p, g, and received x into MPI integers. */ SECITEM_TO_MPINT(params->prime, &p); SECITEM_TO_MPINT(params->base, &g); OCTETS_TO_MPINT(seed->data, &x, seed->len); /* Store x in private key */ SECITEM_AllocItem(arena, &key->privateValue, seed->len); PORT_Memcpy(key->privateValue.data, seed->data, seed->len); /* Compute public key y = g**x mod p */ CHECK_MPI_OK( mp_exptmod(&g, &x, &p, &y) ); /* Store y in public key */ MPINT_TO_SECITEM(&y, &key->publicValue, arena); *privKey = key; key = NULL; cleanup: mp_clear(&p); mp_clear(&g); mp_clear(&x); mp_clear(&y); if (key) PORT_FreeArena(key->params.arena, PR_TRUE); if (err) { translate_mpi_error(err); return SECFailure; } return SECSuccess; }
/* * Attempt to read in an entire SSL3 record. * Blocks here for blocking sockets, otherwise returns -1 with * PR_WOULD_BLOCK_ERROR when socket would block. * * returns 1 if received a complete SSL3 record. * returns 0 if recv returns EOF * returns -1 if recv returns <0 * (The error value may have already been set to PR_WOULD_BLOCK_ERROR) * * Caller must hold the recv buf lock. * * The Gather state machine has 3 states: GS_INIT, GS_HEADER, GS_DATA. * GS_HEADER: waiting for the 5-byte SSL3 record header to come in. * GS_DATA: waiting for the body of the SSL3 record to come in. * * This loop returns when either (a) an error or EOF occurs, * (b) PR_WOULD_BLOCK_ERROR, * (c) data (entire SSL3 record) has been received. */ static int ssl3_GatherData(sslSocket *ss, sslGather *gs, int flags) { unsigned char *bp; unsigned char *lbp; int nb; int err; int rv = 1; PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); if (gs->state == GS_INIT) { gs->state = GS_HEADER; gs->remainder = 5; gs->offset = 0; gs->writeOffset = 0; gs->readOffset = 0; gs->inbuf.len = 0; } lbp = gs->inbuf.buf; for(;;) { SSL_TRC(30, ("%d: SSL3[%d]: gather state %d (need %d more)", SSL_GETPID(), ss->fd, gs->state, gs->remainder)); bp = ((gs->state != GS_HEADER) ? lbp : gs->hdr) + gs->offset; nb = ssl_DefRecv(ss, bp, gs->remainder, flags); if (nb > 0) { PRINT_BUF(60, (ss, "raw gather data:", bp, nb)); } else if (nb == 0) { /* EOF */ SSL_TRC(30, ("%d: SSL3[%d]: EOF", SSL_GETPID(), ss->fd)); rv = 0; break; } else /* if (nb < 0) */ { SSL_DBG(("%d: SSL3[%d]: recv error %d", SSL_GETPID(), ss->fd, PR_GetError())); rv = SECFailure; break; } PORT_Assert( nb <= gs->remainder ); if (nb > gs->remainder) { /* ssl_DefRecv is misbehaving! this error is fatal to SSL. */ gs->state = GS_INIT; /* so we don't crash next time */ rv = SECFailure; break; } gs->offset += nb; gs->remainder -= nb; if (gs->state == GS_DATA) gs->inbuf.len += nb; /* if there's more to go, read some more. */ if (gs->remainder > 0) { continue; } /* have received entire record header, or entire record. */ switch (gs->state) { case GS_HEADER: /* ** Have received SSL3 record header in gs->hdr. ** Now extract the length of the following encrypted data, ** and then read in the rest of the SSL3 record into gs->inbuf. */ gs->remainder = (gs->hdr[3] << 8) | gs->hdr[4]; /* This is the max fragment length for an encrypted fragment ** plus the size of the record header. */ if(gs->remainder > (MAX_FRAGMENT_LENGTH + 2048 + 5)) { SSL3_SendAlert(ss, alert_fatal, unexpected_message); gs->state = GS_INIT; PORT_SetError(SSL_ERROR_RX_RECORD_TOO_LONG); return SECFailure; } gs->state = GS_DATA; gs->offset = 0; gs->inbuf.len = 0; if (gs->remainder > gs->inbuf.space) { err = sslBuffer_Grow(&gs->inbuf, gs->remainder); if (err) { /* realloc has set error code to no mem. */ return err; } lbp = gs->inbuf.buf; } break; /* End this case. Continue around the loop. */ case GS_DATA: /* ** SSL3 record has been completely received. */ gs->state = GS_INIT; return 1; } } return rv; }
/* Parses one AVA, starting at *pbp. Stops at endptr. * Advances *pbp past parsed AVA and trailing separator (if present). * On any error, returns NULL and *pbp is undefined. * On success, returns CERTAVA allocated from arena, and (*pbp)[-1] was * the last character parsed. *pbp is either equal to endptr or * points to first character after separator. */ static CERTAVA * ParseRFC1485AVA(PRArenaPool *arena, const char **pbp, const char *endptr) { CERTAVA *a; const NameToKind *n2k; const char *bp; int vt = -1; int valLen; SECOidTag kind = SEC_OID_UNKNOWN; SECStatus rv = SECFailure; SECItem derOid = { 0, NULL, 0 }; SECItem derVal = { 0, NULL, 0}; char sep = 0; char tagBuf[32]; char valBuf[384]; PORT_Assert(arena); if (SECSuccess != scanTag(pbp, endptr, tagBuf, sizeof tagBuf) || !(valLen = scanVal(pbp, endptr, valBuf, sizeof valBuf))) { goto loser; } bp = *pbp; if (bp < endptr) { sep = *bp++; /* skip over separator */ } *pbp = bp; /* if we haven't finished, insist that we've stopped on a separator */ if (sep && sep != ',' && sep != ';' && sep != '+') { goto loser; } /* is this a dotted decimal OID attribute type ? */ if (!PL_strncasecmp("oid.", tagBuf, 4)) { rv = SEC_StringToOID(arena, &derOid, tagBuf, strlen(tagBuf)); } else { for (n2k = name2kinds; n2k->name; n2k++) { SECOidData *oidrec; if (PORT_Strcasecmp(n2k->name, tagBuf) == 0) { kind = n2k->kind; vt = n2k->valueType; oidrec = SECOID_FindOIDByTag(kind); if (oidrec == NULL) goto loser; derOid = oidrec->oid; break; } } } if (kind == SEC_OID_UNKNOWN && rv != SECSuccess) goto loser; /* Is this a hex encoding of a DER attribute value ? */ if ('#' == valBuf[0]) { /* convert attribute value from hex to binary */ rv = hexToBin(arena, &derVal, valBuf + 1, valLen - 1); if (rv) goto loser; a = CERT_CreateAVAFromRaw(arena, &derOid, &derVal); } else { if (kind == SEC_OID_UNKNOWN) goto loser; if (kind == SEC_OID_AVA_COUNTRY_NAME && valLen != 2) goto loser; if (vt == SEC_ASN1_PRINTABLE_STRING && !IsPrintable((unsigned char*) valBuf, valLen)) goto loser; if (vt == SEC_ASN1_DS) { /* RFC 4630: choose PrintableString or UTF8String */ if (IsPrintable((unsigned char*) valBuf, valLen)) vt = SEC_ASN1_PRINTABLE_STRING; else vt = SEC_ASN1_UTF8_STRING; } derVal.data = (unsigned char*) valBuf; derVal.len = valLen; a = CERT_CreateAVAFromSECItem(arena, kind, vt, &derVal); } return a; loser: /* matched no kind -- invalid tag */ PORT_SetError(SEC_ERROR_INVALID_AVA); return 0; }
SECStatus SSL_GetChannelInfo(PRFileDesc *fd, SSLChannelInfo *info, PRUintn len) { sslSocket *ss; SSLChannelInfo inf; sslSessionID *sid; /* Check if we can properly return the length of data written and that * we're not asked to return more information than we know how to provide. */ if (!info || len < sizeof inf.length || len > sizeof inf) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } ss = ssl_FindSocket(fd); if (!ss) { SSL_DBG(("%d: SSL[%d]: bad socket in SSL_GetChannelInfo", SSL_GETPID(), fd)); return SECFailure; } memset(&inf, 0, sizeof inf); inf.length = PR_MIN(sizeof inf, len); if (ss->opt.useSecurity && ss->enoughFirstHsDone) { sid = ss->sec.ci.sid; inf.protocolVersion = ss->version; inf.authKeyBits = ss->sec.authKeyBits; inf.keaKeyBits = ss->sec.keaKeyBits; if (ss->ssl3.initialized) { SSLCipherSuiteInfo cinfo; SECStatus rv; ssl_GetSpecReadLock(ss); /* XXX The cipher suite should be in the specs and this * function should get it from cwSpec rather than from the "hs". * See bug 275744 comment 69 and bug 766137. */ inf.cipherSuite = ss->ssl3.hs.cipher_suite; inf.compressionMethod = ss->ssl3.cwSpec->compression_method; ssl_ReleaseSpecReadLock(ss); inf.compressionMethodName = ssl_GetCompressionMethodName(inf.compressionMethod); /* Fill in the cipher details from the cipher suite. */ rv = SSL_GetCipherSuiteInfo(inf.cipherSuite, &cinfo, sizeof(cinfo)); if (rv != SECSuccess) { return SECFailure; /* Error code already set. */ } inf.symCipher = cinfo.symCipher; inf.macAlgorithm = cinfo.macAlgorithm; /* Get these fromm |ss->sec| because that is accurate * even with TLS 1.3 disaggregated cipher suites. */ inf.keaType = ss->sec.keaType; inf.keaGroup = ss->sec.keaGroup ? ss->sec.keaGroup->name : ssl_grp_none; inf.keaKeyBits = ss->sec.keaKeyBits; inf.authType = ss->sec.authType; inf.authKeyBits = ss->sec.authKeyBits; inf.signatureScheme = ss->sec.signatureScheme; } if (sid) { unsigned int sidLen; inf.creationTime = sid->creationTime; inf.lastAccessTime = sid->lastAccessTime; inf.expirationTime = sid->expirationTime; inf.extendedMasterSecretUsed = (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 || sid->u.ssl3.keys.extendedMasterSecretUsed) ? PR_TRUE : PR_FALSE; inf.earlyDataAccepted = (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted || ss->ssl3.hs.zeroRttState == ssl_0rtt_done); sidLen = sid->u.ssl3.sessionIDLength; sidLen = PR_MIN(sidLen, sizeof inf.sessionID); inf.sessionIDLength = sidLen; memcpy(inf.sessionID, sid->u.ssl3.sessionID, sidLen); } } memcpy(info, &inf, inf.length); return SECSuccess; }
Result CheckIssuerIndependentProperties(TrustDomain& trustDomain, BackCert& cert, PRTime time, EndEntityOrCA endEntityOrCA, KeyUsages requiredKeyUsagesIfPresent, SECOidTag requiredEKUIfPresent, SECOidTag requiredPolicy, unsigned int subCACount, /*optional out*/ TrustDomain::TrustLevel* trustLevelOut) { Result rv; TrustDomain::TrustLevel trustLevel; rv = MapSECStatus(trustDomain.GetCertTrust(endEntityOrCA, requiredPolicy, cert.GetNSSCert(), &trustLevel)); if (rv != Success) { return rv; } if (trustLevel == TrustDomain::ActivelyDistrusted) { PORT_SetError(SEC_ERROR_UNTRUSTED_CERT); return RecoverableError; } if (trustLevel != TrustDomain::TrustAnchor && trustLevel != TrustDomain::InheritsTrust) { // The TrustDomain returned a trust level that we weren't expecting. PORT_SetError(PR_INVALID_STATE_ERROR); return FatalError; } if (trustLevelOut) { *trustLevelOut = trustLevel; } bool isTrustAnchor = endEntityOrCA == MustBeCA && trustLevel == TrustDomain::TrustAnchor; PLArenaPool* arena = cert.GetArena(); if (!arena) { return FatalError; } // 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, isTrustAnchor, cert.encodedKeyUsage, requiredKeyUsagesIfPresent, arena); if (rv != Success) { return rv; } // 4.2.1.4. Certificate Policies rv = CheckCertificatePolicies(cert, endEntityOrCA, isTrustAnchor, 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(cert, endEntityOrCA, isTrustAnchor, 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.encodedExtendedKeyUsage, requiredEKUIfPresent); 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: This check must come after the other checks in order for error // ranking to work correctly. rv = CheckTimes(cert.GetNSSCert(), time); if (rv != Success) { return rv; } return Success; }
/* this function converts a password to unicode and encures that the * required double 0 byte be placed at the end of the string */ PRBool sec_pkcs12_convert_item_to_unicode(PRArenaPool *arena, SECItem *dest, SECItem *src, PRBool zeroTerm, PRBool asciiConvert, PRBool toUnicode) { PRBool success = PR_FALSE; if(!src || !dest) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return PR_FALSE; } dest->len = src->len * 3 + 2; if(arena) { dest->data = (unsigned char*)PORT_ArenaZAlloc(arena, dest->len); } else { dest->data = (unsigned char*)PORT_ZAlloc(dest->len); } if(!dest->data) { dest->len = 0; return PR_FALSE; } if(!asciiConvert) { success = PORT_UCS2_UTF8Conversion(toUnicode, src->data, src->len, dest->data, dest->len, &dest->len); } else { #ifndef IS_LITTLE_ENDIAN PRBool swapUnicode = PR_FALSE; #else PRBool swapUnicode = PR_TRUE; #endif success = PORT_UCS2_ASCIIConversion(toUnicode, src->data, src->len, dest->data, dest->len, &dest->len, swapUnicode); } if(!success) { if(!arena) { PORT_Free(dest->data); dest->data = NULL; dest->len = 0; } return PR_FALSE; } if((dest->data[dest->len-1] || dest->data[dest->len-2]) && zeroTerm) { if(dest->len + 2 > 3 * src->len) { if(arena) { dest->data = (unsigned char*)PORT_ArenaGrow(arena, dest->data, dest->len, dest->len + 2); } else { dest->data = (unsigned char*)PORT_Realloc(dest->data, dest->len + 2); } if(!dest->data) { return PR_FALSE; } } dest->len += 2; dest->data[dest->len-1] = dest->data[dest->len-2] = 0; } return PR_TRUE; }
/* * Delete a module from the Data Base */ static SECStatus nssutil_DeleteSecmodDBEntry(const char *appName, const char *filename, const char *dbname, const char *args, PRBool rw) { /* SHDB_FIXME implement */ os_stat_type stat_existing; os_open_permissions_type file_mode; FILE *fd = NULL; FILE *fd2 = NULL; char line[MAX_LINE_LENGTH]; char *dbname2 = NULL; char *block = NULL; char *name = NULL; char *lib = NULL; int name_len = 0, lib_len = 0; PRBool skip = PR_FALSE; PRBool found = PR_FALSE; if (dbname == NULL) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } if (!rw) { PORT_SetError(SEC_ERROR_READ_ONLY); return SECFailure; } dbname2 = PORT_Strdup(dbname); if (dbname2 == NULL) goto loser; dbname2[strlen(dbname) - 1]++; /* get the permissions of the existing file, or use the default */ if (!os_stat(dbname, &stat_existing)) { file_mode = stat_existing.st_mode; } else { file_mode = os_open_permissions_default; } /* do we really want to use streams here */ fd = fopen(dbname, "r"); if (fd == NULL) goto loser; fd2 = lfopen(dbname2, lfopen_truncate, file_mode); if (fd2 == NULL) goto loser; name = NSSUTIL_ArgGetParamValue("name", args); if (name) { name_len = PORT_Strlen(name); } lib = NSSUTIL_ArgGetParamValue("library", args); if (lib) { lib_len = PORT_Strlen(lib); } /* * the following loop takes line separated config files and collapses * the lines to a single string, escaping and quoting as necessary. */ /* loop state variables */ block = NULL; skip = PR_FALSE; while (fgets(line, sizeof(line), fd) != NULL) { /* If we are processing a block (we haven't hit a blank line yet */ if (*line != '\n') { /* skip means we are in the middle of a block we are deleting */ if (skip) { continue; } /* if we haven't found the block yet, check to see if this block * matches our requirements */ if (!found && ((name && (PORT_Strncasecmp(line, "name=", 5) == 0) && (PORT_Strncmp(line + 5, name, name_len) == 0)) || (lib && (PORT_Strncasecmp(line, "library=", 8) == 0) && (PORT_Strncmp(line + 8, lib, lib_len) == 0)))) { /* yup, we don't need to save any more data, */ PORT_Free(block); block = NULL; /* we don't need to collect more of this block */ skip = PR_TRUE; /* we don't need to continue searching for the block */ found = PR_TRUE; continue; } /* not our match, continue to collect data in this block */ block = nssutil_DupCat(block, line); continue; } /* we've collected a block of data that wasn't the module we were * looking for, write it out */ if (block) { fwrite(block, PORT_Strlen(block), 1, fd2); PORT_Free(block); block = NULL; } /* If we didn't just delete the this block, keep the blank line */ if (!skip) { fputs(line, fd2); } /* we are definately not in a deleted block anymore */ skip = PR_FALSE; } fclose(fd); fclose(fd2); if (found) { /* rename dbname2 to dbname */ PR_Delete(dbname); PR_Rename(dbname2, dbname); } else { PR_Delete(dbname2); } PORT_Free(dbname2); PORT_Free(lib); PORT_Free(name); PORT_Free(block); return SECSuccess; loser: if (fd != NULL) { fclose(fd); } if (fd2 != NULL) { fclose(fd2); } if (dbname2) { PR_Delete(dbname2); PORT_Free(dbname2); } PORT_Free(lib); PORT_Free(name); return SECFailure; }
PK11SymKey * NSS_CMSRecipientInfo_UnwrapBulkKey(NSSCMSRecipientInfo *ri, int subIndex, CERTCertificate *cert, SECKEYPrivateKey *privkey, SECOidTag bulkalgtag) { PK11SymKey *bulkkey = NULL; SECAlgorithmID *encalg; SECOidTag encalgtag; SECItem *enckey; int error; ri->cert = CERT_DupCertificate(cert); /* mark the recipientInfo so we can find it later */ switch (ri->recipientInfoType) { case NSSCMSRecipientInfoID_KeyTrans: encalg = &(ri->ri.keyTransRecipientInfo.keyEncAlg); encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyTransRecipientInfo.keyEncAlg)); enckey = &(ri->ri.keyTransRecipientInfo.encKey); /* ignore subIndex */ switch (encalgtag) { case SEC_OID_PKCS1_RSA_ENCRYPTION: /* RSA encryption algorithm: */ /* get the symmetric (bulk) key by unwrapping it using our private key */ bulkkey = NSS_CMSUtil_DecryptSymKey_RSA(privkey, enckey, bulkalgtag); break; default: error = SEC_ERROR_UNSUPPORTED_KEYALG; goto loser; } break; case NSSCMSRecipientInfoID_KeyAgree: encalg = &(ri->ri.keyAgreeRecipientInfo.keyEncAlg); encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyAgreeRecipientInfo.keyEncAlg)); enckey = &(ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[subIndex]->encKey); switch (encalgtag) { case SEC_OID_X942_DIFFIE_HELMAN_KEY: /* Diffie-Helman key exchange */ /* XXX not yet implemented */ /* XXX problem: SEC_OID_X942_DIFFIE_HELMAN_KEY points to a PKCS3 mechanism! */ /* we support ephemeral-static DH only, so if the recipientinfo */ /* has originator stuff in it, we punt (or do we? shouldn't be that hard...) */ /* first, we derive the KEK (a symkey!) using a Derive operation, then we get the */ /* content encryption key using a Unwrap op */ /* the derive operation has to generate the key using the algorithm in RFC2631 */ error = SEC_ERROR_UNSUPPORTED_KEYALG; goto loser; break; default: error = SEC_ERROR_UNSUPPORTED_KEYALG; goto loser; } break; case NSSCMSRecipientInfoID_KEK: encalg = &(ri->ri.kekRecipientInfo.keyEncAlg); encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.kekRecipientInfo.keyEncAlg)); enckey = &(ri->ri.kekRecipientInfo.encKey); /* not supported yet */ error = SEC_ERROR_UNSUPPORTED_KEYALG; goto loser; break; } /* XXXX continue here */ return bulkkey; loser: PORT_SetError(error); return NULL; }
/* 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; }
SECStatus NSS_CMSRecipientInfo_WrapBulkKey(NSSCMSRecipientInfo *ri, PK11SymKey *bulkkey, SECOidTag bulkalgtag) { CERTCertificate *cert; SECOidTag certalgtag; SECStatus rv = SECSuccess; NSSCMSRecipientEncryptedKey *rek; NSSCMSOriginatorIdentifierOrKey *oiok; CERTSubjectPublicKeyInfo *spki, *freeSpki = NULL; PLArenaPool *poolp; NSSCMSKeyTransRecipientInfoEx *extra = NULL; PRBool usesSubjKeyID; poolp = ri->cmsg->poolp; cert = ri->cert; usesSubjKeyID = nss_cmsrecipientinfo_usessubjectkeyid(ri); if (cert) { spki = &cert->subjectPublicKeyInfo; certalgtag = SECOID_GetAlgorithmTag(&(spki->algorithm)); } else if (usesSubjKeyID) { extra = &ri->ri.keyTransRecipientInfoEx; /* sanity check */ PORT_Assert(extra->pubKey); if (!extra->pubKey) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } spki = freeSpki = SECKEY_CreateSubjectPublicKeyInfo(extra->pubKey); certalgtag = SECOID_GetAlgorithmTag(&spki->algorithm); } else { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } /* XXX set ri->recipientInfoType to the proper value here */ /* or should we look if it's been set already ? */ certalgtag = SECOID_GetAlgorithmTag(&spki->algorithm); switch (certalgtag) { case SEC_OID_PKCS1_RSA_ENCRYPTION: /* wrap the symkey */ if (cert) { rv = NSS_CMSUtil_EncryptSymKey_RSA(poolp, cert, bulkkey, &ri->ri.keyTransRecipientInfo.encKey); if (rv != SECSuccess) break; } else if (usesSubjKeyID) { PORT_Assert(extra != NULL); rv = NSS_CMSUtil_EncryptSymKey_RSAPubKey(poolp, extra->pubKey, bulkkey, &ri->ri.keyTransRecipientInfo.encKey); if (rv != SECSuccess) break; } rv = SECOID_SetAlgorithmID(poolp, &(ri->ri.keyTransRecipientInfo.keyEncAlg), certalgtag, NULL); break; case SEC_OID_X942_DIFFIE_HELMAN_KEY: /* dh-public-number */ rek = ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[0]; if (rek == NULL) { rv = SECFailure; break; } oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey); PORT_Assert(oiok->identifierType == NSSCMSOriginatorIDOrKey_OriginatorPublicKey); /* see RFC2630 12.3.1.1 */ if (SECOID_SetAlgorithmID(poolp, &oiok->id.originatorPublicKey.algorithmIdentifier, SEC_OID_X942_DIFFIE_HELMAN_KEY, NULL) != SECSuccess) { rv = SECFailure; break; } /* this will generate a key pair, compute the shared secret, */ /* derive a key and ukm for the keyEncAlg out of it, encrypt the bulk key with */ /* the keyEncAlg, set encKey, keyEncAlg, publicKey etc. */ rv = NSS_CMSUtil_EncryptSymKey_ESDH(poolp, cert, bulkkey, &rek->encKey, &ri->ri.keyAgreeRecipientInfo.ukm, &ri->ri.keyAgreeRecipientInfo.keyEncAlg, &oiok->id.originatorPublicKey.publicKey); break; default: /* other algorithms not supported yet */ /* NOTE that we do not support any KEK algorithm */ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); rv = SECFailure; break; } if (freeSpki) SECKEY_DestroySubjectPublicKeyInfo(freeSpki); return rv; }
NSSCMSRecipientInfo * nss_cmsrecipientinfo_create(NSSCMSMessage *cmsg, NSSCMSRecipientIDSelector type, CERTCertificate *cert, SECKEYPublicKey *pubKey, SECItem *subjKeyID, void* pwfn_arg, SECItem* DERinput) { NSSCMSRecipientInfo *ri; void *mark; SECOidTag certalgtag; SECStatus rv = SECSuccess; NSSCMSRecipientEncryptedKey *rek; NSSCMSOriginatorIdentifierOrKey *oiok; unsigned long version; SECItem *dummy; PLArenaPool *poolp; CERTSubjectPublicKeyInfo *spki, *freeSpki = NULL; NSSCMSRecipientIdentifier *rid; extern const SEC_ASN1Template NSSCMSRecipientInfoTemplate[]; if (!cmsg) { /* a CMSMessage wasn't supplied, create a fake one to hold the pwfunc * and a private arena pool */ cmsg = NSS_CMSMessage_Create(NULL); cmsg->pwfn_arg = pwfn_arg; /* mark it as a special cms message */ cmsg->contentInfo.contentTypeTag = (SECOidData *)&fakeContent; } poolp = cmsg->poolp; mark = PORT_ArenaMark(poolp); ri = (NSSCMSRecipientInfo *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSRecipientInfo)); if (ri == NULL) goto loser; ri->cmsg = cmsg; if (DERinput) { /* decode everything from DER */ SECItem newinput; SECStatus rv = SECITEM_CopyItem(poolp, &newinput, DERinput); if (SECSuccess != rv) goto loser; rv = SEC_QuickDERDecodeItem(poolp, ri, NSSCMSRecipientInfoTemplate, &newinput); if (SECSuccess != rv) goto loser; } switch (type) { case NSSCMSRecipientID_IssuerSN: { ri->cert = CERT_DupCertificate(cert); if (NULL == ri->cert) goto loser; spki = &(cert->subjectPublicKeyInfo); break; } case NSSCMSRecipientID_SubjectKeyID: { PORT_Assert(pubKey); spki = freeSpki = SECKEY_CreateSubjectPublicKeyInfo(pubKey); break; } case NSSCMSRecipientID_BrandNew: goto done; break; default: /* unkown type */ goto loser; break; } certalgtag = SECOID_GetAlgorithmTag(&(spki->algorithm)); rid = &ri->ri.keyTransRecipientInfo.recipientIdentifier; switch (certalgtag) { case SEC_OID_PKCS1_RSA_ENCRYPTION: ri->recipientInfoType = NSSCMSRecipientInfoID_KeyTrans; rid->identifierType = type; if (type == NSSCMSRecipientID_IssuerSN) { rid->id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert); if (rid->id.issuerAndSN == NULL) { break; } } else if (type == NSSCMSRecipientID_SubjectKeyID){ NSSCMSKeyTransRecipientInfoEx *riExtra; rid->id.subjectKeyID = PORT_ArenaNew(poolp, SECItem); if (rid->id.subjectKeyID == NULL) { rv = SECFailure; PORT_SetError(SEC_ERROR_NO_MEMORY); break; } SECITEM_CopyItem(poolp, rid->id.subjectKeyID, subjKeyID); if (rid->id.subjectKeyID->data == NULL) { rv = SECFailure; PORT_SetError(SEC_ERROR_NO_MEMORY); break; } riExtra = &ri->ri.keyTransRecipientInfoEx; riExtra->version = 0; riExtra->pubKey = SECKEY_CopyPublicKey(pubKey); if (riExtra->pubKey == NULL) { rv = SECFailure; PORT_SetError(SEC_ERROR_NO_MEMORY); break; } } else { PORT_SetError(SEC_ERROR_INVALID_ARGS); rv = SECFailure; } break; case SEC_OID_X942_DIFFIE_HELMAN_KEY: /* dh-public-number */ PORT_Assert(type == NSSCMSRecipientID_IssuerSN); if (type != NSSCMSRecipientID_IssuerSN) { rv = SECFailure; break; } /* a key agreement op */ ri->recipientInfoType = NSSCMSRecipientInfoID_KeyAgree; if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) { rv = SECFailure; break; } /* we do not support the case where multiple recipients * share the same KeyAgreeRecipientInfo and have multiple RecipientEncryptedKeys * in this case, we would need to walk all the recipientInfos, take the * ones that do KeyAgreement algorithms and join them, algorithm by algorithm * Then, we'd generate ONE ukm and OriginatorIdentifierOrKey */ /* only epheremal-static Diffie-Hellman is supported for now * this is the only form of key agreement that provides potential anonymity * of the sender, plus we do not have to include certs in the message */ /* force single recipientEncryptedKey for now */ if ((rek = NSS_CMSRecipientEncryptedKey_Create(poolp)) == NULL) { rv = SECFailure; break; } /* hardcoded IssuerSN choice for now */ rek->recipientIdentifier.identifierType = NSSCMSKeyAgreeRecipientID_IssuerSN; if ((rek->recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL) { rv = SECFailure; break; } oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey); /* see RFC2630 12.3.1.1 */ oiok->identifierType = NSSCMSOriginatorIDOrKey_OriginatorPublicKey; rv = NSS_CMSArray_Add(poolp, (void ***)&ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys, (void *)rek); break; default: /* other algorithms not supported yet */ /* NOTE that we do not support any KEK algorithm */ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); rv = SECFailure; break; } if (rv == SECFailure) goto loser; /* set version */ switch (ri->recipientInfoType) { case NSSCMSRecipientInfoID_KeyTrans: if (ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType == NSSCMSRecipientID_IssuerSN) version = NSS_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_ISSUERSN; else version = NSS_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_SUBJKEY; dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.keyTransRecipientInfo.version), version); if (dummy == NULL) goto loser; break; case NSSCMSRecipientInfoID_KeyAgree: dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.keyAgreeRecipientInfo.version), NSS_CMS_KEYAGREE_RECIPIENT_INFO_VERSION); if (dummy == NULL) goto loser; break; case NSSCMSRecipientInfoID_KEK: /* NOTE: this cannot happen as long as we do not support any KEK algorithm */ dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.kekRecipientInfo.version), NSS_CMS_KEK_RECIPIENT_INFO_VERSION); if (dummy == NULL) goto loser; break; } done: PORT_ArenaUnmark (poolp, mark); if (freeSpki) SECKEY_DestroySubjectPublicKeyInfo(freeSpki); return ri; loser: if (ri && ri->cert) { CERT_DestroyCertificate(ri->cert); } if (freeSpki) { SECKEY_DestroySubjectPublicKeyInfo(freeSpki); } PORT_ArenaRelease (poolp, mark); if (cmsg->contentInfo.contentTypeTag == &fakeContent) { NSS_CMSMessage_Destroy(cmsg); } return NULL; }
SECStatus NSSCertDBTrustDomain::CheckRevocation( insanity::pkix::EndEntityOrCA endEntityOrCA, const CERTCertificate* cert, /*const*/ CERTCertificate* issuerCert, PRTime time, /*optional*/ const SECItem* stapledOCSPResponse) { // Actively distrusted certificates will have already been blocked by // GetCertTrust. // TODO: need to verify that IsRevoked isn't called for trust anchors AND // that that fact is documented in insanity. PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: Top of CheckRevocation\n")); PORT_Assert(cert); PORT_Assert(issuerCert); if (!cert || !issuerCert) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } // If we have a stapled OCSP response then the verification of that response // determines the result unless the OCSP response is expired. We make an // exception for expired responses because some servers, nginx in particular, // are known to serve expired responses due to bugs. if (stapledOCSPResponse) { PR_ASSERT(endEntityOrCA == MustBeEndEntity); SECStatus rv = VerifyEncodedOCSPResponse(*this, cert, issuerCert, time, stapledOCSPResponse); if (rv == SECSuccess) { return rv; } if (PR_GetError() != SEC_ERROR_OCSP_OLD_RESPONSE) { return rv; } } // TODO(bug 921885): We need to change this when we add EV support. // TODO: when !mOCSPDownloadEnabled, we still need to handle the fallback for // expired responses. But, if/when we disable OCSP fetching by default, it // would be ambiguous whether !mOCSPDownloadEnabled means "I want the default" // or "I really never want you to ever fetch OCSP." if (mOCSPDownloadEnabled) { // We don't do OCSP fetching for intermediates. if (endEntityOrCA == MustBeCA) { PR_ASSERT(!stapledOCSPResponse); return SECSuccess; } ScopedPtr<char, PORT_Free_string> url(CERT_GetOCSPAuthorityInfoAccessLocation(cert)); // Nothing to do if we don't have an OCSP responder URI for the cert; just // assume it is good. Note that this is the confusing, but intended, // interpretation of "strict" revocation checking in the face of a // certificate that lacks an OCSP responder URI. if (!url) { if (stapledOCSPResponse) { PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0); return SECFailure; } return SECSuccess; } ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return SECFailure; } const SECItem* request = CreateEncodedOCSPRequest(arena.get(), cert, issuerCert); if (!request) { return SECFailure; } const SECItem* response(CERT_PostOCSPRequest(arena.get(), url.get(), request)); if (!response) { if (mOCSPStrict) { return SECFailure; } // Soft fail -> success :( } else { SECStatus rv = VerifyEncodedOCSPResponse(*this, cert, issuerCert, time, response); if (rv == SECSuccess) { return SECSuccess; } PRErrorCode error = PR_GetError(); switch (error) { case SEC_ERROR_OCSP_UNKNOWN_CERT: case SEC_ERROR_REVOKED_CERTIFICATE: return SECFailure; default: if (mOCSPStrict) { return SECFailure; } break; // Soft fail -> success :( } } } PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: end of CheckRevocation")); return SECSuccess; }
/* signature is caller-supplied buffer of at least 20 bytes. ** On input, signature->len == size of buffer to hold signature. ** digest->len == size of digest. */ SECStatus DSA_VerifyDigest(DSAPublicKey *key, const SECItem *signature, const SECItem *digest) { /* FIPS-compliance dictates that digest is a SHA hash. */ mp_int p, q, g; /* PQG parameters */ mp_int r_, s_; /* tuple (r', s') is received signature) */ mp_int u1, u2, v, w; /* intermediate values used in verification */ mp_int y; /* public key */ mp_err err; int dsa_subprime_len, dsa_signature_len, offset; SECItem localDigest; unsigned char localDigestData[DSA_MAX_SUBPRIME_LEN]; SECStatus verified = SECFailure; /* Check args. */ if (!key || !signature || !digest ) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } dsa_subprime_len = PQG_GetLength(&key->params.subPrime); dsa_signature_len = dsa_subprime_len*2; if ((signature->len != dsa_signature_len) || (digest->len > HASH_LENGTH_MAX) || (digest->len < SHA1_LENGTH)) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } /* DSA accepts digests not equal to dsa_subprime_len, if the * digests are greater, than they are truncated to the size of * dsa_subprime_len, using the left most bits. If they are less * then they are padded on the left.*/ PORT_Memset(localDigestData, 0, dsa_subprime_len); offset = (digest->len < dsa_subprime_len) ? (dsa_subprime_len - digest->len) : 0; PORT_Memcpy(localDigestData+offset, digest->data, dsa_subprime_len - offset); localDigest.data = localDigestData; localDigest.len = dsa_subprime_len; /* Initialize MPI integers. */ MP_DIGITS(&p) = 0; MP_DIGITS(&q) = 0; MP_DIGITS(&g) = 0; MP_DIGITS(&y) = 0; MP_DIGITS(&r_) = 0; MP_DIGITS(&s_) = 0; MP_DIGITS(&u1) = 0; MP_DIGITS(&u2) = 0; MP_DIGITS(&v) = 0; MP_DIGITS(&w) = 0; CHECK_MPI_OK( mp_init(&p) ); CHECK_MPI_OK( mp_init(&q) ); CHECK_MPI_OK( mp_init(&g) ); CHECK_MPI_OK( mp_init(&y) ); CHECK_MPI_OK( mp_init(&r_) ); CHECK_MPI_OK( mp_init(&s_) ); CHECK_MPI_OK( mp_init(&u1) ); CHECK_MPI_OK( mp_init(&u2) ); CHECK_MPI_OK( mp_init(&v) ); CHECK_MPI_OK( mp_init(&w) ); /* ** Convert stored PQG and public key into MPI integers. */ SECITEM_TO_MPINT(key->params.prime, &p); SECITEM_TO_MPINT(key->params.subPrime, &q); SECITEM_TO_MPINT(key->params.base, &g); SECITEM_TO_MPINT(key->publicValue, &y); /* ** Convert received signature (r', s') into MPI integers. */ OCTETS_TO_MPINT(signature->data, &r_, dsa_subprime_len); OCTETS_TO_MPINT(signature->data + dsa_subprime_len, &s_, dsa_subprime_len); /* ** Verify that 0 < r' < q and 0 < s' < q */ if (mp_cmp_z(&r_) <= 0 || mp_cmp_z(&s_) <= 0 || mp_cmp(&r_, &q) >= 0 || mp_cmp(&s_, &q) >= 0) { /* err is zero here. */ PORT_SetError(SEC_ERROR_BAD_SIGNATURE); goto cleanup; /* will return verified == SECFailure */ } /* ** FIPS 186-1, Section 6, Step 1 ** ** w = (s')**-1 mod q */ CHECK_MPI_OK( mp_invmod(&s_, &q, &w) ); /* w = (s')**-1 mod q */ /* ** FIPS 186-1, Section 6, Step 2 ** ** u1 = ((Hash(M')) * w) mod q */ SECITEM_TO_MPINT(localDigest, &u1); /* u1 = HASH(M') */ CHECK_MPI_OK( mp_mulmod(&u1, &w, &q, &u1) ); /* u1 = u1 * w mod q */ /* ** FIPS 186-1, Section 6, Step 3 ** ** u2 = ((r') * w) mod q */ CHECK_MPI_OK( mp_mulmod(&r_, &w, &q, &u2) ); /* ** FIPS 186-1, Section 6, Step 4 ** ** v = ((g**u1 * y**u2) mod p) mod q */ CHECK_MPI_OK( mp_exptmod(&g, &u1, &p, &g) ); /* g = g**u1 mod p */ CHECK_MPI_OK( mp_exptmod(&y, &u2, &p, &y) ); /* y = y**u2 mod p */ CHECK_MPI_OK( mp_mulmod(&g, &y, &p, &v) ); /* v = g * y mod p */ CHECK_MPI_OK( mp_mod(&v, &q, &v) ); /* v = v mod q */ /* ** Verification: v == r' */ if (mp_cmp(&v, &r_)) { PORT_SetError(SEC_ERROR_BAD_SIGNATURE); verified = SECFailure; /* Signature failed to verify. */ } else { verified = SECSuccess; /* Signature verified. */ } cleanup: mp_clear(&p); mp_clear(&q); mp_clear(&g); mp_clear(&y); mp_clear(&r_); mp_clear(&s_); mp_clear(&u1); mp_clear(&u2); mp_clear(&v); mp_clear(&w); if (err) { translate_mpi_error(err); } return verified; }
/* * Add a module to the Data base */ static SECStatus nssutil_AddSecmodDBEntry(const char *appName, const char *filename, const char *dbname, const char *module, PRBool rw) { os_stat_type stat_existing; os_open_permissions_type file_mode; FILE *fd = NULL; char *block = NULL; PRBool libFound = PR_FALSE; if (dbname == NULL) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } /* can't write to a read only module */ if (!rw) { PORT_SetError(SEC_ERROR_READ_ONLY); return SECFailure; } /* remove the previous version if it exists */ (void)nssutil_DeleteSecmodDBEntry(appName, filename, dbname, module, rw); /* get the permissions of the existing file, or use the default */ if (!os_stat(dbname, &stat_existing)) { file_mode = stat_existing.st_mode; } else { file_mode = os_open_permissions_default; } fd = lfopen(dbname, lfopen_append, file_mode); if (fd == NULL) { return SECFailure; } module = NSSUTIL_ArgStrip(module); while (*module) { int count; char *keyEnd = PORT_Strchr(module, '='); char *value; if (PORT_Strncmp(module, "library=", 8) == 0) { libFound = PR_TRUE; } if (keyEnd == NULL) { block = nssutil_DupCat(block, module); break; } block = nssutil_DupnCat(block, module, keyEnd - module + 1); if (block == NULL) { goto loser; } value = NSSUTIL_ArgFetchValue(&keyEnd[1], &count); if (value) { block = nssutil_DupCat(block, NSSUTIL_ArgStrip(value)); PORT_Free(value); } if (block == NULL) { goto loser; } block = nssutil_DupnCat(block, "\n", 1); module = keyEnd + 1 + count; module = NSSUTIL_ArgStrip(module); } if (block) { if (!libFound) { fprintf(fd, "library=\n"); } fwrite(block, PORT_Strlen(block), 1, fd); fprintf(fd, "\n"); PORT_Free(block); block = NULL; } fclose(fd); return SECSuccess; loser: PORT_Free(block); fclose(fd); return SECFailure; }
Result CheckIssuerIndependentProperties(TrustDomain& trustDomain, BackCert& cert, PRTime time, EndEntityOrCA endEntityOrCA, KeyUsages requiredKeyUsagesIfPresent, KeyPurposeId requiredEKUIfPresent, const CertPolicyId& requiredPolicy, unsigned int subCACount, /*optional out*/ TrustLevel* trustLevelOut) { Result rv; TrustLevel trustLevel; rv = MapSECStatus(trustDomain.GetCertTrust(endEntityOrCA, requiredPolicy, cert.GetNSSCert(), &trustLevel)); if (rv != Success) { return rv; } if (trustLevel == TrustLevel::ActivelyDistrusted) { return Fail(RecoverableError, SEC_ERROR_UNTRUSTED_CERT); } if (trustLevel != TrustLevel::TrustAnchor && trustLevel != TrustLevel::InheritsTrust) { // The TrustDomain returned a trust level that we weren't expecting. PORT_SetError(PR_INVALID_STATE_ERROR); return FatalError; } if (trustLevelOut) { *trustLevelOut = trustLevel; } // XXX: Good enough for now. There could be an illegal explicit version // number or one we don't support, but we can safely treat those all as v3 // for now since processing of v3 certificates is strictly more strict than // processing of v1 certificates. der::Version version = (!cert.GetNSSCert()->version.data && !cert.GetNSSCert()->version.len) ? der::Version::v1 : der::Version::v3; PLArenaPool* arena = cert.GetArena(); if (!arena) { return FatalError; } // 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.encodedKeyUsage, requiredKeyUsagesIfPresent, arena); if (rv != Success) { return rv; } // 4.2.1.4. Certificate Policies rv = CheckCertificatePolicies(endEntityOrCA, cert.encodedCertificatePolicies, cert.encodedInhibitAnyPolicy, 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.encodedBasicConstraints, version, 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.encodedExtendedKeyUsage, requiredEKUIfPresent); 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: This check must come after the other checks in order for error // ranking to work correctly. rv = CheckTimes(cert.GetNSSCert(), time); if (rv != Success) { return rv; } return Success; }
/* * initialize a user PIN Value */ SECStatus PK11_InitPin(PK11SlotInfo *slot, const char *ssopw, const char *userpw) { CK_SESSION_HANDLE rwsession = CK_INVALID_SESSION; CK_RV crv; SECStatus rv = SECFailure; int len; int ssolen; if (userpw == NULL) userpw = ""; if (ssopw == NULL) ssopw = ""; len = PORT_Strlen(userpw); ssolen = PORT_Strlen(ssopw); /* get a rwsession */ rwsession = PK11_GetRWSession(slot); if (rwsession == CK_INVALID_SESSION) { PORT_SetError(SEC_ERROR_BAD_DATA); slot->lastLoginCheck = 0; return rv; } if (slot->protectedAuthPath) { len = 0; ssolen = 0; ssopw = NULL; userpw = NULL; } /* check the password */ crv = PK11_GETTAB(slot)->C_Login(rwsession,CKU_SO, (unsigned char *)ssopw,ssolen); slot->lastLoginCheck = 0; if (crv != CKR_OK) { PORT_SetError(PK11_MapError(crv)); goto done; } crv = PK11_GETTAB(slot)->C_InitPIN(rwsession,(unsigned char *)userpw,len); if (crv != CKR_OK) { PORT_SetError(PK11_MapError(crv)); } else { rv = SECSuccess; } done: PK11_GETTAB(slot)->C_Logout(rwsession); slot->lastLoginCheck = 0; PK11_RestoreROSession(slot,rwsession); if (rv == SECSuccess) { /* update our view of the world */ PK11_InitToken(slot,PR_TRUE); if (slot->needLogin) { PK11_EnterSlotMonitor(slot); PK11_GETTAB(slot)->C_Login(slot->session,CKU_USER, (unsigned char *)userpw,len); slot->lastLoginCheck = 0; PK11_ExitSlotMonitor(slot); } } return rv; }
SECStatus SSL_ExportKeyingMaterial(PRFileDesc *fd, const char *label, unsigned int labelLen, PRBool hasContext, const unsigned char *context, unsigned int contextLen, unsigned char *out, unsigned int outLen) { sslSocket *ss; unsigned char *val = NULL; unsigned int valLen, i; SECStatus rv = SECFailure; ss = ssl_FindSocket(fd); if (!ss) { SSL_DBG(("%d: SSL[%d]: bad socket in ExportKeyingMaterial", SSL_GETPID(), fd)); return SECFailure; } if (!label || !labelLen || !out || !outLen || (hasContext && (!context || !contextLen))) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) { return tls13_Exporter(ss, ss->ssl3.hs.exporterSecret, label, labelLen, context, hasContext ? contextLen : 0, out, outLen); } /* construct PRF arguments */ valLen = SSL3_RANDOM_LENGTH * 2; if (hasContext) { valLen += 2 /* PRUint16 length */ + contextLen; } val = PORT_Alloc(valLen); if (!val) { return SECFailure; } i = 0; PORT_Memcpy(val + i, &ss->ssl3.hs.client_random.rand, SSL3_RANDOM_LENGTH); i += SSL3_RANDOM_LENGTH; PORT_Memcpy(val + i, &ss->ssl3.hs.server_random.rand, SSL3_RANDOM_LENGTH); i += SSL3_RANDOM_LENGTH; if (hasContext) { val[i++] = contextLen >> 8; val[i++] = contextLen; PORT_Memcpy(val + i, context, contextLen); i += contextLen; } PORT_Assert(i == valLen); /* Allow TLS keying material to be exported sooner, when the master * secret is available and we have sent ChangeCipherSpec. */ ssl_GetSpecReadLock(ss); if (!ss->ssl3.cwSpec->master_secret && !ss->ssl3.cwSpec->msItem.len) { PORT_SetError(SSL_ERROR_HANDSHAKE_NOT_COMPLETED); rv = SECFailure; } else { HASH_HashType ht = ssl3_GetTls12HashType(ss); rv = ssl3_TLSPRFWithMasterSecret(ss->ssl3.cwSpec, label, labelLen, val, valLen, out, outLen, ht); } ssl_ReleaseSpecReadLock(ss); PORT_ZFree(val, valLen); return rv; }
/* * authenticate to a slot. This loops until we can't recover, the user * gives up, or we succeed. If we're already logged in and this function * is called we will still prompt for a password, but we will probably * succeed no matter what the password was (depending on the implementation * of the PKCS 11 module. */ SECStatus PK11_DoPassword(PK11SlotInfo *slot, PRBool loadCerts, void *wincx) { SECStatus rv = SECFailure; char * password; PRBool attempt = PR_FALSE; if (PK11_NeedUserInit(slot)) { PORT_SetError(SEC_ERROR_IO); return SECFailure; } /* * Central server type applications which control access to multiple * slave applications to single crypto devices need to virtuallize the * login state. This is done by a callback out of PK11_IsLoggedIn and * here. If we are actually logged in, then we got here because the * higher level code told us that the particular client application may * still need to be logged in. If that is the case, we simply tell the * server code that it should now verify the clients password and tell us * the results. */ if (PK11_IsLoggedIn(slot,NULL) && (PK11_Global.verifyPass != NULL)) { if (!PK11_Global.verifyPass(slot,wincx)) { PORT_SetError(SEC_ERROR_BAD_PASSWORD); return SECFailure; } return SECSuccess; } /* get the password. This can drop out of the while loop * for the following reasons: * (1) the user refused to enter a password. * (return error to caller) * (2) the token user password is disabled [usually due to * too many failed authentication attempts]. * (return error to caller) * (3) the password was successful. */ while ((password = pk11_GetPassword(slot, attempt, wincx)) != NULL) { /* if the token has a protectedAuthPath, the application may have * already issued the C_Login as part of it's pk11_GetPassword call. * In this case the application will tell us what the results were in * the password value (retry or the authentication was successful) so * we can skip our own C_Login call (which would force the token to * try to login again). * * Applications that don't know about protectedAuthPath will return a * password, which we will ignore and trigger the token to * 'authenticate' itself anyway. Hopefully the blinking display on * the reader, or the flashing light under the thumbprint reader will * attract the user's attention */ attempt = PR_TRUE; if (slot->protectedAuthPath) { /* application tried to authenticate and failed. it wants to try * again, continue looping */ if (strcmp(password, PK11_PW_RETRY) == 0) { rv = SECWouldBlock; PORT_Free(password); continue; } /* applicaton tried to authenticate and succeeded we're done */ if (strcmp(password, PK11_PW_AUTHENTICATED) == 0) { rv = SECSuccess; PORT_Free(password); break; } } rv = pk11_CheckPassword(slot,password); PORT_Memset(password, 0, PORT_Strlen(password)); PORT_Free(password); if (rv != SECWouldBlock) break; } if (rv == SECSuccess) { if (!PK11_IsFriendly(slot)) { nssTrustDomain_UpdateCachedTokenCerts(slot->nssToken->trustDomain, slot->nssToken); } } else if (!attempt) PORT_SetError(SEC_ERROR_BAD_PASSWORD); return rv; }
/* Gather in a record and when complete, Handle that record. * Repeat this until the handshake is complete, * or until application data is available. * * Returns 1 when the handshake is completed without error, or * application data is available. * Returns 0 if ssl3_GatherData hits EOF. * Returns -1 on read error, or PR_WOULD_BLOCK_ERROR, or handleRecord error. * Returns -2 on SECWouldBlock return from ssl3_HandleRecord. * * Called from ssl_GatherRecord1stHandshake in sslcon.c, * and from SSL_ForceHandshake in sslsecur.c * and from ssl3_GatherAppDataRecord below (<- DoRecv in sslsecur.c). * * Caller must hold the recv buf lock. */ int ssl3_GatherCompleteHandshake(sslSocket *ss, int flags) { SSL3Ciphertext cText; int rv; PRBool canFalseStart = PR_FALSE; PORT_Assert( ss->opt.noLocks || ssl_HaveRecvBufLock(ss) ); do { /* Without this, we may end up wrongly reporting * SSL_ERROR_RX_UNEXPECTED_* errors if we receive any records from the * peer while we are waiting to be restarted. */ ssl_GetSSL3HandshakeLock(ss); rv = ss->ssl3.hs.restartTarget == NULL ? SECSuccess : SECFailure; ssl_ReleaseSSL3HandshakeLock(ss); if (rv != SECSuccess) { PORT_SetError(PR_WOULD_BLOCK_ERROR); return (int) SECFailure; } /* Treat an empty msgState like a NULL msgState. (Most of the time * when ssl3_HandleHandshake returns SECWouldBlock, it leaves * behind a non-NULL but zero-length msgState). * Test: async_cert_restart_server_sends_hello_request_first_in_separate_record */ if (ss->ssl3.hs.msgState.buf != NULL) { if (ss->ssl3.hs.msgState.len == 0) { ss->ssl3.hs.msgState.buf = NULL; } } if (ss->ssl3.hs.msgState.buf != NULL) { /* ssl3_HandleHandshake previously returned SECWouldBlock and the * as-yet-unprocessed plaintext of that previous handshake record. * We need to process it now before we overwrite it with the next * handshake record. */ rv = ssl3_HandleRecord(ss, NULL, &ss->gs.buf); } else { /* bring in the next sslv3 record. */ rv = ssl3_GatherData(ss, &ss->gs, flags); if (rv <= 0) { return rv; } /* decipher it, and handle it if it's a handshake. * If it's application data, ss->gs.buf will not be empty upon return. * If it's a change cipher spec, alert, or handshake message, * ss->gs.buf.len will be 0 when ssl3_HandleRecord returns SECSuccess. */ cText.type = (SSL3ContentType)ss->gs.hdr[0]; cText.version = (ss->gs.hdr[1] << 8) | ss->gs.hdr[2]; cText.buf = &ss->gs.inbuf; rv = ssl3_HandleRecord(ss, &cText, &ss->gs.buf); } if (rv < 0) { return ss->recvdCloseNotify ? 0 : rv; } /* If we kicked off a false start in ssl3_HandleServerHelloDone, break * out of this loop early without finishing the handshake. */ if (ss->opt.enableFalseStart) { ssl_GetSSL3HandshakeLock(ss); canFalseStart = (ss->ssl3.hs.ws == wait_change_cipher || ss->ssl3.hs.ws == wait_new_session_ticket) && ssl3_CanFalseStart(ss); ssl_ReleaseSSL3HandshakeLock(ss); } } while (ss->ssl3.hs.ws != idle_handshake && !canFalseStart && ss->gs.buf.len == 0); ss->gs.readOffset = 0; ss->gs.writeOffset = ss->gs.buf.len; return 1; }
/* * Check the user's password. Log into the card if it's correct. * succeed if the user is already logged in. */ SECStatus pk11_CheckPassword(PK11SlotInfo *slot,char *pw) { int len = 0; CK_RV crv; SECStatus rv; int64 currtime = PR_Now(); PRBool mustRetry; int retry = 0; if (slot->protectedAuthPath) { len = 0; pw = NULL; } else if (pw == NULL) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } else { len = PORT_Strlen(pw); } do { PK11_EnterSlotMonitor(slot); crv = PK11_GETTAB(slot)->C_Login(slot->session,CKU_USER, (unsigned char *)pw,len); slot->lastLoginCheck = 0; mustRetry = PR_FALSE; PK11_ExitSlotMonitor(slot); switch (crv) { /* if we're already logged in, we're good to go */ case CKR_OK: slot->authTransact = PK11_Global.transaction; /* Fall through */ case CKR_USER_ALREADY_LOGGED_IN: slot->authTime = currtime; rv = SECSuccess; break; case CKR_PIN_INCORRECT: PORT_SetError(SEC_ERROR_BAD_PASSWORD); rv = SECWouldBlock; /* everything else is ok, only the pin is bad */ break; /* someone called reset while we fetched the password, try again once * if the token is still there. */ case CKR_SESSION_HANDLE_INVALID: case CKR_SESSION_CLOSED: if (retry++ == 0) { rv = PK11_InitToken(slot,PR_FALSE); if (rv == SECSuccess) { if (slot->session != CK_INVALID_SESSION) { mustRetry = PR_TRUE; } else { PORT_SetError(PK11_MapError(crv)); rv = SECFailure; } } break; } /* Fall through */ default: PORT_SetError(PK11_MapError(crv)); rv = SECFailure; /* some failure we can't fix by retrying */ } } while (mustRetry); return rv; }
/* * SecCmsSignedDataEncodeAfterData - do all the necessary things to a SignedData * after all the encapsulated data was passed through the encoder. * * In detail: * - create the signatures in all the SignerInfos * * Please note that nothing is done to the Certificates and CRLs in the message - this * is entirely the responsibility of our callers. */ OSStatus SecCmsSignedDataEncodeAfterData(SecCmsSignedDataRef sigd) { SecCmsSignerInfoRef *signerinfos, signerinfo; SecCmsContentInfoRef cinfo; SECOidTag digestalgtag; OSStatus ret = SECFailure; OSStatus rv; CSSM_DATA_PTR contentType; int certcount; int i, ci, n, rci, si; PLArenaPool *poolp; CFArrayRef certlist; extern const SecAsn1Template SecCmsSignerInfoTemplate[]; poolp = sigd->cmsg->poolp; cinfo = &(sigd->contentInfo); /* did we have digest calculation going on? */ if (cinfo->digcx) { rv = SecCmsDigestContextFinishMultiple(cinfo->digcx, (SecArenaPoolRef)poolp, &(sigd->digests)); if (rv != SECSuccess) goto loser; /* error has been set by SecCmsDigestContextFinishMultiple */ cinfo->digcx = NULL; } signerinfos = sigd->signerInfos; certcount = 0; /* prepare all the SignerInfos (there may be none) */ for (i=0; i < SecCmsSignedDataSignerInfoCount(sigd); i++) { signerinfo = SecCmsSignedDataGetSignerInfo(sigd, i); /* find correct digest for this signerinfo */ digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo); n = SecCmsAlgArrayGetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag); if (n < 0 || sigd->digests == NULL || sigd->digests[n] == NULL) { /* oops - digest not found */ PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND); goto loser; } /* XXX if our content is anything else but data, we need to force the * presence of signed attributes (RFC2630 5.3 "signedAttributes is a * collection...") */ /* pass contentType here as we want a contentType attribute */ if ((contentType = SecCmsContentInfoGetContentTypeOID(cinfo)) == NULL) goto loser; /* sign the thing */ rv = SecCmsSignerInfoSign(signerinfo, sigd->digests[n], contentType); if (rv != SECSuccess) goto loser; /* while we're at it, count number of certs in certLists */ certlist = SecCmsSignerInfoGetCertList(signerinfo); if (certlist) certcount += CFArrayGetCount(certlist); } /* Now we can get a timestamp, since we have all the digests */ // We force the setting of a callback, since this is the most usual case if (!sigd->cmsg->tsaCallback) SecCmsMessageSetTSACallback(sigd->cmsg, (SecCmsTSACallback)SecCmsTSADefaultCallback); if (sigd->cmsg->tsaCallback && sigd->cmsg->tsaContext) { CSSM_DATA tsaResponse = {0,}; SecAsn1TSAMessageImprint messageImprint = {{{0},},{0,}}; // <rdar://problem/11073466> Add nonce support for timestamping client uint64_t nonce = 0; require_noerr(getRandomNonce(&nonce), tsxit); dprintf("SecCmsSignedDataSignerInfoCount: %d\n", SecCmsSignedDataSignerInfoCount(sigd)); // Calculate hash of encDigest and put in messageImprint.hashedMessage SecCmsSignerInfoRef signerinfo = SecCmsSignedDataGetSignerInfo(sigd, 0); // NB - assume 1 signer only! CSSM_DATA *encDigest = SecCmsSignerInfoGetEncDigest(signerinfo); require_noerr(createTSAMessageImprint(sigd, encDigest, &messageImprint), tsxit); // Callback to fire up XPC service to talk to TimeStamping server, etc. require_noerr(rv =(*sigd->cmsg->tsaCallback)(sigd->cmsg->tsaContext, &messageImprint, nonce, &tsaResponse), tsxit); require_noerr(rv = validateTSAResponseAndAddTimeStamp(signerinfo, &tsaResponse, nonce), tsxit); /* It is likely that every occurrence of "goto loser" in this file should also do a PORT_SetError. Since it is not clear what might depend on this behavior, we just do this in the timestamping case. */ tsxit: if (rv) { dprintf("Original timestamp error: %d\n", (int)rv); rv = remapTimestampError(rv); PORT_SetError(rv); goto loser; } } /* this is a SET OF, so we need to sort them guys */ rv = SecCmsArraySortByDER((void **)signerinfos, SecCmsSignerInfoTemplate, NULL); if (rv != SECSuccess) goto loser; /* * now prepare certs & crls */ /* count the rest of the certs */ if (sigd->certs != NULL) certcount += CFArrayGetCount(sigd->certs); if (certcount == 0) { sigd->rawCerts = NULL; } else { /* * Combine all of the certs and cert chains into rawcerts. * Note: certcount is an upper bound; we may not need that many slots * but we will allocate anyway to avoid having to do another pass. * (The temporary space saving is not worth it.) * * XXX ARGH - this NEEDS to be fixed. need to come up with a decent * SetOfDERcertficates implementation */ sigd->rawCerts = (CSSM_DATA_PTR *)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(CSSM_DATA_PTR)); if (sigd->rawCerts == NULL) return SECFailure; /* * XXX Want to check for duplicates and not add *any* cert that is * already in the set. This will be more important when we start * dealing with larger sets of certs, dual-key certs (signing and * encryption), etc. For the time being we can slide by... * * XXX ARGH - this NEEDS to be fixed. need to come up with a decent * SetOfDERcertficates implementation */ rci = 0; if (signerinfos != NULL) { for (si = 0; signerinfos[si] != NULL; si++) { signerinfo = signerinfos[si]; for (ci = 0; ci < CFArrayGetCount(signerinfo->certList); ci++) { sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(CSSM_DATA)); SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(signerinfo->certList, ci); SecCertificateGetData(cert, sigd->rawCerts[rci++]); } } } if (sigd->certs != NULL) { for (ci = 0; ci < CFArrayGetCount(sigd->certs); ci++) { sigd->rawCerts[rci] = PORT_ArenaZAlloc(poolp, sizeof(CSSM_DATA)); SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(sigd->certs, ci); SecCertificateGetData(cert, sigd->rawCerts[rci++]); } } sigd->rawCerts[rci] = NULL; /* this is a SET OF, so we need to sort them guys - we have the DER already, though */ SecCmsArraySort((void **)sigd->rawCerts, SecCmsUtilDERCompare, NULL, NULL); } ret = SECSuccess; loser: dprintf("SecCmsSignedDataEncodeAfterData: ret: %ld, rv: %ld\n", (long)ret, (long)rv); return ret; }
int list_signatures(pesign_context *ctx) { cert_iter iter; int rc = cert_iter_init(&iter, ctx->inpe); if (rc < 0) { printf("No certificate list found.\n"); return rc; } void *data; ssize_t datalen; int nsigs = 0; rc = 0; while (1) { rc = next_cert(&iter, &data, &datalen); if (rc <= 0) break; SEC_PKCS7DecoderContext *dc = NULL; saw_content = 0; dc = SEC_PKCS7DecoderStart(handle_bytes, NULL, NULL, NULL, NULL, NULL, decryption_allowed); if (dc == NULL) { fprintf(stderr, "SEC_PKCS7DecoderStart failed\n"); exit(1); } SECStatus status = SEC_PKCS7DecoderUpdate(dc, data, datalen); if (status != SECSuccess) { fprintf(stderr, "Found invalid certificate\n"); continue; } SEC_PKCS7ContentInfo *cinfo = SEC_PKCS7DecoderFinish(dc); if (cinfo == NULL) { fprintf(stderr, "Found invalid certificate\n"); continue; } nsigs++; printf("---------------------------------------------\n"); printf("Content was%s encrypted.\n", SEC_PKCS7ContentIsEncrypted(cinfo) ? "" : " not"); if (SEC_PKCS7ContentIsSigned(cinfo)) { char *signer_cname, *signer_ename; SECItem *signing_time; if (saw_content) { printf("Signature is "); PORT_SetError(0); if (SEC_PKCS7VerifySignature(cinfo, certUsageEmailSigner, PR_FALSE)) { printf("valid.\n"); } else { printf("invalid (Reason: 0x%08x).\n", (uint32_t)PORT_GetError()); } } else { printf("Content is detached; signature cannot " "be verified.\n"); } signer_cname = SEC_PKCS7GetSignerCommonName(cinfo); if (signer_cname != NULL) { printf("The signer's common name is %s\n", signer_cname); PORT_Free(signer_cname); } else { printf("No signer common name.\n"); } signer_ename = SEC_PKCS7GetSignerEmailAddress(cinfo); if (signer_ename != NULL) { printf("The signer's email address is %s\n", signer_ename); PORT_Free(signer_ename); } else { printf("No signer email address.\n"); } signing_time = SEC_PKCS7GetSigningTime(cinfo); if (signing_time != NULL) { printf("Signing time: %s\n", DER_TimeChoiceDayToAscii(signing_time)); } else { printf("No signing time included.\n"); } printf("There were%s certs or crls included.\n", SEC_PKCS7ContainsCertsOrCrls(cinfo) ? "" : " no"); SEC_PKCS7DestroyContentInfo(cinfo); } } if (nsigs) { printf("---------------------------------------------\n"); } else { printf("No signatures found.\n"); } return rc; }
/* Generates a new EC key pair. The private key is a supplied * value and the public key is the result of performing a scalar * point multiplication of that value with the curve's base point. */ SECStatus ec_NewKey(ECParams *ecParams, ECPrivateKey **privKey, const unsigned char *privKeyBytes, int privKeyLen) { SECStatus rv = SECFailure; #ifdef NSS_ENABLE_ECC PRArenaPool *arena; ECPrivateKey *key; mp_int k; mp_err err = MP_OKAY; int len; #if EC_DEBUG printf("ec_NewKey called\n"); #endif MP_DIGITS(&k) = 0; if (!ecParams || !privKey || !privKeyBytes || (privKeyLen < 0)) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } /* Initialize an arena for the EC key. */ if (!(arena = PORT_NewArena(NSS_FREEBL_DEFAULT_CHUNKSIZE))) return SECFailure; key = (ECPrivateKey *)PORT_ArenaZAlloc(arena, sizeof(ECPrivateKey)); if (!key) { PORT_FreeArena(arena, PR_TRUE); return SECFailure; } /* Set the version number (SEC 1 section C.4 says it should be 1) */ SECITEM_AllocItem(arena, &key->version, 1); key->version.data[0] = 1; /* Copy all of the fields from the ECParams argument to the * ECParams structure within the private key. */ key->ecParams.arena = arena; key->ecParams.type = ecParams->type; key->ecParams.fieldID.size = ecParams->fieldID.size; key->ecParams.fieldID.type = ecParams->fieldID.type; if (ecParams->fieldID.type == ec_field_GFp) { CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.fieldID.u.prime, &ecParams->fieldID.u.prime)); } else { CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.fieldID.u.poly, &ecParams->fieldID.u.poly)); } key->ecParams.fieldID.k1 = ecParams->fieldID.k1; key->ecParams.fieldID.k2 = ecParams->fieldID.k2; key->ecParams.fieldID.k3 = ecParams->fieldID.k3; CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.a, &ecParams->curve.a)); CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.b, &ecParams->curve.b)); CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curve.seed, &ecParams->curve.seed)); CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.base, &ecParams->base)); CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.order, &ecParams->order)); key->ecParams.cofactor = ecParams->cofactor; CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.DEREncoding, &ecParams->DEREncoding)); key->ecParams.name = ecParams->name; CHECK_SEC_OK(SECITEM_CopyItem(arena, &key->ecParams.curveOID, &ecParams->curveOID)); len = (ecParams->fieldID.size + 7) >> 3; SECITEM_AllocItem(arena, &key->publicValue, 2*len + 1); len = ecParams->order.len; SECITEM_AllocItem(arena, &key->privateValue, len); /* Copy private key */ if (privKeyLen >= len) { memcpy(key->privateValue.data, privKeyBytes, len); } else { memset(key->privateValue.data, 0, (len - privKeyLen)); memcpy(key->privateValue.data + (len - privKeyLen), privKeyBytes, privKeyLen); } /* Compute corresponding public key */ CHECK_MPI_OK( mp_init(&k) ); CHECK_MPI_OK( mp_read_unsigned_octets(&k, key->privateValue.data, (mp_size) len) ); rv = ec_points_mul(ecParams, &k, NULL, NULL, &(key->publicValue)); if (rv != SECSuccess) goto cleanup; *privKey = key; cleanup: mp_clear(&k); if (rv) PORT_FreeArena(arena, PR_TRUE); #if EC_DEBUG printf("ec_NewKey returning %s\n", (rv == SECSuccess) ? "success" : "failure"); #endif #else PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); #endif /* NSS_ENABLE_ECC */ return rv; }
static SECStatus dsa_SignDigest(DSAPrivateKey *key, SECItem *signature, const SECItem *digest, const unsigned char *kb) { mp_int p, q, g; /* PQG parameters */ mp_int x, k; /* private key & pseudo-random integer */ mp_int r, s; /* tuple (r, s) is signature) */ mp_err err = MP_OKAY; SECStatus rv = SECSuccess; unsigned int dsa_subprime_len, dsa_signature_len, offset; SECItem localDigest; unsigned char localDigestData[DSA_MAX_SUBPRIME_LEN]; /* FIPS-compliance dictates that digest is a SHA hash. */ /* Check args. */ if (!key || !signature || !digest) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } dsa_subprime_len = PQG_GetLength(&key->params.subPrime); dsa_signature_len = dsa_subprime_len*2; if ((signature->len < dsa_signature_len) || (digest->len > HASH_LENGTH_MAX) || (digest->len < SHA1_LENGTH)) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } /* DSA accepts digests not equal to dsa_subprime_len, if the * digests are greater, then they are truncated to the size of * dsa_subprime_len, using the left most bits. If they are less * then they are padded on the left.*/ PORT_Memset(localDigestData, 0, dsa_subprime_len); offset = (digest->len < dsa_subprime_len) ? (dsa_subprime_len - digest->len) : 0; PORT_Memcpy(localDigestData+offset, digest->data, dsa_subprime_len - offset); localDigest.data = localDigestData; localDigest.len = dsa_subprime_len; /* Initialize MPI integers. */ MP_DIGITS(&p) = 0; MP_DIGITS(&q) = 0; MP_DIGITS(&g) = 0; MP_DIGITS(&x) = 0; MP_DIGITS(&k) = 0; MP_DIGITS(&r) = 0; MP_DIGITS(&s) = 0; CHECK_MPI_OK( mp_init(&p) ); CHECK_MPI_OK( mp_init(&q) ); CHECK_MPI_OK( mp_init(&g) ); CHECK_MPI_OK( mp_init(&x) ); CHECK_MPI_OK( mp_init(&k) ); CHECK_MPI_OK( mp_init(&r) ); CHECK_MPI_OK( mp_init(&s) ); /* ** Convert stored PQG and private key into MPI integers. */ SECITEM_TO_MPINT(key->params.prime, &p); SECITEM_TO_MPINT(key->params.subPrime, &q); SECITEM_TO_MPINT(key->params.base, &g); SECITEM_TO_MPINT(key->privateValue, &x); OCTETS_TO_MPINT(kb, &k, dsa_subprime_len); /* ** FIPS 186-1, Section 5, Step 1 ** ** r = (g**k mod p) mod q */ CHECK_MPI_OK( mp_exptmod(&g, &k, &p, &r) ); /* r = g**k mod p */ CHECK_MPI_OK( mp_mod(&r, &q, &r) ); /* r = r mod q */ /* ** FIPS 186-1, Section 5, Step 2 ** ** s = (k**-1 * (HASH(M) + x*r)) mod q */ SECITEM_TO_MPINT(localDigest, &s); /* s = HASH(M) */ CHECK_MPI_OK( mp_invmod(&k, &q, &k) ); /* k = k**-1 mod q */ CHECK_MPI_OK( mp_mulmod(&x, &r, &q, &x) ); /* x = x * r mod q */ CHECK_MPI_OK( mp_addmod(&s, &x, &q, &s) ); /* s = s + x mod q */ CHECK_MPI_OK( mp_mulmod(&s, &k, &q, &s) ); /* s = s * k mod q */ /* ** verify r != 0 and s != 0 ** mentioned as optional in FIPS 186-1. */ if (mp_cmp_z(&r) == 0 || mp_cmp_z(&s) == 0) { PORT_SetError(SEC_ERROR_NEED_RANDOM); rv = SECFailure; goto cleanup; } /* ** Step 4 ** ** Signature is tuple (r, s) */ err = mp_to_fixlen_octets(&r, signature->data, dsa_subprime_len); if (err < 0) goto cleanup; err = mp_to_fixlen_octets(&s, signature->data + dsa_subprime_len, dsa_subprime_len); if (err < 0) goto cleanup; err = MP_OKAY; signature->len = dsa_signature_len; cleanup: PORT_Memset(localDigestData, 0, DSA_MAX_SUBPRIME_LEN); mp_clear(&p); mp_clear(&q); mp_clear(&g); mp_clear(&x); mp_clear(&k); mp_clear(&r); mp_clear(&s); if (err) { translate_mpi_error(err); rv = SECFailure; } return rv; }
/* * Computes scalar point multiplication pointQ = k1 * G + k2 * pointP for * the curve whose parameters are encoded in params with base point G. */ SECStatus ec_points_mul(const ECParams *params, const mp_int *k1, const mp_int *k2, const SECItem *pointP, SECItem *pointQ) { mp_int Px, Py, Qx, Qy; mp_int Gx, Gy, order, irreducible, a, b; #if 0 /* currently don't support non-named curves */ unsigned int irr_arr[5]; #endif ECGroup *group = NULL; SECStatus rv = SECFailure; mp_err err = MP_OKAY; int len; #if EC_DEBUG int i; char mpstr[256]; printf("ec_points_mul: params [len=%d]:", params->DEREncoding.len); for (i = 0; i < params->DEREncoding.len; i++) printf("%02x:", params->DEREncoding.data[i]); printf("\n"); if (k1 != NULL) { mp_tohex(k1, mpstr); printf("ec_points_mul: scalar k1: %s\n", mpstr); mp_todecimal(k1, mpstr); printf("ec_points_mul: scalar k1: %s (dec)\n", mpstr); } if (k2 != NULL) { mp_tohex(k2, mpstr); printf("ec_points_mul: scalar k2: %s\n", mpstr); mp_todecimal(k2, mpstr); printf("ec_points_mul: scalar k2: %s (dec)\n", mpstr); } if (pointP != NULL) { printf("ec_points_mul: pointP [len=%d]:", pointP->len); for (i = 0; i < pointP->len; i++) printf("%02x:", pointP->data[i]); printf("\n"); } #endif /* NOTE: We only support uncompressed points for now */ len = (params->fieldID.size + 7) >> 3; if (pointP != NULL) { if ((pointP->data[0] != EC_POINT_FORM_UNCOMPRESSED) || (pointP->len != (2 * len + 1))) { PORT_SetError(SEC_ERROR_UNSUPPORTED_EC_POINT_FORM); return SECFailure; }; } MP_DIGITS(&Px) = 0; MP_DIGITS(&Py) = 0; MP_DIGITS(&Qx) = 0; MP_DIGITS(&Qy) = 0; MP_DIGITS(&Gx) = 0; MP_DIGITS(&Gy) = 0; MP_DIGITS(&order) = 0; MP_DIGITS(&irreducible) = 0; MP_DIGITS(&a) = 0; MP_DIGITS(&b) = 0; CHECK_MPI_OK( mp_init(&Px) ); CHECK_MPI_OK( mp_init(&Py) ); CHECK_MPI_OK( mp_init(&Qx) ); CHECK_MPI_OK( mp_init(&Qy) ); CHECK_MPI_OK( mp_init(&Gx) ); CHECK_MPI_OK( mp_init(&Gy) ); CHECK_MPI_OK( mp_init(&order) ); CHECK_MPI_OK( mp_init(&irreducible) ); CHECK_MPI_OK( mp_init(&a) ); CHECK_MPI_OK( mp_init(&b) ); if ((k2 != NULL) && (pointP != NULL)) { /* Initialize Px and Py */ CHECK_MPI_OK( mp_read_unsigned_octets(&Px, pointP->data + 1, (mp_size) len) ); CHECK_MPI_OK( mp_read_unsigned_octets(&Py, pointP->data + 1 + len, (mp_size) len) ); } /* construct from named params, if possible */ if (params->name != ECCurve_noName) { group = ECGroup_fromName(params->name); } #if 0 /* currently don't support non-named curves */ if (group == NULL) { /* Set up mp_ints containing the curve coefficients */ CHECK_MPI_OK( mp_read_unsigned_octets(&Gx, params->base.data + 1, (mp_size) len) ); CHECK_MPI_OK( mp_read_unsigned_octets(&Gy, params->base.data + 1 + len, (mp_size) len) ); SECITEM_TO_MPINT( params->order, &order ); SECITEM_TO_MPINT( params->curve.a, &a ); SECITEM_TO_MPINT( params->curve.b, &b ); if (params->fieldID.type == ec_field_GFp) { SECITEM_TO_MPINT( params->fieldID.u.prime, &irreducible ); group = ECGroup_consGFp(&irreducible, &a, &b, &Gx, &Gy, &order, params->cofactor); } else { SECITEM_TO_MPINT( params->fieldID.u.poly, &irreducible ); irr_arr[0] = params->fieldID.size; irr_arr[1] = params->fieldID.k1; irr_arr[2] = params->fieldID.k2; irr_arr[3] = params->fieldID.k3; irr_arr[4] = 0; group = ECGroup_consGF2m(&irreducible, irr_arr, &a, &b, &Gx, &Gy, &order, params->cofactor); } } #endif if (group == NULL) goto cleanup; if ((k2 != NULL) && (pointP != NULL)) { CHECK_MPI_OK( ECPoints_mul(group, k1, k2, &Px, &Py, &Qx, &Qy) ); } else { CHECK_MPI_OK( ECPoints_mul(group, k1, NULL, NULL, NULL, &Qx, &Qy) ); } /* Construct the SECItem representation of point Q */ pointQ->data[0] = EC_POINT_FORM_UNCOMPRESSED; CHECK_MPI_OK( mp_to_fixlen_octets(&Qx, pointQ->data + 1, (mp_size) len) ); CHECK_MPI_OK( mp_to_fixlen_octets(&Qy, pointQ->data + 1 + len, (mp_size) len) ); rv = SECSuccess; #if EC_DEBUG printf("ec_points_mul: pointQ [len=%d]:", pointQ->len); for (i = 0; i < pointQ->len; i++) printf("%02x:", pointQ->data[i]); printf("\n"); #endif cleanup: ECGroup_free(group); mp_clear(&Px); mp_clear(&Py); mp_clear(&Qx); mp_clear(&Qy); mp_clear(&Gx); mp_clear(&Gy); mp_clear(&order); mp_clear(&irreducible); mp_clear(&a); mp_clear(&b); if (err) { MP_TO_SEC_ERROR(err); rv = SECFailure; } return rv; }
/* Computes the ECDSA signature (a concatenation of two values r and s) * on the digest using the given key and the random value kb (used in * computing s). */ SECStatus ECDSA_SignDigestWithSeed(ECPrivateKey *key, SECItem *signature, const SECItem *digest, const unsigned char *kb, const int kblen) { SECStatus rv = SECFailure; #ifdef NSS_ENABLE_ECC mp_int x1; mp_int d, k; /* private key, random integer */ mp_int r, s; /* tuple (r, s) is the signature */ mp_int n; mp_err err = MP_OKAY; ECParams *ecParams = NULL; SECItem kGpoint = { siBuffer, NULL, 0}; int flen = 0; /* length in bytes of the field size */ unsigned olen; /* length in bytes of the base point order */ unsigned obits; /* length in bits of the base point order */ #if EC_DEBUG char mpstr[256]; #endif /* Initialize MPI integers. */ /* must happen before the first potential call to cleanup */ MP_DIGITS(&x1) = 0; MP_DIGITS(&d) = 0; MP_DIGITS(&k) = 0; MP_DIGITS(&r) = 0; MP_DIGITS(&s) = 0; MP_DIGITS(&n) = 0; /* Check args */ if (!key || !signature || !digest || !kb || (kblen < 0)) { PORT_SetError(SEC_ERROR_INVALID_ARGS); goto cleanup; } ecParams = &(key->ecParams); flen = (ecParams->fieldID.size + 7) >> 3; olen = ecParams->order.len; if (signature->data == NULL) { /* a call to get the signature length only */ goto finish; } if (signature->len < 2*olen) { PORT_SetError(SEC_ERROR_OUTPUT_LEN); goto cleanup; } CHECK_MPI_OK( mp_init(&x1) ); CHECK_MPI_OK( mp_init(&d) ); CHECK_MPI_OK( mp_init(&k) ); CHECK_MPI_OK( mp_init(&r) ); CHECK_MPI_OK( mp_init(&s) ); CHECK_MPI_OK( mp_init(&n) ); SECITEM_TO_MPINT( ecParams->order, &n ); SECITEM_TO_MPINT( key->privateValue, &d ); CHECK_MPI_OK( mp_read_unsigned_octets(&k, kb, kblen) ); /* Make sure k is in the interval [1, n-1] */ if ((mp_cmp_z(&k) <= 0) || (mp_cmp(&k, &n) >= 0)) { #if EC_DEBUG printf("k is outside [1, n-1]\n"); mp_tohex(&k, mpstr); printf("k : %s \n", mpstr); mp_tohex(&n, mpstr); printf("n : %s \n", mpstr); #endif PORT_SetError(SEC_ERROR_NEED_RANDOM); goto cleanup; } /* ** We do not want timing information to leak the length of k, ** so we compute k*G using an equivalent scalar of fixed ** bit-length. ** Fix based on patch for ECDSA timing attack in the paper ** by Billy Bob Brumley and Nicola Tuveri at ** http://eprint.iacr.org/2011/232 ** ** How do we convert k to a value of a fixed bit-length? ** k starts off as an integer satisfying 0 <= k < n. Hence, ** n <= k+n < 2n, which means k+n has either the same number ** of bits as n or one more bit than n. If k+n has the same ** number of bits as n, the second addition ensures that the ** final value has exactly one more bit than n. Thus, we ** always end up with a value that exactly one more bit than n. */ CHECK_MPI_OK( mp_add(&k, &n, &k) ); if (mpl_significant_bits(&k) <= mpl_significant_bits(&n)) { CHECK_MPI_OK( mp_add(&k, &n, &k) ); } /* ** ANSI X9.62, Section 5.3.2, Step 2 ** ** Compute kG */ kGpoint.len = 2*flen + 1; kGpoint.data = PORT_Alloc(2*flen + 1); if ((kGpoint.data == NULL) || (ec_points_mul(ecParams, &k, NULL, NULL, &kGpoint) != SECSuccess)) goto cleanup; /* ** ANSI X9.62, Section 5.3.3, Step 1 ** ** Extract the x co-ordinate of kG into x1 */ CHECK_MPI_OK( mp_read_unsigned_octets(&x1, kGpoint.data + 1, (mp_size) flen) ); /* ** ANSI X9.62, Section 5.3.3, Step 2 ** ** r = x1 mod n NOTE: n is the order of the curve */ CHECK_MPI_OK( mp_mod(&x1, &n, &r) ); /* ** ANSI X9.62, Section 5.3.3, Step 3 ** ** verify r != 0 */ if (mp_cmp_z(&r) == 0) { PORT_SetError(SEC_ERROR_NEED_RANDOM); goto cleanup; } /* ** ANSI X9.62, Section 5.3.3, Step 4 ** ** s = (k**-1 * (HASH(M) + d*r)) mod n */ SECITEM_TO_MPINT(*digest, &s); /* s = HASH(M) */ /* In the definition of EC signing, digests are truncated * to the length of n in bits. * (see SEC 1 "Elliptic Curve Digit Signature Algorithm" section 4.1.*/ CHECK_MPI_OK( (obits = mpl_significant_bits(&n)) ); if (digest->len*8 > obits) { mpl_rsh(&s,&s,digest->len*8 - obits); } #if EC_DEBUG mp_todecimal(&n, mpstr); printf("n : %s (dec)\n", mpstr); mp_todecimal(&d, mpstr); printf("d : %s (dec)\n", mpstr); mp_tohex(&x1, mpstr); printf("x1: %s\n", mpstr); mp_todecimal(&s, mpstr); printf("digest: %s (decimal)\n", mpstr); mp_todecimal(&r, mpstr); printf("r : %s (dec)\n", mpstr); mp_tohex(&r, mpstr); printf("r : %s\n", mpstr); #endif CHECK_MPI_OK( mp_invmod(&k, &n, &k) ); /* k = k**-1 mod n */ CHECK_MPI_OK( mp_mulmod(&d, &r, &n, &d) ); /* d = d * r mod n */ CHECK_MPI_OK( mp_addmod(&s, &d, &n, &s) ); /* s = s + d mod n */ CHECK_MPI_OK( mp_mulmod(&s, &k, &n, &s) ); /* s = s * k mod n */ #if EC_DEBUG mp_todecimal(&s, mpstr); printf("s : %s (dec)\n", mpstr); mp_tohex(&s, mpstr); printf("s : %s\n", mpstr); #endif /* ** ANSI X9.62, Section 5.3.3, Step 5 ** ** verify s != 0 */ if (mp_cmp_z(&s) == 0) { PORT_SetError(SEC_ERROR_NEED_RANDOM); goto cleanup; } /* ** ** Signature is tuple (r, s) */ CHECK_MPI_OK( mp_to_fixlen_octets(&r, signature->data, olen) ); CHECK_MPI_OK( mp_to_fixlen_octets(&s, signature->data + olen, olen) ); finish: signature->len = 2*olen; rv = SECSuccess; err = MP_OKAY; cleanup: mp_clear(&x1); mp_clear(&d); mp_clear(&k); mp_clear(&r); mp_clear(&s); mp_clear(&n); if (kGpoint.data) { PORT_ZFree(kGpoint.data, 2*flen + 1); } if (err) { MP_TO_SEC_ERROR(err); rv = SECFailure; } #if EC_DEBUG printf("ECDSA signing with seed %s\n", (rv == SECSuccess) ? "succeeded" : "failed"); #endif #else PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); #endif /* NSS_ENABLE_ECC */ return rv; }
SECStatus NSSCertDBTrustDomain::CheckRevocation( mozilla::pkix::EndEntityOrCA endEntityOrCA, const CERTCertificate* cert, /*const*/ CERTCertificate* issuerCert, PRTime time, /*optional*/ const SECItem* stapledOCSPResponse) { // Actively distrusted certificates will have already been blocked by // GetCertTrust. // TODO: need to verify that IsRevoked isn't called for trust anchors AND // that that fact is documented in mozillapkix. PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: Top of CheckRevocation\n")); PORT_Assert(cert); PORT_Assert(issuerCert); if (!cert || !issuerCert) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } // Bug 991815: The BR allow OCSP for intermediates to be up to one year old. // Since this affects EV there is no reason why DV should be more strict // so all intermediatates are allowed to have OCSP responses up to one year // old. uint16_t maxOCSPLifetimeInDays = 10; if (endEntityOrCA == EndEntityOrCA::MustBeCA) { maxOCSPLifetimeInDays = 365; } // If we have a stapled OCSP response then the verification of that response // determines the result unless the OCSP response is expired. We make an // exception for expired responses because some servers, nginx in particular, // are known to serve expired responses due to bugs. // We keep track of the result of verifying the stapled response but don't // immediately return failure if the response has expired. PRErrorCode stapledOCSPResponseErrorCode = 0; if (stapledOCSPResponse) { PR_ASSERT(endEntityOrCA == MustBeEndEntity); bool expired; SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(cert, issuerCert, time, maxOCSPLifetimeInDays, stapledOCSPResponse, ResponseWasStapled, expired); if (rv == SECSuccess) { // stapled OCSP response present and good Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 1); PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: stapled OCSP response: good")); return rv; } stapledOCSPResponseErrorCode = PR_GetError(); if (stapledOCSPResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE || expired) { // stapled OCSP response present but expired Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 3); PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: expired stapled OCSP response")); } else { // stapled OCSP response present but invalid for some reason Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 4); PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: stapled OCSP response: failure")); return rv; } } else { // no stapled OCSP response Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 2); PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: no stapled OCSP response")); } PRErrorCode cachedResponseErrorCode = 0; PRTime cachedResponseValidThrough = 0; bool cachedResponsePresent = mOCSPCache.Get(cert, issuerCert, cachedResponseErrorCode, cachedResponseValidThrough); if (cachedResponsePresent) { if (cachedResponseErrorCode == 0 && cachedResponseValidThrough >= time) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: cached OCSP response: good")); return SECSuccess; } // If we have a cached revoked response, use it. if (cachedResponseErrorCode == SEC_ERROR_REVOKED_CERTIFICATE) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: cached OCSP response: revoked")); PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0); return SECFailure; } // The cached response may indicate an unknown certificate or it may be // expired. Don't return with either of these statuses yet - we may be // able to fetch a more recent one. PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: cached OCSP response: error %ld valid " "until %lld", cachedResponseErrorCode, cachedResponseValidThrough)); // When a good cached response has expired, it is more convenient // to convert that to an error code and just deal with // cachedResponseErrorCode from here on out. if (cachedResponseErrorCode == 0 && cachedResponseValidThrough < time) { cachedResponseErrorCode = SEC_ERROR_OCSP_OLD_RESPONSE; } // We may have a cached indication of server failure. Ignore it if // it has expired. if (cachedResponseErrorCode != 0 && cachedResponseErrorCode != SEC_ERROR_OCSP_UNKNOWN_CERT && cachedResponseErrorCode != SEC_ERROR_OCSP_OLD_RESPONSE && cachedResponseValidThrough < time) { cachedResponseErrorCode = 0; cachedResponsePresent = false; } } else { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: no cached OCSP response")); } // At this point, if and only if cachedErrorResponseCode is 0, there was no // cached response. PR_ASSERT((!cachedResponsePresent && cachedResponseErrorCode == 0) || (cachedResponsePresent && cachedResponseErrorCode != 0)); // TODO: We still need to handle the fallback for expired responses. But, // if/when we disable OCSP fetching by default, it would be ambiguous whether // security.OCSP.enable==0 means "I want the default" or "I really never want // you to ever fetch OCSP." if ((mOCSPFetching == NeverFetchOCSP) || (endEntityOrCA == MustBeCA && (mOCSPFetching == FetchOCSPForDVHardFail || mOCSPFetching == FetchOCSPForDVSoftFail))) { // We're not going to be doing any fetching, so if there was a cached // "unknown" response, say so. if (cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) { PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0); return SECFailure; } // If we're doing hard-fail, we want to know if we have a cached response // that has expired. if (mOCSPFetching == FetchOCSPForDVHardFail && cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) { PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0); return SECFailure; } return SECSuccess; } if (mOCSPFetching == LocalOnlyOCSPForEV) { PR_SetError(cachedResponseErrorCode != 0 ? cachedResponseErrorCode : SEC_ERROR_OCSP_UNKNOWN_CERT, 0); return SECFailure; } ScopedPtr<char, PORT_Free_string> url(CERT_GetOCSPAuthorityInfoAccessLocation(cert)); if (!url) { if (mOCSPFetching == FetchOCSPForEV || cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) { PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0); return SECFailure; } if (cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) { PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0); return SECFailure; } if (stapledOCSPResponseErrorCode != 0) { PR_SetError(stapledOCSPResponseErrorCode, 0); return SECFailure; } // Nothing to do if we don't have an OCSP responder URI for the cert; just // assume it is good. Note that this is the confusing, but intended, // interpretation of "strict" revocation checking in the face of a // certificate that lacks an OCSP responder URI. return SECSuccess; } ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return SECFailure; } // Only request a response if we didn't have a cached indication of failure // (don't keep requesting responses from a failing server). const SECItem* response = nullptr; if (cachedResponseErrorCode == 0 || cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT || cachedResponseErrorCode == SEC_ERROR_OCSP_OLD_RESPONSE) { const SECItem* request(CreateEncodedOCSPRequest(arena.get(), cert, issuerCert)); if (!request) { return SECFailure; } response = DoOCSPRequest(arena.get(), url.get(), request, OCSPFetchingTypeToTimeoutTime(mOCSPFetching)); } if (!response) { PRErrorCode error = PR_GetError(); if (error == 0) { error = cachedResponseErrorCode; } PRTime timeout = time + ServerFailureDelay; if (mOCSPCache.Put(cert, issuerCert, error, time, timeout) != SECSuccess) { return SECFailure; } PR_SetError(error, 0); if (mOCSPFetching != FetchOCSPForDVSoftFail) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: returning SECFailure after " "OCSP request failure")); return SECFailure; } if (cachedResponseErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: returning SECFailure from cached " "response after OCSP request failure")); PR_SetError(cachedResponseErrorCode, 0); return SECFailure; } if (stapledOCSPResponseErrorCode != 0) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: returning SECFailure from expired " "stapled response after OCSP request failure")); PR_SetError(stapledOCSPResponseErrorCode, 0); return SECFailure; } PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: returning SECSuccess after " "OCSP request failure")); return SECSuccess; // Soft fail -> success :( } // If the response from the network has expired but indicates a revoked // or unknown certificate, PR_GetError() will return the appropriate error. // We actually ignore expired here. bool expired; SECStatus rv = VerifyAndMaybeCacheEncodedOCSPResponse(cert, issuerCert, time, maxOCSPLifetimeInDays, response, ResponseIsFromNetwork, expired); if (rv == SECSuccess || mOCSPFetching != FetchOCSPForDVSoftFail) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: returning after VerifyEncodedOCSPResponse")); return rv; } PRErrorCode error = PR_GetError(); if (error == SEC_ERROR_OCSP_UNKNOWN_CERT || error == SEC_ERROR_REVOKED_CERTIFICATE) { return rv; } if (stapledOCSPResponseErrorCode != 0) { PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: returning SECFailure from expired stapled " "response after OCSP request verification failure")); PR_SetError(stapledOCSPResponseErrorCode, 0); return SECFailure; } PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("NSSCertDBTrustDomain: end of CheckRevocation")); return SECSuccess; // Soft fail -> success :( }
/* ** Performs an ECDH key derivation by computing the scalar point ** multiplication of privateValue and publicValue (with or without the ** cofactor) and returns the x-coordinate of the resulting elliptic ** curve point in derived secret. If successful, derivedSecret->data ** is set to the address of the newly allocated buffer containing the ** derived secret, and derivedSecret->len is the size of the secret ** produced. It is the caller's responsibility to free the allocated ** buffer containing the derived secret. */ SECStatus ECDH_Derive(SECItem *publicValue, ECParams *ecParams, SECItem *privateValue, PRBool withCofactor, SECItem *derivedSecret) { SECStatus rv = SECFailure; #ifdef NSS_ENABLE_ECC unsigned int len = 0; SECItem pointQ = {siBuffer, NULL, 0}; mp_int k; /* to hold the private value */ mp_int cofactor; mp_err err = MP_OKAY; #if EC_DEBUG int i; #endif if (!publicValue || !ecParams || !privateValue || !derivedSecret) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } MP_DIGITS(&k) = 0; memset(derivedSecret, 0, sizeof *derivedSecret); len = (ecParams->fieldID.size + 7) >> 3; pointQ.len = 2*len + 1; if ((pointQ.data = PORT_Alloc(2*len + 1)) == NULL) goto cleanup; CHECK_MPI_OK( mp_init(&k) ); CHECK_MPI_OK( mp_read_unsigned_octets(&k, privateValue->data, (mp_size) privateValue->len) ); if (withCofactor && (ecParams->cofactor != 1)) { /* multiply k with the cofactor */ MP_DIGITS(&cofactor) = 0; CHECK_MPI_OK( mp_init(&cofactor) ); mp_set(&cofactor, ecParams->cofactor); CHECK_MPI_OK( mp_mul(&k, &cofactor, &k) ); } /* Multiply our private key and peer's public point */ if (ec_points_mul(ecParams, NULL, &k, publicValue, &pointQ) != SECSuccess) goto cleanup; if (ec_point_at_infinity(&pointQ)) { PORT_SetError(SEC_ERROR_BAD_KEY); /* XXX better error code? */ goto cleanup; } /* Allocate memory for the derived secret and copy * the x co-ordinate of pointQ into it. */ SECITEM_AllocItem(NULL, derivedSecret, len); memcpy(derivedSecret->data, pointQ.data + 1, len); rv = SECSuccess; #if EC_DEBUG printf("derived_secret:\n"); for (i = 0; i < derivedSecret->len; i++) printf("%02x:", derivedSecret->data[i]); printf("\n"); #endif cleanup: mp_clear(&k); if (err) { MP_TO_SEC_ERROR(err); } if (pointQ.data) { PORT_ZFree(pointQ.data, 2*len + 1); } #else PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG); #endif /* NSS_ENABLE_ECC */ return rv; }