Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -13,11 +13,10 @@ SRCS = OFASN1BitString.m \ OFASN1Boolean.m \ OFASN1Enumerated.m \ OFASN1IA5String.m \ OFASN1Integer.m \ - OFASN1Null.m \ OFASN1NumericString.m \ OFASN1ObjectIdentifier.m \ OFASN1OctetString.m \ OFASN1PrintableString.m \ OFASN1UTF8String.m \ Index: src/OFASN1BitString.h ================================================================== --- src/OFASN1BitString.h +++ src/OFASN1BitString.h @@ -13,32 +13,50 @@ * 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 "OFObject.h" #import "OFASN1Value.h" OF_ASSUME_NONNULL_BEGIN @class OFData; /*! - * @brief An ASN.1 bit string. + * @brief An ASN.1 BitString. */ -@interface OFASN1BitString: OFASN1Value +@interface OFASN1BitString: OFObject { OFData *_bitStringValue; size_t _bitStringLength; } /*! - * @brief The bit string value. + * @brief The BitString value. */ @property (readonly, nonatomic) OFData *bitStringValue; /*! - * @brief The length of the bit string in bits. + * @brief The length of the BitString in bits. */ @property (readonly, nonatomic) size_t bitStringLength; + +- (instancetype)init OF_UNAVAILABLE; + +/*! + * @brief Initializes an already allocated ASN.1 BitString with the specified + * arguments. + * + * @param tagClass The tag class of the value's type + * @param tagNumber The tag number of the value's type + * @param constructed Whether the value if of a constructed type + * @param DEREncodedContents The DER-encoded contents octets of the value. + * @return An initialized ASN.1 BitString + */ +- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass + tagNumber: (of_asn1_tag_number_t)tagNumber + constructed: (bool)constructed + DEREncodedContents: (OFData *)DEREncodedContents; @end OF_ASSUME_NONNULL_END Index: src/OFASN1BitString.m ================================================================== --- src/OFASN1BitString.m +++ src/OFASN1BitString.m @@ -26,46 +26,47 @@ #import "OFOutOfRangeException.h" @implementation OFASN1BitString @synthesize bitStringValue = _bitStringValue; @synthesize bitStringLength = _bitStringLength; + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} - (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]; + self = [super init]; @try { void *pool = objc_autoreleasePoolPush(); unsigned char lastByteBits; - size_t count = [_DEREncodedContents count]; + size_t count = [DEREncodedContents count]; - if (_tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || - _tagNumber != OF_ASN1_TAG_NUMBER_BIT_STRING || - _constructed) + if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || + tagNumber != OF_ASN1_TAG_NUMBER_BIT_STRING || constructed) @throw [OFInvalidArgumentException exception]; - if (count == 0) + if ([DEREncodedContents itemSize] != 1 || count == 0) @throw [OFInvalidFormatException exception]; lastByteBits = - *(unsigned char *)[_DEREncodedContents itemAtIndex: 0]; + *(unsigned char *)[DEREncodedContents itemAtIndex: 0]; if (count == 1 && lastByteBits != 0) @throw [OFInvalidFormatException exception]; if (SIZE_MAX / 8 < count - 1 || SIZE_MAX - (count - 1) * 8 < lastByteBits) @throw [OFOutOfRangeException exception]; _bitStringLength = (count - 1) * 8 + lastByteBits; - _bitStringValue = [[_DEREncodedContents + _bitStringValue = [[DEREncodedContents subdataWithRange: of_range(1, count - 1)] copy]; objc_autoreleasePoolPop(pool); } @catch (id e) { [self release]; @@ -79,12 +80,34 @@ { [_bitStringValue release]; [super dealloc]; } + +- (bool)isEqual: (id)object +{ + OFASN1BitString *bitString; + + if (![object isKindOfClass: [OFASN1BitString class]]) + return false; + + bitString = object; + + if (![bitString->_bitStringValue isEqual: _bitStringValue]) + return false; + if (bitString->_bitStringLength != _bitStringLength) + return false; + + return true; +} + +- (uint32_t)hash +{ + return [_bitStringValue hash] + (uint32_t)_bitStringLength; +} - (OFString *)description { return [OFString stringWithFormat: @"", _bitStringValue, _bitStringLength]; } @end Index: src/OFASN1Boolean.h ================================================================== --- src/OFASN1Boolean.h +++ src/OFASN1Boolean.h @@ -13,24 +13,42 @@ * 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 "OFObject.h" #import "OFASN1Value.h" OF_ASSUME_NONNULL_BEGIN /*! - * @brief An ASN.1 boolean. + * @brief An ASN.1 Boolean. */ -@interface OFASN1Boolean: OFASN1Value +@interface OFASN1Boolean: OFObject { bool _booleanValue; } /*! - * @brief The boolean value. + * @brief The Boolean value. */ @property (readonly, nonatomic) bool booleanValue; + +- (instancetype)init OF_UNAVAILABLE; + +/*! + * @brief Initializes an already allocated ASN.1 Boolean with the specified + * arguments. + * + * @param tagClass The tag class of the value's type + * @param tagNumber The tag number of the value's type + * @param constructed Whether the value if of a constructed type + * @param DEREncodedContents The DER-encoded contents octets of the value. + * @return An initialized ASN.1 Boolean + */ +- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass + tagNumber: (of_asn1_tag_number_t)tagNumber + constructed: (bool)constructed + DEREncodedContents: (OFData *)DEREncodedContents; @end OF_ASSUME_NONNULL_END Index: src/OFASN1Boolean.m ================================================================== --- src/OFASN1Boolean.m +++ src/OFASN1Boolean.m @@ -24,32 +24,35 @@ #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" @implementation OFASN1Boolean @synthesize booleanValue = _booleanValue; + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} - (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]; + self = [super init]; @try { unsigned char value; - if (_tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || - _tagNumber != OF_ASN1_TAG_NUMBER_BOOLEAN || _constructed) + if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || + tagNumber != OF_ASN1_TAG_NUMBER_BOOLEAN || constructed) @throw [OFInvalidArgumentException exception]; - if ([_DEREncodedContents count] != 1) + if ([DEREncodedContents itemSize] != 1 || + [DEREncodedContents count] != 1) @throw [OFInvalidFormatException exception]; - value = *(unsigned char *)[_DEREncodedContents itemAtIndex: 0]; + value = *(unsigned char *)[DEREncodedContents itemAtIndex: 0]; if (value != 0 && value != 0xFF) @throw [OFInvalidFormatException exception]; _booleanValue = value; @@ -58,13 +61,33 @@ @throw e; } return self; } + +- (bool)isEqual: (id)object +{ + OFASN1Boolean *boolean; + + if (![object isKindOfClass: [OFASN1Boolean class]]) + return false; + + boolean = object; + + if (boolean->_booleanValue != _booleanValue) + return false; + + return true; +} + +- (uint32_t)hash +{ + return (uint32_t)_booleanValue; +} - (OFString *)description { return (_booleanValue ? @"" : @""); } @end Index: src/OFASN1Enumerated.h ================================================================== --- src/OFASN1Enumerated.h +++ src/OFASN1Enumerated.h @@ -13,24 +13,42 @@ * 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 "OFObject.h" #import "OFASN1Value.h" OF_ASSUME_NONNULL_BEGIN /*! - * @brief An ASN.1 enumerated. + * @brief An ASN.1 Enumerated. */ -@interface OFASN1Enumerated: OFASN1Value +@interface OFASN1Enumerated: OFObject { intmax_t _integerValue; } /*! * @brief The integer value. */ @property (readonly, nonatomic) intmax_t integerValue; + +- (instancetype)init OF_UNAVAILABLE; + +/*! + * @brief Initializes an already allocated ASN.1 Enumerated with the specified + * arguments. + * + * @param tagClass The tag class of the value's type + * @param tagNumber The tag number of the value's type + * @param constructed Whether the value if of a constructed type + * @param DEREncodedContents The DER-encoded contents octets of the value. + * @return An initialized ASN.1 Enumerated + */ +- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass + tagNumber: (of_asn1_tag_number_t)tagNumber + constructed: (bool)constructed + DEREncodedContents: (OFData *)DEREncodedContents; @end OF_ASSUME_NONNULL_END Index: src/OFASN1Enumerated.m ================================================================== --- src/OFASN1Enumerated.m +++ src/OFASN1Enumerated.m @@ -26,37 +26,62 @@ extern intmax_t of_asn1_integer_parse(const unsigned char *buffer, size_t length); @implementation OFASN1Enumerated @synthesize integerValue = _integerValue; + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} - (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]; + self = [super init]; @try { - if (_tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || - _tagNumber != OF_ASN1_TAG_NUMBER_ENUMERATED || _constructed) + if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || + tagNumber != OF_ASN1_TAG_NUMBER_ENUMERATED || constructed) + @throw [OFInvalidArgumentException exception]; + + if ([DEREncodedContents itemSize] != 1) @throw [OFInvalidArgumentException exception]; _integerValue = of_asn1_integer_parse( - [_DEREncodedContents items], [_DEREncodedContents count]); + [DEREncodedContents items], [DEREncodedContents count]); } @catch (id e) { [self release]; @throw e; } return self; } + +- (bool)isEqual: (id)object +{ + OFASN1Enumerated *enumerated; + + if (![object isKindOfClass: [OFASN1Enumerated class]]) + return false; + + enumerated = object; + + if (enumerated->_integerValue != _integerValue) + return false; + + return true; +} + +- (uint32_t)hash +{ + return (uint32_t)_integerValue; +} - (OFString *)description { return [OFString stringWithFormat: @"", _integerValue]; } @end Index: src/OFASN1IA5String.h ================================================================== --- src/OFASN1IA5String.h +++ src/OFASN1IA5String.h @@ -13,20 +13,21 @@ * 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 "OFObject.h" #import "OFASN1Value.h" OF_ASSUME_NONNULL_BEGIN @class OFString; /*! * @brief An ASN.1 IA5String. */ -@interface OFASN1IA5String: OFASN1Value +@interface OFASN1IA5String: OFObject { OFString *_IA5StringValue; } /*! @@ -36,8 +37,25 @@ /*! * @brief The string value. */ @property (readonly, nonatomic) OFString *stringValue; + +- (instancetype)init OF_UNAVAILABLE; + +/*! + * @brief Initializes an already allocated ASN.1 IA5String with the specified + * arguments. + * + * @param tagClass The tag class of the value's type + * @param tagNumber The tag number of the value's type + * @param constructed Whether the value if of a constructed type + * @param DEREncodedContents The DER-encoded contents octets of the value. + * @return An initialized ASN.1 IA5String + */ +- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass + tagNumber: (of_asn1_tag_number_t)tagNumber + constructed: (bool)constructed + DEREncodedContents: (OFData *)DEREncodedContents; @end OF_ASSUME_NONNULL_END Index: src/OFASN1IA5String.m ================================================================== --- src/OFASN1IA5String.m +++ src/OFASN1IA5String.m @@ -23,31 +23,35 @@ #import "OFInvalidArgumentException.h" @implementation OFASN1IA5String @synthesize IA5StringValue = _IA5StringValue; + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} - (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]; + self = [super init]; @try { - if (_tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || - _tagNumber != OF_ASN1_TAG_NUMBER_IA5_STRING || - _constructed) + if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || + tagNumber != OF_ASN1_TAG_NUMBER_IA5_STRING || constructed) + @throw [OFInvalidArgumentException exception]; + + if ([DEREncodedContents itemSize] != 1) @throw [OFInvalidArgumentException exception]; _IA5StringValue = [[OFString alloc] - initWithCString: [_DEREncodedContents items] + initWithCString: [DEREncodedContents items] encoding: OF_STRING_ENCODING_ASCII - length: [_DEREncodedContents count]]; + length: [DEREncodedContents count]]; } @catch (id e) { [self release]; @throw e; } @@ -63,12 +67,32 @@ - (OFString *)stringValue { return [self IA5StringValue]; } + +- (bool)isEqual: (id)object +{ + OFASN1IA5String *IA5String; + + if (![object isKindOfClass: [OFASN1IA5String class]]) + return false; + + IA5String = object; + + if (![IA5String->_IA5StringValue isEqual: _IA5StringValue]) + return false; + + return true; +} + +- (uint32_t)hash +{ + return [_IA5StringValue hash]; +} - (OFString *)description { return [OFString stringWithFormat: @"", _IA5StringValue]; } @end Index: src/OFASN1Integer.h ================================================================== --- src/OFASN1Integer.h +++ src/OFASN1Integer.h @@ -13,24 +13,42 @@ * 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 "OFObject.h" #import "OFASN1Value.h" OF_ASSUME_NONNULL_BEGIN /*! - * @brief An ASN.1 integer. + * @brief An ASN.1 Integer. */ -@interface OFASN1Integer: OFASN1Value +@interface OFASN1Integer: OFObject { intmax_t _integerValue; } /*! - * @brief The integer value. + * @brief The Integer value. */ @property (readonly, nonatomic) intmax_t integerValue; + +- (instancetype)init OF_UNAVAILABLE; + +/*! + * @brief Initializes an already allocated ASN.1 Integer with the specified + * arguments. + * + * @param tagClass The tag class of the value's type + * @param tagNumber The tag number of the value's type + * @param constructed Whether the value if of a constructed type + * @param DEREncodedContents The DER-encoded contents octets of the value. + * @return An initialized ASN.1 Integer + */ +- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass + tagNumber: (of_asn1_tag_number_t)tagNumber + constructed: (bool)constructed + DEREncodedContents: (OFData *)DEREncodedContents; @end OF_ASSUME_NONNULL_END Index: src/OFASN1Integer.m ================================================================== --- src/OFASN1Integer.m +++ src/OFASN1Integer.m @@ -48,37 +48,62 @@ return value; } @implementation OFASN1Integer @synthesize integerValue = _integerValue; + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} - (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]; + self = [super init]; @try { - if (_tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || - _tagNumber != OF_ASN1_TAG_NUMBER_INTEGER || _constructed) + if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || + tagNumber != OF_ASN1_TAG_NUMBER_INTEGER || constructed) + @throw [OFInvalidArgumentException exception]; + + if ([DEREncodedContents itemSize] != 1) @throw [OFInvalidArgumentException exception]; _integerValue = of_asn1_integer_parse( - [_DEREncodedContents items], [_DEREncodedContents count]); + [DEREncodedContents items], [DEREncodedContents count]); } @catch (id e) { [self release]; @throw e; } return self; } + +- (bool)isEqual: (id)object +{ + OFASN1Integer *integer; + + if (![object isKindOfClass: [OFASN1Integer class]]) + return false; + + integer = object; + + if (integer->_integerValue != _integerValue) + return false; + + return true; +} + +- (uint32_t)hash +{ + return (uint32_t)_integerValue; +} - (OFString *)description { return [OFString stringWithFormat: @"", _integerValue]; } @end DELETED src/OFASN1Null.h Index: src/OFASN1Null.h ================================================================== --- src/OFASN1Null.h +++ src/OFASN1Null.h @@ -1,28 +0,0 @@ -/* - * 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 null value. - */ -@interface OFASN1Null: OFASN1Value -@end - -OF_ASSUME_NONNULL_END DELETED src/OFASN1Null.m Index: src/OFASN1Null.m ================================================================== --- src/OFASN1Null.m +++ src/OFASN1Null.m @@ -1,57 +0,0 @@ -/* - * 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 "OFASN1Null.h" -#import "OFData.h" -#import "OFString.h" - -#import "OFInvalidArgumentException.h" -#import "OFInvalidFormatException.h" - -@implementation OFASN1Null -- (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_NULL || _constructed) - @throw [OFInvalidArgumentException exception]; - - if ([_DEREncodedContents count] != 0) - @throw [OFInvalidFormatException exception]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -- (OFString *)description -{ - return @""; -} -@end Index: src/OFASN1NumericString.h ================================================================== --- src/OFASN1NumericString.h +++ src/OFASN1NumericString.h @@ -13,20 +13,21 @@ * 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 "OFObject.h" #import "OFASN1Value.h" OF_ASSUME_NONNULL_BEGIN @class OFString; /*! * @brief An ASN.1 NumericString. */ -@interface OFASN1NumericString: OFASN1Value +@interface OFASN1NumericString: OFObject { OFString *_numericStringValue; } /*! @@ -36,8 +37,25 @@ /*! * @brief The string value. */ @property (readonly, nonatomic) OFString *stringValue; + +- (instancetype)init OF_UNAVAILABLE; + +/*! + * @brief Initializes an already allocated ASN.1 NumericString with the + * specified arguments. + * + * @param tagClass The tag class of the value's type + * @param tagNumber The tag number of the value's type + * @param constructed Whether the value if of a constructed type + * @param DEREncodedContents The DER-encoded contents octets of the value. + * @return An initialized ASN.1 NumericString + */ +- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass + tagNumber: (of_asn1_tag_number_t)tagNumber + constructed: (bool)constructed + DEREncodedContents: (OFData *)DEREncodedContents; @end OF_ASSUME_NONNULL_END Index: src/OFASN1NumericString.m ================================================================== --- src/OFASN1NumericString.m +++ src/OFASN1NumericString.m @@ -24,38 +24,43 @@ #import "OFInvalidArgumentException.h" #import "OFInvalidEncodingException.h" @implementation OFASN1NumericString @synthesize numericStringValue = _numericStringValue; + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} - (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]; + self = [super init]; @try { - const unsigned char *items = [_DEREncodedContents items]; - size_t count = [_DEREncodedContents count]; + const unsigned char *items = [DEREncodedContents items]; + size_t count = [DEREncodedContents count]; + + if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || + tagNumber != OF_ASN1_TAG_NUMBER_NUMERIC_STRING || + constructed) + @throw [OFInvalidArgumentException exception]; - if (_tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || - _tagNumber != OF_ASN1_TAG_NUMBER_NUMERIC_STRING || - _constructed) + if ([DEREncodedContents itemSize] != 1) @throw [OFInvalidArgumentException exception]; for (size_t i = 0; i < count; i++) if (!of_ascii_isdigit(items[i]) && items[i] != ' ') @throw [OFInvalidEncodingException exception]; _numericStringValue = [[OFString alloc] - initWithCString: [_DEREncodedContents items] + initWithCString: [DEREncodedContents items] encoding: OF_STRING_ENCODING_ASCII - length: [_DEREncodedContents count]]; + length: [DEREncodedContents count]]; } @catch (id e) { [self release]; @throw e; } @@ -71,12 +76,32 @@ - (OFString *)stringValue { return [self numericStringValue]; } + +- (bool)isEqual: (id)object +{ + OFASN1NumericString *numericString; + + if (![object isKindOfClass: [OFASN1NumericString class]]) + return false; + + numericString = object; + + if (![numericString->_numericStringValue isEqual: _numericStringValue]) + return false; + + return true; +} + +- (uint32_t)hash +{ + return [_numericStringValue hash]; +} - (OFString *)description { return [OFString stringWithFormat: @"", _numericStringValue]; } @end Index: src/OFASN1ObjectIdentifier.h ================================================================== --- src/OFASN1ObjectIdentifier.h +++ src/OFASN1ObjectIdentifier.h @@ -13,27 +13,45 @@ * 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 "OFObject.h" #import "OFASN1Value.h" OF_ASSUME_NONNULL_BEGIN @class OFArray OF_GENERIC(ObjetType); @class OFNumber; /*! - * @brief An ASN.1 Object Identifier. + * @brief An ASN.1 ObjectIdentifier. */ -@interface OFASN1ObjectIdentifier: OFASN1Value +@interface OFASN1ObjectIdentifier: OFObject { OFArray OF_GENERIC(OFNumber *) *_subidentifiers; } /*! - * @brief The subidentifiers of the Object Identifier. + * @brief The subidentifiers of the ObjectIdentifier. */ @property (readonly, nonatomic) OFArray OF_GENERIC(OFNumber *) *subidentifiers; + +- (instancetype)init OF_UNAVAILABLE; + +/*! + * @brief Initializes an already allocated ASN.1 ObjectIdentifier with the + * specified arguments. + * + * @param tagClass The tag class of the value's type + * @param tagNumber The tag number of the value's type + * @param constructed Whether the value if of a constructed type + * @param DEREncodedContents The DER-encoded contents octets of the value. + * @return An initialized ASN.1 ObjectIdentifier + */ +- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass + tagNumber: (of_asn1_tag_number_t)tagNumber + constructed: (bool)constructed + DEREncodedContents: (OFData *)DEREncodedContents; @end OF_ASSUME_NONNULL_END Index: src/OFASN1ObjectIdentifier.m ================================================================== --- src/OFASN1ObjectIdentifier.m +++ src/OFASN1ObjectIdentifier.m @@ -27,36 +27,38 @@ #import "OFInvalidFormatException.h" #import "OFOutOfRangeException.h" @implementation OFASN1ObjectIdentifier @synthesize subidentifiers = _subidentifiers; + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} - (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]; + self = [super init]; @try { void *pool = objc_autoreleasePoolPush(); - const unsigned char *items = [_DEREncodedContents items]; - size_t count = [_DEREncodedContents count]; + 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) + if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || + tagNumber != OF_ASN1_TAG_NUMBER_OBJECT_IDENTIFIER || + constructed) @throw [OFInvalidArgumentException exception]; - if (count == 0) - @throw [OFInvalidFormatException exception]; + if ([DEREncodedContents itemSize] != 1 || count == 0) + @throw [OFInvalidArgumentException exception]; for (size_t i = 0; i < count; i++) { if (bits == 0 && items[i] == 0x80) @throw [OFInvalidFormatException exception]; @@ -110,13 +112,33 @@ { [_subidentifiers release]; [super dealloc]; } + +- (bool)isEqual: (id)object +{ + OFASN1ObjectIdentifier *objectIdentifier; + + if (![object isKindOfClass: [OFASN1ObjectIdentifier class]]) + return false; + + objectIdentifier = object; + + if (![objectIdentifier->_subidentifiers isEqual: _subidentifiers]) + return false; + + return true; +} + +- (uint32_t)hash +{ + return [_subidentifiers hash]; +} - (OFString *)description { return [OFString stringWithFormat: @"", [_subidentifiers componentsJoinedByString: @"."]]; } @end Index: src/OFASN1OctetString.h ================================================================== --- src/OFASN1OctetString.h +++ src/OFASN1OctetString.h @@ -13,22 +13,44 @@ * 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 "OFObject.h" #import "OFASN1Value.h" OF_ASSUME_NONNULL_BEGIN @class OFData; /*! - * @brief An ASN.1 octet string. + * @brief An ASN.1 OctetString. */ -@interface OFASN1OctetString: OFASN1Value +@interface OFASN1OctetString: OFObject +{ + OFData *_octetStringValue; +} + /*! - * @brief The octet string value. + * @brief The OctetString value. */ @property (readonly, nonatomic) OFData *octetStringValue; + +- (instancetype)init OF_UNAVAILABLE; + +/*! + * @brief Initializes an already allocated ASN.1 OctetString with the specified + * arguments. + * + * @param tagClass The tag class of the value's type + * @param tagNumber The tag number of the value's type + * @param constructed Whether the value if of a constructed type + * @param DEREncodedContents The DER-encoded contents octets of the value. + * @return An initialized ASN.1 OctetString + */ +- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass + tagNumber: (of_asn1_tag_number_t)tagNumber + constructed: (bool)constructed + DEREncodedContents: (OFData *)DEREncodedContents; @end OF_ASSUME_NONNULL_END Index: src/OFASN1OctetString.m ================================================================== --- src/OFASN1OctetString.m +++ src/OFASN1OctetString.m @@ -22,39 +22,70 @@ #import "OFString.h" #import "OFInvalidArgumentException.h" @implementation OFASN1OctetString +@synthesize octetStringValue = _octetStringValue; + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} + - (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]; + self = [super init]; @try { - if (_tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || - _tagNumber != OF_ASN1_TAG_NUMBER_OCTET_STRING || - _constructed) + if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || + tagNumber != OF_ASN1_TAG_NUMBER_OCTET_STRING || + constructed) + @throw [OFInvalidArgumentException exception]; + + if ([DEREncodedContents itemSize] != 1) @throw [OFInvalidArgumentException exception]; + + _octetStringValue = [DEREncodedContents copy]; } @catch (id e) { [self release]; @throw e; } return self; } -- (OFData *)octetStringValue +- (void)dealloc +{ + [_octetStringValue release]; + + [super dealloc]; +} + +- (bool)isEqual: (id)object +{ + OFASN1OctetString *octetString; + + if (![object isKindOfClass: [OFASN1OctetString class]]) + return false; + + octetString = object; + + if (![octetString->_octetStringValue isEqual: _octetStringValue]) + return false; + + return true; +} + +- (uint32_t)hash { - return _DEREncodedContents; + return [_octetStringValue hash]; } - (OFString *)description { return [OFString stringWithFormat: @"", - _DEREncodedContents]; + _octetStringValue]; } @end Index: src/OFASN1PrintableString.h ================================================================== --- src/OFASN1PrintableString.h +++ src/OFASN1PrintableString.h @@ -13,20 +13,21 @@ * 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 "OFObject.h" #import "OFASN1Value.h" OF_ASSUME_NONNULL_BEGIN @class OFString; /*! * @brief An ASN.1 PrintableString. */ -@interface OFASN1PrintableString: OFASN1Value +@interface OFASN1PrintableString: OFObject { OFString *_printableStringValue; } /*! @@ -36,8 +37,25 @@ /*! * @brief The string value. */ @property (readonly, nonatomic) OFString *stringValue; + +- (instancetype)init OF_UNAVAILABLE; + +/*! + * @brief Initializes an already allocated ASN.1 PrintableString with the + * specified arguments. + * + * @param tagClass The tag class of the value's type + * @param tagNumber The tag number of the value's type + * @param constructed Whether the value if of a constructed type + * @param DEREncodedContents The DER-encoded contents octets of the value. + * @return An initialized ASN.1 PrintableString + */ +- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass + tagNumber: (of_asn1_tag_number_t)tagNumber + constructed: (bool)constructed + DEREncodedContents: (OFData *)DEREncodedContents; @end OF_ASSUME_NONNULL_END Index: src/OFASN1PrintableString.m ================================================================== --- src/OFASN1PrintableString.m +++ src/OFASN1PrintableString.m @@ -24,28 +24,33 @@ #import "OFInvalidArgumentException.h" #import "OFInvalidEncodingException.h" @implementation OFASN1PrintableString @synthesize printableStringValue = _printableStringValue; + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} - (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]; + self = [super init]; @try { - const unsigned char *items = [_DEREncodedContents items]; - size_t count = [_DEREncodedContents count]; + const unsigned char *items = [DEREncodedContents items]; + size_t count = [DEREncodedContents count]; + + if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || + tagNumber != OF_ASN1_TAG_NUMBER_PRINTABLE_STRING || + constructed) + @throw [OFInvalidArgumentException exception]; - if (_tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || - _tagNumber != OF_ASN1_TAG_NUMBER_PRINTABLE_STRING || - _constructed) + if ([DEREncodedContents itemSize] != 1) @throw [OFInvalidArgumentException exception]; for (size_t i = 0; i < count; i++) { if (of_ascii_isalnum(items[i])) continue; @@ -68,13 +73,13 @@ @throw [OFInvalidEncodingException exception]; } } _printableStringValue = [[OFString alloc] - initWithCString: [_DEREncodedContents items] + initWithCString: [DEREncodedContents items] encoding: OF_STRING_ENCODING_ASCII - length: [_DEREncodedContents count]]; + length: [DEREncodedContents count]]; } @catch (id e) { [self release]; @throw e; } @@ -90,12 +95,33 @@ - (OFString *)stringValue { return [self printableStringValue]; } + +- (bool)isEqual: (id)object +{ + OFASN1PrintableString *printableString; + + if (![object isKindOfClass: [OFASN1PrintableString class]]) + return false; + + printableString = object; + + if (![printableString->_printableStringValue isEqual: + _printableStringValue]) + return false; + + return true; +} + +- (uint32_t)hash +{ + return [_printableStringValue hash]; +} - (OFString *)description { return [OFString stringWithFormat: @"", _printableStringValue]; } @end Index: src/OFASN1UTF8String.h ================================================================== --- src/OFASN1UTF8String.h +++ src/OFASN1UTF8String.h @@ -13,31 +13,49 @@ * 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 "OFObject.h" #import "OFASN1Value.h" OF_ASSUME_NONNULL_BEGIN @class OFString; /*! - * @brief An ASN.1 UTF-8 string. + * @brief An ASN.1 UTF8String. */ -@interface OFASN1UTF8String: OFASN1Value +@interface OFASN1UTF8String: OFObject { OFString *_UTF8StringValue; } /*! - * @brief The UTF-8 string value. + * @brief The UTF8String value. */ @property (readonly, nonatomic) OFString *UTF8StringValue; /*! * @brief The string value. */ @property (readonly, nonatomic) OFString *stringValue; + +- (instancetype)init OF_UNAVAILABLE; + +/*! + * @brief Initializes an already allocated ASN.1 UTF8String with the specified + * arguments. + * + * @param tagClass The tag class of the value's type + * @param tagNumber The tag number of the value's type + * @param constructed Whether the value if of a constructed type + * @param DEREncodedContents The DER-encoded contents octets of the value. + * @return An initialized ASN.1 UTF8String + */ +- (instancetype)initWithTagClass: (of_asn1_tag_class_t)tagClass + tagNumber: (of_asn1_tag_number_t)tagNumber + constructed: (bool)constructed + DEREncodedContents: (OFData *)DEREncodedContents; @end OF_ASSUME_NONNULL_END Index: src/OFASN1UTF8String.m ================================================================== --- src/OFASN1UTF8String.m +++ src/OFASN1UTF8String.m @@ -23,30 +23,34 @@ #import "OFInvalidArgumentException.h" @implementation OFASN1UTF8String @synthesize UTF8StringValue = _UTF8StringValue; + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} - (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]; + self = [super init]; @try { - if (_tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || - _tagNumber != OF_ASN1_TAG_NUMBER_UTF8_STRING || - _constructed) + if (tagClass != OF_ASN1_TAG_CLASS_UNIVERSAL || + tagNumber != OF_ASN1_TAG_NUMBER_UTF8_STRING || constructed) + @throw [OFInvalidArgumentException exception]; + + if ([DEREncodedContents itemSize] != 1) @throw [OFInvalidArgumentException exception]; _UTF8StringValue = [[OFString alloc] - initWithUTF8String: [_DEREncodedContents items] - length: [_DEREncodedContents count]]; + initWithUTF8String: [DEREncodedContents items] + length: [DEREncodedContents count]]; } @catch (id e) { [self release]; @throw e; } @@ -62,12 +66,32 @@ - (OFString *)stringValue { return [self UTF8StringValue]; } + +- (bool)isEqual: (id)object +{ + OFASN1UTF8String *UTF8String; + + if (![object isKindOfClass: [OFASN1UTF8String class]]) + return false; + + UTF8String = object; + + if (![UTF8String->_UTF8StringValue isEqual: _UTF8StringValue]) + return false; + + return true; +} + +- (uint32_t)hash +{ + return [_UTF8StringValue hash]; +} - (OFString *)description { return [OFString stringWithFormat: @"", _UTF8StringValue]; } @end Index: src/OFASN1Value.h ================================================================== --- src/OFASN1Value.h +++ src/OFASN1Value.h @@ -70,11 +70,11 @@ } of_asn1_tag_number_t; /*! * @brief A class representing an ASN.1 value. */ -@interface OFASN1Value: OFObject +@interface OFASN1Value: OFObject { of_asn1_tag_class_t _tagClass; of_asn1_tag_number_t _tagNumber; bool _constructed; OFData *_DEREncodedContents; Index: src/OFASN1Value.m ================================================================== --- src/OFASN1Value.m +++ src/OFASN1Value.m @@ -110,15 +110,10 @@ OF_HASH_FINALIZE(hash); return hash; } -- (id)copy -{ - return [self retain]; -} - - (OFString *)description { return [OFString stringWithFormat: @"> 6 - tagNumber: tag & 0x1F - constructed: tag & ASN1_TAG_CONSTRUCTED_MASK - DEREncodedContents: contents]; + *object = [[[valueClass alloc] + initWithTagClass: tag >> 6 + tagNumber: tag & 0x1F + constructed: tag & ASN1_TAG_CONSTRUCTED_MASK + DEREncodedContents: contents] autorelease]; return bytesConsumed; } @implementation OFData (ASN1DERValue) - (id)ASN1DERValue Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -126,10 +126,22 @@ #import "OFSandbox.h" #ifdef OF_WINDOWS # import "OFWindowsRegistryKey.h" #endif + +#import "OFASN1BitString.h" +#import "OFASN1Boolean.h" +#import "OFASN1Enumerated.h" +#import "OFASN1IA5String.h" +#import "OFASN1Integer.h" +#import "OFASN1NumericString.h" +#import "OFASN1ObjectIdentifier.h" +#import "OFASN1OctetString.h" +#import "OFASN1PrintableString.h" +#import "OFASN1UTF8String.h" +#import "OFASN1Value.h" #import "OFAllocFailedException.h" #import "OFException.h" #ifdef OF_HAVE_SOCKETS # import "OFAcceptFailedException.h" Index: tests/OFASN1DERValueTests.m ================================================================== --- tests/OFASN1DERValueTests.m +++ tests/OFASN1DERValueTests.m @@ -20,17 +20,17 @@ #import "OFData.h" #import "OFASN1BitString.h" #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 "OFNull.h" #import "OFNumber.h" #import "OFSet.h" #import "OFString.h" #import "OFAutoreleasePool.h" @@ -187,12 +187,11 @@ count: 2] ASN1DERValue]) /* Null */ TEST(@"Parsing of null", [[[OFData dataWithItems: "\x05\x00" - count: 2] ASN1DERValue] - isKindOfClass: [OFASN1Null class]]) + count: 2] ASN1DERValue] isEqual: [OFNull null]]) EXPECT_EXCEPTION(@"Detection of invalid null", OFInvalidFormatException, [[OFData dataWithItems: "\x05\x01\x00" count: 3] ASN1DERValue])