package authorkey_test import ( "bytes" "fmt" "strings" "testing" "g.activething.com/go/DateKeys/authorkey" "g.activething.com/go/DateKeys/codec/bech32" "g.activething.com/go/DateKeys/internal/ed25519strict" ) func TestStrings(t *testing.T) { seed := bytes.Repeat([]byte{7}, 32) k, err := authorkey.NewFromSeed(seed) if err != nil { t.Fatal(err) } pub, err := authorkey.PublicString(k.Public()) if err != nil { t.Fatal(err) } secret := k.Secret() if len(pub) != authorkey.PublicLength || !strings.HasPrefix(pub, "dkauthor1") { t.Errorf("public %q", pub) } if len(secret) != authorkey.SecretLength || !strings.HasPrefix(secret, "DKAUTHOR-SECRET-KEY-1") { t.Errorf("secret %q", secret) } back, err := authorkey.ParseSecret(secret) if err != nil || !bytes.Equal(back.Public(), k.Public()) { t.Fatalf("ParseSecret: %v", err) } a, err := authorkey.ParsePublic(pub) if err != nil || !bytes.Equal(a, k.Public()) { t.Fatalf("ParsePublic: %v", err) } msg := []byte("datekeys:dkc3:author-signature:v1") if !ed25519strict.Verify(a, msg, k.Sign(msg)) { t.Error("the signature does not verify") } // Printing a key, or an options struct that holds one, never shows the // secret. for _, s := range []string{fmt.Sprint(k), fmt.Sprintf("%v %+v %#v %s", k, k, k, k), fmt.Sprintf("%+v", struct{ K *authorkey.Key }{k})} { if strings.Contains(s, secret[len("DKAUTHOR-SECRET-KEY-1"):]) { t.Fatalf("the secret key is printed: %s", s) } } k.Clear() if !bytes.Equal(k.Public(), make([]byte, 32)) { t.Error("Clear leaves the key") } } func TestParseRejects(t *testing.T) { k, _ := authorkey.Generate() pub, _ := authorkey.PublicString(k.Public()) secret := k.Secret() short, _ := bech32.Encode("dkauthor", make([]byte, 31)) other, _ := bech32.Encode("dkauthoz", k.Public()) last := "q" if pub[66] == 'q' { last = "p" } identity := make([]byte, 32) identity[0] = 1 small, _ := authorkey.PublicString(identity) // y = 2 is canonical, and (y² − 1)/(d·y² + 1) is not a square: no point. two := make([]byte, 32) two[0] = 2 offCurve, _ := authorkey.PublicString(two) for _, c := range []struct{ s, want string }{ {strings.ToUpper(pub), "lower case"}, {pub[:66] + last, "checksum"}, {short, "characters"}, {other, "is not a public key"}, {small, "small order"}, {offCurve, "not a point of the curve"}, } { if _, err := authorkey.ParsePublic(c.s); err == nil || !strings.Contains(err.Error(), c.want) { t.Errorf("ParsePublic(%q) = %v, want %q", c.s, err, c.want) } } for _, c := range []struct{ s, want string }{ {strings.ToLower(secret), "upper case"}, {secret[:78], "characters"}, {"DKAUTHOR-SECRET-KEY-1" + strings.Repeat("Q", 58), "checksum"}, } { if _, err := authorkey.ParseSecret(c.s); err == nil || !strings.Contains(err.Error(), c.want) { t.Errorf("ParseSecret(%q) = %v, want %q", c.s, err, c.want) } } if _, err := authorkey.PublicString(make([]byte, 31)); err == nil { t.Error("PublicString takes 31 bytes") } if _, err := authorkey.NewFromSeed(make([]byte, 31)); err == nil { t.Error("NewFromSeed takes 31 bytes") } } func TestFiles(t *testing.T) { k, _ := authorkey.Generate() var enc bytes.Buffer if err := authorkey.Encrypt(&enc, k, "contraseña larga"); err != nil { t.Fatal(err) } if !bytes.HasPrefix(enc.Bytes(), []byte("age-encryption.org/v1\n-> scrypt ")) || !bytes.Contains(enc.Bytes(), []byte(" 16\n")) { t.Errorf("not age scrypt with a work factor of 16: %q", enc.Bytes()[:60]) } got, err := authorkey.Read(bytes.NewReader(enc.Bytes()), "contraseña larga") if err != nil || !bytes.Equal(got.Public(), k.Public()) { t.Fatalf("Read: %v", err) } if _, err := authorkey.Read(bytes.NewReader(enc.Bytes()), "otra"); err == nil { t.Error("another passphrase opens the file") } if _, err := authorkey.Read(bytes.NewReader(enc.Bytes()), ""); err == nil || !strings.Contains(err.Error(), "needs its passphrase") { t.Errorf("no passphrase: %v", err) } if err := authorkey.Encrypt(&enc, k, ""); err == nil { t.Error("Encrypt takes an empty passphrase") } plain := authorkey.Marshal(k) if got, err := authorkey.Read(bytes.NewReader(plain), ""); err != nil || !bytes.Equal(got.Public(), k.Public()) { t.Fatalf("Read of a plain file: %v", err) } for _, c := range []struct{ file, want string }{ {"# nothing\n\n", "no secret key"}, {string(plain) + k.String() + "\n", "one secret key"}, {"age1notakey\n", "characters"}, {strings.Repeat("#", 64<<10+1), "more than"}, } { if _, err := authorkey.Read(strings.NewReader(c.file), ""); err == nil || !strings.Contains(err.Error(), c.want) { t.Errorf("Read(%.20q) = %v, want %q", c.file, err, c.want) } } }