/ OSX / sec / Security / SecEMCS.m
SecEMCS.m
  1  /*
  2   * Copyright (c) 2015 Apple Inc. All Rights Reserved.
  3   *
  4   * @APPLE_LICENSE_HEADER_START@
  5   *
  6   * This file contains Original Code and/or Modifications of Original Code
  7   * as defined in and that are subject to the Apple Public Source License
  8   * Version 2.0 (the 'License'). You may not use this file except in
  9   * compliance with the License. Please obtain a copy of the License at
 10   * http://www.opensource.apple.com/apsl/ and read it before using this
 11   * file.
 12   *
 13   * The Original Code and all software distributed under the License are
 14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 18   * Please see the License for the specific language governing rights and
 19   * limitations under the License.
 20   *
 21   * @APPLE_LICENSE_HEADER_END@
 22   */
 23  
 24  #define __KEYCHAINCORE__ 1
 25  
 26  #include <Foundation/Foundation.h>
 27  #include <Security/SecBase.h>
 28  #include <Security/SecBasePriv.h>
 29  #include <Security/SecCFAllocator.h>
 30  #include <corecrypto/ccpbkdf2.h>
 31  #include <corecrypto/ccsha2.h>
 32  #include <corecrypto/ccaes.h>
 33  #include <corecrypto/ccmode.h>
 34  #include <corecrypto/ccwrap.h>
 35  
 36  #include <utilities/SecCFWrappers.h>
 37  #include <AssertMacros.h>
 38  
 39  #include "SecEMCSPriv.h"
 40  
 41  static CFStringRef kiDMSSalt = CFSTR("salt");
 42  static CFStringRef kiDMSIterrations = CFSTR("iter");
 43  static CFStringRef kiDMSWrapEMCSKey = CFSTR("wkey");
 44  
 45  #define MIN_ITERATIONS  1000
 46  #define MIN_SALTLEN 16
 47  #define KEY_LENGTH 16
 48  
 49  /*
 50   *
 51   */
 52  
 53  static CFDataRef
 54  CopyWrappedKey(CFDataRef wrappingKey, CFDataRef unwrappedKey)
 55  {
 56      const struct ccmode_ecb *ecb_mode = ccaes_ecb_encrypt_mode();
 57      ccecb_ctx_decl(ccecb_context_size(ecb_mode), key);
 58      CFMutableDataRef wrappedKey = NULL;
 59  
 60      require(CFDataGetLength(wrappingKey) == KEY_LENGTH, out);
 61  
 62      ccecb_init(ecb_mode, key, CFDataGetLength(wrappingKey), CFDataGetBytePtr(wrappingKey));
 63  
 64      wrappedKey = CFDataCreateMutableWithScratch(NULL, ccwrap_wrapped_size(CFDataGetLength(unwrappedKey)));
 65      require(wrappingKey, out);
 66  
 67      size_t obytes = 0;
 68      int wrap_status = ccwrap_auth_encrypt(ecb_mode, key, CFDataGetLength(unwrappedKey), CFDataGetBytePtr(unwrappedKey),
 69                                            &obytes, CFDataGetMutableBytePtr(wrappedKey));
 70      if (wrap_status == 0) {
 71          assert(obytes == (size_t)CFDataGetLength(wrappedKey));
 72      } else {
 73          CFReleaseNull(wrappedKey);
 74          goto out;
 75      }
 76  
 77   out:
 78      ccecb_ctx_clear(ccecb_context_size(ecb_mode), key);
 79      return wrappedKey;
 80  }
 81  
 82  static NSData *
 83  CopyUnwrappedKey(CFDataRef wrappingKey, CFDataRef wrappedKey)
 84  {
 85      const struct ccmode_ecb *ecb_mode = ccaes_ecb_decrypt_mode();
 86      ccecb_ctx_decl(ccecb_context_size(ecb_mode), key);
 87      NSMutableData *unwrappedKey = NULL;
 88  
 89      require(CFDataGetLength(wrappedKey) >= CCWRAP_SEMIBLOCK, out);
 90      require(CFDataGetLength(wrappingKey) == KEY_LENGTH, out);
 91  
 92      ccecb_init(ecb_mode, key, CFDataGetLength(wrappingKey), CFDataGetBytePtr(wrappingKey));
 93  
 94      unwrappedKey = CFBridgingRelease(CFDataCreateMutableWithScratch(SecCFAllocatorZeroize(), ccwrap_unwrapped_size(CFDataGetLength(wrappedKey))));
 95      require(unwrappedKey, out);
 96  
 97      size_t obytes = 0;
 98      int unwrap_status = ccwrap_auth_decrypt(ecb_mode, key, CFDataGetLength(wrappedKey), CFDataGetBytePtr(wrappedKey),
 99                                              &obytes, [unwrappedKey mutableBytes]);
100      if (unwrap_status == 0) {
101          assert(obytes == (size_t)[unwrappedKey length]);
102      } else {
103          unwrappedKey = NULL;
104          goto out;
105      }
106  
107   out:
108      ccecb_ctx_clear(ccecb_context_size(ecb_mode), key);
109      return unwrappedKey;
110  }
111  
112  /*
113   *
114   */
115  
116  static CFDataRef
117  CreateDerivedKey(CFDataRef salt, long iterations, NSString *managedCredential)
118  {
119      if (iterations < MIN_ITERATIONS || CFDataGetLength(salt) < MIN_SALTLEN)
120          return NULL;
121  
122      /*
123       * Assume users use the same normalization rules always
124       */
125  
126      CFMutableDataRef key = CFDataCreateMutable(SecCFAllocatorZeroize(), KEY_LENGTH);
127      if (key == NULL) {
128          return NULL;
129      }
130  
131      CFDataSetLength(key, KEY_LENGTH);
132  
133      int ret;
134      ret = ccpbkdf2_hmac(ccsha256_di(),
135                          strlen(managedCredential.UTF8String), managedCredential.UTF8String,
136                          CFDataGetLength(salt), CFDataGetBytePtr(salt),
137                          iterations,
138                          KEY_LENGTH, CFDataGetMutableBytePtr(key));
139      if (ret) {
140          CFRelease(key);
141          return NULL;
142      }
143      return key;
144  }
145  
146  
147  /*
148   * Given a dictionary stored in iDMS and a passcode, return a crypto key
149   */
150  
151  NSData *
152  SecEMCSCreateDerivedEMCSKey(NSDictionary *iDMSData, NSString *managedCredential, NSError **error)
153  {
154      CFDataRef userDerivedKey = NULL, emcsKey = NULL;
155      CFNumberRef number = NULL;
156      CFDataRef salt = NULL;
157      NSData *key = NULL;
158      long iterations;
159  
160      salt = CFDictionaryGetValue((__bridge CFDictionaryRef)iDMSData, kiDMSSalt);
161      number = CFDictionaryGetValue((__bridge CFDictionaryRef)iDMSData, kiDMSIterrations);
162      emcsKey = CFDictionaryGetValue((__bridge CFDictionaryRef)iDMSData, kiDMSWrapEMCSKey);
163  
164      /* validate parameters */
165      if (!isData(salt) || !isNumber(number) || !isData(emcsKey))
166          return NULL;
167  
168      if (!CFNumberGetValue(number, kCFNumberLongType, &iterations))
169          return NULL;
170  
171      userDerivedKey = CreateDerivedKey(salt, iterations, managedCredential);
172      if (userDerivedKey == NULL)
173          return NULL;
174  
175      key = CopyUnwrappedKey(userDerivedKey, emcsKey);
176      CFRelease(userDerivedKey);
177  
178      return key;
179  }
180  
181  /*
182   * Return a dictionary to be stored in iDMS
183   */
184  
185  NSDictionary *
186  SecEMCSCreateNewiDMSKey(NSDictionary *options,
187                          NSData *oldEMCSKey,
188                          NSString *managedCredential,
189                          NSData **emcsKey,
190                          NSError **error)
191  {
192      CFMutableDataRef salt = NULL;
193      const long iter = MIN_ITERATIONS;
194      CFDataRef wrappedEMCSKey = NULL;
195      CFMutableDataRef localEmcsKey = NULL;
196      CFNumberRef iterations = NULL;
197      CFDataRef userDerivedKey = NULL;
198      CFDictionaryRef key = NULL;
199  
200      if (emcsKey)
201          *emcsKey = NULL;
202  
203      if (oldEMCSKey) {
204          if (CFGetTypeID((__bridge CFTypeRef)(oldEMCSKey)) != CFDataGetTypeID())
205              return NULL;
206          if (CFDataGetLength((__bridge CFDataRef)oldEMCSKey) != KEY_LENGTH)
207              return NULL;
208      }
209  
210      salt = CFDataCreateMutableWithScratch(NULL, MIN_SALTLEN);
211      if (salt == NULL)
212          goto out;
213  
214      if (SecRandomCopyBytes(NULL, CFDataGetLength(salt), CFDataGetMutableBytePtr(salt)) != 0)
215          goto out;
216  
217  
218      iterations = CFNumberCreate(NULL, kCFNumberLongType, &iter);
219      if (iterations == NULL)
220          goto out;
221  
222      if (oldEMCSKey) {
223          localEmcsKey = CFDataCreateMutableCopy(SecCFAllocatorZeroize(), 0, (__bridge CFDataRef)oldEMCSKey);
224      } else {
225          localEmcsKey = CFDataCreateMutableWithScratch(SecCFAllocatorZeroize(), KEY_LENGTH);
226          if (localEmcsKey == NULL)
227              goto out;
228          if (SecRandomCopyBytes(NULL, CFDataGetLength(localEmcsKey), CFDataGetMutableBytePtr(localEmcsKey)) != 0)
229              goto out;
230      }
231  
232      userDerivedKey = CreateDerivedKey(salt, iter, managedCredential);
233      if (userDerivedKey == NULL)
234          goto out;
235  
236      wrappedEMCSKey = CopyWrappedKey(userDerivedKey, localEmcsKey);
237      CFRelease(userDerivedKey);
238      if (wrappedEMCSKey == NULL)
239          goto out;
240  
241      const void *keys[] = {
242          kiDMSSalt,
243          kiDMSIterrations,
244          kiDMSWrapEMCSKey,
245      };
246      const void *values[] = {
247          salt,
248          iterations,
249          wrappedEMCSKey,
250      };
251      _Static_assert(sizeof(keys)/sizeof(keys[0]) == sizeof(values)/sizeof(values[0]), "keys != values");
252  
253      key = CFDictionaryCreate(NULL, keys, values, sizeof(keys)/sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
254      if (key && emcsKey)
255          *emcsKey = CFRetain(localEmcsKey);
256  
257   out:
258      CFReleaseNull(salt);
259      CFReleaseNull(iterations);
260      CFReleaseNull(localEmcsKey);
261      CFReleaseNull(wrappedEMCSKey);
262  
263      return (__bridge NSDictionary *)key;
264  }