2 * Copyright (c) 2017 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #define __STDC_WANT_LIB_EXT1__ 1
29 #import <AssertMacros.h>
30 #import <Foundation/Foundation.h>
34 #include <CommonCrypto/CommonCrypto.h>
35 #include <CommonCrypto/CommonRandom.h>
36 #include <corecrypto/ccaes.h>
37 #include <corecrypto/ccmode_siv.h>
39 #import "keychain/categories/NSError+UsefulConstructors.h"
41 @implementation CKKSBaseAESSIVKey
42 - (instancetype)init {
43 if(self = [super init]) {
44 self->size = CKKSWrappedKeySize;
49 - (instancetype)initWithBytes:(uint8_t *)bytes len:(size_t)len {
50 if(self = [super init]) {
51 if(len <= CKKSWrappedKeySize) {
53 memcpy(self->key, bytes, self->size);
58 - (instancetype)initWithBase64:(NSString*)base64bytes {
59 if(self = [super init]) {
60 NSData* data = [[NSData alloc] initWithBase64EncodedString: base64bytes options:0];
66 if(data.length <= CKKSWrappedKeySize) {
67 self->size = data.length;
68 memcpy(self->key, data.bytes, self->size);
74 - (BOOL)isEqual: (id) object {
75 if(![object isKindOfClass:[CKKSBaseAESSIVKey class]]) {
79 CKKSBaseAESSIVKey* obj = (CKKSBaseAESSIVKey*) object;
80 if (self->size == obj->size && 0 == memcmp(self->key, obj->key, self->size)) {
92 memset_s(self->key, self->size, 0x00, CKKSWrappedKeySize);
95 - (instancetype)copyWithZone:(NSZone *)zone {
96 return [[[self class] allocWithZone:zone] initWithBytes:self->key len:self->size];
101 @implementation CKKSWrappedAESSIVKey
102 - (instancetype)init {
103 if(self = [super init]) {
104 self->size = CKKSWrappedKeySize;
108 - (instancetype)initWithBytes:(uint8_t *)bytes len:(size_t)len {
109 if(len != CKKSWrappedKeySize) {
111 exceptionWithName:@"WrongKeySizeException"
112 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long)len, CKKSWrappedKeySize]
115 if(self = [super initWithBytes: bytes len: len]) {
119 - (instancetype)initWithBase64:(NSString*)base64bytes {
120 if(self = [super initWithBase64: base64bytes]) {
121 if(self->size != CKKSWrappedKeySize) {
123 exceptionWithName:@"WrongKeySizeException"
124 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long)self->size, CKKSWrappedKeySize]
130 - (instancetype)initWithData: (NSData*) data {
131 if(data.length != CKKSWrappedKeySize) {
133 exceptionWithName:@"WrongKeySizeException"
134 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long) data.length, CKKSWrappedKeySize]
137 if(self = [super initWithBytes: (uint8_t*) data.bytes len: data.length]) {
141 - (NSData*) wrappedData {
142 return [[NSData alloc] initWithBytes:self->key length:self->size];
144 - (NSString*) base64WrappedKey {
145 return [[self wrappedData] base64EncodedStringWithOptions:0];
150 @implementation CKKSAESSIVKey
151 - (instancetype)init {
152 if(self = [super init]) {
153 self->size = CKKSKeySize;
157 - (instancetype)initWithBytes:(uint8_t *)bytes len:(size_t)len {
158 if(len != CKKSKeySize) {
160 exceptionWithName:@"WrongKeySizeException"
161 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long)len, CKKSKeySize]
164 if(self = [super initWithBytes: bytes len: len]) {
168 - (instancetype)initWithBase64:(NSString*)base64bytes {
169 if(self = [super initWithBase64: base64bytes]) {
170 if(self->size != CKKSKeySize) {
172 exceptionWithName:@"WrongKeySizeException"
173 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long)self->size, CKKSKeySize]
180 + (instancetype)randomKey {
181 CKKSAESSIVKey* key = [[CKKSAESSIVKey alloc] init];
183 CCRNGStatus status = CCRandomGenerateBytes(key->key, key->size);
184 if(status != kCCSuccess) {
186 exceptionWithName:@"randomnessException"
187 reason:[NSString stringWithFormat: @"CCRandomGenerateBytes failed with %d", status]
194 - (CKKSWrappedAESSIVKey*)wrapAESKey: (CKKSAESSIVKey*) keyToWrap error: (NSError * __autoreleasing *) error {
195 NSError* localerror = nil;
196 bool success = false;
199 localerror = [NSError errorWithDomain:@"securityd"
201 description:@"No key given"];
208 CKKSWrappedAESSIVKey* wrappedKey = nil;
209 uint8_t buffer[CKKSWrappedKeySize] = {};
211 size_t ciphertextLength = ccsiv_ciphertext_size(ccaes_siv_encrypt_mode(), CKKSKeySize);
212 require_action_quiet(ciphertextLength == CKKSWrappedKeySize, out, localerror = [NSError errorWithDomain:@"securityd"
214 userInfo:@{NSLocalizedDescriptionKey: @"wrapped key size does not match key size"}]);
216 success = [self doSIV: ccaes_siv_encrypt_mode()
218 text: [[NSData alloc] initWithBytesNoCopy: (void*) (keyToWrap->key) length: keyToWrap->size freeWhenDone: NO]
219 buffer: buffer bufferLength: sizeof(buffer)
220 authenticatedData: nil
222 require_quiet(success, out);
224 wrappedKey = [[CKKSWrappedAESSIVKey alloc] initWithBytes:buffer len:sizeof(buffer)];
226 memset_s(buffer, sizeof(buffer), 0x00, CKKSKeySize);
227 if(error && localerror != nil) {
233 - (CKKSAESSIVKey*)unwrapAESKey: (CKKSWrappedAESSIVKey*) keyToUnwrap error: (NSError * __autoreleasing *) error {
234 NSError* localerror = nil;
235 bool success = false;
237 CKKSAESSIVKey* unwrappedKey = nil;
238 uint8_t buffer[CKKSKeySize] = {};
240 size_t plaintextLength = ccsiv_plaintext_size(ccaes_siv_decrypt_mode(), CKKSWrappedKeySize);
241 require_action_quiet(plaintextLength == CKKSKeySize, out, localerror = [NSError errorWithDomain:@"securityd"
243 userInfo:@{NSLocalizedDescriptionKey: @"unwrapped key size does not match key size"}]);
245 success = [self doSIV: ccaes_siv_decrypt_mode()
247 text: [[NSData alloc] initWithBytesNoCopy: (void*) (keyToUnwrap->key) length: keyToUnwrap->size freeWhenDone: NO]
248 buffer: buffer bufferLength:sizeof(buffer)
249 authenticatedData: nil
252 require_quiet(success, out);
254 unwrappedKey = [[CKKSAESSIVKey alloc] initWithBytes: buffer len:sizeof(buffer)];
256 memset_s(buffer, sizeof(buffer), 0x00, CKKSKeySize);
257 if(error && localerror != nil) {
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;
268 NSMutableData* buffer = nil;
269 NSMutableData* nonce = nil;
271 bool success = false;
273 const struct ccmode_siv* mode = ccaes_siv_encrypt_mode();
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) {
280 *error = [NSError errorWithDomain:@"CommonCrypto"
282 userInfo:@{NSLocalizedDescriptionKey: @"IV generation failed"}];
287 ciphertextLength = ccsiv_ciphertext_size(mode, plaintext.length);
288 buffer = [[NSMutableData alloc] initWithLength: ciphertextLength];
290 success = [self doSIV: mode
293 buffer: buffer.mutableBytes
294 bufferLength: buffer.length
295 authenticatedData: ad error: error];
301 NSMutableData* ret = [[NSMutableData alloc] init];
302 [ret appendData: nonce];
303 [ret appendData: buffer];
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;
312 NSMutableData* plaintext = nil;
316 bool success = false;
318 const struct ccmode_siv* mode = ccaes_siv_decrypt_mode();
321 nonceLength = (128/8);
322 if(ciphertext.length <= nonceLength) {
324 *error = [NSError errorWithDomain:@"securityd"
326 userInfo:@{NSLocalizedDescriptionKey: @"ciphertext too short"}];
331 ciphertextLength = ciphertext.length - (nonceLength);
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];
337 // alloc space for plaintext
338 plaintextLength = ccsiv_plaintext_size(mode, ciphertextLength);
339 plaintext = [[NSMutableData alloc] initWithLength: plaintextLength];
341 success = [self doSIV: mode
344 buffer: plaintext.mutableBytes
345 bufferLength: plaintext.length
346 authenticatedData: ad error: error];
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 {
358 NSArray<NSString*>* adKeys = nil;
359 NSError* localerror = nil;
362 ccsiv_ctx_decl(mode->size, ctx);
364 require_action_quiet(mode, out, localerror = [NSError errorWithDomain:@"securityd"
366 userInfo:@{NSLocalizedDescriptionKey: @"no mode given"}]);
368 status = ccsiv_init(mode, ctx, self->size, self->key);
369 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
371 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_init"}]);
374 status = ccsiv_set_nonce(mode, ctx, nonce.length, nonce.bytes);
375 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
377 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_set_nonce"}]);
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"
387 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_aad"}]);
391 status = ccsiv_crypt(mode, ctx, text.length, text.bytes, buffer);
392 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
394 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_crypt"}]);
396 ccsiv_ctx_clear(mode->size, ctx);
401 return localerror == NULL;