* Added MYEncoder/Decoder (CMS)
authorsnej@snej.local
Sun Apr 12 22:02:20 2009 -0700 (2009-04-12)
changeset 84c0eafa7b233
parent 7 dee779b84a95
child 9 aa5eb3fd6ebf
* Added MYEncoder/Decoder (CMS)
* Fixed some key-generation parameters to make the keys work with CMS.
* Added MYCrypto+Cocoa, for identity picker.
MYCertGen.m
MYCertificate-iPhone.m
MYCertificate.h
MYCertificate.m
MYCrypto+Cocoa.h
MYCrypto+Cocoa.m
MYCrypto-iPhone.xcodeproj/project.pbxproj
MYCrypto.xcodeproj/project.pbxproj
MYCryptoTest.m
MYCrypto_Private.h
MYDecoder.h
MYDecoder.m
MYEncoder.h
MYEncoder.m
MYIdentity.h
MYIdentity.m
MYKey.m
MYPrivateKey.h
MYPrivateKey.m
MYSymmetricKey.m
README.textile
     1.1 --- a/MYCertGen.m	Thu Apr 09 22:47:11 2009 -0700
     1.2 +++ b/MYCertGen.m	Sun Apr 12 22:02:20 2009 -0700
     1.3 @@ -177,7 +177,7 @@
     1.4      // that's binary 111111000; see http://tools.ietf.org/html/rfc3280#section-4.2.1.3
     1.5      CSSM_X509_EXTENSION keyUsage = {
     1.6          CSSMOID_KeyUsage, 
     1.7 -        true, 
     1.8 +        false,      // non-critical
     1.9          CSSM_X509_DATAFORMAT_PARSED,
    1.10          {.parsedValue = &keyUsageBits}
    1.11      };
    1.12 @@ -187,11 +187,11 @@
    1.13          UInt32 count;
    1.14          const CSSM_OID *oids;
    1.15      };
    1.16 -    CSSM_OID usageOids[2] = {CSSMOID_ServerAuth, CSSMOID_ClientAuth};
    1.17 -    struct ExtendedUsageList extUsageBits = {2, usageOids};
    1.18 +    CSSM_OID usageOids[3] = {CSSMOID_ServerAuth, CSSMOID_ClientAuth, CSSMOID_ExtendedKeyUsageAny};
    1.19 +    struct ExtendedUsageList extUsageBits = {3, usageOids};
    1.20      CSSM_X509_EXTENSION extendedKeyUsage = {
    1.21          CSSMOID_ExtendedKeyUsage,
    1.22 -        true,
    1.23 +        false,      // non-critical
    1.24          CSSM_X509_DATAFORMAT_PARSED,
    1.25          {.parsedValue = &extUsageBits}
    1.26      };
    1.27 @@ -466,10 +466,9 @@
    1.28      Log(@"CSSM_CL_HANDLE = %p", cl);
    1.29      CAssert(cl);
    1.30      
    1.31 -    MYKeychain *keychain = [MYKeychain allKeychains];
    1.32 -    Log(@"Looking for a key pair...");
    1.33 -    MYPrivateKey *privateKey = [[keychain enumeratePrivateKeys] nextObject];
    1.34 -    Log(@"Using key pair { %@, %@ }", privateKey, privateKey.publicKey);
    1.35 +    Log(@"Generating a key pair...");
    1.36 +    MYPrivateKey *privateKey = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 2048];
    1.37 +    Log(@"Key-pair = { %@, %@ }", privateKey, privateKey.publicKey);
    1.38      
    1.39      Log(@"...creating cert...");
    1.40      
    1.41 @@ -483,6 +482,7 @@
    1.42                                                        ));
    1.43      Log(@"Cert = %@", cert);
    1.44      CAssert(cert);
    1.45 +    [cert.certificateData writeToFile: @"/tmp/MYCryptoTest.cer" atomically: NO];
    1.46      
    1.47      Log(@"Cert name = %@", cert.commonName);
    1.48      Log(@"Cert email = %@", cert.emailAddresses);
    1.49 @@ -491,5 +491,7 @@
    1.50      CAssertEqual(cert.emailAddresses, $array(@"waldo@example.com"));
    1.51      CAssertEqual(cert.publicKey.publicKeyDigest, privateKey.publicKeyDigest);
    1.52      
    1.53 -    [cert.certificateData writeToFile: @"/tmp/MYCryptoTest.cer" atomically: NO];
    1.54 +    CAssert([[MYKeychain defaultKeychain] addCertificate: cert]);
    1.55 +    
    1.56 +    CAssert([cert setUserTrust: kSecTrustResultProceed]);
    1.57  }
     2.1 --- a/MYCertificate-iPhone.m	Thu Apr 09 22:47:11 2009 -0700
     2.2 +++ b/MYCertificate-iPhone.m	Sun Apr 12 22:02:20 2009 -0700
     2.3 @@ -15,6 +15,10 @@
     2.4  @implementation MYCertificate
     2.5  
     2.6  
     2.7 ++ (MYCertificate*) certificateWithCertificateRef: (SecCertificateRef)certificateRef {
     2.8 +    return [[[self alloc] initWithCertificateRef: certificateRef] autorelease];
     2.9 +}
    2.10 +
    2.11  /** Creates a MYCertificate object for an existing Keychain certificate reference. */
    2.12  - (id) initWithCertificateRef: (SecCertificateRef)certificateRef {
    2.13      self = [super initWithKeychainItemRef: (SecKeychainItemRef)certificateRef];
    2.14 @@ -34,6 +38,10 @@
    2.15  }
    2.16  
    2.17  
    2.18 +- (BOOL)isEqualToCertificate:(MYCertificate*)cert {
    2.19 +    return [self isEqual: cert] || [self.certificateData isEqual: cert.certificateData];
    2.20 +}
    2.21 +
    2.22  @synthesize certificateRef=_certificateRef;
    2.23  
    2.24  - (NSData*) certificateData {
     3.1 --- a/MYCertificate.h	Thu Apr 09 22:47:11 2009 -0700
     3.2 +++ b/MYCertificate.h	Sun Apr 12 22:02:20 2009 -0700
     3.3 @@ -22,11 +22,17 @@
     3.4  }
     3.5  
     3.6  /** Creates a MYCertificate object for an existing Keychain certificate reference. */
     3.7 ++ (MYCertificate*) certificateWithCertificateRef: (SecCertificateRef)certificateRef;
     3.8 +
     3.9 +/** Initializes a MYCertificate object for an existing Keychain certificate reference. */
    3.10  - (id) initWithCertificateRef: (SecCertificateRef)certificateRef;
    3.11  
    3.12  /** Creates a MYCertificate object from exported key data, but does not add it to any keychain. */
    3.13  - (id) initWithCertificateData: (NSData*)data;
    3.14  
    3.15 +/** Checks whether two MYCertificate objects have bit-for-bit identical certificate data. */
    3.16 +- (BOOL)isEqualToCertificate:(MYCertificate*)cert;
    3.17 +
    3.18  /** The Keychain object reference for this certificate. */
    3.19  @property (readonly) SecCertificateRef certificateRef;
    3.20  
    3.21 @@ -63,4 +69,25 @@
    3.22  #endif
    3.23  //@}
    3.24  
    3.25 +
    3.26 +/** @name Expert
    3.27 + */
    3.28 +//@{
    3.29 +#if !TARGET_OS_IPHONE
    3.30 +
    3.31 ++ (SecPolicyRef) X509Policy;
    3.32 ++ (SecPolicyRef) SSLPolicy;
    3.33 ++ (SecPolicyRef) SMIMEPolicy;
    3.34 +- (CSSM_CERT_TYPE) certificateType;
    3.35 +- (NSArray*) trustSettings;
    3.36 +- (BOOL) setUserTrust: (SecTrustUserSetting)trustSetting;
    3.37 +    
    3.38 +#endif
    3.39 +//@}
    3.40 +    
    3.41  @end
    3.42 +
    3.43 +
    3.44 +NSString* MYPolicyGetName( SecPolicyRef policy );
    3.45 +NSString* MYTrustDescribe( SecTrustRef trust );
    3.46 +NSString* MYTrustResultDescribe( SecTrustResultType result );
     4.1 --- a/MYCertificate.m	Thu Apr 09 22:47:11 2009 -0700
     4.2 +++ b/MYCertificate.m	Sun Apr 12 22:02:20 2009 -0700
     4.3 @@ -8,6 +8,8 @@
     4.4  
     4.5  #import "MYCertificate.h"
     4.6  #import "MYCrypto_Private.h"
     4.7 +#import "MYDigest.h"
     4.8 +#import "MYErrorUtils.h"
     4.9  
    4.10  #if !MYCRYPTO_USE_IPHONE_API
    4.11  
    4.12 @@ -24,6 +26,10 @@
    4.13      return self;
    4.14  }
    4.15  
    4.16 ++ (MYCertificate*) certificateWithCertificateRef: (SecCertificateRef)certificateRef {
    4.17 +    return [[[self alloc] initWithCertificateRef: certificateRef] autorelease];
    4.18 +}
    4.19 +
    4.20  /** Creates a MYCertificate object from exported key data, but does not add it to any keychain. */
    4.21  - (id) initWithCertificateData: (NSData*)data
    4.22                            type: (CSSM_CERT_TYPE) type
    4.23 @@ -48,6 +54,21 @@
    4.24                                  encoding: CSSM_CERT_ENCODING_BER];
    4.25  }
    4.26  
    4.27 +
    4.28 +- (NSString*) description {
    4.29 +    return $sprintf(@"%@[%@ %@/%p]", 
    4.30 +                    [self class],
    4.31 +                    self.commonName,
    4.32 +                    self.certificateData.my_SHA1Digest.abbreviatedHexString,
    4.33 +                    _certificateRef);
    4.34 +}
    4.35 +
    4.36 +
    4.37 +- (BOOL)isEqualToCertificate:(MYCertificate*)cert {
    4.38 +    return [self isEqual: cert] || [self.certificateData isEqual: cert.certificateData];
    4.39 +}
    4.40 +
    4.41 +
    4.42  + (MYCertificate*) preferredCertificateForName: (NSString*)name {
    4.43      SecCertificateRef certRef = NULL;
    4.44      if (!check(SecCertificateCopyPreference((CFStringRef)name, 0, &certRef),
    4.45 @@ -61,6 +82,7 @@
    4.46                   @"SecCertificateSetPreference");
    4.47  }
    4.48  
    4.49 +
    4.50  @synthesize certificateRef=_certificateRef;
    4.51  
    4.52  - (NSData*) certificateData {
    4.53 @@ -98,7 +120,133 @@
    4.54  }
    4.55  
    4.56  
    4.57 +#pragma mark -
    4.58 +#pragma mark TRUST/POLICY STUFF:
    4.59 +
    4.60 +
    4.61 ++ (SecPolicyRef) policyForOID: (CSSM_OID) policyOID {
    4.62 +    SecPolicySearchRef search;
    4.63 +    if (!check(SecPolicySearchCreate(CSSM_CERT_X_509v3, &policyOID, NULL, &search),
    4.64 +           @"SecPolicySearchCreate"))
    4.65 +        return nil;
    4.66 +    SecPolicyRef policy = NULL;
    4.67 +    if (!check(SecPolicySearchCopyNext(search, &policy), @"SecPolicySearchCopyNext"))
    4.68 +        policy = NULL;
    4.69 +    CFRelease(search);
    4.70 +    return policy;
    4.71 +}
    4.72 +
    4.73 ++ (SecPolicyRef) X509Policy {
    4.74 +    static SecPolicyRef sX509Policy = NULL;
    4.75 +    if (!sX509Policy)
    4.76 +        sX509Policy = [self policyForOID: CSSMOID_APPLE_X509_BASIC];
    4.77 +    return sX509Policy;
    4.78 +}
    4.79 +
    4.80 ++ (SecPolicyRef) SSLPolicy {
    4.81 +    static SecPolicyRef sSSLPolicy = NULL;
    4.82 +    if (!sSSLPolicy)
    4.83 +        sSSLPolicy = [self policyForOID: CSSMOID_APPLE_TP_SSL];
    4.84 +    return sSSLPolicy;
    4.85 +}
    4.86 +
    4.87 ++ (SecPolicyRef) SMIMEPolicy {
    4.88 +    static SecPolicyRef sSMIMEPolicy = NULL;
    4.89 +    if (!sSMIMEPolicy)
    4.90 +        sSMIMEPolicy = [self policyForOID: CSSMOID_APPLE_TP_SMIME];
    4.91 +    return sSMIMEPolicy;
    4.92 +}
    4.93 +
    4.94 +
    4.95 +- (CSSM_CERT_TYPE) certificateType {
    4.96 +    CSSM_CERT_TYPE type = CSSM_CERT_UNKNOWN;
    4.97 +    if (!check(SecCertificateGetType(_certificateRef, &type), @"SecCertificateGetType"))
    4.98 +        type = CSSM_CERT_UNKNOWN;
    4.99 +    return type;
   4.100 +}
   4.101 +
   4.102 +- (NSArray*) trustSettings {
   4.103 +    CFArrayRef settings = NULL;
   4.104 +    OSStatus err = SecTrustSettingsCopyTrustSettings(_certificateRef, kSecTrustSettingsDomainUser, 
   4.105 +                                                     &settings);
   4.106 +    if (err == errSecItemNotFound || !check(err,@"SecTrustSettingsCopyTrustSettings") || !settings)
   4.107 +        return nil;
   4.108 +    return [(id)CFMakeCollectable(settings) autorelease];
   4.109 +}
   4.110 +        
   4.111 +
   4.112 +- (BOOL) setUserTrust: (SecTrustUserSetting)trustSetting
   4.113 +{
   4.114 +    if (trustSetting == kSecTrustResultProceed) {
   4.115 +        return check(SecTrustSettingsSetTrustSettings(_certificateRef, 
   4.116 +                                                      kSecTrustSettingsDomainUser, nil),
   4.117 +                     @"SecTrustSettingsSetTrustSettings");
   4.118 +    } else if (trustSetting == kSecTrustResultDeny) {
   4.119 +        OSStatus err = SecTrustSettingsRemoveTrustSettings(_certificateRef, 
   4.120 +                                                           kSecTrustSettingsDomainUser);
   4.121 +        return err == errSecItemNotFound || check(err, @"SecTrustSettingsRemoveTrustSettings");
   4.122 +    } else
   4.123 +        return paramErr;
   4.124 +}
   4.125 +
   4.126 +
   4.127  @end
   4.128  
   4.129  
   4.130 +NSString* MYPolicyGetName( SecPolicyRef policy ) {
   4.131 +    if (!policy)
   4.132 +        return @"(null)";
   4.133 +    CSSM_OID oid = {};
   4.134 +    SecPolicyGetOID(policy, &oid);
   4.135 +    return $sprintf(@"SecPolicy[%@]", OIDAsString(oid));
   4.136 +}
   4.137 +
   4.138 +NSString* MYTrustResultDescribe( SecTrustResultType result ) {
   4.139 +    static NSString* const kTrustResultNames[kSecTrustResultOtherError+1] = {
   4.140 +        @"Invalid",
   4.141 +        @"Proceed",
   4.142 +        @"Confirm",
   4.143 +        @"Deny",
   4.144 +        @"Unspecified",
   4.145 +        @"RecoverableTrustFailure",
   4.146 +        @"FatalTrustFailure",
   4.147 +        @"OtherError"
   4.148 +    };
   4.149 +    if (result>=0 && result <=kSecTrustResultOtherError)
   4.150 +        return kTrustResultNames[result];
   4.151 +    else
   4.152 +        return $sprintf(@"(Unknown trust result %i)", result);
   4.153 +}
   4.154 +
   4.155 +
   4.156 +NSString* MYTrustDescribe( SecTrustRef trust ) {
   4.157 +    SecTrustResultType result;
   4.158 +    CFArrayRef certChain=NULL;
   4.159 +    CSSM_TP_APPLE_EVIDENCE_INFO* statusChain;
   4.160 +    OSStatus err = SecTrustGetResult(trust, &result, &certChain, &statusChain);
   4.161 +    NSString *desc;
   4.162 +    if (err)
   4.163 +        desc = $sprintf(@"SecTrust[%p, err=%@]", trust, MYErrorName(NSOSStatusErrorDomain, err));
   4.164 +    else
   4.165 +        desc = $sprintf(@"SecTrust[%@, %u in chain]", 
   4.166 +                        MYTrustResultDescribe(result),
   4.167 +                        CFArrayGetCount(certChain));
   4.168 +    if (certChain) CFRelease(certChain);
   4.169 +    return desc;
   4.170 +}
   4.171 +
   4.172 +
   4.173 +
   4.174 +TestCase(Trust) {
   4.175 +    Log(@"X.509 policy = %@", MYPolicyGetName([MYCertificate X509Policy]));
   4.176 +    Log(@"  SSL policy = %@", MYPolicyGetName([MYCertificate SSLPolicy]));
   4.177 +    Log(@"SMIME policy = %@", MYPolicyGetName([MYCertificate SMIMEPolicy]));
   4.178 +    for (MYCertificate *cert in [[MYKeychain defaultKeychain] enumerateCertificates]) {
   4.179 +        NSArray *settings = cert.trustSettings;
   4.180 +        if (settings)
   4.181 +            Log(@"---- %@ = %@", cert, settings);
   4.182 +    }
   4.183 +}
   4.184 +
   4.185 +
   4.186  #endif !MYCRYPTO_USE_IPHONE_API
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/MYCrypto+Cocoa.h	Sun Apr 12 22:02:20 2009 -0700
     5.3 @@ -0,0 +1,27 @@
     5.4 +//
     5.5 +//  MYCrypto+Cocoa.h
     5.6 +//  MYCrypto
     5.7 +//
     5.8 +//  Created by Jens Alfke on 4/10/09.
     5.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
    5.10 +//
    5.11 +
    5.12 +#import <SecurityInterface/SFChooseIdentityPanel.h>
    5.13 +@class MYIdentity;
    5.14 +
    5.15 +
    5.16 +@interface SFChooseIdentityPanel (MYCrypto)
    5.17 +
    5.18 +- (NSInteger)my_runModalForIdentities:(NSArray *)myIdentitObjects
    5.19 +                              message:(NSString *)message;
    5.20 +
    5.21 +- (void)my_beginSheetForWindow:(NSWindow *)docWindow 
    5.22 +                 modalDelegate:(id)delegate 
    5.23 +                didEndSelector:(SEL)didEndSelector
    5.24 +                   contextInfo:(void *)contextInfo 
    5.25 +                    identities:(NSArray *)myIdentitObjects 
    5.26 +                       message:(NSString *)message;
    5.27 +
    5.28 +- (MYIdentity*) my_identity;
    5.29 +
    5.30 +@end
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/MYCrypto+Cocoa.m	Sun Apr 12 22:02:20 2009 -0700
     6.3 @@ -0,0 +1,48 @@
     6.4 +//
     6.5 +//  MYCrypto+Cocoa.m
     6.6 +//  MYCrypto
     6.7 +//
     6.8 +//  Created by Jens Alfke on 4/10/09.
     6.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
    6.10 +//
    6.11 +
    6.12 +#import "MYCrypto+Cocoa.h"
    6.13 +#import "MYCrypto_Private.h"
    6.14 +#import "MYIdentity.h"
    6.15 +
    6.16 +
    6.17 +@implementation SFChooseIdentityPanel (MYCrypto)
    6.18 +
    6.19 +
    6.20 +- (NSInteger)my_runModalForIdentities:(NSArray *)identities 
    6.21 +                              message:(NSString *)message
    6.22 +{
    6.23 +    NSMutableArray *identityRefs = $marray();
    6.24 +    for (MYIdentity *ident in identities)
    6.25 +        [identityRefs addObject: (id)ident.identityRef];
    6.26 +    return [self runModalForIdentities: identityRefs message: message];
    6.27 +}
    6.28 +
    6.29 +- (void)my_beginSheetForWindow:(NSWindow *)docWindow 
    6.30 +                 modalDelegate:(id)delegate 
    6.31 +                didEndSelector:(SEL)didEndSelector
    6.32 +                   contextInfo:(void *)contextInfo 
    6.33 +                    identities:(NSArray *)identities 
    6.34 +                       message:(NSString *)message
    6.35 +{
    6.36 +    NSMutableArray *identityRefs = $marray();
    6.37 +    for (MYIdentity *ident in identities)
    6.38 +        [identityRefs addObject: (id)ident.identityRef];
    6.39 +    [self beginSheetForWindow:docWindow 
    6.40 +                modalDelegate:delegate 
    6.41 +               didEndSelector:didEndSelector
    6.42 +                  contextInfo:contextInfo 
    6.43 +                   identities:identityRefs
    6.44 +                      message:message];
    6.45 +}
    6.46 +
    6.47 +- (MYIdentity*) my_identity {
    6.48 +    return [MYIdentity identityWithIdentityRef: [self identity]];
    6.49 +}
    6.50 +
    6.51 +@end
     7.1 --- a/MYCrypto-iPhone.xcodeproj/project.pbxproj	Thu Apr 09 22:47:11 2009 -0700
     7.2 +++ b/MYCrypto-iPhone.xcodeproj/project.pbxproj	Sun Apr 12 22:02:20 2009 -0700
     7.3 @@ -22,7 +22,7 @@
     7.4  		276FB34B0F856CA400CB326E /* MYCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = 276FB34A0F856CA400CB326E /* MYCertificate.m */; };
     7.5  		27A430120F87C6C10063D362 /* MYKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E823110F81D56E0019BE60 /* MYKey.m */; };
     7.6  		27A430140F87C6D50063D362 /* MYSymmetricKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A430130F87C6D50063D362 /* MYSymmetricKey.m */; };
     7.7 -		27E823210F81D56E0019BE60 /* MYCryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E8230F0F81D56E0019BE60 /* MYCryptor.m */; };
     7.8 +		27E3A6AA0F91B8B30079D4D9 /* MYCryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E3A6A70F91B8B30079D4D9 /* MYCryptor.m */; };
     7.9  		27E823240F81D56E0019BE60 /* MYKeychainItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E823150F81D56E0019BE60 /* MYKeychainItem.m */; };
    7.10  		27E823270F81D56E0019BE60 /* MYDigest.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E8231C0F81D56E0019BE60 /* MYDigest.m */; };
    7.11  		27E823290F81D56E0019BE60 /* MYCrypto.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 27E8231E0F81D56E0019BE60 /* MYCrypto.xcconfig */; };
    7.12 @@ -58,9 +58,9 @@
    7.13  		27A430130F87C6D50063D362 /* MYSymmetricKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYSymmetricKey.m; sourceTree = "<group>"; };
    7.14  		27A430150F87C6DB0063D362 /* MYSymmetricKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYSymmetricKey.h; sourceTree = "<group>"; };
    7.15  		27AAD9710F8927DB0064DD7C /* MYCryptoConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCryptoConfig.h; sourceTree = "<group>"; };
    7.16 +		27E3A6A60F91B8B30079D4D9 /* MYCryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCryptor.h; sourceTree = "<group>"; };
    7.17 +		27E3A6A70F91B8B30079D4D9 /* MYCryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYCryptor.m; sourceTree = "<group>"; };
    7.18  		27E8230C0F81D56E0019BE60 /* MYCertificate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCertificate.h; sourceTree = "<group>"; };
    7.19 -		27E8230E0F81D56E0019BE60 /* MYCryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCryptor.h; sourceTree = "<group>"; };
    7.20 -		27E8230F0F81D56E0019BE60 /* MYCryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYCryptor.m; sourceTree = "<group>"; };
    7.21  		27E823100F81D56E0019BE60 /* MYKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYKey.h; sourceTree = "<group>"; };
    7.22  		27E823110F81D56E0019BE60 /* MYKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYKey.m; sourceTree = "<group>"; };
    7.23  		27E823120F81D56E0019BE60 /* MYKeychain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYKeychain.h; sourceTree = "<group>"; };
    7.24 @@ -125,6 +125,15 @@
    7.25  			name = Products;
    7.26  			sourceTree = "<group>";
    7.27  		};
    7.28 +		27E3A6A10F91B8B30079D4D9 /* Encryption */ = {
    7.29 +			isa = PBXGroup;
    7.30 +			children = (
    7.31 +				27E3A6A60F91B8B30079D4D9 /* MYCryptor.h */,
    7.32 +				27E3A6A70F91B8B30079D4D9 /* MYCryptor.m */,
    7.33 +			);
    7.34 +			name = Encryption;
    7.35 +			sourceTree = "<group>";
    7.36 +		};
    7.37  		27E8230B0F81D56E0019BE60 /* Source */ = {
    7.38  			isa = PBXGroup;
    7.39  			children = (
    7.40 @@ -132,8 +141,6 @@
    7.41  				27E8230C0F81D56E0019BE60 /* MYCertificate.h */,
    7.42  				276FB34A0F856CA400CB326E /* MYCertificate.m */,
    7.43  				273391CC0F81EC70009414D9 /* MYCertificate-iPhone.m */,
    7.44 -				27E8230E0F81D56E0019BE60 /* MYCryptor.h */,
    7.45 -				27E8230F0F81D56E0019BE60 /* MYCryptor.m */,
    7.46  				27059CF10F8F0F8E00A8422F /* MYIdentity.h */,
    7.47  				27059CF20F8F0F9200A8422F /* MYIdentity.m */,
    7.48  				27E823100F81D56E0019BE60 /* MYKey.h */,
    7.49 @@ -182,6 +189,7 @@
    7.50  			isa = PBXGroup;
    7.51  			children = (
    7.52  				27E8230B0F81D56E0019BE60 /* Source */,
    7.53 +				27E3A6A10F91B8B30079D4D9 /* Encryption */,
    7.54  				27E8232B0F81D5760019BE60 /* MYUtilities */,
    7.55  				080E96DDFE201D6D7F000001 /* iPhone */,
    7.56  				29B97323FDCFA39411CA2CEA /* Frameworks */,
    7.57 @@ -259,7 +267,6 @@
    7.58  			files = (
    7.59  				1D60589B0D05DD56006BFB54 /* MYCrypto_iPhone_main.m in Sources */,
    7.60  				1D3623260D0F684500981E51 /* MYCrypto_iPhoneAppDelegate.m in Sources */,
    7.61 -				27E823210F81D56E0019BE60 /* MYCryptor.m in Sources */,
    7.62  				27E823240F81D56E0019BE60 /* MYKeychainItem.m in Sources */,
    7.63  				27E823270F81D56E0019BE60 /* MYDigest.m in Sources */,
    7.64  				27E823370F81D5760019BE60 /* CollectionUtils.m in Sources */,
    7.65 @@ -278,6 +285,7 @@
    7.66  				27FE453F0F87CC8500A86D63 /* MYKey-iPhone.m in Sources */,
    7.67  				2748607F0F8D5E0600FE617B /* MYPrivateKey.m in Sources */,
    7.68  				27059CF30F8F0F9200A8422F /* MYIdentity.m in Sources */,
    7.69 +				27E3A6AA0F91B8B30079D4D9 /* MYCryptor.m in Sources */,
    7.70  			);
    7.71  			runOnlyForDeploymentPostprocessing = 0;
    7.72  		};
     8.1 --- a/MYCrypto.xcodeproj/project.pbxproj	Thu Apr 09 22:47:11 2009 -0700
     8.2 +++ b/MYCrypto.xcodeproj/project.pbxproj	Sun Apr 12 22:02:20 2009 -0700
     8.3 @@ -7,6 +7,10 @@
     8.4  	objects = {
     8.5  
     8.6  /* Begin PBXBuildFile section */
     8.7 +		27059D530F8F9BB500A8422F /* MYEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 27059D500F8F9BB500A8422F /* MYEncoder.m */; };
     8.8 +		27059D770F8FA23100A8422F /* MYCrypto+Cocoa.m in Sources */ = {isa = PBXBuildFile; fileRef = 27059D760F8FA23100A8422F /* MYCrypto+Cocoa.m */; };
     8.9 +		27059D840F8FA82500A8422F /* SecurityInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27059D830F8FA82500A8422F /* SecurityInterface.framework */; };
    8.10 +		27059DE50F8FAF6500A8422F /* MYDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 27059D520F8F9BB500A8422F /* MYDecoder.m */; };
    8.11  		270B879F0F8C565000C56781 /* MYPrivateKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 270B879E0F8C565000C56781 /* MYPrivateKey.m */; };
    8.12  		274861D50F8E4B5200FE617B /* MYCertGen.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A42ECD0F8689D30063D362 /* MYCertGen.m */; };
    8.13  		274863A20F8EF39400FE617B /* MYIdentity.m in Sources */ = {isa = PBXBuildFile; fileRef = 274863A10F8EF39400FE617B /* MYIdentity.m */; };
    8.14 @@ -44,6 +48,13 @@
    8.15  
    8.16  /* Begin PBXFileReference section */
    8.17  		08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
    8.18 +		27059D4F0F8F9BB500A8422F /* MYEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYEncoder.h; sourceTree = "<group>"; };
    8.19 +		27059D500F8F9BB500A8422F /* MYEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYEncoder.m; sourceTree = "<group>"; };
    8.20 +		27059D510F8F9BB500A8422F /* MYDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYDecoder.h; sourceTree = "<group>"; };
    8.21 +		27059D520F8F9BB500A8422F /* MYDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYDecoder.m; sourceTree = "<group>"; };
    8.22 +		27059D750F8FA23100A8422F /* MYCrypto+Cocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MYCrypto+Cocoa.h"; sourceTree = "<group>"; };
    8.23 +		27059D760F8FA23100A8422F /* MYCrypto+Cocoa.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MYCrypto+Cocoa.m"; sourceTree = "<group>"; };
    8.24 +		27059D830F8FA82500A8422F /* SecurityInterface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SecurityInterface.framework; path = /System/Library/Frameworks/SecurityInterface.framework; sourceTree = "<absolute>"; };
    8.25  		270B879D0F8C565000C56781 /* MYPrivateKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYPrivateKey.h; sourceTree = "<group>"; };
    8.26  		270B879E0F8C565000C56781 /* MYPrivateKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYPrivateKey.m; sourceTree = "<group>"; };
    8.27  		2748604D0F8D5C4C00FE617B /* MYCrypto_Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = MYCrypto_Release.xcconfig; sourceTree = "<group>"; };
    8.28 @@ -98,6 +109,7 @@
    8.29  				8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */,
    8.30  				27CFF51F0F7E94AE000B418E /* Security.framework in Frameworks */,
    8.31  				27E820720F7EA6260019BE60 /* CoreServices.framework in Frameworks */,
    8.32 +				27059D840F8FA82500A8422F /* SecurityInterface.framework in Frameworks */,
    8.33  			);
    8.34  			runOnlyForDeploymentPostprocessing = 0;
    8.35  		};
    8.36 @@ -108,6 +120,7 @@
    8.37  			isa = PBXGroup;
    8.38  			children = (
    8.39  				08FB7795FE84155DC02AAC07 /* Source */,
    8.40 +				27059D4E0F8F9B9E00A8422F /* Encryption */,
    8.41  				274861440F8D757600FE617B /* Certificate Generation */,
    8.42  				270B881C0F8D055A00C56781 /* Internal */,
    8.43  				27CFF4CC0F7E86E8000B418E /* MYUtilities */,
    8.44 @@ -116,6 +129,7 @@
    8.45  				1AB674ADFE9D54B511CA2CBB /* Products */,
    8.46  				27CFF51E0F7E94AE000B418E /* Security.framework */,
    8.47  				27E820710F7EA6260019BE60 /* CoreServices.framework */,
    8.48 +				27059D830F8FA82500A8422F /* SecurityInterface.framework */,
    8.49  			);
    8.50  			name = MYCrypto;
    8.51  			sourceTree = "<group>";
    8.52 @@ -125,8 +139,6 @@
    8.53  			children = (
    8.54  				27CFF4B10F7E8535000B418E /* MYCertificate.h */,
    8.55  				27CFF4B20F7E8535000B418E /* MYCertificate.m */,
    8.56 -				27CFF4B30F7E8535000B418E /* MYCryptor.h */,
    8.57 -				27CFF4B40F7E8535000B418E /* MYCryptor.m */,
    8.58  				27CFF4BF0F7E8535000B418E /* MYDigest.h */,
    8.59  				27CFF4C00F7E8535000B418E /* MYDigest.m */,
    8.60  				274863A00F8EF39400FE617B /* MYIdentity.h */,
    8.61 @@ -145,6 +157,8 @@
    8.62  				27A42D410F858ED80063D362 /* MYSymmetricKey.m */,
    8.63  				27AAD97B0F892A0D0064DD7C /* MYCryptoConfig.h */,
    8.64  				27EAF0390F8B2D700091AF95 /* README.textile */,
    8.65 +				27059D750F8FA23100A8422F /* MYCrypto+Cocoa.h */,
    8.66 +				27059D760F8FA23100A8422F /* MYCrypto+Cocoa.m */,
    8.67  			);
    8.68  			name = Source;
    8.69  			sourceTree = "<group>";
    8.70 @@ -165,6 +179,19 @@
    8.71  			name = Products;
    8.72  			sourceTree = "<group>";
    8.73  		};
    8.74 +		27059D4E0F8F9B9E00A8422F /* Encryption */ = {
    8.75 +			isa = PBXGroup;
    8.76 +			children = (
    8.77 +				27059D4F0F8F9BB500A8422F /* MYEncoder.h */,
    8.78 +				27059D500F8F9BB500A8422F /* MYEncoder.m */,
    8.79 +				27059D510F8F9BB500A8422F /* MYDecoder.h */,
    8.80 +				27059D520F8F9BB500A8422F /* MYDecoder.m */,
    8.81 +				27CFF4B30F7E8535000B418E /* MYCryptor.h */,
    8.82 +				27CFF4B40F7E8535000B418E /* MYCryptor.m */,
    8.83 +			);
    8.84 +			name = Encryption;
    8.85 +			sourceTree = "<group>";
    8.86 +		};
    8.87  		270B881C0F8D055A00C56781 /* Internal */ = {
    8.88  			isa = PBXGroup;
    8.89  			children = (
    8.90 @@ -263,7 +290,7 @@
    8.91  			);
    8.92  			runOnlyForDeploymentPostprocessing = 0;
    8.93  			shellPath = /bin/csh;
    8.94 -			shellScript = "doxygen\n";
    8.95 +			shellScript = "doxygen |& sed s/Warning/warning/\n";
    8.96  			showEnvVarsInLog = 0;
    8.97  		};
    8.98  /* End PBXShellScriptBuildPhase section */
    8.99 @@ -291,6 +318,9 @@
   8.100  				270B879F0F8C565000C56781 /* MYPrivateKey.m in Sources */,
   8.101  				274861D50F8E4B5200FE617B /* MYCertGen.m in Sources */,
   8.102  				274863A20F8EF39400FE617B /* MYIdentity.m in Sources */,
   8.103 +				27059D530F8F9BB500A8422F /* MYEncoder.m in Sources */,
   8.104 +				27059D770F8FA23100A8422F /* MYCrypto+Cocoa.m in Sources */,
   8.105 +				27059DE50F8FAF6500A8422F /* MYDecoder.m in Sources */,
   8.106  			);
   8.107  			runOnlyForDeploymentPostprocessing = 0;
   8.108  		};
     9.1 --- a/MYCryptoTest.m	Thu Apr 09 22:47:11 2009 -0700
     9.2 +++ b/MYCryptoTest.m	Sun Apr 12 22:02:20 2009 -0700
     9.3 @@ -11,6 +11,7 @@
     9.4  #import "MYKeychain.h"
     9.5  #import "MYDigest.h"
     9.6  #import "MYIdentity.h"
     9.7 +#import "MYCrypto+Cocoa.h"
     9.8  #import "MYCrypto_Private.h"
     9.9  
    9.10  
    9.11 @@ -78,7 +79,7 @@
    9.12      Log(@"Enumerator = %@", e);
    9.13      CAssert(e);
    9.14      for (MYIdentity *ident in e) {
    9.15 -        Log(@"Found %@ -- name=%@, emails=(%@), key=%@",
    9.16 +        Log(@"Found %@\n\tcommonName=%@\n\temails=(%@)\n\tkey=%@",
    9.17              ident, ident.commonName, 
    9.18  #if TARGET_OS_IPHONE
    9.19              nil,
    9.20 @@ -172,33 +173,63 @@
    9.21  #pragma mark KEY-PAIRS:
    9.22  
    9.23  
    9.24 -TestCase(MYPrivateKey) {
    9.25 -    RequireTestCase(MYKeychain);
    9.26 -    
    9.27 -    Log(@"Generating key pair...");
    9.28 -    MYPrivateKey *pair = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 512];
    9.29 -    Log(@"...created { %@ , %@ }.", pair, pair.publicKey);
    9.30 +static void TestUseKeyPair(MYPrivateKey *pair) {
    9.31 +    Log(@"---- TestUseKeyPair { %@ , %@ }.", pair, pair.publicKey);
    9.32      CAssert(pair);
    9.33      CAssert(pair.keyRef);
    9.34      MYPublicKey *publicKey = pair.publicKey;
    9.35      CAssert(publicKey.keyRef);
    9.36      
    9.37 +    NSData *pubKeyData = publicKey.keyData;
    9.38 +    Log(@"Public key = %@ (%u bytes)",pubKeyData,pubKeyData.length);
    9.39 +    CAssert(pubKeyData);
    9.40 +    
    9.41 +    MYSHA1Digest *pubKeyDigest = publicKey.publicKeyDigest;
    9.42 +    Log(@"Public key digest = %@",pubKeyDigest);
    9.43 +    CAssertEqual(pair.publicKeyDigest, pubKeyDigest);
    9.44 +    
    9.45 +    Log(@"SHA1 of pub key = %@", pubKeyData.my_SHA1Digest.asData);
    9.46 +    
    9.47 +    // Let's sign data:
    9.48 +    NSData *data = [@"This is a test. This is only a test!" dataUsingEncoding: NSUTF8StringEncoding];
    9.49 +    NSData *sig = [pair signData: data];
    9.50 +    Log(@"Signature = %@ (%u bytes)",sig,sig.length);
    9.51 +    CAssert(sig);
    9.52 +    CAssert( [publicKey verifySignature: sig ofData: data] );
    9.53 +    
    9.54 +    // Now let's encrypt...
    9.55 +    NSData *crypted = [publicKey encryptData: data];
    9.56 +    Log(@"Encrypted = %@ (%u bytes)",crypted,crypted.length);
    9.57 +    CAssert(crypted);
    9.58 +    CAssertEqual([pair decryptData: crypted], data);
    9.59 +    Log(@"Verified decryption.");
    9.60 +    
    9.61 +    // Test creating a standalone public key:
    9.62 +    MYPublicKey *pub = [[MYPublicKey alloc] initWithKeyRef: publicKey.keyRef];
    9.63 +    CAssert( [pub verifySignature: sig ofData: data] );
    9.64 +    Log(@"Verified signature.");
    9.65 +    
    9.66 +    // Test creating a public key from data:
    9.67 +    Log(@"Reconstituting public key from data...");
    9.68 +    pub = [[MYPublicKey alloc] initWithKeyData: pubKeyData];
    9.69 +    CAssert(pub);
    9.70 +    CAssertEqual(pub.keyData, pubKeyData);
    9.71 +    CAssertEqual(pub.publicKeyDigest, pubKeyDigest);
    9.72 +    CAssert( [pub verifySignature: sig ofData: data] );
    9.73 +    Log(@"Verified signature from reconstituted key.");
    9.74 +}
    9.75 +
    9.76 +
    9.77 +TestCase(MYGenerateKeyPair) {
    9.78 +    RequireTestCase(MYKeychain);
    9.79 +    
    9.80 +    Log(@"Generating key pair...");
    9.81 +    MYPrivateKey *pair = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 512];
    9.82 +    MYPublicKey *publicKey = pair.publicKey;
    9.83 +    Log(@"...created { %@ , %@ }.", pair, publicKey);
    9.84 +    
    9.85      @try{
    9.86 -        NSData *pubKeyData = publicKey.keyData;
    9.87 -        Log(@"Public key = %@ (%u bytes)",pubKeyData,pubKeyData.length);
    9.88 -        CAssert(pubKeyData);
    9.89 -        
    9.90 -        MYSHA1Digest *pubKeyDigest = publicKey.publicKeyDigest;
    9.91 -        Log(@"Public key digest = %@",pubKeyDigest);
    9.92 -        CAssertEqual(pair.publicKeyDigest, pubKeyDigest);
    9.93 -        
    9.94 -        Log(@"SHA1 of pub key = %@", pubKeyData.my_SHA1Digest.asData);
    9.95 -        
    9.96 -        NSData *data = [@"This is a test. This is only a test!" dataUsingEncoding: NSUTF8StringEncoding];
    9.97 -        NSData *sig = [pair signData: data];
    9.98 -        Log(@"Signature = %@ (%u bytes)",sig,sig.length);
    9.99 -        CAssert(sig);
   9.100 -        CAssert( [publicKey verifySignature: sig ofData: data] );
   9.101 +        TestUseKeyPair(pair);
   9.102          
   9.103          [pair setName: @"Test KeyPair Label"];
   9.104          CAssertEqual(pair.name, @"Test KeyPair Label");
   9.105 @@ -212,28 +243,6 @@
   9.106          CAssertEqual(pair.alias, @"TestCase@mooseyard.com");
   9.107          CAssertEqual(publicKey.alias, @"TestCase@mooseyard.com");
   9.108          
   9.109 -        // Test creating a standalone public key:
   9.110 -        MYPublicKey *pub = [[MYPublicKey alloc] initWithKeyRef: publicKey.keyRef];
   9.111 -        CAssert( [pub verifySignature: sig ofData: data] );
   9.112 -        Log(@"Verified signature.");
   9.113 -
   9.114 -        // Test creating a public key from data:
   9.115 -        Log(@"Reconstituting public key from data...");
   9.116 -        pub = [[MYPublicKey alloc] initWithKeyData: pubKeyData];
   9.117 -        CAssert(pub);
   9.118 -        CAssertEqual(pub.keyData, pubKeyData);
   9.119 -        CAssertEqual(pub.publicKeyDigest, pubKeyDigest);
   9.120 -        CAssert( [pub verifySignature: sig ofData: data] );
   9.121 -        Log(@"Verified signature from reconstituted key.");
   9.122 -                
   9.123 -        // Now let's encrypt...
   9.124 -        NSData *crypted = [pub encryptData: data];
   9.125 -        Log(@"Encrypted = %@ (%u bytes)",crypted,crypted.length);
   9.126 -        CAssert(crypted);
   9.127 -        
   9.128 -        CAssertEqual([pair decryptData: crypted], data);
   9.129 -        Log(@"Verified decryption.");
   9.130 -        
   9.131          CAssert([pair removeFromKeychain]);
   9.132          Log(@"Removed key-pair.");
   9.133          pair = nil;
   9.134 @@ -249,6 +258,24 @@
   9.135  }
   9.136  
   9.137  
   9.138 +TestCase(MYUseIdentity) {
   9.139 +    MYIdentity *me = nil;//[MYIdentity preferredIdentityForName: @"MYCryptoTest"];
   9.140 +    if (!me) {
   9.141 +        NSArray *idents = [[[MYKeychain allKeychains] enumerateIdentities] allObjects];
   9.142 +        SFChooseIdentityPanel *panel = [SFChooseIdentityPanel sharedChooseIdentityPanel];
   9.143 +        [panel setAlternateButtonTitle: @"Cancel"];
   9.144 +        if ([panel my_runModalForIdentities: idents 
   9.145 +                                    message: @"Choose an identity for the MYEncoder test case:"]
   9.146 +            != NSOKButton) {
   9.147 +            [NSException raise: NSGenericException format: @"User canceled"];
   9.148 +        }
   9.149 +        me = [panel my_identity];
   9.150 +        [me makePreferredIdentityForName: @"MYCryptoTest"];
   9.151 +    }
   9.152 +    CAssert(me,@"No default identity has been set up in the Keychain");
   9.153 +    TestUseKeyPair(me.privateKey);
   9.154 +}
   9.155 +
   9.156  
   9.157  #pragma mark -
   9.158  #pragma mark KEYPAIR EXPORT:
    10.1 --- a/MYCrypto_Private.h	Thu Apr 09 22:47:11 2009 -0700
    10.2 +++ b/MYCrypto_Private.h	Sun Apr 12 22:02:20 2009 -0700
    10.3 @@ -114,6 +114,8 @@
    10.4  #undef check
    10.5  BOOL check(OSStatus err, NSString *what);
    10.6  
    10.7 +#define checksave(CALL) ({OSStatus err=(CALL); check(err,@""#CALL) || (_error=err, NO);})
    10.8 +
    10.9  #if !MYCRYPTO_USE_IPHONE_API
   10.10  BOOL checkcssm(CSSM_RETURN err, NSString *what);
   10.11  
   10.12 @@ -121,4 +123,7 @@
   10.13                      SecExternalItemType type,
   10.14                      SecKeychainRef keychain,
   10.15                      SecKeyImportExportParameters *params /*non-null*/);
   10.16 +
   10.17 +NSString* OIDAsString(CSSM_OID OID);
   10.18 +
   10.19  #endif
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/MYDecoder.h	Sun Apr 12 22:02:20 2009 -0700
    11.3 @@ -0,0 +1,170 @@
    11.4 +//
    11.5 +//  CryptoDecoder.h
    11.6 +//  Cloudy
    11.7 +//
    11.8 +//  Created by Jens Alfke on 1/16/08.
    11.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   11.10 +//
   11.11 +
   11.12 +#import <Cocoa/Cocoa.h>
   11.13 +#import <Security/CMSDecoder.h>
   11.14 +
   11.15 +@class MYKeychain, MYCertificate;
   11.16 +
   11.17 +
   11.18 +/** Decodes a CMS-formatted message into the original data, and identifies & verifies signatures. */
   11.19 +@interface MYDecoder : NSObject 
   11.20 +{
   11.21 +    @private
   11.22 +    CMSDecoderRef _decoder;
   11.23 +    OSStatus _error;
   11.24 +    SecPolicyRef _policy;
   11.25 +}
   11.26 +
   11.27 +/** Initializes a new decoder. */
   11.28 +- (id) init;
   11.29 +
   11.30 +/** Initializes a new decoder and reads the entire message data. */
   11.31 +- (id) initWithData: (NSData*)data error: (NSError**)outError;
   11.32 +
   11.33 +/** Specifies a keychain to use to look up certificates and keys, instead of the default
   11.34 +    keychain search path. */
   11.35 +- (BOOL) useKeychain: (MYKeychain*)keychain;
   11.36 +
   11.37 +/** Adds data to the decoder. You can add the entire data at once, or in bits and pieces
   11.38 +    (if you're reading it from a stream). */
   11.39 +- (BOOL) addData: (NSData*)data;
   11.40 +
   11.41 +/** The error, if any, that occurred while decoding the content.
   11.42 +    If -addData: returns NO, read this property to find out what went wrong.
   11.43 +    The most likely error is (NSOSStatusErrorDomain, errSecUnknownFormat). */
   11.44 +@property (readonly) NSError *error;
   11.45 +
   11.46 +/** If the message content is detached (stored separately from the encoded message),
   11.47 +    you must copy it to this property before calling -finish, so that the decoder can use it
   11.48 +    to verify signatures. */
   11.49 +@property (retain) NSData *detachedContent;
   11.50 +
   11.51 +/** Tells the decoder that all of the data has been read, after the last call to -addData:. 
   11.52 +    You must call this before accessing the message content or metadata. */
   11.53 +- (BOOL) finish;
   11.54 +
   11.55 +/** The decoded message content. */
   11.56 +@property (readonly) NSData* content;
   11.57 +
   11.58 +/** YES if the message was signed. (Use the signers property to see who signed it.) */
   11.59 +@property (readonly) BOOL isSigned;
   11.60 +
   11.61 +/** YES if the message was encrypted. */
   11.62 +@property (readonly) BOOL isEncrypted;
   11.63 +
   11.64 +/** An array of MYSigner objects representing the identities who signed the message.
   11.65 +    Nil if the message is unsigned. */
   11.66 +@property (readonly) NSArray* signers;
   11.67 + 
   11.68 +/** All of the certificates (as MYCertificate objects) that were attached to the message. */
   11.69 +@property (readonly) NSArray* certificates;
   11.70 +
   11.71 +
   11.72 +/** @name Expert
   11.73 + *  Advanced methods. 
   11.74 + */
   11.75 +//@{
   11.76 +
   11.77 +/** The X.509 content-type of the message contents.
   11.78 +    The Data field points to autoreleased memory: do not free it yourself, and do not
   11.79 +    expect it to remain valid after the calling method returns. */
   11.80 +@property (readonly) CSSM_OID contentType;
   11.81 +
   11.82 +/** The Policy that will be used to evaluate trust when calling MYSigner.copyTrust.
   11.83 +    NULL by default. */
   11.84 +@property (assign) SecPolicyRef policy;
   11.85 +
   11.86 +/** Returns a string with detailed information about the message metadata.
   11.87 +    Not user-presentable; used for debugging. */
   11.88 +- (NSString*) dump;
   11.89 +
   11.90 +//@}
   11.91 +
   11.92 +@end
   11.93 +
   11.94 +
   11.95 +/** Represents a signer of a CMS message, as returned by the MYDecoder.signers property. */
   11.96 +@interface MYSigner : NSObject
   11.97 +{
   11.98 +    @private
   11.99 +    CMSDecoderRef _decoder;
  11.100 +    size_t _index;
  11.101 +    CFTypeRef _policy;
  11.102 +    CMSSignerStatus _status;
  11.103 +    OSStatus _verifyResult;
  11.104 +    SecTrustRef _trust;
  11.105 +}
  11.106 +
  11.107 +/** The status of the signature, i.e. whether it's valid or not.
  11.108 + *  Values include:
  11.109 + *	  kCMSSignerValid               :both signature and signer certificate verified OK.
  11.110 + *	  kCMSSignerNeedsDetachedContent:the MYDecoder's detachedContent property must be set,
  11.111 + *                                   to ascertain the signature status.
  11.112 + *	  kCMSSignerInvalidSignature    :bad signature -- either the content or the signature
  11.113 + *                                   data were tampered with after the message was encoded.
  11.114 + *	  kCMSSignerInvalidCert         :an error occurred verifying the signer's certificate.
  11.115 + *							         Further information available via the verifyResult
  11.116 + *                                   and copyTrust methods.
  11.117 + */
  11.118 +@property (readonly) CMSSignerStatus status;
  11.119 +
  11.120 +/** The signer's certificate.
  11.121 +    You should check the status property first, to see whether the signature and certificate
  11.122 +    are valid.
  11.123 +    For safety purposes, if you haven't checked status yet, this method will return nil
  11.124 +    if the signer status is not kCMSSignerValid. */
  11.125 +@property (readonly) MYCertificate *certificate;
  11.126 +
  11.127 +/** The signer's email address (if any), as stored in the certificate. */
  11.128 +@property (readonly) NSString* emailAddress;
  11.129 +
  11.130 +/** @name Expert
  11.131 + *  Advanced methods. 
  11.132 + */
  11.133 +//@{
  11.134 +
  11.135 +/** Returns the SecTrustRef that was used to verify the certificate.
  11.136 +    You can use this object to get more detailed information about how the verification was done.
  11.137 +    If you set the parent decoder's policy property, then that SecPolicy will be used to evaluate
  11.138 +    trust; otherwise you'll need to do it yourself using the SecTrust object.
  11.139 +    You must CFRelease the result when you're finished with it. */
  11.140 +- (SecTrustRef) trust;
  11.141 +
  11.142 +/** The result of certificate verification, as a CSSM_RESULT code; 
  11.143 + *  a nonzero value indicates an error.
  11.144 + *
  11.145 + * Some of the most common and interesting errors are:
  11.146 + *
  11.147 + * CSSMERR_TP_INVALID_ANCHOR_CERT : The cert was verified back to a 
  11.148 + *		self-signed (root) cert which was present in the message, but 
  11.149 + *		that root cert is not a known, trusted root cert. 
  11.150 + * CSSMERR_TP_NOT_TRUSTED: The cert could not be verified back to 
  11.151 + *		a root cert.
  11.152 + * CSSMERR_TP_VERIFICATION_FAILURE: A root cert was found which does
  11.153 + *   	not self-verify. 
  11.154 + * CSSMERR_TP_VERIFY_ACTION_FAILED: Indicates a failure of the requested 
  11.155 + *		policy action. 
  11.156 + * CSSMERR_TP_INVALID_CERTIFICATE: Indicates a bad leaf cert. 
  11.157 + * CSSMERR_TP_CERT_EXPIRED: A cert in the chain was expired at the time of
  11.158 + *		verification.
  11.159 + * CSSMERR_TP_CERT_NOT_VALID_YET: A cert in the chain was not yet valie at 
  11.160 + *		the time of	verification.
  11.161 + */
  11.162 +@property (readonly) OSStatus verifyResult;
  11.163 +
  11.164 +//@}
  11.165 +
  11.166 +@end
  11.167 +
  11.168 +
  11.169 +enum {
  11.170 +    /** Returned from MYSigner.status to indicate a failure (non-noErr return value)
  11.171 +     of the underlying CMSDecoderCopySignerStatus call. Should never occur. */
  11.172 +    kMYSignerStatusCheckFailed = 666
  11.173 +};
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/MYDecoder.m	Sun Apr 12 22:02:20 2009 -0700
    12.3 @@ -0,0 +1,389 @@
    12.4 +//
    12.5 +//  MYDecoder.m
    12.6 +//  Cloudy
    12.7 +//
    12.8 +//  Created by Jens Alfke on 1/16/08.
    12.9 +//  Copyright 2008 Jens Alfke. All rights reserved.
   12.10 +//
   12.11 +
   12.12 +#import "MYDecoder.h"
   12.13 +#import "MYCrypto_Private.h"
   12.14 +#import "Test.h"
   12.15 +#import "MYErrorUtils.h"
   12.16 +
   12.17 +
   12.18 +@interface MYSigner ()
   12.19 +- (id) initWithDecoder: (CMSDecoderRef)decoder index: (size_t)index policy: (CFTypeRef)policy;
   12.20 +@end
   12.21 +
   12.22 +
   12.23 +@implementation MYDecoder
   12.24 +
   12.25 +
   12.26 +- (id) initWithData: (NSData*)data error: (NSError**)outError
   12.27 +{
   12.28 +    self = [self init];
   12.29 +    if( self ) {
   12.30 +        [self addData: data];
   12.31 +        [self finish];
   12.32 +        *outError = self.error;
   12.33 +        if( *outError ) {
   12.34 +            [self release];
   12.35 +            return nil;
   12.36 +        }
   12.37 +    }
   12.38 +    return self;
   12.39 +}
   12.40 +
   12.41 +- (id) init
   12.42 +{
   12.43 +    self = [super init];
   12.44 +    if (self != nil) {
   12.45 +        OSStatus err = CMSDecoderCreate(&_decoder);
   12.46 +        if( err ) {
   12.47 +            [self release];
   12.48 +            self = nil;
   12.49 +        }
   12.50 +    }
   12.51 +    return self;
   12.52 +}
   12.53 +
   12.54 +- (void) dealloc
   12.55 +{
   12.56 +    if( _decoder ) CFRelease(_decoder);
   12.57 +    if (_policy) CFRelease(_policy);
   12.58 +    [super dealloc];
   12.59 +}
   12.60 +
   12.61 +
   12.62 +- (BOOL) addData: (NSData*)data
   12.63 +{
   12.64 +    Assert(data);
   12.65 +    return !_error && checksave( CMSDecoderUpdateMessage(_decoder, data.bytes, data.length) );
   12.66 +}
   12.67 +
   12.68 +
   12.69 +- (BOOL) finish
   12.70 +{
   12.71 +    return !_error && checksave( CMSDecoderFinalizeMessage(_decoder) );
   12.72 +}
   12.73 +
   12.74 +- (NSError*) error
   12.75 +{
   12.76 +    if( _error )
   12.77 +        return MYError(_error, NSOSStatusErrorDomain, 
   12.78 +                       @"%@", MYErrorName(NSOSStatusErrorDomain,_error));
   12.79 +    else
   12.80 +        return nil;
   12.81 +}
   12.82 +
   12.83 +- (BOOL) useKeychain: (MYKeychain*)keychain
   12.84 +{
   12.85 +    return !_error && checksave( CMSDecoderSetSearchKeychain(_decoder, keychain.keychainRef) );
   12.86 +}
   12.87 +
   12.88 +
   12.89 +@synthesize policy=_policy;
   12.90 +
   12.91 +
   12.92 +- (NSData*) _dataFromFunction: (OSStatus (*)(CMSDecoderRef,CFDataRef*))function
   12.93 +{
   12.94 +    CFDataRef data=NULL;
   12.95 +    if( checksave( (*function)(_decoder, &data) ) )
   12.96 +       return [(NSData*)CFMakeCollectable(data) autorelease];
   12.97 +    else
   12.98 +        return nil;
   12.99 +}
  12.100 +
  12.101 +
  12.102 +- (NSData*) detachedContent
  12.103 +{
  12.104 +    return [self _dataFromFunction: &CMSDecoderCopyDetachedContent];
  12.105 +}
  12.106 +
  12.107 +- (void) setDetachedContent: (NSData*)detachedContent
  12.108 +{
  12.109 +    if( ! _error )
  12.110 +        checksave( CMSDecoderSetDetachedContent(_decoder, (CFDataRef)detachedContent) );
  12.111 +}
  12.112 +
  12.113 +- (CSSM_OID) contentType
  12.114 +{
  12.115 +    NSData *data = [self _dataFromFunction: &CMSDecoderCopyEncapsulatedContentType];
  12.116 +    return (CSSM_OID){data.length,(uint8*)data.bytes};      // safe since data is autoreleased
  12.117 +}
  12.118 +
  12.119 +- (NSData*) content
  12.120 +{
  12.121 +    return [self _dataFromFunction: &CMSDecoderCopyContent];
  12.122 +}
  12.123 +
  12.124 +- (BOOL) isSigned
  12.125 +{
  12.126 +    size_t n;
  12.127 +    return checksave( CMSDecoderGetNumSigners(_decoder, &n) ) && n > 0;
  12.128 +}
  12.129 +
  12.130 +- (BOOL) isEncrypted
  12.131 +{
  12.132 +    Boolean isEncrypted;
  12.133 +    return check(CMSDecoderIsContentEncrypted(_decoder,&isEncrypted), @"CMSDecoderIsContentEncrypted")
  12.134 +        && isEncrypted;
  12.135 +}
  12.136 +
  12.137 +- (NSArray*) signers
  12.138 +{
  12.139 +    size_t n;
  12.140 +    if( ! checksave( CMSDecoderGetNumSigners(_decoder, &n) ) )
  12.141 +        return nil;
  12.142 +    NSMutableArray *signers = [NSMutableArray arrayWithCapacity: n];
  12.143 +    for( size_t i=0; i<n; i++ ) {
  12.144 +        MYSigner *signer = [[MYSigner alloc] initWithDecoder: _decoder
  12.145 +                                                       index: i
  12.146 +                                                      policy: _policy];
  12.147 +        [signers addObject: signer];
  12.148 +        [signer release];
  12.149 +    }
  12.150 +    return signers;
  12.151 +}
  12.152 +
  12.153 +
  12.154 +- (NSArray*) certificates
  12.155 +{
  12.156 +    CFArrayRef certRefs = NULL;
  12.157 +    if( ! checksave( CMSDecoderCopyAllCerts(_decoder, &certRefs) ) || ! certRefs )
  12.158 +        return nil;
  12.159 +    unsigned n = CFArrayGetCount(certRefs);
  12.160 +    NSMutableArray *certs = [NSMutableArray arrayWithCapacity: n];
  12.161 +    for( unsigned i=0; i<n; i++ ) {
  12.162 +        SecCertificateRef certRef = (SecCertificateRef) CFArrayGetValueAtIndex(certRefs, i);
  12.163 +        [certs addObject: [MYCertificate certificateWithCertificateRef: certRef]];
  12.164 +    }
  12.165 +    CFRelease(certRefs);
  12.166 +    return certs;
  12.167 +}
  12.168 +
  12.169 +
  12.170 +- (NSString*) dump
  12.171 +{
  12.172 +    static const char * kStatusNames[kCMSSignerInvalidIndex+1] = {
  12.173 +        "kCMSSignerUnsigned", "kCMSSignerValid", "kCMSSignerNeedsDetachedContent",	
  12.174 +        "kCMSSignerInvalidSignature","kCMSSignerInvalidCert","kCMSSignerInvalidIndex"};			
  12.175 +        
  12.176 +    CSSM_OID contentType = self.contentType;
  12.177 +    NSMutableString *s = [NSMutableString stringWithFormat:  @"%@<%p>:\n"
  12.178 +                                                              "\tcontentType = %@ (\"%@\")\n"
  12.179 +                                                              "\tcontent = %u bytes\n"
  12.180 +                                                              "\tsigned=%i, encrypted=%i\n"
  12.181 +                                                              "\tpolicy=%@\n"
  12.182 +                                                              "\t%u certificates\n"
  12.183 +                                                              "\tsigners =\n",
  12.184 +                              self.class, self,
  12.185 +                              OIDAsString(contentType), @"??"/*nameOfOID(&contentType)*/,
  12.186 +                              self.content.length,
  12.187 +                              self.isSigned,self.isEncrypted,
  12.188 +                              MYPolicyGetName(_policy),
  12.189 +                              self.certificates.count];
  12.190 +    for( MYSigner *signer in self.signers ) {
  12.191 +        CMSSignerStatus status = signer.status;
  12.192 +        const char *statusName = (status<=kCMSSignerInvalidIndex) ?kStatusNames[status] :"??";
  12.193 +        [s appendFormat:@"\t\t- status = %s\n"
  12.194 +                         "\t\t  verifyResult = %@\n"
  12.195 +                         "\t\t  trust = %@\n"
  12.196 +                         "\t\t  cert = %@\n",
  12.197 +                 statusName,
  12.198 +                 (signer.verifyResult ?MYErrorName(NSOSStatusErrorDomain,signer.verifyResult)
  12.199 +                                      :@"OK"),
  12.200 +                 MYTrustDescribe(signer.trust),
  12.201 +                 signer.certificate];
  12.202 +    }
  12.203 +    return s;
  12.204 +}
  12.205 +
  12.206 +
  12.207 +@end
  12.208 +
  12.209 +
  12.210 +
  12.211 +#pragma mark -
  12.212 +@implementation MYSigner : NSObject
  12.213 +
  12.214 +#define kUncheckedStatus ((CMSSignerStatus)-1)
  12.215 +
  12.216 +- (id) initWithDecoder: (CMSDecoderRef)decoder index: (size_t)index policy: (CFTypeRef)policy
  12.217 +{
  12.218 +    self = [super init];
  12.219 +    if( self ) {
  12.220 +        CFRetain(decoder);
  12.221 +        _decoder = decoder;
  12.222 +        _index = index;
  12.223 +        if(policy) _policy = CFRetain(policy);
  12.224 +        _status = kUncheckedStatus;
  12.225 +    }
  12.226 +    return self;
  12.227 +}
  12.228 +
  12.229 +- (void) dealloc
  12.230 +{
  12.231 +    if(_decoder) CFRelease(_decoder);
  12.232 +    if(_policy) CFRelease(_policy);
  12.233 +    if(_trust) CFRelease(_trust);
  12.234 +    [super dealloc];
  12.235 +}
  12.236 +
  12.237 +- (void) _getInfo {
  12.238 +    if (_status == kUncheckedStatus) {
  12.239 +        if( !check(CMSDecoderCopySignerStatus(_decoder, _index, _policy, (_policy!=nil),
  12.240 +                                              &_status, &_trust, &_verifyResult), 
  12.241 +                   @"CMSDecoderCopySignerStatus"))
  12.242 +            _status = kMYSignerStatusCheckFailed;
  12.243 +    }
  12.244 +}
  12.245 +
  12.246 +- (CMSSignerStatus) status
  12.247 +{
  12.248 +    [self _getInfo];
  12.249 +    return _status;
  12.250 +}
  12.251 +
  12.252 +- (OSStatus) verifyResult
  12.253 +{
  12.254 +    [self _getInfo];
  12.255 +    return _verifyResult;
  12.256 +}
  12.257 +
  12.258 +- (SecTrustRef) trust
  12.259 +{
  12.260 +    [self _getInfo];
  12.261 +    return _trust;
  12.262 +}
  12.263 +
  12.264 +
  12.265 +- (NSString*) emailAddress
  12.266 +{
  12.267 +    // Don't let caller see the addr if they haven't checked validity & the signature's invalid:
  12.268 +    if (_status==kUncheckedStatus && self.status != kCMSSignerValid)
  12.269 +        return nil;
  12.270 +    
  12.271 +    CFStringRef email=NULL;
  12.272 +    if( CMSDecoderCopySignerEmailAddress(_decoder, _index, &email) == noErr )
  12.273 +        return [(NSString*)CFMakeCollectable(email) autorelease];
  12.274 +    return nil;
  12.275 +}
  12.276 +
  12.277 +- (MYCertificate *) certificate
  12.278 +{
  12.279 +    // Don't let caller see the cert if they haven't checked validity & the signature's invalid:
  12.280 +    if (_status==kUncheckedStatus && self.status != kCMSSignerValid)
  12.281 +        return nil;
  12.282 +    
  12.283 +    SecCertificateRef certRef=NULL;
  12.284 +    OSStatus err = CMSDecoderCopySignerCert(_decoder, _index, &certRef);
  12.285 +    if( err == noErr )
  12.286 +        return [MYCertificate certificateWithCertificateRef: certRef];
  12.287 +    else {
  12.288 +        Warn(@"CMSDecoderCopySignerCert returned err %i",err);
  12.289 +        return nil;
  12.290 +    }
  12.291 +}
  12.292 +
  12.293 +
  12.294 +- (NSString*) description
  12.295 +{
  12.296 +    NSMutableString *desc = [NSMutableString stringWithFormat: @"%@[st=%i", self.class,(int)self.status];
  12.297 +    int verify = self.verifyResult;
  12.298 +    if( verify )
  12.299 +        [desc appendFormat: @"; verify error %i",verify];
  12.300 +    else {
  12.301 +        MYCertificate *cert = self.certificate;
  12.302 +        if( cert )
  12.303 +            [desc appendFormat: @"; %@",cert.commonName];
  12.304 +    }
  12.305 +    [desc appendString: @"]"];
  12.306 +    return desc;
  12.307 +}
  12.308 +
  12.309 +
  12.310 +@end
  12.311 +
  12.312 +
  12.313 +
  12.314 +// Taken from Keychain.framework
  12.315 +NSString* OIDAsString(const CSSM_OID oid) {
  12.316 +    if ((NULL == oid.Data) || (0 >= oid.Length)) {
  12.317 +        return nil;
  12.318 +    } else {
  12.319 +        NSMutableString *result = [NSMutableString stringWithCapacity:(4 * oid.Length)];
  12.320 +        unsigned int i;
  12.321 +        
  12.322 +        for (i = 0; i < oid.Length; ++i) {
  12.323 +            [result appendFormat:@"%s%hhu", ((0 == i) ? "" : ", "), oid.Data[i]];
  12.324 +        }
  12.325 +        
  12.326 +        return result;
  12.327 +    }
  12.328 +}
  12.329 +
  12.330 +
  12.331 +
  12.332 +#pragma mark -
  12.333 +#pragma mark TEST CASE:
  12.334 +
  12.335 +
  12.336 +#if DEBUG
  12.337 +
  12.338 +#import "MYEncoder.h"
  12.339 +#import "MYIdentity.h"
  12.340 +
  12.341 +static void TestRoundTrip( NSString *title, NSData *source, MYIdentity *signer, MYCertificate *recipient )
  12.342 +{
  12.343 +    Log(@"Testing MYEncoder/Decoder %@...",title);
  12.344 +    NSError *error;
  12.345 +    NSData *encoded = [MYEncoder encodeData: source signer: signer recipient: recipient error: &error];
  12.346 +    CAssertEq(error,nil);
  12.347 +    CAssert([encoded length]);
  12.348 +    Log(@"MYEncoder encoded %u bytes into %u bytes", source.length,encoded.length);
  12.349 +    Log(@"Decoding...");
  12.350 +    MYDecoder *d = [[MYDecoder alloc] init];
  12.351 +    d.policy = [MYCertificate X509Policy];
  12.352 +    [d addData: encoded];
  12.353 +    [d finish];
  12.354 +
  12.355 +    CAssertEq(d.error,nil);
  12.356 +    Log(@"%@", d.dump);
  12.357 +    CAssert(d.content);
  12.358 +    CAssert([d.content isEqual: source]);
  12.359 +    CAssertEq(d.detachedContent,nil);
  12.360 +    CAssertEq(d.isSigned,(signer!=nil));
  12.361 +    CAssertEq(d.isEncrypted,(recipient!=nil));
  12.362 +
  12.363 +    if( signer ) {
  12.364 +        CAssert(d.certificates.count >= 1);     // may include extra parent certs
  12.365 +        CAssertEq(d.signers.count,1U);
  12.366 +        MYSigner *outSigner = [d.signers objectAtIndex: 0];
  12.367 +        CAssertEq(outSigner.status,(CMSSignerStatus)kCMSSignerValid);
  12.368 +        CAssertEq(outSigner.verifyResult,noErr);
  12.369 +        CAssert([outSigner.certificate isEqualToCertificate: signer]);
  12.370 +    } else {
  12.371 +        CAssertEq(d.certificates.count, 0U);
  12.372 +        CAssertEq(d.signers.count,0U);
  12.373 +    }
  12.374 +}
  12.375 +
  12.376 +
  12.377 +TestCase(MYDecoder) {
  12.378 +    RequireTestCase(MYEncoder);
  12.379 +    
  12.380 +    MYIdentity *me = [MYIdentity preferredIdentityForName: @"MYCryptoTest"];
  12.381 +    CAssert(me,@"No default identity has been set up in the Keychain");
  12.382 +    Log(@"Using %@", me);
  12.383 +    
  12.384 +    NSData *source = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"];
  12.385 +    CAssert(source);
  12.386 +    
  12.387 +    TestRoundTrip(@"signing",            source, me,  nil);
  12.388 +    TestRoundTrip(@"encryption",         source, nil, me);
  12.389 +    TestRoundTrip(@"signing+encryption", source, me,  me);
  12.390 +}
  12.391 +
  12.392 +#endif DEBUG
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/MYEncoder.h	Sun Apr 12 22:02:20 2009 -0700
    13.3 @@ -0,0 +1,90 @@
    13.4 +//
    13.5 +//  MYEncoder.h
    13.6 +//  MYCrypto
    13.7 +//
    13.8 +//  Created by Jens Alfke on 1/16/08.
    13.9 +//  Copyright 2008-2009 Jens Alfke. All rights reserved.
   13.10 +//
   13.11 +
   13.12 +#import <Foundation/Foundation.h>
   13.13 +#import <Security/CMSEncoder.h>
   13.14 +
   13.15 +@class MYIdentity, MYCertificate;
   13.16 +
   13.17 +
   13.18 +/** Creates a CMS-formatted message from a blob of data; it can be signed and/or encrypted. */
   13.19 +@interface MYEncoder : NSObject 
   13.20 +{
   13.21 +    @private
   13.22 +    CMSEncoderRef _encoder;
   13.23 +    OSStatus _error;
   13.24 +}
   13.25 +
   13.26 +/** A convenience method for one-shot encoding of a block of data.
   13.27 +    @param data  The data that will be signed/encrypted.
   13.28 +    @param signerOrNil  If non-nil, an Identity whose private key will sign the data.
   13.29 +    @param recipientOrNil  If non-nil, the data will be encrypted so only the owner of this
   13.30 +                certificate can read it.
   13.31 +    @param outError  On return, will be set to an NSError if something went wrong.
   13.32 +    @return  The encoded data. */
   13.33 ++ (NSData*) encodeData: (NSData*)data
   13.34 +                signer: (MYIdentity*)signerOrNil
   13.35 +             recipient: (MYCertificate*)recipientOrNil
   13.36 +                 error: (NSError**)outError;
   13.37 +
   13.38 +/** Initializes a new encoder.
   13.39 +    You must add at least one signer or recipient. */
   13.40 +- (id) init;
   13.41 +
   13.42 +/** Tells the encoder to sign the content with this identity's private key.
   13.43 +    (Multiple signers can be added, but this is rare.) */
   13.44 +- (BOOL) addSigner: (MYIdentity*)signer;
   13.45 +
   13.46 +/** Tells the encoder to encrypt the content with this recipient's public key.
   13.47 +    Multiple recipients can be added; any one of them will be able to decrypt the message. */
   13.48 +- (BOOL) addRecipient: (MYCertificate*)recipient;
   13.49 +
   13.50 +/** The current error status of the encoder.
   13.51 +    If something goes wrong with an operation, it will return NO,
   13.52 +    and this property will contain the error. */
   13.53 +@property (readonly) NSError* error;
   13.54 +
   13.55 +/** Setting this property to YES tells the encoder not to copy the content itself into the
   13.56 +    encoded message. The encodedData property will then contain only metadata, such as
   13.57 +    signatures and certificates.
   13.58 +    This is useful if you're working with a data format that already specifies a content
   13.59 +    format: it allows you to attach the encoded data elsewhere, e.g. in a header or metadata
   13.60 +    attribute. */
   13.61 +@property BOOL hasDetachedContent;
   13.62 +
   13.63 +/** Adds data to the encoder. You can add the entire data at once, or in bits and pieces
   13.64 +    (if you're reading it from a stream). */
   13.65 +- (BOOL) addData: (NSData*)data;
   13.66 +
   13.67 +/** The signed/encoded output data.
   13.68 +    Don't call this until after the last call to -addData:. */
   13.69 +- (NSData*) encodedData;
   13.70 +
   13.71 +
   13.72 +/** @name Expert
   13.73 + *  Advanced methods. 
   13.74 + */
   13.75 +//@{
   13.76 +
   13.77 +/** Adds a timestamp showing when the message was encoded.
   13.78 +    [Unfortunately there is no system API for reading these timestamps in decoded messages...] */
   13.79 +- (BOOL) addTimestamp;
   13.80 +
   13.81 +/** Specifies which certificates to include in the message: none, only the signer certs,
   13.82 +    or the signer certs' entire chain (the default). */
   13.83 +@property CMSCertificateChainMode certificateChainMode;
   13.84 +
   13.85 +/** Adds an extra certificate to the encoded data, for the recipient's use. Rarely needed. */
   13.86 +- (BOOL) addSupportingCert: (MYCertificate*)supportingCert;
   13.87 +
   13.88 +/** The X.509 content type of the message data. */
   13.89 +@property CSSM_OID contentType;
   13.90 +
   13.91 +//@}
   13.92 +
   13.93 +@end
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/MYEncoder.m	Sun Apr 12 22:02:20 2009 -0700
    14.3 @@ -0,0 +1,201 @@
    14.4 +//
    14.5 +//  MYEncoder.m
    14.6 +//  MYCrypto
    14.7 +//
    14.8 +//  Created by Jens Alfke on 1/16/08.
    14.9 +//  Copyright 2008-2009 Jens Alfke. All rights reserved.
   14.10 +//
   14.11 +
   14.12 +#import "MYEncoder.h"
   14.13 +#import "MYIdentity.h"
   14.14 +#import "MYCrypto_Private.h"
   14.15 +#import "Test.h"
   14.16 +#import "MYErrorUtils.h"
   14.17 +
   14.18 +
   14.19 +@implementation MYEncoder
   14.20 +
   14.21 +
   14.22 +- (id) init
   14.23 +{
   14.24 +    self = [super init];
   14.25 +    if (self != nil) {
   14.26 +        if( ! checksave(CMSEncoderCreate(&_encoder)) ) {
   14.27 +            [self release];
   14.28 +            return nil;
   14.29 +        }
   14.30 +    }
   14.31 +    return self;
   14.32 +}
   14.33 +
   14.34 +- (void) dealloc
   14.35 +{
   14.36 +    if(_encoder) CFRelease(_encoder);
   14.37 +    [super dealloc];
   14.38 +}
   14.39 +
   14.40 +
   14.41 +
   14.42 +- (BOOL) addSigner: (MYIdentity*)signer
   14.43 +{
   14.44 +    Assert(signer);
   14.45 +    return checksave( CMSEncoderAddSigners(_encoder, signer.identityRef) );
   14.46 +}
   14.47 +
   14.48 +- (BOOL) addRecipient: (MYCertificate*)recipient
   14.49 +{
   14.50 +    Assert(recipient);
   14.51 +    return checksave( CMSEncoderAddRecipients(_encoder, recipient.certificateRef) );
   14.52 +}
   14.53 +
   14.54 +- (BOOL) addSupportingCert: (MYCertificate*)supportingCert
   14.55 +{
   14.56 +    Assert(supportingCert);
   14.57 +    return checksave( CMSEncoderAddSupportingCerts(_encoder, supportingCert.certificateRef) );
   14.58 +}
   14.59 +
   14.60 +- (BOOL) addTimestamp
   14.61 +{
   14.62 +    return checksave( CMSEncoderAddSignedAttributes(_encoder, kCMSAttrSigningTime) );
   14.63 +}
   14.64 +
   14.65 +
   14.66 +- (NSError*) error
   14.67 +{
   14.68 +    if( _error )
   14.69 +        return MYError(_error, NSOSStatusErrorDomain, 
   14.70 +                       @"%@", MYErrorName(NSOSStatusErrorDomain,_error));
   14.71 +    else
   14.72 +        return nil;
   14.73 +}
   14.74 +
   14.75 +
   14.76 +- (CMSCertificateChainMode) certificateChainMode
   14.77 +{
   14.78 +    CMSCertificateChainMode mode;
   14.79 +    if( CMSEncoderGetCertificateChainMode(_encoder, &mode) == noErr )
   14.80 +        return mode;
   14.81 +    else
   14.82 +        return -1;
   14.83 +}
   14.84 +
   14.85 +- (void) setCertificateChainMode: (CMSCertificateChainMode)mode
   14.86 +{
   14.87 +    checksave( CMSEncoderSetCertificateChainMode(_encoder, mode) );
   14.88 +}
   14.89 +
   14.90 +- (BOOL) hasDetachedContent
   14.91 +{
   14.92 +    Boolean detached;
   14.93 +    return CMSEncoderGetHasDetachedContent(_encoder, &detached)==noErr && detached;
   14.94 +}
   14.95 +
   14.96 +- (void) setHasDetachedContent: (BOOL)detached
   14.97 +{
   14.98 +    checksave( CMSEncoderSetHasDetachedContent(_encoder, detached) );
   14.99 +}
  14.100 +
  14.101 +- (NSData*) _dataFromFunction: (OSStatus (*)(CMSEncoderRef,CFDataRef*))function
  14.102 +{
  14.103 +    CFDataRef data=NULL;
  14.104 +    if( checksave( (*function)(_encoder, &data) ) )
  14.105 +        return [(NSData*)CFMakeCollectable(data) autorelease];
  14.106 +    else
  14.107 +        return nil;
  14.108 +}
  14.109 +
  14.110 +
  14.111 +- (CSSM_OID) contentType
  14.112 +{
  14.113 +   NSData *data = [self _dataFromFunction: &CMSEncoderCopyEncapsulatedContentType];
  14.114 +   return (CSSM_OID){data.length,(uint8*)data.bytes};
  14.115 +}
  14.116 +
  14.117 +- (void) setContentType: (CSSM_OID)contentType
  14.118 +{
  14.119 +    checksave( CMSEncoderSetEncapsulatedContentType(_encoder, &contentType) );
  14.120 +}
  14.121 +
  14.122 +
  14.123 +- (BOOL) addData: (NSData*)data
  14.124 +{
  14.125 +    Assert(data);
  14.126 +    return ! _error && checksave( CMSEncoderUpdateContent(_encoder, data.bytes, data.length) );
  14.127 +}
  14.128 +
  14.129 +
  14.130 +- (NSData*) encodedData
  14.131 +{
  14.132 +    if( ! _error )
  14.133 +        return [self _dataFromFunction: &CMSEncoderCopyEncodedContent];
  14.134 +    else
  14.135 +        return nil;
  14.136 +}
  14.137 +
  14.138 +
  14.139 ++ (NSData*) encodeData: (NSData*)data
  14.140 +                signer: (MYIdentity*)signer
  14.141 +             recipient: (MYCertificate*)recipient
  14.142 +                 error: (NSError**)outError
  14.143 +{
  14.144 +    MYEncoder *e = [[self alloc] init];
  14.145 +    if( signer )
  14.146 +        [e addSigner: signer];
  14.147 +    if( recipient )
  14.148 +        [e addRecipient: recipient];
  14.149 +    [e addData: data];
  14.150 +    *outError = e.error;
  14.151 +    NSData *result = e.encodedData;
  14.152 +    [e release];
  14.153 +    return result;
  14.154 +}
  14.155 +
  14.156 +
  14.157 +@end
  14.158 +
  14.159 +
  14.160 +#if DEBUG
  14.161 +
  14.162 +#import "MYCrypto+Cocoa.h"
  14.163 +
  14.164 +TestCase(MYEncoder) {
  14.165 +    MYIdentity *me = nil;//[MYIdentity preferredIdentityForName: @"MYCryptoTest"];
  14.166 +    if (!me) {
  14.167 +        NSArray *idents = [[[MYKeychain allKeychains] enumerateIdentities] allObjects];
  14.168 +        SFChooseIdentityPanel *panel = [SFChooseIdentityPanel sharedChooseIdentityPanel];
  14.169 +        [panel setAlternateButtonTitle: @"Cancel"];
  14.170 +        if ([panel my_runModalForIdentities: idents 
  14.171 +                                    message: @"Choose an identity for the MYEncoder test case:"]
  14.172 +                != NSOKButton) {
  14.173 +            [NSException raise: NSGenericException format: @"User canceled"];
  14.174 +        }
  14.175 +        me = [panel my_identity];
  14.176 +        [me makePreferredIdentityForName: @"MYCryptoTest"];
  14.177 +    }
  14.178 +    CAssert(me,@"No default identity has been set up in the Keychain");
  14.179 +    
  14.180 +    NSData *source = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"];
  14.181 +    CAssert(source);
  14.182 +    
  14.183 +    NSError *error;
  14.184 +    NSData *encoded;
  14.185 +    
  14.186 +    Log(@"Testing signing...");
  14.187 +    encoded = [MYEncoder encodeData: source signer: me recipient: nil error: &error];
  14.188 +    CAssertEq(error,nil);
  14.189 +    CAssert([encoded length]);
  14.190 +    Log(@"MYEncoder signed %u bytes into %u bytes", source.length,encoded.length);
  14.191 +    
  14.192 +    Log(@"Testing encryption...");
  14.193 +    encoded = [MYEncoder encodeData: source signer: nil recipient: me error: &error];
  14.194 +    CAssertEq(error,nil);
  14.195 +    CAssert([encoded length]);
  14.196 +    Log(@"MYEncoder encrypted %u bytes into %u bytes", source.length,encoded.length);
  14.197 +    
  14.198 +    Log(@"Testing signing+encryption...");
  14.199 +    encoded = [MYEncoder encodeData: source signer: me recipient: me error: &error];
  14.200 +    CAssertEq(error,nil);
  14.201 +    CAssert([encoded length]);
  14.202 +    Log(@"MYEncoder signed/encrypted %u bytes into %u bytes", source.length,encoded.length);
  14.203 +}
  14.204 +#endif
    15.1 --- a/MYIdentity.h	Thu Apr 09 22:47:11 2009 -0700
    15.2 +++ b/MYIdentity.h	Sun Apr 12 22:02:20 2009 -0700
    15.3 @@ -17,12 +17,20 @@
    15.4      SecIdentityRef _identityRef;
    15.5  }
    15.6  
    15.7 -/** Initializes a MYIdentity given an existing SecIdentityRef. */
    15.8 -- (id) initWithIdentityRef: (SecIdentityRef)identityRef;
    15.9 +/** Creates a MYIdentity object for an existing Keychain identity reference. */
   15.10 ++ (MYIdentity*) identityWithIdentityRef: (SecIdentityRef)identityRef;
   15.11 +
   15.12 +/** The underlying SecIdentityRef. */
   15.13 +@property (readonly) SecIdentityRef identityRef;
   15.14  
   15.15  /** The identity's associated private key. */
   15.16  @property (readonly) MYPrivateKey *privateKey;
   15.17  
   15.18 +
   15.19 +/** @name Mac-Only
   15.20 + *  Functionality not available on iPhone. 
   15.21 + */
   15.22 +//@{
   15.23  #if !TARGET_OS_IPHONE
   15.24  
   15.25  /** Returns the identity that's been set as the preferred one for the given name, or nil. */
   15.26 @@ -33,5 +41,17 @@
   15.27  - (BOOL) makePreferredIdentityForName: (NSString*)name;
   15.28  
   15.29  #endif
   15.30 +//@}
   15.31 +
   15.32 +
   15.33 +/** @name Expert
   15.34 + *  Advanced methods. 
   15.35 + */
   15.36 +//@{
   15.37 +
   15.38 +/** Initializes a MYIdentity given an existing SecIdentityRef. */
   15.39 +- (id) initWithIdentityRef: (SecIdentityRef)identityRef;
   15.40 +
   15.41 +//@}
   15.42  
   15.43  @end
    16.1 --- a/MYIdentity.m	Thu Apr 09 22:47:11 2009 -0700
    16.2 +++ b/MYIdentity.m	Sun Apr 12 22:02:20 2009 -0700
    16.3 @@ -13,6 +13,11 @@
    16.4  @implementation MYIdentity
    16.5  
    16.6  
    16.7 +/** Creates a MYIdentity object for an existing Keychain identity reference. */
    16.8 ++ (MYIdentity*) identityWithIdentityRef: (SecIdentityRef)identityRef {
    16.9 +    return [[[self alloc] initWithIdentityRef: identityRef] autorelease];
   16.10 +}
   16.11 +
   16.12  - (id) initWithIdentityRef: (SecIdentityRef)identityRef {
   16.13      Assert(identityRef);
   16.14      SecCertificateRef certificateRef;
   16.15 @@ -57,6 +62,8 @@
   16.16  }
   16.17  
   16.18  
   16.19 +@synthesize identityRef=_identityRef;
   16.20 +
   16.21  - (MYPrivateKey*) privateKey {
   16.22      SecKeyRef keyRef = NULL;
   16.23      if (!check(SecIdentityCopyPrivateKey(_identityRef, &keyRef), @"SecIdentityCopyPrivateKey"))
   16.24 @@ -74,10 +81,10 @@
   16.25  {
   16.26      Assert(name);
   16.27      SecIdentityRef identityRef;
   16.28 -    if (!check(SecIdentityCopyPreference((CFStringRef)name, 0, NULL, &identityRef),
   16.29 -               @"SecIdentityCopyPreference"))
   16.30 +    OSStatus err = SecIdentityCopyPreference((CFStringRef)name, 0, NULL, &identityRef);
   16.31 +    if (err==errKCItemNotFound || !check(err,@"SecIdentityCopyPreference") || !identityRef)
   16.32          return nil;
   16.33 -    return identityRef ?[[[self alloc] initWithIdentityRef: identityRef] autorelease] :nil;
   16.34 +    return [self identityWithIdentityRef: identityRef];
   16.35  }
   16.36  
   16.37  - (BOOL) makePreferredIdentityForName: (NSString*)name {
    17.1 --- a/MYKey.m	Thu Apr 09 22:47:11 2009 -0700
    17.2 +++ b/MYKey.m	Sun Apr 12 22:02:20 2009 -0700
    17.3 @@ -40,6 +40,10 @@
    17.4  }
    17.5  
    17.6  
    17.7 +- (NSString*) description {
    17.8 +    return $sprintf(@"%@[%@ /%p]", [self class], (self.name ?:@""), self.keychainItemRef);
    17.9 +}
   17.10 +
   17.11  - (SecExternalItemType) keyType {
   17.12      AssertAbstractMethod();
   17.13  }
    18.1 --- a/MYPrivateKey.h	Thu Apr 09 22:47:11 2009 -0700
    18.2 +++ b/MYPrivateKey.h	Sun Apr 12 22:02:20 2009 -0700
    18.3 @@ -51,7 +51,10 @@
    18.4  /** Creates a self-signed identity certificate using this key-pair.
    18.5      The attributes describe the certificate's metadata, including its expiration date and the
    18.6      subject's name. Keys for the dictionary are given below; the only mandatory one is
    18.7 -    kMYIdentityCommonNameKey. */
    18.8 +    kMYIdentityCommonNameKey.
    18.9 +    The resulting identity certificate includes X.509 extended attributes allowing it to be
   18.10 +    used for SSL connections. (Plug: See my MYNetwork library for an easy way to run SSL
   18.11 +    servers and clients.) */
   18.12  - (MYIdentity*) createSelfSignedIdentityWithAttributes: (NSDictionary*)attributes;
   18.13  
   18.14  /** Exports the private key as a data blob, so that it can be stored as a backup, or transferred
   18.15 @@ -84,6 +87,7 @@
   18.16  
   18.17  
   18.18  /* Attribute keys for creating identities: */
   18.19 +
   18.20  #define kMYIdentityCommonNameKey    @"Common Name"      // NSString. Required!
   18.21  #define kMYIdentityGivenNameKey     @"Given Name"
   18.22  #define kMYIdentitySurnameKey       @"Surname"
    19.1 --- a/MYPrivateKey.m	Thu Apr 09 22:47:11 2009 -0700
    19.2 +++ b/MYPrivateKey.m	Sun Apr 12 22:02:20 2009 -0700
    19.3 @@ -155,9 +155,9 @@
    19.4                             0LL,
    19.5                             CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_VERIFY,        // public key
    19.6                             CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT,
    19.7 -                           CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_SIGN,          // private key
    19.8 -                           CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_PERMANENT,
    19.9 -                           NULL, // SecAccessRef
   19.10 +                           CSSM_KEYUSE_ANY,                                 // private key
   19.11 +                           CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE,
   19.12 +                           NULL,                                            // SecAccessRef
   19.13                             &pubKey, &privKey);
   19.14  #endif
   19.15      if (!check(err, @"SecKeyCreatePair")) {
   19.16 @@ -172,7 +172,10 @@
   19.17  
   19.18  
   19.19  - (NSString*) description {
   19.20 -    return $sprintf(@"%@[%@]", [self class], self.publicKeyDigest.abbreviatedHexString);
   19.21 +    return $sprintf(@"%@[%@ %@ /%p]", [self class], 
   19.22 +                    self.publicKeyDigest.abbreviatedHexString,
   19.23 +                    (self.name ?:@""),
   19.24 +                    self.keychainItemRef);
   19.25  }
   19.26  
   19.27  @synthesize publicKey=_publicKey;
    20.1 --- a/MYSymmetricKey.m	Thu Apr 09 22:47:11 2009 -0700
    20.2 +++ b/MYSymmetricKey.m	Sun Apr 12 22:02:20 2009 -0700
    20.3 @@ -62,7 +62,6 @@
    20.4                                      {(id)kSecAttrCanSign, $false},
    20.5                                      {(id)kSecAttrCanVerify, $false},
    20.6                                      {(id)kSecValueData, keyData},
    20.7 -    //{(id)kSecAttrApplicationTag, [@"foo" dataUsingEncoding: NSUTF8StringEncoding]}, //TEMP
    20.8                                      {(id)kSecReturnPersistentRef, $true});
    20.9      if (!check(SecItemAdd((CFDictionaryRef)keyAttrs, (CFTypeRef*)&keyRef), @"SecItemAdd")) {
   20.10          [self release];
    21.1 --- a/README.textile	Thu Apr 09 22:47:11 2009 -0700
    21.2 +++ b/README.textile	Sun Apr 12 22:02:20 2009 -0700
    21.3 @@ -20,6 +20,8 @@
    21.4  * Cryptographic digests/hashes (effectively-unique IDs for data)
    21.5  * The Keychain (a secure, encrypted storage system for keys and passwords)
    21.6  
    21.7 +It's open source, released under a friendly BSD license.
    21.8 +
    21.9  h3. Requirements
   21.10  
   21.11  * Mac OS X 10.5 or later _[has been tested on 10.5.6]_
   21.12 @@ -32,7 +34,7 @@
   21.13  
   21.14  * "Download the current source code":http://mooseyard.com/hg/hgwebdir.cgi/MYCrypto/archive/tip.zip
   21.15  * To check out the source code using "Mercurial":http://selenic.com/mercurial/:<br>
   21.16 -@hg clone /hg/hgwebdir.cgi/MYCrypto/ MYCrypto@
   21.17 +@hg clone http://mooseyard.com/hg/hgwebdir.cgi/MYCrypto/ MYCrypto@
   21.18  * As described above, you'll also need to download or check out MYUtilities and put it in a directory next to MYCrypto.
   21.19  * Or if you're just looking:
   21.20  ** "Browse the source code":http://mooseyard.com/hg/hgwebdir.cgi/MYCrypto/file/tip