snej@8: // snej@8: // MYEncoder.m snej@8: // MYCrypto snej@8: // snej@8: // Created by Jens Alfke on 1/16/08. snej@8: // Copyright 2008-2009 Jens Alfke. All rights reserved. snej@8: // snej@8: snej@8: #import "MYEncoder.h" snej@8: #import "MYIdentity.h" snej@8: #import "MYCrypto_Private.h" snej@8: #import "Test.h" snej@8: #import "MYErrorUtils.h" snej@8: snej@8: snej@8: @implementation MYEncoder snej@8: snej@8: snej@8: - (id) init snej@8: { snej@8: self = [super init]; snej@8: if (self != nil) { snej@8: if( ! checksave(CMSEncoderCreate(&_encoder)) ) { snej@8: [self release]; snej@8: return nil; snej@8: } snej@8: } snej@8: return self; snej@8: } snej@8: snej@8: - (void) dealloc snej@8: { snej@8: if(_encoder) CFRelease(_encoder); snej@8: [super dealloc]; snej@8: } snej@8: snej@8: snej@8: snej@8: - (BOOL) addSigner: (MYIdentity*)signer snej@8: { snej@8: Assert(signer); snej@8: return checksave( CMSEncoderAddSigners(_encoder, signer.identityRef) ); snej@8: } snej@8: snej@8: - (BOOL) addRecipient: (MYCertificate*)recipient snej@8: { snej@8: Assert(recipient); snej@8: return checksave( CMSEncoderAddRecipients(_encoder, recipient.certificateRef) ); snej@8: } snej@8: snej@8: - (BOOL) addSupportingCert: (MYCertificate*)supportingCert snej@8: { snej@8: Assert(supportingCert); snej@8: return checksave( CMSEncoderAddSupportingCerts(_encoder, supportingCert.certificateRef) ); snej@8: } snej@8: snej@8: - (BOOL) addTimestamp snej@8: { snej@8: return checksave( CMSEncoderAddSignedAttributes(_encoder, kCMSAttrSigningTime) ); snej@8: } snej@8: snej@8: snej@8: - (NSError*) error snej@8: { snej@8: if( _error ) snej@8: return MYError(_error, NSOSStatusErrorDomain, snej@8: @"%@", MYErrorName(NSOSStatusErrorDomain,_error)); snej@8: else snej@8: return nil; snej@8: } snej@8: snej@8: snej@8: - (CMSCertificateChainMode) certificateChainMode snej@8: { snej@8: CMSCertificateChainMode mode; snej@8: if( CMSEncoderGetCertificateChainMode(_encoder, &mode) == noErr ) snej@8: return mode; snej@8: else snej@8: return -1; snej@8: } snej@8: snej@8: - (void) setCertificateChainMode: (CMSCertificateChainMode)mode snej@8: { snej@8: checksave( CMSEncoderSetCertificateChainMode(_encoder, mode) ); snej@8: } snej@8: snej@8: - (BOOL) hasDetachedContent snej@8: { snej@8: Boolean detached; snej@8: return CMSEncoderGetHasDetachedContent(_encoder, &detached)==noErr && detached; snej@8: } snej@8: snej@8: - (void) setHasDetachedContent: (BOOL)detached snej@8: { snej@8: checksave( CMSEncoderSetHasDetachedContent(_encoder, detached) ); snej@8: } snej@8: snej@8: - (NSData*) _dataFromFunction: (OSStatus (*)(CMSEncoderRef,CFDataRef*))function snej@8: { snej@8: CFDataRef data=NULL; snej@8: if( checksave( (*function)(_encoder, &data) ) ) snej@8: return [(NSData*)CFMakeCollectable(data) autorelease]; snej@8: else snej@8: return nil; snej@8: } snej@8: snej@8: snej@8: - (CSSM_OID) contentType snej@8: { snej@8: NSData *data = [self _dataFromFunction: &CMSEncoderCopyEncapsulatedContentType]; snej@8: return (CSSM_OID){data.length,(uint8*)data.bytes}; snej@8: } snej@8: snej@8: - (void) setContentType: (CSSM_OID)contentType snej@8: { snej@8: checksave( CMSEncoderSetEncapsulatedContentType(_encoder, &contentType) ); snej@8: } snej@8: snej@8: snej@8: - (BOOL) addData: (NSData*)data snej@8: { snej@8: Assert(data); snej@8: return ! _error && checksave( CMSEncoderUpdateContent(_encoder, data.bytes, data.length) ); snej@8: } snej@8: snej@8: snej@8: - (NSData*) encodedData snej@8: { snej@8: if( ! _error ) snej@8: return [self _dataFromFunction: &CMSEncoderCopyEncodedContent]; snej@8: else snej@8: return nil; snej@8: } snej@8: snej@8: snej@8: + (NSData*) encodeData: (NSData*)data snej@8: signer: (MYIdentity*)signer snej@8: recipient: (MYCertificate*)recipient snej@8: error: (NSError**)outError snej@8: { snej@8: MYEncoder *e = [[self alloc] init]; snej@8: if( signer ) snej@8: [e addSigner: signer]; snej@8: if( recipient ) snej@8: [e addRecipient: recipient]; snej@8: [e addData: data]; snej@8: *outError = e.error; snej@8: NSData *result = e.encodedData; snej@8: [e release]; snej@8: return result; snej@8: } snej@8: snej@8: snej@8: @end snej@8: snej@8: snej@8: #if DEBUG snej@8: snej@8: #import "MYCrypto+Cocoa.h" snej@8: snej@8: TestCase(MYEncoder) { snej@8: MYIdentity *me = nil;//[MYIdentity preferredIdentityForName: @"MYCryptoTest"]; snej@8: if (!me) { snej@8: NSArray *idents = [[[MYKeychain allKeychains] enumerateIdentities] allObjects]; snej@8: SFChooseIdentityPanel *panel = [SFChooseIdentityPanel sharedChooseIdentityPanel]; snej@8: [panel setAlternateButtonTitle: @"Cancel"]; snej@8: if ([panel my_runModalForIdentities: idents snej@8: message: @"Choose an identity for the MYEncoder test case:"] snej@8: != NSOKButton) { snej@8: [NSException raise: NSGenericException format: @"User canceled"]; snej@8: } snej@8: me = [panel my_identity]; snej@8: [me makePreferredIdentityForName: @"MYCryptoTest"]; snej@8: } snej@8: CAssert(me,@"No default identity has been set up in the Keychain"); snej@8: snej@8: NSData *source = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"]; snej@8: CAssert(source); snej@8: snej@8: NSError *error; snej@8: NSData *encoded; snej@8: snej@8: Log(@"Testing signing..."); snej@8: encoded = [MYEncoder encodeData: source signer: me recipient: nil error: &error]; snej@8: CAssertEq(error,nil); snej@8: CAssert([encoded length]); snej@8: Log(@"MYEncoder signed %u bytes into %u bytes", source.length,encoded.length); snej@8: snej@8: Log(@"Testing encryption..."); snej@8: encoded = [MYEncoder encodeData: source signer: nil recipient: me error: &error]; snej@8: CAssertEq(error,nil); snej@8: CAssert([encoded length]); snej@8: Log(@"MYEncoder encrypted %u bytes into %u bytes", source.length,encoded.length); snej@8: snej@8: Log(@"Testing signing+encryption..."); snej@8: encoded = [MYEncoder encodeData: source signer: me recipient: me error: &error]; snej@8: CAssertEq(error,nil); snej@8: CAssert([encoded length]); snej@8: Log(@"MYEncoder signed/encrypted %u bytes into %u bytes", source.length,encoded.length); snej@8: } snej@8: #endif