SOSRecoveryKeyBag.m
1 /* 2 * Copyright (c) 2016 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 // 25 // SOSRecoveryKeyBag.c 26 // sec 27 // 28 29 #include "SOSRecoveryKeyBag.h" 30 #include "AssertMacros.h" 31 #include "keychain/SecureObjectSync/SOSGenCount.h" 32 #include "keychain/SecureObjectSync/SOSAccountPriv.h" 33 #include "keychain/SecureObjectSync/SOSRecoveryKeyBag.h" 34 #include <utilities/SecCFWrappers.h> 35 #include <utilities/SecAKSWrappers.h> 36 #include <utilities/SecBuffer.h> 37 #include <utilities/SecCFError.h> 38 #include <utilities/der_set.h> 39 #include <utilities/der_plist_internal.h> 40 #include <Security/SecRandom.h> 41 #include <corecrypto/ccec.h> 42 #include <corecrypto/ccsha2.h> 43 #include <corecrypto/ccrng.h> 44 45 #include <limits.h> 46 47 #include "keychain/SecureObjectSync/SOSInternal.h" 48 49 #define CURRENT_RKB_VERSION 1 50 51 // 52 // MARK: Type creation 53 // 54 55 struct __OpaqueSOSRecoveryKeyBag { 56 CFRuntimeBase _base; 57 CFStringRef accountDSID; 58 SOSGenCountRef generation; 59 uint64_t rkbVersion; 60 CFDataRef recoveryKeyBag; 61 }; 62 63 64 65 static void SOSRecoveryKeyBagDestroy(CFTypeRef aObj) { 66 SOSRecoveryKeyBagRef rb = (SOSRecoveryKeyBagRef) aObj; 67 68 CFReleaseNull(rb->accountDSID); 69 CFReleaseNull(rb->generation); 70 CFReleaseNull(rb->recoveryKeyBag); 71 } 72 73 static CFStringRef SOSRecoveryKeyBagCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) { 74 SOSRecoveryKeyBagRef rb = (SOSRecoveryKeyBagRef) aObj; 75 CFStringRef gcString = SOSGenerationCountCopyDescription(rb->generation); 76 CFStringRef rkbID = SOSCopyIDOfDataBufferWithLength(rb->recoveryKeyBag, 8, NULL); 77 78 CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0); 79 80 CFStringAppendFormat(description, NULL, CFSTR("<SOSRecoveryKeyBag@%p DSID: %@ version: %d gencount: %@ RecoveryKeyID: %@ "), rb, rb->accountDSID, (int) rb->rkbVersion, gcString, rkbID); 81 CFStringAppend(description, CFSTR(">")); 82 83 CFReleaseNull(gcString); 84 CFReleaseNull(rkbID); 85 return description; 86 } 87 88 CFGiblisFor(SOSRecoveryKeyBag); 89 90 // Der encoding/decoding 91 const uint8_t* der_decode_RecoveryKeyBag(CFAllocatorRef allocator, 92 SOSRecoveryKeyBagRef* RecoveryKeyBag, CFErrorRef *error, 93 const uint8_t* der, const uint8_t *der_end) { 94 if (der == NULL) return der; 95 const uint8_t *result = NULL; 96 97 SOSRecoveryKeyBagRef rb = CFTypeAllocate(SOSRecoveryKeyBag, struct __OpaqueSOSRecoveryKeyBag, allocator); 98 require_quiet(SecAllocationError(rb, error, CFSTR("Recovery bag allocation failed")), fail); 99 100 const uint8_t *sequence_end = NULL; 101 der = ccder_decode_sequence_tl(&sequence_end, der, der_end); 102 require_quiet(sequence_end == der_end, fail); 103 104 der = der_decode_string(kCFAllocatorDefault, &rb->accountDSID, error, der, sequence_end); 105 rb->generation = SOSGenCountCreateFromDER(kCFAllocatorDefault, error, &der, sequence_end); 106 der = ccder_decode_uint64(&rb->rkbVersion, der, sequence_end); 107 der = der_decode_data(allocator, &rb->recoveryKeyBag, error, der, sequence_end); 108 109 require_quiet(SecRequirementError(der == der_end, error, CFSTR("Extra space in sequence")), fail); 110 if (RecoveryKeyBag) CFTransferRetained(*RecoveryKeyBag, rb); 111 result = der; 112 fail: 113 CFReleaseNull(rb); 114 return result; 115 } 116 117 static bool SOSRecoveryKeyBagIsComplete(SOSRecoveryKeyBagRef RecoveryKeyBag, CFErrorRef *error) { 118 if(!RecoveryKeyBag) { 119 SOSCreateError(kSOSErrorEncodeFailure, CFSTR("NULL RecoveryKeyBag"), NULL, error); 120 return false; 121 } 122 bool retval = true; 123 if(!RecoveryKeyBag->recoveryKeyBag) { 124 SOSCreateError(kSOSErrorEncodeFailure, CFSTR("RecoveryKeyBag has no public key"), NULL, error); 125 retval = false; 126 } 127 if(!RecoveryKeyBag->accountDSID) { 128 SOSCreateError(kSOSErrorEncodeFailure, CFSTR("RecoveryKeyBag has no DSID"), NULL, error); 129 retval = false; 130 } 131 if(!RecoveryKeyBag->generation) { 132 SOSCreateError(kSOSErrorEncodeFailure, CFSTR("RecoveryKeyBag has no generation"), NULL, error); 133 retval = false; 134 } 135 return retval; 136 } 137 138 size_t der_sizeof_RecoveryKeyBag(SOSRecoveryKeyBagRef RecoveryKeyBag, CFErrorRef *error) { 139 size_t result = 0; 140 if(SOSRecoveryKeyBagIsComplete(RecoveryKeyBag, error)) { 141 size_t partSize = der_sizeof_string(RecoveryKeyBag->accountDSID, NULL); 142 partSize += SOSGenCountGetDEREncodedSize(RecoveryKeyBag->generation, NULL); 143 partSize += ccder_sizeof_uint64(RecoveryKeyBag->rkbVersion); 144 partSize += der_sizeof_data(RecoveryKeyBag->recoveryKeyBag, NULL); 145 result = ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, partSize); 146 } 147 return result; 148 } 149 150 uint8_t* der_encode_RecoveryKeyBag(SOSRecoveryKeyBagRef RecoveryKeyBag, CFErrorRef *error, 151 const uint8_t *der, uint8_t *der_end) { 152 uint8_t *result = NULL; 153 if (der_end == NULL) return der_end; 154 if(SOSRecoveryKeyBagIsComplete(RecoveryKeyBag, error)) { 155 der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, 156 der_encode_string(RecoveryKeyBag->accountDSID, error, der, 157 SOSGenCountEncodeToDER(RecoveryKeyBag->generation, error, der, 158 ccder_encode_uint64(RecoveryKeyBag->rkbVersion, der, 159 der_encode_data(RecoveryKeyBag->recoveryKeyBag, error, der, der_end))))); 160 161 require_quiet(der_end == der, errOut); 162 result = der_end; 163 } 164 errOut: 165 return result; 166 } 167 168 SOSRecoveryKeyBagRef SOSRecoveryKeyBagCreateForAccount(CFAllocatorRef allocator, CFTypeRef account, CFDataRef pubData, CFErrorRef *error) { 169 SOSRecoveryKeyBagRef retval = NULL; 170 SOSGenCountRef gencount = NULL; 171 require_action_quiet(account, errOut, SOSCreateError(kSOSErrorEncodeFailure, CFSTR("Null Account Object"), NULL, error)); 172 CFStringRef dsid = NULL; 173 dsid = asString(SOSAccountGetValue((__bridge SOSAccount*)account, kSOSDSIDKey, NULL), error); 174 require_action_quiet(dsid, errOut, SOSCreateError(kSOSErrorEncodeFailure, CFSTR("Couldn't get dsid for recovery keybag components"), NULL, error)); 175 176 gencount = SOSGenerationCreate(); 177 178 require_action_quiet(pubData && dsid && gencount, errOut, SOSCreateError(kSOSErrorEncodeFailure, CFSTR("Couldn't get recovery keybag components"), NULL, error)); 179 retval = CFTypeAllocate(SOSRecoveryKeyBag, struct __OpaqueSOSRecoveryKeyBag, allocator); 180 require_action_quiet(retval, errOut, SOSCreateError(kSOSErrorEncodeFailure, CFSTR("Couldn't get memory for recoveryKeyBag"), NULL, error)); 181 retval->rkbVersion = CURRENT_RKB_VERSION; 182 retval->accountDSID = CFStringCreateCopy(allocator, dsid); 183 CFRetainAssign(retval->generation, gencount); 184 retval->recoveryKeyBag = CFDataCreateCopy(allocator, pubData); 185 errOut: 186 CFReleaseNull(gencount); 187 return retval; 188 } 189 190 191 CFDataRef SOSRecoveryKeyCopyKeyForAccount(CFAllocatorRef allocator, CFTypeRef account, SOSRecoveryKeyBagRef recoveryKeyBag, CFErrorRef *error) { 192 CFDataRef retval = NULL; 193 require_action_quiet(recoveryKeyBag && recoveryKeyBag->recoveryKeyBag && recoveryKeyBag->accountDSID, 194 errOut, SOSCreateError(kSOSErrorDecodeFailure, CFSTR("Null recoveryKeyBag Object"), NULL, error)); 195 CFStringRef dsid = NULL; 196 dsid = asString(SOSAccountGetValue((__bridge SOSAccount *)(account), kSOSDSIDKey, NULL), error); 197 198 require_action_quiet(dsid, errOut, SOSCreateError(kSOSErrorDecodeFailure, CFSTR("No DSID in Account"), NULL, error)); 199 require_action_quiet(CFEqual(dsid, recoveryKeyBag->accountDSID), errOut, SOSCreateError(kSOSErrorDecodeFailure, CFSTR("Account/RecoveryKeybag DSID miss-match"), NULL, error)); 200 retval = CFDataCreateCopy(allocator, recoveryKeyBag->recoveryKeyBag); 201 errOut: 202 return retval; 203 } 204 205 206 CFDataRef SOSRecoveryKeyBagCopyEncoded(SOSRecoveryKeyBagRef RecoveryKeyBag, CFErrorRef* error) { 207 CFDataRef result = NULL; 208 CFMutableDataRef encoded = NULL; 209 210 require_quiet(RecoveryKeyBag, errOut); 211 size_t encodedSize = der_sizeof_RecoveryKeyBag(RecoveryKeyBag, error); 212 require_quiet(encodedSize, errOut); 213 214 encoded = CFDataCreateMutableWithScratch(kCFAllocatorDefault, encodedSize); 215 require_quiet(SecAllocationError(encoded, error, CFSTR("Failed to create scratch")), errOut); 216 217 uint8_t *encode_to = CFDataGetMutableBytePtr(encoded); 218 uint8_t *encode_to_end = encode_to + CFDataGetLength(encoded); 219 require_quiet(encode_to == der_encode_RecoveryKeyBag(RecoveryKeyBag, error, encode_to, encode_to_end), errOut); 220 221 CFTransferRetained(result, encoded); 222 223 errOut: 224 CFReleaseSafe(encoded); 225 return result; 226 } 227 228 229 230 SOSRecoveryKeyBagRef SOSRecoveryKeyBagCreateFromData(CFAllocatorRef allocator, CFDataRef data, CFErrorRef *error) { 231 SOSRecoveryKeyBagRef result = NULL; 232 SOSRecoveryKeyBagRef decodedBag = NULL; 233 234 const uint8_t *der = CFDataGetBytePtr(data); 235 const uint8_t *der_end = der + CFDataGetLength(data); 236 237 der = der_decode_RecoveryKeyBag(allocator, &decodedBag, error, der, der_end); 238 239 require_quiet(SecRequirementError(der == der_end, error, CFSTR("Didn't consume all data supplied")), fail); 240 241 CFTransferRetained(result, decodedBag); 242 243 fail: 244 CFReleaseNull(decodedBag); 245 return result; 246 } 247 248 CFDataRef SOSRecoveryKeyBagGetKeyData(SOSRecoveryKeyBagRef rkbg, CFErrorRef *error) { 249 return rkbg->recoveryKeyBag; 250 } 251 252 bool SOSRecoveryKeyBagDSIDIs(SOSRecoveryKeyBagRef rkbg, CFStringRef dsid) { 253 if(!rkbg) return false; 254 return CFEqualSafe(rkbg->accountDSID, dsid); 255 } 256 257 258 259