Source code

Revision control

Copy as Markdown

Other Tools

/* 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 <limits.h> /* for UINT_MAX and ULONG_MAX */
#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 "cmac.h"
#include "alghmac.h"
#include "softoken.h"
#include "secasn1.h"
#include "secerr.h"
#include "kem.h"
#include "kyber.h"
#include "prprf.h"
#include "prenv.h"
#define __PASTE(x, y) x##y
#define BAD_PARAM_CAST(pMech, typeSize) (!pMech->pParameter || pMech->ulParameterLen < typeSize)
/*
* we renamed all our internal functions, get the correct
* definitions for them...
*/
#undef CK_PKCS11_FUNCTION_INFO
#undef CK_NEED_ARG_LIST
#define CK_PKCS11_3_0 1
#define CK_EXTERN extern
#define CK_PKCS11_FUNCTION_INFO(func) \
CK_RV __PASTE(NS, func)
#define CK_NEED_ARG_LIST 1
#include "pkcs11f.h"
/* create a definition of SHA1 that's consistent
* with the rest of the CKM_SHAxxx hashes*/
#define CKM_SHA1 CKM_SHA_1
#define CKM_SHA1_HMAC CKM_SHA_1_HMAC
#define CKM_SHA1_HMAC_GENERAL CKM_SHA_1_HMAC_GENERAL
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);
}
static void
sftk_ZSpace(void *data, PRBool freeit)
{
size_t len = *(size_t *)data;
PORT_ZFree(data, len);
}
/*
* 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;
CK_RV crv = CKR_OK;
/* 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) {
crv = CKR_HOST_MEMORY;
goto done;
}
rv = DES_Encrypt(descx, enc_dest, &leng, 8, enc_src, 8);
DES_DestroyContext(descx, PR_TRUE);
if (rv != SECSuccess) {
crv = sftk_MapCryptError(PORT_GetError());
goto done;
}
/* 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) {
crv = CKR_HOST_MEMORY;
goto done;
}
rv = DES_Encrypt(descx, deskey, &leng, 8, enc_src, 8);
DES_DestroyContext(descx, PR_TRUE);
if (rv != SECSuccess) {
crv = sftk_MapCryptError(PORT_GetError());
goto done;
}
/* set the corret parity on our new des key */
sftk_FormatDESKey(deskey, 8);
done:
PORT_Memset(enc_src, 0, sizeof enc_src);
PORT_Memset(enc_dest, 0, sizeof enc_dest);
return crv;
}
/* 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;
}
/*
* 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 (sftk_GetHashTypeFromMechanism(params->hashAlg) == HASH_AlgNULL ||
sftk_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 ||
(sftk_GetHashTypeFromMechanism(params->hashAlg) == HASH_AlgNULL) ||
(sftk_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:
case SFTK_MESSAGE_ENCRYPT:
case SFTK_MESSAGE_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:
case SFTK_MESSAGE_SIGN:
case SFTK_MESSAGE_VERIFY:
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:
case SFTK_MESSAGE_ENCRYPT:
case SFTK_MESSAGE_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:
case SFTK_MESSAGE_SIGN:
case SFTK_MESSAGE_VERIFY:
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.
*/
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.
*/
void
sftk_TerminateOp(SFTKSession *session, SFTKContextType ctype,
SFTKSessionContext *context)
{
session->lastOpWasFIPS = context->isFIPS;
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.
*/
CK_RV
sftk_InitGeneric(SFTKSession *session, CK_MECHANISM *pMechanism,
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;
context->isFIPS = sftk_operationIsFIPS(session->slot, pMechanism,
operation, key);
*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 void
sftk_freeRSAOAEPInfo(SFTKOAEPInfo *info, PRBool freeit)
{
PORT_ZFree(info->params.pSourceData, info->params.ulSourceDataLen);
PORT_ZFree(info, sizeof(SFTKOAEPInfo));
}
static SECStatus
sftk_RSAEncryptOAEP(SFTKOAEPInfo *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.pub->keyType == NSSLOWKEYRSAKey);
if (info->key.pub->keyType != NSSLOWKEYRSAKey) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
hashAlg = sftk_GetHashTypeFromMechanism(info->params.hashAlg);
maskHashAlg = sftk_GetHashTypeFromMechanism(info->params.mgf);
return RSA_EncryptOAEP(&info->key.pub->u.rsa, hashAlg, maskHashAlg,
(const unsigned char *)info->params.pSourceData,
info->params.ulSourceDataLen, NULL, 0,
output, outputLen, maxLen, input, inputLen);
}
static SECStatus
sftk_RSADecryptOAEP(SFTKOAEPInfo *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.priv->keyType == NSSLOWKEYRSAKey);
if (info->key.priv->keyType != NSSLOWKEYRSAKey) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
return SECFailure;
}
hashAlg = sftk_GetHashTypeFromMechanism(info->params.hashAlg);
maskHashAlg = sftk_GetHashTypeFromMechanism(info->params.mgf);
rv = RSA_DecryptOAEP(&info->key.priv->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_ZFree(ctx->adOverflow, ctx->adLen);
ctx->adOverflow = NULL;
} else {
PORT_Memset(ctx->ad, 0, ctx->adLen);
}
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);
}
static SECStatus
sftk_ChaCha20Ctr(const SFTKChaCha20CtrInfo *ctx,
unsigned char *output, unsigned int *outputLen,
unsigned int maxOutputLen,
const unsigned char *input, unsigned int inputLen)
{
if (maxOutputLen < inputLen) {
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
return SECFailure;
}
ChaCha20_Xor(output, input, inputLen, ctx->key,
ctx->nonce, ctx->counter);
*outputLen = inputLen;
return SECSuccess;
}
static void
sftk_ChaCha20Ctr_DestroyContext(SFTKChaCha20CtrInfo *ctx,
PRBool freeit)
{
memset(ctx, 0, sizeof(*ctx));
if (freeit) {
PORT_Free(ctx);
}
}
/** 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.
*/
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;
#ifndef NSS_DISABLE_DEPRECATED_RC2
CK_RC2_CBC_PARAMS *rc2_param;
unsigned effectiveKeyLength;
#endif
#if NSS_SOFTOKEN_DOES_RC5
CK_RC5_CBC_PARAMS *rc5_param;
SECItem rc5Key;
#endif
CK_NSS_GCM_PARAMS nss_gcm_param;
void *aes_param;
CK_NSS_AEAD_PARAMS nss_aead_params;
CK_NSS_AEAD_PARAMS *nss_aead_params_ptr = NULL;
CK_KEY_TYPE key_type;
CK_RV crv = CKR_OK;
unsigned char newdeskey[24];
PRBool useNewKey = PR_FALSE;
int t;
if (!pMechanism) {
return CKR_MECHANISM_PARAM_INVALID;
}
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, pMechanism, &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;
{
SFTKOAEPInfo *info;
CK_RSA_PKCS_OAEP_PARAMS *params =
(CK_RSA_PKCS_OAEP_PARAMS *)pMechanism->pParameter;
/* make a copy of the source data value for future
* use (once the user has reclaimed his data in pParameter)*/
void *newSource = NULL;
if (params->pSourceData) {
newSource = PORT_Alloc(params->ulSourceDataLen);
if (newSource == NULL) {
crv = CKR_HOST_MEMORY;
break;
}
PORT_Memcpy(newSource, params->pSourceData, params->ulSourceDataLen);
}
info = PORT_New(SFTKOAEPInfo);
if (info == NULL) {
PORT_ZFree(newSource, params->ulSourceDataLen);
crv = CKR_HOST_MEMORY;
break;
}
info->params = *params;
info->params.pSourceData = newSource;
info->isEncrypt = isEncrypt;
/* now setup encryption and decryption contexts */
if (isEncrypt) {
info->key.pub = sftk_GetPubKey(key, CKK_RSA, &crv);
if (info->key.pub == NULL) {
sftk_freeRSAOAEPInfo(info, PR_TRUE);
crv = CKR_KEY_HANDLE_INVALID;
break;
}
context->update = (SFTKCipher)sftk_RSAEncryptOAEP;
context->maxLen = nsslowkey_PublicModulusLen(info->key.pub);
} else {
info->key.priv = sftk_GetPrivKey(key, CKK_RSA, &crv);
if (info->key.priv == NULL) {
sftk_freeRSAOAEPInfo(info, PR_TRUE);
crv = CKR_KEY_HANDLE_INVALID;
break;
}
context->update = (SFTKCipher)sftk_RSADecryptOAEP;
context->maxLen = nsslowkey_PrivateModulusLen(info->key.priv);
}
context->cipherInfo = info;
}
context->destroy = (SFTKDestroy)sftk_freeRSAOAEPInfo;
break;
#ifndef NSS_DISABLE_DEPRECATED_RC2
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;
}
if (BAD_PARAM_CAST(pMechanism, sizeof(CK_RC2_CBC_PARAMS))) {
crv = CKR_MECHANISM_PARAM_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;
#endif /* NSS_DISABLE_DEPRECATED_RC2 */
#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;
}
if (BAD_PARAM_CAST(pMechanism, sizeof(CK_RC5_CBC_PARAMS))) {
crv = CKR_MECHANISM_PARAM_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:
if ((t != NSS_DES && t != NSS_DES_EDE3) && (pMechanism->pParameter == NULL ||
pMechanism->ulParameterLen < 8)) {
crv = CKR_DOMAIN_PARAMS_INVALID;
break;
}
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;
#ifndef NSS_DISABLE_DEPRECATED_SEED
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;
#endif /* NSS_DISABLE_DEPRECATED_SEED */
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:
aes_param = pMechanism->pParameter;
/*
* Due to a mismatch between the documentation and the header
* file, two different definitions for CK_GCM_PARAMS exist.
* The header file is normative according to Oasis, but NSS used
* the documentation. In PKCS #11 v3.0, this was reconciled in
* favor of the header file definition. To maintain binary
* compatibility, NSS now defines CK_GCM_PARAMS_V3 as the official
* version v3 (V2.4 header file) and CK_NSS_GCM_PARAMS as the
* legacy (V2.4 documentation, NSS version). CK_GCM_PARAMS
* is defined as CK_GCM_PARAMS_V3 if NSS_PKCS11_2_0_COMPAT is not
* defined and CK_NSS_GCM_PARAMS if it is. Internally
* softoken continues to use the legacy version. The code below
* automatically detects which parameter was passed in and
* converts CK_GCM_PARAMS_V3 to the CK_NSS_GCM_PARAMS (legacy
* version) on the fly. NSS proper will eventually start
* using the CK_GCM_PARAMS_V3 version and fall back to the
* CK_NSS_GCM_PARAMS if the CK_GCM_PARAMS_V3 version fails with
* CKR_MECHANISM_PARAM_INVALID.
*/
if (pMechanism->mechanism == CKM_AES_GCM) {
if (!aes_param) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
if (pMechanism->ulParameterLen == sizeof(CK_GCM_PARAMS_V3)) {
/* convert the true V3 parameters into the old NSS parameters */
CK_GCM_PARAMS_V3 *gcm_params = (CK_GCM_PARAMS_V3 *)aes_param;
if (gcm_params->ulIvLen * 8 != gcm_params->ulIvBits) {
/* only support byte aligned IV lengths */
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
aes_param = (void *)&nss_gcm_param;
nss_gcm_param.pIv = gcm_params->pIv;
nss_gcm_param.ulIvLen = gcm_params->ulIvLen;
nss_gcm_param.pAAD = gcm_params->pAAD;
nss_gcm_param.ulAADLen = gcm_params->ulAADLen;
nss_gcm_param.ulTagBits = gcm_params->ulTagBits;
} else if (pMechanism->ulParameterLen != sizeof(CK_NSS_GCM_PARAMS)) {
/* neither old nor new style params, must be invalid */
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
} else if ((pMechanism->mechanism == CKM_AES_CTR && BAD_PARAM_CAST(pMechanism, sizeof(CK_AES_CTR_PARAMS))) ||
((pMechanism->mechanism == CKM_AES_CBC || pMechanism->mechanism == CKM_AES_CTS) && BAD_PARAM_CAST(pMechanism, AES_BLOCK_SIZE))) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
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 *)aes_param,
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:
case CKM_CHACHA20_POLY1305:
if (pMechanism->mechanism == CKM_NSS_CHACHA20_POLY1305) {
if (key_type != CKK_NSS_CHACHA20) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
if ((pMechanism->pParameter == NULL) ||
(pMechanism->ulParameterLen != sizeof(CK_NSS_AEAD_PARAMS))) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
nss_aead_params_ptr = (CK_NSS_AEAD_PARAMS *)pMechanism->pParameter;
} else {
CK_SALSA20_CHACHA20_POLY1305_PARAMS_PTR chacha_poly_params;
if (key_type != CKK_CHACHA20) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
if ((pMechanism->pParameter == NULL) ||
(pMechanism->ulParameterLen !=
sizeof(CK_SALSA20_CHACHA20_POLY1305_PARAMS))) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
chacha_poly_params = (CK_SALSA20_CHACHA20_POLY1305_PARAMS_PTR)
pMechanism->pParameter;
nss_aead_params_ptr = &nss_aead_params;
nss_aead_params.pNonce = chacha_poly_params->pNonce;
nss_aead_params.ulNonceLen = chacha_poly_params->ulNonceLen;
nss_aead_params.pAAD = chacha_poly_params->pAAD;
nss_aead_params.ulAADLen = chacha_poly_params->ulAADLen;
nss_aead_params.ulTagLen = 16; /* Poly1305 is always 16 */
}
context->multi = PR_FALSE;
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,
nss_aead_params_ptr);
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_CHACHA20_CTR: /* old NSS private version */
case CKM_CHACHA20: /* PKCS #11 v3 version */
{
unsigned char *counter;
unsigned char *nonce;
unsigned long counter_len;
unsigned long nonce_len;
context->multi = PR_FALSE;
if (pMechanism->mechanism == CKM_NSS_CHACHA20_CTR) {
if (key_type != CKK_NSS_CHACHA20) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
if (pMechanism->pParameter == NULL || pMechanism->ulParameterLen != 16) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
counter_len = 4;
counter = pMechanism->pParameter;
nonce = counter + 4;
nonce_len = 12;
} else {
CK_CHACHA20_PARAMS_PTR chacha20_param_ptr;
if (key_type != CKK_CHACHA20) {
crv = CKR_KEY_TYPE_INCONSISTENT;
break;
}
if (pMechanism->pParameter == NULL || pMechanism->ulParameterLen != sizeof(CK_CHACHA20_PARAMS)) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
chacha20_param_ptr = (CK_CHACHA20_PARAMS_PTR)pMechanism->pParameter;
if ((chacha20_param_ptr->blockCounterBits != 32) &&
(chacha20_param_ptr->blockCounterBits != 64)) {
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
counter_len = chacha20_param_ptr->blockCounterBits / PR_BITS_PER_BYTE;
counter = chacha20_param_ptr->pBlockCounter;
nonce = chacha20_param_ptr->pNonce;
nonce_len = chacha20_param_ptr->ulNonceBits / PR_BITS_PER_BYTE;
}
att = sftk_FindAttribute(key, CKA_VALUE);
if (att == NULL) {
crv = CKR_KEY_HANDLE_INVALID;
break;
}
SFTKChaCha20CtrInfo *ctx = PORT_ZNew(SFTKChaCha20CtrInfo);
if (!ctx) {
sftk_FreeAttribute(att);
crv = CKR_HOST_MEMORY;
break;
}
if (att->attrib.ulValueLen != sizeof(ctx->key)) {
sftk_FreeAttribute(att);
PORT_Free(ctx);
crv = CKR_KEY_HANDLE_INVALID;
break;
}
memcpy(ctx->key, att->attrib.pValue, att->attrib.ulValueLen);
sftk_FreeAttribute(att);
/* make sure we don't overflow our parameters */
if ((sizeof(ctx->counter) < counter_len) ||
(sizeof(ctx->nonce) < nonce_len)) {
PORT_Free(ctx);
crv = CKR_MECHANISM_PARAM_INVALID;
break;
}
/* The counter is little endian. */
int i = 0;
for (; i < counter_len; ++i) {
ctx->counter |= (PRUint32)counter[i] << (i * 8);
}
memcpy(ctx->nonce, nonce, nonce_len);
context->cipherInfo = ctx;
context->update = (SFTKCipher)sftk_ChaCha20Ctr;
context->destroy = (SFTKDestroy)sftk_ChaCha20Ctr_DestroyContext;
break;
}
case CKM_NSS_AES_KEY_WRAP_PAD:
case CKM_AES_KEY_WRAP_PAD:
context->doPad = PR_TRUE;
/* fall thru */
case CKM_NSS_AES_KEY_WRAP:
case CKM_AES_KEY_WRAP:
context->blockSize = 8;
case CKM_AES_KEY_WRAP_KWP:
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 = 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;
}
if (pMechanism->mechanism == CKM_AES_KEY_WRAP_KWP) {
context->update = (SFTKCipher)(isEncrypt ? AESKeyWrap_EncryptKWP
: AESKeyWrap_DecryptKWP);
} else {
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, maxout, 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);
if (rv != SECSuccess) {
return sftk_MapCryptError(PORT_GetError());
}
*pulEncryptedPartLen = (CK_ULONG)(outlen + padoutlen);
return CKR_OK;
}
/* 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) {
outlen = context->rsa ? context->maxLen : ulDataLen + 2 * context->blockSize;
goto done;
}
if (context->doPad) {
if (context->multi) {
CK_ULONG updateLen = maxoutlen;
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,
&updateLen);
if (crv != CKR_OK) {
updateLen = 0;
}
maxoutlen -= updateLen;
pEncryptedData += updateLen;
finalLen = maxoutlen;
crv2 = NSC_EncryptFinal(hSession, pEncryptedData, &finalLen);
if (crv == CKR_OK && crv2 == CKR_OK) {
*pulEncryptedDataLen = updateLen + 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.