Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package sshamble for openSUSE:Factory 
checked in at 2026-07-31 15:30:30
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/sshamble (Old)
 and      /work/SRC/openSUSE:Factory/.sshamble.new.2004 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "sshamble"

Fri Jul 31 15:30:30 2026 rev:4 rq:1368482 version:0.3.10

Changes:
--------
--- /work/SRC/openSUSE:Factory/sshamble/sshamble.changes        2025-09-22 
16:40:17.010933473 +0200
+++ /work/SRC/openSUSE:Factory/.sshamble.new.2004/sshamble.changes      
2026-07-31 16:10:31.291137783 +0200
@@ -1,0 +2,8 @@
+Wed Jul 29 07:38:59 UTC 2026 - Martin Hauke <[email protected]>
+
+- Update to version 0.3.10
+  * fix: detect RSA public keys against modern OpenSSH (issue #48).
+  * bump dependencies.
+  * refactor code.
+
+-------------------------------------------------------------------

Old:
----
  sshamble-0.3.3.tar.gz

New:
----
  sshamble-0.3.10.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ sshamble.spec ++++++
--- /var/tmp/diff_new_pack.LJNm2R/_old  2026-07-31 16:10:31.827156429 +0200
+++ /var/tmp/diff_new_pack.LJNm2R/_new  2026-07-31 16:10:31.831156569 +0200
@@ -1,8 +1,8 @@
 #
 # spec file for package sshamble
 #
-# Copyright (c) 2025 SUSE LLC and contributors
-# Copyright (c) 2024-2025, Martin Hauke <[email protected]>
+# Copyright (c) 2026 SUSE LLC and contributors
+# Copyright (c) 2024-2026, Martin Hauke <[email protected]>
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
 
 
 Name:           sshamble
-Version:        0.3.3
+Version:        0.3.10
 Release:        0
 Summary:        Security testing toolset for SSH
 License:        BSD-2-Clause

++++++ sshamble-0.3.3.tar.gz -> sshamble-0.3.10.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/README.md 
new/sshamble-0.3.10/README.md
--- old/sshamble-0.3.3/README.md        2025-08-19 06:31:06.000000000 +0200
+++ new/sshamble-0.3.10/README.md       2026-07-28 04:41:47.000000000 +0200
@@ -19,6 +19,7 @@
 
 Binaries are available from the [releases 
page](https://github.com/runZeroInc/sshamble/releases).
 
+*(Note: on MacOS, you will likely have to disable Gatekeeper for this binary, 
with `sudo xattr -rd com.apple.quarantine ./sshamble`. This is not an 
invitation to violate your asset owner's security policy.)*
 
 To build SSHamble from source, ensure that you have a recent version of Go 
(1.24+) installed.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/auth/config.go 
new/sshamble-0.3.10/auth/config.go
--- old/sshamble-0.3.3/auth/config.go   2025-08-19 06:31:06.000000000 +0200
+++ new/sshamble-0.3.10/auth/config.go  2026-07-28 04:41:47.000000000 +0200
@@ -69,18 +69,18 @@
        }
 
        KeyExchanges = []string{
-               "curve25519-sha256",
+               ssh.KeyExchangeMLKEM768X25519,
+               ssh.KeyExchangeCurve25519,
                "[email protected]",
-               "diffie-hellman-group-exchange-sha256",
-               "ecdh-sha2-nistp256",
-               "ecdh-sha2-nistp384",
-               "ecdh-sha2-nistp521",
-               "diffie-hellman-group14-sha256",
-               "diffie-hellman-group16-sha512",
-               "diffie-hellman-group14-sha1",
-               "diffie-hellman-group1-sha1",
-               "diffie-hellman-group-exchange-sha256",
-               "diffie-hellman-group-exchange-sha1",
+               ssh.KeyExchangeDHGEXSHA256,
+               ssh.KeyExchangeECDHP256,
+               ssh.KeyExchangeECDHP384,
+               ssh.KeyExchangeECDHP521,
+               ssh.KeyExchangeDH14SHA256,
+               ssh.KeyExchangeDH16SHA512,
+               ssh.InsecureKeyExchangeDH14SHA1,
+               ssh.InsecureKeyExchangeDH1SHA1,
+               ssh.InsecureKeyExchangeDHGEXSHA1,
        }
 
        KexMACs = []string{
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/auth/config_test.go 
new/sshamble-0.3.10/auth/config_test.go
--- old/sshamble-0.3.3/auth/config_test.go      1970-01-01 01:00:00.000000000 
+0100
+++ new/sshamble-0.3.10/auth/config_test.go     2026-07-28 04:41:47.000000000 
+0200
@@ -0,0 +1,38 @@
+package auth
+
+import (
+       "slices"
+       "testing"
+
+       "github.com/runZeroInc/excrypto/x/crypto/ssh"
+)
+
+func TestKeyExchangesIncludeMLKEM(t *testing.T) {
+       if len(KeyExchanges) == 0 {
+               t.Fatal("KeyExchanges must not be empty")
+       }
+       if got := KeyExchanges[0]; got != ssh.KeyExchangeMLKEM768X25519 {
+               t.Fatalf("first key exchange = %q, want %q", got, 
ssh.KeyExchangeMLKEM768X25519)
+       }
+       if !slices.Contains(KeyExchanges, ssh.KeyExchangeMLKEM768X25519) {
+               t.Fatalf("KeyExchanges does not include %q", 
ssh.KeyExchangeMLKEM768X25519)
+       }
+}
+
+func TestKeyExchangesAreSupported(t *testing.T) {
+       supported := map[string]struct{}{
+               "[email protected]": {},
+       }
+       for _, kex := range ssh.SupportedAlgorithms().KeyExchanges {
+               supported[kex] = struct{}{}
+       }
+       for _, kex := range ssh.InsecureAlgorithms().KeyExchanges {
+               supported[kex] = struct{}{}
+       }
+
+       for _, kex := range KeyExchanges {
+               if _, ok := supported[kex]; !ok {
+                       t.Fatalf("unsupported configured key exchange: %q", kex)
+               }
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/auth/halfauth_research.go 
new/sshamble-0.3.10/auth/halfauth_research.go
--- old/sshamble-0.3.3/auth/halfauth_research.go        2025-08-19 
06:31:06.000000000 +0200
+++ new/sshamble-0.3.10/auth/halfauth_research.go       2026-07-28 
04:41:47.000000000 +0200
@@ -29,3 +29,8 @@
        copy(s.SignData, data)
        return nil, ErrHalfAuth
 }
+
+// SignWithAlgorithm makes HalfSigner an ssh.AlgorithmSigner so RSA keys are 
probed with rsa-sha2-* instead of SHA-1 ssh-rsa.
+func (s *HalfSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm 
string) (*ssh.Signature, error) {
+       return s.Sign(rand, data)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/auth/options.go 
new/sshamble-0.3.10/auth/options.go
--- old/sshamble-0.3.3/auth/options.go  2025-08-19 06:31:06.000000000 +0200
+++ new/sshamble-0.3.10/auth/options.go 2026-07-28 04:41:47.000000000 +0200
@@ -2,6 +2,7 @@
 
 import (
        "net"
+       "slices"
        "time"
        "unicode/utf8"
 
@@ -115,12 +116,7 @@
 }
 
 func (o *Options) SkipStage(stage string) bool {
-       for _, skipStage := range o.skipStages {
-               if stage == skipStage {
-                       return true
-               }
-       }
-       return false
+       return slices.Contains(o.skipStages, stage)
 }
 
 func ReverseString(s string) string {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/auth/results.go 
new/sshamble-0.3.10/auth/results.go
--- old/sshamble-0.3.3/auth/results.go  2025-08-19 06:31:06.000000000 +0200
+++ new/sshamble-0.3.10/auth/results.go 2026-07-28 04:41:47.000000000 +0200
@@ -1,6 +1,7 @@
 package auth
 
 import (
+       "slices"
        "strings"
        "time"
 
@@ -58,43 +59,42 @@
 }
 
 func (r *AuthResult) SupportsAuth(t string) bool {
-       for _, v := range r.Methods {
-               if v == t {
-                       return true
-               }
-       }
-       return false
+       return slices.Contains(r.Methods, t)
 }
 
 func (r *AuthResult) SupportsHostKey(t string) bool {
        if r.KexInit == nil {
                return false
        }
-       for _, v := range r.KexInit.ServerHostKeyAlgos {
-               if v == t {
-                       return true
-               }
-       }
-       return false
+       return slices.Contains(r.KexInit.ServerHostKeyAlgos, t)
 }
 
 func (r *AuthResult) SupportsPubKeyType(t string) bool {
-       // Example: 
ssh-ed25519,[email protected],[email protected],ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,rsa-sha2-256,rsa-sha2-512,ssh-rsa,ssh-dss
        okTypes, ok := r.Extensions["server-sig-algs"]
-       if !ok {
-               // Assume all types are supported unless the server
-               // has told us otherwise via the extension.
+       if !ok || strings.TrimSpace(okTypes) == "" {
                return true
        }
-       for _, kt := range strings.Split(okTypes, ",") {
+       // ssh-rsa keys are usable when the server advertises any RSA signature 
algo;
+       // modern OpenSSH (>= 8.8) only offers rsa-sha2-256/rsa-sha2-512, not 
ssh-rsa.
+       wanted := sigAlgosForKeyType(t)
+       for kt := range strings.SplitSeq(okTypes, ",") {
                kt = strings.TrimSpace(kt)
-               if strings.EqualFold(kt, t) {
-                       return true
+               for _, w := range wanted {
+                       if strings.EqualFold(kt, w) {
+                               return true
+                       }
                }
        }
        return false
 }
 
+func sigAlgosForKeyType(t string) []string {
+       if t == "ssh-rsa" {
+               return []string{"ssh-rsa", "rsa-sha2-256", "rsa-sha2-512"}
+       }
+       return []string{t}
+}
+
 func (r *AuthResult) AddVuln(v VulnResult) {
        r.Vulns = append(r.Vulns, v)
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/auth/results_test.go 
new/sshamble-0.3.10/auth/results_test.go
--- old/sshamble-0.3.3/auth/results_test.go     1970-01-01 01:00:00.000000000 
+0100
+++ new/sshamble-0.3.10/auth/results_test.go    2026-07-28 04:41:47.000000000 
+0200
@@ -0,0 +1,27 @@
+package auth
+
+import "testing"
+
+func TestSupportsPubKeyType(t *testing.T) {
+       const modern = 
"ssh-ed25519,ecdsa-sha2-nistp256,rsa-sha2-512,rsa-sha2-256"
+       const legacy = "ssh-ed25519,rsa-sha2-256,rsa-sha2-512,ssh-rsa"
+
+       cases := []struct {
+               sigAlgs string
+               keyType string
+               want    bool
+       }{
+               {modern, "ssh-rsa", true},
+               {modern, "ssh-ed25519", true},
+               {legacy, "ssh-rsa", true},
+               {"ssh-ed25519", "ssh-rsa", false},
+               {modern, "ecdsa-sha2-nistp521", false},
+       }
+
+       for _, tc := range cases {
+               r := &AuthResult{Extensions: 
map[string]string{"server-sig-algs": tc.sigAlgs}}
+               if got := r.SupportsPubKeyType(tc.keyType); got != tc.want {
+                       t.Errorf("SupportsPubKeyType(%q) with %q = %v, want 
%v", tc.keyType, tc.sigAlgs, got, tc.want)
+               }
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/badkeys/badkeys.go 
new/sshamble-0.3.10/badkeys/badkeys.go
--- old/sshamble-0.3.3/badkeys/badkeys.go       2025-08-19 06:31:06.000000000 
+0200
+++ new/sshamble-0.3.10/badkeys/badkeys.go      2026-07-28 04:41:47.000000000 
+0200
@@ -6,86 +6,10 @@
        "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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/badkeys/large_e_test.go 
new/sshamble-0.3.10/badkeys/large_e_test.go
--- old/sshamble-0.3.3/badkeys/large_e_test.go  1970-01-01 01:00:00.000000000 
+0100
+++ new/sshamble-0.3.10/badkeys/large_e_test.go 2026-07-28 04:41:47.000000000 
+0200
@@ -0,0 +1,89 @@
+package badkeys
+
+import (
+       "math/big"
+       "testing"
+
+       "github.com/runZeroInc/excrypto/crypto/rsa"
+       "github.com/runZeroInc/excrypto/crypto/x509"
+       "github.com/runZeroInc/excrypto/x/crypto/ssh"
+)
+
+// TestLargeRSAExponentChain verifies that an RSA public key with a very large
+// public exponent (> 2^31, here ~1397 bits) can round-trip through every layer
+// sshamble relies on: x509 PKIX marshal/parse, SSH wire-format marshal/parse,
+// and the badkeys fingerprinting helper.
+//
+// Companion fixture: badkeys/tests/rsa_2048_large_e.yml.
+// Upstream: https://github.com/runZeroInc/excrypto/pull/78,
+// https://github.com/zmap/zgrab2/pull/714.
+func TestLargeRSAExponentChain(t *testing.T) {
+       // Real 2048-bit modulus paired with a deliberately oversized public 
exponent
+       // (matches the fixture in badkeys/tests/rsa_2048_large_e.yml).
+       const nDec = 
"18546553133979686295706754998899930900284384519884436022491634865630824361571989091727223217760704713562088617769642972778706810069040146840815408068924221304482467411754327717732219383528142044820647352015027973435620587096945826941424583339838200914868370389431967066469883816302332032037291718905474557496674201134355854318405776150935618480911797504314370087472798726795714549286504512398139371824841363689318200316196228724354062568227493421963401085883965211168501435120756253842277566370639741145538747372187952581380944184780911614372327720305137590972683248723463443042150650954101508292155326597597151852727"
+       const eDec = 
"180551674620205768325404188012693994494974589455798193523634712608811452174151879339798865932167024867098385482983633145471353137270073888488487188773259587111887661299046218807614426891874011840094262739842776840031222130852779463045751206192002039235899666579777076498654001351987196402428277588714906438364535810862902900704632084301624950750661275401550586885487965175176824611882045496694270842154131510904790726769"
+
+       n, ok := new(big.Int).SetString(nDec, 10)
+       if !ok {
+               t.Fatalf("invalid N literal")
+       }
+       e, ok := new(big.Int).SetString(eDec, 10)
+       if !ok {
+               t.Fatalf("invalid E literal")
+       }
+       if e.BitLen() <= 32 {
+               t.Fatalf("test E is not large enough: bitlen=%d", e.BitLen())
+       }
+
+       pub := &rsa.PublicKey{N: n, E: e}
+
+       // 1. x509 PKIX round-trip.
+       der, err := x509.MarshalPKIXPublicKey(pub)
+       if err != nil {
+               t.Fatalf("MarshalPKIXPublicKey: %v", err)
+       }
+       parsed, err := x509.ParsePKIXPublicKey(der)
+       if err != nil {
+               t.Fatalf("ParsePKIXPublicKey: %v", err)
+       }
+       rpub, ok := parsed.(*rsa.PublicKey)
+       if !ok {
+               t.Fatalf("ParsePKIXPublicKey returned %T, want *rsa.PublicKey", 
parsed)
+       }
+       if rpub.E.Cmp(e) != 0 || rpub.N.Cmp(n) != 0 {
+               t.Fatalf("x509 round-trip mismatch: got N.bits=%d E.bits=%d, 
want N.bits=%d E.bits=%d",
+                       rpub.N.BitLen(), rpub.E.BitLen(), n.BitLen(), 
e.BitLen())
+       }
+
+       // 2. SSH wire-format round-trip (parseRSA used to reject E.BitLen() > 
24).
+       sshPub, err := ssh.NewPublicKey(pub)
+       if err != nil {
+               t.Fatalf("ssh.NewPublicKey: %v", err)
+       }
+       sshWire := sshPub.Marshal()
+       reparsed, err := ssh.ParsePublicKey(sshWire)
+       if err != nil {
+               t.Fatalf("ssh.ParsePublicKey: %v", err)
+       }
+       cpk, ok := reparsed.(ssh.CryptoPublicKey)
+       if !ok {
+               t.Fatalf("ssh.ParsePublicKey result is not CryptoPublicKey: 
%T", reparsed)
+       }
+       rsshPub, ok := cpk.CryptoPublicKey().(*rsa.PublicKey)
+       if !ok {
+               t.Fatalf("ssh re-parse returned %T, want *rsa.PublicKey", 
cpk.CryptoPublicKey())
+       }
+       if rsshPub.E.Cmp(e) != 0 || rsshPub.N.Cmp(n) != 0 {
+               t.Fatalf("ssh wire round-trip mismatch for large E: got 
E.bits=%d, want %d", rsshPub.E.BitLen(), e.BitLen())
+       }
+
+       // 3. badkeys fingerprint helper consumes the result.
+       prefix, err := PrefixFromPublicKey(reparsed)
+       if err != nil {
+               t.Fatalf("PrefixFromPublicKey: %v", err)
+       }
+       if len(prefix) != 15 {
+               t.Fatalf("unexpected prefix length: %d", len(prefix))
+       }
+       t.Logf("large-E badkeys prefix: %s (E bitlen=%d)", 
PrefixToString(prefix), e.BitLen())
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/badkeys/parser.go 
new/sshamble-0.3.10/badkeys/parser.go
--- old/sshamble-0.3.3/badkeys/parser.go        1970-01-01 01:00:00.000000000 
+0100
+++ new/sshamble-0.3.10/badkeys/parser.go       2026-07-28 04:41:47.000000000 
+0200
@@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/badkeys/parser_test.go 
new/sshamble-0.3.10/badkeys/parser_test.go
--- old/sshamble-0.3.3/badkeys/parser_test.go   1970-01-01 01:00:00.000000000 
+0100
+++ new/sshamble-0.3.10/badkeys/parser_test.go  2026-07-28 04:41:47.000000000 
+0200
@@ -0,0 +1,207 @@
+package badkeys
+
+import (
+       stddsa "crypto/dsa"
+       stdecdh "crypto/ecdh"
+       "crypto/ecdsa"
+       "crypto/ed25519"
+       stdrsa "crypto/rsa"
+       "encoding/pem"
+       "testing"
+
+       exdsa "github.com/runZeroInc/excrypto/crypto/dsa"
+       execdh "github.com/runZeroInc/excrypto/crypto/ecdh"
+       execdsa "github.com/runZeroInc/excrypto/crypto/ecdsa"
+       exed25519 "github.com/runZeroInc/excrypto/crypto/ed25519"
+       "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 *stdrsa.PrivateKey:
+               ktype = "rsa"
+               publicKey = &pk.PublicKey
+               privateKey = pk
+               pubOK = true
+       case *execdsa.PrivateKey:
+               ktype = "ecdsa"
+               publicKey = &pk.PublicKey
+               privateKey = pk
+               pubOK = true
+       case *ecdsa.PrivateKey:
+               ktype = "ecdsa"
+               publicKey = &pk.PublicKey
+               privateKey = pk
+               pubOK = true
+       case exed25519.PrivateKey:
+               ktype = "ed25519"
+               publicKey = pk.Public()
+               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
+               }
+       } else {
+               pubOK = true
+       }
+
+       switch pk := publicKey.(type) {
+       case *rsa.PublicKey:
+               ktype = "rsa"
+               publicKey = pk
+       case *stdrsa.PublicKey:
+               ktype = "rsa"
+               publicKey = pk
+       case *execdsa.PublicKey:
+               ktype = "ecdsa"
+               publicKey = pk
+       case *ecdsa.PublicKey:
+               ktype = "ecdsa"
+               publicKey = pk
+       case exed25519.PublicKey:
+               ktype = "ed25519"
+               publicKey = pk
+       case ed25519.PublicKey:
+               ktype = "ed25519"
+               publicKey = pk
+       case *exdsa.PublicKey:
+               ktype = "dsa"
+               publicKey = pk
+       case *stddsa.PublicKey:
+               ktype = "dsa"
+               publicKey = pk
+       case *execdh.PublicKey:
+               if pk.Curve() == execdh.X25519() {
+                       ktype = "x25519"
+               } else {
+                       ktype = "ecdh"
+               }
+               publicKey = pk
+       case *stdecdh.PublicKey:
+               if pk.Curve() == stdecdh.X25519() {
+                       ktype = "x25519"
+               } else {
+                       ktype = "ecdh"
+               }
+               publicKey = pk
+       case x509.X25519PublicKey:
+               ktype = "x25519"
+               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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/dsa_1024_legacy.yml 
new/sshamble-0.3.10/badkeys/tests/dsa_1024_legacy.yml
--- old/sshamble-0.3.3/badkeys/tests/dsa_1024_legacy.yml        1970-01-01 
01:00:00.000000000 +0100
+++ new/sshamble-0.3.10/badkeys/tests/dsa_1024_legacy.yml       2026-07-28 
04:41:47.000000000 +0200
@@ -0,0 +1,18 @@
+name: DSA 1024 Legacy Public Key
+comment: Ancient and weak SSH DSS style key material, kept as a public key
+  fixture because DSA is legacy and typically only needed for parsing and
+  fingerprint coverage.
+keyType: dsa
+key: |-
+  -----BEGIN PUBLIC KEY-----
+  MIIBwDCCATQGByqGSM44BAEwggEnAoGBAO8S9U4Zn2HGWdjNNrfM/1k7vURd4G/4
+  00yVQX9GVwnGPfaOx+EGaajfsJEuiiKBzXeJjLZgN+5vk75CnxRYZRQexoCwssmP
+  hj4EOXPP8mIf/yUgSndk0kAuPqEuyZ5qE3tlhC7lJq1I5LbfeNq5sy2RxTJ/yGEq
+  jdXcWBD46ck1Ah0Amk6M09DvHPDIS0YFPhakHo0I/RpH4SFdtOSQcwKBgQCiuzWe
+  AaYPmQgRL1GbWIgWHh3cw0zYiltjOeLcQkhzBF2Wg/pMBlv6D+um2QGcIWdtOhT/
+  6EfGHfaY5riR/Q2/NXEPzHG816s7UPoFFfrW5ibg7/EMqeAzZRM3dFKIql0+o0SC
+  eopnYWHzNFu0xncOF2O9bI+JrrhYxdC68IEReQOBhQACgYEA0srq4q1Q5/rMfyIW
+  LvV2JS87xnKwraf6vnApqYCca7+hf11LJgq7PLbw/keFQ+edagrhRLjhdX0PZQ05
+  Uv9gOqqxkNU6FMYlvL5ZxIQSsN2rityVgOsa4e7adNoEb1NzwFNAmf3xnT+hCTVW
+  rqfviwWsHrXEOMQSkeNKSUEdmeo=
+  -----END PUBLIC KEY-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/ecdsa_p256.yml 
new/sshamble-0.3.10/badkeys/tests/ecdsa_p256.yml
--- old/sshamble-0.3.3/badkeys/tests/ecdsa_p256.yml     1970-01-01 
01:00:00.000000000 +0100
+++ new/sshamble-0.3.10/badkeys/tests/ecdsa_p256.yml    2026-07-28 
04:41:47.000000000 +0200
@@ -0,0 +1,9 @@
+name: ECDSA P-256 Private Key
+comment: Common modern elliptic-curve SSH key on the NIST P-256 curve.
+keyType: ecdsa
+key: |-
+  -----BEGIN PRIVATE KEY-----
+  MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgk1NiW92Y1XvDLIe9
+  otm1R4epNmZwa0SoBULaZTpb67ShRANCAATZO3Lt3C4nlqacTFlTmMSLulETmclw
+  e4gHoNVOw+wV7zNhzjGCP09QuwPXdg6HlXtEqvVB3D7f+t91lDIB3sjm
+  -----END PRIVATE KEY-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/ecdsa_p384.yml 
new/sshamble-0.3.10/badkeys/tests/ecdsa_p384.yml
--- old/sshamble-0.3.3/badkeys/tests/ecdsa_p384.yml     1970-01-01 
01:00:00.000000000 +0100
+++ new/sshamble-0.3.10/badkeys/tests/ecdsa_p384.yml    2026-07-28 
04:41:47.000000000 +0200
@@ -0,0 +1,10 @@
+name: ECDSA P-384 Private Key
+comment: Higher-strength ECDSA variant for coverage across supported NIST 
curves.
+keyType: ecdsa
+key: |-
+  -----BEGIN PRIVATE KEY-----
+  MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDunAsSiHOrREJrVrlE
+  zuVsuODLajuPAymv5Q0mTA8pIhSDQXIsa6hfDoe8K+ibAQShZANiAAQVlf80Fdxp
+  0UhCHdY8Tx25Tqz/RVAalIJH2llPczoIEM/n6sk5gNR2FxfBLBuIndBeUQv0XBxI
+  yWxPcTUSOINxjcxKwsHaJtvosgZ4H2rfr3GLMMZzcm/pLw9tAAcvN4o=
+  -----END PRIVATE KEY-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/ecdsa_p521.yml 
new/sshamble-0.3.10/badkeys/tests/ecdsa_p521.yml
--- old/sshamble-0.3.3/badkeys/tests/ecdsa_p521.yml     1970-01-01 
01:00:00.000000000 +0100
+++ new/sshamble-0.3.10/badkeys/tests/ecdsa_p521.yml    2026-07-28 
04:41:47.000000000 +0200
@@ -0,0 +1,12 @@
+name: ECDSA P-521 Private Key
+comment: Largest NIST ECDSA curve supported by typical SSH implementations.
+keyType: ecdsa
+key: |-
+  -----BEGIN PRIVATE KEY-----
+  MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBOHNQ7XYIdx1dtztT
+  7hrnSH+Ava9MDje28iAoE0v20jqvJMqfSpzQtsI6bHDc2AINYuLHYgijIru9+9pt
+  d4OP10+hgYkDgYYABADUkYJ5XV0HTDXTYXeuQu7+fBf3lWokJ7DugVcorTCieI8d
+  Vvu02ez74j1sd8IlQ5NY/uJW4bUqT5rg/XLeYVz23ACsHKqGVmvs+Ql6b/XanjUM
+  4LkVNuNIJ+pWdoeV/ipCphvEg3XnBNXGoMHGtRqBLpnbLbhzz416J3zff2vN84ZI
+  WA==
+  -----END PRIVATE KEY-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/ed25519_modern.yml 
new/sshamble-0.3.10/badkeys/tests/ed25519_modern.yml
--- old/sshamble-0.3.3/badkeys/tests/ed25519_modern.yml 1970-01-01 
01:00:00.000000000 +0100
+++ new/sshamble-0.3.10/badkeys/tests/ed25519_modern.yml        2026-07-28 
04:41:47.000000000 +0200
@@ -0,0 +1,7 @@
+name: Ed25519 Modern Private Key
+comment: Contemporary SSH key type with compact modern private key encoding.
+keyType: ed25519
+key: |-
+  -----BEGIN PRIVATE KEY-----
+  MC4CAQAwBQYDK2VwBCIEICAy5fQCuNqe5FoE6+mLvyD4Gip4hoebhK0oOTY3F+Pl
+  -----END PRIVATE KEY-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/embed.go 
new/sshamble-0.3.10/badkeys/tests/embed.go
--- old/sshamble-0.3.3/badkeys/tests/embed.go   1970-01-01 01:00:00.000000000 
+0100
+++ new/sshamble-0.3.10/badkeys/tests/embed.go  2026-07-28 04:41:47.000000000 
+0200
@@ -0,0 +1,9 @@
+package tables
+
+import (
+       "embed"
+)
+
+//go:embed all:*.yml
+
+var TestData embed.FS
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/rsa_1024_legacy.yml 
new/sshamble-0.3.10/badkeys/tests/rsa_1024_legacy.yml
--- old/sshamble-0.3.3/badkeys/tests/rsa_1024_legacy.yml        1970-01-01 
01:00:00.000000000 +0100
+++ new/sshamble-0.3.10/badkeys/tests/rsa_1024_legacy.yml       2026-07-28 
04:41:47.000000000 +0200
@@ -0,0 +1,21 @@
+name: RSA 1024 Legacy Private Key
+comment: Weak old RSA key size to cover insecure legacy PEM private key parsing
+  and hashing.
+keyType: rsa
+key: |-
+  -----BEGIN PRIVATE KEY-----
+  MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALM31Su5InGugVJw
+  03P86pRf1QqGJmMVQqvDPA6nLdOSyjl8IJG9qjxFB68XMbqf6OOfZsHlx1CtmDF3
+  xQA9YH9ZWX/mobnCDEFmheM8T2kPQe2tBdFCjccrF3W2gYA9tFUYZs+Ilvba27H6
+  2/ediivy0/ojB8jmx2KrWWYb2oWJAgMBAAECgYBmddktsh57F+VfmJmLimEiitdl
+  uORhaVpAkNHZHsMYXqe2zbxhFiopYt3NwFjx9VJUMGGzIQcenOCSB4Nda7mqn51K
+  4eypvQt1ryPt30BKBUPLdCFg8OJYVQXzJZ0b4qYOmt1cTem/JUuevilFmGRUr65A
+  gDNa+qYEXC3nt3QKAQJBAOSr3BOV3gq4xMx/mgMJBPNb2CEtjRMbdLuPxHSzHo6k
+  3YCHejMsbp9r4hp11DsFHXkzrOcptaz4oEMhamNguLkCQQDIovbUt3AwMs8J2QxI
+  kKNQCC/klF8QBZ9RuYqIj9AgP3o17AUnBexZNSJfpbWdwgy51OIjYXu41KRiPsoG
+  gStRAkAus+8BzQUJeY0wGwuQfSIWtfZhaFU8xsnDmOxNzVt7VK6sln7dvsBlLPqD
+  3vbWiwfm/ifdGoaxaiIJM+TvoQx5AkBOOPMA5e/Qas67aSrX08RIPJjnhk01wutl
+  gnAXY/Jh0D3GbhloKHAIK6YWIlYXhoNNpEjyQFxrKERapV5AgPWxAkEAnliacbWt
+  aGbZS5tRI70DSrcH+u8XNkabzduTXRp84zJ6rlNGgHs6gBpA/b7V/FmnajL3X+14
+  jog/d6f5pBwc7w==
+  -----END PRIVATE KEY-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/rsa_2048_large_e.yml 
new/sshamble-0.3.10/badkeys/tests/rsa_2048_large_e.yml
--- old/sshamble-0.3.3/badkeys/tests/rsa_2048_large_e.yml       1970-01-01 
01:00:00.000000000 +0100
+++ new/sshamble-0.3.10/badkeys/tests/rsa_2048_large_e.yml      2026-07-28 
04:41:47.000000000 +0200
@@ -0,0 +1,21 @@
+name: RSA 2048 Public Key With Large Public Exponent
+comment: |-
+  Real-world pathological RSA public key whose public exponent E exceeds the
+  historical 32-bit cap (here ~1397 bits, comparable to the keys observed in
+  the wild by zmap/zgrab2 and runZero). Verifies sshamble can parse, 
fingerprint,
+  and round-trip RSA keys with arbitrarily large E through excrypto.
+  See: https://github.com/runZeroInc/excrypto/pull/78 and 
https://github.com/zmap/zgrab2/pull/714
+keyType: rsa
+key: |-
+  -----BEGIN PUBLIC KEY-----
+  MIIBzzANBgkqhkiG9w0BAQEFAAOCAbwAMIIBtwKCAQEAkurAjiPidJjnDVC/agAy
+  rMKpmW635I/jT5Jg9snANq6Awt1mXoHd3gUjq32EHwr7Tn+jIqtg8PSvShd6iq3o
+  StSVCyYFjP2kibX+PsZiv3WlJWiXOPjHWnd2fN77VRpZkh0mLxS1HV6ptIgDmTgv
+  RmQbfX8ZtcCbXORqHVy+ZYIm67wc0K142bco5mD0EzYz8Ldh3W1pEnz9VEKFImAR
+  m76pI4JQMuk+lK0oH0wyLe06maGqoVBKblmugo+lgghRBBg5KCfpu+OH+I/AIAtD
+  6ACejQcuTcbJTbWcxzK74v1H8DqstPhLUU6cgwD/65p/Iw2j23iC3/A0oXWKjsd4
+  twKBrxC0fZgQ7eYj8ptZqxZBkTN2C5UqUsu0Z1Xe4ACTg6K/QHFOWab/4DQnD5nh
+  ZnTEchDQbZriXOJg7YkdAVK+RNqJVpq0DeaBPPzN1+ylAH6QcRle+cPjglIZQaD5
+  T795U+jbMcNWuVA4bf0WcXWjsgXkPdoU9V4mJBAo+6HeSz5IVc44a9Aa5czvpgqW
+  7FOZQot0cEQFuHXPSKapd/6go3PmPflp53RqNsurSsF6NGs=
+  -----END PUBLIC KEY-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/rsa_3072_modern.yml 
new/sshamble-0.3.10/badkeys/tests/rsa_3072_modern.yml
--- old/sshamble-0.3.3/badkeys/tests/rsa_3072_modern.yml        1970-01-01 
01:00:00.000000000 +0100
+++ new/sshamble-0.3.10/badkeys/tests/rsa_3072_modern.yml       2026-07-28 
04:41:47.000000000 +0200
@@ -0,0 +1,44 @@
+name: RSA 3072 Modern Private Key
+comment: Modern RSA key size to complement the legacy RSA fixture.
+keyType: rsa
+key: |-
+  -----BEGIN PRIVATE KEY-----
+  MIIG/gIBADANBgkqhkiG9w0BAQEFAASCBugwggbkAgEAAoIBgQCyEplVLhDlygts
+  xcVGMHhU1JRZy0vX+uyPWluThZJ0+lP+wyoFhGeMPoAOPiDqN81Cqt8iAPu/hwNX
+  eaSxmnZG1M6YYYIFuVXy3lixz7XG7cGwJSzaEXv6Znm8dLaVhL22B2lBukHUzdNU
+  S6sSVp4MRIlFFzQFh4QDGGmFktArpwnbdNxH1exxZ5tcCvN3SVRNfH8hrOnBqmos
+  Hzx+NlsIetdJcAAZkGBuVRHnAaqBm5B8sK9xHhiYYoaTpLhE1QJv0v53HJvtD1TM
+  AyNSmtWgKD2YYyPg7qONJFl8xJ6LKqug8irCPL8uSOWyWCB7mPVnVJn+Dsp/A847
+  J+rCw7bLuYYjRyo5c1fqN9iTnc9FTopAfLQP+TliHG3l7m8KvUWFoFCuMVSGYdii
+  1VmYUsSbI6X4ReR84iyJSDRWHJfxIxrGmy/iZcQ53gx0h7Yn18eyjaurALDiTEGr
+  PQkYFzTfNzzqUA29pvnuSwb8OCMNRkZ6RSHOxXPpfmYR+nLKLcsCAwEAAQKCAYA0
+  euPCpVVoi4+ramvV4EIOaiogfpgnGnXuV6hmhTahEAeCLR1sy9VCkUbnK6tCq9hr
+  /fTNzjFjhFRZSundR9L01BHg+de6fSEV9y53lOoTKw3qrkIWMJHzvZDCGDmZPio1
+  A+Ei9FQjddeC39wWhv2ESwyTlp+Hw6UFzoOG0LpWKx/uE/Y5m5PX5jt0uGuBa9ST
+  E45aK94ma+VnhHstDTLuAFoDdCUrY4/v5FVm2ehY0zKVdPL3qPGBs4PK7oA6FPZp
+  hYRXaGV6fTIuSZADNcaG2DW/o2HT4bQnvgtn/U7s5D1BbH32uZDkbmHGhvaL3Uu3
+  mXnT8EV8V5Qktgt1umhovq8yzrMXid8fX64X7EPSDrZZKSmFIlOONL/PM84gAWYo
+  NDy/uuZVS59Y6FjJwDcUwKaj82ng2HIHsJEanftv4X1FiLf3U2NLU/OGG/XKpVhz
+  MWO0NohN3katwWIRj9P10HVyYmhAdg/BcOC4UF0utItCFFrRq85Ks/YGy0bJ+LEC
+  gcEA2Q+A6b5rk9Lqm99OR7Oi4dI9g4irc/E94VhciZ4a3e1aDdC20IMFSOzMg8cT
+  GXudHJCHz3iiTr8U/grR/6pbk/zKj2Nqz02cN79W/ir8HxnO+PZkF8tqkwBNOoch
+  3Fk/uyFwcp7GthVvFroPQZJcrGafpxpUlra/7v3REwyjNa/Mj30nG4swbtYqcTH1
+  nqGztsFTcIrnihvud/uXodL977eSMagl47oROvPoCOZBdQnexlswXqsW8UG+QVZZ
+  A6Q7AoHBANIEk9O5pR0gHwsGVK82dl2ET0BYerp+bw5LHwvH3CcwqKqgk4hwjDOp
+  l+1g/A/rlKCBXqkODLevyry3g3m0hcFeWYQUIJDAQZa4Bx6Q6KgcYMfhCs+ut3D3
+  nKGFW54eEHdJXMHX1VMZWtkO9hfi7dPtsiNBr49nGnNoZ6IpJSHQdGTbpnFOUZ5B
+  Kcp0Uw3bvf7xDcOvbd8OhqPDjLap7zHe4drH7s+/pIigaIrZMb/DjY4vGvhKBq1w
+  iwRE4yvTsQKBwHlSsBR2SbzzXAzJsQBkyLr9/0IA5wxEH/lPoVGOXKgE3ZvxN+2t
+  /gs3yEwzvlo0/KkVo8N0QwxOw5YHSD9l4hACcNnakL3UPGfwe9hPAGMF0ujXFzUx
+  jp0hwwjetS9rdzLKSwbV9m2Z52DFmHdnLoNyTPRixDsFZJxZpD3Ih4UfgPAG3RAJ
+  KYYaENj6OrZy3Xdd8ZxrF2XbnNvVdXiO7T7K1V0KBjuWKcFQyTYINTgMz0cBezS+
+  3nD09Tmh4NknNwKBwQCz4P/MZEErhQuRocZ+m6MdzekjL6VOfmQYVfctW+0QE/ki
+  y44RWnR6+1MJGfxKlngiUSwGl+YEniiDIpsVbqzxRiJdoe+Si1iiAtT2NR9QfZ3X
+  FMTxxK+RrkMVhOdHsnBf+xHbZUVte1X4e3wEUXVqAziEQgpgROq+1jmNBrirEaPf
+  jtIsUexOI3nr0V4hjIhbB5PGe0cbuZFQSnkHKt2YVVIU1RGSFo6SBvIDmqull5Ry
+  p53t81lsy+wFqAr6ysECgcEAhO7WzGyqyLAwIMnxKxOZOLJ0AltdSIVNnCp7zHUz
+  PpC0EVO4+mH0KWV0PYoxW+dtTYkjNLs9h3mljufjNsOAfKBFYcCk8BkMXOt5btGa
+  iCr+pxy6uYQHwki0mn6HZC82bHMKCqAj8R/8Wt8xandO983G7/vip3w+1fWZZtmX
+  7lfBsPCMgxx5p5qQS2PNdEsTy3Hl+XvoBEt4Q07KU5HbaP8LH/04y4GVLlNn7A/n
+  n5X0SuT/56Ld5P3LKj6ZW1N3
+  -----END PRIVATE KEY-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/rsa_512_very_weak.yml 
new/sshamble-0.3.10/badkeys/tests/rsa_512_very_weak.yml
--- old/sshamble-0.3.3/badkeys/tests/rsa_512_very_weak.yml      1970-01-01 
01:00:00.000000000 +0100
+++ new/sshamble-0.3.10/badkeys/tests/rsa_512_very_weak.yml     2026-07-28 
04:41:47.000000000 +0200
@@ -0,0 +1,14 @@
+name: RSA 512 Very Weak Private Key
+comment: Deliberately tiny RSA key to cover very old and insecure key material.
+keyType: rsa
+key: |-
+  -----BEGIN PRIVATE KEY-----
+  MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAqrkftwcmmAWKXpZl
+  eTSMVv52hhO2+EyyOcvfmA2PA2B+jsXqRLSmipuegR2QpjkywPikdex6qaT50MhV
+  jwSgOwIDAQABAkEAl5OTyAIuTu0kZEStxYZVT+vPuc0JN72Iw+3yEGOBZWG0qskm
+  fib4AYeKgD8nQlHzgJ/Ef49gXiWMxPHP0ICRIQIhANhCGlfzHaPc3cVRHYRjZLPT
+  t3GyVtW9xJ2jzVHNqILxAiEAyhjRNX+0XSwJ6t/2yxVLDqaQoxfmMUwhoy3GOG4x
+  PesCIFYoT9KGW2KzVe+M15Os/avfR5CWmp3hUvnzDpPuuZohAiBHhTrcnQIeiwxD
+  c9kqC7IRUD8ViMqtHY0162gf5ldE2wIhALuqGP6PJGfb6NINJ3gafNA46FGYIWUK
+  A+qF2TnHaPvm
+  -----END PRIVATE KEY-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/x25519_modern.yml 
new/sshamble-0.3.10/badkeys/tests/x25519_modern.yml
--- old/sshamble-0.3.3/badkeys/tests/x25519_modern.yml  1970-01-01 
01:00:00.000000000 +0100
+++ new/sshamble-0.3.10/badkeys/tests/x25519_modern.yml 2026-07-28 
04:41:47.000000000 +0200
@@ -0,0 +1,9 @@
+name: X25519 Modern Public Key
+comment: Modern Curve25519 public key coverage for the supported X25519 hashing
+  path. This is not post-quantum, but it is the closest modern key family
+  implemented beyond the classic SSH key types.
+keyType: x25519
+key: |-
+  -----BEGIN PUBLIC KEY-----
+  MCowBQYDK2VuAyEAFyMUk2Xo7bihdKZr2+odfIPvvaJEj+jjEhC3xQc8tVk=
+  -----END PUBLIC KEY-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/cmd/check_pubkey.go 
new/sshamble-0.3.10/cmd/check_pubkey.go
--- old/sshamble-0.3.3/cmd/check_pubkey.go      2025-08-19 06:31:06.000000000 
+0200
+++ new/sshamble-0.3.10/cmd/check_pubkey.go     2026-07-28 04:41:47.000000000 
+0200
@@ -45,7 +45,6 @@
 
        cnt := 0
        for _, tk := range testHalfKeys {
-               tk := tk
                kt := tk.PublicKey().Type()
                cnt++
                res := auth.SSHAuth(addr, options.WithStopStage("auth"), 
auth.SSHAuthHandlerSingle(ssh.AuthMethod(
@@ -81,7 +80,6 @@
        // Test to see if any full key is accepted
        cnt = 0
        for _, tk := range testKeys {
-               tk := tk
                if _, ok := 
pubAccepted[base64.StdEncoding.EncodeToString(tk.PublicKey().Marshal())]; !ok {
                        // Skip full key tests where the half key was not 
accepted
                        continue
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/cmd/cmd_scan.go 
new/sshamble-0.3.10/cmd/cmd_scan.go
--- old/sshamble-0.3.3/cmd/cmd_scan.go  2025-08-19 06:31:06.000000000 +0200
+++ new/sshamble-0.3.10/cmd/cmd_scan.go 2026-07-28 04:41:47.000000000 +0200
@@ -3,6 +3,7 @@
 import (
        "bufio"
        "io"
+       "maps"
        "net"
        "os"
        "regexp"
@@ -219,12 +220,12 @@
 
        // Parse the check lists and categories
        enabledChecks := make(map[string]struct{})
-       for _, v := range strings.Split(gEnabledChecks, ",") {
+       for v := range strings.SplitSeq(gEnabledChecks, ",") {
                v = strings.ToLower(strings.TrimSpace(v))
                enabledChecks[v] = struct{}{}
        }
        enabledCategories := make(map[string]struct{})
-       for _, v := range strings.Split(gEnabledCategories, ",") {
+       for v := range strings.SplitSeq(gEnabledCategories, ",") {
                v = strings.ToLower(strings.TrimSpace(v))
                enabledCategories[v] = struct{}{}
        }
@@ -455,7 +456,7 @@
 
 func uniqueUsers(ustr string) []string {
        uniq := make(map[string]struct{}, 0)
-       for _, user := range strings.Split(ustr, ",") {
+       for user := range strings.SplitSeq(ustr, ",") {
                uniq[user] = struct{}{}
        }
        res := []string{}
@@ -744,9 +745,7 @@
 func (conf *ScanConfig) GetAllHostKeys(addr string, options *auth.Options, 
root *auth.AuthResult, cached *auth.AuthResult) {
        if cached != nil && len(cached.HostKeys) > 0 {
                // Use cached hostkeys if available
-               for k, v := range cached.HostKeys {
-                       root.HostKeys[k] = v
-               }
+               maps.Copy(root.HostKeys, cached.HostKeys)
                return
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/cmd/interact.go 
new/sshamble-0.3.10/cmd/interact.go
--- old/sshamble-0.3.3/cmd/interact.go  2025-08-19 06:31:06.000000000 +0200
+++ new/sshamble-0.3.10/cmd/interact.go 2026-07-28 04:41:47.000000000 +0200
@@ -326,7 +326,7 @@
                // Start piping our raw-mode stdin manager
                go conf.InteractRelay(addr, quit, sesInput, sclient, ses, state)
 
-               for _, cmd := range strings.Split(gInteractAuto, ",") {
+               for cmd := range strings.SplitSeq(gInteractAuto, ",") {
                        cmd = strings.TrimSpace(cmd)
                        if cmd == "" {
                                continue
@@ -582,7 +582,7 @@
 
        case "sendb":
                data := processEscapedByteString(strings.Join(args[1:], " "))
-               for i := 0; i < len(data); i++ {
+               for i := range data {
                        _, err := shell.Write(data[i : i+1])
                        if err != nil {
                                return false, err
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/cmd/os_freebsd.go 
new/sshamble-0.3.10/cmd/os_freebsd.go
--- old/sshamble-0.3.3/cmd/os_freebsd.go        2025-08-19 06:31:06.000000000 
+0200
+++ new/sshamble-0.3.10/cmd/os_freebsd.go       2026-07-28 04:41:47.000000000 
+0200
@@ -1,5 +1,4 @@
 //go:build freebsd
-// +build freebsd
 
 package cmd
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/cmd/os_other.go 
new/sshamble-0.3.10/cmd/os_other.go
--- old/sshamble-0.3.3/cmd/os_other.go  2025-08-19 06:31:06.000000000 +0200
+++ new/sshamble-0.3.10/cmd/os_other.go 2026-07-28 04:41:47.000000000 +0200
@@ -1,5 +1,4 @@
 //go:build !windows && !freebsd
-// +build !windows,!freebsd
 
 package cmd
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/cmd/os_windows.go 
new/sshamble-0.3.10/cmd/os_windows.go
--- old/sshamble-0.3.3/cmd/os_windows.go        2025-08-19 06:31:06.000000000 
+0200
+++ new/sshamble-0.3.10/cmd/os_windows.go       2026-07-28 04:41:47.000000000 
+0200
@@ -1,5 +1,4 @@
 //go:build windows
-// +build windows
 
 package cmd
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/cmd/targets.go 
new/sshamble-0.3.10/cmd/targets.go
--- old/sshamble-0.3.3/cmd/targets.go   2025-08-19 06:31:06.000000000 +0200
+++ new/sshamble-0.3.10/cmd/targets.go  2026-07-28 04:41:47.000000000 +0200
@@ -11,7 +11,7 @@
 
 func parseTargets(input string) ([]string, error) {
        res := []string{}
-       for _, target := range strings.Fields(strings.ReplaceAll(input, ",", " 
")) {
+       for target := range strings.FieldsSeq(strings.ReplaceAll(input, ",", " 
")) {
                target = strings.ToLower(strings.TrimSpace(target))
                if target == "" {
                        continue
@@ -69,8 +69,8 @@
        // Use a map to dedup and shuffle ports
        ports := make(map[int]bool)
 
-       bits := strings.Split(pspec, ",")
-       for _, bit := range bits {
+       bits := strings.SplitSeq(pspec, ",")
+       for bit := range bits {
                bit = strings.TrimSpace(bit)
 
                if bit == "" {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/go.mod new/sshamble-0.3.10/go.mod
--- old/sshamble-0.3.3/go.mod   2025-08-19 06:31:06.000000000 +0200
+++ new/sshamble-0.3.10/go.mod  2026-07-28 04:41:47.000000000 +0200
@@ -1,35 +1,38 @@
 module github.com/runZeroInc/sshamble
 
-go 1.24
+go 1.26.0
+
+// NOTE: Uncomment to test with a development version of excrypto
+// replace github.com/runZeroInc/excrypto => ../excrypto
 
 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-20250629231451-7e62a70a4cc3
-       github.com/sirupsen/logrus v1.9.3
-       github.com/spf13/cobra v1.9.1
-       github.com/spf13/viper v1.20.1
-       github.com/ulikunitz/xz v0.5.12
-       golang.org/x/crypto v0.41.0
-       golang.org/x/term v0.34.0
-       gonum.org/v1/gonum v0.16.0
+       github.com/runZeroInc/excrypto v0.42.2
+       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.16
+       go.yaml.in/yaml/v3 v3.0.5
+       golang.org/x/crypto v0.54.0
+       golang.org/x/term v0.45.0
+       gonum.org/v1/gonum v0.17.0
 )
 
 require (
-       github.com/fsnotify/fsnotify v1.9.0 // indirect
-       github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
+       github.com/fsnotify/fsnotify v1.10.1 // indirect
+       github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
        github.com/inconshreveable/mousetrap v1.1.0 // indirect
-       github.com/pelletier/go-toml/v2 v2.2.4 // indirect
-       github.com/sagikazarmark/locafero v0.10.0 // indirect
-       github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // 
indirect
-       github.com/spf13/afero v1.14.0 // indirect
-       github.com/spf13/cast v1.9.2 // indirect
-       github.com/spf13/pflag v1.0.7 // indirect
+       github.com/pelletier/go-toml/v2 v2.4.3 // indirect
+       github.com/sagikazarmark/locafero v0.12.0 // indirect
+       github.com/spf13/afero v1.15.0 // indirect
+       github.com/spf13/cast v1.10.0 // indirect
+       github.com/spf13/pflag v1.0.10 // indirect
        github.com/subosito/gotenv v1.6.0 // indirect
-       github.com/weppos/publicsuffix-go v0.40.2 // indirect
-       golang.org/x/net v0.43.0 // indirect
-       golang.org/x/sys v0.35.0 // indirect
-       golang.org/x/text v0.28.0 // indirect
-       gopkg.in/yaml.v3 v3.0.1 // indirect
+       github.com/weppos/publicsuffix-go v0.50.3 // indirect
+       golang.org/x/net v0.57.0 // indirect
+       golang.org/x/sys v0.47.0 // indirect
+       golang.org/x/text v0.40.0 // indirect
+       gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sshamble-0.3.3/go.sum new/sshamble-0.3.10/go.sum
--- old/sshamble-0.3.3/go.sum   2025-08-19 06:31:06.000000000 +0200
+++ new/sshamble-0.3.10/go.sum  2026-07-28 04:41:47.000000000 +0200
@@ -1,29 +1,16 @@
-cloud.google.com/go/compute/metadata v0.2.0/go.mod 
h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
-github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod 
h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
-github.com/bwesterb/go-ristretto v1.2.0/go.mod 
h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
-github.com/cloudflare/circl v1.1.0/go.mod 
h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
 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/golang/protobuf v1.3.1/go.mod 
h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.5.0/go.mod 
h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.2/go.mod 
h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/google/go-cmp v0.5.2/go.mod 
h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5/go.mod 
h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.8/go.mod 
h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.5.9/go.mod 
h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.6.0/go.mod 
h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/fsnotify/fsnotify v1.10.0 
h1:Xx/5Ydg9CeBDX/wi4VJqStNtohYjitZhhlHt4h3St1M=
+github.com/fsnotify/fsnotify v1.10.0/go.mod 
h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
+github.com/fsnotify/fsnotify v1.10.1 
h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
+github.com/fsnotify/fsnotify v1.10.1/go.mod 
h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
+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=
 github.com/google/go-cmp v0.7.0/go.mod 
h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
-github.com/google/go-github/v50 v50.2.0/go.mod 
h1:VBY8FB6yPIjrtKhozXv4FQupxKLS6H4m6xFZlT43q8Q=
-github.com/google/go-querystring v1.1.0/go.mod 
h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
 github.com/inconshreveable/mousetrap v1.1.0 
h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod 
h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -34,138 +21,74 @@
 github.com/logrusorgru/aurora/v3 v3.0.0/go.mod 
h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc=
 github.com/mmcloughlin/professor v0.0.0-20170922221822-6b97112ab8b3 
h1:2YMbJ6WbdQI9K73chxh9OWMDsZ2PNjAIRGTonp3T0l0=
 github.com/mmcloughlin/professor v0.0.0-20170922221822-6b97112ab8b3/go.mod 
h1:LQkXsHRSPIEklPCq8OMQAzYNS2NGtYStdNE/ej1oJU8=
-github.com/pelletier/go-toml/v2 v2.2.4 
h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
-github.com/pelletier/go-toml/v2 v2.2.4/go.mod 
h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
+github.com/pelletier/go-toml/v2 v2.3.1 
h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc=
+github.com/pelletier/go-toml/v2 v2.3.1/go.mod 
h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
+github.com/pelletier/go-toml/v2 v2.4.3 
h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY=
+github.com/pelletier/go-toml/v2 v2.4.3/go.mod 
h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
 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/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-20250629231451-7e62a70a4cc3 
h1:nHWx/OmoqfYMkTKY383N//ll3a36JjoszUqurhucPW4=
-github.com/runZeroInc/excrypto v0.0.0-20250629231451-7e62a70a4cc3/go.mod 
h1:Kk/CHQ2eYnHRYmR1eT1mA6L8LbNXA10fg8m8CqF6s2E=
+github.com/runZeroInc/excrypto v0.39.0 
h1:QBUmJoVBdzfUTxLUN7wlro7d1ko4KBnwfPpVPDtDXSA=
+github.com/runZeroInc/excrypto v0.39.0/go.mod 
h1:rj/ZnkrH3P9juT/ROY80Zsrmkv7ZFZwlXLU6cf7ZuGo=
+github.com/runZeroInc/excrypto v0.42.1 
h1:n9eADIuoxyL3/dlTP9dyIK0EE8um/6tWJJAea+x7aq4=
+github.com/runZeroInc/excrypto v0.42.1/go.mod 
h1:2+D8ndV+Ux7bkqknBACE8nbu1f2cb4G+B6OdcKbBL5c=
+github.com/runZeroInc/excrypto v0.42.2 
h1:VJgxfApAsXaYUXgNGDyH+FM0BkbjqtDodXud+MalBSo=
+github.com/runZeroInc/excrypto v0.42.2/go.mod 
h1:2+D8ndV+Ux7bkqknBACE8nbu1f2cb4G+B6OdcKbBL5c=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod 
h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sagikazarmark/locafero v0.10.0 
h1:FM8Cv6j2KqIhM2ZK7HZjm4mpj9NBktLgowT1aN9q5Cc=
-github.com/sagikazarmark/locafero v0.10.0/go.mod 
h1:Ieo3EUsjifvQu4NZwV5sPd4dwvu0OCgEQV7vjc9yDjw=
-github.com/sirupsen/logrus v1.9.3 
h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
-github.com/sirupsen/logrus v1.9.3/go.mod 
h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 
h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
-github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod 
h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
-github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
-github.com/spf13/afero v1.14.0/go.mod 
h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
-github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
-github.com/spf13/cast v1.9.2/go.mod 
h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
-github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
-github.com/spf13/cobra v1.9.1/go.mod 
h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
-github.com/spf13/pflag v1.0.6/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
-github.com/spf13/pflag v1.0.7/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
-github.com/spf13/viper v1.20.1/go.mod 
h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
-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.10.0 
h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
-github.com/stretchr/testify v1.10.0/go.mod 
h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+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.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.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=
+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/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.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
-github.com/ulikunitz/xz v0.5.12/go.mod 
h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
-github.com/weppos/publicsuffix-go v0.40.2 
h1:LlnoSH0Eqbsi3ReXZWBKCK5lHyzf3sc1JEHH1cnlfho=
-github.com/weppos/publicsuffix-go v0.40.2/go.mod 
h1:XsLZnULC3EJ1Gvk9GVjuCTZ8QUu9ufE4TZpOizDShko=
-github.com/yuin/goldmark v1.4.13/go.mod 
h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod 
h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod 
h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.7.0/go.mod 
h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
-golang.org/x/crypto v0.13.0/go.mod 
h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
-golang.org/x/crypto v0.19.0/go.mod 
h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
-golang.org/x/crypto v0.23.0/go.mod 
h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
-golang.org/x/crypto v0.25.0/go.mod 
h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
-golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
-golang.org/x/crypto v0.41.0/go.mod 
h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod 
h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod 
h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod 
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod 
h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod 
h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
-golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
-golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
-golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
-golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
-golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
-golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
-golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
-golang.org/x/oauth2 v0.6.0/go.mod 
h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
-golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
-golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod 
h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod 
h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod 
h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
-golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
-golang.org/x/term v0.12.0/go.mod 
h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
-golang.org/x/term v0.17.0/go.mod 
h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
-golang.org/x/term v0.20.0/go.mod 
h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
-golang.org/x/term v0.22.0/go.mod 
h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
-golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
-golang.org/x/term v0.34.0/go.mod 
h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.13.0/go.mod 
h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.14.0/go.mod 
h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.15.0/go.mod 
h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/text v0.16.0/go.mod 
h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
-golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
-golang.org/x/text v0.28.0/go.mod 
h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.1.12/go.mod 
h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.6.0/go.mod 
h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.13.0/go.mod 
h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
-golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod 
h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
-gonum.org/v1/gonum v0.16.0/go.mod 
h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
-google.golang.org/appengine v1.6.7/go.mod 
h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod 
h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod 
h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.28.0/go.mod 
h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.33.0/go.mod 
h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+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/ulikunitz/xz v0.5.16 h1:ld6NyySjx5lowVKwJvMRLnW5nxKX/xnpSiFYZ/Lxur0=
+github.com/ulikunitz/xz v0.5.16/go.mod 
h1:H9Rt/W6/Qj27PGauhQc6nfCDy7vHpzsOThBSaYDoEhw=
+github.com/weppos/publicsuffix-go v0.50.3 
h1:eT5dcjHQcVDNc0igpFEsGHKIip30feuB2zuuI9eJxiE=
+github.com/weppos/publicsuffix-go v0.50.3/go.mod 
h1:/rOa781xBykZhHK/I3QeHo92qdDKVmKZKF7s8qAEM/4=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
+go.yaml.in/yaml/v3 v3.0.4/go.mod 
h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
+go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
+go.yaml.in/yaml/v3 v3.0.5/go.mod 
h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
+golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
+golang.org/x/crypto v0.51.0/go.mod 
h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
+golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
+golang.org/x/crypto v0.54.0/go.mod 
h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
+golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
+golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
+golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
+golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
+golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
+golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
+golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
+golang.org/x/term v0.43.0/go.mod 
h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
+golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
+golang.org/x/term v0.45.0/go.mod 
h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
+golang.org/x/text v0.37.0/go.mod 
h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
+golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
+golang.org/x/text v0.40.0/go.mod 
h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
+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=

++++++ vendor.tar.gz ++++++
++++ 298111 lines of diff (skipped)

Reply via email to