@@ -6,14 +6,19 @@ import (
66 "crypto/ecdsa"
77 "crypto/ed25519"
88 "crypto/rand"
9+ "encoding/json"
910 "fmt"
1011 "maps"
1112 "time"
1213
14+ gethkeystore "github.com/ethereum/go-ethereum/accounts/keystore"
1315 "golang.org/x/crypto/curve25519"
16+ "google.golang.org/protobuf/proto"
1417
1518 "github.com/ethereum/go-ethereum/crypto"
19+
1620 "github.com/smartcontractkit/chainlink-common/keystore/internal"
21+ "github.com/smartcontractkit/chainlink-common/keystore/serialization"
1722)
1823
1924var (
@@ -51,9 +56,9 @@ type ImportKeysRequest struct {
5156}
5257
5358type ImportKeyRequest struct {
54- KeyName string
55- KeyType KeyType
56- Data [] byte
59+ KeyName string
60+ Data [] byte
61+ Password string
5762}
5863
5964type ImportKeysResponse struct {}
@@ -227,12 +232,90 @@ func (k *keystore) DeleteKeys(ctx context.Context, req DeleteKeysRequest) (Delet
227232 return DeleteKeysResponse {}, nil
228233}
229234
230- func (k * keystore ) ImportKeys (ctx context.Context , req ImportKeysRequest ) (ImportKeysResponse , error ) {
235+ func (ks * keystore ) ImportKeys (ctx context.Context , req ImportKeysRequest ) (ImportKeysResponse , error ) {
236+ ks .mu .Lock ()
237+ defer ks .mu .Unlock ()
238+
239+ ksCopy := maps .Clone (ks .keystore )
240+ for _ , keyReq := range req .Keys {
241+ if err := ValidKeyName (keyReq .KeyName ); err != nil {
242+ return ImportKeysResponse {}, fmt .Errorf ("%w: %s" , ErrInvalidKeyName , err )
243+ }
244+ if _ , ok := ksCopy [keyReq .KeyName ]; ok {
245+ return ImportKeysResponse {}, fmt .Errorf ("%w: %s" , ErrKeyAlreadyExists , keyReq .KeyName )
246+ }
247+ encData := gethkeystore.CryptoJSON {}
248+ err := json .Unmarshal (keyReq .Data , & encData )
249+ if err != nil {
250+ return ImportKeysResponse {}, fmt .Errorf ("key = %s, failed to unmarshal encrypted import data: %w" , keyReq .KeyName , err )
251+ }
252+ decData , err := gethkeystore .DecryptDataV3 (encData , keyReq .Password )
253+ if err != nil {
254+ return ImportKeysResponse {}, fmt .Errorf ("key = %s, failed to decrypt key: %w" , keyReq .KeyName , err )
255+ }
256+ keypb := & serialization.Key {}
257+ err = proto .Unmarshal (decData , keypb )
258+ if err != nil {
259+ return ImportKeysResponse {}, fmt .Errorf ("key = %s, failed to unmarshal key: %w" , keyReq .KeyName , err )
260+ }
261+ pkRaw := internal .NewRaw (keypb .PrivateKey )
262+ keyType := KeyType (keypb .KeyType )
263+ publicKey , err := publicKeyFromPrivateKey (pkRaw , keyType )
264+ if err != nil {
265+ return ImportKeysResponse {}, fmt .Errorf ("key = %s, failed to get public key from private key: %w" , keyReq .KeyName , err )
266+ }
267+ metadata := keypb .Metadata
268+ // The proto compiler sets empty slices to nil during the serialization (https://github.com/golang/protobuf/issues/1348).
269+ // We set metadata back to empty slice to be consistent with the Create method which initializes it as such.
270+ if metadata == nil {
271+ metadata = []byte {}
272+ }
273+ ksCopy [keyReq .KeyName ] = newKey (keyType , pkRaw , publicKey , time .Unix (keypb .CreatedAt , 0 ), metadata )
274+ }
275+ // Persist it to storage.
276+ if err := ks .save (ctx , ksCopy ); err != nil {
277+ return ImportKeysResponse {}, fmt .Errorf ("failed to save keystore: %w" , err )
278+ }
279+ // If we succeed to save, update the in memory keystore.
280+ ks .keystore = ksCopy
231281 return ImportKeysResponse {}, nil
232282}
233283
234- func (k * keystore ) ExportKeys (ctx context.Context , req ExportKeysRequest ) (ExportKeysResponse , error ) {
235- return ExportKeysResponse {}, nil
284+ func (ks * keystore ) ExportKeys (_ context.Context , req ExportKeysRequest ) (ExportKeysResponse , error ) {
285+ ks .mu .RLock ()
286+ defer ks .mu .RUnlock ()
287+
288+ result := ExportKeysResponse {}
289+ for _ , keyReq := range req .Keys {
290+ key , ok := ks .keystore [keyReq .KeyName ]
291+ if ! ok {
292+ return ExportKeysResponse {}, fmt .Errorf ("%w: %s" , ErrKeyNotFound , keyReq .KeyName )
293+ }
294+ keypb := & serialization.Key {
295+ Name : keyReq .KeyName ,
296+ KeyType : string (key .keyType ),
297+ PrivateKey : internal .Bytes (key .privateKey ),
298+ CreatedAt : key .createdAt .Unix (),
299+ Metadata : key .metadata ,
300+ }
301+ serialized , err := proto .Marshal (keypb )
302+ if err != nil {
303+ return ExportKeysResponse {}, fmt .Errorf ("key = %s, failed to marshal key: %w" , keyReq .KeyName , err )
304+ }
305+ encData , err := gethkeystore .EncryptDataV3 (serialized , []byte (keyReq .Enc .Password ), keyReq .Enc .ScryptParams .N , keyReq .Enc .ScryptParams .P )
306+ if err != nil {
307+ return ExportKeysResponse {}, fmt .Errorf ("key = %s, failed to encrypt key: %w" , keyReq .KeyName , err )
308+ }
309+ encDataBytes , err := json .Marshal (encData )
310+ if err != nil {
311+ return ExportKeysResponse {}, fmt .Errorf ("key = %s, failed to marshal encrypted key: %w" , keyReq .KeyName , err )
312+ }
313+ result .Keys = append (result .Keys , ExportKeyResponse {
314+ KeyName : keyReq .KeyName ,
315+ Data : encDataBytes ,
316+ })
317+ }
318+ return result , nil
236319}
237320
238321func (ks * keystore ) SetMetadata (ctx context.Context , req SetMetadataRequest ) (SetMetadataResponse , error ) {
0 commit comments