Updated the README for the 0.1 release.
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.");