reflection work so far...
This commit is contained in:
parent
bf887ce948
commit
1471dc29ed
4
.githooks/pre-commit/01-go_gen.py
Executable file
4
.githooks/pre-commit/01-go_gen.py
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
go generate
|
||||||
|
git add consts_param_map.go
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -26,6 +26,14 @@
|
|||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
|
|
||||||
|
# The test data is intended to be generated locally.
|
||||||
|
# There's no reason to pollute the repo with it.
|
||||||
|
_extra/gen_test_pki/_testdata/*
|
||||||
|
!_extra/gen_test_pki/_testdata/.keep
|
||||||
|
_extra/gen_test_pki/gen_test_pki
|
||||||
|
_testdata/*
|
||||||
|
!_testdata/.keep
|
||||||
|
|
||||||
# Test binary, built with `go test -c`
|
# Test binary, built with `go test -c`
|
||||||
*.test
|
*.test
|
||||||
|
|
||||||
|
0
_extra/gen_test_pki/_testdata/.keep
Normal file
0
_extra/gen_test_pki/_testdata/.keep
Normal file
144
_extra/gen_test_pki/consts.go
Normal file
144
_extra/gen_test_pki/consts.go
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
`crypto/x509`
|
||||||
|
`crypto/x509/pkix`
|
||||||
|
`embed`
|
||||||
|
`net`
|
||||||
|
`time`
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
pairTypes []string = []string{
|
||||||
|
"ca",
|
||||||
|
"inter",
|
||||||
|
"leaf_server",
|
||||||
|
"leaf_user",
|
||||||
|
}
|
||||||
|
|
||||||
|
keyTypes []string = []string{
|
||||||
|
/*
|
||||||
|
Per:
|
||||||
|
https://pkg.go.dev/crypto/x509#CreateCertificate
|
||||||
|
https://pkg.go.dev/crypto/x509#CreateCertificateRequest
|
||||||
|
ECDH keys are not supported for certificates (only ECDSA, ED25519, and RSA).
|
||||||
|
*/
|
||||||
|
// "ecdh",
|
||||||
|
"ecdsa",
|
||||||
|
"ed25519",
|
||||||
|
"rsa",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populated by init.
|
||||||
|
pairs map[string]*Pair = make(map[string]*Pair)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed "_testdata/*"
|
||||||
|
pems embed.FS
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
caCn string = "gen_test_pki Root CA"
|
||||||
|
interCn string = "gen_test_pki Intermediate CA"
|
||||||
|
serverCn string = "server.example.com"
|
||||||
|
userCn string = "username@example.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
pkixCommon *pkix.Name = &pkix.Name{
|
||||||
|
Country: []string{
|
||||||
|
"XX",
|
||||||
|
},
|
||||||
|
Organization: []string{
|
||||||
|
"An Example Organization",
|
||||||
|
},
|
||||||
|
OrganizationalUnit: []string{
|
||||||
|
"An Example Department",
|
||||||
|
},
|
||||||
|
Locality: []string{
|
||||||
|
"Some City",
|
||||||
|
},
|
||||||
|
Province: []string{
|
||||||
|
"Some State",
|
||||||
|
},
|
||||||
|
StreetAddress: []string{
|
||||||
|
"123 Example Street",
|
||||||
|
},
|
||||||
|
PostalCode: []string{
|
||||||
|
"12345",
|
||||||
|
},
|
||||||
|
// SerialNumber: "", // SerialNumber should be blank, and contextually generated via getSerial().
|
||||||
|
// CommonName: "", // CommonName should be blank, and contextually generated via getSubj().
|
||||||
|
Names: nil,
|
||||||
|
ExtraNames: nil,
|
||||||
|
}
|
||||||
|
certTpl map[string]*x509.Certificate = map[string]*x509.Certificate{
|
||||||
|
"ca": &x509.Certificate{
|
||||||
|
SerialNumber: getSerial(),
|
||||||
|
Subject: getSubj(caCn),
|
||||||
|
NotBefore: time.Now().Add(time.Second * -10),
|
||||||
|
NotAfter: time.Now().Add(10 * 365 * 24 * time.Hour), // (about) 10 years
|
||||||
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
IsCA: true,
|
||||||
|
MaxPathLen: 1,
|
||||||
|
},
|
||||||
|
"inter": &x509.Certificate{
|
||||||
|
SerialNumber: getSerial(),
|
||||||
|
Subject: getSubj(interCn),
|
||||||
|
NotBefore: time.Now().Add(time.Second * -9),
|
||||||
|
NotAfter: time.Now().Add(9 * 365 * 24 * time.Hour), // (about) 9 years
|
||||||
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
IsCA: true,
|
||||||
|
MaxPathLen: 0,
|
||||||
|
},
|
||||||
|
"leaf_server": &x509.Certificate{
|
||||||
|
SerialNumber: getSerial(),
|
||||||
|
Subject: getSubj(serverCn),
|
||||||
|
NotBefore: time.Now().Add(time.Second * -8),
|
||||||
|
NotAfter: time.Now().Add(9 * 365 * 24 * time.Hour), // (about) 8 years
|
||||||
|
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||||
|
x509.ExtKeyUsageServerAuth,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"leaf_user": &x509.Certificate{
|
||||||
|
SerialNumber: getSerial(),
|
||||||
|
Subject: getSubj(userCn),
|
||||||
|
NotBefore: time.Now().Add(time.Second * -8),
|
||||||
|
NotAfter: time.Now().Add(9 * 365 * 24 * time.Hour), // (about) 8 years
|
||||||
|
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{
|
||||||
|
x509.ExtKeyUsageClientAuth,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
csrs map[string]*x509.CertificateRequest = map[string]*x509.CertificateRequest{
|
||||||
|
"inter": &x509.CertificateRequest{
|
||||||
|
Subject: getSubj(interCn),
|
||||||
|
},
|
||||||
|
"leaf_server": &x509.CertificateRequest{
|
||||||
|
Subject: getSubj(serverCn),
|
||||||
|
IPAddresses: []net.IP{
|
||||||
|
net.IP(net.ParseIP("127.0.0.1")),
|
||||||
|
net.IP(net.ParseIP("::ffff:127.0.0.1")),
|
||||||
|
net.IP(net.ParseIP("::1")),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"leaf_user": &x509.CertificateRequest{
|
||||||
|
Subject: getSubj(userCn),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
parents map[string]string = map[string]string{
|
||||||
|
"inter": "ca",
|
||||||
|
"leaf_server": "inter",
|
||||||
|
"leaf_user": "inter",
|
||||||
|
}
|
||||||
|
certgenOrder []string = []string{
|
||||||
|
"inter",
|
||||||
|
"leaf_server",
|
||||||
|
"leaf_user",
|
||||||
|
}
|
||||||
|
)
|
476
_extra/gen_test_pki/funcs.go
Normal file
476
_extra/gen_test_pki/funcs.go
Normal file
@ -0,0 +1,476 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
`bytes`
|
||||||
|
`crypto`
|
||||||
|
`crypto/ecdh`
|
||||||
|
`crypto/ecdsa`
|
||||||
|
`crypto/ed25519`
|
||||||
|
`crypto/elliptic`
|
||||||
|
`crypto/rand`
|
||||||
|
`crypto/rsa`
|
||||||
|
`crypto/x509`
|
||||||
|
`crypto/x509/pkix`
|
||||||
|
`encoding/binary`
|
||||||
|
`encoding/pem`
|
||||||
|
`errors`
|
||||||
|
`fmt`
|
||||||
|
`log`
|
||||||
|
`math/big`
|
||||||
|
`os`
|
||||||
|
`path/filepath`
|
||||||
|
|
||||||
|
`github.com/brunoga/deep`
|
||||||
|
`github.com/google/uuid`
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
getKeyFpath returns a (relative) path for a key PEM file (PKCS#8).
|
||||||
|
|
||||||
|
This is used both when fetching from embed.FS and when generating new keys.
|
||||||
|
*/
|
||||||
|
func getKeyFpath(pairType, keyType string) (path string) {
|
||||||
|
|
||||||
|
path = filepath.Join("_testdata", fmt.Sprintf("%s_%s_key.pem", pairType, keyType))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
getCertFpath returns a (relative) path for a cert PEM file.
|
||||||
|
|
||||||
|
This is used both when fetching from embed.FS and when generating new certs.
|
||||||
|
*/
|
||||||
|
func getCertFpath(pairType, keyType string) (path string) {
|
||||||
|
|
||||||
|
path = filepath.Join("_testdata", fmt.Sprintf("%s_%s_cert.pem", pairType, keyType))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
getChainFpath returns a (relative) path for a chained cert PEM file.
|
||||||
|
|
||||||
|
This is used both when fetching from embed.FS and when generating new certs.
|
||||||
|
*/
|
||||||
|
func getChainFpath(pairType, keyType string) (path string) {
|
||||||
|
|
||||||
|
path = filepath.Join("_testdata", fmt.Sprintf("%s_%s_cert_chained.pem", pairType, keyType))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
getCsrFpath returns a (relative) path for a CSR PEM file.
|
||||||
|
|
||||||
|
This is used both when fetching from embed.FS and when generating new CSRs.
|
||||||
|
*/
|
||||||
|
func getCsrFpath(pairType, keyType string) (path string) {
|
||||||
|
|
||||||
|
path = filepath.Join("_testdata", fmt.Sprintf("%s_%s_csr.pem", pairType, keyType))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
getKeypair takes cert type t and key type kt and returns the crypto.Private and crypto.Public keys for it.
|
||||||
|
|
||||||
|
It assumes that loadKeys() at the *least* has already been called.
|
||||||
|
*/
|
||||||
|
func getKeypair(t, kt string) (priv crypto.PrivateKey, pub crypto.PublicKey) {
|
||||||
|
|
||||||
|
priv = pairs[t].privKeys[kt]
|
||||||
|
|
||||||
|
switch k := priv.(type) {
|
||||||
|
case *ecdh.PrivateKey:
|
||||||
|
pub = k.Public()
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
pub = k.Public()
|
||||||
|
case ed25519.PrivateKey: // This is correct. Unlike other kt's, ed25519 doesn't use pointers.
|
||||||
|
pub = k.Public()
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
pub = k.Public()
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
getSerial returns a (pseudo-)random certificate based on a UUIDv4 (RFC 4122 type 4).
|
||||||
|
|
||||||
|
This guarantees not only that renewals (if issued/implemented) are reasonably guaranteed
|
||||||
|
to be different from the past issuance but also that the serial issuance is non-sequential
|
||||||
|
(both are common modern requirements of modern browser-trusted CAs; see
|
||||||
|
https://cabforum.org/working-groups/server/baseline-requirements/documents/)
|
||||||
|
*/
|
||||||
|
func getSerial() (serial *big.Int) {
|
||||||
|
|
||||||
|
var b []byte
|
||||||
|
var n int64
|
||||||
|
var u uuid.UUID = uuid.New()
|
||||||
|
|
||||||
|
b = u[:]
|
||||||
|
|
||||||
|
n = int64(binary.BigEndian.Uint64(b))
|
||||||
|
// Serials must be positive.
|
||||||
|
if n < 0 {
|
||||||
|
n = -n
|
||||||
|
}
|
||||||
|
|
||||||
|
serial = big.NewInt(n)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTpl returns a version of certificate template tpl with a randomized serial.
|
||||||
|
func getTpl(tpl *x509.Certificate) (newTpl *x509.Certificate) {
|
||||||
|
|
||||||
|
newTpl = new(x509.Certificate)
|
||||||
|
*newTpl = *tpl
|
||||||
|
|
||||||
|
newTpl.SerialNumber = getSerial()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSubj returns a cert/CSR-specific pkix.Name from a given cn (commonName).
|
||||||
|
func getSubj(cn string) (newSubj pkix.Name) {
|
||||||
|
|
||||||
|
newSubj = deep.MustCopy(*pkixCommon)
|
||||||
|
newSubj.CommonName = cn
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadKeys either loads from pems or generates and writes out the PEM keys.
|
||||||
|
func loadKeys() (err error) {
|
||||||
|
|
||||||
|
var b []byte
|
||||||
|
var t string
|
||||||
|
var kt string
|
||||||
|
var ok bool
|
||||||
|
var pemBlock *pem.Block
|
||||||
|
var keybuf *bytes.Buffer = new(bytes.Buffer)
|
||||||
|
|
||||||
|
// Load in any existing keys.
|
||||||
|
for _, t = range pairTypes {
|
||||||
|
for _, kt = range keyTypes {
|
||||||
|
log.Printf("Loading %s key %s\n", t, kt)
|
||||||
|
if b, err = pems.ReadFile(getKeyFpath(t, kt)); err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
// Will generate missing below
|
||||||
|
pairs[t] = &Pair{
|
||||||
|
pairType: t,
|
||||||
|
keyBytes: make(map[string][]byte),
|
||||||
|
privKeys: make(map[string]crypto.PrivateKey),
|
||||||
|
certBytes: make(map[string][]byte),
|
||||||
|
certs: make(map[string]*x509.Certificate),
|
||||||
|
csrBytes: make(map[string][]byte),
|
||||||
|
csrs: make(map[string]*x509.CertificateRequest),
|
||||||
|
chainParentBytes: make(map[string][]byte),
|
||||||
|
chainParent: make(map[string]*x509.Certificate),
|
||||||
|
}
|
||||||
|
err = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, ok = pairs[t]; !ok {
|
||||||
|
pairs[t] = &Pair{
|
||||||
|
pairType: t,
|
||||||
|
keyBytes: make(map[string][]byte),
|
||||||
|
privKeys: make(map[string]crypto.PrivateKey),
|
||||||
|
certBytes: make(map[string][]byte),
|
||||||
|
certs: make(map[string]*x509.Certificate),
|
||||||
|
csrBytes: make(map[string][]byte),
|
||||||
|
csrs: make(map[string]*x509.CertificateRequest),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pairs[t].keyBytes[kt] = b
|
||||||
|
if pairs[t].privKeys[kt], err = x509.ParsePKCS8PrivateKey(b); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Generate any missing keys.
|
||||||
|
for _, t = range pairTypes {
|
||||||
|
for _, kt = range keyTypes {
|
||||||
|
if _, ok = pairs[t].privKeys[kt]; !ok {
|
||||||
|
log.Printf("Generating %s key %s\n", t, kt)
|
||||||
|
keybuf.Reset()
|
||||||
|
switch kt {
|
||||||
|
case "ecdh":
|
||||||
|
if pairs[t].privKeys[kt], err = ecdh.X25519().GenerateKey(rand.Reader); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case "ecdsa":
|
||||||
|
if pairs[t].privKeys[kt], err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case "ed25519":
|
||||||
|
if _, pairs[t].privKeys[kt], err = ed25519.GenerateKey(rand.Reader); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case "rsa":
|
||||||
|
if pairs[t].privKeys[kt], err = rsa.GenerateKey(rand.Reader, 4096); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if b, err = x509.MarshalPKCS8PrivateKey(pairs[t].privKeys[kt]); err != nil {
|
||||||
|
log.Panicln(err)
|
||||||
|
}
|
||||||
|
pemBlock = &pem.Block{
|
||||||
|
Type: "PRIVATE KEY",
|
||||||
|
Headers: nil,
|
||||||
|
Bytes: b,
|
||||||
|
}
|
||||||
|
b = pem.EncodeToMemory(pemBlock)
|
||||||
|
pairs[t].keyBytes[kt] = b
|
||||||
|
if err = os.WriteFile(getKeyFpath(t, kt), b, 0o0600); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadCerts combines all loadCert* functions in the proper order. It is expected that loadKeys has already been run.
|
||||||
|
func loadCerts() (err error) {
|
||||||
|
|
||||||
|
var b []byte
|
||||||
|
var t string
|
||||||
|
var kt string
|
||||||
|
var tkt [2]string
|
||||||
|
var chainMissing [][2]string = make([][2]string, 0, (len(certgenOrder)-1)*len(keyTypes))
|
||||||
|
|
||||||
|
if err = loadCertCa(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = loadCertIssued(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// And create chained certs of leaves so they fully validate.
|
||||||
|
for _, t = range certgenOrder {
|
||||||
|
if t == "inter" {
|
||||||
|
continue // Don't bother with chaining the intermediate. If we play around with multiple intermediates, we will.
|
||||||
|
}
|
||||||
|
for _, kt = range keyTypes {
|
||||||
|
pairs[t].chainParent[kt] = pairs[parents[t]].certs[kt]
|
||||||
|
log.Printf("Loading %s chain %s\n", t, kt)
|
||||||
|
if b, err = pems.ReadFile(getChainFpath(t, kt)); err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
err = nil
|
||||||
|
chainMissing = append(chainMissing, [2]string{t, kt})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Found
|
||||||
|
pairs[t].chainParentBytes[kt] = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Generate" missing.
|
||||||
|
for _, tkt = range chainMissing {
|
||||||
|
t = tkt[0]
|
||||||
|
kt = tkt[1]
|
||||||
|
log.Printf("Building %s chain %s\n", t, kt)
|
||||||
|
b = append(pairs[t].certBytes[kt], pairs[parents[t]].certBytes[kt]...)
|
||||||
|
if err = os.WriteFile(getChainFpath(t, kt), b, 0o0600); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadCertCa loads (or generates) the root CA/anchor. It is expected that loadKeys has already been run.
|
||||||
|
func loadCertCa() (err error) {
|
||||||
|
|
||||||
|
var b []byte
|
||||||
|
var kt string
|
||||||
|
var ok bool
|
||||||
|
var privKey crypto.PrivateKey
|
||||||
|
var pubKey crypto.PublicKey
|
||||||
|
var pemBlock *pem.Block
|
||||||
|
var ktTpl *x509.Certificate
|
||||||
|
|
||||||
|
for _, kt = range keyTypes {
|
||||||
|
log.Printf("Loading CA certificate %s\n", kt)
|
||||||
|
if b, err = pems.ReadFile(getCertFpath("ca", kt)); err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
// Will generate missing below.
|
||||||
|
err = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Assume the mapped Pair exists per loadKeys.
|
||||||
|
pairs["ca"].certBytes[kt] = b
|
||||||
|
pemBlock, _ = pem.Decode(b)
|
||||||
|
if pairs["ca"].certs[kt], err = x509.ParseCertificate(pemBlock.Bytes); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate missing CA certs.
|
||||||
|
for _, kt = range keyTypes {
|
||||||
|
log.Printf("Generating CA certificate %s\n", kt)
|
||||||
|
if _, ok = pairs["ca"].certs[kt]; !ok {
|
||||||
|
ktTpl = getTpl(certTpl["ca"])
|
||||||
|
privKey, pubKey = getKeypair("ca", kt)
|
||||||
|
// Specifying the same cert template for both the template and parent params creates a self-signed.
|
||||||
|
if b, err = x509.CreateCertificate(
|
||||||
|
rand.Reader,
|
||||||
|
ktTpl,
|
||||||
|
ktTpl,
|
||||||
|
pubKey,
|
||||||
|
privKey,
|
||||||
|
); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if pairs["ca"].certs[kt], err = x509.ParseCertificate(b); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pemBlock = &pem.Block{
|
||||||
|
Type: "CERTIFICATE",
|
||||||
|
Headers: nil,
|
||||||
|
Bytes: b,
|
||||||
|
}
|
||||||
|
b = pem.EncodeToMemory(pemBlock)
|
||||||
|
pairs["ca"].certBytes[kt] = b
|
||||||
|
if err = os.WriteFile(getCertFpath("ca", kt), b, 0o0600); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadCertIssued handles the intermediate, "server" leaf, and "user" leaf.
|
||||||
|
func loadCertIssued() (err error) {
|
||||||
|
|
||||||
|
var b []byte
|
||||||
|
var ok bool
|
||||||
|
var t string
|
||||||
|
var kt string
|
||||||
|
var tkt [2]string
|
||||||
|
var ktMap map[string]bool
|
||||||
|
var caCert *x509.Certificate
|
||||||
|
var caPrivKey crypto.PrivateKey
|
||||||
|
var certPrivKey crypto.PrivateKey
|
||||||
|
var certPubKey crypto.PublicKey
|
||||||
|
var pemBlock *pem.Block
|
||||||
|
var ktTpl *x509.Certificate
|
||||||
|
// map[<t>][<kt>]; map so we can condense dupes
|
||||||
|
var certMissing map[string]map[string]bool = make(map[string]map[string]bool)
|
||||||
|
var csrMissing [][2]string = make([][2]string, 0, len(certgenOrder)*len(keyTypes))
|
||||||
|
|
||||||
|
// CSRS
|
||||||
|
// Find existing CSRs and certs
|
||||||
|
for _, t = range certgenOrder {
|
||||||
|
for _, kt = range keyTypes {
|
||||||
|
log.Printf("Loading %s CSR %s\n", t, kt)
|
||||||
|
if b, err = pems.ReadFile(getCsrFpath(t, kt)); err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
err = nil
|
||||||
|
csrMissing = append(csrMissing, [2]string{t, kt})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Assume the mapped Pair exists per loadKeys.
|
||||||
|
pairs[t].csrBytes[kt] = b
|
||||||
|
pemBlock, _ = pem.Decode(b)
|
||||||
|
if pairs[t].csrs[kt], err = x509.ParseCertificateRequest(pemBlock.Bytes); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("Loading %s certificate %s\n", t, kt)
|
||||||
|
if b, err = pems.ReadFile(getCertFpath(t, kt)); err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
err = nil
|
||||||
|
if _, ok = certMissing[t]; !ok {
|
||||||
|
certMissing[t] = make(map[string]bool)
|
||||||
|
}
|
||||||
|
certMissing[t][kt] = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pairs[t].certBytes[kt] = b
|
||||||
|
pemBlock, _ = pem.Decode(b)
|
||||||
|
if pairs[t].certs[kt], err = x509.ParseCertificate(pemBlock.Bytes); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate missing CSRs.
|
||||||
|
for _, tkt = range csrMissing {
|
||||||
|
t = tkt[0]
|
||||||
|
kt = tkt[1]
|
||||||
|
log.Printf("Generating %s CSR %s\n", t, kt)
|
||||||
|
certPrivKey, certPubKey = getKeypair(t, kt)
|
||||||
|
if b, err = x509.CreateCertificateRequest(rand.Reader, csrs[t], certPrivKey); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if pairs[t].csrs[kt], err = x509.ParseCertificateRequest(b); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pemBlock = &pem.Block{
|
||||||
|
Type: "CERTIFICATE REQUEST",
|
||||||
|
Headers: nil,
|
||||||
|
Bytes: b,
|
||||||
|
}
|
||||||
|
b = pem.EncodeToMemory(pemBlock)
|
||||||
|
pairs[t].csrBytes[kt] = b
|
||||||
|
if err = os.WriteFile(getCsrFpath(t, kt), b, 0o0600); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, ok = certMissing[t]; !ok {
|
||||||
|
certMissing[t] = make(map[string]bool)
|
||||||
|
}
|
||||||
|
certMissing[t][kt] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force re-gen of certs for above new CSRs and gen missing.
|
||||||
|
for _, t = range certgenOrder {
|
||||||
|
if ktMap, ok = certMissing[t]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for kt, _ = range ktMap {
|
||||||
|
log.Printf("Generating %s certificate %s\n", t, kt)
|
||||||
|
caCert = pairs[parents[t]].certs[kt]
|
||||||
|
caPrivKey = pairs[parents[t]].privKeys[kt]
|
||||||
|
_, certPubKey = getKeypair(t, kt)
|
||||||
|
ktTpl = getTpl(certTpl[t])
|
||||||
|
if b, err = x509.CreateCertificate(
|
||||||
|
rand.Reader,
|
||||||
|
ktTpl,
|
||||||
|
caCert,
|
||||||
|
certPubKey,
|
||||||
|
caPrivKey,
|
||||||
|
); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if pairs[t].certs[kt], err = x509.ParseCertificate(b); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pemBlock = &pem.Block{
|
||||||
|
Type: "CERTIFICATE",
|
||||||
|
Headers: nil,
|
||||||
|
Bytes: b,
|
||||||
|
}
|
||||||
|
b = pem.EncodeToMemory(pemBlock)
|
||||||
|
pairs[t].certBytes[kt] = b
|
||||||
|
if err = os.WriteFile(getCertFpath(t, kt), b, 0o0600); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
9
_extra/gen_test_pki/go.mod
Normal file
9
_extra/gen_test_pki/go.mod
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module r00t2.io/cryptparse/_extra/gen_test_pki
|
||||||
|
|
||||||
|
go 1.23.2
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/brunoga/deep v1.2.4
|
||||||
|
github.com/davecgh/go-spew v1.1.1
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
)
|
6
_extra/gen_test_pki/go.sum
Normal file
6
_extra/gen_test_pki/go.sum
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
github.com/brunoga/deep v1.2.4 h1:Aj9E9oUbE+ccbyh35VC/NHlzzjfIVU69BXu2mt2LmL8=
|
||||||
|
github.com/brunoga/deep v1.2.4/go.mod h1:GDV6dnXqn80ezsLSZ5Wlv1PdKAWAO4L5PnKYtv2dgaI=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
21
_extra/gen_test_pki/init.go
Normal file
21
_extra/gen_test_pki/init.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
`log`
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Keys need to be loaded/generated first for everything.
|
||||||
|
if err = loadKeys(); err != nil {
|
||||||
|
log.Panicln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then the certificates.
|
||||||
|
if err = loadCerts(); err != nil {
|
||||||
|
log.Panicln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
13
_extra/gen_test_pki/main.go
Normal file
13
_extra/gen_test_pki/main.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
`fmt`
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// Everything's handled in init()
|
||||||
|
|
||||||
|
fmt.Println("Done.")
|
||||||
|
|
||||||
|
}
|
18
_extra/gen_test_pki/types.go
Normal file
18
_extra/gen_test_pki/types.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
`crypto`
|
||||||
|
`crypto/x509`
|
||||||
|
)
|
||||||
|
|
||||||
|
type Pair struct {
|
||||||
|
pairType string // see pairTypes
|
||||||
|
keyBytes map[string][]byte // see keyTypes for keys for this map
|
||||||
|
privKeys map[string]crypto.PrivateKey // see keyTypes for keys for this map
|
||||||
|
certBytes map[string][]byte // see keyTypes for keys for this map
|
||||||
|
certs map[string]*x509.Certificate // see keyTypes for keys for this map
|
||||||
|
csrs map[string]*x509.CertificateRequest // see keyTypes for keys for this map; does not exist for ca
|
||||||
|
csrBytes map[string][]byte // see keyTypes for keys for this map; does not exist for ca
|
||||||
|
chainParentBytes map[string][]byte
|
||||||
|
chainParent map[string]*x509.Certificate
|
||||||
|
}
|
0
_testdata/.keep
Normal file
0
_testdata/.keep
Normal file
208
consts.go
208
consts.go
@ -12,6 +12,10 @@ var (
|
|||||||
tlsCurveNmToCurve map[string]tls.CurveID
|
tlsCurveNmToCurve map[string]tls.CurveID
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dfltStructTag string = "tlsUri"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MaxTlsCipher uint16 = tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
MaxTlsCipher uint16 = tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||||
MaxCurveId tls.CurveID = tls.X25519 // 29
|
MaxCurveId tls.CurveID = tls.X25519 // 29
|
||||||
@ -20,50 +24,56 @@ const (
|
|||||||
DefaultNetType string = "tcp"
|
DefaultNetType string = "tcp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TlsUriParam* specifiy URL query parameters to parse a tls:// URI, and are used by TlsUri methods.
|
const (
|
||||||
|
// KeyLogEnv specifies the TLS key log file.
|
||||||
|
// !! ONLY USE THIS FOR DEBUGGING !!
|
||||||
|
KeyLogEnv string = "SSLKEYLOGFILE"
|
||||||
|
/*
|
||||||
|
KeyLogEnvVal specifies the special ParamKeylog value to use the
|
||||||
|
value of the environment variable as named in KeyLogEnv.
|
||||||
|
*/
|
||||||
|
KeyLogEnvVal string = "_env_"
|
||||||
|
// KeyLogBufVal specifies the special ParamKeylog value to use an in-memory buffer.
|
||||||
|
KeyLogBufVal string = "_buf_"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run ./internal/constmap
|
||||||
|
|
||||||
|
/*
|
||||||
|
TlsUriParam* specifiy URL query parameters to parse a tls:// URI, and are used by TlsUri methods.
|
||||||
|
|
||||||
|
NOTE: If these consts' type or "Param*" prefix changes, internal/constmap/consts.go will also need to be changed.
|
||||||
|
The above go:generate creates (within this main module namespace):
|
||||||
|
tlsUriParamStrMap (a map of string(<tlsUriParam> const name) => <parameter>)
|
||||||
|
tlsUriStrParamMap (a map of <parameter> => string(<tlsUriParam> const name))
|
||||||
|
*/
|
||||||
const (
|
const (
|
||||||
/*
|
/*
|
||||||
TlsUriParamCa specifies a path to a CA certificate PEM-encded DER file.
|
ParamCa specifies a path to a CA certificate PEM-encded DER file.
|
||||||
|
|
||||||
|
If not specified, the system's roots/trust anchors are used.
|
||||||
|
Files specified here will be included *in addition to* any
|
||||||
|
embedded root anchors found in the ParamCert parameter's
|
||||||
|
value, if using concatenated cert chains.
|
||||||
|
|
||||||
It may be specified multiple times in a TLS URI.
|
It may be specified multiple times in a TLS URI.
|
||||||
*/
|
*/
|
||||||
TlsUriParamCa string = "pki_ca"
|
ParamCa tlsUriParam = "pki_ca"
|
||||||
/*
|
/*
|
||||||
TlsUriParamCert specifies a path to a client certificate PEM-encded DER file.
|
ParamCert specifies a path to a leaf certificate PEM-encded DER file.
|
||||||
|
|
||||||
|
It may (and should/must) include any intermediate certificates necessary for
|
||||||
|
validation chain on the remote end.
|
||||||
|
It may include trust anchors, which will be considered *in addition to*
|
||||||
|
any ParamCa.
|
||||||
|
It may include a corresponding private key, which will be included for consideration
|
||||||
|
*in addition to* any ParamKey.
|
||||||
|
|
||||||
It may be specified multiple times in a TLS URI.
|
It may be specified multiple times in a TLS URI.
|
||||||
*/
|
*/
|
||||||
TlsUriParamCert string = "pki_cert"
|
ParamCert tlsUriParam = "pki_cert"
|
||||||
/*
|
/*
|
||||||
TlsUriParamKey specifies a path to a private key as a PEM-encded file.
|
ParamCipher specifies one (or more) cipher(s)
|
||||||
|
|
||||||
It may be PKCS#1, PKCS#8, or PEM-encoded ASN.1 DER EC key.
|
|
||||||
|
|
||||||
Supported private key types are RSA, ED25519, ECDSA, and ECDH.
|
|
||||||
|
|
||||||
It may be specified multiple times in a TLS URI.
|
|
||||||
*/
|
|
||||||
TlsUriParamKey string = "pki_key"
|
|
||||||
/*
|
|
||||||
TlsUriParamNoVerify, if `1`, `yes`, `y`, or `true` indicate
|
|
||||||
that the TLS connection should not require verification of
|
|
||||||
the remote end (e.g. hostname matches, trusted chain, etc.).
|
|
||||||
|
|
||||||
Any other value for this parameter will be parsed as "False"
|
|
||||||
(meaning the remote end's certificate SHOULD be verified).
|
|
||||||
|
|
||||||
Only the first defined instance is parsed.
|
|
||||||
*/
|
|
||||||
TlsUriParamNoVerify string = "no_verify"
|
|
||||||
/*
|
|
||||||
TlsUriParamSni indicates that the TLS connection should expect this hostname
|
|
||||||
instead of the hostname specified in the URI itself.
|
|
||||||
|
|
||||||
Only the first defined instance is parsed.
|
|
||||||
*/
|
|
||||||
TlsUriParamSni string = "sni"
|
|
||||||
/*
|
|
||||||
TlsUriParamCipher specifies one (or more) cipher(s)
|
|
||||||
to specify for the TLS connection cipher negotiation.
|
to specify for the TLS connection cipher negotiation.
|
||||||
Note that TLS 1.3 has a fixed set of ciphers, and
|
Note that TLS 1.3 has a fixed set of ciphers, and
|
||||||
this list may not be respected by the remote end.
|
this list may not be respected by the remote end.
|
||||||
@ -74,16 +84,82 @@ const (
|
|||||||
|
|
||||||
It may be specified multiple times in a TLS URI.
|
It may be specified multiple times in a TLS URI.
|
||||||
*/
|
*/
|
||||||
TlsUriParamCipher string = "cipher"
|
ParamCipher tlsUriParam = "cipher"
|
||||||
/*
|
/*
|
||||||
TlsUriParamCurve specifies one (or more) curve(s)
|
ParamCurve specifies one (or more) curve(s)
|
||||||
to specify for the TLS connection cipher negotiation.
|
to specify for the TLS connection cipher negotiation.
|
||||||
|
|
||||||
It may be specified multiple times in a TLS URI.
|
It may be specified multiple times in a TLS URI.
|
||||||
*/
|
*/
|
||||||
TlsUriParamCurve string = "curve"
|
ParamCurve tlsUriParam = "curve"
|
||||||
/*
|
/*
|
||||||
TlsUriParamMinTls defines the minimum version of the
|
ParamIgnoreMissing, if `1`, `yes`, `y`, or `true` indicates
|
||||||
|
that missing cert/ca/key files should not return an error if they do not exist.
|
||||||
|
|
||||||
|
Only the first defined instance is parsed.
|
||||||
|
*/
|
||||||
|
ParamIgnoreMissing tlsUriParam = "ignore_missing"
|
||||||
|
/*
|
||||||
|
ParamKey specifies a path to a private key as a PEM-encded file.
|
||||||
|
|
||||||
|
It may be PKCS#1, PKCS#8, or PEM-encoded ASN.1 DER EC key.
|
||||||
|
|
||||||
|
Supported private key types are RSA, ED25519, and ECDSA.
|
||||||
|
(Technically ECDH keys are supported, but cannot be paired with certificates
|
||||||
|
so trying to use them may result in errors or undefined behavior.
|
||||||
|
Future versions may support this as a parameter for *kex* in a TLS
|
||||||
|
*connection*, but this is unplanned at the moment.)
|
||||||
|
|
||||||
|
It may be specified multiple times in a TLS URI.
|
||||||
|
*/
|
||||||
|
ParamKey tlsUriParam = "pki_key"
|
||||||
|
/*
|
||||||
|
ParamKeylog is a way to specify the SSLKEYLOGFILE.
|
||||||
|
|
||||||
|
This parameter's value can be:
|
||||||
|
|
||||||
|
* a filepath; parent directories will attempt to be created if
|
||||||
|
they do not exist, and the file will be truncated if it exists.
|
||||||
|
The consumer/downstream is responsible for calling .Close()
|
||||||
|
on it when done.
|
||||||
|
* the special string as defined by KeyLogEnvVal to use the filepath
|
||||||
|
of whatever is set in the environment variable as defined by
|
||||||
|
KeyLogEnv (which is likely the default variable name).
|
||||||
|
It is assumed to be a filepath.
|
||||||
|
The consumer/downstream is responsible for calling .Close()
|
||||||
|
on it when done.
|
||||||
|
* the special string as defined by KeyLogBufVal to use an in-memory
|
||||||
|
buffer instead
|
||||||
|
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
!! DO NOT, UNDER ANY CIRCUMSTANCES, ENABLE THIS UNLESS YOU ARE !!
|
||||||
|
!! ABSOLUTELY SURE WHAT YOU ARE DOING. !!
|
||||||
|
!! IT SEVERELY COMPROMISES SECURITY !!
|
||||||
|
!! AND IS ONLY INTENDED FOR DEBUGGING PURPOSES! !!
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
|
||||||
|
See https://www.ietf.org/archive/id/draft-thomson-tls-keylogfile-00.html
|
||||||
|
for details.
|
||||||
|
|
||||||
|
The exact type of the returned tls.Config.KeyLogWriter will be:
|
||||||
|
|
||||||
|
* an *os.File if a filepath or the KeyLogEnvVal value was specified
|
||||||
|
* a *bytes.Buffer if the KeyLogBufVal value was specified
|
||||||
|
|
||||||
|
Only the first defined instance is parsed.
|
||||||
|
*/
|
||||||
|
ParamKeylog tlsUriParam = "debug_keylog"
|
||||||
|
/*
|
||||||
|
ParamMaxTls defines the minimum version of the
|
||||||
|
TLS protocol to use.
|
||||||
|
|
||||||
|
See ParamMinTls for syntax of the value.
|
||||||
|
|
||||||
|
Only the first defined instance is parsed.
|
||||||
|
*/
|
||||||
|
ParamMaxTls tlsUriParam = "max_tls"
|
||||||
|
/*
|
||||||
|
ParamMinTls defines the minimum version of the
|
||||||
TLS protocol to use.
|
TLS protocol to use.
|
||||||
It is recommended to use "TLS_1.3".
|
It is recommended to use "TLS_1.3".
|
||||||
|
|
||||||
@ -101,18 +177,36 @@ const (
|
|||||||
|
|
||||||
Only the first defined instance is parsed.
|
Only the first defined instance is parsed.
|
||||||
*/
|
*/
|
||||||
TlsUriParamMinTls string = "min_tls"
|
ParamMinTls tlsUriParam = "min_tls"
|
||||||
/*
|
/*
|
||||||
TlsUriParamMaxTls defines the minimum version of the
|
ParamMtlsCa specifies a path to a CA certificate PEM-encoded DER file.
|
||||||
TLS protocol to use.
|
|
||||||
|
|
||||||
See TlsUriParamMinTls for syntax of the value.
|
Unlike ParamCa, this is explicitly used to validate clients
|
||||||
|
(see ParamMtlsMode).
|
||||||
|
|
||||||
Only the first defined instance is parsed.
|
If not specified (and ParamMtlsMode is anything *but* `NoClientCert`/`0`),
|
||||||
|
the same evaluated roots/trust anchors used for ParamCa will be used.
|
||||||
|
|
||||||
|
It may be specified multiple times in a TLS URI.
|
||||||
*/
|
*/
|
||||||
TlsUriParamMaxTls string = "max_tls"
|
ParamMtlsCa tlsUriParam = "mtls_ca"
|
||||||
/*
|
/*
|
||||||
TlsUriParamNet is used by TlsUri.ToConn and TlsUri.ToTlsConn to explicitly specify a network.
|
ParamMtlsMode specifies if TLS client certificate auth should be used or not,
|
||||||
|
and what mode/type of requirement.
|
||||||
|
This is only useful if running a server/listener with mTLS auth.
|
||||||
|
Clients should leave it empty.
|
||||||
|
Servers/listeners should leave it empty if they do not wish to use
|
||||||
|
mTLS auth for clients.
|
||||||
|
|
||||||
|
The string may either be the name (as per
|
||||||
|
https://pkg.go.dev/crypto/tls#ClientAuthType)
|
||||||
|
or an int (normal, hex, etc. string representation) of the constant's value.
|
||||||
|
|
||||||
|
See also ParamMtlsCa.
|
||||||
|
*/
|
||||||
|
ParamMtlsMode tlsUriParam = "mtls_auth"
|
||||||
|
/*
|
||||||
|
ParamNet is used by TlsUri.ToConn and TlsUri.ToTlsConn to explicitly specify a network.
|
||||||
|
|
||||||
The default is "tcp".
|
The default is "tcp".
|
||||||
|
|
||||||
@ -120,15 +214,33 @@ const (
|
|||||||
|
|
||||||
Only the first defined instance is parsed.
|
Only the first defined instance is parsed.
|
||||||
*/
|
*/
|
||||||
TlsUriParamNet string = "net"
|
ParamNet tlsUriParam = "net"
|
||||||
|
/*
|
||||||
|
ParamNoVerify, if `1`, `yes`, `y`, or `true` indicates
|
||||||
|
that the TLS connection should not require verification of
|
||||||
|
the remote end (e.g. hostname matches, trusted chain, etc.).
|
||||||
|
|
||||||
|
Any other value for this parameter will be parsed as "False"
|
||||||
|
(meaning the remote end's certificate SHOULD be verified).
|
||||||
|
|
||||||
|
Only the first defined instance is parsed.
|
||||||
|
*/
|
||||||
|
ParamNoVerify tlsUriParam = "no_verify"
|
||||||
|
/*
|
||||||
|
ParamSni indicates that the TLS connection should expect this hostname
|
||||||
|
instead of the hostname specified in the URI itself.
|
||||||
|
|
||||||
|
Only the first defined instance is parsed.
|
||||||
|
*/
|
||||||
|
ParamSni tlsUriParam = "sni"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
paramBoolValsTrue []string = []string{
|
paramBoolValsTrue []string = []string{
|
||||||
"1", "yes", "y", "true",
|
"true", "yes", "y", "1",
|
||||||
}
|
}
|
||||||
paramBoolValsFalse []string = []string{
|
paramBoolValsFalse []string = []string{
|
||||||
"0", "no", "n", "false",
|
"false", "no", "n", "0",
|
||||||
}
|
}
|
||||||
validate *validator.Validate = validator.New(validator.WithRequiredStructEnabled())
|
validate *validator.Validate = validator.New(validator.WithRequiredStructEnabled())
|
||||||
)
|
)
|
||||||
|
45
consts_param_map.go
Normal file
45
consts_param_map.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package cryptparse
|
||||||
|
|
||||||
|
/*
|
||||||
|
THIS FILE IS AUTOMATICALLY GENERATED.
|
||||||
|
DO NOT EDIT.
|
||||||
|
SEE internal/constmap/ FOR DETAILS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var (
|
||||||
|
// tlsUriParamStrMap contains a map of the constant string *name* of a tlsUriParam as mapped to its *value* (at time of generation).
|
||||||
|
tlsUriParamStrMap map[string]string = map[string]string{
|
||||||
|
"ParamCa": "pki_ca",
|
||||||
|
"ParamCert": "pki_cert",
|
||||||
|
"ParamCipher": "cipher",
|
||||||
|
"ParamCurve": "curve",
|
||||||
|
"ParamIgnoreMissing": "ignore_missing",
|
||||||
|
"ParamKey": "pki_key",
|
||||||
|
"ParamKeylog": "debug_keylog",
|
||||||
|
"ParamMaxTls": "max_tls",
|
||||||
|
"ParamMinTls": "min_tls",
|
||||||
|
"ParamMtlsCa": "mtls_ca",
|
||||||
|
"ParamMtlsMode": "mtls_auth",
|
||||||
|
"ParamNet": "net",
|
||||||
|
"ParamNoVerify": "no_verify",
|
||||||
|
"ParamSni": "sni",
|
||||||
|
}
|
||||||
|
|
||||||
|
// tlsUriStrParamMap contains a map of the *value* (at time of generation) of tlsUriParam constants to the constant string *name*.
|
||||||
|
tlsUriStrParamMap map[string]string = map[string]string{
|
||||||
|
"pki_ca": "ParamCa",
|
||||||
|
"pki_cert": "ParamCert",
|
||||||
|
"cipher": "ParamCipher",
|
||||||
|
"curve": "ParamCurve",
|
||||||
|
"ignore_missing": "ParamIgnoreMissing",
|
||||||
|
"pki_key": "ParamKey",
|
||||||
|
"debug_keylog": "ParamKeylog",
|
||||||
|
"max_tls": "ParamMaxTls",
|
||||||
|
"min_tls": "ParamMinTls",
|
||||||
|
"mtls_ca": "ParamMtlsCa",
|
||||||
|
"mtls_auth": "ParamMtlsMode",
|
||||||
|
"net": "ParamNet",
|
||||||
|
"no_verify": "ParamNoVerify",
|
||||||
|
"sni": "ParamSni",
|
||||||
|
}
|
||||||
|
)
|
4
errs.go
4
errs.go
@ -5,9 +5,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
ErrBadMtlsMode error = errors.New("invalid TLS client cert auth/mTLS mode/type")
|
||||||
|
ErrBadPortRange error = errors.New("invalid port; must be between 0 and 65535")
|
||||||
ErrBadTlsCipher error = errors.New("invalid TLS cipher suite")
|
ErrBadTlsCipher error = errors.New("invalid TLS cipher suite")
|
||||||
ErrBadTlsCurve error = errors.New("invalid TLS curve")
|
ErrBadTlsCurve error = errors.New("invalid TLS curve")
|
||||||
ErrBadTlsVer error = errors.New("invalid TLS version")
|
ErrBadTlsVer error = errors.New("invalid TLS version")
|
||||||
|
ErrReqParams error = errors.New("a nil/uninitialized requestUriParams was passed")
|
||||||
|
ErrReqStruct error = errors.New("invalid type; a pointer to a struct is required")
|
||||||
ErrUnknownCipher error = errors.New("unknown TLS cipher")
|
ErrUnknownCipher error = errors.New("unknown TLS cipher")
|
||||||
ErrUnknownKey error = errors.New("unknown key type")
|
ErrUnknownKey error = errors.New("unknown key type")
|
||||||
)
|
)
|
||||||
|
234
funcs.go
234
funcs.go
@ -11,11 +11,14 @@ import (
|
|||||||
`crypto/x509`
|
`crypto/x509`
|
||||||
`encoding/pem`
|
`encoding/pem`
|
||||||
`errors`
|
`errors`
|
||||||
|
`io`
|
||||||
`net/url`
|
`net/url`
|
||||||
`os`
|
`os`
|
||||||
|
`path/filepath`
|
||||||
`strconv`
|
`strconv`
|
||||||
`strings`
|
`strings`
|
||||||
|
|
||||||
|
`r00t2.io/sysutils/envs`
|
||||||
`r00t2.io/sysutils/paths`
|
`r00t2.io/sysutils/paths`
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -85,6 +88,60 @@ func IsMatchedPair(privKey crypto.PrivateKey, cert *x509.Certificate) (isMatched
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ParseMtlsMode parses string s and attempts to derive a TLS client certificate
|
||||||
|
auth mode from it.
|
||||||
|
|
||||||
|
The string may either be the name (as per https://pkg.go.dev/crypto/tls#ClientAuthType)
|
||||||
|
or an int (normal, hex, etc. string representation).
|
||||||
|
*/
|
||||||
|
func ParseMtlsMode(s string) (mode tls.ClientAuthType, err error) {
|
||||||
|
|
||||||
|
var nm string
|
||||||
|
var n int64
|
||||||
|
var m tls.ClientAuthType
|
||||||
|
|
||||||
|
if n, err = strconv.ParseInt(s, 10, 64); err != nil {
|
||||||
|
if errors.Is(err, strconv.ErrSyntax) {
|
||||||
|
// It's a name; parse below.
|
||||||
|
err = nil
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// It's a number.
|
||||||
|
m = tls.ClientAuthType(n)
|
||||||
|
if !strings.HasPrefix(m.String(), "ClientAuthType(") {
|
||||||
|
// It's valid; send it.
|
||||||
|
mode = m
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
// It's invalid.
|
||||||
|
err = ErrBadMtlsMode
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's a name. First normalize the string so we don't need to do so many transforms.
|
||||||
|
nm = strings.ToLower(strings.TrimSpace(s))
|
||||||
|
// Then keep going until we either find it or we run out of valid auth types.
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
m = tls.ClientAuthType(i)
|
||||||
|
if strings.ToLower(m.String()) == nm {
|
||||||
|
// Found; send it.
|
||||||
|
mode = m
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(m.String(), "ClientAuthType(") {
|
||||||
|
// We've reached the end of valid auth names and it still wasn't found.
|
||||||
|
err = ErrBadMtlsMode
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ParseTlsCipher parses string s and attempts to derive a TLS cipher suite (as a uint16) from it.
|
ParseTlsCipher parses string s and attempts to derive a TLS cipher suite (as a uint16) from it.
|
||||||
Use ParseTlsCipherSuite if you wish for a tls.CipherSuite instead.
|
Use ParseTlsCipherSuite if you wish for a tls.CipherSuite instead.
|
||||||
@ -403,34 +460,42 @@ func ParseTlsUri(tlsUri *url.URL) (tlsConf *tls.Config, err error) {
|
|||||||
|
|
||||||
var b []byte
|
var b []byte
|
||||||
var rootCAs *x509.CertPool
|
var rootCAs *x509.CertPool
|
||||||
|
var mtlsCAs *x509.CertPool
|
||||||
var intermediateCAs []*x509.Certificate
|
var intermediateCAs []*x509.Certificate
|
||||||
|
var concatCAs []*x509.Certificate
|
||||||
var privKeys []crypto.PrivateKey
|
var privKeys []crypto.PrivateKey
|
||||||
var tlsCerts []tls.Certificate
|
var tlsCerts []tls.Certificate
|
||||||
var allowInvalid bool
|
var allowInvalid bool
|
||||||
var ciphers []uint16
|
var ciphers []uint16
|
||||||
var curves []tls.CurveID
|
var curves []tls.CurveID
|
||||||
var params map[string][]string
|
|
||||||
var ok bool
|
var ok bool
|
||||||
var val string
|
var val string
|
||||||
var minVer uint16
|
var minVer uint16
|
||||||
var maxVer uint16
|
var maxVer uint16
|
||||||
|
var ignoreMissing bool
|
||||||
|
var keylog io.Writer
|
||||||
|
var clientAuthType tls.ClientAuthType
|
||||||
|
var params map[tlsUriParam][]string = make(map[tlsUriParam][]string)
|
||||||
var buf *bytes.Buffer = new(bytes.Buffer)
|
var buf *bytes.Buffer = new(bytes.Buffer)
|
||||||
var srvNm string = tlsUri.Hostname()
|
var srvNm string = tlsUri.Hostname()
|
||||||
|
|
||||||
params = tlsUri.Query()
|
if tlsUri.Query() == nil || len(tlsUri.Query()) == 0 {
|
||||||
|
|
||||||
if params == nil {
|
|
||||||
tlsConf = &tls.Config{
|
tlsConf = &tls.Config{
|
||||||
ServerName: srvNm,
|
ServerName: srvNm,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for k, v := range tlsUri.Query() {
|
||||||
|
params[tlsUriParam(k)] = v
|
||||||
|
}
|
||||||
|
|
||||||
// These are all filepath(s).
|
// These are all filepath(s).
|
||||||
for _, k := range []string{
|
for _, k := range []tlsUriParam{
|
||||||
TlsUriParamCa,
|
ParamCa,
|
||||||
TlsUriParamCert,
|
ParamCert,
|
||||||
TlsUriParamKey,
|
ParamKey,
|
||||||
|
ParamMtlsCa,
|
||||||
} {
|
} {
|
||||||
if _, ok = params[k]; ok {
|
if _, ok = params[k]; ok {
|
||||||
for idx, _ := range params[k] {
|
for idx, _ := range params[k] {
|
||||||
@ -441,15 +506,59 @@ func ParseTlsUri(tlsUri *url.URL) (tlsConf *tls.Config, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, ok = params[ParamIgnoreMissing]; ok {
|
||||||
|
val = strings.ToLower(params[ParamIgnoreMissing][0])
|
||||||
|
for _, i := range paramBoolValsTrue {
|
||||||
|
if i == val {
|
||||||
|
ignoreMissing = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This *might* be a filepath.
|
||||||
|
if _, ok = params[ParamKeylog]; ok {
|
||||||
|
switch params[ParamKeylog][0] {
|
||||||
|
case KeyLogBufVal:
|
||||||
|
keylog = new(bytes.Buffer)
|
||||||
|
case KeyLogEnvVal:
|
||||||
|
val = params[ParamKeylog][0]
|
||||||
|
if envs.HasEnv(val) {
|
||||||
|
val = os.Getenv(val)
|
||||||
|
if err = paths.RealPath(&val); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = os.MkdirAll(filepath.Dir(val), 0700); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if keylog, err = os.OpenFile(val, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o0600); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if err = paths.RealPath(¶ms[ParamKeylog][0]); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = os.MkdirAll(filepath.Dir(params[ParamKeylog][0]), 0700); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if keylog, err = os.OpenFile(params[ParamKeylog][0], os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o0600); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CA cert(s).
|
// CA cert(s).
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
if _, ok = params[TlsUriParamCa]; ok {
|
if _, ok = params[ParamCa]; ok {
|
||||||
rootCAs = x509.NewCertPool()
|
rootCAs = x509.NewCertPool()
|
||||||
for _, c := range params[TlsUriParamCa] {
|
for _, c := range params[ParamCa] {
|
||||||
if b, err = os.ReadFile(c); err != nil {
|
if b, err = os.ReadFile(c); err != nil {
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
if errors.Is(err, os.ErrNotExist) && ignoreMissing {
|
||||||
err = nil
|
err = nil
|
||||||
continue
|
continue
|
||||||
|
} else {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buf.Write(b)
|
buf.Write(b)
|
||||||
@ -463,12 +572,12 @@ func ParseTlsUri(tlsUri *url.URL) (tlsConf *tls.Config, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keys. These are done first so we can match to a client certificate.
|
// Keys. These are done first so we can match to a leaf certificate.
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
if _, ok = params[TlsUriParamKey]; ok {
|
if _, ok = params[ParamKey]; ok {
|
||||||
for _, k := range params[TlsUriParamKey] {
|
for _, k := range params[ParamKey] {
|
||||||
if b, err = os.ReadFile(k); err != nil {
|
if b, err = os.ReadFile(k); err != nil {
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
if errors.Is(err, os.ErrNotExist) && ignoreMissing {
|
||||||
err = nil
|
err = nil
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
@ -482,12 +591,12 @@ func ParseTlsUri(tlsUri *url.URL) (tlsConf *tls.Config, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// (Client) Certificate(s).
|
// (Leaf) Certificate(s).
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
if _, ok = params[TlsUriParamCert]; ok {
|
if _, ok = params[ParamCert]; ok {
|
||||||
for _, c := range params[TlsUriParamCert] {
|
for _, c := range params[ParamCert] {
|
||||||
if b, err = os.ReadFile(c); err != nil {
|
if b, err = os.ReadFile(c); err != nil {
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
if errors.Is(err, os.ErrNotExist) && ignoreMissing {
|
||||||
err = nil
|
err = nil
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
@ -496,19 +605,24 @@ func ParseTlsUri(tlsUri *url.URL) (tlsConf *tls.Config, err error) {
|
|||||||
}
|
}
|
||||||
buf.Write(b)
|
buf.Write(b)
|
||||||
}
|
}
|
||||||
if tlsCerts, err = ParseLeafCert(buf.Bytes(), privKeys, intermediateCAs...); err != nil {
|
if tlsCerts, concatCAs, err = ParseLeafCert(buf.Bytes(), privKeys, intermediateCAs...); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if concatCAs != nil {
|
||||||
|
for _, ca := range concatCAs {
|
||||||
|
rootCAs.AddCert(ca)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hostname (Override).
|
// Hostname (Override).
|
||||||
if _, ok = params[TlsUriParamSni]; ok {
|
if _, ok = params[ParamSni]; ok {
|
||||||
srvNm = params[TlsUriParamSni][0]
|
srvNm = params[ParamSni][0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable Verification.
|
// Disable Verification.
|
||||||
if _, ok = params[TlsUriParamNoVerify]; ok {
|
if _, ok = params[ParamNoVerify]; ok {
|
||||||
val = strings.ToLower(params[TlsUriParamNoVerify][0])
|
val = strings.ToLower(params[ParamNoVerify][0])
|
||||||
for _, i := range paramBoolValsTrue {
|
for _, i := range paramBoolValsTrue {
|
||||||
if i == val {
|
if i == val {
|
||||||
allowInvalid = true
|
allowInvalid = true
|
||||||
@ -517,39 +631,73 @@ func ParseTlsUri(tlsUri *url.URL) (tlsConf *tls.Config, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Client/mTLS cert auth mode.
|
||||||
|
if _, ok = params[ParamMtlsMode]; ok {
|
||||||
|
val = params[ParamMtlsMode][0]
|
||||||
|
if clientAuthType, err = ParseMtlsMode(val); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Client/mTLS roots.
|
||||||
|
buf.Reset()
|
||||||
|
if clientAuthType != tls.NoClientCert {
|
||||||
|
if _, ok = params[ParamMtlsCa]; ok {
|
||||||
|
mtlsCAs = x509.NewCertPool()
|
||||||
|
for _, c := range params[ParamMtlsCa] {
|
||||||
|
if b, err = os.ReadFile(c); err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) && ignoreMissing {
|
||||||
|
err = nil
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.Write(b)
|
||||||
|
}
|
||||||
|
if mtlsCAs, _, _, err = ParseCA(buf.Bytes()); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mtlsCAs = rootCAs.Clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ciphers.
|
// Ciphers.
|
||||||
if _, ok = params[TlsUriParamCipher]; ok {
|
if _, ok = params[ParamCipher]; ok {
|
||||||
ciphers = ParseTlsCiphers(strings.Join(params[TlsUriParamCipher], ","))
|
ciphers = ParseTlsCiphers(strings.Join(params[ParamCipher], ","))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minimum TLS Protocol Version.
|
// Minimum TLS Protocol Version.
|
||||||
if _, ok = params[TlsUriParamMinTls]; ok {
|
if _, ok = params[ParamMinTls]; ok {
|
||||||
if minVer, err = ParseTlsVersion(params[TlsUriParamMinTls][0]); err != nil {
|
if minVer, err = ParseTlsVersion(params[ParamMinTls][0]); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maximum TLS Protocol Version.
|
// Maximum TLS Protocol Version.
|
||||||
if _, ok = params[TlsUriParamMaxTls]; ok {
|
if _, ok = params[ParamMaxTls]; ok {
|
||||||
if maxVer, err = ParseTlsVersion(params[TlsUriParamMaxTls][0]); err != nil {
|
if maxVer, err = ParseTlsVersion(params[ParamMaxTls][0]); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Curves.
|
// Curves.
|
||||||
if _, ok = params[TlsUriParamCurve]; ok {
|
if _, ok = params[ParamCurve]; ok {
|
||||||
curves = ParseTlsCurves(strings.Join(params[TlsUriParamCurve], ","))
|
curves = ParseTlsCurves(strings.Join(params[ParamCurve], ","))
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConf = &tls.Config{
|
tlsConf = &tls.Config{
|
||||||
Certificates: tlsCerts,
|
Certificates: tlsCerts,
|
||||||
RootCAs: rootCAs,
|
RootCAs: rootCAs,
|
||||||
ServerName: srvNm,
|
ServerName: srvNm,
|
||||||
|
ClientAuth: clientAuthType,
|
||||||
|
ClientCAs: mtlsCAs,
|
||||||
InsecureSkipVerify: allowInvalid,
|
InsecureSkipVerify: allowInvalid,
|
||||||
CipherSuites: ciphers,
|
CipherSuites: ciphers,
|
||||||
MinVersion: minVer,
|
MinVersion: minVer,
|
||||||
MaxVersion: maxVer,
|
MaxVersion: maxVer,
|
||||||
CurvePreferences: curves,
|
CurvePreferences: curves,
|
||||||
|
KeyLogWriter: keylog,
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -694,36 +842,36 @@ func ParseDhParams(dhRaw []byte) (params []*dhparam.DH, err error) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ParseLeafCert parses PEM bytes from a (client) certificate file, iterates over a slice of
|
ParseLeafCert parses PEM bytes from a certificate file, iterates over a slice of
|
||||||
crypto.PrivateKey (finding one that matches), and returns one (or more) tls.Certificate.
|
crypto.PrivateKey (finding one that matches), and returns one (or more) tls.Certificate.
|
||||||
|
|
||||||
The key may also be combined with the certificate in the same file.
|
The key may also be combined with the certificate in the same file.
|
||||||
|
|
||||||
If no private key matches or no client cert is found in the file, tlsCerts will be nil/missing
|
If no private key matches or no leaf cert is found in the file, tlsCerts will be nil/missing
|
||||||
that certificate but no error will be returned.
|
that certificate but no error will be returned.
|
||||||
This behavior can be avoided by passing a nil slice to keys.
|
This behavior can be avoided by passing a nil slice to keys.
|
||||||
|
|
||||||
Any leaf certificates ("server" certificate, as opposed to a signer/issuer) found in the file
|
Any leaf certificates ("server"/"client" certificate, as opposed to a signer/issuer) found in the file
|
||||||
will be assumed to be the desired one(s).
|
will be assumed to be the desired one(s).
|
||||||
|
|
||||||
Any additional/supplementary intermediates may be provided. Any present in the PEM bytes (certRaw) will be included.
|
Any additional/supplementary intermediates may be provided. Any present in the PEM bytes (certRaw) will be included
|
||||||
|
in the tls.Certificate.Certificates.
|
||||||
|
|
||||||
Any *root* CAs found will be discarded. They should/can be extracted seperately via ParseCA.
|
Any *root* CAs found will be split out separately into caCerts and NOT included.
|
||||||
|
They should/can be extracted seperately via ParseCA.
|
||||||
|
|
||||||
The parsed and paired certificates and keys can be found in each respective tls.Certificate.Leaf and tls.Certificate.PrivateKey.
|
The parsed and paired certificates and keys can be found in each respective tls.Certificate[n].Leaf and tls.Certificate[n].PrivateKey.
|
||||||
Any certs without a corresponding key will be discarded.
|
Any certs without a corresponding key will be discarded.
|
||||||
*/
|
*/
|
||||||
func ParseLeafCert(certRaw []byte, keys []crypto.PrivateKey, intermediates ...*x509.Certificate) (tlsCerts []tls.Certificate, err error) {
|
func ParseLeafCert(certRaw []byte, keys []crypto.PrivateKey, intermediates ...*x509.Certificate) (tlsCerts []tls.Certificate, caCerts []*x509.Certificate, err error) {
|
||||||
|
|
||||||
var pemBlocks []*pem.Block
|
var pemBlocks []*pem.Block
|
||||||
var cert *x509.Certificate
|
var cert *x509.Certificate
|
||||||
var certs []*x509.Certificate
|
var certs []*x509.Certificate
|
||||||
var caCerts []*x509.Certificate
|
|
||||||
var parsedKeys []crypto.PrivateKey
|
var parsedKeys []crypto.PrivateKey
|
||||||
var isMatched bool
|
var isMatched bool
|
||||||
var foundKey crypto.PrivateKey
|
var foundKey crypto.PrivateKey
|
||||||
var interBytes [][]byte
|
var interBytes [][]byte
|
||||||
var skipKeyPair bool = keys == nil
|
|
||||||
var parsedKeysBuf *bytes.Buffer = new(bytes.Buffer)
|
var parsedKeysBuf *bytes.Buffer = new(bytes.Buffer)
|
||||||
|
|
||||||
if pemBlocks, err = SplitPem(certRaw); err != nil {
|
if pemBlocks, err = SplitPem(certRaw); err != nil {
|
||||||
@ -777,7 +925,7 @@ func ParseLeafCert(certRaw []byte, keys []crypto.PrivateKey, intermediates ...*x
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if foundKey == nil && !skipKeyPair {
|
if foundKey == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
tlsCerts = append(
|
tlsCerts = append(
|
||||||
@ -790,8 +938,6 @@ func ParseLeafCert(certRaw []byte, keys []crypto.PrivateKey, intermediates ...*x
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = caCerts
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
173
funcs_fieldmap_tlsuri.go
Normal file
173
funcs_fieldmap_tlsuri.go
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
package cryptparse
|
||||||
|
|
||||||
|
import (
|
||||||
|
`reflect`
|
||||||
|
`sync`
|
||||||
|
|
||||||
|
`r00t2.io/goutils/multierr`
|
||||||
|
`r00t2.io/goutils/structutils`
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
FieldMapTlsURI returns TlsUri tu from struct, map, or slice s.
|
||||||
|
|
||||||
|
If a map, the keys should be the Param* tlsUriParam contants' *values*
|
||||||
|
(e.g. map[string][]string{"ignore_missing": []string{"true"}}).
|
||||||
|
If a slice, it should be a slice of strings (or nested other supported type)
|
||||||
|
which follow the pattern `<key>=<value>` where `<key>` is one of the
|
||||||
|
Param* tlsUriParam constants' *values*.
|
||||||
|
Obviously in both these cases, the host/port will not be set.
|
||||||
|
See the NOTE below.
|
||||||
|
|
||||||
|
The default is to recurse into e.g. nested structs and sub-structs
|
||||||
|
unless they are tagged with the value "-".
|
||||||
|
|
||||||
|
This function operates on the struct tag `tlsUri` (e.g. `tlsUri:"-"`, `tlsUri:"ca"`, etc.).
|
||||||
|
If you need to use an explicit/different struct tag name, use FieldMapTlsURIWithTag.
|
||||||
|
|
||||||
|
See the struct tags on TlsFlat for an example of usage.
|
||||||
|
|
||||||
|
NOTE: The host and port are not currently directly
|
||||||
|
configurable via struct tags. These can be set
|
||||||
|
via the SetHost, SetPort, SetPortStr, SetHostPort,
|
||||||
|
and/or SetHostPortStr on the returned TlsUri.
|
||||||
|
*/
|
||||||
|
func FieldMapTlsURI[T any](s T) (tu *TlsUri, err error) {
|
||||||
|
|
||||||
|
if tu, err = FieldMapTlsURIWithTag(s, dfltStructTag); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldMapTlsURIWithTag is exactly like FieldMapTlsURI but allows specifying an explict tag name.
|
||||||
|
func FieldMapTlsURIWithTag[T any](s T, tagName string,) (tu *TlsUri, err error) {
|
||||||
|
|
||||||
|
var ptrVal reflect.Value
|
||||||
|
var sVal reflect.Value = reflect.ValueOf(s)
|
||||||
|
var sType reflect.Type = sVal.Type()
|
||||||
|
var params *tlsUriParams
|
||||||
|
|
||||||
|
*params = make(map[tlsUriParam][]string)
|
||||||
|
|
||||||
|
if sVal.IsNil() || sVal.IsZero() || !sVal.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ptrVal = sVal.Elem()
|
||||||
|
|
||||||
|
if err = fieldMapTlsURIStruct(params, ptrVal, tagName); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// fieldMapTlsURIStruct parses a reflect.Value and populates params with any matching fields found.
|
||||||
|
func fieldMapTlsURIStruct(params *tlsUriParams, v reflect.Value, tagName string) (err error) {
|
||||||
|
|
||||||
|
var field reflect.StructField
|
||||||
|
var fieldVal reflect.Value
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
var errChan chan error
|
||||||
|
var numJobs int
|
||||||
|
var doneChan chan bool = make(chan bool, 1)
|
||||||
|
var mErr *multierr.MultiError = multierr.NewMultiError(nil)
|
||||||
|
var t reflect.Type = v.Type()
|
||||||
|
var kind reflect.Kind = t.Kind()
|
||||||
|
|
||||||
|
if params == nil {
|
||||||
|
err = ErrReqParams
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if kind != reflect.Struct {
|
||||||
|
err = ErrReqStruct
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
numJobs = v.NumField()
|
||||||
|
wg.Add(numJobs)
|
||||||
|
errChan = make(chan error, numJobs)
|
||||||
|
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
field = t.Field(i)
|
||||||
|
fieldVal = v.Field(i)
|
||||||
|
|
||||||
|
go func(f reflect.StructField, fv reflect.Value) {
|
||||||
|
var fErr error
|
||||||
|
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
if fErr = fieldMapTlsURIStructField(params, f, fv, tagName); fErr != nil {
|
||||||
|
errChan <- fErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(field, fieldVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(errChan)
|
||||||
|
doneChan <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
<-doneChan
|
||||||
|
|
||||||
|
for i := 0; i < numJobs; i++ {
|
||||||
|
if err = <-errChan; err != nil {
|
||||||
|
mErr.AddError(err)
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mErr.IsEmpty() {
|
||||||
|
err = mErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// fieldMapTlsURIStructField parses a single struct field.
|
||||||
|
func fieldMapTlsURIStructField(params *tlsUriParams, field reflect.StructField, fv reflect.Value, tagName string) (err error) {
|
||||||
|
|
||||||
|
var parsedTagOpts map[string]bool
|
||||||
|
|
||||||
|
if params == nil {
|
||||||
|
err = ErrReqParams
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fv.CanSet() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if explicitly instructed to do so.
|
||||||
|
parsedTagOpts = structutils.TagToBoolMap(field, tagName, structutils.TagMapTrim)
|
||||||
|
if parsedTagOpts["-"] {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if fv.Kind() == reflect.Ptr {
|
||||||
|
err = fieldMapTlsURIStructField(params, field, fv.Elem(), tagName)
|
||||||
|
} else {
|
||||||
|
err = fieldMapTlsUriValue(params, fv, tagName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// fieldMapTlsUriValue is a type dispatcher for a reflected value.
|
||||||
|
func fieldMapTlsUriValue(params *tlsUriParams, v reflect.Value, tagName string) (err error) {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
65
funcs_reflectflat.go
Normal file
65
funcs_reflectflat.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package cryptparse
|
||||||
|
|
||||||
|
import (
|
||||||
|
`fmt`
|
||||||
|
`net/url`
|
||||||
|
`reflect`
|
||||||
|
|
||||||
|
`r00t2.io/goutils/structutils`
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
reflectFlatUri is the reflection used for a TlsFlat when converting to a TlsUri.
|
||||||
|
*/
|
||||||
|
func reflectFlatUri(s *TlsFlat, tagName string) (tu *TlsUri, err error) {
|
||||||
|
|
||||||
|
var uri *url.URL
|
||||||
|
var field reflect.StructField
|
||||||
|
var fieldVal reflect.Value
|
||||||
|
var params url.Values = make(url.Values)
|
||||||
|
var sVal reflect.Value = reflect.ValueOf(s)
|
||||||
|
var sType reflect.Type = sVal.Type()
|
||||||
|
var kind reflect.Kind = sType.Kind()
|
||||||
|
var parsedTagOpts map[string]bool
|
||||||
|
|
||||||
|
if s == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if uri, err = url.Parse(fmt.Sprintf("tls://%s", s.Host)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if s.Port != nil {
|
||||||
|
uri.Host = fmt.Sprintf("%s:%d", uri.Host, *s.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < sType.NumField(); i++ {
|
||||||
|
field = sType.Field(i)
|
||||||
|
fieldVal = sVal.Field(i)
|
||||||
|
if !fieldVal.CanSet() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedTagOpts = structutils.TagToBoolMap(field, "tlsUri")
|
||||||
|
if parsedTagOpts["-"] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
if len(params) != 0 {
|
||||||
|
if uri.Path == "" {
|
||||||
|
uri.Path = "/"
|
||||||
|
}
|
||||||
|
if uri, err = url.Parse(fmt.Sprintf("%s%s", uri.String(), params.Encode())); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tu = &TlsUri{
|
||||||
|
URL: uri,
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
60
funcs_reflecturi.go
Normal file
60
funcs_reflecturi.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package cryptparse
|
||||||
|
|
||||||
|
import (
|
||||||
|
`reflect`
|
||||||
|
`strconv`
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
reflectUriFlat is the reflection used for a TlsUri when converting to a TlsFlat.
|
||||||
|
*/
|
||||||
|
func reflectUriFlat(s *TlsUri, tagName string) (f *TlsFlat, err error) {
|
||||||
|
|
||||||
|
var ptrVal reflect.Value
|
||||||
|
var ptrType reflect.Type
|
||||||
|
var ptrKind reflect.Kind
|
||||||
|
var n uint64
|
||||||
|
var flat TlsFlat
|
||||||
|
var sVal reflect.Value = reflect.ValueOf(s)
|
||||||
|
var sType reflect.Type = sVal.Type()
|
||||||
|
var kind reflect.Kind = sType.Kind()
|
||||||
|
var params map[tlsUriParam][]string = make(map[tlsUriParam][]string)
|
||||||
|
|
||||||
|
if s == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range params {
|
||||||
|
if tlsUriParam(k) != "" {
|
||||||
|
params[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flat = TlsFlat{
|
||||||
|
Host: s.Hostname(),
|
||||||
|
Port: nil,
|
||||||
|
SniName: nil,
|
||||||
|
SkipVerify: false,
|
||||||
|
Certs: nil,
|
||||||
|
CaFiles: nil,
|
||||||
|
CipherSuites: nil,
|
||||||
|
Curves: nil,
|
||||||
|
MinTlsProtocol: nil,
|
||||||
|
MaxTlsProtocol: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Port() != "" {
|
||||||
|
f.Port = new(uint16)
|
||||||
|
if n, err = strconv.ParseUint(s.Port(), 10, 16); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*f.Port = uint16(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: lookup map, map[string]tlsUriParam where key is name of constant as string, and value is that corresponding value.
|
||||||
|
// Use that value to lookup in params.
|
||||||
|
|
||||||
|
f = &flat
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
137
funcs_test.go
137
funcs_test.go
@ -2,9 +2,117 @@ package cryptparse
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
`crypto/tls`
|
`crypto/tls`
|
||||||
|
`embed`
|
||||||
|
`errors`
|
||||||
|
`fmt`
|
||||||
|
`io/fs`
|
||||||
|
`net/url`
|
||||||
|
`os`
|
||||||
|
`strings`
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Generated from ../_extra/gen_test_pki
|
||||||
|
|
||||||
|
//go:embed "_testdata"
|
||||||
|
testPems embed.FS
|
||||||
|
testTmpPemFiles map[string]*os.File
|
||||||
|
testKt string = "ed25519"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testInit(t *testing.T) (err error) {
|
||||||
|
|
||||||
|
var n string
|
||||||
|
var nkt string
|
||||||
|
var b []byte
|
||||||
|
var names []fs.DirEntry
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if testTmpPemFiles == nil {
|
||||||
|
testTmpPemFiles = make(map[string]*os.File)
|
||||||
|
}
|
||||||
|
|
||||||
|
if names, err = testPems.ReadDir("_testdata"); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// only ".keep" is present.
|
||||||
|
if len(names) == 1 {
|
||||||
|
t.Fatalf(
|
||||||
|
"There aren't any test PEMs."+
|
||||||
|
"You must `go run *.go` in _extras/gen_test_pki and copy the %s PEMs into _testdata.",
|
||||||
|
testKt,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// populate tmpFiles from the embed.FS `pems` and write out to temp files.
|
||||||
|
for _, p := range []string{
|
||||||
|
"ca",
|
||||||
|
"inter",
|
||||||
|
"leaf_server",
|
||||||
|
"leaf_user",
|
||||||
|
} {
|
||||||
|
for _, pt := range []string{
|
||||||
|
"cert",
|
||||||
|
"csr",
|
||||||
|
"key",
|
||||||
|
} {
|
||||||
|
n = fmt.Sprintf("%s_%s", p, pt)
|
||||||
|
nkt = fmt.Sprintf("%s_%s_%s", p, testKt, pt)
|
||||||
|
if _, ok = testTmpPemFiles[n]; !ok {
|
||||||
|
if b, err = testPems.ReadFile(fmt.Sprintf("_testdata/%s.pem", nkt)); err != nil {
|
||||||
|
t.Fatalf("Read '%s' failed: %v", nkt, err)
|
||||||
|
}
|
||||||
|
if testTmpPemFiles[n], err = os.CreateTemp("", fmt.Sprintf(".*.%s.pem", n)); err != nil {
|
||||||
|
t.Fatalf("Create temp file for %s failed: %v", n, err)
|
||||||
|
}
|
||||||
|
if _, err = testTmpPemFiles[n].Write(b); err != nil {
|
||||||
|
t.Fatalf("Write to %s failed: %v", n, err)
|
||||||
|
}
|
||||||
|
if err = testTmpPemFiles[n].Close(); err != nil {
|
||||||
|
t.Fatalf("Closing %s failed: %v", n, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(p, "leaf_") {
|
||||||
|
n = fmt.Sprintf("%s_chained", p)
|
||||||
|
nkt = fmt.Sprintf("%s_%s_cert_chained.pem", p, testKt)
|
||||||
|
if _, ok = testTmpPemFiles[n]; !ok {
|
||||||
|
if b, err = testPems.ReadFile(fmt.Sprintf("_testdata/%s.pem", nkt)); err != nil {
|
||||||
|
t.Fatalf("Read '%s' failed: %v", nkt, err)
|
||||||
|
}
|
||||||
|
if testTmpPemFiles[n], err = os.CreateTemp("", fmt.Sprintf(".*.%s.pem", n)); err != nil {
|
||||||
|
t.Fatalf("Create temp file for %s failed: %v", n, err)
|
||||||
|
}
|
||||||
|
if _, err = testTmpPemFiles[n].Write(b); err != nil {
|
||||||
|
t.Fatalf("Write to %s failed: %v", n, err)
|
||||||
|
}
|
||||||
|
if err = testTmpPemFiles[n].Close(); err != nil {
|
||||||
|
t.Fatalf("Closing %s failed: %v", n, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Cleanup(func() {
|
||||||
|
var cErr error
|
||||||
|
for k, f := range testTmpPemFiles {
|
||||||
|
if cErr = f.Close(); cErr != nil && !errors.Is(cErr, os.ErrClosed) {
|
||||||
|
t.Logf("Error when closing %s '%s': %v", k, f.Name(), cErr)
|
||||||
|
cErr = nil
|
||||||
|
} else if cErr != nil {
|
||||||
|
cErr = nil
|
||||||
|
}
|
||||||
|
if cErr = os.Remove(f.Name()); cErr != nil {
|
||||||
|
t.Logf("Error when removing %s '%s': %v", k, f.Name(), cErr)
|
||||||
|
cErr = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func TestCiphers(t *testing.T) {
|
func TestCiphers(t *testing.T) {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@ -35,3 +143,32 @@ func TestCiphers(t *testing.T) {
|
|||||||
|
|
||||||
_ = cs
|
_ = cs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTlsUri(t *testing.T) {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var uStr string
|
||||||
|
var u *url.URL
|
||||||
|
var tlsU *TlsUri
|
||||||
|
|
||||||
|
if err = testInit(t); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
uStr = fmt.Sprintf(
|
||||||
|
"https://:9091/?"+
|
||||||
|
"pki_ca=%s&"+ // testTmpFiles["ca_cert"]
|
||||||
|
"pki_cert=%s&"+ // testTmpFiles["leaf_server_chained"]
|
||||||
|
"pki_key=%s&"+ // testTmpFiles["leaf_server_key"]
|
||||||
|
"min_tls=1.2&max_tls=1.2&"+
|
||||||
|
"sni=server.example.com",
|
||||||
|
testTmpPemFiles["ca_cert"], testTmpPemFiles["leaf_server_chained"], testTmpPemFiles["leaf_server_key"],
|
||||||
|
)
|
||||||
|
if u, err = url.Parse(uStr); err != nil {
|
||||||
|
t.Fatalf("Failed to parse URL string '%s': %v", uStr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsU = &TlsUri{
|
||||||
|
URL: u,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -57,6 +57,7 @@ func (t *TlsFlat) ToTlsConfig() (tlsConf *tls.Config, err error) {
|
|||||||
var curves []tls.CurveID
|
var curves []tls.CurveID
|
||||||
var minVer uint16
|
var minVer uint16
|
||||||
var maxVer uint16
|
var maxVer uint16
|
||||||
|
var concatCAs []*x509.Certificate
|
||||||
var buf *bytes.Buffer = new(bytes.Buffer)
|
var buf *bytes.Buffer = new(bytes.Buffer)
|
||||||
var srvNm string = t.SniName
|
var srvNm string = t.SniName
|
||||||
|
|
||||||
@ -107,10 +108,15 @@ func (t *TlsFlat) ToTlsConfig() (tlsConf *tls.Config, err error) {
|
|||||||
if b, err = os.ReadFile(c.CertFile); err != nil {
|
if b, err = os.ReadFile(c.CertFile); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if parsedTlsCerts, err = ParseLeafCert(b, privKeys, intermediateCAs...); err != nil {
|
if parsedTlsCerts, concatCAs, err = ParseLeafCert(b, privKeys, intermediateCAs...); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tlsCerts = append(tlsCerts, parsedTlsCerts...)
|
tlsCerts = append(tlsCerts, parsedTlsCerts...)
|
||||||
|
if concatCAs != nil {
|
||||||
|
for _, ca := range concatCAs {
|
||||||
|
rootCAs.AddCert(ca)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,49 +169,49 @@ func (t *TlsFlat) ToTlsUri() (tlsUri *TlsUri, err error) {
|
|||||||
// CA cert(s).
|
// CA cert(s).
|
||||||
if t.CaFiles != nil {
|
if t.CaFiles != nil {
|
||||||
for _, c := range t.CaFiles {
|
for _, c := range t.CaFiles {
|
||||||
u.Query().Add(TlsUriParamCa, c)
|
u.Query().Add(ParamCa, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keys and Certs.
|
// Keys and Certs.
|
||||||
if t.Certs != nil {
|
if t.Certs != nil {
|
||||||
for _, c := range t.Certs {
|
for _, c := range t.Certs {
|
||||||
u.Query().Add(TlsUriParamCert, c.CertFile)
|
u.Query().Add(ParamCert, c.CertFile)
|
||||||
if c.KeyFile != nil {
|
if c.KeyFile != nil {
|
||||||
u.Query().Add(TlsUriParamKey, *c.KeyFile)
|
u.Query().Add(ParamKey, *c.KeyFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enforce the SNI hostname.
|
// Enforce the SNI hostname.
|
||||||
u.Query().Add(TlsUriParamSni, t.SniName)
|
u.Query().Add(ParamSni, t.SniName)
|
||||||
|
|
||||||
// Disable Verification.
|
// Disable Verification.
|
||||||
if t.SkipVerify {
|
if t.SkipVerify {
|
||||||
u.Query().Add(TlsUriParamNoVerify, "1")
|
u.Query().Add(ParamNoVerify, "1")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ciphers.
|
// Ciphers.
|
||||||
if t.CipherSuites != nil {
|
if t.CipherSuites != nil {
|
||||||
for _, c := range t.CipherSuites {
|
for _, c := range t.CipherSuites {
|
||||||
u.Query().Add(TlsUriParamCipher, c)
|
u.Query().Add(ParamCipher, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minimum TLS Protocol Version.
|
// Minimum TLS Protocol Version.
|
||||||
if t.MinTlsProtocol != nil {
|
if t.MinTlsProtocol != nil {
|
||||||
u.Query().Add(TlsUriParamMinTls, *t.MinTlsProtocol)
|
u.Query().Add(ParamMinTls, *t.MinTlsProtocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maximum TLS Protocol Version.
|
// Maximum TLS Protocol Version.
|
||||||
if t.MaxTlsProtocol != nil {
|
if t.MaxTlsProtocol != nil {
|
||||||
u.Query().Add(TlsUriParamMaxTls, *t.MaxTlsProtocol)
|
u.Query().Add(ParamMaxTls, *t.MaxTlsProtocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Curves.
|
// Curves.
|
||||||
if t.Curves != nil {
|
if t.Curves != nil {
|
||||||
for _, c := range t.Curves {
|
for _, c := range t.Curves {
|
||||||
u.Query().Add(TlsUriParamCurve, c)
|
u.Query().Add(ParamCurve, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
223
funcs_tlsflat.go.old
Normal file
223
funcs_tlsflat.go.old
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
package cryptparse
|
||||||
|
|
||||||
|
import (
|
||||||
|
`bytes`
|
||||||
|
`crypto`
|
||||||
|
`crypto/tls`
|
||||||
|
`crypto/x509`
|
||||||
|
`errors`
|
||||||
|
`fmt`
|
||||||
|
`net/url`
|
||||||
|
`os`
|
||||||
|
`strings`
|
||||||
|
|
||||||
|
`r00t2.io/sysutils/paths`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Normalize ensures that all specified filepaths are absolute, etc.
|
||||||
|
func (t *TlsFlat) Normalize() (err error) {
|
||||||
|
|
||||||
|
if t.Certs != nil {
|
||||||
|
for _, c := range t.Certs {
|
||||||
|
if err = paths.RealPath(&c.CertFile); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if c.KeyFile != nil {
|
||||||
|
if err = paths.RealPath(c.KeyFile); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t.CaFiles != nil {
|
||||||
|
for idx, _ := range t.CaFiles {
|
||||||
|
if err = paths.RealPath(&t.CaFiles[idx]); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ToTlsConfig returns a tls.Config from a TlsFlat. Note that it will have Normalize called on it.
|
||||||
|
|
||||||
|
Unfortunately it's not possible for this library to do the reverse, as CA certificates are not able to be extracted from an x509.CertPool.
|
||||||
|
*/
|
||||||
|
func (t *TlsFlat) ToTlsConfig() (tlsConf *tls.Config, err error) {
|
||||||
|
|
||||||
|
var b []byte
|
||||||
|
var rootCAs *x509.CertPool
|
||||||
|
var intermediateCAs []*x509.Certificate
|
||||||
|
var privKeys []crypto.PrivateKey
|
||||||
|
var tlsCerts []tls.Certificate
|
||||||
|
var parsedTlsCerts []tls.Certificate
|
||||||
|
var ciphers []uint16
|
||||||
|
var curves []tls.CurveID
|
||||||
|
var minVer uint16
|
||||||
|
var maxVer uint16
|
||||||
|
var concatCAs []*x509.Certificate
|
||||||
|
var buf *bytes.Buffer = new(bytes.Buffer)
|
||||||
|
var srvNm string = t.SniName
|
||||||
|
|
||||||
|
// Normalize any filepaths before validation.
|
||||||
|
if err = t.Normalize(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// And validate.
|
||||||
|
if err = validate.Struct(t); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CA cert(s).
|
||||||
|
buf.Reset()
|
||||||
|
if t.CaFiles != nil {
|
||||||
|
rootCAs = x509.NewCertPool()
|
||||||
|
for _, c := range t.CaFiles {
|
||||||
|
if b, err = os.ReadFile(c); err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
err = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.Write(b)
|
||||||
|
}
|
||||||
|
if rootCAs, _, intermediateCAs, err = ParseCA(buf.Bytes()); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if rootCAs, err = x509.SystemCertPool(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys and Certs. They are assumed to be matched.
|
||||||
|
if t.Certs != nil {
|
||||||
|
for _, c := range t.Certs {
|
||||||
|
privKeys = nil
|
||||||
|
if c.KeyFile != nil {
|
||||||
|
if b, err = os.ReadFile(*c.KeyFile); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if privKeys, err = ParsePrivateKey(b); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if b, err = os.ReadFile(c.CertFile); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if parsedTlsCerts, concatCAs, err = ParseLeafCert(b, privKeys, intermediateCAs...); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tlsCerts = append(tlsCerts, parsedTlsCerts...)
|
||||||
|
if concatCAs != nil {
|
||||||
|
for _, ca := range concatCAs {
|
||||||
|
rootCAs.AddCert(ca)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ciphers.
|
||||||
|
if t.CipherSuites != nil {
|
||||||
|
ciphers = ParseTlsCiphers(strings.Join(t.CipherSuites, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimum TLS Protocol Version.
|
||||||
|
if t.MinTlsProtocol != nil {
|
||||||
|
if minVer, err = ParseTlsVersion(*t.MinTlsProtocol); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximum TLS Protocol Version.
|
||||||
|
if t.MaxTlsProtocol != nil {
|
||||||
|
if maxVer, err = ParseTlsVersion(*t.MaxTlsProtocol); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Curves.
|
||||||
|
if t.Curves != nil {
|
||||||
|
curves = ParseTlsCurves(strings.Join(t.Curves, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConf = &tls.Config{
|
||||||
|
Certificates: tlsCerts,
|
||||||
|
RootCAs: rootCAs,
|
||||||
|
ServerName: srvNm,
|
||||||
|
InsecureSkipVerify: t.SkipVerify,
|
||||||
|
CipherSuites: ciphers,
|
||||||
|
MinVersion: minVer,
|
||||||
|
MaxVersion: maxVer,
|
||||||
|
CurvePreferences: curves,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToTlsUri returns a TlsUri from a TlsFlat.
|
||||||
|
func (t *TlsFlat) ToTlsUri() (tlsUri *TlsUri, err error) {
|
||||||
|
|
||||||
|
var u *url.URL
|
||||||
|
|
||||||
|
if u, err = url.Parse(fmt.Sprintf("tls://%v/", t.SniName)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CA cert(s).
|
||||||
|
if t.CaFiles != nil {
|
||||||
|
for _, c := range t.CaFiles {
|
||||||
|
u.Query().Add(ParamCa, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys and Certs.
|
||||||
|
if t.Certs != nil {
|
||||||
|
for _, c := range t.Certs {
|
||||||
|
u.Query().Add(ParamCert, c.CertFile)
|
||||||
|
if c.KeyFile != nil {
|
||||||
|
u.Query().Add(ParamKey, *c.KeyFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce the SNI hostname.
|
||||||
|
u.Query().Add(ParamSni, t.SniName)
|
||||||
|
|
||||||
|
// Disable Verification.
|
||||||
|
if t.SkipVerify {
|
||||||
|
u.Query().Add(ParamNoVerify, "1")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ciphers.
|
||||||
|
if t.CipherSuites != nil {
|
||||||
|
for _, c := range t.CipherSuites {
|
||||||
|
u.Query().Add(ParamCipher, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimum TLS Protocol Version.
|
||||||
|
if t.MinTlsProtocol != nil {
|
||||||
|
u.Query().Add(ParamMinTls, *t.MinTlsProtocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximum TLS Protocol Version.
|
||||||
|
if t.MaxTlsProtocol != nil {
|
||||||
|
u.Query().Add(ParamMaxTls, *t.MaxTlsProtocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Curves.
|
||||||
|
if t.Curves != nil {
|
||||||
|
for _, c := range t.Curves {
|
||||||
|
u.Query().Add(ParamCurve, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsUri = &TlsUri{
|
||||||
|
URL: u,
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
155
funcs_tlsuri.go
155
funcs_tlsuri.go
@ -3,12 +3,108 @@ package cryptparse
|
|||||||
import (
|
import (
|
||||||
`crypto`
|
`crypto`
|
||||||
`crypto/tls`
|
`crypto/tls`
|
||||||
|
`fmt`
|
||||||
`net`
|
`net`
|
||||||
`net/url`
|
`net/url`
|
||||||
`os`
|
`os`
|
||||||
|
`strconv`
|
||||||
`strings`
|
`strings`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
SetHost allows one to explicitly set the host component of the URI.
|
||||||
|
A host can be removed from a TlsUri by invoking this method with an empty hostAddr string.
|
||||||
|
|
||||||
|
No validation is performed.
|
||||||
|
*/
|
||||||
|
func (t *TlsUri) SetHost(hostAddr string) {
|
||||||
|
|
||||||
|
if t == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Port() == "" {
|
||||||
|
t.Host = hostAddr
|
||||||
|
} else {
|
||||||
|
t.Host = fmt.Sprintf("%s:%s", hostAddr, t.Port())
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
SetHostPort is a small wrapper around the SetHost and SetPort methods, combining them into one.
|
||||||
|
|
||||||
|
Refer to the comments for each on usage.
|
||||||
|
*/
|
||||||
|
func (t *TlsUri) SetHostPort(host string, port *uint16) {
|
||||||
|
|
||||||
|
t.SetPort(port)
|
||||||
|
t.SetHost(host)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
SetHostPortStr is a small wrapper around the SetHost and SetPortStr methods, combining them into one.
|
||||||
|
|
||||||
|
Refer to the comments for each on usage.
|
||||||
|
*/
|
||||||
|
func (t *TlsUri) SetHostPortStr(host string, port string) (err error) {
|
||||||
|
|
||||||
|
if err = t.SetPortStr(port); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.SetHost(host)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
SetPort allows one to explicitly set the port component of the URI.
|
||||||
|
A port can be removed from a TlsUri by invoking this method with a nil port.
|
||||||
|
*/
|
||||||
|
func (t *TlsUri) SetPort(port *uint16) {
|
||||||
|
|
||||||
|
if t == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if port == nil {
|
||||||
|
t.Host = t.Hostname()
|
||||||
|
} else {
|
||||||
|
t.Host = fmt.Sprintf("%s:%d", t.Hostname(), *port)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
SetPortStr allows one to specify the port number as a string instead of a uint16 ptr.
|
||||||
|
If port is an empty string, any existing defined port will be removed from t.
|
||||||
|
*/
|
||||||
|
func (t *TlsUri) SetPortStr(port string) (err error) {
|
||||||
|
|
||||||
|
var n uint64
|
||||||
|
var u uint16
|
||||||
|
|
||||||
|
if port == "" {
|
||||||
|
t.Host = t.Hostname()
|
||||||
|
} else {
|
||||||
|
if n, err = strconv.ParseUint(port, 10, 16); err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n > 65535 {
|
||||||
|
err = ErrBadPortRange
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u = uint16(n)
|
||||||
|
t.Host = fmt.Sprintf("%s:%d", t.Hostname(), u)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
WithConn returns a (crypto/)tls.Conn from an existing/already dialed net.Conn.
|
WithConn returns a (crypto/)tls.Conn from an existing/already dialed net.Conn.
|
||||||
|
|
||||||
@ -45,8 +141,8 @@ func (t *TlsUri) ToConn() (conn net.Conn, err error) {
|
|||||||
params = t.Query()
|
params = t.Query()
|
||||||
|
|
||||||
if params != nil {
|
if params != nil {
|
||||||
if _, ok = params[TlsUriParamNet]; ok {
|
if _, ok = params[ParamNet]; ok {
|
||||||
netType = params[TlsUriParamNet][0]
|
netType = params[ParamNet][0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
netType = strings.ToLower(netType)
|
netType = strings.ToLower(netType)
|
||||||
@ -99,8 +195,8 @@ func (t *TlsUri) ToTlsConn() (conn *tls.Conn, err error) {
|
|||||||
params = t.Query()
|
params = t.Query()
|
||||||
|
|
||||||
if params != nil {
|
if params != nil {
|
||||||
if _, ok = params[TlsUriParamNet]; ok {
|
if _, ok = params[ParamNet]; ok {
|
||||||
netType = params[TlsUriParamNet][0]
|
netType = params[ParamNet][0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
netType = strings.ToLower(netType)
|
netType = strings.ToLower(netType)
|
||||||
@ -124,7 +220,6 @@ func (t *TlsUri) ToTlsFlat() (tlsFlat *TlsFlat, err error) {
|
|||||||
|
|
||||||
var b []byte
|
var b []byte
|
||||||
var params url.Values
|
var params url.Values
|
||||||
var paramMap map[string][]string
|
|
||||||
// These also have maps so they can backmap filenames.
|
// These also have maps so they can backmap filenames.
|
||||||
var privKeys []crypto.PrivateKey
|
var privKeys []crypto.PrivateKey
|
||||||
var privKeyMap map[string][]crypto.PrivateKey
|
var privKeyMap map[string][]crypto.PrivateKey
|
||||||
@ -133,6 +228,8 @@ func (t *TlsUri) ToTlsFlat() (tlsFlat *TlsFlat, err error) {
|
|||||||
var isMatch bool
|
var isMatch bool
|
||||||
var fCert *TlsFlatCert
|
var fCert *TlsFlatCert
|
||||||
var val string
|
var val string
|
||||||
|
var ok bool
|
||||||
|
var paramMap map[tlsUriParam][]string = make(map[tlsUriParam][]string)
|
||||||
var f TlsFlat = TlsFlat{
|
var f TlsFlat = TlsFlat{
|
||||||
SniName: t.Hostname(),
|
SniName: t.Hostname(),
|
||||||
SkipVerify: false,
|
SkipVerify: false,
|
||||||
@ -145,22 +242,24 @@ func (t *TlsUri) ToTlsFlat() (tlsFlat *TlsFlat, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
params = t.Query()
|
params = t.Query()
|
||||||
paramMap = params
|
|
||||||
|
|
||||||
if params == nil {
|
if params == nil {
|
||||||
tlsFlat = &f
|
tlsFlat = &f
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// CA cert(s).
|
for k, v := range params {
|
||||||
if t.Query().Has(TlsUriParamCa) {
|
paramMap[tlsUriParam(k)] = v
|
||||||
f.CaFiles = append(f.CaFiles, paramMap[TlsUriParamCa]...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keys and Certs. These are done first so we can match to a client certificate.
|
// CA cert(s).
|
||||||
if t.Query().Has(TlsUriParamKey) {
|
if _, ok = paramMap[ParamCa]; ok {
|
||||||
|
f.CaFiles = append(f.CaFiles, paramMap[ParamCa]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys and Certs. These are done first so we can match to a leaf certificate.
|
||||||
|
if _, ok = paramMap[ParamKey]; ok {
|
||||||
privKeyMap = make(map[string][]crypto.PrivateKey)
|
privKeyMap = make(map[string][]crypto.PrivateKey)
|
||||||
for _, kFile := range paramMap[TlsUriParamKey] {
|
for _, kFile := range paramMap[ParamKey] {
|
||||||
if b, err = os.ReadFile(kFile); err != nil {
|
if b, err = os.ReadFile(kFile); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -170,13 +269,13 @@ func (t *TlsUri) ToTlsFlat() (tlsFlat *TlsFlat, err error) {
|
|||||||
privKeys = append(privKeys, privKeyMap[kFile]...)
|
privKeys = append(privKeys, privKeyMap[kFile]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if t.Query().Has(TlsUriParamCert) {
|
if t_, ok = paramMap[ParamCert]; ok {
|
||||||
tlsCertMap = make(map[string][]tls.Certificate)
|
tlsCertMap = make(map[string][]tls.Certificate)
|
||||||
for _, cFile := range paramMap[TlsUriParamCert] {
|
for _, cFile := range paramMap[ParamCert] {
|
||||||
if b, err = os.ReadFile(cFile); err != nil {
|
if b, err = os.ReadFile(cFile); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if tlsCertMap[cFile], err = ParseLeafCert(b, privKeys); err != nil {
|
if tlsCertMap[cFile], _, err = ParseLeafCert(b, privKeys); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tlsCerts = append(tlsCerts, tlsCertMap[cFile]...)
|
tlsCerts = append(tlsCerts, tlsCertMap[cFile]...)
|
||||||
@ -201,13 +300,13 @@ func (t *TlsUri) ToTlsFlat() (tlsFlat *TlsFlat, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Hostname.
|
// Hostname.
|
||||||
if t.Query().Has(TlsUriParamSni) {
|
if t.Query().Has(ParamSni) {
|
||||||
f.SniName = t.Query().Get(TlsUriParamSni)
|
f.SniName = t.Query().Get(ParamSni)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable verification.
|
// Disable verification.
|
||||||
if t.Query().Has(TlsUriParamNoVerify) {
|
if t.Query().Has(ParamNoVerify) {
|
||||||
val = strings.ToLower(t.Query().Get(TlsUriParamNoVerify))
|
val = strings.ToLower(t.Query().Get(ParamNoVerify))
|
||||||
for _, i := range paramBoolValsTrue {
|
for _, i := range paramBoolValsTrue {
|
||||||
if val == i {
|
if val == i {
|
||||||
f.SkipVerify = true
|
f.SkipVerify = true
|
||||||
@ -217,25 +316,25 @@ func (t *TlsUri) ToTlsFlat() (tlsFlat *TlsFlat, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ciphers.
|
// Ciphers.
|
||||||
if t.Query().Has(TlsUriParamCipher) {
|
if t.Query().Has(ParamCipher) {
|
||||||
f.CipherSuites = params[TlsUriParamCipher]
|
f.CipherSuites = params[ParamCipher]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minimum TLS Protocol Version.
|
// Minimum TLS Protocol Version.
|
||||||
if t.Query().Has(TlsUriParamMinTls) {
|
if t.Query().Has(ParamMinTls) {
|
||||||
f.MinTlsProtocol = new(string)
|
f.MinTlsProtocol = new(string)
|
||||||
*f.MinTlsProtocol = t.Query().Get(TlsUriParamMinTls)
|
*f.MinTlsProtocol = t.Query().Get(ParamMinTls)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maximum TLS Protocol Version.
|
// Maximum TLS Protocol Version.
|
||||||
if t.Query().Has(TlsUriParamMaxTls) {
|
if t.Query().Has(ParamMaxTls) {
|
||||||
f.MaxTlsProtocol = new(string)
|
f.MaxTlsProtocol = new(string)
|
||||||
*f.MaxTlsProtocol = t.Query().Get(TlsUriParamMaxTls)
|
*f.MaxTlsProtocol = t.Query().Get(ParamMaxTls)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Curves.
|
// Curves.
|
||||||
if t.Query().Has(TlsUriParamCurve) {
|
if t.Query().Has(ParamCurve) {
|
||||||
f.Curves = params[TlsUriParamCurve]
|
f.Curves = params[ParamCurve]
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsFlat = &f
|
tlsFlat = &f
|
||||||
|
24
go.mod
Normal file
24
go.mod
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
module r00t2.io/cryptparse
|
||||||
|
|
||||||
|
go 1.23.2
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/Luzifer/go-dhparam v1.3.0
|
||||||
|
github.com/davecgh/go-spew v1.1.1
|
||||||
|
github.com/go-playground/validator/v10 v10.22.1
|
||||||
|
github.com/oriser/regroup v0.0.0-20240925165441-f6bb0e08289e
|
||||||
|
r00t2.io/sysutils v1.7.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
|
||||||
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
|
golang.org/x/crypto v0.28.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect
|
||||||
|
golang.org/x/net v0.30.0 // indirect
|
||||||
|
golang.org/x/sys v0.26.0 // indirect
|
||||||
|
golang.org/x/text v0.19.0 // indirect
|
||||||
|
r00t2.io/goutils v1.6.0 // indirect
|
||||||
|
)
|
42
go.sum
Normal file
42
go.sum
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
github.com/Luzifer/go-dhparam v1.3.0 h1:GyB+YSU2jpUbCR9SjvT8W575BPeLzd2Tt2/3BirUFKM=
|
||||||
|
github.com/Luzifer/go-dhparam v1.3.0/go.mod h1:zOdP6tT8XK6Gndh6p0GrvOpUaFawGLSzT6+n1CHY9Hk=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
|
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
||||||
|
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
|
github.com/oriser/regroup v0.0.0-20240925165441-f6bb0e08289e h1:cL0lMYYEbfEUBghQd4ytnl8B8Ktdm+JremTyAagegZ0=
|
||||||
|
github.com/oriser/regroup v0.0.0-20240925165441-f6bb0e08289e/go.mod h1:tUOeYZJlwO7jSmM5ko1jTCiQaWQMvh58IENEfjwYzh8=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||||
|
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||||
|
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o=
|
||||||
|
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||||
|
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||||
|
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||||
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||||
|
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||||
|
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
r00t2.io/goutils v1.6.0 h1:oBC6PgBv0y/fdHeCmWgORHpBiU8uWw7IfFQJX5rIuzY=
|
||||||
|
r00t2.io/goutils v1.6.0/go.mod h1:9ObJI9S71wDLTOahwoOPs19DhZVYrOh4LEHmQ8SW4Lk=
|
||||||
|
r00t2.io/sysutils v1.1.1/go.mod h1:Wlfi1rrJpoKBOjWiYM9rw2FaiZqraD6VpXyiHgoDo/o=
|
||||||
|
r00t2.io/sysutils v1.7.1 h1:OXvzcpGC+WCHusDKCZqsDqUYr73qBbWqt6mM/qY/OHs=
|
||||||
|
r00t2.io/sysutils v1.7.1/go.mod h1:Sk/7riJp9fteeW9STkdQ/k22huL1J6r05n6wLh5byHY=
|
32
internal/constmap/consts.go
Normal file
32
internal/constmap/consts.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ `embed`
|
||||||
|
`text/template`
|
||||||
|
|
||||||
|
`github.com/oriser/regroup`
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
pfx string = "Param"
|
||||||
|
matchType string = "tlsUriParam"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed "tpl/consts_param_map.go.tpl"
|
||||||
|
constmapTplBytes []byte
|
||||||
|
tpl = template.Must(template.New("consts").Parse(string(constmapTplBytes)))
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// If we restructure, these paths will need to be changed.
|
||||||
|
// constsPath string = filepath.Join("..", "..", "consts.go")
|
||||||
|
// outPath string = filepath.Join("..", "..", "consts_param_map.go")
|
||||||
|
constsPath string = "consts.go"
|
||||||
|
outPath string = "consts_param_map.go"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// The most complex part about this pattern is it has to quote the backticks as their own string addition.
|
||||||
|
stripQuotesPtrn *regroup.ReGroup = regroup.MustCompile(`^(` + "`" + `(?P<m1>.+)` + "`" + `|"(?P<m2>.+)")$`)
|
||||||
|
)
|
94
internal/constmap/funcs.go
Normal file
94
internal/constmap/funcs.go
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
`errors`
|
||||||
|
`go/ast`
|
||||||
|
`go/token`
|
||||||
|
`log`
|
||||||
|
`strings`
|
||||||
|
|
||||||
|
`github.com/oriser/regroup`
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
getParamSpec takes an ast.Decl d and returns a slice of
|
||||||
|
ParamConst found in it.
|
||||||
|
|
||||||
|
If no ParamConst are found, foundParams will be nil.
|
||||||
|
*/
|
||||||
|
func getValueSpec(d ast.Decl) (foundParams []*ParamConst) {
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
var idx int
|
||||||
|
var gd *ast.GenDecl
|
||||||
|
var spec ast.Spec
|
||||||
|
var vs *ast.ValueSpec
|
||||||
|
var vsId *ast.Ident
|
||||||
|
var nm *ast.Ident
|
||||||
|
var bl *ast.BasicLit
|
||||||
|
|
||||||
|
if gd, ok = d.(*ast.GenDecl); !ok || gd.Tok != token.CONST {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, spec = range gd.Specs {
|
||||||
|
if vs, ok = spec.(*ast.ValueSpec); !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if vs.Type != nil {
|
||||||
|
if vsId, ok = vs.Type.(*ast.Ident); !ok || vsId.Name != matchType {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for idx, nm = range vs.Names {
|
||||||
|
if !strings.HasPrefix(nm.Name, pfx) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if bl, ok = vs.Values[idx].(*ast.BasicLit); !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
foundParams = append(
|
||||||
|
foundParams,
|
||||||
|
&ParamConst{
|
||||||
|
ConstName: nm.Name,
|
||||||
|
// UriParamName: bl.Value,
|
||||||
|
UriParamName: stripQuotes(bl.Value),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
stripQuotes removes Golang-AST defining quotes.
|
||||||
|
This probably doesn't work for multiline, but should be fine for our purposes.
|
||||||
|
*/
|
||||||
|
func stripQuotes(inStr string) (outStr string) {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var matches map[string]string
|
||||||
|
var nomchErr *regroup.NoMatchFoundError = new(regroup.NoMatchFoundError)
|
||||||
|
|
||||||
|
outStr = inStr
|
||||||
|
|
||||||
|
if matches, err = stripQuotesPtrn.Groups(inStr); err != nil {
|
||||||
|
if errors.As(err, &nomchErr) {
|
||||||
|
err = nil
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
log.Panicln(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range matches {
|
||||||
|
if v != "" {
|
||||||
|
outStr = v
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
57
internal/constmap/main.go
Normal file
57
internal/constmap/main.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
`bytes`
|
||||||
|
`fmt`
|
||||||
|
`go/ast`
|
||||||
|
`go/parser`
|
||||||
|
`go/token`
|
||||||
|
`log`
|
||||||
|
`os`
|
||||||
|
|
||||||
|
`r00t2.io/sysutils/paths`
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
DO NOT RUN THIS ANYWHERE BUT FROM WHERE <r00t2.io/cryptparse>/consts.go IS LOCATED.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// I *cannot believe* a library does not exist that will do this for me.
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var err error
|
||||||
|
var tfs *token.FileSet
|
||||||
|
var af *ast.File
|
||||||
|
var foundParams []*ParamConst
|
||||||
|
var paramConsts []*ParamConst
|
||||||
|
var buf *bytes.Buffer = new(bytes.Buffer)
|
||||||
|
|
||||||
|
if err = paths.RealPath(&constsPath); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = paths.RealPath(&outPath); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tfs = token.NewFileSet()
|
||||||
|
|
||||||
|
if af, err = parser.ParseFile(tfs, constsPath, nil, parser.AllErrors|parser.ParseComments); err != nil {
|
||||||
|
log.Panicln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, d := range af.Decls {
|
||||||
|
if foundParams = getValueSpec(d); foundParams == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
paramConsts = append(paramConsts, foundParams...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tpl.Execute(buf, paramConsts); err != nil {
|
||||||
|
log.Panicln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = os.WriteFile(outPath, buf.Bytes(), 0644); err != nil {
|
||||||
|
log.Panicln(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("++ Generated %s ++\n", outPath)
|
||||||
|
}
|
24
internal/constmap/tpl/consts_param_map.go.tpl
Normal file
24
internal/constmap/tpl/consts_param_map.go.tpl
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{{- /*gotype: r00t2.io/cryptparse/internal/constmap.ParamConsts*/ -}}
|
||||||
|
package cryptparse
|
||||||
|
|
||||||
|
/*
|
||||||
|
THIS FILE IS AUTOMATICALLY GENERATED.
|
||||||
|
DO NOT EDIT.
|
||||||
|
SEE internal/constmap/ FOR DETAILS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var (
|
||||||
|
// tlsUriParamStrMap contains a map of the constant string *name* of a tlsUriParam as mapped to its *value* (at time of generation).
|
||||||
|
tlsUriParamStrMap map[string]string = map[string]string{
|
||||||
|
{{- range $p := . }}
|
||||||
|
{{ printf "%#v" $p.ConstName }}: {{ printf "%#v" $p.UriParamName }},
|
||||||
|
{{- end }}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tlsUriStrParamMap contains a map of the *value* (at time of generation) of tlsUriParam constants to the constant string *name*.
|
||||||
|
tlsUriStrParamMap map[string]string = map[string]string{
|
||||||
|
{{- range $p := . }}
|
||||||
|
{{ printf "%#v" $p.UriParamName }}: {{ printf "%#v" $p.ConstName }},
|
||||||
|
{{- end }}
|
||||||
|
}
|
||||||
|
)
|
8
internal/constmap/types.go
Normal file
8
internal/constmap/types.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
type ParamConsts []*ParamConst
|
||||||
|
|
||||||
|
type ParamConst struct {
|
||||||
|
ConstName string
|
||||||
|
UriParamName string
|
||||||
|
}
|
4
tlsuri_test.go
Normal file
4
tlsuri_test.go
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package cryptparse
|
||||||
|
|
||||||
|
// TODO; see _testdata
|
||||||
|
// need to cover intermediates
|
76
types.go
76
types.go
@ -11,40 +11,76 @@ import (
|
|||||||
`github.com/Luzifer/go-dhparam`
|
`github.com/Luzifer/go-dhparam`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// tlsUriParam is an unexported type used to define TlsUri parameter names (and thus tags).
|
||||||
|
type tlsUriParam string
|
||||||
|
|
||||||
|
// tlsUriParams is a collection of tlsUriParam and their value(s).
|
||||||
|
type tlsUriParams map[tlsUriParam][]string
|
||||||
|
|
||||||
// PemBlocks is a combined set of multiple pem.Blocks.
|
// PemBlocks is a combined set of multiple pem.Blocks.
|
||||||
type PemBlocks []*pem.Block
|
type PemBlocks []*pem.Block
|
||||||
|
|
||||||
// TlsFlat provides an easy structure to marshal/unmarshal a tls.Config from/to a data structure (JSON, XML, etc.).
|
// TlsFlat provides an easy structure to marshal/unmarshal a tls.Config and/or a TlsUri from/to a data structure (JSON, XML, etc.).
|
||||||
type TlsFlat struct {
|
type TlsFlat struct {
|
||||||
XMLName xml.Name `xml:"tlsConfig" json:"-" yaml:"-" toml:"-"`
|
XMLName xml.Name `xml:"tlsConfig" json:"-" yaml:"-" toml:"-"`
|
||||||
// SniName represents the expected Server Name Indicator's name. See TlsUriParamSni.
|
// Host is the host name. It may or may not be the same as SniName, and may be an empty string.
|
||||||
SniName string `json:"sni_name" toml:"SNIName" yaml:"SNI Name" xml:"sniName,attr" required:"true" validate:"required"`
|
Host string `json:"host,omitempty" toml:"Host,omitempty" yaml:"Host,omitempty" xml:"host,attr,omitempty" tlsUri:"-"` // No reflection is done as it's directly managed.
|
||||||
// SkipVerify, if true, will bypass certificate verification. You generally should not enable this. See TlsUriParamNoVerify.
|
// Port is the port number, if specified. Only relevant for listeners/clients and TlsUri.
|
||||||
|
Port *uint16 `json:"port,omitempty" toml:"Port,omitempty" yaml:"Port,omitempty" xml:"port,attr,omitempty" tlsUri:"-"` // No reflection is done as it's directly managed.
|
||||||
|
// CaFiles contains filepaths to CA certificates/"trust anchors" in PEM format. They may be combined. See ParamCa.
|
||||||
|
CaFiles []string `json:"ca_files,omitempty" toml:"CaFiles,omitempty" yaml:"CA Files,omitempty" xml:"roots>ca,omitempty" tlsUri:"ParamCa" validate:"omitempty,dive,filepath"`
|
||||||
|
// Certs contains 0 or more TlsFlatCert certificate definitions. See ParamCert and ParamKey as well.
|
||||||
|
Certs []*TlsFlatCert `json:"certs,omitempty" toml:"Certs,omitempty" yaml:"Certificates,omitempty" xml:"certs>cert,omitempty" validate:"omitempty,dive"`
|
||||||
|
// CipherSuites represents desired ciphers/cipher suites for this TLS environment. See ParamCipher.
|
||||||
|
CipherSuites []string `json:"cipher_suites,omitempty" toml:"CipherSuites,omitempty" yaml:"Cipher Suites,omitempty" xml:"ciphers,omitempty" tlsUri:"ParamCipher" validate:"omitempty,dive"`
|
||||||
|
// Curves specifies desired cryptographic curves to be used. See ParamCurve.
|
||||||
|
Curves []string `json:"curves,omitempty" toml:"Curves,omitempty" yaml:"Curves,omitempty" xml:"curves>curve,omitempty" tlsUri:"ParamCurve" validate:"omitempty,dive"`
|
||||||
|
// IgnoreMissing, if true, specifies that missing files should be ignored instead of throwing an error.
|
||||||
|
IgnoreMissing bool `json:"ignore_missing,omitempty" toml:"IgnoreMissing,omitempty" yaml:"Ignore Missing,omitempty" xml:"ignoreMissing,attr,omitempty" tlsUri:"ParamIgnoreMissing"`
|
||||||
|
/*
|
||||||
|
Keylog specifies an SSLKEYLOGFILE.
|
||||||
|
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
!! DO NOT, UNDER ANY CIRCUMSTANCES, ENABLE THIS UNLESS YOU ARE !!
|
||||||
|
!! ABSOLUTELY SURE WHAT YOU ARE DOING. !!
|
||||||
|
!! IT SEVERELY COMPROMISES SECURITY !!
|
||||||
|
!! AND IS ONLY INTENDED FOR DEBUGGING PURPOSES! !!
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
|
||||||
|
See ParamKeylog for details and special values.
|
||||||
|
*/
|
||||||
|
Keylog *string `json:"keylog,omitempty" toml:"Keylog,omitempty" yaml:"Keylog,omitempty" xml:"keylog,attr,omitempty" validate:"omitempty,dive"`
|
||||||
|
// MaxTlsProtocol specifies the maximum TLS version. See ParamMaxTls.
|
||||||
|
MaxTlsProtocol *string `json:"max_tls_protocol,omitempty" xml:"maxTlsProtocol,attr,omitempty" yaml:"MaxTlsProtocol,omitempty" toml:"MaxTlsProtocol,omitempty" tlsUri:"ParamMaxTls"`
|
||||||
|
// MinTlsProtocol specifies the minimum TLS version. See ParamMinTls.
|
||||||
|
MinTlsProtocol *string `json:"min_tls_protocol,omitempty" xml:"minTlsProtocol,attr,omitempty" yaml:"MinTlsProtocol,omitempty" toml:"MinTlsProtocol,omitempty" tlsUri:"ParamMinTls"`
|
||||||
|
// MutualTlsCAs specify path(s) to CA certificates/"trust anchors" in PEM format. See ParamMtlsCa.
|
||||||
|
MutualTlsCAs []string `json:"mtls_ca,omitempty" toml:"mTLSRoots,omitempty" yaml:"MTLS CA Files,omitempty" xml:"mTlsRoots>ca,omitempty" tlsUri:"ParamMtlsCa"`
|
||||||
|
// MutualTls specifies mutual TLS and, if enabled, what type/mode/level of required validation. See ParamMtlsMode.
|
||||||
|
MutualTls *string `json:"mtls_auth" toml:"mTLS,omitempty" yaml:"mTLS Type,omitempty" xml:"mtlsAuth,attr,omitempty" tlsUri:"ParamMtlsMode"`
|
||||||
|
// NetMode is the "network type" as found in e.g. net.Dial. See ParamNet for details.
|
||||||
|
NetMode *string
|
||||||
|
// SkipVerify, if true, will bypass certificate verification. You generally should not enable this. See ParamNoVerify.
|
||||||
SkipVerify bool `json:"skip_verify,omitempty" toml:"SkipVerify,omitempty" yaml:"Skip Verification,omitempty" xml:"skipVerify,attr,omitempty"`
|
SkipVerify bool `json:"skip_verify,omitempty" toml:"SkipVerify,omitempty" yaml:"Skip Verification,omitempty" xml:"skipVerify,attr,omitempty"`
|
||||||
// Certs contains 0 or more TlsFlatCert certificate definitions. See TlsUriParamCert and TlsUriParamKey as well.
|
/*
|
||||||
Certs []*TlsFlatCert `json:"certs,omitempty" toml:"Certs,omitempty" yaml:"Certificates,omitempty" xml:"certs>cert,omitempty"validate:"omitempty,dive"`
|
SniName represents the expected Server Name Indicator's name. If not nil, Host will be used to connect/listen
|
||||||
// CaFiles contains filepaths to CA certificates/"trust anchors" in PEM format. They may be combined. See TlsUriParamCa.
|
and this name will be used for certificate validation/verification.
|
||||||
CaFiles []string `json:"ca_files,omitempty" toml:"CaFiles,omitempty" yaml:"CA Files,omitempty" xml:"roots>ca,omitempty" validate:"omitempty,dive,filepath"`
|
See ParamSni.
|
||||||
// CipherSuites represents desired ciphers/cipher suites for this TLS environment. See TlsUriParamCipher.
|
*/
|
||||||
CipherSuites []string `json:"cipher_suites,omitempty" toml:"CipherSuites,omitempty" yaml:"Cipher Suites,omitempty" xml:"ciphers,omitempty"`
|
SniName *string `json:"sni_name" toml:"SNIName" yaml:"SNI Name" xml:"sniName,attr" tlsUri:"ParamSni" required:"true" validate:"required"`
|
||||||
// Curves specifies desired cryptographic curves to be used. See TlsUriParamCurve.
|
|
||||||
Curves []string `json:"curves,omitempty" xml:"curves>curve,omitempty" yaml:"Curves,omitempty" toml:"Curves,omitempty" validate:"omitempty,dive"`
|
|
||||||
// MinTlsProtocol specifies the minimum TLS version. See TlsUriParamMinTls.
|
|
||||||
MinTlsProtocol *string `json:"min_tls_protocol,omitempty" xml:"minTlsProtocol,attr,omitempty" yaml:"MinTlsProtocol,omitempty" toml:"MinTlsProtocol,omitempty"`
|
|
||||||
// MaxTlsProtocol specifies the maximum TLS version. See TlsUriParamMaxTls.
|
|
||||||
MaxTlsProtocol *string `json:"max_tls_protocol,omitempty" xml:"maxTlsProtocol,attr,omitempty" yaml:"MaxTlsProtocol,omitempty" toml:"MaxTlsProtocol,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TlsFlatCert represents a certificate (and, possibly, paired key).
|
// TlsFlatCert represents a certificate (and, possibly, paired key).
|
||||||
type TlsFlatCert struct {
|
type TlsFlatCert struct {
|
||||||
XMLName xml.Name `xml:"cert" json:"-" yaml:"-" toml:"-"`
|
XMLName xml.Name `xml:"cert" json:"-" yaml:"-" toml:"-"`
|
||||||
// KeyFile is a filepath to a PEM-encoded key file. See TlsUriParamKey.
|
// KeyFile is a filepath to a PEM-encoded key file. See ParamKey.
|
||||||
KeyFile *string `json:"key,omitempty" xml:"key,attr,omitempty" yaml:"Key,omitempty" toml:"Key,omitempty" validate:"omitempty,filepath"`
|
KeyFile *string `json:"key,omitempty" xml:"key,attr,omitempty" yaml:"Key,omitempty" toml:"Key,omitempty" tlsUri:"ParamKey" validate:"omitempty,filepath"`
|
||||||
// CertFile is a filepath to a PEM-encoded certificate file. See TlsUriParamCert.
|
// CertFile is a filepath to a PEM-encoded certificate file. See ParamCert.
|
||||||
CertFile string `json:"cert" xml:",chardata" yaml:"Certificate" toml:"Certificate" required:"true" validate:"required,filepath"`
|
CertFile string `json:"cert" xml:",chardata" yaml:"Certificate" toml:"Certificate" required:"true" tlsUri:"ParamCert" validate:"required,filepath"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TlsPkiChain contains a whole X.509 PKI chain -- Root CA(s) (trust anchors) which sign Intermediate(s) which sign Certificate(s).
|
// TlsPkiChain contains a whole X.509 PKI chain -- Root CA(s) (trust anchors) which sign Intermediate(s) which sign Certificate(s).
|
||||||
|
// TODO
|
||||||
type TlsPkiChain struct {
|
type TlsPkiChain struct {
|
||||||
/*
|
/*
|
||||||
Roots are all trust anchors/root certificates.
|
Roots are all trust anchors/root certificates.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user