Commit d4b42f8e authored by Jeromy's avatar Jeromy
Browse files

fixes for sha3

parent 8f79df77
package multihash
import (
"encoding/hex"
"errors"
"fmt"
b58 "QmNsoHoCVhgXcv1Yg45jtkMgimxorTAN36fV9AQMFXHHAQ/go-base58"
)
// errors
var (
ErrUnknownCode = errors.New("unknown multihash code")
ErrTooShort = errors.New("multihash too short. must be > 3 bytes")
ErrTooLong = errors.New("multihash too long. must be < 129 bytes")
ErrLenNotSupported = errors.New("multihash does not yet support digests longer than 127 bytes")
)
// ErrInconsistentLen is returned when a decoded multihash has an inconsistent length
type ErrInconsistentLen struct {
dm *DecodedMultihash
}
func (e ErrInconsistentLen) Error() string {
return fmt.Sprintf("multihash length inconsistent: %v", e.dm)
}
// constants
const (
SHA1 = 0x11
SHA2_256 = 0x12
SHA2_512 = 0x13
SHA3 = 0x14
BLAKE2B = 0x40
BLAKE2S = 0x41
)
// Names maps the name of a hash to the code
var Names = map[string]int{
"sha1": SHA1,
"sha2-256": SHA2_256,
"sha2-512": SHA2_512,
"sha3": SHA3,
"blake2b": BLAKE2B,
"blake2s": BLAKE2S,
}
// Codes maps a hash code to it's name
var Codes = map[int]string{
SHA1: "sha1",
SHA2_256: "sha2-256",
SHA2_512: "sha2-512",
SHA3: "sha3",
BLAKE2B: "blake2b",
BLAKE2S: "blake2s",
}
// DefaultLengths maps a hash code to it's default length
var DefaultLengths = map[int]int{
SHA1: 20,
SHA2_256: 32,
SHA2_512: 64,
SHA3: 64,
BLAKE2B: 64,
BLAKE2S: 32,
}
type DecodedMultihash struct {
Code int
Name string
Length int
Digest []byte
}
type Multihash []byte
func (m *Multihash) HexString() string {
return hex.EncodeToString([]byte(*m))
}
func (m *Multihash) String() string {
return m.HexString()
}
func FromHexString(s string) (Multihash, error) {
b, err := hex.DecodeString(s)
if err != nil {
return Multihash{}, err
}
return Cast(b)
}
func (m Multihash) B58String() string {
return b58.Encode([]byte(m))
}
func FromB58String(s string) (m Multihash, err error) {
// panic handler, in case we try accessing bytes incorrectly.
defer func() {
if e := recover(); e != nil {
m = Multihash{}
err = e.(error)
}
}()
//b58 smells like it can panic...
b := b58.Decode(s)
return Cast(b)
}
func Cast(buf []byte) (Multihash, error) {
dm, err := Decode(buf)
if err != nil {
return Multihash{}, err
}
if !ValidCode(dm.Code) {
return Multihash{}, ErrUnknownCode
}
return Multihash(buf), nil
}
// Decode a hash from the given Multihash.
func Decode(buf []byte) (*DecodedMultihash, error) {
if len(buf) < 3 {
return nil, ErrTooShort
}
if len(buf) > 129 {
return nil, ErrTooLong
}
dm := &DecodedMultihash{
Code: int(uint8(buf[0])),
Name: Codes[int(uint8(buf[0]))],
Length: int(uint8(buf[1])),
Digest: buf[2:],
}
if len(dm.Digest) != dm.Length {
return nil, ErrInconsistentLen{dm}
}
return dm, nil
}
// Encode a hash digest along with the specified function code.
// Note: the length is derived from the length of the digest itself.
func Encode(buf []byte, code int) ([]byte, error) {
if !ValidCode(code) {
return nil, ErrUnknownCode
}
if len(buf) > 127 {
return nil, ErrLenNotSupported
}
pre := make([]byte, 2)
pre[0] = byte(uint8(code))
pre[1] = byte(uint8(len(buf)))
return append(pre, buf...), nil
}
func EncodeName(buf []byte, name string) ([]byte, error) {
return Encode(buf, Names[name])
}
// ValidCode checks whether a multihash code is valid.
func ValidCode(code int) bool {
if AppCode(code) {
return true
}
if _, ok := Codes[code]; ok {
return true
}
return false
}
// AppCode checks whether a multihash code is part of the App range.
func AppCode(code int) bool {
return code >= 0 && code < 0x10
}
The MIT License (MIT)
Copyright (c) 2014 Juan Batiz-Benet
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# multihash tool
The `multihash` tool uses `go-multihash` to hash things much like `shasum`.
Warning: this is a **multihash** tool! Its digests follow the [multihash](https://github.com/jbenet/multihash) format.
### Install
- From Source:
```
go get github.com/jbenet/go-multihash/multihash
```
- Precompiled Binaries: https://gobuilder.me/github.com/jbenet/go-multihash/multihash
### Usage
```sh
> multihash -h
usage: ./multihash [options] [FILE]
Print or check multihash checksums.
With no FILE, or when FILE is -, read standard input.
Options:
-a="sha2-256": one of: sha1, sha2-256, sha2-512, sha3 (shorthand)
-algorithm="sha2-256": one of: sha1, sha2-256, sha2-512, sha3
-c="": check checksum matches (shorthand)
-check="": check checksum matches
-e="base58": one of: raw, hex, base58, base64 (shorthand)
-encoding="base58": one of: raw, hex, base58, base64
-l=-1: checksums length in bits (truncate). -1 is default (shorthand)
-length=-1: checksums length in bits (truncate). -1 is default
```
### Examples
#### Input
```sh
# from stdin
> multihash < main.go
QmRZxt2b1FVZPNqd8hsiykDL3TdBDeTSPX9Kv46HmX4Gx8
# from file
> ./multihash main.go
QmRZxt2b1FVZPNqd8hsiykDL3TdBDeTSPX9Kv46HmX4Gx8
# from stdin "filename"
> multihash - < main.go
QmRZxt2b1FVZPNqd8hsiykDL3TdBDeTSPX9Kv46HmX4Gx8
```
#### Algorithms
```sh
> multihash -a ?
error: algorithm '?' not one of: sha1, sha2-256, sha2-512, sha3
> multihash -a sha1 < main.go
5drkbcqJUo6fZVvcZJeVEVWAgndvLm
> multihash -a sha2-256 < main.go
QmcK3s36goo9v2HYcfTrDKKwxaxmJJ59etodQQFYsL5T5N
> multihash -a sha2-512 < main.go
8VuDcW4CooyPQA8Cc4eYpwjhyDJZqu5m5ZMDFzWULYsVS8d119JaGeNWsZbZ2ZG2kPtbrMx31MidokCigaD65yUPAs
> multihash -a sha3 < main.go
8tWDCTfAX24DYmzNixTj2ARJkqwRG736VHx5aJppmqRjhW9QT1EuTgKUmu9Pmunzq292jzPKxb2VxSsTXmjFY1HD3B
```
#### Encodings
```sh
> multihash -e raw < main.go
Ϛ�����I�5 S��WG>���_��]g�����u
> multihash -e hex < main.go
1220cf9aa2b8a38b9b49d135095390059a57473e97aceb5fcae25d67a8b6feb58275
> multihash -e base64 < main.go
EiDPmqK4o4ubSdE1CVOQBZpXRz6XrOtfyuJdZ6i2/rWCdQ==
> multihash -e base58 < main.go
Qmf1QjEXDmqBm7RqHKqFGNUyhzUjnX7cmgKMrGzzPceZDQ
```
#### Digest Length
```sh
# we're outputing hex (good byte alignment) to show the codes changing
# notice the multihash code (first 2 chars) differs!
> multihash -e hex -a sha2-256 -l 256 < main.go
1220cf9aa2b8a38b9b49d135095390059a57473e97aceb5fcae25d67a8b6feb58275
> multihash -e hex -a sha2-512 -l 256 < main.go
132047a4b6c629f5545f529b0ff461dc09119969f3593186277a1cc7a8ea3560a6f1
> multihash -e hex -a sha3 -l 256 < main.go
14206b9222a1a47939e665261bd2b5573e55e7988675223adde73c1011066ad66335
# notice the multihash length (next 2 chars) differs!
> multihash -e hex -a sha2-256 -l 256 < main.go
1220cf9aa2b8a38b9b49d135095390059a57473e97aceb5fcae25d67a8b6feb58275
> multihash -e hex -a sha2-256 -l 200 < main.go
1219cf9aa2b8a38b9b49d135095390059a57473e97aceb5fcae25d
```
#### Verify Checksum
```sh
> multihash -c QmRZxt2b1FVZPNqd8hsiykDL3TdBDeTSPX9Kv46HmX4Gx8 < main.go
OK checksums match (-q for no output)
> multihash -c QmcKaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa < main.go
error: computed checksum did not match (-q for no output)
# works with other arguments too
> multihash -e hex -l 128 -c "12102ffc284a1e82bf51e567c75b2ae6edb9" < main.go
OK checksums match (-q for no output)
```
#!/bin/sh
bin=multihash
# this script is currently brain dead.
# it merely tries two locations.
# in the future maybe use value of $PATH.
binpath=/usr/local/bin
if [ -d "$binpath" ]; then
mv "$bin" "$binpath/$bin"
echo "installed $binpath/$bin"
exit 0
fi
binpath=/usr/bin
if [ -d "$binpath" ]; then
mv "$bin" "$binpath/$bin"
echo "installed $binpath/$bin"
exit 0
fi
package main
import (
"flag"
"fmt"
"io"
"os"
mh "QmdeauTdyf38KDQB4Cc4CurPWRRb5pej27NCXPA7kbPTJy/go-multihash"
mhopts "QmdeauTdyf38KDQB4Cc4CurPWRRb5pej27NCXPA7kbPTJy/go-multihash/opts"
)
var usage = `usage: %s [options] [FILE]
Print or check multihash checksums.
With no FILE, or when FILE is -, read standard input.
Options:
`
// flags
var opts *mhopts.Options
var checkRaw string
var checkMh mh.Multihash
var inputFilename string
var quiet bool
func init() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, usage, os.Args[0])
flag.PrintDefaults()
}
opts = mhopts.SetupFlags(flag.CommandLine)
checkStr := "check checksum matches"
flag.StringVar(&checkRaw, "check", "", checkStr)
flag.StringVar(&checkRaw, "c", "", checkStr+" (shorthand)")
quietStr := "quiet output (no newline on checksum, no error text)"
flag.BoolVar(&quiet, "quiet", false, quietStr)
flag.BoolVar(&quiet, "q", false, quietStr+" (shorthand)")
}
func parseFlags(o *mhopts.Options) error {
flag.Parse()
if err := o.ParseError(); err != nil {
return err
}
if checkRaw != "" {
var err error
checkMh, err = mhopts.Decode(o.Encoding, checkRaw)
if err != nil {
return fmt.Errorf("fail to decode check '%s': %s", checkRaw, err)
}
}
return nil
}
func getInput() (io.ReadCloser, error) {
args := flag.Args()
switch {
case len(args) < 1:
inputFilename = "-"
return os.Stdin, nil
case args[0] == "-":
inputFilename = "-"
return os.Stdin, nil
default:
inputFilename = args[0]
f, err := os.Open(args[0])
if err != nil {
return nil, fmt.Errorf("failed to open '%s': %s", args[0], err)
}
return f, nil
}
}
func printHash(o *mhopts.Options, r io.Reader) error {
h, err := o.Multihash(r)
if err != nil {
return err
}
s, err := mhopts.Encode(o.Encoding, h)
if err != nil {
return err
}
if quiet {
fmt.Print(s)
} else {
fmt.Println(s)
}
return nil
}
func main() {
checkErr := func(err error) {
if err != nil {
die("error: ", err)
}
}
err := parseFlags(opts)
checkErr(err)
inp, err := getInput()
checkErr(err)
if checkMh != nil {
err = opts.Check(inp, checkMh)
checkErr(err)
if !quiet {
fmt.Println("OK checksums match (-q for no output)")
}
} else {
err = printHash(opts, inp)
checkErr(err)
}
inp.Close()
}
func die(v ...interface{}) {
if !quiet {
fmt.Fprint(os.Stderr, v...)
fmt.Fprint(os.Stderr, "\n")
}
// flag.Usage()
os.Exit(1)
}
package multihash
import (
"bytes"
"encoding/hex"
"fmt"
"testing"
)
// maybe silly, but makes it so changing
// the table accidentally has to happen twice.
var tCodes = map[int]string{
0x11: "sha1",
0x12: "sha2-256",
0x13: "sha2-512",
0x14: "sha3",
0x40: "blake2b",
0x41: "blake2s",
}
type TestCase struct {
hex string
code int
name string
}
var testCases = []TestCase{
TestCase{"0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33", 0x11, "sha1"},
TestCase{"0beec7b5", 0x11, "sha1"},
TestCase{"2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae", 0x12, "sha2-256"},
TestCase{"2c26b46b", 0x12, "sha2-256"},
TestCase{"0beec7b5ea3f0fdbc9", 0x40, "blake2b"},
}
func (tc TestCase) Multihash() (Multihash, error) {
ob, err := hex.DecodeString(tc.hex)
if err != nil {
return nil, err
}
b := make([]byte, 2+len(ob))
b[0] = byte(uint8(tc.code))
b[1] = byte(uint8(len(ob)))
copy(b[2:], ob)
return Cast(b)
}
func TestEncode(t *testing.T) {
for _, tc := range testCases {
ob, err := hex.DecodeString(tc.hex)
if err != nil {
t.Error(err)
continue
}
pre := make([]byte, 2)
pre[0] = byte(uint8(tc.code))
pre[1] = byte(uint8(len(ob)))
nb := append(pre, ob...)
encC, err := Encode(ob, tc.code)
if err != nil {
t.Error(err)
continue
}
if !bytes.Equal(encC, nb) {
t.Error("encoded byte mismatch: ", encC, nb)
}
encN, err := EncodeName(ob, tc.name)
if err != nil {
t.Error(err)
continue
}
if !bytes.Equal(encN, nb) {
t.Error("encoded byte mismatch: ", encN, nb)
}
h, err := tc.Multihash()
if err != nil {
t.Error(err)
}
if !bytes.Equal(h, nb) {
t.Error("Multihash func mismatch.")
}
}
}
func ExampleEncodeName() {
// ignores errors for simplicity - don't do that at home.
buf, _ := hex.DecodeString("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33")
mhbuf, _ := EncodeName(buf, "sha1")
mhhex := hex.EncodeToString(mhbuf)
fmt.Printf("hex: %v\n", mhhex)
// Output:
// hex: 11140beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33
}
func TestDecode(t *testing.T) {
for _, tc := range testCases {
ob, err := hex.DecodeString(tc.hex)
if err != nil {
t.Error(err)
continue
}
pre := make([]byte, 2)
pre[0] = byte(uint8(tc.code))
pre[1] = byte(uint8(len(ob)))
nb := append(pre, ob...)
dec, err := Decode(nb)
if err != nil {
t.Error(err)
continue
}
if dec.Code != tc.code {
t.Error("decoded code mismatch: ", dec.Code, tc.code)
}
if dec.Name != tc.name {
t.Error("decoded name mismatch: ", dec.Name, tc.name)
}
if dec.Length != len(ob) {
t.Error("decoded length mismatch: ", dec.Length, len(ob))
}
if !bytes.Equal(dec.Digest, ob) {
t.Error("decoded byte mismatch: ", dec.Digest, ob)
}
}
}
func TestTable(t *testing.T) {
for k, v := range tCodes {
if Codes[k] != v {
t.Error("Table mismatch: ", Codes[k], v)
}
if Names[v] != k {
t.Error("Table mismatch: ", Names[v], k)
}
}
}
func ExampleDecode() {
// ignores errors for simplicity - don't do that at home.
buf, _ := hex.DecodeString("0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33")
mhbuf, _ := EncodeName(buf, "sha1")
o, _ := Decode(mhbuf)
mhhex := hex.EncodeToString(o.Digest)
fmt.Printf("obj: %v 0x%x %d %s\n", o.Name, o.Code, o.Length, mhhex)
// Output:
// obj: sha1 0x11 20 0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33
}
func TestValidCode(t *testing.T) {
for i := 0; i < 0xff; i++ {
_, ok := tCodes[i]
b := AppCode(i) || ok
if ValidCode(i) != b {
t.Error("ValidCode incorrect for: ", i)
}
}
}
func TestAppCode(t *testing.T) {
for i := 0; i < 0xff; i++ {
b := i >= 0 && i < 0x10
if AppCode(i) != b {
t.Error("AppCode incorrect for: ", i)
}
}
}
func TestCast(t *testing.T) {
for _, tc := range testCases {
ob, err := hex.DecodeString(tc.hex)
if err != nil {
t.Error(err)
continue
}
pre := make([]byte, 2)
pre[0] = byte(uint8(tc.code))
pre[1] = byte(uint8(len(ob)))
nb := append(pre, ob...)
if _, err := Cast(nb); err != nil {
t.Error(err)
continue
}
if _, err = Cast(ob); err == nil {
t.Error("cast failed to detect non-multihash")
continue
}
}
}
func TestHex(t *testing.T) {
for _, tc := range testCases {
ob, err := hex.DecodeString(tc.hex)
if err != nil {
t.Error(err)
continue
}
pre := make([]byte, 2)
pre[0] = byte(uint8(tc.code))
pre[1] = byte(uint8(len(ob)))
nb := append(pre, ob...)
hs := hex.EncodeToString(nb)
mh, err := FromHexString(hs)
if err != nil {
t.Error(err)
continue
}
if !bytes.Equal(mh, nb) {
t.Error("FromHexString failed", nb, mh)
continue
}
if mh.HexString() != hs {
t.Error("Multihash.HexString failed", hs, mh.HexString)
continue
}
}
}
func BenchmarkEncode(b *testing.B) {
tc := testCases[0]
ob, err := hex.DecodeString(tc.hex)
if err != nil {
b.Error(err)
return
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
Encode(ob, tc.code)
}
}
func BenchmarkDecode(b *testing.B) {
tc := testCases[0]
ob, err := hex.DecodeString(tc.hex)
if err != nil {
b.Error(err)
return
}
pre := make([]byte, 2)
pre[0] = byte(uint8(tc.code))
pre[1] = byte(uint8(len(ob)))
nb := append(pre, ob...)
b.ResetTimer()
for i := 0; i < b.N; i++ {
Decode(nb)
}
}
# mhopts - multihash options for writing commands
`mhopts` is a small package that helps to write commands which
may take multihash options. Check it out in action:
- [multihash](../multihash)
- [hashpipe](https://github.com/jbenet/go-hashpipe)
Godoc: [https://godoc.org/github.com/jbenet/go-multihash/opts](https://godoc.org/github.com/jbenet/go-multihash/opts)
package opts
import (
"encoding/base64"
"encoding/hex"
"fmt"
base58 "QmNsoHoCVhgXcv1Yg45jtkMgimxorTAN36fV9AQMFXHHAQ/go-base58"
mh "QmdeauTdyf38KDQB4Cc4CurPWRRb5pej27NCXPA7kbPTJy/go-multihash"
)
func Decode(encoding, digest string) (mh.Multihash, error) {
switch encoding {
case "raw":
return mh.Cast([]byte(digest))
case "hex":
return hex.DecodeString(digest)
case "base58":
return base58.Decode(digest), nil
case "base64":
return base64.StdEncoding.DecodeString(digest)
default:
return nil, fmt.Errorf("unknown encoding: %s", encoding)
}
}
func Encode(encoding string, hash mh.Multihash) (string, error) {
switch encoding {
case "raw":
return string(hash), nil
case "hex":
return hex.EncodeToString(hash), nil
case "base58":
return base58.Encode(hash), nil
case "base64":
return base64.StdEncoding.EncodeToString(hash), nil
default:
return "", fmt.Errorf("unknown encoding: %s", encoding)
}
}
// Package opts helps to write commands which may take multihash
// options.
package opts
import (
"bytes"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"strings"
mh "QmdeauTdyf38KDQB4Cc4CurPWRRb5pej27NCXPA7kbPTJy/go-multihash"
)
// package errors
var (
ErrMatch = errors.New("multihash checksums did not match")
)
// Options is a struct used to parse cli flags.
type Options struct {
Encoding string
Algorithm string
AlgorithmCode int
Length int
fs *flag.FlagSet
}
// FlagValues are the values the various option flags can take.
var FlagValues = struct {
Encodings []string
Algorithms []string
}{
Encodings: []string{"raw", "hex", "base58", "base64"},
Algorithms: []string{"sha1", "sha2-256", "sha2-512", "sha3"},
}
// SetupFlags adds multihash related options to given flagset.
func SetupFlags(f *flag.FlagSet) *Options {
// TODO: add arg for adding opt prefix and/or overriding opts
o := new(Options)
algoStr := "one of: " + strings.Join(FlagValues.Algorithms, ", ")
f.StringVar(&o.Algorithm, "algorithm", "sha2-256", algoStr)
f.StringVar(&o.Algorithm, "a", "sha2-256", algoStr+" (shorthand)")
encStr := "one of: " + strings.Join(FlagValues.Encodings, ", ")
f.StringVar(&o.Encoding, "encoding", "base58", encStr)
f.StringVar(&o.Encoding, "e", "base58", encStr+" (shorthand)")
lengthStr := "checksums length in bits (truncate). -1 is default"
f.IntVar(&o.Length, "length", -1, lengthStr)
f.IntVar(&o.Length, "l", -1, lengthStr+" (shorthand)")
return o
}
// Parse parses the values of flags from given argument slice.
// It is equivalent to flags.Parse(args)
func (o *Options) Parse(args []string) error {
if err := o.fs.Parse(args); err != nil {
return err
}
return o.ParseError()
}
// ParseError checks the parsed options for errors.
func (o *Options) ParseError() error {
if !strIn(o.Encoding, FlagValues.Encodings) {
return fmt.Errorf("encoding '%s' not %s", o.Encoding, FlagValues.Encodings)
}
if !strIn(o.Algorithm, FlagValues.Algorithms) {
return fmt.Errorf("algorithm '%s' not %s", o.Algorithm, FlagValues.Algorithms)
}
var found bool
o.AlgorithmCode, found = mh.Names[o.Algorithm]
if !found {
return fmt.Errorf("algorithm '%s' not found (lib error, pls report).", o.Algorithm)
}
if o.Length >= 0 {
if o.Length%8 != 0 {
return fmt.Errorf("length must be multiple of 8")
}
o.Length = o.Length / 8
if o.Length > mh.DefaultLengths[o.AlgorithmCode] {
o.Length = mh.DefaultLengths[o.AlgorithmCode]
}
}
return nil
}
// strIn checks wither string a is in set.
func strIn(a string, set []string) bool {
for _, s := range set {
if s == a {
return true
}
}
return false
}
// Check reads all the data in r, calculates its multihash,
// and checks it matches h1
func (o *Options) Check(r io.Reader, h1 mh.Multihash) error {
h2, err := o.Multihash(r)
if err != nil {
return err
}
if !bytes.Equal(h1, h2) {
return fmt.Errorf("computed checksum did not match")
}
return nil
}
// Multihash reads all the data in r and calculates its multihash.
func (o *Options) Multihash(r io.Reader) (mh.Multihash, error) {
b, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
return mh.Sum(b, o.AlgorithmCode, o.Length)
}
{
"name": "go-multihash",
"author": "whyrusleeping",
"version": "1.0.0",
"gxDependencies": [
{
"name": "go-base58",
"hash": "QmNsoHoCVhgXcv1Yg45jtkMgimxorTAN36fV9AQMFXHHAQ",
"version": "1.0.0"
},
{
"name": "crypto-sha3",
"hash": "QmSQRViqskLPYFbjKhWE5EaW8eou9SD2j52QaBkQdigMsG",
"version": "1.0.0"
}
],
"language": "go",
"gx": {
"dvcsimport": "github.com/jbenet/go-multihash"
}
}
\ No newline at end of file
package multihash
import (
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"errors"
"fmt"
sha3 "QmSQRViqskLPYFbjKhWE5EaW8eou9SD2j52QaBkQdigMsG/crypto-sha3"
)
var ErrSumNotSupported = errors.New("Function not implemented. Complain to lib maintainer.")
func Sum(data []byte, code int, length int) (Multihash, error) {
m := Multihash{}
err := error(nil)
if !ValidCode(code) {
return m, fmt.Errorf("invalid multihash code %d", code)
}
var d []byte
switch code {
case SHA1:
d = sumSHA1(data)
case SHA2_256:
d = sumSHA256(data)
case SHA2_512:
d = sumSHA512(data)
case SHA3:
d, err = sumSHA3(data)
default:
return m, ErrSumNotSupported
}
if err != nil {
return m, err
}
if length < 0 {
var ok bool
length, ok = DefaultLengths[code]
if !ok {
return m, fmt.Errorf("no default length for code %d", code)
}
}
return Encode(d[0:length], code)
}
func sumSHA1(data []byte) []byte {
a := sha1.Sum(data)
return a[0:20]
}
func sumSHA256(data []byte) []byte {
a := sha256.Sum256(data)
return a[0:32]
}
func sumSHA512(data []byte) []byte {
a := sha512.Sum512(data)
return a[0:64]
}
func sumSHA3(data []byte) ([]byte, error) {
h := sha3.New512()
if _, err := h.Write(data); err != nil {
return nil, err
}
return h.Sum(nil), nil
}
package multihash
import (
"bytes"
"testing"
)
type SumTestCase struct {
code int
length int
input string
hex string
}
var sumTestCases = []SumTestCase{
SumTestCase{SHA1, -1, "foo", "11140beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"},
SumTestCase{SHA1, 10, "foo", "110a0beec7b5ea3f0fdbc95d"},
SumTestCase{SHA2_256, -1, "foo", "12202c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"},
SumTestCase{SHA2_256, 16, "foo", "12102c26b46b68ffc68ff99b453c1d304134"},
SumTestCase{SHA2_512, -1, "foo", "1340f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7"},
SumTestCase{SHA2_512, 32, "foo", "1320f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc663832"},
}
func TestSum(t *testing.T) {
for _, tc := range sumTestCases {
m1, err := FromHexString(tc.hex)
if err != nil {
t.Error(err)
continue
}
m2, err := Sum([]byte(tc.input), tc.code, tc.length)
if err != nil {
t.Error(tc.code, "sum failed.", err)
continue
}
if !bytes.Equal(m1, m2) {
t.Error(tc.code, "sum failed.", m1, m2)
}
s1 := m1.HexString()
if s1 != tc.hex {
t.Error("hex strings not the same")
}
s2 := m1.B58String()
m3, err := FromB58String(s2)
if err != nil {
t.Error("failed to decode b58")
} else if !bytes.Equal(m3, m1) {
t.Error("b58 failing bytes")
} else if s2 != m3.B58String() {
t.Error("b58 failing string")
}
}
}
func BenchmarkSum(b *testing.B) {
tc := sumTestCases[0]
for i := 0; i < b.N; i++ {
Sum([]byte(tc.input), tc.code, tc.length)
}
}
BINS = bin/multihash
MULTIHASH_ROOT = ../
MULTIHASH_CMD = ../multihash
all: deps
deps: bins
clean:
rm $(BINS)
bins: $(BINS)
bin/multihash: $(MULTIHASH_ROOT)/**/*.go
go build -o bin/multihash $(MULTIHASH_CMD)
test: test_expensive
test_expensive:
cd sharness && make TEST_EXPENSIVE=1
test_cheap:
cd sharness && make
.PHONY: all clean
# Run tests
#
# Copyright (c) 2014 Christian Couder
# MIT Licensed; see the LICENSE file in this repository.
#
# NOTE: run with TEST_VERBOSE=1 for verbose sharness tests.
T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
BINS = bin/multihash
SHARNESS = lib/sharness/sharness.sh
all: clean deps $(T) aggregate
clean:
@echo "*** $@ ***"
-rm -rf test-results
$(T):
@echo "*** $@ ***"
./$@
aggregate:
@echo "*** $@ ***"
lib/test-aggregate-results.sh
deps: $(SHARNESS) $(BINS)
$(SHARNESS):
@echo "*** installing $@ ***"
lib/install-sharness.sh
bin/%:
@echo "*** installing $@ ***"
cd .. && make $@
.PHONY: all clean $(T) aggregate
#!/bin/sh
# install sharness.sh
#
# Copyright (c) 2014 Juan Batiz-Benet
# MIT Licensed; see the LICENSE file in this repository.
#
# settings
version=50229a79ba22b2f13ccd82451d86570fecbd194c
urlprefix=https://github.com/mlafeldt/sharness.git
clonedir=lib
sharnessdir=sharness
die() {
echo >&2 "$@"
exit 1
}
mkdir -p "$clonedir" || die "Could not create '$clonedir' directory"
cd "$clonedir" || die "Could not cd into '$clonedir' directory"
git clone "$urlprefix" || die "Could not clone '$urlprefix'"
cd "$sharnessdir" || die "Could not cd into '$sharnessdir' directory"
git checkout "$version" || die "Could not checkout '$version'"
exit 0
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment