podman

Форк
0
290 строк · 7.2 Кб
1
// Package encrypted provides a simple, secure system for encrypting data
2
// symmetrically with a passphrase.
3
//
4
// It uses scrypt derive a key from the passphrase and the NaCl secret box
5
// cipher for authenticated encryption.
6
package encrypted
7

8
import (
9
	"crypto/rand"
10
	"encoding/json"
11
	"errors"
12
	"fmt"
13
	"io"
14

15
	"golang.org/x/crypto/nacl/secretbox"
16
	"golang.org/x/crypto/scrypt"
17
)
18

19
const saltSize = 32
20

21
const (
22
	boxKeySize   = 32
23
	boxNonceSize = 24
24
)
25

26
// KDFParameterStrength defines the KDF parameter strength level to be used for
27
// encryption key derivation.
28
type KDFParameterStrength uint8
29

30
const (
31
	// Legacy defines legacy scrypt parameters (N:2^15, r:8, p:1)
32
	Legacy KDFParameterStrength = iota + 1
33
	// Standard defines standard scrypt parameters which is focusing 100ms of computation (N:2^16, r:8, p:1)
34
	Standard
35
	// OWASP defines OWASP recommended scrypt parameters (N:2^17, r:8, p:1)
36
	OWASP
37
)
38

39
var (
40
	// legacyParams represents old scrypt derivation parameters for backward
41
	// compatibility.
42
	legacyParams = scryptParams{
43
		N: 32768, // 2^15
44
		R: 8,
45
		P: 1,
46
	}
47

48
	// standardParams defines scrypt parameters based on the scrypt creator
49
	// recommendation to limit key derivation in time boxed to 100ms.
50
	standardParams = scryptParams{
51
		N: 65536, // 2^16
52
		R: 8,
53
		P: 1,
54
	}
55

56
	// owaspParams defines scrypt parameters recommended by OWASP
57
	owaspParams = scryptParams{
58
		N: 131072, // 2^17
59
		R: 8,
60
		P: 1,
61
	}
62

63
	// defaultParams defines scrypt parameters which will be used to generate a
64
	// new key.
65
	defaultParams = standardParams
66
)
67

68
const (
69
	nameScrypt    = "scrypt"
70
	nameSecretBox = "nacl/secretbox"
71
)
72

73
type data struct {
74
	KDF        scryptKDF       `json:"kdf"`
75
	Cipher     secretBoxCipher `json:"cipher"`
76
	Ciphertext []byte          `json:"ciphertext"`
77
}
78

79
type scryptParams struct {
80
	N int `json:"N"`
81
	R int `json:"r"`
82
	P int `json:"p"`
83
}
84

85
func (sp *scryptParams) Equal(in *scryptParams) bool {
86
	return in != nil && sp.N == in.N && sp.P == in.P && sp.R == in.R
87
}
88

89
func newScryptKDF(level KDFParameterStrength) (scryptKDF, error) {
90
	salt := make([]byte, saltSize)
91
	if err := fillRandom(salt); err != nil {
92
		return scryptKDF{}, fmt.Errorf("unable to generate a random salt: %w", err)
93
	}
94

95
	var params scryptParams
96
	switch level {
97
	case Legacy:
98
		params = legacyParams
99
	case Standard:
100
		params = standardParams
101
	case OWASP:
102
		params = owaspParams
103
	default:
104
		// Fallback to default parameters
105
		params = defaultParams
106
	}
107

108
	return scryptKDF{
109
		Name:   nameScrypt,
110
		Params: params,
111
		Salt:   salt,
112
	}, nil
113
}
114

115
type scryptKDF struct {
116
	Name   string       `json:"name"`
117
	Params scryptParams `json:"params"`
118
	Salt   []byte       `json:"salt"`
119
}
120

121
func (s *scryptKDF) Key(passphrase []byte) ([]byte, error) {
122
	return scrypt.Key(passphrase, s.Salt, s.Params.N, s.Params.R, s.Params.P, boxKeySize)
123
}
124

125
// CheckParams checks that the encoded KDF parameters are what we expect them to
126
// be. If we do not do this, an attacker could cause a DoS by tampering with
127
// them.
128
func (s *scryptKDF) CheckParams() error {
129
	switch {
130
	case legacyParams.Equal(&s.Params):
131
	case standardParams.Equal(&s.Params):
132
	case owaspParams.Equal(&s.Params):
133
	default:
134
		return errors.New("unsupported scrypt parameters")
135
	}
136

137
	return nil
138
}
139

140
func newSecretBoxCipher() (secretBoxCipher, error) {
141
	nonce := make([]byte, boxNonceSize)
142
	if err := fillRandom(nonce); err != nil {
143
		return secretBoxCipher{}, err
144
	}
145
	return secretBoxCipher{
146
		Name:  nameSecretBox,
147
		Nonce: nonce,
148
	}, nil
149
}
150

151
type secretBoxCipher struct {
152
	Name  string `json:"name"`
153
	Nonce []byte `json:"nonce"`
154

155
	encrypted bool
156
}
157

158
func (s *secretBoxCipher) Encrypt(plaintext, key []byte) []byte {
159
	var keyBytes [boxKeySize]byte
160
	var nonceBytes [boxNonceSize]byte
161

162
	if len(key) != len(keyBytes) {
163
		panic("incorrect key size")
164
	}
165
	if len(s.Nonce) != len(nonceBytes) {
166
		panic("incorrect nonce size")
167
	}
168

169
	copy(keyBytes[:], key)
170
	copy(nonceBytes[:], s.Nonce)
171

172
	// ensure that we don't re-use nonces
173
	if s.encrypted {
174
		panic("Encrypt must only be called once for each cipher instance")
175
	}
176
	s.encrypted = true
177

178
	return secretbox.Seal(nil, plaintext, &nonceBytes, &keyBytes)
179
}
180

181
func (s *secretBoxCipher) Decrypt(ciphertext, key []byte) ([]byte, error) {
182
	var keyBytes [boxKeySize]byte
183
	var nonceBytes [boxNonceSize]byte
184

185
	if len(key) != len(keyBytes) {
186
		panic("incorrect key size")
187
	}
188
	if len(s.Nonce) != len(nonceBytes) {
189
		// return an error instead of panicking since the nonce is user input
190
		return nil, errors.New("encrypted: incorrect nonce size")
191
	}
192

193
	copy(keyBytes[:], key)
194
	copy(nonceBytes[:], s.Nonce)
195

196
	res, ok := secretbox.Open(nil, ciphertext, &nonceBytes, &keyBytes)
197
	if !ok {
198
		return nil, errors.New("encrypted: decryption failed")
199
	}
200
	return res, nil
201
}
202

203
// Encrypt takes a passphrase and plaintext, and returns a JSON object
204
// containing ciphertext and the details necessary to decrypt it.
205
func Encrypt(plaintext, passphrase []byte) ([]byte, error) {
206
	return EncryptWithCustomKDFParameters(plaintext, passphrase, Standard)
207
}
208

209
// EncryptWithCustomKDFParameters takes a passphrase, the plaintext and a KDF
210
// parameter level (Legacy, Standard, or OWASP), and returns a JSON object
211
// containing ciphertext and the details necessary to decrypt it.
212
func EncryptWithCustomKDFParameters(plaintext, passphrase []byte, kdfLevel KDFParameterStrength) ([]byte, error) {
213
	k, err := newScryptKDF(kdfLevel)
214
	if err != nil {
215
		return nil, err
216
	}
217
	key, err := k.Key(passphrase)
218
	if err != nil {
219
		return nil, err
220
	}
221

222
	c, err := newSecretBoxCipher()
223
	if err != nil {
224
		return nil, err
225
	}
226

227
	data := &data{
228
		KDF:    k,
229
		Cipher: c,
230
	}
231
	data.Ciphertext = c.Encrypt(plaintext, key)
232

233
	return json.Marshal(data)
234
}
235

236
// Marshal encrypts the JSON encoding of v using passphrase.
237
func Marshal(v interface{}, passphrase []byte) ([]byte, error) {
238
	return MarshalWithCustomKDFParameters(v, passphrase, Standard)
239
}
240

241
// MarshalWithCustomKDFParameters encrypts the JSON encoding of v using passphrase.
242
func MarshalWithCustomKDFParameters(v interface{}, passphrase []byte, kdfLevel KDFParameterStrength) ([]byte, error) {
243
	data, err := json.MarshalIndent(v, "", "\t")
244
	if err != nil {
245
		return nil, err
246
	}
247
	return EncryptWithCustomKDFParameters(data, passphrase, kdfLevel)
248
}
249

250
// Decrypt takes a JSON-encoded ciphertext object encrypted using Encrypt and
251
// tries to decrypt it using passphrase. If successful, it returns the
252
// plaintext.
253
func Decrypt(ciphertext, passphrase []byte) ([]byte, error) {
254
	data := &data{}
255
	if err := json.Unmarshal(ciphertext, data); err != nil {
256
		return nil, err
257
	}
258

259
	if data.KDF.Name != nameScrypt {
260
		return nil, fmt.Errorf("encrypted: unknown kdf name %q", data.KDF.Name)
261
	}
262
	if data.Cipher.Name != nameSecretBox {
263
		return nil, fmt.Errorf("encrypted: unknown cipher name %q", data.Cipher.Name)
264
	}
265
	if err := data.KDF.CheckParams(); err != nil {
266
		return nil, err
267
	}
268

269
	key, err := data.KDF.Key(passphrase)
270
	if err != nil {
271
		return nil, err
272
	}
273

274
	return data.Cipher.Decrypt(data.Ciphertext, key)
275
}
276

277
// Unmarshal decrypts the data using passphrase and unmarshals the resulting
278
// plaintext into the value pointed to by v.
279
func Unmarshal(data []byte, v interface{}, passphrase []byte) error {
280
	decrypted, err := Decrypt(data, passphrase)
281
	if err != nil {
282
		return err
283
	}
284
	return json.Unmarshal(decrypted, v)
285
}
286

287
func fillRandom(b []byte) error {
288
	_, err := io.ReadFull(rand.Reader, b)
289
	return err
290
}
291

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.