* More work on iPhone compatibility.
authorJens Alfke <jens@mooseyard.com>
Tue Jun 09 23:58:03 2009 -0700 (2009-06-09)
changeset 246856e071d25a
parent 23 39fec79de6e8
child 25 38c3c3923e1f
* More work on iPhone compatibility.
* Restored the signature-verification code to MYCertInfo, which I'd removed earlier. I now need it to verify self-signed certs, since the Security framework won't do it for me.
* Merged MYCertificate-iPhone.m into MYCertificate.m since there's more shared code now.
MYCertificate-iPhone.m
MYCertificate.h
MYCertificate.m
MYCertificateInfo.h
MYCertificateInfo.m
MYCrypto-iPhone.xcodeproj/project.pbxproj
MYKey-iPhone.m
MYKey.h
MYKeychain-iPhone.m
MYKeychainItem.m
MYPrivateKey.m
MYPublicKey.m
Tests/generated.cer
Tests/selfsigned_altered.cer
     1.1 --- a/MYCertificate-iPhone.m	Sun Jun 07 21:53:56 2009 -0700
     1.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.3 @@ -1,107 +0,0 @@
     1.4 -//
     1.5 -//  MYCertificate-iPhone.m
     1.6 -//  MYCrypto-iPhone
     1.7 -//
     1.8 -//  Created by Jens Alfke on 3/30/09.
     1.9 -//  Copyright 2009 Jens Alfke. All rights reserved.
    1.10 -//
    1.11 -
    1.12 -#import "MYCertificate.h"
    1.13 -#import "MYCertificateInfo.h"
    1.14 -#import "MYCrypto_Private.h"
    1.15 -
    1.16 -#if MYCRYPTO_USE_IPHONE_API
    1.17 -
    1.18 -
    1.19 -@implementation MYCertificate
    1.20 -
    1.21 -
    1.22 -+ (MYCertificate*) certificateWithCertificateRef: (SecCertificateRef)certificateRef {
    1.23 -    return [[[self alloc] initWithCertificateRef: certificateRef] autorelease];
    1.24 -}
    1.25 -
    1.26 -/** Creates a MYCertificate object for an existing Keychain certificate reference. */
    1.27 -- (id) initWithCertificateRef: (SecCertificateRef)certificateRef {
    1.28 -    self = [super initWithKeychainItemRef: (SecKeychainItemRef)certificateRef];
    1.29 -    if (self) {
    1.30 -        _certificateRef = certificateRef;     // superclass has already CFRetained it
    1.31 -    }
    1.32 -    return self;
    1.33 -}
    1.34 -
    1.35 -/** Creates a MYCertificate object from exported key data, but does not add it to any keychain. */
    1.36 -- (id) initWithCertificateData: (NSData*)data
    1.37 -{
    1.38 -    SecCertificateRef certificateRef = SecCertificateCreateWithData(NULL, (CFDataRef)data);
    1.39 -    self = [self initWithCertificateRef: certificateRef];
    1.40 -    CFRelease(certificateRef);
    1.41 -    return self;
    1.42 -}
    1.43 -
    1.44 -- (void) dealloc
    1.45 -{
    1.46 -    [_info release];
    1.47 -    [super dealloc];
    1.48 -}
    1.49 -
    1.50 -
    1.51 -- (BOOL)isEqualToCertificate:(MYCertificate*)cert {
    1.52 -    return [self isEqual: cert] || [self.certificateData isEqual: cert.certificateData];
    1.53 -}
    1.54 -
    1.55 -@synthesize certificateRef=_certificateRef;
    1.56 -
    1.57 -- (NSData*) certificateData {
    1.58 -    CFDataRef data = SecCertificateCopyData(_certificateRef);
    1.59 -    return data ?[(id)CFMakeCollectable(data) autorelease] :nil;
    1.60 -}
    1.61 -
    1.62 -- (MYPublicKey*) publicKey {
    1.63 -    SecTrustRef trust = NULL;
    1.64 -    SecPolicyRef policy = SecPolicyCreateBasicX509();
    1.65 -    OSStatus err = SecTrustCreateWithCertificates((CFArrayRef)$array((id)_certificateRef),
    1.66 -                                                  policy,
    1.67 -                                                  &trust);
    1.68 -    CFRelease(policy);
    1.69 -    if (!check(err,@"SecTrustCreateWithCertificates"))
    1.70 -        return nil;
    1.71 -    
    1.72 -    MYPublicKey *key = nil;
    1.73 -    SecKeyRef keyRef = SecTrustCopyPublicKey(trust);
    1.74 -    if (keyRef) {
    1.75 -        key = [[[MYPublicKey alloc] initWithKeyRef: keyRef] autorelease];
    1.76 -        CFRelease(keyRef);
    1.77 -    }
    1.78 -    CFRelease(trust);
    1.79 -    return key;
    1.80 -}
    1.81 -
    1.82 -- (MYIdentity*) identity {
    1.83 -    return [self.keychain identityWithDigest: self.publicKey.publicKeyDigest];
    1.84 -}
    1.85 -
    1.86 -
    1.87 -- (MYCertificateInfo*) info {
    1.88 -    if (!_info) {
    1.89 -        NSError *error;
    1.90 -        _info = [[MYCertificateInfo alloc] initWithCertificateData: self.certificateData
    1.91 -                                                             error: &error];
    1.92 -        if (!_info)
    1.93 -            Warn(@"Couldn't parse certificate %@: %@", self, error);
    1.94 -    }
    1.95 -    return _info;
    1.96 -}
    1.97 -
    1.98 -- (NSString*) commonName {
    1.99 -    CFStringRef name = SecCertificateCopySubjectSummary(_certificateRef);
   1.100 -    return name ?[(id)CFMakeCollectable(name) autorelease] :nil;
   1.101 -}
   1.102 -
   1.103 -- (NSArray*) emailAddresses {
   1.104 -    NSString *email = self.info.subject.emailAddress;
   1.105 -    return email ?$array(email) :nil;
   1.106 -}
   1.107 -
   1.108 -@end
   1.109 -
   1.110 -#endif MYCRYPTO_USE_IPHONE_API
     2.1 --- a/MYCertificate.h	Sun Jun 07 21:53:56 2009 -0700
     2.2 +++ b/MYCertificate.h	Tue Jun 09 23:58:03 2009 -0700
     2.3 @@ -55,6 +55,9 @@
     2.4  /** The list (if any) of the subject's email addresses. */
     2.5  @property (readonly) NSArray *emailAddresses;
     2.6  
     2.7 +- (SecTrustResultType) evaluateTrustWithPolicy: (SecPolicyRef)policy;
     2.8 +- (SecTrustResultType) evaluateTrust;
     2.9 +
    2.10  
    2.11  /** @name Mac-Only
    2.12   *  Functionality not available on iPhone. 
    2.13 @@ -80,21 +83,24 @@
    2.14  /** @name Expert
    2.15   */
    2.16  //@{
    2.17 -#if !TARGET_OS_IPHONE
    2.18  
    2.19  + (SecPolicyRef) X509Policy;
    2.20  + (SecPolicyRef) SSLPolicy;
    2.21 +
    2.22 +#if !TARGET_OS_IPHONE
    2.23  + (SecPolicyRef) SMIMEPolicy;
    2.24  - (CSSM_CERT_TYPE) certificateType;
    2.25  - (NSArray*) trustSettings;
    2.26  - (BOOL) setUserTrust: (SecTrustUserSetting)trustSetting;
    2.27 +#endif
    2.28      
    2.29 -#endif
    2.30  //@}
    2.31      
    2.32  @end
    2.33  
    2.34  
    2.35 +NSString* MYTrustResultDescribe( SecTrustResultType result );
    2.36 +#if !TARGET_OS_IPHONE
    2.37  NSString* MYPolicyGetName( SecPolicyRef policy );
    2.38  NSString* MYTrustDescribe( SecTrustRef trust );
    2.39 -NSString* MYTrustResultDescribe( SecTrustResultType result );
    2.40 +#endif
     3.1 --- a/MYCertificate.m	Sun Jun 07 21:53:56 2009 -0700
     3.2 +++ b/MYCertificate.m	Tue Jun 09 23:58:03 2009 -0700
     3.3 @@ -13,8 +13,6 @@
     3.4  #import "MYCertificateInfo.h"
     3.5  #import "MYErrorUtils.h"
     3.6  
     3.7 -#if !MYCRYPTO_USE_IPHONE_API
     3.8 -
     3.9  
    3.10  @implementation MYCertificate
    3.11  
    3.12 @@ -34,28 +32,51 @@
    3.13  
    3.14  /** Creates a MYCertificate object from exported key data, but does not add it to any keychain. */
    3.15  - (id) initWithCertificateData: (NSData*)data
    3.16 +#if !MYCRYPTO_USE_IPHONE_API
    3.17                            type: (CSSM_CERT_TYPE) type
    3.18                        encoding: (CSSM_CERT_ENCODING) encoding
    3.19 +#endif
    3.20  {
    3.21      Assert(data);
    3.22 +    SecCertificateRef certificateRef = NULL;
    3.23 +#if MYCRYPTO_USE_IPHONE_API
    3.24 +    certificateRef = SecCertificateCreateWithData(NULL, (CFDataRef)data);
    3.25 +#else
    3.26      CSSM_DATA cssmData = {.Data=(void*)data.bytes, .Length=data.length};
    3.27 -    SecCertificateRef certificateRef = NULL;
    3.28      if (!check(SecCertificateCreateFromData(&cssmData, type, encoding, &certificateRef),
    3.29 -        @"SecCertificateCreateFromData")) {
    3.30 +               @"SecCertificateCreateFromData"))
    3.31 +        certificateRef = NULL;
    3.32 +#endif
    3.33 +    if (!certificateRef) {
    3.34          [self release];
    3.35          return nil;
    3.36      }
    3.37      self = [self initWithCertificateRef: certificateRef];
    3.38      CFRelease(certificateRef);
    3.39 +    
    3.40 +    // If the cert is self-signed, verify its signature. Apple's frameworks don't do this,
    3.41 +    // even the SecTrust API; if the signature doesn't verify, they just assume it could be
    3.42 +    // signed by a different cert. Seems like a bad decision to me, so I'll add the check:
    3.43 +    MYCertificateInfo *info = self.info;
    3.44 +    if (info.isRoot) {
    3.45 +        Log(@"Verifying self-signed certificate %@ ...", self);
    3.46 +        if (![info verifySignatureWithKey: self.publicKey]) {
    3.47 +            Log(@"Self-signed cert failed signature verification (%@)", self);
    3.48 +            [self release];
    3.49 +            return nil;
    3.50 +        }
    3.51 +    }
    3.52 +    
    3.53      return self;
    3.54  }
    3.55  
    3.56 +#if !MYCRYPTO_USE_IPHONE_API
    3.57  - (id) initWithCertificateData: (NSData*)data {
    3.58      return [self initWithCertificateData: data 
    3.59                                      type: CSSM_CERT_X_509v3 
    3.60                                  encoding: CSSM_CERT_ENCODING_BER];
    3.61  }
    3.62 -
    3.63 +#endif
    3.64  - (void) dealloc
    3.65  {
    3.66      [_info release];
    3.67 @@ -78,6 +99,7 @@
    3.68  }
    3.69  
    3.70  
    3.71 +#if !TARGET_OS_IPHONE
    3.72  + (MYCertificate*) preferredCertificateForName: (NSString*)name {
    3.73      SecCertificateRef certRef = NULL;
    3.74      if (!check(SecCertificateCopyPreference((CFStringRef)name, 0, &certRef),
    3.75 @@ -90,23 +112,49 @@
    3.76      return check(SecCertificateSetPreference(_certificateRef, (CFStringRef)name, 0, NULL),
    3.77                   @"SecCertificateSetPreference");
    3.78  }
    3.79 +#endif TARGET_OS_IPHONE
    3.80  
    3.81  
    3.82  @synthesize certificateRef=_certificateRef;
    3.83  
    3.84  - (NSData*) certificateData {
    3.85 +#if MYCRYPTO_USE_IPHONE_API
    3.86 +    CFDataRef data = SecCertificateCopyData(_certificateRef);
    3.87 +    return data ?[(id)CFMakeCollectable(data) autorelease] :nil;
    3.88 +#else
    3.89      CSSM_DATA cssmData;
    3.90      if (!check(SecCertificateGetData(_certificateRef, &cssmData),
    3.91                 @"SecCertificateGetData"))
    3.92          return nil;
    3.93      return [NSData dataWithBytes: cssmData.Data length: cssmData.Length];
    3.94 +#endif
    3.95  }
    3.96  
    3.97  - (MYPublicKey*) publicKey {
    3.98      SecKeyRef keyRef = NULL;
    3.99 +#if MYCRYPTO_USE_IPHONE_API
   3.100 +    SecTrustRef trust = NULL;
   3.101 +    SecPolicyRef policy = SecPolicyCreateBasicX509();
   3.102 +    OSStatus err = SecTrustCreateWithCertificates((CFArrayRef)$array((id)_certificateRef),
   3.103 +                                                  policy,
   3.104 +                                                  &trust);
   3.105 +    CFRelease(policy);
   3.106 +    if (!check(err,@"SecTrustCreateWithCertificates"))
   3.107 +        return nil;
   3.108 +    SecTrustResultType result;
   3.109 +    if (!check(SecTrustEvaluate(trust, &result), @"SecTrustEvaluate")) {
   3.110 +        CFRelease(trust);
   3.111 +        return nil;
   3.112 +    }
   3.113 +    keyRef = SecTrustCopyPublicKey(trust);
   3.114 +    CFRelease(trust);
   3.115 +#else
   3.116      if (!check(SecCertificateCopyPublicKey(_certificateRef, &keyRef),
   3.117                 @"SecCertificateCopyPublicKey") || !keyRef)
   3.118          return nil;
   3.119 +#endif
   3.120 +    if (!keyRef)
   3.121 +        return nil;
   3.122      MYPublicKey *key = [[[MYPublicKey alloc] initWithKeyRef: keyRef] autorelease];
   3.123      CFRelease(keyRef);
   3.124      return key;
   3.125 @@ -129,18 +177,27 @@
   3.126  
   3.127  - (NSString*) commonName {
   3.128      CFStringRef name = NULL;
   3.129 +#if MYCRYPTO_USE_IPHONE_API
   3.130 +    name = SecCertificateCopySubjectSummary(_certificateRef);
   3.131 +#else
   3.132      if (!check(SecCertificateCopyCommonName(_certificateRef, &name),
   3.133 -               @"SecCertificateCopyCommonName") || !name)
   3.134 +               @"SecCertificateCopyCommonName"))
   3.135          return nil;
   3.136 -    return [(id)CFMakeCollectable(name) autorelease];
   3.137 +#endif
   3.138 +    return name ?[NSMakeCollectable(name) autorelease] :nil;
   3.139  }
   3.140  
   3.141  - (NSArray*) emailAddresses {
   3.142 +#if MYCRYPTO_USE_IPHONE_API
   3.143 +    NSString *email = self.info.subject.emailAddress;
   3.144 +    return email ?$array(email) :nil;
   3.145 +#else
   3.146      CFArrayRef addrs = NULL;
   3.147      if (!check(SecCertificateCopyEmailAddresses(_certificateRef, &addrs),
   3.148                 @"SecCertificateCopyEmailAddresses") || !addrs)
   3.149          return nil;
   3.150      return [(id)CFMakeCollectable(addrs) autorelease];
   3.151 +#endif
   3.152  }
   3.153  
   3.154  
   3.155 @@ -148,6 +205,37 @@
   3.156  #pragma mark TRUST/POLICY STUFF:
   3.157  
   3.158  
   3.159 +- (SecTrustResultType) evaluateTrustWithPolicy: (SecPolicyRef)policy {
   3.160 +    SecTrustRef trust;
   3.161 +    if (!check(SecTrustCreateWithCertificates((CFArrayRef)$array((id)_certificateRef), policy, &trust), 
   3.162 +               @"SecTrustCreateWithCertificates"))
   3.163 +        return kSecTrustResultOtherError;
   3.164 +    SecTrustResultType result;
   3.165 +    if (!check(SecTrustEvaluate(trust, &result), @"SecTrustEvaluate"))
   3.166 +        result = kSecTrustResultOtherError;
   3.167 +    
   3.168 +#if 0
   3.169 +    // This is just to log details:
   3.170 +    CSSM_TP_APPLE_EVIDENCE_INFO *status;
   3.171 +    CFArrayRef certChain;
   3.172 +    if (check(SecTrustGetResult(trust, &result, &certChain, &status), @"SecTrustGetResult")) {
   3.173 +        Log(@"evaluateTrust: result=%@, bits=%X, certChain=%@", MYTrustResultDescribe(result),status->StatusBits, certChain);
   3.174 +        for (unsigned i=0; i<status->NumStatusCodes; i++)
   3.175 +            Log(@"    #%i: %X", i, status->StatusCodes[i]);
   3.176 +        CFRelease(certChain);
   3.177 +    }
   3.178 +#endif
   3.179 +    
   3.180 +    CFRelease(trust);
   3.181 +    return result;
   3.182 +}
   3.183 +
   3.184 +- (SecTrustResultType) evaluateTrust {
   3.185 +    return [self evaluateTrustWithPolicy: [[self class] X509Policy]];
   3.186 +}
   3.187 +
   3.188 +
   3.189 +#if !MYCRYPTO_USE_IPHONE_API
   3.190  + (SecPolicyRef) policyForOID: (CSSM_OID) policyOID {
   3.191      SecPolicySearchRef search;
   3.192      if (!check(SecPolicySearchCreate(CSSM_CERT_X_509v3, &policyOID, NULL, &search),
   3.193 @@ -159,29 +247,41 @@
   3.194      CFRelease(search);
   3.195      return policy;
   3.196  }
   3.197 +#endif
   3.198  
   3.199  + (SecPolicyRef) X509Policy {
   3.200      static SecPolicyRef sX509Policy = NULL;
   3.201 -    if (!sX509Policy)
   3.202 +    if (!sX509Policy) {
   3.203 +#if MYCRYPTO_USE_IPHONE_API
   3.204 +        sX509Policy = SecPolicyCreateBasicX509();
   3.205 +#else
   3.206          sX509Policy = [self policyForOID: CSSMOID_APPLE_X509_BASIC];
   3.207 +#endif
   3.208 +    }
   3.209      return sX509Policy;
   3.210  }
   3.211  
   3.212  + (SecPolicyRef) SSLPolicy {
   3.213      static SecPolicyRef sSSLPolicy = NULL;
   3.214 -    if (!sSSLPolicy)
   3.215 +    if (!sSSLPolicy) {
   3.216 +#if MYCRYPTO_USE_IPHONE_API
   3.217 +        sSSLPolicy = SecPolicyCreateSSL(NO,NULL);
   3.218 +#else
   3.219          sSSLPolicy = [self policyForOID: CSSMOID_APPLE_TP_SSL];
   3.220 +#endif
   3.221 +    }
   3.222      return sSSLPolicy;
   3.223  }
   3.224  
   3.225 +#if !TARGET_OS_IPHONE
   3.226  + (SecPolicyRef) SMIMEPolicy {
   3.227      static SecPolicyRef sSMIMEPolicy = NULL;
   3.228 -    if (!sSMIMEPolicy)
   3.229 +    if (!sSMIMEPolicy) {
   3.230          sSMIMEPolicy = [self policyForOID: CSSMOID_APPLE_TP_SMIME];
   3.231 +    }
   3.232      return sSMIMEPolicy;
   3.233  }
   3.234  
   3.235 -
   3.236  - (CSSM_CERT_TYPE) certificateType {
   3.237      CSSM_CERT_TYPE type = CSSM_CERT_UNKNOWN;
   3.238      if (!check(SecCertificateGetType(_certificateRef, &type), @"SecCertificateGetType"))
   3.239 @@ -212,19 +312,12 @@
   3.240      } else
   3.241          return paramErr;
   3.242  }
   3.243 +#endif
   3.244  
   3.245  
   3.246  @end
   3.247  
   3.248  
   3.249 -NSString* MYPolicyGetName( SecPolicyRef policy ) {
   3.250 -    if (!policy)
   3.251 -        return @"(null)";
   3.252 -    CSSM_OID oid = {};
   3.253 -    SecPolicyGetOID(policy, &oid);
   3.254 -    return $sprintf(@"SecPolicy[%@]", OIDAsString(oid));
   3.255 -}
   3.256 -
   3.257  NSString* MYTrustResultDescribe( SecTrustResultType result ) {
   3.258      static NSString* const kTrustResultNames[kSecTrustResultOtherError+1] = {
   3.259          @"Invalid",
   3.260 @@ -243,6 +336,15 @@
   3.261  }
   3.262  
   3.263  
   3.264 +#if !TARGET_OS_IPHONE
   3.265 +NSString* MYPolicyGetName( SecPolicyRef policy ) {
   3.266 +    if (!policy)
   3.267 +        return @"(null)";
   3.268 +    CSSM_OID oid = {};
   3.269 +    SecPolicyGetOID(policy, &oid);
   3.270 +    return $sprintf(@"SecPolicy[%@]", OIDAsString(oid));
   3.271 +}
   3.272 +
   3.273  NSString* MYTrustDescribe( SecTrustRef trust ) {
   3.274      SecTrustResultType result;
   3.275      CFArrayRef certChain=NULL;
   3.276 @@ -275,9 +377,10 @@
   3.277          return result;
   3.278      }
   3.279  }
   3.280 +#endif
   3.281  
   3.282  
   3.283 -
   3.284 +#if !TARGET_OS_IPHONE
   3.285  TestCase(Trust) {
   3.286      Log(@"X.509 policy = %@", MYPolicyGetName([MYCertificate X509Policy]));
   3.287      Log(@"  SSL policy = %@", MYPolicyGetName([MYCertificate SSLPolicy]));
   3.288 @@ -288,9 +391,7 @@
   3.289              Log(@"---- %@ = %@", cert, settings);
   3.290      }
   3.291  }
   3.292 -
   3.293 -
   3.294 -#endif !MYCRYPTO_USE_IPHONE_API
   3.295 +#endif
   3.296  
   3.297  
   3.298  
     4.1 --- a/MYCertificateInfo.h	Sun Jun 07 21:53:56 2009 -0700
     4.2 +++ b/MYCertificateInfo.h	Tue Jun 09 23:58:03 2009 -0700
     4.3 @@ -14,6 +14,7 @@
     4.4  {
     4.5      @private
     4.6      NSArray *_root;
     4.7 +    NSData *_data;
     4.8  }
     4.9  
    4.10  /** Initialize by parsing X.509 certificate data.
    4.11 @@ -35,6 +36,10 @@
    4.12  /** Returns YES if the issuer is the same as the subject. (Aka a "self-signed" certificate.) */
    4.13  @property (readonly) BOOL isRoot;
    4.14  
    4.15 +/** Verifies the certificate's signature, using the given public key.
    4.16 +    If the certificate is root/self-signed, use the cert's own subject public key. */
    4.17 +- (BOOL) verifySignatureWithKey: (MYPublicKey*)issuerPublicKey;
    4.18 +
    4.19  @end
    4.20  
    4.21  
     5.1 --- a/MYCertificateInfo.m	Sun Jun 07 21:53:56 2009 -0700
     5.2 +++ b/MYCertificateInfo.m	Tue Jun 09 23:58:03 2009 -0700
     5.3 @@ -107,12 +107,17 @@
     5.4          return nil;
     5.5      }
     5.6  
     5.7 -    return [self initWithRoot: root];
     5.8 +    self = [self initWithRoot: root];
     5.9 +    if (self) {
    5.10 +        _data = [data copy];
    5.11 +    }
    5.12 +    return self;
    5.13  }
    5.14  
    5.15  - (void) dealloc
    5.16  {
    5.17      [_root release];
    5.18 +    [_data release];
    5.19      [super dealloc];
    5.20  }
    5.21  
    5.22 @@ -157,6 +162,42 @@
    5.23      return [[[MYPublicKey alloc] initWithKeyData: keyData.bits] autorelease];
    5.24  }
    5.25  
    5.26 +- (NSData*) signedData {
    5.27 +    if (!_data)
    5.28 +        return nil;
    5.29 +    // The root object is a sequence; we want to extract the 1st object of that sequence.
    5.30 +    const UInt8 *certStart = _data.bytes;
    5.31 +    const UInt8 *start = MYBERGetContents(_data, nil);
    5.32 +    if (!start) return nil;
    5.33 +    size_t length = MYBERGetLength([NSData dataWithBytesNoCopy: (void*)start
    5.34 +                                                        length: _data.length - (start-certStart)
    5.35 +                                                  freeWhenDone: NO],
    5.36 +                                   NULL);
    5.37 +    if (length==0)
    5.38 +        return nil;
    5.39 +    return [NSData dataWithBytes: start length: (start + length - certStart)];
    5.40 +}
    5.41 +
    5.42 +- (MYOID*) signatureAlgorithmID {
    5.43 +    return $castIf(MYOID, $atIf($castIf(NSArray,$atIf(_root,1)), 0));
    5.44 +}
    5.45 +
    5.46 +- (NSData*) signature {
    5.47 +    id signature = $atIf(_root,2);
    5.48 +    if ([signature isKindOfClass: [MYBitString class]])
    5.49 +        signature = [signature bits];
    5.50 +    return $castIf(NSData,signature);
    5.51 +}
    5.52 +
    5.53 +- (BOOL) verifySignatureWithKey: (MYPublicKey*)issuerPublicKey {
    5.54 +    if (!$equal(self.signatureAlgorithmID, kRSAWithSHA1AlgorithmID))
    5.55 +        return NO;
    5.56 +    NSData *signedData = self.signedData;
    5.57 +    NSData *signature = self.signature;
    5.58 +    return signedData && signature && [issuerPublicKey verifySignature: signature ofData: signedData];
    5.59 +}
    5.60 +
    5.61 +
    5.62  @end
    5.63  
    5.64  
    5.65 @@ -364,28 +405,45 @@
    5.66      CAssert(subject.commonName);
    5.67      
    5.68      // Now go through MYCertificate:
    5.69 +    Log(@"Creating a MYCertificate from the data...");
    5.70      MYCertificate *cert = [[MYCertificate alloc] initWithCertificateData: certData];
    5.71 +    Log(@"MYCertificate = %@", cert);
    5.72      CAssert(cert);
    5.73      CAssertEqual(cert.info, pcert);
    5.74 +    Log(@"Trust = %@", MYTrustResultDescribe([cert evaluateTrust]));
    5.75      
    5.76      return pcert;
    5.77  }
    5.78  
    5.79 -static MYCertificateInfo* testCert(NSString *filename, BOOL selfSigned) {
    5.80 +static NSData* readTestFile(NSString *filename) {
    5.81  #if TARGET_OS_IPHONE
    5.82      filename = [[NSBundle mainBundle] pathForResource: filename ofType: @"cer"];
    5.83  #else
    5.84      filename = [[@"../../Tests/" stringByAppendingPathComponent: filename]
    5.85                  stringByAppendingPathExtension: @"cer"];
    5.86  #endif
    5.87 -    Log(@"--- Creating MYCertificateInfo from %@", filename);
    5.88 -    return testCertData([NSData dataWithContentsOfFile: filename], selfSigned);
    5.89 +    Log(@"--- Testing certificate file %@", filename);
    5.90 +    NSData *data = [NSData dataWithContentsOfFile: filename];
    5.91 +    CAssert(data, @"Couldn't read file %@", filename);
    5.92 +    return data;
    5.93 +}
    5.94 +
    5.95 +static MYCertificateInfo* testCert(NSString *filename, BOOL selfSigned) {
    5.96 +    return testCertData(readTestFile(filename), selfSigned);
    5.97  }
    5.98  
    5.99  
   5.100  TestCase(ParsedCert) {
   5.101      testCert(@"selfsigned", YES);
   5.102      testCert(@"iphonedev", NO);
   5.103 +    
   5.104 +    // Now test a self-signed cert with a bad signature:
   5.105 +    MYCertificate *cert = [[MYCertificate alloc] initWithCertificateData: readTestFile(@"selfsigned_altered")];
   5.106 +    Log(@"MYCertificate = %@", cert);
   5.107 +    CAssertNil(cert);
   5.108 +
   5.109 +    Log(@"Checking /tmp/generated.cer");
   5.110 +    testCertData([NSData dataWithContentsOfFile: @"/tmp/generated.cer"], YES);//TEMP
   5.111  }
   5.112  
   5.113  
   5.114 @@ -418,7 +476,11 @@
   5.115          CAssert(certData);
   5.116          CAssertNil(error);
   5.117          CAssert(certData);
   5.118 -#if !TARGET_OS_IPHONE
   5.119 +#if TARGET_OS_IPHONE
   5.120 +        NSString *path = [@"/tmp/generated.cer" stringByStandardizingPath]; //TEMP
   5.121 +        CAssert([certData writeToFile: path atomically: YES]);
   5.122 +        Log(@"Wrote generated cert to %@",path);
   5.123 +#else
   5.124          [certData writeToFile: @"../../Tests/generated.cer" atomically: YES];
   5.125  #endif
   5.126          MYCertificateInfo *pcert2 = testCertData(certData, YES);
     6.1 --- a/MYCrypto-iPhone.xcodeproj/project.pbxproj	Sun Jun 07 21:53:56 2009 -0700
     6.2 +++ b/MYCrypto-iPhone.xcodeproj/project.pbxproj	Tue Jun 09 23:58:03 2009 -0700
     6.3 @@ -12,7 +12,6 @@
     6.4  		1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; };
     6.5  		1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; };
     6.6  		27059CF30F8F0F9200A8422F /* MYIdentity.m in Sources */ = {isa = PBXBuildFile; fileRef = 27059CF20F8F0F9200A8422F /* MYIdentity.m */; };
     6.7 -		273391CD0F81EC70009414D9 /* MYCertificate-iPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = 273391CC0F81EC70009414D9 /* MYCertificate-iPhone.m */; };
     6.8  		273392120F8283B8009414D9 /* MYKeychain-iPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = 273392110F8283B8009414D9 /* MYKeychain-iPhone.m */; };
     6.9  		274110090F99234100AD413F /* MYSymmetricKey-iPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = 274110080F99234100AD413F /* MYSymmetricKey-iPhone.m */; };
    6.10  		2748607F0F8D5E0600FE617B /* MYPrivateKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 2748607E0F8D5E0600FE617B /* MYPrivateKey.m */; };
    6.11 @@ -20,6 +19,7 @@
    6.12  		275D9FF00FD8795300D85A86 /* MYBERParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 275D9FE80FD8795300D85A86 /* MYBERParser.m */; };
    6.13  		275D9FF10FD8795300D85A86 /* MYDEREncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 275D9FEA0FD8795300D85A86 /* MYDEREncoder.m */; };
    6.14  		275D9FF20FD8795300D85A86 /* MYOID.m in Sources */ = {isa = PBXBuildFile; fileRef = 275D9FEC0FD8795300D85A86 /* MYOID.m */; };
    6.15 +		275ED90F0FDF8C22006A371D /* selfsigned_altered.cer in Resources */ = {isa = PBXBuildFile; fileRef = 275ED90E0FDF8C22006A371D /* selfsigned_altered.cer */; };
    6.16  		276FB13F0F84090900CB326E /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 276FB13E0F84090900CB326E /* Security.framework */; };
    6.17  		276FB16E0F84152B00CB326E /* MYCryptoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 276FB16D0F84152B00CB326E /* MYCryptoTest.m */; };
    6.18  		276FB3190F856AA700CB326E /* MYPublicKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 276FB3180F856AA700CB326E /* MYPublicKey.m */; };
    6.19 @@ -54,7 +54,6 @@
    6.20  		1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
    6.21  		27059CF10F8F0F8E00A8422F /* MYIdentity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYIdentity.h; sourceTree = "<group>"; };
    6.22  		27059CF20F8F0F9200A8422F /* MYIdentity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYIdentity.m; sourceTree = "<group>"; };
    6.23 -		273391CC0F81EC70009414D9 /* MYCertificate-iPhone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MYCertificate-iPhone.m"; sourceTree = "<group>"; };
    6.24  		273392110F8283B8009414D9 /* MYKeychain-iPhone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MYKeychain-iPhone.m"; sourceTree = "<group>"; };
    6.25  		274110080F99234100AD413F /* MYSymmetricKey-iPhone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MYSymmetricKey-iPhone.m"; sourceTree = "<group>"; };
    6.26  		2748607D0F8D5DF200FE617B /* MYPrivateKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYPrivateKey.h; sourceTree = "<group>"; };
    6.27 @@ -67,6 +66,7 @@
    6.28  		275D9FEA0FD8795300D85A86 /* MYDEREncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYDEREncoder.m; sourceTree = "<group>"; };
    6.29  		275D9FEB0FD8795300D85A86 /* MYOID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYOID.h; sourceTree = "<group>"; };
    6.30  		275D9FEC0FD8795300D85A86 /* MYOID.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYOID.m; sourceTree = "<group>"; };
    6.31 +		275ED90E0FDF8C22006A371D /* selfsigned_altered.cer */ = {isa = PBXFileReference; lastKnownFileType = file; name = selfsigned_altered.cer; path = Tests/selfsigned_altered.cer; sourceTree = "<group>"; };
    6.32  		276FB13E0F84090900CB326E /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
    6.33  		276FB16D0F84152B00CB326E /* MYCryptoTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYCryptoTest.m; sourceTree = "<group>"; };
    6.34  		276FB3180F856AA700CB326E /* MYPublicKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYPublicKey.m; sourceTree = "<group>"; };
    6.35 @@ -161,6 +161,7 @@
    6.36  				275D9FEC0FD8795300D85A86 /* MYOID.m */,
    6.37  				27958CA10FDB5F8F00095275 /* iphonedev.cer */,
    6.38  				27958CA20FDB5F8F00095275 /* selfsigned.cer */,
    6.39 +				275ED90E0FDF8C22006A371D /* selfsigned_altered.cer */,
    6.40  			);
    6.41  			name = Certificates;
    6.42  			sourceTree = "<group>";
    6.43 @@ -180,7 +181,6 @@
    6.44  				27AAD9710F8927DB0064DD7C /* MYCryptoConfig.h */,
    6.45  				27E8230C0F81D56E0019BE60 /* MYCertificate.h */,
    6.46  				276FB34A0F856CA400CB326E /* MYCertificate.m */,
    6.47 -				273391CC0F81EC70009414D9 /* MYCertificate-iPhone.m */,
    6.48  				27059CF10F8F0F8E00A8422F /* MYIdentity.h */,
    6.49  				27059CF20F8F0F9200A8422F /* MYIdentity.m */,
    6.50  				27E823100F81D56E0019BE60 /* MYKey.h */,
    6.51 @@ -299,6 +299,7 @@
    6.52  				27E8233C0F81D5760019BE60 /* MYError_CSSMErrorDomain.strings in Resources */,
    6.53  				27958CA30FDB5F8F00095275 /* iphonedev.cer in Resources */,
    6.54  				27958CA40FDB5F8F00095275 /* selfsigned.cer in Resources */,
    6.55 +				275ED90F0FDF8C22006A371D /* selfsigned_altered.cer in Resources */,
    6.56  			);
    6.57  			runOnlyForDeploymentPostprocessing = 0;
    6.58  		};
    6.59 @@ -318,7 +319,6 @@
    6.60  				27E823390F81D5760019BE60 /* Logging.m in Sources */,
    6.61  				27E8233A0F81D5760019BE60 /* Test.m in Sources */,
    6.62  				27E8233B0F81D5760019BE60 /* MYErrorUtils.m in Sources */,
    6.63 -				273391CD0F81EC70009414D9 /* MYCertificate-iPhone.m in Sources */,
    6.64  				273392120F8283B8009414D9 /* MYKeychain-iPhone.m in Sources */,
    6.65  				276FB16E0F84152B00CB326E /* MYCryptoTest.m in Sources */,
    6.66  				276FB3190F856AA700CB326E /* MYPublicKey.m in Sources */,
     7.1 --- a/MYKey-iPhone.m	Sun Jun 07 21:53:56 2009 -0700
     7.2 +++ b/MYKey-iPhone.m	Tue Jun 09 23:58:03 2009 -0700
     7.3 @@ -59,7 +59,7 @@
     7.4  
     7.5  
     7.6  - (id) initWithKeyRef: (SecKeyRef)key {
     7.7 -    return [super initWithKeychainItemRef: (SecKeychainItemRef)key];
     7.8 +    return [self initWithKeychainItemRef: (SecKeychainItemRef)key];
     7.9  }
    7.10  
    7.11  
    7.12 @@ -114,7 +114,8 @@
    7.13          return nil;
    7.14      else {
    7.15          Assert(data!=NULL);
    7.16 -        return [(id)CFMakeCollectable(data) autorelease];
    7.17 +        _keyData = NSMakeCollectable(data);
    7.18 +        return _keyData;
    7.19      }
    7.20      // The format of this data is not documented. There's been some reverse-engineering:
    7.21      // https://devforums.apple.com/message/32089#32089
    7.22 @@ -147,9 +148,7 @@
    7.23  - (BOOL) setValue: (NSString*)value ofAttribute: (SecKeychainAttrType)attribute {
    7.24      if (!value)
    7.25          value = (id)[NSNull null];
    7.26 -    NSDictionary *query = $dict( {(id)kSecClass, (id)kSecClassKey},
    7.27 -                                {(id)kSecAttrKeyClass, (id)self.keyClass},
    7.28 -                                {(id)kSecMatchItemList, self._itemList} );
    7.29 +    NSDictionary *query = $dict( {(id)kSecValueRef, (id)self.keyRef} );
    7.30      NSDictionary *attrs = $dict( {(id)attribute, value} );
    7.31      return check(SecItemUpdate((CFDictionaryRef)query, (CFDictionaryRef)attrs), @"SecItemUpdate");
    7.32  }
     8.1 --- a/MYKey.h	Sun Jun 07 21:53:56 2009 -0700
     8.2 +++ b/MYKey.h	Tue Jun 09 23:58:03 2009 -0700
     8.3 @@ -29,6 +29,7 @@
     8.4      Concrete subclasses are MYSymmetricKey and MYPublicKey. */
     8.5  @interface MYKey : MYKeychainItem
     8.6  { 
     8.7 +    @private
     8.8      NSData *_keyData;
     8.9  }
    8.10  
     9.1 --- a/MYKeychain-iPhone.m	Sun Jun 07 21:53:56 2009 -0700
     9.2 +++ b/MYKeychain-iPhone.m	Tue Jun 09 23:58:03 2009 -0700
     9.3 @@ -220,6 +220,22 @@
     9.4  }
     9.5  
     9.6  
     9.7 +- (BOOL) _verifyPublicKeyRef: (MYKeychainItemRef)itemRef {
     9.8 +    // Enumerating the keychain sometimes returns public-key refs that give not-found errors
     9.9 +    // when you try to use them for anything. As a workaround, detect these early on before
    9.10 +    // even creating a MYPublicKey:
    9.11 +    NSDictionary *info = $dict({(id)kSecValueRef, (id)itemRef},
    9.12 +    {(id)kSecReturnAttributes, $true});
    9.13 +    CFDictionaryRef attrs = NULL;
    9.14 +    OSStatus err = SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&attrs);
    9.15 +    if (attrs) CFRelease(attrs);
    9.16 +    if (err == errSecItemNotFound) {
    9.17 +        Log(@"MYKeyEnumerator: Ignoring bogus(?) key with ref %p", itemRef);
    9.18 +        return NO;
    9.19 +    } else
    9.20 +        return YES;
    9.21 +}        
    9.22 +
    9.23  - (id) nextObject {
    9.24      if (!_results)
    9.25          return nil;
    9.26 @@ -229,7 +245,8 @@
    9.27          if (_itemClass == kSecAttrKeyClassPrivate) {
    9.28              _currentObject = [[MYPrivateKey alloc] initWithKeyRef: (SecKeyRef)found];
    9.29          } else if (_itemClass == kSecAttrKeyClassPublic) {
    9.30 -            _currentObject = [[MYPublicKey alloc] initWithKeyRef: (SecKeyRef)found];
    9.31 +            if ([self _verifyPublicKeyRef: found])
    9.32 +                _currentObject = [[MYPublicKey alloc] initWithKeyRef: (SecKeyRef)found];
    9.33          } else if (_itemClass == kSecAttrKeyClassSymmetric) {
    9.34              _currentObject = [[MYSymmetricKey alloc] initWithKeyRef: (SecKeyRef)found];
    9.35          } else if (_itemClass == kSecClassCertificate) {
    10.1 --- a/MYKeychainItem.m	Sun Jun 07 21:53:56 2009 -0700
    10.2 +++ b/MYKeychainItem.m	Tue Jun 09 23:58:03 2009 -0700
    10.3 @@ -68,13 +68,6 @@
    10.4      return $array((id)_itemRef);
    10.5  }
    10.6  
    10.7 -#if MYCRYPTO_USE_IPHONE_API
    10.8 -- (CFDictionaryRef) asQuery {
    10.9 -    return (CFDictionaryRef) $dict( {(id)kSecClass, (id)kSecClassKey},//FIX
   10.10 -                                    {(id)kSecMatchItemList, self._itemList} );
   10.11 -}
   10.12 -#endif
   10.13 -
   10.14  
   10.15  - (MYKeychain*) keychain {
   10.16  #if MYCRYPTO_USE_IPHONE_API
   10.17 @@ -95,7 +88,7 @@
   10.18  - (BOOL) removeFromKeychain {
   10.19      OSStatus err;
   10.20  #if MYCRYPTO_USE_IPHONE_API
   10.21 -    err = SecItemDelete(self.asQuery);
   10.22 +    err = SecItemDelete((CFDictionaryRef) $dict( {(id)kSecValueRef, (id)_itemRef} ));
   10.23  #else
   10.24      err = SecKeychainItemDelete((SecKeychainItemRef)_itemRef);
   10.25      if (err==errSecInvalidItemRef)
   10.26 @@ -127,8 +120,7 @@
   10.27  + (NSData*) _getAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item {
   10.28      NSData *value = nil;
   10.29  #if MYCRYPTO_USE_IPHONE_API
   10.30 -    NSDictionary *info = $dict( {(id)kSecClass, (id)kSecClassKey},
   10.31 -                                {(id)kSecMatchItemList, $array((id)item)},
   10.32 +    NSDictionary *info = $dict( {(id)kSecValueRef, (id)item},
   10.33                                  {(id)kSecReturnAttributes, $true} );
   10.34      CFDictionaryRef attrs;
   10.35      if (!check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&attrs), @"SecItemCopyMatching"))
   10.36 @@ -182,9 +174,7 @@
   10.37  {
   10.38  #if MYCRYPTO_USE_IPHONE_API
   10.39      id value = stringValue ?(id)stringValue :(id)[NSNull null];
   10.40 -    NSDictionary *query = $dict({(id)kSecClass, (id)kSecClassKey},
   10.41 -                                {(id)kSecAttrKeyType, (id)attr},
   10.42 -                                {(id)kSecMatchItemList, $array((id)item)});
   10.43 +    NSDictionary *query = $dict({(id)kSecValueRef, (id)item});
   10.44      NSDictionary *attrs = $dict({(id)attr, value});
   10.45      return check(SecItemUpdate((CFDictionaryRef)query, (CFDictionaryRef)attrs), @"SecItemUpdate");
   10.46      
    11.1 --- a/MYPrivateKey.m	Sun Jun 07 21:53:56 2009 -0700
    11.2 +++ b/MYPrivateKey.m	Tue Jun 09 23:58:03 2009 -0700
    11.3 @@ -178,8 +178,16 @@
    11.4  - (MYSHA1Digest*) _keyDigest {
    11.5      if (_publicKey)
    11.6          return _publicKey.publicKeyDigest;
    11.7 -    else
    11.8 -        return [MYSHA1Digest digestFromDigestData: [self _attribute: kSecAttrApplicationLabel]];
    11.9 +    else {
   11.10 +        NSData *digestData;
   11.11 +#if MYCRYPTO_USE_IPHONE_API
   11.12 +        digestData = [self _attribute: kSecAttrApplicationLabel];
   11.13 +#else
   11.14 +        digestData = [[self class] _getAttribute: kSecKeyLabel 
   11.15 +                                          ofItem: (SecKeychainItemRef)self.keyRef]; 
   11.16 +#endif
   11.17 +        return [MYSHA1Digest digestFromDigestData: digestData];
   11.18 +    }
   11.19  }
   11.20  
   11.21  - (MYSHA1Digest*) publicKeyDigest {
    12.1 --- a/MYPublicKey.m	Sun Jun 07 21:53:56 2009 -0700
    12.2 +++ b/MYPublicKey.m	Tue Jun 09 23:58:03 2009 -0700
    12.3 @@ -29,7 +29,6 @@
    12.4      return [self initWithKeyData: keyData];
    12.5  }
    12.6  
    12.7 -
    12.8  - (void) dealloc
    12.9  {
   12.10      [_digest release];
   12.11 @@ -48,10 +47,6 @@
   12.12  - (SecExternalItemType) keyType {
   12.13      return kSecAttrKeyTypeRSA;
   12.14  }
   12.15 -
   12.16 -- (MYSHA1Digest*) _keyDigest {
   12.17 -    return (MYSHA1Digest*) [MYSHA1Digest digestFromDigestData: [self _attribute: kSecAttrApplicationLabel]];
   12.18 -}
   12.19  #endif
   12.20  
   12.21  - (NSUInteger)hash {
    13.1 Binary file Tests/generated.cer has changed
    14.1 Binary file Tests/selfsigned_altered.cer has changed