From c54facf66be1c4e137121f36b300543f6673ea7c Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Sun, 6 Apr 2014 12:22:58 +0200 Subject: [PATCH] Add hashing package --- hashing/hashing_suite_test.go | 12 ++++ hashing/hashing_test.go | 77 ++++++++++++++++++++++++ hashing/implementation.go | 109 ++++++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 hashing/hashing_suite_test.go create mode 100644 hashing/hashing_test.go create mode 100644 hashing/implementation.go diff --git a/hashing/hashing_suite_test.go b/hashing/hashing_suite_test.go new file mode 100644 index 000000000..880599f83 --- /dev/null +++ b/hashing/hashing_suite_test.go @@ -0,0 +1,12 @@ +package hashing_test + +import ( + "testing" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestHashing(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Hashing Suite") +} diff --git a/hashing/hashing_test.go b/hashing/hashing_test.go new file mode 100644 index 000000000..67a8e8676 --- /dev/null +++ b/hashing/hashing_test.go @@ -0,0 +1,77 @@ +package hashing_test + +import ( + "bytes" + "crypto/md5" + "crypto/sha1" + "encoding/hex" + "hash" + + "github.com/fd0/khepri/hashing" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Hashing", func() { + var static_tests = []struct { + hash func() hash.Hash + text string + digest string + }{ + {md5.New, "foobar\n", "14758f1afd44c09b7992073ccf00b43d"}, + // test data from http://www.nsrl.nist.gov/testdata/ + {sha1.New, "abc", "a9993e364706816aba3e25717850c26c9cd0d89d"}, + {sha1.New, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "84983e441c3bd26ebaae4aa1f95129e5e54670f1"}, + } + + var _ = Describe("Reader", func() { + Context("Static Strings", func() { + It("Should compute digest", func() { + for _, t := range static_tests { + r := hashing.NewReader(bytes.NewBuffer([]byte(t.text)), t.hash) + + n, err := r.Read(make([]byte, len(t.text)+1)) + + if n != len(t.text) { + Fail("not enough bytes read") + } + + if err != nil { + panic(err) + } + + digest := r.Hash() + + h := hex.EncodeToString(digest) + Expect(h).Should(Equal(t.digest)) + } + }) + }) + }) + + var _ = Describe("Writer", func() { + Context("Static Strings", func() { + It("Should compute digest", func() { + for _, t := range static_tests { + var buf bytes.Buffer + w := hashing.NewWriter(&buf, t.hash) + + n, err := w.Write([]byte(t.text)) + + if n != len(t.text) { + Fail("not enough bytes read") + } + + if err != nil { + panic(err) + } + + digest := w.Hash() + + h := hex.EncodeToString(digest) + Expect(h).Should(Equal(t.digest)) + } + }) + }) + }) +}) diff --git a/hashing/implementation.go b/hashing/implementation.go new file mode 100644 index 000000000..b83f24cb0 --- /dev/null +++ b/hashing/implementation.go @@ -0,0 +1,109 @@ +// Copyright (c) 2014, Alexander Neumann +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Packgae hashing provides hashing readers and writers. +package hashing + +import ( + "hash" + "io" +) + +// Reader is the interfaces that wraps a normal reader. When Hash() is called, +// it returns the hash for all data that has been read so far. +type Reader interface { + io.Reader + Hash() []byte +} + +// Writer is the interfaces that wraps a normal writer. When Hash() is called, +// it returns the hash for all data that has been written so far. +type Writer interface { + io.Writer + Hash() []byte +} + +type reader struct { + reader io.Reader + hash hash.Hash +} + +// NewReader wraps an io.Reader and in addition feeds all data read through the +// given hash. +func NewReader(r io.Reader, h func() hash.Hash) *reader { + return &reader{ + reader: r, + hash: h(), + } +} + +func (h *reader) Read(p []byte) (int, error) { + // call original reader + n, err := h.reader.Read(p) + + // hash bytes + if n > 0 { + // hash + h.hash.Write(p[0:n]) + } + + // return result + return n, err +} + +func (h *reader) Hash() []byte { + return h.hash.Sum([]byte{}) +} + +type writer struct { + writer io.Writer + hash hash.Hash +} + +// NewWriter wraps an io.Reader and in addition feeds all data written through +// the given hash. +func NewWriter(w io.Writer, h func() hash.Hash) *writer { + return &writer{ + writer: w, + hash: h(), + } +} + +func (h *writer) Write(p []byte) (int, error) { + // call original writer + n, err := h.writer.Write(p) + + // hash bytes + if n > 0 { + // hash + h.hash.Write(p[0:n]) + } + + // return result + return n, err +} + +func (h *writer) Hash() []byte { + return h.hash.Sum([]byte{}) +}