github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/chat/signencrypt/codec.go (about)

     1  // This is a construction for encrypting and signing a message, using a
     2  // symmetric encryption key and a signing keypair, in a way that supports safe
     3  // streaming decryption. We need this for chat attachments because we've chosen
     4  // to use signing keys for authenticity in chat, and we don't want one
     5  // participant to be able to modify another's attachment, even with an evil
     6  // server's help. It's *almost* enough that we record the hash of the
     7  // attachment along with the symmetric key used to encrypt it, but that by
     8  // itself doesn't allow safe streaming decryption. Instead, we use this
     9  // construction to sign each chunk of the attachment as we encrypt it. (Note
    10  // that it's still possible for a sender with the server's help to modify their
    11  // *own* attachments after the fact, if clients aren't checking the hash. This
    12  // isn't perfect, but it's better than any participant being able to do it.)
    13  //
    14  // This file has 100% test coverage. Please keep it that way :-)
    15  //
    16  // Seal inputs:
    17  // - plaintext bytes (streaming is fine)
    18  // - a crypto_secretbox symmetric key
    19  // - a crypto_sign private key
    20  // - a globally unique (with respect to these keys) 16-byte nonce
    21  //
    22  // Seal steps:
    23  // 1) Chunk the message into chunks exactly one megabyte long (2^20 bytes), with
    24  //    exactly one short chunk at the end, which might be zero bytes.
    25  // 2) Compute the SHA512 hash of each plaintext chunk.
    26  // 3) Concatenate the 16-byte nonce above with the 8-byte unsigned big-endian
    27  //    chunk number, where the first chunk is zero. This is the 24-byte chunk
    28  //    nonce.
    29  // 4) Concatenate five things:
    30  //    - a signature prefix string which must contain no null bytes.
    31  //      for example "Keybase-Chat-Attachment-1"
    32  //    - a null byte terminator for the prefix string
    33  //    - the encryption key (why?! read below)
    34  //    - the chunk nonce from #3
    35  //    - the hash from #2.
    36  // 5) Sign the concatenation from #4, giving a detached 64-byte crypto_sign
    37  //    signature.
    38  // 6) Concatenate the signature from #5 + the plaintext chunk.
    39  // 7) Encrypt the concatenation from #6 with the crypto_secretbox key and the
    40  //    chunk nonce from #3.
    41  // 8) Concatenate all the ciphertexts from #7 into the output.
    42  //
    43  // Open inputs:
    44  // - ciphertext bytes (streaming is fine)
    45  // - the same crypto_secretbox symmetric key
    46  // - the corresponding crypto_sign public key
    47  // - the same nonce
    48  //
    49  // Open steps:
    50  // 1) Chop the input stream into chunks of exactly (2^20 + 80) bytes, with
    51  //    exactly one short chunk at the end. If this short chunk is less than 80
    52  //    bytes (the size of an Ed25519 signature and a Poly1305 authenticator put
    53  //    together), return a truncation error.
    54  // 2) Decrypt each input chunk with the crypto_secretbox key and chunk nonce as
    55  //    in seal step #7.
    56  // 3) Split each decrypted chunk into a 64-byte signature and the following
    57  //    plaintext.
    58  // 4) Hash that plaintext and make the concatenation from seal step #4.
    59  // 5) Verify the signature against that concatenation.
    60  // 6) Emit each verified plaintext chunk as output.
    61  //
    62  // Design Notes:
    63  //
    64  // Combining signing and encryption is surprisingly tricky! See
    65  // http://world.std.com/~dtd/sign_encrypt/sign_encrypt7.html for lots of
    66  // details about the issues that come up. (Note that "encryption" in that
    67  // paper refers mostly to RSA encryption like PGP's, which doesn't involve
    68  // a sender key the way Diffie-Hellman / NaCl's crypto_box does. This makes
    69  // me appreciate just how many problems the crypto_box construction is
    70  // solving.)
    71  //
    72  // Many of these issues probably don't apply to chat attachments (yet?!),
    73  // because recipients will know what keys to use ahead of time. But there
    74  // are other places where we use signing+encryption that have different
    75  // properties, and I want to be able to use this design as a reference. The
    76  // short version of the problem is that both encrypt-then-sign and
    77  // sign-then-encrypt have to worry about what happens when someone reuses
    78  // the inner layer with a new outer layer.
    79  //
    80  // Encrypt-then-sign has a "sender impersonation" problem. The
    81  // man-in-the-middle can re-sign an encrypted payload with their own key
    82  // and claim authorship of the message. If the message itself contains
    83  // secrets, like in an auth protocol for example, the MITM can fake knowing
    84  // those secrets. (Also, encrypt-then-sign has the more obvious downside
    85  // that encryption is hiding only the contents of a signature and not its
    86  // author.)
    87  //
    88  // Sign-then-encrypt has a "surreptitious forwarding" problem. A recipient
    89  // can re-encrypt the signed payload to another unintended recipient.
    90  // Recipients must not rely on the encryption layer to mean that the sender
    91  // intended the message for them. In fact PGP is vulnerable to this attack,
    92  // unless the user/application understands the very subtle difference
    93  // between "I can read this" and "this was written to me".
    94  //
    95  // So, simply using encryption and signing together isn't good enough! The
    96  // paper linked above mentions a few different solutions, but in general
    97  // the fix is that the inner layer needs to assert something about the
    98  // outer layer, so that the outer layer can't be changed without the inner
    99  // key. We could simply include the outer key verbatim inside the inner
   100  // layer, but a better approach is to mix the outer key into the inner
   101  // crypto, so that it's impossible to forget to check it.
   102  //
   103  // We prefer sign-then-encrypt, because hiding the author of the signature
   104  // is a feature. That means the inner signing layer needs to assert the
   105  // encryption key. We do this by including the encryption key as
   106  // "associated data" that gets signed along with the plaintext. Since we
   107  // already need to do that with a nonce and a chunk number, including the
   108  // the encryption key is easy. We don't need to worry about whether the
   109  // signature might leak the encryption key either, because the signature
   110  // gets encrypted.
   111  //
   112  // Apart from these signing gymnastics, all the large-encrypted-message
   113  // considerations from https://www.imperialviolet.org/2015/05/16/aeads.html
   114  // apply here. Namely we use a chunk number to prevent reordering, and we
   115  // require a short chunk at the end to detect truncation. A globally unique
   116  // nonce (for encryption *and* signing) prevents chunk swapping in between
   117  // messages, and is required for encryption in any case. (It's expected
   118  // that the chat client will pass in all zeroes for the nonce, because both
   119  // keys are one-time-use. That's up to the client. G-d help us if we ever
   120  // reuse those keys.) We also follow the "prefix signatures with an ASCII
   121  // context string and a null byte" recommendation from
   122  // https://www.ietf.org/mail-archive/web/tls/current/msg14734.html.
   123  
   124  package signencrypt
   125  
   126  import (
   127  	"bytes"
   128  	"crypto/sha512"
   129  	"encoding/binary"
   130  	"fmt"
   131  	"io"
   132  
   133  	"github.com/keybase/client/go/kbcrypto"
   134  	"github.com/keybase/client/go/msgpack"
   135  	"github.com/keybase/go-crypto/ed25519"
   136  	"golang.org/x/crypto/nacl/secretbox"
   137  )
   138  
   139  type Nonce *[NonceSize]byte
   140  type SecretboxKey *[SecretboxKeySize]byte
   141  type SecretboxNonce *[SecretboxNonceSize]byte
   142  type SignKey *[ed25519.PrivateKeySize]byte
   143  type VerifyKey *[ed25519.PublicKeySize]byte
   144  
   145  const NonceSize = 16
   146  const SecretboxKeySize = 32
   147  const SecretboxNonceSize = 24
   148  const DefaultPlaintextChunkLength int64 = 1 << 20
   149  
   150  // ===================================
   151  // single packet encoding and decoding
   152  // ===================================
   153  
   154  func makeChunkNonce(nonce Nonce, chunkNum uint64) SecretboxNonce {
   155  	var ret [SecretboxNonceSize]byte
   156  	copy(ret[0:16], nonce[:])
   157  	var chunkNumBytes [8]byte
   158  	binary.BigEndian.PutUint64(chunkNumBytes[:], chunkNum)
   159  	copy(ret[16:24], chunkNumBytes[:])
   160  	return &ret
   161  }
   162  
   163  func makeSignatureInput(plaintext []byte, encKey SecretboxKey, signaturePrefix kbcrypto.SignaturePrefix, chunkNonce SecretboxNonce) []byte {
   164  	// Check that the prefix does not include any null bytes.
   165  	if bytes.IndexByte([]byte(signaturePrefix), 0x00) != -1 {
   166  		panic(fmt.Sprintf("signature prefix contains null byte: %q", signaturePrefix))
   167  	}
   168  
   169  	chunkHash := sha512.Sum512(plaintext)
   170  	var ret []byte
   171  	ret = append(ret, signaturePrefix...)
   172  	// We follow the "prefix signatures with an ASCII context string and a null byte" recommendation from
   173  	// https://www.ietf.org/mail-archive/web/tls/current/msg14734.html.
   174  	ret = append(ret, 0x00)
   175  	ret = append(ret, encKey[:]...)
   176  	ret = append(ret, chunkNonce[:]...)
   177  	ret = append(ret, chunkHash[:]...)
   178  	return ret
   179  }
   180  
   181  func getPacketLen(plaintextChunkLen int64) int64 {
   182  	return plaintextChunkLen + secretbox.Overhead + ed25519.SignatureSize
   183  }
   184  
   185  func getPlaintextPacketLen(cipherChunkLen int64) int64 {
   186  	return cipherChunkLen - (secretbox.Overhead + ed25519.SignatureSize)
   187  }
   188  
   189  func sealPacket(plaintext []byte, encKey SecretboxKey, signKey SignKey, signaturePrefix kbcrypto.SignaturePrefix, nonce SecretboxNonce) []byte {
   190  	signatureInput := makeSignatureInput(plaintext, encKey, signaturePrefix, nonce)
   191  	signature := ed25519.Sign(signKey[:], signatureInput)
   192  	signedChunk := signature
   193  	signedChunk = append(signedChunk, plaintext...)
   194  	packet := secretbox.Seal(nil, signedChunk, nonce, encKey)
   195  	return packet
   196  }
   197  
   198  func openPacket(packet []byte, encKey SecretboxKey, verifyKey VerifyKey, signaturePrefix kbcrypto.SignaturePrefix, nonce SecretboxNonce) ([]byte, error) {
   199  	signedChunk, secretboxValid := secretbox.Open(nil, packet, nonce, encKey)
   200  	if !secretboxValid {
   201  		return nil, NewError(BadSecretbox, "secretbox failed to open")
   202  	}
   203  	// Avoid panicking on signatures that are too short.
   204  	if len(signedChunk) < ed25519.SignatureSize {
   205  		return nil, NewError(ShortSignature, "signature too short")
   206  	}
   207  	signature := signedChunk[0:ed25519.SignatureSize]
   208  	plaintext := signedChunk[ed25519.SignatureSize:]
   209  	signatureInput := makeSignatureInput(plaintext, encKey, signaturePrefix, nonce)
   210  	signatureValid := ed25519.Verify(verifyKey[:], signatureInput, signature)
   211  	if !signatureValid {
   212  		return nil, NewError(BadSignature, "signature failed to verify")
   213  	}
   214  	return plaintext, nil
   215  }
   216  
   217  // ===================
   218  // incremental encoder
   219  // ===================
   220  
   221  type Encoder struct {
   222  	encKey            SecretboxKey
   223  	signKey           SignKey
   224  	signaturePrefix   kbcrypto.SignaturePrefix
   225  	nonce             Nonce
   226  	buf               []byte
   227  	chunkNum          uint64
   228  	plaintextChunkLen int64
   229  }
   230  
   231  func NewEncoder(encKey SecretboxKey, signKey SignKey, signaturePrefix kbcrypto.SignaturePrefix, nonce Nonce) *Encoder {
   232  	return &Encoder{
   233  		encKey:            encKey,
   234  		signKey:           signKey,
   235  		signaturePrefix:   signaturePrefix,
   236  		nonce:             nonce,
   237  		buf:               nil,
   238  		chunkNum:          0,
   239  		plaintextChunkLen: DefaultPlaintextChunkLength,
   240  	}
   241  }
   242  
   243  func (e *Encoder) sealOnePacket(plaintextChunkLen int64) []byte {
   244  	// Note that this function handles the `plaintextChunkLen == 0` case.
   245  	if plaintextChunkLen > int64(len(e.buf)) {
   246  		panic("encoder tried to seal a packet that was too big")
   247  	}
   248  	plaintextChunk := e.buf[0:plaintextChunkLen]
   249  	chunkNonce := makeChunkNonce(e.nonce, e.chunkNum)
   250  	packet := sealPacket(plaintextChunk, e.encKey, e.signKey, e.signaturePrefix, chunkNonce)
   251  	e.buf = e.buf[plaintextChunkLen:]
   252  	e.chunkNum++
   253  	return packet
   254  }
   255  
   256  // Write plaintext bytes into the encoder. If any output bytes are ready,
   257  // return them. Callers must call Finish() when they're done, so that any
   258  // remaining input bytes can be written out as a short (or empty) chunk.
   259  // Otherwise you will both lose data and cause truncation errors on
   260  // decoding.
   261  func (e *Encoder) Write(plaintext []byte) []byte {
   262  	e.buf = append(e.buf, plaintext...)
   263  	var output []byte
   264  	// If buf is big enough to make new packets, make as many as we can.
   265  	for int64(len(e.buf)) >= e.plaintextChunkLen {
   266  		packet := e.sealOnePacket(e.plaintextChunkLen)
   267  		output = append(output, packet...)
   268  	}
   269  	return output
   270  }
   271  
   272  // Finish writes out any remaining buffered input bytes (possibly zero bytes)
   273  // as a short chunk. This should only be called once, and after that you can't
   274  // use this encoder again.
   275  func (e *Encoder) Finish() []byte {
   276  	if int64(len(e.buf)) >= e.plaintextChunkLen {
   277  		panic("encoder buffer has more bytes than expected")
   278  	}
   279  	packet := e.sealOnePacket(int64(len(e.buf)))
   280  	return packet
   281  }
   282  
   283  func (e *Encoder) ChangePlaintextChunkLenForTesting(plaintextChunkLen int64) {
   284  	e.plaintextChunkLen = plaintextChunkLen
   285  }
   286  
   287  // ===================
   288  // incremental decoder
   289  // ===================
   290  
   291  type Decoder struct {
   292  	encKey          SecretboxKey
   293  	verifyKey       VerifyKey
   294  	signaturePrefix kbcrypto.SignaturePrefix
   295  	nonce           Nonce
   296  	buf             []byte
   297  	chunkNum        uint64
   298  	err             error
   299  	packetLen       int64
   300  }
   301  
   302  func NewDecoder(encKey SecretboxKey, verifyKey VerifyKey, signaturePrefix kbcrypto.SignaturePrefix, nonce Nonce) *Decoder {
   303  	return &Decoder{
   304  		encKey:          encKey,
   305  		verifyKey:       verifyKey,
   306  		signaturePrefix: signaturePrefix,
   307  		nonce:           nonce,
   308  		buf:             nil,
   309  		chunkNum:        0,
   310  		err:             nil,
   311  		packetLen:       getPacketLen(DefaultPlaintextChunkLength),
   312  	}
   313  }
   314  
   315  func (d *Decoder) setChunkNum(num uint64) {
   316  	d.chunkNum = num
   317  }
   318  
   319  func (d *Decoder) openOnePacket(packetLen int64) ([]byte, error) {
   320  	if packetLen > int64(len(d.buf)) {
   321  		panic("decoder tried to open a packet that was too big")
   322  	}
   323  	packet := d.buf[0:packetLen]
   324  	chunkNonce := makeChunkNonce(d.nonce, d.chunkNum)
   325  	plaintext, err := openPacket(packet, d.encKey, d.verifyKey, d.signaturePrefix, chunkNonce)
   326  	if err != nil {
   327  		return nil, err
   328  	}
   329  	d.buf = d.buf[packetLen:]
   330  	d.chunkNum++
   331  	return plaintext, nil
   332  }
   333  
   334  // Write ciphertext bytes into the decoder. If any packets are ready to
   335  // open, open them and either return their plaintext bytes as output or any
   336  // error that comes up. Callers must call Finish() when they're done, to
   337  // decode the final short packet and check for truncation. If Write ever
   338  // returns an error, subsequent calls to Write will always return the same
   339  // error.
   340  func (d *Decoder) Write(ciphertext []byte) ([]byte, error) {
   341  	// If we've ever seen an error, just return that again.
   342  	if d.err != nil {
   343  		return nil, d.err
   344  	}
   345  	d.buf = append(d.buf, ciphertext...)
   346  	// If buf is big enough to open new packets, open as many as we can.
   347  	// We assume that every packet other than the last (which we handle in
   348  	// Finish) is the same length, which makes this loop very simple.
   349  	var output []byte
   350  	for int64(len(d.buf)) >= d.packetLen {
   351  		var plaintext []byte
   352  		plaintext, d.err = d.openOnePacket(d.packetLen)
   353  		if d.err != nil {
   354  			return nil, d.err
   355  		}
   356  		output = append(output, plaintext...)
   357  	}
   358  	return output, nil
   359  }
   360  
   361  // Finish decodes any remaining bytes as a short (or empty) packet. This
   362  // produces the final bytes of the plaintext, and implicitly checks for
   363  // truncation. This should only be called once, and after that you can't use
   364  // this decoder again.
   365  func (d *Decoder) Finish() ([]byte, error) {
   366  	// If we've ever seen an error, just return that again.
   367  	if d.err != nil {
   368  		return nil, d.err
   369  	}
   370  	if int64(len(d.buf)) >= d.packetLen {
   371  		panic("decoder buffer has more bytes than expected")
   372  	}
   373  	// If we've been truncated at a packet boundary, this open will fail on a
   374  	// simple length check. If we've been truncated in the middle of a packet,
   375  	// this open will fail to validate.
   376  	var plaintext []byte
   377  	plaintext, d.err = d.openOnePacket(int64(len(d.buf)))
   378  	return plaintext, d.err
   379  }
   380  
   381  func (d *Decoder) ChangePlaintextChunkLenForTesting(plaintextChunkLen int64) {
   382  	d.packetLen = getPacketLen(plaintextChunkLen)
   383  }
   384  
   385  // ===============================================
   386  // Reader-based wrappers for encoding and decoding
   387  // ===============================================
   388  
   389  type codec interface {
   390  	Write([]byte) ([]byte, error)
   391  	Finish() ([]byte, error)
   392  }
   393  
   394  // The incremental encoder never returns errors, so its type signatures are
   395  // different than the decoder's. This struct trivially wraps them to fit the
   396  // codec signature.
   397  type encoderCodecShim struct {
   398  	*Encoder
   399  }
   400  
   401  func (s *encoderCodecShim) Write(b []byte) ([]byte, error) {
   402  	return s.Encoder.Write(b), nil
   403  }
   404  
   405  func (s *encoderCodecShim) Finish() ([]byte, error) {
   406  	return s.Encoder.Finish(), nil
   407  }
   408  
   409  var _ codec = (*Decoder)(nil)
   410  var _ codec = (*encoderCodecShim)(nil)
   411  
   412  type codecReadWrapper struct {
   413  	codec       codec
   414  	innerReader io.Reader
   415  	outputBuf   []byte
   416  	codecErr    error
   417  	innerEOF    bool
   418  }
   419  
   420  var _ io.Reader = (*codecReadWrapper)(nil)
   421  
   422  func (r *codecReadWrapper) Read(callerBuf []byte) (int, error) {
   423  	// Crypto errors are unrecoverable. If we've ever seen one, just keep
   424  	// returning it.
   425  	if r.codecErr != nil {
   426  		return 0, r.codecErr
   427  	}
   428  	// While we need more data, keep reading from the inner reader.
   429  	for !r.innerEOF && len(r.outputBuf) == 0 {
   430  		var readBuf [4096]byte
   431  		n, ioErr := r.innerReader.Read(readBuf[:])
   432  		// Always handle the bytes we read, regardless of errors, in accordance
   433  		// with https://golang.org/pkg/io/#Reader.
   434  		if n > 0 {
   435  			var newOutput []byte
   436  			newOutput, r.codecErr = r.codec.Write(readBuf[0:n])
   437  			// For codec errors, short circuit and never read again.
   438  			if r.codecErr != nil {
   439  				return 0, r.codecErr
   440  			}
   441  			r.outputBuf = append(r.outputBuf, newOutput...)
   442  		}
   443  		// Now handle EOF or other errors.
   444  		if ioErr == io.EOF {
   445  			// When we see EOF we finish the internal codec. We won't run this
   446  			// loop anymore, but we might still need to return bytes from our
   447  			// own buffer for many subsequent reads. Also nil out the codec and
   448  			// the inner reader, since we shouldn't touch them again.
   449  			r.innerEOF = true
   450  			var finalOutput []byte
   451  			finalOutput, r.codecErr = r.codec.Finish()
   452  			// For codec errors, short circuit and never read again.
   453  			if r.codecErr != nil {
   454  				return 0, r.codecErr
   455  			}
   456  			r.outputBuf = append(r.outputBuf, finalOutput...)
   457  			r.codec = nil
   458  			r.innerReader = nil
   459  		} else if ioErr != nil {
   460  			// If we have a real IO error, short circuit and return it. This
   461  			// reader remains valid, though, and the caller is allowed to
   462  			// retry.
   463  			return 0, ioErr
   464  		}
   465  	}
   466  	// Now, if we have any buffered data, return as much of that as we can.
   467  	if len(r.outputBuf) > 0 {
   468  		copied := copy(callerBuf, r.outputBuf)
   469  		r.outputBuf = r.outputBuf[copied:]
   470  		return copied, nil
   471  	}
   472  	// Otherwise return EOF.
   473  	return 0, io.EOF
   474  }
   475  
   476  // NewEncodingReader creates a new streaming encoder.
   477  // The signaturePrefix argument must not contain the null container.
   478  func NewEncodingReader(encKey SecretboxKey, signKey SignKey, signaturePrefix kbcrypto.SignaturePrefix, nonce Nonce, innerReader io.Reader) io.Reader {
   479  	return &codecReadWrapper{
   480  		innerReader: innerReader,
   481  		codec:       &encoderCodecShim{NewEncoder(encKey, signKey, signaturePrefix, nonce)},
   482  	}
   483  }
   484  
   485  func NewDecodingReader(encKey SecretboxKey, verifyKey VerifyKey, signaturePrefix kbcrypto.SignaturePrefix, nonce Nonce, innerReader io.Reader) io.Reader {
   486  	return &codecReadWrapper{
   487  		innerReader: innerReader,
   488  		codec:       NewDecoder(encKey, verifyKey, signaturePrefix, nonce),
   489  	}
   490  }
   491  
   492  // =============================
   493  // chunk helpers
   494  // =============================
   495  
   496  type chunkSpec struct {
   497  	index                  int64
   498  	ptStart, ptEnd         int64
   499  	cipherStart, cipherEnd int64
   500  }
   501  
   502  func chunkFromIndex(index int64) (res chunkSpec) {
   503  	res.index = index
   504  	res.ptStart = res.index * DefaultPlaintextChunkLength
   505  	res.ptEnd = res.ptStart + DefaultPlaintextChunkLength
   506  	res.cipherStart = res.index * getPacketLen(DefaultPlaintextChunkLength)
   507  	res.cipherEnd = res.cipherStart + getPacketLen(DefaultPlaintextChunkLength)
   508  	return res
   509  }
   510  
   511  func getChunksInRange(plaintextBegin, plaintextEnd, plaintextLen int64) (res []chunkSpec) {
   512  	beginChunk := chunkFromIndex(plaintextBegin / DefaultPlaintextChunkLength)
   513  	endChunk := chunkFromIndex(plaintextEnd / DefaultPlaintextChunkLength)
   514  	cipherLen := GetSealedSize(plaintextLen)
   515  	for i := beginChunk.index; i <= endChunk.index; i++ {
   516  		res = append(res, chunkFromIndex(i))
   517  	}
   518  	if res[len(res)-1].ptEnd >= plaintextLen {
   519  		res[len(res)-1].ptEnd = plaintextLen
   520  	}
   521  	if res[len(res)-1].cipherEnd >= cipherLen {
   522  		res[len(res)-1].cipherEnd = cipherLen
   523  	}
   524  	return res
   525  }
   526  
   527  // =============================
   528  // all-at-once wrapper functions
   529  // =============================
   530  
   531  func GetSealedSize(plaintextLen int64) int64 {
   532  	// All the full packets.
   533  	fullChunks := plaintextLen / DefaultPlaintextChunkLength
   534  	totalLen := fullChunks * getPacketLen(DefaultPlaintextChunkLength)
   535  	// Exactly one short packet, even if it's empty.
   536  	remainingPlaintext := plaintextLen % DefaultPlaintextChunkLength
   537  	totalLen += getPacketLen(remainingPlaintext)
   538  	return totalLen
   539  }
   540  
   541  func GetPlaintextSize(cipherLen int64) int64 {
   542  	fullChunks := cipherLen / getPacketLen(DefaultPlaintextChunkLength)
   543  	totalLen := fullChunks * DefaultPlaintextChunkLength
   544  	remainingCiphertext := cipherLen % getPacketLen(DefaultPlaintextChunkLength)
   545  	totalLen += getPlaintextPacketLen(remainingCiphertext)
   546  	return totalLen
   547  }
   548  
   549  // SealWhole seals all at once using the streaming encoding.
   550  func SealWhole(plaintext []byte, encKey SecretboxKey, signKey SignKey, signaturePrefix kbcrypto.SignaturePrefix, nonce Nonce) []byte {
   551  	encoder := NewEncoder(encKey, signKey, signaturePrefix, nonce)
   552  	output := encoder.Write(plaintext)
   553  	output = append(output, encoder.Finish()...)
   554  	return output
   555  }
   556  
   557  func OpenWhole(sealed []byte, encKey SecretboxKey, verifyKey VerifyKey, signaturePrefix kbcrypto.SignaturePrefix, nonce Nonce) ([]byte, error) {
   558  	decoder := NewDecoder(encKey, verifyKey, signaturePrefix, nonce)
   559  	output, err := decoder.Write(sealed)
   560  	if err != nil {
   561  		return nil, err
   562  	}
   563  	moreOutput, err := decoder.Finish()
   564  	if err != nil {
   565  		return nil, err
   566  	}
   567  	return append(output, moreOutput...), nil
   568  }
   569  
   570  // include verification of associated data. see
   571  // https://en.wikipedia.org/wiki/Authenticated_encryption#Authenticated_encryption_with_associated_data_(AEAD)
   572  
   573  type AEADMessage struct {
   574  	Version       int               `codec:"v" json:"v"`
   575  	AssocDataHash [sha512.Size]byte `codec:"a" json:"a"`
   576  	Message       []byte            `codec:"m" json:"m"`
   577  }
   578  
   579  // SealWithAssociatedData is a wrapper around SealWhole which adds an associatedData object
   580  // (see AEAD ciphers) which must be message-packable into bytes. This exact object is required
   581  // to call OpenWithAssociatedData on the ciphertext.
   582  func SealWithAssociatedData(msg []byte, associatedData interface{}, encKey SecretboxKey, signKey SignKey, signaturePrefix kbcrypto.SignaturePrefix, nonce Nonce) (ret []byte, err error) {
   583  	adEncoded, err := msgpack.Encode(associatedData)
   584  	if err != nil {
   585  		return ret, err
   586  	}
   587  	adHash := sha512.Sum512(adEncoded)
   588  	aeadMsg := AEADMessage{
   589  		Version:       1,
   590  		AssocDataHash: adHash,
   591  		Message:       msg,
   592  	}
   593  	clearBytes, err := msgpack.Encode(aeadMsg)
   594  	if err != nil {
   595  		return ret, err
   596  	}
   597  	return SealWhole(clearBytes, encKey, signKey, signaturePrefix, nonce), nil
   598  }
   599  
   600  func OpenWithAssociatedData(sealed []byte, associatedData interface{}, encKey SecretboxKey, verifyKey VerifyKey, signaturePrefix kbcrypto.SignaturePrefix, nonce Nonce) (ret []byte, err error) {
   601  	clearBytes, err := OpenWhole(sealed, encKey, verifyKey, signaturePrefix, nonce)
   602  	if err != nil {
   603  		return ret, err
   604  	}
   605  	var aeadMessage AEADMessage
   606  	err = msgpack.Decode(&aeadMessage, clearBytes)
   607  	if err != nil {
   608  		return ret, err
   609  	}
   610  	if aeadMessage.Version != 1 {
   611  		return ret, NewError(BadSecretbox, "can only accept AEAD messages with version 1, but got %d", aeadMessage.Version)
   612  	}
   613  	actualADHash := aeadMessage.AssocDataHash
   614  	msg := aeadMessage.Message
   615  
   616  	adEncoded, err := msgpack.Encode(associatedData)
   617  	if err != nil {
   618  		return ret, err
   619  	}
   620  	adHash := sha512.Sum512(adEncoded)
   621  	if !bytes.Equal(adHash[:], actualADHash[:]) {
   622  		return ret, NewError(AssociatedDataMismatch, "fingerprint of associated data did not match")
   623  	}
   624  	return msg, nil
   625  }
   626  
   627  // ======
   628  // errors
   629  // ======
   630  
   631  type ErrorType int
   632  
   633  const (
   634  	BadSecretbox ErrorType = iota
   635  	ShortSignature
   636  	BadSignature
   637  	AssociatedDataMismatch
   638  )
   639  
   640  type Error struct {
   641  	Type    ErrorType
   642  	Message string
   643  }
   644  
   645  func NewError(errorType ErrorType, message string, args ...interface{}) error {
   646  	return Error{
   647  		Type:    errorType,
   648  		Message: fmt.Sprintf(message, args...),
   649  	}
   650  }
   651  
   652  func (e Error) Error() string {
   653  	return e.Message
   654  }