mirror of
https://github.com/DNSCrypt/dnscrypt-proxy.git
synced 2025-01-01 00:57:32 +01:00
183 lines
6.1 KiB
Go
183 lines
6.1 KiB
Go
// 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
|
|
}
|