]> git.saurik.com Git - apple/security.git/blame - sec/Security/SecOTRPublicIdentity.c
Security-55471.14.4.tar.gz
[apple/security.git] / sec / Security / SecOTRPublicIdentity.c
CommitLineData
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
40enum {
41 kOTRPIDER_SigningID = 1,
42 kOTRPIDER_SupportsHashes =3,
43};
44
45//
46// SecOTRPublicIdentity implementation
47//
48
49CFGiblisFor(SecOTRPublicIdentity);
50
51static bool sAdvertiseHashes = false;
52
53static 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
63static void SecOTRPublicIdentityDestroy(CFTypeRef cf) {
64 SecOTRPublicIdentityRef requestor = (SecOTRPublicIdentityRef)cf;
65
66 CFReleaseNull(requestor->publicSigningKey);
67}
68
69static 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;
81fail:
82 SecOTRCreateError(secOTRErrorOSError, status, CFSTR("Error verifying message. OSStatus in error code."), NULL, error);
83 return false;
84}
85
86static 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
98fail:
99 CFReleaseSafe(stream);
100 return result;
101}
102
103void SecOTRAdvertiseHashes(bool advertise) {
104 sAdvertiseHashes = advertise;
105}
106
107SecOTRPublicIdentityRef 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
120fail:
121 CFReleaseSafe(result);
122 return NULL;
123}
124
125
126SecOTRPublicIdentityRef 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;
134fail:
135 CFRelease(result->publicSigningKey);
136 CFReleaseSafe(result);
137 return NULL;
138}
139
140typedef SecKeyRef (*SecOTRPublicKeyCreateFunction)(CFAllocatorRef allocator, const uint8_t** data, size_t* limit);
141
142static 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
153fail:
154 CFReleaseSafe(createdKey);
155 return NULL;
156}
157
158
159SecOTRPublicIdentityRef 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
187fail:
188 SecOTRCreateError(secOTRErrorLocal, kSecOTRErrorCreatePublicIdentity, CFSTR("Error creating public identity from bytes"), stackedError, error);
189 CFReleaseSafe(newID);
190 return NULL;
191}
192
193SecOTRPublicIdentityRef 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
203bool 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
218bool 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
237size_t SecOTRPISignatureSize(SecOTRPublicIdentityRef publicID)
238{
239 return SecKeyGetSize(publicID->publicSigningKey, kSecKeySignatureSize);
240}
241
242bool 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
268fail:
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
277static 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
286static 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
305bool 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;
318fail:
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
335fail2:
336 return false;
337}
338
339void SecOTRPICopyHash(SecOTRPublicIdentityRef publicID, uint8_t hash[kMPIDHashSize])
340{
341 memcpy(hash, publicID->hash, kMPIDHashSize);
342}
343
344void SecOTRPIAppendHash(SecOTRPublicIdentityRef publicID, CFMutableDataRef appendTo)
345{
346 CFDataAppendBytes(appendTo, publicID->hash, sizeof(publicID->hash));
347}
348
349bool SecOTRPICompareHash(SecOTRPublicIdentityRef publicID, const uint8_t hash[kMPIDHashSize])
350{
351 return 0 == memcmp(hash, publicID->hash, kMPIDHashSize);
352}