aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-android/app/src/test/kotlin/org/meshbay/client/KeyChannelsTest.kt
blob: c4333db317aa6e7925610bfe279f7fbd01b13a79 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package org.meshbay.client

import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters
import org.bouncycastle.crypto.signers.Ed25519Signer
import org.json.JSONArray
import org.json.JSONObject
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Test
import org.meshbay.client.bridge.KeyChannels
import org.meshbay.client.bridge.Refused
import org.meshbay.client.keys.Kdf

class KeyChannelsTest {
    private val user = "0f1e2d3c-4b5a-4968-8776-a5b4c3d2e1f0"
    private val node = Kdf.b64(ByteArray(32) { 0x11 })
    private var asked = 0
    private var answer = false
    private val secrets = FakeSecrets()
    private val keys = KeyChannels(secrets, confirm = { asked++; answer }, declined = { "Cancelled" })

    private fun call(ch: String, vararg args: Any?) = keys.call(ch, JSONArray(args.toList()))

    @Test fun `ids and node keys are checked before anything is done`() {
        for (bad in listOf("", "../x", "not-an-id", "0f1e2d3c-4b5a-4968-8776-a5b4c3d2e1f0x", null)) {
            assertThrows("$bad", Refused::class.java) { call("keys:fingerprint", bad) }
        }
        for (bad in listOf("", "a/b c", "x".repeat(101), null)) {
            assertThrows("$bad", Refused::class.java) { call("keys:identity", user, bad) }
        }
    }

    @Test fun `the device key signs the bytes the hub verifies and never leaves`() {
        val pub = call("device:ensure") as String
        assertEquals(pub, call("device:ensure"))           // once, then the same key
        val s = call("device:sign", "alice") as JSONObject
        val msg = "meshbay:user_auth:alice:${s.getLong("timestamp")}".toByteArray()
        val v = Ed25519Signer().apply { init(false, Ed25519PublicKeyParameters(Kdf.unb64(pub), 0)); update(msg, 0, msg.size) }
        assertTrue(v.verifySignature(Kdf.unb64(s.getString("signature"))))
        call("device:forget")
        assertNull(call("device:public"))
    }

    @Test fun `browser access is widened only by the person, natively`() {
        call("keys:created-here", user)
        assertEquals(false, call("keys:browser-access", user))
        answer = false
        val e = assertThrows(Refused::class.java) { call("keys:set-browser-access", user, true) }
        assertEquals("Cancelled", e.message)
        assertEquals(1, asked)
        assertEquals(false, call("keys:browser-access", user))
        answer = true
        assertEquals(true, call("keys:set-browser-access", user, true))
        // Narrowing asks nobody.
        assertEquals(false, call("keys:set-browser-access", user, false))
        assertEquals(2, asked)
    }

    @Test fun `without OS key storage nothing is minted or derived`() {
        val none = KeyChannels(FakeSecrets("unavailable"), confirm = { true }, declined = { "" })
        assertEquals(false, none.call("keys:available", JSONArray()))
        assertThrows(Refused::class.java) { none.call("keys:mint", JSONArray(listOf(user, node))) }
        assertEquals(false, none.call("keys:has-session", JSONArray(listOf(user))))
    }

    @Test fun `a minted identity answers with public keys only`() {
        val r = call("keys:mint", user, node) as JSONObject
        assertEquals(setOf("pkEdB64", "pkXB64"), r.keys().asSequence().toSet())
        val id = call("keys:identity", user, node) as JSONObject
        assertEquals(r.getString("pkEdB64"), id.getString("pkEdB64"))
        assertEquals(JSONObject.NULL, id.get("sealedWith"))
    }

    @Test fun `channels outside the list are refused`() {
        assertThrows(Refused::class.java) { call("keys:export") }
        assertFalse(keys.handles("hub:fetch"))
    }
}