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 }