mesh/merge-gate head 1bc5719b8b85 merged into trunk a2d2af00dc5d beside mesh-host#116@bff1ac14 — pass, with a note: builds mesh-sdk (+40 dependent(s)) → ac…
mesh/repo-check head 1bc5719b8b85 merged into trunk a2d2af00dc5d beside mesh-host#116@bff1ac14 — pass: its merge-check.sh passed, each part in its toolchai…
mesh/delivery delivered
mesh/delivery-group group release/57-config-and-provisioning delivered: every member is delivered
157 lines
4.5 KiB
Go
157 lines
4.5 KiB
Go
package seal
|
|
|
|
import (
|
|
"crypto/ecdh"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"os"
|
|
"testing"
|
|
)
|
|
|
|
type fixture struct {
|
|
Recipient KeyPair `json:"recipient"`
|
|
Sealed []struct {
|
|
Plaintext string `json:"plaintext"`
|
|
Box Box `json:"box"`
|
|
} `json:"sealed"`
|
|
Vectors []struct {
|
|
Plaintext string `json:"plaintext"`
|
|
EphPrivate string `json:"ephPrivate"`
|
|
IV string `json:"iv"`
|
|
Box Box `json:"box"`
|
|
} `json:"vectors"`
|
|
}
|
|
|
|
func readFixture(t *testing.T) fixture {
|
|
t.Helper()
|
|
raw, err := os.ReadFile("testdata/sealed-by-licence-manager.json")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var f fixture
|
|
if err := json.Unmarshal(raw, &f); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(f.Sealed) == 0 || len(f.Vectors) == 0 {
|
|
t.Fatal("the fixture holds no boxes")
|
|
}
|
|
return f
|
|
}
|
|
|
|
// Every box the licence manager's own Seal made opens here to its plaintext.
|
|
func TestABoxTheLicenceManagerSealedOpens(t *testing.T) {
|
|
f := readFixture(t)
|
|
for _, s := range f.Sealed {
|
|
got, err := Open(s.Box, f.Recipient.PrivateKey)
|
|
if err != nil || got != s.Plaintext {
|
|
t.Fatalf("opened %q, %v; want %q", got, err, s.Plaintext)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Given the same one-time key and iv, this package seals byte for byte what the licence manager sealed:
|
|
// the reverse direction, so whatever opens the licence manager's boxes opens these.
|
|
func TestSealIsByteForByteTheLicenceManagersBox(t *testing.T) {
|
|
f := readFixture(t)
|
|
for _, v := range f.Vectors {
|
|
d := base64.StdEncoding.DecodeString
|
|
ephRaw, err1 := d(v.EphPrivate)
|
|
iv, err2 := d(v.IV)
|
|
if err1 != nil || err2 != nil {
|
|
t.Fatal(err1, err2)
|
|
}
|
|
eph, err := ecdh.X25519().NewPrivateKey(ephRaw)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := sealWith(v.Plaintext, f.Recipient.PublicKey, eph, iv)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got != v.Box {
|
|
t.Fatalf("sealed %+v\nthe licence manager sealed %+v", got, v.Box)
|
|
}
|
|
if plain, err := Open(got, f.Recipient.PrivateKey); err != nil || plain != v.Plaintext {
|
|
t.Fatalf("reopened %q, %v", plain, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// A box the agent module's TypeScript sealed (the fixture the licence manager keeps) opens here too.
|
|
func TestABoxSealedInTypeScriptOpens(t *testing.T) {
|
|
raw, err := os.ReadFile("testdata/sealed-by-typescript.json")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var f struct {
|
|
PrivateKey string `json:"privateKey"`
|
|
Box Box `json:"box"`
|
|
Plaintext string `json:"plaintext"`
|
|
}
|
|
if err := json.Unmarshal(raw, &f); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got, err := Open(f.Box, f.PrivateKey); err != nil || got != f.Plaintext {
|
|
t.Fatalf("opened %q, %v", got, err)
|
|
}
|
|
}
|
|
|
|
// What Seal makes opens with its recipient's key and no other, and a fresh box differs every time.
|
|
func TestABoxOpensOnlyForItsRecipient(t *testing.T) {
|
|
a, err := GenerateKeyPair()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b, _ := GenerateKeyPair()
|
|
box, err := Seal("a token", a.PublicKey)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got, err := Open(box, a.PrivateKey); err != nil || got != "a token" {
|
|
t.Fatalf("opened %q, %v", got, err)
|
|
}
|
|
if _, err := Open(box, b.PrivateKey); err == nil {
|
|
t.Fatal("a box opened for another key")
|
|
}
|
|
again, _ := Seal("a token", a.PublicKey)
|
|
if again.Eph == box.Eph || again.IV == box.IV {
|
|
t.Fatal("two boxes share a one-time key or iv")
|
|
}
|
|
}
|
|
|
|
// A box changed in any field, or of another version, does not open.
|
|
func TestATamperedBoxDoesNotOpen(t *testing.T) {
|
|
f := readFixture(t)
|
|
good := f.Sealed[1].Box
|
|
flip := func(s string) string {
|
|
b, _ := base64.StdEncoding.DecodeString(s)
|
|
b[len(b)-1] ^= 1
|
|
return base64.StdEncoding.EncodeToString(b)
|
|
}
|
|
for name, box := range map[string]Box{
|
|
"ct": {V: 1, Eph: good.Eph, IV: good.IV, Tag: good.Tag, Ct: flip(good.Ct)},
|
|
"tag": {V: 1, Eph: good.Eph, IV: good.IV, Tag: flip(good.Tag), Ct: good.Ct},
|
|
"iv": {V: 1, Eph: good.Eph, IV: flip(good.IV), Tag: good.Tag, Ct: good.Ct},
|
|
"eph": {V: 1, Eph: flip(good.Eph), IV: good.IV, Tag: good.Tag, Ct: good.Ct},
|
|
"version": {V: 2, Eph: good.Eph, IV: good.IV, Tag: good.Tag, Ct: good.Ct},
|
|
} {
|
|
if _, err := Open(box, f.Recipient.PrivateKey); err == nil {
|
|
t.Fatalf("a box with a changed %s opened", name)
|
|
}
|
|
}
|
|
}
|
|
|
|
// A key that is not X25519 PEM is refused, not misread.
|
|
func TestAKeyOfAnotherKindIsRefused(t *testing.T) {
|
|
if _, err := Seal("x", "not a key"); err == nil {
|
|
t.Fatal("sealed to text")
|
|
}
|
|
f := readFixture(t)
|
|
if _, err := Open(f.Sealed[0].Box, f.Recipient.PublicKey); err == nil {
|
|
t.Fatal("opened with a public key")
|
|
}
|
|
if _, err := Seal("x", f.Recipient.PrivateKey); err == nil {
|
|
t.Fatal("sealed to a private key")
|
|
}
|
|
}
|