blob: 031632de54ec1b8bdac09f20db7064555328794e [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
Yingdi Yufc40d872014-02-18 12:56:04 -080044namespace ndn {
45
Yingdi Yu7036ce22014-06-19 18:53:37 -070046using std::string;
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 Afanasyev24b75c82014-05-31 15:59:31 +0300188 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 Afanasyev24b75c82014-05-31 15:59:31 +0300197 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 Afanasyev24b75c82014-05-31 15:59:31 +0300206 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 Afanasyev24b75c82014-05-31 15:59:31 +0300215 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;
Yingdi Yu7036ce22014-06-19 18:53:37 -0700259 std::fill(m_impl->m_password.begin(), m_impl->m_password.end(), 0);
Yingdi Yube4150e2014-02-18 13:02:46 -0800260 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;
Yingdi Yu7036ce22014-06-19 18:53:37 -0700268 std::fill(m_impl->m_password.begin(), m_impl->m_password.end(), 0);
Yingdi Yube4150e2014-02-18 13:02:46 -0800269 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 Yu7036ce22014-06-19 18:53:37 -0700366SecTpmOsx::generateKeyPairInTpmInternal(const Name& keyName,
367 const KeyParams& params,
368 bool needRetry)
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700369{
370
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700371 if (doesKeyExistInTpm(keyName, KEY_CLASS_PUBLIC))
372 {
373 throw Error("keyName has existed");
374 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800375
376 string keyNameUri = m_impl->toInternalKeyName(keyName, KEY_CLASS_PUBLIC);
377
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700378 CFReleaser<CFStringRef> keyLabel =
379 CFStringCreateWithCString(0,
380 keyNameUri.c_str(),
381 kCFStringEncodingUTF8);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800382
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700383 CFReleaser<CFMutableDictionaryRef> attrDict =
384 CFDictionaryCreateMutable(0,
385 3,
386 &kCFTypeDictionaryKeyCallBacks,
387 0);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700388
Yingdi Yu7036ce22014-06-19 18:53:37 -0700389 KeyType keyType = params.getKeyType();
390 uint32_t keySize;
391 switch (keyType)
392 {
393 case KEY_TYPE_RSA:
394 {
395 const RsaKeyParams& rsaParams = static_cast<const RsaKeyParams&>(params);
396 keySize = rsaParams.getKeySize();
397 break;
398 }
399 default:
400 throw Error("Fail to create a key pair: Unsupported key type");
401 }
402
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700403 CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(0, kCFNumberIntType, &keySize);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800404
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700405 CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType, m_impl->getAsymKeyType(keyType));
406 CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
407 CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800408
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700409 CFReleaser<SecKeyRef> publicKey, privateKey;
Yingdi Yu7036ce22014-06-19 18:53:37 -0700410 // C-style cast is used as per Apple convention
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700411 OSStatus res = SecKeyGeneratePair((CFDictionaryRef)attrDict.get(),
412 &publicKey.get(), &privateKey.get());
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800413
Yingdi Yube4150e2014-02-18 13:02:46 -0800414 if (res == errSecSuccess)
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800415 {
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800416 return;
417 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700418
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700419 if (res == errSecAuthFailed && !needRetry)
Yingdi Yube4150e2014-02-18 13:02:46 -0800420 {
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700421 if (unlockTpm(0, 0, false))
Yingdi Yu7036ce22014-06-19 18:53:37 -0700422 generateKeyPairInTpmInternal(keyName, params, true);
Yingdi Yu2e57a582014-02-20 23:34:43 -0800423 else
424 throw Error("Fail to unlock the keychain");
Yingdi Yube4150e2014-02-18 13:02:46 -0800425 }
426 else
427 {
Yingdi Yube4150e2014-02-18 13:02:46 -0800428 throw Error("Fail to create a key pair");
429 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800430}
431
432void
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700433SecTpmOsx::deleteKeyPairInTpmInternal(const Name& keyName, bool needRetry)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800434{
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700435 CFReleaser<CFStringRef> keyLabel =
436 CFStringCreateWithCString(0,
437 keyName.toUri().c_str(),
438 kCFStringEncodingUTF8);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800439
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700440 CFReleaser<CFMutableDictionaryRef> searchDict =
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700441 CFDictionaryCreateMutable(0, 5,
442 &kCFTypeDictionaryKeyCallBacks,
443 &kCFTypeDictionaryValueCallBacks);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800444
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700445 CFDictionaryAddValue(searchDict.get(), kSecClass, kSecClassKey);
446 CFDictionaryAddValue(searchDict.get(), kSecAttrLabel, keyLabel.get());
447 CFDictionaryAddValue(searchDict.get(), kSecMatchLimit, kSecMatchLimitAll);
448 OSStatus res = SecItemDelete(searchDict.get());
Yingdi Yube4150e2014-02-18 13:02:46 -0800449
450 if (res == errSecSuccess)
451 return;
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700452
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700453 if (res == errSecAuthFailed && !needRetry)
Yingdi Yube4150e2014-02-18 13:02:46 -0800454 {
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700455 if (unlockTpm(0, 0, false))
Yingdi Yu2e57a582014-02-20 23:34:43 -0800456 deleteKeyPairInTpmInternal(keyName, true);
Yingdi Yube4150e2014-02-18 13:02:46 -0800457 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800458}
459
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700460void
Yingdi Yu7036ce22014-06-19 18:53:37 -0700461SecTpmOsx::generateSymmetricKeyInTpm(const Name& keyName, const KeyParams& params)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800462{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800463 throw Error("SecTpmOsx::generateSymmetricKeyInTpm is not supported");
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700464 // if (doesKeyExistInTpm(keyName, KEY_CLASS_SYMMETRIC))
Yingdi Yu2e57a582014-02-20 23:34:43 -0800465 // throw Error("keyName has existed!");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800466
Yingdi Yu2e57a582014-02-20 23:34:43 -0800467 // string keyNameUri = m_impl->toInternalKeyName(keyName, KEY_CLASS_SYMMETRIC);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800468
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700469 // CFReleaser<CFMutableDictionaryRef> attrDict =
470 // CFDictionaryCreateMutable(kCFAllocatorDefault,
471 // 0,
472 // &kCFTypeDictionaryKeyCallBacks,
473 // &kCFTypeDictionaryValueCallBacks);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800474
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700475 // CFReleaser<CFStringRef> keyLabel =
476 // CFStringCreateWithCString(0,
477 // keyNameUri.c_str(),
478 // kCFStringEncodingUTF8);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800479
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700480 // CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(0, kCFNumberIntType, &keySize);
481
482 // CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType, m_impl->getSymKeyType(keyType));
483 // CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
484 // CFDictionaryAddValue(attrDict.get(), kSecAttrIsPermanent, kCFBooleanTrue);
485 // CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800486
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700487 // CFErrorRef error = 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800488
Yingdi Yu2e57a582014-02-20 23:34:43 -0800489 // SecKeyRef symmetricKey = SecKeyGenerateSymmetric(attrDict, &error);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800490
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700491 // if (error)
Yingdi Yu2e57a582014-02-20 23:34:43 -0800492 // throw Error("Fail to create a symmetric key");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800493}
494
Yingdi Yu2e57a582014-02-20 23:34:43 -0800495shared_ptr<PublicKey>
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700496SecTpmOsx::getPublicKeyFromTpm(const Name& keyName)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800497{
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700498 CFReleaser<SecKeychainItemRef> publicKey = m_impl->getKey(keyName, KEY_CLASS_PUBLIC);
499 if (publicKey.get() == 0)
500 {
501 throw Error("Requested public key [" + keyName.toUri() + "] does not exist in OSX Keychain");
502 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800503
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700504 CFReleaser<CFDataRef> exportedKey;
505 OSStatus res = SecItemExport(publicKey.get(),
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800506 kSecFormatOpenSSL,
507 0,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700508 0,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700509 &exportedKey.get());
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800510 if (res != errSecSuccess)
511 {
512 throw Error("Cannot export requested public key from OSX Keychain");
513 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800514
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700515 shared_ptr<PublicKey> key = make_shared<PublicKey>(CFDataGetBytePtr(exportedKey.get()),
516 CFDataGetLength(exportedKey.get()));
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800517 return key;
518}
519
520ConstBufferPtr
Yingdi Yu5e96e002014-04-23 18:32:15 -0700521SecTpmOsx::exportPrivateKeyPkcs8FromTpmInternal(const Name& keyName, bool needRetry)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800522{
523 using namespace CryptoPP;
524
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700525 CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, KEY_CLASS_PRIVATE);
526 if (privateKey.get() == 0)
527 {
528 /// @todo Can this happen because of keychain is locked?
529 throw Error("Private key [" + keyName.toUri() + "] does not exist in OSX Keychain");
530 }
531
532 CFReleaser<CFDataRef> exportedKey;
533 OSStatus res = SecItemExport(privateKey.get(),
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800534 kSecFormatOpenSSL,
535 0,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700536 0,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700537 &exportedKey.get());
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800538
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700539 if (res != errSecSuccess)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800540 {
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700541 if (res == errSecAuthFailed && !needRetry)
Yingdi Yube4150e2014-02-18 13:02:46 -0800542 {
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700543 if (unlockTpm(0, 0, false))
Yingdi Yu5e96e002014-04-23 18:32:15 -0700544 return exportPrivateKeyPkcs8FromTpmInternal(keyName, true);
Yingdi Yu2e57a582014-02-20 23:34:43 -0800545 else
546 return shared_ptr<Buffer>();
Yingdi Yube4150e2014-02-18 13:02:46 -0800547 }
548 else
549 return shared_ptr<Buffer>();
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800550 }
551
552 OBufferStream pkcs1Os;
553 FileSink sink(pkcs1Os);
554
555 uint32_t version = 0;
Yingdi Yu7036ce22014-06-19 18:53:37 -0700556 OID algorithm("1.2.840.113549.1.1.1"); // "RSA encryption"
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800557 SecByteBlock rawKeyBits;
558 // PrivateKeyInfo ::= SEQUENCE {
559 // version INTEGER,
560 // privateKeyAlgorithm SEQUENCE,
561 // privateKey OCTECT STRING}
562 DERSequenceEncoder privateKeyInfo(sink);
563 {
564 DEREncodeUnsigned<uint32_t>(privateKeyInfo, version, INTEGER);
565 DERSequenceEncoder privateKeyAlgorithm(privateKeyInfo);
566 {
567 algorithm.encode(privateKeyAlgorithm);
568 DEREncodeNull(privateKeyAlgorithm);
569 }
570 privateKeyAlgorithm.MessageEnd();
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700571 DEREncodeOctetString(privateKeyInfo,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700572 CFDataGetBytePtr(exportedKey.get()),
573 CFDataGetLength(exportedKey.get()));
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800574 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700575 privateKeyInfo.MessageEnd();
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800576
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800577 return pkcs1Os.buf();
578}
579
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700580#ifdef __GNUC__
581#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
582#pragma GCC diagnostic push
583#endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
584#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
585#endif // __GNUC__
586
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800587bool
Yingdi Yu5e96e002014-04-23 18:32:15 -0700588SecTpmOsx::importPrivateKeyPkcs8IntoTpmInternal(const Name& keyName,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700589 const uint8_t* buf, size_t size,
590 bool needRetry)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800591{
592 using namespace CryptoPP;
593
594 StringSource privateKeySource(buf, size, true);
595 uint32_t tmpNum;
596 OID tmpOID;
597 SecByteBlock rawKeyBits;
598 // PrivateKeyInfo ::= SEQUENCE {
599 // INTEGER,
600 // SEQUENCE,
601 // OCTECT STRING}
602 BERSequenceDecoder privateKeyInfo(privateKeySource);
603 {
604 BERDecodeUnsigned<uint32_t>(privateKeyInfo, tmpNum, INTEGER);
605 BERSequenceDecoder sequenceDecoder(privateKeyInfo);
606 {
607 tmpOID.decode(sequenceDecoder);
608 BERDecodeNull(sequenceDecoder);
609 }
610 BERDecodeOctetString(privateKeyInfo, rawKeyBits);
611 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700612 privateKeyInfo.MessageEnd();
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800613
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700614 CFReleaser<CFDataRef> importedKey = CFDataCreateWithBytesNoCopy(0,
615 rawKeyBits.BytePtr(),
616 rawKeyBits.size(),
617 kCFAllocatorNull);
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800618
619 SecExternalFormat externalFormat = kSecFormatOpenSSL;
620 SecExternalItemType externalType = kSecItemTypePrivateKey;
621 SecKeyImportExportParameters keyParams;
622 memset(&keyParams, 0, sizeof(keyParams));
623 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
624 keyParams.keyAttributes = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT;
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700625 CFReleaser<SecAccessRef> access;
626 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
627 keyName.toUri().c_str(),
628 kCFStringEncodingUTF8);
629 SecAccessCreate(keyLabel.get(), 0, &access.get());
630 keyParams.accessRef = access.get();
631 CFReleaser<CFArrayRef> outItems;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800632
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700633#ifdef __clang__
Junxiao Shi482ccc52014-03-31 13:05:24 -0700634#pragma clang diagnostic push
635#pragma clang diagnostic ignored "-Wdeprecated-declarations"
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700636#endif // __clang__
637
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700638 OSStatus res = SecKeychainItemImport(importedKey.get(),
639 0,
640 &externalFormat,
641 &externalType,
642 0,
643 &keyParams,
644 m_impl->m_keyChainRef,
645 &outItems.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700646
647#ifdef __clang__
Junxiao Shi482ccc52014-03-31 13:05:24 -0700648#pragma clang diagnostic pop
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700649#endif // __clang__
650
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700651 if (res != errSecSuccess)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800652 {
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700653 if (res == errSecAuthFailed && !needRetry)
Yingdi Yube4150e2014-02-18 13:02:46 -0800654 {
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700655 if (unlockTpm(0, 0, false))
Yingdi Yu5e96e002014-04-23 18:32:15 -0700656 return importPrivateKeyPkcs8IntoTpmInternal(keyName, buf, size, true);
Yingdi Yu2e57a582014-02-20 23:34:43 -0800657 else
658 return false;
Yingdi Yube4150e2014-02-18 13:02:46 -0800659 }
660 else
661 return false;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800662 }
663
Yingdi Yu7036ce22014-06-19 18:53:37 -0700664 // C-style cast is used as per Apple convention
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700665 SecKeychainItemRef privateKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800666 SecKeychainAttribute attrs[1]; // maximum number of attributes
667 SecKeychainAttributeList attrList = { 0, attrs };
668 string keyUri = keyName.toUri();
669 {
670 attrs[attrList.count].tag = kSecKeyPrintName;
671 attrs[attrList.count].length = keyUri.size();
Yingdi Yu7036ce22014-06-19 18:53:37 -0700672 attrs[attrList.count].data = const_cast<char*>(keyUri.c_str());
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800673 attrList.count++;
674 }
675
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700676 res = SecKeychainItemModifyAttributesAndData(privateKey,
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800677 &attrList,
678 0,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700679 0);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700680
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700681 if (res != errSecSuccess)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800682 {
683 return false;
684 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700685
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800686 return true;
687}
688
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700689#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
690#pragma GCC diagnostic pop
691#endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
692
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800693bool
694SecTpmOsx::importPublicKeyPkcs1IntoTpm(const Name& keyName, const uint8_t* buf, size_t size)
695{
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700696 CFReleaser<CFDataRef> importedKey = CFDataCreateWithBytesNoCopy(0,
697 buf,
698 size,
699 kCFAllocatorNull);
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800700
701 SecExternalFormat externalFormat = kSecFormatOpenSSL;
702 SecExternalItemType externalType = kSecItemTypePublicKey;
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700703 CFReleaser<CFArrayRef> outItems;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800704
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700705 OSStatus res = SecItemImport(importedKey.get(),
706 0,
707 &externalFormat,
708 &externalType,
709 0,
710 0,
711 m_impl->m_keyChainRef,
712 &outItems.get());
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800713
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700714 if (res != errSecSuccess)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800715 return false;
716
Yingdi Yu7036ce22014-06-19 18:53:37 -0700717 // C-style cast is used as per Apple convention
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700718 SecKeychainItemRef publicKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800719 SecKeychainAttribute attrs[1]; // maximum number of attributes
720 SecKeychainAttributeList attrList = { 0, attrs };
721 string keyUri = keyName.toUri();
722 {
723 attrs[attrList.count].tag = kSecKeyPrintName;
724 attrs[attrList.count].length = keyUri.size();
Yingdi Yu7036ce22014-06-19 18:53:37 -0700725 attrs[attrList.count].data = const_cast<char*>(keyUri.c_str());
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800726 attrList.count++;
727 }
728
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700729 res = SecKeychainItemModifyAttributesAndData(publicKey,
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800730 &attrList,
731 0,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700732 0);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700733
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700734 if (res != errSecSuccess)
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800735 return false;
736
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800737 return true;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800738}
739
740Block
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700741SecTpmOsx::signInTpmInternal(const uint8_t* data, size_t dataLength,
742 const Name& keyName, DigestAlgorithm digestAlgorithm, bool needRetry)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800743{
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700744 CFReleaser<CFDataRef> dataRef = CFDataCreateWithBytesNoCopy(0,
745 data,
746 dataLength,
747 kCFAllocatorNull);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800748
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700749 CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, KEY_CLASS_PRIVATE);
750 if (privateKey.get() == 0)
751 {
752 throw Error("Private key [" + keyName.toUri() + "] does not exist in OSX Keychain");
753 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700754
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700755 CFReleaser<CFErrorRef> error;
Yingdi Yu7036ce22014-06-19 18:53:37 -0700756 // C-style cast is used as per Apple convention
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700757 CFReleaser<SecTransformRef> signer = SecSignTransformCreate((SecKeyRef)privateKey.get(),
758 &error.get());
759 if (error.get() != 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700760 throw Error("Fail to create signer");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800761
762 // Set input
Yingdi Yu7036ce22014-06-19 18:53:37 -0700763 SecTransformSetAttribute(signer.get(),
764 kSecTransformInputAttributeName,
765 dataRef.get(),
766 &error.get());
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700767 if (error.get() != 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700768 throw Error("Fail to configure input of signer");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800769
770 // Enable use of padding
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700771 SecTransformSetAttribute(signer.get(),
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800772 kSecPaddingKey,
773 kSecPaddingPKCS1Key,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700774 &error.get());
775 if (error.get() != 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700776 throw Error("Fail to configure digest algorithm of signer");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800777
778 // Set padding type
Yingdi Yu7036ce22014-06-19 18:53:37 -0700779 SecTransformSetAttribute(signer.get(),
780 kSecDigestTypeAttribute,
781 m_impl->getDigestAlgorithm(digestAlgorithm),
782 &error.get());
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700783 if (error.get() != 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700784 throw Error("Fail to configure digest algorithm of signer");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800785
786 // Set padding attribute
787 long digestSize = m_impl->getDigestSize(digestAlgorithm);
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700788 CFReleaser<CFNumberRef> cfDigestSize = CFNumberCreate(0, kCFNumberLongType, &digestSize);
Yingdi Yu7036ce22014-06-19 18:53:37 -0700789 SecTransformSetAttribute(signer.get(),
790 kSecDigestLengthAttribute,
791 cfDigestSize.get(),
792 &error.get());
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700793 if (error.get() != 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700794 throw Error("Fail to configure digest size of signer");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800795
796 // Actually sign
Yingdi Yu7036ce22014-06-19 18:53:37 -0700797 // C-style cast is used as per Apple convention
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700798 CFReleaser<CFDataRef> signature = (CFDataRef)SecTransformExecute(signer.get(), &error.get());
799 if (error.get() != 0)
Yingdi Yube4150e2014-02-18 13:02:46 -0800800 {
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700801 if (!needRetry)
Yingdi Yube4150e2014-02-18 13:02:46 -0800802 {
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700803 if (unlockTpm(0, 0, false))
Yingdi Yu2e57a582014-02-20 23:34:43 -0800804 return signInTpmInternal(data, dataLength, keyName, digestAlgorithm, true);
805 else
806 throw Error("Fail to unlock the keychain");
Yingdi Yube4150e2014-02-18 13:02:46 -0800807 }
808 else
809 {
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700810 CFShow(error.get());
Yingdi Yube4150e2014-02-18 13:02:46 -0800811 throw Error("Fail to sign data");
812 }
813 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800814
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700815 if (signature.get() == 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700816 throw Error("Signature is NULL!\n");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800817
818 return Block(Tlv::SignatureValue,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700819 make_shared<Buffer>(CFDataGetBytePtr(signature.get()),
820 CFDataGetLength(signature.get())));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800821}
822
823ConstBufferPtr
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700824SecTpmOsx::decryptInTpm(const uint8_t* data, size_t dataLength, const Name& keyName, bool sym)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800825{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800826 throw Error("SecTpmOsx::decryptInTpm is not supported");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800827
Yingdi Yu2e57a582014-02-20 23:34:43 -0800828 // KeyClass keyClass;
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700829 // if (sym)
Yingdi Yu2e57a582014-02-20 23:34:43 -0800830 // keyClass = KEY_CLASS_SYMMETRIC;
831 // else
832 // keyClass = KEY_CLASS_PRIVATE;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800833
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700834 // CFDataRef dataRef = CFDataCreate(0,
Yingdi Yu2e57a582014-02-20 23:34:43 -0800835 // reinterpret_cast<const unsigned char*>(data),
836 // dataLength
837 // );
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800838
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700839 // CFReleaser<SecKeyRef> decryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
840 // if (decryptKey.get() == 0)
841 // {
842 // /// @todo Can this happen because of keychain is locked?
843 // throw Error("Decruption key [" + ??? + "] does not exist in OSX Keychain");
844 // }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800845
Yingdi Yu2e57a582014-02-20 23:34:43 -0800846 // CFErrorRef error;
847 // SecTransformRef decrypt = SecDecryptTransformCreate(decryptKey, &error);
848 // if (error) throw Error("Fail to create decrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800849
Yingdi Yu2e57a582014-02-20 23:34:43 -0800850 // Boolean set_res = SecTransformSetAttribute(decrypt,
851 // kSecTransformInputAttributeName,
852 // dataRef,
853 // &error);
854 // if (error) throw Error("Fail to configure decrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800855
Yingdi Yu2e57a582014-02-20 23:34:43 -0800856 // CFDataRef output = (CFDataRef) SecTransformExecute(decrypt, &error);
857 // if (error)
858 // {
859 // CFShow(error);
860 // throw Error("Fail to decrypt data");
861 // }
862 // if (!output) throw Error("Output is NULL!\n");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800863
Yingdi Yu2e57a582014-02-20 23:34:43 -0800864 // return make_shared<Buffer>(CFDataGetBytePtr(output), CFDataGetLength(output));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800865}
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700866
Yingdi Yu2e57a582014-02-20 23:34:43 -0800867void
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700868SecTpmOsx::addAppToAcl(const Name& keyName, KeyClass keyClass, const string& appPath, AclType acl)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800869{
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700870 if (keyClass == KEY_CLASS_PRIVATE && acl == ACL_TYPE_PRIVATE)
Yingdi Yu2e57a582014-02-20 23:34:43 -0800871 {
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700872 CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, keyClass);
873 if (privateKey.get() == 0)
874 {
875 throw Error("Private key [" + keyName.toUri() + "] does not exist in OSX Keychain");
876 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700877
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700878 CFReleaser<SecAccessRef> accRef;
Yingdi Yu7036ce22014-06-19 18:53:37 -0700879 SecKeychainItemCopyAccess(privateKey.get(), &accRef.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700880
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700881 CFReleaser<CFArrayRef> signACL = SecAccessCopyMatchingACLList(accRef.get(),
882 kSecACLAuthorizationSign);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700883
Yingdi Yu7036ce22014-06-19 18:53:37 -0700884 // C-style cast is used as per Apple convention
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700885 SecACLRef aclRef = (SecACLRef)CFArrayGetValueAtIndex(signACL.get(), 0);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700886
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700887 CFReleaser<CFArrayRef> appList;
888 CFReleaser<CFStringRef> description;
Yingdi Yu2e57a582014-02-20 23:34:43 -0800889 SecKeychainPromptSelector promptSelector;
Yingdi Yu7036ce22014-06-19 18:53:37 -0700890 SecACLCopyContents(aclRef,
891 &appList.get(),
892 &description.get(),
893 &promptSelector);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700894
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700895 CFReleaser<CFMutableArrayRef> newAppList = CFArrayCreateMutableCopy(0,
896 0,
897 appList.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700898
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700899 CFReleaser<SecTrustedApplicationRef> trustedApp;
Yingdi Yu7036ce22014-06-19 18:53:37 -0700900 SecTrustedApplicationCreateFromPath(appPath.c_str(),
901 &trustedApp.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700902
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700903 CFArrayAppendValue(newAppList.get(), trustedApp.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700904
Yingdi Yu7036ce22014-06-19 18:53:37 -0700905 SecACLSetContents(aclRef,
906 newAppList.get(),
907 description.get(),
908 promptSelector);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700909
Yingdi Yu7036ce22014-06-19 18:53:37 -0700910 SecKeychainItemSetAccess(privateKey.get(), accRef.get());
Yingdi Yu2e57a582014-02-20 23:34:43 -0800911 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800912}
913
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800914ConstBufferPtr
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700915SecTpmOsx::encryptInTpm(const uint8_t* data, size_t dataLength, const Name& keyName, bool sym)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800916{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800917 throw Error("SecTpmOsx::encryptInTpm is not supported");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800918
Yingdi Yu2e57a582014-02-20 23:34:43 -0800919 // KeyClass keyClass;
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700920 // if (sym)
Yingdi Yu2e57a582014-02-20 23:34:43 -0800921 // keyClass = KEY_CLASS_SYMMETRIC;
922 // else
923 // keyClass = KEY_CLASS_PUBLIC;
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700924
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700925 // CFDataRef dataRef = CFDataCreate(0,
Yingdi Yu2e57a582014-02-20 23:34:43 -0800926 // reinterpret_cast<const unsigned char*>(data),
927 // dataLength
928 // );
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700929
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700930 // CFReleaser<SecKeyRef> encryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
931 // if (encryptKey.get() == 0)
932 // {
933 // throw Error("Encryption key [" + ???? + "] does not exist in OSX Keychain");
934 // }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800935
Yingdi Yu2e57a582014-02-20 23:34:43 -0800936 // CFErrorRef error;
937 // SecTransformRef encrypt = SecEncryptTransformCreate(encryptKey, &error);
938 // if (error) throw Error("Fail to create encrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800939
Yingdi Yu2e57a582014-02-20 23:34:43 -0800940 // Boolean set_res = SecTransformSetAttribute(encrypt,
941 // kSecTransformInputAttributeName,
942 // dataRef,
943 // &error);
944 // if (error) throw Error("Fail to configure encrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800945
Yingdi Yu2e57a582014-02-20 23:34:43 -0800946 // CFDataRef output = (CFDataRef) SecTransformExecute(encrypt, &error);
947 // if (error) throw Error("Fail to encrypt data");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800948
Yingdi Yu2e57a582014-02-20 23:34:43 -0800949 // if (!output) throw Error("Output is NULL!\n");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800950
Yingdi Yu2e57a582014-02-20 23:34:43 -0800951 // return make_shared<Buffer> (CFDataGetBytePtr(output), CFDataGetLength(output));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800952}
953
954bool
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700955SecTpmOsx::doesKeyExistInTpm(const Name& keyName, KeyClass keyClass)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800956{
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800957 string keyNameUri = m_impl->toInternalKeyName(keyName, keyClass);
958
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700959 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
960 keyNameUri.c_str(),
961 kCFStringEncodingUTF8);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700962
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700963 CFReleaser<CFMutableDictionaryRef> attrDict =
964 CFDictionaryCreateMutable(0,
965 4,
966 &kCFTypeDictionaryKeyCallBacks,
967 0);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800968
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700969 CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
970 // CFDictionaryAddValue(attrDict.get(), kSecAttrKeyClass, m_impl->getKeyClass(keyClass));
971 CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
972 CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700973
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700974 CFReleaser<SecKeychainItemRef> itemRef;
Yingdi Yu7036ce22014-06-19 18:53:37 -0700975 // C-style cast is used as per Apple convention
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700976 OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&itemRef.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700977
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700978 if (res == errSecSuccess)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800979 return true;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800980 else
981 return false;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800982
983}
984
Yingdi Yu4b752752014-02-18 12:24:03 -0800985bool
986SecTpmOsx::generateRandomBlock(uint8_t* res, size_t size)
987{
Yingdi Yu7036ce22014-06-19 18:53:37 -0700988 return SecRandomCopyBytes(kSecRandomDefault, size, res) == 0;
Yingdi Yu4b752752014-02-18 12:24:03 -0800989}
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800990
991////////////////////////////////
992// OSXPrivateKeyStorage::Impl //
993////////////////////////////////
994
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700995CFReleaser<SecKeychainItemRef>
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700996SecTpmOsx::Impl::getKey(const Name& keyName, KeyClass keyClass)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800997{
998 string keyNameUri = toInternalKeyName(keyName, keyClass);
999
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -07001000 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
1001 keyNameUri.c_str(),
1002 kCFStringEncodingUTF8);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001003
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -07001004 CFReleaser<CFMutableDictionaryRef> attrDict =
1005 CFDictionaryCreateMutable(0,
1006 5,
1007 &kCFTypeDictionaryKeyCallBacks,
1008 0);
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001009
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -07001010 CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
1011 CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
1012 CFDictionaryAddValue(attrDict.get(), kSecAttrKeyClass, getKeyClass(keyClass));
1013 CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001014
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -07001015 CFReleaser<SecKeychainItemRef> keyItem;
Yingdi Yu7036ce22014-06-19 18:53:37 -07001016 // C-style cast is used as per Apple convention
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -07001017 OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&keyItem.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001018
Yingdi Yu4b8c6a22014-04-15 23:00:54 -07001019 if (res != errSecSuccess)
1020 return 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001021 else
1022 return keyItem;
1023}
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001024
1025string
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -07001026SecTpmOsx::Impl::toInternalKeyName(const Name& keyName, KeyClass keyClass)
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001027{
1028 string keyUri = keyName.toUri();
1029
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -07001030 if (KEY_CLASS_SYMMETRIC == keyClass)
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001031 return keyUri + "/symmetric";
1032 else
1033 return keyUri;
1034}
1035
Alexander Afanasyev24b75c82014-05-31 15:59:31 +03001036CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001037SecTpmOsx::Impl::getAsymKeyType(KeyType keyType)
1038{
Yingdi Yu7036ce22014-06-19 18:53:37 -07001039 switch (keyType) {
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001040 case KEY_TYPE_RSA:
1041 return kSecAttrKeyTypeRSA;
1042 default:
Yingdi Yu4b8c6a22014-04-15 23:00:54 -07001043 return 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001044 }
1045}
1046
Alexander Afanasyev24b75c82014-05-31 15:59:31 +03001047CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001048SecTpmOsx::Impl::getSymKeyType(KeyType keyType)
1049{
Yingdi Yu7036ce22014-06-19 18:53:37 -07001050 switch (keyType) {
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001051 case KEY_TYPE_AES:
1052 return kSecAttrKeyTypeAES;
1053 default:
Yingdi Yu4b8c6a22014-04-15 23:00:54 -07001054 return 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001055 }
1056}
1057
Alexander Afanasyev24b75c82014-05-31 15:59:31 +03001058CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001059SecTpmOsx::Impl::getKeyClass(KeyClass keyClass)
1060{
Yingdi Yu7036ce22014-06-19 18:53:37 -07001061 switch (keyClass) {
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001062 case KEY_CLASS_PRIVATE:
1063 return kSecAttrKeyClassPrivate;
1064 case KEY_CLASS_PUBLIC:
1065 return kSecAttrKeyClassPublic;
1066 case KEY_CLASS_SYMMETRIC:
1067 return kSecAttrKeyClassSymmetric;
1068 default:
Yingdi Yu4b8c6a22014-04-15 23:00:54 -07001069 return 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001070 }
1071}
1072
Alexander Afanasyev24b75c82014-05-31 15:59:31 +03001073CFStringRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001074SecTpmOsx::Impl::getDigestAlgorithm(DigestAlgorithm digestAlgo)
1075{
Yingdi Yu7036ce22014-06-19 18:53:37 -07001076 switch (digestAlgo) {
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001077 case DIGEST_ALGORITHM_SHA256:
1078 return kSecDigestSHA2;
1079 default:
Yingdi Yu4b8c6a22014-04-15 23:00:54 -07001080 return 0;
Jeff Thompson2747dc02013-10-04 19:11:34 -07001081 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001082}
Jeff Thompson2747dc02013-10-04 19:11:34 -07001083
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001084long
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001085SecTpmOsx::Impl::getDigestSize(DigestAlgorithm digestAlgo)
1086{
Yingdi Yu7036ce22014-06-19 18:53:37 -07001087 switch (digestAlgo) {
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001088 case DIGEST_ALGORITHM_SHA256:
1089 return 256;
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001090 default:
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001091 return -1;
Jeff Thompson2747dc02013-10-04 19:11:34 -07001092 }
Jeff Thompson2747dc02013-10-04 19:11:34 -07001093}
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001094
Yingdi Yufc40d872014-02-18 12:56:04 -08001095} // namespace ndn