1//
2//  SecOTRUtils.c
3//  libSecOTR
4//
5//  Created by Keith Henrickson on 6/11/12.
6//
7//
8
9#include "SecOTR.h"
10#include "SecOTRIdentityPriv.h"
11#include "SecOTRSessionPriv.h"
12#include <utilities/SecCFWrappers.h>
13#include <stdlib.h>
14
15#include <AssertMacros.h>
16
17#include <Security/SecBase.h>
18#include <Security/SecItem.h>
19#include <Security/SecKey.h>
20#include <Security/SecKeyPriv.h>
21#include <Security/SecBase64.h>
22
23#include <TargetConditionals.h>
24
25CFStringRef sLocalErrorDomain = CFSTR("com.apple.security.otr.error");
26
27void SecOTRCreateError(enum SecOTRError family, CFIndex errorCode, CFStringRef descriptionString, CFErrorRef previousError, CFErrorRef *newError) {
28    if (newError && !(*newError)) {
29        const void* keys[2] = {kCFErrorDescriptionKey, kCFErrorUnderlyingErrorKey};
30        const void* values[2] = {descriptionString, previousError};
31        *newError = CFErrorCreateWithUserInfoKeysAndValues(kCFAllocatorDefault, (family == secOTRErrorLocal) ? sLocalErrorDomain : kCFErrorDomainOSStatus, errorCode, keys, values, (previousError != NULL) ? 2 : 1);
32    } else {
33        CFReleaseSafe(previousError);
34    }
35}
36
37OSStatus insertSize(CFIndex size, uint8_t* here)
38{
39    require(size < 0xFFFF, fail);
40
41    uint8_t bytes[] = { (size >> 8) & 0xFF, size & 0xFF };
42    memcpy(here, bytes, sizeof(bytes));
43
44    return errSecSuccess;
45
46fail:
47    return errSecParam;
48}
49
50OSStatus appendSize(CFIndex size, CFMutableDataRef into)
51{
52    require(size < 0xFFFF, fail);
53
54    uint8_t bytes[] = { (size >> 8) & 0xFF, size & 0xFF };
55    CFDataAppendBytes(into, bytes, sizeof(bytes));
56
57    return errSecSuccess;
58
59fail:
60    return errSecParam;
61}
62
63OSStatus readSize(const uint8_t** data, size_t* limit, uint16_t* size)
64{
65    require(limit != NULL, fail);
66    require(data != NULL, fail);
67    require(size != NULL, fail);
68    require(*limit > 1, fail);
69
70    *size = ((uint16_t)(*data)[0]) << 8 | ((uint16_t) (*data)[1]) << 0;
71
72    *limit -= 2;
73    *data += 2;
74
75    return errSecSuccess;
76fail:
77    return errSecParam;
78}
79
80OSStatus appendSizeAndData(CFDataRef data, CFMutableDataRef appendTo)
81{
82    OSStatus status = errSecNotAvailable;
83
84    require_noerr(appendSize(CFDataGetLength(data), appendTo), exit);
85    CFDataAppend(appendTo, data);
86
87    status = errSecSuccess;
88
89exit:
90    return status;
91}
92
93OSStatus appendPublicOctetsAndSize(SecKeyRef fromKey, CFMutableDataRef appendTo)
94{
95    OSStatus status = errSecDecode;
96    CFDataRef serializedKey = NULL;
97
98    require_noerr(SecKeyCopyPublicBytes(fromKey, &serializedKey), exit);
99    require(serializedKey, exit);
100
101    status = appendSizeAndData(serializedKey, appendTo);
102
103exit:
104    CFReleaseNull(serializedKey);
105    return status;
106}
107
108OSStatus appendPublicOctets(SecKeyRef fromKey, CFMutableDataRef appendTo)
109{
110    OSStatus status = errSecDecode;
111    CFDataRef serializedKey = NULL;
112
113    require_noerr(SecKeyCopyPublicBytes(fromKey, &serializedKey), exit);
114    require(serializedKey, exit);
115
116    CFDataAppend(appendTo, serializedKey);
117
118    status = errSecSuccess;
119
120exit:
121    CFReleaseNull(serializedKey);
122    return status;
123}
124
125
126/* Given an EC public key in encoded form return a SecKeyRef representing
127 that key. Supported encodings are kSecKeyEncodingPkcs1. */
128static SecKeyRef SecKeyCreateECPublicKey(CFAllocatorRef allocator,
129                                  const uint8_t *keyData, CFIndex keyDataLength) {
130    CFDataRef tempData = CFDataCreate(kCFAllocatorDefault, keyData, keyDataLength);
131    SecKeyRef newPublicKey = SecKeyCreateFromPublicData(kCFAllocatorDefault, kSecECDSAAlgorithmID, tempData);
132
133    CFRelease(tempData);
134    return newPublicKey;
135}
136
137typedef SecKeyRef (*createFunction_t)(CFAllocatorRef allocator,
138                                      const uint8_t *keyData, CFIndex keyDataLength);
139
140static SecKeyRef CallCreateFunctionFrom(CFAllocatorRef allocator, const uint8_t** data, size_t* limit, createFunction_t create)
141{
142    uint16_t foundLength = 0;
143    const uint8_t* foundData = NULL;
144
145    require(limit != NULL, fail);
146    require(data != NULL, fail);
147
148    require_noerr(readSize(data, limit, &foundLength), fail);
149    require(foundLength <= *limit, fail);
150
151    foundData = *data;
152
153    *limit -= foundLength;
154    *data += foundLength;
155
156fail:
157
158    return create(allocator, foundData, foundLength);
159}
160
161SecKeyRef CreateECPublicKeyFrom(CFAllocatorRef allocator, const uint8_t** data, size_t* limit)
162{
163    return CallCreateFunctionFrom(allocator, data, limit, &SecKeyCreateECPublicKey);
164}
165
166void SecOTRGetIncomingBytes(CFDataRef incomingMessage, CFMutableDataRef decodedBytes)
167{
168    CFDataRef header = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("?OTR:"), kCFStringEncodingUTF8, '?');
169    CFDataRef footer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("."), kCFStringEncodingUTF8, '?');
170    CFRange headerLoc = CFDataFind(incomingMessage, header, CFRangeMake(0, CFDataGetLength(header)), 0);
171    CFRange footerLoc = CFDataFind(incomingMessage, footer, CFRangeMake(0, CFDataGetLength(incomingMessage)), 0);
172    if (kCFNotFound == headerLoc.location) {
173        CFDataAppend(decodedBytes, incomingMessage);
174    } else {
175        CFDataRef bodyData = CFDataCreateReferenceFromRange(kCFAllocatorDefault, incomingMessage, CFRangeMake(headerLoc.length, footerLoc.location - headerLoc.length));
176        size_t size = SecBase64Decode((char*)CFDataGetBytePtr(bodyData), CFDataGetLength(bodyData), NULL, 0);
177        uint8_t decodedByteArray[size];
178        SecBase64Decode((char*)CFDataGetBytePtr(bodyData), CFDataGetLength(bodyData), decodedByteArray, size);
179        CFDataAppendBytes(decodedBytes, decodedByteArray, size);
180        CFRelease(bodyData);
181    }
182    CFRelease(header);
183    CFRelease(footer);
184}
185
186void SecOTRPrepareOutgoingBytes(CFMutableDataRef destinationMessage, CFMutableDataRef protectedMessage)
187{
188    CFDataRef header = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("?OTR:"), kCFStringEncodingUTF8, '?');
189    CFDataRef footer = CFStringCreateExternalRepresentation(kCFAllocatorDefault, CFSTR("."), kCFStringEncodingUTF8, '?');
190    size_t base64Len = SecBase64Encode(CFDataGetBytePtr(destinationMessage), CFDataGetLength(destinationMessage), NULL, 0);
191    char base64Message [base64Len];
192    SecBase64Encode(CFDataGetBytePtr(destinationMessage), CFDataGetLength(destinationMessage), base64Message, base64Len);
193    CFDataRef base64Data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (uint8_t*)base64Message, base64Len, kCFAllocatorNull);
194
195    CFDataAppend(protectedMessage, header);
196    CFDataAppend(protectedMessage, base64Data);
197    CFDataAppend(protectedMessage, footer);
198
199    CFRelease(header);
200    CFRelease(footer);
201    CFRelease(base64Data);
202}
203
204
205