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