vendor update github.com/minio/sio (#5599)
This change updates the sio library and adds DARE 2.0 support to the server.master
parent
1b7b8f14c9
commit
4e5237b02a
@ -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 |
||||
} |
@ -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 |
||||
} |
Loading…
Reference in new issue