2 * Copyright (c) 2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #include "SecOTRIdentityPriv.h"
27 #include <utilities/SecCFWrappers.h>
29 #include <AssertMacros.h>
31 #include <CoreFoundation/CFNumber.h>
32 #include <CoreFoundation/CFString.h>
33 #include <CoreFoundation/CFData.h>
35 #include <Security/SecKey.h>
36 #include <Security/SecKeyPriv.h>
38 #include <corecrypto/ccn.h>
39 #include <corecrypto/ccec.h>
40 #include <corecrypto/ccder.h>
42 #import <sys/syslog.h>
44 #include "SecOTRErrors.h"
45 #include <TargetConditionals.h>
47 #include <CommonCrypto/CommonDigest.h>
48 #include <CommonCrypto/CommonDigestSPI.h>
51 * Support for encoding and decoding DH parameter blocks.
52 * Apple form encodes the reciprocal of the prime p.
56 kOTRPIDER_SigningID
= 1,
57 kOTRPIDER_SupportsHashes
=3,
61 // SecOTRPublicIdentity implementation
64 CFGiblisFor(SecOTRPublicIdentity
);
66 static bool sAdvertiseHashes
= false;
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>"),
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]);
78 static void SecOTRPublicIdentityDestroy(CFTypeRef cf
) {
79 SecOTRPublicIdentityRef requestor
= (SecOTRPublicIdentityRef
)cf
;
81 CFReleaseNull(requestor
->publicSigningKey
);
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 */
93 OSStatus status
= SecKeyDigestAndVerify(key
, algId
, dataToDigest
, dataToDigestLen
, sig
, sigLen
);
94 require_noerr(status
, fail
);
97 SecOTRCreateError(secOTRErrorOSError
, status
, CFSTR("Error verifying message. OSStatus in error code."), NULL
, error
);
101 static bool SecOTRPICacheHash(SecOTRPublicIdentityRef pubID
, CFErrorRef
*error
)
105 CFMutableDataRef stream
= CFDataCreateMutable(NULL
, 0);
107 require(SecOTRPIAppendSerialization(pubID
, stream
, error
), fail
);
109 CCDigest(kCCDigestSHA1
, CFDataGetBytePtr(stream
), (CC_LONG
)CFDataGetLength(stream
), pubID
->hash
);
114 CFReleaseSafe(stream
);
118 void SecOTRAdvertiseHashes(bool advertise
) {
119 sAdvertiseHashes
= advertise
;
122 bool SecOTRPICompareToPublicKey(SecOTRPublicIdentityRef pubID
, SecKeyRef publicKey
) {
123 return CFEqualSafe(pubID
->publicSigningKey
, publicKey
);
126 SecOTRPublicIdentityRef
SecOTRPublicIdentityCopyFromPrivate(CFAllocatorRef allocator
, SecOTRFullIdentityRef fullID
, CFErrorRef
*error
)
128 SecOTRPublicIdentityRef result
= CFTypeAllocate(SecOTRPublicIdentity
, struct _SecOTRPublicIdentity
, allocator
);
130 EnsureOTRAlgIDInited();
132 result
->publicSigningKey
= fullID
->publicSigningKey
;
133 CFRetain(result
->publicSigningKey
);
135 require(SecOTRPICacheHash(result
, error
), fail
);
140 CFReleaseSafe(result
);
145 SecOTRPublicIdentityRef
SecOTRPublicIdentityCreateFromSecKeyRef(CFAllocatorRef allocator
, SecKeyRef publicKey
,
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
);
154 CFRelease(result
->publicSigningKey
);
155 CFReleaseSafe(result
);
159 typedef SecKeyRef (*SecOTRPublicKeyCreateFunction
)(CFAllocatorRef allocator
, const uint8_t** data
, size_t* limit
);
161 static SecKeyRef
SecOTRCreatePublicKeyFrom(const uint8_t* keyData
, size_t keyDataSize
, ccder_tag tagContainingKey
, SecOTRPublicKeyCreateFunction createFunction
)
163 SecKeyRef createdKey
= NULL
;
165 createdKey
= createFunction(kCFAllocatorDefault
, &keyData
, &keyDataSize
);
167 require(createdKey
!= NULL
, fail
);
168 require(keyDataSize
== 0, fail
);
173 CFReleaseSafe(createdKey
);
178 SecOTRPublicIdentityRef
SecOTRPublicIdentityCreateFromBytes(CFAllocatorRef allocator
, const uint8_t**bytes
, size_t* size
, CFErrorRef
*error
)
180 CFErrorRef stackedError
= NULL
;
182 SecOTRPublicIdentityRef newID
= CFTypeAllocate(SecOTRPublicIdentity
, struct _SecOTRPublicIdentity
, allocator
);
184 EnsureOTRAlgIDInited();
186 const uint8_t* fullSequenceEnd
= *bytes
+ *size
;
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
);
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
;
198 newID
->wantsHashes
= (NULL
!= ccder_decode_tl(CCDER_CONTEXT_SPECIFIC
| kOTRPIDER_SupportsHashes
, &keyDataSize
, keyData
, fullSequenceEnd
));
200 require(SecOTRPICacheHash(newID
, &stackedError
), fail
);
208 SecOTRCreateError(secOTRErrorLocal
, kSecOTRErrorCreatePublicIdentity
, CFSTR("Error creating public identity from bytes"), stackedError
, error
);
209 CFReleaseSafe(newID
);
213 SecOTRPublicIdentityRef
SecOTRPublicIdentityCreateFromData(CFAllocatorRef allocator
, CFDataRef serializedData
, CFErrorRef
*error
)
215 if (serializedData
== NULL
)
218 size_t length
= (size_t)CFDataGetLength(serializedData
);
219 const uint8_t* bytes
= CFDataGetBytePtr(serializedData
);
220 return SecOTRPublicIdentityCreateFromBytes(allocator
, &bytes
, &length
, error
);
223 bool SecOTRPIEqualToBytes(SecOTRPublicIdentityRef id
, const uint8_t*bytes
, CFIndex size
)
225 CFDataRef dataToMatch
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, bytes
, size
, kCFAllocatorNull
);
226 CFMutableDataRef idStreamed
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
228 SecOTRPIAppendSerialization(id
, idStreamed
, NULL
);
230 bool equal
= CFEqualSafe(dataToMatch
, idStreamed
);
233 CFDataPerformWithHexString(dataToMatch
, ^(CFStringRef dataToMatchString
) {
234 CFDataPerformWithHexString(idStreamed
, ^(CFStringRef idStreamedString
) {
235 secnotice("otr", "ID Comparison failed: d: %@ id: %@", dataToMatchString
, idStreamedString
);
240 CFReleaseNull(dataToMatch
);
241 CFReleaseNull(idStreamed
);
246 bool SecOTRPIEqual(SecOTRPublicIdentityRef left
, SecOTRPublicIdentityRef right
)
251 CFMutableDataRef leftData
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
252 CFMutableDataRef rightData
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
254 SecOTRPIAppendSerialization(left
, leftData
, NULL
);
255 SecOTRPIAppendSerialization(right
, rightData
, NULL
);
257 bool match
= CFEqualSafe(leftData
, rightData
);
259 CFReleaseNull(leftData
);
260 CFReleaseNull(rightData
);
265 size_t SecOTRPISignatureSize(SecOTRPublicIdentityRef publicID
)
267 return SecKeyGetSize(publicID
->publicSigningKey
, kSecKeySignatureSize
);
270 bool SecOTRPIAppendSerialization(SecOTRPublicIdentityRef publicID
, CFMutableDataRef serializeInto
, CFErrorRef
*error
)
272 CFIndex start
= CFDataGetLength(serializeInto
);
273 CFMutableDataRef signingKeySerialized
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
275 uint8_t sendHashes
[1] = { 0xFF };
277 require_noerr(appendPublicOctetsAndSize(publicID
->publicSigningKey
, signingKeySerialized
), fail
);
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));
283 CFDataIncreaseLength(serializeInto
, outputSize
);
285 uint8_t *outputBuffer
= CFDataGetMutableBytePtr(serializeInto
) + start
;
286 uint8_t *outputBufferEnd
= outputBuffer
+ outputSize
;
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
));
292 require_quiet(result
== outputBuffer
, fail
);
294 CFReleaseSafe(signingKeySerialized
);
299 CFReleaseSafe(signingKeySerialized
);
301 CFDataSetLength(serializeInto
, start
);
303 SecOTRCreateError(secOTRErrorLocal
, kSecOTRErrorCreatePublicBytes
, CFSTR("Unable to create public key bytes"), NULL
, error
);
307 static const uint8_t *mp_decode_forced_uint(cc_size n
, cc_unit
*r
, const uint8_t *der
, const uint8_t *der_end
) {
309 der
= ccder_decode_tl(CCDER_INTEGER
, &len
, der
, der_end
);
310 if (der
&& ccn_read_uint(n
, r
, len
, der
) >= 0)
316 static void SecOTRPIRecreateSignature(const uint8_t *oldSignature
, size_t oldSignatureSize
, uint8_t **newSignature
, size_t *newSignatureSize
)
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());
321 const uint8_t *oldSignatureEnd
= oldSignature
+ oldSignatureSize
;
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.
328 const uint8_t *outputPointer
= *newSignature
;
329 uint8_t *outputEndPointer
= *newSignature
+ *newSignatureSize
;
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;
336 bool SecOTRPIVerifySignature(SecOTRPublicIdentityRef publicID
,
337 const uint8_t *dataToHash
, size_t amountToHash
,
338 const uint8_t *signatureStart
, size_t signatureSize
, CFErrorRef
*error
)
340 require(signatureSize
> 0, fail
);
341 require(*signatureStart
== signatureSize
- 1, fail
);
345 require(SecKeyDigestAndVerifyWithError(publicID
->publicSigningKey
, kOTRSignatureAlgIDPtr
,
346 dataToHash
, amountToHash
,
347 (uint8_t*)signatureStart
, signatureSize
, NULL
), fail
);
350 uint8_t *replacementSignature
= malloc(signatureSize
+ 3);
351 require(replacementSignature
!= NULL
, fail2
);
353 size_t replacementSignatureLen
= sizeof(replacementSignature
);
354 uint8_t *replacementSignaturePtr
= replacementSignature
;
356 SecOTRPIRecreateSignature(signatureStart
, signatureSize
, &replacementSignaturePtr
, &replacementSignatureLen
);
358 require_action(replacementSignaturePtr
, fail2
, SecOTRCreateError(secOTRErrorLocal
, kSecOTRErrorSignatureDidNotRecreate
, CFSTR("Unable to recreate signature blob."), NULL
, error
));
360 require(SecKeyDigestAndVerifyWithError(publicID
->publicSigningKey
, kOTRSignatureAlgIDPtr
,
361 dataToHash
, amountToHash
,
362 replacementSignaturePtr
, replacementSignatureLen
, error
), fail2
);
363 free(replacementSignature
);
367 free(replacementSignature
);
371 void SecOTRPICopyHash(SecOTRPublicIdentityRef publicID
, uint8_t hash
[kMPIDHashSize
])
373 memcpy(hash
, publicID
->hash
, kMPIDHashSize
);
376 void SecOTRPIAppendHash(SecOTRPublicIdentityRef publicID
, CFMutableDataRef appendTo
)
378 CFDataAppendBytes(appendTo
, publicID
->hash
, sizeof(publicID
->hash
));
381 bool SecOTRPICompareHash(SecOTRPublicIdentityRef publicID
, const uint8_t hash
[kMPIDHashSize
])
383 return 0 == memcmp(hash
, publicID
->hash
, kMPIDHashSize
);