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