| Index: mozilla/security/nss/lib/softoken/rsawrapr.c
|
| ===================================================================
|
| --- mozilla/security/nss/lib/softoken/rsawrapr.c (revision 180595)
|
| +++ mozilla/security/nss/lib/softoken/rsawrapr.c (working copy)
|
| @@ -5,7 +5,7 @@
|
| * This Source Code Form is subject to the terms of the Mozilla Public
|
| * License, v. 2.0. If a copy of the MPL was not distributed with this
|
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
| -/* $Id: rsawrapr.c,v 1.21 2012/06/26 22:27:31 rrelyea%redhat.com Exp $ */
|
| +/* $Id: rsawrapr.c,v 1.22 2013/02/05 02:19:52 ryan.sleevi%gmail.com Exp $ */
|
|
|
| #include "blapi.h"
|
| #include "softoken.h"
|
| @@ -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 @@
|
| 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 @@
|
|
|
| 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 @@
|
| }
|
|
|
| /*
|
| + * 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 @@
|
| (*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 @@
|
| 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;
|
| +}
|
|
|