oae

package module
v0.0.0-...-36600e4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 21, 2022 License: MIT Imports: 12 Imported by: 0

README

go-oae

Go Reference Build status

go-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) in Go.

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.

Installation

Install using go get -u github.com/mrknow-all/go-oae.

Example

This is the basic example which encrypts file into to another file.

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 := NewEncryptingWriterWithHeader(ciphertext, topSecretKey, nil, 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 := NewDecryptingReaderWithHeader(plaintext, topSecretKey, nil)
    if err != nil {
        return err
    }
    _, err = io.Copy(plaintext, reader)
    if err != nil {
        return err
    }
    return nil
}

See documentation for more examples and API documentation.

License

This library is distributed under MIT license, see LICENSE.

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

View Source
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.

const (
	AesGcm128Sha256 Algorithm = iota + 1
	AesGcm256Sha256
	ChaCha20Poly1305Sha256
)

func (Algorithm) CiphertextLength

func (a Algorithm) CiphertextLength(segmentSize int, plaintextLength int64) (int64, error)

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.

func (Algorithm) KeySize

func (a Algorithm) KeySize() int

KeySize returns the minimum length of the key which must be passed to the NewEncryptingWriter.

func (Algorithm) PlaintextLength

func (a Algorithm) PlaintextLength(segmentSize int, ciphertextLength int64) (int64, error)

PlaintextLength returns the length of the plaintext which will be read during decryption.

type CiphertextHeader

type CiphertextHeader struct {
	Algorithm   Algorithm
	SegmentSize int
	Salt        []byte
	NoncePrefix []byte
}

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

func NewDecryptingReaderWithHeader(r io.Reader, key []byte, aad []byte) (*DecryptingReader, error)

NewDecryptingReaderWithHeader is a helper which reads the header from ciphertext and creates a new DecryptingReader. See NewDecryptingReader for details.

func (*DecryptingReader) Read

func (d *DecryptingReader) Read(p []byte) (n int, err error)

Read reads the next portion of ciphertext. Errors from wrapped reader are returned as is.

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.

func (*EncryptingReader) Read

func (e *EncryptingReader) Read(p []byte) (int, error)

Read reads plaintext from wrapped reader, encrypts it and returns ciphertext. Errors from wrapped reader are returned as is.

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.

func (*EncryptingWriter) Write

func (e *EncryptingWriter) Write(p []byte) (int, error)

Write encrypts plaintext p and writes full ciphertext segments into the wrapped writer. Errors from wrapped writer are returned as is.

type Range

type Range struct {
	Begin int64
	End   int64
}

Range is a half-open range of indices (the end is non-inclusive, i.e. [Begin, End))

func (Range) Validate

func (r Range) Validate() error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL