summaryrefslogtreecommitdiffstats
path: root/key.go
blob: 73642db2a8b76434166c0c06445afa28bcda3206 (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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
package libtrust

import (
	"crypto"
	"crypto/ecdsa"
	"crypto/rsa"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
)

// PublicKey is a generic interface for a Public Key.
type PublicKey interface {
	// KeyType returns the key type for this key. For elliptic curve keys,
	// this value should be "EC". For RSA keys, this value should be "RSA".
	KeyType() string
	// KeyID returns a distinct identifier which is unique to this Public Key.
	// The format generated by this library is a base32 encoding of a 240 bit
	// hash of the public key data divided into 12 groups like so:
	//    ABCD:EFGH:IJKL:MNOP:QRST:UVWX:YZ23:4567:ABCD:EFGH:IJKL:MNOP
	KeyID() string
	// Verify verifyies the signature of the data in the io.Reader using this
	// Public Key. The alg parameter should identify the digital signature
	// algorithm which was used to produce the signature and should be
	// supported by this public key. Returns a nil error if the signature
	// is valid.
	Verify(data io.Reader, alg string, signature []byte) error
	// CryptoPublicKey returns the internal object which can be used as a
	// crypto.PublicKey for use with other standard library operations. The type
	// is either *rsa.PublicKey or *ecdsa.PublicKey
	CryptoPublicKey() crypto.PublicKey
	// These public keys can be serialized to the standard JSON encoding for
	// JSON Web Keys. See section 6 of the IETF draft RFC for JOSE JSON Web
	// Algorithms.
	MarshalJSON() ([]byte, error)
	// These keys can also be serialized to the standard PEM encoding.
	PEMBlock() (*pem.Block, error)
	// The string representation of a key is its key type and ID.
	String() string
	AddExtendedField(string, interface{})
	GetExtendedField(string) interface{}
}

// PrivateKey is a generic interface for a Private Key.
type PrivateKey interface {
	// A PrivateKey contains all fields and methods of a PublicKey of the
	// same type. The MarshalJSON method also outputs the private key as a
	// JSON Web Key, and the PEMBlock method outputs the private key as a
	// PEM block.
	PublicKey
	// PublicKey returns the PublicKey associated with this PrivateKey.
	PublicKey() PublicKey
	// Sign signs the data read from the io.Reader using a signature algorithm
	// supported by the private key. If the specified hashing algorithm is
	// supported by this key, that hash function is used to generate the
	// signature otherwise the the default hashing algorithm for this key is
	// used. Returns the signature and identifier of the algorithm used.
	Sign(data io.Reader, hashID crypto.Hash) (signature []byte, alg string, err error)
	// CryptoPrivateKey returns the internal object which can be used as a
	// crypto.PublicKey for use with other standard library operations. The
	// type is either *rsa.PublicKey or *ecdsa.PublicKey
	CryptoPrivateKey() crypto.PrivateKey
}

// FromCryptoPublicKey returns a libtrust PublicKey representation of the given
// *ecdsa.PublicKey or *rsa.PublicKey. Returns a non-nil error when the given
// key is of an unsupported type.
func FromCryptoPublicKey(cryptoPublicKey crypto.PublicKey) (PublicKey, error) {
	switch cryptoPublicKey := cryptoPublicKey.(type) {
	case *ecdsa.PublicKey:
		return fromECPublicKey(cryptoPublicKey)
	case *rsa.PublicKey:
		return fromRSAPublicKey(cryptoPublicKey), nil
	default:
		return nil, fmt.Errorf("public key type %T is not supported", cryptoPublicKey)
	}
}

// FromCryptoPrivateKey returns a libtrust PrivateKey representation of the given
// *ecdsa.PrivateKey or *rsa.PrivateKey. Returns a non-nil error when the given
// key is of an unsupported type.
func FromCryptoPrivateKey(cryptoPrivateKey crypto.PrivateKey) (PrivateKey, error) {
	switch cryptoPrivateKey := cryptoPrivateKey.(type) {
	case *ecdsa.PrivateKey:
		return fromECPrivateKey(cryptoPrivateKey)
	case *rsa.PrivateKey:
		return fromRSAPrivateKey(cryptoPrivateKey), nil
	default:
		return nil, fmt.Errorf("private key type %T is not supported", cryptoPrivateKey)
	}
}

// UnmarshalPublicKeyPEM parses the PEM encoded data and returns a libtrust
// PublicKey or an error if there is a problem with the encoding.
func UnmarshalPublicKeyPEM(data []byte) (PublicKey, error) {
	pemBlock, _ := pem.Decode(data)
	if pemBlock == nil {
		return nil, errors.New("unable to find PEM encoded data")
	} else if pemBlock.Type != "PUBLIC KEY" {
		return nil, fmt.Errorf("unable to get PublicKey from PEM type: %s", pemBlock.Type)
	}

	return pubKeyFromPEMBlock(pemBlock)
}

// UnmarshalPublicKeyPEMBundle parses the PEM encoded data as a bundle of
// PEM blocks appended one after the other and returns a slice of PublicKey
// objects that it finds.
func UnmarshalPublicKeyPEMBundle(data []byte) ([]PublicKey, error) {
	pubKeys := []PublicKey{}

	for {
		var pemBlock *pem.Block
		pemBlock, data = pem.Decode(data)
		if pemBlock == nil {
			break
		} else if pemBlock.Type != "PUBLIC KEY" {
			return nil, fmt.Errorf("unable to get PublicKey from PEM type: %s", pemBlock.Type)
		}

		pubKey, err := pubKeyFromPEMBlock(pemBlock)
		if err != nil {
			return nil, err
		}

		pubKeys = append(pubKeys, pubKey)
	}

	return pubKeys, nil
}

// UnmarshalPrivateKeyPEM parses the PEM encoded data and returns a libtrust
// PrivateKey or an error if there is a problem with the encoding.
func UnmarshalPrivateKeyPEM(data []byte) (PrivateKey, error) {
	pemBlock, _ := pem.Decode(data)
	if pemBlock == nil {
		return nil, errors.New("unable to find PEM encoded data")
	}

	var key PrivateKey

	switch {
	case pemBlock.Type == "RSA PRIVATE KEY":
		rsaPrivateKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
		if err != nil {
			return nil, fmt.Errorf("unable to decode RSA Private Key PEM data: %s", err)
		}
		key = fromRSAPrivateKey(rsaPrivateKey)
	case pemBlock.Type == "EC PRIVATE KEY":
		ecPrivateKey, err := x509.ParseECPrivateKey(pemBlock.Bytes)
		if err != nil {
			return nil, fmt.Errorf("unable to decode EC Private Key PEM data: %s", err)
		}
		key, err = fromECPrivateKey(ecPrivateKey)
		if err != nil {
			return nil, err
		}
	default:
		return nil, fmt.Errorf("unable to get PrivateKey from PEM type: %s", pemBlock.Type)
	}

	addPEMHeadersToKey(pemBlock, key.PublicKey())

	return key, nil
}

// UnmarshalPublicKeyJWK unmarshals the given JSON Web Key into a generic
// Public Key to be used with libtrust.
func UnmarshalPublicKeyJWK(data []byte) (PublicKey, error) {
	jwk := make(map[string]interface{})

	err := json.Unmarshal(data, &jwk)
	if err != nil {
		return nil, fmt.Errorf(
			"decoding JWK Public Key JSON data: %s\n", err,
		)
	}

	// Get the Key Type value.
	kty, err := stringFromMap(jwk, "kty")
	if err != nil {
		return nil, fmt.Errorf("JWK Public Key type: %s", err)
	}

	switch {
	case kty == "EC":
		// Call out to unmarshal EC public key.
		return ecPublicKeyFromMap(jwk)
	case kty == "RSA":
		// Call out to unmarshal RSA public key.
		return rsaPublicKeyFromMap(jwk)
	default:
		return nil, fmt.Errorf(
			"JWK Public Key type not supported: %q\n", kty,
		)
	}
}

// UnmarshalPublicKeyJWKSet parses the JSON encoded data as a JSON Web Key Set
// and returns a slice of Public Key objects.
func UnmarshalPublicKeyJWKSet(data []byte) ([]PublicKey, error) {
	rawKeys, err := loadJSONKeySetRaw(data)
	if err != nil {
		return nil, err
	}

	pubKeys := make([]PublicKey, 0, len(rawKeys))

	for _, rawKey := range rawKeys {
		pubKey, err := UnmarshalPublicKeyJWK(rawKey)
		if err != nil {
			return nil, err
		}
		pubKeys = append(pubKeys, pubKey)
	}

	return pubKeys, nil
}

// UnmarshalPrivateKeyJWK unmarshals the given JSON Web Key into a generic
// Private Key to be used with libtrust.
func UnmarshalPrivateKeyJWK(data []byte) (PrivateKey, error) {
	jwk := make(map[string]interface{})

	err := json.Unmarshal(data, &jwk)
	if err != nil {
		return nil, fmt.Errorf(
			"decoding JWK Private Key JSON data: %s\n", err,
		)
	}

	// Get the Key Type value.
	kty, err := stringFromMap(jwk, "kty")
	if err != nil {
		return nil, fmt.Errorf("JWK Private Key type: %s", err)
	}

	switch {
	case kty == "EC":
		// Call out to unmarshal EC private key.
		return ecPrivateKeyFromMap(jwk)
	case kty == "RSA":
		// Call out to unmarshal RSA private key.
		return rsaPrivateKeyFromMap(jwk)
	default:
		return nil, fmt.Errorf(
			"JWK Private Key type not supported: %q\n", kty,
		)
	}
}