]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/CKKSSIV.m
Security-59306.80.4.tar.gz
[apple/security.git] / keychain / ckks / CKKSSIV.m
1 /*
2 * Copyright (c) 2017 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #if OCTAGON
25
26 #define __STDC_WANT_LIB_EXT1__ 1
27 #include <string.h>
28
29 #import <AssertMacros.h>
30 #import <Foundation/Foundation.h>
31 #import <Foundation/NSData_Private.h>
32
33 #import "CKKSSIV.h"
34
35 #include <CommonCrypto/CommonCrypto.h>
36 #include <CommonCrypto/CommonRandom.h>
37 #include <corecrypto/ccaes.h>
38 #include <corecrypto/ccmode_siv.h>
39
40 #import "keychain/categories/NSError+UsefulConstructors.h"
41
42 @implementation CKKSBaseAESSIVKey
43 - (instancetype)init {
44 if(self = [super init]) {
45 self->size = CKKSWrappedKeySize;
46 [self zeroKey];
47 }
48 return self;
49 }
50 - (instancetype)initWithBytes:(uint8_t *)bytes len:(size_t)len {
51 if(self = [super init]) {
52 if(len <= CKKSWrappedKeySize) {
53 self->size = len;
54 memcpy(self->key, bytes, self->size);
55 }
56 }
57 return self;
58 }
59 - (instancetype)initWithBase64:(NSString*)base64bytes {
60 if(self = [super init]) {
61 NSData* data = [[NSData alloc] initWithBase64EncodedString: base64bytes options:0];
62
63 if(!data) {
64 return nil;
65 }
66
67 if(data.length <= CKKSWrappedKeySize) {
68 self->size = data.length;
69 memcpy(self->key, data.bytes, self->size);
70 }
71 }
72 return self;
73 }
74
75 - (BOOL)isEqual: (id) object {
76 if(![object isKindOfClass:[CKKSBaseAESSIVKey class]]) {
77 return NO;
78 }
79
80 CKKSBaseAESSIVKey* obj = (CKKSBaseAESSIVKey*) object;
81 if (self->size == obj->size && 0 == memcmp(self->key, obj->key, self->size)) {
82 return YES;
83 } else {
84 return NO;
85 }
86 }
87
88 - (void)dealloc {
89 [self zeroKey];
90 }
91
92 - (void)zeroKey {
93 memset_s(self->key, self->size, 0x00, CKKSWrappedKeySize);
94 }
95
96 - (instancetype)copyWithZone:(NSZone *)zone {
97 return [[[self class] allocWithZone:zone] initWithBytes:self->key len:self->size];
98 }
99
100 @end
101
102 @implementation CKKSWrappedAESSIVKey
103 - (instancetype)init {
104 if(self = [super init]) {
105 self->size = CKKSWrappedKeySize;
106 }
107 return self;
108 }
109 - (instancetype)initWithBytes:(uint8_t *)bytes len:(size_t)len {
110 if(len != CKKSWrappedKeySize) {
111 @throw [NSException
112 exceptionWithName:@"WrongKeySizeException"
113 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long)len, CKKSWrappedKeySize]
114 userInfo:nil];
115 }
116 if(self = [super initWithBytes: bytes len: len]) {
117 }
118 return self;
119 }
120 - (instancetype)initWithBase64:(NSString*)base64bytes {
121 if(self = [super initWithBase64: base64bytes]) {
122 if(self->size != CKKSWrappedKeySize) {
123 @throw [NSException
124 exceptionWithName:@"WrongKeySizeException"
125 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long)self->size, CKKSWrappedKeySize]
126 userInfo:nil];
127 }
128 }
129 return self;
130 }
131 - (instancetype)initWithData: (NSData*) data {
132 if(data.length != CKKSWrappedKeySize) {
133 @throw [NSException
134 exceptionWithName:@"WrongKeySizeException"
135 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long) data.length, CKKSWrappedKeySize]
136 userInfo:nil];
137 }
138 if(self = [super initWithBytes: (uint8_t*) data.bytes len: data.length]) {
139 }
140 return self;
141 }
142 - (NSData*) wrappedData {
143 return [[NSData alloc] initWithBytes:self->key length:self->size];
144 }
145 - (NSString*) base64WrappedKey {
146 return [[self wrappedData] base64EncodedStringWithOptions:0];
147 }
148
149 + (BOOL)supportsSecureCoding {
150 return YES;
151 }
152
153 - (void)encodeWithCoder:(nonnull NSCoder *)coder {
154 [coder encodeBytes:self->key length:self->size forKey:@"wrappedkey"];
155 }
156
157 - (nullable instancetype)initWithCoder:(nonnull NSCoder *)decoder {
158 self = [super init];
159 if (self) {
160 NSUInteger len = 0;
161 const uint8_t * bytes = [decoder decodeBytesForKey:@"wrappedkey" returnedLength:&len];
162
163 if(bytes) {
164 memcpy(self->key, bytes, (size_t) len <= CKKSWrappedKeySize ? len : CKKSWrappedKeySize);
165 }
166 }
167 return self;
168 }
169
170 @end
171
172 @implementation CKKSAESSIVKey
173 - (instancetype)init {
174 if(self = [super init]) {
175 self->size = CKKSKeySize;
176 }
177 return self;
178 }
179 - (instancetype)initWithBytes:(uint8_t *)bytes len:(size_t)len {
180 if(len != CKKSKeySize) {
181 @throw [NSException
182 exceptionWithName:@"WrongKeySizeException"
183 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long)len, CKKSKeySize]
184 userInfo:nil];
185 }
186 if(self = [super initWithBytes: bytes len: len]) {
187 }
188 return self;
189 }
190 - (instancetype)initWithBase64:(NSString*)base64bytes {
191 if(self = [super initWithBase64: base64bytes]) {
192 if(self->size != CKKSKeySize) {
193 @throw [NSException
194 exceptionWithName:@"WrongKeySizeException"
195 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long)self->size, CKKSKeySize]
196 userInfo:nil];
197 }
198 }
199 return self;
200 }
201
202 + (instancetype _Nullable)randomKey:(NSError* __autoreleasing *)error
203 {
204 CKKSAESSIVKey* key = [[CKKSAESSIVKey alloc] init];
205
206 CCRNGStatus status = CCRandomGenerateBytes(key->key, key->size);
207 if(status != kCCSuccess) {
208 if(error) {
209 *error = [NSError errorWithDomain:@"corecrypto"
210 code:status
211 description:[NSString stringWithFormat: @"CCRandomGenerateBytes failed with %d", status]];
212 }
213 return nil;
214 }
215
216 return key;
217 }
218
219 - (CKKSWrappedAESSIVKey*)wrapAESKey: (CKKSAESSIVKey*) keyToWrap error: (NSError * __autoreleasing *) error {
220 NSError* localerror = nil;
221 bool success = false;
222
223 if(!keyToWrap) {
224 localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
225 code:errSecParam
226 description:@"No key given"];
227 if(error) {
228 *error = localerror;
229 }
230 return nil;
231 }
232
233 CKKSWrappedAESSIVKey* wrappedKey = nil;
234 uint8_t buffer[CKKSWrappedKeySize] = {};
235
236 size_t ciphertextLength = ccsiv_ciphertext_size(ccaes_siv_encrypt_mode(), CKKSKeySize);
237 require_action_quiet(ciphertextLength == CKKSWrappedKeySize, out, localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
238 code:errSecParam
239 userInfo:@{NSLocalizedDescriptionKey: @"wrapped key size does not match key size"}]);
240
241 success = [self doSIV: ccaes_siv_encrypt_mode()
242 nonce: nil
243 text: [NSData _newZeroingDataWithBytes:(void*) (keyToWrap->key) length:keyToWrap->size]
244 buffer: buffer
245 bufferLength: sizeof(buffer)
246 authenticatedData: nil
247 error: error];
248 require_quiet(success, out);
249
250 wrappedKey = [[CKKSWrappedAESSIVKey alloc] initWithBytes:buffer len:sizeof(buffer)];
251 out:
252 memset_s(buffer, sizeof(buffer), 0x00, CKKSKeySize);
253 if(error && localerror != nil) {
254 *error = localerror;
255 }
256 return wrappedKey;
257 }
258
259 - (CKKSAESSIVKey*)unwrapAESKey: (CKKSWrappedAESSIVKey*) keyToUnwrap error: (NSError * __autoreleasing *) error {
260 NSError* localerror = nil;
261 bool success = false;
262
263 CKKSAESSIVKey* unwrappedKey = nil;
264 uint8_t buffer[CKKSKeySize] = {};
265
266 size_t plaintextLength = ccsiv_plaintext_size(ccaes_siv_decrypt_mode(), CKKSWrappedKeySize);
267 require_action_quiet(plaintextLength == CKKSKeySize, out, localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
268 code:errSecParam
269 userInfo:@{NSLocalizedDescriptionKey: @"unwrapped key size does not match key size"}]);
270
271 success = [self doSIV: ccaes_siv_decrypt_mode()
272 nonce: nil
273 text: [[NSData alloc] initWithBytesNoCopy: (void*) (keyToUnwrap->key) length: keyToUnwrap->size freeWhenDone: NO]
274 buffer: buffer bufferLength:sizeof(buffer)
275 authenticatedData: nil
276 error: error];
277
278 require_quiet(success, out);
279
280 unwrappedKey = [[CKKSAESSIVKey alloc] initWithBytes: buffer len:sizeof(buffer)];
281 out:
282 memset_s(buffer, sizeof(buffer), 0x00, CKKSKeySize);
283 if(error && localerror != nil) {
284 *error = localerror;
285 }
286 return unwrappedKey;
287 }
288
289
290 - (NSData*)encryptData: (NSData*) plaintext authenticatedData: (NSDictionary<NSString*, NSData*>*) ad error: (NSError * __autoreleasing *) error {
291 size_t nonceLength = (128/8);
292 size_t ciphertextLength = 0;
293
294 NSMutableData* buffer = nil;
295 NSMutableData* nonce = nil;
296
297 bool success = false;
298
299 const struct ccmode_siv* mode = ccaes_siv_encrypt_mode();
300
301 // alloc space for nonce and ciphertext.
302 nonce = [[NSMutableData alloc] initWithLength: nonceLength];
303 CCRNGStatus status = CCRandomGenerateBytes(nonce.mutableBytes, nonce.length);
304 if(status != kCCSuccess) {
305 if(error) {
306 *error = [NSError errorWithDomain:@"CommonCrypto"
307 code:status
308 userInfo:@{NSLocalizedDescriptionKey: @"IV generation failed"}];
309 }
310 return nil;
311 }
312
313 ciphertextLength = ccsiv_ciphertext_size(mode, plaintext.length);
314 buffer = [[NSMutableData alloc] initWithLength: ciphertextLength];
315
316 success = [self doSIV: mode
317 nonce: nonce
318 text: plaintext
319 buffer: buffer.mutableBytes
320 bufferLength: buffer.length
321 authenticatedData: ad error: error];
322
323 if(!success) {
324 return nil;
325 }
326
327 NSMutableData* ret = [[NSMutableData alloc] init];
328 [ret appendData: nonce];
329 [ret appendData: buffer];
330 return ret;
331 }
332
333 - (NSData*)decryptData: (NSData*) ciphertext authenticatedData: (NSDictionary<NSString*, NSData*>*) ad error: (NSError * __autoreleasing *) error {
334 size_t nonceLength = (128/8);
335 size_t ciphertextLength = 0;
336 size_t plaintextLength = 0;
337
338 NSMutableData* plaintext = nil;
339 NSData* nonce = nil;
340 NSData* text = nil;
341
342 bool success = false;
343
344 const struct ccmode_siv* mode = ccaes_siv_decrypt_mode();
345
346 // compute sizes.
347 nonceLength = (128/8);
348 if(ciphertext.length <= nonceLength) {
349 if(error) {
350 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
351 code:4
352 userInfo:@{NSLocalizedDescriptionKey: @"ciphertext too short"}];
353 }
354 return nil;
355 }
356
357 ciphertextLength = ciphertext.length - (nonceLength);
358
359 // pointer arithmetic. tsk tsk.
360 nonce = [[NSData alloc] initWithBytesNoCopy: (void*) ciphertext.bytes length: nonceLength freeWhenDone: NO];
361 text = [[NSData alloc] initWithBytesNoCopy: (void*) (ciphertext.bytes + nonceLength) length: ciphertextLength freeWhenDone: NO];
362
363 // alloc space for plaintext
364 plaintextLength = ccsiv_plaintext_size(mode, ciphertextLength);
365 plaintext = [[NSMutableData alloc] initWithLength: plaintextLength];
366
367 success = [self doSIV: mode
368 nonce: nonce
369 text: text
370 buffer: plaintext.mutableBytes
371 bufferLength: plaintext.length
372 authenticatedData: ad error: error];
373
374 if(!success) {
375 return nil;
376 }
377
378 return plaintext;
379 }
380
381 // Does NOT check buffer size. Make sure you get it right for the mode you're requesting!
382 - (bool)doSIV: (const struct ccmode_siv*) mode nonce: (NSData*) nonce text: (NSData*) text buffer: (uint8_t*) buffer bufferLength: (size_t) bufferLength authenticatedData: (NSDictionary<NSString*, NSData*>*) ad error: (NSError * __autoreleasing *) error {
383
384 NSArray<NSString*>* adKeys = nil;
385 NSError* localerror = nil;
386 int status = 0;
387
388 ccsiv_ctx_decl(mode->size, ctx);
389
390 require_action_quiet(mode, out, localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
391 code:1
392 userInfo:@{NSLocalizedDescriptionKey: @"no mode given"}]);
393
394 status = ccsiv_init(mode, ctx, self->size, self->key);
395 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
396 code:status
397 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_init"}]);
398
399 if(nonce) {
400 status = ccsiv_set_nonce(mode, ctx, nonce.length, nonce.bytes);
401 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
402 code:status
403 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_set_nonce"}]);
404 }
405
406 // Add authenticated data, sorted by Key Order
407 adKeys = [[ad allKeys] sortedArrayUsingSelector:@selector(compare:)];
408 for(NSString* adKey in adKeys) {
409 NSData* adValue = [ad objectForKey: adKey];
410 status = ccsiv_aad(mode, ctx, adValue.length, adValue.bytes);
411 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
412 code:status
413 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_aad"}]);
414 }
415
416 // Actually go.
417 status = ccsiv_crypt(mode, ctx, text.length, text.bytes, buffer);
418 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
419 code:status
420 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_crypt"}]);
421 out:
422 ccsiv_ctx_clear(mode->size, ctx);
423
424 if(error) {
425 *error = localerror;
426 }
427 return localerror == NULL;
428 }
429
430
431 @end
432
433 #endif // OCTAGON