This commit is contained in:
Samuel Lorch 2025-03-13 17:47:32 +01:00 committed by GitHub
commit 1456570c17
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 353 additions and 36 deletions

View file

@ -6,12 +6,11 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
"github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/gopenpgp/v3/crypto"
"github.com/google/go-querystring/query" "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 // 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) 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 // Enable Debug Logging
Debug bool Debug bool
} }
@ -67,6 +69,8 @@ func NewClient(httpClient *http.Client, UserAgent, BaseURL, UserPrivateKey, User
return nil, fmt.Errorf("Parsing Base URL: %w", err) 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 // Verify that the Given Privatekey and Password are valid and work Together if we were provieded one
if UserPrivateKey != "" { if UserPrivateKey != "" {
privateKeyObj, err := crypto.NewKeyFromArmored(UserPrivateKey) privateKeyObj, err := crypto.NewKeyFromArmored(UserPrivateKey)
@ -93,6 +97,7 @@ func NewClient(httpClient *http.Client, UserAgent, BaseURL, UserPrivateKey, User
userAgent: UserAgent, userAgent: UserAgent,
userPassword: []byte(UserPassword), userPassword: []byte(UserPassword),
userPrivateKey: UserPrivateKey, userPrivateKey: UserPrivateKey,
pgp: pgp,
} }
return c, err return c, err
} }
@ -150,7 +155,7 @@ func (c *Client) do(ctx context.Context, req *http.Request, v *APIResponse) (*ht
resp.Body.Close() resp.Body.Close()
}() }()
bodyBytes, err := ioutil.ReadAll(resp.Body) bodyBytes, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return resp, fmt.Errorf("Error Reading Resopnse Body: %w", err) return resp, fmt.Errorf("Error Reading Resopnse Body: %w", err)
} }
@ -231,3 +236,8 @@ func (c *Client) setMetadataTypeSettings(ctx context.Context) error {
} }
return nil 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 ( import (
"fmt" "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 // 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) { func (c *Client) EncryptMessage(message string) (string, error) {
if c.userPrivateKey == "" { key, err := c.getPrivateKey(c.userPrivateKey, c.userPassword)
return "", fmt.Errorf("Client has no Private Key") if err != nil {
} else if c.userPublicKey == "" { return "", fmt.Errorf("Get Private Key: %w", err)
return "", fmt.Errorf("Client has no Public Key")
} }
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 // 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) { func (c *Client) EncryptMessageWithPublicKey(publickey, message string) (string, error) {
if c.userPrivateKey == "" { key, err := c.getPrivateKey(c.userPrivateKey, c.userPassword)
return "", fmt.Errorf("Client has no Private Key") 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 // DecryptMessage decrypts a message using the users Private Key
func (c *Client) DecryptMessage(message string) (string, error) { func (c *Client) DecryptMessage(message string) (string, error) {
if c.userPrivateKey == "" { key, err := c.getPrivateKey(c.userPrivateKey, c.userPassword)
return "", fmt.Errorf("Client has no Private Key") 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 // 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.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
} }

19
api/foreign_model.go Normal file
View file

@ -0,0 +1,19 @@
package api
type ForeignModelTypes string
const (
ForeignModelTypesResource ForeignModelTypes = "Resource"
ForeignModelTypesSecret ForeignModelTypes = "Secret"
ForeignModelTypesFolder = "Folder"
ForeignModelTypesComment = "Comment"
ForeignModelTypesTag = "Tag"
)
func (s ForeignModelTypes) IsValid() bool {
switch s {
case ForeignModelTypesResource, ForeignModelTypesSecret, ForeignModelTypesFolder, ForeignModelTypesComment, ForeignModelTypesTag:
return true
}
return false
}

View file

@ -0,0 +1,61 @@
package api
import (
"context"
"encoding/json"
"fmt"
)
// MetadataSessionKey is a MetadataSessionKey
type MetadataSessionKey struct {
ID string `json:"id,omitempty"`
UserID string `json:"user_id,omitempty"`
Data string `json:"data,omitempty"`
Created Time `json:"created,omitempty"`
Modified Time `json:"modified,omitempty"`
}
// MetadataSessionKeyData is a MetadataSessionKeyData
type MetadataSessionKeyData struct {
// ObjectType Must always be PASSBOLT_SESSION_KEYS
ObjectType string `json:"object_type,omitempty"`
SessionKeys []MetadataSessionKeyDataElement `json:"session_keys,omitempty"`
}
// MetadataSessionKeyData is a MetadataSessionKeyData
type MetadataSessionKeyDataElement struct {
ForeignModel ForeignModelTypes `json:"foreign_model"`
ForeignID string `json:"foreign_id"`
SessionKey string `json:"session_key"`
Modified Time `json:"modified"`
}
// GetMetadataTypeSettings gets the Servers Settings about which Types to use
func (c *Client) GetMetadataSessionKeys(ctx context.Context) ([]MetadataSessionKey, error) {
msg, err := c.DoCustomRequest(ctx, "GET", "/metadata/session-keys.json", "v2", nil, nil)
if err != nil {
return nil, err
}
var metadataSessionKeys []MetadataSessionKey
err = json.Unmarshal(msg.Body, &metadataSessionKeys)
if err != nil {
return nil, err
}
return metadataSessionKeys, nil
}
// TODO add Create and Update
// DeleteSessionKey Deletes a Passbolt SessionKey
func (c *Client) DeleteSessionKey(ctx context.Context, sessionKeyID string) error {
err := checkUUIDFormat(sessionKeyID)
if err != nil {
return fmt.Errorf("Checking ID format: %w", err)
}
_, err = c.DoCustomRequest(ctx, "DELETE", "/metadata/session-keys/"+sessionKeyID+".json", "v2", nil, nil)
if err != nil {
return err
}
return nil
}

86
api/metadatakey.go Normal file
View file

@ -0,0 +1,86 @@
package api
import (
"context"
"encoding/json"
)
type MetadataKeyType string
const (
MetadataKeyTypeUserKey MetadataKeyType = "user_key"
MetadataKeyTypeSharedKey = "shared_key"
)
func (s MetadataKeyType) IsValid() bool {
switch s {
case MetadataKeyTypeUserKey, MetadataKeyTypeSharedKey:
return true
}
return false
}
// MetadataKey is a MetadataKey
type MetadataKey struct {
ID string `json:"id,omitempty"`
Fingerprint string `json:"fingerprint,omitempty"`
ArmoredKey string `json:"armored_key,omitempty"`
Created Time `json:"created,omitempty"`
Modified Time `json:"modified,omitempty"`
// These are always null? Used for Key Rotation?
//"expired": null,
//"deleted": null,
CreatedBy *string `json:"created_by,omitempty"`
ModifiedBy *string `json:"modified_by,omitempty"`
MetadataPrivateKeys []MetadataPrivateKey `json:"metadata_private_keys,omitempty"`
}
// MetadataPrivateKey is a MetadataPrivateKey
type MetadataPrivateKey struct {
ID string `json:"id,omitempty"`
MetadataKeyID string `json:"metadata_key_id,omitempty"`
UserID *string `json:"user_id,omitempty"` // TODO, is this nullable. The Docs says yes and no
Data string `json:"data,omitempty"`
Created Time `json:"created,omitempty"`
Modified Time `json:"modified,omitempty"`
CreatedBy *string `json:"created_by,omitempty"`
ModifiedBy *string `json:"modified_by,omitempty"`
}
// MetadataPrivateKeyData is a MetadataPrivateKeyData
type MetadataPrivateKeyData struct {
// ObjectType Must always be PASSBOLT_METADATA_PRIVATE_KEY
ObjectType string `json:"object_type,omitempty"`
// Domain Must be the Passbolt Server URL
Domain string `json:"domain,omitempty"`
Fingerprint string `json:"fingerprint,omitempty"`
ArmoredKey string `json:"armored_key,omitempty"`
// Passphrase must be Empty for Server Keys
Passphrase string `json:"passphrase,omitempty"`
}
// GetMetadataKeysOptions are all available query parameters
type GetMetadataKeysOptions struct {
FilterDeleted bool `url:"filter[deleted,omitempty"`
FilterExpired bool `url:"filter[expired,omitempty"`
ContainMetadataPrivateKeys bool `url:"contain[metadata_private_keys],omitempty"`
}
// GetMetadataKeys gets all Passbolt GetMetadataKeys
func (c *Client) GetMetadataKeys(ctx context.Context, opts *GetMetadataKeysOptions) ([]MetadataKey, error) {
msg, err := c.DoCustomRequest(ctx, "GET", "/metadata/keys.json", "v2", nil, opts)
if err != nil {
return nil, err
}
var metadataKeys []MetadataKey
err = json.Unmarshal(msg.Body, &metadataKeys)
if err != nil {
return nil, err
}
return metadataKeys, nil
}

View file

@ -9,6 +9,7 @@ import (
// Resource is a Resource. // Resource is a Resource.
// Warning: Since Passbolt v3 some fields here may not be populated as they may be in the Secret depending on the ResourceType, // Warning: Since Passbolt v3 some fields here may not be populated as they may be in the Secret depending on the ResourceType,
// for now the only Field like that is the Description. // for now the only Field like that is the Description.
// With Passbolt v5 it is now Possible that all Unencrypted User Supplied Fields are empty, and need to be decrypted from the Metadata Message
type Resource struct { type Resource struct {
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
Created *Time `json:"created,omitempty"` Created *Time `json:"created,omitempty"`
@ -27,8 +28,13 @@ type Resource struct {
FolderParentID string `json:"folder_parent_id,omitempty"` FolderParentID string `json:"folder_parent_id,omitempty"`
ResourceTypeID string `json:"resource_type_id,omitempty"` ResourceTypeID string `json:"resource_type_id,omitempty"`
ResourceType ResourceType `json:"resource_type,omitempty"` ResourceType ResourceType `json:"resource_type,omitempty"`
Secrets []Secret `json:"secrets,omitempty"`
Tags []Tag `json:"tags,omitempty"` MetadataKeyID string `json:"metadata_key_id,omitempty"`
MetadataKeyType MetadataKeyType `json:"metadata_key_type,omitempty"`
Metadata string `json:"metadata,omitempty"`
Secrets []Secret `json:"secrets,omitempty"`
Tags []Tag `json:"tags,omitempty"`
} }
// Tag is a Passbolt Password Tag // Tag is a Passbolt Password Tag
@ -48,6 +54,8 @@ type GetResourcesOptions struct {
// Parent Folder id // Parent Folder id
FilterHasParent []string `url:"filter[has-parent][],omitempty"` FilterHasParent []string `url:"filter[has-parent][],omitempty"`
FilterHasTag string `url:"filter[has-tag],omitempty"` FilterHasTag string `url:"filter[has-tag],omitempty"`
// TODO Are undescores correct heare?
MetadataKeyType MetadataKeyType `url:"filter[metadata_key_type],omitempty"`
ContainCreator bool `url:"contain[creator],omitempty"` ContainCreator bool `url:"contain[creator],omitempty"`
ContainFavorites bool `url:"contain[favorite],omitempty"` ContainFavorites bool `url:"contain[favorite],omitempty"`

View file

@ -24,6 +24,9 @@ type User struct {
GPGKey *GPGKey `json:"gpgKey,omitempty"` GPGKey *GPGKey `json:"gpgKey,omitempty"`
LastLoggedIn string `json:"last_logged_in,omitempty"` LastLoggedIn string `json:"last_logged_in,omitempty"`
Locale string `json:"locale,omitempty"` Locale string `json:"locale,omitempty"`
// Admin only, needs contains
MissingMetadataKeyIDs []string `json:"missing_metadata_key_ids,omitempty"`
} }
// Profile is a Profile // Profile is a Profile
@ -42,6 +45,8 @@ type GetUsersOptions struct {
FilterHasGroup []string `url:"filter[has-group][],omitempty"` FilterHasGroup []string `url:"filter[has-group][],omitempty"`
FilterHasAccess []string `url:"filter[has-access][],omitempty"` FilterHasAccess []string `url:"filter[has-access][],omitempty"`
FilterIsAdmin bool `url:"filter[is-admin],omitempty"` FilterIsAdmin bool `url:"filter[is-admin],omitempty"`
// Admin only, TODO are underscores correct?
MissingMetadataKeyIDs bool `url:"filter[missing_metadata_key_ids],omitempty"`
ContainLastLoggedIn bool `url:"contain[LastLoggedIn],omitempty"` ContainLastLoggedIn bool `url:"contain[LastLoggedIn],omitempty"`
} }
@ -82,9 +87,11 @@ func (c *Client) GetMe(ctx context.Context) (*User, error) {
// GetUser gets a Passbolt User // GetUser gets a Passbolt User
func (c *Client) GetUser(ctx context.Context, userID string) (*User, error) { func (c *Client) GetUser(ctx context.Context, userID string) (*User, error) {
err := checkUUIDFormat(userID) if userID != "me" {
if err != nil { err := checkUUIDFormat(userID)
return nil, fmt.Errorf("Checking ID format: %w", err) if err != nil {
return nil, fmt.Errorf("Checking ID format: %w", err)
}
} }
msg, err := c.DoCustomRequest(ctx, "GET", "/users/"+userID+".json", "v2", nil, nil) msg, err := c.DoCustomRequest(ctx, "GET", "/users/"+userID+".json", "v2", nil, nil)
if err != nil { if err != nil {
@ -101,9 +108,11 @@ func (c *Client) GetUser(ctx context.Context, userID string) (*User, error) {
// UpdateUser Updates a existing Passbolt User // UpdateUser Updates a existing Passbolt User
func (c *Client) UpdateUser(ctx context.Context, userID string, user User) (*User, error) { func (c *Client) UpdateUser(ctx context.Context, userID string, user User) (*User, error) {
err := checkUUIDFormat(userID) if userID != "me" {
if err != nil { err := checkUUIDFormat(userID)
return nil, fmt.Errorf("Checking ID format: %w", err) if err != nil {
return nil, fmt.Errorf("Checking ID format: %w", err)
}
} }
msg, err := c.DoCustomRequest(ctx, "PUT", "/users/"+userID+".json", "v2", user, nil) msg, err := c.DoCustomRequest(ctx, "PUT", "/users/"+userID+".json", "v2", user, nil)
if err != nil { if err != nil {
@ -119,11 +128,13 @@ func (c *Client) UpdateUser(ctx context.Context, userID string, user User) (*Use
// DeleteUser Deletes a Passbolt User // DeleteUser Deletes a Passbolt User
func (c *Client) DeleteUser(ctx context.Context, userID string) error { func (c *Client) DeleteUser(ctx context.Context, userID string) error {
err := checkUUIDFormat(userID) if userID != "me" {
if err != nil { err := checkUUIDFormat(userID)
return fmt.Errorf("Checking ID format: %w", err) if err != nil {
return fmt.Errorf("Checking ID format: %w", err)
}
} }
_, err = c.DoCustomRequest(ctx, "DELETE", "/users/"+userID+".json", "v2", nil, nil) _, err := c.DoCustomRequest(ctx, "DELETE", "/users/"+userID+".json", "v2", nil, nil)
if err != nil { if err != nil {
return err return err
} }

View file

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

1
go.mod
View file

@ -12,6 +12,7 @@ require (
require ( require (
github.com/ProtonMail/go-crypto v1.1.6 // indirect github.com/ProtonMail/go-crypto v1.1.6 // indirect
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // 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/cloudflare/circl v1.6.0 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
golang.org/x/crypto v0.35.0 // 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.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 h1:1jHlELwCR00qovx2B50DkL/FjYwt/P91RnlsqeOp2Hs=
github.com/ProtonMail/gopenpgp/v2 v2.8.3/go.mod h1:LiuOTbnJit8w9ZzOoLscj0kmdALY7hfoCVh5Qlb0bcg= 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/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.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE= github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE=

View file

@ -6,9 +6,6 @@ import (
"strings" "strings"
"github.com/passbolt/go-passbolt/api" "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 // 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 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 { if err != nil {
return "", fmt.Errorf("Generating Private Key: %w", err) return "", fmt.Errorf("Generating Private Key: %w", err)
} }
key, err := crypto.NewKeyFromArmoredReader(strings.NewReader(privateKey)) defer key.ClearPrivateParams()
if err != nil {
return "", fmt.Errorf("Reading Private Key: %w", err)
}
publicKey, err := key.GetArmoredPublicKey() publicKey, err := key.GetArmoredPublicKey()
if err != nil { if err != nil {
return "", fmt.Errorf("Get Public Key: %w", err) 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{ request := api.SetupCompleteRequest{
AuthenticationToken: api.AuthenticationToken{ AuthenticationToken: api.AuthenticationToken{
Token: token, Token: token,