Skip to content

Commit 09eecc5

Browse files
integrate new cidre features
1 parent a25c2ed commit 09eecc5

File tree

7 files changed

+69
-51
lines changed

7 files changed

+69
-51
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* `X500Name`
1818
* Dependency update:
1919
* Add `com.eygraber:uri-kmp:0.0.20` (for URI parsing)
20-
* Add `at.asitplus:cidre:0.2.0` (CIDR math)
20+
* Add `at.asitplus:cidre:0.3.0` (CIDR math)
2121
* Changed `AttributeTypeAndValue` from sealed to open and removed the `Other` subclass. Unknown OIDs now fall back to a plain `AttributeTypeAndValue` instance
2222
* Add stricter length checks to be more resilient towards adversarial inputs
2323
* Correct serialization logic for symmetric `CoseKey` in `CoseKeySerializer`

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ kotlinpoet = "2.2.0"
1313
runner = "1.5.2"
1414
kotlincryptoRandom = "0.5.0"
1515
kotest= "6.0.0.M6"
16-
cidre = "0.2.0"
16+
cidre = "0.3.0"
1717
urikmp = "0.0.20"
1818

1919
[libraries]

indispensable/src/commonMain/kotlin/at/asitplus/signum/indispensable/pki/RelativeDistinguishedName.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ open class AttributeTypeAndValue(
254254

255255
if (toRFC2253String() != other.toRFC2253String()) return false
256256
if (oid != other.oid) return false
257+
if (attrType != other.attrType) return false
257258

258259
return true
259260
}

indispensable/src/commonMain/kotlin/at/asitplus/signum/indispensable/pki/generalNames/GeneralName.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ sealed interface GeneralNameOption {
1414
* - `null`: no validation implemented
1515
*/
1616
val isValid: Boolean?
17+
18+
/**
19+
* Indicates whether validation was performed when this object was created.
20+
* This flag is controlled internally by the class and cannot be set by external users.
21+
*/
1722
val performValidation: Boolean
1823

1924
enum class NameType(val value: ULong) {

indispensable/src/commonMain/kotlin/at/asitplus/signum/indispensable/pki/generalNames/IPAddressName.kt

Lines changed: 38 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,13 @@ import at.asitplus.cidre.IpAddressAndPrefix
55
import at.asitplus.cidre.IpFamily
66
import at.asitplus.cidre.IpInterface
77
import at.asitplus.cidre.IpNetwork
8-
import at.asitplus.cidre.byteops.toNetmask
9-
import at.asitplus.cidre.byteops.toPrefix
108
import at.asitplus.cidre.isV4
119
import at.asitplus.cidre.isV6
1210
import at.asitplus.signum.indispensable.asn1.Asn1Decodable
1311
import at.asitplus.signum.indispensable.asn1.Asn1Encodable
1412
import at.asitplus.signum.indispensable.asn1.Asn1Exception
1513
import at.asitplus.signum.indispensable.asn1.Asn1Primitive
1614
import at.asitplus.signum.indispensable.asn1.encoding.encodeToAsn1OctetStringPrimitive
17-
import kotlinx.io.IOException
1815

1916
data class IPAddressName internal constructor(
2017
val address: IpAddress<*, *>?,
@@ -24,24 +21,25 @@ data class IPAddressName internal constructor(
2421
override val type: GeneralNameOption.NameType = GeneralNameOption.NameType.IP
2522
) : GeneralNameOption, Asn1Encodable<Asn1Primitive> {
2623

24+
override val isValid: Boolean by lazy { address != null }
25+
26+
init {
27+
if (performValidation && !isValid) throw Asn1Exception("Invalid IpAddressName.")
28+
}
29+
2730
/**
28-
* Always `true`, since creation of [IPAddressName] is only possible if the
29-
* underlying [IpAddress] or [IpAddressAndPrefix] are valid
31+
* @throws Asn1Exception if illegal IpAddress is provided
3032
*/
31-
override val isValid: Boolean = address != null
33+
@Throws(Asn1Exception::class)
34+
constructor(address: IpAddress<*, *>, addressAndPrefix: IpAddressAndPrefix<*, *>? = null)
35+
: this(address, addressAndPrefix, addressAndPrefix?.toX509Octets() ?: address.octets, true)
3236

3337
/**
34-
* @throws Asn1Exception if illegal IpAddressName is provided
38+
* @throws Asn1Exception if illegal IpAddress is provided
3539
*/
3640
@Throws(Asn1Exception::class)
37-
constructor(
38-
address: IpAddress<*, *>? = null,
39-
addressAndPrefix: IpAddressAndPrefix<*, *>? = null,
40-
// TODO remove rawBytes param, use CIDRE method for calculating rawBytes
41-
rawBytes: ByteArray
42-
) : this (address, addressAndPrefix, rawBytes, true) {
43-
if (!isValid) throw Asn1Exception("Invalid IpAddressName.")
44-
}
41+
constructor(addressAndPrefix: IpAddressAndPrefix<*, *>)
42+
: this(addressAndPrefix.address, addressAndPrefix, addressAndPrefix.toX509Octets(), true)
4543

4644
val network: IpNetwork<*, *>? by lazy {
4745
when (addressAndPrefix) {
@@ -60,36 +58,31 @@ data class IPAddressName internal constructor(
6058
override fun doDecode(src: Asn1Primitive): IPAddressName {
6159
val content = src.content
6260
return when (content.size) {
63-
IpFamily.V4.numberOfOctets -> IPAddressName(IpAddress.V4(content), rawBytes = IpAddress.V4(content).octets)
64-
IpFamily.V6.numberOfOctets -> IPAddressName(IpAddress.V6(content), rawBytes = IpAddress.V6(content).octets)
65-
2 * IpFamily.V4.numberOfOctets -> createNetworkV4(content)
66-
2 * IpFamily.V6.numberOfOctets -> createNetworkV6(content)
67-
else -> throw IOException("Invalid IP/Network length: ${content.size}")
68-
}
69-
}
70-
//TODO change after CIDRE update
71-
private fun createNetworkV4(bytes: ByteArray): IPAddressName {
72-
val address = IpAddress.V4(bytes.copyOfRange(0, 4))
73-
val prefix = bytes.copyOfRange(4, 8).toPrefix()
74-
return IPAddressName(address, addressAndPrefix = IpInterface.V4(address, prefix), address.octets + prefix.toNetmask(4))
75-
}
76-
//TODO change after CIDRE update
77-
private fun createNetworkV6(bytes: ByteArray): IPAddressName {
78-
val address = IpAddress.V6(bytes.copyOfRange(0, 16))
79-
val prefix = bytes.copyOfRange(16, 32).toPrefix()
80-
return IPAddressName(address, addressAndPrefix = IpInterface.V6(address, prefix), address.octets + prefix.toNetmask(16))
81-
}
61+
IpFamily.V4.numberOfOctets -> IPAddressName(
62+
IpAddress.V4(content), rawBytes = IpAddress.V4(content).octets
63+
)
64+
65+
IpFamily.V6.numberOfOctets -> IPAddressName(
66+
IpAddress.V6(content), rawBytes = IpAddress.V6(content).octets
67+
)
8268

83-
@Throws(Asn1Exception::class)
84-
fun fromString(name: String): IPAddressName =
85-
runCatching {
86-
IpInterface(name).let { iface ->
87-
IPAddressName(iface.address, iface, iface.address.octets + iface.netmask)
69+
else -> {
70+
val addressAndPrefix = IpInterface.fromX509Octets(content)
71+
IPAddressName(addressAndPrefix)
8872
}
89-
}.getOrElse {
90-
val addr = IpAddress(name)
91-
IPAddressName(addr, null, addr.octets)
9273
}
74+
}
75+
76+
/**
77+
* @throws IllegalArgumentException if an invalid string is provided
78+
* @throws Asn1Exception if an invalid [address] is provided
79+
* */
80+
@Throws(Asn1Exception::class, IllegalArgumentException::class)
81+
fun fromString(stringRepresentation: String): IPAddressName = runCatching {
82+
IPAddressName(IpInterface(stringRepresentation))
83+
}.getOrElse {
84+
IPAddressName(IpAddress(stringRepresentation))
85+
}
9386
}
9487

9588
override fun toString(): String = addressAndPrefix?.toString() ?: address.toString()
@@ -123,8 +116,7 @@ data class IPAddressName internal constructor(
123116
if (input !is IPAddressName) return GeneralNameOption.ConstraintResult.DIFF_TYPE
124117
if (this == input) return GeneralNameOption.ConstraintResult.MATCH
125118

126-
if (network == null && input.network == null &&
127-
((address!!.isV4() && input.address!!.isV4()) || (address.isV6() && input.address!!.isV6()))) {
119+
if (network == null && input.network == null && ((address!!.isV4() && input.address!!.isV4()) || (address.isV6() && input.address!!.isV6()))) {
128120
return GeneralNameOption.ConstraintResult.SAME_TYPE
129121
}
130122

@@ -144,17 +136,15 @@ data class IPAddressName internal constructor(
144136
if (network != null) {
145137
val thisNet = network as IpNetwork<Number, Any>
146138
val otherAddress = input.address as IpAddress<Number, Any>
147-
return if (thisNet.contains(otherAddress))
148-
GeneralNameOption.ConstraintResult.WIDENS
139+
return if (thisNet.contains(otherAddress)) GeneralNameOption.ConstraintResult.WIDENS
149140
else GeneralNameOption.ConstraintResult.SAME_TYPE
150141
}
151142

152143
// Other is subnet, this is host
153144
if (input.network != null) {
154145
val thisAddress = address as IpAddress<Number, Any>
155146
val otherNet = input.network as IpNetwork<Number, Any>
156-
return if (otherNet.contains(thisAddress))
157-
GeneralNameOption.ConstraintResult.NARROWS
147+
return if (otherNet.contains(thisAddress)) GeneralNameOption.ConstraintResult.NARROWS
158148
else GeneralNameOption.ConstraintResult.SAME_TYPE
159149
}
160150

indispensable/src/commonMain/kotlin/at/asitplus/signum/indispensable/pki/generalNames/X500Name.kt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ data class X500Name internal constructor(
8282

8383
override fun toString() = "X500Name(RDNs=${relativeDistinguishedNames.joinToString()})"
8484

85+
86+
8587
override fun constrains(input: GeneralNameOption?): GeneralNameOption.ConstraintResult {
8688
if (!isValid || input?.isValid == false) throw Asn1Exception("Invalid X500Name")
8789
if (input !is X500Name) return GeneralNameOption.ConstraintResult.DIFF_TYPE
@@ -117,4 +119,24 @@ data class X500Name internal constructor(
117119
rdn.sortedAttrsAndValues.joinToString("+") { atv -> atv.toRFC2253String() }
118120
}
119121
}
122+
123+
override fun equals(other: Any?): Boolean {
124+
if (this === other) return true
125+
if (other == null || this::class != other::class) return false
126+
127+
other as X500Name
128+
129+
if (relativeDistinguishedNames != other.relativeDistinguishedNames) return false
130+
if (type != other.type) return false
131+
132+
return true
133+
}
134+
135+
override fun hashCode(): Int {
136+
var result = performValidation.hashCode()
137+
result = 31 * result + isValid.hashCode()
138+
result = 31 * result + relativeDistinguishedNames.hashCode()
139+
result = 31 * result + type.hashCode()
140+
return result
141+
}
120142
}

indispensable/src/jvmTest/kotlin/at/asitplus/signum/indispensable/pki/generalNames/GeneralNamesConstrainsTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class GeneralNamesConstrainsTest : FreeSpec ({
7272
val testEmailIsNotConstraint = arrayOf(".abc.test.com", "www.test.com", "test1@abc.test.com", "bc.test.com")
7373
val widenNames = arrayOf(".test.com")
7474
val narrowNames = arrayOf(".test.com")
75-
val dummyOtherDNS = DNSName(Asn1String.IA5(""), allowWildcard = true, performValidation = false)
75+
val dummyOtherDNS = DNSName(Asn1String.IA5("example.com"), allowWildcard = true, performValidation = false)
7676

7777
testGeneralNameConstraints(
7878
name = "RFC822Name.constrains",

0 commit comments

Comments
 (0)