vendor update github.com/minio/sio (#5599)

This change updates the sio library and adds DARE 2.0 support
to the server.
master
Andreas Auernhammer 7 years ago committed by kannappanr
parent 1b7b8f14c9
commit 4e5237b02a
  1. 7
      vendor/github.com/minio/sio/README.md
  2. 411
      vendor/github.com/minio/sio/dare.go
  3. 117
      vendor/github.com/minio/sio/generic.go
  4. 39
      vendor/github.com/minio/sio/header.go
  5. 20
      vendor/github.com/minio/sio/internal/cpu/aes.go
  6. 20
      vendor/github.com/minio/sio/internal/cpu/aesni_amd64.go
  7. 27
      vendor/github.com/minio/sio/internal/cpu/aesni_amd64.s
  8. 19
      vendor/github.com/minio/sio/internal/cpu/aesni_generic.go
  9. 163
      vendor/github.com/minio/sio/reader-v1.go
  10. 178
      vendor/github.com/minio/sio/reader-v2.go
  11. 169
      vendor/github.com/minio/sio/reader.go
  12. 257
      vendor/github.com/minio/sio/sio.go
  13. 199
      vendor/github.com/minio/sio/writer-v1.go
  14. 177
      vendor/github.com/minio/sio/writer-v2.go
  15. 193
      vendor/github.com/minio/sio/writer.go
  16. 6
      vendor/vendor.json

@ -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*

411
vendor/github.com/minio/sio/dare.go generated vendored

@ -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
}

@ -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)
}

@ -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[:]) }

@ -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() }

@ -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

@ -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

@ -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 }

@ -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
}

@ -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
}

@ -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
}

257
vendor/github.com/minio/sio/sio.go generated vendored

@ -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
}

@ -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
}

@ -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
}

@ -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
}

@ -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=",

Loading…
Cancel
Save