Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cb0403e08
|
||
|
|
0318a9759b
|
||
|
|
1a93d5d9f3
|
||
|
|
5dc944cf21
|
||
|
|
77a85a4f84
|
@@ -13,13 +13,14 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
MaxTlsCipher uint16 = tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
MaxCurveId tls.CurveID = tls.X25519 // 29
|
||||
MinTlsVer uint16 = tls.VersionSSL30
|
||||
MaxTlsVer uint16 = tls.VersionTLS13
|
||||
MaxTlsCipher uint16 = tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
|
||||
MaxCurveId tls.CurveID = tls.X25519 // 29
|
||||
MinTlsVer uint16 = tls.VersionSSL30
|
||||
MaxTlsVer uint16 = tls.VersionTLS13
|
||||
DefaultNetType string = "tcp"
|
||||
)
|
||||
|
||||
// TlsUriParam* specifiy URL query parameters to parse a tls:// URI.
|
||||
// TlsUriParam* specifiy URL query parameters to parse a tls:// URI, and are used by TlsUri methods.
|
||||
const (
|
||||
/*
|
||||
TlsUriParamCa specifies a path to a CA certificate PEM-encded DER file.
|
||||
@@ -110,6 +111,16 @@ const (
|
||||
Only the first defined instance is parsed.
|
||||
*/
|
||||
TlsUriParamMaxTls string = "max_tls"
|
||||
/*
|
||||
TlsUriParamNet is used by TlsUri.ToConn and TlsUri.ToTlsConn to explicitly specify a network.
|
||||
|
||||
The default is "tcp".
|
||||
|
||||
See net.Dial()'s "network" parameter for valid network types.
|
||||
|
||||
Only the first defined instance is parsed.
|
||||
*/
|
||||
TlsUriParamNet string = "net"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -5,8 +5,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBadTlsCipher error = errors.New("invalid TLS cipher suite")
|
||||
ErrBadTlsCurve error = errors.New("invalid TLS curve")
|
||||
ErrBadTlsVer error = errors.New("invalid TLS version")
|
||||
ErrUnknownKey error = errors.New("unknown key type")
|
||||
ErrBadTlsCipher error = errors.New("invalid TLS cipher suite")
|
||||
ErrBadTlsCurve error = errors.New("invalid TLS curve")
|
||||
ErrBadTlsVer error = errors.New("invalid TLS version")
|
||||
ErrUnknownCipher error = errors.New("unknown TLS cipher")
|
||||
ErrUnknownKey error = errors.New("unknown key type")
|
||||
)
|
||||
|
||||
@@ -128,7 +128,7 @@ func ParseTlsCipher(s string) (cipherSuite uint16, err error) {
|
||||
if tlsCipherNmToUint == nil {
|
||||
tlsCipherNmToUint = make(map[string]uint16)
|
||||
for i = 0; i <= MaxTlsCipher; i++ {
|
||||
if nm = tls.VersionName(i); !strings.HasPrefix(nm, "0x") {
|
||||
if nm = tls.CipherSuiteName(i); !strings.HasPrefix(nm, "0x") {
|
||||
tlsCipherNmToUint[nm] = i
|
||||
}
|
||||
}
|
||||
@@ -142,6 +142,56 @@ func ParseTlsCipher(s string) (cipherSuite uint16, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// ParseTlsCipherStrict is like ParseTlsCipher, but an ErrBadTlsCipher or ErrUnknownCipher error will be raised if no matching cipher is found.
|
||||
func ParseTlsCipherStrict(s string) (cipherSuite uint16, err error) {
|
||||
|
||||
var nm string
|
||||
var n uint64
|
||||
var i uint16
|
||||
var ok bool
|
||||
|
||||
if n, err = strconv.ParseUint(s, 10, 16); err != nil {
|
||||
if errors.Is(err, strconv.ErrSyntax) {
|
||||
// It's a name; parse below.
|
||||
err = nil
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// It's a number.
|
||||
if nm = tls.CipherSuiteName(uint16(n)); strings.HasPrefix(nm, "0x") {
|
||||
// ...but invalid.
|
||||
err = ErrBadTlsCipher
|
||||
return
|
||||
} else {
|
||||
// Valid (as number). Return it.
|
||||
cipherSuite = uint16(n)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
s = strings.ToUpper(s)
|
||||
s = strings.ReplaceAll(s, " ", "_")
|
||||
|
||||
// We build a dynamic map of cipher suite names to uint16s (if not already created).
|
||||
if tlsCipherNmToUint == nil {
|
||||
tlsCipherNmToUint = make(map[string]uint16)
|
||||
for i = 0; i <= MaxTlsCipher; i++ {
|
||||
if nm = tls.CipherSuiteName(i); !strings.HasPrefix(nm, "0x") {
|
||||
tlsCipherNmToUint[nm] = i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if i, ok = tlsCipherNmToUint[s]; ok {
|
||||
cipherSuite = i
|
||||
} else {
|
||||
err = ErrUnknownCipher
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
ParseTlsCiphers parses s as a comma-separated list of cipher suite names/integers and returns a slice of suites.
|
||||
|
||||
@@ -198,6 +248,31 @@ func ParseTlsCipherSuite(s string) (cipherSuite *tls.CipherSuite, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// ParseTlsCipherSuiteStrict is like ParseTlsCipherSuite, but an ErrBadTlsCipher or ErrUnknownCipher error will be raised if no matching cipher is found.
|
||||
func ParseTlsCipherSuiteStrict(s string) (cipherSuite *tls.CipherSuite, err error) {
|
||||
|
||||
var cipherId uint16
|
||||
|
||||
if cipherId, err = ParseTlsCipherStrict(s); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, v := range tls.CipherSuites() {
|
||||
if v.ID == cipherId {
|
||||
cipherSuite = v
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, v := range tls.InsecureCipherSuites() {
|
||||
if v.ID == cipherId {
|
||||
cipherSuite = v
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ParseTlsCipherSuites is like ParseTlsCiphers but returns a []*tls.CipherSuite instead of a []uint16 of TLS cipher identifiers.
|
||||
func ParseTlsCipherSuites(s string) (cipherSuites []*tls.CipherSuite, err error) {
|
||||
|
||||
|
||||
37
cryptparse/funcs_test.go
Normal file
37
cryptparse/funcs_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package cryptparse
|
||||
|
||||
import (
|
||||
`crypto/tls`
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCiphers(t *testing.T) {
|
||||
|
||||
var err error
|
||||
var cs *tls.CipherSuite
|
||||
|
||||
// Good ciphers
|
||||
for _, cn := range []string{
|
||||
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
|
||||
"tls ecdhe ecdsa with chacha20 poly1305 sha256",
|
||||
} {
|
||||
if cs, err = ParseTlsCipherSuiteStrict(cn); err != nil {
|
||||
t.Fatalf("ERROR parsing good cipher '%s': %v", cn, err)
|
||||
}
|
||||
if cs.Name != cn {
|
||||
t.Logf("Cipher name change: '%s' => '%s'", cn, cs.Name)
|
||||
}
|
||||
t.Logf("Cipher for '%s':\n%#v", cn, cs)
|
||||
}
|
||||
|
||||
// Bad ciphers
|
||||
for _, cn := range []string{
|
||||
"TLS_BAD_CIPHER",
|
||||
} {
|
||||
if cs, err = ParseTlsCipherSuiteStrict(cn); err == nil {
|
||||
t.Fatalf("ERROR parsing bad cipher '%s'; err is nil", cn)
|
||||
}
|
||||
}
|
||||
|
||||
_ = cs
|
||||
}
|
||||
@@ -3,11 +3,68 @@ package cryptparse
|
||||
import (
|
||||
`crypto`
|
||||
`crypto/tls`
|
||||
`net`
|
||||
`net/url`
|
||||
`os`
|
||||
`strings`
|
||||
)
|
||||
|
||||
/*
|
||||
WithConn returns a (crypto/)tls.Conn from an existing/already dialed net.Conn.
|
||||
|
||||
underlying should be a "bare" net.Conn; behavior is undefined/unknown if the underlying conn is already a (crypto/)tls.Conn.
|
||||
*/
|
||||
func (t *TlsUri) WithConn(underlying net.Conn) (conn *tls.Conn, err error) {
|
||||
|
||||
var cfg *tls.Config
|
||||
|
||||
if cfg, err = t.ToTlsConfig(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
conn = tls.Client(underlying, cfg)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
ToConn returns a "bare" net.Conn (already dialed) from a TlsUri.
|
||||
|
||||
Note that this does NOT include the TLS configured or initialized; use TlsUri.ToTlsConn for that.
|
||||
(A (crypto/)tls.Conn conforms to net.Conn.)
|
||||
|
||||
An error will be returned if no port is explicitly defined in the TlsUri.
|
||||
*/
|
||||
func (t *TlsUri) ToConn() (conn net.Conn, err error) {
|
||||
|
||||
var ok bool
|
||||
var connHost string
|
||||
var params map[string][]string
|
||||
var netType string = DefaultNetType
|
||||
|
||||
params = t.Query()
|
||||
|
||||
if params != nil {
|
||||
if _, ok = params[TlsUriParamNet]; ok {
|
||||
netType = params[TlsUriParamNet][0]
|
||||
}
|
||||
}
|
||||
netType = strings.ToLower(netType)
|
||||
|
||||
switch netType {
|
||||
case "unix", "unixgram", "unixpacket":
|
||||
connHost = t.Path
|
||||
default:
|
||||
connHost = t.Host
|
||||
}
|
||||
|
||||
if conn, err = net.Dial(netType, connHost); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
ToTlsConfig returns a *tls.Config from a TlsUri.
|
||||
|
||||
@@ -22,6 +79,46 @@ func (t *TlsUri) ToTlsConfig() (cfg *tls.Config, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
ToTlsConn returns a (crypto/)tls.Conn (already dialed) from a TlsUri.
|
||||
|
||||
An error will be returned if no port is explicitly defined in the TlsUri.
|
||||
*/
|
||||
func (t *TlsUri) ToTlsConn() (conn *tls.Conn, err error) {
|
||||
|
||||
var ok bool
|
||||
var cfg *tls.Config
|
||||
var connHost string
|
||||
var params map[string][]string
|
||||
var netType string = DefaultNetType
|
||||
|
||||
if cfg, err = t.ToTlsConfig(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
params = t.Query()
|
||||
|
||||
if params != nil {
|
||||
if _, ok = params[TlsUriParamNet]; ok {
|
||||
netType = params[TlsUriParamNet][0]
|
||||
}
|
||||
}
|
||||
netType = strings.ToLower(netType)
|
||||
|
||||
switch netType {
|
||||
case "unix", "unixgram", "unixpacket":
|
||||
connHost = t.Path
|
||||
default:
|
||||
connHost = t.Host
|
||||
}
|
||||
|
||||
if conn, err = tls.Dial(netType, connHost, cfg); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ToTlsFlat returns a *TlsFlat from a TlsUri.
|
||||
func (t *TlsUri) ToTlsFlat() (tlsFlat *TlsFlat, err error) {
|
||||
|
||||
@@ -146,7 +243,7 @@ func (t *TlsUri) ToTlsFlat() (tlsFlat *TlsFlat, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// ToURL returns the *url.URL representation of a TlsUri.
|
||||
// ToURL returns the *url.URL representation of a TlsUri. Note that the params will remain, so remove them explicitly if needed.
|
||||
func (t *TlsUri) ToURL() (u *url.URL) {
|
||||
|
||||
if t == nil {
|
||||
|
||||
@@ -17,6 +17,34 @@ import (
|
||||
`r00t2.io/sysutils/paths`
|
||||
)
|
||||
|
||||
/*
|
||||
DefEnv operates like Python's .get() method on dicts (maps);
|
||||
if the environment variable specified by key does not exist/is not specified,
|
||||
then the value specified by fallback will be returned instead
|
||||
otherwise key's value is returned.
|
||||
*/
|
||||
func DefEnv(key, fallback string) (value string) {
|
||||
|
||||
var exists bool
|
||||
|
||||
if value, exists = os.LookupEnv(key); !exists {
|
||||
value = fallback
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DefEnvBlank is like DefEnv but will ADDITIONALLY/ALSO apply fallback if key is *defined/exists but is an empty string*.
|
||||
func DefEnvBlank(key, fallback string) (value string) {
|
||||
|
||||
value = DefEnv(key, fallback)
|
||||
if value == "" {
|
||||
value = fallback
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// GetEnvMap returns a map of all environment variables. All values are strings.
|
||||
func GetEnvMap() (envVars map[string]string) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user