Whew! MYParsedCertificate can now generate certs from scratch. Also added improvements and fixes to the BER/DER codecs.
5 // Created by Jens Alfke on 1/16/08.
6 // Copyright 2008 Jens Alfke. All rights reserved.
10 #import "MYCrypto_Private.h"
12 #import "MYErrorUtils.h"
15 @interface MYSigner ()
16 - (id) initWithDecoder: (CMSDecoderRef)decoder index: (size_t)index policy: (CFTypeRef)policy;
20 @implementation MYDecoder
23 - (id) initWithData: (NSData*)data error: (NSError**)outError
29 *outError = self.error;
42 OSStatus err = CMSDecoderCreate(&_decoder);
53 if( _decoder ) CFRelease(_decoder);
54 if (_policy) CFRelease(_policy);
59 - (BOOL) addData: (NSData*)data
62 return !_error && checksave( CMSDecoderUpdateMessage(_decoder, data.bytes, data.length) );
68 return !_error && checksave( CMSDecoderFinalizeMessage(_decoder) );
74 return MYError(_error, NSOSStatusErrorDomain,
75 @"%@", MYErrorName(NSOSStatusErrorDomain,_error));
80 - (BOOL) useKeychain: (MYKeychain*)keychain
82 return !_error && checksave( CMSDecoderSetSearchKeychain(_decoder, keychain.keychainRef) );
86 @synthesize policy=_policy;
89 - (NSData*) _dataFromFunction: (OSStatus (*)(CMSDecoderRef,CFDataRef*))function
92 if( checksave( (*function)(_decoder, &data) ) )
93 return [(NSData*)CFMakeCollectable(data) autorelease];
99 - (NSData*) detachedContent
101 return [self _dataFromFunction: &CMSDecoderCopyDetachedContent];
104 - (void) setDetachedContent: (NSData*)detachedContent
107 checksave( CMSDecoderSetDetachedContent(_decoder, (CFDataRef)detachedContent) );
110 - (CSSM_OID) contentType
112 NSData *data = [self _dataFromFunction: &CMSDecoderCopyEncapsulatedContentType];
113 return (CSSM_OID){data.length,(uint8*)data.bytes}; // safe since data is autoreleased
118 return [self _dataFromFunction: &CMSDecoderCopyContent];
124 return checksave( CMSDecoderGetNumSigners(_decoder, &n) ) && n > 0;
130 return check(CMSDecoderIsContentEncrypted(_decoder,&isEncrypted), @"CMSDecoderIsContentEncrypted")
137 if( ! checksave( CMSDecoderGetNumSigners(_decoder, &n) ) )
139 NSMutableArray *signers = [NSMutableArray arrayWithCapacity: n];
140 for( size_t i=0; i<n; i++ ) {
141 MYSigner *signer = [[MYSigner alloc] initWithDecoder: _decoder
144 [signers addObject: signer];
151 - (NSArray*) certificates
153 CFArrayRef certRefs = NULL;
154 if( ! checksave( CMSDecoderCopyAllCerts(_decoder, &certRefs) ) || ! certRefs )
156 unsigned n = CFArrayGetCount(certRefs);
157 NSMutableArray *certs = [NSMutableArray arrayWithCapacity: n];
158 for( unsigned i=0; i<n; i++ ) {
159 SecCertificateRef certRef = (SecCertificateRef) CFArrayGetValueAtIndex(certRefs, i);
160 [certs addObject: [MYCertificate certificateWithCertificateRef: certRef]];
169 static const char * kStatusNames[kCMSSignerInvalidIndex+1] = {
170 "kCMSSignerUnsigned", "kCMSSignerValid", "kCMSSignerNeedsDetachedContent",
171 "kCMSSignerInvalidSignature","kCMSSignerInvalidCert","kCMSSignerInvalidIndex"};
173 CSSM_OID contentType = self.contentType;
174 NSMutableString *s = [NSMutableString stringWithFormat: @"%@<%p>:\n"
175 "\tcontentType = %@ (\"%@\")\n"
176 "\tcontent = %u bytes\n"
177 "\tsigned=%i, encrypted=%i\n"
179 "\t%u certificates\n"
182 OIDAsString(contentType), @"??"/*nameOfOID(&contentType)*/,
184 self.isSigned,self.isEncrypted,
185 MYPolicyGetName(_policy),
186 self.certificates.count];
187 for( MYSigner *signer in self.signers ) {
188 CMSSignerStatus status = signer.status;
189 const char *statusName = (status<=kCMSSignerInvalidIndex) ?kStatusNames[status] :"??";
190 [s appendFormat:@"\t\t- status = %s\n"
191 "\t\t verifyResult = %@\n"
195 (signer.verifyResult ?MYErrorName(NSOSStatusErrorDomain,signer.verifyResult)
197 MYTrustDescribe(signer.trust),
209 @implementation MYSigner : NSObject
211 #define kUncheckedStatus ((CMSSignerStatus)-1)
213 - (id) initWithDecoder: (CMSDecoderRef)decoder index: (size_t)index policy: (CFTypeRef)policy
220 if(policy) _policy = CFRetain(policy);
221 _status = kUncheckedStatus;
228 if(_decoder) CFRelease(_decoder);
229 if(_policy) CFRelease(_policy);
230 if(_trust) CFRelease(_trust);
235 if (_status == kUncheckedStatus) {
236 if( !check(CMSDecoderCopySignerStatus(_decoder, _index, _policy, (_policy!=nil),
237 &_status, &_trust, &_verifyResult),
238 @"CMSDecoderCopySignerStatus"))
239 _status = kMYSignerStatusCheckFailed;
243 - (CMSSignerStatus) status
249 - (OSStatus) verifyResult
252 return _verifyResult;
255 - (SecTrustRef) trust
262 - (NSString*) emailAddress
264 // Don't let caller see the addr if they haven't checked validity & the signature's invalid:
265 if (_status==kUncheckedStatus && self.status != kCMSSignerValid)
268 CFStringRef email=NULL;
269 if( CMSDecoderCopySignerEmailAddress(_decoder, _index, &email) == noErr )
270 return [(NSString*)CFMakeCollectable(email) autorelease];
274 - (MYCertificate *) certificate
276 // Don't let caller see the cert if they haven't checked validity & the signature's invalid:
277 if (_status==kUncheckedStatus && self.status != kCMSSignerValid)
280 SecCertificateRef certRef=NULL;
281 OSStatus err = CMSDecoderCopySignerCert(_decoder, _index, &certRef);
283 return [MYCertificate certificateWithCertificateRef: certRef];
285 Warn(@"CMSDecoderCopySignerCert returned err %i",err);
291 - (NSString*) description
293 NSMutableString *desc = [NSMutableString stringWithFormat: @"%@[st=%i", self.class,(int)self.status];
294 int verify = self.verifyResult;
296 [desc appendFormat: @"; verify error %i",verify];
298 MYCertificate *cert = self.certificate;
300 [desc appendFormat: @"; %@",cert.commonName];
302 [desc appendString: @"]"];
313 #pragma mark TEST CASE:
318 #import "MYEncoder.h"
319 #import "MYIdentity.h"
321 static void TestRoundTrip( NSString *title, NSData *source, MYIdentity *signer, MYCertificate *recipient )
323 Log(@"Testing MYEncoder/Decoder %@...",title);
325 NSData *encoded = [MYEncoder encodeData: source signer: signer recipient: recipient error: &error];
326 CAssertEq(error,nil);
327 CAssert([encoded length]);
328 Log(@"MYEncoder encoded %u bytes into %u bytes", source.length,encoded.length);
330 MYDecoder *d = [[MYDecoder alloc] init];
331 d.policy = [MYCertificate X509Policy];
332 [d addData: encoded];
335 CAssertEq(d.error,nil);
338 CAssert([d.content isEqual: source]);
339 CAssertEq(d.detachedContent,nil);
340 CAssertEq(d.isSigned,(signer!=nil));
341 CAssertEq(d.isEncrypted,(recipient!=nil));
344 CAssert(d.certificates.count >= 1); // may include extra parent certs
345 CAssertEq(d.signers.count,1U);
346 MYSigner *outSigner = [d.signers objectAtIndex: 0];
347 CAssertEq(outSigner.status,(CMSSignerStatus)kCMSSignerValid);
348 CAssertEq(outSigner.verifyResult,noErr);
349 CAssert([outSigner.certificate isEqualToCertificate: signer]);
351 CAssertEq(d.certificates.count, 0U);
352 CAssertEq(d.signers.count,0U);
357 TestCase(MYDecoder) {
358 RequireTestCase(MYEncoder);
360 MYIdentity *me = [MYIdentity preferredIdentityForName: @"MYCryptoTest"];
361 CAssert(me,@"No default identity has been set up in the Keychain");
362 Log(@"Using %@", me);
364 NSData *source = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"];
367 TestRoundTrip(@"signing", source, me, nil);
368 TestRoundTrip(@"encryption", source, nil, me);
369 TestRoundTrip(@"signing+encryption", source, me, me);
377 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
379 Redistribution and use in source and binary forms, with or without modification, are permitted
380 provided that the following conditions are met:
382 * Redistributions of source code must retain the above copyright notice, this list of conditions
383 and the following disclaimer.
384 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
385 and the following disclaimer in the documentation and/or other materials provided with the
388 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
389 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
390 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
391 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
392 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
393 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
394 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
395 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.