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 {
158 if ((self = [super init])) {
160 const uint8_t * bytes = [decoder decodeBytesForKey:@"wrappedkey" returnedLength:&len];
163 memcpy(self->key, bytes, (size_t) len <= CKKSWrappedKeySize ? len : CKKSWrappedKeySize);
169 + (CKKSWrappedAESSIVKey*)zeroedKey
171 NSData* zeroedData = [NSMutableData dataWithLength:CKKSWrappedKeySize];
172 return [[CKKSWrappedAESSIVKey alloc] initWithData:zeroedData];
177 @implementation CKKSAESSIVKey
178 - (instancetype)init {
179 if(self = [super init]) {
180 self->size = CKKSKeySize;
184 - (instancetype)initWithBytes:(uint8_t *)bytes len:(size_t)len {
185 if(len != CKKSKeySize) {
187 exceptionWithName:@"WrongKeySizeException"
188 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long)len, CKKSKeySize]
191 if(self = [super initWithBytes: bytes len: len]) {
195 - (instancetype)initWithBase64:(NSString*)base64bytes {
196 if(self = [super initWithBase64: base64bytes]) {
197 if(self->size != CKKSKeySize) {
199 exceptionWithName:@"WrongKeySizeException"
200 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long)self->size, CKKSKeySize]
207 + (instancetype _Nullable)randomKey:(NSError* __autoreleasing *)error
209 CKKSAESSIVKey* key = [[CKKSAESSIVKey alloc] init];
211 CCRNGStatus status = CCRandomGenerateBytes(key->key, key->size);
212 if(status != kCCSuccess) {
214 *error = [NSError errorWithDomain:@"corecrypto"
216 description:[NSString stringWithFormat: @"CCRandomGenerateBytes failed with %d", status]];
224 - (CKKSWrappedAESSIVKey*)wrapAESKey: (CKKSAESSIVKey*) keyToWrap error: (NSError * __autoreleasing *) error {
225 NSError* localerror = nil;
226 bool success = false;
229 localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
231 description:@"No key given"];
238 CKKSWrappedAESSIVKey* wrappedKey = nil;
239 uint8_t buffer[CKKSWrappedKeySize] = {};
241 size_t ciphertextLength = ccsiv_ciphertext_size(ccaes_siv_encrypt_mode(), CKKSKeySize);
242 require_action_quiet(ciphertextLength == CKKSWrappedKeySize, out, localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
244 userInfo:@{NSLocalizedDescriptionKey: @"wrapped key size does not match key size"}]);
246 success = [self doSIV: ccaes_siv_encrypt_mode()
248 text: [NSData _newZeroingDataWithBytes:(void*) (keyToWrap->key) length:keyToWrap->size]
250 bufferLength: sizeof(buffer)
251 authenticatedData: nil
253 require_quiet(success, out);
255 wrappedKey = [[CKKSWrappedAESSIVKey alloc] initWithBytes:buffer len:sizeof(buffer)];
257 memset_s(buffer, sizeof(buffer), 0x00, CKKSKeySize);
258 if(error && localerror != nil) {
264 - (CKKSAESSIVKey*)unwrapAESKey: (CKKSWrappedAESSIVKey*) keyToUnwrap error: (NSError * __autoreleasing *) error {
265 NSError* localerror = nil;
266 bool success = false;
268 CKKSAESSIVKey* unwrappedKey = nil;
269 uint8_t buffer[CKKSKeySize] = {};
271 size_t plaintextLength = ccsiv_plaintext_size(ccaes_siv_decrypt_mode(), CKKSWrappedKeySize);
272 require_action_quiet(plaintextLength == CKKSKeySize, out, localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
274 userInfo:@{NSLocalizedDescriptionKey: @"unwrapped key size does not match key size"}]);
276 success = [self doSIV: ccaes_siv_decrypt_mode()
278 text: [[NSData alloc] initWithBytesNoCopy: (void*) (keyToUnwrap->key) length: keyToUnwrap->size freeWhenDone: NO]
279 buffer: buffer bufferLength:sizeof(buffer)
280 authenticatedData: nil
283 require_quiet(success, out);
285 unwrappedKey = [[CKKSAESSIVKey alloc] initWithBytes: buffer len:sizeof(buffer)];
287 memset_s(buffer, sizeof(buffer), 0x00, CKKSKeySize);
288 if(error && localerror != nil) {
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;
299 NSMutableData* buffer = nil;
300 NSMutableData* nonce = nil;
302 bool success = false;
304 const struct ccmode_siv* mode = ccaes_siv_encrypt_mode();
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) {
311 *error = [NSError errorWithDomain:@"CommonCrypto"
313 userInfo:@{NSLocalizedDescriptionKey: @"IV generation failed"}];
318 ciphertextLength = ccsiv_ciphertext_size(mode, plaintext.length);
319 buffer = [[NSMutableData alloc] initWithLength: ciphertextLength];
321 success = [self doSIV: mode
324 buffer: buffer.mutableBytes
325 bufferLength: buffer.length
326 authenticatedData: ad error: error];
332 NSMutableData* ret = [[NSMutableData alloc] init];
333 [ret appendData: nonce];
334 [ret appendData: buffer];
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;
343 NSMutableData* plaintext = nil;
347 bool success = false;
349 const struct ccmode_siv* mode = ccaes_siv_decrypt_mode();
352 nonceLength = (128/8);
353 if(ciphertext.length <= nonceLength) {
355 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
357 userInfo:@{NSLocalizedDescriptionKey: @"ciphertext too short"}];
362 ciphertextLength = ciphertext.length - (nonceLength);
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];
368 // alloc space for plaintext
369 plaintextLength = ccsiv_plaintext_size(mode, ciphertextLength);
370 plaintext = [[NSMutableData alloc] initWithLength: plaintextLength];
372 success = [self doSIV: mode
375 buffer: plaintext.mutableBytes
376 bufferLength: plaintext.length
377 authenticatedData: ad error: error];
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 {
389 NSArray<NSString*>* adKeys = nil;
390 NSError* localerror = nil;
393 ccsiv_ctx_decl(mode->size, ctx);
395 require_action_quiet(mode, out, localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
397 userInfo:@{NSLocalizedDescriptionKey: @"no mode given"}]);
399 status = ccsiv_init(mode, ctx, self->size, self->key);
400 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
402 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_init"}]);
405 status = ccsiv_set_nonce(mode, ctx, nonce.length, nonce.bytes);
406 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
408 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_set_nonce"}]);
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"
418 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_aad"}]);
422 status = ccsiv_crypt(mode, ctx, text.length, text.bytes, buffer);
423 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
425 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_crypt"}]);
427 ccsiv_ctx_clear(mode->size, ctx);
432 return localerror == NULL;
435 - (NSData*)keyMaterial
437 return [NSData _newZeroingDataWithBytes:self->key length:self->size];