123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- // Copyright 2012 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- /*
- Package box authenticates and encrypts small messages using public-key cryptography.
- Box uses Curve25519, XSalsa20 and Poly1305 to encrypt and authenticate
- messages. The length of messages is not hidden.
- It is the caller's responsibility to ensure the uniqueness of nonces—for
- example, by using nonce 1 for the first message, nonce 2 for the second
- message, etc. Nonces are long enough that randomly generated nonces have
- negligible risk of collision.
- Messages should be small because:
- 1. The whole message needs to be held in memory to be processed.
- 2. Using large messages pressures implementations on small machines to decrypt
- and process plaintext before authenticating it. This is very dangerous, and
- this API does not allow it, but a protocol that uses excessive message sizes
- might present some implementations with no other choice.
- 3. Fixed overheads will be sufficiently amortised by messages as small as 8KB.
- 4. Performance may be improved by working with messages that fit into data caches.
- Thus large amounts of data should be chunked so that each message is small.
- (Each message still needs a unique nonce.) If in doubt, 16KB is a reasonable
- chunk size.
- This package is interoperable with NaCl: https://nacl.cr.yp.to/box.html.
- Anonymous sealing/opening is an extension of NaCl defined by and interoperable
- with libsodium:
- https://libsodium.gitbook.io/doc/public-key_cryptography/sealed_boxes.
- */
- package box // import "golang.org/x/crypto/nacl/box"
- import (
- cryptorand "crypto/rand"
- "io"
- "golang.org/x/crypto/blake2b"
- "golang.org/x/crypto/curve25519"
- "golang.org/x/crypto/nacl/secretbox"
- "golang.org/x/crypto/salsa20/salsa"
- )
- const (
- // Overhead is the number of bytes of overhead when boxing a message.
- Overhead = secretbox.Overhead
- // AnonymousOverhead is the number of bytes of overhead when using anonymous
- // sealed boxes.
- AnonymousOverhead = Overhead + 32
- )
- // GenerateKey generates a new public/private key pair suitable for use with
- // Seal and Open.
- func GenerateKey(rand io.Reader) (publicKey, privateKey *[32]byte, err error) {
- publicKey = new([32]byte)
- privateKey = new([32]byte)
- _, err = io.ReadFull(rand, privateKey[:])
- if err != nil {
- publicKey = nil
- privateKey = nil
- return
- }
- curve25519.ScalarBaseMult(publicKey, privateKey)
- return
- }
- var zeros [16]byte
- // Precompute calculates the shared key between peersPublicKey and privateKey
- // and writes it to sharedKey. The shared key can be used with
- // OpenAfterPrecomputation and SealAfterPrecomputation to speed up processing
- // when using the same pair of keys repeatedly.
- func Precompute(sharedKey, peersPublicKey, privateKey *[32]byte) {
- curve25519.ScalarMult(sharedKey, privateKey, peersPublicKey)
- salsa.HSalsa20(sharedKey, &zeros, sharedKey, &salsa.Sigma)
- }
- // Seal appends an encrypted and authenticated copy of message to out, which
- // will be Overhead bytes longer than the original and must not overlap it. The
- // nonce must be unique for each distinct message for a given pair of keys.
- func Seal(out, message []byte, nonce *[24]byte, peersPublicKey, privateKey *[32]byte) []byte {
- var sharedKey [32]byte
- Precompute(&sharedKey, peersPublicKey, privateKey)
- return secretbox.Seal(out, message, nonce, &sharedKey)
- }
- // SealAfterPrecomputation performs the same actions as Seal, but takes a
- // shared key as generated by Precompute.
- func SealAfterPrecomputation(out, message []byte, nonce *[24]byte, sharedKey *[32]byte) []byte {
- return secretbox.Seal(out, message, nonce, sharedKey)
- }
- // Open authenticates and decrypts a box produced by Seal and appends the
- // message to out, which must not overlap box. The output will be Overhead
- // bytes smaller than box.
- func Open(out, box []byte, nonce *[24]byte, peersPublicKey, privateKey *[32]byte) ([]byte, bool) {
- var sharedKey [32]byte
- Precompute(&sharedKey, peersPublicKey, privateKey)
- return secretbox.Open(out, box, nonce, &sharedKey)
- }
- // OpenAfterPrecomputation performs the same actions as Open, but takes a
- // shared key as generated by Precompute.
- func OpenAfterPrecomputation(out, box []byte, nonce *[24]byte, sharedKey *[32]byte) ([]byte, bool) {
- return secretbox.Open(out, box, nonce, sharedKey)
- }
- // SealAnonymous appends an encrypted and authenticated copy of message to out,
- // which will be AnonymousOverhead bytes longer than the original and must not
- // overlap it. This differs from Seal in that the sender is not required to
- // provide a private key.
- func SealAnonymous(out, message []byte, recipient *[32]byte, rand io.Reader) ([]byte, error) {
- if rand == nil {
- rand = cryptorand.Reader
- }
- ephemeralPub, ephemeralPriv, err := GenerateKey(rand)
- if err != nil {
- return nil, err
- }
- var nonce [24]byte
- if err := sealNonce(ephemeralPub, recipient, &nonce); err != nil {
- return nil, err
- }
- if total := len(out) + AnonymousOverhead + len(message); cap(out) < total {
- original := out
- out = make([]byte, 0, total)
- out = append(out, original...)
- }
- out = append(out, ephemeralPub[:]...)
- return Seal(out, message, &nonce, recipient, ephemeralPriv), nil
- }
- // OpenAnonymous authenticates and decrypts a box produced by SealAnonymous and
- // appends the message to out, which must not overlap box. The output will be
- // AnonymousOverhead bytes smaller than box.
- func OpenAnonymous(out, box []byte, publicKey, privateKey *[32]byte) (message []byte, ok bool) {
- if len(box) < AnonymousOverhead {
- return nil, false
- }
- var ephemeralPub [32]byte
- copy(ephemeralPub[:], box[:32])
- var nonce [24]byte
- if err := sealNonce(&ephemeralPub, publicKey, &nonce); err != nil {
- return nil, false
- }
- return Open(out, box[32:], &nonce, &ephemeralPub, privateKey)
- }
- // sealNonce generates a 24 byte nonce that is a blake2b digest of the
- // ephemeral public key and the receiver's public key.
- func sealNonce(ephemeralPub, peersPublicKey *[32]byte, nonce *[24]byte) error {
- h, err := blake2b.New(24, nil)
- if err != nil {
- return err
- }
- if _, err = h.Write(ephemeralPub[:]); err != nil {
- return err
- }
- if _, err = h.Write(peersPublicKey[:]); err != nil {
- return err
- }
- h.Sum(nonce[:0])
- return nil
- }
|