зеркало из https://github.com/mozilla/gecko-dev.git
Move the implementation of the TLS Pseudo Random Function (PRF) from
pkcs11c.c into a new file: tlsprf.c.
This commit is contained in:
Родитель
1bea4fac99
Коммит
140acf91e3
|
@ -77,4 +77,5 @@ CSRCS = \
|
||||||
rawhash.c \
|
rawhash.c \
|
||||||
rsawrapr.c \
|
rsawrapr.c \
|
||||||
softkver.c \
|
softkver.c \
|
||||||
|
tlsprf.c \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
|
@ -85,13 +85,6 @@
|
||||||
|
|
||||||
#include "pkcs11f.h"
|
#include "pkcs11f.h"
|
||||||
|
|
||||||
|
|
||||||
/* forward static declaration. */
|
|
||||||
static SECStatus pk11_PRF(const SECItem *secret, const char *label,
|
|
||||||
SECItem *seed, SECItem *result, PRBool isFIPS);
|
|
||||||
|
|
||||||
#define PK11_OFFSETOF(str, memb) ((PRPtrdiff)(&(((str *)0)->memb)))
|
|
||||||
|
|
||||||
static void pk11_Null(void *data, PRBool freeit)
|
static void pk11_Null(void *data, PRBool freeit)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -1419,165 +1412,6 @@ pk11_doSSLMACInit(PK11SessionContext *context,SECOidTag oid,
|
||||||
return CKR_OK;
|
return CKR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
PRUint32 cxSize; /* size of allocated block, in bytes. */
|
|
||||||
PRUint32 cxKeyLen; /* number of bytes of cxBuf containing key. */
|
|
||||||
PRUint32 cxDataLen; /* number of bytes of cxBuf containing data. */
|
|
||||||
SECStatus cxRv; /* records failure of void functions. */
|
|
||||||
PRBool cxIsFIPS; /* true if conforming to FIPS 198. */
|
|
||||||
unsigned char cxBuf[512]; /* actual size may be larger than 512. */
|
|
||||||
} TLSPRFContext;
|
|
||||||
|
|
||||||
static void
|
|
||||||
pk11_TLSPRFHashUpdate(TLSPRFContext *cx, const unsigned char *data,
|
|
||||||
unsigned int data_len)
|
|
||||||
{
|
|
||||||
PRUint32 bytesUsed = PK11_OFFSETOF(TLSPRFContext, cxBuf) +
|
|
||||||
cx->cxKeyLen + cx->cxDataLen;
|
|
||||||
|
|
||||||
if (cx->cxRv != SECSuccess) /* function has previously failed. */
|
|
||||||
return;
|
|
||||||
if (bytesUsed + data_len > cx->cxSize) {
|
|
||||||
/* We don't use realloc here because
|
|
||||||
** (a) realloc doesn't zero out the old block, and
|
|
||||||
** (b) if realloc fails, we lose the old block.
|
|
||||||
*/
|
|
||||||
PRUint32 blockSize = bytesUsed + data_len + 512;
|
|
||||||
TLSPRFContext *new_cx = (TLSPRFContext *)PORT_Alloc(blockSize);
|
|
||||||
if (!new_cx) {
|
|
||||||
cx->cxRv = SECFailure;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PORT_Memcpy(new_cx, cx, cx->cxSize);
|
|
||||||
new_cx->cxSize = blockSize;
|
|
||||||
|
|
||||||
PORT_ZFree(cx, cx->cxSize);
|
|
||||||
cx = new_cx;
|
|
||||||
}
|
|
||||||
PORT_Memcpy(cx->cxBuf + cx->cxKeyLen + cx->cxDataLen, data, data_len);
|
|
||||||
cx->cxDataLen += data_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
pk11_TLSPRFEnd(TLSPRFContext *ctx, unsigned char *hashout,
|
|
||||||
unsigned int *pDigestLen, unsigned int maxDigestLen)
|
|
||||||
{
|
|
||||||
*pDigestLen = 0; /* tells Verify that no data has been input yet. */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compute the PRF values from the data previously input. */
|
|
||||||
static SECStatus
|
|
||||||
pk11_TLSPRFUpdate(TLSPRFContext *cx,
|
|
||||||
unsigned char *sig, /* output goes here. */
|
|
||||||
unsigned int * sigLen, /* how much output. */
|
|
||||||
unsigned int maxLen, /* output buffer size */
|
|
||||||
unsigned char *hash, /* unused. */
|
|
||||||
unsigned int hashLen) /* unused. */
|
|
||||||
{
|
|
||||||
SECStatus rv;
|
|
||||||
SECItem sigItem;
|
|
||||||
SECItem seedItem;
|
|
||||||
SECItem secretItem;
|
|
||||||
|
|
||||||
if (cx->cxRv != SECSuccess)
|
|
||||||
return cx->cxRv;
|
|
||||||
|
|
||||||
secretItem.data = cx->cxBuf;
|
|
||||||
secretItem.len = cx->cxKeyLen;
|
|
||||||
|
|
||||||
seedItem.data = cx->cxBuf + cx->cxKeyLen;
|
|
||||||
seedItem.len = cx->cxDataLen;
|
|
||||||
|
|
||||||
sigItem.data = sig;
|
|
||||||
sigItem.len = maxLen;
|
|
||||||
|
|
||||||
rv = pk11_PRF(&secretItem, NULL, &seedItem, &sigItem, cx->cxIsFIPS);
|
|
||||||
if (rv == SECSuccess && sigLen != NULL)
|
|
||||||
*sigLen = sigItem.len;
|
|
||||||
return rv;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static SECStatus
|
|
||||||
pk11_TLSPRFVerify(TLSPRFContext *cx,
|
|
||||||
unsigned char *sig, /* input, for comparison. */
|
|
||||||
unsigned int sigLen, /* length of sig. */
|
|
||||||
unsigned char *hash, /* data to be verified. */
|
|
||||||
unsigned int hashLen) /* size of hash data. */
|
|
||||||
{
|
|
||||||
unsigned char * tmp = (unsigned char *)PORT_Alloc(sigLen);
|
|
||||||
unsigned int tmpLen = sigLen;
|
|
||||||
SECStatus rv;
|
|
||||||
|
|
||||||
if (!tmp)
|
|
||||||
return SECFailure;
|
|
||||||
if (hashLen) {
|
|
||||||
/* hashLen is non-zero when the user does a one-step verify.
|
|
||||||
** In this case, none of the data has been input yet.
|
|
||||||
*/
|
|
||||||
pk11_TLSPRFHashUpdate(cx, hash, hashLen);
|
|
||||||
}
|
|
||||||
rv = pk11_TLSPRFUpdate(cx, tmp, &tmpLen, sigLen, NULL, 0);
|
|
||||||
if (rv == SECSuccess) {
|
|
||||||
rv = (SECStatus)(1 - !PORT_Memcmp(tmp, sig, sigLen));
|
|
||||||
}
|
|
||||||
PORT_ZFree(tmp, sigLen);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
pk11_TLSPRFHashDestroy(TLSPRFContext *cx, PRBool freeit)
|
|
||||||
{
|
|
||||||
if (freeit)
|
|
||||||
PORT_ZFree(cx, cx->cxSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
static CK_RV
|
|
||||||
pk11_TLSPRFInit(PK11SessionContext *context,
|
|
||||||
PK11Object * key,
|
|
||||||
CK_KEY_TYPE key_type)
|
|
||||||
{
|
|
||||||
PK11Attribute * keyVal;
|
|
||||||
TLSPRFContext * prf_cx;
|
|
||||||
CK_RV crv = CKR_HOST_MEMORY;
|
|
||||||
PRUint32 keySize;
|
|
||||||
PRUint32 blockSize;
|
|
||||||
|
|
||||||
if (key_type != CKK_GENERIC_SECRET)
|
|
||||||
return CKR_KEY_TYPE_INCONSISTENT; /* CKR_KEY_FUNCTION_NOT_PERMITTED */
|
|
||||||
|
|
||||||
context->multi = PR_TRUE;
|
|
||||||
|
|
||||||
keyVal = pk11_FindAttribute(key, CKA_VALUE);
|
|
||||||
keySize = (!keyVal) ? 0 : keyVal->attrib.ulValueLen;
|
|
||||||
blockSize = keySize + sizeof(TLSPRFContext);
|
|
||||||
prf_cx = (TLSPRFContext *)PORT_Alloc(blockSize);
|
|
||||||
if (!prf_cx)
|
|
||||||
goto done;
|
|
||||||
prf_cx->cxSize = blockSize;
|
|
||||||
prf_cx->cxKeyLen = keySize;
|
|
||||||
prf_cx->cxDataLen = 0;
|
|
||||||
prf_cx->cxRv = SECSuccess;
|
|
||||||
prf_cx->cxIsFIPS = (key->slot->slotID == FIPS_SLOT_ID);
|
|
||||||
if (keySize)
|
|
||||||
PORT_Memcpy(prf_cx->cxBuf, keyVal->attrib.pValue, keySize);
|
|
||||||
|
|
||||||
context->hashInfo = (void *) prf_cx;
|
|
||||||
context->cipherInfo = (void *) prf_cx;
|
|
||||||
context->hashUpdate = (PK11Hash) pk11_TLSPRFHashUpdate;
|
|
||||||
context->end = (PK11End) pk11_TLSPRFEnd;
|
|
||||||
context->update = (PK11Cipher) pk11_TLSPRFUpdate;
|
|
||||||
context->verify = (PK11Verify) pk11_TLSPRFVerify;
|
|
||||||
context->destroy = (PK11Destroy) pk11_Null;
|
|
||||||
context->hashdestroy = (PK11Destroy) pk11_TLSPRFHashDestroy;
|
|
||||||
crv = CKR_OK;
|
|
||||||
|
|
||||||
done:
|
|
||||||
if (keyVal)
|
|
||||||
pk11_FreeAttribute(keyVal);
|
|
||||||
return crv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
************** Crypto Functions: Sign ************************
|
************** Crypto Functions: Sign ************************
|
||||||
*/
|
*/
|
||||||
|
@ -4076,127 +3910,6 @@ pk11_MapKeySize(CK_KEY_TYPE keyType) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PHASH_STATE_MAX_LEN 20
|
|
||||||
|
|
||||||
/* TLS P_hash function */
|
|
||||||
static SECStatus
|
|
||||||
pk11_P_hash(HASH_HashType hashType, const SECItem *secret, const char *label,
|
|
||||||
SECItem *seed, SECItem *result, PRBool isFIPS)
|
|
||||||
{
|
|
||||||
unsigned char state[PHASH_STATE_MAX_LEN];
|
|
||||||
unsigned char outbuf[PHASH_STATE_MAX_LEN];
|
|
||||||
unsigned int state_len = 0, label_len = 0, outbuf_len = 0, chunk_size;
|
|
||||||
unsigned int remaining;
|
|
||||||
unsigned char *res;
|
|
||||||
SECStatus status;
|
|
||||||
HMACContext *cx;
|
|
||||||
SECStatus rv = SECFailure;
|
|
||||||
const SECHashObject *hashObj = &SECRawHashObjects[hashType];
|
|
||||||
|
|
||||||
PORT_Assert((secret != NULL) && (secret->data != NULL || !secret->len));
|
|
||||||
PORT_Assert((seed != NULL) && (seed->data != NULL));
|
|
||||||
PORT_Assert((result != NULL) && (result->data != NULL));
|
|
||||||
|
|
||||||
remaining = result->len;
|
|
||||||
res = result->data;
|
|
||||||
|
|
||||||
if (label != NULL)
|
|
||||||
label_len = PORT_Strlen(label);
|
|
||||||
|
|
||||||
cx = HMAC_Create(hashObj, secret->data, secret->len, isFIPS);
|
|
||||||
if (cx == NULL)
|
|
||||||
goto loser;
|
|
||||||
|
|
||||||
/* initialize the state = A(1) = HMAC_hash(secret, seed) */
|
|
||||||
HMAC_Begin(cx);
|
|
||||||
HMAC_Update(cx, (unsigned char *)label, label_len);
|
|
||||||
HMAC_Update(cx, seed->data, seed->len);
|
|
||||||
status = HMAC_Finish(cx, state, &state_len, PHASH_STATE_MAX_LEN);
|
|
||||||
if (status != SECSuccess)
|
|
||||||
goto loser;
|
|
||||||
|
|
||||||
/* generate a block at a time until we're done */
|
|
||||||
while (remaining > 0) {
|
|
||||||
|
|
||||||
HMAC_Begin(cx);
|
|
||||||
HMAC_Update(cx, state, state_len);
|
|
||||||
if (label_len)
|
|
||||||
HMAC_Update(cx, (unsigned char *)label, label_len);
|
|
||||||
HMAC_Update(cx, seed->data, seed->len);
|
|
||||||
status = HMAC_Finish(cx, outbuf, &outbuf_len, PHASH_STATE_MAX_LEN);
|
|
||||||
if (status != SECSuccess)
|
|
||||||
goto loser;
|
|
||||||
|
|
||||||
/* Update the state = A(i) = HMAC_hash(secret, A(i-1)) */
|
|
||||||
HMAC_Begin(cx);
|
|
||||||
HMAC_Update(cx, state, state_len);
|
|
||||||
status = HMAC_Finish(cx, state, &state_len, PHASH_STATE_MAX_LEN);
|
|
||||||
if (status != SECSuccess)
|
|
||||||
goto loser;
|
|
||||||
|
|
||||||
chunk_size = PR_MIN(outbuf_len, remaining);
|
|
||||||
PORT_Memcpy(res, &outbuf, chunk_size);
|
|
||||||
res += chunk_size;
|
|
||||||
remaining -= chunk_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = SECSuccess;
|
|
||||||
|
|
||||||
loser:
|
|
||||||
/* if (cx) HMAC_Destroy(cx); */
|
|
||||||
/* clear out state so it's not left on the stack */
|
|
||||||
if (cx) HMAC_Destroy(cx);
|
|
||||||
PORT_Memset(state, 0, sizeof(state));
|
|
||||||
PORT_Memset(outbuf, 0, sizeof(outbuf));
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SECStatus
|
|
||||||
pk11_PRF(const SECItem *secret, const char *label, SECItem *seed,
|
|
||||||
SECItem *result, PRBool isFIPS)
|
|
||||||
{
|
|
||||||
SECStatus rv = SECFailure, status;
|
|
||||||
unsigned int i;
|
|
||||||
SECItem tmp = { siBuffer, NULL, 0};
|
|
||||||
SECItem S1;
|
|
||||||
SECItem S2;
|
|
||||||
|
|
||||||
PORT_Assert((secret != NULL) && (secret->data != NULL || !secret->len));
|
|
||||||
PORT_Assert((seed != NULL) && (seed->data != NULL));
|
|
||||||
PORT_Assert((result != NULL) && (result->data != NULL));
|
|
||||||
|
|
||||||
S1.type = siBuffer;
|
|
||||||
S1.len = (secret->len / 2) + (secret->len & 1);
|
|
||||||
S1.data = secret->data;
|
|
||||||
|
|
||||||
S2.type = siBuffer;
|
|
||||||
S2.len = S1.len;
|
|
||||||
S2.data = secret->data + (secret->len - S2.len);
|
|
||||||
|
|
||||||
tmp.data = (unsigned char*)PORT_Alloc(result->len);
|
|
||||||
if (tmp.data == NULL)
|
|
||||||
goto loser;
|
|
||||||
tmp.len = result->len;
|
|
||||||
|
|
||||||
status = pk11_P_hash(HASH_AlgMD5, &S1, label, seed, result, isFIPS);
|
|
||||||
if (status != SECSuccess)
|
|
||||||
goto loser;
|
|
||||||
|
|
||||||
status = pk11_P_hash(HASH_AlgSHA1, &S2, label, seed, &tmp, isFIPS);
|
|
||||||
if (status != SECSuccess)
|
|
||||||
goto loser;
|
|
||||||
|
|
||||||
for (i = 0; i < result->len; i++)
|
|
||||||
result->data[i] ^= tmp.data[i];
|
|
||||||
|
|
||||||
rv = SECSuccess;
|
|
||||||
|
|
||||||
loser:
|
|
||||||
if (tmp.data != NULL)
|
|
||||||
PORT_ZFree(tmp.data, tmp.len);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SSL Key generation given pre master secret
|
* SSL Key generation given pre master secret
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -669,6 +669,19 @@ PK11Object * pk11_NewTokenObject(PK11Slot *slot, SECItem *dbKey,
|
||||||
CK_OBJECT_HANDLE handle);
|
CK_OBJECT_HANDLE handle);
|
||||||
PK11TokenObject *pk11_convertSessionToToken(PK11Object *so);
|
PK11TokenObject *pk11_convertSessionToToken(PK11Object *so);
|
||||||
|
|
||||||
|
/****************************************
|
||||||
|
* implement TLS Pseudo Random Function (PRF)
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern SECStatus
|
||||||
|
pk11_PRF(const SECItem *secret, const char *label, SECItem *seed,
|
||||||
|
SECItem *result, PRBool isFIPS);
|
||||||
|
|
||||||
|
extern CK_RV
|
||||||
|
pk11_TLSPRFInit(PK11SessionContext *context,
|
||||||
|
PK11Object * key,
|
||||||
|
CK_KEY_TYPE key_type);
|
||||||
|
|
||||||
SEC_END_PROTOS
|
SEC_END_PROTOS
|
||||||
|
|
||||||
#endif /* _PKCS11I_H_ */
|
#endif /* _PKCS11I_H_ */
|
||||||
|
|
|
@ -0,0 +1,325 @@
|
||||||
|
/* tlsprf.c - TLS Pseudo Random Function (PRF) implementation
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public
|
||||||
|
* License Version 1.1 (the "License"); you may not use this file
|
||||||
|
* except in compliance with the License. You may obtain a copy of
|
||||||
|
* the License at http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS
|
||||||
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||||
|
* implied. See the License for the specific language governing
|
||||||
|
* rights and limitations under the License.
|
||||||
|
*
|
||||||
|
* The Original Code is the Netscape security libraries.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is Netscape
|
||||||
|
* Communications Corporation. Portions created by Netscape are
|
||||||
|
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
|
||||||
|
* Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the
|
||||||
|
* terms of the GNU General Public License Version 2 or later (the
|
||||||
|
* "GPL"), in which case the provisions of the GPL are applicable
|
||||||
|
* instead of those above. If you wish to allow use of your
|
||||||
|
* version of this file only under the terms of the GPL and not to
|
||||||
|
* allow others to use your version of this file under the MPL,
|
||||||
|
* indicate your decision by deleting the provisions above and
|
||||||
|
* replace them with the notice and other provisions required by
|
||||||
|
* the GPL. If you do not delete the provisions above, a recipient
|
||||||
|
* may use your version of this file under either the MPL or the
|
||||||
|
* GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pkcs11i.h"
|
||||||
|
#include "sechash.h"
|
||||||
|
#include "alghmac.h"
|
||||||
|
|
||||||
|
#define PK11_OFFSETOF(str, memb) ((PRPtrdiff)(&(((str *)0)->memb)))
|
||||||
|
|
||||||
|
#define PHASH_STATE_MAX_LEN 20
|
||||||
|
|
||||||
|
/* TLS P_hash function */
|
||||||
|
static SECStatus
|
||||||
|
pk11_P_hash(HASH_HashType hashType, const SECItem *secret, const char *label,
|
||||||
|
SECItem *seed, SECItem *result, PRBool isFIPS)
|
||||||
|
{
|
||||||
|
unsigned char state[PHASH_STATE_MAX_LEN];
|
||||||
|
unsigned char outbuf[PHASH_STATE_MAX_LEN];
|
||||||
|
unsigned int state_len = 0, label_len = 0, outbuf_len = 0, chunk_size;
|
||||||
|
unsigned int remaining;
|
||||||
|
unsigned char *res;
|
||||||
|
SECStatus status;
|
||||||
|
HMACContext *cx;
|
||||||
|
SECStatus rv = SECFailure;
|
||||||
|
const SECHashObject *hashObj = &SECRawHashObjects[hashType];
|
||||||
|
|
||||||
|
PORT_Assert((secret != NULL) && (secret->data != NULL || !secret->len));
|
||||||
|
PORT_Assert((seed != NULL) && (seed->data != NULL));
|
||||||
|
PORT_Assert((result != NULL) && (result->data != NULL));
|
||||||
|
|
||||||
|
remaining = result->len;
|
||||||
|
res = result->data;
|
||||||
|
|
||||||
|
if (label != NULL)
|
||||||
|
label_len = PORT_Strlen(label);
|
||||||
|
|
||||||
|
cx = HMAC_Create(hashObj, secret->data, secret->len, isFIPS);
|
||||||
|
if (cx == NULL)
|
||||||
|
goto loser;
|
||||||
|
|
||||||
|
/* initialize the state = A(1) = HMAC_hash(secret, seed) */
|
||||||
|
HMAC_Begin(cx);
|
||||||
|
HMAC_Update(cx, (unsigned char *)label, label_len);
|
||||||
|
HMAC_Update(cx, seed->data, seed->len);
|
||||||
|
status = HMAC_Finish(cx, state, &state_len, PHASH_STATE_MAX_LEN);
|
||||||
|
if (status != SECSuccess)
|
||||||
|
goto loser;
|
||||||
|
|
||||||
|
/* generate a block at a time until we're done */
|
||||||
|
while (remaining > 0) {
|
||||||
|
|
||||||
|
HMAC_Begin(cx);
|
||||||
|
HMAC_Update(cx, state, state_len);
|
||||||
|
if (label_len)
|
||||||
|
HMAC_Update(cx, (unsigned char *)label, label_len);
|
||||||
|
HMAC_Update(cx, seed->data, seed->len);
|
||||||
|
status = HMAC_Finish(cx, outbuf, &outbuf_len, PHASH_STATE_MAX_LEN);
|
||||||
|
if (status != SECSuccess)
|
||||||
|
goto loser;
|
||||||
|
|
||||||
|
/* Update the state = A(i) = HMAC_hash(secret, A(i-1)) */
|
||||||
|
HMAC_Begin(cx);
|
||||||
|
HMAC_Update(cx, state, state_len);
|
||||||
|
status = HMAC_Finish(cx, state, &state_len, PHASH_STATE_MAX_LEN);
|
||||||
|
if (status != SECSuccess)
|
||||||
|
goto loser;
|
||||||
|
|
||||||
|
chunk_size = PR_MIN(outbuf_len, remaining);
|
||||||
|
PORT_Memcpy(res, &outbuf, chunk_size);
|
||||||
|
res += chunk_size;
|
||||||
|
remaining -= chunk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = SECSuccess;
|
||||||
|
|
||||||
|
loser:
|
||||||
|
/* if (cx) HMAC_Destroy(cx); */
|
||||||
|
/* clear out state so it's not left on the stack */
|
||||||
|
if (cx) HMAC_Destroy(cx);
|
||||||
|
PORT_Memset(state, 0, sizeof(state));
|
||||||
|
PORT_Memset(outbuf, 0, sizeof(outbuf));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECStatus
|
||||||
|
pk11_PRF(const SECItem *secret, const char *label, SECItem *seed,
|
||||||
|
SECItem *result, PRBool isFIPS)
|
||||||
|
{
|
||||||
|
SECStatus rv = SECFailure, status;
|
||||||
|
unsigned int i;
|
||||||
|
SECItem tmp = { siBuffer, NULL, 0};
|
||||||
|
SECItem S1;
|
||||||
|
SECItem S2;
|
||||||
|
|
||||||
|
PORT_Assert((secret != NULL) && (secret->data != NULL || !secret->len));
|
||||||
|
PORT_Assert((seed != NULL) && (seed->data != NULL));
|
||||||
|
PORT_Assert((result != NULL) && (result->data != NULL));
|
||||||
|
|
||||||
|
S1.type = siBuffer;
|
||||||
|
S1.len = (secret->len / 2) + (secret->len & 1);
|
||||||
|
S1.data = secret->data;
|
||||||
|
|
||||||
|
S2.type = siBuffer;
|
||||||
|
S2.len = S1.len;
|
||||||
|
S2.data = secret->data + (secret->len - S2.len);
|
||||||
|
|
||||||
|
tmp.data = (unsigned char*)PORT_Alloc(result->len);
|
||||||
|
if (tmp.data == NULL)
|
||||||
|
goto loser;
|
||||||
|
tmp.len = result->len;
|
||||||
|
|
||||||
|
status = pk11_P_hash(HASH_AlgMD5, &S1, label, seed, result, isFIPS);
|
||||||
|
if (status != SECSuccess)
|
||||||
|
goto loser;
|
||||||
|
|
||||||
|
status = pk11_P_hash(HASH_AlgSHA1, &S2, label, seed, &tmp, isFIPS);
|
||||||
|
if (status != SECSuccess)
|
||||||
|
goto loser;
|
||||||
|
|
||||||
|
for (i = 0; i < result->len; i++)
|
||||||
|
result->data[i] ^= tmp.data[i];
|
||||||
|
|
||||||
|
rv = SECSuccess;
|
||||||
|
|
||||||
|
loser:
|
||||||
|
if (tmp.data != NULL)
|
||||||
|
PORT_ZFree(tmp.data, tmp.len);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pk11_TLSPRFNull(void *data, PRBool freeit)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PRUint32 cxSize; /* size of allocated block, in bytes. */
|
||||||
|
PRUint32 cxKeyLen; /* number of bytes of cxBuf containing key. */
|
||||||
|
PRUint32 cxDataLen; /* number of bytes of cxBuf containing data. */
|
||||||
|
SECStatus cxRv; /* records failure of void functions. */
|
||||||
|
PRBool cxIsFIPS; /* true if conforming to FIPS 198. */
|
||||||
|
unsigned char cxBuf[512]; /* actual size may be larger than 512. */
|
||||||
|
} TLSPRFContext;
|
||||||
|
|
||||||
|
static void
|
||||||
|
pk11_TLSPRFHashUpdate(TLSPRFContext *cx, const unsigned char *data,
|
||||||
|
unsigned int data_len)
|
||||||
|
{
|
||||||
|
PRUint32 bytesUsed = PK11_OFFSETOF(TLSPRFContext, cxBuf) +
|
||||||
|
cx->cxKeyLen + cx->cxDataLen;
|
||||||
|
|
||||||
|
if (cx->cxRv != SECSuccess) /* function has previously failed. */
|
||||||
|
return;
|
||||||
|
if (bytesUsed + data_len > cx->cxSize) {
|
||||||
|
/* We don't use realloc here because
|
||||||
|
** (a) realloc doesn't zero out the old block, and
|
||||||
|
** (b) if realloc fails, we lose the old block.
|
||||||
|
*/
|
||||||
|
PRUint32 blockSize = bytesUsed + data_len + 512;
|
||||||
|
TLSPRFContext *new_cx = (TLSPRFContext *)PORT_Alloc(blockSize);
|
||||||
|
if (!new_cx) {
|
||||||
|
cx->cxRv = SECFailure;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PORT_Memcpy(new_cx, cx, cx->cxSize);
|
||||||
|
new_cx->cxSize = blockSize;
|
||||||
|
|
||||||
|
PORT_ZFree(cx, cx->cxSize);
|
||||||
|
cx = new_cx;
|
||||||
|
}
|
||||||
|
PORT_Memcpy(cx->cxBuf + cx->cxKeyLen + cx->cxDataLen, data, data_len);
|
||||||
|
cx->cxDataLen += data_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pk11_TLSPRFEnd(TLSPRFContext *ctx, unsigned char *hashout,
|
||||||
|
unsigned int *pDigestLen, unsigned int maxDigestLen)
|
||||||
|
{
|
||||||
|
*pDigestLen = 0; /* tells Verify that no data has been input yet. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute the PRF values from the data previously input. */
|
||||||
|
static SECStatus
|
||||||
|
pk11_TLSPRFUpdate(TLSPRFContext *cx,
|
||||||
|
unsigned char *sig, /* output goes here. */
|
||||||
|
unsigned int * sigLen, /* how much output. */
|
||||||
|
unsigned int maxLen, /* output buffer size */
|
||||||
|
unsigned char *hash, /* unused. */
|
||||||
|
unsigned int hashLen) /* unused. */
|
||||||
|
{
|
||||||
|
SECStatus rv;
|
||||||
|
SECItem sigItem;
|
||||||
|
SECItem seedItem;
|
||||||
|
SECItem secretItem;
|
||||||
|
|
||||||
|
if (cx->cxRv != SECSuccess)
|
||||||
|
return cx->cxRv;
|
||||||
|
|
||||||
|
secretItem.data = cx->cxBuf;
|
||||||
|
secretItem.len = cx->cxKeyLen;
|
||||||
|
|
||||||
|
seedItem.data = cx->cxBuf + cx->cxKeyLen;
|
||||||
|
seedItem.len = cx->cxDataLen;
|
||||||
|
|
||||||
|
sigItem.data = sig;
|
||||||
|
sigItem.len = maxLen;
|
||||||
|
|
||||||
|
rv = pk11_PRF(&secretItem, NULL, &seedItem, &sigItem, cx->cxIsFIPS);
|
||||||
|
if (rv == SECSuccess && sigLen != NULL)
|
||||||
|
*sigLen = sigItem.len;
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static SECStatus
|
||||||
|
pk11_TLSPRFVerify(TLSPRFContext *cx,
|
||||||
|
unsigned char *sig, /* input, for comparison. */
|
||||||
|
unsigned int sigLen, /* length of sig. */
|
||||||
|
unsigned char *hash, /* data to be verified. */
|
||||||
|
unsigned int hashLen) /* size of hash data. */
|
||||||
|
{
|
||||||
|
unsigned char * tmp = (unsigned char *)PORT_Alloc(sigLen);
|
||||||
|
unsigned int tmpLen = sigLen;
|
||||||
|
SECStatus rv;
|
||||||
|
|
||||||
|
if (!tmp)
|
||||||
|
return SECFailure;
|
||||||
|
if (hashLen) {
|
||||||
|
/* hashLen is non-zero when the user does a one-step verify.
|
||||||
|
** In this case, none of the data has been input yet.
|
||||||
|
*/
|
||||||
|
pk11_TLSPRFHashUpdate(cx, hash, hashLen);
|
||||||
|
}
|
||||||
|
rv = pk11_TLSPRFUpdate(cx, tmp, &tmpLen, sigLen, NULL, 0);
|
||||||
|
if (rv == SECSuccess) {
|
||||||
|
rv = (SECStatus)(1 - !PORT_Memcmp(tmp, sig, sigLen));
|
||||||
|
}
|
||||||
|
PORT_ZFree(tmp, sigLen);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
pk11_TLSPRFHashDestroy(TLSPRFContext *cx, PRBool freeit)
|
||||||
|
{
|
||||||
|
if (freeit)
|
||||||
|
PORT_ZFree(cx, cx->cxSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
CK_RV
|
||||||
|
pk11_TLSPRFInit(PK11SessionContext *context,
|
||||||
|
PK11Object * key,
|
||||||
|
CK_KEY_TYPE key_type)
|
||||||
|
{
|
||||||
|
PK11Attribute * keyVal;
|
||||||
|
TLSPRFContext * prf_cx;
|
||||||
|
CK_RV crv = CKR_HOST_MEMORY;
|
||||||
|
PRUint32 keySize;
|
||||||
|
PRUint32 blockSize;
|
||||||
|
|
||||||
|
if (key_type != CKK_GENERIC_SECRET)
|
||||||
|
return CKR_KEY_TYPE_INCONSISTENT; /* CKR_KEY_FUNCTION_NOT_PERMITTED */
|
||||||
|
|
||||||
|
context->multi = PR_TRUE;
|
||||||
|
|
||||||
|
keyVal = pk11_FindAttribute(key, CKA_VALUE);
|
||||||
|
keySize = (!keyVal) ? 0 : keyVal->attrib.ulValueLen;
|
||||||
|
blockSize = keySize + sizeof(TLSPRFContext);
|
||||||
|
prf_cx = (TLSPRFContext *)PORT_Alloc(blockSize);
|
||||||
|
if (!prf_cx)
|
||||||
|
goto done;
|
||||||
|
prf_cx->cxSize = blockSize;
|
||||||
|
prf_cx->cxKeyLen = keySize;
|
||||||
|
prf_cx->cxDataLen = 0;
|
||||||
|
prf_cx->cxRv = SECSuccess;
|
||||||
|
prf_cx->cxIsFIPS = (key->slot->slotID == FIPS_SLOT_ID);
|
||||||
|
if (keySize)
|
||||||
|
PORT_Memcpy(prf_cx->cxBuf, keyVal->attrib.pValue, keySize);
|
||||||
|
|
||||||
|
context->hashInfo = (void *) prf_cx;
|
||||||
|
context->cipherInfo = (void *) prf_cx;
|
||||||
|
context->hashUpdate = (PK11Hash) pk11_TLSPRFHashUpdate;
|
||||||
|
context->end = (PK11End) pk11_TLSPRFEnd;
|
||||||
|
context->update = (PK11Cipher) pk11_TLSPRFUpdate;
|
||||||
|
context->verify = (PK11Verify) pk11_TLSPRFVerify;
|
||||||
|
context->destroy = (PK11Destroy) pk11_TLSPRFNull;
|
||||||
|
context->hashdestroy = (PK11Destroy) pk11_TLSPRFHashDestroy;
|
||||||
|
crv = CKR_OK;
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (keyVal)
|
||||||
|
pk11_FreeAttribute(keyVal);
|
||||||
|
return crv;
|
||||||
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче