aboutsummaryrefslogtreecommitdiffstats
path: root/SoftHSMv2/src/lib/data_mgr/SecureDataManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'SoftHSMv2/src/lib/data_mgr/SecureDataManager.cpp')
-rw-r--r--SoftHSMv2/src/lib/data_mgr/SecureDataManager.cpp537
1 files changed, 537 insertions, 0 deletions
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;
+}
+