-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
138 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// A digest that contains the algoritm as a prefix. Example `sha256:d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592` | ||
package typeddigest | ||
|
||
import ( | ||
"bytes" | ||
"crypto/sha256" | ||
"encoding/hex" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"strings" | ||
) | ||
|
||
const ( | ||
algSha256 = "sha256" | ||
) | ||
|
||
type Hash struct { | ||
alg string // "sha256" | ||
digest []byte | ||
} | ||
|
||
func (h *Hash) String() string { | ||
return fmt.Sprintf("%s:%x", h.alg, h.digest) | ||
} | ||
|
||
func (h *Hash) Equal(other *Hash) bool { | ||
return h.alg == other.alg && bytes.Equal(h.digest, other.digest) | ||
} | ||
|
||
func Parse(val string) (*Hash, error) { | ||
withErr := func(err error) (*Hash, error) { return nil, fmt.Errorf("typeddigest.Parse: %w", err) } | ||
|
||
pos := strings.Index(val, ":") | ||
if pos == -1 { | ||
return withErr(errors.New("bad format, '<alg>:' prefix not found")) | ||
} | ||
|
||
alg := val[:pos] | ||
digestHex := val[pos+1:] | ||
|
||
if alg != algSha256 { | ||
return withErr(fmt.Errorf("unsupported algorithm: %s", alg)) | ||
} | ||
|
||
digest, err := hex.DecodeString(digestHex) | ||
if err != nil { | ||
return withErr(err) | ||
} | ||
|
||
if expectedSize := sha256.Size; len(digest) != expectedSize { | ||
return withErr(fmt.Errorf("wrong digest size: expected %d; got %d", expectedSize, len(digest))) | ||
} | ||
|
||
return &Hash{alg, digest}, nil | ||
} | ||
|
||
func DigesterForAlgOf(other *Hash) func(io.Reader) (*Hash, error) { | ||
switch other.alg { | ||
case algSha256: | ||
return Sha256 | ||
default: | ||
return func(io.Reader) (*Hash, error) { | ||
return nil, fmt.Errorf("typeddigest.DigesterForAlgOf: unsupported algorithm: %s", other.alg) | ||
} | ||
} | ||
} | ||
|
||
func Sha256(input io.Reader) (*Hash, error) { | ||
withErr := func(err error) (*Hash, error) { return nil, fmt.Errorf("typeddigest.Sha256: %w", err) } | ||
|
||
hash := sha256.New() | ||
if _, err := io.Copy(hash, input); err != nil { | ||
return withErr(err) | ||
} | ||
|
||
return &Hash{algSha256, hash.Sum(nil)}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package typeddigest | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/function61/gokit/testing/assert" | ||
) | ||
|
||
func TestParse(t *testing.T) { | ||
for _, tc := range []struct { | ||
input string | ||
output string | ||
}{ | ||
{ | ||
"sha256:d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", | ||
"ok", | ||
}, | ||
{ | ||
"sha256:88", | ||
"ERROR: typeddigest.Parse: wrong digest size: expected 32; got 1", | ||
}, | ||
{ | ||
"md5:d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592", | ||
"ERROR: typeddigest.Parse: unsupported algorithm: md5", | ||
}, | ||
{ | ||
"", | ||
"ERROR: typeddigest.Parse: bad format, '<alg>:' prefix not found", | ||
}, | ||
{ | ||
"sha256:nothex", | ||
"ERROR: typeddigest.Parse: encoding/hex: invalid byte: U+006E 'n'", | ||
}, | ||
} { | ||
tc := tc // pin | ||
t.Run(tc.input, func(t *testing.T) { | ||
th, err := Parse(tc.input) | ||
asOutput := func() string { | ||
if err != nil { | ||
return fmt.Sprintf("ERROR: %v", err) | ||
} else { | ||
// test stability | ||
assert.EqualString(t, th.String(), tc.input) | ||
return "ok" | ||
} | ||
}() | ||
|
||
assert.EqualString(t, asOutput, tc.output) | ||
}) | ||
} | ||
} | ||
|
||
func TestSha256(t *testing.T) { | ||
th, err := Sha256(strings.NewReader("The quick brown fox jumps over the lazy dog")) | ||
assert.Ok(t, err) | ||
|
||
assert.EqualString(t, th.String(), "sha256:d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592") | ||
} |