]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecOTRPublicIdentity.c
Security-59306.80.4.tar.gz
[apple/security.git] / OSX / sec / Security / SecOTRPublicIdentity.c
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/SecCFWrappers.h>
28
29 #include <AssertMacros.h>
30
31 #include <CoreFoundation/CFNumber.h>
32 #include <CoreFoundation/CFString.h>
33 #include <CoreFoundation/CFData.h>
34
35 #include <Security/SecKey.h>
36 #include <Security/SecKeyPriv.h>
37
38 #include <corecrypto/ccn.h>
39 #include <corecrypto/ccec.h>
40 #include <corecrypto/ccder.h>
41
42 #import <sys/syslog.h>
43
44 #include "SecOTRErrors.h"
45 #include <TargetConditionals.h>
46
47 #include <CommonCrypto/CommonDigest.h>
48 #include <CommonCrypto/CommonDigestSPI.h>
49
50 /*
51 * Support for encoding and decoding DH parameter blocks.
52 * Apple form encodes the reciprocal of the prime p.
53 */
54
55 enum {
56 kOTRPIDER_SigningID = 1,
57 kOTRPIDER_SupportsHashes =3,
58 };
59
60 //
61 // SecOTRPublicIdentity implementation
62 //
63
64 CFGiblisFor(SecOTRPublicIdentity);
65
66 static bool sAdvertiseHashes = false;
67
68 static CF_RETURNS_RETAINED CFStringRef SecOTRPublicIdentityCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
69 SecOTRPublicIdentityRef requestor = (SecOTRPublicIdentityRef)cf;
70 return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecOTRPublicIdentity: %p %02x%02x%02x%02x%02x%02x%02x%02x>"),
71 requestor,
72 requestor->hash[0], requestor->hash[1],
73 requestor->hash[2], requestor->hash[3],
74 requestor->hash[4], requestor->hash[5],
75 requestor->hash[6], requestor->hash[7]);
76 }
77
78 static void SecOTRPublicIdentityDestroy(CFTypeRef cf) {
79 SecOTRPublicIdentityRef requestor = (SecOTRPublicIdentityRef)cf;
80
81 CFReleaseNull(requestor->publicSigningKey);
82 }
83
84 static bool SecKeyDigestAndVerifyWithError(
85 SecKeyRef key, /* Public key */
86 const SecAsn1AlgId *algId, /* algorithm oid/params */
87 const uint8_t *dataToDigest, /* signature over this data */
88 size_t dataToDigestLen,/* length of dataToDigest */
89 uint8_t *sig, /* signature to verify */
90 size_t sigLen, /* length of sig */
91 CFErrorRef *error) {
92
93 OSStatus status = SecKeyDigestAndVerify(key, algId, dataToDigest, dataToDigestLen, sig, sigLen);
94 require_noerr(status, fail);
95 return true;
96 fail:
97 SecOTRCreateError(secOTRErrorOSError, status, CFSTR("Error verifying message. OSStatus in error code."), NULL, error);
98 return false;
99 }
100
101 static bool SecOTRPICacheHash(SecOTRPublicIdentityRef pubID, CFErrorRef *error)
102 {
103 bool result = false;
104
105 CFMutableDataRef stream = CFDataCreateMutable(NULL, 0);
106
107 require(SecOTRPIAppendSerialization(pubID, stream, error), fail);
108
109 CCDigest(kCCDigestSHA1, CFDataGetBytePtr(stream), (CC_LONG)CFDataGetLength(stream), pubID->hash);
110
111 result = true;
112
113 fail:
114 CFReleaseSafe(stream);
115 return result;
116 }
117
118 void SecOTRAdvertiseHashes(bool advertise) {
119 sAdvertiseHashes = advertise;
120 }
121
122 bool SecOTRPICompareToPublicKey(SecOTRPublicIdentityRef pubID, SecKeyRef publicKey) {
123 return CFEqualSafe(pubID->publicSigningKey, publicKey);
124 }
125
126 SecOTRPublicIdentityRef SecOTRPublicIdentityCopyFromPrivate(CFAllocatorRef allocator, SecOTRFullIdentityRef fullID, CFErrorRef *error)
127 {
128 SecOTRPublicIdentityRef result = CFTypeAllocate(SecOTRPublicIdentity, struct _SecOTRPublicIdentity, allocator);
129
130 EnsureOTRAlgIDInited();
131
132 result->publicSigningKey = fullID->publicSigningKey;
133 CFRetain(result->publicSigningKey);
134
135 require(SecOTRPICacheHash(result, error), fail);
136
137 return result;
138
139 fail:
140 CFReleaseSafe(result);
141 return NULL;
142 }
143
144
145 SecOTRPublicIdentityRef SecOTRPublicIdentityCreateFromSecKeyRef(CFAllocatorRef allocator, SecKeyRef publicKey,
146 CFErrorRef *error) {
147 // TODO - make sure this is an appropriate key type
148 SecOTRPublicIdentityRef result = CFTypeAllocate(SecOTRPublicIdentity, struct _SecOTRPublicIdentity, allocator);
149 result->publicSigningKey = publicKey;
150 CFRetain(result->publicSigningKey);
151 require(SecOTRPICacheHash(result, error), fail);
152 return result;
153 fail:
154 CFRelease(result->publicSigningKey);
155 CFReleaseSafe(result);
156 return NULL;
157 }
158
159 typedef SecKeyRef (*SecOTRPublicKeyCreateFunction)(CFAllocatorRef allocator, const uint8_t** data, size_t* limit);
160
161 static SecKeyRef SecOTRCreatePublicKeyFrom(const uint8_t* keyData, size_t keyDataSize, ccder_tag tagContainingKey, SecOTRPublicKeyCreateFunction createFunction)
162 {
163 SecKeyRef createdKey = NULL;
164
165 createdKey = createFunction(kCFAllocatorDefault, &keyData, &keyDataSize);
166
167 require(createdKey != NULL, fail);
168 require(keyDataSize == 0, fail);
169
170 return createdKey;
171
172 fail:
173 CFReleaseSafe(createdKey);
174 return NULL;
175 }
176
177
178 SecOTRPublicIdentityRef SecOTRPublicIdentityCreateFromBytes(CFAllocatorRef allocator, const uint8_t**bytes, size_t* size, CFErrorRef *error)
179 {
180 CFErrorRef stackedError = NULL;
181
182 SecOTRPublicIdentityRef newID = CFTypeAllocate(SecOTRPublicIdentity, struct _SecOTRPublicIdentity, allocator);
183
184 EnsureOTRAlgIDInited();
185
186 const uint8_t* fullSequenceEnd = *bytes + *size;
187
188 const uint8_t* keyData = ccder_decode_sequence_tl(&fullSequenceEnd, *bytes, fullSequenceEnd);
189 size_t fullSize = (size_t)(fullSequenceEnd - *bytes);
190
191 size_t keyDataSize;
192 keyData = ccder_decode_tl(CCDER_CONTEXT_SPECIFIC | kOTRPIDER_SigningID, &keyDataSize, keyData, fullSequenceEnd);
193 newID->publicSigningKey = SecOTRCreatePublicKeyFrom(keyData, keyDataSize, kOTRPIDER_SigningID, &CreateECPublicKeyFrom);
194 require(newID->publicSigningKey != NULL, fail);
195 keyData += keyDataSize;
196
197 newID->wantsHashes = (NULL != ccder_decode_tl(CCDER_CONTEXT_SPECIFIC | kOTRPIDER_SupportsHashes, &keyDataSize, keyData, fullSequenceEnd));
198
199 require(SecOTRPICacheHash(newID, &stackedError), fail);
200
201 *bytes += fullSize;
202 *size -= fullSize;
203
204 return newID;
205
206 fail:
207 SecOTRCreateError(secOTRErrorLocal, kSecOTRErrorCreatePublicIdentity, CFSTR("Error creating public identity from bytes"), stackedError, error);
208 CFReleaseSafe(newID);
209 return NULL;
210 }
211
212 SecOTRPublicIdentityRef SecOTRPublicIdentityCreateFromData(CFAllocatorRef allocator, CFDataRef serializedData, CFErrorRef *error)
213 {
214 if (serializedData == NULL)
215 return NULL;
216
217 size_t length = (size_t)CFDataGetLength(serializedData);
218 const uint8_t* bytes = CFDataGetBytePtr(serializedData);
219 return SecOTRPublicIdentityCreateFromBytes(allocator, &bytes, &length, error);
220 }
221
222 bool SecOTRPIEqualToBytes(SecOTRPublicIdentityRef id, const uint8_t*bytes, CFIndex size)
223 {
224 CFDataRef dataToMatch = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, bytes, size, kCFAllocatorNull);
225 CFMutableDataRef idStreamed = CFDataCreateMutable(kCFAllocatorDefault, 0);
226
227 SecOTRPIAppendSerialization(id, idStreamed, NULL);
228
229 bool equal = CFEqualSafe(dataToMatch, idStreamed);
230
231 if (!equal) {
232 CFDataPerformWithHexString(dataToMatch, ^(CFStringRef dataToMatchString) {
233 CFDataPerformWithHexString(idStreamed, ^(CFStringRef idStreamedString) {
234 secnotice("otr", "ID Comparison failed: d: %@ id: %@", dataToMatchString, idStreamedString);
235 });
236 });
237 }
238
239 CFReleaseNull(dataToMatch);
240 CFReleaseNull(idStreamed);
241
242 return equal;
243 }
244
245 bool SecOTRPIEqual(SecOTRPublicIdentityRef left, SecOTRPublicIdentityRef right)
246 {
247 if (left == right)
248 return true;
249
250 CFMutableDataRef leftData = CFDataCreateMutable(kCFAllocatorDefault, 0);
251 CFMutableDataRef rightData = CFDataCreateMutable(kCFAllocatorDefault, 0);
252
253 SecOTRPIAppendSerialization(left, leftData, NULL);
254 SecOTRPIAppendSerialization(right, rightData, NULL);
255
256 bool match = CFEqualSafe(leftData, rightData);
257
258 CFReleaseNull(leftData);
259 CFReleaseNull(rightData);
260
261 return match;
262 }
263
264 size_t SecOTRPISignatureSize(SecOTRPublicIdentityRef publicID)
265 {
266 return SecKeyGetSize(publicID->publicSigningKey, kSecKeySignatureSize);
267 }
268
269 bool SecOTRPIAppendSerialization(SecOTRPublicIdentityRef publicID, CFMutableDataRef serializeInto, CFErrorRef *error)
270 {
271 CFIndex start = CFDataGetLength(serializeInto);
272 CFMutableDataRef signingKeySerialized = CFDataCreateMutable(kCFAllocatorDefault, 0);
273
274 uint8_t sendHashes[1] = { 0xFF };
275
276 require_noerr(appendPublicOctetsAndSize(publicID->publicSigningKey, signingKeySerialized), fail);
277
278 size_t outputSize = ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
279 ccder_sizeof_implicit_raw_octet_string(CCDER_CONTEXT_SPECIFIC | kOTRPIDER_SigningID, (size_t)CFDataGetLength(signingKeySerialized)) +
280 (sAdvertiseHashes ? ccder_sizeof_implicit_raw_octet_string(CCDER_CONTEXT_SPECIFIC | kOTRPIDER_SupportsHashes, sizeof(sendHashes)) : 0));
281
282 CFDataIncreaseLength(serializeInto, outputSize);
283
284 uint8_t *outputBuffer = CFDataGetMutableBytePtr(serializeInto) + start;
285 uint8_t *outputBufferEnd = outputBuffer + outputSize;
286
287 uint8_t *result = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, outputBufferEnd, outputBuffer,
288 ccder_encode_implicit_raw_octet_string(CCDER_CONTEXT_SPECIFIC | kOTRPIDER_SigningID, (size_t)CFDataGetLength(signingKeySerialized), CFDataGetBytePtr(signingKeySerialized), outputBuffer,
289 sAdvertiseHashes ? ccder_encode_implicit_raw_octet_string(CCDER_CONTEXT_SPECIFIC | kOTRPIDER_SupportsHashes, sizeof(sendHashes), sendHashes, outputBuffer, outputBufferEnd) : outputBufferEnd));
290
291 require_quiet(result == outputBuffer, fail);
292
293 CFReleaseSafe(signingKeySerialized);
294
295 return true;
296
297 fail:
298 CFReleaseSafe(signingKeySerialized);
299
300 CFDataSetLength(serializeInto, start);
301
302 SecOTRCreateError(secOTRErrorLocal, kSecOTRErrorCreatePublicBytes, CFSTR("Unable to create public key bytes"), NULL, error);
303 return false;
304 }
305
306 static const uint8_t *mp_decode_forced_uint(cc_size n, cc_unit *r, const uint8_t *der, const uint8_t *der_end) {
307 size_t len;
308 der = ccder_decode_tl(CCDER_INTEGER, &len, der, der_end);
309 if (der && ccn_read_uint(n, r, len, der) >= 0)
310 return der + len;
311
312 return NULL;
313 }
314
315 static void SecOTRPIRecreateSignature(const uint8_t *oldSignature, size_t oldSignatureSize, uint8_t **newSignature, size_t *newSignatureSize)
316 {
317 cc_unit r[ccec_cp_n(ccec_cp_256())], s[ccec_cp_n(ccec_cp_256())];
318 cc_size n = ccec_cp_n(ccec_cp_256());
319
320 const uint8_t *oldSignatureEnd = oldSignature + oldSignatureSize;
321
322 oldSignature = ccder_decode_sequence_tl(&oldSignatureEnd, oldSignature, oldSignatureEnd);
323 oldSignature = mp_decode_forced_uint(n, r, oldSignature, oldSignatureEnd);
324 oldSignature = mp_decode_forced_uint(n, s, oldSignature, oldSignatureEnd);
325 (void) oldSignature; // We could check for a good end, but it'll come out when we re-encode.
326
327 const uint8_t *outputPointer = *newSignature;
328 uint8_t *outputEndPointer = *newSignature + *newSignatureSize;
329
330 *newSignature = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, outputEndPointer, outputPointer, ccder_encode_integer(n, r, outputPointer, ccder_encode_integer(n, s, outputPointer, outputEndPointer)));
331 long newSigSize = outputEndPointer - *newSignature;
332 *newSignatureSize = (newSigSize >= 0) ? (size_t)newSigSize : 0;
333 }
334
335 bool SecOTRPIVerifySignature(SecOTRPublicIdentityRef publicID,
336 const uint8_t *dataToHash, size_t amountToHash,
337 const uint8_t *signatureStart, size_t signatureSize, CFErrorRef *error)
338 {
339 require(signatureSize > 0, fail);
340 require(*signatureStart == signatureSize - 1, fail);
341 signatureSize -= 1;
342 signatureStart += 1;
343
344 require(SecKeyDigestAndVerifyWithError(publicID->publicSigningKey, kOTRSignatureAlgIDPtr,
345 dataToHash, amountToHash,
346 (uint8_t*)signatureStart, signatureSize, NULL), fail);
347 return true;
348 fail: ;
349 uint8_t *replacementSignature = malloc(signatureSize + 3);
350 require(replacementSignature != NULL, fail2);
351
352 size_t replacementSignatureLen = sizeof(replacementSignature);
353 uint8_t *replacementSignaturePtr = replacementSignature;
354
355 SecOTRPIRecreateSignature(signatureStart, signatureSize, &replacementSignaturePtr, &replacementSignatureLen);
356
357 require_action(replacementSignaturePtr, fail2, SecOTRCreateError(secOTRErrorLocal, kSecOTRErrorSignatureDidNotRecreate, CFSTR("Unable to recreate signature blob."), NULL, error));
358
359 require(SecKeyDigestAndVerifyWithError(publicID->publicSigningKey, kOTRSignatureAlgIDPtr,
360 dataToHash, amountToHash,
361 replacementSignaturePtr, replacementSignatureLen, error), fail2);
362 free(replacementSignature);
363 return true;
364
365 fail2:
366 free(replacementSignature);
367 return false;
368 }
369
370 void SecOTRPICopyHash(SecOTRPublicIdentityRef publicID, uint8_t hash[kMPIDHashSize])
371 {
372 memcpy(hash, publicID->hash, kMPIDHashSize);
373 }
374
375 void SecOTRPIAppendHash(SecOTRPublicIdentityRef publicID, CFMutableDataRef appendTo)
376 {
377 CFDataAppendBytes(appendTo, publicID->hash, sizeof(publicID->hash));
378 }
379
380 bool SecOTRPICompareHash(SecOTRPublicIdentityRef publicID, const uint8_t hash[kMPIDHashSize])
381 {
382 return 0 == memcmp(hash, publicID->hash, kMPIDHashSize);
383 }