blob: 931d8fdb57d5efd796b2454539ac825e48ca895b [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2013-2016 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 "v1/cryptopp.hpp"
#include <sys/types.h>
#include <sys/stat.h>
#include <algorithm>
namespace ndn {
namespace security {
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<v1::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<v1::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<v1::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 security
} // namespace ndn