Skip to content

Commit

Permalink
add implicit tagging tests
Browse files Browse the repository at this point in the history
  • Loading branch information
JesusMcCloud committed Sep 16, 2024
1 parent 4bf81d1 commit b8e15dc
Show file tree
Hide file tree
Showing 12 changed files with 259 additions and 108 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
* `Iterator<Byte>.decodeAsn1VarUInt()`
* `Iterable<Byte>.decodeAsn1VarUInt()`
* `ByteArray.decodeAsn1VarUInt()`
* Revamp implicit tagging

## 3.0

Expand Down
117 changes: 61 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -432,72 +432,77 @@ DSL, which returns an `Asn1Structure`:

```kotlin
Asn1.Sequence {
+Tagged(1uL) {
+Asn1Primitive(Asn1Element.Tag.BOOL, byteArrayOf(0x00)) //or +Asn1.Bool(false)
}
+Asn1.Set {
+Asn1.Sequence {
+Asn1.SetOf {
+PrintableString("World")
+PrintableString("Hello")
}
+Asn1.Set {
+PrintableString("World")
+PrintableString("Hello")
+Utf8String("!!!")
}

}
+Tagged(1uL) {
+Asn1Primitive(Asn1Element.Tag.BOOL, byteArrayOf(0x00)) //or +Asn1.Bool(false)
}
+Asn1.Set {
+Asn1.Sequence {
+Asn1.SetOf {
+PrintableString("World")
+PrintableString("Hello")
}
+Asn1.Set {
+PrintableString("World")
+PrintableString("Hello")
+Utf8String("!!!")
}

}
+Asn1.Null()
}
+Asn1.Null()

+ObjectIdentifier("1.2.603.624.97")
+ObjectIdentifier("1.2.603.624.97")

+Utf8String("Foo")
+PrintableString("Bar")
+(Utf8String("Foo") withImplicitTag (0xCAFEuL withClass TagClass.PRIVATE))
+PrintableString("Bar")

+Asn1.Set {
+Asn1.Int(3)
+Asn1.Long(-65789876543L)
+Asn1.Bool(false)
+Asn1.Bool(true)
}
+Asn1.Sequence {
+Asn1.Null()
+Asn1String.Numeric("12345")
+UtcTime(Clock.System.now())
}
}
//fake Primitive
+(Asn1.Sequence { +Asn1.Int(42) } withImplicitTag (0x5EUL without CONSTRUCTED))

+Asn1.Set {
+Asn1.Int(3)
+Asn1.Int(-65789876543L)
+Asn1.Bool(false)
+Asn1.Bool(true)
}
+Asn1.Sequence {
+Asn1.Null()
+Asn1String.Numeric("12345")
+UtcTime(Clock.System.now())
}
} withImplicitTag (1337uL withClass TagClass.APPLICATION)
```

In accordance with DER-Encoding, this produces the following ASN.1 structure:

```
SEQUENCE (8 elem)
[1] (1 elem)
BOOLEAN false
SET (1 elem)
SEQUENCE (2 elem)
SET (2 elem)
PrintableString Hello
PrintableString World
SET (3 elem)
UTF8String !!!
PrintableString World
PrintableString Hello
NULL
OBJECT IDENTIFIER 1.2.603.624.97
UTF8String Foo
PrintableString Bar
SET (4 elem)
BOOLEAN false
BOOLEAN true
INTEGER 3
INTEGER (36 bit) -65789876543
SEQUENCE (3 elem)
Application 1337 (9 elem)
[1] (1 elem)
BOOLEAN false
SET (1 elem)
SEQUENCE (2 elem)
SET (2 elem)
PrintableString World
PrintableString Hello
SET (3 elem)
UTF8String !!!
PrintableString World
PrintableString Hello
NULL
NumericString 12345
UTCTime 2023-10-21 21:14:49 UTC
OBJECT IDENTIFIER 1.2.603.624.97
Private 51966 (3 byte) Foo
PrintableString Bar
[94] (3 byte) 02012A
SET (4 elem)
BOOLEAN false
BOOLEAN true
INTEGER 3
INTEGER (36 bit) -65789876543
SEQUENCE (3 elem)
NULL
NumericString 12345
UTCTime 2024-09-16 11:53:51 UTC
```

## Limitations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,33 +28,33 @@ sealed interface CryptoSignature : Asn1Encodable<Asn1Element> {
val signature: Asn1Element


/**
* Well-defined CryptoSignatures, which can also be encoded to raw bytes, in addition to the DER encoding
* specified in the X.509 profile.
* RSA Signatures and EC Signatures with a known curve fall into this category.
*
* **This is the opposite of a [NotRawByteEncodable] signature**
*/
sealed interface RawByteEncodable : CryptoSignature {
/**
* Removes ASN1 Structure and returns the signature value(s) as ByteArray
* Well-defined CryptoSignatures, which can also be encoded to raw bytes, in addition to the DER encoding
* specified in the X.509 profile.
* RSA Signatures and EC Signatures with a known curve fall into this category.
*
* **This is the opposite of a [NotRawByteEncodable] signature**
*/
val rawByteArray: ByteArray
}
sealed interface RawByteEncodable : CryptoSignature {
/**
* Removes ASN1 Structure and returns the signature value(s) as ByteArray
*/
val rawByteArray: ByteArray
}

/**
* **This is the opposite of a [RawByteEncodable] signature**
*
* This inverse "non-trait" is required to group [CryptoSignature] subtypes which cannot be encoded into raw byte arrays,
* since not all properties required to do so are known. For example, EC signatures parsed from an
* [X509Certificate] do not specify a curve. For signatures obtained this way, it is impossible to know
* how the components should be padded before encoding it into raw bytes.
*
* The reason this interface exists, is that it allows for grouping all such signatures in the same manner
* as the [RawByteEncodable] ones, to allow for exhaustive `when` clauses
*
*/
sealed interface NotRawByteEncodable : CryptoSignature
/**
* **This is the opposite of a [RawByteEncodable] signature**
*
* This inverse "non-trait" is required to group [CryptoSignature] subtypes which cannot be encoded into raw byte arrays,
* since not all properties required to do so are known. For example, EC signatures parsed from an
* [X509Certificate] do not specify a curve. For signatures obtained this way, it is impossible to know
* how the components should be padded before encoding it into raw bytes.
*
* The reason this interface exists, is that it allows for grouping all such signatures in the same manner
* as the [RawByteEncodable] ones, to allow for exhaustive `when` clauses
*
*/
sealed interface NotRawByteEncodable : CryptoSignature


fun encodeToTlvBitString(): Asn1Element
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ sealed class Asn1Element(
* Creates a new implicitly tagged ASN.1 Element from this ASN.1 Element.
* NOTE: The [TagClass] of the provided [tag] will be used! If you want the result to have [TagClass.CONTEXT_SPECIFIC],
* also invoke `tag withClass TagClass.CONTEXT_SPECIFIC`!. If a CONSTRUCTED Tag is applied to an ASN.1 Primitive,
* the CONSTRUCTED bit is overridden and set to zero.
* the CONSTRUCTED bit is overridden and set to zero.
*/
inline infix fun withImplicitTag(tag: Tag): Asn1Element = when (this) {
is Asn1Structure -> {
Expand Down Expand Up @@ -222,14 +222,14 @@ sealed class Asn1Element(

/**
* Convenience helper to easily construct implicitly tagged elements.
* Shorthand for `Tag(tagValue, constructed=false, tagClass= TagClass.CONTEXT_SPECIFIC)
* Shorthand for `Tag(tagValue, constructed=false, tagClass=TagClass.CONTEXT_SPECIFIC)
*/
fun ULong.toImplicitTag() =
Asn1Element.Tag(this, constructed = false, tagClass = TagClass.CONTEXT_SPECIFIC)

/**
* Convenience helper to easily construct implicitly tagged elements.
* Shorthand for `Tag(tagValue, constructed=true, tagClass= TagClass.CONTEXT_SPECIFIC)
* Shorthand for `Tag(tagValue, constructed=true, tagClass=TagClass.CONTEXT_SPECIFIC)
*/
fun ULong.toExplicitTag() =
Asn1Element.Tag(this, constructed = true, tagClass = TagClass.CONTEXT_SPECIFIC)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ class Asn1TreeBuilder {
operator fun Asn1Encodable<*>.unaryPlus() {
+encodeToTlv()
}

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import kotlinx.serialization.Serializable
*/
@Serializable
sealed class Asn1String : Asn1Encodable<Asn1Primitive> {
abstract val tagNum: ULong
abstract val tag: ULong
abstract val value: String

/**
Expand All @@ -18,7 +18,7 @@ sealed class Asn1String : Asn1Encodable<Asn1Primitive> {
@Serializable
@SerialName("UTF8String")
class UTF8(override val value: String) : Asn1String() {
override val tagNum = BERTags.UTF8_STRING.toULong()
override val tag = BERTags.UTF8_STRING.toULong()
}

/**
Expand All @@ -27,7 +27,7 @@ sealed class Asn1String : Asn1Encodable<Asn1Primitive> {
@Serializable
@SerialName("UniversalString")
class Universal(override val value: String) : Asn1String() {
override val tagNum = BERTags.UNIVERSAL_STRING.toULong()
override val tag = BERTags.UNIVERSAL_STRING.toULong()
}

/**
Expand All @@ -36,7 +36,7 @@ sealed class Asn1String : Asn1Encodable<Asn1Primitive> {
@Serializable
@SerialName("VisibleString")
class Visible(override val value: String) : Asn1String() {
override val tagNum = BERTags.VISIBLE_STRING.toULong()
override val tag = BERTags.VISIBLE_STRING.toULong()
}

/**
Expand All @@ -45,7 +45,7 @@ sealed class Asn1String : Asn1Encodable<Asn1Primitive> {
@Serializable
@SerialName("IA5String")
class IA5(override val value: String) : Asn1String() {
override val tagNum = BERTags.IA5_STRING.toULong()
override val tag = BERTags.IA5_STRING.toULong()
}

/**
Expand All @@ -54,7 +54,7 @@ sealed class Asn1String : Asn1Encodable<Asn1Primitive> {
@Serializable
@SerialName("TeletexString")
class Teletex(override val value: String) : Asn1String() {
override val tagNum = BERTags.T61_STRING.toULong()
override val tag = BERTags.T61_STRING.toULong()
}

/**
Expand All @@ -63,7 +63,7 @@ sealed class Asn1String : Asn1Encodable<Asn1Primitive> {
@Serializable
@SerialName("BMPString")
class BMP(override val value: String) : Asn1String() {
override val tagNum = BERTags.BMP_STRING.toULong()
override val tag = BERTags.BMP_STRING.toULong()
}

/**
Expand All @@ -79,7 +79,7 @@ sealed class Asn1String : Asn1Encodable<Asn1Primitive> {
?: throw Asn1Exception("Input contains invalid chars: '$value'")
}

override val tagNum = BERTags.PRINTABLE_STRING.toULong()
override val tag = BERTags.PRINTABLE_STRING.toULong()
}

/**
Expand All @@ -94,24 +94,24 @@ sealed class Asn1String : Asn1Encodable<Asn1Primitive> {
?: throw Asn1Exception("Input contains invalid chars: '$value'")
}

override val tagNum = BERTags.NUMERIC_STRING.toULong()
override val tag = BERTags.NUMERIC_STRING.toULong()
}

override fun encodeToTlv() = Asn1Primitive(tagNum, value.encodeToByteArray())
override fun encodeToTlv() = Asn1Primitive(tag, value.encodeToByteArray())
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false

other as Asn1String

if (tagNum != other.tagNum) return false
if (tag != other.tag) return false
if (value != other.value) return false

return true
}

override fun hashCode(): Int {
var result = tagNum.hashCode()
var result = tag.hashCode()
result = 31 * result + value.hashCode()
return result
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class Asn1Time(instant: Instant, formatOverride: Format? = null) : Asn1Encodable
@Throws(Asn1Exception::class)
override fun doDecode(src: Asn1Primitive) =
Asn1Time(src.readInstant(), if (src.tag == Asn1Element.Tag.TIME_UTC) Format.UTC else Format.GENERALIZED)

}

override fun encodeToTlv(): Asn1Primitive =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,7 @@ class ObjectIdentifier @Throws(Asn1Exception::class) constructor(@Transient vara
}
return ObjectIdentifier(*collected.toUIntArray())
}

}

}

object ObjectIdSerializer : KSerializer<ObjectIdentifier> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ data class TbsCertificationRequest(
attributes = attributes,
)
}

}
}

Expand Down Expand Up @@ -152,6 +151,5 @@ data class Pkcs10CertificationRequest(
if (src.hasMoreChildren()) throw Asn1StructuralException("Superfluous structure in CSR Structure")
return Pkcs10CertificationRequest(tbsCsr, sigAlg, signature.rawBytes)
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,5 @@ data class Pkcs10CertificationRequestAttribute(
val value = (src.children.last() as Asn1Set).children
return Pkcs10CertificationRequestAttribute(id, value)
}

}
}
Loading

0 comments on commit b8e15dc

Please sign in to comment.