Add experimental send creation support

This commit is contained in:
Bernd Schoolmann 2024-02-04 19:34:09 +01:00
parent b297bf1ea3
commit d0e0d66509
No known key found for this signature in database
5 changed files with 276 additions and 0 deletions

30
agent/actions/send.go Normal file
View File

@ -0,0 +1,30 @@
package actions
import (
"context"
"fmt"
"github.com/quexten/goldwarden/agent/bitwarden"
"github.com/quexten/goldwarden/agent/config"
"github.com/quexten/goldwarden/agent/sockets"
"github.com/quexten/goldwarden/agent/vault"
"github.com/quexten/goldwarden/ipc/messages"
)
func handleCreateSend(msg messages.IPCMessage, cfg *config.Config, vault *vault.Vault, callingContext *sockets.CallingContext) (response messages.IPCMessage, err error) {
token, err := cfg.GetToken()
if err != nil {
return messages.IPCMessage{}, fmt.Errorf("error getting token: %w", err)
}
parsedMsg := messages.ParsePayload(msg).(messages.CreateSendRequest)
ctx := context.WithValue(context.TODO(), bitwarden.AuthToken{}, token.AccessToken)
_, err = bitwarden.CreateSend(ctx, cfg, vault, parsedMsg.Name, parsedMsg.Text)
response, err = messages.IPCMessageFromPayload(messages.CreateSendResponse{})
return
}
func init() {
AgentActionsRegistry.Register(messages.MessageTypeForEmptyPayload(messages.CreateSendRequest{}), ensureIsNotLocked(ensureIsLoggedIn(handleCreateSend)))
}

View File

@ -218,6 +218,20 @@ func EncryptWith(data []byte, encType EncStringType, key SymmetricEncryptionKey)
return s, nil
}
func EncryptWithToString(data []byte, encType EncStringType, key SymmetricEncryptionKey) (string, error) {
s, err := EncryptWith(data, encType, key)
if err != nil {
return "", err
}
marshalled, err := s.MarshalText()
if err != nil {
return "", err
}
return string(marshalled), nil
}
func GenerateAsymmetric(useMemguard bool) (AsymmetricEncryptionKey, error) {
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {

107
agent/bitwarden/send.go Normal file
View File

@ -0,0 +1,107 @@
package bitwarden
import (
"context"
"crypto/rand"
"crypto/sha256"
"io"
"time"
"github.com/quexten/goldwarden/agent/bitwarden/crypto"
"github.com/quexten/goldwarden/agent/config"
"github.com/quexten/goldwarden/agent/vault"
"golang.org/x/crypto/hkdf"
)
type SendFileMetadata struct {
FileName string `json:"fileName"`
Id string `json:"id"`
Size int `json:"size"`
SizeName string `json:"sizeName"`
}
type SendTextMetadata struct {
Hidden bool `json:"hidden"`
Response *string `json:"response"`
Text string `json:"text"`
}
type SendMetadata struct {
CreatorIdentifier string
ExpirationDate string
File SendFileMetadata
Id string
Name string
Object string
Text SendTextMetadata
Type int
}
type SendCreateRequest struct {
DeletionDate string `json:"deletionDate"`
Disabled bool `json:"disabled"`
ExpirationDate *string `json:"expirationDate"`
HideEmail bool `json:"hideEmail"`
Key string `json:"key"`
MaxAccessCount *int `json:"maxAccessCount"`
Name string `json:"name"`
Notes *string `json:"notes"`
Text SendTextMetadata `json:"text"`
Type int `json:"type"`
}
func CreateSend(ctx context.Context, cfg *config.Config, vault *vault.Vault, name string, text string) (SendMetadata, error) {
timestampIn14Days := time.Now().AddDate(0, 0, 14)
timestampIn14DaysStr := timestampIn14Days.Format("2006-01-02T15:04:05Z")
// generate 32 byte key
sendSourceKey := make([]byte, 32)
_, err := io.ReadFull(rand.Reader, sendSourceKey)
if err != nil {
return SendMetadata{}, err
}
encryptedSendSourceKey, err := crypto.EncryptWithToString(sendSourceKey, crypto.AesCbc256_HmacSha256_B64, vault.Keyring.GetAccountKey())
if err != nil {
return SendMetadata{}, err
}
sendUseKeyPairBytes := make([]byte, 64)
hkdf.New(sha256.New, sendSourceKey, []byte("bitwarden-send"), []byte("send")).Read(sendUseKeyPairBytes)
sendUseKeyPair, err := crypto.MemorySymmetricEncryptionKeyFromBytes(sendUseKeyPairBytes)
if err != nil {
return SendMetadata{}, err
}
encryptedName, err := crypto.EncryptWithToString([]byte(name), crypto.AesCbc256_HmacSha256_B64, sendUseKeyPair)
if err != nil {
return SendMetadata{}, err
}
encryptedText, err := crypto.EncryptWithToString([]byte(text), crypto.AesCbc256_HmacSha256_B64, sendUseKeyPair)
if err != nil {
return SendMetadata{}, err
}
sendRequest := SendCreateRequest{
DeletionDate: timestampIn14DaysStr,
Disabled: false,
HideEmail: false,
Key: encryptedSendSourceKey,
Name: encryptedName,
Text: SendTextMetadata{
Hidden: false,
Text: encryptedText,
},
Type: 0,
}
var result interface{}
err = authenticatedHTTPPost(ctx, cfg.ConfigFile.ApiUrl+"/sends", &result, sendRequest)
if err != nil {
return SendMetadata{}, err
}
return SendMetadata{}, nil
}

53
cmd/send.go Normal file
View File

@ -0,0 +1,53 @@
package cmd
import (
"fmt"
"github.com/quexten/goldwarden/ipc/messages"
"github.com/spf13/cobra"
)
var sendCmd = &cobra.Command{
Use: "send",
Short: "Commands for managing sends",
Long: `Commands for managing sends.`,
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
},
}
var sendCreateCmd = &cobra.Command{
Use: "create",
Short: "Uploads a Bitwarden send.",
Long: `Uploads a Bitwarden send.`,
Run: func(cmd *cobra.Command, args []string) {
loginIfRequired()
name, _ := cmd.Flags().GetString("name")
text, _ := cmd.Flags().GetString("text")
result, err := commandClient.SendToAgent(messages.CreateSendRequest{
Name: name,
Text: text,
})
if err != nil {
handleSendToAgentError(err)
return
}
switch result.(type) {
case messages.CreateSendResponse:
fmt.Println("Send created: " + result.(messages.CreateSendResponse).URL)
break
case messages.ActionResponse:
println("Error: " + result.(messages.ActionResponse).Message)
return
}
},
}
func init() {
rootCmd.AddCommand(sendCmd)
sendCmd.AddCommand(sendCreateCmd)
sendCreateCmd.Flags().StringP("name", "n", "", "Name of the send")
sendCreateCmd.Flags().StringP("text", "t", "", "Text of the send")
}

72
ipc/messages/send.go Normal file
View File

@ -0,0 +1,72 @@
package messages
import "encoding/json"
type GetSendRequest struct {
Name string
Text string
}
type GetSendResponse struct {
Found bool
Text string
}
type CreateSendRequest struct {
Name string
Text string
}
type CreateSendResponse struct {
URL string
}
type ListSendsRequest struct {
}
func init() {
registerPayloadParser(func(payload []byte) (interface{}, error) {
var req GetSendRequest
err := json.Unmarshal(payload, &req)
if err != nil {
panic("Unmarshal: " + err.Error())
}
return req, nil
}, GetSendRequest{})
registerPayloadParser(func(payload []byte) (interface{}, error) {
var req GetSendResponse
err := json.Unmarshal(payload, &req)
if err != nil {
panic("Unmarshal: " + err.Error())
}
return req, nil
}, GetSendResponse{})
registerPayloadParser(func(payload []byte) (interface{}, error) {
var req CreateSendRequest
err := json.Unmarshal(payload, &req)
if err != nil {
panic("Unmarshal: " + err.Error())
}
return req, nil
}, CreateSendRequest{})
registerPayloadParser(func(payload []byte) (interface{}, error) {
var req CreateSendResponse
err := json.Unmarshal(payload, &req)
if err != nil {
panic("Unmarshal: " + err.Error())
}
return req, nil
}, CreateSendResponse{})
registerPayloadParser(func(payload []byte) (interface{}, error) {
var req ListSendsRequest
err := json.Unmarshal(payload, &req)
if err != nil {
panic("Unmarshal: " + err.Error())
}
return req, nil
}, ListSendsRequest{})
}