From 7850f9a6916d2c2522ff844394bb25e9ba77ec1e Mon Sep 17 00:00:00 2001 From: Josh Rickmar Date: Wed, 21 Aug 2024 01:21:13 +0000 Subject: [PATCH] main,gencerts: Punycode non-ASCII hostnames When a certificate is autogenerated by dcrd or with gencerts, errors would occur if any hostname contained non-ASCII characters. While X509 certificates do support UTF8 strings, Go does not yet support creating these. Instead, as a workaround and to keep certificate generation working to avoid errors at dcrd startup, convert hostnames with non-ASCII Unicode characters to their IDNA form, which uses Punycode to ASCII-encode the problematic Unicode characters. --- certgen/certgen.go | 24 ++++++++++++++++++++++++ certgen/go.mod | 6 +++++- certgen/go.sum | 4 ++++ cmd/gencerts/gencerts.go | 26 ++++++++++++++++++++++++++ go.mod | 3 ++- go.sum | 1 + 6 files changed, 62 insertions(+), 2 deletions(-) diff --git a/certgen/certgen.go b/certgen/certgen.go index 6eaeb58e9..7836992f7 100644 --- a/certgen/certgen.go +++ b/certgen/certgen.go @@ -19,10 +19,21 @@ import ( "net" "os" "time" + "unicode" "github.com/decred/dcrd/crypto/rand" + "golang.org/x/net/idna" ) +func isASCII(s string) bool { + for _, c := range s { + if c > unicode.MaxASCII { + return false + } + } + return true +} + // NewTLSCertPair returns a new PEM-encoded x.509 certificate pair with new // ECDSA keys. The machine's local interface addresses and all variants of IPv4 // and IPv6 localhost are included as valid IP addresses. @@ -50,6 +61,12 @@ func NewTLSCertPair(curve elliptic.Curve, organization string, validUntil time.T if err != nil { return nil, nil, err } + if !isASCII(host) { + host, err = idna.ToASCII(host) + if err != nil { + return nil, nil, err + } + } ipAddresses := []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")} dnsNames := []string{host} @@ -71,6 +88,13 @@ func NewTLSCertPair(curve elliptic.Curve, organization string, validUntil time.T return } } + if !isASCII(host) { + var err error + host, err = idna.ToASCII(host) + if err != nil { + return + } + } dnsNames = append(dnsNames, host) } diff --git a/certgen/go.mod b/certgen/go.mod index 25456d706..a5603ae81 100644 --- a/certgen/go.mod +++ b/certgen/go.mod @@ -2,9 +2,13 @@ module github.com/decred/dcrd/certgen go 1.18 -require github.com/decred/dcrd/crypto/rand v1.0.0 +require ( + github.com/decred/dcrd/crypto/rand v1.0.0 + golang.org/x/net v0.21.0 +) require ( golang.org/x/crypto v0.24.0 // indirect golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect ) diff --git a/certgen/go.sum b/certgen/go.sum index 4e1602259..887093007 100644 --- a/certgen/go.sum +++ b/certgen/go.sum @@ -2,5 +2,9 @@ github.com/decred/dcrd/crypto/rand v1.0.0 h1:Ah9Asl36OZt09sGSMbJZuL1HfwGdlC38q/Z github.com/decred/dcrd/crypto/rand v1.0.0/go.mod h1:coa7BbxSTiKH6esi257plGfMFYuGL4MTbQlLYnOdzpE= golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= diff --git a/cmd/gencerts/gencerts.go b/cmd/gencerts/gencerts.go index 71f870797..b21cd87f9 100644 --- a/cmd/gencerts/gencerts.go +++ b/cmd/gencerts/gencerts.go @@ -20,9 +20,11 @@ import ( "net" "os" "time" + "unicode" "github.com/decred/dcrd/crypto/rand" flags "github.com/jessevdk/go-flags" + "golang.org/x/net/idna" ) func fatalf(format string, args ...interface{}) { @@ -212,6 +214,15 @@ type certWithPEM struct { Cert *x509.Certificate } +func isASCII(s string) bool { + for _, c := range s { + if c > unicode.MaxASCII { + return false + } + } + return true +} + func newTemplate(hosts []string, org string, validUntil time.Time) (*x509.Certificate, error) { now := time.Now() if validUntil.After(endOfTime) { @@ -229,6 +240,13 @@ func newTemplate(hosts []string, org string, validUntil time.Time) (*x509.Certif var hostnames []string var ips []net.IP for _, h := range hosts { + if !isASCII(h) { + var err error + h, err = idna.ToASCII(h) + if err != nil { + return nil, err + } + } if ip := net.ParseIP(h); ip != nil { ips = append(ips, ip) continue @@ -236,6 +254,14 @@ func newTemplate(hosts []string, org string, validUntil time.Time) (*x509.Certif hostnames = append(hostnames, h) } + if !isASCII(cn) { + var err error + cn, err = idna.ToASCII(cn) + if err != nil { + return nil, err + } + } + template := &x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ diff --git a/go.mod b/go.mod index b4efcb2fa..1bf5c3c7b 100644 --- a/go.mod +++ b/go.mod @@ -40,6 +40,7 @@ require ( github.com/jrick/bitset v1.0.0 github.com/jrick/logrotate v1.0.0 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 + golang.org/x/net v0.25.0 golang.org/x/sys v0.21.0 golang.org/x/term v0.21.0 lukechampine.com/blake3 v1.3.0 @@ -55,8 +56,8 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect golang.org/x/crypto v0.24.0 // indirect - golang.org/x/net v0.25.0 // indirect golang.org/x/sync v0.7.0 // indirect + golang.org/x/text v0.16.0 // indirect ) replace ( diff --git a/go.sum b/go.sum index 03863f6a3..fa66cb916 100644 --- a/go.sum +++ b/go.sum @@ -88,6 +88,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=