1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package ssh
17
18 import (
19 "crypto"
20 "crypto/ecdsa"
21 "crypto/ed25519"
22 "crypto/elliptic"
23 "encoding/binary"
24 "errors"
25 "fmt"
26 "io"
27 "net/http"
28
29 "github.com/asaskevich/govalidator"
30 "github.com/sigstore/rekor/pkg/pki/identity"
31 "github.com/sigstore/sigstore/pkg/cryptoutils"
32 sigsig "github.com/sigstore/sigstore/pkg/signature"
33 "golang.org/x/crypto/ssh"
34 )
35
36 type Signature struct {
37 signature *ssh.Signature
38 pk ssh.PublicKey
39 hashAlg string
40 }
41
42
43 func NewSignature(r io.Reader) (*Signature, error) {
44 b, err := io.ReadAll(r)
45 if err != nil {
46 return nil, err
47 }
48 sig, err := Decode(b)
49 if err != nil {
50 return nil, err
51 }
52 return sig, nil
53 }
54
55
56 func (s Signature) CanonicalValue() ([]byte, error) {
57 return []byte(Armor(s.signature, s.pk)), nil
58 }
59
60
61 func (s Signature) Verify(r io.Reader, k interface{}, _ ...sigsig.VerifyOption) error {
62 if s.signature == nil {
63 return errors.New("ssh signature has not been initialized")
64 }
65
66 key, ok := k.(*PublicKey)
67 if !ok {
68 return fmt.Errorf("invalid public key type for: %v", k)
69 }
70
71 ck, err := key.CanonicalValue()
72 if err != nil {
73 return err
74 }
75 cs, err := s.CanonicalValue()
76 if err != nil {
77 return err
78 }
79 return Verify(r, cs, ck)
80 }
81
82
83 type PublicKey struct {
84 key ssh.PublicKey
85 comment string
86 }
87
88
89 func NewPublicKey(r io.Reader) (*PublicKey, error) {
90
91
92
93
94
95 rawPub, err := io.ReadAll(http.MaxBytesReader(nil, io.NopCloser(r), 65536))
96 if err != nil {
97 return nil, err
98 }
99
100 key, comment, _, _, err := ssh.ParseAuthorizedKey(rawPub)
101 if err != nil {
102 return nil, err
103 }
104
105 return &PublicKey{key: key, comment: comment}, nil
106 }
107
108
109 func (k PublicKey) CanonicalValue() ([]byte, error) {
110 if k.key == nil {
111 return nil, errors.New("ssh public key has not been initialized")
112 }
113 return ssh.MarshalAuthorizedKey(k.key), nil
114 }
115
116
117 func (k PublicKey) EmailAddresses() []string {
118 if govalidator.IsEmail(k.comment) {
119 return []string{k.comment}
120 }
121 return nil
122 }
123
124
125 func (k PublicKey) Subjects() []string {
126 return k.EmailAddresses()
127 }
128
129
130 func (k PublicKey) Identities() ([]identity.Identity, error) {
131
132 var sshKey ssh.PublicKey
133 switch v := k.key.(type) {
134 case *ssh.Certificate:
135 sshKey = v.Key
136 default:
137 sshKey = k.key
138 }
139
140
141
142
143
144 var cryptoPubKey crypto.PublicKey
145 if v, ok := sshKey.(ssh.CryptoPublicKey); ok {
146 cryptoPubKey = v.CryptoPublicKey()
147 } else {
148 switch sshKey.Type() {
149 case ssh.KeyAlgoSKECDSA256:
150 var w struct {
151 Curve string
152 KeyBytes []byte
153 Application string
154 Rest []byte `ssh:"rest"`
155 }
156 _, k, ok := parseString(sshKey.Marshal())
157 if !ok {
158 return nil, fmt.Errorf("error parsing ssh.KeyAlgoSKED25519 key")
159 }
160 if err := ssh.Unmarshal(k, &w); err != nil {
161 return nil, err
162 }
163 if w.Curve != "nistp256" {
164 return nil, errors.New("ssh: unsupported curve")
165 }
166 ecdsaPubKey := new(ecdsa.PublicKey)
167 ecdsaPubKey.Curve = elliptic.P256()
168
169 ecdsaPubKey.X, ecdsaPubKey.Y = elliptic.Unmarshal(ecdsaPubKey.Curve, w.KeyBytes)
170 if ecdsaPubKey.X == nil || ecdsaPubKey.Y == nil {
171 return nil, errors.New("ssh: invalid curve point")
172 }
173 cryptoPubKey = ecdsaPubKey
174 case ssh.KeyAlgoSKED25519:
175 var w struct {
176 KeyBytes []byte
177 Application string
178 Rest []byte `ssh:"rest"`
179 }
180 _, k, ok := parseString(sshKey.Marshal())
181 if !ok {
182 return nil, fmt.Errorf("error parsing ssh.KeyAlgoSKED25519 key")
183 }
184 if err := ssh.Unmarshal(k, &w); err != nil {
185 return nil, err
186 }
187 if l := len(w.KeyBytes); l != ed25519.PublicKeySize {
188 return nil, fmt.Errorf("invalid size %d for Ed25519 public key", l)
189 }
190 cryptoPubKey = ed25519.PublicKey(w.KeyBytes)
191 default:
192
193 return nil, fmt.Errorf("unknown key type: %T", sshKey)
194 }
195 }
196
197 pkixKey, err := cryptoutils.MarshalPublicKeyToDER(cryptoPubKey)
198 if err != nil {
199 return nil, err
200 }
201 fp := ssh.FingerprintSHA256(k.key)
202 return []identity.Identity{{
203 Crypto: k.key,
204 Raw: pkixKey,
205 Fingerprint: fp,
206 }}, nil
207 }
208
209
210
211 func parseString(in []byte) (out, rest []byte, ok bool) {
212 if len(in) < 4 {
213 return
214 }
215 length := binary.BigEndian.Uint32(in)
216 in = in[4:]
217 if uint32(len(in)) < length {
218 return
219 }
220 out = in[:length]
221 rest = in[length:]
222 ok = true
223 return
224 }
225
View as plain text