* Added ASN.1 / BER / DER utilities, to be used in generating and parsing X.509 certs.
authorJens Alfke <jens@mooseyard.com>
Tue Jun 02 13:16:28 2009 -0700 (2009-06-02)
changeset 16c409dbc4f068
parent 15 2ac5704e229f
child 17 90a70925562b
* Added ASN.1 / BER / DER utilities, to be used in generating and parsing X.509 certs.
* Added Keychain user-interaction-allowed setter. Added doc comments to MYSymmetricKey.
MYASN1Object.h
MYASN1Object.m
MYBERParser.h
MYBERParser.m
MYCertificate.h
MYCertificate.m
MYCrypto-iPhone.xcodeproj/project.pbxproj
MYCrypto.h
MYCrypto.xcodeproj/project.pbxproj
MYCryptoTest.m
MYDEREncoder.h
MYDEREncoder.m
MYKey-iPhone.m
MYKeychain.h
MYKeychain.m
MYOID.h
MYOID.m
MYSymmetricKey.h
MYSymmetricKey.m
Tests/iphonedev.cer
Tests/selfsigned.cer
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/MYASN1Object.h	Tue Jun 02 13:16:28 2009 -0700
     1.3 @@ -0,0 +1,63 @@
     1.4 +//
     1.5 +//  MYASN1Object.h
     1.6 +//  MYCrypto-iPhone
     1.7 +//
     1.8 +//  Created by Jens Alfke on 5/28/09.
     1.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
    1.10 +//
    1.11 +
    1.12 +#import <Foundation/Foundation.h>
    1.13 +
    1.14 +
    1.15 +/** A generic ASN.1 data value. The BER parser instantiates these to represent parsed values that
    1.16 +    it doesn't know how to represent otherwise. */
    1.17 +@interface MYASN1Object : NSObject
    1.18 +{
    1.19 +    @private
    1.20 +    uint32_t _tag;
    1.21 +    uint8_t _tagClass;
    1.22 +    BOOL _constructed;
    1.23 +    NSData *_value;
    1.24 +    NSArray *_components;
    1.25 +}
    1.26 +
    1.27 +- (id) initWithTag: (uint32_t)tag
    1.28 +           ofClass: (uint8_t)tagClass 
    1.29 +       constructed: (BOOL)constructed
    1.30 +             value: (NSData*)value;
    1.31 +- (id) initWithTag: (uint32_t)tag
    1.32 +           ofClass: (uint8_t)tagClass 
    1.33 +        components: (NSArray*)components;
    1.34 +
    1.35 +@property (readonly) uint32_t tag;
    1.36 +@property (readonly) uint8_t tagClass;
    1.37 +@property (readonly) BOOL constructed;
    1.38 +@property (readonly) NSData *value;
    1.39 +@property (readonly) NSArray *components;
    1.40 +
    1.41 ++ (NSString*) dump: (id)object;
    1.42 +
    1.43 +@end
    1.44 +
    1.45 +
    1.46 +/** An ASN.1 "big" (arbitrary-length) integer.
    1.47 +    The value contains the bytes of the integer, in big-endian order. */
    1.48 +@interface MYASN1BigInteger : MYASN1Object
    1.49 +@end
    1.50 +
    1.51 +
    1.52 +/** An ordered string of bits, as used in ASN.1.
    1.53 +    This differs from NSData in that it need not occupy a whole number of bytes;
    1.54 +    that is, the number of bits need not be a multiple of 8. */
    1.55 +@interface MYBitString : NSObject 
    1.56 +{
    1.57 +    NSData *_bits;
    1.58 +    NSUInteger _bitCount;
    1.59 +}
    1.60 +
    1.61 +- (id)initWithBits: (NSData*)bits count: (NSUInteger)bitCount;
    1.62 +
    1.63 +@property (readonly, nonatomic) NSData *bits;
    1.64 +@property (readonly, nonatomic) NSUInteger bitCount;
    1.65 +
    1.66 +@end
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/MYASN1Object.m	Tue Jun 02 13:16:28 2009 -0700
     2.3 @@ -0,0 +1,132 @@
     2.4 +//
     2.5 +//  MYASN1Object.m
     2.6 +//  MYCrypto-iPhone
     2.7 +//
     2.8 +//  Created by Jens Alfke on 5/28/09.
     2.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
    2.10 +//
    2.11 +
    2.12 +#import "MYASN1Object.h"
    2.13 +
    2.14 +
    2.15 +@implementation MYASN1Object
    2.16 +
    2.17 +
    2.18 +- (id) initWithTag: (uint32_t)tag
    2.19 +           ofClass: (uint8_t)tagClass 
    2.20 +       constructed: (BOOL)constructed
    2.21 +             value: (NSData*)value
    2.22 +{
    2.23 +    Assert(value);
    2.24 +    self = [super init];
    2.25 +    if (self != nil) {
    2.26 +        _tag = tag;
    2.27 +        _tagClass = tagClass;
    2.28 +        _constructed = constructed;
    2.29 +        _value = [value copy];
    2.30 +    }
    2.31 +    return self;
    2.32 +}
    2.33 +
    2.34 +- (id) initWithTag: (uint32_t)tag
    2.35 +           ofClass: (uint8_t)tagClass 
    2.36 +        components: (NSArray*)components
    2.37 +{
    2.38 +    Assert(components);
    2.39 +    self = [super init];
    2.40 +    if (self != nil) {
    2.41 +        _tag = tag;
    2.42 +        _tagClass = tagClass;
    2.43 +        _constructed = YES;
    2.44 +        _components = [components copy];
    2.45 +    }
    2.46 +    return self;
    2.47 +}
    2.48 +
    2.49 +- (void) dealloc
    2.50 +{
    2.51 +    [_value release];
    2.52 +    [_components release];
    2.53 +    [super dealloc];
    2.54 +}
    2.55 +
    2.56 +
    2.57 +@synthesize tag=_tag, tagClass=_tagClass, constructed=_constructed, value=_value, components=_components;
    2.58 +
    2.59 +
    2.60 +- (NSString*)description {
    2.61 +    if (_components)
    2.62 +        return $sprintf(@"%@[%hhu/%u/%u]%@", self.class, _tagClass,(unsigned)_constructed,_tag, _components);
    2.63 +    else
    2.64 +        return $sprintf(@"%@[%hhu/%u/%u, %u bytes]", self.class, _tagClass,(unsigned)_constructed,_tag, _value.length);
    2.65 +}
    2.66 +
    2.67 +static void dump(id object, NSMutableString *output, NSString *indent) {
    2.68 +    if ([object isKindOfClass: [MYASN1Object class]]) {
    2.69 +        MYASN1Object *asn1Obj = object;
    2.70 +        [output appendFormat: @"%@%@[%hhu/%u]", indent, asn1Obj.class, asn1Obj.tagClass,asn1Obj.tag];
    2.71 +        if (asn1Obj.components) {
    2.72 +            [output appendString: @":\n"];
    2.73 +            NSString *subindent = [indent stringByAppendingString: @"    "];
    2.74 +            for (id o in asn1Obj.components)
    2.75 +                dump(o,output, subindent);
    2.76 +        } else
    2.77 +            [output appendFormat: @" %@\n", asn1Obj.value];
    2.78 +    } else if([object respondsToSelector: @selector(objectEnumerator)]) {
    2.79 +        [output appendString: indent];
    2.80 +        if ([object isKindOfClass: [NSArray class]])
    2.81 +            [output appendString: @"Sequence:\n"];
    2.82 +        else if ([object isKindOfClass: [NSSet class]])
    2.83 +            [output appendString: @"Set:\n"];
    2.84 +        else
    2.85 +            [output appendFormat: @"%@:\n", [object class]];
    2.86 +        NSString *subindent = [indent stringByAppendingString: @"    "];
    2.87 +        for (id o in object)
    2.88 +            dump(o,output, subindent);
    2.89 +    } else {
    2.90 +        [output appendFormat: @"%@%@\n", indent, object];
    2.91 +    }
    2.92 +}
    2.93 +
    2.94 ++ (NSString*) dump: (id)object {
    2.95 +    NSMutableString *output = [NSMutableString stringWithCapacity: 512];
    2.96 +    dump(object,output,@"");
    2.97 +    return output;
    2.98 +}
    2.99 +
   2.100 +
   2.101 +@end
   2.102 +
   2.103 +
   2.104 +
   2.105 +@implementation MYASN1BigInteger
   2.106 +
   2.107 +@end
   2.108 +
   2.109 +
   2.110 +
   2.111 +@implementation MYBitString
   2.112 +
   2.113 +
   2.114 +- (id)initWithBits: (NSData*)bits count: (unsigned)bitCount;
   2.115 +{
   2.116 +    Assert(bits);
   2.117 +    Assert(bitCount <= 8*bits.length);
   2.118 +    self = [super init];
   2.119 +    if (self != nil) {
   2.120 +        _bits = [bits copy];
   2.121 +        _bitCount = bitCount;
   2.122 +    }
   2.123 +    return self;
   2.124 +}
   2.125 +
   2.126 +- (void) dealloc
   2.127 +{
   2.128 +    [_bits release];
   2.129 +    [super dealloc];
   2.130 +}
   2.131 +
   2.132 +
   2.133 +@synthesize bits=_bits, bitCount=_bitCount;
   2.134 +
   2.135 +@end
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/MYBERParser.h	Tue Jun 02 13:16:28 2009 -0700
     3.3 @@ -0,0 +1,19 @@
     3.4 +//
     3.5 +//  MYBERParser.h
     3.6 +//  MYCrypto
     3.7 +//
     3.8 +//  Created by Jens Alfke on 6/2/09.
     3.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
    3.10 +//
    3.11 +
    3.12 +#import <Cocoa/Cocoa.h>
    3.13 +
    3.14 +
    3.15 +#define MYASN1ErrorDomain @"MYASN1ErrorDomain"
    3.16 +
    3.17 +
    3.18 +/** Parses a block of BER-formatted data into an object tree. */
    3.19 +id MYBERParse (NSData *ber, NSError **outError);
    3.20 +
    3.21 +NSDateFormatter* MYBERGeneralizedTimeFormatter();
    3.22 +NSDateFormatter* MYBERUTCTimeFormatter();
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/MYBERParser.m	Tue Jun 02 13:16:28 2009 -0700
     4.3 @@ -0,0 +1,298 @@
     4.4 +//
     4.5 +//  MYBERParser.m
     4.6 +//  MYCrypto
     4.7 +//
     4.8 +//  Created by Jens Alfke on 6/2/09.
     4.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
    4.10 +//
    4.11 +
    4.12 +#import "MYBERParser.h"
    4.13 +#import "MYASN1Object.h"
    4.14 +#import "MYOID.h"
    4.15 +#import "MYErrorUtils.h"
    4.16 +#import "CollectionUtils.h"
    4.17 +#import "Test.h"
    4.18 +
    4.19 +
    4.20 +#define MYBERParserException @"MYBERParserException"
    4.21 +
    4.22 +
    4.23 +
    4.24 +typedef struct {
    4.25 +    const uint8_t *nextChar;
    4.26 +    size_t length;
    4.27 +} InputData;
    4.28 +
    4.29 +
    4.30 +static void requireLength (size_t length, size_t expectedLength) {
    4.31 +    if (length != expectedLength)
    4.32 +        [NSException raise: MYBERParserException format: @"Unexpected value length"];
    4.33 +}
    4.34 +
    4.35 +
    4.36 +static const void* readOrDie (InputData *input, size_t len) {
    4.37 +    if (len > input->length)
    4.38 +        [NSException raise: MYBERParserException format: @"Unexpected EOF on input"];
    4.39 +    const void *bytes = input->nextChar;
    4.40 +    input->nextChar += len;
    4.41 +    input->length -= len;
    4.42 +    return bytes;
    4.43 +}
    4.44 +
    4.45 +
    4.46 +static NSData* readDataOrDie(InputData *input, size_t length) {
    4.47 +    return [NSMutableData dataWithBytes: readOrDie(input,length) length: length];
    4.48 +}
    4.49 +
    4.50 +
    4.51 +static NSString* readStringOrDie(InputData *input, size_t length, NSStringEncoding encoding) {
    4.52 +    NSString *str = [[NSString alloc] initWithBytes: readOrDie(input,length) 
    4.53 +                                             length: length
    4.54 +                                           encoding: encoding];
    4.55 +    if (!str)
    4.56 +        [NSException raise: MYBERParserException format: @"Unparseable string"];
    4.57 +    return [str autorelease];
    4.58 +}    
    4.59 +
    4.60 +
    4.61 +static uint32_t readBigEndianUnsignedInteger (InputData *input, size_t length) {
    4.62 +    if (length == 0 || length > 4)
    4.63 +        [NSException raise: MYBERParserException format: @"Invalid integer length"];
    4.64 +    uint32_t result = 0;
    4.65 +    memcpy(((uint8_t*)&result)+(4-length), readOrDie(input, length), length);
    4.66 +    return result;
    4.67 +}
    4.68 +
    4.69 +static int32_t readBigEndianSignedInteger (InputData *input, size_t length) {
    4.70 +    int32_t result = (int32_t) readBigEndianUnsignedInteger(input,length);
    4.71 +    uint8_t *dst = ((uint8_t*)&result)+(4-length);
    4.72 +    if (*dst & 0x80) { // sign-extend negative value
    4.73 +        while (--dst >= (uint8_t*)&result)
    4.74 +            *dst = 0xFF;
    4.75 +    }
    4.76 +    return result;
    4.77 +}
    4.78 +
    4.79 +
    4.80 +NSDateFormatter* MYBERGeneralizedTimeFormatter() {
    4.81 +    static NSDateFormatter *sFmt;
    4.82 +    if (!sFmt) {
    4.83 +        sFmt = [[NSDateFormatter alloc] init];
    4.84 +        sFmt.dateFormat = @"yyyyMMddHHmmss'Z'";
    4.85 +        sFmt.timeZone = [NSTimeZone timeZoneWithName: @"GMT"];
    4.86 +    }
    4.87 +    return sFmt;
    4.88 +}
    4.89 +
    4.90 +NSDateFormatter* MYBERUTCTimeFormatter() {
    4.91 +    static NSDateFormatter *sFmt;
    4.92 +    if (!sFmt) {
    4.93 +        sFmt = [[NSDateFormatter alloc] init];
    4.94 +        sFmt.dateFormat = @"yyMMddHHmmss'Z'";
    4.95 +        sFmt.timeZone = [NSTimeZone timeZoneWithName: @"GMT"];
    4.96 +    }
    4.97 +    return sFmt;
    4.98 +}
    4.99 +
   4.100 +static NSDate* parseDate (NSString *dateStr, unsigned tag) {
   4.101 +    NSDateFormatter *fmt = (tag==23 ?MYBERUTCTimeFormatter() :MYBERGeneralizedTimeFormatter());
   4.102 +    NSDate *date = [fmt dateFromString: dateStr];
   4.103 +    if (!date)
   4.104 +        [NSException raise: MYBERParserException format: @"Unparseable date '%@'", dateStr];
   4.105 +    return date;
   4.106 +}
   4.107 +
   4.108 +
   4.109 +static id parseBER(InputData *input) {
   4.110 +    struct {
   4.111 +        unsigned tag            :5;
   4.112 +        unsigned isConstructed  :1;
   4.113 +        unsigned tagClass       :2;
   4.114 +        unsigned length         :7;
   4.115 +        unsigned isLengthLong   :1;
   4.116 +    } header;
   4.117 +    memcpy(&header, readOrDie(input,2), 2);
   4.118 +    
   4.119 +    if (header.tag == 0x1F)
   4.120 +        [NSException raise: MYBERParserException format: @"Long tags not supported"];
   4.121 +    
   4.122 +    // Parse the length:
   4.123 +    size_t length;
   4.124 +    if (!header.isLengthLong)
   4.125 +        length = header.length;
   4.126 +    else if (header.length == 0)
   4.127 +        [NSException raise: MYBERParserException format: @"Indefinite length not supported"];
   4.128 +    else
   4.129 +        length = NSSwapBigIntToHost(readBigEndianUnsignedInteger(input,header.length));
   4.130 +    
   4.131 +    Class defaultClass = [MYASN1Object class];
   4.132 +    
   4.133 +    // Tag values can be found in <Security/x509defs.h>. I'm not using them here because that
   4.134 +    // header does not exist on iPhone!
   4.135 +    
   4.136 +    if (header.isConstructed) {
   4.137 +        // Constructed:
   4.138 +        NSMutableArray *items = $marray();
   4.139 +        InputData subInput = {input->nextChar, length};
   4.140 +        while (subInput.length > 0) {
   4.141 +            [items addObject: parseBER(&subInput)];
   4.142 +        }
   4.143 +        input->nextChar += length;
   4.144 +        input->length -= length;
   4.145 +
   4.146 +        switch (header.tag) {
   4.147 +            case 16: // sequence
   4.148 +                return items;
   4.149 +            case 17: // set
   4.150 +                return [NSSet setWithArray: items];
   4.151 +            default:
   4.152 +                return [[[MYASN1Object alloc] initWithTag: header.tag
   4.153 +                                                  ofClass: header.tagClass
   4.154 +                                               components: items] autorelease];
   4.155 +        }
   4.156 +    } else {
   4.157 +        // Primitive:
   4.158 +        switch (header.tag) {
   4.159 +            case 1: { // boolean
   4.160 +                requireLength(length,1);
   4.161 +                return *(const uint8_t*)readOrDie(input, 1) ?$true :$false;
   4.162 +            }
   4.163 +            case 2: // integer
   4.164 +            case 10: // enum
   4.165 +            {
   4.166 +                if (length <= 4) {
   4.167 +                    int32_t value = NSSwapBigIntToHost(readBigEndianSignedInteger(input,length));
   4.168 +                    return [NSNumber numberWithInteger: value];
   4.169 +                } else {
   4.170 +                    // Big integer!
   4.171 +                    defaultClass = [MYASN1BigInteger class];
   4.172 +                    break;
   4.173 +                }
   4.174 +            }
   4.175 +            case 3: // bitstring
   4.176 +            {
   4.177 +                UInt8 unusedBits = *(const UInt8*) readOrDie(input, 1);
   4.178 +                if (unusedBits)
   4.179 +                    Log(@"Bit-string has %u unused bits", (unsigned)unusedBits);
   4.180 +                if (unusedBits > 7 || length < 1)
   4.181 +                    [NSException raise: MYBERParserException format: @"Bogus bit-string"];
   4.182 +                return [[[MYBitString alloc] initWithBits: readDataOrDie(input, length-1)
   4.183 +                                                    count: 8*(length-1) - unusedBits] autorelease];
   4.184 +            }
   4.185 +            case 4: // octetstring
   4.186 +                return readDataOrDie(input, length);
   4.187 +            case 5: // null
   4.188 +                requireLength(length,0);
   4.189 +                return [NSNull null];
   4.190 +            case 6: // OID
   4.191 +                return [[[MYOID alloc] initWithBEREncoding: readDataOrDie(input, length)] autorelease];
   4.192 +            case 12: // UTF8String
   4.193 +                return readStringOrDie(input,length,NSUTF8StringEncoding);
   4.194 +            case 18: // numeric string
   4.195 +            case 19: // printable string:
   4.196 +                return readStringOrDie(input,length,NSASCIIStringEncoding);
   4.197 +            case 23: // UTC time:
   4.198 +            case 24: // Generalized time:
   4.199 +                return parseDate(readStringOrDie(input,length,NSASCIIStringEncoding), header.tag);
   4.200 +            default:
   4.201 +                break;
   4.202 +        }
   4.203 +    }
   4.204 +
   4.205 +    // Generic case -- create and return a MYASN1Object:
   4.206 +    NSData *value = readDataOrDie(input, length);
   4.207 +    id result = [[[defaultClass alloc] initWithTag: header.tag
   4.208 +                                           ofClass: header.tagClass 
   4.209 +                                       constructed: header.isConstructed
   4.210 +                                             value: value] autorelease];
   4.211 +    if( defaultClass == [MYASN1Object class])
   4.212 +        Warn(@"parseBER: Returning default %@", result);
   4.213 +    return result;
   4.214 +}
   4.215 +
   4.216 +
   4.217 +id MYBERParse (NSData *ber, NSError **outError) {
   4.218 +    @try{
   4.219 +        InputData input = {ber.bytes, ber.length};
   4.220 +        return parseBER(&input);
   4.221 +    }@catch (NSException *x) {
   4.222 +        if ($equal(x.name, MYBERParserException)) {
   4.223 +            *outError = MYError(1,MYASN1ErrorDomain, @"%@", x.reason);
   4.224 +        } else {
   4.225 +            @throw(x);
   4.226 +        }
   4.227 +    }
   4.228 +    return nil;
   4.229 +}
   4.230 +
   4.231 +
   4.232 +
   4.233 +
   4.234 +#pragma mark -
   4.235 +#pragma mark TEST CASES:
   4.236 +
   4.237 +
   4.238 +#define $data(BYTES...)    ({const uint8_t bytes[] = {BYTES}; [NSData dataWithBytes: bytes length: sizeof(bytes)];})
   4.239 +
   4.240 +TestCase(ParseBER) {
   4.241 +    CAssertEqual(MYBERParse($data(0x05, 0x00), nil),
   4.242 +                 [NSNull null]);
   4.243 +    CAssertEqual(MYBERParse($data(0x01, 0x01, 0xFF), nil),
   4.244 +                 $true);
   4.245 +    CAssertEqual(MYBERParse($data(0x01, 0x01, 0x00), nil),
   4.246 +                 $false);
   4.247 +    
   4.248 +    // integers:
   4.249 +    CAssertEqual(MYBERParse($data(0x02, 0x01, 0x00), nil),
   4.250 +                 $object(0));
   4.251 +    CAssertEqual(MYBERParse($data(0x02, 0x01, 0x48), nil),
   4.252 +                 $object(72));
   4.253 +    CAssertEqual(MYBERParse($data(0x02, 0x01, 0x80), nil),
   4.254 +                 $object(-128));
   4.255 +    CAssertEqual(MYBERParse($data(0x02, 0x02, 0x00, 0x80), nil),
   4.256 +                 $object(128));
   4.257 +    CAssertEqual(MYBERParse($data(0x02, 0x02, 0x30,0x39), nil),
   4.258 +                 $object(12345));
   4.259 +    CAssertEqual(MYBERParse($data(0x02, 0x02, 0xCF, 0xC7), nil),
   4.260 +                 $object(-12345));
   4.261 +    CAssertEqual(MYBERParse($data(0x02, 0x04, 0x07, 0x5B, 0xCD, 0x15), nil),
   4.262 +                 $object(123456789));
   4.263 +    CAssertEqual(MYBERParse($data(0x02, 0x04, 0xF8, 0xA4, 0x32, 0xEB), nil),
   4.264 +                 $object(-123456789));
   4.265 +    CAssertEqual(MYBERParse($data(0x02, 0x04, 0xF8, 0xA4, 0x32, 0xEB), nil),
   4.266 +                 $object(-123456789));
   4.267 +    
   4.268 +    // octet strings:
   4.269 +    CAssertEqual(MYBERParse($data(0x04, 0x05, 'h', 'e', 'l', 'l', 'o'), nil),
   4.270 +                 [@"hello" dataUsingEncoding: NSASCIIStringEncoding]);
   4.271 +    CAssertEqual(MYBERParse($data(0x04, 0x00), nil),
   4.272 +                 [NSData data]);
   4.273 +    CAssertEqual(MYBERParse($data(0x0C, 0x05, 'h', 'e', 'l', 'l', 'o'), nil),
   4.274 +                 @"hello");
   4.275 +    
   4.276 +    // sequences:
   4.277 +    CAssertEqual(MYBERParse($data(0x30, 0x06,  0x02, 0x01, 0x48,  0x01, 0x01, 0xFF), nil),
   4.278 +                 $array($object(72), $true));
   4.279 +    CAssertEqual(MYBERParse($data(0x30, 0x10,  
   4.280 +                                  0x30, 0x06,  0x02, 0x01, 0x48,  0x01, 0x01, 0xFF,
   4.281 +                                  0x30, 0x06,  0x02, 0x01, 0x48,  0x01, 0x01, 0xFF), nil),
   4.282 +                 $array( $array($object(72), $true), $array($object(72), $true)));
   4.283 +}
   4.284 +
   4.285 +
   4.286 +TestCase(ParseCert) {
   4.287 +    NSData *cert = [NSData dataWithContentsOfFile: @"../../Tests/selfsigned.cer"];
   4.288 +    NSError *error = nil;
   4.289 +    id parsed = MYBERParse(cert,&error);
   4.290 +    CAssert(parsed);
   4.291 +    CAssertNil(error);
   4.292 +    NSString *dump = [MYASN1Object dump: parsed];
   4.293 +    CAssert(dump);
   4.294 +
   4.295 +    cert = [NSData dataWithContentsOfFile: @"../../Tests/iphonedev.cer"];
   4.296 +    parsed = MYBERParse(cert,&error);
   4.297 +    CAssert(parsed);
   4.298 +    CAssertNil(error);
   4.299 +    dump = [MYASN1Object dump: parsed];
   4.300 +    CAssert(dump);
   4.301 +}
     5.1 --- a/MYCertificate.h	Sun Apr 19 22:05:51 2009 -0700
     5.2 +++ b/MYCertificate.h	Tue Jun 02 13:16:28 2009 -0700
     5.3 @@ -12,7 +12,7 @@
     5.4  #import <Security/cssmtype.h>
     5.5  #endif
     5.6  
     5.7 -@class MYPublicKey;
     5.8 +@class MYPublicKey, MYIdentity;
     5.9  
    5.10  
    5.11  /** An X.509 certificate. */
    5.12 @@ -57,6 +57,9 @@
    5.13                            type: (CSSM_CERT_TYPE) type
    5.14                        encoding: (CSSM_CERT_ENCODING) encoding;
    5.15  
    5.16 +/** The Identity (if any) that this Certificate is part of. */
    5.17 +@property (readonly) MYIdentity *identity;
    5.18 +
    5.19  /** The list (if any) of the subject's email addresses. */
    5.20  @property (readonly) NSArray *emailAddresses;
    5.21  
     6.1 --- a/MYCertificate.m	Sun Apr 19 22:05:51 2009 -0700
     6.2 +++ b/MYCertificate.m	Tue Jun 02 13:16:28 2009 -0700
     6.3 @@ -8,6 +8,7 @@
     6.4  
     6.5  #import "MYCertificate.h"
     6.6  #import "MYCrypto_Private.h"
     6.7 +#import "MYIdentity.h"
     6.8  #import "MYDigest.h"
     6.9  #import "MYErrorUtils.h"
    6.10  
    6.11 @@ -103,6 +104,10 @@
    6.12      return key;
    6.13  }
    6.14  
    6.15 +- (MYIdentity*) identity {
    6.16 +    return [[[MYIdentity alloc] initWithCertificateRef: _certificateRef] autorelease];
    6.17 +}
    6.18 +
    6.19  - (NSString*) commonName {
    6.20      CFStringRef name = NULL;
    6.21      if (!check(SecCertificateCopyCommonName(_certificateRef, &name),
     7.1 --- a/MYCrypto-iPhone.xcodeproj/project.pbxproj	Sun Apr 19 22:05:51 2009 -0700
     7.2 +++ b/MYCrypto-iPhone.xcodeproj/project.pbxproj	Tue Jun 02 13:16:28 2009 -0700
     7.3 @@ -23,6 +23,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 +		27B8522D0FCEE6F9005631F9 /* MYASN1.m in Sources */ = {isa = PBXBuildFile; fileRef = 27B8522C0FCEE6F9005631F9 /* MYASN1.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 @@ -60,6 +61,8 @@
    7.12  		27A430130F87C6D50063D362 /* MYSymmetricKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYSymmetricKey.m; sourceTree = "<group>"; };
    7.13  		27A430150F87C6DB0063D362 /* MYSymmetricKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYSymmetricKey.h; sourceTree = "<group>"; };
    7.14  		27AAD9710F8927DB0064DD7C /* MYCryptoConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCryptoConfig.h; sourceTree = "<group>"; };
    7.15 +		27B8522B0FCEE6F9005631F9 /* MYASN1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYASN1.h; sourceTree = "<group>"; };
    7.16 +		27B8522C0FCEE6F9005631F9 /* MYASN1.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYASN1.m; sourceTree = "<group>"; };
    7.17  		27E3A6A60F91B8B30079D4D9 /* MYCryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCryptor.h; sourceTree = "<group>"; };
    7.18  		27E3A6A70F91B8B30079D4D9 /* MYCryptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYCryptor.m; sourceTree = "<group>"; };
    7.19  		27E8230C0F81D56E0019BE60 /* MYCertificate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCertificate.h; sourceTree = "<group>"; };
    7.20 @@ -139,6 +142,8 @@
    7.21  		27E8230B0F81D56E0019BE60 /* Source */ = {
    7.22  			isa = PBXGroup;
    7.23  			children = (
    7.24 +				27B8522B0FCEE6F9005631F9 /* MYASN1.h */,
    7.25 +				27B8522C0FCEE6F9005631F9 /* MYASN1.m */,
    7.26  				27AAD9710F8927DB0064DD7C /* MYCryptoConfig.h */,
    7.27  				27E8230C0F81D56E0019BE60 /* MYCertificate.h */,
    7.28  				276FB34A0F856CA400CB326E /* MYCertificate.m */,
    7.29 @@ -290,6 +295,7 @@
    7.30  				27059CF30F8F0F9200A8422F /* MYIdentity.m in Sources */,
    7.31  				27E3A6AA0F91B8B30079D4D9 /* MYCryptor.m in Sources */,
    7.32  				274110090F99234100AD413F /* MYSymmetricKey-iPhone.m in Sources */,
    7.33 +				27B8522D0FCEE6F9005631F9 /* MYASN1.m in Sources */,
    7.34  			);
    7.35  			runOnlyForDeploymentPostprocessing = 0;
    7.36  		};
    7.37 @@ -335,7 +341,7 @@
    7.38  				ONLY_ACTIVE_ARCH = YES;
    7.39  				PREBINDING = NO;
    7.40  				"PROVISIONING_PROFILE[sdk=iphoneos*]" = "84D61190-B2EB-4F8E-A189-99F18EE9A07E";
    7.41 -				SDKROOT = iphoneos3.0;
    7.42 +				SDKROOT = iphonesimulator2.2.1;
    7.43  			};
    7.44  			name = Debug;
    7.45  		};
    7.46 @@ -348,7 +354,7 @@
    7.47  				GCC_WARN_ABOUT_RETURN_TYPE = YES;
    7.48  				GCC_WARN_UNUSED_VARIABLE = YES;
    7.49  				PREBINDING = NO;
    7.50 -				SDKROOT = iphoneos3.0;
    7.51 +				SDKROOT = iphonesimulator2.2.1;
    7.52  			};
    7.53  			name = Release;
    7.54  		};
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/MYCrypto.h	Tue Jun 02 13:16:28 2009 -0700
     8.3 @@ -0,0 +1,15 @@
     8.4 +//
     8.5 +//  MYCrypto.h
     8.6 +//  MYCrypto
     8.7 +//
     8.8 +//  Created by Jens Alfke on 5/12/09.
     8.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
    8.10 +//
    8.11 +
    8.12 +
    8.13 +#import "MYDigest.h"
    8.14 +#import "MYKeychain.h"
    8.15 +#import "MYSymmetricKey.h"
    8.16 +#import "MYPublicKey.h"
    8.17 +#import "MYPrivateKey.h"
    8.18 +#import "MYIdentity.h"
     9.1 --- a/MYCrypto.xcodeproj/project.pbxproj	Sun Apr 19 22:05:51 2009 -0700
     9.2 +++ b/MYCrypto.xcodeproj/project.pbxproj	Tue Jun 02 13:16:28 2009 -0700
     9.3 @@ -11,6 +11,39 @@
     9.4  		27059D770F8FA23100A8422F /* MYCrypto+Cocoa.m in Sources */ = {isa = PBXBuildFile; fileRef = 27059D760F8FA23100A8422F /* MYCrypto+Cocoa.m */; };
     9.5  		27059D840F8FA82500A8422F /* SecurityInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27059D830F8FA82500A8422F /* SecurityInterface.framework */; };
     9.6  		27059DE50F8FAF6500A8422F /* MYDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 27059D520F8F9BB500A8422F /* MYDecoder.m */; };
     9.7 +		2706F1AE0F9D3C7100292CCF /* MYCertificate.h in Headers */ = {isa = PBXBuildFile; fileRef = 27CFF4B10F7E8535000B418E /* MYCertificate.h */; };
     9.8 +		2706F1AF0F9D3C7100292CCF /* MYCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CFF4B20F7E8535000B418E /* MYCertificate.m */; };
     9.9 +		2706F1B00F9D3C7200292CCF /* MYCrypto+Cocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = 27059D750F8FA23100A8422F /* MYCrypto+Cocoa.h */; };
    9.10 +		2706F1B10F9D3C7300292CCF /* MYCrypto+Cocoa.m in Sources */ = {isa = PBXBuildFile; fileRef = 27059D760F8FA23100A8422F /* MYCrypto+Cocoa.m */; };
    9.11 +		2706F1B20F9D3C7400292CCF /* MYCryptoConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 27AAD97B0F892A0D0064DD7C /* MYCryptoConfig.h */; };
    9.12 +		2706F1B30F9D3C7600292CCF /* MYDigest.h in Headers */ = {isa = PBXBuildFile; fileRef = 27CFF4BF0F7E8535000B418E /* MYDigest.h */; };
    9.13 +		2706F1B40F9D3C7700292CCF /* MYDigest.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CFF4C00F7E8535000B418E /* MYDigest.m */; };
    9.14 +		2706F1B50F9D3C7700292CCF /* MYIdentity.h in Headers */ = {isa = PBXBuildFile; fileRef = 274863A00F8EF39400FE617B /* MYIdentity.h */; };
    9.15 +		2706F1B60F9D3C7800292CCF /* MYIdentity.m in Sources */ = {isa = PBXBuildFile; fileRef = 274863A10F8EF39400FE617B /* MYIdentity.m */; };
    9.16 +		2706F1B70F9D3C7900292CCF /* MYKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 27E822A00F81C5660019BE60 /* MYKey.h */; };
    9.17 +		2706F1B80F9D3C7A00292CCF /* MYKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E822A10F81C5660019BE60 /* MYKey.m */; };
    9.18 +		2706F1B90F9D3C7C00292CCF /* MYKeychain.h in Headers */ = {isa = PBXBuildFile; fileRef = 27CFF4B50F7E8535000B418E /* MYKeychain.h */; };
    9.19 +		2706F1BA0F9D3C7D00292CCF /* MYKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CFF4B60F7E8535000B418E /* MYKeychain.m */; };
    9.20 +		2706F1BB0F9D3C7D00292CCF /* MYKeychainItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 27CFF4B70F7E8535000B418E /* MYKeychainItem.h */; };
    9.21 +		2706F1BC0F9D3C7E00292CCF /* MYKeychainItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CFF4B80F7E8535000B418E /* MYKeychainItem.m */; };
    9.22 +		2706F1BD0F9D3C7E00292CCF /* MYPrivateKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 270B879D0F8C565000C56781 /* MYPrivateKey.h */; };
    9.23 +		2706F1BE0F9D3C7F00292CCF /* MYPrivateKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 270B879E0F8C565000C56781 /* MYPrivateKey.m */; };
    9.24 +		2706F1BF0F9D3C8000292CCF /* MYPublicKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 27CFF4BC0F7E8535000B418E /* MYPublicKey.h */; };
    9.25 +		2706F1C00F9D3C8000292CCF /* MYPublicKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CFF4BD0F7E8535000B418E /* MYPublicKey.m */; };
    9.26 +		2706F1C10F9D3C8100292CCF /* MYSymmetricKey-iPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = 27410FEF0F99200A00AD413F /* MYSymmetricKey-iPhone.m */; };
    9.27 +		2706F1C20F9D3C8200292CCF /* MYSymmetricKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 27A42D400F858ED80063D362 /* MYSymmetricKey.h */; };
    9.28 +		2706F1C30F9D3C8300292CCF /* MYSymmetricKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A42D410F858ED80063D362 /* MYSymmetricKey.m */; };
    9.29 +		2706F1C40F9D3C8800292CCF /* MYCryptor.h in Headers */ = {isa = PBXBuildFile; fileRef = 27CFF4B30F7E8535000B418E /* MYCryptor.h */; };
    9.30 +		2706F1C50F9D3C8900292CCF /* MYCryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CFF4B40F7E8535000B418E /* MYCryptor.m */; };
    9.31 +		2706F1C60F9D3C8900292CCF /* MYDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 27059D510F8F9BB500A8422F /* MYDecoder.h */; };
    9.32 +		2706F1C70F9D3C8A00292CCF /* MYDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 27059D520F8F9BB500A8422F /* MYDecoder.m */; };
    9.33 +		2706F1C80F9D3C8B00292CCF /* MYEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 27059D4F0F8F9BB500A8422F /* MYEncoder.h */; };
    9.34 +		2706F1C90F9D3C8B00292CCF /* MYEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 27059D500F8F9BB500A8422F /* MYEncoder.m */; };
    9.35 +		2706F1CA0F9D3C9200292CCF /* MYCertGen.h in Headers */ = {isa = PBXBuildFile; fileRef = 27A42ECC0F8689D30063D362 /* MYCertGen.h */; };
    9.36 +		2706F1CB0F9D3C9300292CCF /* MYCertGen.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A42ECD0F8689D30063D362 /* MYCertGen.m */; };
    9.37 +		270A7A730FD58FF200770C4D /* MYBERParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 270A7A710FD58FF200770C4D /* MYBERParser.h */; };
    9.38 +		270A7A740FD58FF200770C4D /* MYBERParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 270A7A720FD58FF200770C4D /* MYBERParser.m */; };
    9.39 +		270A7A750FD58FF200770C4D /* MYBERParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 270A7A720FD58FF200770C4D /* MYBERParser.m */; };
    9.40  		270B879F0F8C565000C56781 /* MYPrivateKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 270B879E0F8C565000C56781 /* MYPrivateKey.m */; };
    9.41  		27410FF00F99200A00AD413F /* MYSymmetricKey-iPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = 27410FEF0F99200A00AD413F /* MYSymmetricKey-iPhone.m */; };
    9.42  		274861D50F8E4B5200FE617B /* MYCertGen.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A42ECD0F8689D30063D362 /* MYCertGen.m */; };
    9.43 @@ -18,6 +51,15 @@
    9.44  		27A42CBF0F8578B40063D362 /* MYCryptoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A42CBE0F8578B40063D362 /* MYCryptoTest.m */; };
    9.45  		27A42D1E0F8586CE0063D362 /* MYKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 27E822A10F81C5660019BE60 /* MYKey.m */; };
    9.46  		27A42D420F858ED80063D362 /* MYSymmetricKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A42D410F858ED80063D362 /* MYSymmetricKey.m */; };
    9.47 +		27B852F50FCF4EB7005631F9 /* MYASN1Object.m in Sources */ = {isa = PBXBuildFile; fileRef = 27B852F40FCF4EB7005631F9 /* MYASN1Object.m */; };
    9.48 +		27B852F60FCF4EB7005631F9 /* MYASN1Object.h in Headers */ = {isa = PBXBuildFile; fileRef = 27B852F30FCF4EB6005631F9 /* MYASN1Object.h */; };
    9.49 +		27B852F70FCF4EB7005631F9 /* MYASN1Object.m in Sources */ = {isa = PBXBuildFile; fileRef = 27B852F40FCF4EB7005631F9 /* MYASN1Object.m */; };
    9.50 +		27B852FE0FCF4ECB005631F9 /* MYOID.h in Headers */ = {isa = PBXBuildFile; fileRef = 27B852FC0FCF4ECB005631F9 /* MYOID.h */; };
    9.51 +		27B852FF0FCF4ECB005631F9 /* MYOID.m in Sources */ = {isa = PBXBuildFile; fileRef = 27B852FD0FCF4ECB005631F9 /* MYOID.m */; };
    9.52 +		27B853000FCF4ECB005631F9 /* MYOID.m in Sources */ = {isa = PBXBuildFile; fileRef = 27B852FD0FCF4ECB005631F9 /* MYOID.m */; };
    9.53 +		27B855270FD077A6005631F9 /* MYDEREncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 27B855250FD077A6005631F9 /* MYDEREncoder.h */; };
    9.54 +		27B855280FD077A7005631F9 /* MYDEREncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 27B855260FD077A6005631F9 /* MYDEREncoder.m */; };
    9.55 +		27B855290FD077A7005631F9 /* MYDEREncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 27B855260FD077A6005631F9 /* MYDEREncoder.m */; };
    9.56  		27CFF4C10F7E8535000B418E /* MYCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CFF4B20F7E8535000B418E /* MYCertificate.m */; };
    9.57  		27CFF4C20F7E8535000B418E /* MYCryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CFF4B40F7E8535000B418E /* MYCryptor.m */; };
    9.58  		27CFF4C30F7E8535000B418E /* MYKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CFF4B60F7E8535000B418E /* MYKeychain.m */; };
    9.59 @@ -32,6 +74,7 @@
    9.60  		27CFF5220F7E94DF000B418E /* MYCrypto_main.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CFF5210F7E94DF000B418E /* MYCrypto_main.m */; };
    9.61  		27CFF5760F7E999B000B418E /* MYErrorUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 27CFF5750F7E999B000B418E /* MYErrorUtils.m */; };
    9.62  		27E820720F7EA6260019BE60 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27E820710F7EA6260019BE60 /* CoreServices.framework */; };
    9.63 +		27FEB3E70FBA63D200290049 /* MYCrypto.h in Headers */ = {isa = PBXBuildFile; fileRef = 27FEB3E60FBA63D200290049 /* MYCrypto.h */; };
    9.64  		8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; };
    9.65  /* End PBXBuildFile section */
    9.66  
    9.67 @@ -56,6 +99,9 @@
    9.68  		27059D750F8FA23100A8422F /* MYCrypto+Cocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MYCrypto+Cocoa.h"; sourceTree = "<group>"; };
    9.69  		27059D760F8FA23100A8422F /* MYCrypto+Cocoa.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MYCrypto+Cocoa.m"; sourceTree = "<group>"; };
    9.70  		27059D830F8FA82500A8422F /* SecurityInterface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SecurityInterface.framework; path = /System/Library/Frameworks/SecurityInterface.framework; sourceTree = "<absolute>"; };
    9.71 +		2706F1AB0F9D3C5F00292CCF /* libMYCrypto.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libMYCrypto.a; sourceTree = BUILT_PRODUCTS_DIR; };
    9.72 +		270A7A710FD58FF200770C4D /* MYBERParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYBERParser.h; sourceTree = "<group>"; };
    9.73 +		270A7A720FD58FF200770C4D /* MYBERParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYBERParser.m; sourceTree = "<group>"; };
    9.74  		270B879D0F8C565000C56781 /* MYPrivateKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYPrivateKey.h; sourceTree = "<group>"; };
    9.75  		270B879E0F8C565000C56781 /* MYPrivateKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYPrivateKey.m; sourceTree = "<group>"; };
    9.76  		27410FEF0F99200A00AD413F /* MYSymmetricKey-iPhone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MYSymmetricKey-iPhone.m"; sourceTree = "<group>"; };
    9.77 @@ -68,6 +114,12 @@
    9.78  		27A42ECC0F8689D30063D362 /* MYCertGen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCertGen.h; sourceTree = "<group>"; };
    9.79  		27A42ECD0F8689D30063D362 /* MYCertGen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYCertGen.m; sourceTree = "<group>"; };
    9.80  		27AAD97B0F892A0D0064DD7C /* MYCryptoConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCryptoConfig.h; sourceTree = "<group>"; };
    9.81 +		27B852F30FCF4EB6005631F9 /* MYASN1Object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYASN1Object.h; sourceTree = "<group>"; };
    9.82 +		27B852F40FCF4EB7005631F9 /* MYASN1Object.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYASN1Object.m; sourceTree = "<group>"; };
    9.83 +		27B852FC0FCF4ECB005631F9 /* MYOID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYOID.h; sourceTree = "<group>"; };
    9.84 +		27B852FD0FCF4ECB005631F9 /* MYOID.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYOID.m; sourceTree = "<group>"; };
    9.85 +		27B855250FD077A6005631F9 /* MYDEREncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYDEREncoder.h; sourceTree = "<group>"; };
    9.86 +		27B855260FD077A6005631F9 /* MYDEREncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYDEREncoder.m; sourceTree = "<group>"; };
    9.87  		27CFF4B10F7E8535000B418E /* MYCertificate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCertificate.h; sourceTree = "<group>"; };
    9.88  		27CFF4B20F7E8535000B418E /* MYCertificate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYCertificate.m; sourceTree = "<group>"; };
    9.89  		27CFF4B30F7E8535000B418E /* MYCryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCryptor.h; sourceTree = "<group>"; };
    9.90 @@ -100,10 +152,18 @@
    9.91  		27E822A00F81C5660019BE60 /* MYKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYKey.h; sourceTree = "<group>"; };
    9.92  		27E822A10F81C5660019BE60 /* MYKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYKey.m; sourceTree = "<group>"; };
    9.93  		27EAF0390F8B2D700091AF95 /* README.textile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.textile; sourceTree = "<group>"; };
    9.94 +		27FEB3E60FBA63D200290049 /* MYCrypto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYCrypto.h; sourceTree = "<group>"; };
    9.95  		8DD76FA10486AA7600D96B5E /* MYCrypto */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = MYCrypto; sourceTree = BUILT_PRODUCTS_DIR; };
    9.96  /* End PBXFileReference section */
    9.97  
    9.98  /* Begin PBXFrameworksBuildPhase section */
    9.99 +		2706F1A90F9D3C5F00292CCF /* Frameworks */ = {
   9.100 +			isa = PBXFrameworksBuildPhase;
   9.101 +			buildActionMask = 2147483647;
   9.102 +			files = (
   9.103 +			);
   9.104 +			runOnlyForDeploymentPostprocessing = 0;
   9.105 +		};
   9.106  		8DD76F9B0486AA7600D96B5E /* Frameworks */ = {
   9.107  			isa = PBXFrameworksBuildPhase;
   9.108  			buildActionMask = 2147483647;
   9.109 @@ -123,7 +183,7 @@
   9.110  			children = (
   9.111  				08FB7795FE84155DC02AAC07 /* Source */,
   9.112  				27059D4E0F8F9B9E00A8422F /* Encryption */,
   9.113 -				274861440F8D757600FE617B /* Certificate Generation */,
   9.114 +				274861440F8D757600FE617B /* Certificates */,
   9.115  				270B881C0F8D055A00C56781 /* Internal */,
   9.116  				27CFF4CC0F7E86E8000B418E /* MYUtilities */,
   9.117  				C6859EA2029092E104C91782 /* Documentation */,
   9.118 @@ -162,6 +222,7 @@
   9.119  				27059D750F8FA23100A8422F /* MYCrypto+Cocoa.h */,
   9.120  				27059D760F8FA23100A8422F /* MYCrypto+Cocoa.m */,
   9.121  				27EAF0390F8B2D700091AF95 /* README.textile */,
   9.122 +				27FEB3E60FBA63D200290049 /* MYCrypto.h */,
   9.123  			);
   9.124  			name = Source;
   9.125  			sourceTree = "<group>";
   9.126 @@ -178,6 +239,7 @@
   9.127  			isa = PBXGroup;
   9.128  			children = (
   9.129  				8DD76FA10486AA7600D96B5E /* MYCrypto */,
   9.130 +				2706F1AB0F9D3C5F00292CCF /* libMYCrypto.a */,
   9.131  			);
   9.132  			name = Products;
   9.133  			sourceTree = "<group>";
   9.134 @@ -208,13 +270,21 @@
   9.135  			name = Internal;
   9.136  			sourceTree = "<group>";
   9.137  		};
   9.138 -		274861440F8D757600FE617B /* Certificate Generation */ = {
   9.139 +		274861440F8D757600FE617B /* Certificates */ = {
   9.140  			isa = PBXGroup;
   9.141  			children = (
   9.142  				27A42ECC0F8689D30063D362 /* MYCertGen.h */,
   9.143  				27A42ECD0F8689D30063D362 /* MYCertGen.m */,
   9.144 +				27B852F30FCF4EB6005631F9 /* MYASN1Object.h */,
   9.145 +				27B852F40FCF4EB7005631F9 /* MYASN1Object.m */,
   9.146 +				270A7A710FD58FF200770C4D /* MYBERParser.h */,
   9.147 +				270A7A720FD58FF200770C4D /* MYBERParser.m */,
   9.148 +				27B855250FD077A6005631F9 /* MYDEREncoder.h */,
   9.149 +				27B855260FD077A6005631F9 /* MYDEREncoder.m */,
   9.150 +				27B852FC0FCF4ECB005631F9 /* MYOID.h */,
   9.151 +				27B852FD0FCF4ECB005631F9 /* MYOID.m */,
   9.152  			);
   9.153 -			name = "Certificate Generation";
   9.154 +			name = Certificates;
   9.155  			sourceTree = "<group>";
   9.156  		};
   9.157  		27CFF4CC0F7E86E8000B418E /* MYUtilities */ = {
   9.158 @@ -244,7 +314,54 @@
   9.159  		};
   9.160  /* End PBXGroup section */
   9.161  
   9.162 +/* Begin PBXHeadersBuildPhase section */
   9.163 +		2706F1A70F9D3C5F00292CCF /* Headers */ = {
   9.164 +			isa = PBXHeadersBuildPhase;
   9.165 +			buildActionMask = 2147483647;
   9.166 +			files = (
   9.167 +				2706F1AE0F9D3C7100292CCF /* MYCertificate.h in Headers */,
   9.168 +				2706F1B00F9D3C7200292CCF /* MYCrypto+Cocoa.h in Headers */,
   9.169 +				2706F1B20F9D3C7400292CCF /* MYCryptoConfig.h in Headers */,
   9.170 +				2706F1B30F9D3C7600292CCF /* MYDigest.h in Headers */,
   9.171 +				2706F1B50F9D3C7700292CCF /* MYIdentity.h in Headers */,
   9.172 +				2706F1B70F9D3C7900292CCF /* MYKey.h in Headers */,
   9.173 +				2706F1B90F9D3C7C00292CCF /* MYKeychain.h in Headers */,
   9.174 +				2706F1BB0F9D3C7D00292CCF /* MYKeychainItem.h in Headers */,
   9.175 +				2706F1BD0F9D3C7E00292CCF /* MYPrivateKey.h in Headers */,
   9.176 +				2706F1BF0F9D3C8000292CCF /* MYPublicKey.h in Headers */,
   9.177 +				2706F1C20F9D3C8200292CCF /* MYSymmetricKey.h in Headers */,
   9.178 +				2706F1C40F9D3C8800292CCF /* MYCryptor.h in Headers */,
   9.179 +				2706F1C60F9D3C8900292CCF /* MYDecoder.h in Headers */,
   9.180 +				2706F1C80F9D3C8B00292CCF /* MYEncoder.h in Headers */,
   9.181 +				2706F1CA0F9D3C9200292CCF /* MYCertGen.h in Headers */,
   9.182 +				27FEB3E70FBA63D200290049 /* MYCrypto.h in Headers */,
   9.183 +				27B852F60FCF4EB7005631F9 /* MYASN1Object.h in Headers */,
   9.184 +				27B852FE0FCF4ECB005631F9 /* MYOID.h in Headers */,
   9.185 +				27B855270FD077A6005631F9 /* MYDEREncoder.h in Headers */,
   9.186 +				270A7A730FD58FF200770C4D /* MYBERParser.h in Headers */,
   9.187 +			);
   9.188 +			runOnlyForDeploymentPostprocessing = 0;
   9.189 +		};
   9.190 +/* End PBXHeadersBuildPhase section */
   9.191 +
   9.192  /* Begin PBXNativeTarget section */
   9.193 +		2706F1AA0F9D3C5F00292CCF /* Static Lib */ = {
   9.194 +			isa = PBXNativeTarget;
   9.195 +			buildConfigurationList = 2706F1CE0F9D3CDD00292CCF /* Build configuration list for PBXNativeTarget "Static Lib" */;
   9.196 +			buildPhases = (
   9.197 +				2706F1A70F9D3C5F00292CCF /* Headers */,
   9.198 +				2706F1A80F9D3C5F00292CCF /* Sources */,
   9.199 +				2706F1A90F9D3C5F00292CCF /* Frameworks */,
   9.200 +			);
   9.201 +			buildRules = (
   9.202 +			);
   9.203 +			dependencies = (
   9.204 +			);
   9.205 +			name = "Static Lib";
   9.206 +			productName = "Static Lib";
   9.207 +			productReference = 2706F1AB0F9D3C5F00292CCF /* libMYCrypto.a */;
   9.208 +			productType = "com.apple.product-type.library.static";
   9.209 +		};
   9.210  		8DD76F960486AA7600D96B5E /* MYCrypto */ = {
   9.211  			isa = PBXNativeTarget;
   9.212  			buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "MYCrypto" */;
   9.213 @@ -277,6 +394,7 @@
   9.214  			projectRoot = "";
   9.215  			targets = (
   9.216  				8DD76F960486AA7600D96B5E /* MYCrypto */,
   9.217 +				2706F1AA0F9D3C5F00292CCF /* Static Lib */,
   9.218  			);
   9.219  		};
   9.220  /* End PBXProject section */
   9.221 @@ -299,6 +417,32 @@
   9.222  /* End PBXShellScriptBuildPhase section */
   9.223  
   9.224  /* Begin PBXSourcesBuildPhase section */
   9.225 +		2706F1A80F9D3C5F00292CCF /* Sources */ = {
   9.226 +			isa = PBXSourcesBuildPhase;
   9.227 +			buildActionMask = 2147483647;
   9.228 +			files = (
   9.229 +				2706F1AF0F9D3C7100292CCF /* MYCertificate.m in Sources */,
   9.230 +				2706F1B10F9D3C7300292CCF /* MYCrypto+Cocoa.m in Sources */,
   9.231 +				2706F1B40F9D3C7700292CCF /* MYDigest.m in Sources */,
   9.232 +				2706F1B60F9D3C7800292CCF /* MYIdentity.m in Sources */,
   9.233 +				2706F1B80F9D3C7A00292CCF /* MYKey.m in Sources */,
   9.234 +				2706F1BA0F9D3C7D00292CCF /* MYKeychain.m in Sources */,
   9.235 +				2706F1BC0F9D3C7E00292CCF /* MYKeychainItem.m in Sources */,
   9.236 +				2706F1BE0F9D3C7F00292CCF /* MYPrivateKey.m in Sources */,
   9.237 +				2706F1C00F9D3C8000292CCF /* MYPublicKey.m in Sources */,
   9.238 +				2706F1C10F9D3C8100292CCF /* MYSymmetricKey-iPhone.m in Sources */,
   9.239 +				2706F1C30F9D3C8300292CCF /* MYSymmetricKey.m in Sources */,
   9.240 +				2706F1C50F9D3C8900292CCF /* MYCryptor.m in Sources */,
   9.241 +				2706F1C70F9D3C8A00292CCF /* MYDecoder.m in Sources */,
   9.242 +				2706F1C90F9D3C8B00292CCF /* MYEncoder.m in Sources */,
   9.243 +				2706F1CB0F9D3C9300292CCF /* MYCertGen.m in Sources */,
   9.244 +				27B852F70FCF4EB7005631F9 /* MYASN1Object.m in Sources */,
   9.245 +				27B852FF0FCF4ECB005631F9 /* MYOID.m in Sources */,
   9.246 +				27B855280FD077A7005631F9 /* MYDEREncoder.m in Sources */,
   9.247 +				270A7A740FD58FF200770C4D /* MYBERParser.m in Sources */,
   9.248 +			);
   9.249 +			runOnlyForDeploymentPostprocessing = 0;
   9.250 +		};
   9.251  		8DD76F990486AA7600D96B5E /* Sources */ = {
   9.252  			isa = PBXSourcesBuildPhase;
   9.253  			buildActionMask = 2147483647;
   9.254 @@ -325,6 +469,10 @@
   9.255  				27059D770F8FA23100A8422F /* MYCrypto+Cocoa.m in Sources */,
   9.256  				27059DE50F8FAF6500A8422F /* MYDecoder.m in Sources */,
   9.257  				27410FF00F99200A00AD413F /* MYSymmetricKey-iPhone.m in Sources */,
   9.258 +				27B852F50FCF4EB7005631F9 /* MYASN1Object.m in Sources */,
   9.259 +				27B853000FCF4ECB005631F9 /* MYOID.m in Sources */,
   9.260 +				27B855290FD077A7005631F9 /* MYDEREncoder.m in Sources */,
   9.261 +				270A7A750FD58FF200770C4D /* MYBERParser.m in Sources */,
   9.262  			);
   9.263  			runOnlyForDeploymentPostprocessing = 0;
   9.264  		};
   9.265 @@ -368,6 +516,27 @@
   9.266  			};
   9.267  			name = Release;
   9.268  		};
   9.269 +		2706F1AC0F9D3C6200292CCF /* Debug */ = {
   9.270 +			isa = XCBuildConfiguration;
   9.271 +			buildSettings = {
   9.272 +				ALWAYS_SEARCH_USER_PATHS = NO;
   9.273 +				COPY_PHASE_STRIP = NO;
   9.274 +				INSTALL_PATH = /usr/local/lib;
   9.275 +				PRODUCT_NAME = MYCrypto;
   9.276 +			};
   9.277 +			name = Debug;
   9.278 +		};
   9.279 +		2706F1AD0F9D3C6200292CCF /* Release */ = {
   9.280 +			isa = XCBuildConfiguration;
   9.281 +			buildSettings = {
   9.282 +				ALWAYS_SEARCH_USER_PATHS = NO;
   9.283 +				COPY_PHASE_STRIP = YES;
   9.284 +				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
   9.285 +				INSTALL_PATH = /usr/local/lib;
   9.286 +				PRODUCT_NAME = MYCrypto;
   9.287 +			};
   9.288 +			name = Release;
   9.289 +		};
   9.290  /* End XCBuildConfiguration section */
   9.291  
   9.292  /* Begin XCConfigurationList section */
   9.293 @@ -389,6 +558,15 @@
   9.294  			defaultConfigurationIsVisible = 0;
   9.295  			defaultConfigurationName = Release;
   9.296  		};
   9.297 +		2706F1CE0F9D3CDD00292CCF /* Build configuration list for PBXNativeTarget "Static Lib" */ = {
   9.298 +			isa = XCConfigurationList;
   9.299 +			buildConfigurations = (
   9.300 +				2706F1AC0F9D3C6200292CCF /* Debug */,
   9.301 +				2706F1AD0F9D3C6200292CCF /* Release */,
   9.302 +			);
   9.303 +			defaultConfigurationIsVisible = 0;
   9.304 +			defaultConfigurationName = Release;
   9.305 +		};
   9.306  /* End XCConfigurationList section */
   9.307  	};
   9.308  	rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
    10.1 --- a/MYCryptoTest.m	Sun Apr 19 22:05:51 2009 -0700
    10.2 +++ b/MYCryptoTest.m	Tue Jun 02 13:16:28 2009 -0700
    10.3 @@ -155,7 +155,7 @@
    10.4      #endif
    10.5  
    10.6      #if !TARGET_OS_IPHONE
    10.7 -#if 0 // TEMPORARILY OUT OF ORDER
    10.8 +#if 1 // TEMP-ORARILY OUT OF ORDER
    10.9          // Try exporting and importing a wrapped key:
   10.10          Log(@"Testing export/import...");
   10.11          NSData *exported = [key exportWrappedKeyWithPassphrasePrompt: @"Export symmetric key with passphrase:"];
   10.12 @@ -163,14 +163,13 @@
   10.13      #if 1
   10.14          CAssert(exported);
   10.15      #else
   10.16 -        //FIX: Exporting symmetric keys isn't working. Temporarily making this optional.
   10.17          if (!exported)
   10.18              Warn(@"Unable to export wrapped key");
   10.19          else
   10.20      #endif
   10.21          {
   10.22              CAssert(exported);
   10.23 -            MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithKeyData: exported algorithm: algorithm];
   10.24 +            MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithWrappedKeyData: exported];
   10.25              Log(@"Reconstituted as %@", key2);
   10.26              CAssertEqual(key2.keyData,key.keyData);
   10.27              decrypted = [key2 decryptData: encrypted];
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/MYDEREncoder.h	Tue Jun 02 13:16:28 2009 -0700
    11.3 @@ -0,0 +1,25 @@
    11.4 +//
    11.5 +//  MYDEREncoder.h
    11.6 +//  MYCrypto
    11.7 +//
    11.8 +//  Created by Jens Alfke on 5/29/09.
    11.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
   11.10 +//
   11.11 +
   11.12 +#import <Foundation/Foundation.h>
   11.13 +
   11.14 +
   11.15 +@interface MYDEREncoder : NSObject
   11.16 +{
   11.17 +    id _rootObject;
   11.18 +    NSMutableData *_output;
   11.19 +    NSError *_error;
   11.20 +}
   11.21 +
   11.22 +- (id) initWithRootObject: (id)object;
   11.23 ++ (NSData*) encodeRootObject: (id)rootObject error: (NSError**)outError;
   11.24 +
   11.25 +@property (readonly) NSData* output;
   11.26 +@property (readonly, retain) NSError *error;
   11.27 +
   11.28 +@end
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/MYDEREncoder.m	Tue Jun 02 13:16:28 2009 -0700
    12.3 @@ -0,0 +1,352 @@
    12.4 +//
    12.5 +//  MYDEREncoder.m
    12.6 +//  MYCrypto
    12.7 +//
    12.8 +//  Created by Jens Alfke on 5/29/09.
    12.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
   12.10 +//
   12.11 +
   12.12 +#import "MYDEREncoder.h"
   12.13 +#import "MYASN1Object.h"
   12.14 +#import "MYBERParser.h"
   12.15 +#import "MYOID.h"
   12.16 +#import "MYErrorUtils.h"
   12.17 +
   12.18 +
   12.19 +#define MYDEREncoderException @"MYDEREncoderException"
   12.20 +
   12.21 +
   12.22 +@interface MYDEREncoder ()
   12.23 +- (void) _encode: (id)object;
   12.24 +@property (retain) NSError *error;
   12.25 +@end
   12.26 +
   12.27 +
   12.28 +@implementation MYDEREncoder
   12.29 +
   12.30 +
   12.31 +- (id) initWithRootObject: (id)rootObject
   12.32 +{
   12.33 +    self = [super init];
   12.34 +    if (self != nil) {
   12.35 +        _rootObject = [rootObject retain];
   12.36 +    }
   12.37 +    return self;
   12.38 +}
   12.39 +
   12.40 ++ (NSData*) encodeRootObject: (id)rootObject error: (NSError**)outError {
   12.41 +    MYDEREncoder *encoder = [[self alloc] initWithRootObject: rootObject];
   12.42 +    NSData *output = [encoder.output copy];
   12.43 +    if (outError) *outError = [[encoder.error retain] autorelease];
   12.44 +    [encoder release];
   12.45 +    return [output autorelease];
   12.46 +}
   12.47 +
   12.48 +- (void) dealloc
   12.49 +{
   12.50 +    [_rootObject release];
   12.51 +    [_output release];
   12.52 +    [super dealloc];
   12.53 +}
   12.54 +
   12.55 +
   12.56 +
   12.57 +static unsigned sizeOfUnsignedInt (UInt64 n) {
   12.58 +    unsigned bytes = 0;
   12.59 +    for (; n; n >>= 8)
   12.60 +        bytes++;
   12.61 +    return bytes;
   12.62 +}
   12.63 +
   12.64 +static unsigned encodeUnsignedInt (UInt64 n, UInt8 buf[], BOOL padHighBit) {
   12.65 +    unsigned size = MAX(1U, sizeOfUnsignedInt(n));
   12.66 +    UInt64 bigEndian = NSSwapHostLongLongToBig(n);
   12.67 +    const UInt8* src = (UInt8*)&bigEndian + (8-size);
   12.68 +    UInt8 *dst = &buf[0];
   12.69 +    if (padHighBit && (*src & 0x80)) {
   12.70 +        *dst++ = 0;
   12.71 +        size++;
   12.72 +    }
   12.73 +    memcpy(dst, src, size);
   12.74 +    return size;
   12.75 +}
   12.76 +
   12.77 +static unsigned encodeSignedInt (SInt64 n, UInt8 buf[]) {
   12.78 +    if (n >= 0)
   12.79 +        return encodeUnsignedInt(n, buf, YES);
   12.80 +    else {
   12.81 +        unsigned size = MAX(1U, sizeOfUnsignedInt(~n));
   12.82 +        UInt64 bigEndian = NSSwapHostLongLongToBig(n);
   12.83 +        const UInt8* src = (UInt8*)&bigEndian + (8-size);
   12.84 +        UInt8 *dst = &buf[0];
   12.85 +        if (!(*src & 0x80)) {
   12.86 +            *dst++ = 0xFF;
   12.87 +            size++;
   12.88 +        }
   12.89 +        memcpy(dst, src, size);
   12.90 +        return size;
   12.91 +    }
   12.92 +}
   12.93 +
   12.94 +
   12.95 +- (void) _writeTag: (unsigned)tag
   12.96 +             class: (unsigned)tagClass
   12.97 +       constructed: (BOOL) constructed
   12.98 +            length: (size_t)length 
   12.99 +{
  12.100 +    struct {
  12.101 +        unsigned tag            :5;
  12.102 +        unsigned isConstructed  :1;
  12.103 +        unsigned tagClass       :2;
  12.104 +        unsigned length         :7;
  12.105 +        unsigned isLengthLong   :1;
  12.106 +        UInt8    extraLength[9];
  12.107 +    } header;
  12.108 +    size_t headerSize = 2;
  12.109 +    
  12.110 +    header.tag = tag;
  12.111 +    header.isConstructed = constructed;
  12.112 +    header.tagClass = tagClass;
  12.113 +    if (length < 128) {
  12.114 +        header.isLengthLong = NO;
  12.115 +        header.length = length;
  12.116 +    } else {
  12.117 +        header.isLengthLong = YES;
  12.118 +        header.length = encodeUnsignedInt(length, header.extraLength, NO);
  12.119 +        headerSize += header.length;
  12.120 +    }
  12.121 +    [_output appendBytes: &header length: headerSize];
  12.122 +}
  12.123 +
  12.124 +- (void) _writeTag: (unsigned)tag
  12.125 +             class: (unsigned)tagClass
  12.126 +       constructed: (BOOL) constructed
  12.127 +             bytes: (const void*)bytes 
  12.128 +            length: (size_t)length 
  12.129 +{
  12.130 +    [self _writeTag: tag class: tagClass constructed: constructed length: length];
  12.131 +    [_output appendBytes: bytes length: length];
  12.132 +}
  12.133 +
  12.134 +- (void) _writeTag: (unsigned)tag
  12.135 +             class: (unsigned)tagClass
  12.136 +       constructed: (BOOL) constructed
  12.137 +              data: (NSData*)data 
  12.138 +{
  12.139 +    Assert(data);
  12.140 +    [self _writeTag: tag class: tagClass constructed: constructed bytes: data.bytes length: data.length];
  12.141 +}
  12.142 +
  12.143 +
  12.144 +- (void) _encodeNumber: (NSNumber*)number {
  12.145 +    // Special-case detection of booleans by pointer equality, because otherwise they appear
  12.146 +    // identical to 0 and 1:
  12.147 +    if (number==$true || number==$false) {
  12.148 +        UInt8 value = number==$true ?0xFF :0x00;
  12.149 +        [self _writeTag: 1 class: 0 constructed: NO bytes: &value length: 1];
  12.150 +        return;
  12.151 +    }
  12.152 +    
  12.153 +    const char *type = number.objCType;
  12.154 +    if (strlen(type) == 1) {
  12.155 +        switch(type[0]) {
  12.156 +            case 'c':
  12.157 +            case 'i':
  12.158 +            case 's':
  12.159 +            case 'l':
  12.160 +            case 'q':
  12.161 +            {   // Signed integers:
  12.162 +                UInt8 buf[9];
  12.163 +                size_t size = encodeSignedInt(number.longLongValue, buf);
  12.164 +                [self _writeTag: 2 class: 0 constructed: NO bytes: buf length: size];
  12.165 +                return;
  12.166 +            }
  12.167 +            case 'C':
  12.168 +            case 'I':
  12.169 +            case 'S':
  12.170 +            case 'L':
  12.171 +            case 'Q':
  12.172 +            {   // Unsigned integers:
  12.173 +                UInt8 buf[9];
  12.174 +                size_t size = encodeUnsignedInt(number.unsignedLongLongValue, buf, YES);
  12.175 +                [self _writeTag: 2 class: 0 constructed: NO bytes: buf length: size];
  12.176 +                return;
  12.177 +            }
  12.178 +            case 'B':
  12.179 +            {   // bool
  12.180 +                UInt8 value = number.boolValue ?0xFF :0x00;
  12.181 +                [self _writeTag: 1 class: 0 constructed: NO bytes: &value length: 1];
  12.182 +                return;
  12.183 +            }
  12.184 +        }
  12.185 +    }
  12.186 +    [NSException raise: MYDEREncoderException format: @"Can't DER-encode value %@ (typecode=%s)", number,type];
  12.187 +}
  12.188 +
  12.189 +
  12.190 +- (void) _encodeString: (NSString*)string {
  12.191 +    NSData *data = [string dataUsingEncoding: NSASCIIStringEncoding];
  12.192 +    if (data)
  12.193 +        [self _writeTag: 19 class: 0 constructed: NO data: data];
  12.194 +    else
  12.195 +        [self _writeTag: 12 class: 0 constructed: NO data: [string dataUsingEncoding: NSUTF8StringEncoding]];
  12.196 +}
  12.197 +
  12.198 +
  12.199 +- (void) _encodeBitString: (MYBitString*)bitString {
  12.200 +    NSUInteger bitCount = bitString.bitCount;
  12.201 +    [self _writeTag: 3 class: 0 constructed: NO length: 1 + (bitCount/8)];
  12.202 +    UInt8 unused = (8 - (bitCount % 8)) % 8;
  12.203 +    [_output appendBytes: &unused length: 1];
  12.204 +    [_output appendBytes: bitString.bits.bytes length: bitCount/8];
  12.205 +}
  12.206 +
  12.207 +- (void) _encodeDate: (NSDate*)date {
  12.208 +    NSString *dateStr = [MYBERGeneralizedTimeFormatter() stringFromDate: date];
  12.209 +    Log(@"Encoded %@ as '%@'",date,dateStr);//TEMP
  12.210 +    [self _writeTag: 24 class: 0 constructed: NO data: [dateStr dataUsingEncoding: NSASCIIStringEncoding]];
  12.211 +}
  12.212 +
  12.213 +
  12.214 +- (void) _encodeCollection: (id)collection tag: (unsigned)tag class: (unsigned)tagClass {
  12.215 +    MYDEREncoder *subEncoder = [[[self class] alloc] init];
  12.216 +    for (id object in collection)
  12.217 +        [subEncoder _encode: object];
  12.218 +    [self _writeTag: tag class: tagClass constructed: YES data: subEncoder.output];
  12.219 +    [subEncoder release];
  12.220 +}
  12.221 +
  12.222 +
  12.223 +- (void) _encode: (id)object {
  12.224 +    if (!_output)
  12.225 +        _output = [[NSMutableData alloc] initWithCapacity: 1024];
  12.226 +    if ([object isKindOfClass: [NSNumber class]]) {
  12.227 +        [self _encodeNumber: object];
  12.228 +    } else if ([object isKindOfClass: [NSData class]]) {
  12.229 +        [self _writeTag: 4 class: 0 constructed: NO data: object];
  12.230 +    } else if ([object isKindOfClass: [MYBitString class]]) {
  12.231 +        [self _encodeBitString: object];
  12.232 +    } else if ([object isKindOfClass: [NSString class]]) {
  12.233 +        [self _encodeString: object];
  12.234 +    } else if ([object isKindOfClass: [NSDate class]]) {
  12.235 +        [self _encodeDate: object];
  12.236 +    } else if ([object isKindOfClass: [NSNull class]]) {
  12.237 +        [self _writeTag: 5 class: 0 constructed: NO bytes: NULL length: 0];
  12.238 +    } else if ([object isKindOfClass: [NSArray class]]) {
  12.239 +        [self _encodeCollection: object tag: 16 class: 0];
  12.240 +    } else if ([object isKindOfClass: [NSSet class]]) {
  12.241 +        [self _encodeCollection: object tag: 17 class: 0];
  12.242 +    } else if ([object isKindOfClass: [MYOID class]]) {
  12.243 +        [self _writeTag: 6 class: 0 constructed: NO data: [object DEREncoding]];
  12.244 +    } else if ([object isKindOfClass: [MYASN1Object class]]) {
  12.245 +        MYASN1Object *asn = object;
  12.246 +        if (asn.components)
  12.247 +            [self _encodeCollection: asn.components tag: asn.tag class: asn.tagClass];
  12.248 +        else
  12.249 +            [self _writeTag: asn.tag 
  12.250 +                      class: asn.tagClass
  12.251 +                constructed: asn.constructed
  12.252 +                       data: asn.value];
  12.253 +    } else {
  12.254 +        [NSException raise: MYDEREncoderException format: @"Can't DER-encode a %@", [object class]];
  12.255 +    }
  12.256 +}
  12.257 +
  12.258 +
  12.259 +- (NSData*) output {
  12.260 +    if (!_output && !_error) {
  12.261 +        @try{
  12.262 +            [self _encode: _rootObject];
  12.263 +        }@catch (NSException *x) {
  12.264 +            if ($equal(x.name, MYDEREncoderException)) {
  12.265 +                self.error = MYError(2,MYASN1ErrorDomain, @"%@", x.reason);
  12.266 +                return nil;
  12.267 +            } else
  12.268 +                @throw(x);
  12.269 +        }
  12.270 +    }
  12.271 +    return _output;
  12.272 +}
  12.273 +
  12.274 +@synthesize error=_error;
  12.275 +
  12.276 +
  12.277 +@end
  12.278 +
  12.279 +
  12.280 +
  12.281 +#define $data(BYTES...)    ({const uint8_t bytes[] = {BYTES}; [NSData dataWithBytes: bytes length: sizeof(bytes)];})
  12.282 +
  12.283 +TestCase(DEREncoder) {
  12.284 +    CAssertEqual([MYDEREncoder encodeRootObject: [NSNull null] error: nil],
  12.285 +                 $data(0x05, 0x00));
  12.286 +    CAssertEqual([MYDEREncoder encodeRootObject: $true error: nil],
  12.287 +                 $data(0x01, 0x01, 0xFF));
  12.288 +    CAssertEqual([MYDEREncoder encodeRootObject: $false error: nil],
  12.289 +                 $data(0x01, 0x01, 0x00));
  12.290 +
  12.291 +    // Integers:
  12.292 +    CAssertEqual([MYDEREncoder encodeRootObject: $object(0) error: nil],
  12.293 +                 $data(0x02, 0x01, 0x00));
  12.294 +    CAssertEqual([MYDEREncoder encodeRootObject: $object(1) error: nil],
  12.295 +                 $data(0x02, 0x01, 0x01));
  12.296 +    CAssertEqual([MYDEREncoder encodeRootObject: $object(-1) error: nil],
  12.297 +                 $data(0x02, 0x01, 0xFF));
  12.298 +    CAssertEqual([MYDEREncoder encodeRootObject: $object(72) error: nil],
  12.299 +                  $data(0x02, 0x01, 0x48));
  12.300 +    CAssertEqual([MYDEREncoder encodeRootObject: $object(-128) error: nil],
  12.301 +                 $data(0x02, 0x01, 0x80));
  12.302 +    CAssertEqual([MYDEREncoder encodeRootObject: $object(128) error: nil],
  12.303 +                 $data(0x02, 0x02, 0x00, 0x80));
  12.304 +    CAssertEqual([MYDEREncoder encodeRootObject: $object(255) error: nil],
  12.305 +                 $data(0x02, 0x02, 0x00, 0xFF));
  12.306 +    CAssertEqual([MYDEREncoder encodeRootObject: $object(-256) error: nil],
  12.307 +                 $data(0x02, 0x02, 0xFF, 0x00));
  12.308 +    CAssertEqual([MYDEREncoder encodeRootObject: $object(12345) error: nil],
  12.309 +                 $data(0x02, 0x02, 0x30,0x39));
  12.310 +    CAssertEqual([MYDEREncoder encodeRootObject: $object(-12345) error: nil],
  12.311 +                 $data(0x02, 0x02, 0xCF, 0xC7));
  12.312 +    CAssertEqual([MYDEREncoder encodeRootObject: $object(123456789) error: nil],
  12.313 +                 $data(0x02, 0x04, 0x07, 0x5B, 0xCD, 0x15));
  12.314 +    CAssertEqual([MYDEREncoder encodeRootObject: $object(-123456789) error: nil],
  12.315 +                 $data(0x02, 0x04, 0xF8, 0xA4, 0x32, 0xEB));
  12.316 +    CAssertEqual([MYDEREncoder encodeRootObject: $object(-123456789) error: nil],
  12.317 +                 $data(0x02, 0x04, 0xF8, 0xA4, 0x32, 0xEB));
  12.318 +
  12.319 +    // Strings:
  12.320 +    CAssertEqual([MYDEREncoder encodeRootObject: @"hello" error: nil],
  12.321 +                 $data(0x13, 0x05, 'h', 'e', 'l', 'l', 'o'));
  12.322 +    CAssertEqual([MYDEREncoder encodeRootObject: @"thérè" error: nil],
  12.323 +                 $data(0x0C, 0x07, 't', 'h', 0xC3, 0xA9, 'r', 0xC3, 0xA8));
  12.324 +    
  12.325 +    // Dates:
  12.326 +    CAssertEqual([MYDEREncoder encodeRootObject: [NSDate dateWithTimeIntervalSinceReferenceDate: 265336576]
  12.327 +                                          error: nil],
  12.328 +                 $data(0x18, 0x0F, '2', '0', '0', '9', '0', '5', '3', '0', '0', '0', '3', '6', '1', '6', 'Z'));
  12.329 +
  12.330 +    // Sequences:
  12.331 +    CAssertEqual([MYDEREncoder encodeRootObject: $array($object(72), $true) error: nil],
  12.332 +                 $data(0x30, 0x06,  0x02, 0x01, 0x48,  0x01, 0x01, 0xFF));
  12.333 +    CAssertEqual([MYDEREncoder encodeRootObject: $array( $array($object(72), $true), 
  12.334 +                                                         $array($object(72), $true))
  12.335 +                                          error: nil],
  12.336 +                 $data(0x30, 0x10,  
  12.337 +                       0x30, 0x06,  0x02, 0x01, 0x48,  0x01, 0x01, 0xFF,
  12.338 +                       0x30, 0x06,  0x02, 0x01, 0x48,  0x01, 0x01, 0xFF));
  12.339 +}
  12.340 +
  12.341 +
  12.342 +TestCase(EncodeCert) {
  12.343 +    NSError *error = nil;
  12.344 +    NSData *cert = [NSData dataWithContentsOfFile: @"../../Tests/selfsigned.cer"];  //TEMP
  12.345 +    id certObjects = MYBERParse(cert,&error);
  12.346 +    CAssertNil(error);
  12.347 +    Log(@"Decoded as:\n%@", [MYASN1Object dump: certObjects]);
  12.348 +    NSData *encoded = [MYDEREncoder encodeRootObject: certObjects error: &error];
  12.349 +    CAssertNil(error);
  12.350 +    id reDecoded = MYBERParse(encoded, &error);
  12.351 +    CAssertNil(error);
  12.352 +    Log(@"Re-decoded as:\n%@", [MYASN1Object dump: reDecoded]);
  12.353 +    [encoded writeToFile: @"../../Tests/selfsigned_reencoded.cer" atomically: YES];
  12.354 +    CAssertEqual(encoded,cert);
  12.355 +}
    13.1 --- a/MYKey-iPhone.m	Sun Apr 19 22:05:51 2009 -0700
    13.2 +++ b/MYKey-iPhone.m	Tue Jun 02 13:16:28 2009 -0700
    13.3 @@ -59,6 +59,11 @@
    13.4          return nil;
    13.5      else
    13.6          return [(id)CFMakeCollectable(data) autorelease];
    13.7 +    
    13.8 +    // The format of this data is not documented. There's been some reverse-engineering:
    13.9 +    // https://devforums.apple.com/message/32089#32089
   13.10 +    // Apparently it is a DER-formatted sequence of a modulus followed by an exponent.
   13.11 +    // This can be converted to OpenSSL format by wrapping it in some additional DER goop.
   13.12  }
   13.13  
   13.14  
    14.1 --- a/MYKeychain.h	Sun Apr 19 22:05:51 2009 -0700
    14.2 +++ b/MYKeychain.h	Tue Jun 02 13:16:28 2009 -0700
    14.3 @@ -102,6 +102,11 @@
    14.4  //@{
    14.5  #if !TARGET_OS_IPHONE
    14.6  
    14.7 +/** Sets whether the keychain is allowed to pop up panels to interact with the user,
    14.8 +    for example to ask for permission to access keys. If user interaction is not
    14.9 +    allowed, all such requests will fail. */
   14.10 ++ (void) setUserInteractionAllowed: (BOOL)allowed;
   14.11 +
   14.12  /** Enumerates all public keys in the keychain that have the given alias string. */
   14.13  - (NSEnumerator*) symmetricKeysWithAlias: (NSString*)alias;
   14.14  
    15.1 --- a/MYKeychain.m	Sun Apr 19 22:05:51 2009 -0700
    15.2 +++ b/MYKeychain.m	Tue Jun 02 13:16:28 2009 -0700
    15.3 @@ -206,6 +206,11 @@
    15.4  }
    15.5  
    15.6  
    15.7 ++ (void) setUserInteractionAllowed: (BOOL)allowed {
    15.8 +    SecKeychainSetUserInteractionAllowed(allowed);
    15.9 +}
   15.10 +
   15.11 +
   15.12  #pragma mark -
   15.13  #pragma mark SEARCHING:
   15.14  
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/MYOID.h	Tue Jun 02 13:16:28 2009 -0700
    16.3 @@ -0,0 +1,25 @@
    16.4 +//
    16.5 +//  MYOID.h
    16.6 +//  MYCrypto
    16.7 +//
    16.8 +//  Created by Jens Alfke on 5/28/09.
    16.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
   16.10 +//
   16.11 +
   16.12 +#import <Cocoa/Cocoa.h>
   16.13 +
   16.14 +
   16.15 +/** An ASN.1 Object-ID, which is a sequence of integer components that define namespaces. */
   16.16 +@interface MYOID : NSObject <NSCopying>
   16.17 +{
   16.18 +    NSData *_data;
   16.19 +}
   16.20 +
   16.21 +- (id) initWithComponents: (const UInt32*)components count: (unsigned)componentCount;
   16.22 +- (id) initWithBEREncoding: (NSData*)encoding;
   16.23 +- (NSData*) DEREncoding;
   16.24 +
   16.25 +- (const UInt32*) components;
   16.26 +- (unsigned) componentCount;
   16.27 +
   16.28 +@end
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/MYOID.m	Tue Jun 02 13:16:28 2009 -0700
    17.3 @@ -0,0 +1,152 @@
    17.4 +//
    17.5 +//  MYOID.m
    17.6 +//  MYCrypto
    17.7 +//
    17.8 +//  Created by Jens Alfke on 5/28/09.
    17.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
   17.10 +//
   17.11 +
   17.12 +#import "MYOID.h"
   17.13 +
   17.14 +
   17.15 +@implementation MYOID
   17.16 +
   17.17 +
   17.18 +- (id) initWithComponents: (const UInt32*)components count: (unsigned)count
   17.19 +{
   17.20 +    self = [super init];
   17.21 +    if (self != nil) {
   17.22 +        _data = [[NSData alloc] initWithBytes: components length: count*sizeof(UInt32)];
   17.23 +    }
   17.24 +    return self;
   17.25 +}
   17.26 +
   17.27 +- (id) initWithBEREncoding: (NSData*)encoding
   17.28 +{
   17.29 +    self = [super init];
   17.30 +    if (self != nil) {
   17.31 +        size_t len = encoding.length;
   17.32 +        const UInt8 *src = encoding.bytes;
   17.33 +        const UInt8 *end = src+len;
   17.34 +        NSMutableData *data = [NSMutableData dataWithLength: (len+1)*sizeof(UInt32)];
   17.35 +        UInt32* dst = data.mutableBytes;
   17.36 +        
   17.37 +        if (len >= 2) {
   17.38 +            *dst++ = *src / 40;
   17.39 +            *dst++ = *src % 40;
   17.40 +            src++; 
   17.41 +        }
   17.42 +        while (src < end) {
   17.43 +            UInt32 component = 0;
   17.44 +            UInt8 byte;
   17.45 +            do{
   17.46 +                if (src >= end) {
   17.47 +                    [self release];
   17.48 +                    return nil;
   17.49 +                }
   17.50 +                byte = *src++;
   17.51 +                component = (component << 7) | (byte & 0x7F);
   17.52 +            }while (byte & 0x80);
   17.53 +            *dst++ = component;
   17.54 +        }
   17.55 +        data.length = (UInt8*)dst - (UInt8*)data.mutableBytes;
   17.56 +        _data = [data copy];
   17.57 +    }
   17.58 +    return self;
   17.59 +}
   17.60 +
   17.61 ++ (MYOID*) OIDWithEncoding: (NSData*)encoding {
   17.62 +    return [[[self alloc] initWithBEREncoding: encoding] autorelease];
   17.63 +}
   17.64 +
   17.65 +
   17.66 +- (id) copyWithZone: (NSZone*)zone {
   17.67 +    return [self retain];
   17.68 +}
   17.69 +
   17.70 +- (void) dealloc
   17.71 +{
   17.72 +    [_data release];
   17.73 +    [super dealloc];
   17.74 +}
   17.75 +
   17.76 +
   17.77 +- (NSString*) description {
   17.78 +    NSMutableString *desc = [NSMutableString stringWithString: @"{"];
   17.79 +    const UInt32* components = self.components;
   17.80 +    unsigned count = self.componentCount;
   17.81 +    for (unsigned i=0; i<count; i++) {
   17.82 +        if (i>0)
   17.83 +            [desc appendString: @" "];
   17.84 +        [desc appendFormat: @"%u", components[i]];
   17.85 +    }
   17.86 +    [desc appendString: @"}"];
   17.87 +    return desc;
   17.88 +}
   17.89 +
   17.90 +
   17.91 +- (NSData*) componentData       {return _data;}
   17.92 +- (const UInt32*) components    {return (const UInt32*)_data.bytes;}
   17.93 +- (unsigned) componentCount     {return _data.length / sizeof(UInt32);}
   17.94 +
   17.95 +- (NSUInteger)hash {
   17.96 +    return _data.hash;
   17.97 +}
   17.98 +
   17.99 +- (BOOL)isEqual:(id)object {
  17.100 +    return [object isKindOfClass: [MYOID class]] && [_data isEqual: [object componentData]];
  17.101 +}
  17.102 +
  17.103 +
  17.104 +- (NSData*) DEREncoding {
  17.105 +    unsigned count = self.componentCount;
  17.106 +    UInt8 encoding[5*count]; // worst-case size
  17.107 +    const UInt32 *src=self.components, *end=src+count;
  17.108 +    UInt8 *dst = encoding;
  17.109 +    if (count >= 2 && src[0]<=3 && src[1]<40) {
  17.110 +        // Weird collapsing of 1st two components into one byte:
  17.111 +        *dst++ = src[0]*40 + src[1];
  17.112 +        src += 2;
  17.113 +    }
  17.114 +    while (src<end) {
  17.115 +        UInt32 component = *src++;
  17.116 +        // Write the component in 7-bit groups, most significant first:
  17.117 +        BOOL any = NO;
  17.118 +        for (int shift=28; shift>=0; shift -= 7) {
  17.119 +            UInt8 byte = (component >> shift) & 0x7F;
  17.120 +            if (byte || any) {
  17.121 +                if (any)
  17.122 +                    dst[-1] |= 0x80;
  17.123 +                *dst++ = byte;
  17.124 +                any = YES;
  17.125 +            }
  17.126 +        }
  17.127 +    }
  17.128 +    return [NSData dataWithBytes: encoding length: dst-encoding];
  17.129 +}
  17.130 +
  17.131 +
  17.132 +@end
  17.133 +
  17.134 +
  17.135 +
  17.136 +#define $data(BYTES...)    ({const uint8_t bytes[] = {BYTES}; [NSData dataWithBytes: bytes length: sizeof(bytes)];})
  17.137 +
  17.138 +#define $components(INTS...)    ({const UInt32 components[] = {INTS}; components;})
  17.139 +
  17.140 +TestCase(OID) {
  17.141 +    CAssertEqual([[MYOID OIDWithEncoding: $data(0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01)] description],
  17.142 +                 @"{1 2 840 113549 1 1 1}");
  17.143 +    CAssertEqual([[MYOID OIDWithEncoding: $data(0x55,0x04,0x04)] description],
  17.144 +                 @"{2 5 4 4}");
  17.145 +    CAssertEqual([[MYOID OIDWithEncoding: $data(0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x01)] description],
  17.146 +                 @"{1 2 840 113549 1 9 1}");
  17.147 +
  17.148 +    CAssertEqual([[[MYOID alloc] initWithComponents: $components(1,2,840,113549,1,1,1) count: 7] description],
  17.149 +                 @"{1 2 840 113549 1 1 1}");
  17.150 +
  17.151 +    CAssertEqual([[[MYOID alloc] initWithComponents: $components(1,2,840,113549,1,1,1) count: 7] DEREncoding],
  17.152 +                 $data(0x2a, 0x86, 0x48, 0x86,  0xf7, 0x0d, 0x01, 0x01,  0x01));
  17.153 +    CAssertEqual([[[MYOID alloc] initWithComponents: $components(2,5,4,4) count: 4] DEREncoding],
  17.154 +                 $data(0x55,0x04,0x04));
  17.155 +}
    18.1 --- a/MYSymmetricKey.h	Sun Apr 19 22:05:51 2009 -0700
    18.2 +++ b/MYSymmetricKey.h	Tue Jun 02 13:16:28 2009 -0700
    18.3 @@ -10,8 +10,17 @@
    18.4  #import <CommonCrypto/CommonCryptor.h>
    18.5  
    18.6  
    18.7 +/** An old-fashioned symmetric key, so named because it both encrypts and decrypts.
    18.8 +    A key can be generated at random, stored in the keychain, or derived from a user-entered
    18.9 +    passphrase.
   18.10 + 
   18.11 +    These days, symmetric encryption is used mostly on local data such as files, with
   18.12 +    passphrases; or as a transient "session key" for data sent between users, with the
   18.13 +    session key itself encrypted in the message using public-key encryption. (The
   18.14 +    MYEncoder/MYDecoder classes manage this second usage, whose details are tricky.) */
   18.15  @interface MYSymmetricKey : MYKey <MYEncryption, MYDecryption>
   18.16  {
   18.17 +    @private
   18.18  #if !MYCRYPTO_USE_IPHONE_API
   18.19      CSSM_KEY *_ownedCSSMKey;
   18.20  #endif
   18.21 @@ -36,7 +45,17 @@
   18.22  
   18.23  #if !TARGET_OS_IPHONE
   18.24  
   18.25 -- (NSData*) exportWrappedKeyWithPassphrasePrompt: (NSString*)prompt;
   18.26 +/** Exports the key as a data blob, so that it can be stored as a backup, or transferred
   18.27 +    to another computer. Since the key is sensitive, it must be exported in encrypted form
   18.28 +    using a user-chosen passphrase. This method will display a standard alert panel, run by
   18.29 +    the Security agent, that prompts the user to enter a new passphrase for encrypting the key.
   18.30 +    The same passphrase must be re-entered when importing the key from the data blob. */
   18.31 + - (NSData*) exportWrappedKeyWithPassphrasePrompt: (NSString*)prompt;
   18.32 +
   18.33 +/** Recreates a symmetric key from its wrapped (encrypted) form. The user will be prompted for
   18.34 +    the passphrase to decrypt the key; this must be the same passphrase that was entered when
   18.35 +    wrapping the key, e.g. when -exportWrappedKeyWithPassphrasePrompt: was called. */
   18.36 +- (id) initWithWrappedKeyData: (NSData*)wrappedKeyData;
   18.37  
   18.38  /** Converts a passphrase into a symmetric key.
   18.39      The same passphrase (and salt) will always return the same key, so you can use this method
   18.40 @@ -48,12 +67,12 @@
   18.41          passphrase twice, to check for errors, and the nifty passphrase-strength meter will be
   18.42          displayed. If NO, there's only one text-field, and an option to display its contents in
   18.43          the clear.
   18.44 -    @param salt  An arbitrary value whose data will be mixed in with the passphrase before
   18.45 +    @param saltObj  An arbitrary value whose data will be mixed in with the passphrase before
   18.46          hashing, to perturb the resulting bits. The purpose of this is to make it harder for
   18.47          an attacker to brute-force the key using a precompiled list of digests of common
   18.48          passwords. Changing the salt changes the key, so you need to pass the same value when
   18.49          re-deriving the key as you did when first generating it. */
   18.50 - + (MYSymmetricKey*) generateFromUserPassphraseWithAlertTitle: (NSString*)alertTitle
   18.51 ++ (MYSymmetricKey*) generateFromUserPassphraseWithAlertTitle: (NSString*)alertTitle
   18.52                                                   alertPrompt: (NSString*)prompt
   18.53                                                      creating: (BOOL)creating
   18.54                                                          salt: (id)saltObj;
    19.1 --- a/MYSymmetricKey.m	Sun Apr 19 22:05:51 2009 -0700
    19.2 +++ b/MYSymmetricKey.m	Tue Jun 02 13:16:28 2009 -0700
    19.3 @@ -34,8 +34,15 @@
    19.4  
    19.5  static CSSM_KEY* cssmKeyFromData( NSData *keyData, CSSM_ALGORITHMS algorithm,
    19.6                                   MYKeychain *keychain);
    19.7 -//static CSSM_ENCRYPT_MODE defaultModeForAlgorithm(CSSM_ALGORITHMS algorithm);
    19.8 -//CSSM_PADDING defaultPaddingForAlgorithm(CSSM_ALGORITHMS algorithm);
    19.9 +
   19.10 +#if !TARGET_OS_IPHONE
   19.11 +static CSSM_KEY* unwrapCssmKeyFromData(NSData *wrappedData,
   19.12 +                                       CSSM_ALGORITHMS algorithm,
   19.13 +                                       unsigned sizeInBits);
   19.14 +static CSSM_ENCRYPT_MODE defaultModeForAlgorithm(CSSM_ALGORITHMS algorithm);
   19.15 +static CSSM_PADDING defaultPaddingForAlgorithm(CSSM_ALGORITHMS algorithm);
   19.16 +#endif
   19.17 +
   19.18  static CSSM_DATA makeSalt( id salty, size_t length );
   19.19  static CSSM_RETURN impExpCreatePassKey(
   19.20  	const SecKeyImportExportParameters *keyParams,  // required
   19.21 @@ -49,6 +56,10 @@
   19.22  
   19.23  
   19.24  - (id) _initWithCSSMKey: (CSSM_KEY*)cssmKey {
   19.25 +    if (!cssmKey) {
   19.26 +        [self release];
   19.27 +        return nil;
   19.28 +    }
   19.29      SecKeyRef keyRef = NULL;
   19.30      if (SecKeyCreate == NULL) {
   19.31          // If weak-linked SPI fn no longer exists
   19.32 @@ -75,10 +86,6 @@
   19.33      Assert(algorithm <= kCCAlgorithmRC4);
   19.34      Assert(keyData);
   19.35      CSSM_KEY *key = cssmKeyFromData(keyData, CSSMFromCCAlgorithm(algorithm), keychain);
   19.36 -    if (!key) {
   19.37 -        [self release];
   19.38 -        return nil;
   19.39 -    }
   19.40      return [self _initWithCSSMKey: key];
   19.41  }
   19.42  
   19.43 @@ -223,9 +230,15 @@
   19.44  
   19.45  
   19.46  #if !TARGET_OS_IPHONE
   19.47 +- (id) initWithWrappedKeyData: (NSData*)wrappedKeyData {
   19.48 +    return [self _initWithCSSMKey: unwrapCssmKeyFromData(wrappedKeyData,
   19.49 +                                                         CSSM_ALGID_AES,128)];
   19.50 +}
   19.51 +
   19.52 +
   19.53  - (NSData*) exportWrappedKeyWithPassphrasePrompt: (NSString*)prompt
   19.54  {
   19.55 -    // Prompt use for a passphrase to use for the wrapping key:
   19.56 +    // Prompt user for a passphrase to use for the wrapping key:
   19.57      MYSymmetricKey *wrappingKey = [MYSymmetricKey 
   19.58                                     generateFromUserPassphraseWithAlertTitle: @"Export Key" 
   19.59                                     alertPrompt: prompt 
   19.60 @@ -238,15 +251,17 @@
   19.61      // Create the context:
   19.62      CSSM_ACCESS_CREDENTIALS credentials = {};
   19.63      CSSM_CSP_HANDLE cspHandle = self.cssmCSPHandle;
   19.64 -    //CSSM_ALGORITHMS algorithm = wrappingKey.cssmAlgorithm;
   19.65 +    CSSM_ALGORITHMS algorithm = wrappingKey.cssmAlgorithm;
   19.66 +    uint8 iv[16] = {0}; // Right size for AES. Are zeros OK? //FIX: Support other algorithms
   19.67 +    CSSM_DATA ivData = {.Data=(void*)&iv, .Length=sizeof(iv)};
   19.68      CSSM_CC_HANDLE ctx;
   19.69      if (!checkcssm(CSSM_CSP_CreateSymmetricContext(cspHandle,
   19.70 -                                                   wrappingKey.cssmAlgorithm, //CSSM_ALGID_3DES_3KEY_EDE, //algorithm, 
   19.71 -                                                   CSSM_ALGMODE_CBCPadIV8, //defaultModeForAlgorithm(algorithm),
   19.72 +                                                   algorithm, //CSSM_ALGID_3DES_3KEY_EDE
   19.73 +                                                   defaultModeForAlgorithm(algorithm),
   19.74                                                     &credentials, 
   19.75                                                     wrappingKey.cssmKey,
   19.76 -                                                   NULL,
   19.77 -                                                   CSSM_PADDING_PKCS7, //defaultPaddingForAlgorithm(algorithm),
   19.78 +                                                   &ivData,
   19.79 +                                                   defaultPaddingForAlgorithm(algorithm),
   19.80                                                     NULL,
   19.81                                                     &ctx), 
   19.82                     @"CSSM_CSP_CreateSymmetricContext"))
   19.83 @@ -396,6 +411,35 @@
   19.84  }
   19.85  
   19.86  
   19.87 +#if !TARGET_OS_IPHONE
   19.88 +static CSSM_KEY* unwrapCssmKeyFromData(NSData *wrappedData,
   19.89 +                                       CSSM_ALGORITHMS algorithm,
   19.90 +                                       unsigned sizeInBits) {
   19.91 +    Warn(@"MYSymmetricKey: unwrapping is unimplemented; sorry");
   19.92 +    return nil;
   19.93 +#if 0 //not finished yet
   19.94 +    // First create a wrapped-key structure from the data:
   19.95 +    CSSM_WRAP_KEY wrappedKey = {
   19.96 +        .KeyHeader = {
   19.97 +            .BlobType = CSSM_KEYBLOB_WRAPPED,
   19.98 +            .Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS3,
   19.99 +            .AlgorithmId = algorithm,
  19.100 +            .KeyClass = CSSM_KEYCLASS_SESSION_KEY,
  19.101 +            .LogicalKeySizeInBits = sizeInBits,
  19.102 +            .KeyAttr = CSSM_KEYATTR_EXTRACTABLE,
  19.103 +            .KeyUsage = CSSM_KEYUSE_ANY,
  19.104 +            .WrapAlgorithmId = CSSM_ALGID_AES,
  19.105 +        },
  19.106 +        .KeyData = {
  19.107 +            .Data = (void*)wrappedData.bytes,
  19.108 +            .Length = wrappedData.length
  19.109 +        }
  19.110 +    };
  19.111 +#endif
  19.112 +}    
  19.113 +#endif
  19.114 +
  19.115 +
  19.116  // Create salt data of a specific length from an arbitrary NSObject. */
  19.117  static CSSM_DATA makeSalt( id salty, size_t length ) {
  19.118      // Convert to NSData if necessary:
  19.119 @@ -416,7 +460,9 @@
  19.120  
  19.121  #pragma mark -
  19.122  // Code from Keychain.framework:
  19.123 -#if 0
  19.124 +
  19.125 +#if !TARGET_OS_IPHONE
  19.126 +#if 1
  19.127  static CSSM_ENCRYPT_MODE defaultModeForAlgorithm(CSSM_ALGORITHMS algorithm) {
  19.128      switch(algorithm) {
  19.129          // 8-byte block ciphers
  19.130 @@ -438,8 +484,10 @@
  19.131              return CSSM_ALGMODE_NONE;
  19.132      }
  19.133  }
  19.134 +#endif
  19.135  
  19.136 -CSSM_PADDING defaultPaddingForAlgorithm(CSSM_ALGORITHMS algorithm) {
  19.137 +#if 1
  19.138 +static CSSM_PADDING defaultPaddingForAlgorithm(CSSM_ALGORITHMS algorithm) {
  19.139      switch(algorithm) {
  19.140          /* 8-byte block ciphers */
  19.141          case CSSM_ALGID_DES:
  19.142 @@ -465,6 +513,7 @@
  19.143      }
  19.144  }
  19.145  #endif
  19.146 +#endif
  19.147  
  19.148  #pragma mark -
  19.149  // Code below was copied from SecImportExportUtils.cpp in Apple's libsecurity_keychain project
    20.1 Binary file Tests/iphonedev.cer has changed
    21.1 Binary file Tests/selfsigned.cer has changed