Add covenant name validation helpers
This commit is contained in:
parent
6e2b4a125a
commit
9bfec37eca
2 changed files with 205 additions and 0 deletions
112
pkg/covenant/name.go
Normal file
112
pkg/covenant/name.go
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package covenant
|
||||
|
||||
import (
|
||||
"crypto/sha3"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
"dappco.re/go/lns/pkg/primitives"
|
||||
)
|
||||
|
||||
const maxNameSize = 63
|
||||
|
||||
var blacklist = map[string]struct{}{
|
||||
"example": {},
|
||||
"invalid": {},
|
||||
"local": {},
|
||||
"localhost": {},
|
||||
"test": {},
|
||||
}
|
||||
|
||||
// VerifyString reports whether a domain name meets the covenant rules.
|
||||
func VerifyString(name string) bool {
|
||||
if len(name) == 0 || len(name) > maxNameSize {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(name); i++ {
|
||||
ch := name[i]
|
||||
|
||||
if ch&0x80 != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch {
|
||||
case ch >= '0' && ch <= '9':
|
||||
case ch >= 'A' && ch <= 'Z':
|
||||
return false
|
||||
case ch >= 'a' && ch <= 'z':
|
||||
case ch == '-' || ch == '_':
|
||||
if i == 0 || i == len(name)-1 {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
_, blocked := blacklist[name]
|
||||
return !blocked
|
||||
}
|
||||
|
||||
// VerifyBinary reports whether a binary domain name meets the covenant rules.
|
||||
func VerifyBinary(name []byte) bool {
|
||||
if len(name) == 0 || len(name) > maxNameSize {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(name); i++ {
|
||||
ch := name[i]
|
||||
|
||||
if ch&0x80 != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch {
|
||||
case ch >= '0' && ch <= '9':
|
||||
case ch >= 'A' && ch <= 'Z':
|
||||
return false
|
||||
case ch >= 'a' && ch <= 'z':
|
||||
case ch == '-' || ch == '_':
|
||||
if i == 0 || i == len(name)-1 {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
_, blocked := blacklist[string(name)]
|
||||
return !blocked
|
||||
}
|
||||
|
||||
// VerifyName reports whether a name meets the covenant rules.
|
||||
func VerifyName(name []byte) bool {
|
||||
return VerifyBinary(name)
|
||||
}
|
||||
|
||||
// HashString hashes a validated domain name.
|
||||
func HashString(name string) (primitives.Hash, error) {
|
||||
if !VerifyString(name) {
|
||||
return primitives.Hash{}, core.E("covenant.HashString", "invalid name", nil)
|
||||
}
|
||||
|
||||
sum := sha3.Sum256([]byte(name))
|
||||
return primitives.Hash(sum), nil
|
||||
}
|
||||
|
||||
// HashBinary hashes a validated domain name.
|
||||
func HashBinary(name []byte) (primitives.Hash, error) {
|
||||
if !VerifyBinary(name) {
|
||||
return primitives.Hash{}, core.E("covenant.HashBinary", "invalid name", nil)
|
||||
}
|
||||
|
||||
sum := sha3.Sum256(name)
|
||||
return primitives.Hash(sum), nil
|
||||
}
|
||||
|
||||
// HashName hashes a validated domain name.
|
||||
func HashName(name []byte) (primitives.Hash, error) {
|
||||
return HashBinary(name)
|
||||
}
|
||||
93
pkg/covenant/name_test.go
Normal file
93
pkg/covenant/name_test.go
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
package covenant
|
||||
|
||||
import (
|
||||
"crypto/sha3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVerifyString(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
ok bool
|
||||
}{
|
||||
{name: "example", ok: false},
|
||||
{name: "invalid", ok: false},
|
||||
{name: "localhost", ok: false},
|
||||
{name: "test", ok: false},
|
||||
{name: "", ok: false},
|
||||
{name: "foo", ok: true},
|
||||
{name: "foo-bar", ok: true},
|
||||
{name: "foo_bar", ok: true},
|
||||
{name: "-foo", ok: false},
|
||||
{name: "foo-", ok: false},
|
||||
{name: "_foo", ok: false},
|
||||
{name: "foo_", ok: false},
|
||||
{name: "Foo", ok: false},
|
||||
{name: "f\x7fo", ok: false},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
if got := VerifyString(tc.name); got != tc.ok {
|
||||
t.Fatalf("VerifyString(%q) = %v, want %v", tc.name, got, tc.ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyBinary(t *testing.T) {
|
||||
cases := []struct {
|
||||
name []byte
|
||||
ok bool
|
||||
}{
|
||||
{name: nil, ok: false},
|
||||
{name: []byte("example"), ok: false},
|
||||
{name: []byte("foo"), ok: true},
|
||||
{name: []byte("foo-bar"), ok: true},
|
||||
{name: []byte("foo_bar"), ok: true},
|
||||
{name: []byte("-foo"), ok: false},
|
||||
{name: []byte("foo-"), ok: false},
|
||||
{name: []byte("Foo"), ok: false},
|
||||
{name: []byte{0xff}, ok: false},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
if got := VerifyBinary(tc.name); got != tc.ok {
|
||||
t.Fatalf("VerifyBinary(%q) = %v, want %v", tc.name, got, tc.ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashString(t *testing.T) {
|
||||
got, err := HashString("foo-bar")
|
||||
if err != nil {
|
||||
t.Fatalf("HashString returned error: %v", err)
|
||||
}
|
||||
|
||||
want := sha3.Sum256([]byte("foo-bar"))
|
||||
if got != want {
|
||||
t.Fatalf("HashString returned %x, want %x", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashBinary(t *testing.T) {
|
||||
got, err := HashBinary([]byte("foo_bar"))
|
||||
if err != nil {
|
||||
t.Fatalf("HashBinary returned error: %v", err)
|
||||
}
|
||||
|
||||
want := sha3.Sum256([]byte("foo_bar"))
|
||||
if got != want {
|
||||
t.Fatalf("HashBinary returned %x, want %x", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashRejectsInvalidName(t *testing.T) {
|
||||
if _, err := HashString("Foo"); err == nil {
|
||||
t.Fatal("HashString should reject invalid names")
|
||||
}
|
||||
|
||||
if _, err := HashBinary([]byte("foo-")); err == nil {
|
||||
t.Fatal("HashBinary should reject invalid names")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue