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/SecKeyPriv.h>
40 #include <Security/oidsalg.h>
41 #include <Security/SecCertificatePriv.h>
43 #include "SecOTRErrors.h"
45 #include <TargetConditionals.h>
48 // Algorthim ID initialization
51 #define kMessageIdentityRSAKeyBits 1280
52 #define kMessageIdentityECKeyBits 256
54 void EnsureOTRAlgIDInited(void)
56 static dispatch_once_t kSignatureAlgID_ONCE
;
57 static SecAsn1AlgId kOTRECSignatureAlgID
;
59 dispatch_once(&kSignatureAlgID_ONCE
, ^{
60 kOTRECSignatureAlgID
.algorithm
= CSSMOID_ECDSA_WithSHA1
;
61 kOTRSignatureAlgIDPtr
= &kOTRECSignatureAlgID
;
66 static CFStringRef sSigningKeyName
= CFSTR("OTR Signing Key");
69 // SecOTRFullIdentity implementation
72 CFGiblisFor(SecOTRFullIdentity
);
74 static CF_RETURNS_RETAINED CFStringRef
SecOTRFullIdentityCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
75 SecOTRFullIdentityRef requestor
= (SecOTRFullIdentityRef
)cf
;
76 return CFStringCreateWithFormat(kCFAllocatorDefault
,NULL
,CFSTR("<SecOTRPublicIdentity: %p %02x%02x%02x%02x%02x%02x%02x%02x>"),
78 requestor
->publicIDHash
[0], requestor
->publicIDHash
[1],
79 requestor
->publicIDHash
[2], requestor
->publicIDHash
[3],
80 requestor
->publicIDHash
[4], requestor
->publicIDHash
[5],
81 requestor
->publicIDHash
[6], requestor
->publicIDHash
[7]);
84 static void SecOTRFullIdentityDestroy(CFTypeRef cf
) {
85 SecOTRFullIdentityRef requestor
= (SecOTRFullIdentityRef
)cf
;
87 CFReleaseNull(requestor
->privateSigningKey
);
88 CFReleaseNull(requestor
->publicSigningKey
);
89 CFReleaseNull(requestor
->privateKeyPersistentRef
);
97 static OSStatus
SecOTRFIPurgeFromKeychainByValue(SecKeyRef key
, CFStringRef label
)
100 const void *keys
[] = { kSecClass
,
104 const void *values
[] = { kSecClassKey
,
108 CFDictionaryRef dict
= CFDictionaryCreate(NULL
, keys
, values
, array_size(values
), NULL
, NULL
);
109 status
= SecItemDelete(dict
);
115 static bool SecKeyDigestAndSignWithError(
116 SecKeyRef key
, /* Private key */
117 const SecAsn1AlgId
*algId
, /* algorithm oid/params */
118 const uint8_t *dataToDigest
, /* signature over this data */
119 size_t dataToDigestLen
,/* length of dataToDigest */
120 uint8_t *sig
, /* signature, RETURNED */
121 size_t *sigLen
, /* IN/OUT */
124 OSStatus status
= SecKeyDigestAndSign(key
, algId
, dataToDigest
, dataToDigestLen
, sig
, sigLen
);
125 require_noerr(status
, fail
);
128 SecOTRCreateError(secOTRErrorOSError
, status
, CFSTR("Error signing message. OSStatus in error code."), NULL
, error
);
133 // SecOTRFullIdentity Functions
136 static bool SecOTRFICachePublicHash(SecOTRFullIdentityRef fullID
, CFErrorRef
*error
)
138 SecOTRPublicIdentityRef pubID
= SecOTRPublicIdentityCopyFromPrivate(NULL
, fullID
, error
);
140 require(pubID
, fail
);
142 SecOTRPICopyHash(pubID
, fullID
->publicIDHash
);
145 CFReleaseSafe(pubID
);
146 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.
149 #if !TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
150 #define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v);
151 SEC_CONST_DECL (kSecAttrAccessible
, "pdmn");
152 SEC_CONST_DECL (kSecAttrAccessibleAlwaysThisDeviceOnly
, "dku");
155 static SecKeyRef
SecOTRCreateSigningKey(CFAllocatorRef allocator
) {
156 SecKeyRef publicKey
= NULL
;
157 SecKeyRef fullKey
= NULL
;
158 CFDictionaryRef keygen_parameters
= NULL
;
159 const int signing_keySizeLocal
= kMessageIdentityECKeyBits
;
160 CFNumberRef signing_bitsize
= CFNumberCreate(allocator
, kCFNumberIntType
, &signing_keySizeLocal
);
162 const void *signing_keygen_keys
[] = { kSecAttrKeyType
,
163 kSecAttrKeySizeInBits
,
169 const void *signing_keygen_vals
[] = { kSecAttrKeyTypeEC
,
172 kSecAttrAccessibleAlwaysThisDeviceOnly
,
175 keygen_parameters
= CFDictionaryCreate(allocator
,
176 signing_keygen_keys
, signing_keygen_vals
, array_size(signing_keygen_vals
),
177 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
178 CFReleaseNull(signing_bitsize
);
179 require_noerr_action(SecKeyGeneratePair(keygen_parameters
, &publicKey
, &fullKey
),
181 secerror("keygen failed"));
182 SecOTRFIPurgeFromKeychainByValue(publicKey
, sSigningKeyName
);
184 CFReleaseNull(keygen_parameters
);
189 SecOTRFullIdentityRef
SecOTRFullIdentityCreate(CFAllocatorRef allocator
, CFErrorRef
*error
)
191 SecKeyRef signingKey
= SecOTRCreateSigningKey(allocator
);
192 SecOTRFullIdentityRef newID
= SecOTRFullIdentityCreateFromSecKeyRef(allocator
, signingKey
, error
);
193 CFReleaseNull(signingKey
);
200 OSStatus
SecOTRFICreatePrivateKeyReadPersistentRef(const uint8_t **bytes
, size_t *size
, SecKeyRef
* privateKey
, CFDataRef
*newPersistentRef
)
202 OSStatus status
= errSecParam
;
204 CFDataRef foundPersistentRef
= NULL
;
206 require_quiet(newPersistentRef
, fail
);
208 require_noerr_quiet(readSize(bytes
, size
, &dataSize
), fail
);
209 require_quiet(dataSize
<= *size
, fail
);
211 foundPersistentRef
= CFDataCreate(kCFAllocatorDefault
, *bytes
, dataSize
);
212 require_quiet(foundPersistentRef
, fail
);
214 require_noerr_quiet(status
= SecKeyFindWithPersistentRef(foundPersistentRef
, privateKey
), fail
);
219 status
= errSecSuccess
;
221 *newPersistentRef
= foundPersistentRef
;
222 foundPersistentRef
= NULL
;
225 CFReleaseSafe(foundPersistentRef
);
230 OSStatus
SecOTRFICreateKeysFromReadPersistentRef(const uint8_t **bytes
, size_t *size
, SecKeyRef
*publicKey
, SecKeyRef
* privateKey
, CFDataRef
*persistentRef
)
232 SecKeyRef foundKey
= NULL
;
233 CFDataRef foundRef
= NULL
;
235 OSStatus status
= SecOTRFICreatePrivateKeyReadPersistentRef(bytes
, size
, &foundKey
, &foundRef
);
236 require_noerr_quiet(status
, fail
);
238 require_action_quiet(*publicKey
= SecKeyCreatePublicFromPrivate(foundKey
), fail
, status
= errSecInternalComponent
);
240 *privateKey
= foundKey
;
242 *persistentRef
= foundRef
;
246 CFReleaseSafe(foundKey
);
247 CFReleaseSafe(foundRef
);
251 typedef SecKeyRef (*SecOTRPublicKeyCreateFunction
)(CFAllocatorRef allocator
, const uint8_t** data
, size_t* limit
);
254 OSStatus
SecOTRFICreateKeysFromReadPersistentRefAndPublicKey(const uint8_t **bytes
, size_t *size
, SecKeyRef
*publicKey
, SecKeyRef
* privateKey
, CFDataRef
*persistentRef
, SecOTRPublicKeyCreateFunction createPublic
)
256 SecKeyRef foundKey
= NULL
;
257 CFDataRef foundRef
= NULL
;
259 OSStatus status
= SecOTRFICreatePrivateKeyReadPersistentRef(bytes
, size
, &foundKey
, &foundRef
);
260 require_noerr_quiet(status
, fail
);
262 require_action_quiet(*publicKey
= createPublic(NULL
, bytes
, size
), fail
, status
= errSecInternalComponent
);
264 *privateKey
= foundKey
;
266 *persistentRef
= foundRef
;
270 CFReleaseSafe(foundKey
);
271 CFReleaseSafe(foundRef
);
276 OSStatus
SecOTRFIInitFromV1Bytes(SecOTRFullIdentityRef newID
, CFAllocatorRef allocator
,
277 const uint8_t **bytes
,size_t *size
) {
279 require_action(**bytes
== 1, fail
, status
= errSecParam
);
283 require_noerr_quiet(status
= SecOTRFICreateKeysFromReadPersistentRef(bytes
, size
, &newID
->publicSigningKey
, &newID
->privateSigningKey
, &newID
->privateKeyPersistentRef
), fail
);
288 CFReleaseNull(newID
->publicSigningKey
);
289 CFReleaseNull(newID
->privateSigningKey
);
290 CFReleaseNull(newID
->privateKeyPersistentRef
);
296 OSStatus
SecOTRFIInitFromV2Bytes(SecOTRFullIdentityRef newID
, CFAllocatorRef allocator
,
297 const uint8_t **bytes
,size_t *size
) {
299 require_action(**bytes
== 2, fail
, status
= errSecParam
);
303 require_noerr_quiet(status
= SecOTRFICreateKeysFromReadPersistentRefAndPublicKey(bytes
, size
, &newID
->publicSigningKey
, &newID
->privateSigningKey
, &newID
->privateKeyPersistentRef
, &CreateECPublicKeyFrom
), fail
);
308 CFReleaseNull(newID
->privateKeyPersistentRef
);
309 CFReleaseNull(newID
->publicSigningKey
);
310 CFReleaseNull(newID
->privateSigningKey
);
315 // TODO: Probably move to SecKey
316 static CFDataRef
SecKeyCreatePersistentRef(SecKeyRef theKey
, CFErrorRef
*error
) {
317 CFDataRef persistentRef
= NULL
;
318 OSStatus status
= SecKeyCopyPersistentRef(theKey
, &persistentRef
);
320 SecError(status
, error
, CFSTR("failed to find persistent ref for key: %d"), (int)status
);
321 return persistentRef
;
324 SecOTRFullIdentityRef
SecOTRFullIdentityCreateFromSecKeyRef(CFAllocatorRef allocator
, SecKeyRef privateKey
,
326 // TODO - make sure this is an appropriate key type
327 SecOTRFullIdentityRef newID
= CFTypeAllocate(SecOTRFullIdentity
, struct _SecOTRFullIdentity
, allocator
);
328 CFRetainAssign(newID
->privateSigningKey
, privateKey
);
329 require_action(newID
->publicSigningKey
= SecKeyCreatePublicFromPrivate(privateKey
), fail
,
330 SecError(errSecInternalComponent
, error
, CFSTR("Failed to extract public key from private key")));
331 require(newID
->privateKeyPersistentRef
= SecKeyCreatePersistentRef(privateKey
, error
), fail
);
332 require(SecOTRFICachePublicHash(newID
, error
), fail
);
335 CFReleaseNull(newID
);
339 SecOTRFullIdentityRef
SecOTRFullIdentityCreateFromBytes(CFAllocatorRef allocator
, const uint8_t**bytes
, size_t *size
, CFErrorRef
*error
)
341 SecOTRFullIdentityRef newID
= CFTypeAllocate(SecOTRFullIdentity
, struct _SecOTRFullIdentity
, allocator
);
342 EnsureOTRAlgIDInited();
344 require(*size
> 0, fail
);
348 require_noerr_action_quiet(status
= SecOTRFIInitFromV1Bytes(newID
, allocator
, bytes
, size
), fail
, SecError(status
, error
, CFSTR("failed to decode v1 otr session: %d"), (int)status
));
351 require_noerr_action_quiet(status
= SecOTRFIInitFromV2Bytes(newID
, allocator
, bytes
, size
), fail
, SecError(status
, error
, CFSTR("failed to decode v2 otr session: %d"), (int)status
));
353 case 0: // Version 0 was used in seeds of 5.0, transition from those seeds unsupported - keys were in exported data.
355 SecError(errSecParam
, error
, CFSTR("unknown otr session version %hhu"), **bytes
);
359 require(SecOTRFICachePublicHash(newID
, error
), fail
);
365 SecOTRFIPurgeFromKeychain(newID
, NULL
);
367 CFReleaseSafe(newID
);
371 SecOTRFullIdentityRef
SecOTRFullIdentityCreateFromData(CFAllocatorRef allocator
, CFDataRef data
, CFErrorRef
*error
)
376 size_t size
= (size_t)CFDataGetLength(data
);
377 const uint8_t* bytes
= CFDataGetBytePtr(data
);
379 return SecOTRFullIdentityCreateFromBytes(allocator
, &bytes
, &size
, error
);
382 bool SecOTRFIPurgeFromKeychain(SecOTRFullIdentityRef thisID
, CFErrorRef
*error
)
384 OSStatus result
= SecOTRFIPurgeFromKeychainByValue(thisID
->privateSigningKey
, sSigningKeyName
);
385 if (errSecSuccess
== result
) {
388 SecOTRCreateError(secOTRErrorOSError
, result
, CFSTR("OSStatus returned in error code"), NULL
, error
);
394 static OSStatus
SecOTRFIPurgeAllFromKeychainByLabel(CFStringRef label
)
397 const void *keys
[] = { kSecClass
,
401 const void *values
[] = { kSecClassKey
,
402 kSecAttrKeyClassPrivate
,
405 CFDictionaryRef dict
= CFDictionaryCreate(NULL
, keys
, values
, array_size(values
), NULL
, NULL
);
406 bool deleteAtLeastOne
= false;
407 int loopLimiter
= 500;
409 status
= SecItemDelete(dict
);
410 if (status
== errSecSuccess
) {
411 deleteAtLeastOne
= true;
414 } while ((status
== errSecSuccess
) && (loopLimiter
> 0));
415 if ((status
== errSecItemNotFound
) && (deleteAtLeastOne
)) {
416 // We've looped until we can't delete any more keys.
417 // Since this will produce an expected 'itemNotFound', but we don't want to break the contract above
418 // (and also we want to make sense)
419 // we muffle the non-error to a success case, which it is.
420 status
= errSecSuccess
;
427 bool SecOTRFIPurgeAllFromKeychain(CFErrorRef
*error
)
429 OSStatus result
= SecOTRFIPurgeAllFromKeychainByLabel(sSigningKeyName
);
430 if (errSecSuccess
== result
) {
433 SecOTRCreateError(secOTRErrorOSError
, result
, CFSTR("OSStatus returned in error code"), NULL
, error
);
439 static OSStatus
SecOTRFIAppendV2Serialization(SecOTRFullIdentityRef fullID
, CFMutableDataRef serializeInto
)
441 const uint8_t version
= 2;
442 CFIndex start
= CFDataGetLength(serializeInto
);
444 CFDataAppendBytes(serializeInto
, &version
, sizeof(version
));
445 appendSizeAndData(fullID
->privateKeyPersistentRef
, serializeInto
);
446 require(errSecSuccess
== appendPublicOctetsAndSize(fullID
->publicSigningKey
, serializeInto
), fail
);
447 return errSecSuccess
;
450 CFDataSetLength(serializeInto
, start
);
456 bool SecOTRFIAppendSerialization(SecOTRFullIdentityRef fullID
, CFMutableDataRef serializeInto
, CFErrorRef
*error
)
458 OSStatus status
= SecOTRFIAppendV2Serialization(fullID
, serializeInto
);
459 if (errSecSuccess
== status
) {
462 SecOTRCreateError(secOTRErrorOSError
, status
, CFSTR("OSStatus returned in error code"), NULL
, error
);
467 size_t SecOTRFISignatureSize(SecOTRFullIdentityRef fullID
)
469 return SecKeyGetSize(fullID
->publicSigningKey
, kSecKeySignatureSize
);
472 bool SecOTRFIAppendSignature(SecOTRFullIdentityRef fullID
,
473 CFDataRef dataToHash
,
474 CFMutableDataRef appendTo
,
477 const size_t signatureSize
= SecOTRFISignatureSize(fullID
);
478 const CFIndex sourceLength
= CFDataGetLength(dataToHash
);
479 const uint8_t* sourceData
= CFDataGetBytePtr(dataToHash
);
481 CFIndex start
= CFDataGetLength(appendTo
);
483 require(((CFIndex
)signatureSize
) >= 0, fail
);
485 CFDataIncreaseLength(appendTo
, (CFIndex
)signatureSize
+ 1);
487 uint8_t *size
= CFDataGetMutableBytePtr(appendTo
) + start
;
488 uint8_t* signatureStart
= size
+ 1;
489 size_t signatureUsed
= signatureSize
;
491 require(SecKeyDigestAndSignWithError(fullID
->privateSigningKey
, kOTRSignatureAlgIDPtr
,
492 sourceData
, (size_t)sourceLength
,
493 signatureStart
, &signatureUsed
, error
), fail
);
495 require(signatureUsed
< 256, fail
);
496 require(((CFIndex
)signatureUsed
) >= 0, fail
);
497 *size
= signatureUsed
;
499 CFDataSetLength(appendTo
, start
+ (CFIndex
)signatureUsed
+ 1);
504 CFDataSetLength(appendTo
, start
);
509 void SecOTRFIAppendPublicHash(SecOTRFullIdentityRef fullID
, CFMutableDataRef appendTo
)
511 CFDataAppendBytes(appendTo
, fullID
->publicIDHash
, sizeof(fullID
->publicIDHash
));
514 bool SecOTRFIComparePublicHash(SecOTRFullIdentityRef fullID
, const uint8_t hash
[kMPIDHashSize
])
516 return 0 == memcmp(hash
, fullID
->publicIDHash
, kMPIDHashSize
);