]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecOTRPublicIdentity.c
Security-59754.41.1.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 require(keyData != NULL, fail);
190 size_t fullSize = (size_t)(fullSequenceEnd - *bytes);
191
192 size_t keyDataSize;
193 keyData = ccder_decode_tl(CCDER_CONTEXT_SPECIFIC | kOTRPIDER_SigningID, &keyDataSize, keyData, fullSequenceEnd);
194 newID->publicSigningKey = SecOTRCreatePublicKeyFrom(keyData, keyDataSize, kOTRPIDER_SigningID, &CreateECPublicKeyFrom);
195 require(newID->publicSigningKey != NULL, fail);
196 keyData += keyDataSize;
197
198 newID->wantsHashes = (NULL != ccder_decode_tl(CCDER_CONTEXT_SPECIFIC | kOTRPIDER_SupportsHashes, &keyDataSize, keyData, fullSequenceEnd));
199
200 require(SecOTRPICacheHash(newID, &stackedError), fail);
201
202 *bytes += fullSize;
203 *size -= fullSize;
204
205 return newID;
206
207 fail:
208 SecOTRCreateError(secOTRErrorLocal, kSecOTRErrorCreatePublicIdentity, CFSTR("Error creating public identity from bytes"), stackedError, error);
209 CFReleaseSafe(newID);
210 return NULL;
211 }
212
213 SecOTRPublicIdentityRef SecOTRPublicIdentityCreateFromData(CFAllocatorRef allocator, CFDataRef serializedData, CFErrorRef *error)
214 {
215 if (serializedData == NULL)
216 return NULL;
217
218 size_t length = (size_t)CFDataGetLength(serializedData);
219 const uint8_t* bytes = CFDataGetBytePtr(serializedData);
220 return SecOTRPublicIdentityCreateFromBytes(allocator, &bytes, &length, error);
221 }
222
223 bool SecOTRPIEqualToBytes(SecOTRPublicIdentityRef id, const uint8_t*bytes, CFIndex size)
224 {
225 CFDataRef dataToMatch = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, bytes, size, kCFAllocatorNull);
226 CFMutableDataRef idStreamed = CFDataCreateMutable(kCFAllocatorDefault, 0);
227
228 SecOTRPIAppendSerialization(id, idStreamed, NULL);
229
230 bool equal = CFEqualSafe(dataToMatch, idStreamed);
231
232 if (!equal) {
233 CFDataPerformWithHexString(dataToMatch, ^(CFStringRef dataToMatchString) {
234 CFDataPerformWithHexString(idStreamed, ^(CFStringRef idStreamedString) {
235 secnotice("otr", "ID Comparison failed: d: %@ id: %@", dataToMatchString, idStreamedString);
236 });
237 });
238 }
239
240 CFReleaseNull(dataToMatch);
241 CFReleaseNull(idStreamed);
242
243 return equal;
244 }
245
246 bool SecOTRPIEqual(SecOTRPublicIdentityRef left, SecOTRPublicIdentityRef right)
247 {
248 if (left == right)
249 return true;
250
251 CFMutableDataRef leftData = CFDataCreateMutable(kCFAllocatorDefault, 0);
252 CFMutableDataRef rightData = CFDataCreateMutable(kCFAllocatorDefault, 0);
253
254 SecOTRPIAppendSerialization(left, leftData, NULL);
255 SecOTRPIAppendSerialization(right, rightData, NULL);
256
257 bool match = CFEqualSafe(leftData, rightData);
258
259 CFReleaseNull(leftData);
260 CFReleaseNull(rightData);
261
262 return match;
263 }
264
265 size_t SecOTRPISignatureSize(SecOTRPublicIdentityRef publicID)
266 {
267 return SecKeyGetSize(publicID->publicSigningKey, kSecKeySignatureSize);
268 }
269
270 bool SecOTRPIAppendSerialization(SecOTRPublicIdentityRef publicID, CFMutableDataRef serializeInto, CFErrorRef *error)
271 {
272 CFIndex start = CFDataGetLength(serializeInto);
273 CFMutableDataRef signingKeySerialized = CFDataCreateMutable(kCFAllocatorDefault, 0);
274
275 uint8_t sendHashes[1] = { 0xFF };
276
277 require_noerr(appendPublicOctetsAndSize(publicID->publicSigningKey, signingKeySerialized), fail);
278
279 size_t outputSize = ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE,
280 ccder_sizeof_implicit_raw_octet_string(CCDER_CONTEXT_SPECIFIC | kOTRPIDER_SigningID, (size_t)CFDataGetLength(signingKeySerialized)) +
281 (sAdvertiseHashes ? ccder_sizeof_implicit_raw_octet_string(CCDER_CONTEXT_SPECIFIC | kOTRPIDER_SupportsHashes, sizeof(sendHashes)) : 0));
282
283 CFDataIncreaseLength(serializeInto, outputSize);
284
285 uint8_t *outputBuffer = CFDataGetMutableBytePtr(serializeInto) + start;
286 uint8_t *outputBufferEnd = outputBuffer + outputSize;
287
288 uint8_t *result = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, outputBufferEnd, outputBuffer,
289 ccder_encode_implicit_raw_octet_string(CCDER_CONTEXT_SPECIFIC | kOTRPIDER_SigningID, (size_t)CFDataGetLength(signingKeySerialized), CFDataGetBytePtr(signingKeySerialized), outputBuffer,
290 sAdvertiseHashes ? ccder_encode_implicit_raw_octet_string(CCDER_CONTEXT_SPECIFIC | kOTRPIDER_SupportsHashes, sizeof(sendHashes), sendHashes, outputBuffer, outputBufferEnd) : outputBufferEnd));
291
292 require_quiet(result == outputBuffer, fail);
293
294 CFReleaseSafe(signingKeySerialized);
295
296 return true;
297
298 fail:
299 CFReleaseSafe(signingKeySerialized);
300
301 CFDataSetLength(serializeInto, start);
302
303 SecOTRCreateError(secOTRErrorLocal, kSecOTRErrorCreatePublicBytes, CFSTR("Unable to create public key bytes"), NULL, error);
304 return false;
305 }
306
307 static const uint8_t *mp_decode_forced_uint(cc_size n, cc_unit *r, const uint8_t *der, const uint8_t *der_end) {
308 size_t len;
309 der = ccder_decode_tl(CCDER_INTEGER, &len, der, der_end);
310 if (der && ccn_read_uint(n, r, len, der) >= 0)
311 return der + len;
312
313 return NULL;
314 }
315
316 static void SecOTRPIRecreateSignature(const uint8_t *oldSignature, size_t oldSignatureSize, uint8_t **newSignature, size_t *newSignatureSize)
317 {
318 cc_unit r[ccec_cp_n(ccec_cp_256())], s[ccec_cp_n(ccec_cp_256())];
319 cc_size n = ccec_cp_n(ccec_cp_256());
320
321 const uint8_t *oldSignatureEnd = oldSignature + oldSignatureSize;
322
323 oldSignature = ccder_decode_sequence_tl(&oldSignatureEnd, oldSignature, oldSignatureEnd);
324 oldSignature = mp_decode_forced_uint(n, r, oldSignature, oldSignatureEnd);
325 oldSignature = mp_decode_forced_uint(n, s, oldSignature, oldSignatureEnd);
326 (void) oldSignature; // We could check for a good end, but it'll come out when we re-encode.
327
328 const uint8_t *outputPointer = *newSignature;
329 uint8_t *outputEndPointer = *newSignature + *newSignatureSize;
330
331 *newSignature = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, outputEndPointer, outputPointer, ccder_encode_integer(n, r, outputPointer, ccder_encode_integer(n, s, outputPointer, outputEndPointer)));
332 long newSigSize = outputEndPointer - *newSignature;
333 *newSignatureSize = (newSigSize >= 0) ? (size_t)newSigSize : 0;
334 }
335
336 bool SecOTRPIVerifySignature(SecOTRPublicIdentityRef publicID,
337 const uint8_t *dataToHash, size_t amountToHash,
338 const uint8_t *signatureStart, size_t signatureSize, CFErrorRef *error)
339 {
340 require(signatureSize > 0, fail);
341 require(*signatureStart == signatureSize - 1, fail);
342 signatureSize -= 1;
343 signatureStart += 1;
344
345 require(SecKeyDigestAndVerifyWithError(publicID->publicSigningKey, kOTRSignatureAlgIDPtr,
346 dataToHash, amountToHash,
347 (uint8_t*)signatureStart, signatureSize, NULL), fail);
348 return true;
349 fail: ;
350 uint8_t *replacementSignature = malloc(signatureSize + 3);
351 require(replacementSignature != NULL, fail2);
352
353 size_t replacementSignatureLen = sizeof(replacementSignature);
354 uint8_t *replacementSignaturePtr = replacementSignature;
355
356 SecOTRPIRecreateSignature(signatureStart, signatureSize, &replacementSignaturePtr, &replacementSignatureLen);
357
358 require_action(replacementSignaturePtr, fail2, SecOTRCreateError(secOTRErrorLocal, kSecOTRErrorSignatureDidNotRecreate, CFSTR("Unable to recreate signature blob."), NULL, error));
359
360 require(SecKeyDigestAndVerifyWithError(publicID->publicSigningKey, kOTRSignatureAlgIDPtr,
361 dataToHash, amountToHash,
362 replacementSignaturePtr, replacementSignatureLen, error), fail2);
363 free(replacementSignature);
364 return true;
365
366 fail2:
367 free(replacementSignature);
368 return false;
369 }
370
371 void SecOTRPICopyHash(SecOTRPublicIdentityRef publicID, uint8_t hash[kMPIDHashSize])
372 {
373 memcpy(hash, publicID->hash, kMPIDHashSize);
374 }
375
376 void SecOTRPIAppendHash(SecOTRPublicIdentityRef publicID, CFMutableDataRef appendTo)
377 {
378 CFDataAppendBytes(appendTo, publicID->hash, sizeof(publicID->hash));
379 }
380
381 bool SecOTRPICompareHash(SecOTRPublicIdentityRef publicID, const uint8_t hash[kMPIDHashSize])
382 {
383 return 0 == memcmp(hash, publicID->hash, kMPIDHashSize);
384 }