blob: aa3347be4653ff838e6b4dccad9dec3eac00959c [file] [log] [blame]
Alexander Afanasyevc169a812014-05-20 20:37:29 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Jeff Thompson2747dc02013-10-04 19:11:34 -07002/**
Alexander Afanasyevc169a812014-05-20 20:37:29 -04003 * Copyright (c) 2013-2014 Regents of the University of California.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07006 *
Alexander Afanasyevc169a812014-05-20 20:37:29 -04007 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -070020 *
21 * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
Jeff Thompson2747dc02013-10-04 19:11:34 -070022 */
23
Alexander Afanasyeve2dcdfd2014-02-07 15:53:28 -080024#include "common.hpp"
25
Alexander Afanasyev19508852014-01-29 01:01:51 -080026#include "sec-tpm-osx.hpp"
Yingdi Yuf56c68f2014-04-24 21:50:13 -070027#include "public-key.hpp"
Alexander Afanasyev258ec2b2014-05-14 16:15:37 -070028#include "../encoding/oid.hpp"
29#include "../encoding/buffer-stream.hpp"
Junxiao Shi482ccc52014-03-31 13:05:24 -070030#include "cryptopp.hpp"
Jeff Thompson2747dc02013-10-04 19:11:34 -070031
Yingdi Yu2b2b4792014-02-04 16:27:07 -080032#include <pwd.h>
33#include <unistd.h>
34#include <stdlib.h>
35#include <string.h>
Jeff Thompson2747dc02013-10-04 19:11:34 -070036
Alexander Afanasyev04b22a92014-01-05 22:40:17 -080037#include <CoreFoundation/CoreFoundation.h>
38#include <Security/Security.h>
Yingdi Yu4b752752014-02-18 12:24:03 -080039#include <Security/SecRandom.h>
Alexander Afanasyev04b22a92014-01-05 22:40:17 -080040#include <CoreServices/CoreServices.h>
Jeff Thompson2747dc02013-10-04 19:11:34 -070041
Alexander Afanasyev59d67a52014-04-03 16:09:31 -070042#include <Security/SecDigestTransform.h>
43
Jeff Thompson2747dc02013-10-04 19:11:34 -070044using namespace std;
Jeff Thompson2747dc02013-10-04 19:11:34 -070045
Yingdi Yufc40d872014-02-18 12:56:04 -080046namespace ndn {
47
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -070048/**
49 * @brief Helper class to wrap CoreFoundation object pointers
50 *
51 * The class is similar in spirit to shared_ptr, but uses CoreFoundation
52 * mechanisms to retain/release object.
53 *
54 * Original implementation by Christopher Hunt and it was borrowed from
55 * http://www.cocoabuilder.com/archive/cocoa/130776-auto-cfrelease-and.html
56 */
57template<class T>
58class CFReleaser
59{
60public:
61 //////////////////////////////
62 // Construction/destruction //
63
64 CFReleaser()
65 : m_typeRef(0)
66 {
67 }
68
69 CFReleaser(const T& typeRef)
70 : m_typeRef(typeRef)
71 {
72 }
73
74 CFReleaser(const CFReleaser& inReleaser)
75 : m_typeRef(0)
76 {
77 retain(inReleaser.m_typeRef);
78 }
79
80 CFReleaser&
81 operator=(const T& typeRef)
82 {
83 if (typeRef != m_typeRef) {
84 release();
85 m_typeRef = typeRef;
86 }
87 return *this;
88 }
89
90 CFReleaser&
91 operator=(const CFReleaser& inReleaser)
92 {
93 retain(inReleaser.m_typeRef);
94 return *this;
95 }
96
97 ~CFReleaser()
98 {
99 release();
100 }
101
102 ////////////
103 // Access //
104
105 // operator const T&() const
106 // {
107 // return m_typeRef;
108 // }
109
110 // operator T&()
111 // {
112 // return m_typeRef;
113 // }
114
115 const T&
116 get() const
117 {
118 return m_typeRef;
119 }
120
121 T&
122 get()
123 {
124 return m_typeRef;
125 }
126
127 ///////////////////
128 // Miscellaneous //
129
130 void
131 retain(const T& typeRef)
132 {
133 if (typeRef != 0) {
134 CFRetain(typeRef);
135 }
136 release();
137 m_typeRef = typeRef;
138 }
139
140 void release()
141 {
142 if (m_typeRef != 0) {
143 CFRelease(m_typeRef);
144 m_typeRef = 0;
145 }
146 };
147
148private:
149 T m_typeRef;
150};
151
152
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700153class SecTpmOsx::Impl
154{
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800155public:
156 Impl()
Yingdi Yube4150e2014-02-18 13:02:46 -0800157 : m_passwordSet(false)
158 , m_inTerminal(false)
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700159 {
160 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700161
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800162 /**
163 * @brief Convert NDN name of a key to internal name of the key.
164 *
Yingdi Yufc40d872014-02-18 12:56:04 -0800165 * @param keyName
166 * @param keyClass
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800167 * @return the internal key name
168 */
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700169 std::string
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700170 toInternalKeyName(const Name& keyName, KeyClass keyClass);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700171
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800172 /**
173 * @brief Get key.
Yingdi Yufc40d872014-02-18 12:56:04 -0800174 *
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700175 * @param keyName
Yingdi Yufc40d872014-02-18 12:56:04 -0800176 * @param keyClass
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800177 * @returns pointer to the key
178 */
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700179 CFReleaser<SecKeychainItemRef>
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700180 getKey(const Name& keyName, KeyClass keyClass);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700181
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800182 /**
Yingdi Yufc40d872014-02-18 12:56:04 -0800183 * @brief Convert keyType to MAC OS symmetric key key type
184 *
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800185 * @param keyType
186 * @returns MAC OS key type
187 */
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700188 const CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800189 getSymKeyType(KeyType keyType);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700190
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800191 /**
Yingdi Yufc40d872014-02-18 12:56:04 -0800192 * @brief Convert keyType to MAC OS asymmetirc key type
193 *
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800194 * @param keyType
195 * @returns MAC OS key type
196 */
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700197 const CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800198 getAsymKeyType(KeyType keyType);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700199
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800200 /**
Yingdi Yufc40d872014-02-18 12:56:04 -0800201 * @brief Convert keyClass to MAC OS key class
202 *
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800203 * @param keyClass
204 * @returns MAC OS key class
205 */
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700206 const CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800207 getKeyClass(KeyClass keyClass);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700208
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800209 /**
Yingdi Yufc40d872014-02-18 12:56:04 -0800210 * @brief Convert digestAlgo to MAC OS algorithm id
211 *
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800212 * @param digestAlgo
213 * @returns MAC OS algorithm id
214 */
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700215 const CFStringRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800216 getDigestAlgorithm(DigestAlgorithm digestAlgo);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700217
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800218 /**
Yingdi Yufc40d872014-02-18 12:56:04 -0800219 * @brief Get the digest size of the corresponding algorithm
220 *
221 * @param digestAlgo
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800222 * @return digest size
223 */
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700224 long
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800225 getDigestSize(DigestAlgorithm digestAlgo);
226
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800227 ///////////////////////////////////////////////
228 // everything here is public, including data //
229 ///////////////////////////////////////////////
230public:
231 SecKeychainRef m_keyChainRef;
Yingdi Yube4150e2014-02-18 13:02:46 -0800232 bool m_passwordSet;
233 string m_password;
234 bool m_inTerminal;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800235};
236
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800237SecTpmOsx::SecTpmOsx()
238 : m_impl(new Impl)
239{
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700240 if (m_impl->m_inTerminal)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700241 SecKeychainSetUserInteractionAllowed(false);
Yingdi Yube4150e2014-02-18 13:02:46 -0800242 else
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700243 SecKeychainSetUserInteractionAllowed(true);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800244
Yingdi Yube4150e2014-02-18 13:02:46 -0800245 OSStatus res = SecKeychainCopyDefault(&m_impl->m_keyChainRef);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700246
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800247 if (res == errSecNoDefaultKeychain) //If no default key chain, create one.
Yingdi Yube4150e2014-02-18 13:02:46 -0800248 throw Error("No default keychain, create one first!");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800249}
250
251SecTpmOsx::~SecTpmOsx(){
252 //TODO: implement
253}
254
Yingdi Yube4150e2014-02-18 13:02:46 -0800255void
256SecTpmOsx::setTpmPassword(const uint8_t* password, size_t passwordLength)
257{
258 m_impl->m_passwordSet = true;
259 memset(const_cast<char*>(m_impl->m_password.c_str()), 0, m_impl->m_password.size());
260 m_impl->m_password.clear();
261 m_impl->m_password.append(reinterpret_cast<const char*>(password), passwordLength);
262}
263
264void
265SecTpmOsx::resetTpmPassword()
266{
267 m_impl->m_passwordSet = false;
268 memset(const_cast<char*>(m_impl->m_password.c_str()), 0, m_impl->m_password.size());
269 m_impl->m_password.clear();
270}
271
272void
273SecTpmOsx::setInTerminal(bool inTerminal)
274{
275 m_impl->m_inTerminal = inTerminal;
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700276 if (inTerminal)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700277 SecKeychainSetUserInteractionAllowed(false);
Yingdi Yube4150e2014-02-18 13:02:46 -0800278 else
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700279 SecKeychainSetUserInteractionAllowed(true);
Yingdi Yube4150e2014-02-18 13:02:46 -0800280}
281
282bool
Alexander Afanasyev770827c2014-05-13 17:42:55 -0700283SecTpmOsx::getInTerminal() const
Yingdi Yube4150e2014-02-18 13:02:46 -0800284{
285 return m_impl->m_inTerminal;
286}
287
288bool
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700289SecTpmOsx::isLocked()
Yingdi Yube4150e2014-02-18 13:02:46 -0800290{
291 SecKeychainStatus keychainStatus;
292
293 OSStatus res = SecKeychainGetStatus(m_impl->m_keyChainRef, &keychainStatus);
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700294 if (res != errSecSuccess)
Yingdi Yube4150e2014-02-18 13:02:46 -0800295 return true;
296 else
297 return ((kSecUnlockStateStatus & keychainStatus) == 0);
298}
299
Yingdi Yu2e57a582014-02-20 23:34:43 -0800300bool
Yingdi Yube4150e2014-02-18 13:02:46 -0800301SecTpmOsx::unlockTpm(const char* password, size_t passwordLength, bool usePassword)
302{
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700303 OSStatus res;
Yingdi Yube4150e2014-02-18 13:02:46 -0800304
305 // If the default key chain is already unlocked, return immediately.
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700306 if (!isLocked())
Yingdi Yu2e57a582014-02-20 23:34:43 -0800307 return true;
Yingdi Yube4150e2014-02-18 13:02:46 -0800308
309 // If the default key chain is locked, unlock the key chain.
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700310 if (usePassword)
Yingdi Yube4150e2014-02-18 13:02:46 -0800311 {
312 // Use the supplied password.
313 res = SecKeychainUnlock(m_impl->m_keyChainRef,
314 passwordLength,
315 password,
316 true);
317 }
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700318 else if (m_impl->m_passwordSet)
Yingdi Yube4150e2014-02-18 13:02:46 -0800319 {
320 // If no password supplied, then use the configured password if exists.
321 SecKeychainUnlock(m_impl->m_keyChainRef,
322 m_impl->m_password.size(),
323 m_impl->m_password.c_str(),
324 true);
325 }
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700326 else if (m_impl->m_inTerminal)
Yingdi Yube4150e2014-02-18 13:02:46 -0800327 {
328 // If no configured password, get password from terminal if inTerminal set.
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700329 bool isLocked = true;
Yingdi Yube4150e2014-02-18 13:02:46 -0800330 const char* fmt = "Password to unlock the default keychain: ";
331 int count = 0;
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700332
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700333 while (isLocked)
Yingdi Yube4150e2014-02-18 13:02:46 -0800334 {
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700335 if (count > 2)
Yingdi Yube4150e2014-02-18 13:02:46 -0800336 break;
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700337
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700338 char* getPassword = 0;
Yingdi Yube4150e2014-02-18 13:02:46 -0800339 getPassword = getpass(fmt);
340 count++;
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700341
Yingdi Yube4150e2014-02-18 13:02:46 -0800342 if (!getPassword)
343 continue;
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700344
Yingdi Yube4150e2014-02-18 13:02:46 -0800345 res = SecKeychainUnlock(m_impl->m_keyChainRef,
346 strlen(getPassword),
347 getPassword,
348 true);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700349
Yingdi Yube4150e2014-02-18 13:02:46 -0800350 memset(getPassword, 0, strlen(getPassword));
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700351
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700352 if (res == errSecSuccess)
Yingdi Yu2e57a582014-02-20 23:34:43 -0800353 break;
Yingdi Yube4150e2014-02-18 13:02:46 -0800354 }
355 }
356 else
357 {
358 // If inTerminal is not set, get the password from GUI.
359 SecKeychainUnlock(m_impl->m_keyChainRef, 0, 0, false);
360 }
Yingdi Yu2e57a582014-02-20 23:34:43 -0800361
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700362 return !isLocked();
Yingdi Yube4150e2014-02-18 13:02:46 -0800363}
364
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700365void
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700366SecTpmOsx::generateKeyPairInTpmInternal(const Name& keyName, KeyType keyType,
367 int keySize, bool needRetry)
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700368{
369
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700370 if (doesKeyExistInTpm(keyName, KEY_CLASS_PUBLIC))
371 {
372 throw Error("keyName has existed");
373 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800374
375 string keyNameUri = m_impl->toInternalKeyName(keyName, KEY_CLASS_PUBLIC);
376
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700377 CFReleaser<CFStringRef> keyLabel =
378 CFStringCreateWithCString(0,
379 keyNameUri.c_str(),
380 kCFStringEncodingUTF8);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800381
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700382 CFReleaser<CFMutableDictionaryRef> attrDict =
383 CFDictionaryCreateMutable(0,
384 3,
385 &kCFTypeDictionaryKeyCallBacks,
386 0);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700387
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700388 CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(0, kCFNumberIntType, &keySize);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800389
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700390 CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType, m_impl->getAsymKeyType(keyType));
391 CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
392 CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800393
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700394 CFReleaser<SecKeyRef> publicKey, privateKey;
395 OSStatus res = SecKeyGeneratePair((CFDictionaryRef)attrDict.get(),
396 &publicKey.get(), &privateKey.get());
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800397
Yingdi Yube4150e2014-02-18 13:02:46 -0800398 if (res == errSecSuccess)
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800399 {
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800400 return;
401 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700402
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700403 if (res == errSecAuthFailed && !needRetry)
Yingdi Yube4150e2014-02-18 13:02:46 -0800404 {
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700405 if (unlockTpm(0, 0, false))
Yingdi Yu2e57a582014-02-20 23:34:43 -0800406 generateKeyPairInTpmInternal(keyName, keyType, keySize, true);
407 else
408 throw Error("Fail to unlock the keychain");
Yingdi Yube4150e2014-02-18 13:02:46 -0800409 }
410 else
411 {
Yingdi Yube4150e2014-02-18 13:02:46 -0800412 throw Error("Fail to create a key pair");
413 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800414}
415
416void
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700417SecTpmOsx::deleteKeyPairInTpmInternal(const Name& keyName, bool needRetry)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800418{
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700419 CFReleaser<CFStringRef> keyLabel =
420 CFStringCreateWithCString(0,
421 keyName.toUri().c_str(),
422 kCFStringEncodingUTF8);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800423
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700424 CFReleaser<CFMutableDictionaryRef> searchDict =
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700425 CFDictionaryCreateMutable(0, 5,
426 &kCFTypeDictionaryKeyCallBacks,
427 &kCFTypeDictionaryValueCallBacks);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800428
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700429 CFDictionaryAddValue(searchDict.get(), kSecClass, kSecClassKey);
430 CFDictionaryAddValue(searchDict.get(), kSecAttrLabel, keyLabel.get());
431 CFDictionaryAddValue(searchDict.get(), kSecMatchLimit, kSecMatchLimitAll);
432 OSStatus res = SecItemDelete(searchDict.get());
Yingdi Yube4150e2014-02-18 13:02:46 -0800433
434 if (res == errSecSuccess)
435 return;
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700436
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700437 if (res == errSecAuthFailed && !needRetry)
Yingdi Yube4150e2014-02-18 13:02:46 -0800438 {
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700439 if (unlockTpm(0, 0, false))
Yingdi Yu2e57a582014-02-20 23:34:43 -0800440 deleteKeyPairInTpmInternal(keyName, true);
Yingdi Yube4150e2014-02-18 13:02:46 -0800441 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800442}
443
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700444void
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700445SecTpmOsx::generateSymmetricKeyInTpm(const Name& keyName, KeyType keyType, int keySize)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800446{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800447 throw Error("SecTpmOsx::generateSymmetricKeyInTpm is not supported");
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700448 // if (doesKeyExistInTpm(keyName, KEY_CLASS_SYMMETRIC))
Yingdi Yu2e57a582014-02-20 23:34:43 -0800449 // throw Error("keyName has existed!");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800450
Yingdi Yu2e57a582014-02-20 23:34:43 -0800451 // string keyNameUri = m_impl->toInternalKeyName(keyName, KEY_CLASS_SYMMETRIC);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800452
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700453 // CFReleaser<CFMutableDictionaryRef> attrDict =
454 // CFDictionaryCreateMutable(kCFAllocatorDefault,
455 // 0,
456 // &kCFTypeDictionaryKeyCallBacks,
457 // &kCFTypeDictionaryValueCallBacks);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800458
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700459 // CFReleaser<CFStringRef> keyLabel =
460 // CFStringCreateWithCString(0,
461 // keyNameUri.c_str(),
462 // kCFStringEncodingUTF8);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800463
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700464 // CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(0, kCFNumberIntType, &keySize);
465
466 // CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType, m_impl->getSymKeyType(keyType));
467 // CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
468 // CFDictionaryAddValue(attrDict.get(), kSecAttrIsPermanent, kCFBooleanTrue);
469 // CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800470
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700471 // CFErrorRef error = 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800472
Yingdi Yu2e57a582014-02-20 23:34:43 -0800473 // SecKeyRef symmetricKey = SecKeyGenerateSymmetric(attrDict, &error);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800474
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700475 // if (error)
Yingdi Yu2e57a582014-02-20 23:34:43 -0800476 // throw Error("Fail to create a symmetric key");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800477}
478
Yingdi Yu2e57a582014-02-20 23:34:43 -0800479shared_ptr<PublicKey>
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700480SecTpmOsx::getPublicKeyFromTpm(const Name& keyName)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800481{
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700482 CFReleaser<SecKeychainItemRef> publicKey = m_impl->getKey(keyName, KEY_CLASS_PUBLIC);
483 if (publicKey.get() == 0)
484 {
485 throw Error("Requested public key [" + keyName.toUri() + "] does not exist in OSX Keychain");
486 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800487
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700488 CFReleaser<CFDataRef> exportedKey;
489 OSStatus res = SecItemExport(publicKey.get(),
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800490 kSecFormatOpenSSL,
491 0,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700492 0,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700493 &exportedKey.get());
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800494 if (res != errSecSuccess)
495 {
496 throw Error("Cannot export requested public key from OSX Keychain");
497 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800498
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700499 shared_ptr<PublicKey> key = make_shared<PublicKey>(CFDataGetBytePtr(exportedKey.get()),
500 CFDataGetLength(exportedKey.get()));
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800501 return key;
502}
503
504ConstBufferPtr
Yingdi Yu5e96e002014-04-23 18:32:15 -0700505SecTpmOsx::exportPrivateKeyPkcs8FromTpmInternal(const Name& keyName, bool needRetry)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800506{
507 using namespace CryptoPP;
508
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700509 CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, KEY_CLASS_PRIVATE);
510 if (privateKey.get() == 0)
511 {
512 /// @todo Can this happen because of keychain is locked?
513 throw Error("Private key [" + keyName.toUri() + "] does not exist in OSX Keychain");
514 }
515
516 CFReleaser<CFDataRef> exportedKey;
517 OSStatus res = SecItemExport(privateKey.get(),
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800518 kSecFormatOpenSSL,
519 0,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700520 0,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700521 &exportedKey.get());
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800522
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700523 if (res != errSecSuccess)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800524 {
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700525 if (res == errSecAuthFailed && !needRetry)
Yingdi Yube4150e2014-02-18 13:02:46 -0800526 {
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700527 if (unlockTpm(0, 0, false))
Yingdi Yu5e96e002014-04-23 18:32:15 -0700528 return exportPrivateKeyPkcs8FromTpmInternal(keyName, true);
Yingdi Yu2e57a582014-02-20 23:34:43 -0800529 else
530 return shared_ptr<Buffer>();
Yingdi Yube4150e2014-02-18 13:02:46 -0800531 }
532 else
533 return shared_ptr<Buffer>();
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800534 }
535
536 OBufferStream pkcs1Os;
537 FileSink sink(pkcs1Os);
538
539 uint32_t version = 0;
540 OID algorithm("1.2.840.113549.1.1.1");
541 SecByteBlock rawKeyBits;
542 // PrivateKeyInfo ::= SEQUENCE {
543 // version INTEGER,
544 // privateKeyAlgorithm SEQUENCE,
545 // privateKey OCTECT STRING}
546 DERSequenceEncoder privateKeyInfo(sink);
547 {
548 DEREncodeUnsigned<uint32_t>(privateKeyInfo, version, INTEGER);
549 DERSequenceEncoder privateKeyAlgorithm(privateKeyInfo);
550 {
551 algorithm.encode(privateKeyAlgorithm);
552 DEREncodeNull(privateKeyAlgorithm);
553 }
554 privateKeyAlgorithm.MessageEnd();
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700555 DEREncodeOctetString(privateKeyInfo,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700556 CFDataGetBytePtr(exportedKey.get()),
557 CFDataGetLength(exportedKey.get()));
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800558 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700559 privateKeyInfo.MessageEnd();
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800560
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800561 return pkcs1Os.buf();
562}
563
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700564#ifdef __GNUC__
565#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
566#pragma GCC diagnostic push
567#endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
568#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
569#endif // __GNUC__
570
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800571bool
Yingdi Yu5e96e002014-04-23 18:32:15 -0700572SecTpmOsx::importPrivateKeyPkcs8IntoTpmInternal(const Name& keyName,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700573 const uint8_t* buf, size_t size,
574 bool needRetry)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800575{
576 using namespace CryptoPP;
577
578 StringSource privateKeySource(buf, size, true);
579 uint32_t tmpNum;
580 OID tmpOID;
581 SecByteBlock rawKeyBits;
582 // PrivateKeyInfo ::= SEQUENCE {
583 // INTEGER,
584 // SEQUENCE,
585 // OCTECT STRING}
586 BERSequenceDecoder privateKeyInfo(privateKeySource);
587 {
588 BERDecodeUnsigned<uint32_t>(privateKeyInfo, tmpNum, INTEGER);
589 BERSequenceDecoder sequenceDecoder(privateKeyInfo);
590 {
591 tmpOID.decode(sequenceDecoder);
592 BERDecodeNull(sequenceDecoder);
593 }
594 BERDecodeOctetString(privateKeyInfo, rawKeyBits);
595 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700596 privateKeyInfo.MessageEnd();
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800597
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700598 CFReleaser<CFDataRef> importedKey = CFDataCreateWithBytesNoCopy(0,
599 rawKeyBits.BytePtr(),
600 rawKeyBits.size(),
601 kCFAllocatorNull);
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800602
603 SecExternalFormat externalFormat = kSecFormatOpenSSL;
604 SecExternalItemType externalType = kSecItemTypePrivateKey;
605 SecKeyImportExportParameters keyParams;
606 memset(&keyParams, 0, sizeof(keyParams));
607 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
608 keyParams.keyAttributes = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT;
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700609 CFReleaser<SecAccessRef> access;
610 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
611 keyName.toUri().c_str(),
612 kCFStringEncodingUTF8);
613 SecAccessCreate(keyLabel.get(), 0, &access.get());
614 keyParams.accessRef = access.get();
615 CFReleaser<CFArrayRef> outItems;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800616
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700617#ifdef __clang__
Junxiao Shi482ccc52014-03-31 13:05:24 -0700618#pragma clang diagnostic push
619#pragma clang diagnostic ignored "-Wdeprecated-declarations"
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700620#endif // __clang__
621
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700622 OSStatus res = SecKeychainItemImport(importedKey.get(),
623 0,
624 &externalFormat,
625 &externalType,
626 0,
627 &keyParams,
628 m_impl->m_keyChainRef,
629 &outItems.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700630
631#ifdef __clang__
Junxiao Shi482ccc52014-03-31 13:05:24 -0700632#pragma clang diagnostic pop
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700633#endif // __clang__
634
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700635 if (res != errSecSuccess)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800636 {
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700637 if (res == errSecAuthFailed && !needRetry)
Yingdi Yube4150e2014-02-18 13:02:46 -0800638 {
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700639 if (unlockTpm(0, 0, false))
Yingdi Yu5e96e002014-04-23 18:32:15 -0700640 return importPrivateKeyPkcs8IntoTpmInternal(keyName, buf, size, true);
Yingdi Yu2e57a582014-02-20 23:34:43 -0800641 else
642 return false;
Yingdi Yube4150e2014-02-18 13:02:46 -0800643 }
644 else
645 return false;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800646 }
647
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700648 SecKeychainItemRef privateKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800649 SecKeychainAttribute attrs[1]; // maximum number of attributes
650 SecKeychainAttributeList attrList = { 0, attrs };
651 string keyUri = keyName.toUri();
652 {
653 attrs[attrList.count].tag = kSecKeyPrintName;
654 attrs[attrList.count].length = keyUri.size();
655 attrs[attrList.count].data = (void *)keyUri.c_str();
656 attrList.count++;
657 }
658
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700659 res = SecKeychainItemModifyAttributesAndData(privateKey,
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800660 &attrList,
661 0,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700662 0);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700663
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700664 if (res != errSecSuccess)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800665 {
666 return false;
667 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700668
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800669 return true;
670}
671
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700672#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
673#pragma GCC diagnostic pop
674#endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
675
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800676bool
677SecTpmOsx::importPublicKeyPkcs1IntoTpm(const Name& keyName, const uint8_t* buf, size_t size)
678{
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700679 CFReleaser<CFDataRef> importedKey = CFDataCreateWithBytesNoCopy(0,
680 buf,
681 size,
682 kCFAllocatorNull);
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800683
684 SecExternalFormat externalFormat = kSecFormatOpenSSL;
685 SecExternalItemType externalType = kSecItemTypePublicKey;
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700686 CFReleaser<CFArrayRef> outItems;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800687
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700688 OSStatus res = SecItemImport(importedKey.get(),
689 0,
690 &externalFormat,
691 &externalType,
692 0,
693 0,
694 m_impl->m_keyChainRef,
695 &outItems.get());
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800696
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700697 if (res != errSecSuccess)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800698 return false;
699
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700700 SecKeychainItemRef publicKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800701 SecKeychainAttribute attrs[1]; // maximum number of attributes
702 SecKeychainAttributeList attrList = { 0, attrs };
703 string keyUri = keyName.toUri();
704 {
705 attrs[attrList.count].tag = kSecKeyPrintName;
706 attrs[attrList.count].length = keyUri.size();
707 attrs[attrList.count].data = (void *)keyUri.c_str();
708 attrList.count++;
709 }
710
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700711 res = SecKeychainItemModifyAttributesAndData(publicKey,
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800712 &attrList,
713 0,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700714 0);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700715
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700716 if (res != errSecSuccess)
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800717 return false;
718
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800719 return true;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800720}
721
722Block
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700723SecTpmOsx::signInTpmInternal(const uint8_t* data, size_t dataLength,
724 const Name& keyName, DigestAlgorithm digestAlgorithm, bool needRetry)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800725{
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700726 CFReleaser<CFDataRef> dataRef = CFDataCreateWithBytesNoCopy(0,
727 data,
728 dataLength,
729 kCFAllocatorNull);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800730
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700731 CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, KEY_CLASS_PRIVATE);
732 if (privateKey.get() == 0)
733 {
734 throw Error("Private key [" + keyName.toUri() + "] does not exist in OSX Keychain");
735 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700736
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700737 CFReleaser<CFErrorRef> error;
738 CFReleaser<SecTransformRef> signer = SecSignTransformCreate((SecKeyRef)privateKey.get(),
739 &error.get());
740 if (error.get() != 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700741 throw Error("Fail to create signer");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800742
743 // Set input
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700744 Boolean set_res = SecTransformSetAttribute(signer.get(),
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800745 kSecTransformInputAttributeName,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700746 dataRef.get(),
747 &error.get());
748 if (error.get() != 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700749 throw Error("Fail to configure input of signer");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800750
751 // Enable use of padding
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700752 SecTransformSetAttribute(signer.get(),
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800753 kSecPaddingKey,
754 kSecPaddingPKCS1Key,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700755 &error.get());
756 if (error.get() != 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700757 throw Error("Fail to configure digest algorithm of signer");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800758
759 // Set padding type
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700760 set_res = SecTransformSetAttribute(signer.get(),
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800761 kSecDigestTypeAttribute,
762 m_impl->getDigestAlgorithm(digestAlgorithm),
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700763 &error.get());
764 if (error.get() != 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700765 throw Error("Fail to configure digest algorithm of signer");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800766
767 // Set padding attribute
768 long digestSize = m_impl->getDigestSize(digestAlgorithm);
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700769 CFReleaser<CFNumberRef> cfDigestSize = CFNumberCreate(0, kCFNumberLongType, &digestSize);
770 set_res = SecTransformSetAttribute(signer.get(),
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800771 kSecDigestLengthAttribute,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700772 cfDigestSize.get(),
773 &error.get());
774 if (error.get() != 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700775 throw Error("Fail to configure digest size of signer");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800776
777 // Actually sign
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700778 CFReleaser<CFDataRef> signature = (CFDataRef)SecTransformExecute(signer.get(), &error.get());
779 if (error.get() != 0)
Yingdi Yube4150e2014-02-18 13:02:46 -0800780 {
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700781 if (!needRetry)
Yingdi Yube4150e2014-02-18 13:02:46 -0800782 {
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700783 if (unlockTpm(0, 0, false))
Yingdi Yu2e57a582014-02-20 23:34:43 -0800784 return signInTpmInternal(data, dataLength, keyName, digestAlgorithm, true);
785 else
786 throw Error("Fail to unlock the keychain");
Yingdi Yube4150e2014-02-18 13:02:46 -0800787 }
788 else
789 {
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700790 CFShow(error.get());
Yingdi Yube4150e2014-02-18 13:02:46 -0800791 throw Error("Fail to sign data");
792 }
793 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800794
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700795 if (signature.get() == 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700796 throw Error("Signature is NULL!\n");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800797
798 return Block(Tlv::SignatureValue,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700799 make_shared<Buffer>(CFDataGetBytePtr(signature.get()),
800 CFDataGetLength(signature.get())));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800801}
802
803ConstBufferPtr
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700804SecTpmOsx::decryptInTpm(const uint8_t* data, size_t dataLength, const Name& keyName, bool sym)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800805{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800806 throw Error("SecTpmOsx::decryptInTpm is not supported");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800807
Yingdi Yu2e57a582014-02-20 23:34:43 -0800808 // KeyClass keyClass;
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700809 // if (sym)
Yingdi Yu2e57a582014-02-20 23:34:43 -0800810 // keyClass = KEY_CLASS_SYMMETRIC;
811 // else
812 // keyClass = KEY_CLASS_PRIVATE;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800813
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700814 // CFDataRef dataRef = CFDataCreate(0,
Yingdi Yu2e57a582014-02-20 23:34:43 -0800815 // reinterpret_cast<const unsigned char*>(data),
816 // dataLength
817 // );
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800818
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700819 // CFReleaser<SecKeyRef> decryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
820 // if (decryptKey.get() == 0)
821 // {
822 // /// @todo Can this happen because of keychain is locked?
823 // throw Error("Decruption key [" + ??? + "] does not exist in OSX Keychain");
824 // }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800825
Yingdi Yu2e57a582014-02-20 23:34:43 -0800826 // CFErrorRef error;
827 // SecTransformRef decrypt = SecDecryptTransformCreate(decryptKey, &error);
828 // if (error) throw Error("Fail to create decrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800829
Yingdi Yu2e57a582014-02-20 23:34:43 -0800830 // Boolean set_res = SecTransformSetAttribute(decrypt,
831 // kSecTransformInputAttributeName,
832 // dataRef,
833 // &error);
834 // if (error) throw Error("Fail to configure decrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800835
Yingdi Yu2e57a582014-02-20 23:34:43 -0800836 // CFDataRef output = (CFDataRef) SecTransformExecute(decrypt, &error);
837 // if (error)
838 // {
839 // CFShow(error);
840 // throw Error("Fail to decrypt data");
841 // }
842 // if (!output) throw Error("Output is NULL!\n");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800843
Yingdi Yu2e57a582014-02-20 23:34:43 -0800844 // return make_shared<Buffer>(CFDataGetBytePtr(output), CFDataGetLength(output));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800845}
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700846
Yingdi Yu2e57a582014-02-20 23:34:43 -0800847void
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700848SecTpmOsx::addAppToAcl(const Name& keyName, KeyClass keyClass, const string& appPath, AclType acl)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800849{
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700850 if (keyClass == KEY_CLASS_PRIVATE && acl == ACL_TYPE_PRIVATE)
Yingdi Yu2e57a582014-02-20 23:34:43 -0800851 {
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700852 CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, keyClass);
853 if (privateKey.get() == 0)
854 {
855 throw Error("Private key [" + keyName.toUri() + "] does not exist in OSX Keychain");
856 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700857
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700858 CFReleaser<SecAccessRef> accRef;
859 OSStatus acc_res = SecKeychainItemCopyAccess(privateKey.get(), &accRef.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700860
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700861 CFReleaser<CFArrayRef> signACL = SecAccessCopyMatchingACLList(accRef.get(),
862 kSecACLAuthorizationSign);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700863
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700864 SecACLRef aclRef = (SecACLRef)CFArrayGetValueAtIndex(signACL.get(), 0);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700865
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700866 CFReleaser<CFArrayRef> appList;
867 CFReleaser<CFStringRef> description;
Yingdi Yu2e57a582014-02-20 23:34:43 -0800868 SecKeychainPromptSelector promptSelector;
869 OSStatus acl_res = SecACLCopyContents(aclRef,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700870 &appList.get(),
871 &description.get(),
Yingdi Yu2e57a582014-02-20 23:34:43 -0800872 &promptSelector);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700873
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700874 CFReleaser<CFMutableArrayRef> newAppList = CFArrayCreateMutableCopy(0,
875 0,
876 appList.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700877
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700878 CFReleaser<SecTrustedApplicationRef> trustedApp;
Yingdi Yu2e57a582014-02-20 23:34:43 -0800879 acl_res = SecTrustedApplicationCreateFromPath(appPath.c_str(),
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700880 &trustedApp.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700881
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700882 CFArrayAppendValue(newAppList.get(), trustedApp.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700883
Yingdi Yu2e57a582014-02-20 23:34:43 -0800884 acl_res = SecACLSetContents(aclRef,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700885 newAppList.get(),
886 description.get(),
Yingdi Yu2e57a582014-02-20 23:34:43 -0800887 promptSelector);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700888
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700889 acc_res = SecKeychainItemSetAccess(privateKey.get(), accRef.get());
Yingdi Yu2e57a582014-02-20 23:34:43 -0800890 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800891}
892
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800893ConstBufferPtr
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700894SecTpmOsx::encryptInTpm(const uint8_t* data, size_t dataLength, const Name& keyName, bool sym)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800895{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800896 throw Error("SecTpmOsx::encryptInTpm is not supported");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800897
Yingdi Yu2e57a582014-02-20 23:34:43 -0800898 // KeyClass keyClass;
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700899 // if (sym)
Yingdi Yu2e57a582014-02-20 23:34:43 -0800900 // keyClass = KEY_CLASS_SYMMETRIC;
901 // else
902 // keyClass = KEY_CLASS_PUBLIC;
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700903
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700904 // CFDataRef dataRef = CFDataCreate(0,
Yingdi Yu2e57a582014-02-20 23:34:43 -0800905 // reinterpret_cast<const unsigned char*>(data),
906 // dataLength
907 // );
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700908
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700909 // CFReleaser<SecKeyRef> encryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
910 // if (encryptKey.get() == 0)
911 // {
912 // throw Error("Encryption key [" + ???? + "] does not exist in OSX Keychain");
913 // }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800914
Yingdi Yu2e57a582014-02-20 23:34:43 -0800915 // CFErrorRef error;
916 // SecTransformRef encrypt = SecEncryptTransformCreate(encryptKey, &error);
917 // if (error) throw Error("Fail to create encrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800918
Yingdi Yu2e57a582014-02-20 23:34:43 -0800919 // Boolean set_res = SecTransformSetAttribute(encrypt,
920 // kSecTransformInputAttributeName,
921 // dataRef,
922 // &error);
923 // if (error) throw Error("Fail to configure encrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800924
Yingdi Yu2e57a582014-02-20 23:34:43 -0800925 // CFDataRef output = (CFDataRef) SecTransformExecute(encrypt, &error);
926 // if (error) throw Error("Fail to encrypt data");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800927
Yingdi Yu2e57a582014-02-20 23:34:43 -0800928 // if (!output) throw Error("Output is NULL!\n");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800929
Yingdi Yu2e57a582014-02-20 23:34:43 -0800930 // return make_shared<Buffer> (CFDataGetBytePtr(output), CFDataGetLength(output));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800931}
932
933bool
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700934SecTpmOsx::doesKeyExistInTpm(const Name& keyName, KeyClass keyClass)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800935{
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800936 string keyNameUri = m_impl->toInternalKeyName(keyName, keyClass);
937
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700938 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
939 keyNameUri.c_str(),
940 kCFStringEncodingUTF8);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700941
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700942 CFReleaser<CFMutableDictionaryRef> attrDict =
943 CFDictionaryCreateMutable(0,
944 4,
945 &kCFTypeDictionaryKeyCallBacks,
946 0);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800947
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700948 CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
949 // CFDictionaryAddValue(attrDict.get(), kSecAttrKeyClass, m_impl->getKeyClass(keyClass));
950 CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
951 CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700952
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700953 CFReleaser<SecKeychainItemRef> itemRef;
954 OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&itemRef.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700955
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700956 if (res == errSecSuccess)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800957 return true;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800958 else
959 return false;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800960
961}
962
Yingdi Yu4b752752014-02-18 12:24:03 -0800963bool
964SecTpmOsx::generateRandomBlock(uint8_t* res, size_t size)
965{
966 return (SecRandomCopyBytes(kSecRandomDefault, size, res) == 0);
967}
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800968
969////////////////////////////////
970// OSXPrivateKeyStorage::Impl //
971////////////////////////////////
972
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700973CFReleaser<SecKeychainItemRef>
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700974SecTpmOsx::Impl::getKey(const Name& keyName, KeyClass keyClass)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800975{
976 string keyNameUri = toInternalKeyName(keyName, keyClass);
977
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700978 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
979 keyNameUri.c_str(),
980 kCFStringEncodingUTF8);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700981
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700982 CFReleaser<CFMutableDictionaryRef> attrDict =
983 CFDictionaryCreateMutable(0,
984 5,
985 &kCFTypeDictionaryKeyCallBacks,
986 0);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800987
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700988 CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
989 CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
990 CFDictionaryAddValue(attrDict.get(), kSecAttrKeyClass, getKeyClass(keyClass));
991 CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700992
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700993 CFReleaser<SecKeychainItemRef> keyItem;
994 OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&keyItem.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700995
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700996 if (res != errSecSuccess)
997 return 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800998 else
999 return keyItem;
1000}
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001001
1002string
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -07001003SecTpmOsx::Impl::toInternalKeyName(const Name& keyName, KeyClass keyClass)
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001004{
1005 string keyUri = keyName.toUri();
1006
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -07001007 if (KEY_CLASS_SYMMETRIC == keyClass)
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001008 return keyUri + "/symmetric";
1009 else
1010 return keyUri;
1011}
1012
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001013const CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001014SecTpmOsx::Impl::getAsymKeyType(KeyType keyType)
1015{
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -07001016 switch (keyType){
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001017 case KEY_TYPE_RSA:
1018 return kSecAttrKeyTypeRSA;
1019 default:
Yingdi Yu4b8c6a22014-04-15 23:00:54 -07001020 return 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001021 }
1022}
1023
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001024const CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001025SecTpmOsx::Impl::getSymKeyType(KeyType keyType)
1026{
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -07001027 switch (keyType){
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001028 case KEY_TYPE_AES:
1029 return kSecAttrKeyTypeAES;
1030 default:
Yingdi Yu4b8c6a22014-04-15 23:00:54 -07001031 return 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001032 }
1033}
1034
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001035const CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001036SecTpmOsx::Impl::getKeyClass(KeyClass keyClass)
1037{
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -07001038 switch (keyClass){
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001039 case KEY_CLASS_PRIVATE:
1040 return kSecAttrKeyClassPrivate;
1041 case KEY_CLASS_PUBLIC:
1042 return kSecAttrKeyClassPublic;
1043 case KEY_CLASS_SYMMETRIC:
1044 return kSecAttrKeyClassSymmetric;
1045 default:
Yingdi Yu4b8c6a22014-04-15 23:00:54 -07001046 return 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001047 }
1048}
1049
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001050const CFStringRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001051SecTpmOsx::Impl::getDigestAlgorithm(DigestAlgorithm digestAlgo)
1052{
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -07001053 switch (digestAlgo){
Jeff Thompson2747dc02013-10-04 19:11:34 -07001054 // case DIGEST_MD2:
1055 // return kSecDigestMD2;
1056 // case DIGEST_MD5:
1057 // return kSecDigestMD5;
1058 // case DIGEST_SHA1:
1059 // return kSecDigestSHA1;
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001060 case DIGEST_ALGORITHM_SHA256:
1061 return kSecDigestSHA2;
1062 default:
Yingdi Yu4b8c6a22014-04-15 23:00:54 -07001063 return 0;
Jeff Thompson2747dc02013-10-04 19:11:34 -07001064 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001065}
Jeff Thompson2747dc02013-10-04 19:11:34 -07001066
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001067long
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001068SecTpmOsx::Impl::getDigestSize(DigestAlgorithm digestAlgo)
1069{
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -07001070 switch (digestAlgo){
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001071 case DIGEST_ALGORITHM_SHA256:
1072 return 256;
Jeff Thompson2747dc02013-10-04 19:11:34 -07001073 // case DIGEST_SHA1:
1074 // case DIGEST_MD2:
1075 // case DIGEST_MD5:
1076 // return 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001077 default:
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001078 return -1;
Jeff Thompson2747dc02013-10-04 19:11:34 -07001079 }
Jeff Thompson2747dc02013-10-04 19:11:34 -07001080}
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001081
Yingdi Yufc40d872014-02-18 12:56:04 -08001082} // namespace ndn