blob: 6f725091a77ed2eab4facbf36091a223f81acfa9 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* XSEC
*
* XSECCryptoSymmetricKey := Bulk encryption algorithms should all be
* implemented via this interface
*
* Author(s): Milan Tomic
*
*/
#include <xsec/framework/XSECDefs.hpp>
#include <xsec/utils/XSECPlatformUtils.hpp>
#include <xsec/enc/NSS/NSSCryptoProvider.hpp>
#include <xsec/enc/NSS/NSSCryptoSymmetricKey.hpp>
#include <xsec/framework/XSECError.hpp>
#include <xsec/enc/XSECCryptoException.hpp>
#include <xercesc/util/Janitor.hpp>
#if defined (XSEC_HAVE_NSS)
#include "prerror.h"
XERCES_CPP_NAMESPACE_USE
// --------------------------------------------------------------------------------
// Constructor
// --------------------------------------------------------------------------------
NSSCryptoSymmetricKey::NSSCryptoSymmetricKey(XSECCryptoSymmetricKey::SymmetricKeyType type) :
m_keyType(type),
m_keyMode(MODE_NONE),
m_initialised(false),
mp_k(NULL)
{
}
// --------------------------------------------------------------------------------
// Destructor
// --------------------------------------------------------------------------------
NSSCryptoSymmetricKey::~NSSCryptoSymmetricKey() {
if (mp_k != 0)
PK11_FreeSymKey(mp_k);
}
// --------------------------------------------------------------------------------
// Get key type
// --------------------------------------------------------------------------------
XSECCryptoSymmetricKey::SymmetricKeyType NSSCryptoSymmetricKey::getSymmetricKeyType() const {
return m_keyType;
}
// --------------------------------------------------------------------------------
// Get provider name
// --------------------------------------------------------------------------------
const XMLCh * NSSCryptoSymmetricKey::getProviderName() const {
return DSIGConstants::s_unicodeStrPROVNSS;
}
// --------------------------------------------------------------------------------
// Replicate key
// --------------------------------------------------------------------------------
XSECCryptoKey * NSSCryptoSymmetricKey::clone() const {
NSSCryptoSymmetricKey * ret;
XSECnew(ret, NSSCryptoSymmetricKey(m_keyType));
if (mp_k != 0) {
ret->mp_k = PK11_ReferenceSymKey(mp_k);
}
ret->m_keyType = m_keyType;
return ret;
}
// --------------------------------------------------------------------------------
// Store the key value
// --------------------------------------------------------------------------------
void NSSCryptoSymmetricKey::setKey(const unsigned char * key, unsigned int keyLen) {
if (mp_k != 0) {
PK11_FreeSymKey(mp_k);
mp_k = 0;
}
CK_MECHANISM_TYPE cipherMech;
switch (m_keyType) {
case (XSECCryptoSymmetricKey::KEY_3DES_192) :
cipherMech = CKM_DES3_CBC_PAD;
break;
case (XSECCryptoSymmetricKey::KEY_AES_128) :
case (XSECCryptoSymmetricKey::KEY_AES_192) :
case (XSECCryptoSymmetricKey::KEY_AES_256) :
cipherMech = CKM_AES_CBC_PAD;
break;
default:
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Unknown key type");
}
// Turn the raw key into a SECItem
SECItem keyItem;
keyItem.data = (unsigned char*)key;
keyItem.len = keyLen;
PK11SlotInfo * slot = PK11_GetBestSlot(cipherMech, NULL);
// Turn the SECItem into a key object
mp_k = PK11_ImportSymKey(slot,
cipherMech,
PK11_OriginUnwrap,
CKA_ENCRYPT,
&keyItem,
NULL);
if (slot)
PK11_FreeSlot(slot);
}
// --------------------------------------------------------------------------------
// Decrypt context initialisation
// --------------------------------------------------------------------------------
int NSSCryptoSymmetricKey::decryptCtxInit(const unsigned char * iv) {
// Returns amount of IV data used (in bytes)
// Sets m_initialised if the key is OK and the IV is OK.
if (m_initialised)
return 0;
if (mp_k == 0) {
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Cannot initialise without key");
}
else if (m_keyMode == MODE_NONE) {
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Cannot initialise without mode");
}
// Set up the context according to the required cipher type
switch (m_keyType) {
case (XSECCryptoSymmetricKey::KEY_3DES_192) :
// A 3DES key
if (m_keyMode == MODE_CBC) {
if (iv == NULL) {
return 0; // Cannot initialise without an IV
}
SECItem ivItem;
ivItem.data = (unsigned char*)iv;
ivItem.len = 8;
int encryptAlg = (m_doPad == true ? CKM_DES3_CBC_PAD : CKM_DES3_CBC);
SECItem * secParam = PK11_ParamFromIV(encryptAlg, &ivItem);
mp_ctx = PK11_CreateContextBySymKey(encryptAlg, CKA_DECRYPT, mp_k, secParam);
if (secParam)
SECITEM_FreeItem(secParam, PR_TRUE);
m_ivSize = 8;
}
else if (m_keyMode == MODE_ECB) {
SECItem * secParam = PK11_ParamFromIV(CKM_DES3_ECB, NULL);
mp_ctx = PK11_CreateContextBySymKey(CKM_DES3_ECB, CKA_DECRYPT, mp_k, secParam);
if (secParam)
SECITEM_FreeItem(secParam, PR_TRUE);
m_ivSize = 0;
}
else {
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Unrecognized cipher mode");
}
break;
case (XSECCryptoSymmetricKey::KEY_AES_128) :
case (XSECCryptoSymmetricKey::KEY_AES_192) :
case (XSECCryptoSymmetricKey::KEY_AES_256) :
// An AES key
if (m_keyMode == MODE_CBC) {
if (iv == NULL) {
return 0; // Cannot initialise without an IV
}
SECItem ivItem;
ivItem.data = (unsigned char*)iv;
ivItem.len = 16;
SECItem * secParam = PK11_ParamFromIV(CKM_AES_CBC_PAD, &ivItem);
mp_ctx = PK11_CreateContextBySymKey(CKM_AES_CBC_PAD, CKA_DECRYPT, mp_k, secParam);
if (secParam)
SECITEM_FreeItem(secParam, PR_TRUE);
m_ivSize = 16;
}
else if (m_keyMode == MODE_ECB) {
SECItem * secParam = PK11_ParamFromIV(CKM_AES_ECB, NULL);
mp_ctx = PK11_CreateContextBySymKey(CKM_AES_ECB, CKA_DECRYPT, mp_k, secParam);
if (secParam)
SECITEM_FreeItem(secParam, PR_TRUE);
m_ivSize = 0;
}
else {
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Unrecognized cipher mode");
}
break;
default :
// Cannot do this without an IV
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Unknown key type");
}
// Reset some parameters
m_initialised = true;
// Return number of bytes chewed up by IV
return m_ivSize;
}
// --------------------------------------------------------------------------------
// Decrypt initialisation
// --------------------------------------------------------------------------------
bool NSSCryptoSymmetricKey::decryptInit(bool doPad,
SymmetricKeyMode mode,
const unsigned char * iv,
const unsigned char* tag,
unsigned int taglen) {
m_initialised = false;
m_ivSent = iv == NULL;
m_doPad = doPad;
m_keyMode = mode;
decryptCtxInit(iv);
return true;
}
// --------------------------------------------------------------------------------
// Decrypt
// --------------------------------------------------------------------------------
unsigned int NSSCryptoSymmetricKey::decrypt(const unsigned char * inBuf,
unsigned char * plainBuf,
unsigned int inLength,
unsigned int maxOutLength) {
// NOTE: This won't actually stop NSS blowing the buffer, so the onus is
// on the caller.
unsigned int offset = 0;
if (!m_initialised) {
offset = decryptCtxInit(inBuf);
if (offset > inLength) {
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Not enough data passed in to get IV");
}
}
int outl = inLength - offset;
SECStatus s = PK11_CipherOp(mp_ctx, plainBuf, &outl, maxOutLength, (unsigned char*)inBuf, inLength);
if (s != SECSuccess) {
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Error during NSS decrypt");
}
// remove IV
if (m_ivSent) {
memmove(plainBuf, &plainBuf[m_ivSize], outl);
outl -= m_ivSize;
m_ivSent = false;
}
return outl;
}
// --------------------------------------------------------------------------------
// Decrypt finalisation
// --------------------------------------------------------------------------------
unsigned int NSSCryptoSymmetricKey::decryptFinish(unsigned char * plainBuf,
unsigned int maxOutLength) {
unsigned int outl = 0;
SECStatus s = PK11_DigestFinal(mp_ctx, plainBuf, &outl, maxOutLength);
if (s != SECSuccess) {
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Error during NSS decrypt finalisation");
}
PK11_DestroyContext(mp_ctx, PR_TRUE);
mp_ctx = NULL;
m_initialised = false;
return outl;
}
// --------------------------------------------------------------------------------
// Encrypt initialisation
// --------------------------------------------------------------------------------
bool NSSCryptoSymmetricKey::encryptInit(bool doPad,
SymmetricKeyMode mode,
const unsigned char * iv) {
if (m_initialised == true)
return true;
m_doPad = doPad;
m_keyMode = mode;
if (mp_k == 0) {
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Cannot initialise without key");
}
else if (m_keyMode == MODE_NONE) {
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Cannot initialise without mode");
}
// Do some parameter initialisation
m_initialised = true;
// Set up the context according to the required cipher type
const unsigned char * usedIV = NULL;
unsigned char genIV[256];
// Tell the library that the IV still has to be sent
m_ivSent = false;
switch (m_keyType) {
case (XSECCryptoSymmetricKey::KEY_3DES_192) :
// A 3DES key
if (m_keyMode == MODE_CBC) {
if (iv == NULL) {
SECStatus s = PK11_GenerateRandom(genIV, 8);
if (s != SECSuccess) {
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Error generating random IV");
}
usedIV = genIV;
}
else
usedIV = iv;
SECItem ivItem;
ivItem.data = (unsigned char*)usedIV;
ivItem.len = 8;
int encryptAlg = (m_doPad == true ? CKM_DES3_CBC_PAD : CKM_DES3_CBC);
SECItem * secParam = PK11_ParamFromIV(encryptAlg, &ivItem);
mp_ctx = PK11_CreateContextBySymKey(encryptAlg, CKA_ENCRYPT, mp_k, secParam);
if (secParam)
SECITEM_FreeItem(secParam, PR_TRUE);
m_ivSize = 8;
}
else if (m_keyMode == MODE_ECB) {
mp_ctx = PK11_CreateContextBySymKey(CKM_DES3_ECB, CKA_ENCRYPT, mp_k, NULL);
m_ivSize = 0;
}
else {
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Unsupported DES3 cipher mode");
}
break;
case (XSECCryptoSymmetricKey::KEY_AES_128) :
case (XSECCryptoSymmetricKey::KEY_AES_192) :
case (XSECCryptoSymmetricKey::KEY_AES_256) :
// An AES key
if (m_keyMode == MODE_CBC) {
if (iv == NULL) {
SECStatus s = PK11_GenerateRandom(genIV, 16);
if (s != SECSuccess) {
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Error generating random IV");
}
usedIV = genIV;
}
else
usedIV = iv;
SECItem ivItem;
ivItem.data = (unsigned char*)usedIV;
ivItem.len = 16;
SECItem * secParam = PK11_ParamFromIV(CKM_AES_CBC_PAD, &ivItem);
mp_ctx = PK11_CreateContextBySymKey(CKM_AES_CBC_PAD, CKA_ENCRYPT, mp_k, secParam);
if (secParam)
SECITEM_FreeItem(secParam, PR_TRUE);
m_ivSize = 16;
}
else if (m_keyMode == MODE_ECB) {
SECItem * secParam = PK11_ParamFromIV(CKM_AES_ECB, NULL);
mp_ctx = PK11_CreateContextBySymKey(CKM_AES_ECB, CKA_ENCRYPT, mp_k, secParam);
if (secParam)
SECITEM_FreeItem(secParam, PR_TRUE);
m_ivSize = 0;
}
else {
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Unsupported AES cipher mode");
}
break;
default :
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Unknown key type");
}
// Add IV
if (m_keyMode == MODE_CBC || m_keyMode == MODE_GCM) {
memcpy(m_lastBlock, usedIV, m_ivSize);
}
return true;
}
// --------------------------------------------------------------------------------
// Encrypt
// --------------------------------------------------------------------------------
unsigned int NSSCryptoSymmetricKey::encrypt(const unsigned char * inBuf,
unsigned char * cipherBuf,
unsigned int inLength,
unsigned int maxOutLength) {
if (m_initialised == false) {
encryptInit();
}
// NOTE: This won't actually stop NSS blowing the buffer, so the onus is
// on the caller.
unsigned int offset = 0;
if (m_ivSent == false && m_ivSize > 0) {
memcpy(cipherBuf, m_lastBlock, m_ivSize);
m_ivSent = true;
offset = m_ivSize;
}
int outl = 0;
if (inLength + offset > maxOutLength) {
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Not enough space in output buffer for encrypt");
}
SECStatus s = PK11_CipherOp(mp_ctx,
&cipherBuf[offset],
&outl,
maxOutLength - offset,
(unsigned char*)inBuf,
inLength);
if (s != SECSuccess) {
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Error during NSS encrypt");
}
return outl + offset;
}
// --------------------------------------------------------------------------------
// Encrypt finalisation
// --------------------------------------------------------------------------------
unsigned int NSSCryptoSymmetricKey::encryptFinish(unsigned char * cipherBuf,
unsigned int maxOutLength,
unsigned int taglen) {
unsigned int outl = 0;
SECStatus s = PK11_DigestFinal(mp_ctx, cipherBuf, &outl, maxOutLength);
if (s != SECSuccess) {
throw XSECCryptoException(XSECCryptoException::SymmetricError,
"NSS:SymmetricKey - Error during NSS encrypt");
}
PK11_DestroyContext(mp_ctx, PR_TRUE);
mp_ctx = NULL;
// Setup so can be re-used
m_initialised = false;
return outl;
}
#endif /* XSEC_HAVE_NSS */