diff --git a/build.gradle.kts b/build.gradle.kts index 81d9d2f..3dcf194 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,6 +11,7 @@ repositories { } dependencies { + implementation("org.greenbytes.http:structured-fields:0.4") testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") testImplementation("org.mockito:mockito-inline:5.2.0") } diff --git a/src/main/kotlin/dev/usbharu/httpsignature/v2/DefaultHttpSignatureHeaderParser.kt b/src/main/kotlin/dev/usbharu/httpsignature/v2/DefaultHttpSignatureHeaderParser.kt new file mode 100644 index 0000000..98e15d5 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/httpsignature/v2/DefaultHttpSignatureHeaderParser.kt @@ -0,0 +1,58 @@ +package dev.usbharu.httpsignature.v2 + +import org.greenbytes.http.sfv.* + +class DefaultHttpSignatureHeaderParser() : HttpSignatureHeaderParser { + override fun parse(signatureInputs: List, signatures: List): Map { + val signatureInputString = signatureInputs.joinToString(", ") + + val signatureString = signatures.joinToString(", ") + + val parseSignatureInputs = parseSignatureInputs(signatureInputString) + + val parseSignatures = parseSignatures(signatureString) + + require(parseSignatureInputs.size == parseSignatures.size) + + return parseSignatureInputs.map { + val signatureInput = parseSignatureInputs.getValue(it.key) + it.key to Signature( + it.key, + signatureInput.first, + parseSignatures.getValue(it.key), + signatureInput.third, + signatureInput.second + ) + }.toMap() + } + + private fun parseSignatureInputs(signatureInput: String): Map, List>> { + val parser = Parser(signatureInput) + val map = parser.parseDictionary() + .get() + .mapValues { + val innerList = it.value as InnerList + Triple( + it.value.serialize(), + innerList.get().map { it.get().toString() }, + innerList.params.map { param -> + when (val value = param.value) { + is IntegerItem -> LongSignatureParameter(param.key, value.asLong) + is NumberItem -> LongSignatureParameter(param.key, value.asLong) + is StringItem -> StringSignatureParameter(param.key, value.get()) + else -> error("Unknown parameter: $param") + } + } + ) + } + + return map + } + + private fun parseSignatures(signature: String): Map { + val parser = Parser(signature) + val map = parser.parseDictionary().get() + + return map.map { it.key to it.value.get().toString() }.toMap() + } +} \ No newline at end of file diff --git a/src/main/kotlin/dev/usbharu/httpsignature/v2/HttpMessageSignatureSigner.kt b/src/main/kotlin/dev/usbharu/httpsignature/v2/HttpMessageSignatureSigner.kt index ae36e89..fa97ad4 100644 --- a/src/main/kotlin/dev/usbharu/httpsignature/v2/HttpMessageSignatureSigner.kt +++ b/src/main/kotlin/dev/usbharu/httpsignature/v2/HttpMessageSignatureSigner.kt @@ -1,7 +1,7 @@ package dev.usbharu.httpsignature.v2 class HttpMessageSignatureSigner { - fun sign(material: Material, signatureParameters: List, signer: SignatureSigner): Signatures { + fun sign(material: Material, signatureParameters: List, signer: SignatureSigner): Signature { val signatureBase = material.signatureBase.generateSignatureBase(signatureParameters) val signatureInput = @@ -9,6 +9,12 @@ class HttpMessageSignatureSigner { val signature = signer.sign(signatureBase.toByteArray(Charsets.UTF_8), material.privateKey) - return Signatures(signatureInput, signature) + return Signature( + material.label, + signatureInput, + signature, + signatureParameters, + material.signatureBase.coveredComponents() + ) } } \ No newline at end of file diff --git a/src/main/kotlin/dev/usbharu/httpsignature/v2/HttpSignatureHeaderParser.kt b/src/main/kotlin/dev/usbharu/httpsignature/v2/HttpSignatureHeaderParser.kt new file mode 100644 index 0000000..16c4bd4 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/httpsignature/v2/HttpSignatureHeaderParser.kt @@ -0,0 +1,5 @@ +package dev.usbharu.httpsignature.v2 + +interface HttpSignatureHeaderParser { + fun parse(signatureInputs: List, signatures: List): Map +} \ No newline at end of file diff --git a/src/main/kotlin/dev/usbharu/httpsignature/v2/InstantSignatureParameter.kt b/src/main/kotlin/dev/usbharu/httpsignature/v2/InstantSignatureParameter.kt index 2918dce..f21d5f2 100644 --- a/src/main/kotlin/dev/usbharu/httpsignature/v2/InstantSignatureParameter.kt +++ b/src/main/kotlin/dev/usbharu/httpsignature/v2/InstantSignatureParameter.kt @@ -2,7 +2,7 @@ package dev.usbharu.httpsignature.v2 import java.time.Instant -class InstantSignatureParameter(private val instantName: String, val instant: Instant) : SignatureParameter { +data class InstantSignatureParameter(private val instantName: String, val instant: Instant) : SignatureParameter { override val name: String get() = instantName override val value: String diff --git a/src/main/kotlin/dev/usbharu/httpsignature/v2/LongSignatureParameter.kt b/src/main/kotlin/dev/usbharu/httpsignature/v2/LongSignatureParameter.kt new file mode 100644 index 0000000..284e3c6 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/httpsignature/v2/LongSignatureParameter.kt @@ -0,0 +1,8 @@ +package dev.usbharu.httpsignature.v2 + +data class LongSignatureParameter(val longName: String, val longValue: Long) : SignatureParameter { + override val name: String + get() = longName + override val value: String + get() = longValue.toString() +} \ No newline at end of file diff --git a/src/main/kotlin/dev/usbharu/httpsignature/v2/Signature.kt b/src/main/kotlin/dev/usbharu/httpsignature/v2/Signature.kt new file mode 100644 index 0000000..36d8114 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/httpsignature/v2/Signature.kt @@ -0,0 +1,9 @@ +package dev.usbharu.httpsignature.v2 + +data class Signature( + val label: String, + val signatureInput: String, + val signature: String, + val signatureParameters: List, + val coveredComponents: List +) diff --git a/src/main/kotlin/dev/usbharu/httpsignature/v2/SignatureBase.kt b/src/main/kotlin/dev/usbharu/httpsignature/v2/SignatureBase.kt index 1e13482..89efec5 100644 --- a/src/main/kotlin/dev/usbharu/httpsignature/v2/SignatureBase.kt +++ b/src/main/kotlin/dev/usbharu/httpsignature/v2/SignatureBase.kt @@ -32,4 +32,8 @@ class SignatureBase() { ) ) + signatureParameters.map { "${it.name}=${it.value}" }).joinToString(";") } + + fun coveredComponents(): List { + return list.map { it.key } + } } diff --git a/src/main/kotlin/dev/usbharu/httpsignature/v2/Signatures.kt b/src/main/kotlin/dev/usbharu/httpsignature/v2/Signatures.kt deleted file mode 100644 index 0ff510b..0000000 --- a/src/main/kotlin/dev/usbharu/httpsignature/v2/Signatures.kt +++ /dev/null @@ -1,6 +0,0 @@ -package dev.usbharu.httpsignature.v2 - -data class Signatures( - val signatureInput: String, - val signature: String -) diff --git a/src/main/kotlin/dev/usbharu/httpsignature/v2/StringSignatureParameter.kt b/src/main/kotlin/dev/usbharu/httpsignature/v2/StringSignatureParameter.kt index 3ed05ec..c43d432 100644 --- a/src/main/kotlin/dev/usbharu/httpsignature/v2/StringSignatureParameter.kt +++ b/src/main/kotlin/dev/usbharu/httpsignature/v2/StringSignatureParameter.kt @@ -1,6 +1,7 @@ package dev.usbharu.httpsignature.v2 -class StringSignatureParameter(private val stringName: String, private val stringValue: String) : SignatureParameter { +data class StringSignatureParameter(private val stringName: String, private val stringValue: String) : + SignatureParameter { override val name: String get() = stringName override val value: String diff --git a/src/test/kotlin/dev/usbharu/httpsignature/v2/DefaultHttpSignatureHeaderParserTest.kt b/src/test/kotlin/dev/usbharu/httpsignature/v2/DefaultHttpSignatureHeaderParserTest.kt new file mode 100644 index 0000000..80b5de0 --- /dev/null +++ b/src/test/kotlin/dev/usbharu/httpsignature/v2/DefaultHttpSignatureHeaderParserTest.kt @@ -0,0 +1,43 @@ +package dev.usbharu.httpsignature.v2 + +import org.junit.jupiter.api.Test + +class DefaultHttpSignatureHeaderParserTest { + @Test + fun name() { + val defaultHttpSignatureHeaderParser = DefaultHttpSignatureHeaderParser() + defaultHttpSignatureHeaderParser.parse( + listOf( + "sig1=(\"@method\" \"@target-uri\" \"@authority\" " + + " \"content-digest\" \"cache-control\");" + + " created=1618884475;keyid=\"test-key-rsa-pss\"" + ), listOf() + ) + } + + @Test + fun test2() { + val parse = DefaultHttpSignatureHeaderParser().parse( + listOf( + "sig1=(\"@method\" \"@authority\" \"@path\" " + + "\"content-digest\" \"content-type\" \"content-length\")" + + ";created=1618884475;keyid=\"test-key-ecc-p256\", " + + "proxy_sig=(\"@method\" \"@authority\" \"@path\" \"content-digest\" " + + "\"content-type\" \"content-length\" \"forwarded\")" + + ";created=1618884480;keyid=\"test-key-rsa\";alg=\"rsa-v1_5-sha256\"" + + ";expires=1618884540" + ), listOf( + "sig1=:X5spyd6CFnAG5QnDyHfqoSNICd+BUP4LYMz2Q0JXlb//4Ijpzp" + + "+kve2w4NIyqeAuM7jTDX+sNalzA8ESSaHD3A==:, " + + "proxy_sig=:S6ZzPXSdAMOPjN/6KXfXWNO/f7V6cHm7BXYUh3YD/fRad4BCaRZxP+" + + "JH+8XY1I6+8Cy+CM5g92iHgxtRPz+MjniOaYmdkDcnL9cCpXJleXsOckpURl49G" + + "wiyUpZ10KHgOEe11sx3G2gxI8S0jnxQB+Pu68U9vVcasqOWAEObtNKKZd8tSFu7" + + "LB5YAv0RAGhB8tmpv7sFnIm9y+7X5kXQfi8NMaZaA8i2ZHwpBdg7a6CMfwnnrtf" + + "lzvZdXAsD3LH2TwevU+/PBPv0B6NMNk93wUs/vfJvye+YuI87HU38lZHowtznbL" + + "Vdp770I6VHR6WfgS9ddzirrswsE1w5o0LV/g==:" + ) + ) + + println(parse) + } +} \ No newline at end of file