Switch labeled hash to explicit formatting
This commit is contained in:
parent
af4dbad99f
commit
1ac81dde54
2 changed files with 118 additions and 22 deletions
|
@ -3,9 +3,12 @@ package lhash
|
|||
import (
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/mr-tron/base58"
|
||||
|
||||
"github.com/safing/portbase/container"
|
||||
)
|
||||
|
||||
|
@ -60,11 +63,33 @@ func Load(labeledHash []byte) (*LabeledHash, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// LoadFromString loads a labeled hash from the given string.
|
||||
func LoadFromString(labeledHash string) (*LabeledHash, error) {
|
||||
raw, err := base64.RawURLEncoding.DecodeString(labeledHash)
|
||||
// FromHex loads a labeled hash from the given hexadecimal string.
|
||||
func FromHex(hexEncoded string) (*LabeledHash, error) {
|
||||
raw, err := hex.DecodeString(hexEncoded)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode: %s", err)
|
||||
return nil, fmt.Errorf("failed to decode hex: %s", err)
|
||||
}
|
||||
|
||||
return Load(raw)
|
||||
}
|
||||
|
||||
// FromBase64 loads a labeled hash from the given Base64 string using raw url
|
||||
// encoding.
|
||||
func FromBase64(base64Encoded string) (*LabeledHash, error) {
|
||||
raw, err := base64.RawURLEncoding.DecodeString(base64Encoded)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode base64: %s", err)
|
||||
}
|
||||
|
||||
return Load(raw)
|
||||
}
|
||||
|
||||
// FromBase58 loads a labeled hash from the given Base58 string using the BTC
|
||||
// alphabet.
|
||||
func FromBase58(base58Encoded string) (*LabeledHash, error) {
|
||||
raw, err := base58.Decode(base58Encoded)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode base58: %s", err)
|
||||
}
|
||||
|
||||
return Load(raw)
|
||||
|
@ -78,13 +103,37 @@ func (lh *LabeledHash) Bytes() []byte {
|
|||
return c.CompileData()
|
||||
}
|
||||
|
||||
// String returns the string representation of the labeled hash (base64 raw url encoding).
|
||||
func (lh *LabeledHash) String() string {
|
||||
// Hex returns the hexadecimal string representation of the labeled hash.
|
||||
func (lh *LabeledHash) Hex() string {
|
||||
return hex.EncodeToString(lh.Bytes())
|
||||
}
|
||||
|
||||
// Base64 returns the Base64 string representation of the labeled hash using
|
||||
// raw url encoding.
|
||||
func (lh *LabeledHash) Base64() string {
|
||||
return base64.RawURLEncoding.EncodeToString(lh.Bytes())
|
||||
}
|
||||
|
||||
// Matches returns true if the digest of the given data matches the hash.
|
||||
func (lh *LabeledHash) Matches(data []byte) bool {
|
||||
// Base58 returns the Base58 string representation of the labeled hash using
|
||||
// the BTC alphabet.
|
||||
func (lh *LabeledHash) Base58() string {
|
||||
return base58.Encode(lh.Bytes())
|
||||
}
|
||||
|
||||
// Equal returns true if the given labeled hash is equal.
|
||||
// Equality is checked by comparing both the algorithm and the digest value.
|
||||
func (lh *LabeledHash) Equal(other *LabeledHash) bool {
|
||||
return lh.alg == other.alg &&
|
||||
subtle.ConstantTimeCompare(lh.digest, other.digest) == 1
|
||||
}
|
||||
|
||||
// MatchesString returns true if the digest of the given string matches the hash.
|
||||
func (lh *LabeledHash) MatchesString(s string) bool {
|
||||
return lh.MatchesData([]byte(s))
|
||||
}
|
||||
|
||||
// MatchesData returns true if the digest of the given data matches the hash.
|
||||
func (lh *LabeledHash) MatchesData(data []byte) bool {
|
||||
hasher := lh.alg.new()
|
||||
_, _ = hasher.Write(data) // never returns an error
|
||||
defer hasher.Reset() // internal state may leak data if kept in memory
|
||||
|
|
|
@ -7,8 +7,11 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
testEmpty = []byte("")
|
||||
testFox = []byte("The quick brown fox jumps over the lazy dog.")
|
||||
testEmpty = []byte("")
|
||||
testFox = "The quick brown fox jumps over the lazy dog."
|
||||
testFoxData = []byte(testFox)
|
||||
noMatch = "no match"
|
||||
noMatchData = []byte(noMatch)
|
||||
)
|
||||
|
||||
func testAlgorithm(t *testing.T, alg Algorithm, emptyHex, foxHex string) {
|
||||
|
@ -32,33 +35,77 @@ func testAlgorithm(t *testing.T, alg Algorithm, emptyHex, foxHex string) {
|
|||
}
|
||||
|
||||
// test fox
|
||||
lh = Digest(alg, testFox)
|
||||
lh = Digest(alg, testFoxData)
|
||||
if !bytes.Equal(lh.Bytes()[2:], foxBytes) {
|
||||
t.Errorf("alg %d: test fox: digest mismatch, expected %+v, got %+v", alg, foxBytes, lh.Bytes()[2:])
|
||||
}
|
||||
|
||||
// test matching
|
||||
if !lh.Matches(testFox) {
|
||||
// test matching with serialized/loaded labeled hash
|
||||
if !lh.MatchesData(testFoxData) {
|
||||
t.Errorf("alg %d: failed to match reference", alg)
|
||||
}
|
||||
if lh.Matches([]byte("nope")) {
|
||||
if !lh.MatchesString(testFox) {
|
||||
t.Errorf("alg %d: failed to match reference", alg)
|
||||
}
|
||||
if lh.MatchesData(noMatchData) {
|
||||
t.Errorf("alg %d: failed to non-match garbage", alg)
|
||||
}
|
||||
if lh.MatchesString(noMatch) {
|
||||
t.Errorf("alg %d: failed to non-match garbage", alg)
|
||||
}
|
||||
|
||||
// serialize
|
||||
lhs := Digest(alg, testFox).String()
|
||||
// load
|
||||
loaded, err := LoadFromString(lhs)
|
||||
// Test representations
|
||||
|
||||
// Hex
|
||||
lhs := Digest(alg, testFoxData)
|
||||
loaded, err := FromHex(lhs.Hex())
|
||||
if err != nil {
|
||||
t.Errorf("alg %d: failed to load from string: %s", alg, err)
|
||||
t.Errorf("alg %d: failed to load from hex string: %s", alg, err)
|
||||
return
|
||||
}
|
||||
testFormat(t, alg, lhs, loaded)
|
||||
|
||||
// test matching with serialized/loaded labeled hash
|
||||
if !loaded.Matches(testFox) {
|
||||
// Base64
|
||||
lhs = Digest(alg, testFoxData)
|
||||
loaded, err = FromBase64(lhs.Base64())
|
||||
if err != nil {
|
||||
t.Errorf("alg %d: failed to load from base64 string: %s", alg, err)
|
||||
return
|
||||
}
|
||||
testFormat(t, alg, lhs, loaded)
|
||||
|
||||
// Base58
|
||||
lhs = Digest(alg, testFoxData)
|
||||
loaded, err = FromBase58(lhs.Base58())
|
||||
if err != nil {
|
||||
t.Errorf("alg %d: failed to load from base58 string: %s", alg, err)
|
||||
return
|
||||
}
|
||||
testFormat(t, alg, lhs, loaded)
|
||||
}
|
||||
|
||||
func testFormat(t *testing.T, alg Algorithm, lhs, loaded *LabeledHash) {
|
||||
noMatchLH := Digest(alg, noMatchData)
|
||||
|
||||
// Test equality.
|
||||
if !lhs.Equal(loaded) {
|
||||
t.Errorf("alg %d: equality test failed", alg)
|
||||
}
|
||||
if lhs.Equal(noMatchLH) {
|
||||
t.Errorf("alg %d: non-equality test failed", alg)
|
||||
}
|
||||
|
||||
// Test matching.
|
||||
if !loaded.MatchesData(testFoxData) {
|
||||
t.Errorf("alg %d: failed to match reference", alg)
|
||||
}
|
||||
if loaded.Matches([]byte("nope")) {
|
||||
if !loaded.MatchesString(testFox) {
|
||||
t.Errorf("alg %d: failed to match reference", alg)
|
||||
}
|
||||
if loaded.MatchesData(noMatchData) {
|
||||
t.Errorf("alg %d: failed to non-match garbage", alg)
|
||||
}
|
||||
if loaded.MatchesString(noMatch) {
|
||||
t.Errorf("alg %d: failed to non-match garbage", alg)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue