From 0c89b3ccba7c9b7332ab67ae1936aff51ca62367 Mon Sep 17 00:00:00 2001 From: NingSun Date: Thu, 8 Feb 2018 08:34:03 -0800 Subject: Initial sshsm project structure Issue-ID: AAF-94 Change-Id: I5e82fff418e7567b161acf9b98013a9b85ffc5b4 Signed-off-by: NingSun --- SoftHSMv2/src/lib/data_mgr/SecureDataManager.cpp | 537 +++++++++++++++++++++++ 1 file changed, 537 insertions(+) create mode 100644 SoftHSMv2/src/lib/data_mgr/SecureDataManager.cpp (limited to 'SoftHSMv2/src/lib/data_mgr/SecureDataManager.cpp') diff --git a/SoftHSMv2/src/lib/data_mgr/SecureDataManager.cpp b/SoftHSMv2/src/lib/data_mgr/SecureDataManager.cpp new file mode 100644 index 0000000..987e76b --- /dev/null +++ b/SoftHSMv2/src/lib/data_mgr/SecureDataManager.cpp @@ -0,0 +1,537 @@ +/* + * 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; +} + -- cgit 1.2.3-korg