security: Move KeyChain to security::v1 namespace and deprecated it
Change-Id: Ic4b6915ca15998a83b410f3f8fac027f797ee7ca
Refs: #3098
diff --git a/src/security/v1/sec-tpm-file.cpp b/src/security/v1/sec-tpm-file.cpp
new file mode 100644
index 0000000..adda17f
--- /dev/null
+++ b/src/security/v1/sec-tpm-file.cpp
@@ -0,0 +1,593 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ *
+ * @author Xingyu Ma <http://www.linkedin.com/pub/xingyu-ma/1a/384/5a8>
+ * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
+ * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
+ */
+
+#include "sec-tpm-file.hpp"
+
+#include "../../encoding/buffer-stream.hpp"
+
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+
+#include "cryptopp.hpp"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <algorithm>
+
+namespace ndn {
+namespace security {
+namespace v1 {
+
+using std::string;
+using std::ostringstream;
+using std::ofstream;
+
+const std::string SecTpmFile::SCHEME("tpm-file");
+
+class SecTpmFile::Impl
+{
+public:
+ explicit
+ Impl(const string& dir)
+ {
+ boost::filesystem::path actualDir;
+ if (dir.empty()) {
+#ifdef NDN_CXX_HAVE_TESTS
+ if (getenv("TEST_HOME") != nullptr) {
+ actualDir = boost::filesystem::path(getenv("TEST_HOME")) / ".ndn";
+ }
+ else
+#endif // NDN_CXX_HAVE_TESTS
+ if (getenv("HOME") != nullptr) {
+ actualDir = boost::filesystem::path(getenv("HOME")) / ".ndn";
+ }
+ else {
+ actualDir = boost::filesystem::path(".") / ".ndn";
+ }
+ }
+ else {
+ actualDir = boost::filesystem::path(dir);
+ }
+
+ m_keystorePath = actualDir / "ndnsec-tpm-file";
+ boost::filesystem::create_directories(m_keystorePath);
+ }
+
+ boost::filesystem::path
+ transformName(const string& keyName, const string& extension)
+ {
+ using namespace CryptoPP;
+ string digest;
+ SHA256 hash;
+ StringSource src(keyName,
+ true,
+ new HashFilter(hash,
+ new Base64Encoder(new CryptoPP::StringSink(digest))));
+
+ boost::algorithm::trim(digest);
+ std::replace(digest.begin(), digest.end(), '/', '%');
+
+ return m_keystorePath / (digest + extension);
+ }
+
+ string
+ maintainMapping(const string& keyName)
+ {
+ string keyFileName = transformName(keyName, "").string();
+
+ ofstream outfile;
+ string dirFile = (m_keystorePath / "mapping.txt").string();
+
+ outfile.open(dirFile.c_str(), std::ios_base::app);
+ outfile << keyName << ' ' << keyFileName << '\n';
+ outfile.close();
+
+ return keyFileName;
+ }
+
+public:
+ boost::filesystem::path m_keystorePath;
+};
+
+
+SecTpmFile::SecTpmFile(const string& location)
+ : SecTpm(location)
+ , m_impl(new Impl(location))
+ , m_inTerminal(false)
+{
+}
+
+SecTpmFile::~SecTpmFile()
+{
+}
+
+void
+SecTpmFile::generateKeyPairInTpm(const Name& keyName, const KeyParams& params)
+{
+ string keyURI = keyName.toUri();
+
+ if (doesKeyExistInTpm(keyName, KeyClass::PUBLIC))
+ BOOST_THROW_EXCEPTION(Error("public key exists"));
+ if (doesKeyExistInTpm(keyName, KeyClass::PRIVATE))
+ BOOST_THROW_EXCEPTION(Error("private key exists"));
+
+ string keyFileName = m_impl->maintainMapping(keyURI);
+
+ try {
+ switch (params.getKeyType()) {
+ case KeyType::RSA: {
+ using namespace CryptoPP;
+
+ const RsaKeyParams& rsaParams = static_cast<const RsaKeyParams&>(params);
+ AutoSeededRandomPool rng;
+ InvertibleRSAFunction privateKey;
+ privateKey.Initialize(rng, rsaParams.getKeySize());
+
+ 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;
+ }
+
+ case KeyType::EC: {
+ using namespace CryptoPP;
+
+ const EcdsaKeyParams& ecdsaParams = static_cast<const EcdsaKeyParams&>(params);
+
+ CryptoPP::OID curveName;
+ switch (ecdsaParams.getKeySize()) {
+ case 256:
+ curveName = ASN1::secp256r1();
+ break;
+ case 384:
+ curveName = ASN1::secp384r1();
+ break;
+ default:
+ curveName = ASN1::secp256r1();
+ break;
+ }
+
+ AutoSeededRandomPool rng;
+
+ ECDSA<ECP, SHA256>::PrivateKey privateKey;
+ DL_GroupParameters_EC<ECP> cryptoParams(curveName);
+ cryptoParams.SetEncodeAsOID(true);
+ privateKey.Initialize(rng, cryptoParams);
+
+ ECDSA<ECP, SHA256>::PublicKey publicKey;
+ privateKey.MakePublicKey(publicKey);
+ publicKey.AccessGroupParameters().SetEncodeAsOID(true);
+
+ string privateKeyFileName = keyFileName + ".pri";
+ Base64Encoder privateKeySink(new FileSink(privateKeyFileName.c_str()));
+ privateKey.DEREncode(privateKeySink);
+ privateKeySink.MessageEnd();
+
+ string publicKeyFileName = keyFileName + ".pub";
+ Base64Encoder publicKeySink(new FileSink(publicKeyFileName.c_str()));
+ publicKey.Save(publicKeySink);
+ publicKeySink.MessageEnd();
+
+ // set file permission
+ chmod(privateKeyFileName.c_str(), 0000400);
+ chmod(publicKeyFileName.c_str(), 0000444);
+ return;
+ }
+
+ default:
+ BOOST_THROW_EXCEPTION(Error("Unsupported key type"));
+ }
+ }
+ catch (const KeyParams::Error& e) {
+ BOOST_THROW_EXCEPTION(Error(e.what()));
+ }
+ catch (const CryptoPP::Exception& e) {
+ BOOST_THROW_EXCEPTION(Error(e.what()));
+ }
+}
+
+void
+SecTpmFile::deleteKeyPairInTpm(const Name& keyName)
+{
+ boost::filesystem::path publicKeyPath(m_impl->transformName(keyName.toUri(), ".pub"));
+ boost::filesystem::path privateKeyPath(m_impl->transformName(keyName.toUri(), ".pri"));
+
+ if (boost::filesystem::exists(publicKeyPath))
+ boost::filesystem::remove(publicKeyPath);
+
+ if (boost::filesystem::exists(privateKeyPath))
+ boost::filesystem::remove(privateKeyPath);
+}
+
+shared_ptr<PublicKey>
+SecTpmFile::getPublicKeyFromTpm(const Name& keyName)
+{
+ string keyURI = keyName.toUri();
+
+ if (!doesKeyExistInTpm(keyName, KeyClass::PUBLIC))
+ BOOST_THROW_EXCEPTION(Error("Public Key does not exist"));
+
+ ostringstream os;
+ try {
+ using namespace CryptoPP;
+ FileSource(m_impl->transformName(keyURI, ".pub").string().c_str(),
+ true,
+ new Base64Decoder(new FileSink(os)));
+ }
+ catch (const CryptoPP::Exception& e) {
+ BOOST_THROW_EXCEPTION(Error(e.what()));
+ }
+
+ return make_shared<PublicKey>(reinterpret_cast<const uint8_t*>(os.str().c_str()),
+ os.str().size());
+}
+
+std::string
+SecTpmFile::getScheme()
+{
+ return SCHEME;
+}
+
+ConstBufferPtr
+SecTpmFile::exportPrivateKeyPkcs8FromTpm(const Name& keyName)
+{
+ OBufferStream privateKeyOs;
+ CryptoPP::FileSource(m_impl->transformName(keyName.toUri(), ".pri").string().c_str(), true,
+ new CryptoPP::Base64Decoder(new CryptoPP::FileSink(privateKeyOs)));
+
+ return privateKeyOs.buf();
+}
+
+bool
+SecTpmFile::importPrivateKeyPkcs8IntoTpm(const Name& keyName, const uint8_t* buf, size_t size)
+{
+ try {
+ using namespace CryptoPP;
+
+ string keyFileName = m_impl->maintainMapping(keyName.toUri());
+ keyFileName.append(".pri");
+ StringSource(buf, size,
+ true,
+ new Base64Encoder(new FileSink(keyFileName.c_str())));
+ return true;
+ }
+ catch (const CryptoPP::Exception& e) {
+ return false;
+ }
+}
+
+bool
+SecTpmFile::importPublicKeyPkcs1IntoTpm(const Name& keyName, const uint8_t* buf, size_t size)
+{
+ try {
+ using namespace CryptoPP;
+
+ string keyFileName = m_impl->maintainMapping(keyName.toUri());
+ keyFileName.append(".pub");
+ StringSource(buf, size,
+ true,
+ new Base64Encoder(new FileSink(keyFileName.c_str())));
+ return true;
+ }
+ catch (const CryptoPP::Exception& e) {
+ return false;
+ }
+}
+
+Block
+SecTpmFile::signInTpm(const uint8_t* data, size_t dataLength,
+ const Name& keyName, DigestAlgorithm digestAlgorithm)
+{
+ string keyURI = keyName.toUri();
+
+ if (!doesKeyExistInTpm(keyName, KeyClass::PRIVATE))
+ BOOST_THROW_EXCEPTION(Error("private key doesn't exist"));
+
+ try {
+ using namespace CryptoPP;
+ AutoSeededRandomPool rng;
+
+ // Read public key
+ shared_ptr<PublicKey> pubkeyPtr;
+ pubkeyPtr = getPublicKeyFromTpm(keyName);
+
+ switch (pubkeyPtr->getKeyType()) {
+ case KeyType::RSA: {
+ // Read private key
+ ByteQueue bytes;
+ FileSource file(m_impl->transformName(keyURI, ".pri").string().c_str(),
+ true, new Base64Decoder);
+ file.TransferTo(bytes);
+ bytes.MessageEnd();
+ RSA::PrivateKey privateKey;
+ privateKey.Load(bytes);
+
+ // Sign message
+ switch (digestAlgorithm) {
+ case DigestAlgorithm::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:
+ BOOST_THROW_EXCEPTION(Error("Unsupported digest algorithm"));
+ }
+ }
+
+ case KeyType::EC: {
+ // Read private key
+ ByteQueue bytes;
+ FileSource file(m_impl->transformName(keyURI, ".pri").string().c_str(),
+ true, new Base64Decoder);
+ file.TransferTo(bytes);
+ bytes.MessageEnd();
+
+ // Sign message
+ switch (digestAlgorithm) {
+ case DigestAlgorithm::SHA256: {
+ ECDSA<ECP, SHA256>::PrivateKey privateKey;
+ privateKey.Load(bytes);
+ ECDSA<ECP, SHA256>::Signer signer(privateKey);
+
+ OBufferStream os;
+ StringSource(data, dataLength,
+ true,
+ new SignerFilter(rng, signer, new FileSink(os)));
+
+ uint8_t buf[200];
+ size_t bufSize = DSAConvertSignatureFormat(buf, sizeof(buf), DSA_DER,
+ os.buf()->buf(), os.buf()->size(),
+ DSA_P1363);
+
+ shared_ptr<Buffer> sigBuffer = make_shared<Buffer>(buf, bufSize);
+
+ return Block(tlv::SignatureValue, sigBuffer);
+ }
+
+ default:
+ BOOST_THROW_EXCEPTION(Error("Unsupported digest algorithm"));
+ }
+ }
+
+ default:
+ BOOST_THROW_EXCEPTION(Error("Unsupported key type"));
+ }
+ }
+ catch (const CryptoPP::Exception& e) {
+ BOOST_THROW_EXCEPTION(Error(e.what()));
+ }
+}
+
+
+ConstBufferPtr
+SecTpmFile::decryptInTpm(const uint8_t* data, size_t dataLength,
+ const Name& keyName, bool isSymmetric)
+{
+ BOOST_THROW_EXCEPTION(Error("SecTpmFile::decryptInTpm is not supported"));
+ // string keyURI = keyName.toUri();
+ // if (!isSymmetric)
+ // {
+ // if (!doesKeyExistInTpm(keyName, KeyClass::PRIVATE))
+ // throw Error("private key doesn't exist");
+
+ // try{
+ // using namespace CryptoPP;
+ // AutoSeededRandomPool rng;
+
+ // //Read private key
+ // ByteQueue bytes;
+ // FileSource file(m_impl->transformName(keyURI, ".pri").string().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, KeyClass::SYMMETRIC))
+ // // throw Error("symmetric key doesn't exist");
+
+ // // try{
+ // // string keyBits;
+ // // string symKeyFileName = m_impl->transformName(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 uint8_t* data, size_t dataLength,
+ const Name& keyName, bool isSymmetric)
+{
+ BOOST_THROW_EXCEPTION(Error("SecTpmFile::encryptInTpm is not supported"));
+ // string keyURI = keyName.toUri();
+
+ // if (!isSymmetric)
+ // {
+ // if (!doesKeyExistInTpm(keyName, KeyClass::PUBLIC))
+ // throw Error("public key doesn't exist");
+ // try
+ // {
+ // using namespace CryptoPP;
+ // AutoSeededRandomPool rng;
+
+ // //Read private key
+ // ByteQueue bytes;
+ // FileSource file(m_impl->transformName(keyURI, ".pub").string().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, KeyClass::SYMMETRIC))
+ // // throw Error("symmetric key doesn't exist");
+
+ // // try{
+ // // string keyBits;
+ // // string symKeyFileName = m_impl->transformName(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, const KeyParams& params)
+{
+ BOOST_THROW_EXCEPTION(Error("SecTpmFile::generateSymmetricKeyInTpm is not supported"));
+ // string keyURI = keyName.toUri();
+
+ // if (doesKeyExistInTpm(keyName, KeyClass::SYMMETRIC))
+ // throw Error("symmetric key exists");
+
+ // string keyFileName = m_impl->maintainMapping(keyURI);
+ // string symKeyFileName = keyFileName + ".key";
+
+ // try{
+ // switch (keyType){
+ // case KeyType::AES:
+ // {
+ // using namespace CryptoPP;
+ // AutoSeededRandomPool rng;
+
+ // SecByteBlock key(0x00, keySize);
+ // rng.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 == KeyClass::PUBLIC) {
+ return boost::filesystem::exists(m_impl->transformName(keyURI, ".pub"));
+ }
+ if (keyClass == KeyClass::PRIVATE) {
+ return boost::filesystem::exists(m_impl->transformName(keyURI, ".pri"));
+ }
+ if (keyClass == KeyClass::SYMMETRIC) {
+ return boost::filesystem::exists(m_impl->transformName(keyURI, ".key"));
+ }
+ return false;
+}
+
+bool
+SecTpmFile::generateRandomBlock(uint8_t* res, size_t size)
+{
+ try {
+ CryptoPP::AutoSeededRandomPool rng;
+ rng.GenerateBlock(res, size);
+ return true;
+ }
+ catch (const CryptoPP::Exception& e) {
+ return false;
+ }
+}
+
+} // namespace v1
+} // namespace security
+} // namespace ndn