go/seal and its TypeScript twin @novox/mesh-sdk/seal are the ADR 0206 box the licence manager kept to itself. Fixtures made by the licence manager's own seal.go prove both byte for byte, in both directions. (Replaces the placeholder README the branch's first commit carried by mistake.) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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")
|
|
}
|
|
}
|