security: Adding SecTpmFile which a pure file based "TPM".
Change-Id: I73b6ed8e0876217642ab6a8733c4da35ef9e69d9
diff --git a/src/security/sec-tpm-file.cpp b/src/security/sec-tpm-file.cpp
new file mode 100644
index 0000000..c711089
--- /dev/null
+++ b/src/security/sec-tpm-file.cpp
@@ -0,0 +1,380 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/**
+ * Copyright (C) 2013 Regents of the University of California.
+ * @author: Xingyu Ma <maxy12@cs.ucla.edu>
+ * Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ * Yingdi Yu <yingdi@cs.ucla.edu>
+ * See COPYING for copyright and distribution information.
+ */
+
+#include <ndn-cpp-dev/security/sec-tpm-file.hpp>
+
+#include <string>
+
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include <cryptopp/rsa.h>
+#include <cryptopp/files.h>
+#include <cryptopp/base64.h>
+#include <cryptopp/hex.h>
+#include <cryptopp/osrng.h>
+#include <cryptopp/sha.h>
+#include <cryptopp/pssr.h>
+#include <cryptopp/modes.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+using namespace CryptoPP;
+using namespace ndn;
+using namespace std;
+
+namespace ndn
+{
+
+class SecTpmFile::Impl {
+public:
+ Impl(const string &dir)
+ {
+ if(dir.empty())
+ m_keystorePath = boost::filesystem::path(getenv("HOME")) / ".ndnx" / "ndnsec-keys";
+ else
+ m_keystorePath = dir;
+
+ boost::filesystem::create_directories (m_keystorePath);
+ }
+
+public:
+ boost::filesystem::path m_keystorePath;
+};
+
+SecTpmFile::SecTpmFile(const string & dir)
+ : impl_(new Impl(dir))
+{}
+
+void
+SecTpmFile::generateKeyPairInTpm(const Name & keyName, KeyType keyType, int keySize)
+{
+ string keyURI = keyName.toUri();
+
+ if(doesKeyExistInTpm(keyName, KEY_CLASS_PUBLIC))
+ throw Error("public key exists");
+ if(doesKeyExistInTpm(keyName, KEY_CLASS_PRIVATE))
+ throw Error("private key exists");
+
+ string keyFileName = nameTransform(keyURI, "");
+ maintainMapping(keyURI, keyFileName);
+
+ try{
+ switch(keyType){
+ case KEY_TYPE_RSA:
+ {
+ AutoSeededRandomPool rng;
+ InvertibleRSAFunction privateKey;
+ privateKey.Initialize(rng, keySize);
+
+ string privateKeyFileName = keyFileName + ".pri";
+ Base64Encoder privateKeySink(new FileSink(privateKeyFileName.c_str()));
+ privateKey.DEREncode(privateKeySink);
+ privateKeySink.MessageEnd();
+
+ RSAFunction publicKey(privateKey);
+ string publicKeyFileName = keyFileName + ".pub";
+ Base64Encoder publicKeySink(new FileSink(publicKeyFileName.c_str()));
+ publicKey.DEREncode(publicKeySink);
+ publicKeySink.MessageEnd();
+
+ /*set file permission*/
+ chmod(privateKeyFileName.c_str(), 0000400);
+ chmod(publicKeyFileName.c_str(), 0000444);
+ return;
+ }
+ default:
+ throw Error("Unsupported key type!");
+ }
+ }catch(const CryptoPP::Exception& e){
+ throw Error(e.what());
+ }
+}
+
+ptr_lib::shared_ptr<PublicKey>
+SecTpmFile::getPublicKeyFromTpm(const Name & keyName)
+{
+ string keyURI = keyName.toUri();
+
+ if(!doesKeyExistInTpm(keyName, KEY_CLASS_PUBLIC))
+ throw Error("public key doesn't exists");
+
+ string publicKeyFileName = nameTransform(keyURI, ".pub");
+ std::ostringstream os;
+ try{
+ FileSource(publicKeyFileName.c_str(), true, new Base64Decoder(new FileSink(os)));
+ }catch(const CryptoPP::Exception& e){
+ throw Error(e.what());
+ }
+
+ return ptr_lib::make_shared<PublicKey>(reinterpret_cast<const uint8_t*>(os.str().c_str()), os.str().size());
+}
+
+Block
+SecTpmFile::signInTpm(const uint8_t *data, size_t dataLength, const Name& keyName, DigestAlgorithm digestAlgorithm)
+{
+ string keyURI = keyName.toUri();
+
+ if(!doesKeyExistInTpm(keyName, KEY_CLASS_PRIVATE))
+ throw Error("private key doesn't exists");
+
+ try{
+ AutoSeededRandomPool rng;
+
+ //Read private key
+ ByteQueue bytes;
+ string privateKeyFileName = nameTransform(keyURI, ".pri");
+ FileSource file(privateKeyFileName.c_str(), true, new Base64Decoder);
+ file.TransferTo(bytes);
+ bytes.MessageEnd();
+ RSA::PrivateKey privateKey;
+ privateKey.Load(bytes);
+
+ //Sign message
+ switch(digestAlgorithm){
+ case DIGEST_ALGORITHM_SHA256:
+ {
+ RSASS<PKCS1v15, SHA256>::Signer signer(privateKey);
+
+ OBufferStream os;
+ StringSource(data, dataLength, true, new SignerFilter(rng, signer, new FileSink(os)));
+
+ return Block(Tlv::SignatureValue, os.buf());
+ }
+ default:
+ throw Error("Unsupported digest algorithm!");
+ }
+ }catch(const CryptoPP::Exception& e){
+ throw Error(e.what());
+ }
+}
+
+
+ConstBufferPtr
+SecTpmFile::decryptInTpm(const Name& keyName, const uint8_t* data, size_t dataLength, bool isSymmetric)
+{
+ string keyURI = keyName.toUri();
+ if (!isSymmetric)
+ {
+ if(!doesKeyExistInTpm(keyName, KEY_CLASS_PRIVATE))
+ throw Error("private key doesn't exist");
+
+ try{
+ AutoSeededRandomPool rng;
+
+ //Read private key
+ ByteQueue bytes;
+ string privateKeyFileName = nameTransform(keyURI, ".pri");
+ FileSource file(privateKeyFileName.c_str(), true, new Base64Decoder);
+ file.TransferTo(bytes);
+ bytes.MessageEnd();
+ RSA::PrivateKey privateKey;
+ privateKey.Load(bytes);
+ RSAES_PKCS1v15_Decryptor decryptor(privateKey);
+
+ OBufferStream os;
+ StringSource(data, dataLength, true, new PK_DecryptorFilter(rng, decryptor, new FileSink(os)));
+
+ return os.buf();
+ }
+ catch(const CryptoPP::Exception& e){
+ throw Error(e.what());
+ }
+ }
+ else
+ {
+ throw Error("Symmetric encryption is not implemented!");
+ // if(!doesKeyExistInTpm(keyName, KEY_CLASS_SYMMETRIC))
+ // throw Error("symmetric key doesn't exist");
+
+ // try{
+ // string keyBits;
+ // string symKeyFileName = nameTransform(keyURI, ".key");
+ // FileSource(symKeyFileName, true, new HexDecoder(new StringSink(keyBits)));
+
+ // using CryptoPP::AES;
+ // AutoSeededRandomPool rnd;
+ // byte iv[AES::BLOCKSIZE];
+ // rnd.GenerateBlock(iv, AES::BLOCKSIZE);
+
+ // CFB_Mode<AES>::Decryption decryptor;
+ // decryptor.SetKeyWithIV(reinterpret_cast<const uint8_t*>(keyBits.c_str()), keyBits.size(), iv);
+
+ // OBufferStream os;
+ // StringSource(data, dataLength, true, new StreamTransformationFilter(decryptor,new FileSink(os)));
+ // return os.buf();
+
+ // }catch(const CryptoPP::Exception& e){
+ // throw Error(e.what());
+ // }
+ }
+}
+
+ConstBufferPtr
+SecTpmFile::encryptInTpm(const Name& keyName, const uint8_t* data, size_t dataLength, bool isSymmetric)
+{
+ string keyURI = keyName.toUri();
+
+ if (!isSymmetric)
+ {
+ if(!doesKeyExistInTpm(keyName, KEY_CLASS_PUBLIC))
+ throw Error("public key doesn't exist");
+ try
+ {
+ AutoSeededRandomPool rng;
+
+ //Read private key
+ ByteQueue bytes;
+ string publicKeyFileName = nameTransform(keyURI, ".pub");
+ FileSource file(publicKeyFileName.c_str(), true, new Base64Decoder);
+ file.TransferTo(bytes);
+ bytes.MessageEnd();
+ RSA::PublicKey publicKey;
+ publicKey.Load(bytes);
+
+ OBufferStream os;
+ RSAES_PKCS1v15_Encryptor encryptor(publicKey);
+
+ StringSource(data, dataLength, true, new PK_EncryptorFilter(rng, encryptor, new FileSink(os)));
+ return os.buf();
+ }
+ catch(const CryptoPP::Exception& e){
+ throw Error(e.what());
+ }
+ }
+ else
+ {
+ throw Error("Symmetric encryption is not implemented!");
+ // if(!doesKeyExistInTpm(keyName, KEY_CLASS_SYMMETRIC))
+ // throw Error("symmetric key doesn't exist");
+
+ // try{
+ // string keyBits;
+ // string symKeyFileName = nameTransform(keyURI, ".key");
+ // FileSource(symKeyFileName, true, new HexDecoder(new StringSink(keyBits)));
+
+ // using CryptoPP::AES;
+ // AutoSeededRandomPool rnd;
+ // byte iv[AES::BLOCKSIZE];
+ // rnd.GenerateBlock(iv, AES::BLOCKSIZE);
+
+ // CFB_Mode<AES>::Encryption encryptor;
+ // encryptor.SetKeyWithIV(reinterpret_cast<const uint8_t*>(keyBits.c_str()), keyBits.size(), iv);
+
+ // OBufferStream os;
+ // StringSource(data, dataLength, true, new StreamTransformationFilter(encryptor, new FileSink(os)));
+ // return os.buf();
+ // }catch(const CryptoPP::Exception& e){
+ // throw Error(e.what());
+ // }
+ }
+}
+
+
+void
+SecTpmFile::generateSymmetricKeyInTpm(const Name & keyName, KeyType keyType, int keySize)
+{
+ string keyURI = keyName.toUri();
+
+ if(doesKeyExistInTpm(keyName, KEY_CLASS_SYMMETRIC))
+ throw Error("symmetric key exists");
+
+ string keyFileName = nameTransform(keyURI, "");
+ maintainMapping(keyURI, keyFileName);
+ string symKeyFileName = keyFileName + ".key";
+
+ try{
+ switch(keyType){
+ case KEY_TYPE_AES:
+ {
+ AutoSeededRandomPool rnd;
+ SecByteBlock key(0x00, keySize);
+ rnd.GenerateBlock(key, keySize );
+
+ StringSource(key, key.size(), true, new HexEncoder(new FileSink(symKeyFileName.c_str())));
+
+ chmod(symKeyFileName.c_str(), 0000400);
+ return;
+ }
+ default:
+ throw Error("Unsupported symmetric key type!");
+ }
+ }catch(const CryptoPP::Exception& e){
+ throw Error(e.what());
+ }
+}
+
+bool
+SecTpmFile::doesKeyExistInTpm(const Name & keyName, KeyClass keyClass)
+{
+ string keyURI = keyName.toUri();
+ if (keyClass == KEY_CLASS_PUBLIC)
+ {
+ string publicKeyName = SecTpmFile::nameTransform(keyURI, ".pub");
+ fstream fin(publicKeyName.c_str(),ios::in);
+ if (fin)
+ return true;
+ else
+ return false;
+ }
+ if (keyClass == KEY_CLASS_PRIVATE)
+ {
+ string privateKeyName = SecTpmFile::nameTransform(keyURI, ".pri");
+ fstream fin(privateKeyName.c_str(),ios::in);
+ if (fin)
+ return true;
+ else
+ return false;
+ }
+ if (keyClass == KEY_CLASS_SYMMETRIC)
+ {
+ string symmetricKeyName = SecTpmFile::nameTransform(keyURI, ".key");
+ fstream fin(symmetricKeyName.c_str(),ios::in);
+ if (fin)
+ return true;
+ else
+ return false;
+ }
+ return false;
+}
+
+std::string SecTpmFile::nameTransform(const string &keyName, const string &extension)
+{
+ std::string digest;
+ CryptoPP::SHA256 hash;
+ CryptoPP::StringSource foo(keyName, true,
+ new CryptoPP::HashFilter(hash,
+ new CryptoPP::Base64Encoder (new CryptoPP::StringSink(digest))
+ )
+ );
+ boost::algorithm::trim(digest);
+ for (std::string::iterator ch = digest.begin(); ch != digest.end(); ch++)
+ {
+ if (*ch == '/')
+ {
+ *ch = '%';
+ }
+ }
+
+ return (impl_->m_keystorePath / (digest + extension)).string();
+}
+
+void
+SecTpmFile::maintainMapping(string str1, string str2)
+{
+ std::ofstream outfile;
+ string dirFile = (impl_->m_keystorePath / "mapping.txt").string();
+
+ outfile.open(dirFile.c_str(), std::ios_base::app);
+ outfile << str1 << ' ' << str2 << '\n';
+ outfile.close();
+}
+
+} //ndn