commit 8408ba7c9a7723fc5364a90738b9141aab39716e
parent f2bdf70e54307ff7ceabd227dc93c53e77c524ec
Author: Filippo Valsorda <hi@filippo.io>
Date: Tue, 8 Oct 2019 23:24:13 -0400
internal/age: make the scrypt recipient work parameter log2(N)
Fixes #10
Diffstat:
3 files changed, 30 insertions(+), 17 deletions(-)
diff --git a/internal/age/age_test.go b/internal/age/age_test.go
@@ -76,7 +76,7 @@ func TestEncryptDecryptScrypt(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- r.SetWorkFactor(1 << 15)
+ r.SetWorkFactor(15)
buf := &bytes.Buffer{}
w, err := age.Encrypt(buf, r)
if err != nil {
diff --git a/internal/age/recipients_test.go b/internal/age/recipients_test.go
@@ -64,7 +64,7 @@ func TestScryptRoundTrip(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- r.SetWorkFactor(1 << 15)
+ r.SetWorkFactor(15)
i, err := age.NewScryptIdentity(password)
if err != nil {
t.Fatal(err)
diff --git a/internal/age/scrypt.go b/internal/age/scrypt.go
@@ -31,15 +31,20 @@ func NewScryptRecipient(password string) (*ScryptRecipient, error) {
return nil, errors.New("empty scrypt password")
}
r := &ScryptRecipient{
- password: []byte(password),
- workFactor: 1 << 18, // 1s on a modern machine
+ password: []byte(password),
+ // TODO: automatically scale this to 1s (with a min) in the CLI.
+ workFactor: 18, // 1s on a modern machine
}
return r, nil
}
-func (r *ScryptRecipient) SetWorkFactor(N int) {
- // TODO: automatically scale this to 1s (with a min) in the CLI.
- r.workFactor = N
+// SetWorkFactor sets the scrypt work factor to 2^logN.
+// It must be called before Wrap.
+func (r *ScryptRecipient) SetWorkFactor(logN int) {
+ if logN > 30 || logN < 1 {
+ panic("age: SetWorkFactor called with illegal value")
+ }
+ r.workFactor = logN
}
func (r *ScryptRecipient) Wrap(fileKey []byte) (*format.Recipient, error) {
@@ -48,13 +53,13 @@ func (r *ScryptRecipient) Wrap(fileKey []byte) (*format.Recipient, error) {
return nil, err
}
- N := r.workFactor
+ logN := r.workFactor
l := &format.Recipient{
Type: "scrypt",
- Args: []string{format.EncodeToString(salt), strconv.Itoa(N)},
+ Args: []string{format.EncodeToString(salt), strconv.Itoa(logN)},
}
- k, err := scrypt.Key(r.password, salt, N, 8, 1, chacha20poly1305.KeySize)
+ k, err := scrypt.Key(r.password, salt, 1<<logN, 8, 1, chacha20poly1305.KeySize)
if err != nil {
return nil, fmt.Errorf("failed to generate scrypt hash: %v", err)
}
@@ -83,13 +88,18 @@ func NewScryptIdentity(password string) (*ScryptIdentity, error) {
}
i := &ScryptIdentity{
password: []byte(password),
- maxWorkFactor: 1 << 22, // 15s on a modern machine
+ maxWorkFactor: 22, // 15s on a modern machine
}
return i, nil
}
-func (i *ScryptIdentity) SetMaxWorkFactor(N int) {
- i.maxWorkFactor = N
+// SetWorkFactor sets the maximum accepted scrypt work factor to 2^logN.
+// It must be called before Unwrap.
+func (i *ScryptIdentity) SetMaxWorkFactor(logN int) {
+ if logN > 30 || logN < 1 {
+ panic("age: SetMaxWorkFactor called with illegal value")
+ }
+ i.maxWorkFactor = logN
}
func (i *ScryptIdentity) Unwrap(block *format.Recipient) ([]byte, error) {
@@ -106,19 +116,22 @@ func (i *ScryptIdentity) Unwrap(block *format.Recipient) ([]byte, error) {
if len(salt) != 16 {
return nil, errors.New("invalid scrypt recipient block")
}
- N, err := strconv.Atoi(block.Args[1])
+ logN, err := strconv.Atoi(block.Args[1])
if err != nil {
return nil, fmt.Errorf("failed to parse scrypt work factor: %v", err)
}
- if N > i.maxWorkFactor {
- return nil, fmt.Errorf("scrypt work factor too large: %v", N)
+ if logN > i.maxWorkFactor {
+ return nil, fmt.Errorf("scrypt work factor too large: %v", logN)
+ }
+ if logN <= 0 {
+ return nil, fmt.Errorf("invalid scrypt work factor: %v", logN)
}
wrappedKey, err := format.DecodeString(string(block.Body))
if err != nil {
return nil, fmt.Errorf("failed to parse scrypt recipient: %v", err)
}
- k, err := scrypt.Key(i.password, salt, N, 8, 1, chacha20poly1305.KeySize)
+ k, err := scrypt.Key(i.password, salt, 1<<logN, 8, 1, chacha20poly1305.KeySize)
if err != nil {
return nil, fmt.Errorf("failed to generate scrypt hash: %v", err)
}