forked from lightningnetwork/lnd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
crypto.go
140 lines (120 loc) · 4.3 KB
/
crypto.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
package chanbackup
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"fmt"
"io"
"io/ioutil"
"github.com/lightningnetwork/lnd/keychain"
"golang.org/x/crypto/chacha20poly1305"
)
// TODO(roasbeef): interface in front of?
// baseEncryptionKeyLoc is the KeyLocator that we'll use to derive the base
// encryption key used for encrypting all static channel backups. We use this
// to then derive the actual key that we'll use for encryption. We do this
// rather than using the raw key, as we assume that we can't obtain the raw
// keys, and we don't want to require that the HSM know our target cipher for
// encryption.
//
// TODO(roasbeef): possibly unique encrypt?
var baseEncryptionKeyLoc = keychain.KeyLocator{
Family: keychain.KeyFamilyStaticBackup,
Index: 0,
}
// genEncryptionKey derives the key that we'll use to encrypt all of our static
// channel backups. The key itself, is the sha2 of a base key that we get from
// the keyring. We derive the key this way as we don't force the HSM (or any
// future abstractions) to be able to derive and know of the cipher that we'll
// use within our protocol.
func genEncryptionKey(keyRing keychain.KeyRing) ([]byte, error) {
// key = SHA256(baseKey)
baseKey, err := keyRing.DeriveKey(
baseEncryptionKeyLoc,
)
if err != nil {
return nil, err
}
encryptionKey := sha256.Sum256(
baseKey.PubKey.SerializeCompressed(),
)
// TODO(roasbeef): throw back in ECDH?
return encryptionKey[:], nil
}
// encryptPayloadToWriter attempts to write the set of bytes contained within
// the passed byes.Buffer into the passed io.Writer in an encrypted form. We
// use a 24-byte chachapoly AEAD instance with a randomized nonce that's
// pre-pended to the final payload and used as associated data in the AEAD. We
// use the passed keyRing to generate the encryption key, see genEncryptionKey
// for further details.
func encryptPayloadToWriter(payload bytes.Buffer, w io.Writer,
keyRing keychain.KeyRing) error {
// First, we'll derive the key that we'll use to encrypt the payload
// for safe storage without giving away the details of any of our
// channels. The final operation is:
//
// key = SHA256(baseKey)
encryptionKey, err := genEncryptionKey(keyRing)
if err != nil {
return err
}
// Before encryption, we'll initialize our cipher with the target
// encryption key, and also read out our random 24-byte nonce we use
// for encryption. Note that we use NewX, not New, as the latter
// version requires a 12-byte nonce, not a 24-byte nonce.
cipher, err := chacha20poly1305.NewX(encryptionKey)
if err != nil {
return err
}
var nonce [chacha20poly1305.NonceSizeX]byte
if _, err := rand.Read(nonce[:]); err != nil {
return err
}
// Finally, we encrypted the final payload, and write out our
// ciphertext with nonce pre-pended.
ciphertext := cipher.Seal(nil, nonce[:], payload.Bytes(), nonce[:])
if _, err := w.Write(nonce[:]); err != nil {
return err
}
if _, err := w.Write(ciphertext); err != nil {
return err
}
return nil
}
// decryptPayloadFromReader attempts to decrypt the encrypted bytes within the
// passed io.Reader instance using the key derived from the passed keyRing. For
// further details regarding the key derivation protocol, see the
// genEncryptionKey method.
func decryptPayloadFromReader(payload io.Reader,
keyRing keychain.KeyRing) ([]byte, error) {
// First, we'll re-generate the encryption key that we use for all the
// SCBs.
encryptionKey, err := genEncryptionKey(keyRing)
if err != nil {
return nil, err
}
// Next, we'll read out the entire blob as we need to isolate the nonce
// from the rest of the ciphertext.
packedBackup, err := ioutil.ReadAll(payload)
if err != nil {
return nil, err
}
if len(packedBackup) < chacha20poly1305.NonceSizeX {
return nil, fmt.Errorf("payload size too small, must be at "+
"least %v bytes", chacha20poly1305.NonceSizeX)
}
nonce := packedBackup[:chacha20poly1305.NonceSizeX]
ciphertext := packedBackup[chacha20poly1305.NonceSizeX:]
// Now that we have the cipher text and the nonce separated, we can go
// ahead and decrypt the final blob so we can properly serialized the
// SCB.
cipher, err := chacha20poly1305.NewX(encryptionKey)
if err != nil {
return nil, err
}
plaintext, err := cipher.Open(nil, nonce, ciphertext, nonce)
if err != nil {
return nil, err
}
return plaintext, nil
}