blob: f3a782bc25633968254237ac5271647e25e97c09 [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 }
Yingdi Yuc8f883c2014-06-20 23:25:22 -0700399 case KEY_TYPE_ECDSA:
400 {
401 const EcdsaKeyParams& ecdsaParams = static_cast<const EcdsaKeyParams&>(params);
402 keySize = ecdsaParams.getKeySize();
403 break;
404 }
Yingdi Yu7036ce22014-06-19 18:53:37 -0700405 default:
406 throw Error("Fail to create a key pair: Unsupported key type");
407 }
408
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700409 CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(0, kCFNumberIntType, &keySize);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800410
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700411 CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType, m_impl->getAsymKeyType(keyType));
412 CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
413 CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800414
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700415 CFReleaser<SecKeyRef> publicKey, privateKey;
Yingdi Yu7036ce22014-06-19 18:53:37 -0700416 // C-style cast is used as per Apple convention
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700417 OSStatus res = SecKeyGeneratePair((CFDictionaryRef)attrDict.get(),
418 &publicKey.get(), &privateKey.get());
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800419
Yingdi Yube4150e2014-02-18 13:02:46 -0800420 if (res == errSecSuccess)
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800421 {
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800422 return;
423 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700424
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700425 if (res == errSecAuthFailed && !needRetry)
Yingdi Yube4150e2014-02-18 13:02:46 -0800426 {
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700427 if (unlockTpm(0, 0, false))
Yingdi Yu7036ce22014-06-19 18:53:37 -0700428 generateKeyPairInTpmInternal(keyName, params, true);
Yingdi Yu2e57a582014-02-20 23:34:43 -0800429 else
430 throw Error("Fail to unlock the keychain");
Yingdi Yube4150e2014-02-18 13:02:46 -0800431 }
432 else
433 {
Yingdi Yube4150e2014-02-18 13:02:46 -0800434 throw Error("Fail to create a key pair");
435 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800436}
437
438void
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700439SecTpmOsx::deleteKeyPairInTpmInternal(const Name& keyName, bool needRetry)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800440{
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700441 CFReleaser<CFStringRef> keyLabel =
442 CFStringCreateWithCString(0,
443 keyName.toUri().c_str(),
444 kCFStringEncodingUTF8);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800445
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700446 CFReleaser<CFMutableDictionaryRef> searchDict =
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700447 CFDictionaryCreateMutable(0, 5,
448 &kCFTypeDictionaryKeyCallBacks,
449 &kCFTypeDictionaryValueCallBacks);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800450
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700451 CFDictionaryAddValue(searchDict.get(), kSecClass, kSecClassKey);
452 CFDictionaryAddValue(searchDict.get(), kSecAttrLabel, keyLabel.get());
453 CFDictionaryAddValue(searchDict.get(), kSecMatchLimit, kSecMatchLimitAll);
454 OSStatus res = SecItemDelete(searchDict.get());
Yingdi Yube4150e2014-02-18 13:02:46 -0800455
456 if (res == errSecSuccess)
457 return;
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700458
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700459 if (res == errSecAuthFailed && !needRetry)
Yingdi Yube4150e2014-02-18 13:02:46 -0800460 {
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700461 if (unlockTpm(0, 0, false))
Yingdi Yu2e57a582014-02-20 23:34:43 -0800462 deleteKeyPairInTpmInternal(keyName, true);
Yingdi Yube4150e2014-02-18 13:02:46 -0800463 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800464}
465
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700466void
Yingdi Yu7036ce22014-06-19 18:53:37 -0700467SecTpmOsx::generateSymmetricKeyInTpm(const Name& keyName, const KeyParams& params)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800468{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800469 throw Error("SecTpmOsx::generateSymmetricKeyInTpm is not supported");
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700470 // if (doesKeyExistInTpm(keyName, KEY_CLASS_SYMMETRIC))
Yingdi Yu2e57a582014-02-20 23:34:43 -0800471 // throw Error("keyName has existed!");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800472
Yingdi Yu2e57a582014-02-20 23:34:43 -0800473 // string keyNameUri = m_impl->toInternalKeyName(keyName, KEY_CLASS_SYMMETRIC);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800474
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700475 // CFReleaser<CFMutableDictionaryRef> attrDict =
476 // CFDictionaryCreateMutable(kCFAllocatorDefault,
477 // 0,
478 // &kCFTypeDictionaryKeyCallBacks,
479 // &kCFTypeDictionaryValueCallBacks);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800480
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700481 // CFReleaser<CFStringRef> keyLabel =
482 // CFStringCreateWithCString(0,
483 // keyNameUri.c_str(),
484 // kCFStringEncodingUTF8);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800485
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700486 // CFReleaser<CFNumberRef> cfKeySize = CFNumberCreate(0, kCFNumberIntType, &keySize);
487
488 // CFDictionaryAddValue(attrDict.get(), kSecAttrKeyType, m_impl->getSymKeyType(keyType));
489 // CFDictionaryAddValue(attrDict.get(), kSecAttrKeySizeInBits, cfKeySize.get());
490 // CFDictionaryAddValue(attrDict.get(), kSecAttrIsPermanent, kCFBooleanTrue);
491 // CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800492
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700493 // CFErrorRef error = 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800494
Yingdi Yu2e57a582014-02-20 23:34:43 -0800495 // SecKeyRef symmetricKey = SecKeyGenerateSymmetric(attrDict, &error);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800496
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700497 // if (error)
Yingdi Yu2e57a582014-02-20 23:34:43 -0800498 // throw Error("Fail to create a symmetric key");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800499}
500
Yingdi Yu2e57a582014-02-20 23:34:43 -0800501shared_ptr<PublicKey>
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700502SecTpmOsx::getPublicKeyFromTpm(const Name& keyName)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800503{
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700504 CFReleaser<SecKeychainItemRef> publicKey = m_impl->getKey(keyName, KEY_CLASS_PUBLIC);
505 if (publicKey.get() == 0)
506 {
507 throw Error("Requested public key [" + keyName.toUri() + "] does not exist in OSX Keychain");
508 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800509
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700510 CFReleaser<CFDataRef> exportedKey;
511 OSStatus res = SecItemExport(publicKey.get(),
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800512 kSecFormatOpenSSL,
513 0,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700514 0,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700515 &exportedKey.get());
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800516 if (res != errSecSuccess)
517 {
518 throw Error("Cannot export requested public key from OSX Keychain");
519 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800520
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700521 shared_ptr<PublicKey> key = make_shared<PublicKey>(CFDataGetBytePtr(exportedKey.get()),
522 CFDataGetLength(exportedKey.get()));
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800523 return key;
524}
525
526ConstBufferPtr
Yingdi Yu5e96e002014-04-23 18:32:15 -0700527SecTpmOsx::exportPrivateKeyPkcs8FromTpmInternal(const Name& keyName, bool needRetry)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800528{
529 using namespace CryptoPP;
530
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700531 CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, KEY_CLASS_PRIVATE);
532 if (privateKey.get() == 0)
533 {
534 /// @todo Can this happen because of keychain is locked?
535 throw Error("Private key [" + keyName.toUri() + "] does not exist in OSX Keychain");
536 }
537
538 CFReleaser<CFDataRef> exportedKey;
539 OSStatus res = SecItemExport(privateKey.get(),
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800540 kSecFormatOpenSSL,
541 0,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700542 0,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700543 &exportedKey.get());
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800544
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700545 if (res != errSecSuccess)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800546 {
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700547 if (res == errSecAuthFailed && !needRetry)
Yingdi Yube4150e2014-02-18 13:02:46 -0800548 {
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700549 if (unlockTpm(0, 0, false))
Yingdi Yu5e96e002014-04-23 18:32:15 -0700550 return exportPrivateKeyPkcs8FromTpmInternal(keyName, true);
Yingdi Yu2e57a582014-02-20 23:34:43 -0800551 else
552 return shared_ptr<Buffer>();
Yingdi Yube4150e2014-02-18 13:02:46 -0800553 }
554 else
555 return shared_ptr<Buffer>();
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800556 }
557
558 OBufferStream pkcs1Os;
559 FileSink sink(pkcs1Os);
560
561 uint32_t version = 0;
Yingdi Yu7036ce22014-06-19 18:53:37 -0700562 OID algorithm("1.2.840.113549.1.1.1"); // "RSA encryption"
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800563 SecByteBlock rawKeyBits;
564 // PrivateKeyInfo ::= SEQUENCE {
565 // version INTEGER,
566 // privateKeyAlgorithm SEQUENCE,
567 // privateKey OCTECT STRING}
568 DERSequenceEncoder privateKeyInfo(sink);
569 {
570 DEREncodeUnsigned<uint32_t>(privateKeyInfo, version, INTEGER);
571 DERSequenceEncoder privateKeyAlgorithm(privateKeyInfo);
572 {
573 algorithm.encode(privateKeyAlgorithm);
574 DEREncodeNull(privateKeyAlgorithm);
575 }
576 privateKeyAlgorithm.MessageEnd();
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700577 DEREncodeOctetString(privateKeyInfo,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700578 CFDataGetBytePtr(exportedKey.get()),
579 CFDataGetLength(exportedKey.get()));
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800580 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700581 privateKeyInfo.MessageEnd();
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800582
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800583 return pkcs1Os.buf();
584}
585
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700586#ifdef __GNUC__
587#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
588#pragma GCC diagnostic push
589#endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
590#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
591#endif // __GNUC__
592
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800593bool
Yingdi Yu5e96e002014-04-23 18:32:15 -0700594SecTpmOsx::importPrivateKeyPkcs8IntoTpmInternal(const Name& keyName,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700595 const uint8_t* buf, size_t size,
596 bool needRetry)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800597{
598 using namespace CryptoPP;
599
600 StringSource privateKeySource(buf, size, true);
601 uint32_t tmpNum;
602 OID tmpOID;
603 SecByteBlock rawKeyBits;
604 // PrivateKeyInfo ::= SEQUENCE {
605 // INTEGER,
606 // SEQUENCE,
607 // OCTECT STRING}
608 BERSequenceDecoder privateKeyInfo(privateKeySource);
609 {
610 BERDecodeUnsigned<uint32_t>(privateKeyInfo, tmpNum, INTEGER);
611 BERSequenceDecoder sequenceDecoder(privateKeyInfo);
612 {
613 tmpOID.decode(sequenceDecoder);
614 BERDecodeNull(sequenceDecoder);
615 }
616 BERDecodeOctetString(privateKeyInfo, rawKeyBits);
617 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700618 privateKeyInfo.MessageEnd();
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800619
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700620 CFReleaser<CFDataRef> importedKey = CFDataCreateWithBytesNoCopy(0,
621 rawKeyBits.BytePtr(),
622 rawKeyBits.size(),
623 kCFAllocatorNull);
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800624
625 SecExternalFormat externalFormat = kSecFormatOpenSSL;
626 SecExternalItemType externalType = kSecItemTypePrivateKey;
627 SecKeyImportExportParameters keyParams;
628 memset(&keyParams, 0, sizeof(keyParams));
629 keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
630 keyParams.keyAttributes = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT;
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700631 CFReleaser<SecAccessRef> access;
632 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
633 keyName.toUri().c_str(),
634 kCFStringEncodingUTF8);
635 SecAccessCreate(keyLabel.get(), 0, &access.get());
636 keyParams.accessRef = access.get();
637 CFReleaser<CFArrayRef> outItems;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800638
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700639#ifdef __clang__
Junxiao Shi482ccc52014-03-31 13:05:24 -0700640#pragma clang diagnostic push
641#pragma clang diagnostic ignored "-Wdeprecated-declarations"
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700642#endif // __clang__
643
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700644 OSStatus res = SecKeychainItemImport(importedKey.get(),
645 0,
646 &externalFormat,
647 &externalType,
648 0,
649 &keyParams,
650 m_impl->m_keyChainRef,
651 &outItems.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700652
653#ifdef __clang__
Junxiao Shi482ccc52014-03-31 13:05:24 -0700654#pragma clang diagnostic pop
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700655#endif // __clang__
656
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700657 if (res != errSecSuccess)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800658 {
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700659 if (res == errSecAuthFailed && !needRetry)
Yingdi Yube4150e2014-02-18 13:02:46 -0800660 {
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700661 if (unlockTpm(0, 0, false))
Yingdi Yu5e96e002014-04-23 18:32:15 -0700662 return importPrivateKeyPkcs8IntoTpmInternal(keyName, buf, size, true);
Yingdi Yu2e57a582014-02-20 23:34:43 -0800663 else
664 return false;
Yingdi Yube4150e2014-02-18 13:02:46 -0800665 }
666 else
667 return false;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800668 }
669
Yingdi Yu7036ce22014-06-19 18:53:37 -0700670 // C-style cast is used as per Apple convention
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700671 SecKeychainItemRef privateKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800672 SecKeychainAttribute attrs[1]; // maximum number of attributes
673 SecKeychainAttributeList attrList = { 0, attrs };
674 string keyUri = keyName.toUri();
675 {
676 attrs[attrList.count].tag = kSecKeyPrintName;
677 attrs[attrList.count].length = keyUri.size();
Yingdi Yu7036ce22014-06-19 18:53:37 -0700678 attrs[attrList.count].data = const_cast<char*>(keyUri.c_str());
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800679 attrList.count++;
680 }
681
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700682 res = SecKeychainItemModifyAttributesAndData(privateKey,
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800683 &attrList,
684 0,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700685 0);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700686
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700687 if (res != errSecSuccess)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800688 {
689 return false;
690 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700691
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800692 return true;
693}
694
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700695#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
696#pragma GCC diagnostic pop
697#endif // __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
698
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800699bool
700SecTpmOsx::importPublicKeyPkcs1IntoTpm(const Name& keyName, const uint8_t* buf, size_t size)
701{
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700702 CFReleaser<CFDataRef> importedKey = CFDataCreateWithBytesNoCopy(0,
703 buf,
704 size,
705 kCFAllocatorNull);
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800706
707 SecExternalFormat externalFormat = kSecFormatOpenSSL;
708 SecExternalItemType externalType = kSecItemTypePublicKey;
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700709 CFReleaser<CFArrayRef> outItems;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800710
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700711 OSStatus res = SecItemImport(importedKey.get(),
712 0,
713 &externalFormat,
714 &externalType,
715 0,
716 0,
717 m_impl->m_keyChainRef,
718 &outItems.get());
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800719
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700720 if (res != errSecSuccess)
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800721 return false;
722
Yingdi Yu7036ce22014-06-19 18:53:37 -0700723 // C-style cast is used as per Apple convention
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700724 SecKeychainItemRef publicKey = (SecKeychainItemRef)CFArrayGetValueAtIndex(outItems.get(), 0);
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800725 SecKeychainAttribute attrs[1]; // maximum number of attributes
726 SecKeychainAttributeList attrList = { 0, attrs };
727 string keyUri = keyName.toUri();
728 {
729 attrs[attrList.count].tag = kSecKeyPrintName;
730 attrs[attrList.count].length = keyUri.size();
Yingdi Yu7036ce22014-06-19 18:53:37 -0700731 attrs[attrList.count].data = const_cast<char*>(keyUri.c_str());
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800732 attrList.count++;
733 }
734
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700735 res = SecKeychainItemModifyAttributesAndData(publicKey,
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800736 &attrList,
737 0,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700738 0);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700739
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700740 if (res != errSecSuccess)
Alexander Afanasyev60c86812014-02-20 15:19:33 -0800741 return false;
742
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800743 return true;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800744}
745
746Block
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700747SecTpmOsx::signInTpmInternal(const uint8_t* data, size_t dataLength,
748 const Name& keyName, DigestAlgorithm digestAlgorithm, bool needRetry)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800749{
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700750 CFReleaser<CFDataRef> dataRef = CFDataCreateWithBytesNoCopy(0,
751 data,
752 dataLength,
753 kCFAllocatorNull);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800754
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700755 CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, KEY_CLASS_PRIVATE);
756 if (privateKey.get() == 0)
757 {
758 throw Error("Private key [" + keyName.toUri() + "] does not exist in OSX Keychain");
759 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700760
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700761 CFReleaser<CFErrorRef> error;
Yingdi Yu7036ce22014-06-19 18:53:37 -0700762 // C-style cast is used as per Apple convention
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700763 CFReleaser<SecTransformRef> signer = SecSignTransformCreate((SecKeyRef)privateKey.get(),
764 &error.get());
765 if (error.get() != 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700766 throw Error("Fail to create signer");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800767
768 // Set input
Yingdi Yu7036ce22014-06-19 18:53:37 -0700769 SecTransformSetAttribute(signer.get(),
770 kSecTransformInputAttributeName,
771 dataRef.get(),
772 &error.get());
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700773 if (error.get() != 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700774 throw Error("Fail to configure input of signer");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800775
776 // Enable use of padding
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700777 SecTransformSetAttribute(signer.get(),
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800778 kSecPaddingKey,
779 kSecPaddingPKCS1Key,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700780 &error.get());
781 if (error.get() != 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700782 throw Error("Fail to configure digest algorithm of signer");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800783
784 // Set padding type
Yingdi Yu7036ce22014-06-19 18:53:37 -0700785 SecTransformSetAttribute(signer.get(),
786 kSecDigestTypeAttribute,
787 m_impl->getDigestAlgorithm(digestAlgorithm),
788 &error.get());
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700789 if (error.get() != 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700790 throw Error("Fail to configure digest algorithm of signer");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800791
792 // Set padding attribute
793 long digestSize = m_impl->getDigestSize(digestAlgorithm);
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700794 CFReleaser<CFNumberRef> cfDigestSize = CFNumberCreate(0, kCFNumberLongType, &digestSize);
Yingdi Yu7036ce22014-06-19 18:53:37 -0700795 SecTransformSetAttribute(signer.get(),
796 kSecDigestLengthAttribute,
797 cfDigestSize.get(),
798 &error.get());
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700799 if (error.get() != 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700800 throw Error("Fail to configure digest size of signer");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800801
802 // Actually sign
Yingdi Yu7036ce22014-06-19 18:53:37 -0700803 // C-style cast is used as per Apple convention
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700804 CFReleaser<CFDataRef> signature = (CFDataRef)SecTransformExecute(signer.get(), &error.get());
805 if (error.get() != 0)
Yingdi Yube4150e2014-02-18 13:02:46 -0800806 {
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700807 if (!needRetry)
Yingdi Yube4150e2014-02-18 13:02:46 -0800808 {
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700809 if (unlockTpm(0, 0, false))
Yingdi Yu2e57a582014-02-20 23:34:43 -0800810 return signInTpmInternal(data, dataLength, keyName, digestAlgorithm, true);
811 else
812 throw Error("Fail to unlock the keychain");
Yingdi Yube4150e2014-02-18 13:02:46 -0800813 }
814 else
815 {
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700816 CFShow(error.get());
Yingdi Yube4150e2014-02-18 13:02:46 -0800817 throw Error("Fail to sign data");
818 }
819 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800820
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700821 if (signature.get() == 0)
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700822 throw Error("Signature is NULL!\n");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800823
824 return Block(Tlv::SignatureValue,
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700825 make_shared<Buffer>(CFDataGetBytePtr(signature.get()),
826 CFDataGetLength(signature.get())));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800827}
828
829ConstBufferPtr
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700830SecTpmOsx::decryptInTpm(const uint8_t* data, size_t dataLength, const Name& keyName, bool sym)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800831{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800832 throw Error("SecTpmOsx::decryptInTpm is not supported");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800833
Yingdi Yu2e57a582014-02-20 23:34:43 -0800834 // KeyClass keyClass;
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700835 // if (sym)
Yingdi Yu2e57a582014-02-20 23:34:43 -0800836 // keyClass = KEY_CLASS_SYMMETRIC;
837 // else
838 // keyClass = KEY_CLASS_PRIVATE;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800839
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700840 // CFDataRef dataRef = CFDataCreate(0,
Yingdi Yu2e57a582014-02-20 23:34:43 -0800841 // reinterpret_cast<const unsigned char*>(data),
842 // dataLength
843 // );
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800844
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700845 // CFReleaser<SecKeyRef> decryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
846 // if (decryptKey.get() == 0)
847 // {
848 // /// @todo Can this happen because of keychain is locked?
849 // throw Error("Decruption key [" + ??? + "] does not exist in OSX Keychain");
850 // }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800851
Yingdi Yu2e57a582014-02-20 23:34:43 -0800852 // CFErrorRef error;
853 // SecTransformRef decrypt = SecDecryptTransformCreate(decryptKey, &error);
854 // if (error) throw Error("Fail to create decrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800855
Yingdi Yu2e57a582014-02-20 23:34:43 -0800856 // Boolean set_res = SecTransformSetAttribute(decrypt,
857 // kSecTransformInputAttributeName,
858 // dataRef,
859 // &error);
860 // if (error) throw Error("Fail to configure decrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800861
Yingdi Yu2e57a582014-02-20 23:34:43 -0800862 // CFDataRef output = (CFDataRef) SecTransformExecute(decrypt, &error);
863 // if (error)
864 // {
865 // CFShow(error);
866 // throw Error("Fail to decrypt data");
867 // }
868 // if (!output) throw Error("Output is NULL!\n");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800869
Yingdi Yu2e57a582014-02-20 23:34:43 -0800870 // return make_shared<Buffer>(CFDataGetBytePtr(output), CFDataGetLength(output));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800871}
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700872
Yingdi Yu2e57a582014-02-20 23:34:43 -0800873void
Yingdi Yuf56c68f2014-04-24 21:50:13 -0700874SecTpmOsx::addAppToAcl(const Name& keyName, KeyClass keyClass, const string& appPath, AclType acl)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800875{
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700876 if (keyClass == KEY_CLASS_PRIVATE && acl == ACL_TYPE_PRIVATE)
Yingdi Yu2e57a582014-02-20 23:34:43 -0800877 {
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700878 CFReleaser<SecKeychainItemRef> privateKey = m_impl->getKey(keyName, keyClass);
879 if (privateKey.get() == 0)
880 {
881 throw Error("Private key [" + keyName.toUri() + "] does not exist in OSX Keychain");
882 }
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700883
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700884 CFReleaser<SecAccessRef> accRef;
Yingdi Yu7036ce22014-06-19 18:53:37 -0700885 SecKeychainItemCopyAccess(privateKey.get(), &accRef.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700886
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700887 CFReleaser<CFArrayRef> signACL = SecAccessCopyMatchingACLList(accRef.get(),
888 kSecACLAuthorizationSign);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700889
Yingdi Yu7036ce22014-06-19 18:53:37 -0700890 // C-style cast is used as per Apple convention
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700891 SecACLRef aclRef = (SecACLRef)CFArrayGetValueAtIndex(signACL.get(), 0);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700892
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700893 CFReleaser<CFArrayRef> appList;
894 CFReleaser<CFStringRef> description;
Yingdi Yu2e57a582014-02-20 23:34:43 -0800895 SecKeychainPromptSelector promptSelector;
Yingdi Yu7036ce22014-06-19 18:53:37 -0700896 SecACLCopyContents(aclRef,
897 &appList.get(),
898 &description.get(),
899 &promptSelector);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700900
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700901 CFReleaser<CFMutableArrayRef> newAppList = CFArrayCreateMutableCopy(0,
902 0,
903 appList.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700904
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700905 CFReleaser<SecTrustedApplicationRef> trustedApp;
Yingdi Yu7036ce22014-06-19 18:53:37 -0700906 SecTrustedApplicationCreateFromPath(appPath.c_str(),
907 &trustedApp.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700908
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700909 CFArrayAppendValue(newAppList.get(), trustedApp.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700910
Yingdi Yu7036ce22014-06-19 18:53:37 -0700911 SecACLSetContents(aclRef,
912 newAppList.get(),
913 description.get(),
914 promptSelector);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700915
Yingdi Yu7036ce22014-06-19 18:53:37 -0700916 SecKeychainItemSetAccess(privateKey.get(), accRef.get());
Yingdi Yu2e57a582014-02-20 23:34:43 -0800917 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800918}
919
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800920ConstBufferPtr
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700921SecTpmOsx::encryptInTpm(const uint8_t* data, size_t dataLength, const Name& keyName, bool sym)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800922{
Yingdi Yu2e57a582014-02-20 23:34:43 -0800923 throw Error("SecTpmOsx::encryptInTpm is not supported");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800924
Yingdi Yu2e57a582014-02-20 23:34:43 -0800925 // KeyClass keyClass;
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700926 // if (sym)
Yingdi Yu2e57a582014-02-20 23:34:43 -0800927 // keyClass = KEY_CLASS_SYMMETRIC;
928 // else
929 // keyClass = KEY_CLASS_PUBLIC;
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700930
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700931 // CFDataRef dataRef = CFDataCreate(0,
Yingdi Yu2e57a582014-02-20 23:34:43 -0800932 // reinterpret_cast<const unsigned char*>(data),
933 // dataLength
934 // );
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700935
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700936 // CFReleaser<SecKeyRef> encryptKey = (SecKeyRef)m_impl->getKey(keyName, keyClass);
937 // if (encryptKey.get() == 0)
938 // {
939 // throw Error("Encryption key [" + ???? + "] does not exist in OSX Keychain");
940 // }
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800941
Yingdi Yu2e57a582014-02-20 23:34:43 -0800942 // CFErrorRef error;
943 // SecTransformRef encrypt = SecEncryptTransformCreate(encryptKey, &error);
944 // if (error) throw Error("Fail to create encrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800945
Yingdi Yu2e57a582014-02-20 23:34:43 -0800946 // Boolean set_res = SecTransformSetAttribute(encrypt,
947 // kSecTransformInputAttributeName,
948 // dataRef,
949 // &error);
950 // if (error) throw Error("Fail to configure encrypt");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800951
Yingdi Yu2e57a582014-02-20 23:34:43 -0800952 // CFDataRef output = (CFDataRef) SecTransformExecute(encrypt, &error);
953 // if (error) throw Error("Fail to encrypt data");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800954
Yingdi Yu2e57a582014-02-20 23:34:43 -0800955 // if (!output) throw Error("Output is NULL!\n");
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800956
Yingdi Yu2e57a582014-02-20 23:34:43 -0800957 // return make_shared<Buffer> (CFDataGetBytePtr(output), CFDataGetLength(output));
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800958}
959
960bool
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700961SecTpmOsx::doesKeyExistInTpm(const Name& keyName, KeyClass keyClass)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800962{
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800963 string keyNameUri = m_impl->toInternalKeyName(keyName, keyClass);
964
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700965 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
966 keyNameUri.c_str(),
967 kCFStringEncodingUTF8);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700968
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700969 CFReleaser<CFMutableDictionaryRef> attrDict =
970 CFDictionaryCreateMutable(0,
971 4,
972 &kCFTypeDictionaryKeyCallBacks,
973 0);
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800974
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700975 CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
976 // CFDictionaryAddValue(attrDict.get(), kSecAttrKeyClass, m_impl->getKeyClass(keyClass));
977 CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
978 CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700979
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700980 CFReleaser<SecKeychainItemRef> itemRef;
Yingdi Yu7036ce22014-06-19 18:53:37 -0700981 // C-style cast is used as per Apple convention
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -0700982 OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&itemRef.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -0700983
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -0700984 if (res == errSecSuccess)
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800985 return true;
Yingdi Yu8dceb1d2014-02-18 12:45:10 -0800986 else
987 return false;
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800988
989}
990
Yingdi Yu4b752752014-02-18 12:24:03 -0800991bool
992SecTpmOsx::generateRandomBlock(uint8_t* res, size_t size)
993{
Yingdi Yu7036ce22014-06-19 18:53:37 -0700994 return SecRandomCopyBytes(kSecRandomDefault, size, res) == 0;
Yingdi Yu4b752752014-02-18 12:24:03 -0800995}
Yingdi Yu2b2b4792014-02-04 16:27:07 -0800996
997////////////////////////////////
998// OSXPrivateKeyStorage::Impl //
999////////////////////////////////
1000
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -07001001CFReleaser<SecKeychainItemRef>
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -07001002SecTpmOsx::Impl::getKey(const Name& keyName, KeyClass keyClass)
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001003{
1004 string keyNameUri = toInternalKeyName(keyName, keyClass);
1005
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -07001006 CFReleaser<CFStringRef> keyLabel = CFStringCreateWithCString(0,
1007 keyNameUri.c_str(),
1008 kCFStringEncodingUTF8);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001009
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -07001010 CFReleaser<CFMutableDictionaryRef> attrDict =
1011 CFDictionaryCreateMutable(0,
1012 5,
1013 &kCFTypeDictionaryKeyCallBacks,
1014 0);
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001015
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -07001016 CFDictionaryAddValue(attrDict.get(), kSecClass, kSecClassKey);
1017 CFDictionaryAddValue(attrDict.get(), kSecAttrLabel, keyLabel.get());
1018 CFDictionaryAddValue(attrDict.get(), kSecAttrKeyClass, getKeyClass(keyClass));
1019 CFDictionaryAddValue(attrDict.get(), kSecReturnRef, kCFBooleanTrue);
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001020
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -07001021 CFReleaser<SecKeychainItemRef> keyItem;
Yingdi Yu7036ce22014-06-19 18:53:37 -07001022 // C-style cast is used as per Apple convention
Alexander Afanasyevf82d13a2014-04-30 14:30:19 -07001023 OSStatus res = SecItemCopyMatching((CFDictionaryRef)attrDict.get(), (CFTypeRef*)&keyItem.get());
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001024
Yingdi Yu4b8c6a22014-04-15 23:00:54 -07001025 if (res != errSecSuccess)
1026 return 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001027 else
1028 return keyItem;
1029}
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001030
1031string
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -07001032SecTpmOsx::Impl::toInternalKeyName(const Name& keyName, KeyClass keyClass)
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001033{
1034 string keyUri = keyName.toUri();
1035
Alexander Afanasyevfdbfc6d2014-04-14 15:12:11 -07001036 if (KEY_CLASS_SYMMETRIC == keyClass)
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001037 return keyUri + "/symmetric";
1038 else
1039 return keyUri;
1040}
1041
Alexander Afanasyev24b75c82014-05-31 15:59:31 +03001042CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001043SecTpmOsx::Impl::getAsymKeyType(KeyType keyType)
1044{
Yingdi Yu7036ce22014-06-19 18:53:37 -07001045 switch (keyType) {
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001046 case KEY_TYPE_RSA:
1047 return kSecAttrKeyTypeRSA;
Yingdi Yuc8f883c2014-06-20 23:25:22 -07001048 case KEY_TYPE_ECDSA:
1049 return kSecAttrKeyTypeECDSA;
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001050 default:
Yingdi Yu4b8c6a22014-04-15 23:00:54 -07001051 return 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001052 }
1053}
1054
Alexander Afanasyev24b75c82014-05-31 15:59:31 +03001055CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001056SecTpmOsx::Impl::getSymKeyType(KeyType keyType)
1057{
Yingdi Yu7036ce22014-06-19 18:53:37 -07001058 switch (keyType) {
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001059 case KEY_TYPE_AES:
1060 return kSecAttrKeyTypeAES;
1061 default:
Yingdi Yu4b8c6a22014-04-15 23:00:54 -07001062 return 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001063 }
1064}
1065
Alexander Afanasyev24b75c82014-05-31 15:59:31 +03001066CFTypeRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001067SecTpmOsx::Impl::getKeyClass(KeyClass keyClass)
1068{
Yingdi Yu7036ce22014-06-19 18:53:37 -07001069 switch (keyClass) {
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001070 case KEY_CLASS_PRIVATE:
1071 return kSecAttrKeyClassPrivate;
1072 case KEY_CLASS_PUBLIC:
1073 return kSecAttrKeyClassPublic;
1074 case KEY_CLASS_SYMMETRIC:
1075 return kSecAttrKeyClassSymmetric;
1076 default:
Yingdi Yu4b8c6a22014-04-15 23:00:54 -07001077 return 0;
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001078 }
1079}
1080
Alexander Afanasyev24b75c82014-05-31 15:59:31 +03001081CFStringRef
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001082SecTpmOsx::Impl::getDigestAlgorithm(DigestAlgorithm digestAlgo)
1083{
Yingdi Yu7036ce22014-06-19 18:53:37 -07001084 switch (digestAlgo) {
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001085 case DIGEST_ALGORITHM_SHA256:
1086 return kSecDigestSHA2;
1087 default:
Yingdi Yu4b8c6a22014-04-15 23:00:54 -07001088 return 0;
Jeff Thompson2747dc02013-10-04 19:11:34 -07001089 }
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001090}
Jeff Thompson2747dc02013-10-04 19:11:34 -07001091
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001092long
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001093SecTpmOsx::Impl::getDigestSize(DigestAlgorithm digestAlgo)
1094{
Yingdi Yu7036ce22014-06-19 18:53:37 -07001095 switch (digestAlgo) {
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001096 case DIGEST_ALGORITHM_SHA256:
1097 return 256;
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001098 default:
Yingdi Yu2b2b4792014-02-04 16:27:07 -08001099 return -1;
Jeff Thompson2747dc02013-10-04 19:11:34 -07001100 }
Jeff Thompson2747dc02013-10-04 19:11:34 -07001101}
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -07001102
Yingdi Yufc40d872014-02-18 12:56:04 -08001103} // namespace ndn