/*
* Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
*
* 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 "ObjFW.h"
#import "ObjFWTest.h"
@interface OFASN1DERParsingTests: OTTestCase
@end
@implementation OFASN1DERParsingTests
- (void)testBoolean
{
OTAssertFalse(
[[[OFData dataWithItems: "\x01\x01\x00"
count: 3] objectByParsingASN1DER] boolValue]);
OTAssertTrue(
[[[OFData dataWithItems: "\x01\x01\xFF"
count: 3] objectByParsingASN1DER] boolValue]);
}
- (void)testInvalidBooleanFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x01\x01\x01"
count: 3] objectByParsingASN1DER],
OFInvalidFormatException);
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x01\x02\x00\x00"
count: 4] objectByParsingASN1DER],
OFInvalidFormatException);
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x01\x00"
count: 2] objectByParsingASN1DER],
OFInvalidFormatException);
}
- (void)testTruncatedBooleanFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x01\x01"
count: 2] objectByParsingASN1DER],
OFTruncatedDataException);
}
- (void)testInteger
{
OTAssertEqual([[[OFData dataWithItems: "\x02\x00" count: 2]
objectByParsingASN1DER] longLongValue], 0);
OTAssertEqual([[[OFData dataWithItems: "\x02\x01\x01" count: 3]
objectByParsingASN1DER] longLongValue], 1);
OTAssertEqual([[[OFData dataWithItems: "\x02\x02\x01\x04" count: 4]
objectByParsingASN1DER] longLongValue], 260);
OTAssertEqual([[[OFData dataWithItems: "\x02\x01\xFF" count: 3]
objectByParsingASN1DER] longLongValue], -1);
OTAssertEqual([[[OFData dataWithItems: "\x02\x03\xFF\x00\x00" count: 5]
objectByParsingASN1DER] longLongValue], -65536);
OTAssertEqual((unsigned long long)[[[OFData
dataWithItems: "\x02\x09\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
count: 11] objectByParsingASN1DER] longLongValue],
ULLONG_MAX);
}
- (void)testInvalidIntegerFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x02\x02\x00\x00"
count: 4] objectByParsingASN1DER],
OFInvalidFormatException);
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x02\x02\x00\x7F"
count: 4] objectByParsingASN1DER],
OFInvalidFormatException);
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x02\x02\xFF\x80"
count: 4] objectByParsingASN1DER],
OFInvalidFormatException);
}
- (void)testOutOfRangeIntegerFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x02\x09\x01"
"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
count: 11] objectByParsingASN1DER],
OFOutOfRangeException);
}
- (void)testTruncatedIntegerFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x02\x02\x00"
count: 3] objectByParsingASN1DER],
OFTruncatedDataException);
}
- (void)testBitString
{
OFASN1BitString *bitString;
bitString = [[OFData dataWithItems: "\x03\x01\x00"
count: 3] objectByParsingASN1DER];
OTAssertEqualObjects(bitString.bitStringValue, [OFData data]);
OTAssertEqual(bitString.bitStringLength, 0);
bitString = [[OFData dataWithItems: "\x03\x0D\x01Hello World\x80"
count: 15] objectByParsingASN1DER];
OTAssertEqualObjects(bitString.bitStringValue,
[OFData dataWithItems: "Hello World\x80" count: 12]);
OTAssertEqual(bitString.bitStringLength, 95);
bitString = [[OFData dataWithItems: "\x03\x81\x80\x00xxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxx"
count: 131] objectByParsingASN1DER];
OTAssertEqualObjects(bitString.bitStringValue,
[OFData dataWithItems: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
count: 127]);
OTAssertEqual(bitString.bitStringLength, 127 * 8);
}
- (void)testInvalidBitStringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x03\x00"
count: 2] objectByParsingASN1DER],
OFInvalidFormatException);
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x03\x01\x01"
count: 3] objectByParsingASN1DER],
OFInvalidFormatException);
}
- (void)testOutOfRangeBitStringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x03\x89"
"\x01\x01\x01\x01\x01\x01\x01\x01\x01"
count: 11] objectByParsingASN1DER],
OFOutOfRangeException);
}
- (void)testTruncatedBitStringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x03\x01"
count: 2] objectByParsingASN1DER],
OFTruncatedDataException);
}
- (void)testOctetString
{
OTAssertEqualObjects([[[OFData
dataWithItems: "\x04\x0CHello World!"
count: 14] objectByParsingASN1DER] octetStringValue],
[OFData dataWithItems: "Hello World!" count: 12]);
OTAssertEqualObjects(
[[[OFData dataWithItems: "\x04\x81\x80xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxx"
count: 131] objectByParsingASN1DER]
octetStringValue],
[OFData dataWithItems: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
count: 128]);
}
- (void)testOutOfRangeOctetStringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x04\x89"
"\x01\x01\x01\x01\x01\x01\x01\x01\x01"
count: 11] objectByParsingASN1DER],
OFOutOfRangeException);
}
- (void)testTruncatedOctetStringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x04\x01"
count: 2] objectByParsingASN1DER],
OFTruncatedDataException);
}
- (void)testNull
{
OTAssertEqualObjects([[OFData dataWithItems: "\x05\x00" count: 2]
objectByParsingASN1DER], [OFNull null]);
}
- (void)testInvalidNullFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x05\x01\x00"
count: 3] objectByParsingASN1DER],
OFInvalidFormatException);
}
- (void)testObjectIdentifier
{
OFArray *array;
array = [[[OFData dataWithItems: "\x06\x01\x27" count: 3]
objectByParsingASN1DER] subidentifiers];
OTAssertEqual(array.count, 2);
OTAssertEqual([[array objectAtIndex: 0] unsignedLongLongValue], 0);
OTAssertEqual([[array objectAtIndex: 1] unsignedLongLongValue], 39);
array = [[[OFData dataWithItems: "\x06\x01\x4F" count: 3]
objectByParsingASN1DER] subidentifiers];
OTAssertEqual(array.count, 2);
OTAssertEqual([[array objectAtIndex: 0] unsignedLongLongValue], 1);
OTAssertEqual([[array objectAtIndex: 1] unsignedLongLongValue], 39);
array = [[[OFData dataWithItems: "\x06\x02\x88\x37" count: 4]
objectByParsingASN1DER] subidentifiers];
OTAssertEqual(array.count, 2);
OTAssertEqual([[array objectAtIndex: 0] unsignedLongLongValue], 2);
OTAssertEqual([[array objectAtIndex: 1] unsignedLongLongValue], 999);
array = [[[OFData
dataWithItems: "\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0B"
count: 11] objectByParsingASN1DER] subidentifiers];
OTAssertEqual(array.count, 7);
OTAssertEqual([[array objectAtIndex: 0] unsignedLongLongValue], 1);
OTAssertEqual([[array objectAtIndex: 1] unsignedLongLongValue], 2);
OTAssertEqual([[array objectAtIndex: 2] unsignedLongLongValue], 840);
OTAssertEqual([[array objectAtIndex: 3] unsignedLongLongValue], 113549);
OTAssertEqual([[array objectAtIndex: 4] unsignedLongLongValue], 1);
OTAssertEqual([[array objectAtIndex: 5] unsignedLongLongValue], 1);
OTAssertEqual([[array objectAtIndex: 6] unsignedLongLongValue], 11);
}
- (void)testInvalidObjectIdentifierFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x06\x01\x81"
count: 3] objectByParsingASN1DER],
OFInvalidFormatException);
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x06\x02\x80\x01"
count: 4] objectByParsingASN1DER],
OFInvalidFormatException);
}
- (void)testOutOfRangeObjectIdentifier
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x06\x0A\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
"\xFF\x7F"
count: 12] objectByParsingASN1DER],
OFOutOfRangeException);
}
- (void)testTruncatedObjectIdentifierFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x06\x02\x00"
count: 3] objectByParsingASN1DER],
OFTruncatedDataException);
}
- (void)testEnumerated
{
OTAssertEqual([[[OFData dataWithItems: "\x0A\x00" count: 2]
objectByParsingASN1DER] longLongValue], 0);
OTAssertEqual([[[OFData dataWithItems: "\x0A\x01\x01" count: 3]
objectByParsingASN1DER] longLongValue], 1);
OTAssertEqual([[[OFData dataWithItems: "\x0A\x02\x01\x04" count: 4]
objectByParsingASN1DER] longLongValue], 260);
OTAssertEqual([[[OFData dataWithItems: "\x0A\x01\xFF" count: 3]
objectByParsingASN1DER] longLongValue], -1);
OTAssertEqual([[[OFData dataWithItems: "\x0A\x03\xFF\x00\x00" count: 5]
objectByParsingASN1DER] longLongValue], -65536);
OTAssertEqual((unsigned long long)[[[OFData
dataWithItems: "\x0A\x09\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
count: 11] objectByParsingASN1DER] longLongValue],
ULLONG_MAX);
}
- (void)testInvalidEnumeratedFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x0A\x02\x00\x00"
count: 4] objectByParsingASN1DER],
OFInvalidFormatException);
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x0A\x02\x00\x7F"
count: 4] objectByParsingASN1DER],
OFInvalidFormatException);
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x0A\x02\xFF\x80"
count: 4] objectByParsingASN1DER],
OFInvalidFormatException);
}
- (void)testOutOfRangeEnumeratedFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x0A\x09\x01"
"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
count: 11] objectByParsingASN1DER],
OFOutOfRangeException);
}
- (void)testTruncatedEnumeratedFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x0A\x02\x00"
count: 3] objectByParsingASN1DER],
OFTruncatedDataException);
}
- (void)testUTF8String
{
OTAssertEqualObjects(
[[OFData dataWithItems: "\x0C\x0EHällo Wörld!"
count: 16] objectByParsingASN1DER],
@"Hällo Wörld!");
OTAssertEqualObjects(
[[OFData dataWithItems: "\x0C\x81\x80xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxx"
count: 131] objectByParsingASN1DER],
@"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
@"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
}
- (void)testOutOfRangeUTF8StringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x0C\x89"
"\x01\x01\x01\x01\x01\x01\x01\x01\x01"
count: 11] objectByParsingASN1DER],
OFOutOfRangeException);
}
- (void)testTruncatedUTF8StringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x0C\x01"
count: 2] objectByParsingASN1DER],
OFTruncatedDataException);
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x0C\x83\x01\x01"
count: 4] objectByParsingASN1DER],
OFTruncatedDataException);
}
- (void)testInvalidUTF8StringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x0C\x81\x7F"
count: 3] objectByParsingASN1DER],
OFInvalidFormatException);
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x0C\x82\x00\x80xxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxx"
count: 132] objectByParsingASN1DER],
OFInvalidFormatException);
}
- (void)testSequence
{
OFArray *array;
array = [[OFData dataWithItems: "\x30\x00"
count: 2] objectByParsingASN1DER];
OTAssertTrue([array isKindOfClass: [OFArray class]]);
OTAssertEqual(array.count, 0);
array = [[OFData dataWithItems: "\x30\x09\x02\x01\x7B\x0C\x04Test"
count: 11] objectByParsingASN1DER];
OTAssertTrue([array isKindOfClass: [OFArray class]]);
OTAssertEqual(array.count, 2);
OTAssertEqual([[array objectAtIndex: 0] longLongValue], 123);
OTAssertEqualObjects([array objectAtIndex: 1], @"Test");
}
- (void)testTruncatedSequenceFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x30\x01"
count: 2] objectByParsingASN1DER],
OFTruncatedDataException);
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x30\x04\x02\x01\x01\x00\x00"
count: 7] objectByParsingASN1DER],
OFTruncatedDataException);
}
- (void)testSet
{
OFSet *set;
set = [[OFData dataWithItems: "\x31\x00"
count: 2] objectByParsingASN1DER];
OTAssertTrue([set isKindOfClass: [OFSet class]]);
OTAssertEqual(set.count, 0);
set = [[OFData dataWithItems: "\x31\x09\x02\x01\x7B\x0C\x04Test"
count: 11] objectByParsingASN1DER];
OTAssertTrue([set isKindOfClass: [OFSet class]]);
OTAssertEqual(set.count, 2);
OTAssertEqualObjects(set,
([OFSet setWithObjects: [OFNumber numberWithLongLong: 123],
@"Test", nil]));
}
- (void)testInvalidSetFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x31\x06\x02\x01\x02\x02\x01\x01"
count: 8] objectByParsingASN1DER],
OFInvalidFormatException);
}
- (void)testTruncatedSetFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x31\x01"
count: 2] objectByParsingASN1DER],
OFTruncatedDataException);
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x31\x04\x02\x01\x01\x00\x00"
count: 7] objectByParsingASN1DER],
OFTruncatedDataException);
}
- (void)testNumericString
{
OTAssertEqualObjects([[[OFData
dataWithItems: "\x12\x0B" "12345 67890"
count: 13] objectByParsingASN1DER] numericStringValue],
@"12345 67890");
OTAssertEqualObjects([[[OFData
dataWithItems: "\x12\x81\x80" "000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000"
"00000000000000000000000000000000000000000"
count: 131] objectByParsingASN1DER] numericStringValue],
@"00000000000000000000000000000000000000000000000000000000000000000"
@"000000000000000000000000000000000000000000000000000000000000000");
}
- (void)testInvalidNumericStringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x12\x02."
count: 4] objectByParsingASN1DER],
OFInvalidEncodingException);
}
- (void)testOutOfRangeNumericStringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x12\x89"
"\x01\x01\x01\x01\x01\x01\x01\x01\x01"
count: 11] objectByParsingASN1DER],
OFOutOfRangeException);
}
- (void)testTruncatedNumericStringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x12\x01"
count: 2] objectByParsingASN1DER],
OFTruncatedDataException);
}
- (void)testPrintableString
{
OTAssertEqualObjects([[[OFData
dataWithItems: "\x13\x0CHello World."
count: 14] objectByParsingASN1DER] printableStringValue],
@"Hello World.");
OTAssertEqualObjects([[[OFData
dataWithItems: "\x13\x81\x80 '()+,-./:=?abcdefghijklmnopqrstuvwxyzA"
"BCDEFGHIJKLMNOPQRSTUVWXYZ '()+,-./:=?abcdefghijklmn"
"opqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
count: 131] objectByParsingASN1DER] printableStringValue],
@" '()+,-./:=?abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ "
@"'()+,-./:=?abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
}
- (void)testInvalidPrintableStringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x13\x02;"
count: 4] objectByParsingASN1DER],
OFInvalidEncodingException);
}
- (void)testOutOfRangePrintableStringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x13\x89"
"\x01\x01\x01\x01\x01\x01\x01\x01\x01"
count: 11] objectByParsingASN1DER],
OFOutOfRangeException);
}
- (void)testTruncatedPrintableStringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x13\x01"
count: 2] objectByParsingASN1DER],
OFTruncatedDataException);
}
- (void)testIA5String
{
OTAssertEqualObjects([[[OFData
dataWithItems: "\x16\x0CHello World!"
count: 14] objectByParsingASN1DER] IA5StringValue],
@"Hello World!");
OTAssertEqualObjects([[[OFData
dataWithItems: "\x16\x81\x80xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
count: 131] objectByParsingASN1DER] IA5StringValue],
@"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
@"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
}
- (void)testInvalidIA5StringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x16\x02ä"
count: 4] objectByParsingASN1DER],
OFInvalidEncodingException);
}
- (void)testOutOfRangeIA5StringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x16\x89"
"\x01\x01\x01\x01\x01\x01\x01\x01\x01"
count: 11] objectByParsingASN1DER],
OFOutOfRangeException);
}
- (void)testTruncatedIA5StringFails
{
OTAssertThrowsSpecific(
[[OFData dataWithItems: "\x16\x01"
count: 2] objectByParsingASN1DER],
OFTruncatedDataException);
}
@end