Merge branch 'test/label-lattice-interop' into 'main'
Expose the security lattice to the interop driver (relay#49) See merge request paulhitt/flamenet-messenger-android!67
This commit was merged in pull request #94.
This commit is contained in:
@@ -12,6 +12,7 @@ import io.flamenet.e2e.PeerBundle
|
||||
import io.flamenet.e2e.Ed25519
|
||||
import io.flamenet.e2e.Hkdf
|
||||
import io.flamenet.e2e.KeyLog
|
||||
import io.flamenet.e2e.Label
|
||||
import io.flamenet.e2e.SafetyNumber
|
||||
import io.flamenet.e2e.SealedSender
|
||||
import io.flamenet.e2e.X25519
|
||||
@@ -124,6 +125,8 @@ private fun handle(req: JSONObject): JSONObject = when (req.getString("cmd")) {
|
||||
req.bytes("key"), req.bytes("box"), req.bytes("aad")), Charsets.UTF_8))
|
||||
"seal" -> sealSender(req)
|
||||
"open_sealed" -> openSealed(req)
|
||||
"label_encode" -> labelEncode(req)
|
||||
"label_dominates" -> labelDominates(req)
|
||||
"content_encode" -> contentEncode(req)
|
||||
"content_parse" -> contentParse(req)
|
||||
"verify" -> JSONObject().put("ok", Ed25519.verify(
|
||||
@@ -595,3 +598,41 @@ private fun contentParse(req: JSONObject): JSONObject {
|
||||
out.put("control", parsed.isControl)
|
||||
return out
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// The security lattice — §1 of the Files specification, and the core of
|
||||
// relay#49's "labels and security levels are to be kept".
|
||||
//
|
||||
// 🔴 **The ten-byte encoding is authenticated data for every construction that
|
||||
// uses a label.** An encoding that differs between platforms does not warn: it
|
||||
// produces a decryption failure, or — worse — plaintext that quietly belongs to
|
||||
// the wrong sensitivity. Both engines have implemented this independently since
|
||||
// the Files apps shipped, and nothing has ever compared them.
|
||||
//
|
||||
// ⚠️ Each side's own unit tests pin their vectors, which is necessary and not
|
||||
// sufficient. Two engines that each pass their own vectors can still disagree,
|
||||
// and the disagreement here is silent by construction.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
private fun labelFrom(json: JSONObject?): Label {
|
||||
val o = json ?: JSONObject()
|
||||
val bits = o.optJSONArray("compartments")?.let { a ->
|
||||
(0 until a.length()).map { a.getInt(it) }
|
||||
} ?: emptyList()
|
||||
return Label(o.optInt("classification", 0).toShort(), bits.toSet())
|
||||
}
|
||||
|
||||
private fun labelEncode(req: JSONObject): JSONObject {
|
||||
val label = labelFrom(req)
|
||||
return JSONObject()
|
||||
.put("hex", label.bytes().joinToString("") { "%02x".format(it) })
|
||||
// ⚠️ As a STRING. Bit 63 makes the mask a negative Long, and a JSON
|
||||
// number would arrive on the other side as a double and lose the low
|
||||
// bits — turning the one compartment most likely to be mis-shifted
|
||||
// into the one this check cannot see.
|
||||
.put("mask", label.mask.toULong().toString())
|
||||
}
|
||||
|
||||
private fun labelDominates(req: JSONObject): JSONObject =
|
||||
JSONObject().put("dominates",
|
||||
labelFrom(req.optJSONObject("a")).dominates(labelFrom(req.optJSONObject("b"))))
|
||||
|
||||
Reference in New Issue
Block a user