1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/MYDEREncoder.m Tue Jun 02 13:16:28 2009 -0700
1.3 @@ -0,0 +1,352 @@
1.4 +//
1.5 +// MYDEREncoder.m
1.6 +// MYCrypto
1.7 +//
1.8 +// Created by Jens Alfke on 5/29/09.
1.9 +// Copyright 2009 Jens Alfke. All rights reserved.
1.10 +//
1.11 +
1.12 +#import "MYDEREncoder.h"
1.13 +#import "MYASN1Object.h"
1.14 +#import "MYBERParser.h"
1.15 +#import "MYOID.h"
1.16 +#import "MYErrorUtils.h"
1.17 +
1.18 +
1.19 +#define MYDEREncoderException @"MYDEREncoderException"
1.20 +
1.21 +
1.22 +@interface MYDEREncoder ()
1.23 +- (void) _encode: (id)object;
1.24 +@property (retain) NSError *error;
1.25 +@end
1.26 +
1.27 +
1.28 +@implementation MYDEREncoder
1.29 +
1.30 +
1.31 +- (id) initWithRootObject: (id)rootObject
1.32 +{
1.33 + self = [super init];
1.34 + if (self != nil) {
1.35 + _rootObject = [rootObject retain];
1.36 + }
1.37 + return self;
1.38 +}
1.39 +
1.40 ++ (NSData*) encodeRootObject: (id)rootObject error: (NSError**)outError {
1.41 + MYDEREncoder *encoder = [[self alloc] initWithRootObject: rootObject];
1.42 + NSData *output = [encoder.output copy];
1.43 + if (outError) *outError = [[encoder.error retain] autorelease];
1.44 + [encoder release];
1.45 + return [output autorelease];
1.46 +}
1.47 +
1.48 +- (void) dealloc
1.49 +{
1.50 + [_rootObject release];
1.51 + [_output release];
1.52 + [super dealloc];
1.53 +}
1.54 +
1.55 +
1.56 +
1.57 +static unsigned sizeOfUnsignedInt (UInt64 n) {
1.58 + unsigned bytes = 0;
1.59 + for (; n; n >>= 8)
1.60 + bytes++;
1.61 + return bytes;
1.62 +}
1.63 +
1.64 +static unsigned encodeUnsignedInt (UInt64 n, UInt8 buf[], BOOL padHighBit) {
1.65 + unsigned size = MAX(1U, sizeOfUnsignedInt(n));
1.66 + UInt64 bigEndian = NSSwapHostLongLongToBig(n);
1.67 + const UInt8* src = (UInt8*)&bigEndian + (8-size);
1.68 + UInt8 *dst = &buf[0];
1.69 + if (padHighBit && (*src & 0x80)) {
1.70 + *dst++ = 0;
1.71 + size++;
1.72 + }
1.73 + memcpy(dst, src, size);
1.74 + return size;
1.75 +}
1.76 +
1.77 +static unsigned encodeSignedInt (SInt64 n, UInt8 buf[]) {
1.78 + if (n >= 0)
1.79 + return encodeUnsignedInt(n, buf, YES);
1.80 + else {
1.81 + unsigned size = MAX(1U, sizeOfUnsignedInt(~n));
1.82 + UInt64 bigEndian = NSSwapHostLongLongToBig(n);
1.83 + const UInt8* src = (UInt8*)&bigEndian + (8-size);
1.84 + UInt8 *dst = &buf[0];
1.85 + if (!(*src & 0x80)) {
1.86 + *dst++ = 0xFF;
1.87 + size++;
1.88 + }
1.89 + memcpy(dst, src, size);
1.90 + return size;
1.91 + }
1.92 +}
1.93 +
1.94 +
1.95 +- (void) _writeTag: (unsigned)tag
1.96 + class: (unsigned)tagClass
1.97 + constructed: (BOOL) constructed
1.98 + length: (size_t)length
1.99 +{
1.100 + struct {
1.101 + unsigned tag :5;
1.102 + unsigned isConstructed :1;
1.103 + unsigned tagClass :2;
1.104 + unsigned length :7;
1.105 + unsigned isLengthLong :1;
1.106 + UInt8 extraLength[9];
1.107 + } header;
1.108 + size_t headerSize = 2;
1.109 +
1.110 + header.tag = tag;
1.111 + header.isConstructed = constructed;
1.112 + header.tagClass = tagClass;
1.113 + if (length < 128) {
1.114 + header.isLengthLong = NO;
1.115 + header.length = length;
1.116 + } else {
1.117 + header.isLengthLong = YES;
1.118 + header.length = encodeUnsignedInt(length, header.extraLength, NO);
1.119 + headerSize += header.length;
1.120 + }
1.121 + [_output appendBytes: &header length: headerSize];
1.122 +}
1.123 +
1.124 +- (void) _writeTag: (unsigned)tag
1.125 + class: (unsigned)tagClass
1.126 + constructed: (BOOL) constructed
1.127 + bytes: (const void*)bytes
1.128 + length: (size_t)length
1.129 +{
1.130 + [self _writeTag: tag class: tagClass constructed: constructed length: length];
1.131 + [_output appendBytes: bytes length: length];
1.132 +}
1.133 +
1.134 +- (void) _writeTag: (unsigned)tag
1.135 + class: (unsigned)tagClass
1.136 + constructed: (BOOL) constructed
1.137 + data: (NSData*)data
1.138 +{
1.139 + Assert(data);
1.140 + [self _writeTag: tag class: tagClass constructed: constructed bytes: data.bytes length: data.length];
1.141 +}
1.142 +
1.143 +
1.144 +- (void) _encodeNumber: (NSNumber*)number {
1.145 + // Special-case detection of booleans by pointer equality, because otherwise they appear
1.146 + // identical to 0 and 1:
1.147 + if (number==$true || number==$false) {
1.148 + UInt8 value = number==$true ?0xFF :0x00;
1.149 + [self _writeTag: 1 class: 0 constructed: NO bytes: &value length: 1];
1.150 + return;
1.151 + }
1.152 +
1.153 + const char *type = number.objCType;
1.154 + if (strlen(type) == 1) {
1.155 + switch(type[0]) {
1.156 + case 'c':
1.157 + case 'i':
1.158 + case 's':
1.159 + case 'l':
1.160 + case 'q':
1.161 + { // Signed integers:
1.162 + UInt8 buf[9];
1.163 + size_t size = encodeSignedInt(number.longLongValue, buf);
1.164 + [self _writeTag: 2 class: 0 constructed: NO bytes: buf length: size];
1.165 + return;
1.166 + }
1.167 + case 'C':
1.168 + case 'I':
1.169 + case 'S':
1.170 + case 'L':
1.171 + case 'Q':
1.172 + { // Unsigned integers:
1.173 + UInt8 buf[9];
1.174 + size_t size = encodeUnsignedInt(number.unsignedLongLongValue, buf, YES);
1.175 + [self _writeTag: 2 class: 0 constructed: NO bytes: buf length: size];
1.176 + return;
1.177 + }
1.178 + case 'B':
1.179 + { // bool
1.180 + UInt8 value = number.boolValue ?0xFF :0x00;
1.181 + [self _writeTag: 1 class: 0 constructed: NO bytes: &value length: 1];
1.182 + return;
1.183 + }
1.184 + }
1.185 + }
1.186 + [NSException raise: MYDEREncoderException format: @"Can't DER-encode value %@ (typecode=%s)", number,type];
1.187 +}
1.188 +
1.189 +
1.190 +- (void) _encodeString: (NSString*)string {
1.191 + NSData *data = [string dataUsingEncoding: NSASCIIStringEncoding];
1.192 + if (data)
1.193 + [self _writeTag: 19 class: 0 constructed: NO data: data];
1.194 + else
1.195 + [self _writeTag: 12 class: 0 constructed: NO data: [string dataUsingEncoding: NSUTF8StringEncoding]];
1.196 +}
1.197 +
1.198 +
1.199 +- (void) _encodeBitString: (MYBitString*)bitString {
1.200 + NSUInteger bitCount = bitString.bitCount;
1.201 + [self _writeTag: 3 class: 0 constructed: NO length: 1 + (bitCount/8)];
1.202 + UInt8 unused = (8 - (bitCount % 8)) % 8;
1.203 + [_output appendBytes: &unused length: 1];
1.204 + [_output appendBytes: bitString.bits.bytes length: bitCount/8];
1.205 +}
1.206 +
1.207 +- (void) _encodeDate: (NSDate*)date {
1.208 + NSString *dateStr = [MYBERGeneralizedTimeFormatter() stringFromDate: date];
1.209 + Log(@"Encoded %@ as '%@'",date,dateStr);//TEMP
1.210 + [self _writeTag: 24 class: 0 constructed: NO data: [dateStr dataUsingEncoding: NSASCIIStringEncoding]];
1.211 +}
1.212 +
1.213 +
1.214 +- (void) _encodeCollection: (id)collection tag: (unsigned)tag class: (unsigned)tagClass {
1.215 + MYDEREncoder *subEncoder = [[[self class] alloc] init];
1.216 + for (id object in collection)
1.217 + [subEncoder _encode: object];
1.218 + [self _writeTag: tag class: tagClass constructed: YES data: subEncoder.output];
1.219 + [subEncoder release];
1.220 +}
1.221 +
1.222 +
1.223 +- (void) _encode: (id)object {
1.224 + if (!_output)
1.225 + _output = [[NSMutableData alloc] initWithCapacity: 1024];
1.226 + if ([object isKindOfClass: [NSNumber class]]) {
1.227 + [self _encodeNumber: object];
1.228 + } else if ([object isKindOfClass: [NSData class]]) {
1.229 + [self _writeTag: 4 class: 0 constructed: NO data: object];
1.230 + } else if ([object isKindOfClass: [MYBitString class]]) {
1.231 + [self _encodeBitString: object];
1.232 + } else if ([object isKindOfClass: [NSString class]]) {
1.233 + [self _encodeString: object];
1.234 + } else if ([object isKindOfClass: [NSDate class]]) {
1.235 + [self _encodeDate: object];
1.236 + } else if ([object isKindOfClass: [NSNull class]]) {
1.237 + [self _writeTag: 5 class: 0 constructed: NO bytes: NULL length: 0];
1.238 + } else if ([object isKindOfClass: [NSArray class]]) {
1.239 + [self _encodeCollection: object tag: 16 class: 0];
1.240 + } else if ([object isKindOfClass: [NSSet class]]) {
1.241 + [self _encodeCollection: object tag: 17 class: 0];
1.242 + } else if ([object isKindOfClass: [MYOID class]]) {
1.243 + [self _writeTag: 6 class: 0 constructed: NO data: [object DEREncoding]];
1.244 + } else if ([object isKindOfClass: [MYASN1Object class]]) {
1.245 + MYASN1Object *asn = object;
1.246 + if (asn.components)
1.247 + [self _encodeCollection: asn.components tag: asn.tag class: asn.tagClass];
1.248 + else
1.249 + [self _writeTag: asn.tag
1.250 + class: asn.tagClass
1.251 + constructed: asn.constructed
1.252 + data: asn.value];
1.253 + } else {
1.254 + [NSException raise: MYDEREncoderException format: @"Can't DER-encode a %@", [object class]];
1.255 + }
1.256 +}
1.257 +
1.258 +
1.259 +- (NSData*) output {
1.260 + if (!_output && !_error) {
1.261 + @try{
1.262 + [self _encode: _rootObject];
1.263 + }@catch (NSException *x) {
1.264 + if ($equal(x.name, MYDEREncoderException)) {
1.265 + self.error = MYError(2,MYASN1ErrorDomain, @"%@", x.reason);
1.266 + return nil;
1.267 + } else
1.268 + @throw(x);
1.269 + }
1.270 + }
1.271 + return _output;
1.272 +}
1.273 +
1.274 +@synthesize error=_error;
1.275 +
1.276 +
1.277 +@end
1.278 +
1.279 +
1.280 +
1.281 +#define $data(BYTES...) ({const uint8_t bytes[] = {BYTES}; [NSData dataWithBytes: bytes length: sizeof(bytes)];})
1.282 +
1.283 +TestCase(DEREncoder) {
1.284 + CAssertEqual([MYDEREncoder encodeRootObject: [NSNull null] error: nil],
1.285 + $data(0x05, 0x00));
1.286 + CAssertEqual([MYDEREncoder encodeRootObject: $true error: nil],
1.287 + $data(0x01, 0x01, 0xFF));
1.288 + CAssertEqual([MYDEREncoder encodeRootObject: $false error: nil],
1.289 + $data(0x01, 0x01, 0x00));
1.290 +
1.291 + // Integers:
1.292 + CAssertEqual([MYDEREncoder encodeRootObject: $object(0) error: nil],
1.293 + $data(0x02, 0x01, 0x00));
1.294 + CAssertEqual([MYDEREncoder encodeRootObject: $object(1) error: nil],
1.295 + $data(0x02, 0x01, 0x01));
1.296 + CAssertEqual([MYDEREncoder encodeRootObject: $object(-1) error: nil],
1.297 + $data(0x02, 0x01, 0xFF));
1.298 + CAssertEqual([MYDEREncoder encodeRootObject: $object(72) error: nil],
1.299 + $data(0x02, 0x01, 0x48));
1.300 + CAssertEqual([MYDEREncoder encodeRootObject: $object(-128) error: nil],
1.301 + $data(0x02, 0x01, 0x80));
1.302 + CAssertEqual([MYDEREncoder encodeRootObject: $object(128) error: nil],
1.303 + $data(0x02, 0x02, 0x00, 0x80));
1.304 + CAssertEqual([MYDEREncoder encodeRootObject: $object(255) error: nil],
1.305 + $data(0x02, 0x02, 0x00, 0xFF));
1.306 + CAssertEqual([MYDEREncoder encodeRootObject: $object(-256) error: nil],
1.307 + $data(0x02, 0x02, 0xFF, 0x00));
1.308 + CAssertEqual([MYDEREncoder encodeRootObject: $object(12345) error: nil],
1.309 + $data(0x02, 0x02, 0x30,0x39));
1.310 + CAssertEqual([MYDEREncoder encodeRootObject: $object(-12345) error: nil],
1.311 + $data(0x02, 0x02, 0xCF, 0xC7));
1.312 + CAssertEqual([MYDEREncoder encodeRootObject: $object(123456789) error: nil],
1.313 + $data(0x02, 0x04, 0x07, 0x5B, 0xCD, 0x15));
1.314 + CAssertEqual([MYDEREncoder encodeRootObject: $object(-123456789) error: nil],
1.315 + $data(0x02, 0x04, 0xF8, 0xA4, 0x32, 0xEB));
1.316 + CAssertEqual([MYDEREncoder encodeRootObject: $object(-123456789) error: nil],
1.317 + $data(0x02, 0x04, 0xF8, 0xA4, 0x32, 0xEB));
1.318 +
1.319 + // Strings:
1.320 + CAssertEqual([MYDEREncoder encodeRootObject: @"hello" error: nil],
1.321 + $data(0x13, 0x05, 'h', 'e', 'l', 'l', 'o'));
1.322 + CAssertEqual([MYDEREncoder encodeRootObject: @"thérè" error: nil],
1.323 + $data(0x0C, 0x07, 't', 'h', 0xC3, 0xA9, 'r', 0xC3, 0xA8));
1.324 +
1.325 + // Dates:
1.326 + CAssertEqual([MYDEREncoder encodeRootObject: [NSDate dateWithTimeIntervalSinceReferenceDate: 265336576]
1.327 + error: nil],
1.328 + $data(0x18, 0x0F, '2', '0', '0', '9', '0', '5', '3', '0', '0', '0', '3', '6', '1', '6', 'Z'));
1.329 +
1.330 + // Sequences:
1.331 + CAssertEqual([MYDEREncoder encodeRootObject: $array($object(72), $true) error: nil],
1.332 + $data(0x30, 0x06, 0x02, 0x01, 0x48, 0x01, 0x01, 0xFF));
1.333 + CAssertEqual([MYDEREncoder encodeRootObject: $array( $array($object(72), $true),
1.334 + $array($object(72), $true))
1.335 + error: nil],
1.336 + $data(0x30, 0x10,
1.337 + 0x30, 0x06, 0x02, 0x01, 0x48, 0x01, 0x01, 0xFF,
1.338 + 0x30, 0x06, 0x02, 0x01, 0x48, 0x01, 0x01, 0xFF));
1.339 +}
1.340 +
1.341 +
1.342 +TestCase(EncodeCert) {
1.343 + NSError *error = nil;
1.344 + NSData *cert = [NSData dataWithContentsOfFile: @"../../Tests/selfsigned.cer"]; //TEMP
1.345 + id certObjects = MYBERParse(cert,&error);
1.346 + CAssertNil(error);
1.347 + Log(@"Decoded as:\n%@", [MYASN1Object dump: certObjects]);
1.348 + NSData *encoded = [MYDEREncoder encodeRootObject: certObjects error: &error];
1.349 + CAssertNil(error);
1.350 + id reDecoded = MYBERParse(encoded, &error);
1.351 + CAssertNil(error);
1.352 + Log(@"Re-decoded as:\n%@", [MYASN1Object dump: reDecoded]);
1.353 + [encoded writeToFile: @"../../Tests/selfsigned_reencoded.cer" atomically: YES];
1.354 + CAssertEqual(encoded,cert);
1.355 +}