int sign_value(pkcs11_handle_t *h, cert_object_t *cert, CK_BYTE *data, CK_ULONG length, CK_BYTE **signature, CK_ULONG *signature_length) { SECOidTag algtag; SECKEYPrivateKey *key; SECItem result; SECStatus rv; if (h->slot == NULL) { return -1; } /* get the key */ key = PK11_FindPrivateKeyFromCert(h->slot, (CERTCertificate *)cert, NULL); if (key == NULL) { DBG1("Couldn't Find key for Cert: %s", SECU_Strerror(PR_GetError())); return -1; } /* get the oid */ algtag = SEC_GetSignatureAlgorithmOidTag(key->keyType, SEC_OID_SHA1); /* sign the data */ rv = SEC_SignData(&result, data, length, key, algtag); SECKEY_DestroyPrivateKey(key); if (rv != SECSuccess) { DBG1("Signature failed: %s", SECU_Strerror(PR_GetError())); return -1; } *signature = (CK_BYTE *)result.data; *signature_length = result.len; return 0; }
char *oauth_sign_rsa_sha1 (const char *m, const char *k) { PK11SlotInfo *slot = NULL; SECKEYPrivateKey *pkey = NULL; SECItem signature; SECStatus s; SECItem der; char *rv=NULL; char *key = oauth_strip_pkcs(k, NS_PRIV_HEADER, NS_PRIV_TRAILER); if (!key) return NULL; oauth_init_nss(); slot = PK11_GetInternalKeySlot(); if (!slot) goto looser; s = ATOB_ConvertAsciiToItem(&der, key); if (s != SECSuccess) goto looser; s = PK11_ImportDERPrivateKeyInfoAndReturnKey(slot, &der, NULL, NULL, PR_FALSE, PR_TRUE, KU_ALL, &pkey, NULL); SECITEM_FreeItem(&der, PR_FALSE); if (s != SECSuccess) goto looser; if (!pkey) goto looser; if (pkey->keyType != rsaKey) goto looser; s = SEC_SignData(&signature, (unsigned char*) m, strlen(m), pkey, SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE); if (s != SECSuccess) goto looser; rv=oauth_encode_base64(signature.len, signature.data); SECITEM_FreeItem(&signature, PR_FALSE); looser: if (pkey) SECKEY_DestroyPrivateKey(pkey); if (slot) PK11_FreeSlot(slot); free(key); return rv; }
SECStatus SEC_DerSignData(PRArenaPool *arena, SECItem *result, unsigned char *buf, int len, SECKEYPrivateKey *pk, SECOidTag algID) { SECItem it; CERTSignedData sd; SECStatus rv; it.data = 0; /* XXX We should probably have some asserts here to make sure the key type * and algID match */ if (algID == SEC_OID_UNKNOWN) { switch(pk->keyType) { case rsaKey: algID = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; break; case dsaKey: algID = SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST; break; case ecKey: algID = SEC_OID_ANSIX962_ECDSA_SIGNATURE_WITH_SHA1_DIGEST; break; default: PORT_SetError(SEC_ERROR_INVALID_KEY); return SECFailure; } } /* Sign input buffer */ rv = SEC_SignData(&it, buf, len, pk, algID); if (rv) goto loser; /* Fill out SignedData object */ PORT_Memset(&sd, 0, sizeof(sd)); sd.data.data = buf; sd.data.len = len; sd.signature.data = it.data; sd.signature.len = it.len << 3; /* convert to bit string */ rv = SECOID_SetAlgorithmID(arena, &sd.signatureAlgorithm, algID, 0); if (rv) goto loser; /* DER encode the signed data object */ rv = DER_Encode(arena, result, CERTSignedDataTemplate, &sd); /* FALL THROUGH */ loser: PORT_Free(it.data); return rv; }
// A U2F Sign operation creates a signature over the "param" arguments (plus // some other stuff) using the private key indicated in the key handle argument. // // The format of the signed data is as follows: // // 32 Application parameter // 1 User presence (0x01) // 4 Counter // 32 Challenge parameter // // The format of the signature data is as follows: // // 1 User presence // 4 Counter // * Signature // nsresult U2FSoftTokenManager::Sign(nsTArray<uint8_t>& aApplication, nsTArray<uint8_t>& aChallenge, nsTArray<uint8_t>& aKeyHandle, nsTArray<uint8_t>& aSignature) { nsNSSShutDownPreventionLock locker; if (NS_WARN_IF(isAlreadyShutDown())) { return NS_ERROR_NOT_AVAILABLE; } MOZ_ASSERT(mInitialized); if (NS_WARN_IF(!mInitialized)) { nsresult rv = Init(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } MOZ_ASSERT(mWrappingKey); UniquePK11SlotInfo slot(PK11_GetInternalSlot()); MOZ_ASSERT(slot.get()); if (NS_WARN_IF((aChallenge.Length() != kParamLen) || (aApplication.Length() != kParamLen))) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Parameter lengths are wrong! challenge=%d app=%d expected=%d", (uint32_t)aChallenge.Length(), (uint32_t)aApplication.Length(), kParamLen)); return NS_ERROR_ILLEGAL_VALUE; } // Decode the key handle UniqueSECKEYPrivateKey privKey = PrivateKeyFromKeyHandle(slot, mWrappingKey, aKeyHandle.Elements(), aKeyHandle.Length(), aApplication.Elements(), aApplication.Length(), locker); if (NS_WARN_IF(!privKey.get())) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Couldn't get the priv key!")); return NS_ERROR_FAILURE; } // Increment the counter and turn it into a SECItem mCounter += 1; ScopedAutoSECItem counterItem(4); counterItem.data[0] = (mCounter >> 24) & 0xFF; counterItem.data[1] = (mCounter >> 16) & 0xFF; counterItem.data[2] = (mCounter >> 8) & 0xFF; counterItem.data[3] = (mCounter >> 0) & 0xFF; uint32_t counter = mCounter; AbstractThread::MainThread()->Dispatch(NS_NewRunnableFunction( [counter] () { MOZ_ASSERT(NS_IsMainThread()); Preferences::SetUint(PREF_U2F_NSSTOKEN_COUNTER, counter); })); // Compute the signature mozilla::dom::CryptoBuffer signedDataBuf; if (NS_WARN_IF(!signedDataBuf.SetCapacity(1 + 4 + (2 * kParamLen), mozilla::fallible))) { return NS_ERROR_OUT_OF_MEMORY; } // It's OK to ignore the return values here because we're writing into // pre-allocated space signedDataBuf.AppendElements(aApplication.Elements(), aApplication.Length(), mozilla::fallible); signedDataBuf.AppendElement(0x01, mozilla::fallible); signedDataBuf.AppendSECItem(counterItem); signedDataBuf.AppendElements(aChallenge.Elements(), aChallenge.Length(), mozilla::fallible); if (MOZ_LOG_TEST(gNSSTokenLog, LogLevel::Debug)) { nsAutoCString base64; nsresult rv = Base64URLEncode(signedDataBuf.Length(), signedDataBuf.Elements(), Base64URLEncodePaddingPolicy::Omit, base64); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; } MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("U2F Token signing bytes (base64): %s", base64.get())); } ScopedAutoSECItem signatureItem; SECStatus srv = SEC_SignData(&signatureItem, signedDataBuf.Elements(), signedDataBuf.Length(), privKey.get(), SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE); if (NS_WARN_IF(srv != SECSuccess)) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Signature failure: %d", PORT_GetError())); return NS_ERROR_FAILURE; } // Assemble the signature data into a buffer for return mozilla::dom::CryptoBuffer signatureBuf; if (NS_WARN_IF(!signatureBuf.SetCapacity(1 + counterItem.len + signatureItem.len, mozilla::fallible))) { return NS_ERROR_OUT_OF_MEMORY; } // It's OK to ignore the return values here because we're writing into // pre-allocated space signatureBuf.AppendElement(0x01, mozilla::fallible); signatureBuf.AppendSECItem(counterItem); signatureBuf.AppendSECItem(signatureItem); aSignature = signatureBuf; return NS_OK; }
// A U2F Register operation causes a new key pair to be generated by the token. // The token then returns the public key of the key pair, and a handle to the // private key, which is a fancy way of saying "key wrapped private key", as // well as the generated attestation certificate and a signature using that // certificate's private key. // // The KeyHandleFromPrivateKey and PrivateKeyFromKeyHandle methods perform // the actual key wrap/unwrap operations. // // The format of the return registration data is as follows: // // Bytes Value // 1 0x05 // 65 public key // 1 key handle length // * key handle // ASN.1 attestation certificate // * attestation signature // nsresult U2FSoftTokenManager::Register(nsTArray<uint8_t>& aApplication, nsTArray<uint8_t>& aChallenge, /* out */ nsTArray<uint8_t>& aRegistration, /* out */ nsTArray<uint8_t>& aSignature) { nsNSSShutDownPreventionLock locker; if (NS_WARN_IF(isAlreadyShutDown())) { return NS_ERROR_NOT_AVAILABLE; } if (!mInitialized) { nsresult rv = Init(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } // We should already have a wrapping key MOZ_ASSERT(mWrappingKey); UniquePK11SlotInfo slot(PK11_GetInternalSlot()); MOZ_ASSERT(slot.get()); // Construct a one-time-use Attestation Certificate UniqueSECKEYPrivateKey attestPrivKey; UniqueCERTCertificate attestCert; nsresult rv = GetAttestationCertificate(slot, attestPrivKey, attestCert, locker); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; } MOZ_ASSERT(attestCert); MOZ_ASSERT(attestPrivKey); // Generate a new keypair; the private will be wrapped into a Key Handle UniqueSECKEYPrivateKey privKey; UniqueSECKEYPublicKey pubKey; rv = GenEcKeypair(slot, privKey, pubKey, locker); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; } // The key handle will be the result of keywrap(privKey, key=mWrappingKey) UniqueSECItem keyHandleItem = KeyHandleFromPrivateKey(slot, mWrappingKey, aApplication.Elements(), aApplication.Length(), privKey, locker); if (NS_WARN_IF(!keyHandleItem.get())) { return NS_ERROR_FAILURE; } // Sign the challenge using the Attestation privkey (from attestCert) mozilla::dom::CryptoBuffer signedDataBuf; if (NS_WARN_IF(!signedDataBuf.SetCapacity(1 + aApplication.Length() + aChallenge.Length() + keyHandleItem->len + kPublicKeyLen, mozilla::fallible))) { return NS_ERROR_OUT_OF_MEMORY; } // // It's OK to ignore the return values here because we're writing into // // pre-allocated space signedDataBuf.AppendElement(0x00, mozilla::fallible); signedDataBuf.AppendElements(aApplication, mozilla::fallible); signedDataBuf.AppendElements(aChallenge, mozilla::fallible); signedDataBuf.AppendSECItem(keyHandleItem.get()); signedDataBuf.AppendSECItem(pubKey->u.ec.publicValue); ScopedAutoSECItem signatureItem; SECStatus srv = SEC_SignData(&signatureItem, signedDataBuf.Elements(), signedDataBuf.Length(), attestPrivKey.get(), SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE); if (NS_WARN_IF(srv != SECSuccess)) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Signature failure: %d", PORT_GetError())); return NS_ERROR_FAILURE; } // Serialize the registration data mozilla::dom::CryptoBuffer registrationBuf; if (NS_WARN_IF(!registrationBuf.SetCapacity(1 + kPublicKeyLen + 1 + keyHandleItem->len + attestCert.get()->derCert.len + signatureItem.len, mozilla::fallible))) { return NS_ERROR_OUT_OF_MEMORY; } registrationBuf.AppendElement(0x05, mozilla::fallible); registrationBuf.AppendSECItem(pubKey->u.ec.publicValue); registrationBuf.AppendElement(keyHandleItem->len, mozilla::fallible); registrationBuf.AppendSECItem(keyHandleItem.get()); registrationBuf.AppendSECItem(attestCert.get()->derCert); registrationBuf.AppendSECItem(signatureItem); aRegistration = registrationBuf; return NS_OK; }
static int SignFile(FILE *outFile, PRFileDesc *inFile, CERTCertificate *cert) { SECItem data2sign; SECStatus rv; char *data; SECKEYPrivateKey *privKey; SECOidTag algID; PLArenaPool *arena; CERTSignedData sd; SECItem *result; if (outFile == NULL || inFile == NULL || cert == NULL) return -1; /* suck the file in */ if (SECU_ReadDERFromFile(&data2sign, inFile, PR_FALSE, PR_FALSE) != SECSuccess) return -1; privKey = NULL; privKey = PK11_FindKeyByAnyCert(cert, NULL); algID = SEC_GetSignatureAlgorithmOidTag(privKey->keyType, SEC_OID_SHA1); if (algID == SEC_OID_UNKNOWN) return -1; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); PORT_Memset(&sd, 0, sizeof(CERTSignedData)); rv = SEC_SignData(&(sd.signature), data2sign.data, data2sign.len, privKey, algID); if (rv != SECSuccess) { fprintf (stderr, "Could not sign.\n"); return -1; } sd.signature.len = sd.signature.len << 3; rv = SECOID_SetAlgorithmID(arena, &sd.signatureAlgorithm, algID, 0); if (rv != SECSuccess) { fprintf (stderr, "Could not set alg id.\n"); return -1; } result = SEC_ASN1EncodeItem(arena, NULL, &sd, CERTSignatureDataTemplate); SECITEM_FreeItem(&(sd.signature), PR_FALSE); if (!result) { fprintf (stderr, "Could not encode.\n"); return -1; } data = PL_Base64Encode((const char*)result->data, result->len, NULL); if (!data) return -1; fputs("signature:\n", outFile); fputs(data, outFile); fputs("\n", outFile); ExportPublicKey(outFile, cert); SECKEY_DestroyPrivateKey(privKey); PORT_FreeArena(arena, PR_FALSE); return 0; }
/* * NSS_CMSSignerInfo_Sign - sign something * */ SECStatus NSS_CMSSignerInfo_Sign(NSSCMSSignerInfo *signerinfo, SECItem *digest, SECItem *contentType) { CERTCertificate *cert; SECKEYPrivateKey *privkey = NULL; SECOidTag digestalgtag; SECOidTag pubkAlgTag; SECItem signature = { 0 }; SECStatus rv; PLArenaPool *poolp, *tmppoolp = NULL; SECAlgorithmID *algID, freeAlgID; CERTSubjectPublicKeyInfo *spki; PORT_Assert (digest != NULL); poolp = signerinfo->cmsg->poolp; switch (signerinfo->signerIdentifier.identifierType) { case NSSCMSSignerID_IssuerSN: cert = signerinfo->cert; privkey = PK11_FindKeyByAnyCert(cert, signerinfo->cmsg->pwfn_arg); if (privkey == NULL) goto loser; algID = &cert->subjectPublicKeyInfo.algorithm; break; case NSSCMSSignerID_SubjectKeyID: privkey = signerinfo->signingKey; signerinfo->signingKey = NULL; spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey); SECKEY_DestroyPublicKey(signerinfo->pubKey); signerinfo->pubKey = NULL; SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm); SECKEY_DestroySubjectPublicKeyInfo(spki); algID = &freeAlgID; break; default: goto loser; } digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo); /* * XXX I think there should be a cert-level interface for this, * so that I do not have to know about subjectPublicKeyInfo... */ pubkAlgTag = SECOID_GetAlgorithmTag(algID); if (signerinfo->signerIdentifier.identifierType == NSSCMSSignerID_SubjectKeyID) { SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE); } if (signerinfo->authAttr != NULL) { SECOidTag signAlgTag; SECItem encoded_attrs; /* find and fill in the message digest attribute. */ rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr), SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE); if (rv != SECSuccess) goto loser; if (contentType != NULL) { /* if the caller wants us to, find and fill in the content type attribute. */ rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr), SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE); if (rv != SECSuccess) goto loser; } if ((tmppoolp = PORT_NewArena (1024)) == NULL) { PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser; } /* * Before encoding, reorder the attributes so that when they * are encoded, they will be conforming DER, which is required * to have a specific order and that is what must be used for * the hash/signature. We do this here, rather than building * it into EncodeAttributes, because we do not want to do * such reordering on incoming messages (which also uses * EncodeAttributes) or our old signatures (and other "broken" * implementations) will not verify. So, we want to guarantee * that we send out good DER encodings of attributes, but not * to expect to receive them. */ if (NSS_CMSAttributeArray_Reorder(signerinfo->authAttr) != SECSuccess) goto loser; encoded_attrs.data = NULL; encoded_attrs.len = 0; if (NSS_CMSAttributeArray_Encode(tmppoolp, &(signerinfo->authAttr), &encoded_attrs) == NULL) goto loser; signAlgTag = SEC_GetSignatureAlgorithmOidTag(privkey->keyType, digestalgtag); if (signAlgTag == SEC_OID_UNKNOWN) { PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); goto loser; } rv = SEC_SignData(&signature, encoded_attrs.data, encoded_attrs.len, privkey, signAlgTag); PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */ tmppoolp = 0; } else { rv = SGN_Digest(privkey, digestalgtag, &signature, digest); } SECKEY_DestroyPrivateKey(privkey); privkey = NULL; if (rv != SECSuccess) goto loser; if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) != SECSuccess) goto loser; SECITEM_FreeItem(&signature, PR_FALSE); if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), pubkAlgTag, NULL) != SECSuccess) goto loser; return SECSuccess; loser: if (signature.len != 0) SECITEM_FreeItem (&signature, PR_FALSE); if (privkey) SECKEY_DestroyPrivateKey(privkey); if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE); return SECFailure; }
/* * SecCmsSignerInfoSign - sign something * */ OSStatus SecCmsSignerInfoSign(SecCmsSignerInfoRef signerinfo, CSSM_DATA_PTR digest, CSSM_DATA_PTR contentType) { SecCertificateRef cert; SecPrivateKeyRef privkey = NULL; SECOidTag digestalgtag; SECOidTag pubkAlgTag; CSSM_DATA signature = { 0 }; OSStatus rv; PLArenaPool *poolp, *tmppoolp = NULL; const SECAlgorithmID *algID; SECAlgorithmID freeAlgID; //CERTSubjectPublicKeyInfo *spki; PORT_Assert (digest != NULL); poolp = signerinfo->cmsg->poolp; switch (signerinfo->signerIdentifier.identifierType) { case SecCmsSignerIDIssuerSN: privkey = signerinfo->signingKey; signerinfo->signingKey = NULL; cert = signerinfo->cert; if (SecCertificateGetAlgorithmID(cert,&algID)) { PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); goto loser; } break; case SecCmsSignerIDSubjectKeyID: privkey = signerinfo->signingKey; signerinfo->signingKey = NULL; #if 0 spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey); SECKEY_DestroyPublicKey(signerinfo->pubKey); signerinfo->pubKey = NULL; SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm); SECKEY_DestroySubjectPublicKeyInfo(spki); algID = &freeAlgID; #else #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR)) if (SecKeyGetAlgorithmID(signerinfo->pubKey,&algID)) { #else /* TBD: Unify this code. Currently, iOS has an incompatible * SecKeyGetAlgorithmID implementation. */ if (true) { #endif PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); goto loser; } CFRelease(signerinfo->pubKey); signerinfo->pubKey = NULL; #endif break; default: PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE); goto loser; } digestalgtag = SecCmsSignerInfoGetDigestAlgTag(signerinfo); /* * XXX I think there should be a cert-level interface for this, * so that I do not have to know about subjectPublicKeyInfo... */ pubkAlgTag = SECOID_GetAlgorithmTag(algID); if (signerinfo->signerIdentifier.identifierType == SecCmsSignerIDSubjectKeyID) { SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE); } #if 0 // @@@ Not yet /* Fortezza MISSI have weird signature formats. * Map them to standard DSA formats */ pubkAlgTag = PK11_FortezzaMapSig(pubkAlgTag); #endif if (signerinfo->authAttr != NULL) { CSSM_DATA encoded_attrs; /* find and fill in the message digest attribute. */ rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr), SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE); if (rv != SECSuccess) goto loser; if (contentType != NULL) { /* if the caller wants us to, find and fill in the content type attribute. */ rv = SecCmsAttributeArraySetAttr(poolp, &(signerinfo->authAttr), SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE); if (rv != SECSuccess) goto loser; } if ((tmppoolp = PORT_NewArena (1024)) == NULL) { PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser; } /* * Before encoding, reorder the attributes so that when they * are encoded, they will be conforming DER, which is required * to have a specific order and that is what must be used for * the hash/signature. We do this here, rather than building * it into EncodeAttributes, because we do not want to do * such reordering on incoming messages (which also uses * EncodeAttributes) or our old signatures (and other "broken" * implementations) will not verify. So, we want to guarantee * that we send out good DER encodings of attributes, but not * to expect to receive them. */ if (SecCmsAttributeArrayReorder(signerinfo->authAttr) != SECSuccess) goto loser; encoded_attrs.Data = NULL; encoded_attrs.Length = 0; if (SecCmsAttributeArrayEncode(tmppoolp, &(signerinfo->authAttr), &encoded_attrs) == NULL) goto loser; rv = SEC_SignData(&signature, encoded_attrs.Data, (int)encoded_attrs.Length, privkey, digestalgtag, pubkAlgTag); PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */ tmppoolp = 0; } else { rv = SGN_Digest(privkey, digestalgtag, pubkAlgTag, &signature, digest); } SECKEY_DestroyPrivateKey(privkey); privkey = NULL; if (rv != SECSuccess) goto loser; if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) != SECSuccess) goto loser; SECITEM_FreeItem(&signature, PR_FALSE); if(pubkAlgTag == SEC_OID_EC_PUBLIC_KEY) { /* * RFC 3278 section section 2.1.1 states that the signatureAlgorithm * field contains the full ecdsa-with-SHA1 OID, not plain old ecPublicKey * as would appear in other forms of signed datas. However Microsoft doesn't * do this, it puts ecPublicKey there, and if we put ecdsa-with-SHA1 there, * MS can't verify - presumably because it takes the digest of the digest * before feeding it to ECDSA. * We handle this with a preference; default if it's not there is * "Microsoft compatibility mode". */ if(!SecCmsMsEcdsaCompatMode()) { pubkAlgTag = SEC_OID_ECDSA_WithSHA1; } /* else violating the spec for compatibility */ } if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), pubkAlgTag, NULL) != SECSuccess) goto loser; return SECSuccess; loser: if (signature.Length != 0) SECITEM_FreeItem (&signature, PR_FALSE); if (privkey) SECKEY_DestroyPrivateKey(privkey); if (tmppoolp) PORT_FreeArena(tmppoolp, PR_FALSE); return SECFailure; } OSStatus SecCmsSignerInfoVerifyCertificate(SecCmsSignerInfoRef signerinfo, SecKeychainRef keychainOrArray, CFTypeRef policies, SecTrustRef *trustRef) { SecCertificateRef cert; CFAbsoluteTime stime; OSStatus rv; CSSM_DATA_PTR *otherCerts; if ((cert = SecCmsSignerInfoGetSigningCertificate(signerinfo, keychainOrArray)) == NULL) { dprintf("SecCmsSignerInfoVerifyCertificate: no signing cert\n"); signerinfo->verificationStatus = SecCmsVSSigningCertNotFound; return SECFailure; } /* * Get and convert the signing time; if available, it will be used * both on the cert verification and for importing the sender * email profile. */ CFTypeRef timeStampPolicies=SecPolicyCreateAppleTimeStampingAndRevocationPolicies(policies); if (SecCmsSignerInfoGetTimestampTimeWithPolicy(signerinfo, timeStampPolicies, &stime) != SECSuccess) if (SecCmsSignerInfoGetSigningTime(signerinfo, &stime) != SECSuccess) stime = CFAbsoluteTimeGetCurrent(); CFReleaseSafe(timeStampPolicies); rv = SecCmsSignedDataRawCerts(signerinfo->sigd, &otherCerts); if(rv) { return rv; } rv = CERT_VerifyCert(keychainOrArray, cert, otherCerts, policies, stime, trustRef); dprintfRC("SecCmsSignerInfoVerifyCertificate after vfy: certp %p cert.rc %d\n", cert, (int)CFGetRetainCount(cert)); if (rv || !trustRef) { if (PORT_GetError() == SEC_ERROR_UNTRUSTED_CERT) { /* Signature or digest level verificationStatus errors should supercede certificate level errors, so only change the verificationStatus if the status was GoodSignature. */ if (signerinfo->verificationStatus == SecCmsVSGoodSignature) signerinfo->verificationStatus = SecCmsVSSigningCertNotTrusted; } } /* FIXME isn't this leaking the cert? */ dprintf("SecCmsSignerInfoVerifyCertificate: CertVerify rtn %d\n", (int)rv); return rv; }