aboutsummaryrefslogtreecommitdiff
path: root/client/keys.go
blob: b488ea2231356312c470be5cfad8bb5cc6a74adf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package client

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"

	"github.com/pkg/errors"

	"golang.org/x/crypto/ed25519"
	"golang.org/x/crypto/ssh"

	"github.com/mikesmitty/edkey"
)

// Key is a private key.
type Key crypto.Signer

// Options for key generation.
// Defaults will generate a 2048 bit RSA key.
type options struct {
	keytype string
	size    int
}

var defaultOptions = options{
	keytype: "rsa",
	size:    0, // Different key types have different default sizes.
}

// A KeyOption is used to generate keys of different types and sizes.
type KeyOption func(*options)

func pemBlockForKey(priv interface{}) (*pem.Block, error) {
	switch k := priv.(type) {
	case *rsa.PrivateKey:
		return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}, nil
	case *ecdsa.PrivateKey:
		b, err := x509.MarshalECPrivateKey(k)
		if err != nil {
			return nil, err
		}
		return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}, nil
	case *ed25519.PrivateKey:
		b := edkey.MarshalED25519PrivateKey(*k)
		return &pem.Block{Type: "OPENSSH PRIVATE KEY", Bytes: b}, nil
	default:
		return nil, fmt.Errorf("Unable to create PEM blck from key")
	}
}

// KeyType sets the type of key to generate.
// Valid types are: "rsa", "ecdsa", "ed25519".
// Default is "rsa"
func KeyType(keyType string) KeyOption {
	return func(o *options) {
		o.keytype = keyType
	}
}

// KeySize sets the size of the key in bits.
// RSA keys must be a minimum of 1024 bits. The default is 2048 bits.
// ECDSA keys must be one of 256, 384, or 521 bits. The default is 256 bits.
// Ed25519 keys are of a fixed size. This option is ignored.
func KeySize(size int) KeyOption {
	return func(o *options) {
		o.size = size
	}
}

func generateED25519Key() (Key, error) {
	_, k, err := ed25519.GenerateKey(rand.Reader)
	return &k, err
}

func generateRSAKey(size int) (Key, error) {
	return rsa.GenerateKey(rand.Reader, size)
}

func generateECDSAKey(size int) (Key, error) {
	var curve elliptic.Curve
	switch size {
	case 256:
		curve = elliptic.P256()
	case 384:
		curve = elliptic.P384()
	case 521:
		curve = elliptic.P521()
	default:
		return nil, fmt.Errorf("Unsupported ECDSA key size: %d. Valid sizes are '256', '384', '521'", size)
	}
	return ecdsa.GenerateKey(curve, rand.Reader)
}

// GenerateKey generates a ssh key-pair according to the type and size specified.
func GenerateKey(options ...func(*options)) (Key, ssh.PublicKey, error) {
	var privkey Key
	var pubkey ssh.PublicKey
	var err error

	config := defaultOptions
	for _, o := range options {
		o(&config)
	}

	switch config.keytype {
	case "rsa":
		if config.size == 0 {
			config.size = 2048
		}
		privkey, err = generateRSAKey(config.size)
	case "ecdsa":
		if config.size == 0 {
			config.size = 256
		}
		privkey, err = generateECDSAKey(config.size)
	case "ed25519":
		privkey, err = generateED25519Key()
	default:
		privkey, err = generateRSAKey(config.size)
	}
	if err != nil {
		return nil, nil, errors.Wrapf(err, "unable to generate %s key-pair", config.keytype)
	}
	pubkey, err = ssh.NewPublicKey(privkey.Public())
	return privkey, pubkey, errors.Wrap(err, "error parsing public key")
}