1
0
mirror of https://github.com/taigrr/arc synced 2025-01-18 04:33:13 -08:00

initial import

This commit is contained in:
Will
2016-05-22 22:16:37 +09:00
commit 0075ef607f
256 changed files with 58234 additions and 0 deletions

76
archive/archive.go Normal file
View File

@@ -0,0 +1,76 @@
// Copyright (C) 2016 - Will Glozer. All rights reserved.
package archive
import (
"crypto/rand"
"crypto/subtle"
"io"
"github.com/wg/ecies/xchacha20poly1305"
)
const KeySize = xchacha20poly1305.KeySize
type Archive struct {
xchacha20poly1305.XChaCha20Poly1305
tag [xchacha20poly1305.TagSize]byte
io.Reader
io.Writer
}
func NewArchiveFromReader(r io.Reader, key []byte) (*Archive, error) {
var nonce [xchacha20poly1305.NonceSize]byte
a := &Archive{Reader: r}
if _, err := io.ReadFull(r, a.tag[:]); err != nil {
return nil, err
}
if _, err := io.ReadFull(r, nonce[:]); err != nil {
return nil, err
}
err := a.Init(key, nonce[:])
return a, err
}
func NewArchiveForWriter(w io.Writer, key []byte) (*Archive, error) {
var nonce [xchacha20poly1305.NonceSize]byte
a := &Archive{Writer: w}
if _, err := rand.Read(nonce[:]); err != nil {
return nil, err
}
if err := a.Init(key, nonce[:]); err != nil {
return nil, err
}
if _, err := w.Write(a.tag[:]); err != nil {
return nil, err
}
if _, err := w.Write(nonce[:]); err != nil {
return nil, err
}
return a, nil
}
func (a *Archive) Read(b []byte) (int, error) {
n, err := a.Reader.Read(b)
a.Decrypt(b[:n], b[:n])
return n, err
}
func (a *Archive) Write(b []byte) (int, error) {
a.Encrypt(b, b)
return a.Writer.Write(b)
}
func (a *Archive) Verify() bool {
var tag [xchacha20poly1305.TagSize]byte
a.Tag(tag[:0])
return subtle.ConstantTimeCompare(a.tag[:], tag[:]) == 1
}

170
archive/archive_test.go Normal file
View File

@@ -0,0 +1,170 @@
// Copyright (C) 2016 - Will Glozer. All rights reserved.
package archive
import (
"archive/tar"
"bytes"
"crypto/rand"
"io"
"io/ioutil"
"testing"
)
func TestCreateArchive(t *testing.T) {
entries := []*tar.Header{
{Name: "foo", Size: 0},
{Name: "bar", Size: 1<<16 - 1},
{Name: "baz", Size: 64},
}
key := randomKey()
buf, dat, err := createArchive(key, entries)
if err != nil {
t.Fatal(err)
}
r, err := NewReader(buf, key)
if err != nil {
t.Fatal(err)
}
for i, e := range entries {
switch next, err := r.Next(); {
case err != nil:
t.Fatal(err)
case e.Name != next.Name:
t.Fatalf("expected entry name %s got %s", e.Name, next.Name)
case e.Size != next.Size:
t.Fatalf("expected entry size %d got %d", e.Size, next.Size)
}
switch b, err := ioutil.ReadAll(r); {
case err != nil:
t.Fatal(err)
case int(e.Size) != len(b):
t.Fatalf("expected to read %d bytes got %d", e.Size, len(b))
case !bytes.Equal(b, dat[i]):
t.Fatalf("expected content '%v' got '%v'", b, dat[i])
}
}
if !r.Verify() {
t.Fatal("archive verify failed")
}
}
func TestVerifyArchive(t *testing.T) {
entries := []*tar.Header{
{Name: "foo", Size: 32},
{Name: "bar", Size: 64},
}
key := randomKey()
buf, _, err := createArchive(key, entries)
if err != nil {
t.Fatal(err)
}
if valid, _ := Verify(buf, key); !valid {
t.Fatal("archive verify failed")
}
}
func TestVerifyFailWrongKey(t *testing.T) {
entries := []*tar.Header{
{Name: "foo", Size: 32},
{Name: "bar", Size: 64},
}
key := randomKey()
buf, _, err := createArchive(key, entries)
if err != nil {
t.Fatal(err)
}
key[0] = ^key[0]
if valid, _ := Verify(buf, key); valid {
t.Fatal("verified invalid archive")
}
}
func TestVerifyFailByteFlip(t *testing.T) {
entries := []*tar.Header{
{Name: "foo", Size: 32},
{Name: "bar", Size: 64},
}
key := randomKey()
buf, _, err := createArchive(key, entries)
if err != nil {
t.Fatal(err)
}
archive := buf.Bytes()
for i, b := range archive {
archive[i] = ^archive[i]
r := bytes.NewReader(archive)
if valid, _ := Verify(r, key); valid {
t.Fatal("verified invalid archive at", i)
}
archive[i] = b
}
}
func TestWriterInvariants(t *testing.T) {
_, _, err := createArchive(make([]byte, 31), nil)
if err == nil {
t.Fatalf("created archive with 31 byte key")
}
}
func createArchive(key []byte, entries []*tar.Header) (*Buffer, [][]byte, error) {
buf := &Buffer{}
arc, err := NewWriter(buf, key)
if err != nil {
return nil, nil, err
}
dat := make([][]byte, len(entries))
for i, e := range entries {
err := arc.Add(e)
if err != nil {
return nil, nil, err
}
dat[i] = make([]byte, e.Size)
_, err = rand.Read(dat[i])
if err != nil {
return nil, nil, err
}
err = arc.Copy(bytes.NewReader(dat[i]), e.Size)
if err != nil {
return nil, nil, err
}
}
tag, _ := arc.Finish()
copy(buf.Bytes()[0:16], tag)
return buf, dat, nil
}
type Buffer struct {
bytes.Buffer
}
func randomKey() []byte {
key := make([]byte, 32)
_, err := io.ReadFull(rand.Reader, key)
if err != nil {
panic(err)
}
return key
}

47
archive/reader.go Normal file
View File

@@ -0,0 +1,47 @@
// Copyright (C) 2016 - Will Glozer. All rights reserved.
package archive
import (
"archive/tar"
"compress/gzip"
"io"
)
type Reader struct {
archiver *tar.Reader
compressor *gzip.Reader
archive *Archive
}
func NewReader(r io.Reader, key []byte) (*Reader, error) {
archive, err := NewArchiveFromReader(r, key)
if err != nil {
return nil, err
}
compressor, err := gzip.NewReader(archive)
if err != nil {
return nil, err
}
archiver := tar.NewReader(compressor)
return &Reader{
archiver: archiver,
compressor: compressor,
archive: archive,
}, nil
}
func (r *Reader) Next() (*tar.Header, error) {
return r.archiver.Next()
}
func (r *Reader) Read(b []byte) (int, error) {
return r.archiver.Read(b)
}
func (r *Reader) Verify() bool {
return r.archive.Verify()
}

22
archive/verify.go Normal file
View File

@@ -0,0 +1,22 @@
// Copyright (C) 2016 - Will Glozer. All rights reserved.
package archive
import (
"io"
"io/ioutil"
)
func Verify(r io.Reader, key []byte) (bool, error) {
archive, err := NewArchiveFromReader(r, key)
if err != nil {
return false, err
}
_, err = io.Copy(ioutil.Discard, archive)
if err != nil {
return false, err
}
return archive.Verify(), nil
}

63
archive/writer.go Normal file
View File

@@ -0,0 +1,63 @@
// Copyright (C) 2016 - Will Glozer. All rights reserved.
package archive
import (
"archive/tar"
"errors"
"io"
"github.com/klauspost/compress/gzip"
)
var (
ErrShortCopy = errors.New("archive: short copy")
)
type Writer struct {
archiver *tar.Writer
compressor *gzip.Writer
archive *Archive
}
func NewWriter(w io.Writer, key []byte) (*Writer, error) {
archive, err := NewArchiveForWriter(w, key)
if err != nil {
return nil, err
}
compressor := gzip.NewWriter(archive)
archiver := tar.NewWriter(compressor)
return &Writer{
archiver: archiver,
compressor: compressor,
archive: archive,
}, nil
}
func (w *Writer) Add(header *tar.Header) error {
return w.archiver.WriteHeader(header)
}
func (w *Writer) Copy(r io.Reader, size int64) error {
switch n, err := io.Copy(w.archiver, r); {
case err != nil:
return err
case n < size:
return ErrShortCopy
}
return w.archiver.Flush()
}
func (w *Writer) Finish() ([]byte, error) {
if err := w.archiver.Close(); err != nil {
return nil, err
}
if err := w.compressor.Close(); err != nil {
return nil, err
}
return w.archive.Tag(nil), nil
}