2 * Copyright (c) 2011-2014 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@
26 #include "SecOTRIdentityPriv.h"
27 #include <utilities/array_size.h>
28 #include <utilities/SecCFError.h>
29 #include <utilities/SecCFWrappers.h>
31 #include <AssertMacros.h>
33 #include <CoreFoundation/CFNumber.h>
34 #include <CoreFoundation/CFString.h>
35 #include <CoreFoundation/CFData.h>
37 #include <Security/SecItem.h>
38 #include <Security/SecItemPriv.h>
39 #include <Security/SecKeyPriv.h>
41 #include <Security/oidsalg.h>
42 #include <Security/SecCertificatePriv.h>
44 #include "SecOTRErrors.h"
46 #include <TargetConditionals.h>
49 // Algorthim ID initialization
52 #define kMessageIdentityRSAKeyBits 1280
53 #define kMessageIdentityECKeyBits 256
55 const SecAsn1AlgId
*kOTRSignatureAlgIDPtr
;
57 void EnsureOTRAlgIDInited(void)
59 static dispatch_once_t kSignatureAlgID_ONCE
;
60 static SecAsn1AlgId kOTRECSignatureAlgID
;
62 dispatch_once(&kSignatureAlgID_ONCE
, ^{
63 kOTRECSignatureAlgID
.algorithm
= CSSMOID_ECDSA_WithSHA1
;
64 kOTRSignatureAlgIDPtr
= &kOTRECSignatureAlgID
;
69 static CFStringRef sSigningKeyName
= CFSTR("OTR Signing Key");
72 // SecOTRFullIdentity implementation
75 CFGiblisFor(SecOTRFullIdentity
);
77 static CF_RETURNS_RETAINED CFStringRef
SecOTRFullIdentityCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
78 SecOTRFullIdentityRef requestor
= (SecOTRFullIdentityRef
)cf
;
79 return CFStringCreateWithFormat(kCFAllocatorDefault
,NULL
,CFSTR("<SecOTRPublicIdentity: %p %02x%02x%02x%02x%02x%02x%02x%02x>"),
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]);
87 static void SecOTRFullIdentityDestroy(CFTypeRef cf
) {
88 SecOTRFullIdentityRef requestor
= (SecOTRFullIdentityRef
)cf
;
90 CFReleaseNull(requestor
->privateSigningKey
);
91 CFReleaseNull(requestor
->publicSigningKey
);
92 CFReleaseNull(requestor
->privateKeyPersistentRef
);
100 static OSStatus
SecOTRFIPurgeFromKeychainByValue(SecKeyRef key
, CFStringRef label
)
103 const void *keys
[] = { kSecClass
,
107 const void *values
[] = { kSecClassKey
,
111 CFDictionaryRef dict
= CFDictionaryCreate(NULL
, keys
, values
, array_size(values
), NULL
, NULL
);
112 status
= SecItemDelete(dict
);
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 */
127 OSStatus status
= SecKeyDigestAndSign(key
, algId
, dataToDigest
, dataToDigestLen
, sig
, sigLen
);
128 require_noerr(status
, fail
);
131 SecOTRCreateError(secOTRErrorOSError
, status
, CFSTR("Error signing message. OSStatus in error code."), NULL
, error
);
136 // SecOTRFullIdentity Functions
139 static bool SecOTRFICachePublicHash(SecOTRFullIdentityRef fullID
, CFErrorRef
*error
)
141 SecOTRPublicIdentityRef pubID
= SecOTRPublicIdentityCopyFromPrivate(NULL
, fullID
, error
);
143 require(pubID
, fail
);
145 SecOTRPICopyHash(pubID
, fullID
->publicIDHash
);
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.
152 static SecKeyRef
SecOTRCreateSigningKey(CFAllocatorRef allocator
) {
153 SecKeyRef publicKey
= NULL
;
154 SecKeyRef fullKey
= NULL
;
155 CFDictionaryRef keygen_parameters
= NULL
;
156 const int signing_keySizeLocal
= kMessageIdentityECKeyBits
;
157 CFNumberRef signing_bitsize
= CFNumberCreate(allocator
, kCFNumberIntType
, &signing_keySizeLocal
);
159 const void *signing_keygen_keys
[] = { kSecAttrKeyType
,
160 kSecAttrKeySizeInBits
,
166 const void *signing_keygen_vals
[] = { kSecAttrKeyTypeEC
,
169 kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate
,
172 keygen_parameters
= CFDictionaryCreate(allocator
,
173 signing_keygen_keys
, signing_keygen_vals
, array_size(signing_keygen_vals
),
174 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
175 CFReleaseNull(signing_bitsize
);
176 require_noerr_action(SecKeyGeneratePair(keygen_parameters
, &publicKey
, &fullKey
),
178 secerror("keygen failed"));
179 SecOTRFIPurgeFromKeychainByValue(publicKey
, sSigningKeyName
);
181 CFReleaseNull(keygen_parameters
);
186 SecOTRFullIdentityRef
SecOTRFullIdentityCreate(CFAllocatorRef allocator
, CFErrorRef
*error
)
188 SecKeyRef signingKey
= SecOTRCreateSigningKey(allocator
);
189 SecOTRFullIdentityRef newID
= SecOTRFullIdentityCreateFromSecKeyRef(allocator
, signingKey
, error
);
190 CFReleaseNull(signingKey
);
197 OSStatus
SecOTRFICreatePrivateKeyReadPersistentRef(const uint8_t **bytes
, size_t *size
, SecKeyRef
* privateKey
, CFDataRef
*newPersistentRef
)
199 OSStatus status
= errSecParam
;
201 CFDataRef foundPersistentRef
= NULL
;
203 require_quiet(newPersistentRef
, fail
);
205 require_noerr_quiet(readSize(bytes
, size
, &dataSize
), fail
);
206 require_quiet(dataSize
<= *size
, fail
);
208 foundPersistentRef
= CFDataCreate(kCFAllocatorDefault
, *bytes
, dataSize
);
209 require_quiet(foundPersistentRef
, fail
);
211 require_noerr_quiet(status
= SecKeyFindWithPersistentRef(foundPersistentRef
, privateKey
), fail
);
216 status
= errSecSuccess
;
218 *newPersistentRef
= foundPersistentRef
;
219 foundPersistentRef
= NULL
;
222 CFReleaseSafe(foundPersistentRef
);
227 OSStatus
SecOTRFICreateKeysFromReadPersistentRef(const uint8_t **bytes
, size_t *size
, SecKeyRef
*publicKey
, SecKeyRef
* privateKey
, CFDataRef
*persistentRef
)
229 SecKeyRef foundKey
= NULL
;
230 CFDataRef foundRef
= NULL
;
232 OSStatus status
= SecOTRFICreatePrivateKeyReadPersistentRef(bytes
, size
, &foundKey
, &foundRef
);
233 require_noerr_quiet(status
, fail
);
235 require_action_quiet(*publicKey
= SecKeyCreatePublicFromPrivate(foundKey
), fail
, status
= errSecInternalComponent
);
237 *privateKey
= foundKey
;
239 *persistentRef
= foundRef
;
243 CFReleaseSafe(foundKey
);
244 CFReleaseSafe(foundRef
);
248 typedef SecKeyRef (*SecOTRPublicKeyCreateFunction
)(CFAllocatorRef allocator
, const uint8_t** data
, size_t* limit
);
251 OSStatus
SecOTRFICreateKeysFromReadPersistentRefAndPublicKey(const uint8_t **bytes
, size_t *size
, SecKeyRef
*publicKey
, SecKeyRef
* privateKey
, CFDataRef
*persistentRef
, SecOTRPublicKeyCreateFunction createPublic
)
253 SecKeyRef foundKey
= NULL
;
254 CFDataRef foundRef
= NULL
;
256 OSStatus status
= SecOTRFICreatePrivateKeyReadPersistentRef(bytes
, size
, &foundKey
, &foundRef
);
257 require_noerr_quiet(status
, fail
);
259 require_action_quiet(*publicKey
= createPublic(NULL
, bytes
, size
), fail
, status
= errSecInternalComponent
);
261 *privateKey
= foundKey
;
263 *persistentRef
= foundRef
;
267 CFReleaseSafe(foundKey
);
268 CFReleaseSafe(foundRef
);
273 OSStatus
SecOTRFIInitFromV1Bytes(SecOTRFullIdentityRef newID
, CFAllocatorRef allocator
,
274 const uint8_t **bytes
,size_t *size
) {
276 require_action(**bytes
== 1, fail
, status
= errSecParam
);
280 require_noerr_quiet(status
= SecOTRFICreateKeysFromReadPersistentRef(bytes
, size
, &newID
->publicSigningKey
, &newID
->privateSigningKey
, &newID
->privateKeyPersistentRef
), fail
);
285 CFReleaseNull(newID
->publicSigningKey
);
286 CFReleaseNull(newID
->privateSigningKey
);
287 CFReleaseNull(newID
->privateKeyPersistentRef
);
293 OSStatus
SecOTRFIInitFromV2Bytes(SecOTRFullIdentityRef newID
, CFAllocatorRef allocator
,
294 const uint8_t **bytes
,size_t *size
) {
296 require_action(**bytes
== 2, fail
, status
= errSecParam
);
300 require_noerr_quiet(status
= SecOTRFICreateKeysFromReadPersistentRefAndPublicKey(bytes
, size
, &newID
->publicSigningKey
, &newID
->privateSigningKey
, &newID
->privateKeyPersistentRef
, &CreateECPublicKeyFrom
), fail
);
305 CFReleaseNull(newID
->privateKeyPersistentRef
);
306 CFReleaseNull(newID
->publicSigningKey
);
307 CFReleaseNull(newID
->privateSigningKey
);
312 // TODO: Probably move to SecKey
313 static CFDataRef
SecKeyCreatePersistentRef(SecKeyRef theKey
, CFErrorRef
*error
) {
314 CFDataRef persistentRef
= NULL
;
315 OSStatus status
= SecKeyCopyPersistentRef(theKey
, &persistentRef
);
317 SecError(status
, error
, CFSTR("failed to find persistent ref for key: %d"), (int)status
);
318 return persistentRef
;
321 SecOTRFullIdentityRef
SecOTRFullIdentityCreateFromSecKeyRef(CFAllocatorRef allocator
, SecKeyRef privateKey
,
323 // TODO - make sure this is an appropriate key type
324 SecOTRFullIdentityRef newID
= CFTypeAllocate(SecOTRFullIdentity
, struct _SecOTRFullIdentity
, allocator
);
325 CFRetainAssign(newID
->privateSigningKey
, privateKey
);
326 require_action(newID
->publicSigningKey
= SecKeyCreatePublicFromPrivate(privateKey
), fail
,
327 SecError(errSecInternalComponent
, error
, CFSTR("Failed to extract public key from private key")));
328 require(newID
->privateKeyPersistentRef
= SecKeyCreatePersistentRef(privateKey
, error
), fail
);
329 require(SecOTRFICachePublicHash(newID
, error
), fail
);
332 CFReleaseNull(newID
);
336 SecOTRFullIdentityRef
SecOTRFullIdentityCreateFromBytes(CFAllocatorRef allocator
, const uint8_t**bytes
, size_t *size
, CFErrorRef
*error
)
338 SecOTRFullIdentityRef newID
= CFTypeAllocate(SecOTRFullIdentity
, struct _SecOTRFullIdentity
, allocator
);
339 EnsureOTRAlgIDInited();
341 require(*size
> 0, fail
);
345 require_noerr_action_quiet(status
= SecOTRFIInitFromV1Bytes(newID
, allocator
, bytes
, size
), fail
, SecError(status
, error
, CFSTR("failed to decode v1 otr session: %d"), (int)status
));
348 require_noerr_action_quiet(status
= SecOTRFIInitFromV2Bytes(newID
, allocator
, bytes
, size
), fail
, SecError(status
, error
, CFSTR("failed to decode v2 otr session: %d"), (int)status
));
350 case 0: // Version 0 was used in seeds of 5.0, transition from those seeds unsupported - keys were in exported data.
352 SecError(errSecParam
, error
, CFSTR("unknown otr session version %hhu"), **bytes
);
356 require(SecOTRFICachePublicHash(newID
, error
), fail
);
362 SecOTRFIPurgeFromKeychain(newID
, NULL
);
364 CFReleaseSafe(newID
);
368 SecOTRFullIdentityRef
SecOTRFullIdentityCreateFromData(CFAllocatorRef allocator
, CFDataRef data
, CFErrorRef
*error
)
373 size_t size
= (size_t)CFDataGetLength(data
);
374 const uint8_t* bytes
= CFDataGetBytePtr(data
);
376 return SecOTRFullIdentityCreateFromBytes(allocator
, &bytes
, &size
, error
);
379 bool SecOTRFIPurgeFromKeychain(SecOTRFullIdentityRef thisID
, CFErrorRef
*error
)
381 OSStatus result
= SecOTRFIPurgeFromKeychainByValue(thisID
->privateSigningKey
, sSigningKeyName
);
382 if (errSecSuccess
== result
) {
385 SecOTRCreateError(secOTRErrorOSError
, result
, CFSTR("OSStatus returned in error code"), NULL
, error
);
391 static OSStatus
SecOTRFIPurgeAllFromKeychainByLabel(CFStringRef label
)
394 const void *keys
[] = { kSecClass
,
398 const void *values
[] = { kSecClassKey
,
399 kSecAttrKeyClassPrivate
,
402 CFDictionaryRef dict
= CFDictionaryCreate(NULL
, keys
, values
, array_size(values
), NULL
, NULL
);
403 bool deleteAtLeastOne
= false;
404 int loopLimiter
= 500;
406 status
= SecItemDelete(dict
);
407 if (status
== errSecSuccess
) {
408 deleteAtLeastOne
= true;
411 } while ((status
== errSecSuccess
) && (loopLimiter
> 0));
412 if ((status
== errSecItemNotFound
) && (deleteAtLeastOne
)) {
413 // We've looped until we can't delete any more keys.
414 // Since this will produce an expected 'itemNotFound', but we don't want to break the contract above
415 // (and also we want to make sense)
416 // we muffle the non-error to a success case, which it is.
417 status
= errSecSuccess
;
424 bool SecOTRFIPurgeAllFromKeychain(CFErrorRef
*error
)
426 OSStatus result
= SecOTRFIPurgeAllFromKeychainByLabel(sSigningKeyName
);
427 if (errSecSuccess
== result
) {
430 SecOTRCreateError(secOTRErrorOSError
, result
, CFSTR("OSStatus returned in error code"), NULL
, error
);
436 static OSStatus
SecOTRFIAppendV2Serialization(SecOTRFullIdentityRef fullID
, CFMutableDataRef serializeInto
)
438 const uint8_t version
= 2;
439 CFIndex start
= CFDataGetLength(serializeInto
);
441 CFDataAppendBytes(serializeInto
, &version
, sizeof(version
));
442 appendSizeAndData(fullID
->privateKeyPersistentRef
, serializeInto
);
443 require(errSecSuccess
== appendPublicOctetsAndSize(fullID
->publicSigningKey
, serializeInto
), fail
);
444 return errSecSuccess
;
447 CFDataSetLength(serializeInto
, start
);
453 bool SecOTRFIAppendSerialization(SecOTRFullIdentityRef fullID
, CFMutableDataRef serializeInto
, CFErrorRef
*error
)
455 OSStatus status
= SecOTRFIAppendV2Serialization(fullID
, serializeInto
);
456 if (errSecSuccess
== status
) {
459 SecOTRCreateError(secOTRErrorOSError
, status
, CFSTR("OSStatus returned in error code"), NULL
, error
);
464 size_t SecOTRFISignatureSize(SecOTRFullIdentityRef fullID
)
466 return SecKeyGetSize(fullID
->publicSigningKey
, kSecKeySignatureSize
);
469 bool SecOTRFICompareToPublicKey(SecOTRFullIdentityRef fullID
, SecKeyRef publicKey
) {
470 return CFEqualSafe(fullID
->publicSigningKey
, publicKey
);
473 bool SecOTRFIAppendSignature(SecOTRFullIdentityRef fullID
,
474 CFDataRef dataToHash
,
475 CFMutableDataRef appendTo
,
478 const size_t signatureSize
= SecOTRFISignatureSize(fullID
);
479 const CFIndex sourceLength
= CFDataGetLength(dataToHash
);
480 const uint8_t* sourceData
= CFDataGetBytePtr(dataToHash
);
482 CFIndex start
= CFDataGetLength(appendTo
);
484 require(((CFIndex
)signatureSize
) >= 0, fail
);
486 CFDataIncreaseLength(appendTo
, (CFIndex
)signatureSize
+ 1);
488 uint8_t *size
= CFDataGetMutableBytePtr(appendTo
) + start
;
489 uint8_t* signatureStart
= size
+ 1;
490 size_t signatureUsed
= signatureSize
;
492 require(SecKeyDigestAndSignWithError(fullID
->privateSigningKey
, kOTRSignatureAlgIDPtr
,
493 sourceData
, (size_t)sourceLength
,
494 signatureStart
, &signatureUsed
, error
), fail
);
496 require(signatureUsed
< 256, fail
);
497 require(((CFIndex
)signatureUsed
) >= 0, fail
);
498 *size
= signatureUsed
;
500 CFDataSetLength(appendTo
, start
+ (CFIndex
)signatureUsed
+ 1);
505 CFDataSetLength(appendTo
, start
);
510 void SecOTRFIAppendPublicHash(SecOTRFullIdentityRef fullID
, CFMutableDataRef appendTo
)
512 CFDataAppendBytes(appendTo
, fullID
->publicIDHash
, sizeof(fullID
->publicIDHash
));
515 bool SecOTRFIComparePublicHash(SecOTRFullIdentityRef fullID
, const uint8_t hash
[kMPIDHashSize
])
517 return 0 == memcmp(hash
, fullID
->publicIDHash
, kMPIDHashSize
);