]>
Commit | Line | Data |
---|---|---|
427c49bc A |
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 | } |