@@ -4,14 +4,8 @@ import (
4
4
"context"
5
5
"fmt"
6
6
"io"
7
- "os"
8
- "regexp"
9
- "strings"
10
7
11
8
"filippo.io/age"
12
- "filippo.io/age/agessh"
13
- "golang.org/x/crypto/ssh"
14
- "golang.org/x/term"
15
9
16
10
"github.com/slok/agebox/internal/key"
17
11
"github.com/slok/agebox/internal/log"
@@ -52,15 +46,18 @@ func (p PrivateKey) AgeIdentity() age.Identity { return p.identity }
52
46
53
47
var _ model.PrivateKey = & PrivateKey {}
54
48
49
+ type publicKeyParser func (ctx context.Context , key string ) (age.Recipient , error )
50
+ type privateKeyParser func (ctx context.Context , key string ) (age.Identity , error )
51
+
55
52
type factory struct {
56
53
// These are the key parsers used to load keys, they will work in
57
54
// brute force mode being used as a chain, if one fails we continue
58
55
// until one is correct.
59
56
//
60
57
// TODO(slok): We could optimize this as age does, checking
61
58
// the keys headers and selecting the correct one.
62
- publicKeyParsers []func ( string ) (age. Recipient , error )
63
- privateKeyParsers []func ( string ) (age. Identity , error )
59
+ publicKeyParsers []publicKeyParser
60
+ privateKeyParsers []privateKeyParser
64
61
}
65
62
66
63
// Factory is the key.Factory implementation for age supported keys.
@@ -72,13 +69,13 @@ func NewFactory(passphraseReader io.Reader, logger log.Logger) key.Factory {
72
69
logger = logger .WithValues (log.Kv {"svc" : "key.age.Factory" })
73
70
74
71
return factory {
75
- publicKeyParsers : []func ( string ) (age. Recipient , error ) {
76
- agessh . ParseRecipient ,
77
- func ( d string ) (age. Recipient , error ) { return age . ParseX25519Recipient ( d ) } ,
72
+ publicKeyParsers : []publicKeyParser {
73
+ parseSSHPublic () ,
74
+ parseAgePublic () ,
78
75
},
79
- privateKeyParsers : []func ( string ) (age. Identity , error ) {
80
- parseSSHIdentityFunc (passphraseReader , logger ),
81
- parseAgeIdentityFunc (),
76
+ privateKeyParsers : []privateKeyParser {
77
+ parseSSHPrivateFunc (passphraseReader , logger ),
78
+ parseAgePrivateFunc (),
82
79
},
83
80
}
84
81
}
@@ -88,7 +85,7 @@ var _ key.Factory = factory{}
88
85
func (f factory ) GetPublicKey (ctx context.Context , data []byte ) (model.PublicKey , error ) {
89
86
sdata := string (data )
90
87
for _ , f := range f .publicKeyParsers {
91
- recipient , err := f (sdata )
88
+ recipient , err := f (ctx , sdata )
92
89
// If no error, we have our public key.
93
90
if err == nil {
94
91
return PublicKey {
@@ -104,7 +101,7 @@ func (f factory) GetPublicKey(ctx context.Context, data []byte) (model.PublicKey
104
101
func (f factory ) GetPrivateKey (ctx context.Context , data []byte ) (model.PrivateKey , error ) {
105
102
sdata := string (data )
106
103
for _ , f := range f .privateKeyParsers {
107
- identity , err := f (sdata )
104
+ identity , err := f (ctx , sdata )
108
105
// If no error, we have our private key.
109
106
if err == nil {
110
107
return PrivateKey {
@@ -116,74 +113,3 @@ func (f factory) GetPrivateKey(ctx context.Context, data []byte) (model.PrivateK
116
113
117
114
return nil , fmt .Errorf ("invalid private key" )
118
115
}
119
-
120
- func parseSSHIdentityFunc (passphraseR io.Reader , logger log.Logger ) func (string ) (age.Identity , error ) {
121
- return func (d string ) (age.Identity , error ) {
122
- // Get the SSH private key.
123
- secretData := []byte (d )
124
- id , err := agessh .ParseIdentity (secretData )
125
- if err == nil {
126
- return id , nil
127
- }
128
-
129
- // If passphrase required, ask for it.
130
- sshErr , ok := err .(* ssh.PassphraseMissingError )
131
- if ! ok {
132
- return nil , err
133
- }
134
-
135
- if sshErr .PublicKey == nil {
136
- return nil , fmt .Errorf ("passphrase required and public key can't be obtained from private key" )
137
- }
138
-
139
- // Ask for passphrase and get identity.
140
- i , err := agessh .NewEncryptedSSHIdentity (sshErr .PublicKey , secretData , askPasswordStdin (passphraseR , logger ))
141
- if err != nil {
142
- return nil , err
143
- }
144
-
145
- return i , nil
146
- }
147
- }
148
-
149
- func askPasswordStdin (r io.Reader , logger log.Logger ) func () ([]byte , error ) {
150
- return func () ([]byte , error ) {
151
- // If not stdin just return the passphrase.
152
- if r != os .Stdin {
153
- return io .ReadAll (r )
154
- }
155
-
156
- // Check if is a valid terminal and try getting it.
157
- fd := int (os .Stdin .Fd ())
158
- if ! term .IsTerminal (fd ) {
159
- tty , err := os .Open ("/dev/tty" )
160
- if err != nil {
161
- return nil , fmt .Errorf ("standard input is not available or not a terminal, and opening /dev/tty failed: %v" , err )
162
- }
163
- defer tty .Close ()
164
- fd = int (tty .Fd ())
165
- }
166
-
167
- // Ask for password.
168
- logger .Warningf ("SSH key passphrase required" )
169
- logger .Infof ("Enter passphrase for ssh key: " )
170
-
171
- p , err := term .ReadPassword (fd )
172
- if err != nil {
173
- return nil , err
174
- }
175
-
176
- return p , nil
177
- }
178
- }
179
-
180
- var removeCommentRegexp = regexp .MustCompile ("(?m)(^#.*$)" )
181
-
182
- func parseAgeIdentityFunc () func (s string ) (age.Identity , error ) {
183
- return func (d string ) (age.Identity , error ) {
184
- d = removeCommentRegexp .ReplaceAllString (d , "" )
185
- d = strings .TrimSpace (d )
186
-
187
- return age .ParseX25519Identity (d )
188
- }
189
- }
0 commit comments