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"))
}
}
|