-
Notifications
You must be signed in to change notification settings - Fork 233
/
key.go
75 lines (62 loc) · 1.75 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
package ssh
import (
"crypto/ed25519"
"encoding/pem"
sshCrypt "golang.org/x/crypto/ssh"
"math/rand"
)
// // stolen from `mikesmitty`, thanks, you are a mikesmitty and a scholar
func MarshalED25519PrivateKey(key ed25519.PrivateKey, comment string) []byte {
magic := append([]byte("openssh-key-v1"), 0)
var w struct {
CipherName string
KdfName string
KdfOpts string
NumKeys uint32
PubKey []byte
PrivKeyBlock []byte
}
pk1 := struct {
Check1 uint32
Check2 uint32
Keytype string
Pub []byte
Priv []byte
Comment string
Pad []byte `ssh:"rest"`
}{}
ci := rand.Uint32() // skipcq: GSC-G404
pk1.Check1 = ci
pk1.Check2 = ci
pk1.Keytype = sshCrypt.KeyAlgoED25519
pk := key.Public().(ed25519.PublicKey)
pubKey := []byte(pk)
pk1.Pub = pubKey
pk1.Priv = []byte(key)
pk1.Comment = comment
// Add some padding to match the encryption block size within PrivKeyBlock (without Pad field)
// 8 doesn't match the documentation, but that's what ssh-keygen uses for unencrypted keys. *shrug*
bs := 8
blockLen := len(sshCrypt.Marshal(pk1))
padLen := (bs - (blockLen % bs)) % bs
pk1.Pad = make([]byte, padLen)
for i := 0; i < padLen; i++ {
pk1.Pad[i] = byte(i + 1)
}
// Generate the pubkey prefix "\0\0\0\nssh-ed25519\0\0\0 "
prefix := []byte{0x0, 0x0, 0x0, 0x0b}
prefix = append(prefix, []byte(sshCrypt.KeyAlgoED25519)...)
prefix = append(prefix, []byte{0x0, 0x0, 0x0, 0x20}...)
w.CipherName = "none"
w.KdfName = "none"
w.KdfOpts = ""
w.NumKeys = 1
w.PubKey = append(w.PubKey, prefix...)
w.PubKey = append(w.PubKey, pubKey...)
w.PrivKeyBlock = sshCrypt.Marshal(pk1)
magic = append(magic, sshCrypt.Marshal(w)...)
return pem.EncodeToMemory(&pem.Block{
Type: "OPENSSH PRIVATE KEY",
Bytes: magic,
})
}