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>
31 #import <Foundation/NSData_Private.h>
35 #include <CommonCrypto/CommonCrypto.h>
36 #include <CommonCrypto/CommonRandom.h>
37 #include <corecrypto/ccaes.h>
38 #include <corecrypto/ccmode_siv.h>
40 #import "keychain/categories/NSError+UsefulConstructors.h"
42 @implementation CKKSBaseAESSIVKey
43 - (instancetype)init {
44 if(self = [super init]) {
45 self->size = CKKSWrappedKeySize;
50 - (instancetype)initWithBytes:(uint8_t *)bytes len:(size_t)len {
51 if(self = [super init]) {
52 if(len <= CKKSWrappedKeySize) {
54 memcpy(self->key, bytes, self->size);
59 - (instancetype)initWithBase64:(NSString*)base64bytes {
60 if(self = [super init]) {
61 NSData* data = [[NSData alloc] initWithBase64EncodedString: base64bytes options:0];
67 if(data.length <= CKKSWrappedKeySize) {
68 self->size = data.length;
69 memcpy(self->key, data.bytes, self->size);
75 - (BOOL)isEqual: (id) object {
76 if(![object isKindOfClass:[CKKSBaseAESSIVKey class]]) {
80 CKKSBaseAESSIVKey* obj = (CKKSBaseAESSIVKey*) object;
81 if (self->size == obj->size && 0 == memcmp(self->key, obj->key, self->size)) {
93 memset_s(self->key, self->size, 0x00, CKKSWrappedKeySize);
96 - (instancetype)copyWithZone:(NSZone *)zone {
97 return [[[self class] allocWithZone:zone] initWithBytes:self->key len:self->size];
102 @implementation CKKSWrappedAESSIVKey
103 - (instancetype)init {
104 if(self = [super init]) {
105 self->size = CKKSWrappedKeySize;
109 - (instancetype)initWithBytes:(uint8_t *)bytes len:(size_t)len {
110 if(len != CKKSWrappedKeySize) {
112 exceptionWithName:@"WrongKeySizeException"
113 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long)len, CKKSWrappedKeySize]
116 if(self = [super initWithBytes: bytes len: len]) {
120 - (instancetype)initWithBase64:(NSString*)base64bytes {
121 if(self = [super initWithBase64: base64bytes]) {
122 if(self->size != CKKSWrappedKeySize) {
124 exceptionWithName:@"WrongKeySizeException"
125 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long)self->size, CKKSWrappedKeySize]
131 - (instancetype)initWithData: (NSData*) data {
132 if(data.length != CKKSWrappedKeySize) {
134 exceptionWithName:@"WrongKeySizeException"
135 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long) data.length, CKKSWrappedKeySize]
138 if(self = [super initWithBytes: (uint8_t*) data.bytes len: data.length]) {
142 - (NSData*) wrappedData {
143 return [[NSData alloc] initWithBytes:self->key length:self->size];
145 - (NSString*) base64WrappedKey {
146 return [[self wrappedData] base64EncodedStringWithOptions:0];
149 + (BOOL)supportsSecureCoding {
153 - (void)encodeWithCoder:(nonnull NSCoder *)coder {
154 [coder encodeBytes:self->key length:self->size forKey:@"wrappedkey"];
157 - (nullable instancetype)initWithCoder:(nonnull NSCoder *)decoder {
161 const uint8_t * bytes = [decoder decodeBytesForKey:@"wrappedkey" returnedLength:&len];
164 memcpy(self->key, bytes, (size_t) len <= CKKSWrappedKeySize ? len : CKKSWrappedKeySize);
172 @implementation CKKSAESSIVKey
173 - (instancetype)init {
174 if(self = [super init]) {
175 self->size = CKKSKeySize;
179 - (instancetype)initWithBytes:(uint8_t *)bytes len:(size_t)len {
180 if(len != CKKSKeySize) {
182 exceptionWithName:@"WrongKeySizeException"
183 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long)len, CKKSKeySize]
186 if(self = [super initWithBytes: bytes len: len]) {
190 - (instancetype)initWithBase64:(NSString*)base64bytes {
191 if(self = [super initWithBase64: base64bytes]) {
192 if(self->size != CKKSKeySize) {
194 exceptionWithName:@"WrongKeySizeException"
195 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long)self->size, CKKSKeySize]
202 + (instancetype _Nullable)randomKey:(NSError* __autoreleasing *)error
204 CKKSAESSIVKey* key = [[CKKSAESSIVKey alloc] init];
206 CCRNGStatus status = CCRandomGenerateBytes(key->key, key->size);
207 if(status != kCCSuccess) {
209 *error = [NSError errorWithDomain:@"corecrypto"
211 description:[NSString stringWithFormat: @"CCRandomGenerateBytes failed with %d", status]];
219 - (CKKSWrappedAESSIVKey*)wrapAESKey: (CKKSAESSIVKey*) keyToWrap error: (NSError * __autoreleasing *) error {
220 NSError* localerror = nil;
221 bool success = false;
224 localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
226 description:@"No key given"];
233 CKKSWrappedAESSIVKey* wrappedKey = nil;
234 uint8_t buffer[CKKSWrappedKeySize] = {};
236 size_t ciphertextLength = ccsiv_ciphertext_size(ccaes_siv_encrypt_mode(), CKKSKeySize);
237 require_action_quiet(ciphertextLength == CKKSWrappedKeySize, out, localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
239 userInfo:@{NSLocalizedDescriptionKey: @"wrapped key size does not match key size"}]);
241 success = [self doSIV: ccaes_siv_encrypt_mode()
243 text: [NSData _newZeroingDataWithBytes:(void*) (keyToWrap->key) length:keyToWrap->size]
245 bufferLength: sizeof(buffer)
246 authenticatedData: nil
248 require_quiet(success, out);
250 wrappedKey = [[CKKSWrappedAESSIVKey alloc] initWithBytes:buffer len:sizeof(buffer)];
252 memset_s(buffer, sizeof(buffer), 0x00, CKKSKeySize);
253 if(error && localerror != nil) {
259 - (CKKSAESSIVKey*)unwrapAESKey: (CKKSWrappedAESSIVKey*) keyToUnwrap error: (NSError * __autoreleasing *) error {
260 NSError* localerror = nil;
261 bool success = false;
263 CKKSAESSIVKey* unwrappedKey = nil;
264 uint8_t buffer[CKKSKeySize] = {};
266 size_t plaintextLength = ccsiv_plaintext_size(ccaes_siv_decrypt_mode(), CKKSWrappedKeySize);
267 require_action_quiet(plaintextLength == CKKSKeySize, out, localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
269 userInfo:@{NSLocalizedDescriptionKey: @"unwrapped key size does not match key size"}]);
271 success = [self doSIV: ccaes_siv_decrypt_mode()
273 text: [[NSData alloc] initWithBytesNoCopy: (void*) (keyToUnwrap->key) length: keyToUnwrap->size freeWhenDone: NO]
274 buffer: buffer bufferLength:sizeof(buffer)
275 authenticatedData: nil
278 require_quiet(success, out);
280 unwrappedKey = [[CKKSAESSIVKey alloc] initWithBytes: buffer len:sizeof(buffer)];
282 memset_s(buffer, sizeof(buffer), 0x00, CKKSKeySize);
283 if(error && localerror != nil) {
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;
294 NSMutableData* buffer = nil;
295 NSMutableData* nonce = nil;
297 bool success = false;
299 const struct ccmode_siv* mode = ccaes_siv_encrypt_mode();
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) {
306 *error = [NSError errorWithDomain:@"CommonCrypto"
308 userInfo:@{NSLocalizedDescriptionKey: @"IV generation failed"}];
313 ciphertextLength = ccsiv_ciphertext_size(mode, plaintext.length);
314 buffer = [[NSMutableData alloc] initWithLength: ciphertextLength];
316 success = [self doSIV: mode
319 buffer: buffer.mutableBytes
320 bufferLength: buffer.length
321 authenticatedData: ad error: error];
327 NSMutableData* ret = [[NSMutableData alloc] init];
328 [ret appendData: nonce];
329 [ret appendData: buffer];
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;
338 NSMutableData* plaintext = nil;
342 bool success = false;
344 const struct ccmode_siv* mode = ccaes_siv_decrypt_mode();
347 nonceLength = (128/8);
348 if(ciphertext.length <= nonceLength) {
350 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
352 userInfo:@{NSLocalizedDescriptionKey: @"ciphertext too short"}];
357 ciphertextLength = ciphertext.length - (nonceLength);
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];
363 // alloc space for plaintext
364 plaintextLength = ccsiv_plaintext_size(mode, ciphertextLength);
365 plaintext = [[NSMutableData alloc] initWithLength: plaintextLength];
367 success = [self doSIV: mode
370 buffer: plaintext.mutableBytes
371 bufferLength: plaintext.length
372 authenticatedData: ad error: error];
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 {
384 NSArray<NSString*>* adKeys = nil;
385 NSError* localerror = nil;
388 ccsiv_ctx_decl(mode->size, ctx);
390 require_action_quiet(mode, out, localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
392 userInfo:@{NSLocalizedDescriptionKey: @"no mode given"}]);
394 status = ccsiv_init(mode, ctx, self->size, self->key);
395 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
397 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_init"}]);
400 status = ccsiv_set_nonce(mode, ctx, nonce.length, nonce.bytes);
401 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
403 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_set_nonce"}]);
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"
413 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_aad"}]);
417 status = ccsiv_crypt(mode, ctx, text.length, text.bytes, buffer);
418 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
420 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_crypt"}]);
422 ccsiv_ctx_clear(mode->size, ctx);
427 return localerror == NULL;