MYCertificate now checks validity of self-signed certs loaded from the keychain (because the Security framework doesn't validate self-signed certs.)
5 // Created by Jens Alfke on 3/21/09.
6 // Copyright 2009 Jens Alfke. All rights reserved.
13 #if MYCRYPTO_USE_IPHONE_API
14 #import <Security/SecRandom.h>
16 #import "MYCrypto_Private.h"
17 #import "MYKeychain.h"
22 NSString* const CryptorErrorDomain = @"CCCryptor";
24 #if !MYCRYPTO_USE_IPHONE_API
25 static BOOL generateRandomBytes(CSSM_CSP_HANDLE module, uint32_t lengthInBytes, void *dstBytes);
29 @interface MYCryptor ()
30 @property (readwrite, retain) NSError *error;
35 @implementation MYCryptor
38 + (NSData*) randomKeyOfLength: (size_t)lengthInBits {
39 size_t lengthInBytes = (lengthInBits + 7)/8;
40 NSParameterAssert(lengthInBytes<100000);
41 uint8_t *bytes = malloc(lengthInBytes);
42 if (!bytes) return nil;
43 #if MYCRYPTO_USE_IPHONE_API
44 BOOL ok = SecRandomCopyBytes(kSecRandomDefault, lengthInBytes,bytes) >= 0;
46 BOOL ok = generateRandomBytes([[MYKeychain defaultKeychain] CSPHandle], lengthInBytes, bytes);
49 return [NSData dataWithBytesNoCopy: bytes length: lengthInBytes freeWhenDone: YES];
56 + (NSData*) keyOfLength: (size_t)lengthInBits
57 fromPassphrase: (NSString*)passphrase
60 // This follows algorithm PBKDF1 from PKCS#5 v2.0, with Hash=SHA-256 and c=13.
63 passphrase = $sprintf(@"MYCrypto|%@|%@", passphrase, salt);
64 size_t lengthInBytes = (lengthInBits + 7)/8;
65 MYDigest *digest = [[passphrase dataUsingEncoding: NSUTF8StringEncoding] my_SHA256Digest];
66 for (int i=0; i<12; i++)
67 digest = digest.asData.my_SHA256Digest;
68 if (lengthInBytes <= digest.length)
69 return [digest.asData subdataWithRange: NSMakeRange(0,lengthInBytes)];
75 - (id) initWithKey: (NSData*)key
76 algorithm: (CCAlgorithm)algorithm
77 operation: (CCOperation)op {
80 NSParameterAssert(key);
83 _algorithm = algorithm;
84 _options = kCCOptionPKCS7Padding;
89 - (id) initEncryptorWithKey: (NSData*)key algorithm: (CCAlgorithm)algorithm {
90 return [self initWithKey: key algorithm: algorithm operation: kCCEncrypt];
93 - (id) initDecryptorWithKey: (NSData*)key algorithm: (CCAlgorithm)algorithm {
94 return [self initWithKey: key algorithm: algorithm operation: kCCDecrypt];
100 CCCryptorRelease(_cryptor);
102 [_output autorelease];
103 [_outputStream release];
110 CCCryptorRelease(_cryptor);
115 @synthesize key=_key, algorithm=_algorithm, options=_options,
116 outputStream=_outputStream, error=_error;
119 - (BOOL) _check: (CCCryptorStatus)status {
120 if (status == kCCSuccess)
123 Warn(@"MYCryptor: CCCryptor error %i", status);
124 self.error = [NSError errorWithDomain: CryptorErrorDomain code: status userInfo: nil];
130 - (BOOL) _outputBytes: (const void*)bytes length: (size_t)length {
132 size_t written = [_outputStream write: bytes maxLength: length];
134 self.error = _outputStream.streamError;
136 Warn(@"MYCryptor: NSOutputStream error %@", _error);
138 [self _check: kMYCryptorErrorOutputStreamChoked];
140 } else if (written < length) {
141 [self _check: kMYCryptorErrorOutputStreamChoked];
144 } else if (length > 0) {
145 [_output appendBytes: bytes length: length];
152 if (!_cryptor && !_error) {
153 if ([self _check: CCCryptorCreate(_operation, _algorithm, _options,
154 _key.bytes, _key.length, NULL, &_cryptor)]) {
155 _output = [[NSMutableData alloc] initWithCapacity: 1024];
162 - (BOOL) addBytes: (const void*)bytes length: (size_t)length {
164 NSParameterAssert(bytes!=NULL);
165 if(!_error && (_cryptor || [self _start])) {
166 size_t outputLength = CCCryptorGetOutputLength(_cryptor,length,false);
167 void *output = malloc(outputLength);
168 if ([self _check: CCCryptorUpdate(_cryptor, bytes, length,
169 output, outputLength, &outputLength)]) {
170 [self _outputBytes: output length: outputLength];
178 - (BOOL) addData: (NSData*)data
180 return [self addBytes: data.bytes length: data.length];
183 - (BOOL) addString: (NSString*)str {
184 return [self addData: [str dataUsingEncoding: NSUTF8StringEncoding]];
188 - (BOOL) addFromStream: (NSInputStream*)input
190 uint8_t inputBuffer[1024];
192 while (!_error && input.hasBytesAvailable) {
193 avail = sizeof(inputBuffer);
194 NSInteger nRead = [input read: inputBuffer maxLength: sizeof(inputBuffer)];
196 self.error = input.streamError;
198 } else if (nRead == 0) {
200 } else if (![self addBytes: inputBuffer length: nRead])
209 if(!_error && (_cryptor || [self _start])) {
210 size_t outputLength = 100; //CCCryptorGetOutputLength(_cryptor,1,true);
211 void *output = malloc(outputLength);
212 if ([self _check: CCCryptorFinal(_cryptor, output, outputLength, &outputLength)]) {
213 [self _outputBytes: output length: outputLength];
217 CCCryptorRelease(_cryptor);
223 - (NSData*) outputData {
224 if (_cryptor) [self finish];
232 - (NSString*) outputString {
233 NSData *output = self.outputData;
235 NSString *str = [[NSString alloc] initWithData: output encoding: NSUTF8StringEncoding];
236 return [str autorelease];
242 // NSStream delegate method
243 - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
245 case NSStreamEventHasBytesAvailable:
246 [self addFromStream: (NSInputStream*)stream];
248 case NSStreamEventEndEncountered:
251 case NSStreamEventErrorOccurred:
253 self.error = stream.streamError;
267 #if !MYCRYPTO_USE_IPHONE_API
268 static BOOL generateRandomBytes(CSSM_CSP_HANDLE module, uint32_t lengthInBytes, void *dstBytes) {
269 // Adapted from code in Keychain.framework's KeychainUtils.m by Wade Tregaskis.
270 CSSM_CC_HANDLE ccHandle;
271 if (!checkcssm(CSSM_CSP_CreateRandomGenContext(module, CSSM_ALGID_APPLE_YARROW, NULL,
272 lengthInBytes, &ccHandle),
273 @"CSSM_CSP_CreateRandomGenContext"))
275 CSSM_DATA data = {.Data=dstBytes, .Length=lengthInBytes};
276 BOOL ok = checkcssm(CSSM_GenerateRandom(ccHandle, &data), @"CSSM_GenerateRandom");
277 CSSM_DeleteContext(ccHandle);
285 TestCase(MYCryptor) {
287 NSData *key = [MYCryptor randomKeyOfLength: 256];
288 Log(@"Key = %@",key);
289 MYCryptor *enc = [[MYCryptor alloc] initEncryptorWithKey: key algorithm: kCCAlgorithmAES128];
291 CAssert([enc addString: @"This is a test. "]);
292 CAssert([enc addString: @"This is only a test."]);
293 CAssertEqual(enc.error, nil);
294 NSData *encrypted = enc.outputData;
295 CAssertEqual(enc.error, nil);
296 CAssert(encrypted.length > 0);
298 Log(@"Encrypted = %@", encrypted);
301 MYCryptor *dec = [[MYCryptor alloc] initDecryptorWithKey: key algorithm: kCCAlgorithmAES128];
303 CAssert([dec addData: encrypted]);
304 NSString *decrypted = dec.outputString;
305 CAssertEqual(dec.error, nil);
307 Log(@"Decrypted = '%@'", decrypted);
308 CAssertEqual(decrypted, @"This is a test. This is only a test.");
310 // Encryption to stream:
311 key = [MYCryptor randomKeyOfLength: 256];
312 Log(@"Key = %@",key);
313 enc = [[MYCryptor alloc] initEncryptorWithKey: key algorithm: kCCAlgorithmAES128];
315 enc.outputStream = [NSOutputStream outputStreamToMemory];
316 [enc.outputStream open];
317 CAssert([enc addString: @"This is a test. "]);
318 CAssert([enc addString: @"This is only a test."]);
319 CAssert([enc finish]);
320 CAssertEqual(enc.error, nil);
321 encrypted = [[enc.outputStream propertyForKey: NSStreamDataWrittenToMemoryStreamKey] retain];
322 CAssert(encrypted.length > 0);
324 Log(@"Encrypted = %@", encrypted);
326 dec = [[MYCryptor alloc] initDecryptorWithKey: key algorithm: kCCAlgorithmAES128];
328 dec.outputStream = [NSOutputStream outputStreamToMemory];
329 [dec.outputStream open];
330 CAssert([dec addData: encrypted]);
331 CAssert([dec finish]);
332 CAssertEqual(dec.error, nil);
333 NSData *decryptedData = [dec.outputStream propertyForKey: NSStreamDataWrittenToMemoryStreamKey];
335 decrypted = [[NSString alloc] initWithData: decryptedData
336 encoding: NSUTF8StringEncoding];
337 Log(@"Decrypted = '%@'", decrypted);
338 CAssertEqual(decrypted, @"This is a test. This is only a test.");
346 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
348 Redistribution and use in source and binary forms, with or without modification, are permitted
349 provided that the following conditions are met:
351 * Redistributions of source code must retain the above copyright notice, this list of conditions
352 and the following disclaimer.
353 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
354 and the following disclaimer in the documentation and/or other materials provided with the
357 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
358 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
359 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
360 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
361 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
362 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
363 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
364 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.