| /** |
| * 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 */ |