/* 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/. */
/* * RSA block types * * The values of RSA_BlockPrivate and RSA_BlockPublic are fixed. * The value of RSA_BlockRaw isn't fixed by definition, but we are keeping * the value that NSS has been using in the past.
*/ typedefenum {
RSA_BlockPrivate = 1, /* pad for a private-key operation */
RSA_BlockPublic = 2, /* pad for a public-key operation */
RSA_BlockRaw = 4 /* simply justify the block appropriately */
} RSA_BlockType;
/* Constant time comparison of a single byte. * Returns 1 iff a == b, otherwise returns 0. * Note: For ranges of bytes, use constantTimeCompare.
*/ staticunsignedchar
constantTimeEQ8(unsignedchar a, unsignedchar b)
{ unsignedchar c = ~((a - b) | (b - a));
c >>= 7; return c;
}
/* 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.
*/ staticunsignedchar
constantTimeCompare(constunsignedchar *a, constunsignedchar *b, unsignedint len)
{ unsignedchar tmp = 0; unsignedint i; for (i = 0; i < len; ++i, ++a, ++b)
tmp |= *a ^ *b; return constantTimeEQ8(0x00, tmp);
}
/* 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.
*/ staticunsignedint
constantTimeCondition(unsignedint c, unsignedint a, unsignedint b)
{ return (~(c - 1) & a) | ((c - 1) & b);
}
while (byteZero > 0) {
numBits++;
byteZero >>= 1;
}
return numBits;
}
/* * Format one block of data for public/private key encryption using * the rules defined in PKCS #1.
*/ staticunsignedchar *
rsa_FormatOneBlock(unsigned modulusLen,
RSA_BlockType blockType,
SECItem *data)
{ unsignedchar *block; unsignedchar *bp; unsignedint padLen; unsignedint i, j;
SECStatus rv;
block = (unsignedchar *)PORT_Alloc(modulusLen); if (block == NULL) return NULL;
bp = block;
/* * All RSA blocks start with two octets: * 0x00 || BlockType
*/
*bp++ = RSA_BLOCK_FIRST_OCTET;
*bp++ = (unsignedchar)blockType;
switch (blockType) {
/* * Blocks intended for private-key operation.
*/ case RSA_BlockPrivate: /* preferred method */ /* * 0x00 || BT || Pad || 0x00 || ActualData * 1 1 padLen 1 data->len * padLen must be at least RSA_BLOCK_MIN_PAD_LEN (8) bytes. * Pad is either all 0x00 or all 0xff bytes, depending on blockType.
*/
padLen = modulusLen - data->len - 3;
PORT_Assert(padLen >= RSA_BLOCK_MIN_PAD_LEN); if (padLen < RSA_BLOCK_MIN_PAD_LEN) {
PORT_ZFree(block, modulusLen); return NULL;
}
PORT_Memset(bp, RSA_BLOCK_PRIVATE_PAD_OCTET, padLen);
bp += padLen;
*bp++ = RSA_BLOCK_AFTER_PAD_OCTET;
PORT_Memcpy(bp, data->data, data->len); break;
/* * Blocks intended for public-key operation.
*/ case RSA_BlockPublic: /* * 0x00 || BT || Pad || 0x00 || ActualData * 1 1 padLen 1 data->len * Pad is 8 or more non-zero random bytes. * * Build the block left to right. * Fill the entire block from Pad to the end with random bytes. * Use the bytes after Pad as a supply of extra random bytes from * which to find replacements for the zero bytes in Pad. * If we need more than that, refill the bytes after Pad with * new random bytes as necessary.
*/
padLen = modulusLen - (data->len + 3);
PORT_Assert(padLen >= RSA_BLOCK_MIN_PAD_LEN); if (padLen < RSA_BLOCK_MIN_PAD_LEN) {
PORT_ZFree(block, modulusLen); return NULL;
}
j = modulusLen - 2;
rv = RNG_GenerateGlobalRandomBytes(bp, j); if (rv == SECSuccess) { for (i = 0; i < padLen;) { unsignedchar repl; /* Pad with non-zero random data. */ if (bp[i] != RSA_BLOCK_AFTER_PAD_OCTET) {
++i; continue;
} if (j <= padLen) {
rv = RNG_GenerateGlobalRandomBytes(bp + padLen,
modulusLen - (2 + padLen)); if (rv != SECSuccess) break;
j = modulusLen - 2;
} do {
repl = bp[--j];
} while (repl == RSA_BLOCK_AFTER_PAD_OCTET && j > padLen); if (repl != RSA_BLOCK_AFTER_PAD_OCTET) {
bp[i++] = repl;
}
}
} if (rv != SECSuccess) {
PORT_ZFree(block, modulusLen);
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); return NULL;
}
bp += padLen;
*bp++ = RSA_BLOCK_AFTER_PAD_OCTET;
PORT_Memcpy(bp, data->data, data->len); break;
/* modulusLen has to be larger than RSA_BLOCK_MIN_PAD_LEN + 3, and data has to be smaller than modulus - (RSA_BLOCK_MIN_PAD_LEN + 3) */ static SECStatus
rsa_FormatBlock(SECItem *result, unsigned modulusLen,
RSA_BlockType blockType,
SECItem *data)
{ switch (blockType) { case RSA_BlockPrivate: case RSA_BlockPublic: /* * 0x00 || BT || Pad || 0x00 || ActualData * * The "3" below is the first octet + the second octet + the 0x00 * octet that always comes just before the ActualData.
*/ if (modulusLen < (3 + RSA_BLOCK_MIN_PAD_LEN) || data->len > (modulusLen - (3 + RSA_BLOCK_MIN_PAD_LEN))) { return SECFailure;
}
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 * Pad is zeros. The application is responsible for recovering * the actual data.
*/ if (data->len > modulusLen) { return SECFailure;
}
result->data = (unsignedchar *)PORT_ZAlloc(modulusLen);
result->len = modulusLen;
PORT_Memcpy(result->data + (modulusLen - data->len),
data->data, data->len); break;
/* This could be optimized when the clone functions in
* rawhash.c are implemented. */
(*hash->begin)(hashContext);
(*hash->update)(hashContext, mgfSeed, mgfSeedLen);
(*hash->update)(hashContext, C, sizeof C);
tempHash = mask + counter * hash->length; if (counter != (rounds - 1)) {
(*hash->end)(hashContext, tempHash, &digestLen, hash->length);
} else { /* we're in the last round and need to cut the hash */
temp = (unsignedchar *)PORT_Alloc(hash->length); if (!temp) {
rv = SECFailure; goto done;
}
(*hash->end)(hashContext, temp, &digestLen, hash->length);
PORT_Memcpy(tempHash, temp, maskLen - counter * hash->length);
PORT_Free(temp);
}
}
/* * make sure we get the same results
*/ /* XXX(rsleevi): Constant time */ /* NOTE: should we verify the leading zeros? */ if (PORT_Memcmp(buffer + (modulusLen - hashLen), hash, hashLen) != 0) goto loser;
/* * 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(unsignedchar *output, unsignedint *outputLen, unsignedint maxOutputLen, constunsignedchar *input, unsignedint inputLen,
HASH_HashType hashAlg,
HASH_HashType maskHashAlg, constunsignedchar *label, unsignedint labelLen)
{ const SECHashObject *hash; void *hashContext;
SECStatus rv = SECFailure; unsignedchar labelHash[HASH_LENGTH_MAX]; unsignedint i; unsignedint maskLen; unsignedint paddingOffset; unsignedchar *mask = NULL; unsignedchar *tmpOutput = NULL; unsignedchar isGood; unsignedchar foundPaddingEnd;
/* 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 that the padding is zero or more zero octets, followed by a
* 0x01 octet */ for (i = 1 + (hash->length * 2); i < inputLen; ++i) { unsignedchar isZero = constantTimeEQ8(0x00, tmpOutput[i]); unsignedchar 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 */
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(unsignedchar *em, unsignedint emLen, constunsignedchar *input, unsignedint inputLen,
HASH_HashType hashAlg,
HASH_HashType maskHashAlg, constunsignedchar *label, unsignedint labelLen, constunsignedchar *seed, unsignedint seedLen)
{ const SECHashObject *hash; void *hashContext;
SECStatus rv; unsignedchar *mask; unsignedint reservedLen; unsignedint dbMaskLen; unsignedint i;
/* * 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.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);
if (seed == NULL) { /* Step 2.d - Generate seed */
rv = RNG_GenerateGlobalRandomBytes(em + 1, hash->length); if (rv != SECSuccess) { return rv;
}
} else { /* For Known Answer Tests, copy the supplied seed. */
PORT_Memcpy(em + 1, seed, seedLen);
}
/* first get the key hash (should store in the key structure) */
PORT_Memset(keyHash, 0, sizeof(keyHash));
hashContext = (*hash->create)(); if (hashContext == NULL) { return NULL;
}
(*hash->begin)(hashContext); if (privKeyLen < inputLen) { int padLen = inputLen - privKeyLen; while (padLen > sizeof(keyHash)) {
(*hash->update)(hashContext, keyHash, sizeof(keyHash));
padLen -= sizeof(keyHash);
}
(*hash->update)(hashContext, keyHash, padLen);
}
(*hash->update)(hashContext, key->privateExponent.data, privKeyLen);
(*hash->end)(hashContext, keyHash, &keyLen, sizeof(keyHash));
(*hash->destroy)(hashContext, PR_TRUE);
/* now create the hmac key */
hmac = HMAC_Create(hash, keyHash, keyLen, PR_TRUE); if (hmac == NULL) {
PORT_SafeZero(keyHash, sizeof(keyHash)); return NULL;
}
HMAC_Begin(hmac);
HMAC_Update(hmac, input, inputLen);
rv = HMAC_Finish(hmac, keyHash, &keyLen, sizeof(keyHash)); if (rv != SECSuccess) {
PORT_SafeZero(keyHash, sizeof(keyHash));
HMAC_Destroy(hmac, PR_TRUE); return NULL;
} /* Finally set the new key into the hash context. We * reuse the original context allocated above so we don't
* need to allocate and free another one */
rv = HMAC_ReInit(hmac, hash, keyHash, keyLen, PR_TRUE);
PORT_SafeZero(keyHash, sizeof(keyHash)); if (rv != SECSuccess) {
HMAC_Destroy(hmac, PR_TRUE); return NULL;
}
/* encodedLen is in bits, length is in bytes, thus the shifts
* do an implied multiply by 8 */
encodedLen[0] = (length >> 5) & 0xff;
encodedLen[1] = (length << 3) & 0xff;
/* This function takes a 16-bit input number and * creates the smallest mask which covers * the whole number. Examples: * 0x81 -> 0xff * 0x1af -> 0x1ff * 0x4d1 -> 0x7ff
*/ staticint
makeMask16(int len)
{ // or the high bit in each bit location
len |= (len >> 1);
len |= (len >> 2);
len |= (len >> 4);
len |= (len >> 8); return len;
}
#define STRING_AND_LENGTH(s) s, sizeof(s) - 1 staticint
rsa_GetErrorLength(HMACContext *hmac, int hashLen, int maxLegalLen)
{ unsignedchar out[128 * 2]; unsignedchar *outp; int outLength = 0; int lengthMask;
SECStatus rv;
/* * This function can only fail in environmental cases: Programming errors * and out of memory situations. It can't fail if the keys are valid and * the inputs are the proper size. If the actual RSA decryption fails, a * fake value and a fake length, both of which have already been generated * based on the key and input, are returned. * Applications are expected to detect decryption failures based on the fact * that the decrypted value (usually a key) doesn't validate. The prevents
* Blecheinbaucher style attacks against the key. */
SECStatus
RSA_DecryptBlock(RSAPrivateKey *key, unsignedchar *output, unsignedint *outputLen, unsignedint maxOutputLen, constunsignedchar *input, unsignedint inputLen)
{
SECStatus rv;
PRUint32 fail; unsignedint modulusLen = rsa_modulusLen(&key->modulus); unsignedint i; unsignedchar *buffer = NULL; unsignedchar *errorBuffer = NULL; unsignedchar *bp = NULL; unsignedchar *ep = NULL; unsignedint outLen = modulusLen; unsignedint maxLegalLen = modulusLen - 10; unsignedint errorLength; const SECHashObject *hashObj;
HMACContext *hmac = NULL;
/* failures in the top section indicate failures in the environment * (memory) or the library. OK to return errors in these cases because
* it doesn't provide any oracle information to attackers. */ if (inputLen != modulusLen || modulusLen < 10) {
PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;
}
/* Allocate enough space to decrypt */
buffer = PORT_ZAlloc(modulusLen); if (!buffer) { goto loser;
}
errorBuffer = PORT_ZAlloc(modulusLen); if (!errorBuffer) { goto loser;
}
hashObj = HASH_GetRawHashObject(HASH_AlgSHA256); if (hashObj == NULL) { goto loser;
}
/* calculate the values to return in the error case rather than * the actual returned values. This data is the same for the
* same input and private key. */
hmac = rsa_GetHMACContext(hashObj, key, input, inputLen); if (hmac == NULL) { goto loser;
}
errorLength = rsa_GetErrorLength(hmac, hashObj->length, maxLegalLen); if (((int)errorLength) < 0) { goto loser;
} /* we always have to generate a full moduluslen error string. Otherwise * we create a timing dependency on errorLength, which could be used to * determine the difference between errorLength and outputLen and tell
* us that there was a pkcs1 decryption failure */
rv = rsa_HMACPrf(hmac, STRING_AND_LENGTH("message"),
hashObj->length, errorBuffer, modulusLen); if (rv != SECSuccess) { goto loser;
}
HMAC_Destroy(hmac, PR_TRUE);
hmac = NULL;
/* From here on out, we will always return success. If there is * an error, we will return deterministic output based on the key
* and the input data. */
rv = RSA_PrivateKeyOp(key, buffer, input);
/* There have to be at least 8 bytes of padding. */ for (i = 2; i < 10; i++) {
fail |= PORT_CT_EQ(buffer[i], RSA_BLOCK_AFTER_PAD_OCTET);
}
for (i = 10; i < modulusLen; i++) { unsignedint newLen = modulusLen - i - 1;
PRUint32 condition = PORT_CT_EQ(buffer[i], RSA_BLOCK_AFTER_PAD_OCTET) & PORT_CT_EQ(outLen, modulusLen);
outLen = PORT_CT_SEL(condition, newLen, outLen);
} // this can only happen if a zero wasn't found above
fail |= PORT_CT_GE(outLen, modulusLen);
outLen = PORT_CT_SEL(fail, errorLength, outLen);
/* index into the correct buffer. Do it before we truncate outLen if the
* application was asking for less data than we can return */
bp = buffer + modulusLen - outLen;
ep = errorBuffer + modulusLen - outLen;
/* at this point, outLen returns no information about decryption failures, * no need to hide its value. maxOutputLen is how much data the
* application is expecting, which is also not sensitive. */ if (outLen > maxOutputLen) {
outLen = maxOutputLen;
}
/* we can't use PORT_Memcpy because caching could create a time dependency
* on the status of fail. */ for (i = 0; i < outLen; i++) {
output[i] = PORT_CT_SEL(fail, ep[i], bp[i]);
}
*outputLen = outLen;
PORT_Free(buffer);
PORT_Free(errorBuffer);
return SECSuccess;
loser: if (hmac) {
HMAC_Destroy(hmac, PR_TRUE);
}
PORT_Free(buffer);
PORT_Free(errorBuffer);
return SECFailure;
}
/* * Encode a RSA-PSS signature. * Described in RFC 3447, section 9.1.1. * We use mHash instead of M as input. * emBits from the RFC is just modBits - 1, see section 8.1.1. * We only support MGF1 as the MGF.
*/
SECStatus
RSA_EMSAEncodePSS(unsignedchar *em, unsignedint emLen, unsignedint emBits, constunsignedchar *mHash,
HASH_HashType hashAlg,
HASH_HashType maskHashAlg, constunsignedchar *salt, unsignedint saltLen)
{ const SECHashObject *hash; void *hash_context; unsignedchar *dbMask; unsignedint dbMaskLen; unsignedint i;
SECStatus rv;
/* * Verify a RSA-PSS signature. * Described in RFC 3447, section 9.1.2. * We use mHash instead of M as input. * emBits from the RFC is just modBits - 1, see section 8.1.2. * We only support MGF1 as the MGF.
*/ static SECStatus
emsa_pss_verify(constunsignedchar *mHash, constunsignedchar *em, unsignedint emLen, unsignedint emBits,
HASH_HashType hashAlg,
HASH_HashType maskHashAlg, unsignedint saltLen)
{ const SECHashObject *hash; void *hash_context; unsignedchar *db; unsignedchar *H_; /* H' from the RFC */ unsignedint i; unsignedint dbMaskLen; unsignedint zeroBits;
SECStatus rv;
if (sigLen != modulusLen) {
PORT_SetError(SEC_ERROR_BAD_SIGNATURE); goto done;
}
/* * 0x00 || BT || Pad || 0x00 || ActualData * * The "3" below is the first octet + the second octet + the 0x00 * octet that always comes just before the ActualData.
*/ if (dataLen > modulusLen - (3 + RSA_BLOCK_MIN_PAD_LEN)) {
PORT_SetError(SEC_ERROR_BAD_DATA); goto done;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.