Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -10,12 +10,14 @@ LIB_MAJOR = ${OBJFW_LIB_MAJOR} LIB_MINOR = ${OBJFW_LIB_MINOR} SRCS = OFASN1BitString.m \ OFASN1Boolean.m \ + OFASN1Enumerated.m \ OFASN1IA5String.m \ OFASN1Integer.m \ + OFASN1IntegerOrEnumerated.m \ OFASN1Null.m \ OFASN1NumericString.m \ OFASN1OctetString.m \ OFASN1PrintableString.m \ OFASN1UTF8String.m \ ADDED src/OFASN1Enumerated.h Index: src/OFASN1Enumerated.h ================================================================== --- src/OFASN1Enumerated.h +++ src/OFASN1Enumerated.h @@ -0,0 +1,28 @@ +/* + * 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 "OFASN1IntegerOrEnumerated.h" + +OF_ASSUME_NONNULL_BEGIN + +/*! + * @brief An ASN.1 enumerated. + */ +@interface OFASN1Enumerated: OFASN1IntegerOrEnumerated +@end + +OF_ASSUME_NONNULL_END ADDED src/OFASN1Enumerated.m Index: src/OFASN1Enumerated.m ================================================================== --- src/OFASN1Enumerated.m +++ src/OFASN1Enumerated.m @@ -0,0 +1,46 @@ +/* + * 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 "OFASN1Enumerated.h" + +#import "OFInvalidArgumentException.h" + +@implementation OFASN1Enumerated +- (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 { + if (_tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || + _tagNumber != OF_ASN1_TAG_NUMBER_ENUMERATED || _constructed) + @throw [OFInvalidArgumentException exception]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} +@end Index: src/OFASN1Integer.h ================================================================== --- src/OFASN1Integer.h +++ src/OFASN1Integer.h @@ -13,24 +13,16 @@ * 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" +#import "OFASN1IntegerOrEnumerated.h" OF_ASSUME_NONNULL_BEGIN /*! * @brief An ASN.1 integer. */ -@interface OFASN1Integer: OFASN1Value -{ - intmax_t _integerValue; -} - -/*! - * @brief The integer value. - */ -@property (readonly, nonatomic) intmax_t integerValue; +@interface OFASN1Integer: OFASN1IntegerOrEnumerated @end OF_ASSUME_NONNULL_END Index: src/OFASN1Integer.m ================================================================== --- src/OFASN1Integer.m +++ src/OFASN1Integer.m @@ -16,20 +16,14 @@ */ #include "config.h" #import "OFASN1Integer.h" -#import "OFData.h" -#import "OFString.h" #import "OFInvalidArgumentException.h" -#import "OFInvalidFormatException.h" -#import "OFOutOfRangeException.h" @implementation OFASN1Integer -@synthesize integerValue = _integerValue; - - (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass tagNumber: (of_asn1_tag_number_t)tagNumber constructed: (bool)constructed DEREncodedContents: (OFData *)DEREncodedContents { @@ -37,47 +31,16 @@ tagNumber: tagNumber constructed: constructed DEREncodedContents: DEREncodedContents]; @try { - const unsigned char *items; - size_t count; - uintmax_t value; - if (_tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || _tagNumber != OF_ASN1_TAG_NUMBER_INTEGER || _constructed) @throw [OFInvalidArgumentException exception]; - - /* TODO: Support for big numbers */ - items = [_DEREncodedContents items]; - count = [_DEREncodedContents count]; - value = 0; - - if (count > sizeof(uintmax_t) && - (count != sizeof(uintmax_t) + 1 || items[0] != 0)) - @throw [OFOutOfRangeException exception]; - - if (count >= 2 && ((items[0] == 0 && !(items[1] & 0x80)) || - (items[0] == 0xFF && items[1] & 0x80))) - @throw [OFInvalidFormatException exception]; - - if (count >= 1 && items[0] & 0x80) - value = ~(uintmax_t)0; - - while (count--) - value = (value << 8) | *items++; - - _integerValue = value; } @catch (id e) { [self release]; @throw e; } return self; } - -- (OFString *)description -{ - return [OFString stringWithFormat: @"", - _integerValue]; -} @end ADDED src/OFASN1IntegerOrEnumerated.h Index: src/OFASN1IntegerOrEnumerated.h ================================================================== --- src/OFASN1IntegerOrEnumerated.h +++ src/OFASN1IntegerOrEnumerated.h @@ -0,0 +1,36 @@ +/* + * 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 + +/*! + * @brief An ASN.1 integer or enumerated. + */ +@interface OFASN1IntegerOrEnumerated: OFASN1Value +{ + intmax_t _integerValue; +} + +/*! + * @brief The integer value. + */ +@property (readonly, nonatomic) intmax_t integerValue; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFASN1IntegerOrEnumerated.m Index: src/OFASN1IntegerOrEnumerated.m ================================================================== --- src/OFASN1IntegerOrEnumerated.m +++ src/OFASN1IntegerOrEnumerated.m @@ -0,0 +1,84 @@ +/* + * 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 "OFASN1IntegerOrEnumerated.h" +#import "OFData.h" +#import "OFString.h" + +#import "OFInvalidFormatException.h" +#import "OFNotImplementedException.h" +#import "OFOutOfRangeException.h" + +@implementation OFASN1IntegerOrEnumerated +@synthesize integerValue = _integerValue; + ++ (instancetype)alloc +{ + if (self == [OFASN1IntegerOrEnumerated class]) + @throw [OFNotImplementedException exceptionWithSelector: _cmd + object: self]; + + return [super alloc]; +} + +- (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 { + const unsigned char *items = [_DEREncodedContents items]; + size_t count = [_DEREncodedContents count]; + uintmax_t value = 0; + + /* TODO: Support for big numbers */ + if (count > sizeof(uintmax_t) && + (count != sizeof(uintmax_t) + 1 || items[0] != 0)) + @throw [OFOutOfRangeException exception]; + + if (count >= 2 && ((items[0] == 0 && !(items[1] & 0x80)) || + (items[0] == 0xFF && items[1] & 0x80))) + @throw [OFInvalidFormatException exception]; + + if (count >= 1 && items[0] & 0x80) + value = ~(uintmax_t)0; + + while (count--) + value = (value << 8) | *items++; + + _integerValue = value; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (OFString *)description +{ + return [OFString stringWithFormat: @"<%@: %jd>", + [self class], _integerValue]; +} +@end Index: src/OFASN1Value.h ================================================================== --- src/OFASN1Value.h +++ src/OFASN1Value.h @@ -49,10 +49,12 @@ OF_ASN1_TAG_NUMBER_BIT_STRING = 0x03, /*! Octet string */ OF_ASN1_TAG_NUMBER_OCTET_STRING = 0x04, /*! Null */ OF_ASN1_TAG_NUMBER_NULL = 0x05, + /*! Enumerated */ + OF_ASN1_TAG_NUMBER_ENUMERATED = 0x0A, /*! UTF-8 string */ OF_ASN1_TAG_NUMBER_UTF8_STRING = 0x0C, /*! Sequence */ OF_ASN1_TAG_NUMBER_SEQUENCE = 0x10, /*! Set */ Index: src/OFData+ASN1DERValue.m ================================================================== --- src/OFData+ASN1DERValue.m +++ src/OFData+ASN1DERValue.m @@ -18,10 +18,11 @@ #include "config.h" #import "OFData+ASN1DERValue.h" #import "OFASN1BitString.h" #import "OFASN1Boolean.h" +#import "OFASN1Enumerated.h" #import "OFASN1IA5String.h" #import "OFASN1Integer.h" #import "OFASN1Null.h" #import "OFASN1NumericString.h" #import "OFASN1OctetString.h" @@ -172,10 +173,13 @@ valueClass = [OFASN1OctetString class]; break; case OF_ASN1_TAG_NUMBER_NULL: valueClass = [OFASN1Null class]; break; + case OF_ASN1_TAG_NUMBER_ENUMERATED: + valueClass = [OFASN1Enumerated class]; + break; case OF_ASN1_TAG_NUMBER_UTF8_STRING: valueClass = [OFASN1UTF8String class]; break; case OF_ASN1_TAG_NUMBER_SEQUENCE: if (!(tag & ASN1_TAG_CONSTRUCTED_MASK)) Index: tests/OFASN1DERValueTests.m ================================================================== --- tests/OFASN1DERValueTests.m +++ tests/OFASN1DERValueTests.m @@ -88,19 +88,19 @@ (uintmax_t)[[[OFData dataWithItems: "\x02\x09\x00\xFF\xFF\xFF\xFF" "\xFF\xFF\xFF\xFF" count: 11] ASN1DERValue] integerValue] == UINTMAX_MAX) - EXPECT_EXCEPTION(@"Detecting of invalid integer #1", + EXPECT_EXCEPTION(@"Detection of invalid integer #1", OFInvalidFormatException, [[OFData dataWithItems: "\x02\x02\x00\x00" count: 4] ASN1DERValue]) - EXPECT_EXCEPTION(@"Detecting of invalid integer #2", + EXPECT_EXCEPTION(@"Detection of invalid integer #2", OFInvalidFormatException, [[OFData dataWithItems: "\x02\x02\x00\x7F" count: 4] ASN1DERValue]) - EXPECT_EXCEPTION(@"Detecting of invalid integer #3", + EXPECT_EXCEPTION(@"Detection of invalid integer #3", OFInvalidFormatException, [[OFData dataWithItems: "\x02\x02\xFF\x80" count: 4] ASN1DERValue]) EXPECT_EXCEPTION(@"Detection of out of range integer", OFOutOfRangeException, @@ -192,10 +192,49 @@ EXPECT_EXCEPTION(@"Detection of invalid NULL", OFInvalidFormatException, [[OFData dataWithItems: "\x05\x01\x00" count: 3] ASN1DERValue]) + /* Enumerated */ + TEST(@"Parsing of enumerated", + [[[OFData dataWithItems: "\x0A\x00" + count: 2] ASN1DERValue] integerValue] == 0 && + [[[OFData dataWithItems: "\x0A\x01\x01" + count: 3] ASN1DERValue] integerValue] == 1 && + [[[OFData dataWithItems: "\x0A\x02\x01\x04" + count: 4] ASN1DERValue] integerValue] == 260 && + [[[OFData dataWithItems: "\x0A\x01\xFF" + count: 3] ASN1DERValue] integerValue] == -1 && + [[[OFData dataWithItems: "\x0A\x03\xFF\x00\x00" + count: 5] ASN1DERValue] integerValue] == -65536 && + (uintmax_t)[[[OFData dataWithItems: "\x0A\x09\x00\xFF\xFF\xFF\xFF" + "\xFF\xFF\xFF\xFF" + count: 11] ASN1DERValue] + integerValue] == UINTMAX_MAX) + + EXPECT_EXCEPTION(@"Detection of invalid enumerated #1", + OFInvalidFormatException, [[OFData dataWithItems: "\x0A\x02\x00\x00" + count: 4] ASN1DERValue]) + + EXPECT_EXCEPTION(@"Detection of invalid enumerated #2", + OFInvalidFormatException, [[OFData dataWithItems: "\x0A\x02\x00\x7F" + count: 4] ASN1DERValue]) + + EXPECT_EXCEPTION(@"Detection of invalid enumerated #3", + OFInvalidFormatException, [[OFData dataWithItems: "\x0A\x02\xFF\x80" + count: 4] ASN1DERValue]) + + EXPECT_EXCEPTION(@"Detection of out of range enumerated", + OFOutOfRangeException, + [[OFData dataWithItems: "\x0A\x09\x01" + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" + count: 11] ASN1DERValue]) + + EXPECT_EXCEPTION(@"Detection of truncated enumerated", + OFTruncatedDataException, [[OFData dataWithItems: "\x0A\x02\x00" + count: 3] ASN1DERValue]) + /* UTF-8 string */ TEST(@"Parsing of UTF-8 string", [[[[OFData dataWithItems: "\x0C\x0EHällo Wörld!" count: 16] ASN1DERValue] UTF8StringValue] isEqual: @"Hällo Wörld!"] &&