Documentation
¶
Overview ¶
Package sio implements a provable secure authenticated encryption scheme for continuous byte streams.
Index ¶
- Constants
- Variables
- type DecReader
- type DecReaderAt
- type DecWriter
- type EncReader
- type EncWriter
- type Stream
- func (s *Stream) DecryptReader(r io.Reader, nonce, associatedData []byte) *DecReader
- func (s *Stream) DecryptReaderAt(r io.ReaderAt, nonce, associatedData []byte) *DecReaderAt
- func (s *Stream) DecryptWriter(w io.Writer, nonce, associatedData []byte) *DecWriter
- func (s *Stream) EncryptReader(r io.Reader, nonce, associatedData []byte) *EncReader
- func (s *Stream) EncryptWriter(w io.Writer, nonce, associatedData []byte) *EncWriter
- func (s *Stream) NonceSize() int
- func (s *Stream) Overhead(length int64) int64
Examples ¶
Constants ¶
const ( // MaxBufSize is the maximum buffer size for streams. MaxBufSize = (1 << 24) - 1 // BufSize is the recommended buffer size for streams. BufSize = 1 << 14 )
Variables ¶
var ( // ErrAuth is returned when the decryption of a data stream fails. // It indicates that the data is not authentic (e.g. malisously // modified). ErrAuth = errAuth{} // ErrExceeded is returned when no more data can be encrypted / // decrypted securely. It indicates that the data stream is too // large to be encrypted / decrypted with a single key-nonce // combination. ErrExceeded = errExceeded{} )
Functions ¶
This section is empty.
Types ¶
type DecReader ¶
type DecReader struct {
// contains filtered or unexported fields
}
DecReader wraps an io.Reader and decrypts and verifies everything it reads from it.
Example ¶
// Use the key used to encrypt the data. (See e.g. the EncReader example). // Obviously don't use this example key for anything real. key, _ := hex.DecodeString("ffb0823fcab82a983e1725e003c702252ef4fc7054796b3c23d08aa189f662c9") block, _ := aes.NewCipher(key) gcm, _ := cipher.NewGCM(block) stream := sio.NewStream(gcm, sio.BufSize) var ( // Use the nonce value using during encryption. // (See e.g. the EncWriter example). nonce []byte = make([]byte, stream.NonceSize()) // Use the associated data using during encryption. // (See e.g. the EncWriter example). associatedData []byte = nil ) ciphertext := hex.NewDecoder(strings.NewReader("9f54ed8df9cffaff02eddb479b95fd3bed9391758a4f81376cfadd7f8c00")) r := stream.DecryptReader(ciphertext, nonce, associatedData) // Reading from r returns the original plaintext (or an error). if _, err := ioutil.ReadAll(r); err != nil { if err == sio.ErrAuth { // Read data is not authentic -> ciphertext has been modified. // TODO: error handling panic(err) } }
func (*DecReader) Read ¶
Read behaves like specified by the io.Reader interface. It decrypts and verifies up to len(p) bytes which it reads from the underlying io.Reader.
It returns ErrAuth if the read data is not authentic. It returns ErrExceeded when no more data can be decrypted securely.
func (*DecReader) ReadByte ¶
ReadByte reads from the underlying io.Reader and returns one decrypted and verified byte.
It returns ErrAuth if the read data is not authentic. It returns ErrExceeded when no more bytes can be decrypted securely.
func (*DecReader) WriteTo ¶
WriteTo keeps reading from the underlying io.Reader until it enounters an error or io.EOF and decrypts and verifies everything before writting it to w.
It returns ErrAuth if the read data is not authentic. It returns ErrExceeded when no more data can be decrypted securely.
type DecReaderAt ¶
type DecReaderAt struct {
// contains filtered or unexported fields
}
DecReader wraps an io.ReaderAt and decrypts and verifies everything it reads from it.
Example ¶
// Use the key used to encrypt the data. (See e.g. the EncReader example). // Obviously don't use this example key for anything real. key, _ := hex.DecodeString("ffb0823fcab82a983e1725e003c702252ef4fc7054796b3c23d08aa189f662c9") block, _ := aes.NewCipher(key) gcm, _ := cipher.NewGCM(block) stream := sio.NewStream(gcm, sio.BufSize) var ( // Use the nonce value using during encryption. // (See e.g. the EncWriter example). nonce []byte = make([]byte, stream.NonceSize()) // Use the associated data using during encryption. // (See e.g. the EncWriter example). associatedData []byte = nil ) rawBytes, _ := hex.DecodeString("9f54ed8df9cffaff02eddb479b95fd3bed9391758a4f81376cfadd7f8c00") ciphertext := bytes.NewReader(rawBytes) r := stream.DecryptReaderAt(ciphertext, nonce, associatedData) section := io.NewSectionReader(r, 5, 9) // Read the 'plaintext' substring from 'some plaintext' // Reading from section returns the original plaintext (or an error). if _, err := ioutil.ReadAll(section); err != nil { if err == sio.ErrAuth { // Read data is not authentic -> ciphertext has been modified. // TODO: error handling panic(err) } }
func (*DecReaderAt) ReadAt ¶
func (r *DecReaderAt) ReadAt(p []byte, offset int64) (int, error)
ReadAt behaves as specified by the io.ReaderAt interface. It reads len(p) bytes from the underlying io.ReaderAt starting at offset and decrypts and verifies the read data.
It returns ErrAuth if the read data is not authentic. It returns ErrExceeded when no more data can be decrypted securely or the offset excceds the data limit.
type DecWriter ¶
type DecWriter struct {
// contains filtered or unexported fields
}
DecWriter wraps an io.Writer and decrypts and verifies everything written to it. It MUST be closed to complete the decryption successfully.
Example ¶
// Use the key used to encrypt the data. (See e.g. the EncWriter example). // Obviously don't use this example key for anything real. key, _ := hex.DecodeString("ffb0823fcab82a983e1725e003c702252ef4fc7054796b3c23d08aa189f662c9") block, _ := aes.NewCipher(key) gcm, _ := cipher.NewGCM(block) stream := sio.NewStream(gcm, sio.BufSize) var ( // Use the nonce value using during encryption. // (See e.g. the EncWriter example). nonce []byte = make([]byte, stream.NonceSize()) // Use the associated data using during encryption. // (See e.g. the EncWriter example). associatedData []byte = nil ) plaintext := bytes.NewBuffer(nil) w := stream.DecryptWriter(plaintext, nonce, associatedData) defer func() { if err := w.Close(); err != nil { if err == sio.ErrAuth { // During Close() the DecWriter may detect unauthentic data -> decryption error. panic(err) // TODO: error handling } panic(err) // TODO: error handling } }() ciphertext, _ := hex.DecodeString("9f54ed8df9cffaff02eddb479b95fd3bed9391758a4f81376cfadd7f8c00") // Writing ciphertext to w writes decrypted and verified data to // the underlying io.Writer (i.e. the plaintext *bytes.Buffer) or // returns an error. if _, err := w.Write(ciphertext); err != nil { if err == sio.ErrAuth { // Read data is not authentic -> ciphertext has been modified. // TODO: error handling panic(err) } }
func (*DecWriter) Close ¶
Close completes the decryption process and writes any remaining bytes to the underlying io.Writer. If the underlying io.Writer implements io.Closer, Close closes it as well. It is safe to call Close multiple times.
func (*DecWriter) ReadFrom ¶
ReadFrom reads from r until it encounters an error or reaches io.EOF. It decrypts and verifies everything it reads before writting it to the underlying io.Writer. ReadFrom does NOT close the DecWriter such that this must be done explicitly. It must not be called after the DecWriter has been closed.
It returns ErrAuth if the read data is not authentic. It returns ErrExceeded when no more data can be decrypted securely.
func (*DecWriter) Write ¶
Write decrypts and verifies p before writting it to the underlying io.Writer. It must not be called after the DecWriter has been closed.
It returns ErrAuth when some part of p is not authentic and never writes non-authentic data to the underlying io.Writer. It returns ErrExceeded when no more data can be decrypted securely.
type EncReader ¶
type EncReader struct {
// contains filtered or unexported fields
}
EncReader wraps is an io.Reader and encrypts and authenticates everything it reads from it.
Example ¶
// Use an unique key per data stream. For example derive one // from a password using a suitable package like argon2 or // from a master key using e.g. HKDF. // Obviously don't use this example key for anything real. key, _ := hex.DecodeString("ffb0823fcab82a983e1725e003c702252ef4fc7054796b3c23d08aa189f662c9") block, _ := aes.NewCipher(key) gcm, _ := cipher.NewGCM(block) stream := sio.NewStream(gcm, sio.BufSize) var ( // Use a unique nonce per key. If you choose an unique key // you can also set the nonce to all zeros. (What to prefer // depends on the application). nonce []byte = make([]byte, stream.NonceSize()) // If you want to bind additional data to the ciphertext // (e.g. a file name to prevent renaming / moving the file) // set the associated data. But be aware that the associated // data is not encrypted (only authenticated) and must be // available when decrypting the ciphertext again. associatedData []byte = nil ) plaintext := strings.NewReader("some plaintext") r := stream.EncryptReader(plaintext, nonce, associatedData) // Reading from r returns encrypted and authenticated data. ioutil.ReadAll(r)
func (*EncReader) Read ¶
Read behaves like specified by the io.Reader interface. It encryptes and authenticates up to len(p) bytes which it reads from the underlying io.Reader.
It returns ErrExceeded when no more data can be encrypted securely.
func (*EncReader) ReadByte ¶
ReadByte reads from the underlying io.Reader and returns one encrypted and authenticated byte.
It returns ErrExceeded when no more bytes can be encrypted securely.
type EncWriter ¶
type EncWriter struct {
// contains filtered or unexported fields
}
EncWriter wraps an io.Writer and encrypts and authenticates everything written to it. It MUST be closed to complete the encryption successfully.
Example ¶
// Use an unique key per data stream. For example derive one // from a password using a suitable package like argon2 or // from a master key using e.g. HKDF. // Obviously don't use this example key for anything real. key, _ := hex.DecodeString("ffb0823fcab82a983e1725e003c702252ef4fc7054796b3c23d08aa189f662c9") block, _ := aes.NewCipher(key) gcm, _ := cipher.NewGCM(block) stream := sio.NewStream(gcm, sio.BufSize) var ( // Use a unique nonce per key. If you choose an unique key // you can also set the nonce to all zeros. (What to prefer // depends on the application). nonce []byte = make([]byte, stream.NonceSize()) // If you want to bind additional data to the ciphertext // (e.g. a file name to prevent renaming / moving the file) // set the associated data. But be aware that the associated // data is not encrypted (only authenticated) and must be // available when decrypting the ciphertext again. associatedData []byte = nil ) ciphertext := bytes.NewBuffer(nil) // You can also use the plaintext size and stream.Overhead() to avoid re-allocation. w := stream.EncryptWriter(ciphertext, nonce, associatedData) defer func() { if err := w.Close(); err != nil { // The EncWriter must be closed to complete the encryption. panic(err) // TODO: error handling } }() // Writing plaintext to w writes encrypted and authenticated data to // the underlying io.Writer (i.e. the ciphertext *bytes.Buffer) if _, err := io.WriteString(w, "some plaintext"); err != nil { // TODO: error handling }
func (*EncWriter) Close ¶
Close completes the encryption process and writes any remaining bytes to the underlying io.Writer. If the underlying io.Writer implements io.Closer, Close closes it as well. It is safe to call Close multiple times.
func (*EncWriter) ReadFrom ¶
ReadFrom reads from r until it encounters an error or reaches io.EOF. It encrypts and authenticates everything it reads before writting it to the underlying io.Writer. ReadFrom does NOT close the EncWriter such that this must be done explicitly. It must not be called after the EncWriter has been closed.
It returns ErrExceeded when no more data can be encrypted securely. However, the EncWriter can still be closed to complete the encryption successfully.
func (*EncWriter) Write ¶
Write encrypts and authenticates p before writting it to the underlying io.Writer. It must not be called after the EncWriter has been closed.
It returns ErrExceeded when no more data can be encrypted securely. However, the EncWriter can still be closed to complete the encryption successfully.
type Stream ¶
type Stream struct {
// contains filtered or unexported fields
}
A Stream encrypts or decrypts continuous byte streams.
func NewStream ¶
NewStream creates a new Stream that encrypts or decrypts data streams with the cipher. If you don't have special requirements just use the default BufSize.
The returned Stream will allocate a new bufSize large buffer when en/decrypting a data stream.
The cipher must support a NonceSize() >= 4 and the bufSize must be between 1 (inclusive) and MaxBufSize (inclusive).
Example (AESGCM) ¶
// Load your secret key from a safe place. You should use a unique key // per data stream since the nonce size of `Stream` (with AES-GCM) is // to short for chosing a nonce at random (risk of repeating the // nonce is too high). // Obviously don't use this example key for anything real. // If you want to convert a passphrase to a key, use a suitable // package like argon2 or scrypt. Use 16 byte (128 bit) for // AES128-GCM and 32 byte (256 bit) for AES256-GCM. key, _ := hex.DecodeString("4310889c63c87a957e11e0b3d75047beadc8736d72fc1407439862e85a7377f4") block, _ := aes.NewCipher(key) gcm, _ := cipher.NewGCM(block) stream := sio.NewStream(gcm, sio.BufSize) // Print the nonce size for Stream (with AES-GCM) and the overhead added // when encrypting a 1 MiB data stream. fmt.Printf("NonceSize: %d, Overhead: %d", stream.NonceSize(), stream.Overhead(1024*1024))
Output: NonceSize: 8, Overhead: 1024
Example (XChaCha20Poly1305) ¶
// Load your secret key from a safe place. You may reuse it for // en/decrypting multiple data streams. (XChaCha20-Poly1305 nonce // values are large enough to be chosen at random without risking // to select a nonce twice - probability is negligable). // // Obviously don't use this example key for anything real. // If you want to convert a passphrase to a key, use a suitable // package like argon2 or scrypt - or take a look at the sio/sioutil // package. key, _ := hex.DecodeString("f230e700c4f120b623b84ac26cbcb5ae926f44f36589e63745a46ae0ca47137d") x20p1305, _ := chacha20poly1305.NewX(key) stream := sio.NewStream(x20p1305, sio.BufSize) // Print the nonce size for Stream (with XChaCha20-Poly1305) and the // overhead added when encrypting a 1 MiB data stream. fmt.Printf("NonceSize: %d, Overhead: %d", stream.NonceSize(), stream.Overhead(1024*1024))
Output: NonceSize: 20, Overhead: 1024
func (*Stream) DecryptReader ¶
DecryptReader returns a new DecReader that wraps r and decrypts and verifies everything it reads. The nonce and associatedData must match the values used to encrypt the data.
func (*Stream) DecryptReaderAt ¶
func (s *Stream) DecryptReaderAt(r io.ReaderAt, nonce, associatedData []byte) *DecReaderAt
DecryptReaderAt returns a new DecReaderAt that wraps r and decrypts and verifies everything it reads. The nonce and associatedData must match the values used to encrypt the data.
func (*Stream) DecryptWriter ¶
DecryptWriter returns a new DecWriter that wraps w and decrypts and verifies everything written to it. The nonce and associatedData must match the values used when encrypting the data. The associatedData is not written to w.
func (*Stream) EncryptReader ¶
EncryptReader returns a new EncReader that wraps r and encrypts and authenticates everything it reads. The nonce must be NonceSize() bytes long and unique for the same key (used to create cipher.AEAD). The associatedData is authenticated but not encrypted.
func (*Stream) EncryptWriter ¶
EncryptWriter returns a new EncWriter that wraps w and encrypts and authenticates everything written to it. The nonce must be NonceSize() bytes long and unique for the same key (used to create cipher.AEAD). The associatedData is authenticated but neither encrypted nor written to w and must be provided whenever decrypting the data again.