-
Notifications
You must be signed in to change notification settings - Fork 163
/
Copy pathkey.go
216 lines (194 loc) · 6.51 KB
/
key.go
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
// Copyright 2019 Anapaya Systems
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package keyconf
import (
"encoding/pem"
"fmt"
"strconv"
"time"
"github.com/scionproto/scion/go/lib/addr"
"github.com/scionproto/scion/go/lib/common"
"github.com/scionproto/scion/go/lib/scrypto"
"github.com/scionproto/scion/go/lib/serrors"
"github.com/scionproto/scion/go/lib/util"
)
var (
// ErrNoAlgorithm indicates no algorithm was provided.
ErrNoAlgorithm = serrors.New("no algorithm")
// ErrNoKey indicates no key was provided.
ErrNoKey = serrors.New("no key")
// ErrUnsupportedUsage indicates the key usage is not known.
ErrUnsupportedUsage = serrors.New("unsupported key usage")
// ErrUnsupportedType indicates the key type is not known.
ErrUnsupportedType = serrors.New("unsupported key type")
// ErrWildcardIA indicates the IA contains a wildcard.
ErrWildcardIA = serrors.New("wildcard IA")
)
const (
hdrAlgorithm = "algorithm"
hdrNotAfter = "not_after"
hdrNotBefore = "not_before"
hdrUsage = "usage"
hdrVersion = "version"
hdrIA = "ia"
)
// All supported key usages.
const (
ASSigningKey Usage = "as-signing"
ASDecryptionKey Usage = "as-decrypt"
ASRevocationKey Usage = "as-revocation"
IssCertSigningKey Usage = "issuer-cert-signing"
IssRevocationKey Usage = "issuer-revocation"
TRCVotingOnlineKey Usage = "trc-voting-online"
TRCVotingOfflineKey Usage = "trc-voting-offline"
TRCIssuingKey Usage = "trc-issuing"
)
// Usage describes how the key is intended to be used.
type Usage string
// UnmarshalText assigns the key usage if it is known. Otherwise ErrUnsupportedUsage.
func (u *Usage) UnmarshalText(text []byte) error {
s := Usage(text)
usages := []Usage{ASSigningKey, ASDecryptionKey, ASRevocationKey, IssCertSigningKey,
IssRevocationKey, TRCVotingOnlineKey, TRCVotingOfflineKey, TRCIssuingKey}
for _, usage := range usages {
if usage == s {
*u = usage
return nil
}
}
return serrors.WithCtx(ErrUnsupportedUsage, "input", string(text))
}
// Supported key types.
const (
PublicKey Type = "PUBLIC KEY"
PrivateKey Type = "PRIVATE KEY"
SymmetricKey Type = "SYMMETRIC KEY"
)
// Type indicates the key type. (public|private|symmetric)
type Type string
// UnmarshalText assigns the key type if it is known. Otherwise ErrUnsupportedType.
func (t *Type) UnmarshalText(text []byte) error {
s := Type(text)
for _, keyType := range []Type{PublicKey, PrivateKey, SymmetricKey} {
if keyType == s {
*t = keyType
return nil
}
}
return serrors.WithCtx(ErrUnsupportedType, "input", string(text))
}
// Key contains the key with additional metada.
//
// On disk, the key is encoded in PEM with a file name specific to the type,
// usage, and version of the key. The IA is prepended to public key filenames
// to avoid collisions.
type Key struct {
Type Type
Usage Usage
Algorithm string
Validity scrypto.Validity
Version scrypto.KeyVersion
IA addr.IA
Key []byte
}
// KeyFromPEM parses the PEM block.
func KeyFromPEM(block pem.Block) (Key, error) {
k := Key{}
if err := k.Type.UnmarshalText([]byte(block.Type)); err != nil {
return Key{}, serrors.WrapStr("unable to parse key type", err)
}
if err := k.Usage.UnmarshalText([]byte(block.Headers[hdrUsage])); err != nil {
return Key{}, serrors.WrapStr("unable to parse key usage", err)
}
var ok bool
if k.Algorithm, ok = block.Headers[hdrAlgorithm]; !ok {
return Key{}, ErrNoAlgorithm
}
if err := parseTime(&k.Validity.NotBefore.Time, block.Headers[hdrNotBefore]); err != nil {
return Key{}, serrors.WrapStr("unable to parse not_before time", err)
}
if err := parseTime(&k.Validity.NotAfter.Time, block.Headers[hdrNotAfter]); err != nil {
return Key{}, serrors.WrapStr("unable to parse not_after time", err)
}
if err := parseVersion(&k.Version, block.Headers[hdrVersion]); err != nil {
return Key{}, serrors.WrapStr("unable to parse key version", err)
}
if err := k.IA.UnmarshalText([]byte(block.Headers[hdrIA])); err != nil {
return Key{}, serrors.WrapStr("unable to parse IA", err)
}
if k.IA.IsWildcard() {
return Key{}, serrors.WithCtx(ErrWildcardIA, "input", block.Headers[hdrIA])
}
if block.Bytes == nil {
return Key{}, ErrNoKey
}
k.Key = append([]byte(nil), block.Bytes...)
return k, nil
}
// PEM encodes the key with metadata into a PEM block.
func (k Key) PEM() pem.Block {
return pem.Block{
Type: string(k.Type),
Headers: map[string]string{
hdrUsage: string(k.Usage),
hdrAlgorithm: k.Algorithm,
hdrNotBefore: util.TimeToCompact(k.Validity.NotBefore.Truncate(time.Second)),
hdrNotAfter: util.TimeToCompact(k.Validity.NotAfter.Truncate(time.Second)),
hdrVersion: strconv.FormatUint(uint64(k.Version), 10),
hdrIA: k.IA.String(),
},
Bytes: append([]byte(nil), k.Key...),
}
}
// File returns the key filename based on the metadata.
func (k Key) File() string {
if k.Type == PrivateKey {
return PrivateKeyFile(k.Usage, k.Version)
}
return PublicKeyFile(k.Usage, k.IA, k.Version)
}
func (k Key) String() string {
key := "<redacted>"
if k.Type == PublicKey {
key = fmt.Sprintf("%x", k.Key)
}
return fmt.Sprintf("type: %s usage: %s version: %d ia: %s validity: %s algorithm: %s key: %s",
k.Type, k.Usage, k.Version, k.IA, k.Validity, k.Algorithm, key,
)
}
// PrivateKeyFile returns the file name for the private key with the provided
// intended usage and version.
func PrivateKeyFile(usage Usage, version scrypto.KeyVersion) string {
return fmt.Sprintf("%s-v%d.key", usage, version)
}
// PublicKeyFile returns the file name for the public key with the provided
// intended usage and version.
func PublicKeyFile(usage Usage, ia addr.IA, version scrypto.KeyVersion) string {
return fmt.Sprintf("%s-%s-v%d.pub", ia.FileFmt(true), usage, version)
}
func parseTime(t *time.Time, input string) error {
var err error
if *t, err = time.Parse(common.TimeFmtSecs, input); err != nil {
return err
}
return nil
}
func parseVersion(v *scrypto.KeyVersion, input string) error {
ver, err := strconv.ParseUint(input, 10, 64)
if err != nil {
return err
}
*v = scrypto.KeyVersion(ver)
return nil
}