Many improvements

This commit is contained in:
Frank Denis 2018-01-10 16:01:29 +01:00
parent 32a8a3d3e2
commit 9eeb799d6e
6 changed files with 84 additions and 29 deletions

View file

@ -11,12 +11,13 @@ import (
"github.com/jedisct1/xsecretbox"
"github.com/miekg/dns"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/nacl/box"
)
type CertInfo struct {
ServerPk [32]byte
SharedKey [32]byte
MagicQuery [8]byte
MagicQuery [ClientMagicLen]byte
CryptoConstruction CryptoConstruction
}
@ -40,15 +41,15 @@ func FetchCurrentCert(proxy *Proxy, proto string, pk ed25519.PublicKey, serverAd
for _, answerRr := range in.Answer {
binCert, err := packTxtString(strings.Join(answerRr.(*dns.TXT).Txt, ""))
if err != nil {
log.Print("Unable to unpack the certificate")
log.Printf("[%v] Unable to unpack the certificate\n", providerName)
continue
}
if len(binCert) < 124 {
log.Print("Certificate too short")
log.Printf("[%v] Certificate too short\n", providerName)
continue
}
if !bytes.Equal(binCert[:4], CertMagic[:4]) {
log.Print("Invalid cert magic")
log.Printf("[%v] Invalid cert magic\n", providerName)
continue
}
cryptoConstruction := CryptoConstruction(0)
@ -58,47 +59,56 @@ func FetchCurrentCert(proxy *Proxy, proto string, pk ed25519.PublicKey, serverAd
case 0x0002:
cryptoConstruction = XChacha20Poly1305
default:
log.Print("Unsupported crypto construction")
log.Printf("[%v] Unsupported crypto construction\n", providerName)
continue
}
signature := binCert[8:72]
signed := binCert[72:]
if !ed25519.Verify(pk, signed, signature) {
log.Print("Incorrect signature")
log.Printf("[%v] Incorrect signature\n", providerName)
continue
}
serial := binary.BigEndian.Uint32(binCert[112:116])
tsBegin := binary.BigEndian.Uint32(binCert[116:120])
tsEnd := binary.BigEndian.Uint32(binCert[120:124])
if now > tsEnd || now < tsBegin {
log.Print("Certificate not valid at the current date")
log.Printf("[%v] Certificate not valid at the current date\n", providerName)
continue
}
if serial < highestSerial {
log.Print("Superseded by a previous certificate")
log.Printf("[%v] Superseded by a previous certificate\n", providerName)
continue
}
if serial == highestSerial && cryptoConstruction < certInfo.CryptoConstruction {
log.Print("Keeping the previous, preferred crypto construction")
continue
if serial == highestSerial {
if cryptoConstruction < certInfo.CryptoConstruction {
log.Printf("[%v] Keeping the previous, preferred crypto construction", providerName)
continue
} else {
log.Printf("[%v] Upgrading the construction from %v to %v\n", providerName, certInfo.CryptoConstruction, cryptoConstruction)
}
}
if cryptoConstruction != XChacha20Poly1305 {
log.Printf("Cryptographic construction %v not supported\n", cryptoConstruction)
if cryptoConstruction != XChacha20Poly1305 && cryptoConstruction != XSalsa20Poly1305 {
log.Printf("[%v] Cryptographic construction %v not supported\n", providerName, cryptoConstruction)
continue
}
var serverPk [32]byte
copy(serverPk[:], binCert[72:104])
sharedKey, err := xsecretbox.SharedKey(proxy.proxySecretKey, serverPk)
if err != nil {
log.Print("Weak public key")
continue
var sharedKey [32]byte
if cryptoConstruction == XChacha20Poly1305 {
sharedKey, err = xsecretbox.SharedKey(proxy.proxySecretKey, serverPk)
if err != nil {
log.Printf("[%v] Weak public key\n", providerName)
continue
}
} else {
box.Precompute(&sharedKey, &serverPk, &proxy.proxySecretKey)
}
certInfo.SharedKey = sharedKey
highestSerial = serial
certInfo.CryptoConstruction = cryptoConstruction
copy(certInfo.ServerPk[:], serverPk[:])
copy(certInfo.MagicQuery[:], binCert[104:112])
log.Printf("Valid cert found: %x\n", certInfo.ServerPk)
log.Printf("[%v] Valid cert found: %x\n", providerName, certInfo.ServerPk)
}
if certInfo.CryptoConstruction == UndefinedConstruction {
return certInfo, errors.New("No useable certificate found")

View file

@ -14,6 +14,10 @@ const (
XChacha20Poly1305
)
const (
ClientMagicLen = 8
)
var (
CertMagic = [4]byte{0x44, 0x4e, 0x53, 0x43}
ServerMagic = [8]byte{0x72, 0x36, 0x66, 0x6e, 0x76, 0x57, 0x6a, 0x38}

View file

@ -6,12 +6,15 @@ import (
"errors"
"github.com/jedisct1/xsecretbox"
"golang.org/x/crypto/nacl/secretbox"
)
const (
NonceSize = xsecretbox.NonceSize
HalfNonceSize = xsecretbox.NonceSize / 2
TagSize = xsecretbox.TagSize
PublicKeySize = 32
QueryOverhead = ClientMagicLen + PublicKeySize + HalfNonceSize + TagSize
ResponseOverhead = len(ServerMagic) + NonceSize + TagSize
)
@ -41,7 +44,7 @@ func (proxy *Proxy) Encrypt(serverInfo *ServerInfo, packet []byte, proto string)
nonce, clientNonce := make([]byte, NonceSize), make([]byte, HalfNonceSize)
rand.Read(clientNonce)
copy(nonce, clientNonce)
minQuestionSize := ResponseOverhead + len(packet)
minQuestionSize := QueryOverhead + len(packet)
if proto == "udp" {
minQuestionSize = Max(proxy.questionSizeEstimator.MinQuestionSize(), minQuestionSize)
} else {
@ -49,14 +52,21 @@ func (proxy *Proxy) Encrypt(serverInfo *ServerInfo, packet []byte, proto string)
rand.Read(xpad[:])
minQuestionSize += int(xpad[0])
}
paddedLength := Min(MaxDNSUDPPacketSize, (Max(minQuestionSize, ResponseOverhead)+63) & ^63)
if ResponseOverhead+len(packet)+1 > paddedLength {
paddedLength := Min(MaxDNSUDPPacketSize, (Max(minQuestionSize, QueryOverhead)+63) & ^63)
if QueryOverhead+len(packet)+1 > paddedLength {
err = errors.New("Question too large; cannot be padded")
return
}
encrypted = append(serverInfo.MagicQuery[:], proxy.proxyPublicKey[:]...)
encrypted = append(encrypted, nonce[:HalfNonceSize]...)
encrypted = xsecretbox.Seal(encrypted, nonce, pad(packet, paddedLength-ResponseOverhead), serverInfo.SharedKey[:])
padded := pad(packet, paddedLength-QueryOverhead)
if serverInfo.CryptoConstruction == XChacha20Poly1305 {
encrypted = xsecretbox.Seal(encrypted, nonce, padded, serverInfo.SharedKey[:])
} else {
var xsalsaNonce [24]byte
copy(xsalsaNonce[:], nonce)
encrypted = secretbox.Seal(encrypted, padded, &xsalsaNonce, &serverInfo.SharedKey)
}
return
}
@ -72,9 +82,21 @@ func (proxy *Proxy) Decrypt(serverInfo *ServerInfo, encrypted []byte, nonce []by
if !bytes.Equal(nonce[:HalfNonceSize], serverNonce[:HalfNonceSize]) {
return encrypted, errors.New("Unexpected nonce")
}
packet, err := xsecretbox.Open(nil, serverNonce, encrypted[responseHeaderLen:], serverInfo.SharedKey[:])
var packet []byte
var err error
if serverInfo.CryptoConstruction == XChacha20Poly1305 {
packet, err = xsecretbox.Open(nil, serverNonce, encrypted[responseHeaderLen:], serverInfo.SharedKey[:])
} else {
var xsalsaServerNonce [24]byte
copy(xsalsaServerNonce[:], serverNonce)
var ok bool
packet, ok = secretbox.Open(nil, encrypted[responseHeaderLen:], &xsalsaServerNonce, &serverInfo.SharedKey)
if !ok {
err = errors.New("Incorrect tag")
}
}
if err != nil {
return encrypted, errors.New("Incorrect tag")
return encrypted, err
}
packet, err = unpad(packet)
if err != nil || len(packet) < MinDNSPacketSize {

View file

@ -11,7 +11,7 @@
## List of servers to use
## If this line is commented, all registered servers will be used
server_names = ["dnscrypt.org-fr"]
#server_names = ["dnscrypt.org-fr", "adguard-dns"]
## List of local addresses and ports to listen to. Can be IPv4 and/or IPv6.
@ -50,3 +50,13 @@ cert_refresh_delay = 30
provider_name = "2.dnscrypt-cert.fr.dnscrypt.org"
address = "212.47.228.136:443"
public_key = "E801:B84E:A606:BFB0:BAC0:CE43:445B:B15E:BA64:B02F:A3C4:AA31:AE10:636A:0790:324D"
[servers."dnscrypt.eu-nl"]
provider_name = "2.dnscrypt-cert.resolver2.dnscrypt.eu"
address = "77.66.84.233:443"
public_key = "3748:5585:E3B9:D088:FD25:AD36:B037:01F5:520C:D648:9E9A:DD52:1457:4955:9F0A:9955"
[servers."adguard-dns"]
provider_name = "2.dnscrypt.default.ns1.adguard.com"
address = "176.103.130.130:5443"
public_key = "D12B:47F2:52DC:F2C2:BBF8:9910:86EA:F79C:E449:5D8B:16C8:A0C4:322E:52CA:3F39:0873"

View file

@ -125,7 +125,6 @@ func (proxy *Proxy) exchangeWithUDPServer(serverInfo *ServerInfo, encryptedQuery
}
pc.SetDeadline(time.Now().Add(serverInfo.Timeout))
pc.Write(encryptedQuery)
encryptedResponse := make([]byte, MaxDNSPacketSize)
length, err := pc.Read(encryptedResponse)
pc.Close()
@ -157,7 +156,7 @@ func (proxy *Proxy) exchangeWithTCPServer(serverInfo *ServerInfo, encryptedQuery
}
func (proxy *Proxy) processIncomingQuery(serverInfo *ServerInfo, serverProto string, query []byte, clientAddr *net.Addr, clientPc net.Conn) {
if len(query) < MinDNSPacketSize {
if len(query) < MinDNSPacketSize || serverInfo == nil {
return
}
clientProto := "udp"
@ -177,6 +176,7 @@ func (proxy *Proxy) processIncomingQuery(serverInfo *ServerInfo, serverProto str
response, err = proxy.exchangeWithTCPServer(serverInfo, encryptedQuery, clientNonce)
}
if err != nil {
serverInfo.noticeFailure()
return
}
if clientAddr != nil {
@ -190,11 +190,12 @@ func (proxy *Proxy) processIncomingQuery(serverInfo *ServerInfo, serverProto str
if HasTCFlag(response) {
proxy.questionSizeEstimator.blindAdjust()
} else {
proxy.questionSizeEstimator.adjust(len(response))
proxy.questionSizeEstimator.adjust(ResponseOverhead + len(response))
}
} else {
response, err = PrefixWithSize(response)
if err != nil {
serverInfo.noticeFailure()
return
}
clientPc.Write(response)

View file

@ -79,7 +79,12 @@ func (serversInfo *ServersInfo) refresh(proxy *Proxy) {
func (serversInfo *ServersInfo) getOne() *ServerInfo {
serversInfo.RLock()
serverInfo := &serversInfo.inner[rand.Intn(len(serversInfo.inner))]
serversCount := len(serversInfo.inner)
if serversCount <= 0 {
serversInfo.RUnlock()
return nil
}
serverInfo := &serversInfo.inner[rand.Intn(serversCount)]
serversInfo.RUnlock()
return serverInfo
}
@ -113,3 +118,6 @@ func (serversInfo *ServersInfo) fetchServerInfo(proxy *Proxy, name string, stamp
}
return serverInfo, nil
}
func (serverInfo *ServerInfo) noticeFailure() {
}