-
-
Notifications
You must be signed in to change notification settings - Fork 501
/
encrypted_keys.go
104 lines (90 loc) · 2.73 KB
/
encrypted_keys.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
// Copyright 2019 The age Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"errors"
"fmt"
"filippo.io/age"
)
// LazyScryptIdentity is an age.Identity that requests a passphrase only if it
// encounters an scrypt stanza. After obtaining a passphrase, it delegates to
// ScryptIdentity.
type LazyScryptIdentity struct {
Passphrase func() (string, error)
}
var _ age.Identity = &LazyScryptIdentity{}
func (i *LazyScryptIdentity) Unwrap(stanzas []*age.Stanza) (fileKey []byte, err error) {
for _, s := range stanzas {
if s.Type == "scrypt" && len(stanzas) != 1 {
return nil, errors.New("an scrypt recipient must be the only one")
}
}
if len(stanzas) != 1 || stanzas[0].Type != "scrypt" {
return nil, age.ErrIncorrectIdentity
}
pass, err := i.Passphrase()
if err != nil {
return nil, fmt.Errorf("could not read passphrase: %v", err)
}
ii, err := age.NewScryptIdentity(pass)
if err != nil {
return nil, err
}
fileKey, err = ii.Unwrap(stanzas)
if errors.Is(err, age.ErrIncorrectIdentity) {
// ScryptIdentity returns ErrIncorrectIdentity for an incorrect
// passphrase, which would lead Decrypt to returning "no identity
// matched any recipient". That makes sense in the API, where there
// might be multiple configured ScryptIdentity. Since in cmd/age there
// can be only one, return a better error message.
return nil, fmt.Errorf("incorrect passphrase")
}
return fileKey, err
}
type EncryptedIdentity struct {
Contents []byte
Passphrase func() (string, error)
NoMatchWarning func()
identities []age.Identity
}
var _ age.Identity = &EncryptedIdentity{}
func (i *EncryptedIdentity) Recipients() ([]age.Recipient, error) {
if i.identities == nil {
if err := i.decrypt(); err != nil {
return nil, err
}
}
return identitiesToRecipients(i.identities)
}
func (i *EncryptedIdentity) Unwrap(stanzas []*age.Stanza) (fileKey []byte, err error) {
if i.identities == nil {
if err := i.decrypt(); err != nil {
return nil, err
}
}
for _, id := range i.identities {
fileKey, err = id.Unwrap(stanzas)
if errors.Is(err, age.ErrIncorrectIdentity) {
continue
}
if err != nil {
return nil, err
}
return fileKey, nil
}
i.NoMatchWarning()
return nil, age.ErrIncorrectIdentity
}
func (i *EncryptedIdentity) decrypt() error {
d, err := age.Decrypt(bytes.NewReader(i.Contents), &LazyScryptIdentity{i.Passphrase})
if e := new(age.NoIdentityMatchError); errors.As(err, &e) {
return fmt.Errorf("identity file is encrypted with age but not with a passphrase")
}
if err != nil {
return fmt.Errorf("failed to decrypt identity file: %v", err)
}
i.identities, err = parseIdentities(d)
return err
}