293 lines
17 KiB
Kotlin
293 lines
17 KiB
Kotlin
package jp.juggler
|
||
|
||
import jp.juggler.crypt.*
|
||
import jp.juggler.util.data.decodeBase64
|
||
import jp.juggler.util.data.decodeJsonObject
|
||
import jp.juggler.util.log.AdbLog
|
||
import org.junit.Assert.*
|
||
import org.junit.Test
|
||
import java.security.interfaces.ECPrivateKey
|
||
|
||
@Suppress("SpellCheckingInspection")
|
||
class WebPushCryptTest {
|
||
|
||
// https://developers.google.com/web/updates/2016/03/web-push-encryption
|
||
|
||
@Test
|
||
fun testGenerateKeyPair() {
|
||
val provider = defaultSecurityProvider
|
||
val pair = provider.generateKeyPair()
|
||
val privateKey = (pair.private as ECPrivateKey)
|
||
val bytes = encodePrivateKeyRaw(privateKey)
|
||
val newKey = provider.decodePrivateKeyRaw(bytes)
|
||
assertEquals(
|
||
"s is same?",
|
||
privateKey.s,
|
||
newKey.s,
|
||
)
|
||
}
|
||
|
||
@Test
|
||
fun decryptMessage() {
|
||
// Authentication secret (auth_secret)
|
||
// 購読時に指定する
|
||
val authSecret = "BTBZMqHH6r4Tts7J_aSIgg".decodeBase64()
|
||
|
||
// User agent public key (ua_public)
|
||
// 購読時に指定する
|
||
val receiverPublicBytes =
|
||
"BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4".decodeBase64()
|
||
|
||
// User agent private key (ua_private)
|
||
val receiverPrivateBytesShort = "q1dXpw3UpT5VOmu_cf_v6ih07Aems3njxI-JWgLcM94".decodeBase64()
|
||
|
||
// encryption ヘッダから
|
||
val saltBytes = "pr1_1DFjrzX3RNvJPRngDA".decodeBase64()
|
||
|
||
// Application server public key (as_public)
|
||
// crypto-key ヘッダの前半、dh=xxx; の中味
|
||
// crypto-key: dh=BLJQjupjQyujhTC--_5xRUgcBfAP_zINsAGTlaDuEME7s9TVgQYsyrzrgbt1vqScmZkoj4BWfPit6EzzaXDW02I;p256ecdsa=BDmWlrZ3gvcv0R7sBhaSp_99FRSC3bBNn9CElRvbcviwYwVPL1Z-G9srAJS6lv_pMe5IkTmKgBWUCNefnN3QoeQ
|
||
val senderPublicBytes =
|
||
"BLJQjupjQyujhTC--_5xRUgcBfAP_zINsAGTlaDuEME7s9TVgQYsyrzrgbt1vqScmZkoj4BWfPit6EzzaXDW02I"
|
||
.decodeBase64()
|
||
|
||
val body =
|
||
"pTTuh1jT8KJ4zaGwIWjg417KTDzh+eIVe472nMgett3XyhoM5pAz8Yu2RPBXJHE/AojoMA1g+/uzbByu3d1/AygBh99qJ6Xtjya+XBSYoVrNJqT7vq0cKU9bZ8NrEepnaZUc2HjFUDDXNyHi2xBtJnMk/hSZTzyaiCQS2KssGAwixgdK/dTP8Yg+Pul3tgOQvq5CbYFd7iwBQntVv80vO8X+5hyIglA21+6/2fq5lCZSMri5K9/WbSb6erLkxO//A92KjZTnuufE4pUwtIdYW1bFnw5xu6ozjsCsDLbQTSo+JmghOzc/iYx5hG+y5YViC1UXue4eKKlmjbVDRLH6WkEEIKH2cwd4Gf9ewhYwhH7oKKIc4tjvRunq2gtBirQgRYJahgfwykdYA44iyogBc1rFZPGbxr1ph4RxVhdBmIZ+yMN6GQSiDCS+8jKGsc5xnjxrSXXdFva1a2xc1lpiReypZlTTXFmF16Cf+Z6B0UvFTa2AcqEDD0BBlhhbMBoG7n4CRjr5ObE2lG5PBg+gqitx/O1S+X8a4N78L+eK1upEVM+HRQAdCmiqDNJF0/N/VWSMrNCl7HNgnhmYU9Z1aYepiEioz1Tu14UzY/2NOx5z4h4szyJW8s/diAyOhnh+RBRM3QLHtygpLZ3i7o6vVUc="
|
||
.decodeBase64()
|
||
|
||
AesGcmDecoder(
|
||
// receiver private key in X509 format
|
||
receiverPrivateBytes =
|
||
defaultSecurityProvider.decodePrivateKeyRaw(receiverPrivateBytesShort).encoded,
|
||
// receiver public key in 65bytes X9.62 uncompressed format
|
||
receiverPublicBytes = receiverPublicBytes,
|
||
// sender public key in 65bytes X9.62 uncompressed format
|
||
senderPublicBytes = senderPublicBytes,
|
||
// auth secrets created at subscription
|
||
authSecret = authSecret,
|
||
// salt in HTTP header
|
||
saltBytes = saltBytes,
|
||
).run {
|
||
assertEquals(
|
||
"sharedKeyBytes",
|
||
"irnQ9JOfMP/kl/SB8LUHpvjmUjwlkYzypisDnlHVKSA=",
|
||
sharedKeyBytes.encodeBase64()
|
||
)
|
||
|
||
deriveKey()
|
||
|
||
assertEquals(
|
||
"ikm",
|
||
"Tq5bGvBQUWTQdqFfAK1WAE/etlIpcc07QLh+RNCAD9Y=",
|
||
ikm.encodeBase64()
|
||
)
|
||
|
||
assertEquals(
|
||
"contentEncryptionKey",
|
||
"patR3W6rY0PG/5YnwY3/kA==",
|
||
key.encodeBase64()
|
||
)
|
||
|
||
assertEquals(
|
||
"nonce",
|
||
"E/GxDDwW9lfa7/by",
|
||
nonce.encodeBase64()
|
||
)
|
||
decode(body.toByteRange())
|
||
}.decodeUTF8().let {
|
||
assertEquals(
|
||
"text",
|
||
"""{"title":"あなたのトゥートが tateisu 🤹 さんにお気に入り登録されました","image":null,"badge":"https://mastodon2.juggler.jp/badge.png","tag":84,"timestamp":"2018-05-11T17:06:42.887Z","icon":"/system/accounts/avatars/000/000/003/original/72f1da33539be11e.jpg","data":{"content":":enemy_bullet:","nsfw":null,"url":"https://mastodon2.juggler.jp/web/statuses/98793123081777841","actions":[],"access_token":null,"message":"%{count} 件の通知","dir":"ltr"}}""",
|
||
it
|
||
)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 今どきのMastodonはCrypto-Keyが異なる
|
||
* dh=XXX;p256ecdsa=XXX
|
||
*/
|
||
@Test
|
||
fun test2() {
|
||
// 購読時に分かった情報
|
||
val receiverPrivateBytes =
|
||
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgN1UVSn5P4XC9zuuT3sW8TTikA8AhsjZKp9W6BwzSlW2hRANCAAQSFVIOe_wsdFuDCKjrNRM1yWIkwlfx8ZoQ3OyYJ2K5oeXpmjo5EAimVq0Fs0NuxA8Y6F1hTB_Orc4gR1WOK-W4"
|
||
.decodeBase64()
|
||
val receiverPublicBytes =
|
||
"BBIVUg57_Cx0W4MIqOs1EzXJYiTCV_HxmhDc7JgnYrmh5emaOjkQCKZWrQWzQ27EDxjoXWFMH86tziBHVY4r5bg"
|
||
.decodeBase64()
|
||
val senderPublicBytesOld =
|
||
"BLzIgsz-VRhTxuVgNoQljTwAFzxbanfGxNk8tldaruBztvsK9elES_2lE_8c91-RNOInEBEFUrCzDw-60bzUCr8"
|
||
.decodeBase64()
|
||
val authSecret = "WrgqiWu8r3D9Ql3qYGkXTw"
|
||
.decodeBase64()
|
||
|
||
// プッシュメッセージに含まれる情報
|
||
val headerJson = """{
|
||
|"Digest":"SHA-256=kN1u9yotvg3utprFOaJ4qUGJ6cRhxGNfICXAGLjx7uM=",
|
||
|"Content-Encoding":"aesgcm",
|
||
|"Encryption":"salt=xtC3F3tO3UvQBtq-4Q38AQ",
|
||
|"Crypto-Key":"dh=BLivX-rukpGqww9YMqavS7o112MNTobqBqjzPX1ioUojdrDHKM1DwZKn6U2au0ddohfC4BGTDatjF96S7dOP2C8;p256ecdsa=BLzIgsz-VRhTxuVgNoQljTwAFzxbanfGxNk8tldaruBztvsK9elES_2lE_8c91-RNOInEBEFUrCzDw-60bzUCr8",
|
||
|"Authorization":"WebPush eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL21hc3RvZG9uLW1zZy5qdWdnbGVyLmpwIiwiZXhwIjoxNjc0ODQ3MjQ4LCJzdWIiOiJtYWlsdG86dGF0ZWlzdStqdWdnbGVyQGdtYWlsLmNvbSJ9.ksn_40KkHNcS0G3C8jMG-dsA6AcUydYYc1sA8JyXCCuDrNr-CLnl_-ezk1s-pC75yukNN0pIfqPJlJYtY_MQoQ",
|
||
|"<>cameFrom":"unifiedPush"
|
||
|}""".trimMargin().decodeJsonObject()
|
||
|
||
val body =
|
||
"4BVejMFrVADAEzkonO1QqUnDLiWCGAlzSbWg1KkG23BMZNLxUcQPJtULTTEMdoIzg4zZwDmsanDp5fepH8BDKpb5IoQlyFJjN0pq3tebWj79QL9h7QafxPByGx9HF9-zKlOUbmPMc3yS1tfccz30HvVSsMNUtcpuy1hsPkknwRBPI_SoLN20LEtAQ6IKLQGpiNMDx2rND9bd9wLz0Cr4pxUgZVxv0VjpiKHyTup0_BRotGd5e719uMmyDE-hzrqQYRr2QFrvHa1tNk41tpbPpeSAqhihsxU6bXxc4Lbws1Q6DyZBYHSUr6Av4SldA2Kmi3EjLijRvz3YEDWnSGv65V0SJv-O7tmFjexqFmFcF0qfv4Udzl9prjmutztcaWRAH-v9JdP3o2kthpK8RM4NC6Y0yU3GhEQ0z_1cfsuWsAvRe3H8pVcx3GXHVdWJ7VfqAwI-anSZz355-cuBbxAyrsBSp1Ysfru2D_5g53SZ7iNduHsXfKno9e6BkQr6u5pleXVHpRHYimzsjj46wbA0IfvEkhBpp9CoJ3JzPFkBowxpzuzmN1_IDiSDoYWVMYl1GXtwWOmKMKaKOCh3ULk2FH1WdUzNlvMd1N4Kf-MXjHFhkE33jSlLnKRLHqgzDEJgofWEzfoV-zruHFQfKwiN_7_NJdLYuGVV3z0IFiw6g9RdIvrNy7OEsTCaXbWC-xOEelJAvALvu308t_0PxxRdhSLVivrubyLZEKz1R8d6MzJ9b3f_mg6oYiGJ1u5oMrUvUUeNCERq4t9f0WMLo-zLFkNXvGe2TxnKMO9ZsWlXS1OnKnN6olNPKfVAFW50RxD6nBdP3S2a9uI58D9ycanCU_-i8osFrKg-GbYZ2tvRZw"
|
||
.decodeBase64()
|
||
|
||
// Encryption からsaltを読む
|
||
val saltBytes = headerJson.string("Encryption")?.parseSemicolon()
|
||
?.get("salt")?.decodeBase64()
|
||
?: error("missing Encryption.salt")
|
||
|
||
// Crypt-Key から dh と p256ecdsa を見る
|
||
val cryptKeys = headerJson.string("Crypto-Key")?.parseSemicolon()
|
||
?: error("missing Crypto-Key")
|
||
|
||
val senderPublicBytes = cryptKeys["dh"]?.decodeBase64()
|
||
?: senderPublicBytesOld
|
||
|
||
// JWT検証で使われるらしい
|
||
// val p256ecdsa = cryptKeys["p256ecdsa"]?.decodeBase64()
|
||
// ?: error("missing p256ecdsa")
|
||
|
||
AesGcmDecoder(
|
||
// receiver private key in X509 format
|
||
receiverPrivateBytes = receiverPrivateBytes,
|
||
// receiver public key in 65bytes X9.62 uncompressed format
|
||
receiverPublicBytes = receiverPublicBytes,
|
||
// sender public key in 65bytes X9.62 uncompressed format
|
||
senderPublicBytes = senderPublicBytes,
|
||
// auth secrets created at subscription
|
||
authSecret = authSecret,
|
||
// salt in HTTP header
|
||
saltBytes = saltBytes,
|
||
).run {
|
||
deriveKey()
|
||
decode(body.toByteRange())
|
||
}.decodeUTF8().let {
|
||
assertEquals(
|
||
"text",
|
||
"""{"access_token":"ON0yBbjmRS-QuSk5Uv7fjeKFVQESnYCZP39z71On68E","preferred_locale":"ja","notification_id":341159,"notification_type":"favourite","icon":"https://m1j.zzz.ac/accounts/avatars/000/008/939/original/112ed1e5343f2e7b.png","title":"tateisu⛏️@テスト鯖 :ct080:さんにお気に入りに登録されました","body":"クライアントアプリにAPIキーとシークレットを持たせるという考え方は、クライアント側でシークレットが如何に漏洩しやすいかを考えると悪手としかいいようがない。簡単に漏洩して、アプリと無関係なbotに流用/悪用される"}""",
|
||
it
|
||
)
|
||
}
|
||
}
|
||
|
||
// https://github.com/web-push-libs/ecec/blob/master/src/decrypt.c
|
||
|
||
@Test
|
||
fun testRfc8188() {
|
||
val body = "I1BsxtFttlv3u_Oo94xnmwAAEAAA-NAVub2qFgBEuQKRapoZu-IxkIva3MEB1PD-ly8Thjg"
|
||
.decodeBase64()
|
||
val inputKey = "yqdlZ-tYemfogSmv7Ws5PQ"
|
||
.decodeBase64()
|
||
|
||
Aes128GcmDecoder(body.byteRangeReader()).run {
|
||
AdbLog.i("recordSize=$recordSize, keyId.size=${keyId.size}")
|
||
|
||
assertEquals(
|
||
"salt",
|
||
"I1BsxtFttlv3u_Oo94xnmw",
|
||
salt.encodeBase64Url()
|
||
)
|
||
assertEquals(
|
||
"keyId",
|
||
"",
|
||
keyId.encodeBase64Url()
|
||
)
|
||
|
||
deriveKeyRfc8188(inputKey.toByteRange())
|
||
|
||
assertEquals(
|
||
"prk",
|
||
"zyeH5phsIsgUyd4oiSEIy35x-gIi4aM7y0hCF8mwn9g",
|
||
prk.encodeBase64Url()
|
||
)
|
||
assertEquals(
|
||
"key",
|
||
"_wniytB-ofscZDh4tbSjHw",
|
||
key.encodeBase64Url()
|
||
)
|
||
assertEquals(
|
||
"NONCE",
|
||
"Bcs8gkIRKLI8GeI8",
|
||
nonce.encodeBase64Url()
|
||
)
|
||
decode()
|
||
}.decodeUTF8().let {
|
||
assertEquals(
|
||
"text",
|
||
"I am the walrus",
|
||
it
|
||
)
|
||
}
|
||
}
|
||
|
||
@Test
|
||
fun testRfc8188b() {
|
||
val inputKey = "BO3ZVPxUlnLORbVGMpbT1Q"
|
||
.decodeBase64()
|
||
|
||
val rawBody =
|
||
"uNCkWiNYzKTnBN9ji3-qWAAAABkCYTHOG8chz_gnvgOqdGYovxyjuqRyJFjEDyoF1Fvkj6hQPdPHI51OEUKEpgz3SsLWIqS_uA"
|
||
.decodeBase64()
|
||
|
||
Aes128GcmDecoder(rawBody.byteRangeReader()).run {
|
||
AdbLog.i("recordSize=$recordSize, keyId.size=${keyId.size}, encryptedContent.size=${encryptedContent.size}")
|
||
deriveKeyRfc8188(inputKey.toByteRange())
|
||
decode()
|
||
}.decodeUTF8().let {
|
||
assertEquals(
|
||
"text",
|
||
"I am the walrus",
|
||
it
|
||
)
|
||
}
|
||
}
|
||
|
||
@Test
|
||
fun testMisskey13() {
|
||
val receiverPrivateBytes =
|
||
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgr18ZyNLsRg71vHNxBBGFxeIvV497YUaONPJL1EOKLGChRANCAAQUrMEJuCxzjD-S3xDlcgAvmnbKQZVQzkTCkOcKLSHgGopshtc3ETyrD7m7fhuZAlV1KXJVqHVBtMwrwvZ5xIyw"
|
||
.decodeBase64()
|
||
val receiverPublicBytes =
|
||
"BBSswQm4LHOMP5LfEOVyAC-adspBlVDORMKQ5wotIeAaimyG1zcRPKsPubt-G5kCVXUpclWodUG0zCvC9nnEjLA"
|
||
.decodeBase64()
|
||
// val senderPublicBytes =
|
||
// "BBtZLosjNMsuFAQg0QHYIgDNKG6IC_yxMYW1Tx_Cx20FbqEbAt_KN56zLc48yJxmOJhMkjR5Kf68bEIugNQ-wWU"
|
||
// .decodeBase64()
|
||
val authSecret = "x6yts9DKC4ZnnHdPHgiwkQ"
|
||
.decodeBase64()
|
||
|
||
// val headerJson = """{
|
||
// |"TTL":"2419200",
|
||
// |"Content-Encoding":"aes128gcm",
|
||
// |"Authorization":"vapid t=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL21hc3RvZG9uLW1zZy5qdWdnbGVyLmpwIiwiZXhwIjoxNjc0OTU4MTMyLCJzdWIiOiJodHRwczovL2RyZHIuY2x1YiJ9.I9xIycRJXiWYTZQlsTiS5RyceJNXGbrT8N-rac7Q5UouTWnZUmNbxEknOQigZV71qY3DJ_S7RomXJW3p1KwNmQ, k=BBtZLosjNMsuFAQg0QHYIgDNKG6IC_yxMYW1Tx_Cx20FbqEbAt_KN56zLc48yJxmOJhMkjR5Kf68bEIugNQ-wWU",
|
||
// |"<>cameFrom":"fcm"
|
||
// |}""".trimMargin()
|
||
val rawBody =
|
||
"0VUgniuATYrYU481rjSNBAAAEABBBK66EO_qZXsRw0Clzv0LF-SxMe5qRmCgKrZHApIc5ZX_XsXmzBg1rU827Hp9BSdXEWaIaBYAYROjTQfSuHtylP1H0_EOEMNdqswJpEVdVVQrfC_7JAxNAOlXUz4QU3oKl3yhSTlp6M4kFFaBfPrn2SyFI4f5w0wIRATH-Ck0shakAp5hKD7nVwvzEmh3wg8Vug8gPRmGhOPet6DzG5rhLtPng5OJ2eFQUB00duVZyW8TgZ8iPd0GYZkaHw2b16OBN3doLPKiYN_OL-JKVmWutMzivG52DDQ_XqbaFfu5dRLWHkyipq078-nDL6yw9_tNQCdDcVGKx1llxHI6FuPbYgF_RJ8PNZ1Sj-sMRdGtQqcsFH0WDWJu_JBqHSYvBxjEhhDv4qepQTYDl3MYVI-EIbQLIWuCZRo4FRkd594endP6IQ2LH950EE4AMeVHmdxENGELJidrF4eE8reHWf5We0_Jj1m92L8ZJ--z_FMHdOEGlCHwFp1IQPNS-SF6CPZVdrzxjLMo12CELNjh1g9bKxA2ld7OBYcVb3nt6uWBBgiJEqSXEVjzv2JhoHMh3lHLyFngoddfL55aDEVe4gTOwhflPSiKO-8nQd87KLQ_vi4r-SItRzQXhXcI0v7ryFubBwWQrRmRaIaDrMgheEJ50caWgeaDoTXtgkorG63q6KIfkpBlyDyq3YaKpXHJ7rmr-1BYP0Z8PS2O5bqRFyLgtLXIn5a3da_XlTnjdHJH7_N0c8WxVRow5Z5Qzlj790FuGrMQQ8c6SrAC7lTmt6_wq-Ej3SH53jN0HumZ7v4pmkqCR5o7SJYHAgMx9KUeW-Rwr9k3XKVS9MMP9wPhke0Iy51LOfDZ_U6tHVQ_QEkEb1lk1eNSNo0ZSna1vhXh5WB8Pnm3Skxa1k4qvQFKjdNFOZZ57lXqG42oEmplZirhiBgvf40sWmFs_s6_u4tTkD1jagHlrus8l7U5T-34RpzOeqM3z5wqjylBJyubwfEA_7C3WWYOw5U0bduP7QLgFGGzVmu3qAAcQgfHOOehgWXc95j50jXjfV9XD3znXX3WLY0R4qdkWcivQMSxL3PATh1a9fgZJs-SRvH7fW5SrEK3Cj9wAXLfOOcL9gP519eW4sPN-P3xupAfgxXblolBvx1wmdtL51ey1X02cOK_EyFS0pl5D_1vaL6ncq6RDfP2emD-U4jSBEOJbWpzINr9xUnRBLxGYbRZfSSqjdFZGO49JKOW_h9vtZT-NAuPaar0v0Qfz_qF5_-pyo8dQpfNGxZ1ZTzlMwy4v6l18RRAZx2smbgNVmSe2uUCDhcFhO_Y9a9wlbbhYUesenR0g-fbUvVGSdz3V2hoTanQJLwOSbigIFmgMTUnOGPs4uv4ynXleDR6h6zoZ5WwuR8i06AjYq9M5E3nlmxoGKebX_6r1p0Fg0R8QRiz9UmRtxn-G72eLEy5351ZA4xEZrvm4eYoyS7t3BRta0R_4qKFdNONv46ISIj3TU3sUNo2ktIbZE8VjDomxqfgLugbx1Oyvipa9xTFyaJBzhSxxwfdzLlfxdLdrGoh2OseBY56_WR99yh5x6q2UQDo6lyqjzIa8nY_EptfqQmzgCvUJYGmRqXi6Cp5bEyuUvxZ_W-paJoMT5ZW_IjVrSkqYm-oxOkGzKsaAjLmA0e9Qek7uZlLZ9KPl56ON7AHqli0qHi5ZaoosIMVMd6GN0DUfiFHpimGmHw63W0xwArEr0sWf3Wqclk0b3BS7o4oRPMjtjersoFtqxaJ8xJpV4c3UwNjXz9eBQFtJZ_NR1jv_4Kuet3d78RojWEo4OGP9Y7VbPc77yekqm1f1Fxi68w-aZFgL6ClDsy1eAH5HQmYa5KzylTYOt3ZQDXGARiFXR3N0isKPsBBcXZkIzcrKEccfBhYxm2ru3ruNnwMZ4XKzmq5SLRfhxvsCspPaiW3N7oKEbX86ONoAL_i6kcpxAn8iY_XZ36ZyOD_cDkG0OesIdbc4hRcXaLSWmvknXy1KUCbwbe9pMx7HiYn5u_8vgtSELlWQdagc2T3SiD8-gqXTp3RmwwAJSizme2XL9RcjkZOtQnItpNbCwON5afYM85OEKFQkatnt6d8rm2WhoZZ8lU03zD9Kt2Tn9vbkhDYcy_ZE0RHERQLtl5Q4GT81J8RpOEjeJTt2egGFI0PC_tx5MNl_Piy6ows7ly24TsEjNoDJQpnowk_S4OLbN2fhpr1-yhfhbQ-j447FcYkEACjW5t74X4ey8iT88rVMmgefDx7o7NYWW7aKd6vfPpe7EmYiERxCDPYhzQhuKk8vm3FoWM2C6cuuzq1ElUraA70SM9smuwOBJFqgs7ZosIjhb7BdrMiy8lILejHKkTyl-lbhKNzGvO4EIgeHZRn8vzdDmwIINQskT3KgPMpXw"
|
||
.decodeBase64()
|
||
|
||
Aes128GcmDecoder(rawBody.byteRangeReader()).run {
|
||
deriveKeyWebPush(
|
||
// receiver private key in X509 format
|
||
receiverPrivateBytes,
|
||
// receiver public key in 65bytes X9.62 uncompressed format
|
||
receiverPublicBytes,
|
||
// auth secrets created at subscription
|
||
authSecret,
|
||
)
|
||
decode()
|
||
}.decodeUTF8().let {
|
||
assertEquals(
|
||
"text",
|
||
"""{"type":"notification","body":{"id":"9ajlf15org","createdAt":"2023-01-28T14:08:50.844Z","type":"reaction","isRead":false,"userId":"8fhziu1e3v","user":{"id":"8fhziu1e3v","name":"tateisu⛏️@テスト鯖 :ct080:","username":"tateisu","host":"mastodon2.juggler.jp","avatarUrl":"https://m2j.zzz.ac/accounts/avatars/000/000/001/original/e75c1f2674f44551.png","avatarBlurhash":"yDHUW|V]02t30otQ_H?XoeD,WBt7jbt2tMocN2Rn%Fk9nTE1kB%2e:X3bFRVR:ayWBj@%0fkRR?Ej[E3axt7j]ob9Oay%Fj[xajGIW","isBot":true,"isCat":false,"instance":{"name":"ジャグ鯖(テスト用)","softwareName":"mastodon","softwareVersion":"3.5.3","iconUrl":"https://mastodon2.juggler.jp/packs/media/icons/android-chrome-36x36-d64a2e909b4574fb02214762c513032d.png","faviconUrl":"https://mastodon2.juggler.jp/packs/media/icons/favicon-48x48-28b6709639acd18287a854f974311336.png","themeColor":"#6364ff"},"emojis":{"ctf00":"https://m2j.zzz.ac/custom_emojis/images/000/007/937/original/ctf00.png","ct080":"https://m2j.zzz.ac/custom_emojis/images/000/004/225/original/ct080.png","ctff0":"https://m2j.zzz.ac/custom_emojis/images/000/008/177/original/ctff0.png"},"onlineStatus":"unknown"},"note":{"id":"99tzth0zfn","createdAt":"2023-01-10T16:09:58.643Z","userId":"99tcttijnu","text":":thinking_woozy_portal_orange::thinking_garbled:","visibility":"public","localOnly":false,"renoteCount":0,"repliesCount":2,"reactions":{"👍":1,":revbunhdthinking@.:":1,":ainers_misskeyio@misskey.io:":1},"reactionEmojis":{"ainers_misskeyio@misskey.io":"https://s3.arkjp.net/misskey/ea176d9b-a92a-4ec2-b11c-defdac551156.apng"},"fileIds":[],"files":[],"replyId":null,"renoteId":null,"myReaction":":revbunhdthinking@.:"},"reaction":"👍"},"userId":"99tcttijnu","dateTime":1674914932947}""",
|
||
it,
|
||
)
|
||
}
|
||
}
|
||
}
|