2 * Copyright (c) 2016 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 // SOSRecoveryKeyBag.c
29 #include "SOSRecoveryKeyBag.h"
30 #include "AssertMacros.h"
31 #include <Security/SecureObjectSync/SOSGenCount.h>
32 #include <Security/SecureObjectSync/SOSAccountPriv.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>
47 #include "SOSInternal.h"
49 #define CURRENT_RKB_VERSION 1
52 // MARK: Type creation
56 struct __OpaqueSOSRecoveryKeyBag
{
58 CFStringRef accountDSID
;
59 SOSGenCountRef generation
;
61 CFDataRef recoveryKeyBag
;
66 static void SOSRecoveryKeyBagDestroy(CFTypeRef aObj
) {
67 SOSRecoveryKeyBagRef rb
= (SOSRecoveryKeyBagRef
) aObj
;
69 CFReleaseNull(rb
->accountDSID
);
70 CFReleaseNull(rb
->generation
);
71 CFReleaseNull(rb
->recoveryKeyBag
);
74 static CFStringRef
SOSRecoveryKeyBagCopyFormatDescription(CFTypeRef aObj
, CFDictionaryRef formatOptions
) {
75 SOSRecoveryKeyBagRef rb
= (SOSRecoveryKeyBagRef
) aObj
;
76 CFStringRef gcString
= SOSGenerationCountCopyDescription(rb
->generation
);
77 CFStringRef rkbID
= SOSCopyIDOfDataBufferWithLength(rb
->recoveryKeyBag
, 8, NULL
);
79 CFMutableStringRef description
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
81 CFStringAppendFormat(description
, NULL
, CFSTR("<SOSRecoveryKeyBag@%p DSID: %@ version: %d gencount: %@ RecoveryKeyID: %@ "), rb
, rb
->accountDSID
, (int) rb
->rkbVersion
, gcString
, rkbID
);
82 CFStringAppend(description
, CFSTR(">"));
84 CFReleaseNull(gcString
);
89 CFGiblisFor(SOSRecoveryKeyBag
);
91 // Der encoding/decoding
92 const uint8_t* der_decode_RecoveryKeyBag(CFAllocatorRef allocator
,
93 SOSRecoveryKeyBagRef
* RecoveryKeyBag
, CFErrorRef
*error
,
94 const uint8_t* der
, const uint8_t *der_end
) {
95 if (der
== NULL
) return der
;
96 const uint8_t *result
= NULL
;
98 SOSRecoveryKeyBagRef rb
= CFTypeAllocate(SOSRecoveryKeyBag
, struct __OpaqueSOSRecoveryKeyBag
, allocator
);
99 require_quiet(SecAllocationError(rb
, error
, CFSTR("Recovery bag allocation failed")), fail
);
101 const uint8_t *sequence_end
= NULL
;
102 der
= ccder_decode_sequence_tl(&sequence_end
, der
, der_end
);
103 require_quiet(sequence_end
== der_end
, fail
);
105 der
= der_decode_string(kCFAllocatorDefault
, kCFPropertyListImmutable
, &rb
->accountDSID
, error
, der
, sequence_end
);
106 rb
->generation
= SOSGenCountCreateFromDER(kCFAllocatorDefault
, error
, &der
, sequence_end
);
107 der
= ccder_decode_uint64(&rb
->rkbVersion
, der
, sequence_end
);
108 der
= der_decode_data(allocator
, kCFPropertyListImmutable
, &rb
->recoveryKeyBag
, error
, der
, sequence_end
);
110 require_quiet(SecRequirementError(der
== der_end
, error
, CFSTR("Extra space in sequence")), fail
);
111 if (RecoveryKeyBag
) CFTransferRetained(*RecoveryKeyBag
, rb
);
118 size_t der_sizeof_RecoveryKeyBag(SOSRecoveryKeyBagRef RecoveryKeyBag
, CFErrorRef
*error
) {
120 if(RecoveryKeyBag
&& RecoveryKeyBag
->recoveryKeyBag
&& RecoveryKeyBag
->accountDSID
&& RecoveryKeyBag
->generation
) {
121 size_t partSize
= der_sizeof_string(RecoveryKeyBag
->accountDSID
, NULL
);
122 partSize
+= SOSGenCountGetDEREncodedSize(RecoveryKeyBag
->generation
, NULL
);
123 partSize
+= ccder_sizeof_uint64(RecoveryKeyBag
->rkbVersion
);
124 partSize
+= der_sizeof_data(RecoveryKeyBag
->recoveryKeyBag
, NULL
);
125 result
= ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE
, partSize
);
127 SOSCreateError(kSOSErrorEncodeFailure
, CFSTR("Null RecoveryKeyBag"), NULL
, error
);
132 uint8_t* der_encode_RecoveryKeyBag(SOSRecoveryKeyBagRef RecoveryKeyBag
, CFErrorRef
*error
,
133 const uint8_t *der
, uint8_t *der_end
) {
134 uint8_t *result
= NULL
;
135 if (der_end
== NULL
) return der_end
;
136 if(RecoveryKeyBag
&& RecoveryKeyBag
->recoveryKeyBag
&& RecoveryKeyBag
->accountDSID
&& RecoveryKeyBag
->generation
) {
137 der_end
= ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE
, der_end
, der
,
138 der_encode_string(RecoveryKeyBag
->accountDSID
, error
, der
,
139 SOSGenCountEncodeToDER(RecoveryKeyBag
->generation
, error
, der
,
140 ccder_encode_uint64(RecoveryKeyBag
->rkbVersion
, der
,
141 der_encode_data(RecoveryKeyBag
->recoveryKeyBag
, error
, der
, der_end
)))));
143 require_quiet(der_end
== der
, errOut
);
146 SOSCreateError(kSOSErrorEncodeFailure
, CFSTR("Null RecoveryKeyBag"), NULL
, error
);
153 SOSRecoveryKeyBagRef
SOSRecoveryKeyBagCreateForAccount(CFAllocatorRef allocator
, SOSAccountRef account
, CFDataRef pubData
, CFErrorRef
*error
) {
154 SOSRecoveryKeyBagRef retval
= NULL
;
155 SOSGenCountRef gencount
= NULL
;
156 require_action_quiet(account
, errOut
, SOSCreateError(kSOSErrorEncodeFailure
, CFSTR("Null Account Object"), NULL
, error
));
157 CFStringRef dsid
= asString(SOSAccountGetValue(account
, kSOSDSIDKey
, NULL
), error
);
158 gencount
= SOSGenerationCreate();
160 require_action_quiet(pubData
&& dsid
&& gencount
, errOut
, SOSCreateError(kSOSErrorEncodeFailure
, CFSTR("Couldn't get recovery keybag components"), NULL
, error
));
161 retval
= CFTypeAllocate(SOSRecoveryKeyBag
, struct __OpaqueSOSRecoveryKeyBag
, allocator
);
162 require_action_quiet(retval
, errOut
, SOSCreateError(kSOSErrorEncodeFailure
, CFSTR("Couldn't get memory for recoveryKeyBag"), NULL
, error
));
163 retval
->rkbVersion
= CURRENT_RKB_VERSION
;
164 retval
->accountDSID
= CFStringCreateCopy(allocator
, dsid
);
165 CFRetainAssign(retval
->generation
, gencount
);
166 retval
->recoveryKeyBag
= CFDataCreateCopy(allocator
, pubData
);
168 CFReleaseNull(gencount
);
172 CFDataRef
SOSRecoveryKeyCopyKeyForAccount(CFAllocatorRef allocator
, SOSAccountRef account
, SOSRecoveryKeyBagRef recoveryKeyBag
, CFErrorRef
*error
) {
173 CFDataRef retval
= NULL
;
174 require_action_quiet(recoveryKeyBag
&& recoveryKeyBag
->recoveryKeyBag
&& recoveryKeyBag
->accountDSID
,
175 errOut
, SOSCreateError(kSOSErrorDecodeFailure
, CFSTR("Null recoveryKeyBag Object"), NULL
, error
));
176 CFStringRef dsid
= asString(SOSAccountGetValue(account
, kSOSDSIDKey
, NULL
), error
);
177 require_action_quiet(dsid
, errOut
, SOSCreateError(kSOSErrorDecodeFailure
, CFSTR("No DSID in Account"), NULL
, error
));
178 require_action_quiet(CFEqual(dsid
, recoveryKeyBag
->accountDSID
), errOut
, SOSCreateError(kSOSErrorDecodeFailure
, CFSTR("Account/RecoveryKeybag DSID miss-match"), NULL
, error
));
179 retval
= CFDataCreateCopy(allocator
, recoveryKeyBag
->recoveryKeyBag
);
185 CFDataRef
SOSRecoveryKeyBagCopyEncoded(SOSRecoveryKeyBagRef RecoveryKeyBag
, CFErrorRef
* error
) {
186 CFDataRef result
= NULL
;
187 CFMutableDataRef encoded
= NULL
;
189 size_t encodedSize
= der_sizeof_RecoveryKeyBag(RecoveryKeyBag
, error
);
190 require_quiet(encodedSize
, fail
);
192 encoded
= CFDataCreateMutableWithScratch(kCFAllocatorDefault
, encodedSize
);
193 require_quiet(SecAllocationError(encoded
, error
, CFSTR("Failed to create scratch")), fail
);
195 uint8_t *encode_to
= CFDataGetMutableBytePtr(encoded
);
196 uint8_t *encode_to_end
= encode_to
+ CFDataGetLength(encoded
);
197 require_quiet(encode_to
== der_encode_RecoveryKeyBag(RecoveryKeyBag
, error
, encode_to
, encode_to_end
), fail
);
199 CFTransferRetained(result
, encoded
);
202 CFReleaseSafe(encoded
);
208 SOSRecoveryKeyBagRef
SOSRecoveryKeyBagCreateFromData(CFAllocatorRef allocator
, CFDataRef data
, CFErrorRef
*error
) {
209 SOSRecoveryKeyBagRef result
= NULL
;
210 SOSRecoveryKeyBagRef decodedBag
= NULL
;
212 const uint8_t *der
= CFDataGetBytePtr(data
);
213 const uint8_t *der_end
= der
+ CFDataGetLength(data
);
215 der
= der_decode_RecoveryKeyBag(allocator
, &decodedBag
, error
, der
, der_end
);
217 require_quiet(SecRequirementError(der
== der_end
, error
, CFSTR("Didn't consume all data supplied")), fail
);
219 CFTransferRetained(result
, decodedBag
);
222 CFReleaseNull(decodedBag
);
226 CFDataRef
SOSRecoveryKeyBagGetKeyData(SOSRecoveryKeyBagRef rkbg
, CFErrorRef
*error
) {
227 return rkbg
->recoveryKeyBag
;
230 bool SOSRecoveryKeyBagDSIDIs(SOSRecoveryKeyBagRef rkbg
, CFStringRef dsid
) {
231 if(!rkbg
) return false;
232 return CFEqualSafe(rkbg
->accountDSID
, dsid
);