/ keychain / SecureObjectSync / SOSRecoveryKeyBag.m
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