-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathprivkey.go
132 lines (122 loc) · 3.49 KB
/
privkey.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
package stc
import (
"bytes"
"crypto/ed25519"
"errors"
"fmt"
"github.com/xdrpp/stc/stcdetail"
"github.com/xdrpp/stc/stx"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/armor"
"golang.org/x/crypto/openpgp/packet"
"io"
"io/ioutil"
"strings"
)
// Abstract type representing a Stellar private key. Prints and scans
// in StrKey format.
type PrivateKey struct {
stcdetail.PrivateKeyInterface
}
func (sec PrivateKey) Valid() bool {
return sec.PrivateKeyInterface != nil
}
func (sec *PrivateKey) Scan(ss fmt.ScanState, _ rune) error {
bs, err := ss.Token(true, stx.IsStrKeyChar)
if err != nil {
return err
}
key, vers := stx.FromStrKey(bs)
switch vers {
case stx.STRKEY_PRIVKEY | stx.STRKEY_ALG_ED25519:
sec.PrivateKeyInterface =
stcdetail.Ed25519Priv(ed25519.NewKeyFromSeed(key))
return nil
default:
return stx.StrKeyError("Invalid private key")
}
}
// Generates a new Stellar keypair and returns the PrivateKey.
// Currently the only valid value for pkt is
// stx.PUBLIC_KEY_TYPE_ED25519.
func NewPrivateKey(pkt stx.PublicKeyType) PrivateKey {
switch pkt {
case stx.PUBLIC_KEY_TYPE_ED25519:
return PrivateKey{stcdetail.NewEd25519Priv()}
default:
panic(fmt.Sprintf("KeyGen: unsupported PublicKeyType %v", pkt))
}
}
// Writes the a private key to a file in strkey format. If passphrase
// has non-zero length, then the key is symmetrically encrypted in
// ASCII-armored GPG format.
func (sk PrivateKey) Save(file string, passphrase []byte) error {
out := &strings.Builder{}
if len(passphrase) == 0 {
fmt.Fprintln(out, sk.String())
} else {
w0, err := armor.Encode(out, "PGP MESSAGE", nil)
if err != nil {
return err
}
w, err := openpgp.SymmetricallyEncrypt(w0, passphrase, nil,
&packet.Config{
DefaultCipher: packet.CipherAES256,
DefaultCompressionAlgo: packet.CompressionNone,
S2KCount: 65011712,
})
if err != nil {
w0.Close()
return err
}
fmt.Fprintln(w, sk.String())
w.Close()
w0.Close()
out.WriteString("\n")
}
return stcdetail.SafeCreateFile(file, out.String(), 0400)
}
var InvalidPassphrase = errors.New("Invalid passphrase")
var InvalidKeyFile = errors.New("Invalid private key file")
// Reads a private key from a file, prompting for a passphrase if the
// key is in ASCII-armored symmetrically-encrypted GPG format.
func LoadPrivateKey(file string) (PrivateKey, error) {
input, err := ioutil.ReadFile(file)
if err != nil {
return PrivateKey{}, err
}
ret := PrivateKey{}
if _, err = fmt.Fscan(bytes.NewBuffer(input), &ret); err == nil {
return ret, nil
}
block, err := armor.Decode(bytes.NewBuffer(input))
if err != nil {
return ret, InvalidKeyFile
}
md, err := openpgp.ReadMessage(block.Body, nil,
func(keys []openpgp.Key, symmetric bool) ([]byte, error) {
passphrase :=
stcdetail.GetPass(fmt.Sprintf("Passphrase for %s: ", file))
if len(passphrase) > 0 {
return passphrase, nil
}
return nil, InvalidPassphrase
}, nil)
if err != nil {
return ret, err
} else if _, err = fmt.Fscan(md.UnverifiedBody, &ret); err != nil {
return ret, err
} else if io.Copy(ioutil.Discard,
md.UnverifiedBody); md.SignatureError != nil {
return ret, md.SignatureError
}
return ret, nil
}
// Reads a private key from standard input. If standard input is a
// terminal, disables echo and prints prompt to standard error.
func InputPrivateKey(prompt string) (PrivateKey, error) {
key := stcdetail.GetPass(prompt)
var sk PrivateKey
_, err := fmt.Fscan(bytes.NewBuffer(key), &sk)
return sk, err
}