blob: 987e76b096daaf3fb8a726b06e15989070f41685 [file] [log] [blame]
/*
* Copyright (c) 2010 SURFnet bv
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*****************************************************************************
SecureDataManager.cpp
The secure data manager main class. Every token instance has a secure data
manager instance member that is used to decrypt and encrypt sensitive object
attributes such as key material. The secure data manager maintains a key blob
containing a 256-bit AES key that is used in this decryption and encryption
process. The key blob itself is encrypted using a PBE derived key that is
derived from the user PIN and a PBE key that is derived from the SO PIN. It
is up to the token to enforce access control based on which user is logged
in; authentication using the SO PIN is required to be able to change the
user PIN. The master key that is used to decrypt/encrypt sensitive attributes
is stored in memory under a mask that is changed every time the key is used.
*****************************************************************************/
#include "config.h"
#include "SecureDataManager.h"
#include "CryptoFactory.h"
#include "AESKey.h"
#include "SymmetricAlgorithm.h"
#include "RFC4880.h"
// Constructors
// Initialise the object; called by all constructors
void SecureDataManager::initObject()
{
// Get an RNG instance
rng = CryptoFactory::i()->getRNG();
// Get an AES implementation
aes = CryptoFactory::i()->getSymmetricAlgorithm(SymAlgo::AES);
// Initialise masking data
mask = new ByteString();
rng->generateRandom(*mask, 32);
// Set the initial login state
soLoggedIn = userLoggedIn = false;
// Set the magic
magic = ByteString("524A52"); // RJR
// Get a mutex
dataMgrMutex = MutexFactory::i()->getMutex();
}
// Constructs a new SecureDataManager for a blank token; actual
// initialisation is done by setting the SO PIN
SecureDataManager::SecureDataManager()
{
initObject();
}
// Constructs a SecureDataManager using the specified key blob
SecureDataManager::SecureDataManager(const ByteString& soPINBlob, const ByteString& userPINBlob)
{
initObject();
// De-serialise the key blob
soEncryptedKey = soPINBlob;
userEncryptedKey = userPINBlob;
}
// Destructor
SecureDataManager::~SecureDataManager()
{
// Recycle the AES instance
CryptoFactory::i()->recycleSymmetricAlgorithm(aes);
// Clean up the mask
delete mask;
MutexFactory::i()->recycleMutex(dataMgrMutex);
}
// Generic function for creating an encrypted version of the key from the specified passphrase
bool SecureDataManager::pbeEncryptKey(const ByteString& passphrase, ByteString& encryptedKey)
{
// Generate salt
ByteString salt;
if (!rng->generateRandom(salt, 8)) return false;
// Derive the key using RFC4880 PBE
AESKey* pbeKey = NULL;
if (!RFC4880::PBEDeriveKey(passphrase, salt, &pbeKey))
{
return false;
}
// Add the salt
encryptedKey.wipe();
encryptedKey += salt;
// Generate random IV
ByteString IV;
if (!rng->generateRandom(IV, aes->getBlockSize())) return false;
// Add the IV
encryptedKey += IV;
// Encrypt the data
ByteString block;
if (!aes->encryptInit(pbeKey, SymMode::CBC, IV))
{
delete pbeKey;
return false;
}
// First, add the magic
if (!aes->encryptUpdate(magic, block))
{
delete pbeKey;
return false;
}
encryptedKey += block;
// Then, add the key itself
ByteString key;
{
MutexLocker lock(dataMgrMutex);
unmask(key);
bool rv = aes->encryptUpdate(key, block);
remask(key);
if (!rv)
{
delete pbeKey;
return false;
}
}
encryptedKey += block;
// And finalise encryption
if (!aes->encryptFinal(block))
{
delete pbeKey;
return false;
}
encryptedKey += block;
delete pbeKey;
return true;
}
// Set the SO PIN (requires either a blank SecureDataManager or the
// SO to have logged in previously)
bool SecureDataManager::setSOPIN(const ByteString& soPIN)
{
// Check the new PIN
if (soPIN.size() == 0)
{
DEBUG_MSG("Zero length PIN specified");
return false;
}
// Check if the SO needs to be logged in
if ((soEncryptedKey.size() > 0) && !soLoggedIn)
{
DEBUG_MSG("SO must be logged in to change the SO PIN");
return false;
}
// If no SO PIN was set, then this is a SecureDataManager for a blank token. This
// means a new key has to be generated
if (soEncryptedKey.size() == 0)
{
ByteString key;
rng->generateRandom(key, 32);
remask(key);
}
return pbeEncryptKey(soPIN, soEncryptedKey);
}
// Set the user PIN (requires either the SO or the user to have logged
// in previously)
bool SecureDataManager::setUserPIN(const ByteString& userPIN)
{
// Check if the SO or the user is logged in
if (!soLoggedIn && !userLoggedIn)
{
DEBUG_MSG("Must be logged in to change the user PIN");
return false;
}
// Check the new PIN
if (userPIN.size() == 0)
{
DEBUG_MSG("Zero length PIN specified");
return false;
}
return pbeEncryptKey(userPIN, userEncryptedKey);
}
// Generic login function
bool SecureDataManager::login(const ByteString& passphrase, const ByteString& encryptedKey)
{
// Log out first
this->logout();
// First, take the salt from the encrypted key
ByteString salt = encryptedKey.substr(0,8);
// Then, take the IV from the encrypted key
ByteString IV = encryptedKey.substr(8, aes->getBlockSize());
// Now, take the encrypted data from the encrypted key
ByteString encryptedKeyData = encryptedKey.substr(8 + aes->getBlockSize());
// Derive the PBE key
AESKey* pbeKey = NULL;
if (!RFC4880::PBEDeriveKey(passphrase, salt, &pbeKey))
{
return false;
}
// Decrypt the key data
ByteString decryptedKeyData;
ByteString finalBlock;
// NOTE: The login will fail here if incorrect passphrase is supplied
if (!aes->decryptInit(pbeKey, SymMode::CBC, IV) ||
!aes->decryptUpdate(encryptedKeyData, decryptedKeyData) ||
!aes->decryptFinal(finalBlock))
{
delete pbeKey;
return false;
}
delete pbeKey;
decryptedKeyData += finalBlock;
// Check the magic
if (decryptedKeyData.substr(0, 3) != magic)
{
// The passphrase was incorrect
DEBUG_MSG("Incorrect passphrase supplied");
return false;
}
// Strip off the magic
ByteString key = decryptedKeyData.substr(3);
// And mask the key
decryptedKeyData.wipe();
MutexLocker lock(dataMgrMutex);
remask(key);
return true;
}
// Log in using the SO PIN
bool SecureDataManager::loginSO(const ByteString& soPIN)
{
return (soLoggedIn = login(soPIN, soEncryptedKey));
}
// Log in using the user PIN
bool SecureDataManager::loginUser(const ByteString& userPIN)
{
return (userLoggedIn = login(userPIN, userEncryptedKey));
}
// Generic re-authentication function
bool SecureDataManager::reAuthenticate(const ByteString& passphrase, const ByteString& encryptedKey)
{
// First, take the salt from the encrypted key
ByteString salt = encryptedKey.substr(0,8);
// Then, take the IV from the encrypted key
ByteString IV = encryptedKey.substr(8, aes->getBlockSize());
// Now, take the encrypted data from the encrypted key
ByteString encryptedKeyData = encryptedKey.substr(8 + aes->getBlockSize());
// Derive the PBE key
AESKey* pbeKey = NULL;
if (!RFC4880::PBEDeriveKey(passphrase, salt, &pbeKey))
{
return false;
}
// Decrypt the key data
ByteString decryptedKeyData;
ByteString finalBlock;
// NOTE: The login will fail here if incorrect passphrase is supplied
if (!aes->decryptInit(pbeKey, SymMode::CBC, IV) ||
!aes->decryptUpdate(encryptedKeyData, decryptedKeyData) ||
!aes->decryptFinal(finalBlock))
{
delete pbeKey;
return false;
}
delete pbeKey;
decryptedKeyData += finalBlock;
// Check the magic
if (decryptedKeyData.substr(0, 3) != magic)
{
// The passphrase was incorrect
DEBUG_MSG("Incorrect passphrase supplied");
return false;
}
// And mask the key
decryptedKeyData.wipe();
return true;
}
// Re-authenticate the SO
bool SecureDataManager::reAuthenticateSO(const ByteString& soPIN)
{
return reAuthenticate(soPIN, soEncryptedKey);
}
// Re-authenticate the user
bool SecureDataManager::reAuthenticateUser(const ByteString& userPIN)
{
return reAuthenticate(userPIN, userEncryptedKey);
}
// Log out
void SecureDataManager::logout()
{
MutexLocker lock(dataMgrMutex);
// Clear the logged in state
soLoggedIn = userLoggedIn = false;
// Clear the masked key
maskedKey.wipe();
}
// Decrypt the supplied data
bool SecureDataManager::decrypt(const ByteString& encrypted, ByteString& plaintext)
{
// Check the object logged in state
if ((!userLoggedIn && !soLoggedIn) || (maskedKey.size() != 32))
{
return false;
}
// Do not attempt decryption of empty byte strings
if (encrypted.size() == 0)
{
plaintext = ByteString("");
return true;
}
AESKey theKey(256);
ByteString unmaskedKey;
{
MutexLocker lock(dataMgrMutex);
unmask(unmaskedKey);
theKey.setKeyBits(unmaskedKey);
remask(unmaskedKey);
}
// Take the IV from the input data
ByteString IV = encrypted.substr(0, aes->getBlockSize());
if (IV.size() != aes->getBlockSize())
{
ERROR_MSG("Invalid IV in encrypted data");
return false;
}
ByteString finalBlock;
if (!aes->decryptInit(&theKey, SymMode::CBC, IV) ||
!aes->decryptUpdate(encrypted.substr(aes->getBlockSize()), plaintext) ||
!aes->decryptFinal(finalBlock))
{
return false;
}
plaintext += finalBlock;
return true;
}
// Encrypt the supplied data
bool SecureDataManager::encrypt(const ByteString& plaintext, ByteString& encrypted)
{
// Check the object logged in state
if ((!userLoggedIn && !soLoggedIn) || (maskedKey.size() != 32))
{
return false;
}
AESKey theKey(256);
ByteString unmaskedKey;
{
MutexLocker lock(dataMgrMutex);
unmask(unmaskedKey);
theKey.setKeyBits(unmaskedKey);
remask(unmaskedKey);
}
// Wipe encrypted data block
encrypted.wipe();
// Generate random IV
ByteString IV;
if (!rng->generateRandom(IV, aes->getBlockSize())) return false;
ByteString finalBlock;
if (!aes->encryptInit(&theKey, SymMode::CBC, IV) ||
!aes->encryptUpdate(plaintext, encrypted) ||
!aes->encryptFinal(finalBlock))
{
return false;
}
encrypted += finalBlock;
// Add IV to output data
encrypted = IV + encrypted;
return true;
}
// Returns the key blob for the SO PIN
ByteString SecureDataManager::getSOPINBlob()
{
return soEncryptedKey;
}
// Returns the key blob for the user PIN
ByteString SecureDataManager::getUserPINBlob()
{
return userEncryptedKey;
}
// Unmask the key
void SecureDataManager::unmask(ByteString& key)
{
key = maskedKey;
key ^= *mask;
}
// Remask the key
void SecureDataManager::remask(ByteString& key)
{
// Generate a new mask
rng->generateRandom(*mask, 32);
key ^= *mask;
maskedKey = key;
}
// Check if the SO is logged in
bool SecureDataManager::isSOLoggedIn()
{
return soLoggedIn;
}
// Check if the user is logged in
bool SecureDataManager::isUserLoggedIn()
{
return userLoggedIn;
}