Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -16,10 +16,11 @@ OFASN1IA5String.m \ OFASN1Integer.m \ OFASN1IntegerOrEnumerated.m \ OFASN1Null.m \ OFASN1NumericString.m \ + OFASN1ObjectIdentifier.m \ OFASN1OctetString.m \ OFASN1PrintableString.m \ OFASN1UTF8String.m \ OFASN1Value.m \ OFApplication.m \ ADDED src/OFASN1ObjectIdentifier.h Index: src/OFASN1ObjectIdentifier.h ================================================================== --- src/OFASN1ObjectIdentifier.h +++ src/OFASN1ObjectIdentifier.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFASN1Value.h" + +OF_ASSUME_NONNULL_BEGIN + +@class OFArray OF_GENERIC(ObjetType); +@class OFNumber; + +/*! + * @brief An ASN.1 Object Identifier. + */ +@interface OFASN1ObjectIdentifier: OFASN1Value +{ + OFArray OF_GENERIC(OFNumber *) *_subidentifiers; +} + +/*! + * @brief The subidentifiers of the Object Identifier. + */ +@property (readonly, nonatomic) OFArray OF_GENERIC(OFNumber *) *subidentifiers; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFASN1ObjectIdentifier.m Index: src/OFASN1ObjectIdentifier.m ================================================================== --- src/OFASN1ObjectIdentifier.m +++ src/OFASN1ObjectIdentifier.m @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * Jonathan Schleifer + * + * All rights reserved. + * + * This file is part of ObjFW. It may be distributed under the terms of the + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#include "config.h" + +#import "OFASN1ObjectIdentifier.h" +#import "OFArray.h" +#import "OFData.h" +#import "OFNumber.h" +#import "OFString.h" + +#import "OFInvalidArgumentException.h" +#import "OFInvalidFormatException.h" +#import "OFOutOfRangeException.h" + +@implementation OFASN1ObjectIdentifier +@synthesize subidentifiers = _subidentifiers; + +- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass + tagNumber: (of_asn1_tag_number_t)tagNumber + constructed: (bool)constructed + DEREncodedContents: (OFData *)DEREncodedContents +{ + self = [super initWithTagClass: tagClass + tagNumber: tagNumber + constructed: constructed + DEREncodedContents: DEREncodedContents]; + + @try { + void *pool = objc_autoreleasePoolPush(); + const unsigned char *items = [_DEREncodedContents items]; + size_t count = [_DEREncodedContents count]; + OFMutableArray *subidentifiers = [OFMutableArray array]; + uintmax_t value = 0; + uint_fast8_t bits = 0; + + if (_tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || + _tagNumber != OF_ASN1_TAG_NUMBER_OBJECT_IDENTIFIER || + _constructed) + @throw [OFInvalidArgumentException exception]; + + if (count == 0) + @throw [OFInvalidFormatException exception]; + + for (size_t i = 0; i < count; i++) { + if (bits == 0 && items[i] == 0x80) + @throw [OFInvalidFormatException exception]; + + value = (value << 7) | (items[i] & 0x7F); + bits += 7; + + if (bits > sizeof(uintmax_t) * 8) + @throw [OFOutOfRangeException exception]; + + if (items[i] & 0x80) + continue; + + if ([subidentifiers count] == 0) { + if (value < 40) + [subidentifiers addObject: + [OFNumber numberWithUIntMax: 0]]; + else if (value < 80) { + [subidentifiers addObject: + [OFNumber numberWithUIntMax: 1]]; + value -= 40; + } else { + [subidentifiers addObject: + [OFNumber numberWithUIntMax: 2]]; + value -= 80; + } + } + + [subidentifiers addObject: + [OFNumber numberWithUIntMax: value]]; + + value = 0; + bits = 0; + } + + if (items[count - 1] & 0x80) + @throw [OFInvalidFormatException exception]; + + [subidentifiers makeImmutable]; + _subidentifiers = [subidentifiers copy]; + + objc_autoreleasePoolPop(pool); + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_subidentifiers release]; + + [super dealloc]; +} + +- (OFString *)description +{ + return [OFString stringWithFormat: + @"", + [_subidentifiers componentsJoinedByString: @"."]]; +} +@end Index: src/OFASN1Value.h ================================================================== --- src/OFASN1Value.h +++ src/OFASN1Value.h @@ -40,33 +40,35 @@ /*! * @brief ASN.1 tag number. */ typedef enum { /*! Boolean */ - OF_ASN1_TAG_NUMBER_BOOLEAN = 0x01, + OF_ASN1_TAG_NUMBER_BOOLEAN = 0x01, /*! Integer */ - OF_ASN1_TAG_NUMBER_INTEGER = 0x02, + OF_ASN1_TAG_NUMBER_INTEGER = 0x02, /*! Bit string */ - OF_ASN1_TAG_NUMBER_BIT_STRING = 0x03, + OF_ASN1_TAG_NUMBER_BIT_STRING = 0x03, /*! Octet string */ - OF_ASN1_TAG_NUMBER_OCTET_STRING = 0x04, + OF_ASN1_TAG_NUMBER_OCTET_STRING = 0x04, /*! Null */ - OF_ASN1_TAG_NUMBER_NULL = 0x05, + OF_ASN1_TAG_NUMBER_NULL = 0x05, + /*! Object Identifier */ + OF_ASN1_TAG_NUMBER_OBJECT_IDENTIFIER = 0x06, /*! Enumerated */ - OF_ASN1_TAG_NUMBER_ENUMERATED = 0x0A, + OF_ASN1_TAG_NUMBER_ENUMERATED = 0x0A, /*! UTF-8 string */ - OF_ASN1_TAG_NUMBER_UTF8_STRING = 0x0C, + OF_ASN1_TAG_NUMBER_UTF8_STRING = 0x0C, /*! Sequence */ - OF_ASN1_TAG_NUMBER_SEQUENCE = 0x10, + OF_ASN1_TAG_NUMBER_SEQUENCE = 0x10, /*! Set */ - OF_ASN1_TAG_NUMBER_SET = 0x11, + OF_ASN1_TAG_NUMBER_SET = 0x11, /*! NumericString */ - OF_ASN1_TAG_NUMBER_NUMERIC_STRING = 0x12, + OF_ASN1_TAG_NUMBER_NUMERIC_STRING = 0x12, /*! PrintableString */ - OF_ASN1_TAG_NUMBER_PRINTABLE_STRING = 0x13, + OF_ASN1_TAG_NUMBER_PRINTABLE_STRING = 0x13, /*! IA5String */ - OF_ASN1_TAG_NUMBER_IA5_STRING = 0x16 + OF_ASN1_TAG_NUMBER_IA5_STRING = 0x16 } of_asn1_tag_number_t; /*! * @brief A class representing an ASN.1 value. */ Index: src/OFData+ASN1DERValue.m ================================================================== --- src/OFData+ASN1DERValue.m +++ src/OFData+ASN1DERValue.m @@ -23,10 +23,11 @@ #import "OFASN1Enumerated.h" #import "OFASN1IA5String.h" #import "OFASN1Integer.h" #import "OFASN1Null.h" #import "OFASN1NumericString.h" +#import "OFASN1ObjectIdentifier.h" #import "OFASN1OctetString.h" #import "OFASN1PrintableString.h" #import "OFASN1UTF8String.h" #import "OFASN1Value.h" #import "OFArray.h" @@ -173,10 +174,13 @@ valueClass = [OFASN1OctetString class]; break; case OF_ASN1_TAG_NUMBER_NULL: valueClass = [OFASN1Null class]; break; + case OF_ASN1_TAG_NUMBER_OBJECT_IDENTIFIER: + valueClass = [OFASN1ObjectIdentifier class]; + break; case OF_ASN1_TAG_NUMBER_ENUMERATED: valueClass = [OFASN1Enumerated class]; break; case OF_ASN1_TAG_NUMBER_UTF8_STRING: valueClass = [OFASN1UTF8String class]; Index: tests/OFASN1DERValueTests.m ================================================================== --- tests/OFASN1DERValueTests.m +++ tests/OFASN1DERValueTests.m @@ -22,14 +22,16 @@ #import "OFASN1Boolean.h" #import "OFASN1IA5String.h" #import "OFASN1Integer.h" #import "OFASN1Null.h" #import "OFASN1NumericString.h" +#import "OFASN1ObjectIdentifier.h" #import "OFASN1OctetString.h" #import "OFASN1PrintableString.h" #import "OFASN1UTF8String.h" #import "OFArray.h" +#import "OFNumber.h" #import "OFSet.h" #import "OFString.h" #import "OFAutoreleasePool.h" #import "TestsAppDelegate.h" @@ -188,14 +190,61 @@ TEST(@"Parsing of null", [[[OFData dataWithItems: "\x05\x00" count: 2] ASN1DERValue] isKindOfClass: [OFASN1Null class]]) - EXPECT_EXCEPTION(@"Detection of invalid NULL", + EXPECT_EXCEPTION(@"Detection of invalid null", OFInvalidFormatException, [[OFData dataWithItems: "\x05\x01\x00" count: 3] ASN1DERValue]) + /* Object Identifier */ + TEST(@"Parsing of Object Identifier", + (array = [[[OFData dataWithItems: "\x06\x01\x27" + count: 3] ASN1DERValue] + subidentifiers]) && [array count] == 2 && + [[array objectAtIndex: 0] uIntMaxValue] == 0 && + [[array objectAtIndex: 1] uIntMaxValue] == 39 && + (array = [[[OFData dataWithItems: "\x06\x01\x4F" + count: 3] ASN1DERValue] + subidentifiers]) && [array count] == 2 && + [[array objectAtIndex: 0] uIntMaxValue] == 1 && + [[array objectAtIndex: 1] uIntMaxValue] == 39 && + (array = [[[OFData dataWithItems: "\x06\x02\x88\x37" + count: 4] ASN1DERValue] + subidentifiers]) && [array count] == 2 && + [[array objectAtIndex: 0] uIntMaxValue] == 2 && + [[array objectAtIndex: 1] uIntMaxValue] == 999 && + (array = [[[OFData dataWithItems: "\x06\x09\x2A\x86\x48\x86\xF7\x0D" + "\x01\x01\x0B" + count: 11] ASN1DERValue] + subidentifiers]) && [array count] == 7 && + [[array objectAtIndex: 0] uIntMaxValue] == 1 && + [[array objectAtIndex: 1] uIntMaxValue] == 2 && + [[array objectAtIndex: 2] uIntMaxValue] == 840 && + [[array objectAtIndex: 3] uIntMaxValue] == 113549 && + [[array objectAtIndex: 4] uIntMaxValue] == 1 && + [[array objectAtIndex: 5] uIntMaxValue] == 1 && + [[array objectAtIndex: 6] uIntMaxValue] == 11) + + EXPECT_EXCEPTION(@"Detection of invalid Object Identifier #1", + OFInvalidFormatException, [[OFData dataWithItems: "\x06\x01\x81" + count: 3] ASN1DERValue]) + + EXPECT_EXCEPTION(@"Detection of invalid Object Identifier #2", + OFInvalidFormatException, [[OFData dataWithItems: "\x06\x02\x80\x01" + count: 4] ASN1DERValue]) + + EXPECT_EXCEPTION(@"Detection of out of range Object Identifier", + OFOutOfRangeException, + [[OFData dataWithItems: "\x06\x0A\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + "\xFF\x7F" + count: 12] ASN1DERValue]) + + EXPECT_EXCEPTION(@"Detection of truncated Object Identifier", + OFTruncatedDataException, [[OFData dataWithItems: "\x06\x02\x00" + count: 3] ASN1DERValue]) + /* Enumerated */ TEST(@"Parsing of enumerated", [[[OFData dataWithItems: "\x0A\x00" count: 2] ASN1DERValue] integerValue] == 0 && [[[OFData dataWithItems: "\x0A\x01\x01"