From 4e5237b02a71d76b4ccebf743b76d16f4af83dac Mon Sep 17 00:00:00 2001 From: Andreas Auernhammer Date: Fri, 2 Mar 2018 00:28:34 +0100 Subject: [PATCH] vendor update github.com/minio/sio (#5599) This change updates the sio library and adds DARE 2.0 support to the server. --- vendor/github.com/minio/sio/README.md | 7 +- vendor/github.com/minio/sio/dare.go | 411 +++++++++--------- vendor/github.com/minio/sio/generic.go | 117 +++++ vendor/github.com/minio/sio/header.go | 39 -- .../github.com/minio/sio/internal/cpu/aes.go | 20 + .../minio/sio/internal/cpu/aesni_amd64.go | 20 + .../minio/sio/internal/cpu/aesni_amd64.s | 27 ++ .../minio/sio/internal/cpu/aesni_generic.go | 19 + vendor/github.com/minio/sio/reader-v1.go | 163 +++++++ vendor/github.com/minio/sio/reader-v2.go | 178 ++++++++ vendor/github.com/minio/sio/reader.go | 169 ------- vendor/github.com/minio/sio/sio.go | 257 +++++++++++ vendor/github.com/minio/sio/writer-v1.go | 199 +++++++++ vendor/github.com/minio/sio/writer-v2.go | 177 ++++++++ vendor/github.com/minio/sio/writer.go | 193 -------- vendor/vendor.json | 6 +- 16 files changed, 1389 insertions(+), 613 deletions(-) create mode 100644 vendor/github.com/minio/sio/generic.go delete mode 100644 vendor/github.com/minio/sio/header.go create mode 100644 vendor/github.com/minio/sio/internal/cpu/aes.go create mode 100644 vendor/github.com/minio/sio/internal/cpu/aesni_amd64.go create mode 100644 vendor/github.com/minio/sio/internal/cpu/aesni_amd64.s create mode 100644 vendor/github.com/minio/sio/internal/cpu/aesni_generic.go create mode 100644 vendor/github.com/minio/sio/reader-v1.go create mode 100644 vendor/github.com/minio/sio/reader-v2.go delete mode 100644 vendor/github.com/minio/sio/reader.go create mode 100644 vendor/github.com/minio/sio/sio.go create mode 100644 vendor/github.com/minio/sio/writer-v1.go create mode 100644 vendor/github.com/minio/sio/writer-v2.go delete mode 100644 vendor/github.com/minio/sio/writer.go diff --git a/vendor/github.com/minio/sio/README.md b/vendor/github.com/minio/sio/README.md index 1ad7a6ff4..4ddf3e80c 100644 --- a/vendor/github.com/minio/sio/README.md +++ b/vendor/github.com/minio/sio/README.md @@ -56,6 +56,11 @@ Its main properties are: DARE and `github.com/minio/sio` are finalized and can be used in production. +We also provide a CLI tool to en/decrypt arbitrary data streams directly from +your command line: + +**Install ncrypt:** `go get -u github.com/minio/sio/cmd/ncrypt && ncrypt -h` + ## Performance Cipher | 8 KB | 64 KB | 512 KB | 1 MB @@ -63,4 +68,4 @@ Cipher | 8 KB | 64 KB | 512 KB | 1 MB AES_256_GCM | 90 MB/s | 1.96 GB/s | 2.64 GB/s | 2.83 GB/s CHACHA20_POLY1305 | 97 MB/s | 1.23 GB/s | 1.54 GB/s | 1.57 GB/s -*On i7-6500U 2 x 2.5 GHz | Linux 4.10.0-32-generic | Go 1.8.3 | AES-NI & AVX2* +*On i7-6500U 2 x 2.5 GHz | Linux 4.10.0-32-generic | Go 1.8.3 | AES-NI & AVX2* \ No newline at end of file diff --git a/vendor/github.com/minio/sio/dare.go b/vendor/github.com/minio/sio/dare.go index 1de0677b8..cf785e068 100644 --- a/vendor/github.com/minio/sio/dare.go +++ b/vendor/github.com/minio/sio/dare.go @@ -12,266 +12,261 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package sio implements the DARE format. It provides an API -// for secure en/decrypting IO operations. -package sio // import "github.com/minio/sio" +package sio import ( - "crypto/aes" "crypto/cipher" - "crypto/rand" - "errors" + "crypto/subtle" + "encoding/binary" "io" - - "golang.org/x/crypto/chacha20poly1305" ) -// Version10 specifies version 1.0 -const Version10 byte = 0x10 +type headerV10 []byte -const ( - // AES_256_GCM specifies the cipher suite AES-GCM with 256 bit keys. - AES_256_GCM byte = iota - // CHACHA20_POLY1305 specifies the cipher suite ChaCha20Poly1305 with 256 bit keys. - CHACHA20_POLY1305 -) +func (h headerV10) Version() byte { return h[0] } +func (h headerV10) Cipher() byte { return h[1] } +func (h headerV10) Len() int { return int(binary.LittleEndian.Uint16(h[2:])) + 1 } +func (h headerV10) SequenceNumber() uint32 { return binary.LittleEndian.Uint32(h[4:]) } +func (h headerV10) SetVersion() { h[0] = Version10 } +func (h headerV10) SetCipher(suite byte) { h[1] = suite } +func (h headerV10) SetLen(length int) { binary.LittleEndian.PutUint16(h[2:], uint16(length-1)) } +func (h headerV10) SetSequenceNumber(num uint32) { binary.LittleEndian.PutUint32(h[4:], num) } +func (h headerV10) SetRand(randVal []byte) { copy(h[8:headerSize], randVal[:]) } +func (h headerV10) Nonce() []byte { return h[4:headerSize] } +func (h headerV10) AddData() []byte { return h[:4] } -const ( - headerSize = 16 - payloadSize = 64 * 1024 - tagSize = 16 -) +type packageV10 []byte -var ( - errMissingHeader = errors.New("sio: incomplete header") - errPayloadTooShort = errors.New("sio: payload too short") - errPackageOutOfOrder = errors.New("sio: sequence number mismatch") - errTagMismatch = errors.New("sio: authentication failed") - errUnsupportedVersion = errors.New("sio: unsupported version") - errUnsupportedCipher = errors.New("sio: unsupported cipher suite") -) +func (p packageV10) Header() headerV10 { return headerV10(p[:headerSize]) } +func (p packageV10) Payload() []byte { return p[headerSize : p.Length()-tagSize] } +func (p packageV10) Ciphertext() []byte { return p[headerSize:p.Length()] } +func (p packageV10) Length() int { return headerSize + tagSize + p.Header().Len() } -var newAesGcm = func(key []byte) (cipher.AEAD, error) { - aes256, err := aes.NewCipher(key) - if err != nil { - return nil, err +type headerV20 []byte + +func (h headerV20) Version() byte { return h[0] } +func (h headerV20) SetVersion() { h[0] = Version20 } +func (h headerV20) Cipher() byte { return h[1] } +func (h headerV20) SetCipher(cipher byte) { h[1] = cipher } +func (h headerV20) Length() int { return int(binary.LittleEndian.Uint16(h[2:4])) + 1 } +func (h headerV20) SetLength(length int) { binary.LittleEndian.PutUint16(h[2:4], uint16(length-1)) } +func (h headerV20) IsFinal() bool { return h[4]&0x80 == 0x80 } +func (h headerV20) Nonce() []byte { return h[4:headerSize] } +func (h headerV20) AddData() []byte { return h[:4] } +func (h headerV20) SetRand(randVal []byte, final bool) { + copy(h[4:], randVal) + if final { + h[4] |= 0x80 + } else { + h[4] &= 0x7F } - return cipher.NewGCM(aes256) } -var supportedCiphers = [...]func([]byte) (cipher.AEAD, error){ - AES_256_GCM: newAesGcm, - CHACHA20_POLY1305: chacha20poly1305.New, -} +type packageV20 []byte -// Encrypt reads from src until it encounters an io.EOF and encrypts all received -// data. The encrypted data is written to dst. It returns the number of bytes -// encrypted and the first error encountered while encrypting, if any. -// -// Encrypt returns the number of bytes written to dst. -func Encrypt(dst io.Writer, src io.Reader, config Config) (n int64, err error) { - encReader, err := EncryptReader(src, config) - if err != nil { - return - } - return io.CopyBuffer(dst, encReader, make([]byte, payloadSize)) +func (p packageV20) Header() headerV20 { return headerV20(p[:headerSize]) } +func (p packageV20) Payload() []byte { return p[headerSize : headerSize+p.Header().Length()] } +func (p packageV20) Ciphertext() []byte { return p[headerSize:p.Length()] } +func (p packageV20) Length() int { return headerSize + tagSize + p.Header().Length() } + +type authEnc struct { + CipherID byte + SeqNum uint32 + Cipher cipher.AEAD + RandVal []byte } -// Decrypt reads from src until it encounters an io.EOF and decrypts all received -// data. The decrypted data is written to dst. It returns the number of bytes -// decrypted and the first error encountered while decrypting, if any. -// -// Decrypt returns the number of bytes written to dst. Decrypt only writes data to -// dst if the data was decrypted successfully. -func Decrypt(dst io.Writer, src io.Reader, config Config) (n int64, err error) { - decReader, err := DecryptReader(src, config) - if err != nil { - return - } - return io.CopyBuffer(dst, decReader, make([]byte, headerSize+payloadSize+tagSize)) +type authDec struct { + SeqNum uint32 + Ciphers [2]cipher.AEAD } -// EncryptReader wraps the given src and returns an io.Reader which encrypts -// all received data. EncryptReader returns an error if the provided encryption -// configuration is invalid. -func EncryptReader(src io.Reader, config Config) (io.Reader, error) { - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - cipher, err := supportedCiphers[config.CipherSuites[0]](config.Key) +type authEncV10 authEnc + +func newAuthEncV10(cfg *Config) (authEncV10, error) { + cipherID := cfg.CipherSuites[0] + cipher, err := supportedCiphers[cipherID](cfg.Key) if err != nil { - return nil, err + return authEncV10{}, err } - nonce, err := config.generateNonce() - if err != nil { - return nil, err + var randVal [8]byte + if _, err = io.ReadFull(cfg.Rand, randVal[:]); err != nil { + return authEncV10{}, err } - return &encryptedReader{ - src: src, - config: config, - nonce: nonce, - cipher: cipher, - sequenceNumber: config.SequenceNumber, + return authEncV10{ + CipherID: cipherID, + RandVal: randVal[:], + Cipher: cipher, + SeqNum: cfg.SequenceNumber, }, nil } -// DecryptReader wraps the given src and returns an io.Reader which decrypts -// all received data. DecryptReader returns an error if the provided decryption -// configuration is invalid. -func DecryptReader(src io.Reader, config Config) (io.Reader, error) { - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - ciphers, err := config.createCiphers() - if err != nil { - return nil, err - } - return &decryptedReader{ - src: src, - config: config, - ciphers: ciphers, - sequenceNumber: config.SequenceNumber, - }, nil +func (ae *authEncV10) Seal(dst, src []byte) { + header := headerV10(dst[:headerSize]) + header.SetVersion() + header.SetCipher(ae.CipherID) + header.SetLen(len(src)) + header.SetSequenceNumber(ae.SeqNum) + header.SetRand(ae.RandVal) + ae.Cipher.Seal(dst[headerSize:headerSize], header.Nonce(), src, header.AddData()) + ae.SeqNum++ } -// EncryptWriter wraps the given dst and returns an io.WriteCloser which -// encrypts all data written to it. EncryptWriter returns an error if the -// provided decryption configuration is invalid. -// -// The returned io.WriteCloser must be closed successfully to finalize the -// encryption process. -func EncryptWriter(dst io.Writer, config Config) (io.WriteCloser, error) { - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - cipher, err := supportedCiphers[config.CipherSuites[0]](config.Key) - if err != nil { - return nil, err - } - nonce, err := config.generateNonce() - if err != nil { - return nil, err - } - return &encryptedWriter{ - dst: dst, - config: config, - nonce: nonce, - cipher: cipher, - sequenceNumber: config.SequenceNumber, - }, nil -} +type authDecV10 authDec -// DecryptWriter wraps the given dst and returns an io.WriteCloser which -// decrypts all data written to it. DecryptWriter returns an error if the -// provided decryption configuration is invalid. -// -// The returned io.WriteCloser must be closed successfully to finalize the -// decryption process. -func DecryptWriter(dst io.Writer, config Config) (io.WriteCloser, error) { - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - ciphers, err := config.createCiphers() - if err != nil { - return nil, err +func newAuthDecV10(cfg *Config) (authDecV10, error) { + var ciphers [2]cipher.AEAD + for _, v := range cfg.CipherSuites { + aeadCipher, err := supportedCiphers[v](cfg.Key) + if err != nil { + return authDecV10{}, err + } + ciphers[v] = aeadCipher } - return &decryptedWriter{ - dst: dst, - config: config, - ciphers: ciphers, - sequenceNumber: config.SequenceNumber, + return authDecV10{ + SeqNum: cfg.SequenceNumber, + Ciphers: ciphers, }, nil } -func setConfigDefaults(config *Config) error { - if config.MinVersion > Version10 { - return errors.New("sio: unknown minimum version") - } - if config.MaxVersion > Version10 { - return errors.New("dare: unknown maximum version") - } - if len(config.Key) != 32 { - return errors.New("sio: invalid key size") - } - if len(config.CipherSuites) > 2 { - return errors.New("sio: too many cipher suites") +func (ad *authDecV10) Open(dst, src []byte) error { + header := headerV10(src[:headerSize]) + if header.Version() != Version10 { + return errUnsupportedVersion } - for _, c := range config.CipherSuites { - if int(c) >= len(supportedCiphers) { - return errors.New("sio: unknown cipher suite") - } + if header.Cipher() > CHACHA20_POLY1305 { + return errUnsupportedCipher } - if config.MinVersion < Version10 { - config.MinVersion = Version10 + aeadCipher := ad.Ciphers[header.Cipher()] + if aeadCipher == nil { + return errUnsupportedCipher } - if config.MaxVersion < Version10 { - config.MaxVersion = config.MinVersion + if headerSize+header.Len()+tagSize != len(src) { + return errInvalidPayloadSize } - if len(config.CipherSuites) == 0 { - config.CipherSuites = []byte{AES_256_GCM, CHACHA20_POLY1305} + if header.SequenceNumber() != ad.SeqNum { + return errPackageOutOfOrder } - if config.Rand == nil { - config.Rand = rand.Reader + ciphertext := src[headerSize : headerSize+header.Len()+tagSize] + if _, err := aeadCipher.Open(dst[:0], header.Nonce(), ciphertext, header.AddData()); err != nil { + return errTagMismatch } + ad.SeqNum++ return nil } -// Config contains the format configuration. The only field -// which must always be set manually is the secret key. -type Config struct { - // The minimal supported version of the format. If - // not set the default value is used. - MinVersion byte - // The highest supported version of the format. If - // not set the default value is used. - MaxVersion byte - // A list of supported cipher suites. If not set the - // default value is used. - CipherSuites []byte - // The secret encryption key. It must be 32 bytes long. - Key []byte - // The first expected sequence number. It should only - // be set manually when appending data to an existing - // sequence of packages or decrypting a range within - // a sequence of packages. - SequenceNumber uint32 - // The RNG used to generate random values. If not set - // the default value (crypto/rand.Reader) is used. - Rand io.Reader +type authEncV20 struct { + authEnc + finalized bool } -func (c *Config) verifyHeader(header headerV10) error { - if version := header.Version(); version < c.MinVersion || version > c.MaxVersion { - return errUnsupportedVersion +func newAuthEncV20(cfg *Config) (authEncV20, error) { + cipherID := cfg.CipherSuites[0] + cipher, err := supportedCiphers[cipherID](cfg.Key) + if err != nil { + return authEncV20{}, err } - if !c.isCipherSuiteSupported(header.Cipher()) { - return errUnsupportedCipher + var randVal [12]byte + if _, err = io.ReadFull(cfg.Rand, randVal[:]); err != nil { + return authEncV20{}, err } - return nil + return authEncV20{ + authEnc: authEnc{ + CipherID: cipherID, + RandVal: randVal[:], + Cipher: cipher, + SeqNum: cfg.SequenceNumber, + }, + }, nil +} + +func (ae *authEncV20) Seal(dst, src []byte) { ae.seal(dst, src, false) } +func (ae *authEncV20) SealFinal(dst, src []byte) { ae.seal(dst, src, true) } + +func (ae *authEncV20) seal(dst, src []byte, finalize bool) { + if ae.finalized { // callers are not supposed to call Seal(Final) after a SealFinal call happened + panic("sio: cannot seal any package after final one") + } + ae.finalized = finalize + + header := headerV20(dst[:headerSize]) + header.SetVersion() + header.SetCipher(ae.CipherID) + header.SetLength(len(src)) + header.SetRand(ae.RandVal, finalize) + + var nonce [12]byte + copy(nonce[:], header.Nonce()) + binary.LittleEndian.PutUint32(nonce[8:], binary.LittleEndian.Uint32(nonce[8:])^ae.SeqNum) + + ae.Cipher.Seal(dst[headerSize:headerSize], nonce[:], src, header.AddData()) + ae.SeqNum++ } -func (c *Config) createCiphers() ([]cipher.AEAD, error) { - ciphers := make([]cipher.AEAD, len(supportedCiphers)) - for _, v := range c.CipherSuites { - aeadCipher, err := supportedCiphers[v](c.Key) +type authDecV20 struct { + authDec + refHeader headerV20 + finalized bool +} + +func newAuthDecV20(cfg *Config) (authDecV20, error) { + var ciphers [2]cipher.AEAD + for _, v := range cfg.CipherSuites { + aeadCipher, err := supportedCiphers[v](cfg.Key) if err != nil { - return nil, err + return authDecV20{}, err } ciphers[v] = aeadCipher } - return ciphers, nil + return authDecV20{ + authDec: authDec{ + SeqNum: cfg.SequenceNumber, + Ciphers: ciphers, + }, + }, nil } -func (c *Config) generateNonce() (nonce [8]byte, err error) { - _, err = io.ReadFull(c.Rand, nonce[:]) - return nonce, err -} +func (ad *authDecV20) Open(dst, src []byte) error { + if ad.finalized { + return errUnexpectedData + } + if len(src) <= headerSize+tagSize { + return errInvalidPayloadSize + } -func (c *Config) isCipherSuiteSupported(suite byte) bool { - for _, c := range c.CipherSuites { - if suite == c { - return true - } + header := packageV20(src).Header() + if ad.refHeader == nil { + ad.refHeader = make([]byte, headerSize) + copy(ad.refHeader, header) + } + if header.Version() != Version20 { + return errUnsupportedVersion } - return false + if c := header.Cipher(); c > CHACHA20_POLY1305 || ad.Ciphers[c] == nil || c != ad.refHeader.Cipher() { + return errUnsupportedCipher + } + if headerSize+header.Length()+tagSize != len(src) { + return errInvalidPayloadSize + } + if !header.IsFinal() && header.Length() != maxPayloadSize { + return errInvalidPayloadSize + } + refNonce := ad.refHeader.Nonce() + if header.IsFinal() { + ad.finalized = true + refNonce[0] |= 0x80 // set final flag + } + if subtle.ConstantTimeCompare(header.Nonce(), refNonce[:]) != 1 { + return errNonceMismatch + } + + var nonce [12]byte + copy(nonce[:], header.Nonce()) + binary.LittleEndian.PutUint32(nonce[8:], binary.LittleEndian.Uint32(nonce[8:])^ad.SeqNum) + cipher := ad.Ciphers[header.Cipher()] + if _, err := cipher.Open(dst[:0], nonce[:], src[headerSize:headerSize+header.Length()+tagSize], header.AddData()); err != nil { + return errTagMismatch + } + ad.SeqNum++ + return nil } diff --git a/vendor/github.com/minio/sio/generic.go b/vendor/github.com/minio/sio/generic.go new file mode 100644 index 000000000..b6aa3874f --- /dev/null +++ b/vendor/github.com/minio/sio/generic.go @@ -0,0 +1,117 @@ +// Copyright (C) 2018 Minio Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sio + +import ( + "bytes" + "io" +) + +type decWriter struct { + config Config + dst io.Writer + + firstWrite bool +} + +// decryptWriter returns an io.WriteCloser wrapping the given io.Writer. +// The returned io.WriteCloser detects whether the data written to it is +// encrypted using DARE 1.0 or 2.0 and decrypts it using the correct DARE +// version. +func decryptWriter(w io.Writer, config *Config) *decWriter { + return &decWriter{ + config: *config, + dst: w, + firstWrite: true, + } +} + +func (w *decWriter) Write(p []byte) (n int, err error) { + if w.firstWrite { + if len(p) == 0 { + return 0, nil + } + w.firstWrite = false + switch p[0] { + default: + return 0, errUnsupportedVersion + case Version10: + w.dst, err = decryptWriterV10(w.dst, &w.config) + if err != nil { + return 0, err + } + case Version20: + w.dst, err = decryptWriterV20(w.dst, &w.config) + if err != nil { + return 0, err + } + } + } + return w.dst.Write(p) +} + +func (w *decWriter) Close() error { + if closer, ok := w.dst.(io.Closer); ok { + return closer.Close() + } + return nil +} + +type decReader struct { + config Config + src io.Reader + + firstRead bool +} + +// decryptReader returns an io.Reader wrapping the given io.Reader. +// The returned io.Reader detects whether the underlying io.Reader returns +// DARE 1.0 or 2.0 encrypted data and decrypts it using the correct DARE version. +func decryptReader(r io.Reader, config *Config) *decReader { + return &decReader{ + config: *config, + src: r, + firstRead: true, + } +} + +func (r *decReader) Read(p []byte) (n int, err error) { + if r.firstRead { + if len(p) == 0 { + return 0, nil + } + var version [1]byte + if _, err = io.ReadFull(r.src, version[:]); err != nil { + return 0, err + } + r.firstRead = false + r.src = io.MultiReader(bytes.NewReader(version[:]), r.src) + switch version[0] { + default: + return 0, errUnsupportedVersion + case Version10: + r.src, err = decryptReaderV10(r.src, &r.config) + if err != nil { + return 0, err + } + case Version20: + r.src, err = decryptReaderV20(r.src, &r.config) + if err != nil { + return 0, err + } + } + } + return r.src.Read(p) +} diff --git a/vendor/github.com/minio/sio/header.go b/vendor/github.com/minio/sio/header.go deleted file mode 100644 index ddaeeff1c..000000000 --- a/vendor/github.com/minio/sio/header.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2017 Minio Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sio - -import "encoding/binary" - -type headerV10 []byte - -func header(b []byte) headerV10 { return headerV10(b[:headerSize]) } - -func (h headerV10) Version() byte { return h[0] } - -func (h headerV10) Cipher() byte { return h[1] } - -func (h headerV10) Len() int { return int(binary.LittleEndian.Uint16(h[2:])) + 1 } - -func (h headerV10) SequenceNumber() uint32 { return binary.LittleEndian.Uint32(h[4:]) } - -func (h headerV10) SetVersion(version byte) { h[0] = version } - -func (h headerV10) SetCipher(suite byte) { h[1] = suite } - -func (h headerV10) SetLen(length int) { binary.LittleEndian.PutUint16(h[2:], uint16(length-1)) } - -func (h headerV10) SetSequenceNumber(num uint32) { binary.LittleEndian.PutUint32(h[4:], num) } - -func (h headerV10) SetNonce(nonce [8]byte) { copy(h[8:], nonce[:]) } diff --git a/vendor/github.com/minio/sio/internal/cpu/aes.go b/vendor/github.com/minio/sio/internal/cpu/aes.go new file mode 100644 index 000000000..b1dea3422 --- /dev/null +++ b/vendor/github.com/minio/sio/internal/cpu/aes.go @@ -0,0 +1,20 @@ +// Copyright (C) 2018 Minio Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cpu + +// SupportsAES returns true if the CPU offers special +// AES instructions. It indicates whether an AES hardware +// implementation is available. +func SupportsAES() bool { return hasAESNISupport() } diff --git a/vendor/github.com/minio/sio/internal/cpu/aesni_amd64.go b/vendor/github.com/minio/sio/internal/cpu/aesni_amd64.go new file mode 100644 index 000000000..ba7a11596 --- /dev/null +++ b/vendor/github.com/minio/sio/internal/cpu/aesni_amd64.go @@ -0,0 +1,20 @@ +// Copyright (C) 2017 Minio Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build amd64,!gccgo,!appengine + +package cpu + +//go:noescape +func hasAESNISupport() bool diff --git a/vendor/github.com/minio/sio/internal/cpu/aesni_amd64.s b/vendor/github.com/minio/sio/internal/cpu/aesni_amd64.s new file mode 100644 index 000000000..4cc16bf0b --- /dev/null +++ b/vendor/github.com/minio/sio/internal/cpu/aesni_amd64.s @@ -0,0 +1,27 @@ +// Copyright (C) 2017 Minio Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build amd64,!gccgo,!appengine + +#include "textflag.h" + +// func hasAESNISupport() bool +TEXT ·hasAESNISupport(SB),NOSPLIT,$0 + XORQ AX, AX + INCL AX + CPUID + SHRQ $25, CX + ANDQ $1, CX + MOVB CX, ret+0(FP) + RET diff --git a/vendor/github.com/minio/sio/internal/cpu/aesni_generic.go b/vendor/github.com/minio/sio/internal/cpu/aesni_generic.go new file mode 100644 index 000000000..28e41123f --- /dev/null +++ b/vendor/github.com/minio/sio/internal/cpu/aesni_generic.go @@ -0,0 +1,19 @@ +// Copyright (C) 2017 Minio Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build !amd64 gccgo appengine + +package cpu + +func hasAESNISupport() bool { return false } diff --git a/vendor/github.com/minio/sio/reader-v1.go b/vendor/github.com/minio/sio/reader-v1.go new file mode 100644 index 000000000..3ee22f800 --- /dev/null +++ b/vendor/github.com/minio/sio/reader-v1.go @@ -0,0 +1,163 @@ +// Copyright (C) 2017 Minio Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sio + +import "io" + +type encReaderV10 struct { + authEncV10 + src io.Reader + + buffer packageV10 + offset int + payloadSize int +} + +// encryptReaderV10 returns an io.Reader wrapping the given io.Reader. +// The returned io.Reader encrypts everything it reads using DARE 1.0. +func encryptReaderV10(src io.Reader, config *Config) (*encReaderV10, error) { + ae, err := newAuthEncV10(config) + if err != nil { + return nil, err + } + return &encReaderV10{ + authEncV10: ae, + src: src, + buffer: make(packageV10, maxPackageSize), + payloadSize: config.PayloadSize, + }, nil +} + +func (r *encReaderV10) Read(p []byte) (int, error) { + var n int + if r.offset > 0 { // write the buffered package to p + remaining := r.buffer.Length() - r.offset // remaining encrypted bytes + if len(p) < remaining { + n = copy(p, r.buffer[r.offset:r.offset+len(p)]) + r.offset += n + return n, nil + } + n = copy(p, r.buffer[r.offset:r.offset+remaining]) + p = p[remaining:] + r.offset = 0 + } + for len(p) >= headerSize+r.payloadSize+tagSize { + nn, err := io.ReadFull(r.src, p[headerSize:headerSize+r.payloadSize]) // read plaintext from src + if err != nil && err != io.ErrUnexpectedEOF { + return n, err // return if reading from src fails or reached EOF + } + r.Seal(p, p[headerSize:headerSize+nn]) + n += headerSize + nn + tagSize + p = p[headerSize+nn+tagSize:] + } + if len(p) > 0 { + nn, err := io.ReadFull(r.src, r.buffer[headerSize:headerSize+r.payloadSize]) // read plaintext from src + if err != nil && err != io.ErrUnexpectedEOF { + return n, err // return if reading from src fails or reached EOF + } + r.Seal(r.buffer, r.buffer[headerSize:headerSize+nn]) + if length := r.buffer.Length(); length < len(p) { + r.offset = copy(p, r.buffer[:length]) + } else { + r.offset = copy(p, r.buffer[:len(p)]) + } + n += r.offset + } + return n, nil +} + +type decReaderV10 struct { + authDecV10 + src io.Reader + + buffer packageV10 + offset int +} + +// decryptReaderV10 returns an io.Reader wrapping the given io.Reader. +// The returned io.Reader decrypts everything it reads using DARE 1.0. +func decryptReaderV10(src io.Reader, config *Config) (*decReaderV10, error) { + ad, err := newAuthDecV10(config) + if err != nil { + return nil, err + } + return &decReaderV10{ + authDecV10: ad, + src: src, + buffer: make(packageV10, maxPackageSize), + }, nil +} + +func (r *decReaderV10) Read(p []byte) (n int, err error) { + if r.offset > 0 { // write the buffered plaintext to p + payload := r.buffer.Payload() + remaining := len(payload) - r.offset // remaining plaintext bytes + if len(p) < remaining { + n = copy(p, payload[r.offset:+r.offset+len(p)]) + r.offset += n + return + } + n = copy(p, payload[r.offset:r.offset+remaining]) + p = p[remaining:] + r.offset = 0 + } + for len(p) >= maxPayloadSize { + if err = r.readPackage(r.buffer); err != nil { + return n, err + } + length := len(r.buffer.Payload()) + if err = r.Open(p[:length], r.buffer[:r.buffer.Length()]); err != nil { // notice: buffer.Length() may be smaller than len(buffer) + return n, err // decryption failed + } + p = p[length:] + n += length + } + if len(p) > 0 { + if err = r.readPackage(r.buffer); err != nil { + return n, err + } + payload := r.buffer.Payload() + if err = r.Open(payload, r.buffer[:r.buffer.Length()]); err != nil { // notice: buffer.Length() may be smaller than len(buffer) + return n, err // decryption failed + } + if len(payload) < len(p) { + r.offset = copy(p, payload) + } else { + r.offset = copy(p, payload[:len(p)]) + } + n += r.offset + } + return n, nil +} + +func (r *decReaderV10) readPackage(dst packageV10) error { + header := dst.Header() + _, err := io.ReadFull(r.src, header) + if err == io.ErrUnexpectedEOF { + return errInvalidPayloadSize // partial header + } + if err != nil { + return err // reading from src failed or reached EOF + } + + _, err = io.ReadFull(r.src, dst.Ciphertext()) + if err == io.EOF || err == io.ErrUnexpectedEOF { + return errInvalidPayloadSize // reading less data than specified by header + } + if err != nil { + return err // reading from src failed or reached EOF + } + return nil +} diff --git a/vendor/github.com/minio/sio/reader-v2.go b/vendor/github.com/minio/sio/reader-v2.go new file mode 100644 index 000000000..7cd15c2c3 --- /dev/null +++ b/vendor/github.com/minio/sio/reader-v2.go @@ -0,0 +1,178 @@ +// Copyright (C) 2018 Minio Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sio + +import ( + "io" +) + +type encReaderV20 struct { + authEncV20 + src io.Reader + + buffer packageV20 + offset int + lastByte byte + + firstRead bool +} + +// encryptReaderV20 returns an io.Reader wrapping the given io.Reader. +// The returned io.Reader encrypts everything it reads using DARE 2.0. +func encryptReaderV20(src io.Reader, config *Config) (*encReaderV20, error) { + ae, err := newAuthEncV20(config) + if err != nil { + return nil, err + } + return &encReaderV20{ + authEncV20: ae, + src: src, + buffer: make(packageV20, maxPackageSize), + firstRead: true, + }, nil +} + +func (r *encReaderV20) Read(p []byte) (n int, err error) { + if r.firstRead { + r.firstRead = false + _, err = io.ReadFull(r.src, r.buffer[headerSize:headerSize+1]) + if err != nil && err != io.EOF { // ErrUnexpectedEOF cannot happen b/c we read just one single byte + return 0, err + } + if err == io.EOF { + r.finalized = true + return 0, io.EOF + } + r.lastByte = r.buffer[headerSize] + } + + if r.offset > 0 { // write the buffered package to p + remaining := r.buffer.Length() - r.offset + if len(p) < remaining { + r.offset += copy(p, r.buffer[r.offset:r.offset+len(p)]) + return len(p), nil + } + n = copy(p, r.buffer[r.offset:r.offset+remaining]) + p = p[remaining:] + r.offset = 0 + } + if r.finalized { + return n, io.EOF + } + for len(p) >= maxPackageSize { + r.buffer[headerSize] = r.lastByte + nn, err := io.ReadFull(r.src, r.buffer[headerSize+1:headerSize+1+maxPayloadSize]) // try to read the max. payload + if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { + return n, err // failed to read from src + } + if err == io.EOF || err == io.ErrUnexpectedEOF { // read less than 64KB -> final package + r.SealFinal(p, r.buffer[headerSize:headerSize+1+nn]) + return n + headerSize + tagSize + 1 + nn, io.EOF + } + r.lastByte = r.buffer[headerSize+maxPayloadSize] // save last read byte for the next package + r.Seal(p, r.buffer[headerSize:headerSize+maxPayloadSize]) + p = p[maxPackageSize:] + n += maxPackageSize + } + if len(p) > 0 { + r.buffer[headerSize] = r.lastByte + nn, err := io.ReadFull(r.src, r.buffer[headerSize+1:headerSize+1+maxPayloadSize]) // try to read the max. payload + if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF { + return n, err // failed to read from src + } + if err == io.EOF || err == io.ErrUnexpectedEOF { // read less than 64KB -> final package + r.SealFinal(r.buffer, r.buffer[headerSize:headerSize+1+nn]) + if len(p) > r.buffer.Length() { + n += copy(p, r.buffer[:r.buffer.Length()]) + return n, io.EOF + } + } else { + r.lastByte = r.buffer[headerSize+maxPayloadSize] // save last read byte for the next package + r.Seal(r.buffer, r.buffer[headerSize:headerSize+maxPayloadSize]) + } + r.offset = copy(p, r.buffer[:len(p)]) // len(p) < len(r.buffer) - otherwise we would be still in the for-loop + n += r.offset + } + return n, nil +} + +type decReaderV20 struct { + authDecV20 + src io.Reader + + buffer packageV20 + offset int +} + +// decryptReaderV20 returns an io.Reader wrapping the given io.Reader. +// The returned io.Reader decrypts everything it reads using DARE 2.0. +func decryptReaderV20(src io.Reader, config *Config) (*decReaderV20, error) { + ad, err := newAuthDecV20(config) + if err != nil { + return nil, err + } + return &decReaderV20{ + authDecV20: ad, + src: src, + buffer: make(packageV20, maxPackageSize), + }, nil +} + +func (r *decReaderV20) Read(p []byte) (n int, err error) { + if r.offset > 0 { // write the buffered plaintext to p + remaining := len(r.buffer.Payload()) - r.offset + if len(p) < remaining { + n = copy(p, r.buffer.Payload()[r.offset:r.offset+len(p)]) + r.offset += n + return n, nil + } + n = copy(p, r.buffer.Payload()[r.offset:]) + p = p[remaining:] + r.offset = 0 + } + for len(p) >= maxPayloadSize { + nn, err := io.ReadFull(r.src, r.buffer) + if err == io.EOF && !r.finalized { + return n, errUnexpectedEOF // reached EOF but not seen final package yet + } + if err != nil && err != io.ErrUnexpectedEOF { + return n, err // reading from src failed or reached EOF + } + if err = r.Open(p, r.buffer[:nn]); err != nil { + return n, err // decryption failed + } + p = p[len(r.buffer.Payload()):] + n += len(r.buffer.Payload()) + } + if len(p) > 0 { + nn, err := io.ReadFull(r.src, r.buffer) + if err == io.EOF && !r.finalized { + return n, errUnexpectedEOF // reached EOF but not seen final package yet + } + if err != nil && err != io.ErrUnexpectedEOF { + return n, err // reading from src failed or reached EOF + } + if err = r.Open(r.buffer[headerSize:], r.buffer[:nn]); err != nil { + return n, err // decryption failed + } + if payload := r.buffer.Payload(); len(p) < len(payload) { + r.offset = copy(p, payload[:len(p)]) + n += r.offset + } else { + n += copy(p, payload) + } + } + return n, nil +} diff --git a/vendor/github.com/minio/sio/reader.go b/vendor/github.com/minio/sio/reader.go deleted file mode 100644 index ef35a3774..000000000 --- a/vendor/github.com/minio/sio/reader.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright (C) 2017 Minio Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sio - -import ( - "crypto/cipher" - "io" -) - -type encryptedReader struct { - src io.Reader - config Config - - sequenceNumber uint32 - nonce [8]byte - cipher cipher.AEAD - - pack [headerSize + payloadSize + tagSize]byte - offset int -} - -func (r *encryptedReader) Read(p []byte) (n int, err error) { - if r.offset > 0 { - remaining := headerSize + header(r.pack[:]).Len() + tagSize - r.offset - if len(p) < remaining { - n = copy(p, r.pack[r.offset:r.offset+len(p)]) - r.offset += n - return - } - n = copy(p, r.pack[r.offset:r.offset+remaining]) - p = p[remaining:] - r.offset = 0 - } - for len(p) >= headerSize+payloadSize+tagSize { - nn, err := io.ReadFull(r.src, r.pack[headerSize:headerSize+payloadSize]) - if err != nil && err != io.ErrUnexpectedEOF { - return n, err - } - r.encrypt(p, nn) - n += headerSize + nn + tagSize - p = p[headerSize+nn+tagSize:] - } - if len(p) > 0 { - nn, err := io.ReadFull(r.src, r.pack[headerSize:headerSize+payloadSize]) - if err != nil && err != io.ErrUnexpectedEOF { - return n, err - } - r.encrypt(r.pack[:], nn) - if headerSize+nn+tagSize < len(p) { - r.offset = copy(p, r.pack[:headerSize+nn+tagSize]) - } else { - r.offset = copy(p, r.pack[:len(p)]) - } - n += r.offset - } - return -} - -func (r *encryptedReader) encrypt(dst []byte, length int) { - header := header(dst) - header.SetVersion(r.config.MaxVersion) - header.SetCipher(r.config.CipherSuites[0]) - header.SetLen(length) - header.SetSequenceNumber(r.sequenceNumber) - header.SetNonce(r.nonce) - - copy(dst[:headerSize], header) - r.cipher.Seal(dst[headerSize:headerSize], header[4:headerSize], r.pack[headerSize:headerSize+length], header[:4]) - - r.sequenceNumber++ -} - -type decryptedReader struct { - src io.Reader - config Config - - sequenceNumber uint32 - ciphers []cipher.AEAD - - pack [headerSize + payloadSize + tagSize]byte - offset int -} - -func (r *decryptedReader) Read(p []byte) (n int, err error) { - if r.offset > 0 { - remaining := header(r.pack[:]).Len() - r.offset - if len(p) < remaining { - n = copy(p, r.pack[headerSize+r.offset:headerSize+r.offset+len(p)]) - r.offset += n - return - } - n = copy(p, r.pack[headerSize+r.offset:headerSize+r.offset+remaining]) - p = p[remaining:] - r.offset = 0 - } - for len(p) >= payloadSize { - if err = r.readHeader(); err != nil { - return n, err - } - nn, err := r.decrypt(p[:payloadSize]) - if err != nil { - return n, err - } - p = p[nn:] - n += nn - } - if len(p) > 0 { - if err = r.readHeader(); err != nil { - return n, err - } - nn, err := r.decrypt(r.pack[headerSize:]) - if err != nil { - return n, err - } - if nn < len(p) { - r.offset = copy(p, r.pack[headerSize:headerSize+nn]) - } else { - r.offset = copy(p, r.pack[headerSize:headerSize+len(p)]) - } - n += r.offset - } - return -} - -func (r *decryptedReader) readHeader() error { - n, err := io.ReadFull(r.src, header(r.pack[:])) - if n != headerSize && err == io.ErrUnexpectedEOF { - return errMissingHeader - } else if err != nil { - return err - } - return nil -} - -func (r *decryptedReader) decrypt(dst []byte) (n int, err error) { - header := header(r.pack[:]) - if err = r.config.verifyHeader(header); err != nil { - return 0, err - } - if header.SequenceNumber() != r.sequenceNumber { - return 0, errPackageOutOfOrder - } - ciphertext := r.pack[headerSize : headerSize+header.Len()+tagSize] - n, err = io.ReadFull(r.src, ciphertext) - if err == io.EOF || err == io.ErrUnexpectedEOF { - return 0, errPayloadTooShort - } else if err != nil { - return 0, err - } - aeadCipher := r.ciphers[header.Cipher()] - plaintext, err := aeadCipher.Open(dst[:0], header[4:], ciphertext, header[:4]) - if err != nil { - return 0, errTagMismatch - } - r.sequenceNumber++ - return len(plaintext), nil -} diff --git a/vendor/github.com/minio/sio/sio.go b/vendor/github.com/minio/sio/sio.go new file mode 100644 index 000000000..5e043a4a3 --- /dev/null +++ b/vendor/github.com/minio/sio/sio.go @@ -0,0 +1,257 @@ +// Copyright (C) 2018 Minio Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package sio implements the DARE format. It provides an API for secure +// en/decrypting IO operations using io.Reader and io.Writer. +package sio // import "github.com/minio/sio" + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "errors" + "io" + + "github.com/minio/sio/internal/cpu" + "golang.org/x/crypto/chacha20poly1305" +) + +const ( + // Version20 specifies version 2.0 + Version20 byte = 0x20 + // Version10 specifies version 1.0 + Version10 byte = 0x10 +) + +const ( + // AES_256_GCM specifies the cipher suite AES-GCM with 256 bit keys. + AES_256_GCM byte = iota + // CHACHA20_POLY1305 specifies the cipher suite ChaCha20Poly1305 with 256 bit keys. + CHACHA20_POLY1305 +) + +const ( + keySize = 32 + + headerSize = 16 + maxPayloadSize = 1 << 16 + tagSize = 16 + maxPackageSize = headerSize + maxPayloadSize + tagSize +) + +var newAesGcm = func(key []byte) (cipher.AEAD, error) { + aes256, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + return cipher.NewGCM(aes256) +} + +var supportedCiphers = [...]func([]byte) (cipher.AEAD, error){ + AES_256_GCM: newAesGcm, + CHACHA20_POLY1305: chacha20poly1305.New, +} + +var ( + errUnsupportedVersion = errors.New("sio: unsupported version") + errUnsupportedCipher = errors.New("sio: unsupported cipher suite") + errInvalidPayloadSize = errors.New("sio: invalid payload size") + errTagMismatch = errors.New("sio: authentication failed") + + // Version 1.0 specific + errPackageOutOfOrder = errors.New("sio: sequence number mismatch") + + // Version 2.0 specific + errNonceMismatch = errors.New("sio: header nonce mismatch") + errUnexpectedEOF = errors.New("sio: unexpected EOF") + errUnexpectedData = errors.New("sio: unexpected data after final package") +) + +// Config contains the format configuration. The only field +// which must always be set manually is the secret key. +type Config struct { + // The minimal supported version of the format. If + // not set the default value - Version10 - is used. + MinVersion byte + + // The highest supported version of the format. If + // not set the default value - Version20 - is used. + MaxVersion byte + + // A list of supported cipher suites. If not set the + // default value is used. + CipherSuites []byte + + // The secret encryption key. It must be 32 bytes long. + Key []byte + + // The first expected sequence number. It should only + // be set manually when decrypting a range within a + // stream. + SequenceNumber uint32 + + // The RNG used to generate random values. If not set + // the default value (crypto/rand.Reader) is used. + Rand io.Reader + + // The size of the encrypted payload in bytes. The + // default value is 64KB. It should be used to restrict + // the size of encrypted packages. The payload size + // must be between 1 and 64 KB. + // + // This field is specific for version 1.0 and is + // deprecated. + PayloadSize int +} + +// Encrypt reads from src until it encounters an io.EOF and encrypts all received +// data. The encrypted data is written to dst. It returns the number of bytes +// encrypted and the first error encountered while encrypting, if any. +// +// Encrypt returns the number of bytes written to dst. +func Encrypt(dst io.Writer, src io.Reader, config Config) (n int64, err error) { + encReader, err := EncryptReader(src, config) + if err != nil { + return 0, err + } + return io.CopyBuffer(dst, encReader, make([]byte, headerSize+maxPayloadSize+tagSize)) +} + +// Decrypt reads from src until it encounters an io.EOF and decrypts all received +// data. The decrypted data is written to dst. It returns the number of bytes +// decrypted and the first error encountered while decrypting, if any. +// +// Decrypt returns the number of bytes written to dst. Decrypt only writes data to +// dst if the data was decrypted successfully. +func Decrypt(dst io.Writer, src io.Reader, config Config) (n int64, err error) { + decReader, err := DecryptReader(src, config) + if err != nil { + return 0, err + } + return io.CopyBuffer(dst, decReader, make([]byte, maxPayloadSize)) +} + +// EncryptReader wraps the given src and returns an io.Reader which encrypts +// all received data. EncryptReader returns an error if the provided encryption +// configuration is invalid. +func EncryptReader(src io.Reader, config Config) (io.Reader, error) { + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + if config.MaxVersion == Version20 { + return encryptReaderV20(src, &config) + } + return encryptReaderV10(src, &config) +} + +// DecryptReader wraps the given src and returns an io.Reader which decrypts +// all received data. DecryptReader returns an error if the provided decryption +// configuration is invalid. +func DecryptReader(src io.Reader, config Config) (io.Reader, error) { + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + if config.MinVersion == Version10 && config.MaxVersion == Version10 { + return decryptReaderV10(src, &config) + } + if config.MinVersion == Version20 && config.MaxVersion == Version20 { + return decryptReaderV20(src, &config) + } + return decryptReader(src, &config), nil +} + +// EncryptWriter wraps the given dst and returns an io.WriteCloser which +// encrypts all data written to it. EncryptWriter returns an error if the +// provided decryption configuration is invalid. +// +// The returned io.WriteCloser must be closed successfully to finalize the +// encryption process. +func EncryptWriter(dst io.Writer, config Config) (io.WriteCloser, error) { + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + if config.MaxVersion == Version20 { + return encryptWriterV20(dst, &config) + } + return encryptWriterV10(dst, &config) +} + +// DecryptWriter wraps the given dst and returns an io.WriteCloser which +// decrypts all data written to it. DecryptWriter returns an error if the +// provided decryption configuration is invalid. +// +// The returned io.WriteCloser must be closed successfully to finalize the +// decryption process. +func DecryptWriter(dst io.Writer, config Config) (io.WriteCloser, error) { + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + if config.MinVersion == Version10 && config.MaxVersion == Version10 { + return decryptWriterV10(dst, &config) + } + if config.MinVersion == Version20 && config.MaxVersion == Version20 { + return decryptWriterV20(dst, &config) + } + return decryptWriter(dst, &config), nil +} + +func defaultCipherSuites() []byte { + if cpu.SupportsAES() { + return []byte{AES_256_GCM, CHACHA20_POLY1305} + } + return []byte{CHACHA20_POLY1305, AES_256_GCM} +} + +func setConfigDefaults(config *Config) error { + if config.MinVersion > Version20 { + return errors.New("sio: unknown minimum version") + } + if config.MaxVersion > Version20 { + return errors.New("sio: unknown maximum version") + } + if len(config.Key) != keySize { + return errors.New("sio: invalid key size") + } + if len(config.CipherSuites) > 2 { + return errors.New("sio: too many cipher suites") + } + for _, c := range config.CipherSuites { + if int(c) >= len(supportedCiphers) { + return errors.New("sio: unknown cipher suite") + } + } + if config.PayloadSize > maxPayloadSize { + return errors.New("sio: payload size is too large") + } + + if config.MinVersion < Version10 { + config.MinVersion = Version10 + } + if config.MaxVersion < Version10 { + config.MaxVersion = Version20 + } + if config.MinVersion > config.MaxVersion { + return errors.New("sio: minimum version cannot be larger than maximum version") + } + if len(config.CipherSuites) == 0 { + config.CipherSuites = defaultCipherSuites() + } + if config.Rand == nil { + config.Rand = rand.Reader + } + if config.PayloadSize == 0 { + config.PayloadSize = maxPayloadSize + } + return nil +} diff --git a/vendor/github.com/minio/sio/writer-v1.go b/vendor/github.com/minio/sio/writer-v1.go new file mode 100644 index 000000000..590eea3d7 --- /dev/null +++ b/vendor/github.com/minio/sio/writer-v1.go @@ -0,0 +1,199 @@ +// Copyright (C) 2017 Minio Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sio + +import "io" + +type decWriterV10 struct { + authDecV10 + dst io.Writer + + buffer packageV10 + offset int +} + +// decryptWriterV10 returns an io.WriteCloser wrapping the given io.Writer. +// The returned io.WriteCloser decrypts everything written to it using DARE 1.0 +// and writes all decrypted plaintext to the wrapped io.Writer. +// +// The io.WriteCloser must be closed to finalize the decryption successfully. +func decryptWriterV10(dst io.Writer, config *Config) (*decWriterV10, error) { + ad, err := newAuthDecV10(config) + if err != nil { + return nil, err + } + return &decWriterV10{ + authDecV10: ad, + dst: dst, + buffer: make(packageV10, maxPackageSize), + }, nil +} + +func (w *decWriterV10) Write(p []byte) (n int, err error) { + if w.offset > 0 && w.offset < headerSize { // buffer the header -> special code b/c we don't know when to decrypt without header + remaining := headerSize - w.offset + if len(p) < remaining { + n = copy(w.buffer[w.offset:], p) + w.offset += n + return + } + n = copy(w.buffer[w.offset:], p[:remaining]) + p = p[remaining:] + w.offset += n + } + if w.offset >= headerSize { // buffer the ciphertext and tag -> here we know when to decrypt + remaining := w.buffer.Length() - w.offset + if len(p) < remaining { + nn := copy(w.buffer[w.offset:], p) + w.offset += nn + return n + nn, err + } + n += copy(w.buffer[w.offset:], p[:remaining]) + if err = w.Open(w.buffer.Payload(), w.buffer[:w.buffer.Length()]); err != nil { + return n, err + } + if err = flush(w.dst, w.buffer.Payload()); err != nil { // write to underlying io.Writer + return n, err + } + p = p[remaining:] + w.offset = 0 + } + for len(p) > headerSize { + packageLen := headerSize + tagSize + headerV10(p).Len() + if len(p) < packageLen { // p contains not the full package -> cannot decrypt + w.offset = copy(w.buffer[:], p) + n += w.offset + return n, err + } + if err = w.Open(w.buffer[headerSize:packageLen-tagSize], p[:packageLen]); err != nil { + return n, err + } + if err = flush(w.dst, w.buffer[headerSize:packageLen-tagSize]); err != nil { // write to underlying io.Writer + return n, err + } + p = p[packageLen:] + n += packageLen + } + if len(p) > 0 { + w.offset = copy(w.buffer[:], p) + n += w.offset + } + return n, nil +} + +func (w *decWriterV10) Close() error { + if w.offset > 0 { + if w.offset <= headerSize+tagSize { + return errInvalidPayloadSize // the payload is always > 0 + } + header := headerV10(w.buffer[:headerSize]) + if w.offset < headerSize+header.Len()+tagSize { + return errInvalidPayloadSize // there is less data than specified by the header + } + if err := w.Open(w.buffer.Payload(), w.buffer[:w.buffer.Length()]); err != nil { + return err + } + if err := flush(w.dst, w.buffer.Payload()); err != nil { // write to underlying io.Writer + return err + } + } + if dst, ok := w.dst.(io.Closer); ok { + return dst.Close() + } + return nil +} + +type encWriterV10 struct { + authEncV10 + dst io.Writer + + buffer packageV10 + offset int + payloadSize int +} + +// encryptWriterV10 returns an io.WriteCloser wrapping the given io.Writer. +// The returned io.WriteCloser encrypts everything written to it using DARE 1.0 +// and writes all encrypted ciphertext as well as the package header and tag +// to the wrapped io.Writer. +// +// The io.WriteCloser must be closed to finalize the encryption successfully. +func encryptWriterV10(dst io.Writer, config *Config) (*encWriterV10, error) { + ae, err := newAuthEncV10(config) + if err != nil { + return nil, err + } + return &encWriterV10{ + authEncV10: ae, + dst: dst, + buffer: make(packageV10, maxPackageSize), + payloadSize: config.PayloadSize, + }, nil +} + +func (w *encWriterV10) Write(p []byte) (n int, err error) { + if w.offset > 0 { // buffer the plaintext + remaining := w.payloadSize - w.offset + if len(p) < remaining { + n = copy(w.buffer[headerSize+w.offset:], p) + w.offset += n + return + } + n = copy(w.buffer[headerSize+w.offset:], p[:remaining]) + w.Seal(w.buffer, w.buffer[headerSize:headerSize+w.payloadSize]) + if err = flush(w.dst, w.buffer[:w.buffer.Length()]); err != nil { // write to underlying io.Writer + return n, err + } + p = p[remaining:] + w.offset = 0 + } + for len(p) >= w.payloadSize { + w.Seal(w.buffer[:], p[:w.payloadSize]) + if err = flush(w.dst, w.buffer[:w.buffer.Length()]); err != nil { // write to underlying io.Writer + return n, err + } + p = p[w.payloadSize:] + n += w.payloadSize + } + if len(p) > 0 { + w.offset = copy(w.buffer[headerSize:], p) + n += w.offset + } + return +} + +func (w *encWriterV10) Close() error { + if w.offset > 0 { + w.Seal(w.buffer[:], w.buffer[headerSize:headerSize+w.offset]) + if err := flush(w.dst, w.buffer[:w.buffer.Length()]); err != nil { // write to underlying io.Writer + return err + } + } + if dst, ok := w.dst.(io.Closer); ok { + return dst.Close() + } + return nil +} + +func flush(w io.Writer, p []byte) error { + n, err := w.Write(p) + if err != nil { + return err + } + if n != len(p) { // not neccasary if the w follows the io.Writer doc *precisly* + return io.ErrShortWrite + } + return nil +} diff --git a/vendor/github.com/minio/sio/writer-v2.go b/vendor/github.com/minio/sio/writer-v2.go new file mode 100644 index 000000000..4c349cd8e --- /dev/null +++ b/vendor/github.com/minio/sio/writer-v2.go @@ -0,0 +1,177 @@ +// Copyright (C) 2018 Minio Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sio + +import ( + "io" +) + +type encWriterV20 struct { + authEncV20 + dst io.Writer + + buffer packageV20 + offset int +} + +// encryptWriterV20 returns an io.WriteCloser wrapping the given io.Writer. +// The returned io.WriteCloser encrypts everything written to it using DARE 2.0 +// and writes all encrypted ciphertext as well as the package header and tag +// to the wrapped io.Writer. +// +// The io.WriteCloser must be closed to finalize the encryption successfully. +func encryptWriterV20(dst io.Writer, config *Config) (*encWriterV20, error) { + ae, err := newAuthEncV20(config) + if err != nil { + return nil, err + } + return &encWriterV20{ + authEncV20: ae, + dst: dst, + buffer: make(packageV20, maxPackageSize), + }, nil +} + +func (w *encWriterV20) Write(p []byte) (n int, err error) { + if w.finalized { + // The caller closed the encWriterV20 instance (called encWriterV20.Close()). + // This is a bug in the calling code - Write after Close is not allowed. + panic("sio: write to stream after close") + } + if w.offset > 0 { // buffer the plaintext data + remaining := maxPayloadSize - w.offset + if len(p) <= remaining { // <= is important here to buffer up to 64 KB (inclusivly) - see: Close() + w.offset += copy(w.buffer[headerSize+w.offset:], p) + return len(p), nil + } + n = copy(w.buffer[headerSize+w.offset:], p[:remaining]) + w.Seal(w.buffer, w.buffer[headerSize:headerSize+maxPayloadSize]) + if err = flush(w.dst, w.buffer); err != nil { // write to underlying io.Writer + return n, err + } + p = p[remaining:] + w.offset = 0 + } + for len(p) > maxPayloadSize { // > is important here to call Seal (not SealFinal) only if there is at least on package left - see: Close() + w.Seal(w.buffer, p[:maxPayloadSize]) + if err = flush(w.dst, w.buffer); err != nil { // write to underlying io.Writer + return n, err + } + p = p[maxPayloadSize:] + n += maxPayloadSize + } + if len(p) > 0 { + w.offset = copy(w.buffer[headerSize:], p) + n += w.offset + } + return n, nil +} + +func (w *encWriterV20) Close() error { + if w.offset > 0 { // true if at least one Write call happend + w.SealFinal(w.buffer, w.buffer[headerSize:headerSize+w.offset]) + if err := flush(w.dst, w.buffer[:headerSize+w.offset+tagSize]); err != nil { // write to underlying io.Writer + return err + } + w.offset = 0 + } + if closer, ok := w.dst.(io.Closer); ok { + return closer.Close() + } + return nil +} + +type decWriterV20 struct { + authDecV20 + dst io.Writer + + buffer packageV20 + offset int +} + +// decryptWriterV20 returns an io.WriteCloser wrapping the given io.Writer. +// The returned io.WriteCloser decrypts everything written to it using DARE 2.0 +// and writes all decrypted plaintext to the wrapped io.Writer. +// +// The io.WriteCloser must be closed to finalize the decryption successfully. +func decryptWriterV20(dst io.Writer, config *Config) (*decWriterV20, error) { + ad, err := newAuthDecV20(config) + if err != nil { + return nil, err + } + return &decWriterV20{ + authDecV20: ad, + dst: dst, + buffer: make(packageV20, maxPackageSize), + }, nil +} + +func (w *decWriterV20) Write(p []byte) (n int, err error) { + if w.offset > 0 { // buffer package + remaining := headerSize + maxPayloadSize + tagSize - w.offset + if len(p) < remaining { + w.offset += copy(w.buffer[w.offset:], p) + return len(p), nil + } + n = copy(w.buffer[w.offset:], p[:remaining]) + plaintext := w.buffer[headerSize : headerSize+maxPayloadSize] + if err = w.Open(plaintext, w.buffer); err != nil { + return n, err + } + if err = flush(w.dst, plaintext); err != nil { // write to underlying io.Writer + return n, err + } + p = p[remaining:] + w.offset = 0 + } + for len(p) >= maxPackageSize { + plaintext := w.buffer[headerSize : headerSize+maxPayloadSize] + if err = w.Open(plaintext, p[:maxPackageSize]); err != nil { + return n, err + } + if err = flush(w.dst, plaintext); err != nil { // write to underlying io.Writer + return n, err + } + p = p[maxPackageSize:] + n += maxPackageSize + } + if len(p) > 0 { + if w.finalized { + return n, errUnexpectedData + } + w.offset = copy(w.buffer[:], p) + n += w.offset + } + return n, nil +} + +func (w *decWriterV20) Close() error { + if w.offset > 0 { + if w.offset <= headerSize+tagSize { // the payload is always > 0 + return errInvalidPayloadSize + } + if err := w.Open(w.buffer[headerSize:w.offset-tagSize], w.buffer[:w.offset]); err != nil { + return err + } + if err := flush(w.dst, w.buffer[headerSize:w.offset-tagSize]); err != nil { // write to underlying io.Writer + return err + } + w.offset = 0 + } + if closer, ok := w.dst.(io.Closer); ok { + return closer.Close() + } + return nil +} diff --git a/vendor/github.com/minio/sio/writer.go b/vendor/github.com/minio/sio/writer.go deleted file mode 100644 index 6292da6ce..000000000 --- a/vendor/github.com/minio/sio/writer.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (C) 2017 Minio Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sio - -import ( - "crypto/cipher" - "io" -) - -type decryptedWriter struct { - dst io.Writer - config Config - - sequenceNumber uint32 - ciphers []cipher.AEAD - - pack [headerSize + payloadSize + tagSize]byte - offset int -} - -func (w *decryptedWriter) Write(p []byte) (n int, err error) { - if w.offset > 0 && w.offset < headerSize { - remaining := headerSize - w.offset - if len(p) < remaining { - n = copy(w.pack[w.offset:], p) - w.offset += n - return - } - n = copy(w.pack[w.offset:], p[:remaining]) - p = p[remaining:] - w.offset += n - } - if w.offset >= headerSize { - remaining := headerSize + header(w.pack[:]).Len() + tagSize - w.offset - if len(p) < remaining { - nn := copy(w.pack[w.offset:], p) - w.offset += nn - return n + nn, err - } - n += copy(w.pack[w.offset:], p[:remaining]) - if err = w.decrypt(w.pack[:]); err != nil { - return n, err - } - p = p[remaining:] - w.offset = 0 - } - for len(p) > headerSize { - header := header(p) - if len(p) < headerSize+header.Len()+tagSize { - w.offset = copy(w.pack[:], p) - n += w.offset - return - } - if err = w.decrypt(p); err != nil { - return n, err - } - p = p[headerSize+header.Len()+tagSize:] - n += headerSize + header.Len() + tagSize - } - w.offset = copy(w.pack[:], p) - n += w.offset - return -} - -func (w *decryptedWriter) Close() error { - if w.offset > 0 { - if w.offset < headerSize { - return errMissingHeader - } - if w.offset < headerSize+header(w.pack[:]).Len()+tagSize { - return errPayloadTooShort - } - if err := w.decrypt(w.pack[:]); err != nil { - return err - } - } - if dst, ok := w.dst.(io.Closer); ok { - return dst.Close() - } - return nil -} - -func (w *decryptedWriter) decrypt(src []byte) error { - header := header(src) - if err := w.config.verifyHeader(header); err != nil { - return err - } - if header.SequenceNumber() != w.sequenceNumber { - return errPackageOutOfOrder - } - - aeadCipher := w.ciphers[header.Cipher()] - plaintext, err := aeadCipher.Open(w.pack[headerSize:headerSize], header[4:headerSize], src[headerSize:headerSize+header.Len()+tagSize], header[:4]) - if err != nil { - return errTagMismatch - } - - n, err := w.dst.Write(plaintext) - if err != nil { - return err - } - if n != len(plaintext) { - return io.ErrShortWrite - } - - w.sequenceNumber++ - return nil -} - -type encryptedWriter struct { - dst io.Writer - config Config - - nonce [8]byte - sequenceNumber uint32 - cipher cipher.AEAD - - pack [headerSize + payloadSize + tagSize]byte - offset int -} - -func (w *encryptedWriter) Write(p []byte) (n int, err error) { - if w.offset > 0 { - remaining := payloadSize - w.offset - if len(p) < remaining { - n = copy(w.pack[headerSize+w.offset:], p) - w.offset += n - return - } - n = copy(w.pack[headerSize+w.offset:], p[:remaining]) - if err = w.encrypt(w.pack[headerSize : headerSize+payloadSize]); err != nil { - return - } - p = p[remaining:] - w.offset = 0 - } - for len(p) >= payloadSize { - if err = w.encrypt(p[:payloadSize]); err != nil { - return - } - p = p[payloadSize:] - n += payloadSize - } - if len(p) > 0 { - w.offset = copy(w.pack[headerSize:], p) - n += w.offset - } - return -} - -func (w *encryptedWriter) Close() error { - if w.offset > 0 { - return w.encrypt(w.pack[headerSize : headerSize+w.offset]) - } - if dst, ok := w.dst.(io.Closer); ok { - return dst.Close() - } - return nil -} - -func (w *encryptedWriter) encrypt(src []byte) error { - header := header(w.pack[:]) - header.SetVersion(w.config.MaxVersion) - header.SetCipher(w.config.CipherSuites[0]) - header.SetLen(len(src)) - header.SetSequenceNumber(w.sequenceNumber) - header.SetNonce(w.nonce) - - w.cipher.Seal(w.pack[headerSize:headerSize], header[4:headerSize], src, header[:4]) - - n, err := w.dst.Write(w.pack[:headerSize+len(src)+tagSize]) - if err != nil { - return err - } - if n != headerSize+len(src)+tagSize { - return io.ErrShortWrite - } - - w.sequenceNumber++ - return nil -} diff --git a/vendor/vendor.json b/vendor/vendor.json index 3befc707b..e16daf597 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -456,10 +456,10 @@ "revisionTime": "2017-08-28T17:39:33Z" }, { - "checksumSHA1": "/VYXTlcksV92qL4Avr5S3NJ/Fxs=", + "checksumSHA1": "pvBHvHfubwxgvXNz35PDMbBWY9s=", "path": "github.com/minio/sio", - "revision": "d8be2518a912f0db0dd17dc55f46da4360ee60d8", - "revisionTime": "2017-09-06T19:57:40Z" + "revision": "83dd737d26dbcf4847f48ecec77c2c13f739eb25", + "revisionTime": "2018-03-01T21:58:29Z" }, { "checksumSHA1": "zvQr4zOz1/g/Fui6co0sctxrJ28=",