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