Skip to content
Permalink
d89cd00bd6
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
7811 lines (7087 sloc) 266 KB
/* 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/. */
/*
* This file implements PKCS 11 on top of our existing security modules
*
* For more information about PKCS 11 See PKCS 11 Token Inteface Standard.
* This implementation has two slots:
* slot 1 is our generic crypto support. It does not require login.
* It supports Public Key ops, and all they bulk ciphers and hashes.
* It can also support Private Key ops for imported Private keys. It does
* not have any token storage.
* slot 2 is our private key support. It requires a login before use. It
* can store Private Keys and Certs as token objects. Currently only private
* keys and their associated Certificates are saved on the token.
*
* In this implementation, session objects are only visible to the session
* that created or generated them.
*/
#include "seccomon.h"
#include "secitem.h"
#include "secport.h"
#include "blapi.h"
#include "pkcs11.h"
#include "pkcs11i.h"
#include "pkcs1sig.h"
#include "lowkeyi.h"
#include "secder.h"
#include "secdig.h"
#include "lowpbe.h" /* We do PBE below */
#include "pkcs11t.h"
#include "secoid.h"
#include "alghmac.h"
#include "softoken.h"
#include "secasn1.h"
#include "secerr.h"
#include "prprf.h"
#include "prenv.h"
#define __PASTE(x, y) x##y
/*
* we renamed all our internal functions, get the correct
* definitions for them...
*/
#undef CK_PKCS11_FUNCTION_INFO
#undef CK_NEED_ARG_LIST
#define CK_EXTERN extern
#define CK_PKCS11_FUNCTION_INFO(func) \
CK_RV __PASTE(NS, func)
#define CK_NEED_ARG_LIST 1
#include "pkcs11f.h"
typedef struct {
PRUint8 client_version[2];
PRUint8 random[46];
} SSL3RSAPreMasterSecret;
static void
sftk_Null(void *data, PRBool freeit)
{
return;
}
#ifdef EC_DEBUG
#define SEC_PRINT(str1, str2, num, sitem) \
printf("pkcs11c.c:%s:%s (keytype=%d) [len=%d]\n", \
str1, str2, num, sitem->len); \
for (i = 0; i < sitem->len; i++) { \
printf("%02x:", sitem->data[i]); \
} \
printf("\n")
#else
#undef EC_DEBUG
#define SEC_PRINT(a, b, c, d)
#endif
/*
* free routines.... Free local type allocated data, and convert
* other free routines to the destroy signature.
*/
static void
sftk_FreePrivKey(NSSLOWKEYPrivateKey *key, PRBool freeit)
{
nsslowkey_DestroyPrivateKey(key);
}
static void
sftk_Space(void *data, PRBool freeit)
{
PORT_Free(data);
}
/*
* map all the SEC_ERROR_xxx error codes that may be returned by freebl
* functions to CKR_xxx. return CKR_DEVICE_ERROR by default for backward
* compatibility.
*/
static CK_RV
sftk_MapCryptError(int error)
{
switch (error) {
case SEC_ERROR_INVALID_ARGS:
case SEC_ERROR_BAD_DATA: /* MP_RANGE gets mapped to this */
return CKR_ARGUMENTS_BAD;
case SEC_ERROR_INPUT_LEN:
return CKR_DATA_LEN_RANGE;
case SEC_ERROR_OUTPUT_LEN:
return CKR_BUFFER_TOO_SMALL;
case SEC_ERROR_LIBRARY_FAILURE:
return CKR_GENERAL_ERROR;
case SEC_ERROR_NO_MEMORY:
return CKR_HOST_MEMORY;
case SEC_ERROR_BAD_SIGNATURE:
return CKR_SIGNATURE_INVALID;
case SEC_ERROR_INVALID_KEY:
return CKR_KEY_SIZE_RANGE;
case SEC_ERROR_BAD_KEY: /* an EC public key that fails validation */
return CKR_KEY_SIZE_RANGE; /* the closest error code */
case SEC_ERROR_UNSUPPORTED_EC_POINT_FORM:
return CKR_TEMPLATE_INCONSISTENT;
case SEC_ERROR_UNSUPPORTED_KEYALG:
return CKR_MECHANISM_INVALID;
case SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE:
return CKR_DOMAIN_PARAMS_INVALID;
/* key pair generation failed after max number of attempts */
case SEC_ERROR_NEED_RANDOM:
return CKR_FUNCTION_FAILED;
}
return CKR_DEVICE_ERROR;
}
/* used by Decrypt and UnwrapKey (indirectly) */
static CK_RV
sftk_MapDecryptError(int error)
{
switch (error) {
case SEC_ERROR_BAD_DATA:
return CKR_ENCRYPTED_DATA_INVALID;
default:
return sftk_MapCryptError(error);
}
}
/*
* return CKR_SIGNATURE_INVALID instead of CKR_DEVICE_ERROR by default for
* backward compatibilty.
*/
static CK_RV
sftk_MapVerifyError(int error)
{
CK_RV crv = sftk_MapCryptError(error);
if (crv == CKR_DEVICE_ERROR)
crv = CKR_SIGNATURE_INVALID;
return crv;
}
/*
* turn a CDMF key into a des key. CDMF is an old IBM scheme to export DES by
* Deprecating a full des key to 40 bit key strenth.
*/
static CK_RV
sftk_cdmf2des(unsigned char *cdmfkey, unsigned char *deskey)
{
unsigned char key1[8] = { 0xc4, 0x08, 0xb0, 0x54, 0x0b, 0xa1, 0xe0, 0xae };
unsigned char key2[8] = { 0xef, 0x2c, 0x04, 0x1c, 0xe6, 0x38, 0x2f, 0xe6 };
unsigned char enc_src[8];
unsigned char enc_dest[8];
unsigned int leng, i;
DESContext *descx;
SECStatus rv;
/* zero the parity bits */
for (i = 0; i < 8; i++) {
enc_src[i] = cdmfkey[i] & 0xfe;
}
/* encrypt with key 1 */
descx = DES_CreateContext(key1, NULL, NSS_DES, PR_TRUE);
if (descx == NULL)
return CKR_HOST_MEMORY;
rv = DES_Encrypt(descx, enc_dest, &leng, 8, enc_src, 8);
DES_DestroyContext(descx, PR_TRUE);
if (rv != SECSuccess)
return sftk_MapCryptError(PORT_GetError());
/* xor source with des, zero the parity bits and deprecate the key*/
for (i = 0; i < 8; i++) {
if (i & 1) {
enc_src[i] = (enc_src[i] ^ enc_dest[i]) & 0xfe;
} else {
enc_src[i] = (enc_src[i] ^ enc_dest[i]) & 0x0e;
}
}
/* encrypt with key 2 */
descx = DES_CreateContext(key2, NULL, NSS_DES, PR_TRUE);
if (descx == NULL)
return CKR_HOST_MEMORY;
rv = DES_Encrypt(descx, deskey, &leng, 8, enc_src, 8);
DES_DestroyContext(descx, PR_TRUE);
if (rv != SECSuccess)
return sftk_MapCryptError(PORT_GetError());
/* set the corret parity on our new des key */
sftk_FormatDESKey(deskey, 8);
return CKR_OK;
}
/* NSC_DestroyObject destroys an object. */
CK_RV
NSC_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject)
{
SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession);
SFTKSession *session;
SFTKObject *object;
SFTKFreeStatus status;
CHECK_FORK();
if (slot == NULL) {
return CKR_SESSION_HANDLE_INVALID;
}
/*
* This whole block just makes sure we really can destroy the
* requested object.
*/
session = sftk_SessionFromHandle(hSession);
if (session == NULL) {
return CKR_SESSION_HANDLE_INVALID;
}
object = sftk_ObjectFromHandle(hObject, session);
if (object == NULL) {
sftk_FreeSession(session);
return CKR_OBJECT_HANDLE_INVALID;
}
/* don't destroy a private object if we aren't logged in */
if ((!slot->isLoggedIn) && (slot->needLogin) &&
(sftk_isTrue(object, CKA_PRIVATE))) {
sftk_FreeSession(session);
sftk_FreeObject(object);
return CKR_USER_NOT_LOGGED_IN;
}
/* don't destroy a token object if we aren't in a rw session */
if (((session->info.flags & CKF_RW_SESSION) == 0) &&
(sftk_isTrue(object, CKA_TOKEN))) {
sftk_FreeSession(session);
sftk_FreeObject(object);
return CKR_SESSION_READ_ONLY;
}
sftk_DeleteObject(session, object);
sftk_FreeSession(session);
/*
* get some indication if the object is destroyed. Note: this is not
* 100%. Someone may have an object reference outstanding (though that
* should not be the case by here. Also note that the object is "half"
* destroyed. Our internal representation is destroyed, but it may still
* be in the data base.
*/
status = sftk_FreeObject(object);
return (status != SFTK_DestroyFailure) ? CKR_OK : CKR_DEVICE_ERROR;
}
/*
************** Crypto Functions: Utilities ************************
*/
/*
* Utility function for converting PSS/OAEP parameter types into
* HASH_HashTypes. Note: Only SHA family functions are defined in RFC 3447.
*/
static HASH_HashType
GetHashTypeFromMechanism(CK_MECHANISM_TYPE mech)
{
switch (mech) {
case CKM_SHA_1:
case CKG_MGF1_SHA1:
return HASH_AlgSHA1;
case CKM_SHA224:
case CKG_MGF1_SHA224:
return HASH_AlgSHA224;
case CKM_SHA256:
case CKG_MGF1_SHA256:
return HASH_AlgSHA256;
case CKM_SHA384:
case CKG_MGF1_SHA384:
return HASH_AlgSHA384;
case CKM_SHA512:
case CKG_MGF1_SHA512:
return HASH_AlgSHA512;
default:
return HASH_AlgNULL;
}
}
/*
* Returns true if "params" contains a valid set of PSS parameters
*/
static PRBool
sftk_ValidatePssParams(const CK_RSA_PKCS_PSS_PARAMS *params)
{
if (!params) {
return PR_FALSE;
}
if (GetHashTypeFromMechanism(params->hashAlg) == HASH_AlgNULL ||
GetHashTypeFromMechanism(params->mgf) == HASH_AlgNULL) {
return PR_FALSE;
}
return PR_TRUE;
}
/*
* Returns true if "params" contains a valid set of OAEP parameters
*/
static PRBool
sftk_ValidateOaepParams(const CK_RSA_PKCS_OAEP_PARAMS *params)
{
if (!params) {
return PR_FALSE;
}
/* The requirements of ulSourceLen/pSourceData come from PKCS #11, which
* state:
* If the parameter is empty, pSourceData must be NULL and
* ulSourceDataLen must be zero.
*/
if (params->source != CKZ_DATA_SPECIFIED ||
(GetHashTypeFromMechanism(params->hashAlg) == HASH_AlgNULL) ||
(GetHashTypeFromMechanism(params->mgf) == HASH_AlgNULL) ||
(params->ulSourceDataLen == 0 && params->pSourceData != NULL) ||
(params->ulSourceDataLen != 0 && params->pSourceData == NULL)) {
return PR_FALSE;
}
return PR_TRUE;
}
/*
* return a context based on the SFTKContext type.
*/
SFTKSessionContext *
sftk_ReturnContextByType(SFTKSession *session, SFTKContextType type)
{
switch (type) {
case SFTK_ENCRYPT:
case SFTK_DECRYPT:
return session->enc_context;
case SFTK_HASH:
return session->hash_context;
case SFTK_SIGN:
case SFTK_SIGN_RECOVER:
case SFTK_VERIFY:
case SFTK_VERIFY_RECOVER:
return session->hash_context;
}
return NULL;
}
/*
* change a context based on the SFTKContext type.
*/
void
sftk_SetContextByType(SFTKSession *session, SFTKContextType type,
SFTKSessionContext *context)
{
switch (type) {
case SFTK_ENCRYPT:
case SFTK_DECRYPT:
session->enc_context = context;
break;
case SFTK_HASH:
session->hash_context = context;
break;
case SFTK_SIGN:
case SFTK_SIGN_RECOVER:
case SFTK_VERIFY:
case SFTK_VERIFY_RECOVER:
session->hash_context = context;
break;
}
return;
}
/*
* code to grab the context. Needed by every C_XXXUpdate, C_XXXFinal,
* and C_XXX function. The function takes a session handle, the context type,
* and wether or not the session needs to be multipart. It returns the context,
* and optionally returns the session pointer (if sessionPtr != NULL) if session
* pointer is returned, the caller is responsible for freeing it.
*/
static CK_RV
sftk_GetContext(CK_SESSION_HANDLE handle, SFTKSessionContext **contextPtr,
SFTKContextType type, PRBool needMulti, SFTKSession **sessionPtr)
{
SFTKSession *session;
SFTKSessionContext *context;
session = sftk_SessionFromHandle(handle);
if (session == NULL)
return CKR_SESSION_HANDLE_INVALID;
context = sftk_ReturnContextByType(session, type);
/* make sure the context is valid */
if ((context == NULL) || (context->type != type) || (needMulti && !(context->multi))) {
sftk_FreeSession(session);
return CKR_OPERATION_NOT_INITIALIZED;
}
*contextPtr = context;
if (sessionPtr != NULL) {
*sessionPtr = session;
} else {
sftk_FreeSession(session);
}
return CKR_OK;
}
/** Terminate operation (in the PKCS#11 spec sense).
* Intuitive name for FreeContext/SetNullContext pair.
*/
static void
sftk_TerminateOp(SFTKSession *session, SFTKContextType ctype,
SFTKSessionContext *context)
{
sftk_FreeContext(context);
sftk_SetContextByType(session, ctype, NULL);
}
/*
************** Crypto Functions: Encrypt ************************
*/
/*
* All the NSC_InitXXX functions have a set of common checks and processing they
* all need to do at the beginning. This is done here.
*/
static CK_RV
sftk_InitGeneric(SFTKSession *session, SFTKSessionContext **contextPtr,
SFTKContextType ctype, SFTKObject **keyPtr,
CK_OBJECT_HANDLE hKey, CK_KEY_TYPE *keyTypePtr,
CK_OBJECT_CLASS pubKeyType, CK_ATTRIBUTE_TYPE operation)
{
SFTKObject *key = NULL;
SFTKAttribute *att;
SFTKSessionContext *context;
/* We can only init if there is not current context active */
if (sftk_ReturnContextByType(session, ctype) != NULL) {
return CKR_OPERATION_ACTIVE;
}
/* find the key */
if (keyPtr) {
key = sftk_ObjectFromHandle(hKey, session);
if (key == NULL) {
return CKR_KEY_HANDLE_INVALID;
}
/* make sure it's a valid key for this operation */
if (((key->objclass != CKO_SECRET_KEY) && (key->objclass != pubKeyType)) || !sftk_isTrue(key, operation)) {
sftk_FreeObject(key);
return CKR_KEY_TYPE_INCONSISTENT;
}
/* get the key type */
att = sftk_FindAttribute(key, CKA_KEY_TYPE);
if (att == NULL) {
sftk_FreeObject(key);
return CKR_KEY_TYPE_INCONSISTENT;
}
PORT_Assert(att->attrib.ulValueLen == sizeof(CK_KEY_TYPE));
if (att->attrib.ulValueLen != sizeof(CK_KEY_TYPE)) {
sftk_FreeAttribute(att);
sftk_FreeObject(key);
return CKR_ATTRIBUTE_VALUE_INVALID;
}
PORT_Memcpy(keyTypePtr, att->attrib.pValue, sizeof(CK_KEY_TYPE));
sftk_FreeAttribute(att);
*keyPtr = key;
}
/* allocate the context structure */
context = (SFTKSessionContext *)PORT_Alloc(sizeof(SFTKSessionContext));
if (context == NULL) {
if (key)
sftk_FreeObject(key);
return CKR_HOST_MEMORY;
}
context->type = ctype;
context->multi = PR_TRUE;
context->rsa = PR_FALSE;
context->cipherInfo = NULL;
context->hashInfo = NULL;
context->doPad = PR_FALSE;
context->padDataLength = 0;
context->key = key;
context->blockSize = 0;
context->maxLen = 0;
*contextPtr = context;
return CKR_OK;
}
static int
sftk_aes_mode(CK_MECHANISM_TYPE mechanism)
{
switch (mechanism) {
case CKM_AES_CBC_PAD:
case CKM_AES_CBC:
return NSS_AES_CBC;
case CKM_AES_ECB:
return NSS_AES;
case CKM_AES_CTS:
return NSS_AES_CTS;
case CKM_AES_CTR:
return NSS_AES_CTR;
case CKM_AES_GCM:
return NSS_AES_GCM;
}
return -1;
}
static SECStatus
sftk_RSAEncryptRaw(NSSLOWKEYPublicKey *key, unsigned char *output,
unsigned int *outputLen, unsigned int maxLen,
const unsigned char *input, unsigned int inputLen)
{
SECStatus rv = SECFailure;
PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
if (key->keyType != NSSLOWKEYRSAKey) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
rv = RSA_EncryptRaw(&key->u.rsa, output, outputLen, maxLen, input,
inputLen);
if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
sftk_fatalError = PR_TRUE;
}
return rv;
}
static SECStatus
sftk_RSADecryptRaw(NSSLOWKEYPrivateKey *key, unsigned char *output,
unsigned int *outputLen, unsigned int maxLen,
const unsigned char *input, unsigned int inputLen)
{
SECStatus rv = SECFailure;
PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
if (key->keyType != NSSLOWKEYRSAKey) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
rv = RSA_DecryptRaw(&key->u.rsa, output, outputLen, maxLen, input,
inputLen);
if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
sftk_fatalError = PR_TRUE;
}
return rv;
}
static SECStatus
sftk_RSAEncrypt(NSSLOWKEYPublicKey *key, unsigned char *output,
unsigned int *outputLen, unsigned int maxLen,
const unsigned char *input, unsigned int inputLen)
{
SECStatus rv = SECFailure;
PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
if (key->keyType != NSSLOWKEYRSAKey) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
rv = RSA_EncryptBlock(&key->u.rsa, output, outputLen, maxLen, input,
inputLen);
if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
sftk_fatalError = PR_TRUE;
}
return rv;
}
static SECStatus
sftk_RSADecrypt(NSSLOWKEYPrivateKey *key, unsigned char *output,
unsigned int *outputLen, unsigned int maxLen,
const unsigned char *input, unsigned int inputLen)
{
SECStatus rv = SECFailure;
PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
if (key->keyType != NSSLOWKEYRSAKey) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
rv = RSA_DecryptBlock(&key->u.rsa, output, outputLen, maxLen, input,
inputLen);
if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
sftk_fatalError = PR_TRUE;
}
return rv;
}
static SECStatus
sftk_RSAEncryptOAEP(SFTKOAEPEncryptInfo *info, unsigned char *output,
unsigned int *outputLen, unsigned int maxLen,
const unsigned char *input, unsigned int inputLen)
{
HASH_HashType hashAlg;
HASH_HashType maskHashAlg;
PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey);
if (info->key->keyType != NSSLOWKEYRSAKey) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
hashAlg = GetHashTypeFromMechanism(info->params->hashAlg);
maskHashAlg = GetHashTypeFromMechanism(info->params->mgf);
return RSA_EncryptOAEP(&info->key->u.rsa, hashAlg, maskHashAlg,
(const unsigned char *)info->params->pSourceData,
info->params->ulSourceDataLen, NULL, 0,
output, outputLen, maxLen, input, inputLen);
}
static SECStatus
sftk_RSADecryptOAEP(SFTKOAEPDecryptInfo *info, unsigned char *output,
unsigned int *outputLen, unsigned int maxLen,
const unsigned char *input, unsigned int inputLen)
{
SECStatus rv = SECFailure;
HASH_HashType hashAlg;
HASH_HashType maskHashAlg;
PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey);
if (info->key->keyType != NSSLOWKEYRSAKey) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
hashAlg = GetHashTypeFromMechanism(info->params->hashAlg);
maskHashAlg = GetHashTypeFromMechanism(info->params->mgf);
rv = RSA_DecryptOAEP(&info->key->u.rsa, hashAlg, maskHashAlg,
(const unsigned char *)info->params->pSourceData,
info->params->ulSourceDataLen,
output, outputLen, maxLen, input, inputLen);
if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
sftk_fatalError = PR_TRUE;
}
return rv;
}
static SFTKChaCha20Poly1305Info *
sftk_ChaCha20Poly1305_CreateContext(const unsigned char *key,
unsigned int keyLen,
const CK_NSS_AEAD_PARAMS *params)
{
SFTKChaCha20Poly1305Info *ctx;
if (params->ulNonceLen != sizeof(ctx->nonce)) {
PORT_SetError(SEC_ERROR_INPUT_LEN);
return NULL;
}
ctx = PORT_New(SFTKChaCha20Poly1305Info);
if (ctx == NULL) {
return NULL;
}
if (ChaCha20Poly1305_InitContext(&ctx->freeblCtx, key, keyLen,
params->ulTagLen) != SECSuccess) {
PORT_Free(ctx);
return NULL;
}
PORT_Memcpy(ctx->nonce, params->pNonce, sizeof(ctx->nonce));
/* AAD data and length must both be null, or both non-null. */
PORT_Assert((params->pAAD == NULL) == (params->ulAADLen == 0));
if (params->ulAADLen > sizeof(ctx->ad)) {
/* Need to allocate an overflow buffer for the additional data. */
ctx->adOverflow = (unsigned char *)PORT_Alloc(params->ulAADLen);
if (!ctx->adOverflow) {
PORT_Free(ctx);
return NULL;
}
PORT_Memcpy(ctx->adOverflow, params->pAAD, params->ulAADLen);
} else {
ctx->adOverflow = NULL;
if (params->pAAD) {
PORT_Memcpy(ctx->ad, params->pAAD, params->ulAADLen);
}
}
ctx->adLen = params->ulAADLen;
return ctx;
}
static void
sftk_ChaCha20Poly1305_DestroyContext(SFTKChaCha20Poly1305Info *ctx,
PRBool freeit)
{
ChaCha20Poly1305_DestroyContext(&ctx->freeblCtx, PR_FALSE);
if (ctx->adOverflow != NULL) {
PORT_Free(ctx->adOverflow);
ctx->adOverflow = NULL;
}
ctx->adLen = 0;
if (freeit) {
PORT_Free(ctx);
}
}
static SECStatus
sftk_ChaCha20Poly1305_Encrypt(const SFTKChaCha20Poly1305Info *ctx,
unsigned char *output, unsigned int *outputLen,
unsigned int maxOutputLen,
const unsigned char *input, unsigned int inputLen)
{
const unsigned char *ad = ctx->adOverflow;
if (ad == NULL) {
ad = ctx->ad;
}
return ChaCha20Poly1305_Seal(&ctx->freeblCtx, output, outputLen,
maxOutputLen, input, inputLen, ctx->nonce,
sizeof(ctx->nonce), ad, ctx->adLen);
}
static SECStatus
sftk_ChaCha20Poly1305_Decrypt(const SFTKChaCha20Poly1305Info *ctx,
unsigned char *output, unsigned int *outputLen,
unsigned int maxOutputLen,
const unsigned char *input, unsigned int inputLen)
{
const unsigned char *ad = ctx->adOverflow;
if (ad == NULL) {
ad = ctx->ad;
}
return ChaCha20Poly1305_Open(&ctx->freeblCtx, output, outputLen,
maxOutputLen, input, inputLen, ctx->nonce,
sizeof(ctx->nonce), ad, ctx->adLen);
}
/** NSC_CryptInit initializes an encryption/Decryption operation.
*
* Always called by NSC_EncryptInit, NSC_DecryptInit, NSC_WrapKey,NSC_UnwrapKey.
* Called by NSC_SignInit, NSC_VerifyInit (via sftk_InitCBCMac) only for block
* ciphers MAC'ing.
*/
static CK_RV
sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
CK_OBJECT_HANDLE hKey,
CK_ATTRIBUTE_TYPE mechUsage, CK_ATTRIBUTE_TYPE keyUsage,
SFTKContextType contextType, PRBool isEncrypt)
{
SFTKSession *session;
SFTKObject *key;
SFTKSessionContext *context;
SFTKAttribute *att;
CK_RC2_CBC_PARAMS *rc2_param;
#if NSS_SOFTOKEN_DOES_RC5
CK_RC5_CBC_PARAMS *rc5_param;
SECItem rc5Key;
#endif
CK_KEY_TYPE key_type;
CK_RV crv = CKR_OK;
unsigned effectiveKeyLength;
unsigned char newdeskey[24];
PRBool useNewKey = PR_FALSE;
int t;
crv = sftk_MechAllowsOperation(pMechanism->mechanism, mechUsage);
if (crv != CKR_OK)
return crv;
session = sftk_SessionFromHandle(hSession);
if (session == NULL)
return CKR_SESSION_HANDLE_INVALID;
crv = sftk_InitGeneric(session, &context, contextType, &key, hKey, &key_type,
isEncrypt ? CKO_PUBLIC_KEY : CKO_PRIVATE_KEY, keyUsage);
if (crv != CKR_OK) {
sftk_FreeSession(session);
return crv;
}
context->doPad = PR_FALSE;
switch (pMechanism->mechanism) {
case CKM_RSA_PKCS:
case CKM_RSA_X_509:
if (key_type != CKK_RSA) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
context->multi = PR_FALSE;
context->rsa = PR_TRUE;
if (isEncrypt) {
NSSLOWKEYPublicKey *pubKey = sftk_GetPubKey(key, CKK_RSA, &crv);
if (pubKey == NULL) {
crv = CKR_KEY_HANDLE_INVALID;
break;
}
context->maxLen = nsslowkey_PublicModulusLen(pubKey);
context->cipherInfo = (void *)pubKey;
context->update = (SFTKCipher)(pMechanism->mechanism == CKM_RSA_X_509
? sftk_RSAEncryptRaw
: sftk_RSAEncrypt);
} else {
NSSLOWKEYPrivateKey *privKey = sftk_GetPrivKey(key, CKK_RSA, &crv);
if (privKey == NULL) {
crv = CKR_KEY_HANDLE_INVALID;
break;
}
context->maxLen = nsslowkey_PrivateModulusLen(privKey);
context->cipherInfo = (void *)privKey;
context->update = (SFTKCipher)(pMechanism->mechanism == CKM_RSA_X_509
? sftk_RSADecryptRaw
: sftk_RSADecrypt);
}
context->destroy = sftk_Null;
break;
case CKM_RSA_PKCS_OAEP:
if (key_type != CKK_RSA) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
if (pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_OAEP_PARAMS) ||
!sftk_ValidateOaepParams((CK_RSA_PKCS_OAEP_PARAMS *)pMechanism->pParameter)) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
context->multi = PR_FALSE;
context->rsa = PR_TRUE;
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_RSAEncryptOAEP;
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_RSADecryptOAEP;
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 */
case CKM_RC2_ECB:
case CKM_RC2_CBC:
context->blockSize = 8;
if (key_type != CKK_RC2) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
att = sftk_FindAttribute(key, CKA_VALUE);
if (att == NULL) {
crv = CKR_KEY_HANDLE_INVALID;
break;
}
rc2_param = (CK_RC2_CBC_PARAMS *)pMechanism->pParameter;
effectiveKeyLength = (rc2_param->ulEffectiveBits + 7) / 8;
context->cipherInfo =
RC2_CreateContext((unsigned char *)att->attrib.pValue,
att->attrib.ulValueLen, rc2_param->iv,
pMechanism->mechanism == CKM_RC2_ECB ? NSS_RC2 : NSS_RC2_CBC, effectiveKeyLength);
sftk_FreeAttribute(att);
if (context->cipherInfo == NULL) {
crv = CKR_HOST_MEMORY;
break;
}
context->update = (SFTKCipher)(isEncrypt ? RC2_Encrypt : RC2_Decrypt);
context->destroy = (SFTKDestroy)RC2_DestroyContext;
break;
#if NSS_SOFTOKEN_DOES_RC5
case CKM_RC5_CBC_PAD:
context->doPad = PR_TRUE;
/* fall thru */
case CKM_RC5_ECB:
case CKM_RC5_CBC:
if (key_type != CKK_RC5) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
att = sftk_FindAttribute(key, CKA_VALUE);
if (att == NULL) {
crv = CKR_KEY_HANDLE_INVALID;
break;
}
rc5_param = (CK_RC5_CBC_PARAMS *)pMechanism->pParameter;
context->blockSize = rc5_param->ulWordsize * 2;
rc5Key.data = (unsigned char *)att->attrib.pValue;
rc5Key.len = att->attrib.ulValueLen;
context->cipherInfo = RC5_CreateContext(&rc5Key, rc5_param->ulRounds,
rc5_param->ulWordsize, rc5_param->pIv,
pMechanism->mechanism == CKM_RC5_ECB ? NSS_RC5 : NSS_RC5_CBC);
sftk_FreeAttribute(att);
if (context->cipherInfo == NULL) {
crv = CKR_HOST_MEMORY;
break;
}
context->update = (SFTKCipher)(isEncrypt ? RC5_Encrypt : RC5_Decrypt);
context->destroy = (SFTKDestroy)RC5_DestroyContext;
break;
#endif
case CKM_RC4:
if (key_type != CKK_RC4) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
att = sftk_FindAttribute(key, CKA_VALUE);
if (att == NULL) {
crv = CKR_KEY_HANDLE_INVALID;
break;
}
context->cipherInfo =
RC4_CreateContext((unsigned char *)att->attrib.pValue,
att->attrib.ulValueLen);
sftk_FreeAttribute(att);
if (context->cipherInfo == NULL) {
crv = CKR_HOST_MEMORY; /* WRONG !!! */
break;
}
context->update = (SFTKCipher)(isEncrypt ? RC4_Encrypt : RC4_Decrypt);
context->destroy = (SFTKDestroy)RC4_DestroyContext;
break;
case CKM_CDMF_CBC_PAD:
context->doPad = PR_TRUE;
/* fall thru */
case CKM_CDMF_ECB:
case CKM_CDMF_CBC:
if (key_type != CKK_CDMF) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
t = (pMechanism->mechanism == CKM_CDMF_ECB) ? NSS_DES : NSS_DES_CBC;
goto finish_des;
case CKM_DES_ECB:
if (key_type != CKK_DES) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
t = NSS_DES;
goto finish_des;
case CKM_DES_CBC_PAD:
context->doPad = PR_TRUE;
/* fall thru */
case CKM_DES_CBC:
if (key_type != CKK_DES) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
t = NSS_DES_CBC;
goto finish_des;
case CKM_DES3_ECB:
if ((key_type != CKK_DES2) && (key_type != CKK_DES3)) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
t = NSS_DES_EDE3;
goto finish_des;
case CKM_DES3_CBC_PAD:
context->doPad = PR_TRUE;
/* fall thru */
case CKM_DES3_CBC:
if ((key_type != CKK_DES2) && (key_type != CKK_DES3)) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
t = NSS_DES_EDE3_CBC;
finish_des:
context->blockSize = 8;
att = sftk_FindAttribute(key, CKA_VALUE);
if (att == NULL) {
crv = CKR_KEY_HANDLE_INVALID;
break;
}
if (key_type == CKK_DES2 &&
(t == NSS_DES_EDE3_CBC || t == NSS_DES_EDE3)) {
/* extend DES2 key to DES3 key. */
memcpy(newdeskey, att->attrib.pValue, 16);
memcpy(newdeskey + 16, newdeskey, 8);
useNewKey = PR_TRUE;
} else if (key_type == CKK_CDMF) {
crv = sftk_cdmf2des((unsigned char *)att->attrib.pValue, newdeskey);
if (crv != CKR_OK) {
sftk_FreeAttribute(att);
break;
}
useNewKey = PR_TRUE;
}
context->cipherInfo = DES_CreateContext(
useNewKey ? newdeskey : (unsigned char *)att->attrib.pValue,
(unsigned char *)pMechanism->pParameter, t, isEncrypt);
if (useNewKey)
memset(newdeskey, 0, sizeof newdeskey);
sftk_FreeAttribute(att);
if (context->cipherInfo == NULL) {
crv = CKR_HOST_MEMORY;
break;
}
context->update = (SFTKCipher)(isEncrypt ? DES_Encrypt : DES_Decrypt);
context->destroy = (SFTKDestroy)DES_DestroyContext;
break;
case CKM_SEED_CBC_PAD:
context->doPad = PR_TRUE;
/* fall thru */
case CKM_SEED_CBC:
if (!pMechanism->pParameter ||
pMechanism->ulParameterLen != 16) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
/* fall thru */
case CKM_SEED_ECB:
context->blockSize = 16;
if (key_type != CKK_SEED) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
att = sftk_FindAttribute(key, CKA_VALUE);
if (att == NULL) {
crv = CKR_KEY_HANDLE_INVALID;
break;
}
context->cipherInfo = SEED_CreateContext(
(unsigned char *)att->attrib.pValue,
(unsigned char *)pMechanism->pParameter,
pMechanism->mechanism == CKM_SEED_ECB ? NSS_SEED : NSS_SEED_CBC,
isEncrypt);
sftk_FreeAttribute(att);
if (context->cipherInfo == NULL) {
crv = CKR_HOST_MEMORY;
break;
}
context->update = (SFTKCipher)(isEncrypt ? SEED_Encrypt : SEED_Decrypt);
context->destroy = (SFTKDestroy)SEED_DestroyContext;
break;
case CKM_CAMELLIA_CBC_PAD:
context->doPad = PR_TRUE;
/* fall thru */
case CKM_CAMELLIA_CBC:
if (!pMechanism->pParameter ||
pMechanism->ulParameterLen != 16) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
/* fall thru */
case CKM_CAMELLIA_ECB:
context->blockSize = 16;
if (key_type != CKK_CAMELLIA) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
att = sftk_FindAttribute(key, CKA_VALUE);
if (att == NULL) {
crv = CKR_KEY_HANDLE_INVALID;
break;
}
context->cipherInfo = Camellia_CreateContext(
(unsigned char *)att->attrib.pValue,
(unsigned char *)pMechanism->pParameter,
pMechanism->mechanism ==
CKM_CAMELLIA_ECB
? NSS_CAMELLIA
: NSS_CAMELLIA_CBC,
isEncrypt, att->attrib.ulValueLen);
sftk_FreeAttribute(att);
if (context->cipherInfo == NULL) {
crv = CKR_HOST_MEMORY;
break;
}
context->update = (SFTKCipher)(isEncrypt ? Camellia_Encrypt : Camellia_Decrypt);
context->destroy = (SFTKDestroy)Camellia_DestroyContext;
break;
case CKM_AES_CBC_PAD:
context->doPad = PR_TRUE;
/* fall thru */
case CKM_AES_ECB:
case CKM_AES_CBC:
context->blockSize = 16;
case CKM_AES_CTS:
case CKM_AES_CTR:
case CKM_AES_GCM:
if (pMechanism->mechanism == CKM_AES_GCM) {
context->multi = PR_FALSE;
}
if (key_type != CKK_AES) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
att = sftk_FindAttribute(key, CKA_VALUE);
if (att == NULL) {
crv = CKR_KEY_HANDLE_INVALID;
break;
}
context->cipherInfo = AES_CreateContext(
(unsigned char *)att->attrib.pValue,
(unsigned char *)pMechanism->pParameter,
sftk_aes_mode(pMechanism->mechanism),
isEncrypt, att->attrib.ulValueLen, 16);
sftk_FreeAttribute(att);
if (context->cipherInfo == NULL) {
crv = CKR_HOST_MEMORY;
break;
}
context->update = (SFTKCipher)(isEncrypt ? AES_Encrypt : AES_Decrypt);
context->destroy = (SFTKDestroy)AES_DestroyContext;
break;
case CKM_NSS_CHACHA20_POLY1305:
if (pMechanism->ulParameterLen != sizeof(CK_NSS_AEAD_PARAMS)) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
context->multi = PR_FALSE;
if (key_type != CKK_NSS_CHACHA20) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
att = sftk_FindAttribute(key, CKA_VALUE);
if (att == NULL) {
crv = CKR_KEY_HANDLE_INVALID;
break;
}
context->cipherInfo = sftk_ChaCha20Poly1305_CreateContext(
(unsigned char *)att->attrib.pValue, att->attrib.ulValueLen,
(CK_NSS_AEAD_PARAMS *)pMechanism->pParameter);
sftk_FreeAttribute(att);
if (context->cipherInfo == NULL) {
crv = sftk_MapCryptError(PORT_GetError());
break;
}
context->update = (SFTKCipher)(isEncrypt ? sftk_ChaCha20Poly1305_Encrypt : sftk_ChaCha20Poly1305_Decrypt);
context->destroy = (SFTKDestroy)sftk_ChaCha20Poly1305_DestroyContext;
break;
case CKM_NSS_AES_KEY_WRAP_PAD:
context->doPad = PR_TRUE;
/* fall thru */
case CKM_NSS_AES_KEY_WRAP:
context->multi = PR_FALSE;
context->blockSize = 8;
if (key_type != CKK_AES) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
att = sftk_FindAttribute(key, CKA_VALUE);
if (att == NULL) {
crv = CKR_KEY_HANDLE_INVALID;
break;
}
context->cipherInfo = AESKeyWrap_CreateContext(
(unsigned char *)att->attrib.pValue,
(unsigned char *)pMechanism->pParameter,
isEncrypt, att->attrib.ulValueLen);
sftk_FreeAttribute(att);
if (context->cipherInfo == NULL) {
crv = CKR_HOST_MEMORY;
break;
}
context->update = (SFTKCipher)(isEncrypt ? AESKeyWrap_Encrypt
: AESKeyWrap_Decrypt);
context->destroy = (SFTKDestroy)AESKeyWrap_DestroyContext;
break;
default:
crv = CKR_MECHANISM_INVALID;
break;
}
if (crv != CKR_OK) {
sftk_FreeContext(context);
sftk_FreeSession(session);
return crv;
}
sftk_SetContextByType(session, contextType, context);
sftk_FreeSession(session);
return CKR_OK;
}
/* NSC_EncryptInit initializes an encryption operation. */
CK_RV
NSC_EncryptInit(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
{
CHECK_FORK();
return sftk_CryptInit(hSession, pMechanism, hKey, CKA_ENCRYPT, CKA_ENCRYPT,
SFTK_ENCRYPT, PR_TRUE);
}
/* NSC_EncryptUpdate continues a multiple-part encryption operation. */
CK_RV
NSC_EncryptUpdate(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart,
CK_ULONG_PTR pulEncryptedPartLen)
{
SFTKSessionContext *context;
unsigned int outlen, i;
unsigned int padoutlen = 0;
unsigned int maxout = *pulEncryptedPartLen;
CK_RV crv;
SECStatus rv;
CHECK_FORK();
/* make sure we're legal */
crv = sftk_GetContext(hSession, &context, SFTK_ENCRYPT, PR_TRUE, NULL);
if (crv != CKR_OK)
return crv;
if (!pEncryptedPart) {
if (context->doPad) {
CK_ULONG totalDataAvailable = ulPartLen + context->padDataLength;
CK_ULONG blocksToSend = totalDataAvailable / context->blockSize;
*pulEncryptedPartLen = blocksToSend * context->blockSize;
return CKR_OK;
}
*pulEncryptedPartLen = ulPartLen;
return CKR_OK;
}
/* do padding */
if (context->doPad) {
/* deal with previous buffered data */
if (context->padDataLength != 0) {
/* fill in the padded to a full block size */
for (i = context->padDataLength;
(ulPartLen != 0) && i < context->blockSize; i++) {
context->padBuf[i] = *pPart++;
ulPartLen--;
context->padDataLength++;
}
/* not enough data to encrypt yet? then return */
if (context->padDataLength != context->blockSize) {
*pulEncryptedPartLen = 0;
return CKR_OK;
}
/* encrypt the current padded data */
rv = (*context->update)(context->cipherInfo, pEncryptedPart,
&padoutlen, context->blockSize, context->padBuf,
context->blockSize);
if (rv != SECSuccess) {
return sftk_MapCryptError(PORT_GetError());
}
pEncryptedPart += padoutlen;
maxout -= padoutlen;
}
/* save the residual */
context->padDataLength = ulPartLen % context->blockSize;
if (context->padDataLength) {
PORT_Memcpy(context->padBuf,
&pPart[ulPartLen - context->padDataLength],
context->padDataLength);
ulPartLen -= context->padDataLength;
}
/* if we've exhausted our new buffer, we're done */
if (ulPartLen == 0) {
*pulEncryptedPartLen = padoutlen;
return CKR_OK;
}
}
/* do it: NOTE: this assumes buf size in is >= buf size out! */
rv = (*context->update)(context->cipherInfo, pEncryptedPart,
&outlen, maxout, pPart, ulPartLen);
*pulEncryptedPartLen = (CK_ULONG)(outlen + padoutlen);
return (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError());
}
/* NSC_EncryptFinal finishes a multiple-part encryption operation. */
CK_RV
NSC_EncryptFinal(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pulLastEncryptedPartLen)
{
SFTKSession *session;
SFTKSessionContext *context;
unsigned int outlen, i;
unsigned int maxout = *pulLastEncryptedPartLen;
CK_RV crv;
SECStatus rv = SECSuccess;
PRBool contextFinished = PR_TRUE;
CHECK_FORK();
/* make sure we're legal */
crv = sftk_GetContext(hSession, &context, SFTK_ENCRYPT, PR_TRUE, &session);
if (crv != CKR_OK)
return crv;
*pulLastEncryptedPartLen = 0;
if (!pLastEncryptedPart) {
/* caller is checking the amount of remaining data */
if (context->blockSize > 0 && context->doPad) {
*pulLastEncryptedPartLen = context->blockSize;
contextFinished = PR_FALSE; /* still have padding to go */
}
goto finish;
}
/* do padding */
if (context->doPad) {
unsigned char padbyte = (unsigned char)(context->blockSize - context->padDataLength);
/* fill out rest of pad buffer with pad magic*/
for (i = context->padDataLength; i < context->blockSize; i++) {
context->padBuf[i] = padbyte;
}
rv = (*context->update)(context->cipherInfo, pLastEncryptedPart,
&outlen, maxout, context->padBuf, context->blockSize);
if (rv == SECSuccess)
*pulLastEncryptedPartLen = (CK_ULONG)outlen;
}
finish:
if (contextFinished)
sftk_TerminateOp(session, SFTK_ENCRYPT, context);
sftk_FreeSession(session);
return (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError());
}
/* NSC_Encrypt encrypts single-part data. */
CK_RV
NSC_Encrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData,
CK_ULONG_PTR pulEncryptedDataLen)
{
SFTKSession *session;
SFTKSessionContext *context;
unsigned int outlen;
unsigned int maxoutlen = *pulEncryptedDataLen;
CK_RV crv;
CK_RV crv2;
SECStatus rv = SECSuccess;
SECItem pText;
pText.type = siBuffer;
pText.data = pData;
pText.len = ulDataLen;
CHECK_FORK();
/* make sure we're legal */
crv = sftk_GetContext(hSession, &context, SFTK_ENCRYPT, PR_FALSE, &session);
if (crv != CKR_OK)
return crv;
if (!pEncryptedData) {
*pulEncryptedDataLen = context->rsa ? context->maxLen : ulDataLen + 2 * context->blockSize;
goto finish;
}
if (context->doPad) {
if (context->multi) {
CK_ULONG finalLen;
/* padding is fairly complicated, have the update and final
* code deal with it */
sftk_FreeSession(session);
crv = NSC_EncryptUpdate(hSession, pData, ulDataLen, pEncryptedData,
pulEncryptedDataLen);
if (crv != CKR_OK)
*pulEncryptedDataLen = 0;
maxoutlen -= *pulEncryptedDataLen;
pEncryptedData += *pulEncryptedDataLen;
finalLen = maxoutlen;
crv2 = NSC_EncryptFinal(hSession, pEncryptedData, &finalLen);
if (crv2 == CKR_OK)
*pulEncryptedDataLen += finalLen;
return crv == CKR_OK ? crv2 : crv;
}
/* doPad without multi means that padding must be done on the first
** and only update. There will be no final.
*/
PORT_Assert(context->blockSize > 1);
if (context->blockSize > 1) {
CK_ULONG remainder = ulDataLen % context->blockSize;
CK_ULONG padding = context->blockSize - remainder;
pText.len += padding;
pText.data = PORT_ZAlloc(pText.len);
if (pText.data) {
memcpy(pText.data, pData, ulDataLen);
memset(pText.data + ulDataLen, padding, padding);
} else {
crv = CKR_HOST_MEMORY;
goto fail;
}
}
}
/* do it: NOTE: this assumes buf size is big enough. */
rv = (*context->update)(context->cipherInfo, pEncryptedData,
&outlen, maxoutlen, pText.data, pText.len);
crv = (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError());
*pulEncryptedDataLen = (CK_ULONG)outlen;
if (pText.data != pData)
PORT_ZFree(pText.data, pText.len);
fail:
sftk_TerminateOp(session, SFTK_ENCRYPT, context);
finish:
sftk_FreeSession(session);
return crv;
}
/*
************** Crypto Functions: Decrypt ************************
*/
/* NSC_DecryptInit initializes a decryption operation. */
CK_RV
NSC_DecryptInit(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
{
CHECK_FORK();
return sftk_CryptInit(hSession, pMechanism, hKey, CKA_DECRYPT, CKA_DECRYPT,
SFTK_DECRYPT, PR_FALSE);
}
/* NSC_DecryptUpdate continues a multiple-part decryption operation. */
CK_RV
NSC_DecryptUpdate(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen,
CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen)
{
SFTKSessionContext *context;
unsigned int padoutlen = 0;
unsigned int outlen;
unsigned int maxout = *pulPartLen;
CK_RV crv;
SECStatus rv;
CHECK_FORK();
/* make sure we're legal */
crv = sftk_GetContext(hSession, &context, SFTK_DECRYPT, PR_TRUE, NULL);
if (crv != CKR_OK)
return crv;
/* this can only happen on an NSS programming error */
PORT_Assert((context->padDataLength == 0) || context->padDataLength == context->blockSize);
if (context->doPad) {
/* Check the data length for block ciphers. If we are padding,
* then we must be using a block cipher. In the non-padding case
* the error will be returned by the underlying decryption
* function when we do the actual decrypt. We need to do the
* check here to avoid returning a negative length to the caller
* or reading before the beginning of the pEncryptedPart buffer.
*/
if ((ulEncryptedPartLen == 0) ||
(ulEncryptedPartLen % context->blockSize) != 0) {
return CKR_ENCRYPTED_DATA_LEN_RANGE;
}
}
if (!pPart) {
if (context->doPad) {
*pulPartLen =
ulEncryptedPartLen + context->padDataLength - context->blockSize;
return CKR_OK;
}
/* for stream ciphers there is are no constraints on ulEncryptedPartLen.
* for block ciphers, it must be a multiple of blockSize. The error is
* detected when this function is called again do decrypt the output.
*/
*pulPartLen = ulEncryptedPartLen;
return CKR_OK;
}
if (context->doPad) {
/* first decrypt our saved buffer */
if (context->padDataLength != 0) {
rv = (*context->update)(context->cipherInfo, pPart, &padoutlen,
maxout, context->padBuf, context->blockSize);
if (rv != SECSuccess)
return sftk_MapDecryptError(PORT_GetError());
pPart += padoutlen;
maxout -= padoutlen;
}
/* now save the final block for the next decrypt or the final */
PORT_Memcpy(context->padBuf, &pEncryptedPart[ulEncryptedPartLen -
context->blockSize],
context->blockSize);
context->padDataLength = context->blockSize;
ulEncryptedPartLen -= context->padDataLength;
}
/* do it: NOTE: this assumes buf size in is >= buf size out! */
rv = (*context->update)(context->cipherInfo, pPart, &outlen,
maxout, pEncryptedPart, ulEncryptedPartLen);
*pulPartLen = (CK_ULONG)(outlen + padoutlen);
return (rv == SECSuccess) ? CKR_OK : sftk_MapDecryptError(PORT_GetError());
}
/* NSC_DecryptFinal finishes a multiple-part decryption operation. */
CK_RV
NSC_DecryptFinal(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen)
{
SFTKSession *session;
SFTKSessionContext *context;
unsigned int outlen;
unsigned int maxout = *pulLastPartLen;
CK_RV crv;
SECStatus rv = SECSuccess;
CHECK_FORK();
/* make sure we're legal */
crv = sftk_GetContext(hSession, &context, SFTK_DECRYPT, PR_TRUE, &session);
if (crv != CKR_OK)
return crv;
*pulLastPartLen = 0;
if (!pLastPart) {
/* caller is checking the amount of remaining data */
if (context->padDataLength > 0) {
*pulLastPartLen = context->padDataLength;
}
goto finish;
}
if (context->doPad) {
/* decrypt our saved buffer */
if (context->padDataLength != 0) {
/* this assumes that pLastPart is big enough to hold the *whole*
* buffer!!! */
rv = (*context->update)(context->cipherInfo, pLastPart, &outlen,
maxout, context->padBuf, context->blockSize);
if (rv != SECSuccess) {
crv = sftk_MapDecryptError(PORT_GetError());
} else {
unsigned int padSize =
(unsigned int)pLastPart[context->blockSize - 1];
if ((padSize > context->blockSize) || (padSize == 0)) {
crv = CKR_ENCRYPTED_DATA_INVALID;
} else {
unsigned int i;
unsigned int badPadding = 0; /* used as a boolean */
for (i = 0; i < padSize; i++) {
badPadding |=
(unsigned int)pLastPart[context->blockSize - 1 - i] ^
padSize;
}
if (badPadding) {
crv = CKR_ENCRYPTED_DATA_INVALID;
} else {
*pulLastPartLen = outlen - padSize;
}
}
}
}
}
sftk_TerminateOp(session, SFTK_DECRYPT, context);
finish:
sftk_FreeSession(session);
return crv;
}
/* NSC_Decrypt decrypts encrypted data in a single part. */
CK_RV
NSC_Decrypt(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData,
CK_ULONG_PTR pulDataLen)
{
SFTKSession *session;
SFTKSessionContext *context;
unsigned int outlen;
unsigned int maxoutlen = *pulDataLen;
CK_RV crv;
CK_RV crv2;
SECStatus rv = SECSuccess;
CHECK_FORK();
/* make sure we're legal */
crv = sftk_GetContext(hSession, &context, SFTK_DECRYPT, PR_FALSE, &session);
if (crv != CKR_OK)
return crv;
if (!pData) {
*pulDataLen = ulEncryptedDataLen + context->blockSize;
goto finish;
}
if (context->doPad && context->multi) {
CK_ULONG finalLen;
/* padding is fairly complicated, have the update and final
* code deal with it */
sftk_FreeSession(session);
crv = NSC_DecryptUpdate(hSession, pEncryptedData, ulEncryptedDataLen,
pData, pulDataLen);
if (crv != CKR_OK)
*pulDataLen = 0;
maxoutlen -= *pulDataLen;
pData += *pulDataLen;
finalLen = maxoutlen;
crv2 = NSC_DecryptFinal(hSession, pData, &finalLen);
if (crv2 == CKR_OK)
*pulDataLen += finalLen;
return crv == CKR_OK ? crv2 : crv;
}
rv = (*context->update)(context->cipherInfo, pData, &outlen, maxoutlen,
pEncryptedData, ulEncryptedDataLen);
/* XXX need to do MUCH better error mapping than this. */
crv = (rv == SECSuccess) ? CKR_OK : sftk_MapDecryptError(PORT_GetError());
if (rv == SECSuccess && context->doPad) {
unsigned int padding = pData[outlen - 1];
if (padding > context->blockSize || !padding) {
crv = CKR_ENCRYPTED_DATA_INVALID;
} else {
unsigned int i;
unsigned int badPadding = 0; /* used as a boolean */
for (i = 0; i < padding; i++) {
badPadding |= (unsigned int)pData[outlen - 1 - i] ^ padding;
}
if (badPadding) {
crv = CKR_ENCRYPTED_DATA_INVALID;
} else {
outlen -= padding;
}
}
}
*pulDataLen = (CK_ULONG)outlen;
sftk_TerminateOp(session, SFTK_DECRYPT, context);
finish:
sftk_FreeSession(session);
return crv;
}
/*
************** Crypto Functions: Digest (HASH) ************************
*/
/* NSC_DigestInit initializes a message-digesting operation. */
CK_RV
NSC_DigestInit(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism)
{
SFTKSession *session;
SFTKSessionContext *context;
CK_RV crv = CKR_OK;
CHECK_FORK();
session = sftk_SessionFromHandle(hSession);
if (session == NULL)
return CKR_SESSION_HANDLE_INVALID;
crv = sftk_InitGeneric(session, &context, SFTK_HASH, NULL, 0, NULL, 0, 0);
if (crv != CKR_OK) {
sftk_FreeSession(session);
return crv;
}
#define INIT_MECH(mech, mmm) \
case mech: { \
mmm##Context *mmm##_ctx = mmm##_NewContext(); \
context->cipherInfo = (void *)mmm##_ctx; \
context->cipherInfoLen = mmm##_FlattenSize(mmm##_ctx); \
context->currentMech = mech; \
context->hashUpdate = (SFTKHash)mmm##_Update; \
context->end = (SFTKEnd)mmm##_End; \
context->destroy = (SFTKDestroy)mmm##_DestroyContext; \
context->maxLen = mmm##_LENGTH; \
if (mmm##_ctx) \
mmm##_Begin(mmm##_ctx); \
else \
crv = CKR_HOST_MEMORY; \
break; \
}
switch (pMechanism->mechanism) {
INIT_MECH(CKM_MD2, MD2)
INIT_MECH(CKM_MD5, MD5)
INIT_MECH(CKM_SHA_1, SHA1)
INIT_MECH(CKM_SHA224, SHA224)
INIT_MECH(CKM_SHA256, SHA256)
INIT_MECH(CKM_SHA384, SHA384)
INIT_MECH(CKM_SHA512, SHA512)
default:
crv = CKR_MECHANISM_INVALID;
break;
}
if (crv != CKR_OK) {
sftk_FreeContext(context);
sftk_FreeSession(session);
return crv;
}
sftk_SetContextByType(session, SFTK_HASH, context);
sftk_FreeSession(session);
return CKR_OK;
}
/* NSC_Digest digests data in a single part. */
CK_RV
NSC_Digest(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest,
CK_ULONG_PTR pulDigestLen)
{
SFTKSession *session;
SFTKSessionContext *context;
unsigned int digestLen;
unsigned int maxout = *pulDigestLen;
CK_RV crv;
CHECK_FORK();
/* make sure we're legal */
crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_FALSE, &session);
if (crv != CKR_OK)
return crv;
if (pDigest == NULL) {
*pulDigestLen = context->maxLen;
goto finish;
}
/* do it: */
(*context->hashUpdate)(context->cipherInfo, pData, ulDataLen);
/* NOTE: this assumes buf size is bigenough for the algorithm */
(*context->end)(context->cipherInfo, pDigest, &digestLen, maxout);
*pulDigestLen = digestLen;
sftk_TerminateOp(session, SFTK_HASH, context);
finish:
sftk_FreeSession(session);
return CKR_OK;
}
/* NSC_DigestUpdate continues a multiple-part message-digesting operation. */
CK_RV
NSC_DigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
CK_ULONG ulPartLen)
{
SFTKSessionContext *context;
CK_RV crv;
CHECK_FORK();
/* make sure we're legal */
crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_TRUE, NULL);
if (crv != CKR_OK)
return crv;
/* do it: */
(*context->hashUpdate)(context->cipherInfo, pPart, ulPartLen);
return CKR_OK;
}
/* NSC_DigestFinal finishes a multiple-part message-digesting operation. */
CK_RV
NSC_DigestFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest,
CK_ULONG_PTR pulDigestLen)
{
SFTKSession *session;
SFTKSessionContext *context;
unsigned int maxout = *pulDigestLen;
unsigned int digestLen;
CK_RV crv;
CHECK_FORK();
/* make sure we're legal */
crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_TRUE, &session);
if (crv != CKR_OK)
return crv;
if (pDigest != NULL) {
(*context->end)(context->cipherInfo, pDigest, &digestLen, maxout);
*pulDigestLen = digestLen;
sftk_TerminateOp(session, SFTK_HASH, context);
} else {
*pulDigestLen = context->maxLen;
}
sftk_FreeSession(session);
return CKR_OK;
}
/*
* these helper functions are used by Generic Macing and Signing functions
* that use hashes as part of their operations.
*/
#define DOSUB(mmm) \
static CK_RV \
sftk_doSub##mmm(SFTKSessionContext *context) \
{ \
mmm##Context *mmm##_ctx = mmm##_NewContext(); \
context->hashInfo = (void *)mmm##_ctx; \
context->hashUpdate = (SFTKHash)mmm##_Update; \
context->end = (SFTKEnd)mmm##_End; \
context->hashdestroy = (SFTKDestroy)mmm##_DestroyContext; \
if (!context->hashInfo) { \
return CKR_HOST_MEMORY; \
} \
mmm##_Begin(mmm##_ctx); \
return CKR_OK; \
}
DOSUB(MD2)
DOSUB(MD5)
DOSUB(SHA1)
DOSUB(SHA224)
DOSUB(SHA256)
DOSUB(SHA384)
DOSUB(SHA512)
static SECStatus
sftk_SignCopy(
CK_ULONG *copyLen,
void *out, unsigned int *outLength,
unsigned int maxLength,
const unsigned char *hashResult,
unsigned int hashResultLength)
{
unsigned int toCopy = *copyLen;
if (toCopy > maxLength) {
toCopy = maxLength;
}
if (toCopy > hashResultLength) {
toCopy = hashResultLength;
}
memcpy(out, hashResult, toCopy);
if (outLength) {
*outLength = toCopy;
}
return SECSuccess;
}
/* Verify is just a compare for HMAC */
static SECStatus
sftk_HMACCmp(CK_ULONG *copyLen, unsigned char *sig, unsigned int sigLen,
unsigned char *hash, unsigned int hashLen)
{
return (PORT_Memcmp(sig, hash, *copyLen) == 0) ? SECSuccess : SECFailure;
}
/*
* common HMAC initalization routine
*/
static CK_RV
sftk_doHMACInit(SFTKSessionContext *context, HASH_HashType hash,
SFTKObject *key, CK_ULONG mac_size)
{
SFTKAttribute *keyval;
HMACContext *HMACcontext;
CK_ULONG *intpointer;
const SECHashObject *hashObj = HASH_GetRawHashObject(hash);
PRBool isFIPS = (key->slot->slotID == FIPS_SLOT_ID);
/* required by FIPS 198 Section 4 */
if (isFIPS && (mac_size < 4 || mac_size < hashObj->length / 2)) {
return CKR_BUFFER_TOO_SMALL;
}
keyval = sftk_FindAttribute(key, CKA_VALUE);
if (keyval == NULL)
return CKR_KEY_SIZE_RANGE;
HMACcontext = HMAC_Create(hashObj,
(const unsigned char *)keyval->attrib.pValue,
keyval->attrib.ulValueLen, isFIPS);
context->hashInfo = HMACcontext;
context->multi = PR_TRUE;
sftk_FreeAttribute(keyval);
if (context->hashInfo == NULL) {
if (PORT_GetError() == SEC_ERROR_INVALID_ARGS) {
return CKR_KEY_SIZE_RANGE;
}
return CKR_HOST_MEMORY;
}
context->hashUpdate = (SFTKHash)HMAC_Update;
context->end = (SFTKEnd)HMAC_Finish;
context->hashdestroy = (SFTKDestroy)HMAC_Destroy;
intpointer = PORT_New(CK_ULONG);
if (intpointer == NULL) {
return CKR_HOST_MEMORY;
}
*intpointer = mac_size;
context->cipherInfo = intpointer;
context->destroy = (SFTKDestroy)sftk_Space;
context->update = (SFTKCipher)sftk_SignCopy;
context->verify = (SFTKVerify)sftk_HMACCmp;
context->maxLen = hashObj->length;
HMAC_Begin(HMACcontext);
return CKR_OK;
}
/*
* SSL Macing support. SSL Macs are inited, then update with the base
* hashing algorithm, then finalized in sign and verify
*/
/*
* FROM SSL:
* 60 bytes is 3 times the maximum length MAC size that is supported.
* We probably should have one copy of this table. We still need this table
* in ssl to 'sign' the handshake hashes.
*/
static unsigned char ssl_pad_1[60] = {
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
0x36, 0x36, 0x36, 0x36
};
static unsigned char ssl_pad_2[60] = {
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
0x5c, 0x5c, 0x5c, 0x5c
};
static SECStatus
sftk_SSLMACSign(SFTKSSLMACInfo *info, unsigned char *sig, unsigned int *sigLen,
unsigned int maxLen, unsigned char *hash, unsigned int hashLen)
{
unsigned char tmpBuf[SFTK_MAX_MAC_LENGTH];
unsigned int out;
info->begin(info->hashContext);
info->update(info->hashContext, info->key, info->keySize);
info->update(info->hashContext, ssl_pad_2, info->padSize);
info->update(info->hashContext, hash, hashLen);
info->end(info->hashContext, tmpBuf, &out, SFTK_MAX_MAC_LENGTH);
PORT_Memcpy(sig, tmpBuf, info->macSize);
*sigLen = info->macSize;
return SECSuccess;
}
static SECStatus
sftk_SSLMACVerify(SFTKSSLMACInfo *info, unsigned char *sig, unsigned int sigLen,
unsigned char *hash, unsigned int hashLen)
{
unsigned char tmpBuf[SFTK_MAX_MAC_LENGTH];
unsigned int out;
info->begin(info->hashContext);
info->update(info->hashContext, info->key, info->keySize);
info->update(info->hashContext, ssl_pad_2, info->padSize);
info->update(info->hashContext, hash, hashLen);
info->end(info->hashContext, tmpBuf, &out, SFTK_MAX_MAC_LENGTH);
return (PORT_Memcmp(sig, tmpBuf, info->macSize) == 0) ? SECSuccess : SECFailure;
}
/*
* common HMAC initalization routine
*/
static CK_RV
sftk_doSSLMACInit(SFTKSessionContext *context, SECOidTag oid,
SFTKObject *key, CK_ULONG mac_size)
{
SFTKAttribute *keyval;
SFTKBegin begin;
int padSize;
SFTKSSLMACInfo *sslmacinfo;
CK_RV crv = CKR_MECHANISM_INVALID;
if (oid == SEC_OID_SHA1) {
crv = sftk_doSubSHA1(context);
if (crv != CKR_OK)
return crv;
begin = (SFTKBegin)SHA1_Begin;
padSize = 40;
} else {
crv = sftk_doSubMD5(context);
if (crv != CKR_OK)
return crv;
begin = (SFTKBegin)MD5_Begin;
padSize = 48;
}
context->multi = PR_TRUE;
keyval = sftk_FindAttribute(key, CKA_VALUE);
if (keyval == NULL)
return CKR_KEY_SIZE_RANGE;
context->hashUpdate(context->hashInfo, keyval->attrib.pValue,
keyval->attrib.ulValueLen);
context->hashUpdate(context->hashInfo, ssl_pad_1, padSize);
sslmacinfo = (SFTKSSLMACInfo *)PORT_Alloc(sizeof(SFTKSSLMACInfo));
if (sslmacinfo == NULL) {
sftk_FreeAttribute(keyval);
return CKR_HOST_MEMORY;
}
sslmacinfo->macSize = mac_size;
sslmacinfo->hashContext = context->hashInfo;
PORT_Memcpy(sslmacinfo->key, keyval->attrib.pValue,
keyval->attrib.ulValueLen);
sslmacinfo->keySize = keyval->attrib.ulValueLen;
sslmacinfo->begin = begin;
sslmacinfo->end = context->end;
sslmacinfo->update = context->hashUpdate;
sslmacinfo->padSize = padSize;
sftk_FreeAttribute(keyval);
context->cipherInfo = (void *)sslmacinfo;
context->destroy = (SFTKDestroy)sftk_Space;
context->update = (SFTKCipher)sftk_SSLMACSign;
context->verify = (SFTKVerify)sftk_SSLMACVerify;
context->maxLen = mac_size;
return CKR_OK;
}
/*
************** Crypto Functions: Sign ************************
*/
/**
* Check if We're using CBCMacing and initialize the session context if we are.
* @param contextType SFTK_SIGN or SFTK_VERIFY
* @param keyUsage check whether key allows this usage
*/
static CK_RV
sftk_InitCBCMac(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
CK_OBJECT_HANDLE hKey, CK_ATTRIBUTE_TYPE keyUsage,
SFTKContextType contextType)
{
CK_MECHANISM cbc_mechanism;
CK_ULONG mac_bytes = SFTK_INVALID_MAC_SIZE;
CK_RC2_CBC_PARAMS rc2_params;
#if NSS_SOFTOKEN_DOES_RC5
CK_RC5_CBC_PARAMS rc5_params;
CK_RC5_MAC_GENERAL_PARAMS *rc5_mac;
#endif
unsigned char ivBlock[SFTK_MAX_BLOCK_SIZE];
SFTKSessionContext *context;
CK_RV crv;
unsigned int blockSize;
switch (pMechanism->mechanism) {
case CKM_RC2_MAC_GENERAL:
if (!pMechanism->pParameter) {
return CKR_MECHANISM_PARAM_INVALID;
}
mac_bytes =
((CK_RC2_MAC_GENERAL_PARAMS *)pMechanism->pParameter)->ulMacLength;
/* fall through */
case CKM_RC2_MAC:
/* this works because ulEffectiveBits is in the same place in both the
* CK_RC2_MAC_GENERAL_PARAMS and CK_RC2_CBC_PARAMS */
rc2_params.ulEffectiveBits = ((CK_RC2_MAC_GENERAL_PARAMS *)
pMechanism->pParameter)
->ulEffectiveBits;
PORT_Memset(rc2_params.iv, 0, sizeof(rc2_params.iv));
cbc_mechanism.mechanism = CKM_RC2_CBC;
cbc_mechanism.pParameter = &rc2_params;
cbc_mechanism.ulParameterLen = sizeof(rc2_params);
blockSize = 8;
break;
#if NSS_SOFTOKEN_DOES_RC5
case CKM_RC5_MAC_GENERAL:
mac_bytes =
((CK_RC5_MAC_GENERAL_PARAMS *)pMechanism->pParameter)->ulMacLength;
/* fall through */
case CKM_RC5_MAC:
/* this works because ulEffectiveBits is in the same place in both the
* CK_RC5_MAC_GENERAL_PARAMS and CK_RC5_CBC_PARAMS */
rc5_mac = (CK_RC5_MAC_GENERAL_PARAMS *)pMechanism->pParameter;
rc5_params.ulWordsize = rc5_mac->ulWordsize;
rc5_params.ulRounds = rc5_mac->ulRounds;
rc5_params.pIv = ivBlock;
if ((blockSize = rc5_mac->ulWordsize * 2) > SFTK_MAX_BLOCK_SIZE)
return CKR_MECHANISM_PARAM_INVALID;
rc5_params.ulIvLen = blockSize;
PORT_Memset(ivBlock, 0, blockSize);
cbc_mechanism.mechanism = CKM_RC5_CBC;
cbc_mechanism.pParameter = &rc5_params;
cbc_mechanism.ulParameterLen = sizeof(rc5_params);
break;
#endif
/* add cast and idea later */
case CKM_DES_MAC_GENERAL:
mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
/* fall through */
case CKM_DES_MAC:
blockSize = 8;
PORT_Memset(ivBlock, 0, blockSize);
cbc_mechanism.mechanism = CKM_DES_CBC;
cbc_mechanism.pParameter = &ivBlock;
cbc_mechanism.ulParameterLen = blockSize;
break;
case CKM_DES3_MAC_GENERAL:
mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
/* fall through */
case CKM_DES3_MAC:
blockSize = 8;
PORT_Memset(ivBlock, 0, blockSize);
cbc_mechanism.mechanism = CKM_DES3_CBC;
cbc_mechanism.pParameter = &ivBlock;
cbc_mechanism.ulParameterLen = blockSize;
break;
case CKM_CDMF_MAC_GENERAL:
mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
/* fall through */
case CKM_CDMF_MAC:
blockSize = 8;
PORT_Memset(ivBlock, 0, blockSize);
cbc_mechanism.mechanism = CKM_CDMF_CBC;
cbc_mechanism.pParameter = &ivBlock;
cbc_mechanism.ulParameterLen = blockSize;
break;
case CKM_SEED_MAC_GENERAL:
mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
/* fall through */
case CKM_SEED_MAC:
blockSize = 16;
PORT_Memset(ivBlock, 0, blockSize);
cbc_mechanism.mechanism = CKM_SEED_CBC;
cbc_mechanism.pParameter = &ivBlock;
cbc_mechanism.ulParameterLen = blockSize;
break;
case CKM_CAMELLIA_MAC_GENERAL:
mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
/* fall through */
case CKM_CAMELLIA_MAC:
blockSize = 16;
PORT_Memset(ivBlock, 0, blockSize);
cbc_mechanism.mechanism = CKM_CAMELLIA_CBC;
cbc_mechanism.pParameter = &ivBlock;
cbc_mechanism.ulParameterLen = blockSize;
break;
case CKM_AES_MAC_GENERAL:
mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
/* fall through */
case CKM_AES_MAC:
blockSize = 16;
PORT_Memset(ivBlock, 0, blockSize);
cbc_mechanism.mechanism = CKM_AES_CBC;
cbc_mechanism.pParameter = &ivBlock;
cbc_mechanism.ulParameterLen = blockSize;
break;
default:
return CKR_FUNCTION_NOT_SUPPORTED;
}
/* if MAC size is externally supplied, it should be checked.
*/
if (mac_bytes == SFTK_INVALID_MAC_SIZE)
mac_bytes = blockSize >> 1;
else {
if (mac_bytes > blockSize)
return CKR_MECHANISM_PARAM_INVALID;
}
crv = sftk_CryptInit(hSession, &cbc_mechanism, hKey,
CKA_ENCRYPT, /* CBC mech is able to ENCRYPT, not SIGN/VERIFY */
keyUsage, contextType, PR_TRUE);
if (crv != CKR_OK)
return crv;
crv = sftk_GetContext(hSession, &context, contextType, PR_TRUE, NULL);
/* this shouldn't happen! */
PORT_Assert(crv == CKR_OK);
if (crv != CKR_OK)
return crv;
context->blockSize = blockSize;
context->macSize = mac_bytes;
return CKR_OK;
}
/*
* encode RSA PKCS #1 Signature data before signing...
*/
static SECStatus
sftk_RSAHashSign(SFTKHashSignInfo *info, unsigned char *sig,
unsigned int *sigLen, unsigned int maxLen,
const unsigned char *hash, unsigned int hashLen)
{
PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey);
if (info->key->keyType != NSSLOWKEYRSAKey) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
return RSA_HashSign(info->hashOid, info->key, sig, sigLen, maxLen,
hash, hashLen);
}
/* XXX Old template; want to expunge it eventually. */
static DERTemplate SECAlgorithmIDTemplate[] = {
{ DER_SEQUENCE,
0, NULL, sizeof(SECAlgorithmID) },
{ DER_OBJECT_ID,
offsetof(SECAlgorithmID, algorithm) },
{ DER_OPTIONAL | DER_ANY,
offsetof(SECAlgorithmID, parameters) },
{ 0 }
};
/*
* XXX OLD Template. Once all uses have been switched over to new one,
* remove this.
*/
static DERTemplate SGNDigestInfoTemplate[] = {
{ DER_SEQUENCE,
0, NULL, sizeof(SGNDigestInfo) },
{ DER_INLINE,
offsetof(SGNDigestInfo, digestAlgorithm),
SECAlgorithmIDTemplate },
{ DER_OCTET_STRING,
offsetof(SGNDigestInfo, digest) },
{ 0 }
};
/*
* encode RSA PKCS #1 Signature data before signing...
*/
SECStatus
RSA_HashSign(SECOidTag hashOid, NSSLOWKEYPrivateKey *key,
unsigned char *sig, unsigned int *sigLen, unsigned int maxLen,
const unsigned char *hash, unsigned int hashLen)
{
SECStatus rv = SECFailure;
SECItem digder;
PLArenaPool *arena = NULL;
SGNDigestInfo *di = NULL;
digder.data = NULL;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) {
goto loser;
}
/* Construct digest info */
di = SGN_CreateDigestInfo(hashOid, hash, hashLen);
if (!di) {
goto loser;
}
/* Der encode the digest as a DigestInfo */
rv = DER_Encode(arena, &digder, SGNDigestInfoTemplate, di);
if (rv != SECSuccess) {
goto loser;
}
/*
** Encrypt signature after constructing appropriate PKCS#1 signature
** block
*/
rv = RSA_Sign(&key->u.rsa, sig, sigLen, maxLen, digder.data,
digder.len);
if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
sftk_fatalError = PR_TRUE;
}
loser:
SGN_DestroyDigestInfo(di);
if (arena != NULL) {
PORT_FreeArena(arena, PR_FALSE);
}
return rv;
}
static SECStatus
sftk_RSASign(NSSLOWKEYPrivateKey *key, unsigned char *output,
unsigned int *outputLen, unsigned int maxOutputLen,
const unsigned char *input, unsigned int inputLen)
{
SECStatus rv = SECFailure;
PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
if (key->keyType != NSSLOWKEYRSAKey) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
rv = RSA_Sign(&key->u.rsa, output, outputLen, maxOutputLen, input,
inputLen);
if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
sftk_fatalError = PR_TRUE;
}
return rv;
}
static SECStatus
sftk_RSASignRaw(NSSLOWKEYPrivateKey *key, unsigned char *output,
unsigned int *outputLen, unsigned int maxOutputLen,
const unsigned char *input, unsigned int inputLen)
{
SECStatus rv = SECFailure;
PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
if (key->keyType != NSSLOWKEYRSAKey) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
rv = RSA_SignRaw(&key->u.rsa, output, outputLen, maxOutputLen, input,
inputLen);
if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
sftk_fatalError = PR_TRUE;
}
return rv;
}
static SECStatus
sftk_RSASignPSS(SFTKHashSignInfo *info, unsigned char *sig,
unsigned int *sigLen, unsigned int maxLen,
const unsigned char *hash, unsigned int hashLen)
{
SECStatus rv = SECFailure;
HASH_HashType hashAlg;
HASH_HashType maskHashAlg;
CK_RSA_PKCS_PSS_PARAMS *params = (CK_RSA_PKCS_PSS_PARAMS *)info->params;
PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey);
if (info->key->keyType != NSSLOWKEYRSAKey) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
hashAlg = GetHashTypeFromMechanism(params->hashAlg);
maskHashAlg = GetHashTypeFromMechanism(params->mgf);
rv = RSA_SignPSS(&info->key->u.rsa, hashAlg, maskHashAlg, NULL,
params->sLen, sig, sigLen, maxLen, hash, hashLen);
if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
sftk_fatalError = PR_TRUE;
}
return rv;
}
static SECStatus
nsc_DSA_Verify_Stub(void *ctx, void *sigBuf, unsigned int sigLen,
void *dataBuf, unsigned int dataLen)
{
SECItem signature, digest;
NSSLOWKEYPublicKey *key = (NSSLOWKEYPublicKey *)ctx;
signature.data = (unsigned char *)sigBuf;
signature.len = sigLen;
digest.data = (unsigned char *)dataBuf;
digest.len = dataLen;
return DSA_VerifyDigest(&(key->u.dsa), &signature, &digest);
}
static SECStatus
nsc_DSA_Sign_Stub(void *ctx, void *sigBuf,
unsigned int *sigLen, unsigned int maxSigLen,
void *dataBuf, unsigned int dataLen)
{
SECItem signature, digest;
SECStatus rv;
NSSLOWKEYPrivateKey *key = (NSSLOWKEYPrivateKey *)ctx;
signature.data = (unsigned char *)sigBuf;
signature.len = maxSigLen;
digest.data = (unsigned char *)dataBuf;
digest.len = dataLen;
rv = DSA_SignDigest(&(key->u.dsa), &signature, &digest);
if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
sftk_fatalError = PR_TRUE;
}
*sigLen = signature.len;
return rv;
}
static SECStatus
nsc_ECDSAVerifyStub(void *ctx, void *sigBuf, unsigned int sigLen,
void *dataBuf, unsigned int dataLen)
{
SECItem signature, digest;
NSSLOWKEYPublicKey *key = (NSSLOWKEYPublicKey *)ctx;
signature.data = (unsigned char *)sigBuf;
signature.len = sigLen;
digest.data = (unsigned char *)dataBuf;
digest.len = dataLen;
return ECDSA_VerifyDigest(&(key->u.ec), &signature, &digest);
}
static SECStatus
nsc_ECDSASignStub(void *ctx, void *sigBuf,
unsigned int *sigLen, unsigned int maxSigLen,
void *dataBuf, unsigned int dataLen)
{
SECItem signature, digest;
SECStatus rv;
NSSLOWKEYPrivateKey *key = (NSSLOWKEYPrivateKey *)ctx;
signature.data = (unsigned char *)sigBuf;
signature.len = maxSigLen;
digest.data = (unsigned char *)dataBuf;
digest.len = dataLen;
rv = ECDSA_SignDigest(&(key->u.ec), &signature, &digest);
if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
sftk_fatalError = PR_TRUE;
}
*sigLen = signature.len;
return rv;
}
/* NSC_SignInit setups up the signing operations. There are three basic
* types of signing:
* (1) the tradition single part, where "Raw RSA" or "Raw DSA" is applied
* to data in a single Sign operation (which often looks a lot like an
* encrypt, with data coming in and data going out).
* (2) Hash based signing, where we continually hash the data, then apply
* some sort of signature to the end.
* (3) Block Encryption CBC MAC's, where the Data is encrypted with a key,
* and only the final block is part of the mac.
*
* For case number 3, we initialize a context much like the Encryption Context
* (in fact we share code). We detect case 3 in C_SignUpdate, C_Sign, and
* C_Final by the following method... if it's not multi-part, and it's doesn't
* have a hash context, it must be a block Encryption CBC MAC.
*
* For case number 2, we initialize a hash structure, as well as make it
* multi-part. Updates are simple calls to the hash update function. Final
* calls the hashend, then passes the result to the 'update' function (which
* operates as a final signature function). In some hash based MAC'ing (as
* opposed to hash base signatures), the update function is can be simply a
* copy (as is the case with HMAC).
*/
CK_RV
NSC_SignInit(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
{
SFTKSession *session;
SFTKObject *key;
SFTKSessionContext *context;
CK_KEY_TYPE key_type;
CK_RV crv = CKR_OK;
NSSLOWKEYPrivateKey *privKey;
SFTKHashSignInfo *info = NULL;
CHECK_FORK();
/* Block Cipher MACing Algorithms use a different Context init method..*/
crv = sftk_InitCBCMac(hSession, pMechanism, hKey, CKA_SIGN, SFTK_SIGN);
if (crv != CKR_FUNCTION_NOT_SUPPORTED)
return crv;
/* we're not using a block cipher mac */
session = sftk_SessionFromHandle(hSession);
if (session == NULL)
return CKR_SESSION_HANDLE_INVALID;
crv = sftk_InitGeneric(session, &context, SFTK_SIGN, &key, hKey, &key_type,
CKO_PRIVATE_KEY, CKA_SIGN);
if (crv != CKR_OK) {
sftk_FreeSession(session);
return crv;
}
context->multi = PR_FALSE;
#define INIT_RSA_SIGN_MECH(mmm) \
case CKM_##mmm##_RSA_PKCS: \
context->multi = PR_TRUE; \
crv = sftk_doSub##mmm(context); \
if (crv != CKR_OK) \
break; \
context->update = (SFTKCipher)sftk_RSAHashSign; \
info = PORT_New(SFTKHashSignInfo); \
if (info == NULL) { \
crv = CKR_HOST_MEMORY; \
break; \
} \
info->hashOid = SEC_OID_##mmm; \
goto finish_rsa;
switch (pMechanism->mechanism) {
INIT_RSA_SIGN_MECH(MD5)
INIT_RSA_SIGN_MECH(MD2)
INIT_RSA_SIGN_MECH(SHA1)
INIT_RSA_SIGN_MECH(SHA224)
INIT_RSA_SIGN_MECH(SHA256)
INIT_RSA_SIGN_MECH(SHA384)
INIT_RSA_SIGN_MECH(SHA512)
case CKM_RSA_PKCS:
context->update = (SFTKCipher)sftk_RSASign;
goto finish_rsa;
case CKM_RSA_X_509:
context->update = (SFTKCipher)sftk_RSASignRaw;
finish_rsa:
if (key_type != CKK_RSA) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
context->rsa = PR_TRUE;
privKey = sftk_GetPrivKey(key, CKK_RSA, &crv);
if (privKey == NULL) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
/* OK, info is allocated only if we're doing hash and sign mechanism.
* It's necessary to be able to set the correct OID in the final
* signature.
*/
if (info) {
info->key = privKey;
context->cipherInfo = info;
context->destroy = (SFTKDestroy)sftk_Space;
} else {
context->cipherInfo = privKey;
context->destroy = (SFTKDestroy)sftk_Null;
}
context->maxLen = nsslowkey_PrivateModulusLen(privKey);
break;
case CKM_RSA_PKCS_PSS:
if (key_type != CKK_RSA) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
context->rsa = PR_TRUE;
if (pMechanism->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS) ||
!sftk_ValidatePssParams((const CK_RSA_PKCS_PSS_PARAMS *)pMechanism->pParameter)) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
info = PORT_New(SFTKHashSignInfo);
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);
break;
}
context->cipherInfo = info;
context->destroy = (SFTKDestroy)sftk_Space;
context->update = (SFTKCipher)sftk_RSASignPSS;
context->maxLen = nsslowkey_PrivateModulusLen(info->key);
break;
case CKM_DSA_SHA1:
context->multi = PR_TRUE;
crv = sftk_doSubSHA1(context);
if (crv != CKR_OK)
break;
/* fall through */
case CKM_DSA:
if (key_type != CKK_DSA) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
privKey = sftk_GetPrivKey(key, CKK_DSA, &crv);
if (privKey == NULL) {
break;
}
context->cipherInfo = privKey;
context->update = (SFTKCipher)nsc_DSA_Sign_Stub;
context->destroy = (privKey == key->objectInfo) ? (SFTKDestroy)sftk_Null : (SFTKDestroy)sftk_FreePrivKey;
context->maxLen = DSA_MAX_SIGNATURE_LEN;
break;
case CKM_ECDSA_SHA1:
context->multi = PR_TRUE;
crv = sftk_doSubSHA1(context);
if (crv != CKR_OK)
break;
/* fall through */
case CKM_ECDSA:
if (key_type != CKK_EC) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
privKey = sftk_GetPrivKey(key, CKK_EC, &crv);
if (privKey == NULL) {
crv = CKR_HOST_MEMORY;
break;
}
context->cipherInfo = privKey;
context->update = (SFTKCipher)nsc_ECDSASignStub;
context->destroy = (privKey == key->objectInfo) ? (SFTKDestroy)sftk_Null : (SFTKDestroy)sftk_FreePrivKey;
context->maxLen = MAX_ECKEY_LEN * 2;
break;
#define INIT_HMAC_MECH(mmm) \
case CKM_##mmm##_HMAC_GENERAL: \
PORT_Assert(pMechanism->pParameter); \
if (!pMechanism->pParameter) { \
crv = CKR_MECHANISM_PARAM_INVALID; \
break; \
} \
crv = sftk_doHMACInit(context, HASH_Alg##mmm, key, \
*(CK_ULONG *)pMechanism->pParameter); \
break; \
case CKM_##mmm##_HMAC: \
crv = sftk_doHMACInit(context, HASH_Alg##mmm, key, mmm##_LENGTH); \
break;
INIT_HMAC_MECH(MD2)
INIT_HMAC_MECH(MD5)
INIT_HMAC_MECH(SHA224)
INIT_HMAC_MECH(SHA256)
INIT_HMAC_MECH(SHA384)
INIT_HMAC_MECH(SHA512)
case CKM_SHA_1_HMAC_GENERAL:
PORT_Assert(pMechanism->pParameter);
if (!pMechanism->pParameter) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
crv = sftk_doHMACInit(context, HASH_AlgSHA1, key,
*(CK_ULONG *)pMechanism->pParameter);
break;
case CKM_SHA_1_HMAC:
crv = sftk_doHMACInit(context, HASH_AlgSHA1, key, SHA1_LENGTH);
break;
case CKM_SSL3_MD5_MAC:
PORT_Assert(pMechanism->pParameter);
if (!pMechanism->pParameter) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
crv = sftk_doSSLMACInit(context, SEC_OID_MD5, key,
*(CK_ULONG *)pMechanism->pParameter);
break;
case CKM_SSL3_SHA1_MAC:
PORT_Assert(pMechanism->pParameter);
if (!pMechanism->pParameter) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
crv = sftk_doSSLMACInit(context, SEC_OID_SHA1, key,
*(CK_ULONG *)pMechanism->pParameter);
break;
case CKM_TLS_PRF_GENERAL:
crv = sftk_TLSPRFInit(context, key, key_type, HASH_AlgNULL, 0);
break;
case CKM_TLS_MAC: {
CK_TLS_MAC_PARAMS *tls12_mac_params;
HASH_HashType tlsPrfHash;
const char *label;
if (pMechanism->ulParameterLen != sizeof(CK_TLS_MAC_PARAMS)) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
tls12_mac_params = (CK_TLS_MAC_PARAMS *)pMechanism->pParameter;
if (tls12_mac_params->prfMechanism == CKM_TLS_PRF) {
/* The TLS 1.0 and 1.1 PRF */
tlsPrfHash = HASH_AlgNULL;
if (tls12_mac_params->ulMacLength != 12) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
} else {
/* The hash function for the TLS 1.2 PRF */
tlsPrfHash =
GetHashTypeFromMechanism(tls12_mac_params->prfMechanism);
if (tlsPrfHash == HASH_AlgNULL ||
tls12_mac_params->ulMacLength < 12) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
}
if (tls12_mac_params->ulServerOrClient == 1) {
label = "server finished";
} else if (tls12_mac_params->ulServerOrClient == 2) {
label = "client finished";
} else {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
crv = sftk_TLSPRFInit(context, key, key_type, tlsPrfHash,
tls12_mac_params->ulMacLength);
if (crv == CKR_OK) {
context->hashUpdate(context->hashInfo, label, 15);
}
break;
}
case CKM_NSS_TLS_PRF_GENERAL_SHA256:
crv = sftk_TLSPRFInit(context, key, key_type, HASH_AlgSHA256, 0);
break;
case CKM_NSS_HMAC_CONSTANT_TIME: {
sftk_MACConstantTimeCtx *ctx =
sftk_HMACConstantTime_New(pMechanism, key);
CK_ULONG *intpointer;
if (ctx == NULL) {
crv = CKR_ARGUMENTS_BAD;
break;
}
intpointer = PORT_New(CK_ULONG);
if (intpointer == NULL) {
PORT_Free(ctx);
crv = CKR_HOST_MEMORY;
break;
}
*intpointer = ctx->hash->length;
context->cipherInfo = intpointer;
context->hashInfo = ctx;
context->currentMech = pMechanism->mechanism;
context->hashUpdate = sftk_HMACConstantTime_Update;
context->hashdestroy = sftk_MACConstantTime_DestroyContext;
context->end = sftk_MACConstantTime_EndHash;
context->update = (SFTKCipher)sftk_SignCopy;
context->destroy = sftk_Space;
context->maxLen = 64;
context->multi = PR_TRUE;
break;
}
case CKM_NSS_SSL3_MAC_CONSTANT_TIME: {
sftk_MACConstantTimeCtx *ctx =
sftk_SSLv3MACConstantTime_New(pMechanism, key);
CK_ULONG *intpointer;
if (ctx == NULL) {
crv = CKR_ARGUMENTS_BAD;
break;
}
intpointer = PORT_New(CK_ULONG);
if (intpointer == NULL) {
PORT_Free(ctx);
crv = CKR_HOST_MEMORY;
break;
}
*intpointer = ctx->hash->length;
context->cipherInfo = intpointer;
context->hashInfo = ctx;
context->currentMech = pMechanism->mechanism;
context->hashUpdate = sftk_SSLv3MACConstantTime_Update;
context->hashdestroy = sftk_MACConstantTime_DestroyContext;
context->end = sftk_MACConstantTime_EndHash;
context->update = (SFTKCipher)sftk_SignCopy;
context->destroy = sftk_Space;
context->maxLen = 64;
context->multi = PR_TRUE;
break;
}
default:
crv = CKR_MECHANISM_INVALID;
break;
}
if (crv != CKR_OK) {
if (info)
PORT_Free(info);
sftk_FreeContext(context);
sftk_FreeSession(session);
return crv;
}
sftk_SetContextByType(session, SFTK_SIGN, context);
sftk_FreeSession(session);
return CKR_OK;
}
/** MAC one block of data by block cipher
*/
static CK_RV
sftk_MACBlock(SFTKSessionContext *ctx, void *blk)
{
unsigned int outlen;
return (SECSuccess == (ctx->update)(ctx->cipherInfo, ctx->macBuf, &outlen,
SFTK_MAX_BLOCK_SIZE, blk, ctx->blockSize))
? CKR_OK
: sftk_MapCryptError(PORT_GetError());
}
/** MAC last (incomplete) block of data by block cipher
*
* Call once, then terminate MACing operation.
*/
static CK_RV
sftk_MACFinal(SFTKSessionContext *ctx)
{
unsigned int padLen = ctx->padDataLength;
/* pad and proceed the residual */
if (padLen) {
/* shd clr ctx->padLen to make sftk_MACFinal idempotent */
PORT_Memset(ctx->padBuf + padLen, 0, ctx->blockSize - padLen);
return sftk_MACBlock(ctx, ctx->padBuf);
} else
return CKR_OK;
}
/** The common implementation for {Sign,Verify}Update. (S/V only vary in their
* setup and final operations).
*
* A call which results in an error terminates the operation [PKCS#11,v2.11]
*/
static CK_RV
sftk_MACUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
CK_ULONG ulPartLen, SFTKContextType type)
{
SFTKSession *session;
SFTKSessionContext *context;
CK_RV crv;
/* make sure we're legal */
crv = sftk_GetContext(hSession, &context, type, PR_TRUE, &session);
if (crv != CKR_OK)
return crv;
if (context->hashInfo) {
(*context->hashUpdate)(context->hashInfo, pPart, ulPartLen);
} else {
/* must be block cipher MACing */
unsigned int blkSize = context->blockSize;
unsigned char *residual = /* free room in context->padBuf */
context->padBuf + context->padDataLength;
unsigned int minInput = /* min input for MACing at least one block */
blkSize - context->padDataLength;
/* not enough data even for one block */
if (ulPartLen < minInput) {
PORT_Memcpy(residual, pPart, ulPartLen);
context->padDataLength += ulPartLen;
goto cleanup;
}
/* MACing residual */
if (context->padDataLength) {
PORT_Memcpy(residual, pPart, minInput);
ulPartLen -= minInput;
pPart += minInput;
if (CKR_OK != (crv = sftk_MACBlock(context, context->padBuf)))
goto terminate;
}
/* MACing full blocks */
while (ulPartLen >= blkSize) {
if (CKR_OK != (crv = sftk_MACBlock(context, pPart)))
goto terminate;
ulPartLen -= blkSize;
pPart += blkSize;
}
/* save the residual */
if ((context->padDataLength = ulPartLen))
PORT_Memcpy(context->padBuf, pPart, ulPartLen);
} /* blk cipher MACing */
goto cleanup;
terminate:
sftk_TerminateOp(session, type, context);
cleanup:
sftk_FreeSession(session);
return crv;
}
/* NSC_SignUpdate continues a multiple-part signature operation,
* where the signature is (will be) an appendix to the data,
* and plaintext cannot be recovered from the signature
*
* A call which results in an error terminates the operation [PKCS#11,v2.11]
*/
CK_RV
NSC_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
CK_ULONG ulPartLen)
{
CHECK_FORK();
return sftk_MACUpdate(hSession, pPart, ulPartLen, SFTK_SIGN);
}
/* NSC_SignFinal finishes a multiple-part signature operation,
* returning the signature. */
CK_RV
NSC_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature,
CK_ULONG_PTR pulSignatureLen)
{
SFTKSession *session;
SFTKSessionContext *context;
unsigned int outlen;
unsigned int maxoutlen = *pulSignatureLen;
CK_RV crv;
CHECK_FORK();
/* make sure we're legal */
crv = sftk_GetContext(hSession, &context, SFTK_SIGN, PR_TRUE, &session);
if (crv != CKR_OK)
return crv;
if (context->hashInfo) {
unsigned int digestLen;
unsigned char tmpbuf[SFTK_MAX_MAC_LENGTH];
if (!pSignature) {
outlen = context->maxLen;
goto finish;
}
(*context->end)(context->hashInfo, tmpbuf, &digestLen, sizeof(tmpbuf));
if (SECSuccess != (context->update)(context->cipherInfo, pSignature,
&outlen, maxoutlen, tmpbuf, digestLen))
crv = sftk_MapCryptError(PORT_GetError());
/* CKR_BUFFER_TOO_SMALL here isn't continuable, let operation terminate.
* Keeping "too small" CK_RV intact is a standard violation, but allows
* application read EXACT signature length */
} else {
/* must be block cipher MACing */
outlen = context->macSize;
/* null or "too small" buf doesn't terminate operation [PKCS#11,v2.11]*/
if (!pSignature || maxoutlen < outlen) {
if (pSignature)
crv = CKR_BUFFER_TOO_SMALL;
goto finish;
}
if (CKR_OK == (crv = sftk_MACFinal(context)))
PORT_Memcpy(pSignature, context->macBuf, outlen);
}
sftk_TerminateOp(session, SFTK_SIGN, context);
finish:
*pulSignatureLen = outlen;
sftk_FreeSession(session);
return crv;
}
/* NSC_Sign signs (encrypts with private key) data in a single part,
* where the signature is (will be) an appendix to the data,
* and plaintext cannot be recovered from the signature */
CK_RV
NSC_Sign(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature,
CK_ULONG_PTR pulSignatureLen)
{
SFTKSession *session;
SFTKSessionContext *context;
CK_RV crv;
CHECK_FORK();
/* make sure we're legal */
crv = sftk_GetContext(hSession, &context, SFTK_SIGN, PR_FALSE, &session);
if (crv != CKR_OK)
return crv;
if (!pSignature) {
/* see also how C_SignUpdate implements this */
*pulSignatureLen = (!context->multi || context->hashInfo)
? context->maxLen
: context->macSize; /* must be block cipher MACing */
goto finish;
}
/* multi part Signing are completely implemented by SignUpdate and
* sign Final */
if (context->multi) {
/* SignFinal can't follow failed SignUpdate */
if (CKR_OK == (crv = NSC_SignUpdate(hSession, pData, ulDataLen)))
crv = NSC_SignFinal(hSession, pSignature, pulSignatureLen);
} else {
/* single-part PKC signature (e.g. CKM_ECDSA) */
unsigned int outlen;
unsigned int maxoutlen = *pulSignatureLen;
if (SECSuccess != (*context->update)(context->cipherInfo, pSignature,
&outlen, maxoutlen, pData, ulDataLen))
crv = sftk_MapCryptError(PORT_GetError());
*pulSignatureLen = (CK_ULONG)outlen;
/* "too small" here is certainly continuable */
if (crv != CKR_BUFFER_TOO_SMALL)
sftk_TerminateOp(session, SFTK_SIGN, context);
} /* single-part */
finish:
sftk_FreeSession(session);
return crv;
}
/*
************** Crypto Functions: Sign Recover ************************
*/
/* NSC_SignRecoverInit initializes a signature operation,
* where the (digest) data can be recovered from the signature.
* E.g. encryption with the user's private key */
CK_RV
NSC_SignRecoverInit(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
{
CHECK_FORK();
switch (pMechanism->mechanism) {
case CKM_RSA_PKCS:
case CKM_RSA_X_509:
return NSC_SignInit(hSession, pMechanism, hKey);
default:
break;
}
return CKR_MECHANISM_INVALID;
}
/* NSC_SignRecover signs data in a single operation
* where the (digest) data can be recovered from the signature.
* E.g. encryption with the user's private key */
CK_RV
NSC_SignRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen)
{
CHECK_FORK();
return NSC_Sign(hSession, pData, ulDataLen, pSignature, pulSignatureLen);
}
/*
************** Crypto Functions: verify ************************
*/
/* Handle RSA Signature formatting */
static SECStatus
sftk_hashCheckSign(SFTKHashVerifyInfo *info, const unsigned char *sig,
unsigned int sigLen, const unsigned char *digest,
unsigned int digestLen)
{
PORT_Assert(info->key->keyType == NSSLOWKEYRSAKey);
if (info->key->keyType != NSSLOWKEYRSAKey) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
return RSA_HashCheckSign(info->hashOid, info->key, sig, sigLen, digest,
digestLen);
}
SECStatus
RSA_HashCheckSign(SECOidTag digestOid, NSSLOWKEYPublicKey *key,
const unsigned char *sig, unsigned int sigLen,
const unsigned char *digestData, unsigned int digestLen)
{
unsigned char *pkcs1DigestInfoData;
SECItem pkcs1DigestInfo;
SECItem digest;
unsigned int bufferSize;
SECStatus rv;
/* pkcs1DigestInfo.data must be less than key->u.rsa.modulus.len */
bufferSize = key->u.rsa.modulus.len;
pkcs1DigestInfoData = PORT_ZAlloc(bufferSize);
if (!pkcs1DigestInfoData) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
return SECFailure;
}
pkcs1DigestInfo.data = pkcs1DigestInfoData;
pkcs1DigestInfo.len = bufferSize;
/* decrypt the block */
rv = RSA_CheckSignRecover(&key->u.rsa, pkcs1DigestInfo.data,
&pkcs1DigestInfo.len, pkcs1DigestInfo.len,
sig, sigLen);
if (rv != SECSuccess) {
PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
} else {
digest.data = (PRUint8 *)digestData;
digest.len = digestLen;
rv = _SGN_VerifyPKCS1DigestInfo(
digestOid, &digest, &pkcs1DigestInfo,
PR_TRUE /*XXX: unsafeAllowMissingParameters*/);
}
PORT_Free(pkcs1DigestInfoData);