sec-tpm.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
24 #include "sec-tpm.hpp"
25 
26 #include "../../encoding/oid.hpp"
27 #include "../../encoding/buffer-stream.hpp"
28 #include "cryptopp.hpp"
29 #include <unistd.h>
30 
31 namespace ndn {
32 namespace security {
33 namespace v1 {
34 
35 SecTpm::SecTpm(const std::string& location)
36  : m_location(location)
37 {
38 }
39 
41 {
42 }
43 
44 std::string
46 {
47  return this->getScheme() + ":" + m_location;
48 }
49 
51 SecTpm::exportPrivateKeyPkcs5FromTpm(const Name& keyName, const std::string& passwordStr)
52 {
53  using namespace CryptoPP;
54 
55  uint8_t salt[8] = {0};
56  uint8_t iv[8] = {0};
57 
58  // derive key
59  if (!generateRandomBlock(salt, 8) || !generateRandomBlock(iv, 8))
60  BOOST_THROW_EXCEPTION(Error("Cannot generate salt or iv"));
61 
62  uint32_t iterationCount = 2048;
63 
64  PKCS5_PBKDF2_HMAC<SHA1> keyGenerator;
65  size_t derivedLen = 24; // For DES-EDE3-CBC-PAD
66  byte derived[24] = {0};
67  byte purpose = 0;
68 
69  try {
70  keyGenerator.DeriveKey(derived, derivedLen, purpose,
71  reinterpret_cast<const byte*>(passwordStr.c_str()), passwordStr.size(),
72  salt, 8, iterationCount);
73  }
74  catch (const CryptoPP::Exception& e) {
75  BOOST_THROW_EXCEPTION(Error("Cannot derived the encryption key"));
76  }
77 
78  // encrypt
79  CBC_Mode< DES_EDE3 >::Encryption e;
80  e.SetKeyWithIV(derived, derivedLen, iv);
81 
82  ConstBufferPtr pkcs8PrivateKey = exportPrivateKeyPkcs8FromTpm(keyName);
83 
84  if (pkcs8PrivateKey == nullptr)
85  BOOST_THROW_EXCEPTION(Error("Cannot export the private key, #1"));
86 
87  OBufferStream encryptedOs;
88  try {
89  StringSource stringSource(pkcs8PrivateKey->buf(), pkcs8PrivateKey->size(), true,
90  new StreamTransformationFilter(e, new FileSink(encryptedOs)));
91  }
92  catch (const CryptoPP::Exception& e) {
93  BOOST_THROW_EXCEPTION(Error("Cannot export the private key, #2"));
94  }
95 
96  // encode
97  Oid pbes2Id("1.2.840.113549.1.5.13");
98  Oid pbkdf2Id("1.2.840.113549.1.5.12");
99  Oid pbes2encsId("1.2.840.113549.3.7");
100 
101  OBufferStream pkcs8Os;
102  try {
103  FileSink sink(pkcs8Os);
104 
105  // EncryptedPrivateKeyInfo ::= SEQUENCE {
106  // encryptionAlgorithm EncryptionAlgorithmIdentifier,
107  // encryptedData OCTET STRING }
108  DERSequenceEncoder encryptedPrivateKeyInfo(sink);
109  {
110  // EncryptionAlgorithmIdentifier ::= SEQUENCE {
111  // algorithm OBJECT IDENTIFIER {{PBES2-id}},
112  // parameters SEQUENCE {{PBES2-params}} }
113  DERSequenceEncoder encryptionAlgorithm(encryptedPrivateKeyInfo);
114  {
115  pbes2Id.encode(encryptionAlgorithm);
116  // PBES2-params ::= SEQUENCE {
117  // keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
118  // encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} }
119  DERSequenceEncoder pbes2Params(encryptionAlgorithm);
120  {
121  // AlgorithmIdentifier ::= SEQUENCE {
122  // algorithm OBJECT IDENTIFIER {{PBKDF2-id}},
123  // parameters SEQUENCE {{PBKDF2-params}} }
124  DERSequenceEncoder pbes2KDFs(pbes2Params);
125  {
126  pbkdf2Id.encode(pbes2KDFs);
127  // AlgorithmIdentifier ::= SEQUENCE {
128  // salt OCTET STRING,
129  // iterationCount INTEGER (1..MAX),
130  // keyLength INTEGER (1..MAX) OPTIONAL,
131  // prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1 }
132  DERSequenceEncoder pbkdf2Params(pbes2KDFs);
133  {
134  DEREncodeOctetString(pbkdf2Params, salt, 8);
135  DEREncodeUnsigned<uint32_t>(pbkdf2Params, iterationCount, INTEGER);
136  }
137  pbkdf2Params.MessageEnd();
138  }
139  pbes2KDFs.MessageEnd();
140 
141  // AlgorithmIdentifier ::= SEQUENCE {
142  // algorithm OBJECT IDENTIFIER {{DES-EDE3-CBC-PAD}},
143  // parameters OCTET STRING} {{iv}} }
144  DERSequenceEncoder pbes2Encs(pbes2Params);
145  {
146  pbes2encsId.encode(pbes2Encs);
147  DEREncodeOctetString(pbes2Encs, iv, 8);
148  }
149  pbes2Encs.MessageEnd();
150  }
151  pbes2Params.MessageEnd();
152  }
153  encryptionAlgorithm.MessageEnd();
154 
155  DEREncodeOctetString(encryptedPrivateKeyInfo,
156  encryptedOs.buf()->buf(), encryptedOs.buf()->size());
157  }
158  encryptedPrivateKeyInfo.MessageEnd();
159 
160  return pkcs8Os.buf();
161  }
162  catch (const CryptoPP::Exception& e) {
163  BOOST_THROW_EXCEPTION(Error("Cannot export the private key, #3"));
164  }
165 }
166 
167 bool
169  const uint8_t* buf, size_t size,
170  const std::string& passwordStr)
171 {
172  using namespace CryptoPP;
173 
174  Oid pbes2Id;
175  Oid pbkdf2Id;
176  SecByteBlock saltBlock;
177  uint32_t iterationCount;
178  Oid pbes2encsId;
179  SecByteBlock ivBlock;
180  SecByteBlock encryptedDataBlock;
181 
182  try {
183  // decode some decoding processes are not necessary for now,
184  // because we assume only one encryption scheme.
185  StringSource source(buf, size, true);
186 
187  // EncryptedPrivateKeyInfo ::= SEQUENCE {
188  // encryptionAlgorithm EncryptionAlgorithmIdentifier,
189  // encryptedData OCTET STRING }
190  BERSequenceDecoder encryptedPrivateKeyInfo(source);
191  {
192  // EncryptionAlgorithmIdentifier ::= SEQUENCE {
193  // algorithm OBJECT IDENTIFIER {{PBES2-id}},
194  // parameters SEQUENCE {{PBES2-params}} }
195  BERSequenceDecoder encryptionAlgorithm(encryptedPrivateKeyInfo);
196  {
197  pbes2Id.decode(encryptionAlgorithm);
198  // PBES2-params ::= SEQUENCE {
199  // keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
200  // encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} }
201  BERSequenceDecoder pbes2Params(encryptionAlgorithm);
202  {
203  // AlgorithmIdentifier ::= SEQUENCE {
204  // algorithm OBJECT IDENTIFIER {{PBKDF2-id}},
205  // parameters SEQUENCE {{PBKDF2-params}} }
206  BERSequenceDecoder pbes2KDFs(pbes2Params);
207  {
208  pbkdf2Id.decode(pbes2KDFs);
209  // AlgorithmIdentifier ::= SEQUENCE {
210  // salt OCTET STRING,
211  // iterationCount INTEGER (1..MAX),
212  // keyLength INTEGER (1..MAX) OPTIONAL,
213  // prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1 }
214  BERSequenceDecoder pbkdf2Params(pbes2KDFs);
215  {
216  BERDecodeOctetString(pbkdf2Params, saltBlock);
217  BERDecodeUnsigned<uint32_t>(pbkdf2Params, iterationCount, INTEGER);
218  }
219  pbkdf2Params.MessageEnd();
220  }
221  pbes2KDFs.MessageEnd();
222 
223  // AlgorithmIdentifier ::= SEQUENCE {
224  // algorithm OBJECT IDENTIFIER {{DES-EDE3-CBC-PAD}},
225  // parameters OCTET STRING} {{iv}} }
226  BERSequenceDecoder pbes2Encs(pbes2Params);
227  {
228  pbes2encsId.decode(pbes2Encs);
229  BERDecodeOctetString(pbes2Encs, ivBlock);
230  }
231  pbes2Encs.MessageEnd();
232  }
233  pbes2Params.MessageEnd();
234  }
235  encryptionAlgorithm.MessageEnd();
236 
237  BERDecodeOctetString(encryptedPrivateKeyInfo, encryptedDataBlock);
238  }
239  encryptedPrivateKeyInfo.MessageEnd();
240  }
241  catch (const CryptoPP::Exception& e) {
242  return false;
243  }
244 
245  PKCS5_PBKDF2_HMAC<SHA1> keyGenerator;
246  size_t derivedLen = 24; //For DES-EDE3-CBC-PAD
247  byte derived[24] = {0};
248  byte purpose = 0;
249 
250  try {
251  keyGenerator.DeriveKey(derived, derivedLen,
252  purpose,
253  reinterpret_cast<const byte*>(passwordStr.c_str()), passwordStr.size(),
254  saltBlock.BytePtr(), saltBlock.size(),
255  iterationCount);
256  }
257  catch (const CryptoPP::Exception& e) {
258  return false;
259  }
260 
261  //decrypt
262  CBC_Mode< DES_EDE3 >::Decryption d;
263  d.SetKeyWithIV(derived, derivedLen, ivBlock.BytePtr());
264 
265  OBufferStream privateKeyOs;
266  try {
267  StringSource encryptedSource(encryptedDataBlock.BytePtr(), encryptedDataBlock.size(), true,
268  new StreamTransformationFilter(d, new FileSink(privateKeyOs)));
269  }
270  catch (const CryptoPP::Exception& e) {
271  return false;
272  }
273 
274  if (!importPrivateKeyPkcs8IntoTpm(keyName,
275  privateKeyOs.buf()->buf(), privateKeyOs.buf()->size()))
276  return false;
277 
278  // determine key type
279  StringSource privateKeySource(privateKeyOs.buf()->buf(), privateKeyOs.buf()->size(), true);
280 
281  KeyType publicKeyType = KeyType::NONE;
282  SecByteBlock rawKeyBits;
283  // PrivateKeyInfo ::= SEQUENCE {
284  // INTEGER,
285  // SEQUENCE,
286  // OCTECT STRING}
287  BERSequenceDecoder privateKeyInfo(privateKeySource);
288  {
289  uint32_t versionNum;
290  BERDecodeUnsigned<uint32_t>(privateKeyInfo, versionNum, INTEGER);
291  BERSequenceDecoder sequenceDecoder(privateKeyInfo);
292  {
293  Oid keyTypeOid;
294  keyTypeOid.decode(sequenceDecoder);
295  if (keyTypeOid == oid::RSA)
296  publicKeyType = KeyType::RSA;
297  else if (keyTypeOid == oid::ECDSA)
298  publicKeyType = KeyType::EC;
299  else
300  return false; // Unsupported key type;
301  }
302  }
303 
304 
305  // derive public key
306  OBufferStream publicKeyOs;
307 
308  try {
309  switch (publicKeyType) {
310  case KeyType::RSA: {
311  RSA::PrivateKey privateKey;
312  privateKey.Load(StringStore(privateKeyOs.buf()->buf(), privateKeyOs.buf()->size()).Ref());
313  RSAFunction publicKey(privateKey);
314 
315  FileSink publicKeySink(publicKeyOs);
316  publicKey.DEREncode(publicKeySink);
317  publicKeySink.MessageEnd();
318  break;
319  }
320 
321  case KeyType::EC: {
322  ECDSA<ECP, SHA256>::PrivateKey privateKey;
323  privateKey.Load(StringStore(privateKeyOs.buf()->buf(), privateKeyOs.buf()->size()).Ref());
324 
325  ECDSA<ECP, SHA256>::PublicKey publicKey;
326  privateKey.MakePublicKey(publicKey);
327  publicKey.AccessGroupParameters().SetEncodeAsOID(true);
328 
329  FileSink publicKeySink(publicKeyOs);
330  publicKey.DEREncode(publicKeySink);
331  publicKeySink.MessageEnd();
332  break;
333  }
334 
335  default:
336  return false;
337  }
338  }
339  catch (const CryptoPP::Exception& e) {
340  return false;
341  }
342 
343  if (!importPublicKeyPkcs1IntoTpm(keyName, publicKeyOs.buf()->buf(), publicKeyOs.buf()->size()))
344  return false;
345 
346  return true;
347 }
348 
349 bool
350 SecTpm::getImpExpPassWord(std::string& password, const std::string& prompt)
351 {
352  bool isInitialized = false;
353 
354 #ifdef NDN_CXX_HAVE_GETPASS
355  char* pw0 = nullptr;
356 
357  pw0 = getpass(prompt.c_str());
358  if (pw0 == nullptr)
359  return false;
360  std::string password1 = pw0;
361  memset(pw0, 0, strlen(pw0));
362 
363  pw0 = getpass("Confirm:");
364  if (pw0 == nullptr) {
365  std::fill(password1.begin(), password1.end(), 0);
366  return false;
367  }
368 
369  if (password1.compare(pw0) == 0) {
370  isInitialized = true;
371  password.swap(password1);
372  }
373 
374  std::fill(password1.begin(), password1.end(), 0);
375  memset(pw0, 0, strlen(pw0));
376 
377  if (password.empty())
378  return false;
379 
380 #endif // NDN_CXX_HAVE_GETPASS
381 
382  return isInitialized;
383 }
384 
385 } // namespace v1
386 } // namespace security
387 } // namespace ndn
void decode(CryptoPP::BufferedTransformation &in)
Definition: oid.cpp:135
const Oid ECDSA("1.2.840.10045.2.1")
Definition: oid.hpp:102
Copyright (c) 2013-2016 Regents of the University of California.
Definition: common.hpp:74
Copyright (c) 2013-2016 Regents of the University of California.
Definition: oid.hpp:29
void encode(CryptoPP::BufferedTransformation &out) const
Definition: oid.cpp:118
virtual ConstBufferPtr exportPrivateKeyPkcs8FromTpm(const Name &keyName)=0
Export a private key in PKCS#8 format.
virtual std::string getScheme()=0
std::string getTpmLocator()
Definition: sec-tpm.cpp:45
virtual bool generateRandomBlock(uint8_t *res, size_t size)=0
Generate a random block.
bool importPrivateKeyPkcs5IntoTpm(const Name &keyName, const uint8_t *buffer, size_t bufferSize, const std::string &password)
Import a private key in PKCS#5 formatted buffer of size bufferSize.
Definition: sec-tpm.cpp:168
const Oid RSA("1.2.840.113549.1.1.1")
Definition: oid.hpp:101
Definition: oid.hpp:35
Name abstraction to represent an absolute name.
Definition: name.hpp:46
virtual bool importPublicKeyPkcs1IntoTpm(const Name &keyName, const uint8_t *buffer, size_t bufferSize)=0
Import a public key in PKCS#1 formatted buffer of size bufferSize.
shared_ptr< Buffer > buf()
Flush written data to the stream and return shared pointer to the underlying buffer.
SecTpm(const std::string &location)
Definition: sec-tpm.cpp:35
implements an output stream that constructs ndn::Buffer
ConstBufferPtr exportPrivateKeyPkcs5FromTpm(const Name &keyName, const std::string &password)
Export a private key in PKCS#5 format.
Definition: sec-tpm.cpp:51
shared_ptr< const Buffer > ConstBufferPtr
Definition: buffer.hpp:33
virtual bool getImpExpPassWord(std::string &password, const std::string &prompt)
Get import/export password.
Definition: sec-tpm.cpp:350
virtual bool importPrivateKeyPkcs8IntoTpm(const Name &keyName, const uint8_t *buffer, size_t bufferSize)=0
Import a private key from PKCS#8 formatted buffer of size bufferSize.