diff --git a/pkg/covenant/locked_lookup.go b/pkg/covenant/locked_lookup.go index 32f543c..98c162a 100644 --- a/pkg/covenant/locked_lookup.go +++ b/pkg/covenant/locked_lookup.go @@ -44,6 +44,9 @@ var ( ) // HasLockedName reports whether the JS lockup table contains the name. +// +// Lookup is case-insensitive and accepts a canonical `.lthn` suffix in +// addition to bare labels. func HasLockedName(name string) bool { return DefaultLockedCatalog().HasByName(name) } @@ -64,6 +67,9 @@ func HasLockedBinary(name []byte) bool { } // GetLockedName returns the locked-name entry for the provided label. +// +// The input is lower-cased before hashing, and canonical `.lthn` suffixes are +// stripped before lookup. func GetLockedName(name string) (LockedName, bool) { return DefaultLockedCatalog().GetByName(name) } @@ -195,8 +201,8 @@ func (c *LockedCatalog) HasByName(name string) bool { // GetByName returns the locked-name entry for the provided label. // -// The lookup lower-cases ASCII input and hashes it directly, matching the JS -// reference helper's ASCII encoding behavior. +// The lookup lower-cases ASCII input, strips canonical `.lthn` suffixes, and +// hashes the normalized label directly. func (c *LockedCatalog) GetByName(name string) (LockedName, bool) { name, ok := asciiLowerName(name) if !ok { diff --git a/pkg/covenant/locked_lookup_test.go b/pkg/covenant/locked_lookup_test.go index e1094ae..c42a693 100644 --- a/pkg/covenant/locked_lookup_test.go +++ b/pkg/covenant/locked_lookup_test.go @@ -128,6 +128,19 @@ func TestLockedLookupStringAndBinaryAliases(t *testing.T) { if !HasLockedByBinary([]byte("NEC")) { t.Fatal("HasLockedByBinary should report locked catalog labels") } + + item, ok = GetLockedName("nec.lthn") + if !ok { + t.Fatal("GetLockedName should accept canonical .lthn names") + } + + if item.Name != "nec" { + t.Fatalf("item.Name = %q, want %q", item.Name, "nec") + } + + if !HasLockedName("nec.lthn") { + t.Fatal("HasLockedName should accept canonical .lthn names") + } } func TestLockedLookupHasByHashAlias(t *testing.T) { diff --git a/pkg/covenant/name_lookup.go b/pkg/covenant/name_lookup.go index 322033e..68b5f2d 100644 --- a/pkg/covenant/name_lookup.go +++ b/pkg/covenant/name_lookup.go @@ -2,11 +2,13 @@ package covenant +import "bytes" + // asciiLowerName lower-cases ASCII labels without performing Unicode -// case-folding. The JS reference uses ASCII encoding for these catalog lookups, -// so non-ASCII input is rejected instead of being hashed differently. +// case-folding. The helper also strips an optional canonical `.lthn` suffix so +// the catalog lookups accept the same normalized forms as the top-level LNS API. func asciiLowerName(name string) (string, bool) { - if len(name) == 0 || len(name) > maxNameSize { + if len(name) == 0 { return "", false } @@ -26,5 +28,18 @@ func asciiLowerName(name string) (string, bool) { buf[i] = ch } + switch { + case bytes.HasSuffix(buf, []byte(".lthn.")): + buf = buf[:len(buf)-6] + case bytes.HasSuffix(buf, []byte(".lthn")): + buf = buf[:len(buf)-5] + case bytes.HasSuffix(buf, []byte(".")): + buf = buf[:len(buf)-1] + } + + if len(buf) == 0 || len(buf) > maxNameSize { + return "", false + } + return string(buf), true } diff --git a/pkg/covenant/reserved_lookup.go b/pkg/covenant/reserved_lookup.go index 75b959b..b24bf00 100644 --- a/pkg/covenant/reserved_lookup.go +++ b/pkg/covenant/reserved_lookup.go @@ -61,8 +61,8 @@ var ( // HasReservedName reports whether the JS reserved-name table contains the name. // -// Lookup is case-insensitive and matches the reference table's lower-casing -// behaviour. +// Lookup is case-insensitive and accepts a canonical `.lthn` suffix in +// addition to bare labels. func HasReservedName(name string) bool { return DefaultReservedCatalog().HasByName(name) } @@ -84,7 +84,8 @@ func HasReservedBinary(name []byte) bool { // GetReservedName returns the reserved-name entry for the provided label. // -// The input is lower-cased before hashing, matching the JS reference helper. +// The input is lower-cased before hashing, and canonical `.lthn` suffixes are +// stripped before lookup. func GetReservedName(name string) (ReservedName, bool) { return DefaultReservedCatalog().GetByName(name) } @@ -258,8 +259,8 @@ func (c *ReservedCatalog) HasByName(name string) bool { // GetByName returns the reserved-name entry for the provided label. // -// The lookup lower-cases ASCII input and hashes it directly, matching the JS -// reference helper's ASCII encoding behavior. +// The lookup lower-cases ASCII input, strips canonical `.lthn` suffixes, and +// hashes the normalized label directly. func (c *ReservedCatalog) GetByName(name string) (ReservedName, bool) { name, ok := asciiLowerName(name) if !ok { diff --git a/pkg/covenant/reserved_lookup_test.go b/pkg/covenant/reserved_lookup_test.go index 06f9dd4..b1bbc56 100644 --- a/pkg/covenant/reserved_lookup_test.go +++ b/pkg/covenant/reserved_lookup_test.go @@ -132,6 +132,19 @@ func TestReservedLookupStringAndBinaryAliases(t *testing.T) { if !HasReservedByBinary([]byte("RESERVED")) { t.Fatal("HasReservedByBinary should report reserved catalog labels") } + + item, ok = GetReservedName("reserved.lthn") + if !ok { + t.Fatal("GetReservedName should accept canonical .lthn names") + } + + if item.Name != "reserved" { + t.Fatalf("item.Name = %q, want %q", item.Name, "reserved") + } + + if !HasReservedName("reserved.lthn") { + t.Fatal("HasReservedName should accept canonical .lthn names") + } } func TestReservedLookupHasByHashAlias(t *testing.T) {