Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 0 additions & 76 deletions badkeys/badkeys.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
83 changes: 83 additions & 0 deletions badkeys/parser.go
Original file line number Diff line number Diff line change
@@ -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)
}
151 changes: 151 additions & 0 deletions badkeys/parser_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
9 changes: 9 additions & 0 deletions badkeys/tests/embed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package tables

import (
"embed"
)

//go:embed all:*.yml

var TestData embed.FS
65 changes: 65 additions & 0 deletions badkeys/tests/rsa_large_e.yml
Original file line number Diff line number Diff line change
@@ -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-----
Loading
Loading