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@
24 #define __STDC_WANT_LIB_EXT1__ 1
27 #import <AssertMacros.h>
28 #import <Foundation/Foundation.h>
32 #include <CommonCrypto/CommonCrypto.h>
33 #include <CommonCrypto/CommonRandom.h>
34 #include <corecrypto/ccaes.h>
35 #include <corecrypto/ccmode_siv.h>
37 @implementation CKKSBaseAESSIVKey
38 - (instancetype)init {
39 if(self = [super init]) {
40 self->size = CKKSWrappedKeySize;
45 - (instancetype)initWithBytes:(uint8_t *)bytes len:(size_t)len {
46 if(self = [super init]) {
47 if(len <= CKKSWrappedKeySize) {
49 memcpy(self->key, bytes, self->size);
54 - (instancetype)initWithBase64:(NSString*)base64bytes {
55 if(self = [super init]) {
56 NSData* data = [[NSData alloc] initWithBase64EncodedString: base64bytes options:0];
62 if(data.length <= CKKSWrappedKeySize) {
63 self->size = data.length;
64 memcpy(self->key, data.bytes, self->size);
70 - (BOOL)isEqual: (id) object {
71 if(![object isKindOfClass:[CKKSBaseAESSIVKey class]]) {
75 CKKSBaseAESSIVKey* obj = (CKKSBaseAESSIVKey*) object;
76 if (self->size == obj->size && 0 == memcmp(self->key, obj->key, self->size)) {
88 memset_s(self->key, self->size, 0x00, CKKSWrappedKeySize);
91 - (instancetype)copyWithZone:(NSZone *)zone {
92 return [[[self class] allocWithZone:zone] initWithBytes:self->key len:self->size];
97 @implementation CKKSWrappedAESSIVKey
98 - (instancetype)init {
99 if(self = [super init]) {
100 self->size = CKKSWrappedKeySize;
104 - (instancetype)initWithBytes:(uint8_t *)bytes len:(size_t)len {
105 if(len != CKKSWrappedKeySize) {
107 exceptionWithName:@"WrongKeySizeException"
108 reason:[NSString stringWithFormat: @"length (%lu) was not %d", len, CKKSWrappedKeySize]
111 if(self = [super initWithBytes: bytes len: len]) {
115 - (instancetype)initWithBase64:(NSString*)base64bytes {
116 if(self = [super initWithBase64: base64bytes]) {
117 if(self->size != CKKSWrappedKeySize) {
119 exceptionWithName:@"WrongKeySizeException"
120 reason:[NSString stringWithFormat: @"length (%lu) was not %d", self->size, CKKSWrappedKeySize]
126 - (instancetype)initWithData: (NSData*) data {
127 if(data.length != CKKSWrappedKeySize) {
129 exceptionWithName:@"WrongKeySizeException"
130 reason:[NSString stringWithFormat: @"length (%lu) was not %d", (unsigned long) data.length, CKKSWrappedKeySize]
133 if(self = [super initWithBytes: (uint8_t*) data.bytes len: data.length]) {
137 - (NSData*) wrappedData {
138 return [[NSData alloc] initWithBytes:self->key length:self->size];
140 - (NSString*) base64WrappedKey {
141 return [[self wrappedData] base64EncodedStringWithOptions:0];
146 @implementation CKKSAESSIVKey
147 - (instancetype)init {
148 if(self = [super init]) {
149 self->size = CKKSKeySize;
153 - (instancetype)initWithBytes:(uint8_t *)bytes len:(size_t)len {
154 if(len != CKKSKeySize) {
156 exceptionWithName:@"WrongKeySizeException"
157 reason:[NSString stringWithFormat: @"length (%lu) was not %d", len, CKKSKeySize]
160 if(self = [super initWithBytes: bytes len: len]) {
164 - (instancetype)initWithBase64:(NSString*)base64bytes {
165 if(self = [super initWithBase64: base64bytes]) {
166 if(self->size != CKKSKeySize) {
168 exceptionWithName:@"WrongKeySizeException"
169 reason:[NSString stringWithFormat: @"length (%lu) was not %d", self->size, CKKSKeySize]
176 + (instancetype)randomKey {
177 CKKSAESSIVKey* key = [[CKKSAESSIVKey alloc] init];
179 CCRNGStatus status = CCRandomGenerateBytes(key->key, key->size);
180 if(status != kCCSuccess) {
182 exceptionWithName:@"randomnessException"
183 reason:[NSString stringWithFormat: @"CCRandomGenerateBytes failed with %d", status]
190 - (CKKSWrappedAESSIVKey*)wrapAESKey: (CKKSAESSIVKey*) keyToWrap error: (NSError * __autoreleasing *) error {
191 NSError* localerror = nil;
192 bool success = false;
194 CKKSWrappedAESSIVKey* wrappedKey = nil;
195 uint8_t buffer[CKKSWrappedKeySize] = {};
197 size_t ciphertextLength = ccsiv_ciphertext_size(ccaes_siv_encrypt_mode(), CKKSKeySize);
198 require_action_quiet(ciphertextLength == CKKSWrappedKeySize, out, localerror = [NSError errorWithDomain:@"securityd"
200 userInfo:@{NSLocalizedDescriptionKey: @"wrapped key size does not match key size"}]);
202 success = [self doSIV: ccaes_siv_encrypt_mode()
204 text: [[NSData alloc] initWithBytesNoCopy: (void*) (keyToWrap->key) length: keyToWrap->size freeWhenDone: NO]
205 buffer: buffer bufferLength: sizeof(buffer)
206 authenticatedData: nil
208 require_quiet(success, out);
210 wrappedKey = [[CKKSWrappedAESSIVKey alloc] initWithBytes:buffer len:sizeof(buffer)];
212 memset_s(buffer, sizeof(buffer), 0x00, CKKSKeySize);
213 if(error && localerror != nil) {
219 - (CKKSAESSIVKey*)unwrapAESKey: (CKKSWrappedAESSIVKey*) keyToUnwrap error: (NSError * __autoreleasing *) error {
220 NSError* localerror = nil;
221 bool success = false;
223 CKKSAESSIVKey* unwrappedKey = nil;
224 uint8_t buffer[CKKSKeySize] = {};
226 size_t plaintextLength = ccsiv_plaintext_size(ccaes_siv_decrypt_mode(), CKKSWrappedKeySize);
227 require_action_quiet(plaintextLength == CKKSKeySize, out, localerror = [NSError errorWithDomain:@"securityd"
229 userInfo:@{NSLocalizedDescriptionKey: @"unwrapped key size does not match key size"}]);
231 success = [self doSIV: ccaes_siv_decrypt_mode()
233 text: [[NSData alloc] initWithBytesNoCopy: (void*) (keyToUnwrap->key) length: keyToUnwrap->size freeWhenDone: NO]
234 buffer: buffer bufferLength:sizeof(buffer)
235 authenticatedData: nil
238 require_quiet(success, out);
240 unwrappedKey = [[CKKSAESSIVKey alloc] initWithBytes: buffer len:sizeof(buffer)];
242 memset_s(buffer, sizeof(buffer), 0x00, CKKSKeySize);
243 if(error && localerror != nil) {
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;
254 NSMutableData* buffer = nil;
255 NSMutableData* nonce = nil;
257 bool success = false;
259 const struct ccmode_siv* mode = ccaes_siv_encrypt_mode();
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) {
266 *error = [NSError errorWithDomain:@"CommonCrypto"
268 userInfo:@{NSLocalizedDescriptionKey: @"IV generation failed"}];
273 ciphertextLength = ccsiv_ciphertext_size(mode, plaintext.length);
274 buffer = [[NSMutableData alloc] initWithLength: ciphertextLength];
276 success = [self doSIV: mode
279 buffer: buffer.mutableBytes
280 bufferLength: buffer.length
281 authenticatedData: ad error: error];
287 NSMutableData* ret = [[NSMutableData alloc] init];
288 [ret appendData: nonce];
289 [ret appendData: buffer];
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;
298 NSMutableData* plaintext = nil;
302 bool success = false;
304 const struct ccmode_siv* mode = ccaes_siv_decrypt_mode();
307 nonceLength = (128/8);
308 if(ciphertext.length <= nonceLength) {
310 *error = [NSError errorWithDomain:@"securityd"
312 userInfo:@{NSLocalizedDescriptionKey: @"ciphertext too short"}];
317 ciphertextLength = ciphertext.length - (nonceLength);
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];
323 // alloc space for plaintext
324 plaintextLength = ccsiv_plaintext_size(mode, ciphertextLength);
325 plaintext = [[NSMutableData alloc] initWithLength: plaintextLength];
327 success = [self doSIV: mode
330 buffer: plaintext.mutableBytes
331 bufferLength: plaintext.length
332 authenticatedData: ad error: error];
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 {
344 NSArray<NSString*>* adKeys = nil;
345 NSError* localerror = nil;
348 ccsiv_ctx_decl(mode->size, ctx);
350 require_action_quiet(mode, out, localerror = [NSError errorWithDomain:@"securityd"
352 userInfo:@{NSLocalizedDescriptionKey: @"no mode given"}]);
354 status = ccsiv_init(mode, ctx, self->size, self->key);
355 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
357 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_init"}]);
360 status = ccsiv_set_nonce(mode, ctx, nonce.length, nonce.bytes);
361 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
363 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_set_nonce"}]);
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"
373 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_aad"}]);
377 status = ccsiv_crypt(mode, ctx, text.length, text.bytes, buffer);
378 require_action_quiet(status == 0, out, localerror = [NSError errorWithDomain:@"corecrypto"
380 userInfo:@{NSLocalizedDescriptionKey: @"could not ccsiv_crypt"}]);
382 ccsiv_ctx_clear(mode->size, ctx);
387 return localerror == NULL;