1
0
mirror of https://github.com/square/okhttp.git synced 2025-11-23 06:42:24 +03:00
Files
okhttp/okhttp-tls/src/test/java/okhttp3/tls/internal/der/DerTest.kt
2020-11-01 09:12:12 -05:00

969 lines
32 KiB
Kotlin

/*
* Copyright (C) 2020 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.tls.internal.der
import java.math.BigInteger
import java.net.InetAddress
import java.net.ProtocolException
import java.text.SimpleDateFormat
import java.util.Date
import java.util.TimeZone
import okhttp3.tls.internal.der.CertificateAdapters.generalNameDnsName
import okhttp3.tls.internal.der.CertificateAdapters.generalNameIpAddress
import okhttp3.tls.internal.der.ObjectIdentifiers.basicConstraints
import okhttp3.tls.internal.der.ObjectIdentifiers.commonName
import okhttp3.tls.internal.der.ObjectIdentifiers.sha256WithRSAEncryption
import okhttp3.tls.internal.der.ObjectIdentifiers.subjectAlternativeName
import okio.Buffer
import okio.ByteString.Companion.decodeHex
import okio.ByteString.Companion.encodeUtf8
import okio.ByteString.Companion.toByteString
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.fail
internal class DerTest {
@Test fun `decode tag and length`() {
val buffer = Buffer()
.writeByte(0b00011110)
.writeByte(0b10000001)
.writeByte(0b11001001)
val derReader = DerReader(buffer)
derReader.read("test") { header ->
assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_UNIVERSAL)
assertThat(header.tag).isEqualTo(30)
assertThat(header.constructed).isFalse()
assertThat(header.length).isEqualTo(201)
}
assertThat(derReader.hasNext()).isFalse()
}
@Test fun `decode length encoded with leading zero byte`() {
val buffer = Buffer()
.writeByte(0b00000010)
.writeByte(0b10000010)
.writeByte(0b00000000)
.writeByte(0b01111111)
val derReader = DerReader(buffer)
try {
derReader.read("test") {}
fail("")
} catch (expected: ProtocolException) {
assertThat(expected.message).isEqualTo("invalid encoding for length")
}
}
@Test fun `decode length not encoded in shortest form possible`() {
val buffer = Buffer()
.writeByte(0b00000010)
.writeByte(0b10000001)
.writeByte(0b01111111)
val derReader = DerReader(buffer)
try {
derReader.read("test") {}
fail("")
} catch (expected: ProtocolException) {
assertThat(expected.message).isEqualTo("invalid encoding for length")
}
}
@Test fun `decode length equal to Long MAX_VALUE`() {
val buffer = Buffer()
.writeByte(0b00000010)
.writeByte(0b10001000)
.writeByte(0b01111111)
.writeByte(0b11111111)
.writeByte(0b11111111)
.writeByte(0b11111111)
.writeByte(0b11111111)
.writeByte(0b11111111)
.writeByte(0b11111111)
.writeByte(0b11111111)
val derReader = DerReader(buffer)
val header = derReader.readHeader()
assertThat(header.length).isEqualTo(Long.MAX_VALUE)
}
@Test fun `decode length overflowing Long`() {
val buffer = Buffer()
.writeByte(0b00000010)
.writeByte(0b10001000)
.writeByte(0b10000000)
.writeByte(0b00000000)
.writeByte(0b00000000)
.writeByte(0b00000000)
.writeByte(0b00000000)
.writeByte(0b00000000)
.writeByte(0b00000000)
.writeByte(0b00000000)
val derReader = DerReader(buffer)
try {
derReader.read("test") {}
fail("")
} catch (expected: ProtocolException) {
assertThat(expected.message).isEqualTo("length > Long.MAX_VALUE")
}
}
@Test fun `decode length encoded with more than 8 bytes`() {
val buffer = Buffer()
.writeByte(0b00000010)
.writeByte(0b10001001)
.writeByte(0b11111111)
.writeByte(0b11111111)
.writeByte(0b11111111)
.writeByte(0b11111111)
.writeByte(0b11111111)
.writeByte(0b11111111)
.writeByte(0b11111111)
.writeByte(0b11111111)
.writeByte(0b11111111)
.writeByte(0b11111111)
val derReader = DerReader(buffer)
try {
derReader.read("test") {}
fail("")
} catch (expected: ProtocolException) {
assertThat(expected.message)
.isEqualTo("length encoded with more than 8 bytes is not supported")
}
}
@Test fun `encode tag and length`() {
val buffer = Buffer()
val derWriter = DerWriter(buffer)
derWriter.write("test", tagClass = DerHeader.TAG_CLASS_UNIVERSAL, tag = 30L) {
derWriter.writeUtf8("a".repeat(201))
}
assertThat(buffer.readByteString(3)).isEqualTo("1e81c9".decodeHex())
assertThat(buffer.readUtf8()).isEqualTo("a".repeat(201))
}
@Test fun `decode primitive bit string`() {
val buffer = Buffer()
.write("0307040A3B5F291CD0".decodeHex())
val derReader = DerReader(buffer)
derReader.read("test") { header ->
assertThat(header.tag).isEqualTo(3L)
assertThat(derReader.readBitString()).isEqualTo(BitString("0A3B5F291CD0".decodeHex(), 4))
}
assertThat(derReader.hasNext()).isFalse()
}
@Test fun `encode primitive bit string`() {
val buffer = Buffer()
val derWriter = DerWriter(buffer)
derWriter.write("test", tagClass = DerHeader.TAG_CLASS_UNIVERSAL, tag = 3L) {
derWriter.writeBitString(BitString("0A3B5F291CD0".decodeHex(), 4))
}
assertThat(buffer.readByteString()).isEqualTo("0307040A3B5F291CD0".decodeHex())
}
@Test fun `decode primitive string`() {
val buffer = Buffer()
.write("1A054A6F6E6573".decodeHex())
val derReader = DerReader(buffer)
derReader.read("test") { header ->
assertThat(header.tag).isEqualTo(26L)
assertThat(header.constructed).isFalse()
assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_UNIVERSAL)
assertThat(derReader.readOctetString()).isEqualTo("Jones".encodeUtf8())
}
assertThat(derReader.hasNext()).isFalse()
}
@Test fun `encode primitive string`() {
val buffer = Buffer()
val derWriter = DerWriter(buffer)
derWriter.write("test", tagClass = DerHeader.TAG_CLASS_UNIVERSAL, tag = 26L) {
derWriter.writeOctetString("Jones".encodeUtf8())
}
assertThat(buffer.readByteString()).isEqualTo("1A054A6F6E6573".decodeHex())
}
@Test fun `decode implicit prefixed type`() {
// Type1 ::= VisibleString
// Type2 ::= [APPLICATION 3] IMPLICIT Type1
val buffer = Buffer()
.write("43054A6F6E6573".decodeHex())
val derReader = DerReader(buffer)
derReader.read("test") { header ->
assertThat(header.tag).isEqualTo(3L)
assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_APPLICATION)
assertThat(derReader.readOctetString()).isEqualTo("Jones".encodeUtf8())
}
assertThat(derReader.hasNext()).isFalse()
}
@Test fun `encode implicit prefixed type`() {
// Type1 ::= VisibleString
// Type2 ::= [APPLICATION 3] IMPLICIT Type1
val buffer = Buffer()
val derWriter = DerWriter(buffer)
derWriter.write("test", tagClass = DerHeader.TAG_CLASS_APPLICATION, tag = 3L) {
derWriter.writeOctetString("Jones".encodeUtf8())
}
assertThat(buffer.readByteString()).isEqualTo("43054A6F6E6573".decodeHex())
}
@Test fun `decode tagged implicit prefixed type`() {
// Type1 ::= VisibleString
// Type2 ::= [APPLICATION 3] IMPLICIT Type1
// Type3 ::= [2] Type2
val buffer = Buffer()
.write("A20743054A6F6E6573".decodeHex())
val derReader = DerReader(buffer)
derReader.read("test") { header ->
assertThat(header.tag).isEqualTo(2L)
assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_CONTEXT_SPECIFIC)
assertThat(header.length).isEqualTo(7L)
derReader.read("test") { header ->
assertThat(header.tag).isEqualTo(3L)
assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_APPLICATION)
assertThat(header.length).isEqualTo(5L)
assertThat(derReader.readOctetString()).isEqualTo("Jones".encodeUtf8())
}
assertThat(derReader.hasNext()).isFalse()
}
assertThat(derReader.hasNext()).isFalse()
}
@Test fun `encode tagged implicit prefixed type`() {
// Type1 ::= VisibleString
// Type2 ::= [APPLICATION 3] IMPLICIT Type1
// Type3 ::= [2] Type2
val buffer = Buffer()
val derWriter = DerWriter(buffer)
derWriter.write("test", tagClass = DerHeader.TAG_CLASS_CONTEXT_SPECIFIC, tag = 2L) {
derWriter.write("test", tagClass = DerHeader.TAG_CLASS_APPLICATION, tag = 3L) {
derWriter.writeOctetString("Jones".encodeUtf8())
}
}
assertThat(buffer.readByteString()).isEqualTo("A20743054A6F6E6573".decodeHex())
}
@Test fun `decode implicit tagged implicit prefixed type`() {
// Type1 ::= VisibleString
// Type2 ::= [APPLICATION 3] IMPLICIT Type1
// Type3 ::= [2] Type2
// Type4 ::= [APPLICATION 7] IMPLICIT Type3
val buffer = Buffer()
.write("670743054A6F6E6573".decodeHex())
val derReader = DerReader(buffer)
derReader.read("test") { header ->
assertThat(header.tag).isEqualTo(7L)
assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_APPLICATION)
assertThat(header.length).isEqualTo(7L)
derReader.read("test") { header2 ->
assertThat(header2.tag).isEqualTo(3L)
assertThat(header2.tagClass).isEqualTo(DerHeader.TAG_CLASS_APPLICATION)
assertThat(header2.length).isEqualTo(5L)
assertThat(derReader.readOctetString()).isEqualTo("Jones".encodeUtf8())
}
assertThat(derReader.hasNext()).isFalse()
}
assertThat(derReader.hasNext()).isFalse()
}
@Test fun `encode implicit tagged implicit prefixed type`() {
// Type1 ::= VisibleString
// Type2 ::= [APPLICATION 3] IMPLICIT Type1
// Type3 ::= [2] Type2
// Type4 ::= [APPLICATION 7] IMPLICIT Type3
val buffer = Buffer()
val derWriter = DerWriter(buffer)
derWriter.write("test", tagClass = DerHeader.TAG_CLASS_APPLICATION, tag = 7L) {
derWriter.write("test", tagClass = DerHeader.TAG_CLASS_APPLICATION, tag = 3L) {
derWriter.writeOctetString("Jones".encodeUtf8())
}
}
assertThat(buffer.readByteString()).isEqualTo("670743054A6F6E6573".decodeHex())
}
@Test fun `decode implicit implicit prefixed type`() {
// Type1 ::= VisibleString
// Type2 ::= [APPLICATION 3] IMPLICIT Type1
// Type5 ::= [2] IMPLICIT Type2
val buffer = Buffer()
.write("82054A6F6E6573".decodeHex())
val derReader = DerReader(buffer)
derReader.read("test") { header ->
assertThat(header.tag).isEqualTo(2L)
assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_CONTEXT_SPECIFIC)
assertThat(header.length).isEqualTo(5L)
assertThat(derReader.readOctetString()).isEqualTo("Jones".encodeUtf8())
}
assertThat(derReader.hasNext()).isFalse()
}
@Test fun `encode implicit implicit prefixed type`() {
// Type1 ::= VisibleString
// Type2 ::= [APPLICATION 3] IMPLICIT Type1
// Type5 ::= [2] IMPLICIT Type2
val buffer = Buffer()
val derWriter = DerWriter(buffer)
derWriter.write(
name = "test",
tagClass = DerHeader.TAG_CLASS_CONTEXT_SPECIFIC,
tag = 2L
) {
derWriter.writeOctetString("Jones".encodeUtf8())
}
assertThat(buffer.readByteString()).isEqualTo("82054A6F6E6573".decodeHex())
}
@Test fun `decode object identifier without adapter`() {
val buffer = Buffer()
.write("0603883703".decodeHex())
val derReader = DerReader(buffer)
derReader.read("test") { header ->
assertThat(header.tag).isEqualTo(6L)
assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_UNIVERSAL)
assertThat(header.length).isEqualTo(3L)
assertThat(derReader.readObjectIdentifier()).isEqualTo("2.999.3")
}
assertThat(derReader.hasNext()).isFalse()
}
@Test fun `encode object identifier without adapter`() {
val buffer = Buffer()
val derWriter = DerWriter(buffer)
derWriter.write(
name = "test",
tagClass = DerHeader.TAG_CLASS_UNIVERSAL,
tag = 6L
) {
derWriter.writeObjectIdentifier("2.999.3")
}
assertThat(buffer.readByteString()).isEqualTo("0603883703".decodeHex())
}
@Test fun `decode relative object identifier`() {
val buffer = Buffer()
.write("0D04c27B0302".decodeHex())
val derReader = DerReader(buffer)
derReader.read("test") { header ->
assertThat(header.tag).isEqualTo(13L)
assertThat(header.tagClass).isEqualTo(DerHeader.TAG_CLASS_UNIVERSAL)
assertThat(header.length).isEqualTo(4L)
assertThat(derReader.readRelativeObjectIdentifier()).isEqualTo("8571.3.2")
}
assertThat(derReader.hasNext()).isFalse()
}
@Test fun `encode relative object identifier`() {
val buffer = Buffer()
val derWriter = DerWriter(buffer)
derWriter.write(
name = "test",
tagClass = DerHeader.TAG_CLASS_UNIVERSAL,
tag = 13L
) {
derWriter.writeRelativeObjectIdentifier("8571.3.2")
}
assertThat(buffer.readByteString()).isEqualTo("0D04c27B0302".decodeHex())
}
@Test fun `decode raw sequence`() {
val buffer = Buffer()
.write("300A".decodeHex())
.write("1505".decodeHex())
.write("Smith".encodeUtf8())
.write("01".decodeHex())
.write("01".decodeHex())
.write("FF".decodeHex())
val derReader = DerReader(buffer)
derReader.read("test") { header ->
assertThat(header.tag).isEqualTo(16L)
derReader.read("test") { header2 ->
assertThat(header2.tag).isEqualTo(21L)
assertThat(derReader.readOctetString()).isEqualTo("Smith".encodeUtf8())
}
derReader.read("test") { header3 ->
assertThat(header3.tag).isEqualTo(1L)
assertThat(derReader.readBoolean()).isTrue()
}
assertThat(derReader.hasNext()).isFalse()
}
assertThat(derReader.hasNext()).isFalse()
}
@Test fun `encode raw sequence`() {
val buffer = Buffer()
val derWriter = DerWriter(buffer)
derWriter.write(
name = "test",
tagClass = DerHeader.TAG_CLASS_UNIVERSAL,
tag = 16L
) {
derWriter.write(
name = "test",
tagClass = DerHeader.TAG_CLASS_UNIVERSAL,
tag = 21L
) {
derWriter.writeOctetString("Smith".encodeUtf8())
}
derWriter.write(
name = "test",
tagClass = DerHeader.TAG_CLASS_UNIVERSAL,
tag = 1L
) {
derWriter.writeBoolean(true)
}
}
assertThat(buffer.readByteString()).isEqualTo("300a1505536d6974680101ff".decodeHex())
}
@Test fun `sequence of`() {
val bytes = "3009020107020108020109".decodeHex()
val sequenceOf = listOf(7L, 8L, 9L)
val adapter = Adapters.INTEGER_AS_LONG.asSequenceOf()
assertThat(adapter.fromDer(bytes)).isEqualTo(sequenceOf)
assertThat(adapter.toDer(sequenceOf)).isEqualTo(bytes)
}
@Test fun `point with only x set`() {
val bytes = "3003800109".decodeHex()
val point = Point(9L, null)
assertThat(Point.ADAPTER.fromDer(bytes)).isEqualTo(point)
assertThat(Point.ADAPTER.toDer(point)).isEqualTo(bytes)
}
@Test fun `point with only y set`() {
val bytes = "3003810109".decodeHex()
val point = Point(null, 9L)
assertThat(Point.ADAPTER.fromDer(bytes)).isEqualTo(point)
assertThat(Point.ADAPTER.toDer(point)).isEqualTo(bytes)
}
@Test fun `point with both fields set`() {
val bytes = "3006800109810109".decodeHex()
val point = Point(9L, 9L)
assertThat(Point.ADAPTER.fromDer(bytes)).isEqualTo(point)
assertThat(Point.ADAPTER.toDer(point)).isEqualTo(bytes)
}
@Test fun `implicit tag`() {
// [5] IMPLICIT UTF8String
val bytes = "85026869".decodeHex()
val implicitAdapter = Adapters.UTF8_STRING.withTag(tag = 5L)
assertThat(implicitAdapter.fromDer(bytes)).isEqualTo("hi")
assertThat(implicitAdapter.toDer("hi")).isEqualTo(bytes)
}
@Test fun `encode implicit`() {
// [5] IMPLICIT UTF8String
val implicitAdapter = Adapters.UTF8_STRING.withTag(tag = 5L)
val string = implicitAdapter.fromDer("85026869".decodeHex())
assertThat(string).isEqualTo("hi")
}
@Test fun `explicit tag`() {
// [5] EXPLICIT UTF8String
val bytes = "A5040C026869".decodeHex()
val explicitAdapter = Adapters.UTF8_STRING.withExplicitBox(tag = 5L)
assertThat(explicitAdapter.fromDer(bytes)).isEqualTo("hi")
assertThat(explicitAdapter.toDer("hi")).isEqualTo(bytes)
}
@Test fun `boolean`() {
val bytes = "0101FF".decodeHex()
assertThat(Adapters.BOOLEAN.fromDer(bytes)).isEqualTo(true)
assertThat(Adapters.BOOLEAN.toDer(true)).isEqualTo(bytes)
}
@Test fun `positive integer`() {
val bytes = "020132".decodeHex()
assertThat(Adapters.INTEGER_AS_LONG.toDer(50L)).isEqualTo(bytes)
assertThat(Adapters.INTEGER_AS_LONG.fromDer(bytes)).isEqualTo(50L)
}
@Test fun `decode negative integer`() {
val bytes = "02019c".decodeHex()
assertThat(Adapters.INTEGER_AS_LONG.fromDer(bytes)).isEqualTo(-100L)
assertThat(Adapters.INTEGER_AS_LONG.toDer(-100L)).isEqualTo(bytes)
}
@Test fun `five byte integer`() {
val bytes = "02058000000001".decodeHex()
assertThat(Adapters.INTEGER_AS_LONG.fromDer(bytes)).isEqualTo(-549755813887L)
assertThat(Adapters.INTEGER_AS_LONG.toDer(-549755813887L)).isEqualTo(bytes)
}
@Test fun `eight zeros`() {
val bytes = "020200ff".decodeHex()
assertThat(Adapters.INTEGER_AS_LONG.fromDer(bytes)).isEqualTo(255)
assertThat(Adapters.INTEGER_AS_LONG.toDer(255)).isEqualTo(bytes)
}
@Test fun `eight ones`() {
val bytes = "0201ff".decodeHex()
assertThat(Adapters.INTEGER_AS_LONG.toDer(-1L)).isEqualTo(bytes)
assertThat(Adapters.INTEGER_AS_LONG.fromDer(bytes)).isEqualTo(-1L)
}
@Test fun `last byte all zeros`() {
val bytes = "0202ff00".decodeHex()
assertThat(Adapters.INTEGER_AS_LONG.toDer(-256L)).isEqualTo(bytes)
assertThat(Adapters.INTEGER_AS_LONG.fromDer(bytes)).isEqualTo(-256L)
}
@Test fun `max long`() {
val bytes = "02087fffffffffffffff".decodeHex()
assertThat(Adapters.INTEGER_AS_LONG.fromDer(bytes)).isEqualTo(Long.MAX_VALUE)
assertThat(Adapters.INTEGER_AS_LONG.toDer(Long.MAX_VALUE)).isEqualTo(bytes)
}
@Test fun `min long`() {
val bytes = "02088000000000000000".decodeHex()
assertThat(Adapters.INTEGER_AS_LONG.fromDer(bytes)).isEqualTo(Long.MIN_VALUE)
assertThat(Adapters.INTEGER_AS_LONG.toDer(Long.MIN_VALUE)).isEqualTo(bytes)
}
@Test fun `bigger than max long`() {
val bytes = "0209008000000000000001".decodeHex()
val bigInteger = BigInteger("9223372036854775809")
assertThat(Adapters.INTEGER_AS_BIG_INTEGER.fromDer(bytes)).isEqualTo(bigInteger)
assertThat(Adapters.INTEGER_AS_BIG_INTEGER.toDer(bigInteger)).isEqualTo(bytes)
}
@Test fun `utf8 string`() {
val bytes = "0c04f09f988e".decodeHex()
assertThat(Adapters.UTF8_STRING.fromDer(bytes)).isEqualTo("\uD83D\uDE0E")
assertThat(Adapters.UTF8_STRING.toDer("\uD83D\uDE0E")).isEqualTo(bytes)
}
@Test fun `ia5 string`() {
val bytes = "16026869".decodeHex()
assertThat(Adapters.IA5_STRING.fromDer(bytes)).isEqualTo("hi")
assertThat(Adapters.IA5_STRING.toDer("hi")).isEqualTo(bytes)
}
@Test fun `printable string`() {
val bytes = "13026869".decodeHex()
assertThat(Adapters.PRINTABLE_STRING.fromDer(bytes)).isEqualTo("hi")
assertThat(Adapters.PRINTABLE_STRING.toDer("hi")).isEqualTo(bytes)
}
@Test fun `cannot decode utc time with offset`() {
try {
Adapters.UTC_TIME.fromDer("17113139313231353139303231302d30383030".decodeHex())
fail("")
} catch (expected: ProtocolException) {
assertThat(expected).hasMessage("Failed to parse UTCTime 191215190210-0800")
}
}
@Test fun `utc time`() {
val bytes = "170d3139313231363033303231305a".decodeHex()
val utcTime = date("2019-12-16T03:02:10.000+0000").time
assertThat(Adapters.UTC_TIME.toDer(utcTime)).isEqualTo(bytes)
assertThat(Adapters.UTC_TIME.fromDer(bytes)).isEqualTo(utcTime)
}
@Test fun `cannot decode malformed utc time`() {
val bytes = "170d3139313231362333303231305a".decodeHex()
try {
Adapters.UTC_TIME.fromDer(bytes)
fail("")
} catch (expected: ProtocolException) {
assertThat(expected).hasMessage("Failed to parse UTCTime 191216#30210Z")
}
}
@Test fun `cannot decode generalized time with offset`() {
try {
Adapters.GENERALIZED_TIME.fromDer("181332303139313231353139303231302d30383030".decodeHex())
fail("")
} catch (expected: ProtocolException) {
assertThat(expected).hasMessage("Failed to parse GeneralizedTime 20191215190210-0800")
}
}
@Test fun `generalized time`() {
val bytes = "180f32303139313231363033303231305a".decodeHex()
val generalizedTime = date("2019-12-16T03:02:10.000+0000").time
assertThat(Adapters.GENERALIZED_TIME.fromDer(bytes)).isEqualTo(generalizedTime)
assertThat(Adapters.GENERALIZED_TIME.toDer(generalizedTime)).isEqualTo(bytes)
}
@Test fun `cannot decode malformed generalized time`() {
val bytes = "180f32303139313231362333303231305a".decodeHex()
try {
Adapters.GENERALIZED_TIME.fromDer(bytes)
fail("")
} catch (expected: ProtocolException) {
assertThat(expected).hasMessage("Failed to parse GeneralizedTime 20191216#30210Z")
}
}
@Test fun `parse utc time`() {
assertThat(Adapters.parseUtcTime("920521000000Z"))
.isEqualTo(date("1992-05-21T00:00:00.000+0000").time)
assertThat(Adapters.parseUtcTime("920622123421Z"))
.isEqualTo(date("1992-06-22T12:34:21.000+0000").time)
assertThat(Adapters.parseUtcTime("920722132100Z"))
.isEqualTo(date("1992-07-22T13:21:00.000+0000").time)
}
@Test fun `decode utc time two digit year cutoff is 1950`() {
assertThat(Adapters.parseUtcTime("500101000000Z"))
.isEqualTo(date("1950-01-01T00:00:00.000+0000").time)
assertThat(Adapters.parseUtcTime("500101010000Z"))
.isEqualTo(date("1950-01-01T01:00:00.000+0000").time)
assertThat(Adapters.parseUtcTime("491231225959Z"))
.isEqualTo(date("2049-12-31T22:59:59.000+0000").time)
assertThat(Adapters.parseUtcTime("491231235959Z"))
.isEqualTo(date("2049-12-31T23:59:59.000+0000").time)
}
@Test fun `encode utc time two digit year cutoff is 1950`() {
assertThat(Adapters.formatUtcTime(date("1950-01-01T00:00:00.000+0000").time))
.isEqualTo("500101000000Z")
assertThat(Adapters.formatUtcTime(date("2049-12-31T23:59:59.000+0000").time))
.isEqualTo("491231235959Z")
}
@Test fun `parse generalized time`() {
assertThat(Adapters.parseGeneralizedTime("18990101000000Z"))
.isEqualTo(date("1899-01-01T00:00:00.000+0000").time)
assertThat(Adapters.parseGeneralizedTime("19500101000000Z"))
.isEqualTo(date("1950-01-01T00:00:00.000+0000").time)
assertThat(Adapters.parseGeneralizedTime("20500101000000Z"))
.isEqualTo(date("2050-01-01T00:00:00.000+0000").time)
assertThat(Adapters.parseGeneralizedTime("20990101000000Z"))
.isEqualTo(date("2099-01-01T00:00:00.000+0000").time)
assertThat(Adapters.parseGeneralizedTime("19920521000000Z"))
.isEqualTo(date("1992-05-21T00:00:00.000+0000").time)
assertThat(Adapters.parseGeneralizedTime("19920622123421Z"))
.isEqualTo(date("1992-06-22T12:34:21.000+0000").time)
}
@Disabled("fractional seconds are not implemented")
@Test fun `parse generalized time with fractional seconds`() {
assertThat(Adapters.parseGeneralizedTime("19920722132100.3Z"))
.isEqualTo(date("1992-07-22T13:21:00.300+0000").time)
}
@Test fun `format generalized time`() {
assertThat(Adapters.formatGeneralizedTime(date("1899-01-01T00:00:00.000+0000").time))
.isEqualTo("18990101000000Z")
assertThat(Adapters.formatGeneralizedTime(date("1950-01-01T00:00:00.000+0000").time))
.isEqualTo("19500101000000Z")
assertThat(Adapters.formatGeneralizedTime(date("2050-01-01T00:00:00.000+0000").time))
.isEqualTo("20500101000000Z")
assertThat(Adapters.formatGeneralizedTime(date("2099-01-01T00:00:00.000+0000").time))
.isEqualTo("20990101000000Z")
}
@Test fun `decode object identifier`() {
val bytes = "06092a864886f70d01010b".decodeHex()
assertThat(Adapters.OBJECT_IDENTIFIER.fromDer(bytes)).isEqualTo(sha256WithRSAEncryption)
assertThat(Adapters.OBJECT_IDENTIFIER.toDer(sha256WithRSAEncryption)).isEqualTo(bytes)
}
@Test fun `null value`() {
val bytes = "0500".decodeHex()
assertThat(Adapters.NULL.fromDer(bytes)).isNull()
assertThat(Adapters.NULL.toDer(null)).isEqualTo(bytes)
}
@Test fun `sequence algorithm`() {
val bytes = "300d06092a864886f70d01010b0500".decodeHex()
val algorithmIdentifier = AlgorithmIdentifier(
algorithm = sha256WithRSAEncryption,
parameters = null
)
assertThat(CertificateAdapters.algorithmIdentifier.fromDer(bytes))
.isEqualTo(algorithmIdentifier)
assertThat(CertificateAdapters.algorithmIdentifier.toDer(algorithmIdentifier))
.isEqualTo(bytes)
}
@Test fun `bit string`() {
val bytes = "0304066e5dc0".decodeHex()
val bitString = BitString("6e5dc0".decodeHex(), 6)
assertThat(Adapters.BIT_STRING.fromDer(bytes)).isEqualTo(bitString)
assertThat(Adapters.BIT_STRING.toDer(bitString)).isEqualTo(bytes)
}
@Test fun `cannot decode empty bit string`() {
val bytes = "0300".decodeHex()
try {
Adapters.BIT_STRING.fromDer(bytes)
fail("")
} catch (expected: ProtocolException) {
assertThat(expected).hasMessage("malformed bit string")
}
}
@Test fun `octet string`() {
val bytes = "0404030206A0".decodeHex()
val octetString = "030206A0".decodeHex()
assertThat(Adapters.OCTET_STRING.fromDer(bytes)).isEqualTo(octetString)
assertThat(Adapters.OCTET_STRING.toDer(octetString)).isEqualTo(bytes)
}
@Test fun `cannot decode constructed octet string`() {
try {
Adapters.OCTET_STRING.fromDer(
"2410040668656c6c6f200406776f726c6421".decodeHex())
fail("")
} catch (expected: ProtocolException) {
assertThat(expected).hasMessage("constructed octet strings not supported for DER")
}
}
@Test fun `cannot decode constructed bit string`() {
try {
Adapters.BIT_STRING.fromDer(
"231203070068656c6c6f20030700776f726c6421".decodeHex())
fail("")
} catch (expected: ProtocolException) {
assertThat(expected).hasMessage("constructed bit strings not supported for DER")
}
}
@Test fun `cannot decode constructed string`() {
try {
Adapters.UTF8_STRING.fromDer(
"2c100c0668656c6c6f200c06776f726c6421".decodeHex())
fail("")
} catch (expected: ProtocolException) {
assertThat(expected).hasMessage("constructed strings not supported for DER")
}
}
@Test fun `cannot decode indefinite length bit string`() {
try {
Adapters.BIT_STRING.fromDer(
"23800303000A3B0305045F291CD00000".decodeHex())
fail("")
} catch (expected: ProtocolException) {
assertThat(expected).hasMessage("indefinite length not permitted for DER")
}
}
@Test fun `cannot decode constructed octet string in enclosing sequence`() {
val buffer = Buffer()
.write("3A0904034A6F6E04026573".decodeHex())
val derReader = DerReader(buffer)
try {
derReader.read("test") {
derReader.readOctetString()
}
fail("")
} catch (expected: Exception) {
assertThat(expected).hasMessage("constructed octet strings not supported for DER")
}
}
@Test fun `choice IP address`() {
val bytes = "8704c0a80201".decodeHex()
val localhost = InetAddress.getByName("192.168.2.1").address.toByteString()
assertThat(CertificateAdapters.generalName.fromDer(bytes))
.isEqualTo(generalNameIpAddress to localhost)
assertThat(CertificateAdapters.generalName.toDer(generalNameIpAddress to localhost))
.isEqualTo(bytes)
}
@Test fun `choice dns`() {
val bytes = "820b6578616d706c652e636f6d".decodeHex()
assertThat(CertificateAdapters.generalName.fromDer(bytes))
.isEqualTo(generalNameDnsName to "example.com")
assertThat(CertificateAdapters.generalName.toDer(generalNameDnsName to "example.com"))
.isEqualTo(bytes)
}
@Test fun `extension with type hint for basic constraints`() {
val extension = Extension(
basicConstraints,
false,
BasicConstraints(true, 4)
)
val bytes = "300f0603551d13040830060101ff020104".decodeHex()
assertThat(CertificateAdapters.extension.toDer(extension))
.isEqualTo(bytes)
assertThat(CertificateAdapters.extension.fromDer(bytes))
.isEqualTo(extension)
}
@Test fun `extension with type hint for subject alternative names`() {
val extension = Extension(
subjectAlternativeName,
false,
listOf(
generalNameDnsName to "cash.app",
generalNameDnsName to "www.cash.app"
)
)
val bytes = "30210603551d11041a30188208636173682e617070820c7777772e636173682e617070".decodeHex()
assertThat(CertificateAdapters.extension.toDer(extension))
.isEqualTo(bytes)
assertThat(CertificateAdapters.extension.fromDer(bytes))
.isEqualTo(extension)
}
@Test fun `extension with unknown type hint`() {
val extension = Extension(
commonName, // common name is not an extension.
false,
"3006800109810109".decodeHex()
)
val bytes = "300f060355040304083006800109810109".decodeHex()
assertThat(CertificateAdapters.extension.toDer(extension))
.isEqualTo(bytes)
assertThat(CertificateAdapters.extension.fromDer(bytes))
.isEqualTo(extension)
}
/** Tags larger than 30 are a special case. */
@Test fun `large tag`() {
val bytes = "df83fb6800".decodeHex()
val adapter = Adapters.NULL.withTag(tagClass = DerHeader.TAG_CLASS_PRIVATE, tag = 65_000L)
assertThat(adapter.toDer(null)).isEqualTo(bytes)
assertThat(adapter.fromDer(bytes)).isNull()
}
/** Make the claimed length of a nested object larger than the enclosing object. */
@Test fun `large object inside small object`() {
val bytes = "301b300d06092a864886f70d010101050003847fffffff000504030201".decodeHex()
try {
CertificateAdapters.subjectPublicKeyInfo.fromDer(bytes)
fail("")
} catch (expected: ProtocolException) {
assertThat(expected.message).isEqualTo("enclosed object too large")
}
}
/** Object identifiers are nominally self-delimiting. Outrun the limit with one. */
@Test fun `variable length long outruns limit`() {
val bytes = "060229ffffff7f".decodeHex()
try {
Adapters.OBJECT_IDENTIFIER.fromDer(bytes)
fail("")
} catch (expected: ProtocolException) {
assertThat(expected.message).isEqualTo("unexpected byte count at OBJECT IDENTIFIER")
}
}
/**
* ```
* Point ::= SEQUENCE {
* x [0] INTEGER OPTIONAL,
* y [1] INTEGER OPTIONAL
* }
* ```
*/
data class Point(
val x: Long?,
val y: Long?
) {
companion object {
val ADAPTER = Adapters.sequence(
"Point",
Adapters.INTEGER_AS_LONG.withTag(tag = 0L).optional(),
Adapters.INTEGER_AS_LONG.withTag(tag = 1L).optional(),
decompose = { listOf(it.x, it.y) },
construct = { Point(it[0] as Long?, it[1] as Long?) }
)
}
}
private fun date(s: String): Date {
return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").run {
timeZone = TimeZone.getTimeZone("GMT")
parse(s)
}
}
}