Documentation ¶
Overview ¶
Package oae implements online authenticated encryption as described in paper "Online Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance" by Viet Tung Hoang, Reza Reyhanitabar, Phillip Rogaway, and Damian Vizár (https://eprint.iacr.org/2015/189)
Overview ¶
Online authenticated encryption allows encrypting the plaintext stream on the fly and later partially decrypting the ciphertext stream. The ciphertext is authenticated, which means that modification, reordering, appending the data will be detected.
All types in this package implement standard Go interfaces: io.Reader, io.ReadSeeker and io.Writer. Note, that due to tamper-resistant properties of OAE you cannot rewrite or append parts of the ciphertext, only write the new ciphertext.
All constructors accept key and aad parameters. Key must be securely stored in a separate secure storage, e.g. using AWS KMS (using envelope encryption), GCP Secret Manager or Hashicorp Vault. Aad is additional authenticated data, it is passed into HKDF function and may be nil. It is preferrable that user puts some kind of "tag" related to the plaintext in question.
Examples ¶
Encrypt a file into another file and decrypt it back.
func EncryptFile(topSecretKey []byte) error { plaintext, err := os.Open("source.txt") if err != nil { return err } defer plaintext.Close() ciphertext, err := os.Create("encrypted.dat") if err != nil { return err } defer ciphertext.Close() writer, err := oae.NewEncryptingWriterWithHeader(ciphertext, topSecretKey, nil, oae.EncryptOptions{}) if err != nil { return err } _, err = io.Copy(writer, plaintext) if err != nil { return err } err = writer.Close() if err != nil { return err } return nil } func DecryptFile(topSecretKey []byte) error { ciphertext, err := os.Open("encrypted.dat") if err != nil { return err } defer ciphertext.Close() plaintext, err := os.Create("decrypted.txt") if err != nil { return err } defer plaintext.Close() reader, err := oae.NewDecryptingReaderWithHeader(plaintext, topSecretKey, nil) if err != nil { return err } _, err = io.Copy(plaintext, reader) if err != nil { return err } return nil }
Download and decrypt part of the encrypted blob from S3. Header and topSecretKey are stored separately. Note that this example requires passing the plaintextTotal.
func DownloadAndDecryptRange(header oae.CiphertextHeader, topSecretKey []byte, from, to, plaintextTotal int) ([]byte, error) { start, end := header.Algorithm.CiphertextRange(header.SegmentSize, int64(from), int64(to), int64(plaintextTotal)) rangeHeader := fmt.Sprintf("bytes=%d-%d", start, end) var buf aws.WriteAtBuffer _, err := s3Downloader.Download(&buf, &s3.GetObject{ Bucket: aws.String("bucket"), Key: aws.String("key"), Range: aws.String(rangeHeader), }) if err != nil { return nil, err } ciphertext := bytes.NewReader(buf.Bytes()) er, err := oae.NewDecryptingReader(ciphertext, topSecretKey, nil, header) if err != nil { return nil, err } var result bytes.Buffer _, err = io.Copy(&result, er) if err != nil { return nil, err } return result.Bytes(), nil }
Index ¶
- Constants
- func NewEncryptingReader(r io.Reader, key []byte, aad []byte, options EncryptOptions) (CiphertextHeader, *EncryptingReader, error)
- func NewEncryptingWriter(w io.Writer, key []byte, aad []byte, options EncryptOptions) (CiphertextHeader, *EncryptingWriter, error)
- type Algorithm
- func (a Algorithm) CiphertextLength(segmentSize int, plaintextLength int64) (int64, error)
- func (a Algorithm) CiphertextRange(segmentSize int, plaintextRange Range, plaintextTotal int64) (Range, error)
- func (a Algorithm) KeySize() int
- func (a Algorithm) PlaintextLength(segmentSize int, ciphertextLength int64) (int64, error)
- type CiphertextHeader
- type DecryptingReadSeeker
- type DecryptingReader
- type EncryptOptions
- type EncryptingReader
- type EncryptingWriter
- type Range
Constants ¶
const ( // DefaultAlgorithm and DefaultSegmentSize should be used in almost all cases unless you have specific requirements // (see EncryptionOptions for details on segment size). DefaultAlgorithm = AesGcm128Sha256 DefaultSegmentSize = 4096 // MaxSegmentSize is used to limit the memory usage when reading the untrusted ciphertext. MaxSegmentSize = 8 * 1024 * 1024 // MaxNumSegments is the maximum number of ciphertext segments. MaxNumSegments = 0xfffffffe )
Variables ¶
This section is empty.
Functions ¶
func NewEncryptingReader ¶
func NewEncryptingReader(r io.Reader, key []byte, aad []byte, options EncryptOptions) (CiphertextHeader, *EncryptingReader, error)
NewEncryptingReader returns EncryptingReader that reads the plaintext and returns the ciphertext to caller. The key must be securely stored and must have at least algorithm.KeySize() bytes. The aad is the associated data and may be nil. The returned CiphertextHeader must be stored and later passed to the NewDecryptingReader or NewDecryptingReadSeeker. R is not closed automatically.
EncryptingReader is a pull style interface which is very useful e.g. in case when you need to encrypt the file when uploading it via http.Client. While you could use EncryptingWriter and two io.Pipes for this case, this is a cleaner solution.
func NewEncryptingWriter ¶
func NewEncryptingWriter(w io.Writer, key []byte, aad []byte, options EncryptOptions) (CiphertextHeader, *EncryptingWriter, error)
NewEncryptingWriter returns EncryptingWriter that consumes the plaintext and writes the ciphertext into w. The key must be securely stored and must have at least algorithm.KeySize() bytes. The aad is the associated data and may be nil. The returned CiphertextHeader must be stored and later passed to the NewDecryptingReader or NewDecryptingReadSeeker. W is not closed automatically.
Types ¶
type Algorithm ¶
type Algorithm int
Algorithm is the algorithm used for key derivation + encryption.
func (Algorithm) CiphertextLength ¶
CiphertextLength returns the length of the ciphertext which will be produced by encryption process.
NOTE: This function does not account for the length of CiphertextHeader which must be stored either alongside the ciphertext or in the separate metadata storage.
func (Algorithm) CiphertextRange ¶
func (a Algorithm) CiphertextRange(segmentSize int, plaintextRange Range, plaintextTotal int64) (Range, error)
CiphertextRange returns the range that must be read in order to decrypt the plaintextRange slice of the plaintext. plaintextTotal must be the total size of the encrypted plaintext.
type CiphertextHeader ¶
CiphertextHeader is the metadata returned from the NewEncryptingWriter and must be stored alongside the ciphertext and later passed to the NewDecryptingReader or NewDecryptingReadSeeker alongside the aad. You can serialize CiphertextHeader yourself or use MarshalTo and UnmarshalFrom methods.
func (*CiphertextHeader) MarshalTo ¶
func (c *CiphertextHeader) MarshalTo(w io.Writer) error
MarshalTo writes header to writer. You can use it with NewEncryptingWriter:
ew, header, err := oae.NewEncryptingWriter(w, key, aad, oae.EncryptOptions{}) header.MarshalTo(w) ew.Write(plaintext)
NewEncryptingWriterWithHeader calls this method for you.
func (*CiphertextHeader) UnmarshalFrom ¶
func (c *CiphertextHeader) UnmarshalFrom(r io.Reader) error
UnmarshalFrom fills header from reader. You can use it with NewDecryptingReader or NewDecryptingReadSeeker:
var header oae.CiphertextHeader header.UnmarshalFrom(r) er, err := oae.NewDecryptingReader(r, key, aad, header) er.Read(ciphertext)
type DecryptingReadSeeker ¶
type DecryptingReadSeeker struct { // Wrapped reader. R io.ReadSeeker // contains filtered or unexported fields }
DecryptingReadSeeker reads ciphertext and returns plaintext.
func NewDecryptingReadSeeker ¶
func NewDecryptingReadSeeker(r io.ReadSeeker, key []byte, aad []byte, header CiphertextHeader) (*DecryptingReadSeeker, error)
NewDecryptingReadSeeker returns DecryptingReadSeeker that reads the ciphertext and returns the plaintext. Key and aad must match the parameters to NewEncryptingWriter. Header must be the CiphertextHeader returned from NewEncryptingWriter. Wrapped reader must not have any trailing bytes after the ciphertext.
NewDecryptingReadSeeker guarantees to not call any methods of wrapped reader until the first Read() or Seek() is called on the result.
Calling Seek() with io.SeekEnd is not supported. Seeking to the end of plaintext or beyond will return an error. r.Seek() will only be called with io.SeekCurrent.
func NewDecryptingReadSeekerWithHeader ¶
func NewDecryptingReadSeekerWithHeader(r io.ReadSeeker, key []byte, aad []byte) (*DecryptingReadSeeker, error)
NewDecryptingReadSeekerWithHeader is a helper which reads the header from ciphertext and creates a new DecryptingReadSeeker. See NewDecryptingReadSeeker for details.
func (*DecryptingReadSeeker) Read ¶
func (d *DecryptingReadSeeker) Read(p []byte) (int, error)
Read reads the next portion of ciphertext. Errors from wrapped reader are returned as is.
func (*DecryptingReadSeeker) Seek ¶
func (d *DecryptingReadSeeker) Seek(offset int64, whence int) (int64, error)
Seek sets the offset in plaintext. Only SeekStart and SeekCurrent are supported, passing SeekEnd will result in error. Seeking past the end of plaintext will return an error.
Seek will call Seek of the wrapped writer with SeekCurrent at most once.
type DecryptingReader ¶
type DecryptingReader struct { // Wrapped reader. R io.Reader // contains filtered or unexported fields }
DecryptingReader is a variant of DecryptingReadSeeker which supports ciphertext readers without Seek().
func NewDecryptingReader ¶
func NewDecryptingReader(r io.Reader, key []byte, aad []byte, header CiphertextHeader) (*DecryptingReader, error)
NewDecryptingReader returns a wrapper around reader that reads the ciphertext and returns the plaintext to the caller. Key and aad must match the parameters to NewEncryptingWriter. Header must be the CiphertextHeader returned from NewEncryptingWriter. Wrapped reader must not have any trailing bytes after the ciphertext.
NewDecryptingReader guarantees to not call any methods of wrapped reader until the first Read() is called on the result.
func NewDecryptingReaderWithHeader ¶
NewDecryptingReaderWithHeader is a helper which reads the header from ciphertext and creates a new DecryptingReader. See NewDecryptingReader for details.
type EncryptOptions ¶
type EncryptOptions struct { // Zero value means the default algorithm is used. Algorithm Algorithm // SegmentSize limits the amount of data which can be encrypted. The default segment size of 4096 will limit // the plaintext size to ~15.9Tb. Zero value means the default segment size is used. SegmentSize int }
EncryptOptions are the options which are used by NewEncryptingWriter.
type EncryptingReader ¶
type EncryptingReader struct { // Wrapped reader. R io.Reader // contains filtered or unexported fields }
EncryptingReader reads plaintext and returns ciphertext.
func NewEncryptingReaderWithHeader ¶
func NewEncryptingReaderWithHeader(r io.Reader, key []byte, aad []byte, options EncryptOptions) (*EncryptingReader, error)
NewEncryptingReaderWithHeader is a helper which creates a new EncryptingReader which returns the header before the ciphertext. See NewEncryptingReader for details.
type EncryptingWriter ¶
type EncryptingWriter struct { // Wrapped writer. W io.Writer // contains filtered or unexported fields }
EncryptingWriter accepts plaintext and writes ciphertext into wrapped writer.
func NewEncryptingWriterWithHeader ¶
func NewEncryptingWriterWithHeader(w io.Writer, key []byte, aad []byte, options EncryptOptions) (*EncryptingWriter, error)
NewEncryptingWriterWithHeader is a helper which creates a new EncryptingWriter and then writes the header before the ciphertext. See NewEncryptingWriter for details.
func (*EncryptingWriter) Close ¶
func (e *EncryptingWriter) Close() error
Close writes the last ciphertext segment into the wrapped writer, but does not close it. Calling Close the second time is a no-op.