package inspect import ( "context" "crypto/tls" "errors" "net" "net/url" "os" "strings" "time" "github.com/mattn/go-isatty" "github.com/spf13/cobra" "src.lwithers.me.uk/go/rsa/pkg/inspect" ) var ( outputFormat string = "text" display func(src string, info []inspect.Info) ) // Register the "keygen" subcommand. func Register(root *cobra.Command) { cmd := &cobra.Command{ Use: "inspect", Short: "Inspect files and TLS servers", RunE: Inspect, Args: cobra.MinimumNArgs(1), } if isatty.IsTerminal(1) { outputFormat = "coloured" } cmd.Flags().StringVarP(&outputFormat, "output", "o", outputFormat, "Output format (coloured, text, json, yaml)") root.AddCommand(cmd) } // Keygen will generate a new RSA private key and save it to a file. func Inspect(cmd *cobra.Command, args []string) error { switch outputFormat { case "coloured": display = displayColoured case "text": display = displayText case "json": display = displayJSON case "yaml": display = displayYAML default: return errors.New("invalid --output format (try: coloured, text, json or yaml)") } for _, arg := range args { switch { case arg == "-": inspectStdin() case strings.HasPrefix(arg, "https://"): inspectHTTPS(arg) case strings.IndexByte(arg, ':') != 0: _, _, err := net.SplitHostPort(arg) if err == nil { inspectHost(arg) break } fallthrough default: inspectFile(arg) } } return nil } func inspectStdin() { // TODO } func inspectHTTPS(addr string) { u, err := url.Parse(addr) if err != nil { // TODO } inspectHost(u.Host) // TODO: need to add default port } func inspectHost(addr string) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // TODO: context dial conn, err := tls.Dial("tcp", addr, &tls.Config{ // we want to skip verification so we can inspect invalid certs too InsecureSkipVerify: true, // we only enable RSA ciphersuites, but we do allow old ones, again // so that we can report about bad / invalid servers CipherSuites: []uint16{ tls.TLS_RSA_WITH_RC4_128_SHA, tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, tls.TLS_RSA_WITH_AES_128_CBC_SHA, tls.TLS_RSA_WITH_AES_256_CBC_SHA, tls.TLS_RSA_WITH_AES_128_CBC_SHA256, tls.TLS_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384, tls.TLS_CHACHA20_POLY1305_SHA256, }, // we enable TLSv1.0 so that we can complain about it MinVersion: tls.VersionTLS10, }) if err != nil { // TODO } if err := conn.HandshakeContext(ctx); err != nil { // TODO } display(addr, inspect.ConnectionState(conn.ConnectionState())) } func inspectFile(filename string) { raw, err := os.ReadFile(filename) if err != nil { // TODO } info := inspect.LoadPEM(raw) if len(info) == 0 { // TODO } display(filename, info) } func displayText(src string, info []inspect.Info) { } func displayJSON(src string, info []inspect.Info) { } func displayYAML(src string, info []inspect.Info) { }