mirror of
https://github.com/SamTherapy/dnscrypt.git
synced 2024-11-20 04:43:47 +00:00
5ddb58f703
Graceful shutdown of the DNSCrypt server This PR implements Server.Shutdown(ctx context.Context) method that allows to shut down the DNSCrypt server gracefully. Some additional changes that were inadvertently made while doing that: 1. Added benchmark tests 2. Started using dns.ReadFromSessionUDP / dns.WriteToSessionUDP instead of implementing it by ourselves 3. Generally improved tests 4. Added depguard 5. Improved comments overall in the code
106 lines
3 KiB
Go
106 lines
3 KiB
Go
package dnscrypt
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"math/rand"
|
|
"time"
|
|
|
|
"github.com/ameshkov/dnscrypt/v2/xsecretbox"
|
|
"golang.org/x/crypto/nacl/secretbox"
|
|
)
|
|
|
|
// EncryptedResponse is a structure for encrypting/decrypting server responses
|
|
//
|
|
// <dnscrypt-response> ::= <resolver-magic> <nonce> <encrypted-response>
|
|
// <encrypted-response> ::= AE(<shared-key>, <nonce>, <resolver-response> <resolver-response-pad>)
|
|
type EncryptedResponse struct {
|
|
// EsVersion is the encryption to use
|
|
EsVersion CryptoConstruction
|
|
|
|
// Nonce - <nonce> ::= <client-nonce> <resolver-nonce>
|
|
// <client-nonce> ::= the nonce sent by the client in the related query.
|
|
Nonce [nonceSize]byte
|
|
}
|
|
|
|
// Encrypt encrypts the server response
|
|
//
|
|
// EsVersion must be set.
|
|
// Nonce needs to be set to "client-nonce".
|
|
// This method will generate "resolver-nonce" and set it automatically.
|
|
func (r *EncryptedResponse) Encrypt(packet []byte, sharedKey [sharedKeySize]byte) ([]byte, error) {
|
|
var response []byte
|
|
|
|
// Step 1: generate nonce
|
|
rand.Read(r.Nonce[12:16])
|
|
binary.BigEndian.PutUint64(r.Nonce[16:nonceSize], uint64(time.Now().UnixNano()))
|
|
|
|
// Unencrypted part of the query:
|
|
response = append(response, resolverMagic[:]...)
|
|
response = append(response, r.Nonce[:]...)
|
|
|
|
// <resolver-response> <resolver-response-pad>
|
|
padded := pad(packet)
|
|
|
|
// <encrypted-response>
|
|
nonce := r.Nonce
|
|
if r.EsVersion == XChacha20Poly1305 {
|
|
response = xsecretbox.Seal(response, nonce[:], padded, sharedKey[:])
|
|
} else if r.EsVersion == XSalsa20Poly1305 {
|
|
var xsalsaNonce [nonceSize]byte
|
|
copy(xsalsaNonce[:], nonce[:])
|
|
response = secretbox.Seal(response, padded, &xsalsaNonce, &sharedKey)
|
|
} else {
|
|
return nil, ErrEsVersion
|
|
}
|
|
|
|
return response, nil
|
|
}
|
|
|
|
// Decrypt decrypts the server response
|
|
//
|
|
// EsVersion must be set.
|
|
func (r *EncryptedResponse) Decrypt(response []byte, sharedKey [sharedKeySize]byte) ([]byte, error) {
|
|
headerLength := len(resolverMagic) + nonceSize
|
|
if len(response) < headerLength+xsecretbox.TagSize+minDNSPacketSize {
|
|
return nil, ErrInvalidResponse
|
|
}
|
|
|
|
// read and verify <resolver-magic>
|
|
magic := [resolverMagicSize]byte{}
|
|
copy(magic[:], response[:resolverMagicSize])
|
|
if !bytes.Equal(magic[:], resolverMagic[:]) {
|
|
return nil, ErrInvalidResolverMagic
|
|
}
|
|
|
|
// read nonce
|
|
copy(r.Nonce[:], response[resolverMagicSize:nonceSize+resolverMagicSize])
|
|
|
|
// read and decrypt <encrypted-response>
|
|
encryptedResponse := response[nonceSize+resolverMagicSize:]
|
|
var packet []byte
|
|
var err error
|
|
if r.EsVersion == XChacha20Poly1305 {
|
|
packet, err = xsecretbox.Open(nil, r.Nonce[:], encryptedResponse, sharedKey[:])
|
|
if err != nil {
|
|
return nil, ErrInvalidResponse
|
|
}
|
|
} else if r.EsVersion == XSalsa20Poly1305 {
|
|
var xsalsaServerNonce [24]byte
|
|
copy(xsalsaServerNonce[:], r.Nonce[:])
|
|
var ok bool
|
|
packet, ok = secretbox.Open(nil, encryptedResponse, &xsalsaServerNonce, &sharedKey)
|
|
if !ok {
|
|
return nil, ErrInvalidResponse
|
|
}
|
|
} else {
|
|
return nil, ErrEsVersion
|
|
}
|
|
|
|
packet, err = unpad(packet)
|
|
if err != nil {
|
|
return nil, ErrInvalidPadding
|
|
}
|
|
|
|
return packet, nil
|
|
}
|