diff --git a/cryptparse/errs.go b/cryptparse/errs.go index 4debd81..1cdf056 100644 --- a/cryptparse/errs.go +++ b/cryptparse/errs.go @@ -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") ) diff --git a/cryptparse/funcs.go b/cryptparse/funcs.go index e9aeec2..142778d 100644 --- a/cryptparse/funcs.go +++ b/cryptparse/funcs.go @@ -142,6 +142,56 @@ func ParseTlsCipher(s string) (cipherSuite uint16, err error) { return } +// ParseTlsCipherStrict is like ParseTlsCipher, but an 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.VersionName(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 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) {