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