From fb885691f3855da29d7f7c9a4507dcb147e34dcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D0=B4=D0=BE=D0=B2=D0=B0=20=D0=98=D1=80=D0=B8?= =?UTF-8?q?=D0=BD=D0=B0?= Date: Fri, 10 Jan 2020 01:26:41 +0500 Subject: [PATCH] Implement libsodium Crypter (#512) * Implement libsodium Crypter * Environment variable for possible build without libsodium * Add explanation comment Co-authored-by: IrinaSedova --- .travis.yml | 2 + Makefile | 25 +++-- PostgreSQL.md | 10 ++ docker/mysql_tests/Dockerfile | 1 + docker/pg_tests/Dockerfile_prefix | 1 + docker/redis_tests/Dockerfile | 1 + internal/config.go | 2 + internal/configure.go | 4 + internal/configure_crypter.go | 22 +++++ internal/configure_crypter_libsodium.go | 21 ++++ internal/crypto/libsodium/crypter.go | 94 ++++++++++++++++++ internal/crypto/libsodium/crypter_test.go | 69 +++++++++++++ internal/crypto/libsodium/reader.go | 110 +++++++++++++++++++++ internal/crypto/libsodium/testdata/testKey | 2 + internal/crypto/libsodium/writer.go | 101 +++++++++++++++++++ link_brotli.sh | 4 +- link_libsodium.sh | 19 ++++ 17 files changed, 480 insertions(+), 8 deletions(-) create mode 100644 internal/configure_crypter.go create mode 100644 internal/configure_crypter_libsodium.go create mode 100644 internal/crypto/libsodium/crypter.go create mode 100644 internal/crypto/libsodium/crypter_test.go create mode 100644 internal/crypto/libsodium/reader.go create mode 100644 internal/crypto/libsodium/testdata/testKey create mode 100644 internal/crypto/libsodium/writer.go create mode 100755 link_libsodium.sh diff --git a/.travis.yml b/.travis.yml index 68a126dc8..68ad8e4e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,8 @@ jobs: - ${CACHE_FILE_UBUNTU} - ${CACHE_FILE_GOLANG} - stage: test + env: + - USE_LIBSODIUM=1 script: make all_unittests - script: make TEST="pg_backup_mark_impermanent_test" pg_integration_test workspaces: diff --git a/Makefile b/Makefile index 5e81aa549..a3a7f14bb 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,12 @@ PKG := github.com/wal-g/wal-g COVERAGE_FILE := coverage.out TEST := "pg_tests" +ifeq ($(USE_LIBSODIUM),) + LIBSODIUM_TAG="" +else + LIBSODIUM_TAG=" libsodium" +endif + .PHONY: unittest fmt lint install clean test: install deps lint unittest pg_build mysql_build redis_build mongo_build unlink_brotli pg_integration_test mysql_integration_test redis_integration_test @@ -17,7 +23,7 @@ test: install deps lint unittest pg_build mysql_build redis_build mongo_build un pg_test: install deps pg_build lint unlink_brotli pg_integration_test pg_build: $(CMD_FILES) $(PKG_FILES) - (cd $(MAIN_PG_PATH) && go build -tags "brotli lzo" -o wal-g -ldflags "-s -w -X github.com/wal-g/wal-g/cmd/pg.BuildDate=`date -u +%Y.%m.%d_%H:%M:%S` -X github.com/wal-g/wal-g/cmd/pg.GitRevision=`git rev-parse --short HEAD` -X github.com/wal-g/wal-g/cmd/pg.WalgVersion=`git tag -l --points-at HEAD`") + (cd $(MAIN_PG_PATH) && go build -tags "brotli lzo$(LIBSODIUM_TAG)" -o wal-g -ldflags "-s -w -X github.com/wal-g/wal-g/cmd/pg.BuildDate=`date -u +%Y.%m.%d_%H:%M:%S` -X github.com/wal-g/wal-g/cmd/pg.GitRevision=`git rev-parse --short HEAD` -X github.com/wal-g/wal-g/cmd/pg.WalgVersion=`git tag -l --points-at HEAD`") install_and_build_pg: install deps pg_build @@ -59,7 +65,7 @@ pg_install: pg_build mysql_test: install deps mysql_build lint unlink_brotli mysql_integration_test mysql_build: $(CMD_FILES) $(PKG_FILES) - (cd $(MAIN_MYSQL_PATH) && go build -tags brotli -o wal-g -ldflags "-s -w -X github.com/wal-g/wal-g/cmd/mysql.BuildDate=`date -u +%Y.%m.%d_%H:%M:%S` -X github.com/wal-g/wal-g/cmd/mysql.GitRevision=`git rev-parse --short HEAD` -X github.com/wal-g/wal-g/cmd/mysql.WalgVersion=`git tag -l --points-at HEAD`") + (cd $(MAIN_MYSQL_PATH) && go build -tags "brotli$(LIBSODIUM_TAG)" -o wal-g -ldflags "-s -w -X github.com/wal-g/wal-g/cmd/mysql.BuildDate=`date -u +%Y.%m.%d_%H:%M:%S` -X github.com/wal-g/wal-g/cmd/mysql.GitRevision=`git rev-parse --short HEAD` -X github.com/wal-g/wal-g/cmd/mysql.WalgVersion=`git tag -l --points-at HEAD`") load_docker_common: @if [ "x" = "${CACHE_FILE_UBUNTU}x" ]; then\ @@ -84,7 +90,7 @@ mysql_install: mysql_build mongo_test: install deps mongo_build lint unlink_brotli mongo_build: $(CMD_FILES) $(PKG_FILES) - (cd $(MAIN_MONGO_PATH) && go build -tags brotli -o wal-g -ldflags "-s -w -X github.com/wal-g/wal-g/cmd/mongo.BuildDate=`date -u +%Y.%m.%d_%H:%M:%S` -X github.com/wal-g/wal-g/cmd/mongo.GitRevision=`git rev-parse --short HEAD` -X github.com/wal-g/wal-g/cmd/mongo.WalgVersion=`git tag -l --points-at HEAD`") + (cd $(MAIN_MONGO_PATH) && go build -tags "brotli$(LIBSODIUM_TAG)" -o wal-g -ldflags "-s -w -X github.com/wal-g/wal-g/cmd/mongo.BuildDate=`date -u +%Y.%m.%d_%H:%M:%S` -X github.com/wal-g/wal-g/cmd/mongo.GitRevision=`git rev-parse --short HEAD` -X github.com/wal-g/wal-g/cmd/mongo.WalgVersion=`git tag -l --points-at HEAD`") mongo_install: mongo_build mv $(MAIN_MONGO_PATH)/wal-g $(GOBIN)/wal-g @@ -100,7 +106,7 @@ mongo_features: install deps mongo_build lint unlink_brotli redis_test: install deps redis_build lint unlink_brotli redis_integration_test redis_build: $(CMD_FILES) $(PKG_FILES) - (cd $(MAIN_REDIS_PATH) && go build -tags brotli -o wal-g -ldflags "-s -w -X github.com/wal-g/wal-g/cmd/redis.BuildDate=`date -u +%Y.%m.%d_%H:%M:%S` -X github.com/wal-g/wal-g/cmd/redis.GitRevision=`git rev-parse --short HEAD` -X github.com/wal-g/wal-g/cmd/redis.WalgVersion=`git tag -l --points-at HEAD`") + (cd $(MAIN_REDIS_PATH) && go build -tags "brotli$(LIBSODIUM_TAG)" -o wal-g -ldflags "-s -w -X github.com/wal-g/wal-g/cmd/redis.BuildDate=`date -u +%Y.%m.%d_%H:%M:%S` -X github.com/wal-g/wal-g/cmd/redis.GitRevision=`git rev-parse --short HEAD` -X github.com/wal-g/wal-g/cmd/redis.WalgVersion=`git tag -l --points-at HEAD`") redis_integration_test: load_docker_common docker-compose build redis redis_tests @@ -119,6 +125,9 @@ unittest: go test -v $(TEST_MODIFIER) ./internal/compression/ go test -v $(TEST_MODIFIER) ./internal/crypto/openpgp/ go test -v $(TEST_MODIFIER) ./internal/crypto/awskms/ + @if [[ ! -z "${USE_LIBSODIUM}" ]]; then\ + go test -v $(TEST_MODIFIER) ./internal/crypto/libsodium/;\ + fi go test -v $(TEST_MODIFIER) ./internal/databases/mysql go test -v $(TEST_MODIFIER) ./internal/walparser/ go test -v $(TEST_MODIFIER) ./utility @@ -139,6 +148,7 @@ deps: dep ensure -update github.com/cyberdelia/lzo sed -i 's|\(#cgo LDFLAGS:\) .*|\1 -Wl,-Bstatic -llzo2 -Wl,-Bdynamic|' vendor/github.com/cyberdelia/lzo/lzo.go ./link_brotli.sh + ./link_libsodium.sh install: go get -u github.com/golang/dep/cmd/dep @@ -146,5 +156,8 @@ install: unlink_brotli: rm -rf vendor/github.com/google/brotli/* - mv tmp/* vendor/github.com/google/brotli/ - rm -rf tmp/ + mv tmp/brotli/* vendor/github.com/google/brotli/ + rm -rf tmp/brotli + +unlink_libsodium: + rm -rf tmp/libsodium diff --git a/PostgreSQL.md b/PostgreSQL.md index 554f9da90..f3a8a7f43 100644 --- a/PostgreSQL.md +++ b/PostgreSQL.md @@ -8,6 +8,8 @@ Development To compile and build the binary for Postgres: +(To build with libsodium, just set `USE_LIBSODIUM` environment variable) + ``` go get github.com/wal-g/wal-g cd $GOPATH/src/github.com/wal-g/wal-g @@ -63,6 +65,14 @@ This setting allows backup automation tools to add extra information to JSON sen If this setting is specified, during ```wal-push``` WAL-G will check the existence of WAL before uploading it. If the different file is already archived under the same name, WAL-G will return the non-zero exit code to prevent PostgreSQL from removing WAL. +* `WALG_LIBSODIUM_KEY` + +To configure encryption and decryption with libsodium. WAL-G uses an [algorithm](https://download.libsodium.org/doc/secret-key_cryptography/secretstream#algorithm) that only requires a secret key. + +* `WALG_LIBSODIUM_KEY_PATH` + +Similar to `WALG_LIBSODIUM_KEY`, but value is the path to the key on file system. The file content will be trimmed from whitespace characters. + * `WALG_GPG_KEY_ID` (alternative form `WALE_GPG_KEY_ID`) ⚠️ **DEPRECATED** To configure GPG key for encryption and decryption. By default, no encryption is used. Public keyring is cached in the file "/.walg_key_cache". diff --git a/docker/mysql_tests/Dockerfile b/docker/mysql_tests/Dockerfile index d0686df22..74fba3a77 100644 --- a/docker/mysql_tests/Dockerfile +++ b/docker/mysql_tests/Dockerfile @@ -5,6 +5,7 @@ WORKDIR /go/src/github.com/wal-g/wal-g RUN apt-get update && \ apt-get install --yes --no-install-recommends --no-install-suggests +COPY tmp/libsodium tmp/libsodium COPY vendor/ vendor/ COPY internal/ internal/ COPY cmd/ cmd/ diff --git a/docker/pg_tests/Dockerfile_prefix b/docker/pg_tests/Dockerfile_prefix index 3d221b3f8..6fb932d69 100644 --- a/docker/pg_tests/Dockerfile_prefix +++ b/docker/pg_tests/Dockerfile_prefix @@ -6,6 +6,7 @@ RUN apt-get update && \ apt-get install --yes --no-install-recommends --no-install-suggests \ liblzo2-dev +COPY tmp/libsodium tmp/libsodium COPY vendor/ vendor/ COPY internal/ internal/ COPY cmd/ cmd/ diff --git a/docker/redis_tests/Dockerfile b/docker/redis_tests/Dockerfile index cd2a22114..5fa53741b 100644 --- a/docker/redis_tests/Dockerfile +++ b/docker/redis_tests/Dockerfile @@ -5,6 +5,7 @@ WORKDIR /go/src/github.com/wal-g/wal-g RUN apt-get update && \ apt-get install --yes --no-install-recommends --no-install-suggests +COPY tmp/libsodium tmp/libsodium COPY vendor/ vendor/ COPY internal/ internal/ COPY cmd/ cmd/ diff --git a/internal/config.go b/internal/config.go index 5a518f4ed..ca0bd0eac 100644 --- a/internal/config.go +++ b/internal/config.go @@ -25,6 +25,8 @@ const ( TarSizeThresholdSetting = "WALG_TAR_SIZE_THRESHOLD" CseKmsIDSetting = "WALG_CSE_KMS_ID" CseKmsRegionSetting = "WALG_CSE_KMS_REGION" + LibsodiumKeySetting = "WALG_LIBSODIUM_KEY" + LibsodiumKeyPathSetting = "WALG_LIBSODIUM_KEY_PATH" GpgKeyIDSetting = "GPG_KEY_ID" PgpKeySetting = "WALG_PGP_KEY" PgpKeyPathSetting = "WALG_PGP_KEY_PATH" diff --git a/internal/configure.go b/internal/configure.go index 93f9a9a21..69369b37d 100644 --- a/internal/configure.go +++ b/internal/configure.go @@ -259,6 +259,10 @@ func ConfigureCrypter() crypto.Crypter { return awskms.CrypterFromKeyID(viper.GetString(CseKmsIDSetting), viper.GetString(CseKmsRegionSetting)) } + if crypter := configureLibsodiumCrypter(); crypter != nil { + return crypter + } + return nil } diff --git a/internal/configure_crypter.go b/internal/configure_crypter.go new file mode 100644 index 000000000..62b9fefee --- /dev/null +++ b/internal/configure_crypter.go @@ -0,0 +1,22 @@ +// +build !libsodium + +package internal + +// This file contains functions that should return `nil`, +// in order to be able to build wal-g without specific implementations of the crypter. +// And the configure_crypter_.go files must have a real implementation of the function. +// +// Thus, if the tag is missing, the condition: +// if crypter := configureCrypter(); crypter != nil { +// return crypter +// } +// will never be met. +// If there is a tag, we can configure the correct implementation of crypter. + +import ( + "github.com/wal-g/wal-g/internal/crypto" +) + +func configureLibsodiumCrypter() crypto.Crypter { + return nil +} diff --git a/internal/configure_crypter_libsodium.go b/internal/configure_crypter_libsodium.go new file mode 100644 index 000000000..05ffd7678 --- /dev/null +++ b/internal/configure_crypter_libsodium.go @@ -0,0 +1,21 @@ +// +build libsodium + +package internal + +import ( + "github.com/spf13/viper" + "github.com/wal-g/wal-g/internal/crypto" + "github.com/wal-g/wal-g/internal/crypto/libsodium" +) + +func configureLibsodiumCrypter() crypto.Crypter { + if viper.IsSet(LibsodiumKeySetting) { + return libsodium.CrypterFromKey(viper.GetString(LibsodiumKeySetting)) + } + + if viper.IsSet(LibsodiumKeyPathSetting) { + return libsodium.CrypterFromKeyPath(viper.GetString(LibsodiumKeyPathSetting)) + } + + return nil +} diff --git a/internal/crypto/libsodium/crypter.go b/internal/crypto/libsodium/crypter.go new file mode 100644 index 000000000..8f1906fc2 --- /dev/null +++ b/internal/crypto/libsodium/crypter.go @@ -0,0 +1,94 @@ +package libsodium + +// #cgo CFLAGS: -I../../../tmp/libsodium/include +// #cgo LDFLAGS: -L../../../tmp/libsodium/lib -lsodium +// #include +import "C" + +import ( + "io" + "io/ioutil" + "strings" + "sync" + + "github.com/pkg/errors" + "github.com/wal-g/wal-g/internal/crypto" +) + +const ( + chunkSize = 8192 +) + +// libsodium should always be initialised +func init() { + C.sodium_init() +} + +// Crypter is libsodium Crypter implementation +type Crypter struct { + Key string + KeyPath string + + mutex sync.RWMutex +} + +// CrypterFromKey creates Crypter from key +func CrypterFromKey(key string) crypto.Crypter { + return &Crypter{Key: key} +} + +// CrypterFromKeyPath creates Crypter from key path +func CrypterFromKeyPath(path string) crypto.Crypter { + return &Crypter{KeyPath: path} +} + +func (crypter *Crypter) setup() (err error) { + crypter.mutex.RLock() + + if crypter.Key == "" && crypter.KeyPath == "" { + return errors.New("libsodium Crypter must have a key or key path") + } + + if crypter.Key != "" { + crypter.mutex.RUnlock() + + return + } + + crypter.mutex.RUnlock() + + crypter.mutex.Lock() + defer crypter.mutex.Unlock() + + if crypter.Key != "" { + return + } + + key, err := ioutil.ReadFile(crypter.KeyPath) + + if err != nil { + return + } + + crypter.Key = strings.TrimSpace(string(key)) + + return nil +} + +// Encrypt creates encryption writer from ordinary writer +func (crypter *Crypter) Encrypt(writer io.Writer) (io.WriteCloser, error) { + if err := crypter.setup(); err != nil { + return nil, err + } + + return NewWriter(writer, []byte(crypter.Key)) +} + +// Decrypt creates decrypted reader from ordinary reader +func (crypter *Crypter) Decrypt(reader io.Reader) (io.Reader, error) { + if err := crypter.setup(); err != nil { + return nil, err + } + + return NewReader(reader, []byte(crypter.Key)) +} diff --git a/internal/crypto/libsodium/crypter_test.go b/internal/crypto/libsodium/crypter_test.go new file mode 100644 index 000000000..10debd42c --- /dev/null +++ b/internal/crypto/libsodium/crypter_test.go @@ -0,0 +1,69 @@ +// +build libsodium + +package libsodium + +import ( + "bytes" + "io/ioutil" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/wal-g/wal-g/internal/crypto" +) + +const ( + keyPath = "./testdata/testKey" + testKey = "TEST_LIBSODIUM_KEY" +) + +func MockCrypterFromKey() *Crypter { + return CrypterFromKey(testKey).(*Crypter) +} + +func MockCrypterFromKeyPath() *Crypter { + return CrypterFromKeyPath(keyPath).(*Crypter) +} + +func TestMockCrypterFromKey(t *testing.T) { + assert.NoError(t, MockCrypterFromKey().setup(), "setup Crypter from key error") +} + +func TestMockCrypterFromKeyPath(t *testing.T) { + assert.NoError(t, MockCrypterFromKeyPath().setup(), "setup Crypter from key path error") +} + +func TestMockCrypterFromKey_ShouldReturnErrorOnEmptyKey(t *testing.T) { + assert.Error(t, CrypterFromKey("").(*Crypter).setup(), "no error on empty key") +} + +func TestMockCrypterFromKeyPath_ShouldReturnErrorOnNonExistentFile(t *testing.T) { + assert.Error(t, CrypterFromKeyPath("").(*Crypter).setup(), "no error on non-existent key path") +} + +func EncryptionCycle(t *testing.T, crypter crypto.Crypter) { + secret := strings.Repeat(" so very secret thing ", 1000) + + buffer := new(bytes.Buffer) + encrypt, err := crypter.Encrypt(buffer) + assert.NoErrorf(t, err, "encryption error: %v", err) + + encrypt.Write([]byte(secret)) + encrypt.Close() + + decrypt, err := crypter.Decrypt(buffer) + assert.NoErrorf(t, err, "decryption error: %v", err) + + decrypted, err := ioutil.ReadAll(decrypt) + assert.NoErrorf(t, err, "decryption read error: %v", err) + + assert.Equal(t, secret, string(decrypted), "decrypted text not equals to open text") +} + +func TestEncryptionCycleFromKey(t *testing.T) { + EncryptionCycle(t, MockCrypterFromKey()) +} + +func TestEncryptionCycleFromKeyPath(t *testing.T) { + EncryptionCycle(t, MockCrypterFromKeyPath()) +} diff --git a/internal/crypto/libsodium/reader.go b/internal/crypto/libsodium/reader.go new file mode 100644 index 000000000..9962499cc --- /dev/null +++ b/internal/crypto/libsodium/reader.go @@ -0,0 +1,110 @@ +package libsodium + +// #cgo CFLAGS: -I../../../tmp/libsodium/include +// #cgo LDFLAGS: -L../../../tmp/libsodium/lib -lsodium +// #include +import "C" + +import ( + "io" + + "github.com/pkg/errors" +) + +// Reader wraps ordinary reader with libsodium decryption +type Reader struct { + io.Reader + + state C.crypto_secretstream_xchacha20poly1305_state + + in []byte + out []byte + + outIdx int + outLen int +} + +// NewReader creates Reader from ordinary reader and key +func NewReader(reader io.Reader, key []byte) (io.Reader, error) { + header := make([]byte, C.crypto_secretstream_xchacha20poly1305_HEADERBYTES) + + if _, err := io.ReadFull(reader, header); err != nil { + return nil, errors.Wrap(err, "failed to read libsodium header") + } + + var state C.crypto_secretstream_xchacha20poly1305_state + + returnCode := C.crypto_secretstream_xchacha20poly1305_init_pull( + &state, + (*C.uchar)(&header[0]), + (*C.uchar)(&key[0]), + ) + + if returnCode != 0 { + return nil, errors.New("corrupted libsodium header") + } + + return &Reader{ + Reader: reader, + + state: state, + + in: make([]byte, chunkSize+C.crypto_secretstream_xchacha20poly1305_ABYTES), + out: make([]byte, chunkSize), + }, nil +} + +// Read implements io.Reader +func (reader *Reader) Read(p []byte) (n int, err error) { + if reader.outIdx >= reader.outLen { + if err = reader.readNextChunk(); err != nil { + return + } + } + + n = copy(p, reader.out[reader.outIdx:reader.outLen]) + reader.outIdx += n + + return +} + +func (reader *Reader) readNextChunk() (err error) { + n, err := io.ReadFull(reader.Reader, reader.in) + + reader.in = reader.in[:n] + + if err != nil && err != io.ErrUnexpectedEOF { + return + } + + var outLen C.ulonglong + var tag C.uchar + + returnCode := C.crypto_secretstream_xchacha20poly1305_pull( + &reader.state, + (*C.uchar)(&reader.out[0]), + (*C.ulonglong)(&outLen), + (*C.uchar)(&tag), + (*C.uchar)(&reader.in[0]), + (C.ulonglong)(n), + (*C.uchar)(C.NULL), + (C.ulonglong)(0), + ) + + if returnCode != 0 { + err = errors.New("corrupted chunk") + } + + if tag == C.crypto_secretstream_xchacha20poly1305_TAG_FINAL && err != io.ErrUnexpectedEOF { + err = errors.New("premature end") + } + + if err == io.ErrUnexpectedEOF { + err = nil + } + + reader.outIdx = 0 + reader.outLen = int(outLen) + + return +} diff --git a/internal/crypto/libsodium/testdata/testKey b/internal/crypto/libsodium/testdata/testKey new file mode 100644 index 000000000..5ca4dfc3f --- /dev/null +++ b/internal/crypto/libsodium/testdata/testKey @@ -0,0 +1,2 @@ + +-+isw)@(s&56x57eh1%y3@r$*qs_&@$--abnm9m@18@9-e%um1 diff --git a/internal/crypto/libsodium/writer.go b/internal/crypto/libsodium/writer.go new file mode 100644 index 000000000..17b83a860 --- /dev/null +++ b/internal/crypto/libsodium/writer.go @@ -0,0 +1,101 @@ +package libsodium + +// #cgo CFLAGS: -I../../../tmp/libsodium/include +// #cgo LDFLAGS: -L../../../tmp/libsodium/lib -lsodium +// #include +import "C" + +import ( + "io" + + "github.com/pkg/errors" +) + +// Writer wraps ordinary writer with libsodium encryption +type Writer struct { + io.Writer + + state C.crypto_secretstream_xchacha20poly1305_state + + in []byte + out []byte + + inIdx int +} + +// NewWriter creates Writer from ordinary writer and key +func NewWriter(writer io.Writer, key []byte) (io.WriteCloser, error) { + header := make([]byte, C.crypto_secretstream_xchacha20poly1305_HEADERBYTES) + + var state C.crypto_secretstream_xchacha20poly1305_state + + C.crypto_secretstream_xchacha20poly1305_init_push( + &state, + (*C.uchar)(&header[0]), + (*C.uchar)(&key[0]), + ) + + if _, err := writer.Write(header); err != nil { + return nil, errors.Wrap(err, "failed to write libsodium header") + } + + return &Writer{ + Writer: writer, + + state: state, + + in: make([]byte, chunkSize), + out: make([]byte, chunkSize+C.crypto_secretstream_xchacha20poly1305_ABYTES), + }, nil +} + +// Write implements io.Writer +func (writer *Writer) Write(p []byte) (n int, err error) { + for n != len(p) { + count := copy(writer.in[writer.inIdx:], p[n:]) + + writer.inIdx += count + n += count + + if writer.inIdx == len(writer.in) { + if err = writer.writeNextChunk(false); err != nil { + return + } + } + } + + return +} + +func (writer *Writer) writeNextChunk(last bool) (err error) { + var outLen C.ulonglong + var tag C.uchar + + if last { + tag = C.crypto_secretstream_xchacha20poly1305_TAG_FINAL + } + + C.crypto_secretstream_xchacha20poly1305_push( + &writer.state, + (*C.uchar)(&writer.out[0]), + (*C.ulonglong)(&outLen), + (*C.uchar)(&writer.in[0]), + (C.ulonglong)(writer.inIdx), + (*C.uchar)(C.NULL), + (C.ulonglong)(0), + (C.uchar)(tag), + ) + + if _, err = writer.Writer.Write(writer.out[:int(outLen)]); err != nil { + return + } + + writer.inIdx = 0 + + return +} + +// Close implements io.Closer +func (writer *Writer) Close() (err error) { + return writer.writeNextChunk(true) +} diff --git a/link_brotli.sh b/link_brotli.sh index f50e2c097..459f544a7 100755 --- a/link_brotli.sh +++ b/link_brotli.sh @@ -1,8 +1,8 @@ #!/bin/sh -test -d tmp || mkdir tmp +test -d tmp/brotli || mkdir -p tmp/brotli -cp -rf vendor/github.com/google/brotli/* tmp/ +cp -rf vendor/github.com/google/brotli/* tmp/brotli/ cp -rf submodules/brotli/* vendor/github.com/google/brotli/ readonly CWD=$PWD diff --git a/link_libsodium.sh b/link_libsodium.sh new file mode 100755 index 000000000..3bdbee01f --- /dev/null +++ b/link_libsodium.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +readonly CWD=$PWD +readonly LIBSODIUM_VERSION="libsodium-1.0.17" + +test -d tmp/libsodium || mkdir -p tmp/libsodium + +cd tmp/libsodium + +curl -sL https://download.libsodium.org/libsodium/releases/$LIBSODIUM_VERSION.tar.gz -o $LIBSODIUM_VERSION.tar.gz +tar xfz $LIBSODIUM_VERSION.tar.gz --strip-components=1 + +./configure --prefix $PWD +make && make check && make install + +# Remove shared libraries for using static +rm lib/*.so lib/*.so.* + +cd ${CWD}