]> git.saurik.com Git - apple/security.git/blob - KeychainCircle/KCAESGCMDuplexSession.m
Security-59754.41.1.tar.gz
[apple/security.git] / KeychainCircle / KCAESGCMDuplexSession.m
1 //
2 // KCAESGCMDuplexSession.m
3 // Security
4 //
5 //
6
7 #import <KeychainCircle/KCAESGCMDuplexSession.h>
8 #import <KeychainCircle/KCDer.h>
9 #import <KeychainCircle/KCError.h>
10 #import <NSError+KCCreationHelpers.h>
11 #import <NSData+SecRandom.h>
12
13 #include <corecrypto/ccaes.h>
14 #include <corecrypto/ccmode.h>
15 #include <corecrypto/cchkdf.h>
16 #include <corecrypto/ccsha2.h>
17
18 #include <corecrypto/ccder.h>
19
20 #include <libkern/OSByteOrder.h>
21
22
23 #define kdfInfoForwardString "send->recv"
24 #define kdfInfoBackwardString "recv->send"
25 static NSData* kdfInfoSendToReceive = nil;
26 static NSData* kdfInfoReceiveToSend = nil;
27
28 static const int kKCAESGCMTagSize = CCAES_KEY_SIZE_128;
29 static const int kKCAESGCMKeySize = CCAES_KEY_SIZE_128;
30
31 static bool derive_and_init(const struct ccmode_gcm *mode, ccgcm_ctx* ctx, NSData* sharedSecret, NSData* info) {
32 const struct ccdigest_info *di = ccsha256_di();
33
34 NSMutableData* space = [NSMutableData dataWithLength:di->output_size];
35
36 int cc_status = 0;
37
38 cc_status = cchkdf(di,
39 sharedSecret.length, sharedSecret.bytes,
40 0, NULL,
41 info.length, info.bytes,
42 space.length, space.mutableBytes);
43
44 if (cc_status != 0) {
45 return false;
46 }
47 // We only use the first 16 bytes (128 bits) for the key.
48 cc_status = ccgcm_init(mode, ctx, kKCAESGCMKeySize, space.bytes);
49 cc_clear(space.length, space.mutableBytes);
50
51 return cc_status == 0;
52 }
53
54 @interface NSMutableData(KAESGCM)
55 - (void) replaceTrailingWith7LSB: (uint64_t) value;
56 @end
57
58 @implementation NSMutableData(KAESGCM)
59 - (void) replaceTrailingWith7LSB: (uint64_t) value {
60 uint8_t bytes[sizeof(value)];
61 OSWriteBigInt64(bytes, 0, value);
62
63 [self replaceBytesInRange: NSMakeRange(self.length - 7, 7) withBytes: (bytes + 1)];
64 }
65 @end
66
67
68
69 @interface KCAESGCMDuplexSession ()
70 @property (readwrite) bool asSender;
71 @property (readwrite) uint64_t context;
72 @property (readwrite) NSData* secret;
73
74 @property (readwrite) ccgcm_ctx * send;
75 @property (readwrite) ccgcm_ctx * receive;
76
77 @end
78
79 @implementation KCAESGCMDuplexSession
80
81 + (nullable instancetype) sessionAsSender: (NSData*) sharedSecret
82 context: (uint64_t) context {
83 return [[KCAESGCMDuplexSession alloc] initAsSender:sharedSecret
84 context:context];
85 }
86
87 + (nullable instancetype) sessionAsReceiver: (NSData*) sharedSecret
88 context: (uint64_t) context {
89 return [[KCAESGCMDuplexSession alloc] initAsReceiver:sharedSecret
90 context:context];
91
92 }
93
94 static NSString* KCDSSender = @"asSender";
95 static NSString* KCDSSecret = @"secret";
96 static NSString* KCDSContext = @"context";
97 static NSString* KCDSPairingUUID = @"uuid";
98 static NSString* KCDSPiggybackingVersion = @"piggy";
99 static NSString* KCDSEpoch= @"epoch";
100
101 - (void)encodeWithCoder:(NSCoder *)aCoder {
102 [aCoder encodeBool: self.asSender forKey:KCDSSender];
103 [aCoder encodeObject: self.secret forKey:KCDSSecret];
104 [aCoder encodeInt64: self.context forKey:KCDSContext];
105 [aCoder encodeObject: self.pairingUUID forKey:KCDSPairingUUID];
106 [aCoder encodeInt64: self.piggybackingVersion forKey:KCDSPiggybackingVersion];
107 [aCoder encodeInt64:self.epoch forKey:KCDSEpoch];
108 }
109
110 - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
111 bool asSender = [aDecoder decodeBoolForKey:KCDSSender];
112 NSData* secret = [aDecoder decodeObjectOfClass:[NSData class] forKey:KCDSSecret];
113 uint64_t context = [aDecoder decodeInt64ForKey:KCDSContext];
114
115 NSString* pairingUUID = [aDecoder decodeObjectOfClass:[NSString class] forKey:KCDSPairingUUID];
116 uint64_t piggybackingVersion = [aDecoder decodeInt64ForKey:KCDSPiggybackingVersion];
117 uint64_t epoch = [aDecoder decodeInt64ForKey:KCDSEpoch];
118
119 return [self initWithSecret:secret context:context as:asSender pairingUUID:pairingUUID piggybackingVersion:piggybackingVersion epoch:epoch];
120 }
121
122 + (BOOL)supportsSecureCoding {
123 return true;
124 }
125
126
127
128 - (nullable instancetype) initAsSender: (NSData*) sharedSecret context: (uint64_t) context {
129 return [self initWithSecret:sharedSecret context:context as:true];
130 }
131
132 - (nullable instancetype) initAsReceiver: (NSData*) sharedSecret context: (uint64_t) context {
133 return [self initWithSecret:sharedSecret context:context as:false];
134 }
135
136 - (nullable instancetype) initWithSecret: (NSData*) sharedSecret
137 context: (uint64_t) context
138 as: (bool) sender {
139 return [self initWithSecret:sharedSecret
140 context:context
141 as:sender
142 pairingUUID:nil
143 piggybackingVersion:0
144 epoch:1];
145 }
146
147 - (nullable instancetype)initWithSecret:(NSData*)sharedSecret
148 context:(uint64_t)context
149 as:(bool) sender
150 pairingUUID:(NSString* _Nullable)pairingUUID
151 piggybackingVersion:(uint64_t)piggybackingVersion
152 epoch:(uint64_t)epoch
153 {
154 static dispatch_once_t onceToken;
155 dispatch_once(&onceToken, ^{
156 kdfInfoSendToReceive = [NSData dataWithBytesNoCopy: kdfInfoForwardString
157 length: strlen(kdfInfoForwardString)
158 freeWhenDone: false];
159
160 kdfInfoReceiveToSend = [NSData dataWithBytesNoCopy: kdfInfoBackwardString
161 length: strlen(kdfInfoBackwardString)
162 freeWhenDone: false];
163 });
164
165 if ((self = [super init])) {
166 self.asSender = sender;
167 self.secret = sharedSecret;
168 self.send = malloc(ccgcm_context_size(ccaes_gcm_encrypt_mode()));
169 self.receive = malloc(ccgcm_context_size(ccaes_gcm_decrypt_mode()));
170 self.context = context;
171
172 _pairingUUID = pairingUUID;
173 _piggybackingVersion = piggybackingVersion;
174 _epoch = epoch;
175
176 if (self.send == nil || self.receive == nil) {
177 return nil;
178 }
179
180 derive_and_init(ccaes_gcm_encrypt_mode(),
181 self.send, self.secret,
182 sender ? kdfInfoSendToReceive : kdfInfoReceiveToSend);
183 derive_and_init(ccaes_gcm_decrypt_mode(),
184 self.receive, self.secret,
185 !sender ? kdfInfoSendToReceive : kdfInfoReceiveToSend);
186 }
187 return self;
188 }
189
190 - (size_t) encryptCapsuleSize: (NSData*) plaintext IV: (NSData*) iv {
191 size_t iv_size = kcder_sizeof_data(iv, nil);
192 if (iv_size == 0) {
193 return 0;
194 }
195 size_t text_size = kcder_sizeof_data(plaintext, nil);
196 if (text_size == 0) {
197 return 0;
198 }
199 size_t tag_size = kcder_sizeof_data([NSMutableData dataWithLength: kKCAESGCMTagSize], nil);
200 if (tag_size == 0) {
201 return 0;
202 }
203 return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, iv_size + text_size + tag_size);
204 }
205
206 - (bool) GCM:(const struct ccmode_gcm*) mode
207 context:(ccgcm_ctx*) ctx
208 iv:(NSData*) iv
209 size:(size_t) data_size
210 data:(const uint8_t*) data
211 processed:(uint8_t*) result
212 tag:(uint8_t*) tagBuffer
213 error:(NSError**) error {
214 int cc_status;
215
216 cc_status = ccgcm_reset(mode, ctx);
217 if (!CoreCryptoError(cc_status, error, @"ccgcm_reset failed: %d", cc_status))
218 return NO;
219
220 cc_status = ccgcm_set_iv(mode, ctx, iv.length, iv.bytes);
221 if (!CoreCryptoError(cc_status, error, @"ccgcm_set_iv failed: %d", cc_status))
222 return NO;
223
224 cc_status = ccgcm_update(mode, ctx, data_size, data, result);
225 if (!CoreCryptoError(cc_status, error, @"ccgcm_update failed: %d", cc_status))
226 return NO;
227
228 cc_status = ccgcm_finalize(mode, ctx, kKCAESGCMTagSize, tagBuffer);
229 return CoreCryptoError(cc_status, error, @"ccgcm_finalize failed: %d", cc_status);
230 }
231
232
233 - (nullable NSData*) encrypt: (NSData*) data error: (NSError**) error {
234 static const int kIVSizeInBytes = 16;
235
236 NSMutableData* iv = [NSMutableData dataWithRandomBytes: kIVSizeInBytes];
237
238 NSMutableData* result = [NSMutableData dataWithLength: [self encryptCapsuleSize: data IV: iv]];
239
240 // Encode with all the space set up for the result:
241
242 uint8_t* der_end = result.mutableBytes + result.length;
243 const uint8_t* der = result.bytes;
244
245 uint8_t* tag = NULL;
246 uint8_t* encrypted = NULL;
247
248 der_end = ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
249 kcder_encode_data(iv, error, der,
250 kcder_encode_raw_octet_space(data.length, &encrypted, der,
251 kcder_encode_raw_octet_space(kKCAESGCMTagSize, &tag, der, der_end))));
252
253 if (der_end != der) {
254 KCJoiningErrorCreate(kAllocationFailure, error, @"Failed to allocate space for der");
255 return nil;
256 }
257
258 const struct ccmode_gcm * mode = ccaes_gcm_encrypt_mode();
259
260 return [self GCM:mode
261 context:self.send
262 iv:iv
263 size:data.length
264 data:data.bytes
265 processed:encrypted
266 tag:tag
267 error:error] ? result : nil;
268 }
269
270 - (nullable NSData*) decryptAndVerify: (NSData*) data error: (NSError**) error {
271
272 const uint8_t *der = data.bytes;
273 const uint8_t *der_end = der + data.length;
274
275 const uint8_t *sequence_end = 0;
276 der = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, der, der_end);
277
278 if (der == NULL || sequence_end != der_end) {
279 KCJoiningErrorCreate(kDERUnknownEncoding, error, @"decode failed");
280 return nil;
281 }
282
283 const uint8_t *encrypted = 0;
284 size_t encrypted_len = 0;
285 const uint8_t *received_tag = 0;
286
287 NSData* iv;
288
289 der = kcder_decode_data(&iv, error, der, der_end);
290 if (der == NULL) return nil;
291
292 encrypted = ccder_decode_constructed_tl(CCDER_OCTET_STRING, &der, der, der_end);
293 encrypted_len = der - encrypted;
294
295 received_tag = ccder_decode_constructed_tl(CCDER_OCTET_STRING, &der, der, der_end);
296
297 if (der == NULL) {
298 KCJoiningErrorCreate(kDERUnknownEncoding, error, @"Decode failure");
299 return nil;
300 }
301
302 if (der != der_end) {
303 KCJoiningErrorCreate(kDERUnknownEncoding, error, @"Extra space");
304 return nil;
305 }
306
307 if (der - received_tag != kKCAESGCMTagSize) {
308 KCJoiningErrorCreate(kDERUnknownEncoding, error, @"Unexpected tag size: %ld", (long)(der - received_tag));
309 return nil;
310 }
311
312 NSMutableData* decrypted = [NSMutableData dataWithLength: encrypted_len];
313
314 uint8_t tag[kKCAESGCMTagSize];
315 memcpy(tag, received_tag, sizeof(tag));
316
317 const struct ccmode_gcm * mode = ccaes_gcm_decrypt_mode();
318
319 return [self GCM:mode
320 context:self.receive
321 iv:iv
322 size:encrypted_len
323 data:encrypted
324 processed:decrypted.mutableBytes
325 tag:tag
326 error:error] ? decrypted : nil;
327 }
328
329 #pragma clang diagnostic push
330 #pragma clang diagnostic ignored "-Wdeprecated-implementations"
331 - (void) finalize {
332 if (self.send) {
333 ccgcm_ctx_clear(sizeof(*self.send), self.send);
334 free(self.send);
335 }
336 if (self.receive) {
337 ccgcm_ctx_clear(sizeof(*self.receive), self.receive);
338 free(self.receive);
339 }
340 [super finalize];
341 }
342 #pragma clang diagnostic pop
343
344 @end