From a7816003be56ee0b12e0405dc1f341f2fc3a5490 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 13 Mar 2025 17:38:03 +0100 Subject: [PATCH 01/10] go.mod Update gopenpgp to v3 --- go.mod | 1 + go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index 64273dd..5c98b95 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( require ( github.com/ProtonMail/go-crypto v1.1.6 // indirect github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect + github.com/ProtonMail/gopenpgp/v3 v3.1.3 // indirect github.com/cloudflare/circl v1.6.0 // indirect github.com/pkg/errors v0.9.1 // indirect golang.org/x/crypto v0.35.0 // indirect diff --git a/go.sum b/go.sum index 24212a3..ba1847f 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,8 @@ github.com/ProtonMail/gopenpgp/v2 v2.7.5 h1:STOY3vgES59gNgoOt2w0nyHBjKViB/qSg7Nj github.com/ProtonMail/gopenpgp/v2 v2.7.5/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g= github.com/ProtonMail/gopenpgp/v2 v2.8.3 h1:1jHlELwCR00qovx2B50DkL/FjYwt/P91RnlsqeOp2Hs= github.com/ProtonMail/gopenpgp/v2 v2.8.3/go.mod h1:LiuOTbnJit8w9ZzOoLscj0kmdALY7hfoCVh5Qlb0bcg= +github.com/ProtonMail/gopenpgp/v3 v3.1.3 h1:nxUd0Na4MeElx0sA1t6U8/IxmjmCv3MKnTJGhEUK+qY= +github.com/ProtonMail/gopenpgp/v3 v3.1.3/go.mod h1:Ve9JYzwGau9DT0F9C9gsuEBU/T3Zbk0j1/+mPpWBogc= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= From 1c4ccb28ac25d77a28e1cf53872b1d777bf4c2bb Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 13 Mar 2025 17:38:43 +0100 Subject: [PATCH 02/10] Client update to gopenpgp v3 --- api/client.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/api/client.go b/api/client.go index 001fce3..e51ee08 100644 --- a/api/client.go +++ b/api/client.go @@ -6,12 +6,11 @@ import ( "encoding/json" "fmt" "io" - "io/ioutil" "net/http" "net/url" "path" - "github.com/ProtonMail/gopenpgp/v2/crypto" + "github.com/ProtonMail/gopenpgp/v3/crypto" "github.com/google/go-querystring/query" ) @@ -39,6 +38,9 @@ type Client struct { // You need to Return the Cookie that Passbolt expects to verify you MFA, usually it is called passbolt_mfa MFACallback func(ctx context.Context, c *Client, res *APIResponse) (http.Cookie, error) + // gopengpg Handler, allow for custom settings in the future + pgp *crypto.PGPHandle + // Enable Debug Logging Debug bool } @@ -67,6 +69,8 @@ func NewClient(httpClient *http.Client, UserAgent, BaseURL, UserPrivateKey, User return nil, fmt.Errorf("Parsing Base URL: %w", err) } + pgp := crypto.PGP() + // Verify that the Given Privatekey and Password are valid and work Together if we were provieded one if UserPrivateKey != "" { privateKeyObj, err := crypto.NewKeyFromArmored(UserPrivateKey) @@ -93,6 +97,7 @@ func NewClient(httpClient *http.Client, UserAgent, BaseURL, UserPrivateKey, User userAgent: UserAgent, userPassword: []byte(UserPassword), userPrivateKey: UserPrivateKey, + pgp: pgp, } return c, err } @@ -150,7 +155,7 @@ func (c *Client) do(ctx context.Context, req *http.Request, v *APIResponse) (*ht resp.Body.Close() }() - bodyBytes, err := ioutil.ReadAll(resp.Body) + bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return resp, fmt.Errorf("Error Reading Resopnse Body: %w", err) } @@ -231,3 +236,8 @@ func (c *Client) setMetadataTypeSettings(ctx context.Context) error { } return nil } + +// GetPGPHandle Gets the Gopgenpgp Handler +func (c *Client) GetPGPHandle() *crypto.PGPHandle { + return c.pgp +} From e189e2babc6e67971f0c6d111087d6583eec4dc8 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 13 Mar 2025 17:39:12 +0100 Subject: [PATCH 03/10] encryption.go update to gopenpgp v3 --- api/encryption.go | 133 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 121 insertions(+), 12 deletions(-) diff --git a/api/encryption.go b/api/encryption.go index e87537e..ee5e40c 100644 --- a/api/encryption.go +++ b/api/encryption.go @@ -3,33 +3,142 @@ package api import ( "fmt" - "github.com/ProtonMail/gopenpgp/v2/helper" + "github.com/ProtonMail/gopenpgp/v3/crypto" ) // EncryptMessage encrypts a message using the users public key and then signes the message using the users private key func (c *Client) EncryptMessage(message string) (string, error) { - if c.userPrivateKey == "" { - return "", fmt.Errorf("Client has no Private Key") - } else if c.userPublicKey == "" { - return "", fmt.Errorf("Client has no Public Key") + key, err := c.getPrivateKey(c.userPrivateKey, c.userPassword) + if err != nil { + return "", fmt.Errorf("Get Private Key: %w", err) } - return helper.EncryptSignMessageArmored(c.userPublicKey, c.userPrivateKey, c.userPassword, message) + + defer key.ClearPrivateParams() + + encHandle, err := c.pgp.Encryption().SigningKey(key).Recipient(key).New() + if err != nil { + return "", fmt.Errorf("New Encryptor: %w", err) + } + + defer encHandle.ClearPrivateParams() + + encMessage, err := encHandle.Encrypt([]byte(message)) + if err != nil { + return "", fmt.Errorf("Encrypt Message: %w", err) + } + + encArmor, err := encMessage.Armor() + if err != nil { + return "", fmt.Errorf("Armor Message: %w", err) + } + return encArmor, nil } // EncryptMessageWithPublicKey encrypts a message using the provided public key and then signes the message using the users private key func (c *Client) EncryptMessageWithPublicKey(publickey, message string) (string, error) { - if c.userPrivateKey == "" { - return "", fmt.Errorf("Client has no Private Key") + key, err := c.getPrivateKey(c.userPrivateKey, c.userPassword) + if err != nil { + return "", fmt.Errorf("Get Private Key: %w", err) } - return helper.EncryptSignMessageArmored(publickey, c.userPrivateKey, c.userPassword, message) + + defer key.ClearPrivateParams() + + publicKey, err := crypto.NewKeyFromArmored(publickey) + if err != nil { + return "", fmt.Errorf("Get Public Key: %w", err) + } + + encHandle, err := c.pgp.Encryption().SigningKey(key).Recipient(publicKey).New() + if err != nil { + return "", fmt.Errorf("New Encryptor: %w", err) + } + + defer encHandle.ClearPrivateParams() + + encMessage, err := encHandle.Encrypt([]byte(message)) + if err != nil { + return "", fmt.Errorf("Encrypt Message: %w", err) + } + + encArmor, err := encMessage.Armor() + if err != nil { + return "", fmt.Errorf("Armor Message: %w", err) + } + return encArmor, nil } // DecryptMessage decrypts a message using the users Private Key func (c *Client) DecryptMessage(message string) (string, error) { - if c.userPrivateKey == "" { - return "", fmt.Errorf("Client has no Private Key") + key, err := c.getPrivateKey(c.userPrivateKey, c.userPassword) + if err != nil { + return "", fmt.Errorf("Get Private Key: %w", err) } + + defer key.ClearPrivateParams() + + decHandle, err := c.pgp.Decryption().DecryptionKey(key).New() + if err != nil { + return "", fmt.Errorf("New Decryptor: %w", err) + } + + defer decHandle.ClearPrivateParams() + + res, err := decHandle.Decrypt([]byte(message), crypto.Armor) + if err != nil { + return "", fmt.Errorf("Decrypt Message: %w", err) + } + // We cant Verify the signature as we don't store other users public keys locally and don't know which user did encrypt it //return helper.DecryptVerifyMessageArmored(c.userPublicKey, c.userPrivateKey, c.userPassword, message) - return helper.DecryptMessageArmored(c.userPrivateKey, c.userPassword, message) + + return res.String(), nil +} + +// TODO change []byte to string? +func (c *Client) DecryptMessageWithPrivateKey(privateKey string, passphrase []byte, ciphertextArmored string) (string, error) { + key, err := c.getPrivateKey(privateKey, passphrase) + if err != nil { + return "", fmt.Errorf("Get Private Key: %w", err) + } + + defer key.ClearPrivateParams() + + decHandle, err := c.pgp.Decryption().DecryptionKey(key).New() + if err != nil { + return "", fmt.Errorf("New Decryptor: %w", err) + } + + defer decHandle.ClearPrivateParams() + + res, err := decHandle.Decrypt([]byte(ciphertextArmored), crypto.Armor) + if err != nil { + return "", fmt.Errorf("Decrypt: %w", err) + } + + return string(res.Bytes()), nil +} + +func (c *Client) getPrivateKey(privateKey string, passphrase []byte) (*crypto.Key, error) { + if c.userPrivateKey == "" { + return nil, fmt.Errorf("Client has no Private Key") + } + + key, err := crypto.NewKeyFromArmored(privateKey) + if err != nil { + return nil, fmt.Errorf("Key From Armored: %w", err) + } + + locked, err := key.IsLocked() + if err != nil { + return nil, fmt.Errorf("Is Key Locked: %w", err) + } + + if locked { + unlocked, err := key.Unlock(passphrase) + if err != nil { + return nil, fmt.Errorf("Unlock Key: %w", err) + } + return unlocked, nil + } + return key, nil } From 53a858f817078c3e673a0af03161020b43754421 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 13 Mar 2025 17:39:34 +0100 Subject: [PATCH 04/10] verify.go update to gopenpgp v3 --- api/verify.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/verify.go b/api/verify.go index 89a15b8..74e35b6 100644 --- a/api/verify.go +++ b/api/verify.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/ProtonMail/gopenpgp/v2/crypto" + "github.com/ProtonMail/gopenpgp/v3/crypto" "github.com/google/uuid" ) From cca7de6c07d8562b53061bcffce641f28bbb63e1 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Thu, 13 Mar 2025 17:40:16 +0100 Subject: [PATCH 05/10] Setup update to gopenpgp v3 --- helper/setup.go | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/helper/setup.go b/helper/setup.go index 7fdc006..37e202c 100644 --- a/helper/setup.go +++ b/helper/setup.go @@ -6,9 +6,6 @@ import ( "strings" "github.com/passbolt/go-passbolt/api" - - "github.com/ProtonMail/gopenpgp/v2/crypto" - "github.com/ProtonMail/gopenpgp/v2/helper" ) // ParseInviteUrl Parses a Passbolt Invite URL into a user id and token @@ -31,21 +28,34 @@ func SetupAccount(ctx context.Context, c *api.Client, userID, token, password st keyName := install.Profile.FirstName + " " + install.Profile.LastName + " " + install.Username - privateKey, err := helper.GenerateKey(keyName, install.Username, []byte(password), "rsa", 4096) + pgp := c.GetPGPHandle() + + keyHandler := pgp.KeyGeneration().AddUserId(keyName, install.Username).New() + + key, err := keyHandler.GenerateKey() if err != nil { return "", fmt.Errorf("Generating Private Key: %w", err) } - key, err := crypto.NewKeyFromArmoredReader(strings.NewReader(privateKey)) - if err != nil { - return "", fmt.Errorf("Reading Private Key: %w", err) - } + defer key.ClearPrivateParams() publicKey, err := key.GetArmoredPublicKey() if err != nil { return "", fmt.Errorf("Get Public Key: %w", err) } + lockedKey, err := pgp.LockKey(key, []byte(password)) + if err != nil { + return "", fmt.Errorf("Locking Private Key: %w", err) + } + + defer lockedKey.ClearPrivateParams() + + privateKey, err := lockedKey.Armor() + if err != nil { + return "", fmt.Errorf("Get Private Key: %w", err) + } + request := api.SetupCompleteRequest{ AuthenticationToken: api.AuthenticationToken{ Token: token, From 8d6a423d9c97e518e66d7be436bd84c9d5848119 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 14 Mar 2025 14:07:01 +0100 Subject: [PATCH 06/10] Update Login to gopenpgpv3, Simplify login. --- api/auth.go | 34 +++++++--------------------------- api/client.go | 4 ++-- 2 files changed, 9 insertions(+), 29 deletions(-) diff --git a/api/auth.go b/api/auth.go index 406f81a..93e7732 100644 --- a/api/auth.go +++ b/api/auth.go @@ -7,9 +7,6 @@ import ( "net/http" "net/url" "strings" - - "github.com/ProtonMail/gopenpgp/v2/crypto" - "github.com/ProtonMail/gopenpgp/v2/helper" ) // Login is used for login @@ -33,15 +30,14 @@ func (c *Client) CheckSession(ctx context.Context) bool { func (c *Client) Login(ctx context.Context) error { c.csrfToken = http.Cookie{} - if c.userPrivateKey == "" { - return fmt.Errorf("Client has no Private Key") - } - - privateKeyObj, err := crypto.NewKeyFromArmored(c.userPrivateKey) + privateKey, err := c.getPrivateKey(c.userPrivateKey, c.userPassword) if err != nil { return fmt.Errorf("Parsing User Private Key: %w", err) } - data := Login{&GPGAuth{KeyID: privateKeyObj.GetFingerprint()}} + + defer privateKey.ClearPrivateParams() + + data := Login{&GPGAuth{KeyID: privateKey.GetFingerprint()}} res, _, err := c.DoCustomRequestAndReturnRawResponse(ctx, "POST", "/auth/login.json", "v2", data, nil) if err != nil && !strings.Contains(err.Error(), "Error API JSON Response Status: Message: The authentication failed.") { @@ -62,7 +58,7 @@ func (c *Client) Login(ctx context.Context) error { } encAuthToken = strings.ReplaceAll(encAuthToken, "\\ ", " ") - authToken, err := helper.DecryptMessageArmored(c.userPrivateKey, c.userPassword, encAuthToken) + authToken, err := c.DecryptMessage(encAuthToken) if err != nil { return fmt.Errorf("Decrypting User Auth Token: %w", err) } @@ -104,29 +100,13 @@ func (c *Client) Login(ctx context.Context) error { return fmt.Errorf("Getting CSRF Token: %w", err) } - // Get Users Own Public Key from Server + // Get Users ID from Server var user User err = json.Unmarshal(apiMsg.Body, &user) if err != nil { return fmt.Errorf("Parsing User 'Me' JSON from API Request: %w", err) } - // Validate that this Publickey that the Server gave us actually Matches our Privatekey - randomString := randStringBytesRmndr(50) - armor, err := helper.EncryptMessageArmored(user.GPGKey.ArmoredKey, randomString) - if err != nil { - return fmt.Errorf("Encryping PublicKey Validation Message: %w", err) - } - decrypted, err := helper.DecryptMessageArmored(c.userPrivateKey, c.userPassword, armor) - if err != nil { - return fmt.Errorf("Decrypting PublicKey Validation Message (you might be getting Hacked): %w", err) - } - if decrypted != randomString { - return fmt.Errorf("Decrypted PublicKey Validation Message does not Match Original (you might be getting Hacked): %w", err) - } - - // Insert PublicKey into Client after checking it to Prevent ignored errors leading to proceeding with a potentially Malicious PublicKey - c.userPublicKey = user.GPGKey.ArmoredKey c.userID = user.ID // after Login, fetch MetadataTypeSettings to finish the Client Setup diff --git a/api/client.go b/api/client.go index e51ee08..e97a134 100644 --- a/api/client.go +++ b/api/client.go @@ -24,10 +24,10 @@ type Client struct { csrfToken http.Cookie mfaToken http.Cookie - // for some reason []byte is used for Passwords in gopenpgp instead of string like they do for keys... + // userPublicKey has been removed since it can be gotten from the private userPrivateKey + userPassword []byte userPrivateKey string - userPublicKey string userID string // Server Settings Determining which Resource Types we can use From 9206b8ecbd4ac2821a23c3f749969e8bc8f12500 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 14 Mar 2025 14:10:23 +0100 Subject: [PATCH 07/10] go tidy, Remove gopenpgp/v2 --- go.mod | 5 +---- go.sum | 66 ---------------------------------------------------------- 2 files changed, 1 insertion(+), 70 deletions(-) diff --git a/go.mod b/go.mod index 5c98b95..464635a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/passbolt/go-passbolt go 1.23.0 require ( - github.com/ProtonMail/gopenpgp/v2 v2.8.3 + github.com/ProtonMail/gopenpgp/v3 v3.1.3 github.com/google/go-querystring v1.1.0 github.com/google/uuid v1.6.0 github.com/santhosh-tekuri/jsonschema v1.2.4 @@ -11,11 +11,8 @@ require ( require ( github.com/ProtonMail/go-crypto v1.1.6 // indirect - github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect - github.com/ProtonMail/gopenpgp/v3 v3.1.3 // indirect github.com/cloudflare/circl v1.6.0 // indirect github.com/pkg/errors v0.9.1 // indirect golang.org/x/crypto v0.35.0 // indirect golang.org/x/sys v0.30.0 // indirect - golang.org/x/text v0.22.0 // indirect ) diff --git a/go.sum b/go.sum index ba1847f..be3dc48 100644 --- a/go.sum +++ b/go.sum @@ -1,26 +1,11 @@ -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= -github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= -github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= -github.com/ProtonMail/gopenpgp/v2 v2.7.5 h1:STOY3vgES59gNgoOt2w0nyHBjKViB/qSg7NjbQWPJkA= -github.com/ProtonMail/gopenpgp/v2 v2.7.5/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g= -github.com/ProtonMail/gopenpgp/v2 v2.8.3 h1:1jHlELwCR00qovx2B50DkL/FjYwt/P91RnlsqeOp2Hs= -github.com/ProtonMail/gopenpgp/v2 v2.8.3/go.mod h1:LiuOTbnJit8w9ZzOoLscj0kmdALY7hfoCVh5Qlb0bcg= github.com/ProtonMail/gopenpgp/v3 v3.1.3 h1:nxUd0Na4MeElx0sA1t6U8/IxmjmCv3MKnTJGhEUK+qY= github.com/ProtonMail/gopenpgp/v3 v3.1.3/go.mod h1:Ve9JYzwGau9DT0F9C9gsuEBU/T3Zbk0j1/+mPpWBogc= -github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= -github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= -github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -33,63 +18,12 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis= github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From c7a0de783da2344285fd3995eb53a3fd1773618e Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Fri, 14 Mar 2025 14:53:52 +0100 Subject: [PATCH 08/10] encryption.go improvements, Add DecryptMessageWithSessionKey --- api/encryption.go | 57 ++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/api/encryption.go b/api/encryption.go index ee5e40c..3186f0d 100644 --- a/api/encryption.go +++ b/api/encryption.go @@ -69,53 +69,33 @@ func (c *Client) EncryptMessageWithPublicKey(publickey, message string) (string, // DecryptMessage decrypts a message using the users Private Key func (c *Client) DecryptMessage(message string) (string, error) { - key, err := c.getPrivateKey(c.userPrivateKey, c.userPassword) - if err != nil { - return "", fmt.Errorf("Get Private Key: %w", err) - } - - defer key.ClearPrivateParams() - - decHandle, err := c.pgp.Decryption().DecryptionKey(key).New() - if err != nil { - return "", fmt.Errorf("New Decryptor: %w", err) - } - - defer decHandle.ClearPrivateParams() - - res, err := decHandle.Decrypt([]byte(message), crypto.Armor) - if err != nil { - return "", fmt.Errorf("Decrypt Message: %w", err) - } - - // We cant Verify the signature as we don't store other users public keys locally and don't know which user did encrypt it - //return helper.DecryptVerifyMessageArmored(c.userPublicKey, c.userPrivateKey, c.userPassword, message) - - return res.String(), nil + message, _, err := c.DecryptMessageWithPrivateKeyAndReturnSessionKey(c.userPrivateKey, c.userPassword, message) + return message, err } -// TODO change []byte to string? -func (c *Client) DecryptMessageWithPrivateKey(privateKey string, passphrase []byte, ciphertextArmored string) (string, error) { +// DecryptMessageWithPrivateKey Decrypts a Message using the Provided Private Key +// Returns the Session key so that it can be saved in a cache +func (c *Client) DecryptMessageWithPrivateKeyAndReturnSessionKey(privateKey string, passphrase []byte, ciphertextArmored string) (string, *crypto.SessionKey, error) { key, err := c.getPrivateKey(privateKey, passphrase) if err != nil { - return "", fmt.Errorf("Get Private Key: %w", err) + return "", nil, fmt.Errorf("Get Private Key: %w", err) } defer key.ClearPrivateParams() - decHandle, err := c.pgp.Decryption().DecryptionKey(key).New() + decHandle, err := c.pgp.Decryption().DecryptionKey(key).RetrieveSessionKey().New() if err != nil { - return "", fmt.Errorf("New Decryptor: %w", err) + return "", nil, fmt.Errorf("New Decryptor: %w", err) } defer decHandle.ClearPrivateParams() res, err := decHandle.Decrypt([]byte(ciphertextArmored), crypto.Armor) if err != nil { - return "", fmt.Errorf("Decrypt: %w", err) + return "", nil, fmt.Errorf("Decrypt: %w", err) } - return string(res.Bytes()), nil + return res.String(), res.SessionKey(), nil } func (c *Client) getPrivateKey(privateKey string, passphrase []byte) (*crypto.Key, error) { @@ -142,3 +122,20 @@ func (c *Client) getPrivateKey(privateKey string, passphrase []byte) (*crypto.Ke } return key, nil } + +// DecryptMessageWithSessionKey Decrypts a Message using the Provided Session Key +func (c *Client) DecryptMessageWithSessionKey(sessionKey *crypto.SessionKey, ciphertextArmored string) (string, error) { + decHandle, err := c.pgp.Decryption().SessionKey(sessionKey).New() + if err != nil { + return "", fmt.Errorf("New Decryptor: %w", err) + } + + defer decHandle.ClearPrivateParams() + + res, err := decHandle.Decrypt([]byte(ciphertextArmored), crypto.Armor) + if err != nil { + return "", fmt.Errorf("Decrypt: %w", err) + } + + return res.String(), nil +} From 118dd9624b84921814be20728bbbe3739cddbf0a Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 19 Mar 2025 14:40:16 +0100 Subject: [PATCH 09/10] store userprivatekey in clients as crypto.key instead of armor --- api/auth.go | 9 +-------- api/client.go | 28 ++++++++-------------------- api/encryption.go | 43 +++++++++++-------------------------------- api/verify.go | 8 +------- 4 files changed, 21 insertions(+), 67 deletions(-) diff --git a/api/auth.go b/api/auth.go index 93e7732..19da61b 100644 --- a/api/auth.go +++ b/api/auth.go @@ -30,14 +30,7 @@ func (c *Client) CheckSession(ctx context.Context) bool { func (c *Client) Login(ctx context.Context) error { c.csrfToken = http.Cookie{} - privateKey, err := c.getPrivateKey(c.userPrivateKey, c.userPassword) - if err != nil { - return fmt.Errorf("Parsing User Private Key: %w", err) - } - - defer privateKey.ClearPrivateParams() - - data := Login{&GPGAuth{KeyID: privateKey.GetFingerprint()}} + data := Login{&GPGAuth{KeyID: c.userPrivateKey.GetFingerprint()}} res, _, err := c.DoCustomRequestAndReturnRawResponse(ctx, "POST", "/auth/login.json", "v2", data, nil) if err != nil && !strings.Contains(err.Error(), "Error API JSON Response Status: Message: The authentication failed.") { diff --git a/api/client.go b/api/client.go index e97a134..3acf427 100644 --- a/api/client.go +++ b/api/client.go @@ -26,8 +26,7 @@ type Client struct { // userPublicKey has been removed since it can be gotten from the private userPrivateKey - userPassword []byte - userPrivateKey string + userPrivateKey *crypto.Key userID string // Server Settings Determining which Resource Types we can use @@ -71,23 +70,13 @@ func NewClient(httpClient *http.Client, UserAgent, BaseURL, UserPrivateKey, User pgp := crypto.PGP() - // Verify that the Given Privatekey and Password are valid and work Together if we were provieded one + var unlockedKey *crypto.Key = nil if UserPrivateKey != "" { - privateKeyObj, err := crypto.NewKeyFromArmored(UserPrivateKey) + key, err := GetPrivateKeyFromArmor(UserPrivateKey, []byte(UserPassword)) if err != nil { - return nil, fmt.Errorf("Unable to Create Key From UserPrivateKey string: %w", err) + return nil, fmt.Errorf("Get Private Key: %w", err) } - unlockedKeyObj, err := privateKeyObj.Unlock([]byte(UserPassword)) - if err != nil { - return nil, fmt.Errorf("Unable to Unlock UserPrivateKey using UserPassword: %w", err) - } - privateKeyRing, err := crypto.NewKeyRing(unlockedKeyObj) - if err != nil { - return nil, fmt.Errorf("Unable to Create a new Key Ring using the unlocked UserPrivateKey: %w", err) - } - - // Cleanup Secrets - privateKeyRing.ClearPrivateParams() + unlockedKey = key } // Create Client Object @@ -95,8 +84,7 @@ func NewClient(httpClient *http.Client, UserAgent, BaseURL, UserPrivateKey, User httpClient: httpClient, baseURL: u, userAgent: UserAgent, - userPassword: []byte(UserPassword), - userPrivateKey: UserPrivateKey, + userPrivateKey: unlockedKey, pgp: pgp, } return c, err @@ -207,11 +195,11 @@ func (c *Client) GetPublicKey(ctx context.Context) (string, string, error) { } // Lets get the actual Fingerprint instead of trusting the Server - privateKeyObj, err := crypto.NewKeyFromArmored(c.userPrivateKey) + serverKey, err := crypto.NewKeyFromArmored(body.Keydata) if err != nil { return "", "", fmt.Errorf("Parsing Server Key: %w", err) } - return body.Keydata, privateKeyObj.GetFingerprint(), nil + return body.Keydata, serverKey.GetFingerprint(), nil } // setMetadataTypeSettings Gets and configures the Client to use the Types the Server wants us to use diff --git a/api/encryption.go b/api/encryption.go index 3186f0d..1ce2c8f 100644 --- a/api/encryption.go +++ b/api/encryption.go @@ -8,14 +8,7 @@ import ( // EncryptMessage encrypts a message using the users public key and then signes the message using the users private key func (c *Client) EncryptMessage(message string) (string, error) { - key, err := c.getPrivateKey(c.userPrivateKey, c.userPassword) - if err != nil { - return "", fmt.Errorf("Get Private Key: %w", err) - } - - defer key.ClearPrivateParams() - - encHandle, err := c.pgp.Encryption().SigningKey(key).Recipient(key).New() + encHandle, err := c.pgp.Encryption().SigningKey(c.userPrivateKey).Recipient(c.userPrivateKey).New() if err != nil { return "", fmt.Errorf("New Encryptor: %w", err) } @@ -36,19 +29,12 @@ func (c *Client) EncryptMessage(message string) (string, error) { // EncryptMessageWithPublicKey encrypts a message using the provided public key and then signes the message using the users private key func (c *Client) EncryptMessageWithPublicKey(publickey, message string) (string, error) { - key, err := c.getPrivateKey(c.userPrivateKey, c.userPassword) - if err != nil { - return "", fmt.Errorf("Get Private Key: %w", err) - } - - defer key.ClearPrivateParams() - publicKey, err := crypto.NewKeyFromArmored(publickey) if err != nil { return "", fmt.Errorf("Get Public Key: %w", err) } - encHandle, err := c.pgp.Encryption().SigningKey(key).Recipient(publicKey).New() + encHandle, err := c.pgp.Encryption().SigningKey(c.userPrivateKey).Recipient(publicKey).New() if err != nil { return "", fmt.Errorf("New Encryptor: %w", err) } @@ -68,29 +54,26 @@ func (c *Client) EncryptMessageWithPublicKey(publickey, message string) (string, } // DecryptMessage decrypts a message using the users Private Key -func (c *Client) DecryptMessage(message string) (string, error) { - message, _, err := c.DecryptMessageWithPrivateKeyAndReturnSessionKey(c.userPrivateKey, c.userPassword, message) +func (c *Client) DecryptMessage(armoredCiphertext string) (string, error) { + message, _, err := c.DecryptMessageWithPrivateKeyAndReturnSessionKey(c.userPrivateKey, armoredCiphertext) return message, err } // DecryptMessageWithPrivateKey Decrypts a Message using the Provided Private Key // Returns the Session key so that it can be saved in a cache -func (c *Client) DecryptMessageWithPrivateKeyAndReturnSessionKey(privateKey string, passphrase []byte, ciphertextArmored string) (string, *crypto.SessionKey, error) { - key, err := c.getPrivateKey(privateKey, passphrase) - if err != nil { - return "", nil, fmt.Errorf("Get Private Key: %w", err) - } +func (c *Client) DecryptMessageWithPrivateKeyAndReturnSessionKey(privateKey *crypto.Key, armoredCiphertext string) (string, *crypto.SessionKey, error) { - defer key.ClearPrivateParams() - - decHandle, err := c.pgp.Decryption().DecryptionKey(key).RetrieveSessionKey().New() + decHandle, err := c.pgp.Decryption(). + DecryptionKey(privateKey). + RetrieveSessionKey(). + New() if err != nil { return "", nil, fmt.Errorf("New Decryptor: %w", err) } defer decHandle.ClearPrivateParams() - res, err := decHandle.Decrypt([]byte(ciphertextArmored), crypto.Armor) + res, err := decHandle.Decrypt([]byte(armoredCiphertext), crypto.Armor) if err != nil { return "", nil, fmt.Errorf("Decrypt: %w", err) } @@ -98,11 +81,7 @@ func (c *Client) DecryptMessageWithPrivateKeyAndReturnSessionKey(privateKey stri return res.String(), res.SessionKey(), nil } -func (c *Client) getPrivateKey(privateKey string, passphrase []byte) (*crypto.Key, error) { - if c.userPrivateKey == "" { - return nil, fmt.Errorf("Client has no Private Key") - } - +func GetPrivateKeyFromArmor(privateKey string, passphrase []byte) (*crypto.Key, error) { key, err := crypto.NewKeyFromArmored(privateKey) if err != nil { return nil, fmt.Errorf("Key From Armored: %w", err) diff --git a/api/verify.go b/api/verify.go index 74e35b6..8ed16e2 100644 --- a/api/verify.go +++ b/api/verify.go @@ -5,7 +5,6 @@ import ( "fmt" "strings" - "github.com/ProtonMail/gopenpgp/v3/crypto" "github.com/google/uuid" ) @@ -44,15 +43,10 @@ func (c *Client) SetupServerVerification(ctx context.Context) (string, string, e // VerifyServer verifys that the Server is still the same one as during the Setup, Only works before login func (c *Client) VerifyServer(ctx context.Context, token, encToken string) error { - privateKeyObj, err := crypto.NewKeyFromArmored(c.userPrivateKey) - if err != nil { - return fmt.Errorf("Parsing User Private Key: %w", err) - } - data := GPGVerifyContainer{ Req: GPGVerify{ Token: encToken, - KeyID: privateKeyObj.GetFingerprint(), + KeyID: c.userPrivateKey.GetFingerprint(), }, } raw, _, err := c.DoCustomRequestAndReturnRawResponse(ctx, "POST", "/auth/verify.json", "v2", data, nil) From 3376e521b94c8dc4e53c07b8adf4aacc345828f9 Mon Sep 17 00:00:00 2001 From: Samuel Lorch Date: Wed, 19 Mar 2025 15:34:28 +0100 Subject: [PATCH 10/10] Always Copy User Private Key to prevent it getting wiped --- api/client.go | 1 + api/encryption.go | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/api/client.go b/api/client.go index 3acf427..0ce0701 100644 --- a/api/client.go +++ b/api/client.go @@ -26,6 +26,7 @@ type Client struct { // userPublicKey has been removed since it can be gotten from the private userPrivateKey + // be sure to make a copy since using ClearPrivateParams on a handler also wipes the key... userPrivateKey *crypto.Key userID string diff --git a/api/encryption.go b/api/encryption.go index 1ce2c8f..c39eee3 100644 --- a/api/encryption.go +++ b/api/encryption.go @@ -8,7 +8,12 @@ import ( // EncryptMessage encrypts a message using the users public key and then signes the message using the users private key func (c *Client) EncryptMessage(message string) (string, error) { - encHandle, err := c.pgp.Encryption().SigningKey(c.userPrivateKey).Recipient(c.userPrivateKey).New() + key, err := c.userPrivateKey.Copy() + if err != nil { + return "", fmt.Errorf("Get Private Key Copy: %w", err) + } + + encHandle, err := c.pgp.Encryption().SigningKey(key).Recipient(c.userPrivateKey).New() if err != nil { return "", fmt.Errorf("New Encryptor: %w", err) } @@ -34,7 +39,12 @@ func (c *Client) EncryptMessageWithPublicKey(publickey, message string) (string, return "", fmt.Errorf("Get Public Key: %w", err) } - encHandle, err := c.pgp.Encryption().SigningKey(c.userPrivateKey).Recipient(publicKey).New() + key, err := c.userPrivateKey.Copy() + if err != nil { + return "", fmt.Errorf("Get Private Key Copy: %w", err) + } + + encHandle, err := c.pgp.Encryption().SigningKey(key).Recipient(publicKey).New() if err != nil { return "", fmt.Errorf("New Encryptor: %w", err) } @@ -55,7 +65,12 @@ func (c *Client) EncryptMessageWithPublicKey(publickey, message string) (string, // DecryptMessage decrypts a message using the users Private Key func (c *Client) DecryptMessage(armoredCiphertext string) (string, error) { - message, _, err := c.DecryptMessageWithPrivateKeyAndReturnSessionKey(c.userPrivateKey, armoredCiphertext) + key, err := c.userPrivateKey.Copy() + if err != nil { + return "", fmt.Errorf("Get Private Key Copy: %w", err) + } + + message, _, err := c.DecryptMessageWithPrivateKeyAndReturnSessionKey(key, armoredCiphertext) return message, err }