Re-implement ephemeral keys for DNSCrypt
This commit is contained in:
parent
70dca19326
commit
ca80b69b3a
|
@ -27,6 +27,7 @@ type Config struct {
|
|||
KeepAlive int `toml:"keepalive"`
|
||||
CertRefreshDelay int `toml:"cert_refresh_delay"`
|
||||
CertIgnoreTimestamp bool `toml:"cert_ignore_timestamp"`
|
||||
EphemeralKeys bool `toml:"dnscrypt_ephemeral_keys"`
|
||||
LBStrategy string `toml:"lb_strategy"`
|
||||
BlockIPv6 bool `toml:"block_ipv6"`
|
||||
Cache bool
|
||||
|
@ -69,6 +70,7 @@ func newConfig() Config {
|
|||
KeepAlive: 5,
|
||||
CertRefreshDelay: 240,
|
||||
CertIgnoreTimestamp: false,
|
||||
EphemeralKeys: false,
|
||||
Cache: true,
|
||||
CacheSize: 256,
|
||||
CacheNegTTL: 60,
|
||||
|
@ -236,6 +238,7 @@ func ConfigLoad(proxy *Proxy, svcFlag *string) error {
|
|||
proxy.certRefreshDelay = time.Duration(config.CertRefreshDelay) * time.Minute
|
||||
proxy.certRefreshDelayAfterFailure = time.Duration(10 * time.Second)
|
||||
proxy.certIgnoreTimestamp = config.CertIgnoreTimestamp
|
||||
proxy.ephemeralKeys = config.EphemeralKeys
|
||||
if len(config.ListenAddresses) == 0 {
|
||||
dlog.Debug("No local IP/port configured")
|
||||
}
|
||||
|
|
|
@ -3,9 +3,13 @@ package main
|
|||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
|
||||
"github.com/jedisct1/dlog"
|
||||
"github.com/jedisct1/xsecretbox"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
"golang.org/x/crypto/nacl/box"
|
||||
"golang.org/x/crypto/nacl/secretbox"
|
||||
)
|
||||
|
||||
|
@ -40,10 +44,39 @@ func unpad(packet []byte) ([]byte, error) {
|
|||
}
|
||||
}
|
||||
|
||||
func (proxy *Proxy) Encrypt(serverInfo *ServerInfo, packet []byte, proto string) (encrypted []byte, clientNonce []byte, err error) {
|
||||
func ComputeSharedKey(cryptoConstruction CryptoConstruction, secretKey *[32]byte, serverPk *[32]byte, providerName *string) (sharedKey [32]byte) {
|
||||
if cryptoConstruction == XChacha20Poly1305 {
|
||||
var err error
|
||||
sharedKey, err = xsecretbox.SharedKey(*secretKey, *serverPk)
|
||||
if err != nil {
|
||||
dlog.Criticalf("[%v] Weak public key", providerName)
|
||||
}
|
||||
} else {
|
||||
box.Precompute(&sharedKey, serverPk, secretKey)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (proxy *Proxy) Encrypt(serverInfo *ServerInfo, packet []byte, proto string) (sharedKey *[32]byte, encrypted []byte, clientNonce []byte, err error) {
|
||||
nonce, clientNonce := make([]byte, NonceSize), make([]byte, HalfNonceSize)
|
||||
rand.Read(clientNonce)
|
||||
copy(nonce, clientNonce)
|
||||
var publicKey *[PublicKeySize]byte
|
||||
if proxy.ephemeralKeys {
|
||||
h := sha512.New512_256()
|
||||
h.Write(clientNonce)
|
||||
h.Write(proxy.proxySecretKey[:])
|
||||
var ephSk [32]byte
|
||||
h.Sum(ephSk[:0])
|
||||
var xPublicKey [PublicKeySize]byte
|
||||
curve25519.ScalarBaseMult(&xPublicKey, &ephSk)
|
||||
publicKey = &xPublicKey
|
||||
xsharedKey := ComputeSharedKey(serverInfo.CryptoConstruction, &ephSk, &serverInfo.ServerPk, nil)
|
||||
sharedKey = &xsharedKey
|
||||
} else {
|
||||
sharedKey = &serverInfo.SharedKey
|
||||
publicKey = &proxy.proxyPublicKey
|
||||
}
|
||||
minQuestionSize := QueryOverhead + len(packet)
|
||||
if proto == "udp" {
|
||||
minQuestionSize = Max(proxy.questionSizeEstimator.MinQuestionSize(), minQuestionSize)
|
||||
|
@ -57,20 +90,20 @@ func (proxy *Proxy) Encrypt(serverInfo *ServerInfo, packet []byte, proto string)
|
|||
err = errors.New("Question too large; cannot be padded")
|
||||
return
|
||||
}
|
||||
encrypted = append(serverInfo.MagicQuery[:], proxy.proxyPublicKey[:]...)
|
||||
encrypted = append(serverInfo.MagicQuery[:], publicKey[:]...)
|
||||
encrypted = append(encrypted, nonce[:HalfNonceSize]...)
|
||||
padded := pad(packet, paddedLength-QueryOverhead)
|
||||
if serverInfo.CryptoConstruction == XChacha20Poly1305 {
|
||||
encrypted = xsecretbox.Seal(encrypted, nonce, padded, serverInfo.SharedKey[:])
|
||||
encrypted = xsecretbox.Seal(encrypted, nonce, padded, sharedKey[:])
|
||||
} else {
|
||||
var xsalsaNonce [24]byte
|
||||
copy(xsalsaNonce[:], nonce)
|
||||
encrypted = secretbox.Seal(encrypted, padded, &xsalsaNonce, &serverInfo.SharedKey)
|
||||
encrypted = secretbox.Seal(encrypted, padded, &xsalsaNonce, sharedKey)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (proxy *Proxy) Decrypt(serverInfo *ServerInfo, encrypted []byte, nonce []byte) ([]byte, error) {
|
||||
func (proxy *Proxy) Decrypt(serverInfo *ServerInfo, sharedKey *[32]byte, encrypted []byte, nonce []byte) ([]byte, error) {
|
||||
serverMagicLen := len(ServerMagic)
|
||||
responseHeaderLen := serverMagicLen + NonceSize
|
||||
if len(encrypted) < responseHeaderLen+TagSize+int(MinDNSPacketSize) ||
|
||||
|
@ -85,12 +118,12 @@ func (proxy *Proxy) Decrypt(serverInfo *ServerInfo, encrypted []byte, nonce []by
|
|||
var packet []byte
|
||||
var err error
|
||||
if serverInfo.CryptoConstruction == XChacha20Poly1305 {
|
||||
packet, err = xsecretbox.Open(nil, serverNonce, encrypted[responseHeaderLen:], serverInfo.SharedKey[:])
|
||||
packet, err = xsecretbox.Open(nil, serverNonce, encrypted[responseHeaderLen:], sharedKey[:])
|
||||
} else {
|
||||
var xsalsaServerNonce [24]byte
|
||||
copy(xsalsaServerNonce[:], serverNonce)
|
||||
var ok bool
|
||||
packet, ok = secretbox.Open(nil, encrypted[responseHeaderLen:], &xsalsaServerNonce, &serverInfo.SharedKey)
|
||||
packet, ok = secretbox.Open(nil, encrypted[responseHeaderLen:], &xsalsaServerNonce, sharedKey)
|
||||
if !ok {
|
||||
err = errors.New("Incorrect tag")
|
||||
}
|
||||
|
|
|
@ -8,10 +8,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/jedisct1/dlog"
|
||||
"github.com/jedisct1/xsecretbox"
|
||||
"github.com/miekg/dns"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"golang.org/x/crypto/nacl/box"
|
||||
)
|
||||
|
||||
type CertInfo struct {
|
||||
|
@ -120,16 +118,7 @@ func FetchCurrentDNSCryptCert(proxy *Proxy, serverName *string, proto string, pk
|
|||
}
|
||||
var serverPk [32]byte
|
||||
copy(serverPk[:], binCert[72:104])
|
||||
var sharedKey [32]byte
|
||||
if cryptoConstruction == XChacha20Poly1305 {
|
||||
sharedKey, err = xsecretbox.SharedKey(proxy.proxySecretKey, serverPk)
|
||||
if err != nil {
|
||||
dlog.Criticalf("[%v] Weak public key", providerName)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
box.Precompute(&sharedKey, &serverPk, &proxy.proxySecretKey)
|
||||
}
|
||||
sharedKey := ComputeSharedKey(cryptoConstruction, &proxy.proxySecretKey, &serverPk, &providerName)
|
||||
certInfo.SharedKey = sharedKey
|
||||
highestSerial = serial
|
||||
certInfo.CryptoConstruction = cryptoConstruction
|
||||
|
|
|
@ -109,6 +109,14 @@ keepalive = 10
|
|||
cert_refresh_delay = 240
|
||||
|
||||
|
||||
## DNSCrypt: Create a new, unique key for every single DNS query
|
||||
## This may improve privacy but can also have a significant impact on CPU usage
|
||||
## Only enable if you don't have a lot of network load
|
||||
|
||||
# dnscrypt_ephemeral_keys = false
|
||||
|
||||
|
||||
|
||||
## DoH: Disable TLS session tickets
|
||||
## increases privacy but also latency - Bump keepalive up to compensate.
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
type Proxy struct {
|
||||
proxyPublicKey [32]byte
|
||||
proxySecretKey [32]byte
|
||||
ephemeralKeys bool
|
||||
questionSizeEstimator QuestionSizeEstimator
|
||||
serversInfo ServersInfo
|
||||
timeout time.Duration
|
||||
|
@ -193,7 +194,7 @@ func (proxy *Proxy) tcpListenerFromAddr(listenAddr *net.TCPAddr) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (proxy *Proxy) exchangeWithUDPServer(serverInfo *ServerInfo, encryptedQuery []byte, clientNonce []byte) ([]byte, error) {
|
||||
func (proxy *Proxy) exchangeWithUDPServer(serverInfo *ServerInfo, sharedKey *[32]byte, encryptedQuery []byte, clientNonce []byte) ([]byte, error) {
|
||||
pc, err := net.DialUDP("udp", nil, serverInfo.UDPAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -207,10 +208,10 @@ func (proxy *Proxy) exchangeWithUDPServer(serverInfo *ServerInfo, encryptedQuery
|
|||
return nil, err
|
||||
}
|
||||
encryptedResponse = encryptedResponse[:length]
|
||||
return proxy.Decrypt(serverInfo, encryptedResponse, clientNonce)
|
||||
return proxy.Decrypt(serverInfo, sharedKey, encryptedResponse, clientNonce)
|
||||
}
|
||||
|
||||
func (proxy *Proxy) exchangeWithTCPServer(serverInfo *ServerInfo, encryptedQuery []byte, clientNonce []byte) ([]byte, error) {
|
||||
func (proxy *Proxy) exchangeWithTCPServer(serverInfo *ServerInfo, sharedKey *[32]byte, encryptedQuery []byte, clientNonce []byte) ([]byte, error) {
|
||||
pc, err := net.DialTCP("tcp", nil, serverInfo.TCPAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -227,7 +228,7 @@ func (proxy *Proxy) exchangeWithTCPServer(serverInfo *ServerInfo, encryptedQuery
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return proxy.Decrypt(serverInfo, encryptedResponse, clientNonce)
|
||||
return proxy.Decrypt(serverInfo, sharedKey, encryptedResponse, clientNonce)
|
||||
}
|
||||
|
||||
func (proxy *Proxy) clientsCountInc() bool {
|
||||
|
@ -273,15 +274,15 @@ func (proxy *Proxy) processIncomingQuery(serverInfo *ServerInfo, clientProto str
|
|||
if len(response) == 0 {
|
||||
var ttl *uint32
|
||||
if serverInfo.Proto == StampProtoTypeDNSCrypt {
|
||||
encryptedQuery, clientNonce, err := proxy.Encrypt(serverInfo, query, serverProto)
|
||||
sharedKey, encryptedQuery, clientNonce, err := proxy.Encrypt(serverInfo, query, serverProto)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
serverInfo.noticeBegin(proxy)
|
||||
if serverProto == "udp" {
|
||||
response, err = proxy.exchangeWithUDPServer(serverInfo, encryptedQuery, clientNonce)
|
||||
response, err = proxy.exchangeWithUDPServer(serverInfo, sharedKey, encryptedQuery, clientNonce)
|
||||
} else {
|
||||
response, err = proxy.exchangeWithTCPServer(serverInfo, encryptedQuery, clientNonce)
|
||||
response, err = proxy.exchangeWithTCPServer(serverInfo, sharedKey, encryptedQuery, clientNonce)
|
||||
}
|
||||
if err != nil {
|
||||
serverInfo.noticeFailure(proxy)
|
||||
|
|
Loading…
Reference in New Issue