diff --git a/lns.go b/lns.go index a525dec..07c3550 100644 --- a/lns.go +++ b/lns.go @@ -63,6 +63,17 @@ func (s *Service) Resolve(name any) (primitives.Hash, error) { } } +// Hash returns the canonical hash for a validated .lthn name. +// +// Hash is the direct service-level alias for Resolve. +// +// c := core.New(core.WithService(lns.Register)) +// svc := c.Service("lns").(*lns.Service) +// hash, err := svc.Hash("example.lthn") +func (s *Service) Hash(name any) (primitives.Hash, error) { + return s.Resolve(name) +} + // Verify reports whether a .lthn name is valid after canonicalisation. // // This keeps the service-level API aligned with the DNS resolver helpers. @@ -81,6 +92,20 @@ func (s *Service) Verify(name any) bool { } } +// HashString returns the canonical hash for a .lthn name supplied as a string. +// +// ok := svc.HashString("example.lthn") +func (s *Service) HashString(name string) (primitives.Hash, error) { + return s.ResolveString(name) +} + +// HashBinary returns the canonical hash for a .lthn name supplied as bytes. +// +// ok := svc.HashBinary([]byte("example.lthn")) +func (s *Service) HashBinary(name []byte) (primitives.Hash, error) { + return s.ResolveBinary(name) +} + // ResolveString returns the canonical hash for a .lthn name supplied as a string. func (s *Service) ResolveString(name string) (primitives.Hash, error) { normalized, ok := canonicalizeName(name) @@ -110,6 +135,15 @@ func (s *Service) ResolveName(name any) (primitives.Hash, error) { return s.Resolve(name) } +// HashName is an alias for Hash. +// +// c := core.New(core.WithService(lns.Register)) +// svc := c.Service("lns").(*lns.Service) +// hash, err := svc.HashName("example.lthn") +func (s *Service) HashName(name any) (primitives.Hash, error) { + return s.Hash(name) +} + // VerifyName is an alias for Verify. // // c := core.New(core.WithService(lns.Register)) @@ -128,6 +162,15 @@ func (s *Service) ResolveByName(name any) (primitives.Hash, error) { return s.ResolveName(name) } +// HashByName is an alias for HashName. +// +// c := core.New(core.WithService(lns.Register)) +// svc := c.Service("lns").(*lns.Service) +// hash, err := svc.HashByName("example.lthn") +func (s *Service) HashByName(name any) (primitives.Hash, error) { + return s.HashName(name) +} + // VerifyByName is an alias for VerifyName. // // c := core.New(core.WithService(lns.Register)) diff --git a/lns_test.go b/lns_test.go index e41a36e..d4393fc 100644 --- a/lns_test.go +++ b/lns_test.go @@ -150,6 +150,60 @@ func TestServiceResolveNameAliases(t *testing.T) { } } +func TestServiceHashAliases(t *testing.T) { + svc := &Service{} + + got, err := svc.Hash("Foo-Bar.lthn") + if err != nil { + t.Fatalf("Hash returned error: %v", err) + } + + want := primitives.Hash(sha3.Sum256([]byte("foo-bar"))) + if got != want { + t.Fatalf("Hash returned %x, want %x", got, want) + } + + got, err = svc.HashString("Foo-Bar.lthn") + if err != nil { + t.Fatalf("HashString returned error: %v", err) + } + + if got != want { + t.Fatalf("HashString returned %x, want %x", got, want) + } + + got, err = svc.HashBinary([]byte("Foo-Bar.lthn")) + if err != nil { + t.Fatalf("HashBinary returned error: %v", err) + } + + if got != want { + t.Fatalf("HashBinary returned %x, want %x", got, want) + } + + got, err = svc.HashName("Foo-Bar.lthn") + if err != nil { + t.Fatalf("HashName returned error: %v", err) + } + + if got != want { + t.Fatalf("HashName returned %x, want %x", got, want) + } + + got, err = svc.HashByName([]byte("Foo-Bar.lthn")) + if err != nil { + t.Fatalf("HashByName returned error: %v", err) + } + + if got != want { + t.Fatalf("HashByName returned %x, want %x", got, want) + } + + if _, err := svc.Hash(123); err == nil { + t.Fatal("Hash should reject unsupported input types") + } +} + func TestServiceResolveByNameAliases(t *testing.T) { svc := &Service{} diff --git a/pkg/dns/resolve.go b/pkg/dns/resolve.go index e7801af..91f7f0f 100644 --- a/pkg/dns/resolve.go +++ b/pkg/dns/resolve.go @@ -74,6 +74,13 @@ func (s *Service) Resolve(name any) (primitives.Hash, error) { return covenant.HashString(normalized) } +// Hash returns the canonical hash for a .lthn name. +// +// Hash is the direct service-level alias for Resolve. +func (s *Service) Hash(name any) (primitives.Hash, error) { + return s.Resolve(name) +} + // Verify reports whether a .lthn name is valid after canonicalisation. // // This mirrors Resolve's input handling but returns a boolean so callers can @@ -90,6 +97,16 @@ func (s *Service) Verify(name any) bool { return covenant.VerifyString(normalized) } +// HashString returns the canonical hash for a .lthn name supplied as a string. +func (s *Service) HashString(name string) (primitives.Hash, error) { + return s.ResolveString(name) +} + +// HashBinary returns the canonical hash for a .lthn name supplied as bytes. +func (s *Service) HashBinary(name []byte) (primitives.Hash, error) { + return s.ResolveBinary(name) +} + // ResolveString returns the canonical hash for a .lthn name supplied as a string. // // svc := dns.NewService() @@ -123,6 +140,11 @@ func (s *Service) ResolveName(name any) (primitives.Hash, error) { return s.Resolve(name) } +// HashName is an alias for Hash. +func (s *Service) HashName(name any) (primitives.Hash, error) { + return s.Hash(name) +} + // VerifyString reports whether a .lthn name supplied as a string is valid. // // svc := dns.NewService() @@ -160,6 +182,11 @@ func (s *Service) ResolveByName(name any) (primitives.Hash, error) { return s.ResolveName(name) } +// HashByName is an alias for HashName. +func (s *Service) HashByName(name any) (primitives.Hash, error) { + return s.HashName(name) +} + // VerifyByName is an alias for VerifyName. // // svc := dns.NewService() diff --git a/pkg/dns/resolve_test.go b/pkg/dns/resolve_test.go index 9cf18a0..8c7a8a4 100644 --- a/pkg/dns/resolve_test.go +++ b/pkg/dns/resolve_test.go @@ -48,6 +48,60 @@ func TestResolveNameAliases(t *testing.T) { } } +func TestHashAliases(t *testing.T) { + svc := NewService() + + got, err := svc.Hash("Foo-Bar.lthn") + if err != nil { + t.Fatalf("Hash returned error: %v", err) + } + + want := sha3.Sum256([]byte("foo-bar")) + if got != want { + t.Fatalf("Hash returned %x, want %x", got, want) + } + + got, err = svc.HashString("Foo-Bar.lthn") + if err != nil { + t.Fatalf("HashString returned error: %v", err) + } + + if got != want { + t.Fatalf("HashString returned %x, want %x", got, want) + } + + got, err = svc.HashBinary([]byte("Foo-Bar.lthn")) + if err != nil { + t.Fatalf("HashBinary returned error: %v", err) + } + + if got != want { + t.Fatalf("HashBinary returned %x, want %x", got, want) + } + + got, err = svc.HashName("Foo-Bar.lthn") + if err != nil { + t.Fatalf("HashName returned error: %v", err) + } + + if got != want { + t.Fatalf("HashName returned %x, want %x", got, want) + } + + got, err = svc.HashByName([]byte("Foo-Bar.lthn")) + if err != nil { + t.Fatalf("HashByName returned error: %v", err) + } + + if got != want { + t.Fatalf("HashByName returned %x, want %x", got, want) + } + + if _, err := svc.Hash(123); err == nil { + t.Fatal("Hash should reject unsupported input types") + } +} + func TestResolveByNameAliases(t *testing.T) { svc := NewService()