diff --git a/badkeys/badkeys.go b/badkeys/badkeys.go index 37dc5fc..2a48d15 100644 --- a/badkeys/badkeys.go +++ b/badkeys/badkeys.go @@ -6,86 +6,10 @@ import ( "io" "os" "path/filepath" - - "github.com/runZeroInc/excrypto/crypto/dsa" - "github.com/runZeroInc/excrypto/crypto/ecdh" - "github.com/runZeroInc/excrypto/crypto/ecdsa" - "github.com/runZeroInc/excrypto/crypto/ed25519" - - "github.com/runZeroInc/excrypto/crypto/rsa" - "github.com/runZeroInc/excrypto/crypto/sha256" - "github.com/runZeroInc/excrypto/crypto/x509" - "github.com/runZeroInc/excrypto/x/crypto/ssh" - - stddsa "crypto/dsa" - stdecdh "crypto/ecdh" - stdecdsa "crypto/ecdsa" - stded25519 "crypto/ed25519" - stdrsa "crypto/rsa" - - stdssh "golang.org/x/crypto/ssh" ) const BadKeysMetaURL = "https://update.badkeys.info/v0/badkeysdata.json" -// PrefixFromPublicKey implements the badkeys `blocklistmaker` hashing method -func PrefixFromPublicKey(pub any) ([]byte, error) { - var rawb []byte - switch pub := pub.(type) { - - case ssh.PublicKey: - if cpk, ok := pub.(ssh.CryptoPublicKey); ok { - return PrefixFromPublicKey(cpk.CryptoPublicKey()) - } - return nil, fmt.Errorf("unsupported excrypto ssh public key: %v", pub.Type()) - case stdssh.PublicKey: - if cpk, ok := pub.(stdssh.CryptoPublicKey); ok { - return PrefixFromPublicKey(cpk.CryptoPublicKey()) - } - return nil, fmt.Errorf("unsupported stdlib ssh public key: %v", pub.Type()) - - case *rsa.PublicKey: - rawb = pub.N.Bytes() - case *stdrsa.PublicKey: - rawb = pub.N.Bytes() - - case *ecdsa.PublicKey: - rawb = pub.X.Bytes() - case *stdecdsa.PublicKey: - rawb = pub.X.Bytes() - - case ed25519.PublicKey: - rawb = pub - case stded25519.PublicKey: - rawb = pub - - case x509.X25519PublicKey: - rawb = pub - /* - // Not defined by stdlib - case stdx509.X25519PublicKey: - rawb = pub - */ - - case *ecdh.PublicKey: - rawb = pub.Bytes() // Verify - case *stdecdh.PublicKey: - rawb = pub.Bytes() // Verify - - case *dsa.PublicKey: - rawb = pub.Y.Bytes() - case *stddsa.PublicKey: - rawb = pub.Y.Bytes() - - case nil: - return nil, fmt.Errorf("unsupported nil key") - default: - return nil, fmt.Errorf("unsupported key: %T", pub) - } - sha256sum := sha256.Sum256(rawb) - return sha256sum[0:15], nil -} - // GetExecutablePath returns the full path to the running binary func GetExecutablePath() string { filename, _ := os.Executable() diff --git a/badkeys/parser.go b/badkeys/parser.go new file mode 100644 index 0000000..6222221 --- /dev/null +++ b/badkeys/parser.go @@ -0,0 +1,83 @@ +package badkeys + +import ( + stddsa "crypto/dsa" + stdecdh "crypto/ecdh" + stdecdsa "crypto/ecdsa" + stded25519 "crypto/ed25519" + stdrsa "crypto/rsa" + "encoding/base64" + "fmt" + + "github.com/runZeroInc/excrypto/crypto/dsa" + "github.com/runZeroInc/excrypto/crypto/ecdh" + "github.com/runZeroInc/excrypto/crypto/ecdsa" + "github.com/runZeroInc/excrypto/crypto/ed25519" + "github.com/runZeroInc/excrypto/crypto/rsa" + "github.com/runZeroInc/excrypto/crypto/sha256" + "github.com/runZeroInc/excrypto/crypto/x509" + "github.com/runZeroInc/excrypto/x/crypto/ssh" + stdssh "golang.org/x/crypto/ssh" +) + +// PrefixFromPublicKey implements the badkeys `blocklistmaker` hashing method +func PrefixFromPublicKey(pub any) ([]byte, error) { + var rawb []byte + switch pub := pub.(type) { + + case ssh.PublicKey: + if cpk, ok := pub.(ssh.CryptoPublicKey); ok { + return PrefixFromPublicKey(cpk.CryptoPublicKey()) + } + return nil, fmt.Errorf("unsupported excrypto ssh public key: %v", pub.Type()) + case stdssh.PublicKey: + if cpk, ok := pub.(stdssh.CryptoPublicKey); ok { + return PrefixFromPublicKey(cpk.CryptoPublicKey()) + } + return nil, fmt.Errorf("unsupported stdlib ssh public key: %v", pub.Type()) + + case *rsa.PublicKey: + rawb = pub.N.Bytes() + case *stdrsa.PublicKey: + rawb = pub.N.Bytes() + + case *ecdsa.PublicKey: + rawb = pub.X.Bytes() + case *stdecdsa.PublicKey: + rawb = pub.X.Bytes() + + case ed25519.PublicKey: + rawb = pub + case stded25519.PublicKey: + rawb = pub + + case x509.X25519PublicKey: + rawb = pub + /* + // Not defined by stdlib + case stdx509.X25519PublicKey: + rawb = pub + */ + + case *ecdh.PublicKey: + rawb = pub.Bytes() // Verify + case *stdecdh.PublicKey: + rawb = pub.Bytes() // Verify + + case *dsa.PublicKey: + rawb = pub.Y.Bytes() + case *stddsa.PublicKey: + rawb = pub.Y.Bytes() + + case nil: + return nil, fmt.Errorf("unsupported nil key") + default: + return nil, fmt.Errorf("unsupported key: %T", pub) + } + sha256sum := sha256.Sum256(rawb) + return sha256sum[0:15], nil +} + +func PrefixToString(prefix []byte) string { + return base64.RawStdEncoding.EncodeToString(prefix) +} diff --git a/badkeys/parser_test.go b/badkeys/parser_test.go new file mode 100644 index 0000000..877e106 --- /dev/null +++ b/badkeys/parser_test.go @@ -0,0 +1,151 @@ +package badkeys + +import ( + "crypto/ecdsa" + "crypto/ed25519" + "encoding/pem" + "testing" + + "github.com/runZeroInc/excrypto/crypto/rsa" + "github.com/runZeroInc/excrypto/crypto/x509" + cases "github.com/runZeroInc/sshamble/badkeys/tests" + "go.yaml.in/yaml/v3" +) + +func getTests() []testCase { + files, err := cases.TestData.ReadDir(".") + if err != nil { + panic("readdir: " + err.Error()) + } + var testCases []testCase + for _, f := range files { + if f.IsDir() { + continue + } + data, err := cases.TestData.ReadFile(f.Name()) + if err != nil { + panic(err) + } + tc, err := parseTestCase(f.Name(), data) + if err != nil { + panic(err) + } + testCases = append(testCases, tc) + } + return testCases +} + +func parseTestCase(name string, data []byte) (testCase, error) { + tc := testCase{} + err := yaml.Unmarshal(data, &tc) + tc.File = name + return tc, err +} + +type testCase struct { + File string `yaml:"-"` + Name string `yaml:"name"` + KeyType string `yaml:"keyType"` + Comment string `yaml:"comment"` + Key string `yaml:"key"` +} + +func TestSuite(t *testing.T) { + testCases := getTests() + for _, tc := range testCases { + t.Run(tc.File, func(t *testing.T) { + runTestCase(t, tc) + }) + } +} + +func runTestCase(t *testing.T, tc testCase) { + t.Logf("running test case: %s (%s)", tc.Name, tc.File) + b, _ := pem.Decode([]byte(tc.Key)) + if b == nil { + t.Fatalf("failed to decode pem: no block in %s", tc.Key) + } + + var ktype string + decodeErrs := []string{} + var privateKey, publicKey any + var priOK, pubOK bool + + privateKey, err := x509.ParsePKCS1PrivateKey(b.Bytes) + if err != nil { + decodeErrs = append(decodeErrs, "priv-pkcs1: "+err.Error()) + privateKey, err = x509.ParsePKCS8PrivateKey(b.Bytes) + if err != nil { + decodeErrs = append(decodeErrs, "priv-pkcs8: "+err.Error()) + } else { + priOK = true + } + } else { + priOK = true + } + switch pk := privateKey.(type) { + case *rsa.PrivateKey: + ktype = "rsa" + publicKey = &pk.PublicKey + privateKey = pk + pubOK = true + case *ecdsa.PrivateKey: + ktype = "ecdsa" + publicKey = &pk.PublicKey + privateKey = pk + pubOK = true + case *ed25519.PrivateKey: + ktype = "ed25519" + publicKey = pk.Public() + privateKey = pk + pubOK = true + } + + if !pubOK { + publicKey, err = x509.ParsePKIXPublicKey(b.Bytes) + if err != nil { + decodeErrs = append(decodeErrs, "pub-pkix: "+err.Error()) + publicKey, err = x509.ParsePKCS1PublicKey(b.Bytes) + if err != nil { + decodeErrs = append(decodeErrs, "pub-pkcs1: "+err.Error()) + } + } else { + pubOK = true + } + } else { + pubOK = true + } + + switch pk := publicKey.(type) { + case *rsa.PublicKey: + ktype = "rsa" + publicKey = pk + case *ecdsa.PublicKey: + ktype = "ecdsa" + publicKey = pk + case *ed25519.PublicKey: + ktype = "ed25519" + publicKey = pk + } + + t.Logf("pub:%v, priv:%v, type:%v, priv:%v, pub:%v", pubOK, priOK, ktype, privateKey, publicKey) + + if !pubOK { + for _, e := range decodeErrs { + t.Logf("decode: %s", e) + } + t.Fatalf("failed to parse public key, errors: %+v", decodeErrs) + } + + if ktype != tc.KeyType { + t.Fatalf("key type mismatch: got %v, want %v", ktype, tc.KeyType) + } + + praw, err := PrefixFromPublicKey(publicKey) + if err != nil { + t.Fatalf("failed to get raw prefix: %v", err) + } + + penc := PrefixToString(praw) + t.Logf("badkeys.hash: %s", penc) +} diff --git a/badkeys/tests/embed.go b/badkeys/tests/embed.go new file mode 100644 index 0000000..a4ec341 --- /dev/null +++ b/badkeys/tests/embed.go @@ -0,0 +1,9 @@ +package tables + +import ( + "embed" +) + +//go:embed all:*.yml + +var TestData embed.FS diff --git a/badkeys/tests/rsa_large_e.yml b/badkeys/tests/rsa_large_e.yml new file mode 100644 index 0000000..65d3d78 --- /dev/null +++ b/badkeys/tests/rsa_large_e.yml @@ -0,0 +1,65 @@ +name: RSA Large Exponent Key +comment: This RSA key has a public exponent larger than 65537. Such keys are + uncommon and may not be supported by all software. +keyType: rsa +key: |- + -----BEGIN PRIVATE KEY----- + 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 + liMr1WQDzDn7Fq3yJLT964q69JExvw== + -----END PRIVATE KEY----- diff --git a/go.mod b/go.mod index 2881059..d42673c 100644 --- a/go.mod +++ b/go.mod @@ -9,11 +9,12 @@ require ( github.com/google/go-cmp v0.7.0 github.com/logrusorgru/aurora/v3 v3.0.0 github.com/mmcloughlin/professor v0.0.0-20170922221822-6b97112ab8b3 - github.com/runZeroInc/excrypto v0.0.0-20251117153814-87eec3fc5d3e + github.com/runZeroInc/excrypto v0.0.0-20260131232301-7be794a7651f github.com/sirupsen/logrus v1.9.4 github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 github.com/ulikunitz/xz v0.5.15 + go.yaml.in/yaml/v3 v3.0.4 golang.org/x/crypto v0.47.0 golang.org/x/term v0.39.0 gonum.org/v1/gonum v0.17.0 @@ -30,7 +31,6 @@ require ( github.com/spf13/pflag v1.0.10 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/weppos/publicsuffix-go v0.50.2 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/net v0.49.0 // indirect golang.org/x/sys v0.40.0 // indirect golang.org/x/text v0.33.0 // indirect diff --git a/go.sum b/go.sum index 0a2d0d6..fab88d0 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,10 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -28,23 +25,17 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/runZeroInc/excrypto v0.0.0-20251021162936-5c2b17fc0596 h1:SOi6MlYKBzzvRCRNFU4Ys1EeYEwNdd2CyB+mM7Lc0Ks= -github.com/runZeroInc/excrypto v0.0.0-20251021162936-5c2b17fc0596/go.mod h1:D9vJh2x7Q/umzQHSlT3fzAkNaVvQgJrJP96zV7gXmJU= -github.com/runZeroInc/excrypto v0.0.0-20251117153814-87eec3fc5d3e h1:9fJx3YrJf2VYsvZFVAJKSfjBvX9nh8zqUOOoqZqlDSs= -github.com/runZeroInc/excrypto v0.0.0-20251117153814-87eec3fc5d3e/go.mod h1:ixS2HjHFQ55K/UcWZweT+WPGJr8+Te41TvijGDNAbYA= +github.com/runZeroInc/excrypto v0.0.0-20260131232301-7be794a7651f h1:ZPPlBdImu3RvCOIKujNpdi2kcMWm/dBHEfIlK6RGC/E= +github.com/runZeroInc/excrypto v0.0.0-20260131232301-7be794a7651f/go.mod h1:vdQdhzpm7ZVp1Qpjx0JjayltCRH2TeJK93ILaex/ahE= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= -github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -52,48 +43,30 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/weppos/publicsuffix-go v0.50.0 h1:M178k6l8cnh9T1c1cStkhytVxdk5zPd6gGZf8ySIuVo= -github.com/weppos/publicsuffix-go v0.50.0/go.mod h1:VXhClBYMlDrUsome4pOTpe68Ui0p6iQRAbyHQD1yKoU= github.com/weppos/publicsuffix-go v0.50.2 h1:KsJFc8IEKTJovM46SRCnGNsM+rFShxcs6VEHjOJcXzE= github.com/weppos/publicsuffix-go v0.50.2/go.mod h1:CbQCKDtXF8UcT7hrxeMa0MDjwhpOI9iYOU7cfq+yo8k= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=