-
Notifications
You must be signed in to change notification settings - Fork 0
/
epk.go
156 lines (130 loc) · 3.94 KB
/
epk.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
/*-
* Copyright (c) 2016, Jörg Pernfuß
*
* Use of this source code is governed by a 2-clause BSD license
* that can be found in the LICENSE file.
*/
// Package epk implements an encrypted private key
// on top of the Ed25519 signature scheme.
// Given the passphrase and a message it can also
// unlock the key and sign the message.
//
// It uses scrypt as key derivation function and
// ChaCha20/Poly1305 for encryption.
package epk // import "github.com/mjolnir42/epk"
import (
"crypto/cipher"
cryptorand "crypto/rand"
"io"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/scrypt"
)
// EncryptedPrivateKey implements an encrypted private key
type EncryptedPrivateKey struct {
// the private key type: Ed25519
Keytype string
// the used KDF: scrypt
KDF string
// the used KDF parameters: N=65536;r=8;p=1
KDFParam string
// the used encryption algorithm: ChaCha20Poly1305
EncAlgorithm string
// 24 byte salt for the KDF, 12 of which are also
// used as AEAD nonce
Salt []byte
// the encrypted private key
privateKey []byte
}
// New returns the plain public and encrypted private key for
// a generated keypair.
func New(passphrase string) (*EncryptedPrivateKey, ed25519.PublicKey, error) {
var err error
var priv, pub []byte
encPK := new(EncryptedPrivateKey)
if pub, priv, err = ed25519.GenerateKey(cryptorand.Reader); err != nil {
return nil, nil, err
}
encPK.Keytype = `Ed25519`
err = encPK.set([]byte(passphrase), priv)
return encPK, pub, err
}
// Sign signs the message with the private key protected by passphrase
// and returns the signature.
func (e *EncryptedPrivateKey) Sign(passphrase string, message []byte) ([]byte, error) {
pk, err := e.unlock(passphrase)
if err != nil {
return nil, err
}
return e.signMsg(pk, message), nil
}
// Public unlocks the private key and generates the public key from it
func (e *EncryptedPrivateKey) Public(passphrase string) (ed25519.PublicKey, error) {
pk, err := e.unlock(passphrase)
if err != nil {
return nil, err
}
pub := ed25519.PrivateKey(pk).Public()
return pub.(ed25519.PublicKey), nil
}
// set saves the private key after encryption with the passphrase
// as e.privatekey
func (e *EncryptedPrivateKey) set(passphrase, private []byte) error {
if err := e.newSalt(); err != nil {
return err
}
key, err := e.derive(passphrase)
if err != nil {
return err
}
// set this here since derive is actually reused to unlock
e.KDF = `scrypt`
e.KDFParam = `N=65536;r=8;p=1`
if err = e.encrypt(key, private); err != nil {
return err
}
return nil
}
// encrypt encrypts data with key and stored the resulting
// ciphertext in e.privateKey
func (e *EncryptedPrivateKey) encrypt(key, data []byte) error {
var err error
var crypt cipher.AEAD
if crypt, err = chacha20poly1305.New(key); err != nil {
return err
}
e.privateKey = crypt.Seal([]byte{}, e.Salt[:12], data, []byte{})
e.EncAlgorithm = `ChaCha20Poly1305`
return nil
}
// derive uses scrypt to generate a key from a passphrase
func (e *EncryptedPrivateKey) derive(passphrase []byte) ([]byte, error) {
return scrypt.Key(passphrase, e.Salt, 65536, 8, 1, 32)
}
// newSalt reads a new random salt and stores it in e.Salt
func (e *EncryptedPrivateKey) newSalt() error {
e.Salt = make([]byte, 24)
_, err := io.ReadFull(cryptorand.Reader, e.Salt[:24])
return err
}
// unlock decrypts and returns the private key
func (e *EncryptedPrivateKey) unlock(passphrase string) ([]byte, error) {
key, err := e.derive([]byte(passphrase))
if err != nil {
return nil, err
}
crypt, err := chacha20poly1305.New(key)
if err != nil {
return nil, err
}
pk, err := crypt.Open([]byte{}, e.Salt[:12], e.privateKey, []byte{})
if err != nil {
return nil, err
}
return pk, nil
}
// signMsg uses the private key to sign message. It returns the signature.
func (*EncryptedPrivateKey) signMsg(private, message []byte) []byte {
return ed25519.Sign(private, message)
}
// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix