-
Notifications
You must be signed in to change notification settings - Fork 0
/
identity.go
152 lines (120 loc) · 2.87 KB
/
identity.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
141
142
143
144
145
146
147
148
149
150
151
152
package identity
import (
"errors"
"fmt"
"io"
"os"
"time"
"filippo.io/age"
)
const (
// default privage secret key. It can be an age key of a PIV encoded age
// key.
FileName = "privage-key.txt"
TypePiv = "PIV"
TypeAge = "AGE"
)
// An Identity is a wrapper for the age Identity.
type Identity struct {
// The age identity
Id *age.X25519Identity
// Path of the found key.
// Path can contain a normal age key or a PIV encrypted one.
//
// Path can be not empty and still a null Id because of a decoding error.
//
// A empty Path means all possible paths were searched and no files were
// found
Path string
// Err is the error raised finding or validating the a age identity.
Err error
}
// Load returns an Age identity
//
// it tries:
// 1) the path given in the config file
// 2) FILE in the current dir
// 3) FILE in the user HOME
// init method check if exist
func Load(confPath string) Identity {
if len(confPath) > 0 {
ff, err := os.Open(confPath)
defer ff.Close()
if err == nil {
return parseIdentity(ff, confPath)
}
// no Path
return Identity{Err: err}
}
// try current dir
currentDir, err := os.Getwd()
if err != nil {
return Identity{Err: err}
}
currentPath := currentDir + "/" + FileName
fl, err := os.Open(currentPath)
defer fl.Close()
if err == nil {
return parseIdentity(fl, currentPath)
}
if !errors.Is(err, os.ErrNotExist) {
return Identity{Err: err}
}
// try home
homeDir, err := os.UserHomeDir()
if err != nil {
return Identity{Err: err}
}
homePath := homeDir + "/" + FileName
f, err := os.Open(homePath)
defer f.Close()
if err == nil {
return parseIdentity(f, homePath)
}
if !errors.Is(err, os.ErrNotExist) {
return Identity{Err: err}
}
return Identity{Err: err}
}
func parseIdentity(f io.Reader, path string) Identity {
identity := Identity{}
identity.Path = path
identities, err := age.ParseIdentities(f)
if err != nil {
identity.Err = err
} else {
identity.Id = identities[0].(*age.X25519Identity)
}
return identity
}
func FmtType(slot string) string {
if len(slot) > 0 {
return fmt.Sprintf("🔏 yubikey encrypted age, slot 📌 %s", slot)
}
return "🔐 age key"
}
// Create generates a age Identity and writes it in the file at filePath
func Create(filePath string) error {
f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
if err != nil {
return err
}
defer func() {
if err := f.Close(); err != nil {
return
}
}()
k, err := age.GenerateX25519Identity()
if err != nil {
return err
}
fmt.Fprintf(f, "# created: %s\n", time.Now().Format(time.RFC3339))
fmt.Fprintf(f, "# public key: %s\n", k.Recipient())
fmt.Fprintf(f, "%s\n", k)
return nil
}
// BackupFilePath returns a path for a backup identity file.
func BackupFilePath(dir string) string {
now := time.Now().UTC().Format(time.RFC3339)
return fmt.Sprintf("%s/%s-%s.bak", dir, FileName, now)
}