From 34388c726407084e26d4465195aeab764e7d3dc4 Mon Sep 17 00:00:00 2001 From: Nic Klaassen Date: Tue, 15 Oct 2024 18:23:27 -0700 Subject: [PATCH] eliminate rsa.GenerateKey and lib/auth/native --- .../transport/transportv1/client_test.go | 4 +- api/observability/tracing/ssh/ssh_test.go | 18 +- api/utils/keys/privatekey_test.go | 1 + api/utils/sshutils/checker_test.go | 2 + api/utils/sshutils/conn_test.go | 18 +- api/utils/sshutils/test.go | 51 +++--- integration/helpers/instance.go | 18 +- integration/helpers/testmain.go | 4 +- integration/kube/fixtures.go | 6 +- integrations/event-handler/mtls_certs.go | 1 + .../lib/credentials/credentials_test.go | 10 +- .../lib/testing/fakejoin/kubesigner.go | 11 +- lib/auth/auth_test.go | 10 +- lib/auth/auth_with_roles_test.go | 3 +- lib/auth/db_test.go | 4 +- lib/auth/grpcserver_test.go | 2 +- lib/auth/keygen/keygen.go | 6 - lib/auth/keygen/keygen_test.go | 33 ++-- lib/auth/kube_test.go | 8 +- .../workload_identity_service_test.go | 4 +- lib/auth/native/native.go | 164 +++--------------- lib/auth/native/native_test.go | 94 ---------- lib/auth/sessions.go | 7 +- lib/auth/testauthority/testauthority.go | 23 +-- lib/auth/windows/windows.go | 5 +- lib/circleci/token_validator_test.go | 8 +- lib/client/api_test.go | 4 +- lib/client/keystore_test.go | 4 +- lib/client/local_proxy_middleware_test.go | 6 +- lib/cryptosuites/internal/rsa/rsa.go | 129 ++++++++++++++ lib/cryptosuites/internal/rsa/rsa_test.go | 36 ++++ lib/cryptosuites/precompute.go | 38 ++++ lib/cryptosuites/suites.go | 34 ++-- lib/devicetrust/challenge/challenge_test.go | 59 ++----- lib/gcp/token_validator_test.go | 24 +-- lib/githubactions/token_validator_test.go | 14 +- lib/gitlab/token_validator_test.go | 22 +-- .../awsoidc/eice_sendsshpublickey.go | 49 ++---- .../awsoidc/eice_sendsshpublickey_test.go | 12 +- lib/kubernetestoken/token_validator_test.go | 9 +- lib/{auth/native => modules}/boring.go | 2 +- lib/modules/modules.go | 3 +- lib/{auth/native => modules}/notboring.go | 5 +- lib/multiplexer/multiplexer_test.go | 21 +-- lib/proxy/peer/helpers_test.go | 9 +- lib/reversetunnel/cache.go | 7 +- lib/service/certreloader_test.go | 6 +- lib/services/authority.go | 2 +- lib/spacelift/token_validator_test.go | 24 +-- lib/srv/alpnproxy/helpers_test.go | 11 +- lib/srv/app/server_test.go | 3 +- lib/srv/authhandlers_test.go | 18 +- lib/srv/db/access_test.go | 4 +- lib/srv/forward/sshserver.go | 20 ++- lib/srv/forward/sshserver_test.go | 51 +++++- .../transport/transportv1/transport_test.go | 17 +- lib/sshutils/server_test.go | 16 +- lib/tbot/tbot_test.go | 4 +- lib/terraformcloud/token_validator_test.go | 28 +-- lib/tlsca/ca_test.go | 16 +- lib/tpm/tpm_simulator_test.go | 1 + lib/utils/cert/certs.go | 79 ++------- lib/utils/certs.go | 20 ++- lib/utils/chconn_test.go | 12 +- lib/web/apiserver_test.go | 3 +- tool/tbot/kube_test.go | 6 +- tool/tctl/common/admin_action_test.go | 12 +- tool/tsh/common/db_test.go | 5 +- tool/tsh/common/tsh_test.go | 3 +- 69 files changed, 644 insertions(+), 719 deletions(-) delete mode 100644 lib/auth/native/native_test.go create mode 100644 lib/cryptosuites/internal/rsa/rsa.go create mode 100644 lib/cryptosuites/internal/rsa/rsa_test.go create mode 100644 lib/cryptosuites/precompute.go rename lib/{auth/native => modules}/boring.go (98%) rename lib/{auth/native => modules}/notboring.go (85%) diff --git a/api/client/proxy/transport/transportv1/client_test.go b/api/client/proxy/transport/transportv1/client_test.go index 2743443a4b342..ce1579c9ce7cd 100644 --- a/api/client/proxy/transport/transportv1/client_test.go +++ b/api/client/proxy/transport/transportv1/client_test.go @@ -17,8 +17,8 @@ package transportv1 import ( "bytes" "context" + "crypto/ed25519" "crypto/rand" - "crypto/rsa" "errors" "fmt" "io" @@ -555,7 +555,7 @@ func newServer(t *testing.T, srv transportv1pb.TransportServiceServer) testPack // newKeyring returns an [agent.ExtendedAgent] that has // one key populated in it. func newKeyring(t *testing.T) agent.ExtendedAgent { - private, err := rsa.GenerateKey(rand.Reader, 2048) + _, private, err := ed25519.GenerateKey(rand.Reader) require.NoError(t, err) keyring := agent.NewKeyring() diff --git a/api/observability/tracing/ssh/ssh_test.go b/api/observability/tracing/ssh/ssh_test.go index 5564eb40153f2..441073d61b5f9 100644 --- a/api/observability/tracing/ssh/ssh_test.go +++ b/api/observability/tracing/ssh/ssh_test.go @@ -16,12 +16,10 @@ package ssh import ( "context" + "crypto/ed25519" "crypto/rand" - "crypto/rsa" "crypto/subtle" - "crypto/x509" "encoding/json" - "encoding/pem" "errors" "net" "testing" @@ -73,19 +71,11 @@ func (s *server) Stop() error { } func generateSigner(t *testing.T) ssh.Signer { - private, err := rsa.GenerateKey(rand.Reader, 2048) + _, private, err := ed25519.GenerateKey(rand.Reader) require.NoError(t, err) - - block := &pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(private), - } - - privatePEM := pem.EncodeToMemory(block) - signer, err := ssh.ParsePrivateKey(privatePEM) + sshSigner, err := ssh.NewSignerFromSigner(private) require.NoError(t, err) - - return signer + return sshSigner } func (s *server) GetClient(t *testing.T) (ssh.Conn, <-chan ssh.NewChannel, <-chan *ssh.Request) { diff --git a/api/utils/keys/privatekey_test.go b/api/utils/keys/privatekey_test.go index 3d3eb1305f28b..6e4759eb44a2c 100644 --- a/api/utils/keys/privatekey_test.go +++ b/api/utils/keys/privatekey_test.go @@ -36,6 +36,7 @@ import ( ) func TestMarshalAndParseKey(t *testing.T) { + //nolint:forbidigo // Generating a small RSA key allowed for test. rsaKey, err := rsa.GenerateKey(rand.Reader, 1024) require.NoError(t, err) ecKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) diff --git a/api/utils/sshutils/checker_test.go b/api/utils/sshutils/checker_test.go index f32470e812552..be5e5af70c7a2 100644 --- a/api/utils/sshutils/checker_test.go +++ b/api/utils/sshutils/checker_test.go @@ -40,8 +40,10 @@ func TestCheckerValidateFIPS(t *testing.T) { FIPS: true, } + //nolint:forbidigo // Generating RSA keys allowed for key check test. rsaKey, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize) require.NoError(t, err) + //nolint:forbidigo // Generating RSA keys allowed for key check test. smallRSAKey, err := rsa.GenerateKey(rand.Reader, 1024) require.NoError(t, err) ellipticKeyP224, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader) diff --git a/api/utils/sshutils/conn_test.go b/api/utils/sshutils/conn_test.go index ad34220726c12..e3e802ab79e4f 100644 --- a/api/utils/sshutils/conn_test.go +++ b/api/utils/sshutils/conn_test.go @@ -17,10 +17,8 @@ limitations under the License. package sshutils import ( + "crypto/ed25519" "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/pem" "net" "testing" "time" @@ -64,19 +62,11 @@ func (s *server) Stop() error { } func generateSigner(t *testing.T) ssh.Signer { - private, err := rsa.GenerateKey(rand.Reader, 2048) + _, private, err := ed25519.GenerateKey(rand.Reader) require.NoError(t, err) - - block := &pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(private), - } - - privatePEM := pem.EncodeToMemory(block) - signer, err := ssh.ParsePrivateKey(privatePEM) + sshSigner, err := ssh.NewSignerFromSigner(private) require.NoError(t, err) - - return signer + return sshSigner } func (s *server) GetClient(t *testing.T) (ssh.Conn, <-chan ssh.NewChannel, <-chan *ssh.Request) { diff --git a/api/utils/sshutils/test.go b/api/utils/sshutils/test.go index 516e903aa71f2..b52b0dcc4b168 100644 --- a/api/utils/sshutils/test.go +++ b/api/utils/sshutils/test.go @@ -17,25 +17,24 @@ limitations under the License. package sshutils import ( + "crypto" + "crypto/ed25519" "crypto/rand" - "crypto/rsa" "time" "github.com/gravitational/trace" "golang.org/x/crypto/ssh" - - "github.com/gravitational/teleport/api/constants" ) const defaultPrincipal = "127.0.0.1" // MakeTestSSHCA generates a new SSH certificate authority for tests. func MakeTestSSHCA() (ssh.Signer, error) { - privateKey, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize) + _, privateKey, err := ed25519.GenerateKey(rand.Reader) if err != nil { return nil, trace.Wrap(err) } - ca, err := ssh.NewSignerFromKey(privateKey) + ca, err := ssh.NewSignerFromSigner(privateKey) if err != nil { return nil, trace.Wrap(err) } @@ -49,33 +48,41 @@ func MakeSpoofedHostCert(realCA ssh.Signer) (ssh.Signer, error) { if err != nil { return nil, trace.Wrap(err) } - return makeHostCert(realCA.PublicKey(), fakeCA, defaultPrincipal) + _, hostKey, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + return nil, trace.Wrap(err) + } + return makeHostCert(hostKey, realCA.PublicKey(), fakeCA, defaultPrincipal) } // MakeRealHostCert makes an SSH host certificate that is signed by the // provided CA. func MakeRealHostCert(realCA ssh.Signer) (ssh.Signer, error) { - return makeHostCert(realCA.PublicKey(), realCA, defaultPrincipal) + _, hostKey, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + return nil, trace.Wrap(err) + } + return makeHostCert(hostKey, realCA.PublicKey(), realCA, defaultPrincipal) +} + +// MakeRealHostCertWithKey makes an SSH host certificate with the provided key that is +// signed by the provided CA. +func MakeRealHostCertWithKey(hostKey crypto.Signer, realCA ssh.Signer) (ssh.Signer, error) { + return makeHostCert(hostKey, realCA.PublicKey(), realCA, defaultPrincipal) } // MakeRealHostCertWithPrincipals makes an SSH host certificate that is signed by the // provided CA for the provided principals. func MakeRealHostCertWithPrincipals(realCA ssh.Signer, principals ...string) (ssh.Signer, error) { - return makeHostCert(realCA.PublicKey(), realCA, principals...) -} - -func makeHostCert(signKey ssh.PublicKey, signer ssh.Signer, principals ...string) (ssh.Signer, error) { - priv, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize) - if err != nil { - return nil, trace.Wrap(err) - } - - privSigner, err := ssh.NewSignerFromKey(priv) + _, hostKey, err := ed25519.GenerateKey(rand.Reader) if err != nil { return nil, trace.Wrap(err) } + return makeHostCert(hostKey, realCA.PublicKey(), realCA, principals...) +} - pub, err := ssh.NewPublicKey(priv.Public()) +func makeHostCert(hostKey crypto.Signer, caPublicKey ssh.PublicKey, caSigner ssh.Signer, principals ...string) (ssh.Signer, error) { + hostSigner, err := ssh.NewSignerFromSigner(hostKey) if err != nil { return nil, trace.Wrap(err) } @@ -87,9 +94,9 @@ func makeHostCert(signKey ssh.PublicKey, signer ssh.Signer, principals ...string cert := &ssh.Certificate{ Nonce: nonce, - Key: pub, + Key: hostSigner.PublicKey(), CertType: ssh.HostCert, - SignatureKey: signKey, + SignatureKey: caPublicKey, ValidPrincipals: principals, ValidBefore: uint64(time.Now().Add(time.Hour).Unix()), } @@ -104,12 +111,12 @@ func makeHostCert(signKey ssh.PublicKey, signer ssh.Signer, principals ...string bytesForSigning := cert.Marshal() bytesForSigning = bytesForSigning[:len(bytesForSigning)-4] - cert.Signature, err = signer.Sign(rand.Reader, bytesForSigning) + cert.Signature, err = caSigner.Sign(rand.Reader, bytesForSigning) if err != nil { return nil, trace.Wrap(err) } - certSigner, err := ssh.NewCertSigner(cert, privSigner) + certSigner, err := ssh.NewCertSigner(cert, hostSigner) if err != nil { return nil, trace.Wrap(err) } diff --git a/integration/helpers/instance.go b/integration/helpers/instance.go index df01f95d753fa..275837306b9d3 100644 --- a/integration/helpers/instance.go +++ b/integration/helpers/instance.go @@ -21,7 +21,6 @@ package helpers import ( "bytes" "context" - "crypto/rsa" "crypto/tls" "crypto/x509/pkix" "encoding/json" @@ -47,6 +46,7 @@ import ( "github.com/gravitational/teleport/api/breaker" clientproto "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/auth/keygen" "github.com/gravitational/teleport/lib/auth/state" @@ -55,6 +55,7 @@ import ( "github.com/gravitational/teleport/lib/backend/lite" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/cloud/imds" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/httplib/csrf" @@ -343,24 +344,27 @@ func NewInstance(t *testing.T, cfg InstanceConfig) *TeleInstance { } // generate instance secrets (keys): - keygen := keygen.New(context.TODO()) if cfg.Priv == nil || cfg.Pub == nil { - cfg.Priv, cfg.Pub, _ = keygen.GenerateKeyPair() + privateKey, err := cryptosuites.GeneratePrivateKeyWithAlgorithm(cryptosuites.ECDSAP256) + fatalIf(err) + cfg.Priv = privateKey.PrivateKeyPEM() + cfg.Pub = privateKey.MarshalSSHPublicKey() } - rsaKey, err := ssh.ParseRawPrivateKey(cfg.Priv) + key, err := keys.ParsePrivateKey(cfg.Priv) fatalIf(err) - tlsCACert, err := tlsca.GenerateSelfSignedCAWithSigner(rsaKey.(*rsa.PrivateKey), pkix.Name{ + tlsCACert, err := tlsca.GenerateSelfSignedCAWithSigner(key, pkix.Name{ CommonName: cfg.ClusterName, Organization: []string{cfg.ClusterName}, }, nil, defaults.CATTL) fatalIf(err) - signer, err := ssh.ParsePrivateKey(cfg.Priv) + sshSigner, err := ssh.NewSignerFromSigner(key) fatalIf(err) + keygen := keygen.New(context.TODO()) cert, err := keygen.GenerateHostCert(services.HostCertParams{ - CASigner: signer, + CASigner: sshSigner, PublicHostKey: cfg.Pub, HostID: cfg.HostID, NodeName: cfg.NodeName, diff --git a/integration/helpers/testmain.go b/integration/helpers/testmain.go index b045546e24c68..5bc37898bffcf 100644 --- a/integration/helpers/testmain.go +++ b/integration/helpers/testmain.go @@ -23,7 +23,7 @@ import ( "testing" "time" - "github.com/gravitational/teleport/lib/auth/native" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/modules" "github.com/gravitational/teleport/lib/srv" "github.com/gravitational/teleport/lib/utils" @@ -34,7 +34,7 @@ import ( // it as an argument. Otherwise, it will run tests as normal. func TestMainImplementation(m *testing.M) { utils.InitLoggerForTests() - native.PrecomputeTestKeys(m) + cryptosuites.PrecomputeRSATestKeys(m) SetTestTimeouts(3 * time.Second) modules.SetInsecureTestMode(true) // If the test is re-executing itself, execute the command that comes over diff --git a/integration/kube/fixtures.go b/integration/kube/fixtures.go index 42228789a0f1b..5b4ba4910ca33 100644 --- a/integration/kube/fixtures.go +++ b/integration/kube/fixtures.go @@ -29,7 +29,7 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/integration/helpers" - "github.com/gravitational/teleport/lib/auth/native" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/tlsca" "github.com/gravitational/teleport/lib/utils" @@ -88,11 +88,11 @@ func ProxyClient(cfg ProxyConfig) (*kubernetes.Clientset, *rest.Config, error) { if err != nil { return nil, nil, trace.Wrap(err) } - privPEM, _, err := native.GenerateKeyPair() + priv, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) if err != nil { return nil, nil, trace.Wrap(err) } - priv, err := keys.ParsePrivateKey(privPEM) + privPEM, err := keys.MarshalPrivateKey(priv) if err != nil { return nil, nil, trace.Wrap(err) } diff --git a/integrations/event-handler/mtls_certs.go b/integrations/event-handler/mtls_certs.go index 84a7cc80b9827..553acf2c0ef01 100644 --- a/integrations/event-handler/mtls_certs.go +++ b/integrations/event-handler/mtls_certs.go @@ -186,6 +186,7 @@ func (c *MTLSCerts) generate(length int) error { // genCertAndPK generates and returns certificate and primary key func (c *MTLSCerts) genCertAndPK(length int, cert *x509.Certificate, parent *x509.Certificate, signer *rsa.PrivateKey) (*rsa.PrivateKey, []byte, error) { // Generate PK + //nolint:forbidigo // Allow integration to generate RSA key without importing Teleport. pk, err := rsa.GenerateKey(rand.Reader, length) if err != nil { return nil, nil, trace.Wrap(err) diff --git a/integrations/lib/credentials/credentials_test.go b/integrations/lib/credentials/credentials_test.go index a0c917323d726..3bedf5dad56b8 100644 --- a/integrations/lib/credentials/credentials_test.go +++ b/integrations/lib/credentials/credentials_test.go @@ -20,7 +20,6 @@ package credentials import ( "crypto/rand" - "crypto/rsa" "crypto/tls" "crypto/x509" "crypto/x509/pkix" @@ -33,6 +32,7 @@ import ( "golang.org/x/crypto/ssh" "github.com/gravitational/teleport/api/client" + "github.com/gravitational/teleport/lib/cryptosuites" ) // mockTLSCredentials mocks insecure Client credentials. @@ -85,13 +85,13 @@ func TestCheckExpiredCredentials(t *testing.T) { NotAfter: time.Now().Add(1 * time.Hour), } - caKey, err := rsa.GenerateKey(rand.Reader, 1024) + caKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) - clientKey, err := rsa.GenerateKey(rand.Reader, 1024) + clientKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) - validCertBytes, err := x509.CreateCertificate(rand.Reader, validCert, ca, &clientKey.PublicKey, caKey) + validCertBytes, err := x509.CreateCertificate(rand.Reader, validCert, ca, clientKey.Public(), caKey) require.NoError(t, err) - invalidCertBytes, err := x509.CreateCertificate(rand.Reader, expiredCert, ca, &clientKey.PublicKey, caKey) + invalidCertBytes, err := x509.CreateCertificate(rand.Reader, expiredCert, ca, clientKey.Public(), caKey) require.NoError(t, err) expiredCred := &mockTLSCredentials{CertificateChain: &tls.Certificate{Certificate: [][]byte{invalidCertBytes}}} diff --git a/integrations/lib/testing/fakejoin/kubesigner.go b/integrations/lib/testing/fakejoin/kubesigner.go index 3db707a1f8bc5..271c913d2758f 100644 --- a/integrations/lib/testing/fakejoin/kubesigner.go +++ b/integrations/lib/testing/fakejoin/kubesigner.go @@ -19,8 +19,6 @@ package fakejoin import ( - "crypto/rand" - "crypto/rsa" "encoding/json" "fmt" "time" @@ -31,6 +29,7 @@ import ( "github.com/gravitational/trace" "github.com/jonboulle/clockwork" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/kubernetestoken" ) @@ -38,7 +37,6 @@ import ( // allows us to do Kube joining locally. This is useful in tests as this is currently the easiest // delegated join method we can use without having to rely on external infrastructure/providers. type KubernetesSigner struct { - key *rsa.PrivateKey signer jose.Signer jwks *jose.JSONWebKeySet clock clockwork.Clock @@ -48,12 +46,12 @@ const fakeKeyID = "foo" // NewKubernetesSigner generates a keypair and creates a new KubernetesSigner. func NewKubernetesSigner(clock clockwork.Clock) (*KubernetesSigner, error) { - key, err := rsa.GenerateKey(rand.Reader, 2048) + key, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) if err != nil { return nil, trace.Wrap(err, "generating key") } signer, err := jose.NewSigner( - jose.SigningKey{Algorithm: jose.RS256, Key: key}, + jose.SigningKey{Algorithm: jose.ES256, Key: key}, (&jose.SignerOptions{}). WithType("JWT"). WithHeader("kid", fakeKeyID), @@ -65,12 +63,11 @@ func NewKubernetesSigner(clock clockwork.Clock) (*KubernetesSigner, error) { { Key: key.Public(), Use: "sig", - Algorithm: string(jose.RS256), + Algorithm: string(jose.ES256), KeyID: fakeKeyID, }, }} return &KubernetesSigner{ - key: key, signer: signer, jwks: jwks, clock: clock, diff --git a/lib/auth/auth_test.go b/lib/auth/auth_test.go index c8f8bf6a713ea..d9170412d839e 100644 --- a/lib/auth/auth_test.go +++ b/lib/auth/auth_test.go @@ -21,7 +21,6 @@ package auth import ( "context" "crypto/rand" - "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/pem" @@ -66,7 +65,6 @@ import ( "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/auth/keystore" - "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/auth/testauthority" wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/authz" @@ -216,7 +214,7 @@ func newAuthSuite(t *testing.T) *testPack { func TestMain(m *testing.M) { utils.InitLoggerForTests() - native.PrecomputeTestKeys(m) + cryptosuites.PrecomputeRSATestKeys(m) modules.SetInsecureTestMode(true) os.Exit(m.Run()) } @@ -1396,7 +1394,7 @@ func TestSAMLConnectorCRUDEventsEmitted(t *testing.T) { ca, err := tlsca.FromKeys([]byte(fixtures.TLSCACertPEM), []byte(fixtures.TLSCAKeyPEM)) require.NoError(t, err) - privateKey, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize) + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) testClock := clockwork.NewFakeClock() @@ -2581,7 +2579,7 @@ func TestGenerateOpenSSHCert(t *testing.T) { role, ok := r.(*types.RoleV6) require.True(t, ok) - priv, err := native.GeneratePrivateKey() + priv, err := cryptosuites.GeneratePrivateKeyWithAlgorithm(cryptosuites.Ed25519) require.NoError(t, err) reply, err := p.a.GenerateOpenSSHCert(ctx, &proto.OpenSSHCertRequest{ @@ -2693,7 +2691,7 @@ func TestGenerateHostCertWithLocks(t *testing.T) { hostID := uuid.New().String() keygen := testauthority.New() - _, pub, err := keygen.GetNewKeyPairFromPool() + _, pub, err := keygen.GenerateKeyPair() require.NoError(t, err) _, err = p.a.GenerateHostCert(ctx, pub, hostID, "test-node", []string{}, p.clusterName.GetClusterName(), types.RoleNode, time.Minute) diff --git a/lib/auth/auth_with_roles_test.go b/lib/auth/auth_with_roles_test.go index 9eca3cae6fa15..06fcd9e849a09 100644 --- a/lib/auth/auth_with_roles_test.go +++ b/lib/auth/auth_with_roles_test.go @@ -62,7 +62,6 @@ import ( "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/lib/auth/authclient" - "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/auth/testauthority" "github.com/gravitational/teleport/lib/authz" "github.com/gravitational/teleport/lib/cryptosuites" @@ -1484,7 +1483,7 @@ func TestGenerateDatabaseCert(t *testing.T) { } // Generate CSR once for speed sake. - priv, err := native.GeneratePrivateKey() + priv, err := cryptosuites.GeneratePrivateKeyWithAlgorithm(cryptosuites.RSA2048) require.NoError(t, err) csr, err := tlsca.GenerateCertificateRequestPEM(pkix.Name{CommonName: "test"}, priv) diff --git a/lib/auth/db_test.go b/lib/auth/db_test.go index 4127b9a2ed570..13ea93087fd33 100644 --- a/lib/auth/db_test.go +++ b/lib/auth/db_test.go @@ -30,7 +30,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/types" - "github.com/gravitational/teleport/lib/auth/testauthority" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/tlsca" ) @@ -110,7 +110,7 @@ func TestDBCertSigning(t *testing.T) { ctx := context.Background() - privateKey, err := testauthority.New().GeneratePrivateKey() + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.RSA2048) require.NoError(t, err) csr, err := tlsca.GenerateCertificateRequestPEM(pkix.Name{ diff --git a/lib/auth/grpcserver_test.go b/lib/auth/grpcserver_test.go index d1dcd4de42214..c04e48ba5e24a 100644 --- a/lib/auth/grpcserver_test.go +++ b/lib/auth/grpcserver_test.go @@ -2750,7 +2750,7 @@ func TestGenerateDatabaseCerts(t *testing.T) { require.NoError(t, err) // Generate CSR once for speed sake. - priv, err := testauthority.New().GeneratePrivateKey() + priv, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.RSA2048) require.NoError(t, err) csr, err := tlsca.GenerateCertificateRequestPEM(pkix.Name{CommonName: "test"}, priv) require.NoError(t, err) diff --git a/lib/auth/keygen/keygen.go b/lib/auth/keygen/keygen.go index a4d185c722d45..bb77f36012188 100644 --- a/lib/auth/keygen/keygen.go +++ b/lib/auth/keygen/keygen.go @@ -35,7 +35,6 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/wrappers" apiutils "github.com/gravitational/teleport/api/utils" - "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/modules" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/utils" @@ -70,11 +69,6 @@ func New(_ context.Context, opts ...Option) *Keygen { return k } -// GenerateKeyPair returns fresh priv/pub keypair, takes about 300ms to execute. -func (k *Keygen) GenerateKeyPair() ([]byte, []byte, error) { - return native.GenerateKeyPair() -} - // GenerateHostCert generates a host certificate with the passed in parameters. // The private key of the CA to sign the certificate must be provided. func (k *Keygen) GenerateHostCert(c services.HostCertParams) ([]byte, error) { diff --git a/lib/auth/keygen/keygen_test.go b/lib/auth/keygen/keygen_test.go index 09313f5c6b7b6..e2d68d91a923e 100644 --- a/lib/auth/keygen/keygen_test.go +++ b/lib/auth/keygen/keygen_test.go @@ -25,6 +25,7 @@ import ( "time" "github.com/google/go-cmp/cmp" + "github.com/gravitational/trace" "github.com/jonboulle/clockwork" "github.com/stretchr/testify/require" "golang.org/x/crypto/ssh" @@ -32,9 +33,10 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/constants" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/api/utils/sshutils" - "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/auth/test" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/services" ) @@ -48,9 +50,15 @@ func setupNativeContext(ctx context.Context, _ *testing.T) *nativeContext { clock := clockwork.NewFakeClockAt(time.Date(2016, 9, 8, 7, 6, 5, 0, time.UTC)) tt.suite = &test.AuthSuite{ - A: New(ctx, SetClock(clock)), - Keygen: native.GenerateKeyPair, - Clock: clock, + A: New(ctx, SetClock(clock)), + Keygen: func() ([]byte, []byte, error) { + privateKey, err := cryptosuites.GeneratePrivateKeyWithAlgorithm(cryptosuites.ECDSAP256) + if err != nil { + return nil, nil, trace.Wrap(err) + } + return privateKey.PrivateKeyPEM(), privateKey.MarshalSSHPublicKey(), nil + }, + Clock: clock, } return &tt @@ -91,14 +99,16 @@ func TestBuildPrincipals(t *testing.T) { tt := setupNativeContext(context.Background(), t) - caPrivateKey, _, err := native.GenerateKeyPair() + caPrivateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.Ed25519) require.NoError(t, err) - - caSigner, err := ssh.ParsePrivateKey(caPrivateKey) + caSigner, err := ssh.NewSignerFromSigner(caPrivateKey) require.NoError(t, err) - _, hostPublicKey, err := native.GenerateKeyPair() + hostKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.Ed25519) require.NoError(t, err) + hostPrivateKey, err := keys.NewSoftwarePrivateKey(hostKey) + require.NoError(t, err) + hostPublicKey := hostPrivateKey.MarshalSSHPublicKey() tests := []struct { desc string @@ -191,10 +201,9 @@ func TestUserCertCompatibility(t *testing.T) { tt := setupNativeContext(context.Background(), t) - priv, pub, err := native.GenerateKeyPair() + caKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.Ed25519) require.NoError(t, err) - - caSigner, err := ssh.ParsePrivateKey(priv) + caSigner, err := ssh.NewSignerFromSigner(caKey) require.NoError(t, err) tests := []struct { @@ -219,7 +228,7 @@ func TestUserCertCompatibility(t *testing.T) { userCertificateBytes, err := tt.suite.A.GenerateUserCert(services.UserCertParams{ CASigner: caSigner, - PublicUserKey: pub, + PublicUserKey: ssh.MarshalAuthorizedKey(caSigner.PublicKey()), Username: "user", AllowedLogins: []string{"centos", "root"}, TTL: time.Hour, diff --git a/lib/auth/kube_test.go b/lib/auth/kube_test.go index 9b8222deda8b7..08af05f555701 100644 --- a/lib/auth/kube_test.go +++ b/lib/auth/kube_test.go @@ -19,7 +19,6 @@ package auth import ( - "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/pem" @@ -33,6 +32,7 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/entitlements" "github.com/gravitational/teleport/lib/auth/authclient" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/modules" "github.com/gravitational/teleport/lib/tlsca" ) @@ -105,15 +105,15 @@ func TestProcessKubeCSR(t *testing.T) { // newTestCSR creates and PEM-encodes an x509 CSR with given subject. func newTestCSR(subj pkix.Name) ([]byte, error) { - // Use math/rand to avoid blocking on system entropy. - rng := rand.New(rand.NewSource(0)) - priv, err := rsa.GenerateKey(rng, 2048) + priv, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) if err != nil { return nil, err } x509CSR := &x509.CertificateRequest{ Subject: subj, } + // Use math/rand to avoid blocking on system entropy. + rng := rand.New(rand.NewSource(0)) derCSR, err := x509.CreateCertificateRequest(rng, x509CSR, priv) if err != nil { return nil, err diff --git a/lib/auth/machineid/machineidv1/workload_identity_service_test.go b/lib/auth/machineid/machineidv1/workload_identity_service_test.go index 38bff691642f8..dfe099c483b15 100644 --- a/lib/auth/machineid/machineidv1/workload_identity_service_test.go +++ b/lib/auth/machineid/machineidv1/workload_identity_service_test.go @@ -32,7 +32,7 @@ import ( machineidv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/machineid/v1" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/auth" - "github.com/gravitational/teleport/lib/auth/native" + "github.com/gravitational/teleport/lib/cryptosuites" libjwt "github.com/gravitational/teleport/lib/jwt" ) @@ -93,7 +93,7 @@ func TestWorkloadIdentityService_SignX509SVIDs(t *testing.T) { ) require.NoError(t, err) - privateKey, err := native.GenerateRSAPrivateKey() + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) pubBytes, err := x509.MarshalPKIXPublicKey(privateKey.Public()) require.NoError(t, err) diff --git a/lib/auth/native/native.go b/lib/auth/native/native.go index 093e6947713e7..e8128fa06c2dc 100644 --- a/lib/auth/native/native.go +++ b/lib/auth/native/native.go @@ -16,166 +16,44 @@ * along with this program. If not, see . */ +// Package native will be deleted as soon as references are removed from +// teleport.e. package native import ( - "crypto/ed25519" - "crypto/rand" "crypto/rsa" - "crypto/x509" - "encoding/pem" - "sync" "testing" - "time" "github.com/gravitational/trace" - "github.com/sirupsen/logrus" - "github.com/gravitational/teleport" - "github.com/gravitational/teleport/api/constants" - "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/lib/cryptosuites" + "github.com/gravitational/teleport/lib/modules" ) -var log = logrus.WithFields(logrus.Fields{ - teleport.ComponentKey: teleport.ComponentKeyGen, -}) - -// precomputedKeys is a queue of cached keys ready for usage. -var precomputedKeys = make(chan *rsa.PrivateKey, 25) - -// startPrecomputeOnce is used to start the background task that precomputes key pairs. -var startPrecomputeOnce sync.Once - -// GenerateKeyPair generates a new RSA key pair. -func GenerateKeyPair() ([]byte, []byte, error) { - priv, err := GeneratePrivateKey() - if err != nil { - return nil, nil, trace.Wrap(err) - } - return priv.PrivateKeyPEM(), priv.MarshalSSHPublicKey(), nil -} - -// GenerateEICEKey generates a key that can be send to an Amazon EC2 instance using the ec2instanceconnect.SendSSHPublicKey method. -func GenerateEICEKey() (publicKey any, privateKey any, err error) { - if IsBoringBinary() { - privKey, err := GeneratePrivateKey() - if err != nil { - return nil, nil, trace.Wrap(err) - } - - return privKey.Public(), privKey, nil - } - - pubKey, privKey, err := ed25519.GenerateKey(nil) - if err != nil { - return nil, nil, trace.Wrap(err) - } - - return pubKey, privKey, nil -} - -// GeneratePrivateKey generates a new RSA private key. -func GeneratePrivateKey() (*keys.PrivateKey, error) { - rsaKey, err := getOrGenerateRSAPrivateKey() +// GenerateRSAPrivateKey will be deleted as soon as references are removed from +// teleport.e. +func GenerateRSAPrivateKey() (*rsa.PrivateKey, error) { + key, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.RSA2048) if err != nil { return nil, trace.Wrap(err) } - - // We encode the private key in PKCS #1, ASN.1 DER form - // instead of PKCS #8 to maintain compatibility with some - // third party clients. - keyPEM := pem.EncodeToMemory(&pem.Block{ - Type: keys.PKCS1PrivateKeyType, - Headers: nil, - Bytes: x509.MarshalPKCS1PrivateKey(rsaKey), - }) - - return keys.NewPrivateKey(rsaKey, keyPEM) -} - -func GenerateRSAPrivateKey() (*rsa.PrivateKey, error) { - return getOrGenerateRSAPrivateKey() -} - -func getOrGenerateRSAPrivateKey() (*rsa.PrivateKey, error) { - select { - case k := <-precomputedKeys: - return k, nil - default: - rsaKeyPair, err := generateRSAPrivateKey() - if err != nil { - return nil, err - } - return rsaKeyPair, nil - } + return key.(*rsa.PrivateKey), nil } -func generateRSAPrivateKey() (*rsa.PrivateKey, error) { - return rsa.GenerateKey(rand.Reader, constants.RSAKeySize) -} - -func precomputeKeys() { - const backoff = time.Second * 30 - for { - rsaPrivateKey, err := generateRSAPrivateKey() - if err != nil { - log.WithError(err).Errorf("Failed to precompute key pair, retrying in %s (this might be a bug).", backoff) - time.Sleep(backoff) - } - - precomputedKeys <- rsaPrivateKey - } -} - -func precomputeTestKeys() { - generatedTestKeys := generateTestKeys() - keysToReuse := make([]*rsa.PrivateKey, 0, testKeysNumber) - for range testKeysNumber { - k := <-generatedTestKeys - precomputedKeys <- k - keysToReuse = append(keysToReuse, k) - } - for { - for _, k := range keysToReuse { - precomputedKeys <- k - } - } -} - -// testKeysNumber is the number of RSA keys generated in tests. -const testKeysNumber = 25 - -func generateTestKeys() <-chan *rsa.PrivateKey { - generatedTestKeys := make(chan *rsa.PrivateKey, testKeysNumber) - for range testKeysNumber { - // Generate each key in a separate goroutine to take advantage of - // multiple cores if possible. - go func() { - private, err := generateRSAPrivateKey() - if err != nil { - // Use only in tests. Safe to panic. - panic(err) - } - generatedTestKeys <- private - }() - } - return generatedTestKeys +// PrecomputeKeys is an alias of [cryptosuites.PrecomputeRSAKeys]. It will be +// deleted as soon as references are removed from teleport.e. +func PrecomputeKeys() { + cryptosuites.PrecomputeRSAKeys() } -// PrecomputeKeys sets this package into a mode where a small backlog of keys are -// computed in advance. This should only be enabled if large spikes in key computation -// are expected (e.g. in auth/proxy services). Safe to double-call. -func PrecomputeKeys() { - startPrecomputeOnce.Do(func() { - go precomputeKeys() - }) +// PrecomputeTestKeys is an alias of [cryptosuites.PrecomputeRSATestKeys]. It +// will be deleted as soon as references are removed from teleport.e. +func PrecomputeTestKeys(m *testing.M) { + cryptosuites.PrecomputeRSATestKeys(m) } -// PrecomputeTestKeys generates RSA keys and reuse them to reduce CPU usage. This method should -// only be in tests. Safe to call multiple times. -// This function takes *testing.M, so is only can be used from TestMain in tests. -func PrecomputeTestKeys(_ *testing.M) { - startPrecomputeOnce.Do(func() { - go precomputeTestKeys() - }) +// IsBoringBinary is an alias of [modules.IsBoringBinary]. It will be deleted as +// soon as references are removed from teleport.e. +func IsBoringBinary() bool { + return modules.IsBoringBinary() } diff --git a/lib/auth/native/native_test.go b/lib/auth/native/native_test.go deleted file mode 100644 index 319b242b4f3e5..0000000000000 --- a/lib/auth/native/native_test.go +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Teleport - * Copyright (C) 2023 Gravitational, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package native - -import ( - "crypto/ed25519" - "crypto/rsa" - "crypto/x509" - "encoding/pem" - "os" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/gravitational/teleport/lib/utils" -) - -func TestMain(m *testing.M) { - utils.InitLoggerForTests() - os.Exit(m.Run()) -} - -// TestPrecomputeMode verifies that package enters precompute mode when -// PrecomputeKeys is called. -func TestPrecomputeMode(t *testing.T) { - t.Parallel() - - PrecomputeKeys() - - select { - case <-precomputedKeys: - case <-time.After(time.Second * 10): - t.Fatal("Key precompute routine failed to start.") - } -} - -// TestGenerateRSAPKSC1Keypair tests that GeneratePrivateKey generates -// a valid PKCS1 rsa key. -func TestGeneratePKSC1RSAKey(t *testing.T) { - t.Parallel() - - priv, err := GeneratePrivateKey() - require.NoError(t, err) - - block, rest := pem.Decode(priv.PrivateKeyPEM()) - require.NoError(t, err) - require.Empty(t, rest) - - _, err = x509.ParsePKCS1PrivateKey(block.Bytes) - require.NoError(t, err) -} - -func TestGenerateEICEKey_when_boringbinary(t *testing.T) { - if !IsBoringBinary() { - t.Skip() - } - - publicKey, privateKey, err := GenerateEICEKey() - require.NoError(t, err) - - // We expect an RSA Key because boringcrypto doesn't yet support generating ED25519 keys. - require.IsType(t, rsa.PublicKey{}, publicKey) - require.IsType(t, rsa.PrivateKey{}, privateKey) -} - -func TestGenerateEICEKey(t *testing.T) { - if IsBoringBinary() { - t.Skip() - } - - publicKey, privateKey, err := GenerateEICEKey() - require.NoError(t, err) - - // We expect an ED25519 key - require.IsType(t, ed25519.PublicKey{}, publicKey) - require.IsType(t, ed25519.PrivateKey{}, privateKey) -} diff --git a/lib/auth/sessions.go b/lib/auth/sessions.go index 0b394e1450da4..e93ac15133d11 100644 --- a/lib/auth/sessions.go +++ b/lib/auth/sessions.go @@ -38,7 +38,6 @@ import ( apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/entitlements" - "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/defaults" dtconfig "github.com/gravitational/teleport/lib/devicetrust/config" @@ -257,12 +256,12 @@ func (a *Server) newWebSession( return nil, nil, trace.Wrap(err) } if _, isRSA := sshKey.Public().(*rsa.PublicKey); isRSA { - // Ensure the native package is precomputing RSA keys if we ever - // generate one. [native.PrecomputeKeys] is idempotent. + // Start precomputing RSA keys if we ever generate one. + // [cryptosuites.PrecomputeRSAKeys] is idempotent. // Doing this lazily easily handles changing signature algorithm // suites and won't start precomputing keys if they are never needed // (a major benefit in tests). - native.PrecomputeKeys() + cryptosuites.PrecomputeRSAKeys() } } diff --git a/lib/auth/testauthority/testauthority.go b/lib/auth/testauthority/testauthority.go index 2bd004aecf909..8dae039d9c1f4 100644 --- a/lib/auth/testauthority/testauthority.go +++ b/lib/auth/testauthority/testauthority.go @@ -26,9 +26,8 @@ import ( "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/lib/auth/keygen" - "github.com/gravitational/teleport/lib/auth/native" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/services" ) @@ -47,24 +46,14 @@ func NewWithClock(clock clockwork.Clock) *Keygen { return &Keygen{Keygen: inner} } -// GeneratePrivateKey generates a new PrivateKey. -func (n *Keygen) GeneratePrivateKey() (*keys.PrivateKey, error) { - priv, _, err := n.GenerateKeyPair() - if err != nil { - return nil, trace.Wrap(err) - } - - return keys.ParsePrivateKey(priv) -} - -func (n *Keygen) GetNewKeyPairFromPool() (priv []byte, pub []byte, err error) { - return n.GenerateKeyPair() -} - // GenerateKeyPair returns a new private key in PEM format and an ssh // public key in authorized_key format. func (n *Keygen) GenerateKeyPair() (priv []byte, pub []byte, err error) { - return native.GenerateKeyPair() + privateKey, err := cryptosuites.GeneratePrivateKeyWithAlgorithm(cryptosuites.ECDSAP256) + if err != nil { + return nil, nil, trace.Wrap(err) + } + return privateKey.PrivateKeyPEM(), privateKey.MarshalSSHPublicKey(), nil } func (n *Keygen) GenerateHostCert(c services.HostCertParams) ([]byte, error) { diff --git a/lib/auth/windows/windows.go b/lib/auth/windows/windows.go index c44f7f35be8e2..b198075036a4d 100644 --- a/lib/auth/windows/windows.go +++ b/lib/auth/windows/windows.go @@ -34,6 +34,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/tlsca" ) @@ -70,12 +71,12 @@ func getCertRequest(req *GenerateCredentialsRequest) (*certRequest, error) { // Important: rdpclient currently only supports 2048-bit RSA keys. // If you switch the key type here, update handle_general_authentication in // rdp/rdpclient/src/piv.rs accordingly. - rsaKey, err := rsa.GenerateKey(rand.Reader, 2048) + rsaKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.RSA2048) if err != nil { return nil, trace.Wrap(err) } // Also important: rdpclient expects the private key to be in PKCS1 format. - keyDER := x509.MarshalPKCS1PrivateKey(rsaKey) + keyDER := x509.MarshalPKCS1PrivateKey(rsaKey.(*rsa.PrivateKey)) // Generate the Windows-compatible certificate, see // https://docs.microsoft.com/en-us/troubleshoot/windows-server/windows-security/enabling-smart-card-logon-third-party-certification-authorities diff --git a/lib/circleci/token_validator_test.go b/lib/circleci/token_validator_test.go index 99d56f731b461..5d0a65b683ed5 100644 --- a/lib/circleci/token_validator_test.go +++ b/lib/circleci/token_validator_test.go @@ -20,8 +20,6 @@ package circleci import ( "context" - "crypto/rand" - "crypto/rsa" "encoding/json" "fmt" "net/http" @@ -33,6 +31,8 @@ import ( "github.com/go-jose/go-jose/v3/jwt" "github.com/jonboulle/clockwork" "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/lib/cryptosuites" ) // fakeIDP pretends to be a circle CI org OIDC provider, e.g: @@ -76,7 +76,7 @@ func (f *fakeIDP) issuerURLTemplate() string { func newFakeIDP(t *testing.T, organizationID string) *fakeIDP { // Generate keypair for IDP - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.RSA2048) require.NoError(t, err) signer, err := jose.NewSigner( @@ -121,7 +121,7 @@ func newFakeIDP(t *testing.T, organizationID string) *fakeIDP { jwks := jose.JSONWebKeySet{ Keys: []jose.JSONWebKey{ { - Key: &privateKey.PublicKey, + Key: privateKey.Public(), }, }, } diff --git a/lib/client/api_test.go b/lib/client/api_test.go index 536809ceea02b..5558fb27b112a 100644 --- a/lib/client/api_test.go +++ b/lib/client/api_test.go @@ -41,7 +41,7 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils/grpc/interceptors" "github.com/gravitational/teleport/api/utils/keys" - "github.com/gravitational/teleport/lib/auth/native" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/modules" @@ -53,7 +53,7 @@ import ( func TestMain(m *testing.M) { utils.InitLoggerForTests() modules.SetInsecureTestMode(true) - native.PrecomputeTestKeys(m) + cryptosuites.PrecomputeRSATestKeys(m) os.Exit(m.Run()) } diff --git a/lib/client/keystore_test.go b/lib/client/keystore_test.go index 613f0a7481929..620ec8f687981 100644 --- a/lib/client/keystore_test.go +++ b/lib/client/keystore_test.go @@ -218,7 +218,7 @@ func TestCheckKey(t *testing.T) { keyRing := auth.makeSignedKeyRing(t, idx, false) // Swap out the key with a ECDSA SSH key. - ellipticCertificate, _, err := cert.CreateEllipticCertificate("foo", ssh.UserCert) + ellipticCertificate, _, err := cert.CreateTestECDSACertificate("foo", ssh.UserCert) require.NoError(t, err) keyRing.Cert = ssh.MarshalAuthorizedKey(ellipticCertificate) @@ -246,7 +246,7 @@ func TestCheckKeyFIPS(t *testing.T) { keyRing := auth.makeSignedKeyRing(t, idx, false) // Swap out the key with a ECDSA SSH key. - ellipticCertificate, _, err := cert.CreateEllipticCertificate("foo", ssh.UserCert) + ellipticCertificate, _, err := cert.CreateTestECDSACertificate("foo", ssh.UserCert) require.NoError(t, err) keyRing.Cert = ssh.MarshalAuthorizedKey(ellipticCertificate) diff --git a/lib/client/local_proxy_middleware_test.go b/lib/client/local_proxy_middleware_test.go index bc32ab4b16e32..cfb79e335601e 100644 --- a/lib/client/local_proxy_middleware_test.go +++ b/lib/client/local_proxy_middleware_test.go @@ -29,8 +29,8 @@ import ( "github.com/jonboulle/clockwork" "github.com/stretchr/testify/require" - "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/client" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/tlsca" ) @@ -120,7 +120,7 @@ func newMockCertIssuer(t *testing.T, clock clockwork.Clock) *mockCertIssuer { } func (c *mockCertIssuer) initCA(t *testing.T) { - priv, err := native.GeneratePrivateKey() + priv, err := cryptosuites.GeneratePrivateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) cert, err := tlsca.GenerateSelfSignedCAWithConfig(tlsca.GenerateCAConfig{ @@ -147,7 +147,7 @@ func (c *mockCertIssuer) IssueCert(ctx context.Context) (tls.Certificate, error) return tls.Certificate{}, trace.Wrap(c.issueErr) } - priv, err := native.GeneratePrivateKey() + priv, err := cryptosuites.GeneratePrivateKeyWithAlgorithm(cryptosuites.ECDSAP256) if err != nil { return tls.Certificate{}, trace.Wrap(err) } diff --git a/lib/cryptosuites/internal/rsa/rsa.go b/lib/cryptosuites/internal/rsa/rsa.go new file mode 100644 index 0000000000000..2203bd22bf05e --- /dev/null +++ b/lib/cryptosuites/internal/rsa/rsa.go @@ -0,0 +1,129 @@ +// Teleport +// Copyright (C) 2023 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package rsa + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "log/slog" + "sync" + "testing" + "time" + + "github.com/gravitational/teleport" + "github.com/gravitational/teleport/api/constants" +) + +var log = slog.With(teleport.ComponentKey, teleport.ComponentKeyGen) + +// precomputedKeys is a queue of cached keys ready for usage. +var precomputedKeys = make(chan *rsa.PrivateKey, 25) + +// startPrecomputeOnce is used to start the background task that precomputes key pairs. +var startPrecomputeOnce sync.Once + +// GenerateKey returns a newly generated RSA private key. +func GenerateKey() (*rsa.PrivateKey, error) { + return getOrGenerateRSAPrivateKey() +} + +func getOrGenerateRSAPrivateKey() (*rsa.PrivateKey, error) { + select { + case k := <-precomputedKeys: + return k, nil + default: + rsaKeyPair, err := generateRSAPrivateKey() + if err != nil { + return nil, err + } + return rsaKeyPair, nil + } +} + +func generateRSAPrivateKey() (*rsa.PrivateKey, error) { + //nolint:forbidigo // This is the one function allowed to generate RSA keys. + return rsa.GenerateKey(rand.Reader, constants.RSAKeySize) +} + +func precomputeKeys() { + const backoff = time.Second * 30 + for { + rsaPrivateKey, err := generateRSAPrivateKey() + if err != nil { + log.ErrorContext(context.Background(), "Failed to precompute key pair, retrying (this might be a bug).", + slog.Any("error", err), slog.Duration("backoff", backoff)) + time.Sleep(backoff) + } + + precomputedKeys <- rsaPrivateKey + } +} + +func precomputeTestKeys() { + generatedTestKeys := generateTestKeys() + keysToReuse := make([]*rsa.PrivateKey, 0, testKeysNumber) + for range testKeysNumber { + k := <-generatedTestKeys + precomputedKeys <- k + keysToReuse = append(keysToReuse, k) + } + for { + for _, k := range keysToReuse { + precomputedKeys <- k + } + } +} + +// testKeysNumber is the number of RSA keys generated in tests. +const testKeysNumber = 25 + +func generateTestKeys() <-chan *rsa.PrivateKey { + generatedTestKeys := make(chan *rsa.PrivateKey, testKeysNumber) + for range testKeysNumber { + // Generate each key in a separate goroutine to take advantage of + // multiple cores if possible. + go func() { + private, err := generateRSAPrivateKey() + if err != nil { + // Use only in tests. Safe to panic. + panic(err) + } + generatedTestKeys <- private + }() + } + return generatedTestKeys +} + +// PrecomputeKeys sets this package into a mode where a small backlog of keys are +// computed in advance. This should only be enabled if large spikes in key computation +// are expected (e.g. in auth/proxy services). Safe to double-call. +func PrecomputeKeys() { + startPrecomputeOnce.Do(func() { + go precomputeKeys() + }) +} + +// PrecomputeTestKeys generates a fixed number of RSA keys and reuses them to +// reduce CPU usage. This method should only be in tests. Safe to call multiple +// times. This function takes *testing.M so it can only be used from TestMain in +// tests. +func PrecomputeTestKeys(_ *testing.M) { + startPrecomputeOnce.Do(func() { + go precomputeTestKeys() + }) +} diff --git a/lib/cryptosuites/internal/rsa/rsa_test.go b/lib/cryptosuites/internal/rsa/rsa_test.go new file mode 100644 index 0000000000000..15bbf508f37d4 --- /dev/null +++ b/lib/cryptosuites/internal/rsa/rsa_test.go @@ -0,0 +1,36 @@ +// Teleport +// Copyright (C) 2023 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package rsa + +import ( + "testing" + "time" +) + +// TestPrecomputeMode verifies that package enters precompute mode when +// PrecomputeKeys is called. +func TestPrecomputeMode(t *testing.T) { + t.Parallel() + + PrecomputeKeys() + + select { + case <-precomputedKeys: + case <-time.After(time.Second * 10): + t.Fatal("Key precompute routine failed to start.") + } +} diff --git a/lib/cryptosuites/precompute.go b/lib/cryptosuites/precompute.go new file mode 100644 index 0000000000000..6e25378f9eaf6 --- /dev/null +++ b/lib/cryptosuites/precompute.go @@ -0,0 +1,38 @@ +// Teleport +// Copyright (C) 2024 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package cryptosuites + +import ( + "testing" + + "github.com/gravitational/teleport/lib/cryptosuites/internal/rsa" +) + +// PrecomputeKeys sets this package into a mode where a small backlog of RSA keys are +// computed in advance. This should only be enabled if large spikes in RSA key +// computation are expected (e.g. in auth/proxy services when the legacy suite +// is configured). Safe to double-call. +func PrecomputeRSAKeys() { + rsa.PrecomputeKeys() +} + +// PrecomputeRSATestKeys may be called from TestMain to set this package into a +// mode where it will precompute a fixed number of RSA keys and reuse them to +// save on CPU usage. +func PrecomputeRSATestKeys(m *testing.M) { + rsa.PrecomputeTestKeys(m) +} diff --git a/lib/cryptosuites/suites.go b/lib/cryptosuites/suites.go index 6d8d509b74952..641c53ce1c8ac 100644 --- a/lib/cryptosuites/suites.go +++ b/lib/cryptosuites/suites.go @@ -32,7 +32,8 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/types" - "github.com/gravitational/teleport/lib/auth/native" + "github.com/gravitational/teleport/api/utils/keys" + internalrsa "github.com/gravitational/teleport/lib/cryptosuites/internal/rsa" ) const defaultSuite = types.SignatureAlgorithmSuite_SIGNATURE_ALGORITHM_SUITE_LEGACY @@ -76,6 +77,9 @@ const ( // SPIFFECAJWT represents the JWT key for the spiffe CA. SPIFFECAJWT + // OktaCAJWT represents the JWT key for the Okta CA. + OktaCAJWT + // ProxyToDatabaseAgent represents keys used by the Proxy to dial the // Database agent over a reverse tunnel. ProxyToDatabaseAgent @@ -105,10 +109,8 @@ const ( // BotSVID represents a key used for a SPIFFE SVID generated by tbot. BotSVID - // OktaCAJWT represents the JWT key for the Okta CA. - OktaCAJWT - - // TODO(nklaassen): define remaining key purposes. + // EC2InstanceConnect is a key used for the EC2 Instance Connect service. + EC2InstanceConnect // keyPurposeMax is 1 greater than the last valid key purpose, used to test that all values less than this // are valid for each suite. @@ -179,7 +181,8 @@ var ( // never restricted algorithm support. ProxyToDatabaseAgent: RSA2048, ProxyKubeClient: RSA2048, - // TODO(nklaassen): define remaining key purposes. + // EC2InstanceConnect has always used Ed25519 by default. + EC2InstanceConnect: Ed25519, } // balancedV1 strikes a balance between security, compatibility, and @@ -209,7 +212,7 @@ var ( BotSVID: ECDSAP256, ProxyToDatabaseAgent: ECDSAP256, ProxyKubeClient: ECDSAP256, - // TODO(nklaassen): define remaining key purposes. + EC2InstanceConnect: Ed25519, } // fipsv1 is an algorithm suite tailored for FIPS compliance. It is based on @@ -239,7 +242,7 @@ var ( BotSVID: ECDSAP256, ProxyToDatabaseAgent: ECDSAP256, ProxyKubeClient: ECDSAP256, - // TODO(nklaassen): define remaining key purposes. + EC2InstanceConnect: ECDSAP256, } // hsmv1 in an algorithm suite tailored for clusters using an HSM or KMS @@ -271,7 +274,7 @@ var ( BotSVID: ECDSAP256, ProxyToDatabaseAgent: ECDSAP256, ProxyKubeClient: ECDSAP256, - // TODO(nklaassen): define remaining key purposes. + EC2InstanceConnect: Ed25519, } allSuites = map[types.SignatureAlgorithmSuite]suite{ @@ -421,8 +424,19 @@ func GenerateKeyWithAlgorithm(alg Algorithm) (crypto.Signer, error) { } } +// GeneratePrivateKeyWithAlgorithm is exactly like [GenerateKeyWithAlgorithm] +// but wraps the returned key in a [*keys.PrivateKey] for convenience. +func GeneratePrivateKeyWithAlgorithm(alg Algorithm) (*keys.PrivateKey, error) { + key, err := GenerateKeyWithAlgorithm(alg) + if err != nil { + return nil, trace.Wrap(err) + } + privateKey, err := keys.NewSoftwarePrivateKey(key) + return privateKey, trace.Wrap(err) +} + func generateRSA2048() (*rsa.PrivateKey, error) { - key, err := native.GenerateRSAPrivateKey() + key, err := internalrsa.GenerateKey() return key, trace.Wrap(err) } diff --git a/lib/devicetrust/challenge/challenge_test.go b/lib/devicetrust/challenge/challenge_test.go index 01e25c014b4c0..e78443420ed5d 100644 --- a/lib/devicetrust/challenge/challenge_test.go +++ b/lib/devicetrust/challenge/challenge_test.go @@ -17,72 +17,43 @@ package challenge_test import ( - "crypto" - "crypto/ecdsa" - "crypto/ed25519" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" "testing" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/devicetrust/challenge" ) func TestSignAndVerify(t *testing.T) { - ecKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - t.Fatalf("GenerateKey failed: %v", err) - } - rsaKey, err := rsa.GenerateKey(rand.Reader, 2048 /* bits */) - if err != nil { - t.Fatalf("GenerateKey failed: %v", err) - } - edPub, edPriv, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - t.Fatalf("GenerateKey failed: %v", err) - } + for _, algo := range []cryptosuites.Algorithm{ + cryptosuites.RSA2048, + cryptosuites.ECDSAP256, + cryptosuites.Ed25519, + } { + t.Run(algo.String(), func(t *testing.T) { + t.Parallel() - tests := []struct { - name string - signer crypto.Signer - pubKey crypto.PublicKey - }{ - { - name: "ecdsa key", - signer: ecKey, - pubKey: ecKey.Public(), - }, - { - name: "rsa key", - signer: rsaKey, - pubKey: rsaKey.Public(), - }, - { - name: "ed25519 key", - signer: edPriv, - pubKey: edPub, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { chal, err := challenge.New() if err != nil { t.Fatalf("New failed: %v", err) } - sig, err := challenge.Sign(chal, test.signer) + signer, err := cryptosuites.GenerateKeyWithAlgorithm(algo) + require.NoError(t, err) + sig, err := challenge.Sign(chal, signer) if err != nil { t.Fatalf("Sign failed: %v", err) } // Verify correct challenge signature. - if err := challenge.Verify(chal, sig, test.pubKey); err != nil { + if err := challenge.Verify(chal, sig, signer.Public()); err != nil { t.Errorf("Verify returned err=%v, want nil", err) } // Verify bad challenge signature. sig = []byte("invalid sig") - if err := challenge.Verify(chal, sig, test.pubKey); err == nil { + if err := challenge.Verify(chal, sig, signer.Public()); err == nil { t.Error("Verify returned nil err, want non-nil") } }) diff --git a/lib/gcp/token_validator_test.go b/lib/gcp/token_validator_test.go index 48e352743f39b..7dcc139f3e61c 100644 --- a/lib/gcp/token_validator_test.go +++ b/lib/gcp/token_validator_test.go @@ -20,8 +20,7 @@ package gcp import ( "context" - "crypto/rand" - "crypto/rsa" + "crypto" "encoding/json" "net/http" "net/http/httptest" @@ -32,17 +31,20 @@ import ( "github.com/go-jose/go-jose/v3/jwt" "github.com/jonboulle/clockwork" "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/lib/cryptosuites" ) type fakeIDP struct { - t *testing.T - signer jose.Signer - privateKey *rsa.PrivateKey - server *httptest.Server + t *testing.T + signer jose.Signer + publicKey crypto.PublicKey + server *httptest.Server } func newFakeIDP(t *testing.T) *fakeIDP { - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + // GCP uses RSA, prefer to test with it. + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.RSA2048) require.NoError(t, err) signer, err := jose.NewSigner( @@ -52,9 +54,9 @@ func newFakeIDP(t *testing.T) *fakeIDP { require.NoError(t, err) f := &fakeIDP{ - signer: signer, - privateKey: privateKey, - t: t, + signer: signer, + publicKey: privateKey.Public(), + t: t, } providerMux := http.NewServeMux() @@ -106,7 +108,7 @@ func (f *fakeIDP) handleJWKSEndpoint(w http.ResponseWriter, r *http.Request) { jwks := jose.JSONWebKeySet{ Keys: []jose.JSONWebKey{ { - Key: &f.privateKey.PublicKey, + Key: f.publicKey, }, }, } diff --git a/lib/githubactions/token_validator_test.go b/lib/githubactions/token_validator_test.go index 1d7ab775af963..d79475597f321 100644 --- a/lib/githubactions/token_validator_test.go +++ b/lib/githubactions/token_validator_test.go @@ -20,8 +20,7 @@ package githubactions import ( "context" - "crypto/rand" - "crypto/rsa" + "crypto" "encoding/json" "net/http" "net/http/httptest" @@ -32,19 +31,22 @@ import ( "github.com/go-jose/go-jose/v3/jwt" "github.com/jonboulle/clockwork" "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/lib/cryptosuites" ) type fakeIDP struct { t *testing.T signer jose.Signer - privateKey *rsa.PrivateKey + publicKey crypto.PublicKey server *httptest.Server entepriseSlug string ghesMode bool } func newFakeIDP(t *testing.T, ghesMode bool, enterpriseSlug string) *fakeIDP { - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + // Github uses RSA2048, prefer to test with it. + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.RSA2048) require.NoError(t, err) signer, err := jose.NewSigner( @@ -56,7 +58,7 @@ func newFakeIDP(t *testing.T, ghesMode bool, enterpriseSlug string) *fakeIDP { f := &fakeIDP{ signer: signer, ghesMode: ghesMode, - privateKey: privateKey, + publicKey: privateKey.Public(), t: t, entepriseSlug: enterpriseSlug, } @@ -142,7 +144,7 @@ func (f *fakeIDP) handleJWKSEndpoint(w http.ResponseWriter, r *http.Request) { jwks := jose.JSONWebKeySet{ Keys: []jose.JSONWebKey{ { - Key: &f.privateKey.PublicKey, + Key: f.publicKey, }, }, } diff --git a/lib/gitlab/token_validator_test.go b/lib/gitlab/token_validator_test.go index 13a194b1ead01..d6163b2e0702b 100644 --- a/lib/gitlab/token_validator_test.go +++ b/lib/gitlab/token_validator_test.go @@ -20,8 +20,7 @@ package gitlab import ( "context" - "crypto/rand" - "crypto/rsa" + "crypto" "encoding/json" "net/http" "net/http/httptest" @@ -35,18 +34,19 @@ import ( "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/services" ) type fakeIDP struct { - t *testing.T - signer jose.Signer - privateKey *rsa.PrivateKey - server *httptest.Server + t *testing.T + signer jose.Signer + publicKey crypto.PublicKey + server *httptest.Server } func newFakeIDP(t *testing.T) *fakeIDP { - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.RSA2048) require.NoError(t, err) signer, err := jose.NewSigner( @@ -56,9 +56,9 @@ func newFakeIDP(t *testing.T) *fakeIDP { require.NoError(t, err) f := &fakeIDP{ - signer: signer, - privateKey: privateKey, - t: t, + signer: signer, + publicKey: privateKey.Public(), + t: t, } providerMux := http.NewServeMux() @@ -112,7 +112,7 @@ func (f *fakeIDP) handleJWKSEndpoint(w http.ResponseWriter, r *http.Request) { jwks := jose.JSONWebKeySet{ Keys: []jose.JSONWebKey{ { - Key: &f.privateKey.PublicKey, + Key: f.publicKey, }, }, } diff --git a/lib/integrations/awsoidc/eice_sendsshpublickey.go b/lib/integrations/awsoidc/eice_sendsshpublickey.go index f4b74009e2c0b..40b0978f43b09 100644 --- a/lib/integrations/awsoidc/eice_sendsshpublickey.go +++ b/lib/integrations/awsoidc/eice_sendsshpublickey.go @@ -24,8 +24,6 @@ import ( "github.com/aws/aws-sdk-go-v2/service/ec2instanceconnect" "github.com/gravitational/trace" "golang.org/x/crypto/ssh" - - "github.com/gravitational/teleport/lib/auth/native" ) // EICESendSSHPublicKeyClient describes the required methods to send an SSH Public Key to @@ -62,6 +60,9 @@ type SendSSHPublicKeyToEC2Request struct { // EC2SSHLoginUser is the OS user to use when the user wants SSH access. EC2SSHLoginUser string + + // PublicKey is the SSH public key to send. + PublicKey ssh.PublicKey } // CheckAndSetDefaults checks if the required fields are present. @@ -74,55 +75,37 @@ func (r *SendSSHPublicKeyToEC2Request) CheckAndSetDefaults() error { return trace.BadParameter("ec2 ssh login user is required") } + if r.PublicKey == nil { + return trace.BadParameter("SSH public key is required") + } + return nil } // SendSSHPublicKeyToEC2 sends an SSH Public Key to a target EC2 Instance. // This key will be removed by AWS after 60 seconds and can only be used to authenticate the EC2SSHLoginUser. // An [ssh.Signer] is then returned and can be used to access the host. -func SendSSHPublicKeyToEC2(ctx context.Context, clt EICESendSSHPublicKeyClient, req SendSSHPublicKeyToEC2Request) (ssh.Signer, error) { +func SendSSHPublicKeyToEC2(ctx context.Context, clt EICESendSSHPublicKeyClient, req SendSSHPublicKeyToEC2Request) error { if err := req.CheckAndSetDefaults(); err != nil { - return nil, trace.Wrap(err) + return trace.Wrap(err) } - - sshSigner, err := sendSSHPublicKey(ctx, clt, req) - if err != nil { - return nil, trace.Wrap(err) + if err := sendSSHPublicKey(ctx, clt, req); err != nil { + return trace.Wrap(err) } - - return sshSigner, nil + return nil } // sendSSHPublicKey creates a new Private Key and uploads the Public to the ec2 instance. // This key will be removed by AWS after 60 seconds and can only be used to authenticate the EC2SSHLoginUser. // More information: https://docs.aws.amazon.com/ec2-instance-connect/latest/APIReference/API_SendSSHPublicKey.html -func sendSSHPublicKey(ctx context.Context, clt EICESendSSHPublicKeyClient, req SendSSHPublicKeyToEC2Request) (ssh.Signer, error) { - pubKey, privKey, err := native.GenerateEICEKey() - if err != nil { - return nil, trace.Wrap(err) - } - - publicKey, err := ssh.NewPublicKey(pubKey) - if err != nil { - return nil, trace.Wrap(err) - } - - pubKeySSH := string(ssh.MarshalAuthorizedKey(publicKey)) - _, err = clt.SendSSHPublicKey(ctx, +func sendSSHPublicKey(ctx context.Context, clt EICESendSSHPublicKeyClient, req SendSSHPublicKeyToEC2Request) error { + pubKeySSH := string(ssh.MarshalAuthorizedKey(req.PublicKey)) + _, err := clt.SendSSHPublicKey(ctx, &ec2instanceconnect.SendSSHPublicKeyInput{ InstanceId: &req.InstanceID, InstanceOSUser: &req.EC2SSHLoginUser, SSHPublicKey: &pubKeySSH, }, ) - if err != nil { - return nil, trace.Wrap(err) - } - - sshSigner, err := ssh.NewSignerFromKey(privKey) - if err != nil { - return nil, trace.Wrap(err) - } - - return sshSigner, nil + return trace.Wrap(err) } diff --git a/lib/integrations/awsoidc/eice_sendsshpublickey_test.go b/lib/integrations/awsoidc/eice_sendsshpublickey_test.go index 67d3fa2bdadb5..2402cec117fff 100644 --- a/lib/integrations/awsoidc/eice_sendsshpublickey_test.go +++ b/lib/integrations/awsoidc/eice_sendsshpublickey_test.go @@ -27,6 +27,8 @@ import ( "github.com/gravitational/trace" "github.com/stretchr/testify/require" "golang.org/x/crypto/ssh" + + "github.com/gravitational/teleport/lib/cryptosuites" ) func TestSendSSHPublicKeyRequest(t *testing.T) { @@ -91,15 +93,19 @@ func TestSendSSHPublicKeyToEC2(t *testing.T) { m := &mockSendSSHPublicKeyClient{} - sshSigner, err := SendSSHPublicKeyToEC2(ctx, m, SendSSHPublicKeyToEC2Request{ + key, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.Ed25519) + require.NoError(t, err) + sshSigner, err := ssh.NewSignerFromSigner(key) + require.NoError(t, err) + + err = SendSSHPublicKeyToEC2(ctx, m, SendSSHPublicKeyToEC2Request{ InstanceID: "id-123", EC2SSHLoginUser: "root", + PublicKey: sshSigner.PublicKey(), }) require.NoError(t, err) - require.NotNil(t, sshSigner) sshPublicKeyFromSigner := string(ssh.MarshalAuthorizedKey(sshSigner.PublicKey())) - require.Equal(t, sshPublicKeyFromSigner, m.sshKeySent) require.Equal(t, "root", m.sshForUserSent) } diff --git a/lib/kubernetestoken/token_validator_test.go b/lib/kubernetestoken/token_validator_test.go index 10368606e4284..51df225370c22 100644 --- a/lib/kubernetestoken/token_validator_test.go +++ b/lib/kubernetestoken/token_validator_test.go @@ -20,8 +20,6 @@ package kubernetestoken import ( "context" - "crypto/rand" - "crypto/rsa" "encoding/json" "testing" "time" @@ -40,6 +38,7 @@ import ( ctest "k8s.io/client-go/testing" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/lib/cryptosuites" ) var userGroups = []string{"system:serviceaccounts", "system:serviceaccounts:namespace", "system:authenticated"} @@ -286,10 +285,10 @@ func Test_kubernetesSupportsBoundTokens(t *testing.T) { } func testSigner(t *testing.T) ([]byte, jose.Signer) { - key, err := rsa.GenerateKey(rand.Reader, 2048) + key, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) signer, err := jose.NewSigner( - jose.SigningKey{Algorithm: jose.RS256, Key: key}, + jose.SigningKey{Algorithm: jose.ES256, Key: key}, (&jose.SignerOptions{}). WithType("JWT"). WithHeader("kid", "foo"), @@ -300,7 +299,7 @@ func testSigner(t *testing.T) ([]byte, jose.Signer) { { Key: key.Public(), Use: "sig", - Algorithm: string(jose.RS256), + Algorithm: string(jose.ES256), KeyID: "foo", }, }} diff --git a/lib/auth/native/boring.go b/lib/modules/boring.go similarity index 98% rename from lib/auth/native/boring.go rename to lib/modules/boring.go index 0c4a8dfc30ede..afc7588f8306d 100644 --- a/lib/auth/native/boring.go +++ b/lib/modules/boring.go @@ -16,7 +16,7 @@ //go:build boringcrypto -package native +package modules import "crypto/boring" diff --git a/lib/modules/modules.go b/lib/modules/modules.go index 69cb6704f8c9c..a93b359c3ac09 100644 --- a/lib/modules/modules.go +++ b/lib/modules/modules.go @@ -37,7 +37,6 @@ import ( "github.com/gravitational/teleport/api/types/accesslist" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/entitlements" - "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/automaticupgrades" "github.com/gravitational/teleport/lib/tlsca" ) @@ -407,7 +406,7 @@ func (p *defaultModules) SetFeatures(f Features) { } func (p *defaultModules) IsBoringBinary() bool { - return native.IsBoringBinary() + return IsBoringBinary() } // AttestHardwareKey attests a hardware key. diff --git a/lib/auth/native/notboring.go b/lib/modules/notboring.go similarity index 85% rename from lib/auth/native/notboring.go rename to lib/modules/notboring.go index 3fa57fb55e5cb..36cecbce41d72 100644 --- a/lib/auth/native/notboring.go +++ b/lib/modules/notboring.go @@ -16,12 +16,9 @@ //go:build !boringcrypto -package native +package modules // IsBoringBinary checks if the binary was compiled with BoringCrypto. -// -// The boringcrypto GOEXPERIMENT always sets the boringcrypto build tag, so if -// this is compiled in, we're not using BoringCrypto. func IsBoringBinary() bool { return false } diff --git a/lib/multiplexer/multiplexer_test.go b/lib/multiplexer/multiplexer_test.go index 588d98a4da082..8a1d8c0cc4b79 100644 --- a/lib/multiplexer/multiplexer_test.go +++ b/lib/multiplexer/multiplexer_test.go @@ -20,8 +20,6 @@ package multiplexer import ( "context" - "crypto/rand" - "crypto/rsa" "crypto/tls" "crypto/x509" "crypto/x509/pkix" @@ -45,10 +43,9 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials" - "github.com/gravitational/teleport/api/constants" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils/keys" - "github.com/gravitational/teleport/lib/auth/native" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/fixtures" "github.com/gravitational/teleport/lib/httplib" "github.com/gravitational/teleport/lib/jwt" @@ -66,7 +63,7 @@ func TestMain(m *testing.M) { // TestMux tests multiplexing protocols // using the same listener. func TestMux(t *testing.T) { - _, signer, err := cert.CreateCertificate("foo", ssh.HostCert) + _, signer, err := cert.CreateTestEd25519Certificate("foo", ssh.HostCert) require.NoError(t, err) // TestMux tests basic use case of multiplexing TLS @@ -736,22 +733,22 @@ func TestMux(t *testing.T) { certPool.AppendCertsFromPEM(caCert) // Sign server certificate. - serverRSAKey, err := native.GenerateRSAPrivateKey() + serverKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) serverPEM, err := ca.GenerateCertificate(tlsca.CertificateRequest{ Subject: pkix.Name{CommonName: "localhost"}, - PublicKey: serverRSAKey.Public(), + PublicKey: serverKey.Public(), NotAfter: time.Now().Add(time.Hour), DNSNames: []string{"127.0.0.1"}, }) require.NoError(t, err) - serverKeyPEM, err := keys.MarshalPrivateKey(serverRSAKey) + serverKeyPEM, err := keys.MarshalPrivateKey(serverKey) require.NoError(t, err) serverCert, err := tls.X509KeyPair(serverPEM, serverKeyPEM) require.NoError(t, err) // Sign client certificate with database access identity. - clientRSAKey, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize) + clientKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) subject, err := (&tlsca.Identity{ Username: "alice", @@ -763,11 +760,11 @@ func TestMux(t *testing.T) { require.NoError(t, err) clientPEM, err := ca.GenerateCertificate(tlsca.CertificateRequest{ Subject: subject, - PublicKey: clientRSAKey.Public(), + PublicKey: clientKey.Public(), NotAfter: time.Now().Add(time.Hour), }) require.NoError(t, err) - clientKeyPEM, err := keys.MarshalPrivateKey(clientRSAKey) + clientKeyPEM, err := keys.MarshalPrivateKey(clientKey) require.NoError(t, err) clientCert, err := tls.X509KeyPair(clientPEM, clientKeyPEM) require.NoError(t, err) @@ -1179,7 +1176,7 @@ func getTestCertCAsGetterAndSigner(t testing.TB, clusterName string) ([]byte, Ce mockCAGetter := func(ctx context.Context, id types.CertAuthID, loadKeys bool) (types.CertAuthority, error) { return ca, nil } - proxyPriv, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize) + proxyPriv, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) // Create host identity with role "Proxy" diff --git a/lib/proxy/peer/helpers_test.go b/lib/proxy/peer/helpers_test.go index 647ffbcea0277..f9a7b562c5fff 100644 --- a/lib/proxy/peer/helpers_test.go +++ b/lib/proxy/peer/helpers_test.go @@ -23,7 +23,6 @@ import ( "crypto/tls" "crypto/x509" "crypto/x509/pkix" - "encoding/pem" "net" "sync/atomic" "testing" @@ -37,8 +36,9 @@ import ( clientapi "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/types" apiutils "github.com/gravitational/teleport/api/utils" + "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/lib/auth/authclient" - "github.com/gravitational/teleport/lib/auth/native" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/fixtures" "github.com/gravitational/teleport/lib/tlsca" @@ -156,7 +156,7 @@ func certFromIdentity(t *testing.T, ca *tlsca.CertAuthority, ident tlsca.Identit subj, err := ident.Subject() require.NoError(t, err) - privateKey, err := native.GenerateRSAPrivateKey() + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) clock := clockwork.NewRealClock() @@ -171,7 +171,8 @@ func certFromIdentity(t *testing.T, ca *tlsca.CertAuthority, ident tlsca.Identit certBytes, err := ca.GenerateCertificate(request) require.NoError(t, err) - keyPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}) + keyPEM, err := keys.MarshalPrivateKey(privateKey) + require.NoError(t, err) cert, err := tls.X509KeyPair(certBytes, keyPEM) require.NoError(t, err) diff --git a/lib/reversetunnel/cache.go b/lib/reversetunnel/cache.go index 2087ddd218684..afcbd9355153c 100644 --- a/lib/reversetunnel/cache.go +++ b/lib/reversetunnel/cache.go @@ -32,7 +32,6 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/lib/auth/authclient" - "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/utils" @@ -94,12 +93,12 @@ func (c *certificateCache) generateHostCert(ctx context.Context, principals []st } if _, isRSA := hostKey.Public().(*rsa.PublicKey); isRSA { - // Ensure the native package is precomputing RSA keys if we ever - // generate one. [native.PrecomputeKeys] is idempotent. + // Start precomputing RSA keys if we ever generate one. + // [cryptosuites.PrecomputeRSAKeys] is idempotent. // Doing this lazily easily handles changing signature algorithm suites // and won't start precomputing keys if they are never needed (a major // benefit in tests). - native.PrecomputeKeys() + cryptosuites.PrecomputeRSAKeys() } sshPub, err := ssh.NewPublicKey(hostKey.Public()) diff --git a/lib/service/certreloader_test.go b/lib/service/certreloader_test.go index e6b5faeb14172..0c40c91831687 100644 --- a/lib/service/certreloader_test.go +++ b/lib/service/certreloader_test.go @@ -30,7 +30,7 @@ import ( "github.com/stretchr/testify/require" "github.com/gravitational/teleport/lib/service/servicecfg" - "github.com/gravitational/teleport/lib/utils" + "github.com/gravitational/teleport/lib/tlsca" ) func TestCertReloader(t *testing.T) { @@ -121,7 +121,7 @@ func TestCertReloader(t *testing.T) { // Update c0 key, and partially update c0 cert. key, crt := newCertKeyPair(t) write(t, certs[0].PrivateKey, key) - write(t, certs[0].Certificate, crt[0:1024]) + write(t, certs[0].Certificate, crt[0:len(crt)/2]) }, certsReloadErrorAssert: require.Error, certsAssert: func(t *testing.T, before []tls.Certificate, after []tls.Certificate) { @@ -225,7 +225,7 @@ func newCertKeyPair(t *testing.T) ([]byte, []byte) { Organization: []string{"teleport"}, CommonName: "teleport", } - key, crt, err := utils.GenerateSelfSignedSigningCert(entity, nil, time.Hour) + key, crt, err := tlsca.GenerateSelfSignedCA(entity, nil, time.Hour) require.NoError(t, err) return key, crt } diff --git a/lib/services/authority.go b/lib/services/authority.go index b529c0c417920..74dec10149712 100644 --- a/lib/services/authority.go +++ b/lib/services/authority.go @@ -325,7 +325,7 @@ func (c HostCertParams) Check() error { type UserCertParams struct { // CASigner is the signer that will sign the public key of the user with the CA private key CASigner ssh.Signer - // PublicUserKey is the public key of the user + // PublicUserKey is the public key of the user in SSH authorized_keys format. PublicUserKey []byte // TTL defines how long a certificate is valid for TTL time.Duration diff --git a/lib/spacelift/token_validator_test.go b/lib/spacelift/token_validator_test.go index 22c4379c9c08e..551b90d37f10e 100644 --- a/lib/spacelift/token_validator_test.go +++ b/lib/spacelift/token_validator_test.go @@ -20,8 +20,7 @@ package spacelift import ( "context" - "crypto/rand" - "crypto/rsa" + "crypto" "encoding/json" "net/http" "net/http/httptest" @@ -33,17 +32,20 @@ import ( "github.com/go-jose/go-jose/v3/jwt" "github.com/jonboulle/clockwork" "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/lib/cryptosuites" ) type fakeIDP struct { - t *testing.T - signer jose.Signer - privateKey *rsa.PrivateKey - server *httptest.Server + t *testing.T + signer jose.Signer + publicKey crypto.PublicKey + server *httptest.Server } func newFakeIDP(t *testing.T) *fakeIDP { - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + // Spacelift uses RSA, prefer to test with it. + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.RSA2048) require.NoError(t, err) signer, err := jose.NewSigner( @@ -53,9 +55,9 @@ func newFakeIDP(t *testing.T) *fakeIDP { require.NoError(t, err) f := &fakeIDP{ - signer: signer, - privateKey: privateKey, - t: t, + signer: signer, + publicKey: privateKey.Public(), + t: t, } providerMux := http.NewServeMux() @@ -121,7 +123,7 @@ func (f *fakeIDP) handleJWKSEndpoint(w http.ResponseWriter, r *http.Request) { jwks := jose.JSONWebKeySet{ Keys: []jose.JSONWebKey{ { - Key: &f.privateKey.PublicKey, + Key: f.publicKey, }, }, } diff --git a/lib/srv/alpnproxy/helpers_test.go b/lib/srv/alpnproxy/helpers_test.go index 19b0bebcee004..b4e9df20e83f0 100644 --- a/lib/srv/alpnproxy/helpers_test.go +++ b/lib/srv/alpnproxy/helpers_test.go @@ -20,12 +20,9 @@ package alpnproxy import ( "context" - "crypto/rand" - "crypto/rsa" "crypto/tls" "crypto/x509" "crypto/x509/pkix" - "encoding/pem" "fmt" "io" "net" @@ -39,8 +36,10 @@ import ( "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/auth/authclient" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/srv/alpnproxy/common" "github.com/gravitational/teleport/lib/tlsca" @@ -196,7 +195,7 @@ func mustGenCertSignedWithCA(t *testing.T, ca *tlsca.CertAuthority, opts ...sign subj, err := options.identity.Subject() require.NoError(t, err) - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) tlsCert, err := ca.GenerateCertificate(tlsca.CertificateRequest{ @@ -208,8 +207,8 @@ func mustGenCertSignedWithCA(t *testing.T, ca *tlsca.CertAuthority, opts ...sign }) require.NoError(t, err) - keyRaw := x509.MarshalPKCS1PrivateKey(privateKey) - keyPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyRaw}) + keyPEM, err := keys.MarshalPrivateKey(privateKey) + require.NoError(t, err) cert, err := tls.X509KeyPair(tlsCert, keyPEM) require.NoError(t, err) leaf, err := utils.TLSCertLeaf(cert) diff --git a/lib/srv/app/server_test.go b/lib/srv/app/server_test.go index 32932340f4d84..ee9c01accf1fb 100644 --- a/lib/srv/app/server_test.go +++ b/lib/srv/app/server_test.go @@ -55,7 +55,6 @@ import ( "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/auth/authclient" - "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/authz" "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/events" @@ -73,7 +72,7 @@ import ( func TestMain(m *testing.M) { utils.InitLoggerForTests() - native.PrecomputeTestKeys(m) + cryptosuites.PrecomputeRSATestKeys(m) modules.SetInsecureTestMode(true) os.Exit(m.Run()) } diff --git a/lib/srv/authhandlers_test.go b/lib/srv/authhandlers_test.go index 750ebaf522770..78856817654a9 100644 --- a/lib/srv/authhandlers_test.go +++ b/lib/srv/authhandlers_test.go @@ -32,8 +32,8 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/wrappers" "github.com/gravitational/teleport/api/utils/sshutils" - "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/auth/testauthority" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/events/eventstest" "github.com/gravitational/teleport/lib/services" ) @@ -139,8 +139,7 @@ func TestRBAC(t *testing.T) { } // create User CA - userTA := testauthority.New() - userCAPriv, err := userTA.GeneratePrivateKey() + userCAPriv, err := cryptosuites.GeneratePrivateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) userCA, err := types.NewCertAuthority(types.CertAuthoritySpecV2{ Type: types.UserCA, @@ -211,7 +210,7 @@ func TestRBAC(t *testing.T) { caSigner, err := ssh.NewSignerFromKey(userCAPriv) require.NoError(t, err) keygen := testauthority.New() - privateKey, err := native.GeneratePrivateKey() + privateKey, err := cryptosuites.GeneratePrivateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) c, err := keygen.GenerateUserCert(services.UserCertParams{ @@ -243,8 +242,7 @@ func TestRBACJoinMFA(t *testing.T) { const username = "testuser" // create User CA - userTA := testauthority.New() - userCAPriv, err := userTA.GeneratePrivateKey() + userCAPriv, err := cryptosuites.GeneratePrivateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) userCA, err := types.NewCertAuthority(types.CertAuthoritySpecV2{ Type: types.UserCA, @@ -381,15 +379,15 @@ func TestRBACJoinMFA(t *testing.T) { accessPoint.authPref = tt.authPref // create SSH certificate - caSigner, err := ssh.NewSignerFromKey(userCAPriv) + caSigner, err := ssh.NewSignerFromSigner(userCAPriv.Signer) require.NoError(t, err) - keygen := testauthority.New() - privateKey, err := native.GeneratePrivateKey() + privateKey, err := cryptosuites.GeneratePrivateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) + keygen := testauthority.New() c, err := keygen.GenerateUserCert(services.UserCertParams{ CASigner: caSigner, - PublicUserKey: ssh.MarshalAuthorizedKey(privateKey.SSHPublicKey()), + PublicUserKey: privateKey.MarshalSSHPublicKey(), Username: username, AllowedLogins: []string{username}, Traits: wrappers.Traits{ diff --git a/lib/srv/db/access_test.go b/lib/srv/db/access_test.go index 30d4ef22c172a..37848ea294c8e 100644 --- a/lib/srv/db/access_test.go +++ b/lib/srv/db/access_test.go @@ -60,10 +60,10 @@ import ( "github.com/gravitational/teleport/entitlements" "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/auth/authclient" - "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/authz" clients "github.com/gravitational/teleport/lib/cloud" "github.com/gravitational/teleport/lib/cloud/mocks" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/defaults" libevents "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/events/eventstest" @@ -101,7 +101,7 @@ import ( func TestMain(m *testing.M) { utils.InitLoggerForTests() - native.PrecomputeTestKeys(m) + cryptosuites.PrecomputeRSATestKeys(m) modules.SetInsecureTestMode(true) registerTestSnowflakeEngine() registerTestElasticsearchEngine() diff --git a/lib/srv/forward/sshserver.go b/lib/srv/forward/sshserver.go index 8fd276932becf..7b2cf1b83fd3b 100644 --- a/lib/srv/forward/sshserver.go +++ b/lib/srv/forward/sshserver.go @@ -46,6 +46,7 @@ import ( "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/bpf" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/integrations/awsoidc" "github.com/gravitational/teleport/lib/service/servicecfg" @@ -698,16 +699,27 @@ func (s *Server) sendSSHPublicKeyToTarget(ctx context.Context) (ssh.Signer, erro return nil, trace.BadParameter("failed to create an aws client to send ssh public key: %v", err) } - sshSigner, err := awsoidc.SendSSHPublicKeyToEC2(ctx, sendSSHClient, awsoidc.SendSSHPublicKeyToEC2Request{ + sshKey, err := cryptosuites.GenerateKey(ctx, + cryptosuites.GetCurrentSuiteFromAuthPreference(s.GetAccessPoint()), + cryptosuites.EC2InstanceConnect) + if err != nil { + return nil, trace.Wrap(err, "generating SSH key") + } + sshSigner, err := ssh.NewSignerFromSigner(sshKey) + if err != nil { + return nil, trace.Wrap(err, "creating SSH signer") + } + + if err := awsoidc.SendSSHPublicKeyToEC2(ctx, sendSSHClient, awsoidc.SendSSHPublicKeyToEC2Request{ InstanceID: awsInfo.InstanceID, EC2SSHLoginUser: s.identityContext.Login, - }) - if err != nil { + PublicKey: sshSigner.PublicKey(), + }); err != nil { return nil, trace.BadParameter("send ssh public key failed for instance %s: %v", awsInfo.InstanceID, err) } // This is the SSH Signer that the client must use to connect to the EC2. - // This signer generates trusted keys, because the public key was sent to the target EC2 host. + // This signer is trusted because the public key was sent to the target EC2 host. return sshSigner, nil } diff --git a/lib/srv/forward/sshserver_test.go b/lib/srv/forward/sshserver_test.go index c4f4700f8b65c..98e409e63b692 100644 --- a/lib/srv/forward/sshserver_test.go +++ b/lib/srv/forward/sshserver_test.go @@ -20,6 +20,7 @@ package forward import ( "context" + "crypto/ed25519" "crypto/rand" "errors" "os/user" @@ -30,7 +31,9 @@ import ( "golang.org/x/crypto/ssh" "github.com/gravitational/teleport" + "github.com/gravitational/teleport/api/utils/keys" apisshutils "github.com/gravitational/teleport/api/utils/sshutils" + "github.com/gravitational/teleport/lib/fixtures" "github.com/gravitational/teleport/lib/srv" "github.com/gravitational/teleport/lib/sshutils" "github.com/gravitational/teleport/lib/utils" @@ -53,7 +56,7 @@ func TestSignersWithSHA1Fallback(t *testing.T) { assertSHA1Signer := func(t *testing.T, signer ssh.Signer) { require.Equal(t, ssh.CertAlgoRSAv01, signer.PublicKey().Type()) - // We should not be able to case the signer to ssh.AlgorithmSigner. + // We should not be able to cast the signer to ssh.AlgorithmSigner. // Otherwise, x/crypto will use SHA-2-512 for signing. _, ok := signer.(ssh.AlgorithmSigner) require.False(t, ok) @@ -70,13 +73,15 @@ func TestSignersWithSHA1Fallback(t *testing.T) { want func(t *testing.T, got []ssh.Signer) }{ { - name: "simple", + name: "RSA host certificate", signersCb: func(t *testing.T) []ssh.Signer { - signer, err := apisshutils.MakeTestSSHCA() + caSigner, err := apisshutils.MakeTestSSHCA() require.NoError(t, err) - cert, err := apisshutils.MakeRealHostCert(signer) + hostKey, err := keys.ParsePrivateKey(fixtures.PEMBytes["rsa"]) require.NoError(t, err) - return []ssh.Signer{cert} + hostCert, err := apisshutils.MakeRealHostCertWithKey(hostKey.Signer, caSigner) + require.NoError(t, err) + return []ssh.Signer{hostCert} }, want: func(t *testing.T, signers []ssh.Signer) { // We expect 2 certificates, order matters. @@ -86,11 +91,13 @@ func TestSignersWithSHA1Fallback(t *testing.T) { }, }, { - name: "public key only", + name: "RSA host public key", signersCb: func(t *testing.T) []ssh.Signer { - signer, err := apisshutils.MakeTestSSHCA() + hostKey, err := keys.ParsePrivateKey(fixtures.PEMBytes["rsa"]) + require.NoError(t, err) + hostSigner, err := ssh.NewSignerFromSigner(hostKey.Signer) require.NoError(t, err) - return []ssh.Signer{signer} + return []ssh.Signer{hostSigner} }, want: func(t *testing.T, signers []ssh.Signer) { // public key should not be copied @@ -98,6 +105,34 @@ func TestSignersWithSHA1Fallback(t *testing.T) { require.Equal(t, ssh.KeyAlgoRSA, signers[0].PublicKey().Type()) }, }, + { + name: "Ed25519 host certificate", + signersCb: func(t *testing.T) []ssh.Signer { + caSigner, err := apisshutils.MakeTestSSHCA() + require.NoError(t, err) + hostCert, err := apisshutils.MakeRealHostCert(caSigner) + require.NoError(t, err) + return []ssh.Signer{hostCert} + }, + want: func(t *testing.T, signers []ssh.Signer) { + require.Len(t, signers, 1) + require.Equal(t, ssh.CertAlgoED25519v01, signers[0].PublicKey().Type()) + }, + }, + { + name: "Ed25519 host key", + signersCb: func(t *testing.T) []ssh.Signer { + _, hostKey, err := ed25519.GenerateKey(rand.Reader) + require.NoError(t, err) + hostSigner, err := ssh.NewSignerFromSigner(hostKey) + require.NoError(t, err) + return []ssh.Signer{hostSigner} + }, + want: func(t *testing.T, signers []ssh.Signer) { + require.Len(t, signers, 1) + require.Equal(t, ssh.KeyAlgoED25519, signers[0].PublicKey().Type()) + }, + }, } for _, tt := range tests { diff --git a/lib/srv/transport/transportv1/transport_test.go b/lib/srv/transport/transportv1/transport_test.go index 22a5a82797092..e45dc31194d47 100644 --- a/lib/srv/transport/transportv1/transport_test.go +++ b/lib/srv/transport/transportv1/transport_test.go @@ -20,10 +20,6 @@ package transportv1 import ( "context" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "encoding/pem" "errors" "fmt" "io" @@ -48,6 +44,7 @@ import ( streamutils "github.com/gravitational/teleport/api/utils/grpc/stream" "github.com/gravitational/teleport/lib/agentless" "github.com/gravitational/teleport/lib/authz" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/services" "github.com/gravitational/teleport/lib/teleagent" "github.com/gravitational/teleport/lib/utils" @@ -834,13 +831,11 @@ func (s *sshServer) Stop() error { } func generateSigner(t *testing.T, keyring agent.Agent) ssh.Signer { - private, err := rsa.GenerateKey(rand.Reader, 2048) + private, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.Ed25519) require.NoError(t, err) - block := &pem.Block{ - Type: "RSA PRIVATE KEY", - Bytes: x509.MarshalPKCS1PrivateKey(private), - } + signer, err := ssh.NewSignerFromSigner(private) + require.NoError(t, err) require.NoError(t, keyring.Add(agent.AddedKey{ PrivateKey: private, @@ -848,10 +843,6 @@ func generateSigner(t *testing.T, keyring agent.Agent) ssh.Signer { LifetimeSecs: math.MaxUint32, })) - privatePEM := pem.EncodeToMemory(block) - signer, err := ssh.ParsePrivateKey(privatePEM) - require.NoError(t, err) - return signer } diff --git a/lib/sshutils/server_test.go b/lib/sshutils/server_test.go index 704164f3d003b..7b95d3ced55c0 100644 --- a/lib/sshutils/server_test.go +++ b/lib/sshutils/server_test.go @@ -43,7 +43,7 @@ func TestMain(m *testing.M) { func TestStartStop(t *testing.T) { t.Parallel() - _, signer, err := cert.CreateCertificate("foo", ssh.HostCert) + _, signer, err := cert.CreateTestEd25519Certificate("foo", ssh.HostCert) require.NoError(t, err) called := false @@ -95,7 +95,7 @@ func TestStartStop(t *testing.T) { func TestShutdown(t *testing.T) { t.Parallel() - _, signer, err := cert.CreateCertificate("foo", ssh.HostCert) + _, signer, err := cert.CreateTestEd25519Certificate("foo", ssh.HostCert) require.NoError(t, err) closeContext, cancel := context.WithCancel(context.TODO()) @@ -151,7 +151,7 @@ func TestShutdown(t *testing.T) { func TestConfigureCiphers(t *testing.T) { t.Parallel() - _, signer, err := cert.CreateCertificate("foo", ssh.HostCert) + _, signer, err := cert.CreateTestEd25519Certificate("foo", ssh.HostCert) require.NoError(t, err) fn := NewChanHandlerFunc(func(_ context.Context, _ *ConnectionContext, nch ssh.NewChannel) { @@ -200,13 +200,13 @@ func TestConfigureCiphers(t *testing.T) { func TestHostSignerFIPS(t *testing.T) { t.Parallel() - _, signer, err := cert.CreateCertificate("foo", ssh.HostCert) + _, signer, err := cert.CreateTestRSACertificate("foo", ssh.HostCert) require.NoError(t, err) - _, ellipticSigner, err := cert.CreateEllipticCertificate("foo", ssh.HostCert) + _, ellipticSigner, err := cert.CreateTestECDSACertificate("foo", ssh.HostCert) require.NoError(t, err) - _, ed25519Signer, err := cert.CreateEd25519Certificate("foo", ssh.HostCert) + _, ed25519Signer, err := cert.CreateTestEd25519Certificate("foo", ssh.HostCert) require.NoError(t, err) fn := NewChanHandlerFunc(func(_ context.Context, _ *ConnectionContext, nch ssh.NewChannel) { @@ -286,10 +286,10 @@ func pass(need string) PasswordFunc { func TestDynamicHostSigners(t *testing.T) { t.Parallel() - certFoo, signerFoo, err := cert.CreateCertificate("foo", ssh.HostCert) + certFoo, signerFoo, err := cert.CreateTestEd25519Certificate("foo", ssh.HostCert) require.NoError(t, err) - certBar, signerBar, err := cert.CreateCertificate("bar", ssh.HostCert) + certBar, signerBar, err := cert.CreateTestEd25519Certificate("bar", ssh.HostCert) require.NoError(t, err) var activeSigner atomic.Pointer[ssh.Signer] diff --git a/lib/tbot/tbot_test.go b/lib/tbot/tbot_test.go index 4104c9b95d705..0a4659246c1ba 100644 --- a/lib/tbot/tbot_test.go +++ b/lib/tbot/tbot_test.go @@ -50,7 +50,7 @@ import ( "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/integration/helpers" "github.com/gravitational/teleport/lib/auth/authclient" - "github.com/gravitational/teleport/lib/auth/native" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/service" "github.com/gravitational/teleport/lib/service/servicecfg" @@ -68,7 +68,7 @@ import ( func TestMain(m *testing.M) { utils.InitLoggerForTests() - native.PrecomputeTestKeys(m) + cryptosuites.PrecomputeRSATestKeys(m) os.Exit(m.Run()) } diff --git a/lib/terraformcloud/token_validator_test.go b/lib/terraformcloud/token_validator_test.go index c879cfae73715..75541d77ec2e0 100644 --- a/lib/terraformcloud/token_validator_test.go +++ b/lib/terraformcloud/token_validator_test.go @@ -20,8 +20,7 @@ package terraformcloud import ( "context" - "crypto/rand" - "crypto/rsa" + "crypto" "encoding/json" "net/http" "net/http/httptest" @@ -32,18 +31,21 @@ import ( "github.com/go-jose/go-jose/v3/jwt" "github.com/jonboulle/clockwork" "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/lib/cryptosuites" ) type fakeIDP struct { - t *testing.T - signer jose.Signer - privateKey *rsa.PrivateKey - server *httptest.Server - audience string + t *testing.T + signer jose.Signer + publicKey crypto.PublicKey + server *httptest.Server + audience string } func newFakeIDP(t *testing.T, audience string) *fakeIDP { - privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + // Terraform Cloud uses RSA, prefer to test with it. + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.RSA2048) require.NoError(t, err) signer, err := jose.NewSigner( @@ -53,10 +55,10 @@ func newFakeIDP(t *testing.T, audience string) *fakeIDP { require.NoError(t, err) f := &fakeIDP{ - signer: signer, - privateKey: privateKey, - t: t, - audience: audience, + signer: signer, + publicKey: privateKey.Public(), + t: t, + audience: audience, } providerMux := http.NewServeMux() @@ -119,7 +121,7 @@ func (f *fakeIDP) handleJWKSEndpoint(w http.ResponseWriter, r *http.Request) { jwks := jose.JSONWebKeySet{ Keys: []jose.JSONWebKey{ { - Key: &f.privateKey.PublicKey, + Key: f.publicKey, }, }, } diff --git a/lib/tlsca/ca_test.go b/lib/tlsca/ca_test.go index d84dc20fdc71c..91b4db336e173 100644 --- a/lib/tlsca/ca_test.go +++ b/lib/tlsca/ca_test.go @@ -19,8 +19,6 @@ package tlsca import ( - "crypto/rand" - "crypto/rsa" "crypto/tls" "crypto/x509/pkix" "encoding/asn1" @@ -36,9 +34,9 @@ import ( "github.com/stretchr/testify/require" "github.com/gravitational/teleport" - "github.com/gravitational/teleport/api/constants" apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/fixtures" ) @@ -86,7 +84,7 @@ func TestPrincipals(t *testing.T) { ca, err := test.createFunc() require.NoError(t, err) - privateKey, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize) + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) hostnames := []string{"localhost", "example.com"} @@ -122,7 +120,7 @@ func TestRenewableIdentity(t *testing.T) { ca, err := FromKeys([]byte(fixtures.TLSCACertPEM), []byte(fixtures.TLSCAKeyPEM)) require.NoError(t, err) - privateKey, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize) + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) identity := Identity{ @@ -159,7 +157,7 @@ func TestKubeExtensions(t *testing.T) { ca, err := FromKeys([]byte(fixtures.TLSCACertPEM), []byte(fixtures.TLSCAKeyPEM)) require.NoError(t, err) - privateKey, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize) + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) expires := clock.Now().Add(time.Hour) @@ -209,7 +207,7 @@ func TestDatabaseExtensions(t *testing.T) { ca, err := FromKeys([]byte(fixtures.TLSCACertPEM), []byte(fixtures.TLSCAKeyPEM)) require.NoError(t, err) - privateKey, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize) + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) expires := clock.Now().Add(time.Hour) @@ -254,7 +252,7 @@ func TestAzureExtensions(t *testing.T) { ca, err := FromKeys([]byte(fixtures.TLSCACertPEM), []byte(fixtures.TLSCAKeyPEM)) require.NoError(t, err) - privateKey, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize) + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) expires := clock.Now().Add(time.Hour) @@ -360,7 +358,7 @@ func TestGCPExtensions(t *testing.T) { ca, err := FromKeys([]byte(fixtures.TLSCACertPEM), []byte(fixtures.TLSCAKeyPEM)) require.NoError(t, err) - privateKey, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize) + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) expires := clock.Now().Add(time.Hour) diff --git a/lib/tpm/tpm_simulator_test.go b/lib/tpm/tpm_simulator_test.go index a71fce746569d..c84ab6d9b61ca 100644 --- a/lib/tpm/tpm_simulator_test.go +++ b/lib/tpm/tpm_simulator_test.go @@ -106,6 +106,7 @@ func TestWithSimulator(t *testing.T) { NotBefore: time.Now().Add(-time.Hour), NotAfter: time.Now().Add(time.Hour), } + //nolint:forbidigo // Generating large RSA key allowed for TPM simulator. caPrivKey, err := rsa.GenerateKey(rand.Reader, 4096) require.NoError(t, err) caBytes, err := x509.CreateCertificate( diff --git a/lib/utils/cert/certs.go b/lib/utils/cert/certs.go index cad4d986cb64d..47de43a21a42f 100644 --- a/lib/utils/cert/certs.go +++ b/lib/utils/cert/certs.go @@ -19,85 +19,40 @@ package cert import ( - "crypto" - "crypto/ecdsa" - "crypto/ed25519" - "crypto/elliptic" "crypto/rand" "time" "github.com/gravitational/trace" "golang.org/x/crypto/ssh" - "github.com/gravitational/teleport/lib/auth/native" + "github.com/gravitational/teleport/lib/cryptosuites" ) -// CreateCertificate creates a valid 2048-bit RSA certificate. -func CreateCertificate(principal string, certType uint32) (*ssh.Certificate, ssh.Signer, error) { - // Create RSA key for CA and certificate to be signed by CA. - caKey, err := native.GenerateRSAPrivateKey() - if err != nil { - return nil, nil, trace.Wrap(err) - } - key, err := native.GenerateRSAPrivateKey() - if err != nil { - return nil, nil, trace.Wrap(err) - } - - cert, certSigner, err := createCertificate(principal, certType, caKey, key) - if err != nil { - return nil, nil, trace.Wrap(err) - } - - return cert, certSigner, nil +// CreateTestRSACertificate creates a valid 2048-bit RSA certificate. +func CreateTestRSACertificate(principal string, certType uint32) (*ssh.Certificate, ssh.Signer, error) { + return createCertificate(principal, certType, cryptosuites.RSA2048) } -// CreateEllipticCertificate creates a valid ECDSA P-256 certificate. -func CreateEllipticCertificate(principal string, certType uint32) (*ssh.Certificate, ssh.Signer, error) { - // Create ECDSA key for CA and certificate to be signed by CA. - caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - return nil, nil, trace.Wrap(err) - } - key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - return nil, nil, trace.Wrap(err) - } - - cert, certSigner, err := createCertificate(principal, certType, caKey, key) - if err != nil { - return nil, nil, trace.Wrap(err) - } - - return cert, certSigner, nil +// CreateTestECDSACertificate creates a valid ECDSA P-256 certificate. +func CreateTestECDSACertificate(principal string, certType uint32) (*ssh.Certificate, ssh.Signer, error) { + return createCertificate(principal, certType, cryptosuites.ECDSAP256) } -// CreateEd25519Certificate creates an Ed25519 certificate which should be +// CreateTestEd25519Certificate creates an Ed25519 certificate which should be // rejected in FIPS mode. -func CreateEd25519Certificate(principal string, certType uint32) (*ssh.Certificate, ssh.Signer, error) { - // Create Ed25519 key for CA and certificate to be signed by CA. - _, caKey, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - return nil, nil, trace.Wrap(err) - } - _, key, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - return nil, nil, trace.Wrap(err) - } - - cert, certSigner, err := createCertificate(principal, certType, caKey, key) - if err != nil { - return nil, nil, trace.Wrap(err) - } - - return cert, certSigner, nil +func CreateTestEd25519Certificate(principal string, certType uint32) (*ssh.Certificate, ssh.Signer, error) { + return createCertificate(principal, certType, cryptosuites.Ed25519) } // createCertificate creates a SSH certificate for the given key signed by the // given CA key. This function exists here to allow easy key generation for // some of the more core packages like "sshutils". -func createCertificate(principal string, certType uint32, caKey crypto.Signer, key crypto.Signer) (*ssh.Certificate, ssh.Signer, error) { +func createCertificate(principal string, certType uint32, algo cryptosuites.Algorithm) (*ssh.Certificate, ssh.Signer, error) { // Create CA. + caKey, err := cryptosuites.GenerateKeyWithAlgorithm(algo) + if err != nil { + return nil, nil, trace.Wrap(err) + } caPublicKey, err := ssh.NewPublicKey(caKey.Public()) if err != nil { return nil, nil, trace.Wrap(err) @@ -108,6 +63,10 @@ func createCertificate(principal string, certType uint32, caKey crypto.Signer, k } // Create key. + key, err := cryptosuites.GenerateKeyWithAlgorithm(algo) + if err != nil { + return nil, nil, trace.Wrap(err) + } publicKey, err := ssh.NewPublicKey(key.Public()) if err != nil { return nil, nil, trace.Wrap(err) diff --git a/lib/utils/certs.go b/lib/utils/certs.go index c2f3b96c5b482..88244491bb95c 100644 --- a/lib/utils/certs.go +++ b/lib/utils/certs.go @@ -30,9 +30,9 @@ import ( "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/gravitational/teleport/api/constants" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/api/utils/tlsutils" + "github.com/gravitational/teleport/lib/cryptosuites" ) // ParseKeyStorePEM parses signing key store from PEM encoded key pair @@ -62,16 +62,24 @@ type KeyStore struct { cert []byte } +// GetKeyPair implements goxmldsig.X509KeyPair. func (ks *KeyStore) GetKeyPair() (*rsa.PrivateKey, []byte, error) { return ks.privateKey, ks.cert, nil } -// GenerateSelfSignedSigningCert generates self-signed certificate used for digital signatures -func GenerateSelfSignedSigningCert(entity pkix.Name, dnsNames []string, ttl time.Duration) ([]byte, []byte, error) { - priv, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize) +// GenerateSelfSignedSigningCert is an alias of GenerateRSASelfSignedSigningCert +// used due to references in teleport.e. +var GenerateSelfSignedSigningCert = GenerateRSASelfSignedSigningCert + +// GenerateRSASelfSignedSigningCert generates an RSA self-signed certificate used +// for digital signatures. +// This should only be used for the SAML implementation and tests. +func GenerateRSASelfSignedSigningCert(entity pkix.Name, dnsNames []string, ttl time.Duration) ([]byte, []byte, error) { + priv, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.RSA2048) if err != nil { return nil, nil, trace.Wrap(err) } + rsaPriv := priv.(*rsa.PrivateKey) // to account for clock skew notBefore := time.Now().Add(-2 * time.Minute) notAfter := notBefore.Add(ttl) @@ -93,12 +101,12 @@ func GenerateSelfSignedSigningCert(entity pkix.Name, dnsNames []string, ttl time DNSNames: dnsNames, } - derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &rsaPriv.PublicKey, rsaPriv) if err != nil { return nil, nil, trace.Wrap(err) } - keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) + keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rsaPriv)}) certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) return keyPEM, certPEM, nil diff --git a/lib/utils/chconn_test.go b/lib/utils/chconn_test.go index 7a42fe0f663d2..44a3ae314dee7 100644 --- a/lib/utils/chconn_test.go +++ b/lib/utils/chconn_test.go @@ -19,8 +19,6 @@ package utils import ( - "crypto/rand" - "crypto/rsa" "io" "net" "os" @@ -30,9 +28,8 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/crypto/ssh" - "github.com/gravitational/teleport/api/constants" - "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/api/utils/sshutils" + "github.com/gravitational/teleport/lib/cryptosuites" ) // TestChConn validates that reads from the channel connection can be @@ -92,13 +89,10 @@ func startSSHServer(t *testing.T, listener net.Listener, sshConnCh chan<- sshCon require.NoError(t, err) t.Cleanup(func() { nConn.Close() }) - privateKey, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize) + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.Ed25519) require.NoError(t, err) - private, err := keys.MarshalPrivateKey(privateKey) - require.NoError(t, err) - - signer, err := ssh.ParsePrivateKey(private) + signer, err := ssh.NewSignerFromSigner(privateKey) require.NoError(t, err) config := &ssh.ServerConfig{NoClientAuth: true} diff --git a/lib/web/apiserver_test.go b/lib/web/apiserver_test.go index ad16a9c658dc8..5e3fea922d245 100644 --- a/lib/web/apiserver_test.go +++ b/lib/web/apiserver_test.go @@ -103,7 +103,6 @@ import ( "github.com/gravitational/teleport/lib/auth/authclient" tlsutils "github.com/gravitational/teleport/lib/auth/keygen" "github.com/gravitational/teleport/lib/auth/mocku2f" - "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/auth/state" "github.com/gravitational/teleport/lib/auth/testauthority" wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" @@ -180,7 +179,7 @@ func TestMain(m *testing.M) { srv.RunAndExit(os.Args[1]) return } - native.PrecomputeTestKeys(m) + cryptosuites.PrecomputeRSATestKeys(m) // Otherwise run tests as normal. code := m.Run() diff --git a/tool/tbot/kube_test.go b/tool/tbot/kube_test.go index 09a29f6c38c9e..c0f2a45c4ec76 100644 --- a/tool/tbot/kube_test.go +++ b/tool/tbot/kube_test.go @@ -19,8 +19,6 @@ package main import ( - "crypto/rand" - "crypto/rsa" "crypto/x509/pkix" "encoding/json" "testing" @@ -30,9 +28,9 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/crypto/ssh" - "github.com/gravitational/teleport/api/constants" "github.com/gravitational/teleport/api/identityfile" "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/fixtures" "github.com/gravitational/teleport/lib/tlsca" ) @@ -42,7 +40,7 @@ func TestGetKubeCredentialData(t *testing.T) { ca, err := tlsca.FromKeys([]byte(fixtures.TLSCACertPEM), []byte(fixtures.TLSCAKeyPEM)) require.NoError(t, err) - privateKey, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize) + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.ECDSAP256) require.NoError(t, err) clock := clockwork.NewFakeClock() diff --git a/tool/tctl/common/admin_action_test.go b/tool/tctl/common/admin_action_test.go index 199e1901e0c52..4fcdd8661d522 100644 --- a/tool/tctl/common/admin_action_test.go +++ b/tool/tctl/common/admin_action_test.go @@ -40,7 +40,6 @@ import ( "github.com/gravitational/teleport/api/mfa" "github.com/gravitational/teleport/api/types" apiutils "github.com/gravitational/teleport/api/utils" - "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/entitlements" "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/auth/authclient" @@ -453,11 +452,7 @@ func (s *adminActionTestSuite) testUserGroups(t *testing.T) { func (s *adminActionTestSuite) testCertAuthority(t *testing.T) { ctx := context.Background() - sshSigner, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.Ed25519) - require.NoError(t, err) - sshKey, err := keys.NewSoftwarePrivateKey(sshSigner) - require.NoError(t, err) - sshKeyPEM, err := sshKey.MarshalSSHPrivateKey() + sshKey, err := cryptosuites.GeneratePrivateKeyWithAlgorithm(cryptosuites.Ed25519) require.NoError(t, err) tlsKey, cert, err := tlsca.GenerateSelfSignedCA(pkix.Name{CommonName: "Host"}, nil, time.Minute) @@ -468,9 +463,8 @@ func (s *adminActionTestSuite) testCertAuthority(t *testing.T) { ClusterName: "clustername", ActiveKeys: types.CAKeySet{ SSH: []*types.SSHKeyPair{{ - PrivateKey: sshKeyPEM, - PrivateKeyType: types.PrivateKeyType_RAW, - PublicKey: sshKey.MarshalSSHPublicKey(), + PrivateKey: sshKey.PrivateKeyPEM(), + PublicKey: sshKey.MarshalSSHPublicKey(), }}, TLS: []*types.TLSKeyPair{{ Cert: cert, diff --git a/tool/tsh/common/db_test.go b/tool/tsh/common/db_test.go index 7ae5ba046f8b8..5502102ab2459 100644 --- a/tool/tsh/common/db_test.go +++ b/tool/tsh/common/db_test.go @@ -21,8 +21,6 @@ package common import ( "bytes" "context" - "crypto/rand" - "crypto/rsa" "encoding/pem" "fmt" "os" @@ -42,6 +40,7 @@ import ( "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/entitlements" "github.com/gravitational/teleport/lib/client" + "github.com/gravitational/teleport/lib/cryptosuites" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/fixtures" "github.com/gravitational/teleport/lib/modules" @@ -908,7 +907,7 @@ func TestDBInfoHasChanged(t *testing.T) { ca, err := tlsca.FromKeys([]byte(fixtures.TLSCACertPEM), []byte(fixtures.TLSCAKeyPEM)) require.NoError(t, err) - privateKey, err := rsa.GenerateKey(rand.Reader, constants.RSAKeySize) + privateKey, err := cryptosuites.GenerateKeyWithAlgorithm(cryptosuites.RSA2048) require.NoError(t, err) for _, tc := range tests { diff --git a/tool/tsh/common/tsh_test.go b/tool/tsh/common/tsh_test.go index 609c8a827033b..b4d1af2e35cf6 100644 --- a/tool/tsh/common/tsh_test.go +++ b/tool/tsh/common/tsh_test.go @@ -76,7 +76,6 @@ import ( "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/auth/mocku2f" - "github.com/gravitational/teleport/lib/auth/native" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/backend" @@ -127,7 +126,7 @@ func TestMain(m *testing.M) { modules.SetInsecureTestMode(true) utils.InitLoggerForTests() - native.PrecomputeTestKeys(m) + cryptosuites.PrecomputeRSATestKeys(m) os.Exit(m.Run()) }