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