From 9f848c1851348171a8949f0f5b00ae9d9129ef35 Mon Sep 17 00:00:00 2001 From: ItalyPaleAle <43508+ItalyPaleAle@users.noreply.github.com> Date: Sun, 13 Sep 2020 01:08:35 +0000 Subject: [PATCH] Misc updates and improvements Also, removed some duplicate code. --- .gitignore | 3 + Makefile | 18 +++- crypto/key.go | 5 +- crypto/key_test.go | 214 +++++++++++++++++++++++++++++++++++++++++++++ utils/io-ctx.go | 34 +++++-- utils/io.go | 36 -------- 6 files changed, 265 insertions(+), 45 deletions(-) create mode 100644 crypto/key_test.go delete mode 100644 utils/io.go diff --git a/.gitignore b/.gitignore index 4179168..7cab7e8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,9 @@ bin packrd/ *-packr.go +# Test results +coverage.txt + # Development old/ test/ diff --git a/Makefile b/Makefile index 521c157..d116c7a 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,27 @@ APP_VERSION ?= canary +# Performs a builld all: build +# Fetches the tools that are required to build prvt +get-tools: + mkdir -p .bin + curl -sf https://gobinaries.com/github.com/ory/go-acc@v0.2.6 | PREFIX=.bin/ sh + curl -sf https://gobinaries.com/github.com/gobuffalo/packr/packr2@v2.7.1 | PREFIX=.bin/ sh + clean: rm -rfv ui/dist/* - rm -rfv .bin bin - packr2 clean + rm -rfv .bin/prvt* bin + .bin/packr2 clean build: build-ui - packr2 + .bin/packr2 go build -o bin build-ui: (cd ui; npm ci; APP_VERSION="$(APP_VERSION)" npm run build) + +test: + GPGKEY_ID="0x4C6D7DB1D92F58EE" + GPGKEY_USER="prvt CI " + .bin/go-acc ./... -- -v diff --git a/crypto/key.go b/crypto/key.go index 98bbac8..a4e0008 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -83,7 +83,10 @@ func WrapKey(wrappingKey []byte, key []byte) ([]byte, error) { // UnwrapKey unwraps a key wrapped with a 32-byte key func UnwrapKey(wrappingKey []byte, wrappedKey []byte) ([]byte, error) { if len(wrappingKey) != 32 { - return nil, errors.New("keys must be 32-byte long") + return nil, errors.New("wrapping key must be 32-byte long") + } + if len(wrappedKey) != 40 { + return nil, errors.New("wrapped key must be 40-byte long") } // Get the key wrapper diff --git a/crypto/key_test.go b/crypto/key_test.go new file mode 100644 index 0000000..7db1e32 --- /dev/null +++ b/crypto/key_test.go @@ -0,0 +1,214 @@ +/* +Copyright © 2020 Alessandro Segala (@ItalyPaleAle) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +package crypto + +import ( + "bytes" + "encoding/hex" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRandomBytes(t *testing.T) { + var ( + res1 []byte + res2 []byte + err error + ) + // Should return an amount of bytes that's different every time + res1, err = RandomBytes(20) + assert.NoError(t, err) + assert.Len(t, res1, 20) + res2, err = RandomBytes(20) + assert.NoError(t, err) + assert.Len(t, res2, 20) + assert.True(t, !bytes.Equal(res1, res2)) +} + +func TestNewKey(t *testing.T) { + // Should return 32 bytes + res, err := NewKey() + assert.NoError(t, err) + assert.Len(t, res, 32) +} + +func TestNewSalt(t *testing.T) { + // Should return 16 bytes + res, err := NewSalt() + assert.NoError(t, err) + assert.Len(t, res, 16) +} + +func TestKeyFromPassphrase(t *testing.T) { + var ( + key []byte + confHash []byte + err error + ) + // Test from a table + table := []struct { + Salt []byte + Passphrase string + Key []byte + ConfHash []byte + }{ + { + Salt: []byte("aaaaaaaaaaaaaaaa"), + Passphrase: "hello world", + Key: decodeHex("0d8e68828d5242395fbee0cde9ab2e9c26907293c0a32a37f8282c8e6c06b3fc"), + ConfHash: decodeHex("e6493a58c2ca55d3911a4fa99a35339dae949dd69de265020f7443c91d3b3162"), + }, + { + Salt: []byte("aaaaaaaaaaaaaaaa"), + Passphrase: "m'illumino d'immenso", + Key: decodeHex("2efb520aa19ced975217c3b177d746575837822a3078422df49152c34882e261"), + ConfHash: decodeHex("edf8833939295d0dfc4a276cf2c147d7ba9bbd6e6e072304ef600ef6c7af1bb6"), + }, + { + Salt: []byte("abababababababab"), + Passphrase: "m'illumino d'immenso", + Key: decodeHex("9ce59682265c8f4e49cfb255ca8aad51f691a3399b0693ab64ddafddd41af34f"), + ConfHash: decodeHex("d68a5fc851d775c11a563530d0ef6846701a0bcda80d5e5b73463ea1f4ac01a7"), + }, + } + + for _, el := range table { + key, confHash, err = KeyFromPassphrase(el.Passphrase, el.Salt) + assert.NoError(t, err) + assert.Equal(t, el.Key, key) + assert.Equal(t, el.ConfHash, confHash) + } + + // Test errors + key, confHash, err = KeyFromPassphrase("", []byte("abababababababab")) + assert.EqualError(t, err, "empty passphrase") + assert.Nil(t, key) + assert.Nil(t, confHash) + key, confHash, err = KeyFromPassphrase("foo", []byte("123")) + assert.EqualError(t, err, "invalid salt") + assert.Nil(t, key) + assert.Nil(t, confHash) +} + +func TestWrapKey(t *testing.T) { + var ( + res []byte + err error + ) + + // Test from a table + table := []struct { + WrappingKey []byte + Key []byte + Expect []byte + }{ + // Test vectors from https://github.com/google/wycheproof/blob/master/testvectors/kwp_test.json + { + WrappingKey: decodeHex("38e1b1d075d9d852b9a6c01c8ff6965af01bac457a4e339ae3e1d7b2ffacc0cd"), + Key: decodeHex("80ad6820f1c90981e2ca42b817a345c1179d0a11d8e23a8adc0505e13d87295a"), + Expect: decodeHex("b63b7e0fec7e315816233db6758fd3e744b9f6a40862bdf866487e53bcb950d8b2649269e51b4475"), + }, + { + WrappingKey: decodeHex("c641f1689d81caa8ba37d895272240664054ed974cfffc40e6c5c0cad1b916c7"), + Key: decodeHex("3fd0ba19955e46749f54d88e99d080b7339d588fe612ec0f4021ca3ca2104270"), + Expect: decodeHex("837cfc316b49299edaf427e0988020ee876204b29d847669daab72c8660b0d860e9de3bd851198ff"), + }, + { + WrappingKey: decodeHex("aa0ab9d68ed4a04e723f81b44c0c88d0bcde7a80cfd476eb4b8836d9aa01ec4c"), + Key: decodeHex("57faa8766f6d6a0aa1cf643f857c150df5b31303b50af480e21c4b5e8c8a15d5"), + Expect: decodeHex("0e9e2e9aa34bbf973d67bc534ac86fc5b5a5f9da5f026866177894ec6077a5c84501510e1bf4afb3"), + }, + } + + for _, el := range table { + res, err = WrapKey(el.WrappingKey, el.Key) + assert.NoError(t, err) + assert.Equal(t, el.Expect, res) + } + + // Test errors + res, err = WrapKey( + decodeHex("aabbcc"), + decodeHex("57faa8766f6d6a0aa1cf643f857c150df5b31303b50af480e21c4b5e8c8a15d5"), + ) + assert.EqualError(t, err, "keys must be 32-byte long") + assert.Nil(t, res) + res, err = WrapKey( + decodeHex("aa0ab9d68ed4a04e723f81b44c0c88d0bcde7a80cfd476eb4b8836d9aa01ec4c"), + decodeHex("aabbcc"), + ) + assert.EqualError(t, err, "keys must be 32-byte long") + assert.Nil(t, res) +} + +func TestUnwrapKey(t *testing.T) { + var ( + res []byte + err error + ) + + // Test from a table + table := []struct { + WrappingKey []byte + WrappedKey []byte + Expect []byte + }{ + // Test vectors from https://github.com/google/wycheproof/blob/master/testvectors/kwp_test.json + { + WrappingKey: decodeHex("38e1b1d075d9d852b9a6c01c8ff6965af01bac457a4e339ae3e1d7b2ffacc0cd"), + WrappedKey: decodeHex("b63b7e0fec7e315816233db6758fd3e744b9f6a40862bdf866487e53bcb950d8b2649269e51b4475"), + Expect: decodeHex("80ad6820f1c90981e2ca42b817a345c1179d0a11d8e23a8adc0505e13d87295a"), + }, + { + WrappingKey: decodeHex("c641f1689d81caa8ba37d895272240664054ed974cfffc40e6c5c0cad1b916c7"), + WrappedKey: decodeHex("837cfc316b49299edaf427e0988020ee876204b29d847669daab72c8660b0d860e9de3bd851198ff"), + Expect: decodeHex("3fd0ba19955e46749f54d88e99d080b7339d588fe612ec0f4021ca3ca2104270"), + }, + { + WrappingKey: decodeHex("aa0ab9d68ed4a04e723f81b44c0c88d0bcde7a80cfd476eb4b8836d9aa01ec4c"), + WrappedKey: decodeHex("0e9e2e9aa34bbf973d67bc534ac86fc5b5a5f9da5f026866177894ec6077a5c84501510e1bf4afb3"), + Expect: decodeHex("57faa8766f6d6a0aa1cf643f857c150df5b31303b50af480e21c4b5e8c8a15d5"), + }, + } + + for _, el := range table { + res, err = UnwrapKey(el.WrappingKey, el.WrappedKey) + assert.NoError(t, err) + assert.Equal(t, el.Expect, res) + } + + // Test errors + res, err = UnwrapKey( + decodeHex("aabbcc"), + decodeHex("0e9e2e9aa34bbf973d67bc534ac86fc5b5a5f9da5f026866177894ec6077a5c84501510e1bf4afb3"), + ) + assert.EqualError(t, err, "wrapping key must be 32-byte long") + assert.Nil(t, res) + res, err = UnwrapKey( + decodeHex("aa0ab9d68ed4a04e723f81b44c0c88d0bcde7a80cfd476eb4b8836d9aa01ec4c"), + decodeHex("aabbcc"), + ) + assert.EqualError(t, err, "wrapped key must be 40-byte long") + assert.Nil(t, res) +} + +func decodeHex(str string) []byte { + res, _ := hex.DecodeString(str) + return res +} diff --git a/utils/io-ctx.go b/utils/io-ctx.go index b3d806c..0ed210f 100644 --- a/utils/io-ctx.go +++ b/utils/io-ctx.go @@ -22,14 +22,16 @@ import ( "io" ) -type ioctx func(p []byte) (int, error) +type CtxReader func(p []byte) (n int, err error) -func (f ioctx) Read(p []byte) (n int, err error) { - return f(p) -} +func (rf CtxReader) Read(p []byte) (n int, err error) { return rf(p) } + +type CtxWriter func(p []byte) (n int, err error) + +func (rf CtxWriter) Write(p []byte) (n int, err error) { return rf(p) } // ReaderFuncWithContext returns a stream reader that supports a context -func ReaderFuncWithContext(ctx context.Context, r io.Reader) ioctx { +func ReaderFuncWithContext(ctx context.Context, r io.Reader) CtxReader { return func(p []byte) (int, error) { select { case <-ctx.Done(): @@ -39,3 +41,25 @@ func ReaderFuncWithContext(ctx context.Context, r io.Reader) ioctx { } } } + +func CtxCopy(ctx context.Context, dst io.Writer, src io.Reader) (written int64, err error) { + return io.Copy(dst, CtxReader(func(p []byte) (int, error) { + select { + case <-ctx.Done(): + return 0, ctx.Err() + default: + return src.Read(p) + } + })) +} + +func CtxCopyN(ctx context.Context, dst io.Writer, src io.Reader, n int64) (written int64, err error) { + return io.CopyN(dst, CtxReader(func(p []byte) (int, error) { + select { + case <-ctx.Done(): + return 0, ctx.Err() + default: + return src.Read(p) + } + }), n) +} diff --git a/utils/io.go b/utils/io.go deleted file mode 100644 index d68d47a..0000000 --- a/utils/io.go +++ /dev/null @@ -1,36 +0,0 @@ -package utils - -import ( - "context" - "io" -) - -type CtxReader func(p []byte) (n int, err error) - -func (rf CtxReader) Read(p []byte) (n int, err error) { return rf(p) } - -type CtxWriter func(p []byte) (n int, err error) - -func (rf CtxWriter) Write(p []byte) (n int, err error) { return rf(p) } - -func CtxCopy(ctx context.Context, dst io.Writer, src io.Reader) (written int64, err error) { - return io.Copy(dst, CtxReader(func(p []byte) (int, error) { - select { - case <-ctx.Done(): - return 0, ctx.Err() - default: - return src.Read(p) - } - })) -} - -func CtxCopyN(ctx context.Context, dst io.Writer, src io.Reader, n int64) (written int64, err error) { - return io.CopyN(dst, CtxReader(func(p []byte) (int, error) { - select { - case <-ctx.Done(): - return 0, ctx.Err() - default: - return src.Read(p) - } - }), n) -}