blob: 5cca7ca26fa7d310d5c892bceb83c5b5e2dfb31d [file] [log] [blame]
Jeff Thompson2747dc02013-10-04 19:11:34 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2/**
3 * Copyright (C) 2013 Regents of the University of California.
4 * @author: Yingdi Yu <yingdi@cs.ucla.edu>
5 * See COPYING for copyright and distribution information.
6 */
7
Jeff Thompsonf21e2242013-10-09 19:01:49 -07008// Only compile if ndn-cpp-config.h defines NDN_CPP_HAVE_OSX_SECURITY 1.
Jeff Thompson6e229042013-10-10 11:09:49 -07009#include <ndn-cpp/ndn-cpp-config.h>
Jeff Thompsonf21e2242013-10-09 19:01:49 -070010#if NDN_CPP_HAVE_OSX_SECURITY
Jeff Thompson2747dc02013-10-04 19:11:34 -070011
12#include <fstream>
13#include <sstream>
14#include <CoreFoundation/CoreFoundation.h>
15
16#include "../../util/logging.hpp"
17#include "osx-private-key-storage.hpp"
18#include "../security-exception.hpp"
19
20using namespace std;
21using namespace ndn::ptr_lib;
22
23INIT_LOGGER("ndn.OSXPrivateKeyStorage");
24
25namespace ndn
26{
27 OSXPrivateKeyStorage::OSXPrivateKeyStorage(const string & keychainName)
28 : keyChainName_("" == keychainName ? "NDN.keychain" : keychainName)
29 {
30 OSStatus res = SecKeychainCreate(keyChainName_.c_str(), //Keychain path
31 0, //Keychain password length
32 NULL, //Keychain password
33 true, //User prompt
34 NULL, //Initial access of Keychain
35 &keyChainRef_); //Keychain reference
36
37 if (res == errSecDuplicateKeychain)
38 res = SecKeychainOpen(keyChainName_.c_str(),
39 &keyChainRef_);
40
41 if (res != errSecSuccess){
42 _LOG_DEBUG("Fail to initialize keychain ref: " << res);
43 throw SecurityException("Fail to initialize keychain ref");
44 }
45
46 res = SecKeychainCopyDefault(&originalDefaultKeyChain_);
47
48 res = SecKeychainSetDefault(keyChainRef_);
49 if (res != errSecSuccess){
50 _LOG_DEBUG("Fail to set default keychain: " << res);
51 throw SecurityException("Fail to set default keychain");
52 }
53 }
54
55 OSXPrivateKeyStorage::~OSXPrivateKeyStorage(){
56 //TODO: implement
57 }
58
59 void
60 OSXPrivateKeyStorage::generateKeyPair(const Name & keyName, KeyType keyType, int keySize)
61 {
62
63 if(doesKeyExist(keyName, KEY_CLASS_PUBLIC)){
64 _LOG_DEBUG("keyName has existed");
65 throw SecurityException("keyName has existed");
66 }
67
68 string keyNameUri = toInternalKeyName(keyName, KEY_CLASS_PUBLIC);
69
70 SecKeyRef publicKey, privateKey;
71
72 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
73 keyNameUri.c_str(),
74 keyNameUri.size());
75
76 CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL,
77 3,
78 &kCFTypeDictionaryKeyCallBacks,
79 NULL);
80
81 CFDictionaryAddValue(attrDict, kSecAttrKeyType, getAsymKeyType(keyType));
82 CFDictionaryAddValue(attrDict, kSecAttrKeySizeInBits, CFNumberCreate(NULL, kCFNumberIntType, &keySize));
83 CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
84
85 OSStatus res = SecKeyGeneratePair((CFDictionaryRef)attrDict, &publicKey, &privateKey);
86
87 CFRelease(publicKey);
88 CFRelease(privateKey);
89
90 if (res != errSecSuccess){
91 _LOG_DEBUG("Fail to create a key pair: " << res);
92 throw SecurityException("Fail to create a key pair");
93 }
94 }
95
96 void
97 OSXPrivateKeyStorage::generateKey(const Name & keyName, KeyType keyType, int keySize)
98 {
99
100 if(doesKeyExist(keyName, KEY_CLASS_SYMMETRIC))
101 throw SecurityException("keyName has existed!");
102
103 string keyNameUri = toInternalKeyName(keyName, KEY_CLASS_SYMMETRIC);
104
105 CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
106 0,
107 &kCFTypeDictionaryKeyCallBacks,
108 &kCFTypeDictionaryValueCallBacks);
109
110 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
111 keyNameUri.c_str(),
112 keyNameUri.size());
113
114 CFDictionaryAddValue(attrDict, kSecAttrKeyType, getSymKeyType(keyType));
115 CFDictionaryAddValue(attrDict, kSecAttrKeySizeInBits, CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &keySize));
116 CFDictionaryAddValue(attrDict, kSecAttrIsPermanent, kCFBooleanTrue);
117 CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
118
119 CFErrorRef error = NULL;
120
121 SecKeyRef symmetricKey = SecKeyGenerateSymmetric(attrDict, &error);
122
123 if (error)
124 throw SecurityException("Fail to create a symmetric key");
125 }
126
127 shared_ptr<PublicKey> OSXPrivateKeyStorage::getPublicKey(const Name & keyName)
128 {
129 _LOG_TRACE("OSXPrivateKeyStorage::getPublickey");
130
131 SecKeychainItemRef publicKey = getKey(keyName, KEY_CLASS_PUBLIC);
132
133 CFDataRef exportedKey;
134
135 OSStatus res = SecItemExport(publicKey,
136 kSecFormatOpenSSL,
137 0,
138 NULL,
139 &exportedKey);
140
141 Blob blob(CFDataGetBytePtr(exportedKey), CFDataGetLength(exportedKey));
142
143 return PublicKey::fromDer(blob);
144 }
145
146 Blob OSXPrivateKeyStorage::sign(const uint8_t *data, size_t dataLength, const Name & keyName, DigestAlgorithm digestAlgo)
147 {
148 _LOG_TRACE("OSXPrivateKeyStorage::Sign");
149
150 CFDataRef dataRef = CFDataCreate(NULL,
151 reinterpret_cast<const unsigned char*>(data),
152 dataLength
153 );
154
155 SecKeyRef privateKey = (SecKeyRef)getKey(keyName, KEY_CLASS_PRIVATE);
156
157 CFErrorRef error;
158 SecTransformRef signer = SecSignTransformCreate((SecKeyRef)privateKey, &error);
159 if (error) throw SecurityException("Fail to create signer");
160
161 Boolean set_res = SecTransformSetAttribute(signer,
162 kSecTransformInputAttributeName,
163 dataRef,
164 &error);
165 if (error) throw SecurityException("Fail to configure input of signer");
166
167 set_res = SecTransformSetAttribute(signer,
168 kSecDigestTypeAttribute,
169 getDigestAlgorithm(digestAlgo),
170 &error);
171 if (error) throw SecurityException("Fail to configure digest algorithm of signer");
172
173 long digestSize = getDigestSize(digestAlgo);
174
175 set_res = SecTransformSetAttribute(signer,
176 kSecDigestLengthAttribute,
177 CFNumberCreate(NULL, kCFNumberLongType, &digestSize),
178 &error);
179 if (error) throw SecurityException("Fail to configure digest size of signer");
180
181 CFDataRef signature = (CFDataRef) SecTransformExecute(signer, &error);
182 if (error) {
183 CFShow(error);
184 throw SecurityException("Fail to sign data");
185 }
186
187 if (!signature) throw SecurityException("Signature is NULL!\n");
188
189 return Blob(CFDataGetBytePtr(signature), CFDataGetLength(signature));
190 }
191
192 Blob OSXPrivateKeyStorage::decrypt(const Name & keyName, const uint8_t* data, size_t dataLength, bool sym)
193 {
194 _LOG_TRACE("OSXPrivateKeyStorage::Decrypt");
195
196 KeyClass keyClass;
197 if(sym)
198 keyClass = KEY_CLASS_SYMMETRIC;
199 else
200 keyClass = KEY_CLASS_PRIVATE;
201
202 CFDataRef dataRef = CFDataCreate(NULL,
203 reinterpret_cast<const unsigned char*>(data),
204 dataLength
205 );
206
207 // _LOG_DEBUG("CreateData");
208
209 SecKeyRef decryptKey = (SecKeyRef)getKey(keyName, keyClass);
210
211 // _LOG_DEBUG("GetKey");
212
213 CFErrorRef error;
214 SecTransformRef decrypt = SecDecryptTransformCreate(decryptKey, &error);
215 if (error) throw SecurityException("Fail to create decrypt");
216
217 Boolean set_res = SecTransformSetAttribute(decrypt,
218 kSecTransformInputAttributeName,
219 dataRef,
220 &error);
221 if (error) throw SecurityException("Fail to configure decrypt");
222
223 CFDataRef output = (CFDataRef) SecTransformExecute(decrypt, &error);
224 if (error)
225 {
226 CFShow(error);
227 throw SecurityException("Fail to decrypt data");
228 }
229 if (!output) throw SecurityException("Output is NULL!\n");
230
231 return Blob(CFDataGetBytePtr(output), CFDataGetLength(output));
232 }
233
234 bool OSXPrivateKeyStorage::setACL(const Name & keyName, KeyClass keyClass, int acl, const string & appPath)
235 {
236 SecKeychainItemRef privateKey = getKey(keyName, keyClass);
237
238 SecAccessRef accRef;
239 OSStatus acc_res = SecKeychainItemCopyAccess(privateKey, &accRef);
240
241 CFArrayRef signACL = SecAccessCopyMatchingACLList(accRef,
242 kSecACLAuthorizationSign);
243
244 SecACLRef aclRef = (SecACLRef) CFArrayGetValueAtIndex(signACL, 0);
245
246 CFArrayRef appList;
247 CFStringRef description;
248 SecKeychainPromptSelector promptSelector;
249 OSStatus acl_res = SecACLCopyContents(aclRef,
250 &appList,
251 &description,
252 &promptSelector);
253
254 CFMutableArrayRef newAppList = CFArrayCreateMutableCopy(NULL,
255 0,
256 appList);
257
258 SecTrustedApplicationRef trustedApp;
259 acl_res = SecTrustedApplicationCreateFromPath(appPath.c_str(),
260 &trustedApp);
261
262 CFArrayAppendValue(newAppList, trustedApp);
263
264
265 CFArrayRef authList = SecACLCopyAuthorizations(aclRef);
266
267 acl_res = SecACLRemove(aclRef);
268
269 SecACLRef newACL;
270 acl_res = SecACLCreateWithSimpleContents(accRef,
271 newAppList,
272 description,
273 promptSelector,
274 &newACL);
275
276 acl_res = SecACLUpdateAuthorizations(newACL, authList);
277
278 acc_res = SecKeychainItemSetAccess(privateKey, accRef);
279
280 return true;
281 }
282
283 bool OSXPrivateKeyStorage::verifyData(const Name & keyName, const Blob & pData, const Blob & pSig, DigestAlgorithm digestAlgo)
284 {
285 _LOG_TRACE("OSXPrivateKeyStorage::Verify");
286
287 CFDataRef dataRef = CFDataCreate(NULL,
288 reinterpret_cast<const unsigned char*>(pData.buf()),
289 pData.size());
290
291 CFDataRef sigRef = CFDataCreate(NULL,
292 reinterpret_cast<const unsigned char*>(pSig.buf()),
293 pSig.size());
294
295 SecKeyRef publicKey = (SecKeyRef)getKey(keyName, KEY_CLASS_PUBLIC);
296
297 CFErrorRef error;
298 SecTransformRef verifier = SecVerifyTransformCreate(publicKey, sigRef, &error);
299 if (error) throw SecurityException("Fail to create verifier");
300
301 Boolean set_res = SecTransformSetAttribute(verifier,
302 kSecTransformInputAttributeName,
303 dataRef,
304 &error);
305 if (error) throw SecurityException("Fail to configure input of verifier");
306
307 set_res = SecTransformSetAttribute(verifier,
308 kSecDigestTypeAttribute,
309 getDigestAlgorithm(digestAlgo),
310 &error);
311 if (error) throw SecurityException("Fail to configure digest algorithm of verifier");
312
313 long digestSize = getDigestSize(digestAlgo);
314 set_res = SecTransformSetAttribute(verifier,
315 kSecDigestLengthAttribute,
316 CFNumberCreate(NULL, kCFNumberLongType, &digestSize),
317 &error);
318 if (error) throw SecurityException("Fail to configure digest size of verifier");
319
320 CFBooleanRef result = (CFBooleanRef) SecTransformExecute(verifier, &error);
321 if (error) throw SecurityException("Fail to verify data");
322
323 if (result == kCFBooleanTrue)
324 return true;
325 else
326 return false;
327 }
328
329 Blob OSXPrivateKeyStorage::encrypt(const Name & keyName, const uint8_t* data, size_t dataLength, bool sym)
330 {
331 _LOG_TRACE("OSXPrivateKeyStorage::Encrypt");
332
333 KeyClass keyClass;
334 if(sym)
335 keyClass = KEY_CLASS_SYMMETRIC;
336 else
337 keyClass = KEY_CLASS_PUBLIC;
338
339 CFDataRef dataRef = CFDataCreate(NULL,
340 reinterpret_cast<const unsigned char*>(data),
341 dataLength
342 );
343
344 SecKeyRef encryptKey = (SecKeyRef)getKey(keyName, keyClass);
345
346 CFErrorRef error;
347 SecTransformRef encrypt = SecEncryptTransformCreate(encryptKey, &error);
348 if (error) throw SecurityException("Fail to create encrypt");
349
350 Boolean set_res = SecTransformSetAttribute(encrypt,
351 kSecTransformInputAttributeName,
352 dataRef,
353 &error);
354 if (error) throw SecurityException("Fail to configure encrypt");
355
356 CFDataRef output = (CFDataRef) SecTransformExecute(encrypt, &error);
357 if (error) throw SecurityException("Fail to encrypt data");
358
359 if (!output) throw SecurityException("Output is NULL!\n");
360
361 return Blob(CFDataGetBytePtr(output), CFDataGetLength(output));
362 }
363
364 bool OSXPrivateKeyStorage::doesKeyExist(const Name & keyName, KeyClass keyClass)
365 {
366 _LOG_TRACE("OSXPrivateKeyStorage::doesKeyExist");
367
368 string keyNameUri = toInternalKeyName(keyName, keyClass);
369
370 CFStringRef keyLabel = CFStringCreateWithCString(NULL,
371 keyNameUri.c_str(),
372 keyNameUri.size());
373
374 CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL,
375 3,
376 &kCFTypeDictionaryKeyCallBacks,
377 NULL);
378
379 CFDictionaryAddValue(attrDict, kSecAttrKeyClass, getKeyClass(keyClass));
380 CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
381 CFDictionaryAddValue(attrDict, kSecReturnRef, kCFBooleanTrue);
382
383 SecKeychainItemRef itemRef;
384 OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict, (CFTypeRef*)&itemRef);
385
386 if(res == errSecItemNotFound)
387 return true;
388 else
389 return false;
390
391 }
392
393 SecKeychainItemRef OSXPrivateKeyStorage::getKey(const Name & keyName, KeyClass keyClass)
394 {
395 string keyNameUri = toInternalKeyName(keyName, keyClass);
396
397 CFStringRef keyLabel = CFStringCreateWithCString (NULL,
398 keyNameUri.c_str(),
399 keyNameUri.size());
400
401 CFMutableDictionaryRef attrDict = CFDictionaryCreateMutable(NULL,
402 5,
403 &kCFTypeDictionaryKeyCallBacks,
404 NULL);
405
406 CFDictionaryAddValue(attrDict, kSecClass, kSecClassKey);
407 CFDictionaryAddValue(attrDict, kSecAttrLabel, keyLabel);
408 CFDictionaryAddValue(attrDict, kSecAttrKeyClass, getKeyClass(keyClass));
409 CFDictionaryAddValue(attrDict, kSecReturnRef, kCFBooleanTrue);
410
411 SecKeychainItemRef keyItem;
412
413 OSStatus res = SecItemCopyMatching((CFDictionaryRef) attrDict, (CFTypeRef*)&keyItem);
414
415 if(res != errSecSuccess){
416 _LOG_DEBUG("Fail to find the key!");
417 return NULL;
418 }
419 else
420 return keyItem;
421 }
422
423 string OSXPrivateKeyStorage::toInternalKeyName(const Name & keyName, KeyClass keyClass)
424 {
425 string keyUri = keyName.toUri();
426
427 if(KEY_CLASS_SYMMETRIC == keyClass)
428 return keyUri + "/symmetric";
429 else
430 return keyUri;
431 }
432
433 const CFTypeRef OSXPrivateKeyStorage::getAsymKeyType(KeyType keyType)
434 {
435 switch(keyType){
436 case KEY_TYPE_RSA:
437 return kSecAttrKeyTypeRSA;
438 default:
439 _LOG_DEBUG("Unrecognized key type!")
440 return NULL;
441 }
442 }
443
444 const CFTypeRef OSXPrivateKeyStorage::getSymKeyType(KeyType keyType)
445 {
446 switch(keyType){
447 case KEY_TYPE_AES:
448 return kSecAttrKeyTypeAES;
449 default:
450 _LOG_DEBUG("Unrecognized key type!")
451 return NULL;
452 }
453 }
454
455 const CFTypeRef OSXPrivateKeyStorage::getKeyClass(KeyClass keyClass)
456 {
457 switch(keyClass){
458 case KEY_CLASS_PRIVATE:
459 return kSecAttrKeyClassPrivate;
460 case KEY_CLASS_PUBLIC:
461 return kSecAttrKeyClassPublic;
462 case KEY_CLASS_SYMMETRIC:
463 return kSecAttrKeyClassSymmetric;
464 default:
465 _LOG_DEBUG("Unrecognized key class!");
466 return NULL;
467 }
468 }
469
470 SecExternalFormat OSXPrivateKeyStorage::getFormat(KeyFormat format)
471 {
472 switch(format){
473 case KEY_FORMAT_PUBLIC_OPENSSL:
474 return kSecFormatOpenSSL;
475 default:
476 _LOG_DEBUG("Unrecognized output format!");
477 return 0;
478 }
479 }
480
481 const CFStringRef OSXPrivateKeyStorage::getDigestAlgorithm(DigestAlgorithm digestAlgo)
482 {
483 switch(digestAlgo){
484 // case DIGEST_MD2:
485 // return kSecDigestMD2;
486 // case DIGEST_MD5:
487 // return kSecDigestMD5;
488 // case DIGEST_SHA1:
489 // return kSecDigestSHA1;
490 case DIGEST_ALGORITHM_SHA256:
491 return kSecDigestSHA2;
492 default:
493 _LOG_DEBUG("Unrecognized digest algorithm!");
494 return NULL;
495 }
496 }
497
498 long OSXPrivateKeyStorage::getDigestSize(DigestAlgorithm digestAlgo)
499 {
500 switch(digestAlgo){
501 case DIGEST_ALGORITHM_SHA256:
502 return 256;
503 // case DIGEST_SHA1:
504 // case DIGEST_MD2:
505 // case DIGEST_MD5:
506 // return 0;
507 default:
508 _LOG_DEBUG("Unrecognized digest algorithm! Unknown digest size");
509 return -1;
510 }
511 }
512
513}
514
Jeff Thompsonf21e2242013-10-09 19:01:49 -0700515#endif NDN_CPP_HAVE_OSX_SECURITY