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