tests: add signature verification tests for narinfo, mesh, and config

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I06ef2f37f174d278ce4f727836f339dd6a6a6964
This commit is contained in:
raf 2026-03-06 18:34:04 +03:00
commit de100ee611
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
3 changed files with 246 additions and 10 deletions

View file

@ -1,6 +1,9 @@
package narinfo_test
import (
"crypto/ed25519"
"crypto/rand"
"encoding/base64"
"strings"
"testing"
@ -113,7 +116,6 @@ func TestParseMalformedLine(t *testing.T) {
}
func TestParseNarSizeOverflow(t *testing.T) {
// uint64 max: 18446744073709551615 — verify it parses correctly
input := "StorePath: /nix/store/abc-test\nNarSize: 18446744073709551615\n"
ni, err := narinfo.Parse(strings.NewReader(input))
if err != nil {
@ -164,3 +166,153 @@ func TestParseInvalidFileSize(t *testing.T) {
t.Error("expected error for invalid FileSize")
}
}
// Fingerprint and signature verification
func TestFingerprint(t *testing.T) {
ni := &narinfo.NarInfo{
StorePath: "/nix/store/s66mzxpvicwklp6cpph4dc53k5l6bfhe-hello-2.12.1",
NarHash: "sha256:04rrn5x6lnzrfkcy3bh7gf7x6hq3w1kap4wdss2n6n4s19pgbkr7",
NarSize: 226512,
References: []string{"s66mzxpvicwklp6cpph4dc53k5l6bfhe-hello-2.12.1"},
}
fp := ni.Fingerprint()
want := "1;/nix/store/s66mzxpvicwklp6cpph4dc53k5l6bfhe-hello-2.12.1;" +
"sha256:04rrn5x6lnzrfkcy3bh7gf7x6hq3w1kap4wdss2n6n4s19pgbkr7;226512;" +
"/nix/store/s66mzxpvicwklp6cpph4dc53k5l6bfhe-hello-2.12.1"
if fp != want {
t.Errorf("Fingerprint() =\n%q\nwant\n%q", fp, want)
}
}
func TestFingerprintNoRefs(t *testing.T) {
ni := &narinfo.NarInfo{
StorePath: "/nix/store/abc-test",
NarHash: "sha256:abc",
NarSize: 1234,
}
fp := ni.Fingerprint()
if !strings.HasSuffix(fp, ";") {
t.Errorf("Fingerprint with no refs should end with ';', got: %q", fp)
}
}
func TestFingerprintRefsAlreadyPrefixed(t *testing.T) {
ni := &narinfo.NarInfo{
StorePath: "/nix/store/abc-test",
NarHash: "sha256:abc",
NarSize: 1234,
References: []string{"/nix/store/dep-pkg"}, // already prefixed
}
fp := ni.Fingerprint()
if strings.Contains(fp, "/nix/store//nix/store/") {
t.Errorf("Fingerprint double-prefixed refs: %q", fp)
}
}
func TestParsePublicKeyValid(t *testing.T) {
name, key, err := narinfo.ParsePublicKey("cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=")
if err != nil {
t.Fatalf("ParsePublicKey: %v", err)
}
if name != "cache.nixos.org-1" {
t.Errorf("name = %q", name)
}
if len(key) != ed25519.PublicKeySize {
t.Errorf("key len = %d, want %d", len(key), ed25519.PublicKeySize)
}
}
func TestParsePublicKeyMissingColon(t *testing.T) {
_, _, err := narinfo.ParsePublicKey("no-colon-here")
if err == nil {
t.Error("expected error for missing ':'")
}
}
func TestParsePublicKeyBadBase64(t *testing.T) {
_, _, err := narinfo.ParsePublicKey("name:!!!not-base64!!!")
if err == nil {
t.Error("expected error for invalid base64")
}
}
func TestParsePublicKeyWrongSize(t *testing.T) {
// 16 bytes encoded in base64 = 24 chars with padding
b16 := base64.StdEncoding.EncodeToString(make([]byte, 16))
_, _, err := narinfo.ParsePublicKey("name:" + b16)
if err == nil {
t.Error("expected error for wrong key size (16 bytes, not 32)")
}
}
// Generates a fresh ed25519 key, signs a narinfo fingerprint,
// embeds the signature, and verifies it. This covers the full sign/verify path.
func TestVerifyRoundtrip(t *testing.T) {
pub, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
ni := &narinfo.NarInfo{
StorePath: "/nix/store/abc123-test-pkg",
NarHash: "sha256:abcdef123456",
NarSize: 98765,
References: []string{"abc123-test-pkg"},
}
fp := ni.Fingerprint()
sig := ed25519.Sign(priv, []byte(fp))
pubKeyStr := "test-key-1:" + base64.StdEncoding.EncodeToString(pub)
ni.Sig = []string{"test-key-1:" + base64.StdEncoding.EncodeToString(sig)}
ok, err := ni.Verify(pubKeyStr)
if err != nil {
t.Fatalf("Verify error: %v", err)
}
if !ok {
t.Error("Verify returned false for valid signature")
}
}
func TestVerifyWrongKey(t *testing.T) {
_, priv, _ := ed25519.GenerateKey(rand.Reader)
wrongPub, _, _ := ed25519.GenerateKey(rand.Reader) // different key
ni := &narinfo.NarInfo{
StorePath: "/nix/store/abc123-test-pkg",
NarHash: "sha256:abcdef",
NarSize: 1234,
}
fp := ni.Fingerprint()
sig := ed25519.Sign(priv, []byte(fp))
// Register wrong public key but correct key name
wrongKeyStr := "test-key-1:" + base64.StdEncoding.EncodeToString(wrongPub)
ni.Sig = []string{"test-key-1:" + base64.StdEncoding.EncodeToString(sig)}
ok, err := ni.Verify(wrongKeyStr)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if ok {
t.Error("Verify should return false for mismatched key")
}
}
func TestVerifyNoMatchingKeyName(t *testing.T) {
pub, _, _ := ed25519.GenerateKey(rand.Reader)
ni := &narinfo.NarInfo{
StorePath: "/nix/store/abc123-test-pkg",
NarHash: "sha256:abcdef",
NarSize: 1234,
}
ni.Sig = []string{"other-key-1:invalidsig="}
pubKeyStr := "my-key-1:" + base64.StdEncoding.EncodeToString(pub)
ok, err := ni.Verify(pubKeyStr)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if ok {
t.Error("Verify should return false when no Sig line matches key name")
}
}