Skip to content

Commit 9fd8b11

Browse files
committed
Make Oscore split by parts optional
1 parent fa319d1 commit 9fd8b11

8 files changed

Lines changed: 100 additions & 72 deletions

File tree

koap-core/api/koap-core.klib.api

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,25 @@
66
// - Show declarations: true
77

88
// Library unique name: <com.juul.koap:koap-core>
9+
final class com.juul.koap/OscoreParts { // com.juul.koap/OscoreParts|null[0]
10+
constructor <init>(kotlin/ByteArray, kotlin/ByteArray?, kotlin/ByteArray?) // com.juul.koap/OscoreParts.<init>|<init>(kotlin.ByteArray;kotlin.ByteArray?;kotlin.ByteArray?){}[0]
11+
12+
final val kid // com.juul.koap/OscoreParts.kid|{}kid[0]
13+
final fun <get-kid>(): kotlin/ByteArray? // com.juul.koap/OscoreParts.kid.<get-kid>|<get-kid>(){}[0]
14+
final val kidContext // com.juul.koap/OscoreParts.kidContext|{}kidContext[0]
15+
final fun <get-kidContext>(): kotlin/ByteArray? // com.juul.koap/OscoreParts.kidContext.<get-kidContext>|<get-kidContext>(){}[0]
16+
final val partialIv // com.juul.koap/OscoreParts.partialIv|{}partialIv[0]
17+
final fun <get-partialIv>(): kotlin/ByteArray // com.juul.koap/OscoreParts.partialIv.<get-partialIv>|<get-partialIv>(){}[0]
18+
19+
final fun component1(): kotlin/ByteArray // com.juul.koap/OscoreParts.component1|component1(){}[0]
20+
final fun component2(): kotlin/ByteArray? // com.juul.koap/OscoreParts.component2|component2(){}[0]
21+
final fun component3(): kotlin/ByteArray? // com.juul.koap/OscoreParts.component3|component3(){}[0]
22+
final fun copy(kotlin/ByteArray = ..., kotlin/ByteArray? = ..., kotlin/ByteArray? = ...): com.juul.koap/OscoreParts // com.juul.koap/OscoreParts.copy|copy(kotlin.ByteArray;kotlin.ByteArray?;kotlin.ByteArray?){}[0]
23+
final fun equals(kotlin/Any?): kotlin/Boolean // com.juul.koap/OscoreParts.equals|equals(kotlin.Any?){}[0]
24+
final fun hashCode(): kotlin/Int // com.juul.koap/OscoreParts.hashCode|hashCode(){}[0]
25+
final fun toString(): kotlin/String // com.juul.koap/OscoreParts.toString|toString(){}[0]
26+
}
27+
928
sealed class com.juul.koap/Header { // com.juul.koap/Header|null[0]
1029
abstract val code // com.juul.koap/Header.code|{}code[0]
1130
abstract fun <get-code>(): com.juul.koap/Message.Code // com.juul.koap/Header.code.<get-code>|<get-code>(){}[0]
@@ -500,26 +519,20 @@ sealed class com.juul.koap/Message { // com.juul.koap/Message|null[0]
500519
}
501520

502521
final class Oscore : com.juul.koap/Message.Option { // com.juul.koap/Message.Option.Oscore|null[0]
503-
constructor <init>(kotlin/ByteArray, kotlin/ByteArray?, kotlin/ByteArray?) // com.juul.koap/Message.Option.Oscore.<init>|<init>(kotlin.ByteArray;kotlin.ByteArray?;kotlin.ByteArray?){}[0]
522+
constructor <init>(kotlin/ByteArray) // com.juul.koap/Message.Option.Oscore.<init>|<init>(kotlin.ByteArray){}[0]
504523

505-
final val kid // com.juul.koap/Message.Option.Oscore.kid|{}kid[0]
506-
final fun <get-kid>(): kotlin/ByteArray? // com.juul.koap/Message.Option.Oscore.kid.<get-kid>|<get-kid>(){}[0]
507-
final val kidContext // com.juul.koap/Message.Option.Oscore.kidContext|{}kidContext[0]
508-
final fun <get-kidContext>(): kotlin/ByteArray? // com.juul.koap/Message.Option.Oscore.kidContext.<get-kidContext>|<get-kidContext>(){}[0]
509-
final val partialIv // com.juul.koap/Message.Option.Oscore.partialIv|{}partialIv[0]
510-
final fun <get-partialIv>(): kotlin/ByteArray // com.juul.koap/Message.Option.Oscore.partialIv.<get-partialIv>|<get-partialIv>(){}[0]
524+
final val value // com.juul.koap/Message.Option.Oscore.value|{}value[0]
525+
final fun <get-value>(): kotlin/ByteArray // com.juul.koap/Message.Option.Oscore.value.<get-value>|<get-value>(){}[0]
511526

512527
final fun component1(): kotlin/ByteArray // com.juul.koap/Message.Option.Oscore.component1|component1(){}[0]
513-
final fun component2(): kotlin/ByteArray? // com.juul.koap/Message.Option.Oscore.component2|component2(){}[0]
514-
final fun component3(): kotlin/ByteArray? // com.juul.koap/Message.Option.Oscore.component3|component3(){}[0]
515-
final fun copy(kotlin/ByteArray = ..., kotlin/ByteArray? = ..., kotlin/ByteArray? = ...): com.juul.koap/Message.Option.Oscore // com.juul.koap/Message.Option.Oscore.copy|copy(kotlin.ByteArray;kotlin.ByteArray?;kotlin.ByteArray?){}[0]
528+
final fun copy(kotlin/ByteArray = ...): com.juul.koap/Message.Option.Oscore // com.juul.koap/Message.Option.Oscore.copy|copy(kotlin.ByteArray){}[0]
516529
final fun equals(kotlin/Any?): kotlin/Boolean // com.juul.koap/Message.Option.Oscore.equals|equals(kotlin.Any?){}[0]
517530
final fun hashCode(): kotlin/Int // com.juul.koap/Message.Option.Oscore.hashCode|hashCode(){}[0]
518-
final fun optionValue(): kotlin/ByteArray // com.juul.koap/Message.Option.Oscore.optionValue|optionValue(){}[0]
531+
final fun parts(): com.juul.koap/OscoreParts // com.juul.koap/Message.Option.Oscore.parts|parts(){}[0]
519532
final fun toString(): kotlin/String // com.juul.koap/Message.Option.Oscore.toString|toString(){}[0]
520533

521534
final object Companion { // com.juul.koap/Message.Option.Oscore.Companion|null[0]
522-
final fun fromOptionValue(kotlin/ByteArray): com.juul.koap/Message.Option.Oscore // com.juul.koap/Message.Option.Oscore.Companion.fromOptionValue|fromOptionValue(kotlin.ByteArray){}[0]
535+
final fun fromParts(kotlin/ByteArray, kotlin/ByteArray?, kotlin/ByteArray?): com.juul.koap/Message.Option.Oscore // com.juul.koap/Message.Option.Oscore.Companion.fromParts|fromParts(kotlin.ByteArray;kotlin.ByteArray?;kotlin.ByteArray?){}[0]
523536
}
524537
}
525538

koap-core/src/commonMain/kotlin/Decoder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ internal fun ByteArrayReader.readOption(preceding: Format?): Option? {
411411
6 -> Observe(readNumberOfLength(length))
412412
7 -> UriPort(readNumberOfLength(length))
413413
8 -> LocationPath(readUtf8(length))
414-
9 -> Oscore.fromOptionValue(readByteArray(length))
414+
9 -> Oscore(readByteArray(length))
415415
11 -> UriPath(readUtf8(length))
416416
12 -> ContentFormat(readNumberOfLength(length))
417417
14 -> MaxAge(readNumberOfLength(length))

koap-core/src/commonMain/kotlin/Message.kt

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -590,46 +590,29 @@ sealed class Message {
590590

591591
/** RFC 8613 2. OSCORE */
592592
data class Oscore(
593-
val partialIv: ByteArray,
594-
val kidContext: ByteArray?,
595-
val kid: ByteArray?,
593+
val value: ByteArray,
596594
) : Option() {
597595
init {
598-
val value = optionValue()
599596
require(value.size in OSCORE_LENGTH_RANGE) {
600597
"Oscore length of ${value.size} is outside allowable range of $OSCORE_LENGTH_RANGE"
601598
}
602599
}
603600

604601
override fun equals(other: Any?): Boolean =
605-
this === other ||
606-
(
607-
other is Oscore &&
608-
partialIv.contentEquals(other.partialIv) &&
609-
kidContext.contentEquals(other.kidContext) &&
610-
kid.contentEquals(other.kid)
611-
)
602+
this === other || (other is Oscore && value.contentEquals(other.value))
612603

613-
override fun hashCode(): Int {
614-
var result = partialIv.contentHashCode()
615-
result = 31 * result + kidContext.contentHashCode()
616-
result = 31 * result + kid.contentHashCode()
617-
return result
618-
}
604+
override fun hashCode(): Int = value.contentHashCode()
619605

620606
override fun toString(): String {
621-
val args = listOfNotNull(
622-
if (partialIv.size > 0) "partialIv=${partialIv.toHexString()}" else null,
623-
if (kidContext != null) "kidContext=${kidContext.toHexString()}" else null,
624-
if (kid != null) "kid=${kid?.toHexString()}" else null,
625-
).joinToString(separator = ", ")
626-
return "Oscore($args)"
607+
val parts = oscorePartsFromOptionValue(value)
608+
return parts.toString()
627609
}
628610

629-
fun optionValue(): ByteArray = oscoreOptionValue(this)
611+
fun parts(): OscoreParts = oscorePartsFromOptionValue(value)
630612

631613
companion object {
632-
fun fromOptionValue(value: ByteArray): Oscore = oscoreFromOptionValue(value)
614+
fun fromParts(partialIv: ByteArray, kidContext: ByteArray?, kid: ByteArray?): Oscore =
615+
Oscore(oscoreOptionValue(OscoreParts(partialIv, kidContext, kid)))
633616
}
634617
}
635618

koap-core/src/commonMain/kotlin/Oscore.kt

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,44 @@ private const val PARTIAL_IV_LENGTH_MASK = 0x07
77
private const val KID_FLAG = 0x08
88
private const val KID_CONTEXT_FLAG = 0x10
99

10+
data class OscoreParts(
11+
val partialIv: ByteArray,
12+
val kidContext: ByteArray?,
13+
val kid: ByteArray?,
14+
) {
15+
override fun equals(other: Any?): Boolean =
16+
this === other ||
17+
(
18+
other is OscoreParts &&
19+
partialIv.contentEquals(other.partialIv) &&
20+
kidContext.contentEquals(other.kidContext) &&
21+
kid.contentEquals(other.kid)
22+
)
23+
24+
override fun hashCode(): Int {
25+
var result = partialIv.contentHashCode()
26+
result = 31 * result + kidContext.contentHashCode()
27+
result = 31 * result + kid.contentHashCode()
28+
return result
29+
}
30+
31+
override fun toString(): String {
32+
val args = listOfNotNull(
33+
if (partialIv.isNotEmpty()) "partialIv=${partialIv.toHexString()}" else null,
34+
if (kidContext != null) "kidContext=${kidContext.toHexString()}" else null,
35+
if (kid != null) "kid=${kid.toHexString()}" else null,
36+
).joinToString(separator = ", ")
37+
return "Oscore($args)"
38+
}
39+
}
40+
1041
/**
11-
* Creates a binary OSCORE option value from an Oscore option object.
42+
* Creates a binary OSCORE option value from an OscoreParts object.
1243
*
1344
* - [RFC 8613](https://tools.ietf.org/html/rfc8613#section-2) 2. The OSCORE Option
1445
* - [RFC 8613](https://tools.ietf.org/html/rfc8613#section-6.1) 6.1. Encoding of the OSCORE Option Value
1546
*/
16-
internal fun oscoreOptionValue(oscore: Oscore): ByteArray {
47+
internal fun oscoreOptionValue(oscore: OscoreParts): ByteArray {
1748
val partialIvLength = oscore.partialIv.size
1849
require(partialIvLength in PARTIAL_IV_LENGTH_RANGE) {
1950
"Partial IV length of $partialIvLength is outside allowable range of $PARTIAL_IV_LENGTH_RANGE"
@@ -28,7 +59,7 @@ internal fun oscoreOptionValue(oscore: Oscore): ByteArray {
2859
}
2960
var value = byteArrayOf(flagBits.toByte()) + oscore.partialIv
3061
if (oscore.kidContext != null) {
31-
value += kotlin.byteArrayOf(kidContextLength.toByte()) + oscore.kidContext
62+
value += byteArrayOf(kidContextLength.toByte()) + oscore.kidContext
3263
}
3364
if (oscore.kid != null) {
3465
value += oscore.kid
@@ -37,11 +68,12 @@ internal fun oscoreOptionValue(oscore: Oscore): ByteArray {
3768
}
3869

3970
/**
40-
* Parses an Oscore option object from a binary OSCORE option value.
71+
* Parses an OscoreParts object from a binary OSCORE option value.
4172
*/
42-
internal fun oscoreFromOptionValue(value: ByteArray): Oscore {
43-
if (value.size == 0) {
44-
return Oscore(byteArrayOf(), null, null)
73+
internal fun oscorePartsFromOptionValue(value: ByteArray): OscoreParts {
74+
// TODO: detect if value does not conform to expected oscore format
75+
if (value.isEmpty()) {
76+
return OscoreParts(byteArrayOf(), null, null)
4577
}
4678
val flagBits = value[0].toInt()
4779
val partialIvLength = flagBits and PARTIAL_IV_LENGTH_MASK
@@ -56,5 +88,5 @@ internal fun oscoreFromOptionValue(value: ByteArray): Oscore {
5688
}
5789
val kidOffset = (if (kidContextFlag) 2 else 1) + partialIvLength + kidContextLength
5890
val kid = if (kidFlag) value.sliceArray(kidOffset until value.size) else null
59-
return Oscore(partialIv, kidContext, kid)
91+
return OscoreParts(partialIv, kidContext, kid)
6092
}

koap-core/src/commonMain/kotlin/ToFormat.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ internal fun Option.toFormat(): Format =
4848
is Observe -> uint(6, option.value)
4949
is UriPort -> uint(7, option.port)
5050
is LocationPath -> string(8, option.uri)
51-
is Oscore -> opaque(9, option.optionValue())
51+
is Oscore -> opaque(9, option.value)
5252
is UriPath -> string(11, option.uri)
5353
is ContentFormat -> uint(12, option.format)
5454
is MaxAge -> uint(14, option.seconds)

koap-core/src/commonTest/kotlin/DecoderTest.kt

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ class DecoderTest {
266266
encoded = """
267267
90 # Option Delta: 9, Option Length: 0
268268
""",
269-
expected = Oscore(byteArrayOf(), null, null),
269+
expected = Oscore.fromParts(byteArrayOf(), null, null),
270270
)
271271
}
272272

@@ -292,15 +292,15 @@ class DecoderTest {
292292
token = unprotectedCoapRequest.token,
293293
options = listOf(
294294
UriHost("localhost"),
295-
Oscore(partialIv = byteArrayOf(0x14), kidContext = null, kid = byteArrayOf()),
295+
Oscore.fromParts(partialIv = byteArrayOf(0x14), kidContext = null, kid = byteArrayOf()),
296296
),
297297
payload = ciphertext,
298298
),
299299
actual = protectedCoapRequest.decode<Message.Udp>(),
300300
)
301301
assertEquals(
302-
actual = Oscore.fromOptionValue(oscoreOptionValue),
303-
expected = Oscore(partialIv = byteArrayOf(0x14), kidContext = null, kid = byteArrayOf()),
302+
actual = Oscore(oscoreOptionValue),
303+
expected = Oscore.fromParts(partialIv = byteArrayOf(0x14), kidContext = null, kid = byteArrayOf()),
304304
)
305305
}
306306

@@ -326,15 +326,15 @@ class DecoderTest {
326326
token = unprotectedCoapRequest.token,
327327
options = listOf(
328328
UriHost("localhost"),
329-
Oscore(partialIv = byteArrayOf(0x14), kidContext = null, kid = byteArrayOf(0x00)),
329+
Oscore.fromParts(partialIv = byteArrayOf(0x14), kidContext = null, kid = byteArrayOf(0x00)),
330330
),
331331
payload = ciphertext,
332332
),
333333
actual = protectedCoapRequest.decode<Message.Udp>(),
334334
)
335335
assertEquals(
336-
actual = Oscore.fromOptionValue(oscoreOptionValue),
337-
expected = Oscore(partialIv = byteArrayOf(0x14), kidContext = null, kid = byteArrayOf(0x00)),
336+
actual = Oscore(oscoreOptionValue),
337+
expected = Oscore.fromParts(partialIv = byteArrayOf(0x14), kidContext = null, kid = byteArrayOf(0x00)),
338338
)
339339
}
340340

@@ -361,15 +361,15 @@ class DecoderTest {
361361
token = unprotectedCoapRequest.token,
362362
options = listOf(
363363
UriHost("localhost"),
364-
Oscore(partialIv = byteArrayOf(0x14), kidContext = "37CBF3210017A2D3".decodeHex().toByteArray(), kid = byteArrayOf()),
364+
Oscore.fromParts(partialIv = byteArrayOf(0x14), kidContext = "37CBF3210017A2D3".decodeHex().toByteArray(), kid = byteArrayOf()),
365365
),
366366
payload = ciphertext,
367367
),
368368
actual = protectedCoapRequest.decode<Message.Udp>(),
369369
)
370370
assertEquals(
371-
actual = Oscore.fromOptionValue(oscoreOptionValue),
372-
expected = Oscore(
371+
actual = Oscore(oscoreOptionValue),
372+
expected = Oscore.fromParts(
373373
partialIv = byteArrayOf(0x14), kidContext = "37CBF3210017A2D3".decodeHex().toByteArray(), kid = byteArrayOf(),
374374
),
375375
)
@@ -396,15 +396,15 @@ class DecoderTest {
396396
id = unprotectedCoapResponse.id,
397397
token = unprotectedCoapResponse.token,
398398
options = listOf(
399-
Oscore(partialIv = byteArrayOf(), kidContext = null, kid = null),
399+
Oscore.fromParts(partialIv = byteArrayOf(), kidContext = null, kid = null),
400400
),
401401
payload = ciphertext,
402402
),
403403
actual = protectedCoapResponse.decode<Message.Udp>(),
404404
)
405405
assertEquals(
406-
actual = Oscore.fromOptionValue(oscoreOptionValue),
407-
expected = Oscore(partialIv = byteArrayOf(), kidContext = null, kid = null),
406+
actual = Oscore(oscoreOptionValue),
407+
expected = Oscore.fromParts(partialIv = byteArrayOf(), kidContext = null, kid = null),
408408
)
409409
}
410410

@@ -429,15 +429,15 @@ class DecoderTest {
429429
id = unprotectedCoapResponse.id,
430430
token = unprotectedCoapResponse.token,
431431
options = listOf(
432-
Oscore(partialIv = byteArrayOf(0x00), kidContext = null, kid = null),
432+
Oscore.fromParts(partialIv = byteArrayOf(0x00), kidContext = null, kid = null),
433433
),
434434
payload = ciphertext,
435435
),
436436
actual = protectedCoapResponse.decode<Message.Udp>(),
437437
)
438438
assertEquals(
439-
actual = Oscore.fromOptionValue(oscoreOptionValue),
440-
expected = Oscore(partialIv = byteArrayOf(0x00), kidContext = null, kid = null),
439+
actual = Oscore(oscoreOptionValue),
440+
expected = Oscore.fromParts(partialIv = byteArrayOf(0x00), kidContext = null, kid = null),
441441
)
442442
}
443443

koap-core/src/commonTest/kotlin/EncoderTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ class EncoderTest {
374374
@Test
375375
fun writeOscoreOptionWithEmptyValue() {
376376
testWriteOption(
377-
option = Oscore(byteArrayOf(), null, null),
377+
option = Oscore.fromParts(byteArrayOf(), null, null),
378378
expected = """
379379
90 # Option Delta: 9, Option Length: 0
380380
""",
@@ -385,7 +385,7 @@ class EncoderTest {
385385
fun writeOscoreOptionTestVector4() {
386386
// https://datatracker.ietf.org/doc/html/rfc8613#appendix-C.4 Test Vector 4
387387
testWriteOption(
388-
option = Oscore(partialIv = byteArrayOf(0x14), kidContext = null, kid = byteArrayOf()),
388+
option = Oscore.fromParts(partialIv = byteArrayOf(0x14), kidContext = null, kid = byteArrayOf()),
389389
expected = """
390390
92 # Option Delta: 9, Option Length: 2
391391
09 14 # Option Value: 09 14
@@ -406,7 +406,7 @@ class EncoderTest {
406406
@Test
407407
fun oscoreOptionIvParameterTooLongThrowsIllegalArgumentException() {
408408
assertFailsWith<IllegalArgumentException> {
409-
Oscore(partialIv = byteArrayOf(1, 2, 3, 4, 5, 6), kid = null, kidContext = null)
409+
Oscore.fromParts(partialIv = byteArrayOf(1, 2, 3, 4, 5, 6), kid = null, kidContext = null)
410410
}
411411
}
412412

koap-core/src/commonTest/kotlin/MessageTest.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,12 @@ class MessageTest {
6262
assertToString(IfMatch("".encodeToByteArray()), "IfMatch(etag=)")
6363
assertToString(Echo("echo".encodeToByteArray()), "Echo(value=65 63 68 6F)")
6464
assertToString(RequestTag("tag".encodeToByteArray()), "RequestTag(tag=74 61 67)")
65-
assertToString(Oscore.fromOptionValue(byteArrayOf()), "Oscore()")
66-
assertToString(Oscore.fromOptionValue(byteArrayOf(0x01, 0x14)), "Oscore(partialIv=14)")
67-
assertToString(Oscore.fromOptionValue(byteArrayOf(0x09, 0x14)), "Oscore(partialIv=14, kid=)")
68-
assertToString(Oscore.fromOptionValue(byteArrayOf(0x09, 0x14, 0x15)), "Oscore(partialIv=14, kid=15)")
69-
assertToString(Oscore.fromOptionValue(byteArrayOf(0x11, 0x14, 0x01, 0x16)), "Oscore(partialIv=14, kidContext=16)")
70-
assertToString(Oscore.fromOptionValue(byteArrayOf(0x19, 0x14, 0x01, 0x16)), "Oscore(partialIv=14, kidContext=16, kid=)")
65+
assertToString(Oscore(byteArrayOf()), "Oscore()")
66+
assertToString(Oscore(byteArrayOf(0x01, 0x14)), "Oscore(partialIv=14)")
67+
assertToString(Oscore(byteArrayOf(0x09, 0x14)), "Oscore(partialIv=14, kid=)")
68+
assertToString(Oscore(byteArrayOf(0x09, 0x14, 0x15)), "Oscore(partialIv=14, kid=15)")
69+
assertToString(Oscore(byteArrayOf(0x11, 0x14, 0x01, 0x16)), "Oscore(partialIv=14, kidContext=16)")
70+
assertToString(Oscore(byteArrayOf(0x19, 0x14, 0x01, 0x16)), "Oscore(partialIv=14, kidContext=16, kid=)")
7171
assertToString(IfNoneMatch, "IfNoneMatch")
7272
assertToString(Edhoc, "Edhoc")
7373
assertToString(UriHost("localhost"), "UriHost(uri=localhost)")

0 commit comments

Comments
 (0)