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