]>
Commit | Line | Data |
---|---|---|
427c49bc | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2011-2014 Apple Inc. All Rights Reserved. |
427c49bc | 3 | * |
d8f41ccd A |
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@ | |
427c49bc A |
22 | */ |
23 | ||
d8f41ccd | 24 | |
427c49bc A |
25 | #include "SecOTR.h" |
26 | #include "SecOTRIdentityPriv.h" | |
27 | #include <utilities/array_size.h> | |
5c19dc3a | 28 | #include <utilities/SecCFError.h> |
427c49bc A |
29 | #include <utilities/SecCFWrappers.h> |
30 | ||
31 | #include <AssertMacros.h> | |
32 | ||
33 | #include <CoreFoundation/CFNumber.h> | |
34 | #include <CoreFoundation/CFString.h> | |
35 | #include <CoreFoundation/CFData.h> | |
36 | ||
37 | #include <Security/SecItem.h> | |
fa7225c8 | 38 | #include <Security/SecItemPriv.h> |
427c49bc A |
39 | #include <Security/SecKeyPriv.h> |
40 | ||
41 | #include <Security/oidsalg.h> | |
42 | #include <Security/SecCertificatePriv.h> | |
43 | ||
44 | #include "SecOTRErrors.h" | |
45 | ||
46 | #include <TargetConditionals.h> | |
47 | ||
48 | // | |
49 | // Algorthim ID initialization | |
50 | // | |
51 | ||
52 | #define kMessageIdentityRSAKeyBits 1280 | |
53 | #define kMessageIdentityECKeyBits 256 | |
54 | ||
fa7225c8 A |
55 | const SecAsn1AlgId *kOTRSignatureAlgIDPtr; |
56 | ||
427c49bc A |
57 | void EnsureOTRAlgIDInited(void) |
58 | { | |
59 | static dispatch_once_t kSignatureAlgID_ONCE; | |
60 | static SecAsn1AlgId kOTRECSignatureAlgID; | |
61 | ||
62 | dispatch_once(&kSignatureAlgID_ONCE, ^{ | |
63 | kOTRECSignatureAlgID.algorithm = CSSMOID_ECDSA_WithSHA1; | |
64 | kOTRSignatureAlgIDPtr = &kOTRECSignatureAlgID; | |
65 | }); | |
66 | } | |
67 | ||
68 | ||
69 | static CFStringRef sSigningKeyName = CFSTR("OTR Signing Key"); | |
70 | ||
71 | // | |
72 | // SecOTRFullIdentity implementation | |
73 | // | |
74 | ||
75 | CFGiblisFor(SecOTRFullIdentity); | |
76 | ||
d87e1158 | 77 | static CF_RETURNS_RETAINED CFStringRef SecOTRFullIdentityCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { |
427c49bc A |
78 | SecOTRFullIdentityRef requestor = (SecOTRFullIdentityRef)cf; |
79 | return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecOTRPublicIdentity: %p %02x%02x%02x%02x%02x%02x%02x%02x>"), | |
80 | requestor, | |
81 | requestor->publicIDHash[0], requestor->publicIDHash[1], | |
82 | requestor->publicIDHash[2], requestor->publicIDHash[3], | |
83 | requestor->publicIDHash[4], requestor->publicIDHash[5], | |
84 | requestor->publicIDHash[6], requestor->publicIDHash[7]); | |
85 | } | |
86 | ||
87 | static void SecOTRFullIdentityDestroy(CFTypeRef cf) { | |
88 | SecOTRFullIdentityRef requestor = (SecOTRFullIdentityRef)cf; | |
89 | ||
90 | CFReleaseNull(requestor->privateSigningKey); | |
91 | CFReleaseNull(requestor->publicSigningKey); | |
5c19dc3a | 92 | CFReleaseNull(requestor->privateKeyPersistentRef); |
427c49bc A |
93 | } |
94 | ||
95 | ||
96 | // | |
97 | // Shared statics | |
98 | // | |
99 | ||
100 | static OSStatus SecOTRFIPurgeFromKeychainByValue(SecKeyRef key, CFStringRef label) | |
101 | { | |
102 | OSStatus status; | |
103 | const void *keys[] = { kSecClass, | |
104 | kSecAttrLabel, | |
105 | kSecValueRef, | |
106 | }; | |
107 | const void *values[] = { kSecClassKey, | |
108 | label, | |
109 | key, | |
110 | }; | |
111 | CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values, array_size(values), NULL, NULL); | |
112 | status = SecItemDelete(dict); | |
113 | CFReleaseSafe(dict); | |
114 | ||
115 | return status; | |
116 | } | |
117 | ||
118 | static bool SecKeyDigestAndSignWithError( | |
119 | SecKeyRef key, /* Private key */ | |
120 | const SecAsn1AlgId *algId, /* algorithm oid/params */ | |
121 | const uint8_t *dataToDigest, /* signature over this data */ | |
122 | size_t dataToDigestLen,/* length of dataToDigest */ | |
123 | uint8_t *sig, /* signature, RETURNED */ | |
124 | size_t *sigLen, /* IN/OUT */ | |
125 | CFErrorRef *error) { | |
126 | ||
127 | OSStatus status = SecKeyDigestAndSign(key, algId, dataToDigest, dataToDigestLen, sig, sigLen); | |
128 | require_noerr(status, fail); | |
129 | return true; | |
130 | fail: | |
131 | SecOTRCreateError(secOTRErrorOSError, status, CFSTR("Error signing message. OSStatus in error code."), NULL, error); | |
132 | return false; | |
133 | } | |
134 | ||
135 | // | |
136 | // SecOTRFullIdentity Functions | |
137 | // | |
138 | ||
139 | static bool SecOTRFICachePublicHash(SecOTRFullIdentityRef fullID, CFErrorRef *error) | |
140 | { | |
141 | SecOTRPublicIdentityRef pubID = SecOTRPublicIdentityCopyFromPrivate(NULL, fullID, error); | |
142 | ||
143 | require(pubID, fail); | |
144 | ||
145 | SecOTRPICopyHash(pubID, fullID->publicIDHash); | |
146 | ||
147 | fail: | |
148 | CFReleaseSafe(pubID); | |
149 | return (pubID != NULL); // This is safe because we're not accessing the value after release, just checking if it ever had a value of some nature. | |
150 | } | |
151 | ||
5c19dc3a A |
152 | static SecKeyRef SecOTRCreateSigningKey(CFAllocatorRef allocator) { |
153 | SecKeyRef publicKey = NULL; | |
154 | SecKeyRef fullKey = NULL; | |
427c49bc | 155 | CFDictionaryRef keygen_parameters = NULL; |
427c49bc | 156 | const int signing_keySizeLocal = kMessageIdentityECKeyBits; |
5c19dc3a | 157 | CFNumberRef signing_bitsize = CFNumberCreate(allocator, kCFNumberIntType, &signing_keySizeLocal); |
427c49bc A |
158 | |
159 | const void *signing_keygen_keys[] = { kSecAttrKeyType, | |
5c19dc3a A |
160 | kSecAttrKeySizeInBits, |
161 | kSecAttrIsPermanent, | |
162 | kSecAttrAccessible, | |
163 | kSecAttrLabel, | |
164 | }; | |
427c49bc A |
165 | |
166 | const void *signing_keygen_vals[] = { kSecAttrKeyTypeEC, | |
5c19dc3a A |
167 | signing_bitsize, |
168 | kCFBooleanTrue, | |
fa7225c8 | 169 | kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate, |
5c19dc3a A |
170 | sSigningKeyName |
171 | }; | |
172 | keygen_parameters = CFDictionaryCreate(allocator, | |
427c49bc A |
173 | signing_keygen_keys, signing_keygen_vals, array_size(signing_keygen_vals), |
174 | &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); | |
175 | CFReleaseNull(signing_bitsize); | |
5c19dc3a A |
176 | require_noerr_action(SecKeyGeneratePair(keygen_parameters, &publicKey, &fullKey), |
177 | errOut, | |
178 | secerror("keygen failed")); | |
179 | SecOTRFIPurgeFromKeychainByValue(publicKey, sSigningKeyName); | |
427c49bc | 180 | |
5c19dc3a A |
181 | CFReleaseNull(keygen_parameters); |
182 | errOut: | |
183 | return fullKey; | |
184 | } | |
427c49bc | 185 | |
5c19dc3a A |
186 | SecOTRFullIdentityRef SecOTRFullIdentityCreate(CFAllocatorRef allocator, CFErrorRef *error) |
187 | { | |
188 | SecKeyRef signingKey = SecOTRCreateSigningKey(allocator); | |
d64be36e | 189 | SecOTRFullIdentityRef newID = SecOTRFullIdentityCreateFromSecKeyRefSOS(allocator, signingKey, error); |
5c19dc3a | 190 | CFReleaseNull(signingKey); |
427c49bc A |
191 | |
192 | return newID; | |
427c49bc A |
193 | } |
194 | ||
195 | ||
196 | static | |
5c19dc3a | 197 | OSStatus SecOTRFICreatePrivateKeyReadPersistentRef(const uint8_t **bytes, size_t *size, SecKeyRef* privateKey, CFDataRef *newPersistentRef) |
427c49bc A |
198 | { |
199 | OSStatus status = errSecParam; | |
200 | uint16_t dataSize; | |
5c19dc3a A |
201 | CFDataRef foundPersistentRef = NULL; |
202 | ||
203 | require_quiet(newPersistentRef, fail); | |
427c49bc A |
204 | |
205 | require_noerr_quiet(readSize(bytes, size, &dataSize), fail); | |
206 | require_quiet(dataSize <= *size, fail); | |
207 | ||
5c19dc3a A |
208 | foundPersistentRef = CFDataCreate(kCFAllocatorDefault, *bytes, dataSize); |
209 | require_quiet(foundPersistentRef, fail); | |
427c49bc | 210 | |
5c19dc3a | 211 | require_noerr_quiet(status = SecKeyFindWithPersistentRef(foundPersistentRef, privateKey), fail); |
427c49bc A |
212 | |
213 | *bytes += dataSize; | |
214 | *size -= dataSize; | |
215 | ||
216 | status = errSecSuccess; | |
217 | ||
5c19dc3a A |
218 | *newPersistentRef = foundPersistentRef; |
219 | foundPersistentRef = NULL; | |
427c49bc | 220 | |
5c19dc3a A |
221 | fail: |
222 | CFReleaseSafe(foundPersistentRef); | |
427c49bc A |
223 | return status; |
224 | } | |
225 | ||
226 | static | |
5c19dc3a | 227 | OSStatus SecOTRFICreateKeysFromReadPersistentRef(const uint8_t **bytes, size_t *size, SecKeyRef *publicKey, SecKeyRef* privateKey, CFDataRef *persistentRef) |
427c49bc A |
228 | { |
229 | SecKeyRef foundKey = NULL; | |
5c19dc3a | 230 | CFDataRef foundRef = NULL; |
427c49bc | 231 | |
5c19dc3a | 232 | OSStatus status = SecOTRFICreatePrivateKeyReadPersistentRef(bytes, size, &foundKey, &foundRef); |
427c49bc | 233 | require_noerr_quiet(status, fail); |
427c49bc | 234 | |
5c19dc3a | 235 | require_action_quiet(*publicKey = SecKeyCreatePublicFromPrivate(foundKey), fail, status = errSecInternalComponent); |
427c49bc A |
236 | |
237 | *privateKey = foundKey; | |
238 | foundKey = NULL; | |
5c19dc3a A |
239 | *persistentRef = foundRef; |
240 | foundRef = NULL; | |
427c49bc A |
241 | |
242 | fail: | |
243 | CFReleaseSafe(foundKey); | |
5c19dc3a | 244 | CFReleaseSafe(foundRef); |
427c49bc A |
245 | return status; |
246 | } | |
247 | ||
248 | typedef SecKeyRef (*SecOTRPublicKeyCreateFunction)(CFAllocatorRef allocator, const uint8_t** data, size_t* limit); | |
249 | ||
250 | static | |
5c19dc3a | 251 | OSStatus SecOTRFICreateKeysFromReadPersistentRefAndPublicKey(const uint8_t **bytes, size_t *size, SecKeyRef *publicKey, SecKeyRef* privateKey, CFDataRef *persistentRef, SecOTRPublicKeyCreateFunction createPublic) |
427c49bc A |
252 | { |
253 | SecKeyRef foundKey = NULL; | |
5c19dc3a | 254 | CFDataRef foundRef = NULL; |
427c49bc | 255 | |
5c19dc3a | 256 | OSStatus status = SecOTRFICreatePrivateKeyReadPersistentRef(bytes, size, &foundKey, &foundRef); |
427c49bc | 257 | require_noerr_quiet(status, fail); |
427c49bc | 258 | |
5c19dc3a | 259 | require_action_quiet(*publicKey = createPublic(NULL, bytes, size), fail, status = errSecInternalComponent); |
427c49bc A |
260 | |
261 | *privateKey = foundKey; | |
5c19dc3a A |
262 | foundKey = NULL; |
263 | *persistentRef = foundRef; | |
264 | foundRef = NULL; | |
427c49bc A |
265 | |
266 | fail: | |
5c19dc3a A |
267 | CFReleaseSafe(foundKey); |
268 | CFReleaseSafe(foundRef); | |
427c49bc A |
269 | return status; |
270 | } | |
271 | ||
272 | static | |
273 | OSStatus SecOTRFIInitFromV1Bytes(SecOTRFullIdentityRef newID, CFAllocatorRef allocator, | |
274 | const uint8_t **bytes,size_t *size) { | |
5c19dc3a A |
275 | OSStatus status; |
276 | require_action(**bytes == 1, fail, status = errSecParam); | |
427c49bc A |
277 | ++*bytes; |
278 | --*size; | |
279 | ||
5c19dc3a | 280 | require_noerr_quiet(status = SecOTRFICreateKeysFromReadPersistentRef(bytes, size, &newID->publicSigningKey, &newID->privateSigningKey, &newID->privateKeyPersistentRef), fail); |
427c49bc | 281 | |
5c19dc3a | 282 | return status; |
427c49bc A |
283 | |
284 | fail: | |
285 | CFReleaseNull(newID->publicSigningKey); | |
286 | CFReleaseNull(newID->privateSigningKey); | |
5c19dc3a | 287 | CFReleaseNull(newID->privateKeyPersistentRef); |
427c49bc | 288 | |
5c19dc3a | 289 | return status; |
427c49bc A |
290 | } |
291 | ||
292 | static | |
293 | OSStatus SecOTRFIInitFromV2Bytes(SecOTRFullIdentityRef newID, CFAllocatorRef allocator, | |
294 | const uint8_t **bytes,size_t *size) { | |
5c19dc3a A |
295 | OSStatus status; |
296 | require_action(**bytes == 2, fail, status = errSecParam); | |
427c49bc A |
297 | ++*bytes; |
298 | --*size; | |
299 | ||
5c19dc3a | 300 | require_noerr_quiet(status = SecOTRFICreateKeysFromReadPersistentRefAndPublicKey(bytes, size, &newID->publicSigningKey, &newID->privateSigningKey, &newID->privateKeyPersistentRef, &CreateECPublicKeyFrom), fail); |
d64be36e A |
301 | newID->isMessageProtectionKey = false; |
302 | ||
5c19dc3a | 303 | return status; |
427c49bc A |
304 | |
305 | fail: | |
5c19dc3a | 306 | CFReleaseNull(newID->privateKeyPersistentRef); |
427c49bc A |
307 | CFReleaseNull(newID->publicSigningKey); |
308 | CFReleaseNull(newID->privateSigningKey); | |
309 | ||
5c19dc3a A |
310 | return status; |
311 | } | |
312 | ||
d64be36e A |
313 | static |
314 | OSStatus SecOTRFIInitFromV3Bytes(SecOTRFullIdentityRef newID, CFAllocatorRef allocator, | |
315 | const uint8_t **bytes,size_t *size) { | |
316 | OSStatus status = errSecInvalidData; | |
317 | require_action(**bytes == 3, fail, status = errSecParam); | |
318 | ++*bytes; | |
319 | --*size; | |
320 | ||
321 | uint16_t dataSize; | |
322 | require_noerr_quiet(readSize(bytes, size, &dataSize), fail); | |
323 | ||
324 | int32_t keysz32 = 256; | |
325 | CFNumberRef ksizeNumber = CFNumberCreate(NULL, kCFNumberSInt32Type, &keysz32); | |
326 | CFDataRef keyBytes = CFDataCreate(allocator, *bytes, dataSize); | |
327 | ||
328 | CFDictionaryRef dict = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, | |
329 | kSecAttrKeyType, kSecAttrKeyTypeECSECPrimeRandom, | |
330 | kSecAttrKeyClass, kSecAttrKeyClassPrivate, | |
331 | kSecAttrKeySizeInBits, ksizeNumber, | |
332 | kSecAttrIsPermanent, kCFBooleanFalse, NULL); | |
333 | ||
334 | CFErrorRef error; | |
335 | SecKeyRef key = SecKeyCreateWithData(keyBytes, dict, &error); | |
336 | ||
337 | CFReleaseSafe(dict); | |
338 | CFReleaseSafe(keyBytes); | |
339 | CFReleaseSafe(ksizeNumber); | |
340 | ||
341 | if (key == NULL) { | |
342 | CFRelease(error); | |
343 | return errSecInvalidData; | |
344 | } | |
345 | ||
346 | newID->privateKeyPersistentRef = NULL; | |
347 | newID->isMessageProtectionKey = true; | |
348 | newID->privateSigningKey = key; | |
349 | newID->publicSigningKey = SecKeyCopyPublicKey(newID->privateSigningKey); | |
350 | status = errSecSuccess; | |
351 | ||
352 | fail: | |
353 | return status; | |
354 | } | |
355 | ||
5c19dc3a A |
356 | // TODO: Probably move to SecKey |
357 | static CFDataRef SecKeyCreatePersistentRef(SecKeyRef theKey, CFErrorRef *error) { | |
358 | CFDataRef persistentRef = NULL; | |
359 | OSStatus status = SecKeyCopyPersistentRef(theKey, &persistentRef); | |
360 | if (status) | |
361 | SecError(status, error, CFSTR("failed to find persistent ref for key: %d"), (int)status); | |
362 | return persistentRef; | |
427c49bc A |
363 | } |
364 | ||
365 | SecOTRFullIdentityRef SecOTRFullIdentityCreateFromSecKeyRef(CFAllocatorRef allocator, SecKeyRef privateKey, | |
366 | CFErrorRef *error) { | |
d64be36e A |
367 | // TODO - make sure this is an appropriate key type |
368 | SecOTRFullIdentityRef newID = CFTypeAllocate(SecOTRFullIdentity, struct _SecOTRFullIdentity, allocator); | |
369 | CFRetainAssign(newID->privateSigningKey, privateKey); | |
370 | require_action(newID->publicSigningKey = SecKeyCreatePublicFromPrivate(privateKey), fail, | |
371 | SecError(errSecInternalComponent, error, CFSTR("Failed to extract public key from private key"))); | |
372 | // MessageProtection keys are no longer having persistent references. | |
373 | newID->privateKeyPersistentRef = NULL; | |
374 | newID->isMessageProtectionKey = true; | |
375 | ||
376 | require(SecOTRFICachePublicHash(newID, error), fail); | |
377 | return newID; | |
378 | fail: | |
379 | CFReleaseNull(newID); | |
380 | return NULL; | |
381 | } | |
382 | ||
383 | SecOTRFullIdentityRef SecOTRFullIdentityCreateFromSecKeyRefSOS(CFAllocatorRef allocator, SecKeyRef privateKey, | |
384 | CFErrorRef *error) { | |
385 | SecOTRFullIdentityRef newID = SecOTRFullIdentityCreateFromSecKeyRef(allocator, privateKey, error); | |
5c19dc3a | 386 | require(newID->privateKeyPersistentRef = SecKeyCreatePersistentRef(privateKey, error), fail); |
d64be36e | 387 | newID->isMessageProtectionKey = false; |
427c49bc A |
388 | return newID; |
389 | fail: | |
5c19dc3a | 390 | CFReleaseNull(newID); |
427c49bc A |
391 | return NULL; |
392 | } | |
393 | ||
394 | SecOTRFullIdentityRef SecOTRFullIdentityCreateFromBytes(CFAllocatorRef allocator, const uint8_t**bytes, size_t *size, CFErrorRef *error) | |
395 | { | |
396 | SecOTRFullIdentityRef newID = CFTypeAllocate(SecOTRFullIdentity, struct _SecOTRFullIdentity, allocator); | |
397 | EnsureOTRAlgIDInited(); | |
398 | ||
399 | require(*size > 0, fail); | |
5c19dc3a | 400 | OSStatus status; |
427c49bc A |
401 | switch (**bytes) { |
402 | case 1: | |
5c19dc3a | 403 | require_noerr_action_quiet(status = SecOTRFIInitFromV1Bytes(newID, allocator, bytes, size), fail, SecError(status, error, CFSTR("failed to decode v1 otr session: %d"), (int)status)); |
427c49bc A |
404 | break; |
405 | case 2: | |
5c19dc3a | 406 | require_noerr_action_quiet(status = SecOTRFIInitFromV2Bytes(newID, allocator, bytes, size), fail, SecError(status, error, CFSTR("failed to decode v2 otr session: %d"), (int)status)); |
427c49bc | 407 | break; |
d64be36e A |
408 | case 3: |
409 | require_noerr_action_quiet(status = SecOTRFIInitFromV3Bytes(newID, allocator, bytes, size), fail, SecError(status, error, CFSTR("failed to decode v3 otr session: %d"), (int)status)); | |
410 | break; | |
427c49bc A |
411 | case 0: // Version 0 was used in seeds of 5.0, transition from those seeds unsupported - keys were in exported data. |
412 | default: | |
5c19dc3a A |
413 | SecError(errSecParam, error, CFSTR("unknown otr session version %hhu"), **bytes); |
414 | goto fail; | |
427c49bc A |
415 | } |
416 | ||
417 | require(SecOTRFICachePublicHash(newID, error), fail); | |
418 | ||
419 | return newID; | |
420 | ||
421 | fail: | |
422 | if (NULL != newID) { | |
423 | SecOTRFIPurgeFromKeychain(newID, NULL); | |
424 | } | |
425 | CFReleaseSafe(newID); | |
426 | return NULL; | |
427 | } | |
428 | ||
429 | SecOTRFullIdentityRef SecOTRFullIdentityCreateFromData(CFAllocatorRef allocator, CFDataRef data, CFErrorRef *error) | |
430 | { | |
431 | if (data == NULL) | |
432 | return NULL; | |
433 | ||
434 | size_t size = (size_t)CFDataGetLength(data); | |
435 | const uint8_t* bytes = CFDataGetBytePtr(data); | |
436 | ||
437 | return SecOTRFullIdentityCreateFromBytes(allocator, &bytes, &size, error); | |
438 | } | |
439 | ||
440 | bool SecOTRFIPurgeFromKeychain(SecOTRFullIdentityRef thisID, CFErrorRef *error) | |
441 | { | |
442 | OSStatus result = SecOTRFIPurgeFromKeychainByValue(thisID->privateSigningKey, sSigningKeyName); | |
443 | if (errSecSuccess == result) { | |
444 | return true; | |
445 | } else { | |
446 | SecOTRCreateError(secOTRErrorOSError, result, CFSTR("OSStatus returned in error code"), NULL, error); | |
447 | return false; | |
448 | } | |
449 | } | |
450 | ||
451 | ||
452 | static OSStatus SecOTRFIPurgeAllFromKeychainByLabel(CFStringRef label) | |
453 | { | |
454 | OSStatus status; | |
455 | const void *keys[] = { kSecClass, | |
456 | kSecAttrKeyClass, | |
457 | kSecAttrLabel, | |
458 | }; | |
459 | const void *values[] = { kSecClassKey, | |
460 | kSecAttrKeyClassPrivate, | |
461 | label, | |
462 | }; | |
463 | CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values, array_size(values), NULL, NULL); | |
464 | bool deleteAtLeastOne = false; | |
465 | int loopLimiter = 500; | |
466 | do { | |
467 | status = SecItemDelete(dict); | |
468 | if (status == errSecSuccess) { | |
469 | deleteAtLeastOne = true; | |
470 | } | |
471 | loopLimiter--; | |
472 | } while ((status == errSecSuccess) && (loopLimiter > 0)); | |
473 | if ((status == errSecItemNotFound) && (deleteAtLeastOne)) { | |
474 | // We've looped until we can't delete any more keys. | |
475 | // Since this will produce an expected 'itemNotFound', but we don't want to break the contract above | |
476 | // (and also we want to make sense) | |
477 | // we muffle the non-error to a success case, which it is. | |
478 | status = errSecSuccess; | |
479 | } | |
480 | CFReleaseSafe(dict); | |
481 | ||
482 | return status; | |
483 | } | |
484 | ||
485 | bool SecOTRFIPurgeAllFromKeychain(CFErrorRef *error) | |
486 | { | |
487 | OSStatus result = SecOTRFIPurgeAllFromKeychainByLabel(sSigningKeyName); | |
488 | if (errSecSuccess == result) { | |
489 | return true; | |
490 | } else { | |
491 | SecOTRCreateError(secOTRErrorOSError, result, CFSTR("OSStatus returned in error code"), NULL, error); | |
492 | return false; | |
493 | } | |
494 | } | |
495 | ||
d64be36e A |
496 | static OSStatus SecOTRFIAppendV3Serialization(SecOTRFullIdentityRef fullID, CFMutableDataRef serializeInto) |
497 | { | |
498 | const uint8_t version = 3; | |
499 | CFIndex start = CFDataGetLength(serializeInto); | |
500 | CFDataAppendBytes(serializeInto, &version, sizeof(version)); | |
501 | CFErrorRef error = nil; | |
502 | CFDataRef privKeyBytes = SecKeyCopyExternalRepresentation(fullID->privateSigningKey, &error); | |
503 | require(privKeyBytes != nil, fail); | |
504 | appendSizeAndData(privKeyBytes, serializeInto); | |
505 | CFReleaseSafe(privKeyBytes); | |
506 | return errSecSuccess; | |
507 | ||
508 | fail: | |
509 | CFDataSetLength(serializeInto, start); | |
510 | ||
511 | return errSecParam; | |
512 | } | |
427c49bc A |
513 | |
514 | static OSStatus SecOTRFIAppendV2Serialization(SecOTRFullIdentityRef fullID, CFMutableDataRef serializeInto) | |
515 | { | |
516 | const uint8_t version = 2; | |
517 | CFIndex start = CFDataGetLength(serializeInto); | |
518 | ||
519 | CFDataAppendBytes(serializeInto, &version, sizeof(version)); | |
5c19dc3a | 520 | appendSizeAndData(fullID->privateKeyPersistentRef, serializeInto); |
427c49bc A |
521 | require(errSecSuccess == appendPublicOctetsAndSize(fullID->publicSigningKey, serializeInto), fail); |
522 | return errSecSuccess; | |
523 | ||
524 | fail: | |
525 | CFDataSetLength(serializeInto, start); | |
526 | ||
527 | return errSecParam; | |
528 | } | |
529 | ||
530 | ||
531 | bool SecOTRFIAppendSerialization(SecOTRFullIdentityRef fullID, CFMutableDataRef serializeInto, CFErrorRef *error) | |
532 | { | |
d64be36e A |
533 | OSStatus status = errSecParam; |
534 | if (fullID->isMessageProtectionKey) { | |
535 | status = SecOTRFIAppendV3Serialization(fullID, serializeInto); | |
536 | } else { | |
537 | status = SecOTRFIAppendV2Serialization(fullID, serializeInto); | |
538 | } | |
539 | ||
427c49bc A |
540 | if (errSecSuccess == status) { |
541 | return true; | |
542 | } else { | |
543 | SecOTRCreateError(secOTRErrorOSError, status, CFSTR("OSStatus returned in error code"), NULL, error); | |
544 | return false; | |
545 | } | |
546 | } | |
547 | ||
548 | size_t SecOTRFISignatureSize(SecOTRFullIdentityRef fullID) | |
549 | { | |
550 | return SecKeyGetSize(fullID->publicSigningKey, kSecKeySignatureSize); | |
551 | } | |
552 | ||
fa7225c8 A |
553 | bool SecOTRFICompareToPublicKey(SecOTRFullIdentityRef fullID, SecKeyRef publicKey) { |
554 | return CFEqualSafe(fullID->publicSigningKey, publicKey); | |
555 | } | |
556 | ||
427c49bc A |
557 | bool SecOTRFIAppendSignature(SecOTRFullIdentityRef fullID, |
558 | CFDataRef dataToHash, | |
559 | CFMutableDataRef appendTo, | |
560 | CFErrorRef *error) | |
561 | { | |
562 | const size_t signatureSize = SecOTRFISignatureSize(fullID); | |
563 | const CFIndex sourceLength = CFDataGetLength(dataToHash); | |
564 | const uint8_t* sourceData = CFDataGetBytePtr(dataToHash); | |
565 | ||
566 | CFIndex start = CFDataGetLength(appendTo); | |
567 | ||
568 | require(((CFIndex)signatureSize) >= 0, fail); | |
569 | ||
570 | CFDataIncreaseLength(appendTo, (CFIndex)signatureSize + 1); | |
571 | ||
572 | uint8_t *size = CFDataGetMutableBytePtr(appendTo) + start; | |
573 | uint8_t* signatureStart = size + 1; | |
574 | size_t signatureUsed = signatureSize; | |
575 | ||
576 | require(SecKeyDigestAndSignWithError(fullID->privateSigningKey, kOTRSignatureAlgIDPtr, | |
577 | sourceData, (size_t)sourceLength, | |
578 | signatureStart, &signatureUsed, error), fail); | |
579 | ||
580 | require(signatureUsed < 256, fail); | |
581 | require(((CFIndex)signatureUsed) >= 0, fail); | |
582 | *size = signatureUsed; | |
583 | ||
584 | CFDataSetLength(appendTo, start + (CFIndex)signatureUsed + 1); | |
585 | ||
586 | return true; | |
587 | ||
588 | fail: | |
589 | CFDataSetLength(appendTo, start); | |
590 | ||
591 | return false; | |
592 | } | |
593 | ||
594 | void SecOTRFIAppendPublicHash(SecOTRFullIdentityRef fullID, CFMutableDataRef appendTo) | |
595 | { | |
596 | CFDataAppendBytes(appendTo, fullID->publicIDHash, sizeof(fullID->publicIDHash)); | |
597 | } | |
598 | ||
599 | bool SecOTRFIComparePublicHash(SecOTRFullIdentityRef fullID, const uint8_t hash[kMPIDHashSize]) | |
600 | { | |
601 | return 0 == memcmp(hash, fullID->publicIDHash, kMPIDHashSize); | |
602 | } |