mirror of
https://github.com/SamTherapy/dnscrypt.git
synced 2024-11-19 04:24:11 +00:00
d0ae1d198d
The new command allows to convert keys generated by dnscrypt-wrapper to a configuration compatible with the "dnscrypt" tool
167 lines
4.6 KiB
Go
167 lines
4.6 KiB
Go
package dnscrypt
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/AdguardTeam/golibs/log"
|
|
"github.com/ameshkov/dnsstamps"
|
|
"golang.org/x/crypto/curve25519"
|
|
)
|
|
|
|
const dnsCryptV2Prefix = "2.dnscrypt-cert."
|
|
|
|
// ResolverConfig - DNSCrypt resolver configuration
|
|
type ResolverConfig struct {
|
|
// DNSCrypt provider name
|
|
ProviderName string `yaml:"provider_name"`
|
|
|
|
// PublicKey - DNSCrypt resolver public key
|
|
PublicKey string `yaml:"public_key"`
|
|
|
|
// PrivateKey - DNSCrypt resolver private key
|
|
// The main and only purpose of this key is to sign the certificate
|
|
PrivateKey string `yaml:"private_key"`
|
|
|
|
// ResolverSk - hex-encoded short-term private key.
|
|
// This key is used to encrypt/decrypt DNS queries.
|
|
// If not set, we'll generate a new random ResolverSk and ResolverPk.
|
|
ResolverSk string `yaml:"resolver_secret"`
|
|
|
|
// ResolverPk - hex-encoded short-term public key corresponding to ResolverSk.
|
|
// This key is used to encrypt/decrypt DNS queries.
|
|
ResolverPk string `yaml:"resolver_public"`
|
|
|
|
// EsVersion - crypto to use in this resolver
|
|
EsVersion CryptoConstruction `yaml:"es_version"`
|
|
|
|
// CertificateTTL - time-to-live for the certificate that is generated using this ResolverConfig.
|
|
// If not set, we'll use 1 year by default.
|
|
CertificateTTL time.Duration `yaml:"certificate_ttl"`
|
|
}
|
|
|
|
// CreateCert - generates a signed Cert to be used by Server
|
|
func (rc *ResolverConfig) CreateCert() (*Cert, error) {
|
|
log.Printf("Creating signed DNSCrypt certificate")
|
|
|
|
notAfter := time.Now()
|
|
if rc.CertificateTTL > 0 {
|
|
notAfter = notAfter.Add(rc.CertificateTTL)
|
|
} else {
|
|
// Default cert validity is 1 year
|
|
notAfter = notAfter.Add(time.Hour * 24 * 365)
|
|
}
|
|
|
|
cert := &Cert{
|
|
Serial: uint32(time.Now().Unix()),
|
|
NotAfter: uint32(notAfter.Unix()),
|
|
NotBefore: uint32(time.Now().Unix()),
|
|
EsVersion: rc.EsVersion,
|
|
}
|
|
|
|
// short-term public key
|
|
resolverPk, err := HexDecodeKey(rc.ResolverPk)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// short-term private key
|
|
resolverSk, err := HexDecodeKey(rc.ResolverSk)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(resolverPk) != keySize || len(resolverSk) != keySize {
|
|
log.Printf("Short-term keys are not set, generating random ones")
|
|
sk, pk := generateRandomKeyPair()
|
|
resolverSk = sk[:]
|
|
resolverPk = pk[:]
|
|
}
|
|
|
|
copy(cert.ResolverPk[:], resolverPk[:])
|
|
copy(cert.ResolverSk[:], resolverSk)
|
|
|
|
// private key
|
|
privateKey, err := HexDecodeKey(rc.PrivateKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// sign the data
|
|
cert.Sign(privateKey)
|
|
|
|
log.Info("Signed cert: %s", cert.String())
|
|
|
|
// done
|
|
return cert, nil
|
|
}
|
|
|
|
// CreateStamp - generates a DNS stamp for this resolver
|
|
func (rc *ResolverConfig) CreateStamp(addr string) (dnsstamps.ServerStamp, error) {
|
|
stamp := dnsstamps.ServerStamp{
|
|
ProviderName: rc.ProviderName,
|
|
Proto: dnsstamps.StampProtoTypeDNSCrypt,
|
|
}
|
|
|
|
serverPk, err := HexDecodeKey(rc.PublicKey)
|
|
if err != nil {
|
|
return stamp, err
|
|
}
|
|
|
|
stamp.ServerPk = serverPk
|
|
stamp.ServerAddrStr = addr
|
|
return stamp, nil
|
|
}
|
|
|
|
// GenerateResolverConfig - generates resolver configuration for a given provider name.
|
|
// providerName is mandatory. If needed, "2.dnscrypt-cert." prefix is added to it.
|
|
// privateKey is optional. If not set, it will be generated automatically.
|
|
func GenerateResolverConfig(providerName string, privateKey ed25519.PrivateKey) (ResolverConfig, error) {
|
|
rc := ResolverConfig{
|
|
// Use XSalsa20Poly1305 by default
|
|
EsVersion: XSalsa20Poly1305,
|
|
}
|
|
if !strings.HasPrefix(providerName, dnsCryptV2Prefix) {
|
|
providerName = dnsCryptV2Prefix + providerName
|
|
}
|
|
rc.ProviderName = providerName
|
|
|
|
var err error
|
|
if privateKey == nil {
|
|
// privateKey = gene
|
|
_, privateKey, err = ed25519.GenerateKey(rand.Reader)
|
|
if err != nil {
|
|
return rc, err
|
|
}
|
|
}
|
|
rc.PrivateKey = HexEncodeKey(privateKey)
|
|
rc.PublicKey = HexEncodeKey(privateKey.Public().(ed25519.PublicKey))
|
|
|
|
resolverSk, resolverPk := generateRandomKeyPair()
|
|
rc.ResolverSk = HexEncodeKey(resolverSk[:])
|
|
rc.ResolverPk = HexEncodeKey(resolverPk[:])
|
|
return rc, nil
|
|
}
|
|
|
|
// HexEncodeKey - encodes a byte slice to a hex-encoded string.
|
|
func HexEncodeKey(b []byte) string {
|
|
return strings.ToUpper(hex.EncodeToString(b))
|
|
}
|
|
|
|
// HexDecodeKey - decodes a hex-encoded string with (optional) colons
|
|
// to a byte array.
|
|
func HexDecodeKey(str string) ([]byte, error) {
|
|
return hex.DecodeString(strings.ReplaceAll(str, ":", ""))
|
|
}
|
|
|
|
// generateRandomKeyPair - generates a random key-pair
|
|
func generateRandomKeyPair() (privateKey [keySize]byte, publicKey [keySize]byte) {
|
|
privateKey = [keySize]byte{}
|
|
publicKey = [keySize]byte{}
|
|
|
|
_, _ = rand.Read(privateKey[:])
|
|
curve25519.ScalarBaseMult(&publicKey, &privateKey)
|
|
return
|
|
}
|