Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -9,12 +9,14 @@ FRAMEWORK = ${OBJFW_FRAMEWORK} LIB_MAJOR = ${OBJFW_LIB_MAJOR} LIB_MINOR = ${OBJFW_LIB_MINOR} SRCS = OFASN1Boolean.m \ + OFASN1IA5String.m \ OFASN1Integer.m \ OFASN1Null.m \ + OFASN1OctetString.m \ OFASN1UTF8String.m \ OFASN1Value.m \ OFApplication.m \ OFArray.m \ OFAutoreleasePool.m \ ADDED src/OFASN1IA5String.h Index: src/OFASN1IA5String.h ================================================================== --- src/OFASN1IA5String.h +++ src/OFASN1IA5String.h @@ -0,0 +1,43 @@ +/* + * 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 OFString; + +/*! + * @brief An ASN.1 IA5String. + */ +@interface OFASN1IA5String: OFASN1Value +{ + OFString *_IA5StringValue; +} + +/*! + * @brief The IA5String value. + */ +@property (readonly, nonatomic) OFString *IA5StringValue; + +/*! + * @brief The string value. + */ +@property (readonly, nonatomic) OFString *stringValue; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFASN1IA5String.m Index: src/OFASN1IA5String.m ================================================================== --- src/OFASN1IA5String.m +++ src/OFASN1IA5String.m @@ -0,0 +1,68 @@ +/* + * 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 "OFASN1IA5String.h" +#import "OFData.h" +#import "OFString.h" + +#import "OFInvalidArgumentException.h" + +@implementation OFASN1IA5String +@synthesize IA5StringValue = _IA5StringValue; + +- (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_IA5_STRING || + _constructed) + @throw [OFInvalidArgumentException exception]; + + _IA5StringValue = [[OFString alloc] + initWithCString: [_DEREncodedContents items] + encoding: OF_STRING_ENCODING_ASCII + length: [_DEREncodedContents count]]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_IA5StringValue release]; + + [super dealloc]; +} + +- (OFString *)stringValue +{ + return [self IA5StringValue]; +} +@end ADDED src/OFASN1OctetString.h Index: src/OFASN1OctetString.h ================================================================== --- src/OFASN1OctetString.h +++ src/OFASN1OctetString.h @@ -0,0 +1,34 @@ +/* + * 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 OFData; + +/*! + * @brief An ASN.1 octet string. + */ +@interface OFASN1OctetString: OFASN1Value +/*! + * @brief The octet string value. + */ +@property (readonly, nonatomic) OFData *octetStringValue; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFASN1OctetString.m Index: src/OFASN1OctetString.m ================================================================== --- src/OFASN1OctetString.m +++ src/OFASN1OctetString.m @@ -0,0 +1,53 @@ +/* + * 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 "OFASN1OctetString.h" +#import "OFData.h" + +#import "OFInvalidArgumentException.h" + +@implementation OFASN1OctetString +- (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_OCTET_STRING || + _constructed) + @throw [OFInvalidArgumentException exception]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (OFData *)octetStringValue +{ + return _DEREncodedContents; +} +@end Index: src/OFASN1UTF8String.h ================================================================== --- src/OFASN1UTF8String.h +++ src/OFASN1UTF8String.h @@ -24,15 +24,20 @@ /*! * @brief An ASN.1 UTF-8 string. */ @interface OFASN1UTF8String: OFASN1Value { - OFString *_stringValue; + OFString *_UTF8StringValue; } +/*! + * @brief The UTF-8 string value. + */ +@property (readonly, nonatomic) OFString *UTF8StringValue; + /*! * @brief The string value. */ @property (readonly, nonatomic) OFString *stringValue; @end OF_ASSUME_NONNULL_END Index: src/OFASN1UTF8String.m ================================================================== --- src/OFASN1UTF8String.m +++ src/OFASN1UTF8String.m @@ -22,11 +22,11 @@ #import "OFString.h" #import "OFInvalidArgumentException.h" @implementation OFASN1UTF8String -@synthesize stringValue = _stringValue; +@synthesize UTF8StringValue = _UTF8StringValue; - (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass tagNumber: (of_asn1_tag_number_t)tagNumber constructed: (bool)constructed DEREncodedContents: (OFData *)DEREncodedContents @@ -40,11 +40,11 @@ if (_tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || _tagNumber != OF_ASN1_TAG_NUMBER_UTF8_STRING || _constructed) @throw [OFInvalidArgumentException exception]; - _stringValue = [[OFString alloc] + _UTF8StringValue = [[OFString alloc] initWithUTF8String: [_DEREncodedContents items] length: [_DEREncodedContents count]]; } @catch (id e) { [self release]; @throw e; @@ -53,10 +53,15 @@ return self; } - (void)dealloc { - [_stringValue release]; + [_UTF8StringValue release]; [super dealloc]; } + +- (OFString *)stringValue +{ + return [self UTF8StringValue]; +} @end Index: src/OFASN1Value.h ================================================================== --- src/OFASN1Value.h +++ src/OFASN1Value.h @@ -40,19 +40,23 @@ /*! * @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, + /*! Octet string */ + OF_ASN1_TAG_NUMBER_OCTET_STRING = 0x04, /*! Null */ - OF_ASN1_TAG_NUMBER_NULL = 0x05, + OF_ASN1_TAG_NUMBER_NULL = 0x05, /*! 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, + /*! IA5String */ + 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 @@ -17,12 +17,14 @@ #include "config.h" #import "OFData+ASN1DERValue.h" #import "OFASN1Boolean.h" +#import "OFASN1IA5String.h" #import "OFASN1Integer.h" #import "OFASN1Null.h" +#import "OFASN1OctetString.h" #import "OFASN1UTF8String.h" #import "OFASN1Value.h" #import "OFArray.h" #import "OFInvalidArgumentException.h" @@ -39,18 +41,16 @@ static size_t parseObject(OFData *self, id *object, size_t depthLimit); static OFArray * parseSequence(OFData *contents, size_t depthLimit) { + OFMutableArray *ret = [OFMutableArray array]; size_t count = [contents count]; - OFMutableArray *ret; if (depthLimit == 0) @throw [OFOutOfRangeException exception]; - ret = [OFMutableArray array]; - while (count > 0) { id object; size_t objectLength; objectLength = parseObject(contents, &object, depthLimit); @@ -121,19 +121,27 @@ valueClass = [OFASN1Boolean class]; break; case OF_ASN1_TAG_NUMBER_INTEGER: valueClass = [OFASN1Integer class]; break; + case OF_ASN1_TAG_NUMBER_OCTET_STRING: + valueClass = [OFASN1OctetString class]; + break; case OF_ASN1_TAG_NUMBER_NULL: valueClass = [OFASN1Null class]; break; case OF_ASN1_TAG_NUMBER_UTF8_STRING: valueClass = [OFASN1UTF8String class]; break; + case OF_ASN1_TAG_NUMBER_SEQUENCE: + @throw [OFInvalidFormatException exception]; case OF_ASN1_TAG_NUMBER_SEQUENCE | ASN1_TAG_CONSTRUCTED_MASK: *object = parseSequence(contents, depthLimit - 1); return bytesConsumed; + case OF_ASN1_TAG_NUMBER_IA5_STRING: + valueClass = [OFASN1IA5String class]; + break; default: valueClass = [OFASN1Value class]; break; } Index: src/OFData.h ================================================================== --- src/OFData.h +++ src/OFData.h @@ -281,11 +281,11 @@ - (const void *)itemAtIndex: (size_t)index OF_RETURNS_INNER_POINTER; /*! * @brief Returns the data in the specified range as a new OFData. * - * @param range The range of the data for the new data + * @param range The range of the data for the new OFData * @return The data in the specified range as a new OFData */ - (OFData *)subdataWithRange: (of_range_t)range; /*! Index: tests/OFDataASN1DERValueTests.m ================================================================== --- tests/OFDataASN1DERValueTests.m +++ tests/OFDataASN1DERValueTests.m @@ -17,19 +17,22 @@ #include "config.h" #import "OFData.h" #import "OFASN1Boolean.h" +#import "OFASN1IA5String.h" #import "OFASN1Integer.h" #import "OFASN1Null.h" +#import "OFASN1OctetString.h" #import "OFASN1UTF8String.h" #import "OFArray.h" #import "OFString.h" #import "OFAutoreleasePool.h" #import "TestsAppDelegate.h" +#import "OFInvalidEncodingException.h" #import "OFInvalidFormatException.h" #import "OFOutOfRangeException.h" #import "OFTruncatedDataException.h" static OFString *module = @"OFData+ASN1DERValue"; @@ -98,10 +101,36 @@ EXPECT_EXCEPTION(@"Detection of truncated integer", OFTruncatedDataException, [[OFData dataWithItems: "\x02\x02\x00" count: 3] ASN1DERValue]) + TEST(@"Parsing of octet string", + [[[[OFData dataWithItems: "\x04\x0CHello World!" + count: 14] ASN1DERValue] octetStringValue] + isEqual: [OFData dataWithItems: "Hello World!" + count: 12]] && + [[[[OFData dataWithItems: "\x04\x81\x80xxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxx" + count: 131] ASN1DERValue] octetStringValue] + isEqual: [OFData dataWithItems: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxx" + count: 128]]) + + EXPECT_EXCEPTION(@"Detection of out of range octet string", + OFOutOfRangeException, + [[OFData dataWithItems: "\x16\x89" + "\x01\x01\x01\x01\x01\x01\x01\x01\x01" + count: 11] ASN1DERValue]) + + EXPECT_EXCEPTION(@"Detection of truncated octet string", + OFTruncatedDataException, [[OFData dataWithItems: "\x16\x01" + count: 2] ASN1DERValue]) + TEST(@"Parsing of NULL", [[[OFData dataWithItems: "\x05\x00" count: 2] ASN1DERValue] isKindOfClass: [OFASN1Null class]]) @@ -109,17 +138,17 @@ OFInvalidFormatException, [[OFData dataWithItems: "\x05\x01\x00" count: 3] ASN1DERValue]) TEST(@"Parsing of UTF-8 string", [[[[OFData dataWithItems: "\x0C\x0EHällo Wörld!" - count: 16] ASN1DERValue] stringValue] + count: 16] ASN1DERValue] UTF8StringValue] isEqual: @"Hällo Wörld!"] && [[[[OFData dataWithItems: "\x0C\x81\x80xxxxxxxxxxxxxxxxxxxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxx" - count: 131] ASN1DERValue] stringValue] + count: 131] ASN1DERValue] UTF8StringValue] isEqual: @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" @"xxxxxxxxxxxxxxxx"]) EXPECT_EXCEPTION(@"Detection of out of range UTF-8 string", @@ -165,8 +194,36 @@ EXPECT_EXCEPTION(@"Parsing of truncated sequence #2", OFTruncatedDataException, [[OFData dataWithItems: "\x30\x04\x02\x01\x01\x00\x00" count: 7] ASN1DERValue]) + TEST(@"Parsing of IA5String", + [[[[OFData dataWithItems: "\x16\x0CHello World!" + count: 14] ASN1DERValue] IA5StringValue] + isEqual: @"Hello World!"] && + [[[[OFData dataWithItems: "\x16\x81\x80xxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + "xxxxxxxxxxxxxxxxxxxx" + count: 131] ASN1DERValue] IA5StringValue] + isEqual: @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + @"xxxxxxxxxxxxxxxx"]) + + EXPECT_EXCEPTION(@"Detection of invalid IA5String", + OFInvalidEncodingException, + [[OFData dataWithItems: "\x16\x02ä" + count: 4] ASN1DERValue]) + + EXPECT_EXCEPTION(@"Detection of out of range IA5String", + OFOutOfRangeException, + [[OFData dataWithItems: "\x16\x89" + "\x01\x01\x01\x01\x01\x01\x01\x01\x01" + count: 11] ASN1DERValue]) + + EXPECT_EXCEPTION(@"Detection of truncated IA5String", + OFTruncatedDataException, [[OFData dataWithItems: "\x16\x01" + count: 2] ASN1DERValue]) + [pool drain]; } @end