diff options
author | ryan.sleevi%gmail.com <devnull@localhost> | 2013-02-05 02:19:52 +0000 |
---|---|---|
committer | ryan.sleevi%gmail.com <devnull@localhost> | 2013-02-05 02:19:52 +0000 |
commit | 81fad6d3605d9159d6323e3ad74b9cf0f87f15c3 (patch) | |
tree | 78287bd451e6e1c5b4fb23065af02a22ffc766a2 | |
parent | 324f5092192dbcc4916cf21d2ecbad5532ba2ef2 (diff) | |
download | nss-hg-81fad6d3605d9159d6323e3ad74b9cf0f87f15c3.tar.gz |
Bug 158747: Add support for RSA-OAEP to softoken, but do not enable it yet,
pending unit tests. r=wtc
-rw-r--r-- | security/nss/lib/softoken/pkcs11c.c | 69 | ||||
-rw-r--r-- | security/nss/lib/softoken/pkcs11i.h | 15 | ||||
-rw-r--r-- | security/nss/lib/softoken/rsawrapr.c | 662 | ||||
-rw-r--r-- | security/nss/lib/softoken/softoken.h | 16 | ||||
-rw-r--r-- | security/nss/lib/softoken/softoknt.h | 3 |
5 files changed, 528 insertions, 237 deletions
diff --git a/security/nss/lib/softoken/pkcs11c.c b/security/nss/lib/softoken/pkcs11c.c index ee38d8d21..dfe12e12b 100644 --- a/security/nss/lib/softoken/pkcs11c.c +++ b/security/nss/lib/softoken/pkcs11c.c @@ -457,6 +457,24 @@ sftk_aes_mode(CK_MECHANISM_TYPE mechanism) return -1; } +static SECStatus +sftk_EncryptOAEP(SFTKOAEPEncryptInfo *info, unsigned char *output, + unsigned int *outputLen, unsigned int maxLen, + unsigned char *input, unsigned int inputLen) +{ + return RSA_EncryptOAEP(info->params, info->key, output, outputLen, + maxLen, input, inputLen); +} + +static SECStatus +sftk_DecryptOAEP(SFTKOAEPDecryptInfo *info, unsigned char *output, + unsigned int *outputLen, unsigned int maxLen, + unsigned char *input, unsigned int inputLen) +{ + return RSA_DecryptOAEP(info->params, info->key, output, outputLen, + maxLen, input, inputLen); +} + /** NSC_CryptInit initializes an encryption/Decryption operation. * * Always called by NSC_EncryptInit, NSC_DecryptInit, NSC_WrapKey,NSC_UnwrapKey. @@ -513,6 +531,7 @@ sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, if (isEncrypt) { NSSLOWKEYPublicKey *pubKey = sftk_GetPubKey(key,CKK_RSA,&crv); if (pubKey == NULL) { + crv = CKR_KEY_HANDLE_INVALID; break; } context->maxLen = nsslowkey_PublicModulusLen(pubKey); @@ -523,6 +542,7 @@ sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, } else { NSSLOWKEYPrivateKey *privKey = sftk_GetPrivKey(key,CKK_RSA,&crv); if (privKey == NULL) { + crv = CKR_KEY_HANDLE_INVALID; break; } context->maxLen = nsslowkey_PrivateModulusLen(privKey); @@ -533,6 +553,55 @@ sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, } context->destroy = sftk_Null; break; +/* XXX: Disabled until unit tests land. + case CKM_RSA_PKCS_OAEP: + if (key_type != CKK_RSA) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + context->multi = PR_FALSE; + context->rsa = PR_TRUE; + if (pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_OAEP_PARAMS)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + /\* XXX: Need Parameter validation here *\/ + if (isEncrypt) { + SFTKOAEPEncryptInfo *info = PORT_New(SFTKOAEPEncryptInfo); + if (info == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + info->params = pMechanism->pParameter; + info->key = sftk_GetPubKey(key, CKK_RSA, &crv); + if (info->key == NULL) { + PORT_Free(info); + crv = CKR_KEY_HANDLE_INVALID; + break; + } + context->update = (SFTKCipher) sftk_EncryptOAEP; + context->maxLen = nsslowkey_PublicModulusLen(info->key); + context->cipherInfo = info; + } else { + SFTKOAEPDecryptInfo *info = PORT_New(SFTKOAEPDecryptInfo); + if (info == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + info->params = pMechanism->pParameter; + info->key = sftk_GetPrivKey(key, CKK_RSA, &crv); + if (info->key == NULL) { + PORT_Free(info); + crv = CKR_KEY_HANDLE_INVALID; + break; + } + context->update = (SFTKCipher) sftk_DecryptOAEP; + context->maxLen = nsslowkey_PrivateModulusLen(info->key); + context->cipherInfo = info; + } + context->destroy = (SFTKDestroy) sftk_Space; + break; +*/ case CKM_RC2_CBC_PAD: context->doPad = PR_TRUE; /* fall thru */ diff --git a/security/nss/lib/softoken/pkcs11i.h b/security/nss/lib/softoken/pkcs11i.h index 7f4684006..45001b8d0 100644 --- a/security/nss/lib/softoken/pkcs11i.h +++ b/security/nss/lib/softoken/pkcs11i.h @@ -101,6 +101,8 @@ typedef struct SFTKSessionContextStr SFTKSessionContext; typedef struct SFTKSearchResultsStr SFTKSearchResults; typedef struct SFTKHashVerifyInfoStr SFTKHashVerifyInfo; typedef struct SFTKHashSignInfoStr SFTKHashSignInfo; +typedef struct SFTKOAEPEncryptInfoStr SFTKOAEPEncryptInfo; +typedef struct SFTKOAEPDecryptInfoStr SFTKOAEPDecryptInfo; typedef struct SFTKSSLMACInfoStr SFTKSSLMACInfo; typedef struct SFTKItemTemplateStr SFTKItemTemplate; @@ -372,6 +374,19 @@ struct SFTKHashSignInfoStr { NSSLOWKEYPrivateKey *key; }; +/** + * Contexts for RSA-OAEP + */ +struct SFTKOAEPEncryptInfoStr { + CK_RSA_PKCS_OAEP_PARAMS *params; + NSSLOWKEYPublicKey *key; +}; + +struct SFTKOAEPDecryptInfoStr { + CK_RSA_PKCS_OAEP_PARAMS *params; + NSSLOWKEYPrivateKey *key; +}; + /* context for the Final SSLMAC message */ struct SFTKSSLMACInfoStr { void *hashContext; diff --git a/security/nss/lib/softoken/rsawrapr.c b/security/nss/lib/softoken/rsawrapr.c index ee1e61d47..fe4c8f86c 100644 --- a/security/nss/lib/softoken/rsawrapr.c +++ b/security/nss/lib/softoken/rsawrapr.c @@ -19,137 +19,42 @@ #define RSA_BLOCK_PRIVATE_PAD_OCTET 0xff #define RSA_BLOCK_AFTER_PAD_OCTET 0x00 -#define OAEP_SALT_LEN 8 -#define OAEP_PAD_LEN 8 -#define OAEP_PAD_OCTET 0x00 - -#define FLAT_BUFSIZE 512 /* bytes to hold flattened SHA1Context. */ - /* Needed for RSA-PSS functions */ static const unsigned char eightZeros[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; -static SHA1Context * -SHA1_CloneContext(SHA1Context *original) -{ - SHA1Context * clone = NULL; - unsigned char *pBuf; - int sha1ContextSize = SHA1_FlattenSize(original); - SECStatus frv; - unsigned char buf[FLAT_BUFSIZE]; - - PORT_Assert(sizeof buf >= sha1ContextSize); - if (sizeof buf >= sha1ContextSize) { - pBuf = buf; - } else { - pBuf = PORT_Alloc(sha1ContextSize); - if (!pBuf) - goto done; - } - - frv = SHA1_Flatten(original, pBuf); - if (frv == SECSuccess) { - clone = SHA1_Resurrect(pBuf, NULL); - memset(pBuf, 0, sha1ContextSize); - } -done: - if (pBuf != buf) - PORT_Free(pBuf); - return clone; +/* Constant time comparison of a single byte. + * Returns 1 iff a == b, otherwise returns 0. + * Note: For ranges of bytes, use constantTimeCompare. + */ +static unsigned char constantTimeEQ8(unsigned char a, unsigned char b) { + unsigned char c = ~(a - b | b - a); + c >>= 7; + return c; } -/* - * Modify data by XORing it with a special hash of salt. +/* Constant time comparison of a range of bytes. + * Returns 1 iff len bytes of a are identical to len bytes of b, otherwise + * returns 0. */ -static SECStatus -oaep_xor_with_h1(unsigned char *data, unsigned int datalen, - unsigned char *salt, unsigned int saltlen) -{ - SHA1Context *sha1cx; - unsigned char *dp, *dataend; - unsigned char end_octet; - - sha1cx = SHA1_NewContext(); - if (sha1cx == NULL) { - return SECFailure; - } - - /* - * Get a hash of salt started; we will use it several times, - * adding in a different end octet (x00, x01, x02, ...). - */ - SHA1_Begin (sha1cx); - SHA1_Update (sha1cx, salt, saltlen); - end_octet = 0; - - dp = data; - dataend = data + datalen; - - while (dp < dataend) { - SHA1Context *sha1cx_h1; - unsigned int sha1len, sha1off; - unsigned char sha1[SHA1_LENGTH]; - - /* - * Create hash of (salt || end_octet) - */ - sha1cx_h1 = SHA1_CloneContext (sha1cx); - SHA1_Update (sha1cx_h1, &end_octet, 1); - SHA1_End (sha1cx_h1, sha1, &sha1len, sizeof(sha1)); - SHA1_DestroyContext (sha1cx_h1, PR_TRUE); - PORT_Assert (sha1len == SHA1_LENGTH); - - /* - * XOR that hash with the data. - * When we have fewer than SHA1_LENGTH octets of data - * left to xor, use just the low-order ones of the hash. - */ - sha1off = 0; - if ((dataend - dp) < SHA1_LENGTH) - sha1off = SHA1_LENGTH - (dataend - dp); - while (sha1off < SHA1_LENGTH) - *dp++ ^= sha1[sha1off++]; - - /* - * Bump for next hash chunk. - */ - end_octet++; - } - - SHA1_DestroyContext (sha1cx, PR_TRUE); - return SECSuccess; +static unsigned char constantTimeCompare(const unsigned char *a, + const unsigned char *b, + unsigned int len) { + unsigned char tmp = 0; + unsigned int i; + for (i = 0; i < len; ++i, ++a, ++b) + tmp |= *a ^ *b; + return constantTimeEQ8(0x00, tmp); } -/* - * Modify salt by XORing it with a special hash of data. +/* Constant time conditional. + * Returns a if c is 1, or b if c is 0. The result is undefined if c is + * not 0 or 1. */ -static SECStatus -oaep_xor_with_h2(unsigned char *salt, unsigned int saltlen, - unsigned char *data, unsigned int datalen) +static unsigned int constantTimeCondition(unsigned int c, + unsigned int a, + unsigned int b) { - unsigned char sha1[SHA1_LENGTH]; - unsigned char *psalt, *psha1, *saltend; - SECStatus rv; - - /* - * Create a hash of data. - */ - rv = SHA1_HashBuf (sha1, data, datalen); - if (rv != SECSuccess) { - return rv; - } - - /* - * XOR the low-order octets of that hash with salt. - */ - PORT_Assert (saltlen <= SHA1_LENGTH); - saltend = salt + saltlen; - psalt = salt; - psha1 = sha1 + SHA1_LENGTH - saltlen; - while (psalt < saltend) { - *psalt++ ^= *psha1++; - } - - return SECSuccess; + return (~(c - 1) & a) | ((c - 1) & b); } /* @@ -265,97 +170,6 @@ rsa_FormatOneBlock(unsigned modulusLen, RSA_BlockType blockType, PORT_Memcpy (bp, data->data, data->len); break; - /* - * Blocks intended for public-key operation, using - * Optimal Asymmetric Encryption Padding (OAEP). - */ - case RSA_BlockOAEP: - /* - * 0x00 || BT || Modified2(Salt) || Modified1(PaddedData) - * 1 1 OAEP_SALT_LEN OAEP_PAD_LEN + data->len [+ N] - * - * where: - * PaddedData is "Pad1 || ActualData [|| Pad2]" - * Salt is random data. - * Pad1 is all zeros. - * Pad2, if present, is random data. - * (The "modified" fields are all the same length as the original - * unmodified values; they are just xor'd with other values.) - * - * Modified1 is an XOR of PaddedData with a special octet - * string constructed of iterated hashing of Salt (see below). - * Modified2 is an XOR of Salt with the low-order octets of - * the hash of Modified1 (see farther below ;-). - * - * Whew! - */ - - - /* - * Salt - */ - rv = RNG_GenerateGlobalRandomBytes(bp, OAEP_SALT_LEN); - if (rv != SECSuccess) { - sftk_fatalError = PR_TRUE; - PORT_Free (block); - return NULL; - } - bp += OAEP_SALT_LEN; - - /* - * Pad1 - */ - PORT_Memset (bp, OAEP_PAD_OCTET, OAEP_PAD_LEN); - bp += OAEP_PAD_LEN; - - /* - * Data - */ - PORT_Memcpy (bp, data->data, data->len); - bp += data->len; - - /* - * Pad2 - */ - if (bp < (block + modulusLen)) { - rv = RNG_GenerateGlobalRandomBytes(bp, block - bp + modulusLen); - if (rv != SECSuccess) { - sftk_fatalError = PR_TRUE; - PORT_Free (block); - return NULL; - } - } - - /* - * Now we have the following: - * 0x00 || BT || Salt || PaddedData - * (From this point on, "Pad1 || Data [|| Pad2]" is treated - * as the one entity PaddedData.) - * - * We need to turn PaddedData into Modified1. - */ - if (oaep_xor_with_h1(block + 2 + OAEP_SALT_LEN, - modulusLen - 2 - OAEP_SALT_LEN, - block + 2, OAEP_SALT_LEN) != SECSuccess) { - PORT_Free (block); - return NULL; - } - - /* - * Now we have: - * 0x00 || BT || Salt || Modified1(PaddedData) - * - * The remaining task is to turn Salt into Modified2. - */ - if (oaep_xor_with_h2(block + 2, OAEP_SALT_LEN, - block + 2 + OAEP_SALT_LEN, - modulusLen - 2 - OAEP_SALT_LEN) != SECSuccess) { - PORT_Free (block); - return NULL; - } - - break; - default: PORT_Assert (0); PORT_Free (block); @@ -403,26 +217,6 @@ rsa_FormatBlock(SECItem *result, unsigned modulusLen, break; - case RSA_BlockOAEP: - /* - * 0x00 || BT || M1(Salt) || M2(Pad1||ActualData[||Pad2]) - * - * The "2" below is the first octet + the second octet. - * (The other fields do not contain the clear values, but are - * the same length as the clear values.) - */ - PORT_Assert (data->len <= (modulusLen - (2 + OAEP_SALT_LEN - + OAEP_PAD_LEN))); - - result->data = rsa_FormatOneBlock(modulusLen, blockType, data); - if (result->data == NULL) { - result->len = 0; - return SECFailure; - } - result->len = modulusLen; - - break; - case RSA_BlockRaw: /* * Pad || ActualData @@ -959,6 +753,266 @@ MGF1(HASH_HashType hashAlg, unsigned char *mask, unsigned int maskLen, } /* + * Decodes an EME-OAEP encoded block, validating the encoding in constant + * time. + * Described in RFC 3447, section 7.1.2. + * input contains the encoded block, after decryption. + * label is the optional value L that was associated with the message. + * On success, the original message and message length will be stored in + * output and outputLen. + */ +static SECStatus +eme_oaep_decode(unsigned char *output, unsigned int *outputLen, + unsigned int maxOutputLen, + const unsigned char *input, unsigned int inputLen, + HASH_HashType hashAlg, HASH_HashType maskHashAlg, + const unsigned char *label, unsigned int labelLen) +{ + const SECHashObject *hash; + void *hashContext; + SECStatus rv = SECFailure; + unsigned char labelHash[HASH_LENGTH_MAX]; + unsigned int i, maskLen, paddingOffset; + unsigned char *mask = NULL, *tmpOutput = NULL; + unsigned char isGood, foundPaddingEnd; + + hash = HASH_GetRawHashObject(hashAlg); + + /* 1.c */ + if (inputLen < (hash->length * 2) + 2) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + + /* Step 3.a - Generate lHash */ + hashContext = (*hash->create)(); + if (hashContext == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + (*hash->begin)(hashContext); + if (labelLen > 0) + (*hash->update)(hashContext, label, labelLen); + (*hash->end)(hashContext, labelHash, &i, sizeof(labelHash)); + (*hash->destroy)(hashContext, PR_TRUE); + + tmpOutput = (unsigned char*)PORT_Alloc(inputLen); + if (tmpOutput == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto done; + } + + maskLen = inputLen - hash->length - 1; + mask = (unsigned char*)PORT_Alloc(maskLen); + if (mask == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto done; + } + + PORT_Memcpy(tmpOutput, input, inputLen); + + /* 3.c - Generate seedMask */ + MGF1(maskHashAlg, mask, hash->length, &tmpOutput[1 + hash->length], + inputLen - hash->length - 1); + /* 3.d - Unmask seed */ + for (i = 0; i < hash->length; ++i) + tmpOutput[1 + i] ^= mask[i]; + + /* 3.e - Generate dbMask */ + MGF1(maskHashAlg, mask, maskLen, &tmpOutput[1], hash->length); + /* 3.f - Unmask DB */ + for (i = 0; i < maskLen; ++i) + tmpOutput[1 + hash->length + i] ^= mask[i]; + + /* 3.g - Compare Y, lHash, and PS in constant time + * Warning: This code is timing dependent and must not disclose which of + * these were invalid. + */ + paddingOffset = 0; + isGood = 1; + foundPaddingEnd = 0; + + /* Compare Y */ + isGood &= constantTimeEQ8(0x00, tmpOutput[0]); + + /* Compare lHash and lHash' */ + isGood &= constantTimeCompare(&labelHash[0], + &tmpOutput[1 + hash->length], + hash->length); + + /* Compare that the padding is zero or more zero octets, followed by a + * 0x01 octet */ + for (i = 1 + (hash->length * 2); i < inputLen; ++i) { + unsigned char isZero = constantTimeEQ8(0x00, tmpOutput[i]); + unsigned char isOne = constantTimeEQ8(0x01, tmpOutput[i]); + /* non-constant time equivalent: + * if (tmpOutput[i] == 0x01 && !foundPaddingEnd) + * paddingOffset = i; + */ + paddingOffset = constantTimeCondition(isOne & ~foundPaddingEnd, i, + paddingOffset); + /* non-constant time equivalent: + * if (tmpOutput[i] == 0x01) + * foundPaddingEnd = true; + * + * Note: This may yield false positives, as it will be set whenever + * a 0x01 byte is encountered. If there was bad padding (eg: + * 0x03 0x02 0x01), foundPaddingEnd will still be set to true, and + * paddingOffset will still be set to 2. + */ + foundPaddingEnd = constantTimeCondition(isOne, 1, foundPaddingEnd); + /* non-constant time equivalent: + * if (tmpOutput[i] != 0x00 && tmpOutput[i] != 0x01 && + * !foundPaddingEnd) { + * isGood = false; + * } + * + * Note: This may yield false positives, as a message (and padding) + * that is entirely zeros will result in isGood still being true. Thus + * it's necessary to check foundPaddingEnd is positive below. + */ + isGood = constantTimeCondition(~foundPaddingEnd & ~isZero, 0, isGood); + } + + /* While both isGood and foundPaddingEnd may have false positives, they + * cannot BOTH have false positives. If both are not true, then an invalid + * message was received. Note, this comparison must still be done in constant + * time so as not to leak either condition. + */ + if (!(isGood & foundPaddingEnd)) { + PORT_SetError(SEC_ERROR_BAD_DATA); + goto done; + } + + /* End timing dependent code */ + + ++paddingOffset; /* Skip the 0x01 following the end of PS */ + + *outputLen = inputLen - paddingOffset; + if (*outputLen > maxOutputLen) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + goto done; + } + + if (*outputLen) + PORT_Memcpy(output, &tmpOutput[paddingOffset], *outputLen); + rv = SECSuccess; + +done: + if (mask) + PORT_ZFree(mask, maskLen); + if (tmpOutput) + PORT_ZFree(tmpOutput, inputLen); + return rv; +} + +/* + * Generate an EME-OAEP encoded block for encryption + * Described in RFC 3447, section 7.1.1 + * We use input instead of M for the message to be encrypted + * label is the optional value L to be associated with the message. + */ +static SECStatus +eme_oaep_encode(unsigned char *em, unsigned int emLen, + const unsigned char *input, unsigned int inputLen, + HASH_HashType hashAlg, HASH_HashType maskHashAlg, + const unsigned char *label, unsigned int labelLen) +{ + const SECHashObject *hash; + void *hashContext; + SECStatus rv; + unsigned char *mask; + unsigned int reservedLen, dbMaskLen, i; + + hash = HASH_GetRawHashObject(hashAlg); + + /* Step 1.b */ + reservedLen = (2 * hash->length) + 2; + if (emLen < reservedLen || inputLen > (emLen - reservedLen)) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + + /* + * From RFC 3447, Section 7.1 + * +----------+---------+-------+ + * DB = | lHash | PS | M | + * +----------+---------+-------+ + * | + * +----------+ V + * | seed |--> MGF ---> xor + * +----------+ | + * | | + * +--+ V | + * |00| xor <----- MGF <-----| + * +--+ | | + * | | | + * V V V + * +--+----------+----------------------------+ + * EM = |00|maskedSeed| maskedDB | + * +--+----------+----------------------------+ + * + * We use mask to hold the result of the MGF functions, and all other + * values are generated in their final resting place. + */ + *em = 0x00; + + /* Step 2.a - Generate lHash */ + hashContext = (*hash->create)(); + if (hashContext == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + (*hash->begin)(hashContext); + if (labelLen > 0) + (*hash->update)(hashContext, label, labelLen); + (*hash->end)(hashContext, &em[1 + hash->length], &i, hash->length); + (*hash->destroy)(hashContext, PR_TRUE); + + /* Step 2.b - Generate PS */ + if (emLen - reservedLen - inputLen > 0) { + PORT_Memset(em + 1 + (hash->length * 2), 0x00, + emLen - reservedLen - inputLen); + } + + /* Step 2.c. - Generate DB + * DB = lHash || PS || 0x01 || M + * Note that PS and lHash have already been placed into em at their + * appropriate offsets. This just copies M into place + */ + em[emLen - inputLen - 1] = 0x01; + if (inputLen) + PORT_Memcpy(em + emLen - inputLen, input, inputLen); + + /* Step 2.d - Generate seed */ + rv = RNG_GenerateGlobalRandomBytes(em + 1, hash->length); + if (rv != SECSuccess) { + return rv; + } + + /* Step 2.e - Generate dbMask*/ + dbMaskLen = emLen - hash->length - 1; + mask = (unsigned char*)PORT_Alloc(dbMaskLen); + if (mask == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + MGF1(maskHashAlg, mask, dbMaskLen, em + 1, hash->length); + /* Step 2.f - Compute maskedDB*/ + for (i = 0; i < dbMaskLen; ++i) + em[1 + hash->length + i] ^= mask[i]; + + /* Step 2.g - Generate seedMask */ + MGF1(maskHashAlg, mask, hash->length, &em[1 + hash->length], dbMaskLen); + /* Step 2.h - Compute maskedSeed */ + for (i = 0; i < hash->length; ++i) + em[1 + i] ^= mask[i]; + + PORT_ZFree(mask, dbMaskLen); + return SECSuccess; +} + +/* * Encode a RSA-PSS signature. * Described in RFC 3447, section 9.1.1. * We use mHash instead of M as input. @@ -1008,7 +1062,7 @@ emsa_pss_encode(unsigned char *em, unsigned int emLen, (*hash->destroy)(hash_context, PR_TRUE); /* Step 7 + 8 */ - memset(em, 0, dbMaskLen - sLen - 1); + PORT_Memset(em, 0, dbMaskLen - sLen - 1); em[dbMaskLen - sLen - 1] = 0x01; /* Step 9 */ @@ -1251,3 +1305,145 @@ done: PORT_Free(pss_encoded); return rv; } + +/* MGF1 is the only supported MGF. */ +SECStatus +RSA_EncryptOAEP(CK_RSA_PKCS_OAEP_PARAMS *oaepParams, + NSSLOWKEYPublicKey *key, + unsigned char *output, unsigned int *outputLen, + unsigned int maxOutputLen, + const unsigned char *input, unsigned int inputLen) +{ + SECStatus rv = SECFailure; + unsigned int modulusLen = nsslowkey_PublicModulusLen(key); + unsigned char *oaepEncoded = NULL; + unsigned char *sourceData = NULL; + unsigned int sourceDataLen = 0; + + HASH_HashType hashAlg; + HASH_HashType maskHashAlg; + + if (maxOutputLen < modulusLen) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + PORT_Assert(key->keyType == NSSLOWKEYRSAKey); + if (key->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + hashAlg = GetHashTypeFromMechanism(oaepParams->hashAlg); + maskHashAlg = GetHashTypeFromMechanism(oaepParams->mgf); + if ((hashAlg == HASH_AlgNULL) || (maskHashAlg == HASH_AlgNULL)) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + + /* The PKCS#11 source parameter is the "source" of the label parameter. + * The only defined source is explicitly specified, in which case, the + * label is an optional byte string in pSourceData. If ulSourceDataLen is + * zero, then pSourceData MUST be NULL - otherwise, it must be non-NULL. + */ + if (oaepParams->source != CKZ_DATA_SPECIFIED) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + sourceData = (unsigned char*)oaepParams->pSourceData; + sourceDataLen = oaepParams->ulSourceDataLen; + if ((sourceDataLen == 0 && sourceData != NULL) || + (sourceDataLen > 0 && sourceData == NULL)) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + + oaepEncoded = (unsigned char *)PORT_Alloc(modulusLen); + if (oaepEncoded == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + rv = eme_oaep_encode(oaepEncoded, modulusLen, input, inputLen, + hashAlg, maskHashAlg, sourceData, sourceDataLen); + if (rv != SECSuccess) + goto done; + + rv = RSA_PublicKeyOp(&key->u.rsa, output, oaepEncoded); + if (rv != SECSuccess) + goto done; + *outputLen = modulusLen; + +done: + PORT_Free(oaepEncoded); + return rv; +} + +/* MGF1 is the only supported MGF. */ +SECStatus +RSA_DecryptOAEP(CK_RSA_PKCS_OAEP_PARAMS *oaepParams, + NSSLOWKEYPrivateKey *key, + unsigned char *output, unsigned int *outputLen, + unsigned int maxOutputLen, + const unsigned char *input, unsigned int inputLen) +{ + SECStatus rv = SECFailure; + unsigned int modulusLen = nsslowkey_PrivateModulusLen(key); + unsigned char *oaepEncoded = NULL; + unsigned char *sourceData = NULL; + unsigned int sourceDataLen = 0; + + HASH_HashType hashAlg = GetHashTypeFromMechanism(oaepParams->hashAlg); + HASH_HashType maskHashAlg = GetHashTypeFromMechanism(oaepParams->mgf); + + if ((hashAlg == HASH_AlgNULL) || (maskHashAlg == HASH_AlgNULL)) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + + if (inputLen != modulusLen) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + return SECFailure; + } + PORT_Assert(key->keyType == NSSLOWKEYRSAKey); + if (key->keyType != NSSLOWKEYRSAKey) { + PORT_SetError(SEC_ERROR_INVALID_KEY); + return SECFailure; + } + + /* The PKCS#11 source parameter is the "source" of the label parameter. + * The only defined source is explicitly specified, in which case, the + * label is an optional byte string in pSourceData. If ulSourceDataLen is + * zero, then pSourceData MUST be NULL - otherwise, it must be non-NULL. + */ + if (oaepParams->source != CKZ_DATA_SPECIFIED) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + sourceData = (unsigned char*)oaepParams->pSourceData; + sourceDataLen = oaepParams->ulSourceDataLen; + if ((sourceDataLen == 0 && sourceData != NULL) || + (sourceDataLen > 0 && sourceData == NULL)) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + + oaepEncoded = (unsigned char *)PORT_Alloc(modulusLen); + if (oaepEncoded == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + rv = RSA_PrivateKeyOpDoubleChecked(&key->u.rsa, oaepEncoded, input); + if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + goto done; + } + + rv = eme_oaep_decode(output, outputLen, maxOutputLen, oaepEncoded, + modulusLen, hashAlg, maskHashAlg, sourceData, + sourceDataLen); + +done: + if (oaepEncoded) + PORT_ZFree(oaepEncoded, modulusLen); + return rv; +} diff --git a/security/nss/lib/softoken/softoken.h b/security/nss/lib/softoken/softoken.h index 3d683c162..254fa79fb 100644 --- a/security/nss/lib/softoken/softoken.h +++ b/security/nss/lib/softoken/softoken.h @@ -14,7 +14,7 @@ #include "softoknt.h" #include "secoidt.h" -#include "pkcs11t.h" /* CK_RV Required for sftk_fipsPowerUpSelfTest(). */ +#include "pkcs11t.h" SEC_BEGIN_PROTOS @@ -94,6 +94,20 @@ SECStatus RSA_DecryptBlock(NSSLOWKEYPrivateKey *key, unsigned char *output, unsigned int *outputLen, unsigned int maxOutputLen, unsigned char *input, unsigned int inputLen); +extern +SECStatus RSA_EncryptOAEP(CK_RSA_PKCS_OAEP_PARAMS *oaepParams, + NSSLOWKEYPublicKey *key, + unsigned char *output, unsigned int *outputLen, + unsigned int maxOutputLen, + const unsigned char *input, unsigned int inputLen); + +extern +SECStatus RSA_DecryptOAEP(CK_RSA_PKCS_OAEP_PARAMS *oaepParams, + NSSLOWKEYPrivateKey *key, + unsigned char *output, unsigned int *outputLen, + unsigned int maxOutputLen, + const unsigned char *input, unsigned int inputLen); + /* * added to make pkcs #11 happy * RAW is RSA_X_509 diff --git a/security/nss/lib/softoken/softoknt.h b/security/nss/lib/softoken/softoknt.h index b493778a0..7ab8a24ce 100644 --- a/security/nss/lib/softoken/softoknt.h +++ b/security/nss/lib/softoken/softoknt.h @@ -20,9 +20,6 @@ typedef enum { RSA_BlockPrivate0 = 0, /* unused, really */ RSA_BlockPrivate = 1, /* pad for a private-key operation */ RSA_BlockPublic = 2, /* pad for a public-key operation */ - RSA_BlockOAEP = 3, /* use OAEP padding */ - /* XXX is this only for a public-key - operation? If so, add "Public" */ RSA_BlockRaw = 4, /* simply justify the block appropriately */ RSA_BlockTotal } RSA_BlockType; |