Compare commits

...

6 commits

6 changed files with 156 additions and 24 deletions

View file

@ -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
}

View file

@ -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
}

View file

@ -5,7 +5,7 @@ import (
"fmt"
"strings"
"github.com/ProtonMail/gopenpgp/v2/crypto"
"github.com/ProtonMail/gopenpgp/v3/crypto"
"github.com/google/uuid"
)

1
go.mod
View file

@ -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

2
go.sum
View file

@ -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=

View file

@ -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,