SECStatus crmf_destroy_encrypted_value(CRMFEncryptedValue *inEncrValue, PRBool freeit) { if (inEncrValue != NULL) { if (inEncrValue->intendedAlg) { SECOID_DestroyAlgorithmID(inEncrValue->intendedAlg, PR_TRUE); inEncrValue->intendedAlg = NULL; } if (inEncrValue->symmAlg) { SECOID_DestroyAlgorithmID(inEncrValue->symmAlg, PR_TRUE); inEncrValue->symmAlg = NULL; } if (inEncrValue->encSymmKey.data) { PORT_Free(inEncrValue->encSymmKey.data); inEncrValue->encSymmKey.data = NULL; } if (inEncrValue->keyAlg) { SECOID_DestroyAlgorithmID(inEncrValue->keyAlg, PR_TRUE); inEncrValue->keyAlg = NULL; } if (inEncrValue->valueHint.data) { PORT_Free(inEncrValue->valueHint.data); inEncrValue->valueHint.data = NULL; } if (inEncrValue->encValue.data) { PORT_Free(inEncrValue->encValue.data); inEncrValue->encValue.data = NULL; } if (freeit) { PORT_Free(inEncrValue); } } return SECSuccess; }
SECStatus crmf_copy_encryptedvalue_secalg(PLArenaPool *poolp, SECAlgorithmID *srcAlgId, SECAlgorithmID **destAlgId) { SECAlgorithmID *newAlgId; SECStatus rv; newAlgId = (poolp != NULL) ? PORT_ArenaZNew(poolp, SECAlgorithmID) : PORT_ZNew(SECAlgorithmID); if (newAlgId == NULL) { return SECFailure; } rv = SECOID_CopyAlgorithmID(poolp, newAlgId, srcAlgId); if (rv != SECSuccess) { if (!poolp) { SECOID_DestroyAlgorithmID(newAlgId, PR_TRUE); } return rv; } *destAlgId = newAlgId; return rv; }
SECAlgorithmID * CRMF_POPOSigningKeyGetAlgID(CRMFPOPOSigningKey *inSignKey) { SECAlgorithmID *newAlgId = NULL; SECStatus rv; PORT_Assert(inSignKey != NULL); if (inSignKey == NULL) { return NULL; } newAlgId = PORT_ZNew(SECAlgorithmID); if (newAlgId == NULL) { goto loser; } rv = SECOID_CopyAlgorithmID(NULL, newAlgId, inSignKey->algorithmIdentifier); if (rv != SECSuccess) { goto loser; } return newAlgId; loser: if (newAlgId != NULL) { SECOID_DestroyAlgorithmID(newAlgId, PR_TRUE); } return NULL; }
/* * SecCmsEncryptedDataCreate - create an empty encryptedData object. * * "algorithm" specifies the bulk encryption algorithm to use. * "keysize" is the key size. * * An error results in a return value of NULL and an error set. * (Retrieve specific errors via PORT_GetError()/XP_GetError().) */ SecCmsEncryptedDataRef SecCmsEncryptedDataCreate(SecCmsMessageRef cmsg, SECOidTag algorithm, int keysize) { void *mark; SecCmsEncryptedDataRef encd; PLArenaPool *poolp; #if 0 SECAlgorithmID *pbe_algid; #endif OSStatus rv; poolp = cmsg->poolp; mark = PORT_ArenaMark(poolp); encd = (SecCmsEncryptedDataRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsEncryptedData)); if (encd == NULL) goto loser; encd->cmsg = cmsg; /* version is set in SecCmsEncryptedDataEncodeBeforeStart() */ switch (algorithm) { /* XXX hmmm... hardcoded algorithms? */ case SEC_OID_RC2_CBC: case SEC_OID_DES_EDE3_CBC: case SEC_OID_DES_CBC: rv = SecCmsContentInfoSetContentEncAlg((SecArenaPoolRef)poolp, &(encd->contentInfo), algorithm, NULL, keysize); break; default: /* Assume password-based-encryption. At least, try that. */ #if 1 // @@@ Fix me rv = SECFailure; break; #else pbe_algid = PK11_CreatePBEAlgorithmID(algorithm, 1, NULL); if (pbe_algid == NULL) { rv = SECFailure; break; } rv = SecCmsContentInfoSetContentEncAlgID((SecArenaPoolRef)poolp, &(encd->contentInfo), pbe_algid, keysize); SECOID_DestroyAlgorithmID (pbe_algid, PR_TRUE); break; #endif } if (rv != SECSuccess) goto loser; PORT_ArenaUnmark(poolp, mark); return encd; loser: PORT_ArenaRelease(poolp, mark); return NULL; }
/* * NSS_CMSEncryptedData_Create - create an empty encryptedData object. * * "algorithm" specifies the bulk encryption algorithm to use. * "keysize" is the key size. * * An error results in a return value of NULL and an error set. * (Retrieve specific errors via PORT_GetError()/XP_GetError().) */ NSSCMSEncryptedData * NSS_CMSEncryptedData_Create(NSSCMSMessage *cmsg, SECOidTag algorithm, int keysize) { void *mark; NSSCMSEncryptedData *encd; PLArenaPool *poolp; SECAlgorithmID *pbe_algid; SECStatus rv; poolp = cmsg->poolp; mark = PORT_ArenaMark(poolp); encd = PORT_ArenaZNew(poolp, NSSCMSEncryptedData); if (encd == NULL) goto loser; encd->cmsg = cmsg; /* version is set in NSS_CMSEncryptedData_Encode_BeforeStart() */ if (!SEC_PKCS5IsAlgorithmPBEAlgTag(algorithm)) { rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, &(encd->contentInfo), algorithm, NULL, keysize); } else { /* Assume password-based-encryption. * Note: we can't generate pkcs5v2 from this interface. * PK11_CreateBPEAlgorithmID generates pkcs5v2 by accepting * non-PBE oids and assuming that they are pkcs5v2 oids, but * NSS_CMSEncryptedData_Create accepts non-PBE oids as regular * CMS encrypted data, so we can't tell NSS_CMS_EncryptedData_Create * to create pkcs5v2 PBEs */ pbe_algid = PK11_CreatePBEAlgorithmID(algorithm, 1, NULL); if (pbe_algid == NULL) { rv = SECFailure; } else { rv = NSS_CMSContentInfo_SetContentEncAlgID(poolp, &(encd->contentInfo), pbe_algid, keysize); SECOID_DestroyAlgorithmID(pbe_algid, PR_TRUE); } } if (rv != SECSuccess) goto loser; PORT_ArenaUnmark(poolp, mark); return encd; loser: PORT_ArenaRelease(poolp, mark); return NULL; }
int crypto_cert_get_pub_exp_mod(CryptoCert cert, uint32 * key_len, uint8 * exponent, uint32 exp_len, uint8 * modulus, uint32 mod_len) { SECKEYPublicKey * pubkey; SECOidTag tag = SECOID_GetAlgorithmTag(&cert->cert->subjectPublicKeyInfo.algorithm); if ((tag == SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION) || (tag == SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE)) { /* For some reason, Microsoft sets the OID of the Public RSA key to the oid for "MD5 with RSA Encryption" instead of "RSA Encryption". */ SECAlgorithmID org = cert->cert->subjectPublicKeyInfo.algorithm; SECStatus s = SECOID_SetAlgorithmID(cert->cert->subjectPublicKeyInfo.arena, &cert->cert->subjectPublicKeyInfo.algorithm, SEC_OID_PKCS1_RSA_ENCRYPTION, NULL); check(s, "Error setting temp algo oid"); pubkey = SECKEY_ExtractPublicKey(&cert->cert->subjectPublicKeyInfo); SECOID_DestroyAlgorithmID(&cert->cert->subjectPublicKeyInfo.algorithm, False); cert->cert->subjectPublicKeyInfo.algorithm = org; } else { pubkey = SECKEY_ExtractPublicKey(&cert->cert->subjectPublicKeyInfo); } ASSERT(pubkey); ASSERT(pubkey->keyType == rsaKey); *key_len = SECKEY_PublicKeyStrength(pubkey); size_t l = pubkey->u.rsa.publicExponent.len; ASSERT(l <= exp_len); memset(exponent, 0, exp_len - l); memcpy(exponent + exp_len - l, pubkey->u.rsa.publicExponent.data, l); l = pubkey->u.rsa.modulus.len; ASSERT(l <= mod_len); ASSERT(*key_len <= mod_len); memset(modulus, 0, *key_len - l); memcpy(modulus + *key_len - l, pubkey->u.rsa.modulus.data, l); SECKEY_DestroyPublicKey(pubkey); 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; }
static gchar* cipher_pbkdf2_nss_sha1(const gchar *passphrase, const gchar *salt, guint iter_count, guint out_len) { PK11SlotInfo *slot; SECAlgorithmID *algorithm = NULL; PK11SymKey *symkey = NULL; const SECItem *symkey_data = NULL; SECItem salt_item, passphrase_item; guchar *passphrase_buff, *salt_buff; gchar *ret; g_return_val_if_fail(passphrase != NULL, NULL); g_return_val_if_fail(iter_count > 0, NULL); g_return_val_if_fail(out_len > 0, NULL); NSS_NoDB_Init(NULL); slot = PK11_GetBestSlot(PK11_AlgtagToMechanism(SEC_OID_PKCS5_PBKDF2), NULL); if (slot == NULL) { purple_debug_error("cipher-test", "NSS: couldn't get slot: " "%d\n", PR_GetError()); return NULL; } salt_buff = (guchar*)g_strdup(salt ? salt : ""); salt_item.type = siBuffer; salt_item.data = salt_buff; salt_item.len = salt ? strlen(salt) : 0; algorithm = PK11_CreatePBEV2AlgorithmID(SEC_OID_PKCS5_PBKDF2, SEC_OID_AES_256_CBC, SEC_OID_HMAC_SHA1, out_len, iter_count, &salt_item); if (algorithm == NULL) { purple_debug_error("cipher-test", "NSS: couldn't create " "algorithm ID: %d\n", PR_GetError()); PK11_FreeSlot(slot); g_free(salt_buff); return NULL; } passphrase_buff = (guchar*)g_strdup(passphrase); passphrase_item.type = siBuffer; passphrase_item.data = passphrase_buff; passphrase_item.len = strlen(passphrase); symkey = PK11_PBEKeyGen(slot, algorithm, &passphrase_item, PR_FALSE, NULL); if (symkey == NULL) { purple_debug_error("cipher-test", "NSS: Couldn't generate key: " "%d\n", PR_GetError()); SECOID_DestroyAlgorithmID(algorithm, PR_TRUE); PK11_FreeSlot(slot); g_free(passphrase_buff); g_free(salt_buff); return NULL; } if (PK11_ExtractKeyValue(symkey) == SECSuccess) symkey_data = PK11_GetKeyData(symkey); if (symkey_data == NULL || symkey_data->data == NULL) { purple_debug_error("cipher-test", "NSS: Couldn't extract key " "value: %d\n", PR_GetError()); PK11_FreeSymKey(symkey); SECOID_DestroyAlgorithmID(algorithm, PR_TRUE); PK11_FreeSlot(slot); g_free(passphrase_buff); g_free(salt_buff); return NULL; } if (symkey_data->len != out_len) { purple_debug_error("cipher-test", "NSS: Invalid key length: %d " "(should be %d)\n", symkey_data->len, out_len); PK11_FreeSymKey(symkey); SECOID_DestroyAlgorithmID(algorithm, PR_TRUE); PK11_FreeSlot(slot); g_free(passphrase_buff); g_free(salt_buff); return NULL; } ret = purple_base16_encode(symkey_data->data, symkey_data->len); PK11_FreeSymKey(symkey); SECOID_DestroyAlgorithmID(algorithm, PR_TRUE); PK11_FreeSlot(slot); g_free(passphrase_buff); g_free(salt_buff); return ret; }
/** * @brief Create a key from the given passphrase. By default, the PBKDF2 * algorithm is used to generate the key from the passphrase. It is expected * that the same pass phrase will generate the same key, regardless of the * backend crypto platform used. The key is cleaned up when the context * is cleaned, and may be reused with multiple encryption or decryption * operations. * @note If *key is NULL, a apr_crypto_key_t will be created from a pool. If * *key is not NULL, *key must point at a previously created structure. * @param key The key returned, see note. * @param ivSize The size of the initialisation vector will be returned, based * on whether an IV is relevant for this type of crypto. * @param pass The passphrase to use. * @param passLen The passphrase length in bytes * @param salt The salt to use. * @param saltLen The salt length in bytes * @param type 3DES_192, AES_128, AES_192, AES_256. * @param mode Electronic Code Book / Cipher Block Chaining. * @param doPad Pad if necessary. * @param iterations Iteration count * @param f The context to use. * @param p The pool to use. * @return Returns APR_ENOKEY if the pass phrase is missing or empty, or if a backend * error occurred while generating the key. APR_ENOCIPHER if the type or mode * is not supported by the particular backend. APR_EKEYTYPE if the key type is * not known. APR_EPADDING if padding was requested but is not supported. * APR_ENOTIMPL if not implemented. */ static apr_status_t crypto_passphrase(apr_crypto_key_t **k, apr_size_t *ivSize, const char *pass, apr_size_t passLen, const unsigned char * salt, apr_size_t saltLen, const apr_crypto_block_key_type_e type, const apr_crypto_block_key_mode_e mode, const int doPad, const int iterations, const apr_crypto_t *f, apr_pool_t *p) { apr_status_t rv = APR_SUCCESS; PK11SlotInfo * slot; SECItem passItem; SECItem saltItem; SECAlgorithmID *algid; void *wincx = NULL; /* what is wincx? */ apr_crypto_key_t *key = *k; if (!key) { *k = key = apr_array_push(f->keys); } if (!key) { return APR_ENOMEM; } key->f = f; key->provider = f->provider; /* decide on what cipher mechanism we will be using */ switch (type) { case (APR_KEY_3DES_192): if (APR_MODE_CBC == mode) { key->cipherOid = SEC_OID_DES_EDE3_CBC; } else if (APR_MODE_ECB == mode) { return APR_ENOCIPHER; /* No OID for CKM_DES3_ECB; */ } break; case (APR_KEY_AES_128): if (APR_MODE_CBC == mode) { key->cipherOid = SEC_OID_AES_128_CBC; } else { key->cipherOid = SEC_OID_AES_128_ECB; } break; case (APR_KEY_AES_192): if (APR_MODE_CBC == mode) { key->cipherOid = SEC_OID_AES_192_CBC; } else { key->cipherOid = SEC_OID_AES_192_ECB; } break; case (APR_KEY_AES_256): if (APR_MODE_CBC == mode) { key->cipherOid = SEC_OID_AES_256_CBC; } else { key->cipherOid = SEC_OID_AES_256_ECB; } break; default: /* unknown key type, give up */ return APR_EKEYTYPE; } /* AES_128_CBC --> CKM_AES_CBC --> CKM_AES_CBC_PAD */ key->cipherMech = PK11_AlgtagToMechanism(key->cipherOid); if (key->cipherMech == CKM_INVALID_MECHANISM) { return APR_ENOCIPHER; } if (doPad) { CK_MECHANISM_TYPE paddedMech; paddedMech = PK11_GetPadMechanism(key->cipherMech); if (CKM_INVALID_MECHANISM == paddedMech || key->cipherMech == paddedMech) { return APR_EPADDING; } key->cipherMech = paddedMech; } /* Turn the raw passphrase and salt into SECItems */ passItem.data = (unsigned char*) pass; passItem.len = passLen; saltItem.data = (unsigned char*) salt; saltItem.len = saltLen; /* generate the key */ /* pbeAlg and cipherAlg are the same. NSS decides the keylength. */ algid = PK11_CreatePBEV2AlgorithmID(key->cipherOid, key->cipherOid, SEC_OID_HMAC_SHA1, 0, iterations, &saltItem); if (algid) { slot = PK11_GetBestSlot(key->cipherMech, wincx); if (slot) { key->symKey = PK11_PBEKeyGen(slot, algid, &passItem, PR_FALSE, wincx); PK11_FreeSlot(slot); } SECOID_DestroyAlgorithmID(algid, PR_TRUE); } /* sanity check? */ if (!key->symKey) { PRErrorCode perr = PORT_GetError(); if (perr) { f->result->rc = perr; f->result->msg = PR_ErrorToName(perr); rv = APR_ENOKEY; } } key->ivSize = PK11_GetIVLength(key->cipherMech); if (ivSize) { *ivSize = key->ivSize; } return rv; }
/* * 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; }