mirror of
https://github.com/coder/coder.git
synced 2026-06-03 13:08:25 +00:00
bddb808b25
Fixes all our Go file imports to match the preferred spec that we've _mostly_ been using. For example: ``` import ( "context" "time" "github.com/prometheus/client_golang/prometheus" "golang.org/x/xerrors" "gopkg.in/natefinch/lumberjack.v2" "cdr.dev/slog/v3" "github.com/coder/coder/v2/codersdk/agentsdk" "github.com/coder/serpent" ) ``` 3 groups: standard library, 3rd partly libs, Coder libs. This PR makes the change across the codebase. The PR in the stack above modifies our formatting to maintain this state of affairs, and is a separate PR so it's possible to review that one in detail.
145 lines
4.0 KiB
Go
145 lines
4.0 KiB
Go
package gitsshkey
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/ed25519"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"flag"
|
|
"io"
|
|
insecurerand "math/rand"
|
|
"strings"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
"golang.org/x/xerrors"
|
|
)
|
|
|
|
type Algorithm string
|
|
|
|
const (
|
|
// AlgorithmEd25519 is the Edwards-curve Digital Signature Algorithm using Curve25519
|
|
AlgorithmEd25519 Algorithm = "ed25519"
|
|
// AlgorithmECDSA is the Digital Signature Algorithm (DSA) using NIST Elliptic Curve
|
|
AlgorithmECDSA Algorithm = "ecdsa"
|
|
// AlgorithmRSA4096 is the venerable Rivest-Shamir-Adleman algorithm
|
|
// and creates a key with a fixed size of 4096-bit.
|
|
AlgorithmRSA4096 Algorithm = "rsa4096"
|
|
)
|
|
|
|
func entropy() io.Reader {
|
|
if flag.Lookup("test.v") != nil {
|
|
// This helps speed along our tests, esp. in CI where entropy is
|
|
// sparse.
|
|
//nolint:gosec
|
|
return insecurerand.New(insecurerand.NewSource(time.Now().UnixNano()))
|
|
}
|
|
// Buffering to reduce the number of system calls
|
|
// doubles performance without any loss of security.
|
|
return bufio.NewReader(rand.Reader)
|
|
}
|
|
|
|
// ParseAlgorithm returns a valid Algorithm or error if input is not a valid.
|
|
func ParseAlgorithm(t string) (Algorithm, error) {
|
|
ok := []string{
|
|
string(AlgorithmEd25519),
|
|
string(AlgorithmECDSA),
|
|
string(AlgorithmRSA4096),
|
|
}
|
|
|
|
for _, a := range ok {
|
|
if strings.EqualFold(a, t) {
|
|
return Algorithm(a), nil
|
|
}
|
|
}
|
|
|
|
return "", xerrors.Errorf(`invalid key type: %s, must be one of: %s`, t, strings.Join(ok, ","))
|
|
}
|
|
|
|
// Generate creates a private key in the OpenSSH PEM format and public key in
|
|
// the authorized key format.
|
|
func Generate(algo Algorithm) (privateKey string, publicKey string, err error) {
|
|
switch algo {
|
|
case AlgorithmEd25519:
|
|
return ed25519KeyGen()
|
|
case AlgorithmECDSA:
|
|
return ecdsaKeyGen()
|
|
case AlgorithmRSA4096:
|
|
return rsa4096KeyGen()
|
|
default:
|
|
return "", "", xerrors.Errorf("invalid algorithm: %s", algo)
|
|
}
|
|
}
|
|
|
|
// ed25519KeyGen returns an ED25519-based SSH private key.
|
|
func ed25519KeyGen() (privateKey string, publicKey string, err error) {
|
|
_, privateKeyRaw, err := ed25519.GenerateKey(entropy())
|
|
if err != nil {
|
|
return "", "", xerrors.Errorf("generate ed25519 private key: %w", err)
|
|
}
|
|
|
|
// NOTE: as of the time of writing, x/crypto/ssh is unable to marshal an ED25519 private key
|
|
// into the format expected by OpenSSH. See: https://github.com/golang/go/issues/37132
|
|
// Until this support is added, using a third-party implementation.
|
|
byt, err := MarshalED25519PrivateKey(privateKeyRaw)
|
|
if err != nil {
|
|
return "", "", xerrors.Errorf("marshal ed25519 private key: %w", err)
|
|
}
|
|
|
|
return generateKeys(pem.Block{
|
|
Type: "OPENSSH PRIVATE KEY",
|
|
Bytes: byt,
|
|
}, privateKeyRaw)
|
|
}
|
|
|
|
// ecdsaKeyGen returns an ECDSA-based SSH private key.
|
|
func ecdsaKeyGen() (privateKey string, publicKey string, err error) {
|
|
privateKeyRaw, err := ecdsa.GenerateKey(elliptic.P256(), entropy())
|
|
if err != nil {
|
|
return "", "", xerrors.Errorf("generate ecdsa private key: %w", err)
|
|
}
|
|
byt, err := x509.MarshalECPrivateKey(privateKeyRaw)
|
|
if err != nil {
|
|
return "", "", xerrors.Errorf("marshal private key: %w", err)
|
|
}
|
|
|
|
return generateKeys(pem.Block{
|
|
Type: "EC PRIVATE KEY",
|
|
Bytes: byt,
|
|
}, privateKeyRaw)
|
|
}
|
|
|
|
// rsaKeyGen returns an RSA-based SSH private key of size 4096.
|
|
//
|
|
// Administrators may configure this for SSH key compatibility with Azure DevOps.
|
|
func rsa4096KeyGen() (privateKey string, publicKey string, err error) {
|
|
privateKeyRaw, err := rsa.GenerateKey(entropy(), 4096)
|
|
if err != nil {
|
|
return "", "", xerrors.Errorf("generate RSA4096 private key: %w", err)
|
|
}
|
|
|
|
return generateKeys(pem.Block{
|
|
Type: "RSA PRIVATE KEY",
|
|
Bytes: x509.MarshalPKCS1PrivateKey(privateKeyRaw),
|
|
}, privateKeyRaw)
|
|
}
|
|
|
|
func generateKeys(block pem.Block, cp crypto.Signer) (privateKey string, publicKey string, err error) {
|
|
pkBytes := pem.EncodeToMemory(&block)
|
|
privateKey = string(pkBytes)
|
|
|
|
publicKeyRaw := cp.Public()
|
|
p, err := ssh.NewPublicKey(publicKeyRaw)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
publicKey = string(ssh.MarshalAuthorizedKey(p))
|
|
|
|
return privateKey, publicKey, nil
|
|
}
|