]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/CKKSSIV.m
Security-59754.41.1.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 if ((self = [super init])) {
159 NSUInteger len = 0;
160 const uint8_t * bytes = [decoder decodeBytesForKey:@"wrappedkey" returnedLength:&len];
161
162 if(bytes) {
163 memcpy(self->key, bytes, (size_t) len <= CKKSWrappedKeySize ? len : CKKSWrappedKeySize);
164 }
165 }
166 return self;
167 }
168
169 + (CKKSWrappedAESSIVKey*)zeroedKey
170 {
171 NSData* zeroedData = [NSMutableData dataWithLength:CKKSWrappedKeySize];
172 return [[CKKSWrappedAESSIVKey alloc] initWithData:zeroedData];
173 }
174
175 @end
176
177 @implementation CKKSAESSIVKey
178 - (instancetype)init {
179 if(self = [super init]) {
180 self->size = CKKSKeySize;
181 }
182 return self;
183 }
184 - (instancetype)initWithBytes:(uint8_t *)bytes len:(size_t)len {
185 if(len != CKKSKeySize) {
186 @throw [NSException
187 exceptionWithName:@"WrongKeySizeException"
188 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long)len, CKKSKeySize]
189 userInfo:nil];
190 }
191 if(self = [super initWithBytes: bytes len: len]) {
192 }
193 return self;
194 }
195 - (instancetype)initWithBase64:(NSString*)base64bytes {
196 if(self = [super initWithBase64: base64bytes]) {
197 if(self->size != CKKSKeySize) {
198 @throw [NSException
199 exceptionWithName:@"WrongKeySizeException"
200 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long)self->size, CKKSKeySize]
201 userInfo:nil];
202 }
203 }
204 return self;
205 }
206
207 + (instancetype _Nullable)randomKey:(NSError* __autoreleasing *)error
208 {
209 CKKSAESSIVKey* key = [[CKKSAESSIVKey alloc] init];
210
211 CCRNGStatus status = CCRandomGenerateBytes(key->key, key->size);
212 if(status != kCCSuccess) {
213 if(error) {
214 *error = [NSError errorWithDomain:@"corecrypto"
215 code:status
216 description:[NSString stringWithFormat: @"CCRandomGenerateBytes failed with %d", status]];
217 }
218 return nil;
219 }
220
221 return key;
222 }
223
224 - (CKKSWrappedAESSIVKey*)wrapAESKey: (CKKSAESSIVKey*) keyToWrap error: (NSError * __autoreleasing *) error {
225 NSError* localerror = nil;
226 bool success = false;
227
228 if(!keyToWrap) {
229 localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
230 code:errSecParam
231 description:@"No key given"];
232 if(error) {
233 *error = localerror;
234 }
235 return nil;
236 }
237
238 CKKSWrappedAESSIVKey* wrappedKey = nil;
239 uint8_t buffer[CKKSWrappedKeySize] = {};
240
241 size_t ciphertextLength = ccsiv_ciphertext_size(ccaes_siv_encrypt_mode(), CKKSKeySize);
242 require_action_quiet(ciphertextLength == CKKSWrappedKeySize, out, localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
243 code:errSecParam
244 userInfo:@{NSLocalizedDescriptionKey: @"wrapped key size does not match key size"}]);
245
246 success = [self doSIV: ccaes_siv_encrypt_mode()
247 nonce: nil
248 text: [NSData _newZeroingDataWithBytes:(void*) (keyToWrap->key) length:keyToWrap->size]
249 buffer: buffer
250 bufferLength: sizeof(buffer)
251 authenticatedData: nil
252 error: error];
253 require_quiet(success, out);
254
255 wrappedKey = [[CKKSWrappedAESSIVKey alloc] initWithBytes:buffer len:sizeof(buffer)];
256 out:
257 memset_s(buffer, sizeof(buffer), 0x00, CKKSKeySize);
258 if(error && localerror != nil) {
259 *error = localerror;
260 }
261 return wrappedKey;
262 }
263
264 - (CKKSAESSIVKey*)unwrapAESKey: (CKKSWrappedAESSIVKey*) keyToUnwrap error: (NSError * __autoreleasing *) error {
265 NSError* localerror = nil;
266 bool success = false;
267
268 CKKSAESSIVKey* unwrappedKey = nil;
269 uint8_t buffer[CKKSKeySize] = {};
270
271 size_t plaintextLength = ccsiv_plaintext_size(ccaes_siv_decrypt_mode(), CKKSWrappedKeySize);
272 require_action_quiet(plaintextLength == CKKSKeySize, out, localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
273 code:errSecParam
274 userInfo:@{NSLocalizedDescriptionKey: @"unwrapped key size does not match key size"}]);
275
276 success = [self doSIV: ccaes_siv_decrypt_mode()
277 nonce: nil
278 text: [[NSData alloc] initWithBytesNoCopy: (void*) (keyToUnwrap->key) length: keyToUnwrap->size freeWhenDone: NO]
279 buffer: buffer bufferLength:sizeof(buffer)
280 authenticatedData: nil
281 error: error];
282
283 require_quiet(success, out);
284
285 unwrappedKey = [[CKKSAESSIVKey alloc] initWithBytes: buffer len:sizeof(buffer)];
286 out:
287 memset_s(buffer, sizeof(buffer), 0x00, CKKSKeySize);
288 if(error && localerror != nil) {
289 *error = localerror;
290 }
291 return unwrappedKey;
292 }
293
294
295 - (NSData*)encryptData: (NSData*) plaintext authenticatedData: (NSDictionary<NSString*, NSData*>*) ad error: (NSError * __autoreleasing *) error {
296 size_t nonceLength = (128/8);
297 size_t ciphertextLength = 0;
298
299 NSMutableData* buffer = nil;
300 NSMutableData* nonce = nil;
301
302 bool success = false;
303
304 const struct ccmode_siv* mode = ccaes_siv_encrypt_mode();
305
306 // alloc space for nonce and ciphertext.
307 nonce = [[NSMutableData alloc] initWithLength: nonceLength];
308 CCRNGStatus status = CCRandomGenerateBytes(nonce.mutableBytes, nonce.length);
309 if(status != kCCSuccess) {
310 if(error) {
311 *error = [NSError errorWithDomain:@"CommonCrypto"
312 code:status
313 userInfo:@{NSLocalizedDescriptionKey: @"IV generation failed"}];
314 }
315 return nil;
316 }
317
318 ciphertextLength = ccsiv_ciphertext_size(mode, plaintext.length);
319 buffer = [[NSMutableData alloc] initWithLength: ciphertextLength];
320
321 success = [self doSIV: mode
322 nonce: nonce
323 text: plaintext
324 buffer: buffer.mutableBytes
325 bufferLength: buffer.length
326 authenticatedData: ad error: error];
327
328 if(!success) {
329 return nil;
330 }
331
332 NSMutableData* ret = [[NSMutableData alloc] init];
333 [ret appendData: nonce];
334 [ret appendData: buffer];
335 return ret;
336 }
337
338 - (NSData*)decryptData: (NSData*) ciphertext authenticatedData: (NSDictionary<NSString*, NSData*>*) ad error: (NSError * __autoreleasing *) error {
339 size_t nonceLength = (128/8);
340 size_t ciphertextLength = 0;
341 size_t plaintextLength = 0;
342
343 NSMutableData* plaintext = nil;
344 NSData* nonce = nil;
345 NSData* text = nil;
346
347 bool success = false;
348
349 const struct ccmode_siv* mode = ccaes_siv_decrypt_mode();
350
351 // compute sizes.
352 nonceLength = (128/8);
353 if(ciphertext.length <= nonceLength) {
354 if(error) {
355 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
356 code:4
357 userInfo:@{NSLocalizedDescriptionKey: @"ciphertext too short"}];
358 }
359 return nil;
360 }
361
362 ciphertextLength = ciphertext.length - (nonceLength);
363
364 // pointer arithmetic. tsk tsk.
365 nonce = [[NSData alloc] initWithBytesNoCopy: (void*) ciphertext.bytes length: nonceLength freeWhenDone: NO];
366 text = [[NSData alloc] initWithBytesNoCopy: (void*) (ciphertext.bytes + nonceLength) length: ciphertextLength freeWhenDone: NO];
367
368 // alloc space for plaintext
369 plaintextLength = ccsiv_plaintext_size(mode, ciphertextLength);
370 plaintext = [[NSMutableData alloc] initWithLength: plaintextLength];
371
372 success = [self doSIV: mode
373 nonce: nonce
374 text: text
375 buffer: plaintext.mutableBytes
376 bufferLength: plaintext.length
377 authenticatedData: ad error: error];
378
379 if(!success) {
380 return nil;
381 }
382
383 return plaintext;
384 }
385
386 // Does NOT check buffer size. Make sure you get it right for the mode you're requesting!
387 - (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 {
388
389 NSArray<NSString*>* adKeys = nil;
390 NSError* localerror = nil;
391 int status = 0;
392
393 ccsiv_ctx_decl(mode->size, ctx);
394
395 require_action_quiet(mode, out, localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
396 code:1
397 userInfo:@{NSLocalizedDescriptionKey: @"no mode given"}]);
398
399 status = ccsiv_init(mode, ctx, self->size, self->key);
400 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
401 code:status
402 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_init"}]);
403
404 if(nonce) {
405 status = ccsiv_set_nonce(mode, ctx, nonce.length, nonce.bytes);
406 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
407 code:status
408 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_set_nonce"}]);
409 }
410
411 // Add authenticated data, sorted by Key Order
412 adKeys = [[ad allKeys] sortedArrayUsingSelector:@selector(compare:)];
413 for(NSString* adKey in adKeys) {
414 NSData* adValue = [ad objectForKey: adKey];
415 status = ccsiv_aad(mode, ctx, adValue.length, adValue.bytes);
416 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
417 code:status
418 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_aad"}]);
419 }
420
421 // Actually go.
422 status = ccsiv_crypt(mode, ctx, text.length, text.bytes, buffer);
423 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
424 code:status
425 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_crypt"}]);
426 out:
427 ccsiv_ctx_clear(mode->size, ctx);
428
429 if(error) {
430 *error = localerror;
431 }
432 return localerror == NULL;
433 }
434
435 - (NSData*)keyMaterial
436 {
437 return [NSData _newZeroingDataWithBytes:self->key length:self->size];
438 }
439
440
441 @end
442
443 #endif // OCTAGON