Files
GPS2Audio/app/src/main/kotlin/de/waypointaudio/license/CanonicalJson.kt
T
2026-05-30 05:42:44 +00:00

99 lines
3.2 KiB
Kotlin

package de.waypointaudio.license
import org.json.JSONArray
import org.json.JSONObject
/**
* v2.5.20 — Kanonische JSON-Serialisierung, kompatibel zum Python-Tool
* (`canonical_json_bytes`):
* - Keys lexikografisch sortiert.
* - Trennzeichen ohne Whitespace: "," / ":".
* - `ensure_ascii=False` ⇒ Unicode-Strings werden nicht escapet, ausser den
* minimal nötigen JSON-Escapes (`\"`, `\\`, Steuerzeichen < 0x20).
* - `null` für JSON-Null; `JSONObject.NULL` und Kotlin-`null` gleich behandelt.
*
* Diese kanonische Form ist die Eingabe für die ECDSA-P256-SHA256-Signatur.
*/
internal object CanonicalJson {
fun encode(value: Any?): String {
val sb = StringBuilder()
appendValue(sb, value)
return sb.toString()
}
private fun appendValue(sb: StringBuilder, value: Any?) {
when {
value == null || value === JSONObject.NULL -> sb.append("null")
value is Boolean -> sb.append(if (value) "true" else "false")
value is Number -> appendNumber(sb, value)
value is String -> appendString(sb, value)
value is JSONObject -> appendObject(sb, value)
value is JSONArray -> appendArray(sb, value)
else -> appendString(sb, value.toString())
}
}
private fun appendObject(sb: StringBuilder, obj: JSONObject) {
val keys = obj.keys().asSequence().toList().sorted()
sb.append('{')
for ((index, key) in keys.withIndex()) {
if (index > 0) sb.append(',')
appendString(sb, key)
sb.append(':')
appendValue(sb, obj.opt(key))
}
sb.append('}')
}
private fun appendArray(sb: StringBuilder, arr: JSONArray) {
sb.append('[')
for (i in 0 until arr.length()) {
if (i > 0) sb.append(',')
appendValue(sb, arr.opt(i))
}
sb.append(']')
}
private fun appendNumber(sb: StringBuilder, n: Number) {
when (n) {
is Int, is Long, is Short, is Byte -> sb.append(n.toString())
is Double -> {
if (n.isNaN() || n.isInfinite()) {
sb.append("null")
} else if (n == n.toLong().toDouble()) {
sb.append(n.toLong().toString())
} else {
sb.append(n.toString())
}
}
is Float -> appendNumber(sb, n.toDouble())
else -> sb.append(n.toString())
}
}
private fun appendString(sb: StringBuilder, s: String) {
sb.append('"')
for (c in s) {
when (c.code) {
0x22 -> sb.append("\\\"")
0x5C -> sb.append("\\\\")
0x08 -> sb.append("\\b")
0x0C -> sb.append("\\f")
0x0A -> sb.append("\\n")
0x0D -> sb.append("\\r")
0x09 -> sb.append("\\t")
else -> {
val code = c.code
if (code < 0x20) {
sb.append("\\u%04x".format(code))
} else {
sb.append(c)
}
}
}
}
sb.append('"')
}
}