@@ -21,15 +21,19 @@ const EXT_PEM = ".pem"
2121
2222// KeyChainDir is a directory-based keychain.
2323type KeyChainDir struct {
24- mem ndn.KeyChain
25- path string
24+ state * keyChainState
25+ path string
26+ files map [string ]string
27+ refs map [string ]int
2628}
2729
2830// NewKeyChainDir creates a new in-memory keychain.
2931func NewKeyChainDir (path string , pubStore ndn.Store ) (ndn.KeyChain , error ) {
3032 kc := & KeyChainDir {
31- mem : NewKeyChainMem (pubStore ),
32- path : path ,
33+ state : newKeyChainState (pubStore ),
34+ path : path ,
35+ files : make (map [string ]string ),
36+ refs : make (map [string ]int ),
3337 }
3438
3539 // Create directory if it doesn't exist
@@ -55,15 +59,55 @@ func NewKeyChainDir(path string, pubStore ndn.Store) (ndn.KeyChain, error) {
5559 }
5660
5761 filename := filepath .Join (path , entry .Name ())
62+ written := make (map [string ]struct {})
5863 content , err := os .ReadFile (filename )
5964 if err != nil {
6065 log .Warn (kc , "Failed to read keychain entry" , "file" , filename , "err" , err )
6166 continue
6267 }
6368
64- err = InsertFile ( kc . mem , content )
69+ signers , certs , err := sec . DecodeFile ( content )
6570 if err != nil {
66- log .Error (kc , "Failed to insert keychain entries" , "file" , filename , "err" , err )
71+ log .Error (kc , "Failed to parse keychain entry" , "file" , filename , "err" , err )
72+ continue
73+ }
74+
75+ // Normalize keys and certs for hashed names
76+ for _ , wire := range certs {
77+ if err := kc .state .insertCert (wire ); err != nil {
78+ log .Error (kc , "Failed to insert keychain certificate" , "file" , filename , "err" , err )
79+ continue
80+ }
81+ path , err := kc .writeFile (wire , EXT_CERT )
82+ if err != nil {
83+ log .Error (kc , "Failed to persist keychain certificate" , "file" , filename , "err" , err )
84+ continue
85+ }
86+ written [path ] = struct {}{}
87+ }
88+
89+ for _ , signer := range signers {
90+ if err := kc .state .insertKey (signer ); err != nil {
91+ log .Error (kc , "Failed to insert keychain key" , "file" , filename , "err" , err )
92+ continue
93+ }
94+ secret , err := sig .MarshalSecret (signer )
95+ if err != nil {
96+ log .Error (kc , "Failed to marshal keychain key" , "file" , filename , "err" , err )
97+ continue
98+ }
99+ path , err := kc .writeFile (secret .Join (), EXT_KEY )
100+ if err != nil {
101+ log .Error (kc , "Failed to persist keychain key" , "file" , filename , "err" , err )
102+ continue
103+ }
104+ written [path ] = struct {}{}
105+ }
106+
107+ if len (written ) > 0 {
108+ if _ , ok := written [filename ]; ! ok {
109+ _ = os .Remove (filename )
110+ }
67111 }
68112 }
69113
@@ -77,22 +121,22 @@ func (kc *KeyChainDir) String() string {
77121
78122// (AI GENERATED DESCRIPTION): Returns the underlying in‑memory store that backs the KeyChainDir.
79123func (kc * KeyChainDir ) Store () ndn.Store {
80- return kc .mem . Store ()
124+ return kc .state . pubStore
81125}
82126
83127// (AI GENERATED DESCRIPTION): Returns a slice of all identities currently stored in the key‑chain directory.
84128func (kc * KeyChainDir ) Identities () []ndn.KeyChainIdentity {
85- return kc .mem .Identities ()
129+ return kc .state .Identities ()
86130}
87131
88132// (AI GENERATED DESCRIPTION): Retrieves and returns the identity that matches the specified name from the keychain directory’s in‑memory store.
89133func (kc * KeyChainDir ) IdentityByName (name enc.Name ) ndn.KeyChainIdentity {
90- return kc .mem .IdentityByName (name )
134+ return kc .state .IdentityByName (name )
91135}
92136
93137// (AI GENERATED DESCRIPTION): Adds a signer to the in‑memory key chain and writes its secret key to disk in a file with the key extension.
94138func (kc * KeyChainDir ) InsertKey (signer ndn.Signer ) error {
95- err := kc .mem . InsertKey (signer )
139+ err := kc .state . insertKey (signer )
96140 if err != nil {
97141 return err
98142 }
@@ -102,29 +146,136 @@ func (kc *KeyChainDir) InsertKey(signer ndn.Signer) error {
102146 return err
103147 }
104148
105- return kc .writeFile (secret .Join (), EXT_KEY )
149+ _ , err = kc .writeFile (secret .Join (), EXT_KEY )
150+ return err
106151}
107152
108153// (AI GENERATED DESCRIPTION): Inserts the given certificate (in wire format) into the in‑memory key chain and writes it to disk with the certificate file extension.
109154func (kc * KeyChainDir ) InsertCert (wire []byte ) error {
110- err := kc .mem .InsertCert (wire )
155+ err := kc .state .insertCert (wire )
156+ if err != nil {
157+ return err
158+ }
159+
160+ _ , err = kc .writeFile (wire , EXT_CERT )
161+ return err
162+ }
163+
164+ // DeleteKey removes a key and its certificates from the keychain and disk.
165+ func (kc * KeyChainDir ) DeleteKey (keyName enc.Name ) error {
166+ idName , err := sec .GetIdentityFromKeyName (keyName )
167+ if err != nil {
168+ return err
169+ }
170+
171+ id := kc .state .IdentityByName (idName )
172+ if id == nil {
173+ return enc.ErrNotFound {Key : keyName .String ()}
174+ }
175+
176+ var signer ndn.Signer
177+ for _ , key := range id .Keys () {
178+ if key .KeyName ().Equal (keyName ) {
179+ signer = key .Signer ()
180+ break
181+ }
182+ }
183+ if signer == nil {
184+ return enc.ErrNotFound {Key : keyName .String ()}
185+ }
186+
187+ secret , err := sig .MarshalSecret (signer )
188+ if err != nil {
189+ return err
190+ }
191+
192+ // collect certificate wires before removal
193+ certWires := make ([][]byte , 0 )
194+ for _ , cert := range kc .state .CertNames () {
195+ if keyName .IsPrefix (cert ) {
196+ wire , err := kc .Store ().Get (cert , false )
197+ if err != nil {
198+ return err
199+ }
200+ if wire != nil {
201+ certWires = append (certWires , wire )
202+ }
203+ }
204+ }
205+
206+ if err := kc .state .deleteKey (keyName ); err != nil {
207+ return err
208+ }
209+
210+ if err := kc .deleteFile (secret .Join (), EXT_KEY ); err != nil {
211+ return err
212+ }
213+ for _ , wire := range certWires {
214+ if err := kc .deleteFile (wire , EXT_CERT ); err != nil {
215+ return err
216+ }
217+ }
218+ return nil
219+ }
220+
221+ // DeleteCert removes a certificate from the keychain and disk.
222+ func (kc * KeyChainDir ) DeleteCert (name enc.Name ) error {
223+ wire , err := kc .Store ().Get (name , false )
111224 if err != nil {
112225 return err
113226 }
227+ if wire == nil {
228+ return enc.ErrNotFound {Key : name .String ()}
229+ }
230+
231+ if err := kc .state .deleteCert (name ); err != nil {
232+ return err
233+ }
114234
115- return kc .writeFile (wire , EXT_CERT )
235+ return kc .deleteFile (wire , EXT_CERT )
116236}
117237
118238// (AI GENERATED DESCRIPTION): Writes the given binary data to a PEM‑encoded file named after its SHA‑256 hash (plus the supplied extension) in the keychain directory, with permissions set to 0600.
119- func (kc * KeyChainDir ) writeFile (wire []byte , ext string ) error {
239+ func (kc * KeyChainDir ) writeFile (wire []byte , ext string ) ( string , error ) {
120240 hash := sha256 .Sum256 (wire )
121- filename := hex .EncodeToString (hash [:])
122- path := filepath .Join (kc .path , filename + ext )
241+ filename := hex .EncodeToString (hash [:]) + ext
242+ path := filepath .Join (kc .path , filename )
123243
124244 str , err := sec .PemEncode (wire )
125245 if err != nil {
246+ return "" , err
247+ }
248+
249+ if err := os .WriteFile (path , str , 0600 ); err != nil {
250+ return "" , err
251+ }
252+
253+ kc .files [filename ] = path
254+ kc .refs [path ]++
255+ return path , nil
256+ }
257+
258+ func (kc * KeyChainDir ) deleteFile (wire []byte , ext string ) error {
259+ hash := sha256 .Sum256 (wire )
260+ filename := hex .EncodeToString (hash [:]) + ext
261+ path , ok := kc .files [filename ]
262+ if ! ok {
263+ path = filepath .Join (kc .path , filename )
264+ }
265+
266+ if count , ok := kc .refs [path ]; ok {
267+ if count > 1 {
268+ kc .refs [path ] = count - 1
269+ delete (kc .files , filename )
270+ return nil
271+ }
272+ delete (kc .refs , path )
273+ }
274+
275+ if err := os .Remove (path ); err != nil && ! os .IsNotExist (err ) {
126276 return err
127277 }
128278
129- return os .WriteFile (path , str , 0600 )
279+ delete (kc .files , filename )
280+ return nil
130281}
0 commit comments