/
keys.go
145 lines (119 loc) · 3.53 KB
/
keys.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
package edssh
import (
"encoding/pem"
"errors"
"fmt"
"github.com/agl/ed25519"
"golang.org/x/crypto/ssh"
"io"
)
func ParsePrivateKey(pemBytes []byte) (ssh.Signer, error) {
block, _ := pem.Decode(pemBytes)
if block == nil {
return nil, errors.New("edssh: key not found")
}
switch block.Type {
case "OPENSSH PRIVATE KEY":
return ParseEd25519PrivateKey(block.Bytes)
default:
return ssh.ParsePrivateKey(pemBytes)
}
}
type Ed25519PrivateKey struct {
bytes *[ed25519.PrivateKeySize]byte
publicKey *Ed25519PublicKey
}
func ParseEd25519PrivateKey(keyBlock []byte) (*Ed25519PrivateKey, error) {
var (
ciphername, kdfname string
num uint32
err error
ok bool
)
if keyBlock, ok = OpenSSHKey.FormatOK(keyBlock); !ok {
return nil, errors.New("edssh: invalid key")
}
keyBlock, err = OpenSSHKey.ReadString(keyBlock, &ciphername, nil)
keyBlock, err = OpenSSHKey.ReadString(keyBlock, &kdfname, err)
// TODO: support encrypted private key
if ciphername != "none" && kdfname != "none" {
return nil, errors.New("edssh: encrypted key is not supported yet")
}
// kdfoption (zeroed out since we don't support encryption yet)
keyBlock, err = OpenSSHKey.ReadUint32(keyBlock, &num, err)
// number of keys (only 1)
keyBlock, err = OpenSSHKey.ReadUint32(keyBlock, &num, err)
// public key
var pubKeyBuf []byte
keyBlock, err = OpenSSHKey.ReadBuf(keyBlock, &pubKeyBuf, err)
// private key
var privKeyBuf []byte
keyBlock, err = OpenSSHKey.ReadBuf(keyBlock, &privKeyBuf, err)
var privateKey = &Ed25519PrivateKey{}
if err = privateKey.parseFromBuf(privKeyBuf, err); err != nil {
return nil, err
}
return privateKey, nil
}
func (ek *Ed25519PrivateKey) parseFromBuf(buf []byte, prevErr error) error {
if prevErr != nil {
return prevErr
}
var checkint uint32
buf, prevErr = OpenSSHKey.ReadUint32(buf, &checkint, prevErr)
buf, prevErr = OpenSSHKey.ReadUint32(buf, &checkint, prevErr)
var keyType string
buf, prevErr = OpenSSHKey.ReadString(buf, &keyType, prevErr)
var pubKey []byte
buf, prevErr = OpenSSHKey.ReadBuf(buf, &pubKey, prevErr)
var privKey []byte
if buf, prevErr = OpenSSHKey.ReadBuf(buf, &privKey, prevErr); prevErr != nil {
return prevErr
}
if len(privKey) != ed25519.PrivateKeySize {
return errors.New("edssh: invalid private key length")
}
edPubKey := new([ed25519.PublicKeySize]byte)
copy(edPubKey[:], pubKey)
ek.publicKey = &Ed25519PublicKey{edPubKey}
ek.bytes = new([ed25519.PrivateKeySize]byte)
copy(ek.bytes[:], privKey)
return nil
}
func (ek *Ed25519PrivateKey) PublicKey() ssh.PublicKey {
return ek.publicKey
}
func (ek *Ed25519PrivateKey) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) {
signature := ed25519.Sign(ek.bytes, data)
return &ssh.Signature{
Format: ek.PublicKey().Type(),
Blob: (*signature)[:],
}, nil
}
type Ed25519PublicKey struct {
bytes *[ed25519.PublicKeySize]byte
}
func (ek *Ed25519PublicKey) Type() string {
return "ssh-ed25519"
}
func (ek *Ed25519PublicKey) Marshal() []byte {
wirekey := struct {
Name string
Pub []byte
}{
ek.Type(),
(*ek.bytes)[:],
}
return ssh.Marshal(&wirekey)
}
func (ek *Ed25519PublicKey) Verify(message []byte, signature *ssh.Signature) error {
if signature.Format != ek.Type() {
return fmt.Errorf("edssh: signature type %s for key type %s", signature.Format, ek.Type())
}
sig := new([ed25519.SignatureSize]byte)
copy(sig[:], signature.Blob)
if ok := ed25519.Verify(ek.bytes, message, sig); !ok {
return errors.New("edssh: invalid signature for given message")
}
return nil
}