Index: src/OFASN1BitString.h ================================================================== --- src/OFASN1BitString.h +++ src/OFASN1BitString.h @@ -39,10 +39,15 @@ /*! * @brief The length of the BitString in bits. */ @property (readonly, nonatomic) size_t bitStringLength; +/*! + * @brief The BitString in DER encoding. + */ +@property (readonly, nonatomic) OFData *DEREncodedValue; + /*! * @brief Creates an ASN.1 BitString with the specified BitString value and * length. * * @param bitStringValue The value of the BitString Index: src/OFASN1BitString.m ================================================================== --- src/OFASN1BitString.m +++ src/OFASN1BitString.m @@ -42,11 +42,11 @@ { self = [super init]; @try { if (bitStringValue.count * bitStringValue.itemSize != - bitStringLength / 8) + OF_ROUND_UP_POW2(8, bitStringLength) / 8) @throw [OFInvalidFormatException exception]; _bitStringValue = [bitStringValue copy]; _bitStringLength = bitStringLength; } @catch (id e) { @@ -78,20 +78,29 @@ @throw [OFInvalidFormatException exception]; lastByteBits = *(unsigned char *)[DEREncodedContents itemAtIndex: 0]; + if (lastByteBits > 7) + @throw [OFInvalidFormatException exception]; + + /* + * Can't have any bits of the last byte used if we have no byte. + */ 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; + bitStringLength = (count - 1) * 8; bitStringValue = [[DEREncodedContents subdataWithRange: of_range(1, count - 1)] copy]; + + if (lastByteBits != 0) + bitStringLength -= (8 - lastByteBits); } @catch (id e) { [self release]; @throw e; } @@ -112,10 +121,32 @@ { [_bitStringValue release]; [super dealloc]; } + +- (OFData *)DEREncodedValue +{ + size_t bitStringValueCount = [_bitStringValue count]; + unsigned char lastByteBits = _bitStringLength % 8; + unsigned char header[] = { 3, bitStringValueCount + 1, lastByteBits }; + OFMutableData *data; + + if (bitStringValueCount + 1 > UINT8_MAX || + bitStringValueCount != OF_ROUND_UP_POW2(8, _bitStringLength) / 8) + @throw [OFInvalidFormatException exception]; + + data = [OFMutableData dataWithCapacity: 3 + bitStringValueCount]; + [data addItems: header + count: 3]; + [data addItems: [_bitStringValue items] + count: bitStringValueCount]; + + [data makeImmutable]; + + return data; +} - (bool)isEqual: (id)object { OFASN1BitString *bitString; Index: tests/OFASN1DEREncodedValueTests.m ================================================================== --- tests/OFASN1DEREncodedValueTests.m +++ tests/OFASN1DEREncodedValueTests.m @@ -23,10 +23,33 @@ @implementation TestsAppDelegate (OFASN1DEREncodedValueTests) - (void)ASN1DEREncodedValueTests { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + OFData *data; + + module = @"OFASN1BitString"; + TEST(@"-[DEREncodedValue]", + (data = [OFData dataWithItems: "\xFF\x00\xF8" + count: 3]) && + [[[OFASN1BitString bitStringWithBitStringValue: data + bitStringLength: 20] DEREncodedValue] + isEqual: [OFData dataWithItems: "\x03\x04\x04\xFF\x00\xF8" + count: 6]] && + (data = [OFData dataWithItems: "abcdefäöü" + count: 12]) && + [[[OFASN1BitString bitStringWithBitStringValue: data + bitStringLength: 12 * 8] + DEREncodedValue] isEqual: + [OFData dataWithItems: "\x03\x0D\x00" "abcdefäöü" + count: 15]] && + (data = [OFData dataWithItems: "" + count: 0]) && + [[[OFASN1BitString bitStringWithBitStringValue: data + bitStringLength: 0] DEREncodedValue] + isEqual: [OFData dataWithItems: "\x03\x01\x00" + count: 3]]) module = @"OFASN1Boolean"; TEST(@"-[DEREncodedValue]", [[[OFASN1Boolean booleanWithBooleanValue: false] DEREncodedValue] isEqual: [OFData dataWithItems: "\x01\x01\x00" Index: tests/OFASN1DERValueTests.m ================================================================== --- tests/OFASN1DERValueTests.m +++ tests/OFASN1DERValueTests.m @@ -102,11 +102,11 @@ (bitString = [[OFData dataWithItems: "\x03\x0D\x01Hello World\x80" count: 15] ASN1DERValue]) && [bitString.bitStringValue isEqual: [OFData dataWithItems: "Hello World\x80" count: 12]] && - bitString.bitStringLength == 97 && + bitString.bitStringLength == 89 && (bitString = [[OFData dataWithItems: "\x03\x81\x80\x00xxxxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "xxxxxxxxxxxxxxxxxxxxxxxxxxx"