Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion yb-support-tool/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,14 @@ func Upload(email string, caseNum int, files []string) error {

}

fmt.Printf("File uploads complete\nFinalizing Package...\n")
fmt.Printf("File uploads complete\n")

fmt.Println("Uploading keycodes...")
if err := p.UploadKeycodes(); err != nil {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker: wondering whether a keycode upload failure should be fatal here. If SendSafely has no PGP public keys configured for this package (empty PublicKeys slice), UploadKeycodes returns nil and we proceed to finalize. That seems right. But if there are recipients and all keycodes fail, we abort before FinalizePackage. Is that the intended behavior, or would it be better to finalize anyway and let the caller decide? I don't know the SendSafely semantics well enough to say.

return fmt.Errorf("Unable to upload keycodes: %s", err)
}

fmt.Println("Finalizing Package...")
err = p.FinalizePackage()
if err != nil {
return fmt.Errorf("Unable to finalize package: %s", err)
Expand Down
763 changes: 763 additions & 0 deletions yb-support-tool/go_debug.txt

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions yb-support-tool/sendsafelyuploader/encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"io"

"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/armor"
"github.com/ProtonMail/go-crypto/openpgp/packet"
"golang.org/x/crypto/pbkdf2"
)
Expand Down Expand Up @@ -63,3 +64,32 @@ func (p *Package) Encrypt(unencryptedFilePart io.Reader) (*bytes.Buffer, error)
passphrase := p.Info.ServerSecret + p.Uploader.ClientSecret
return encrypt([]byte(passphrase), unencryptedFilePart)
}

func EncryptKeycode(keyCode string, publicKeyArmored string) (string, error) {
keyRing, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(publicKeyArmored))
if err != nil {
return "", fmt.Errorf("failed to read public key: %w", err)
}

var encryptConfig packet.Config
encryptConfig.DefaultCipher = packet.CipherAES256
encryptConfig.DefaultHash = crypto.SHA256

buf := new(bytes.Buffer)
armorWriter, err := armor.Encode(buf, "PGP MESSAGE", nil)
if err != nil {
return "", fmt.Errorf("failed to create armor encoder: %w", err)
}

w, err := openpgp.Encrypt(armorWriter, keyRing, nil, nil, &encryptConfig)
if err != nil {
return "", fmt.Errorf("failed to create PGP encryptor: %w", err)
}
if _, err := io.WriteString(w, keyCode); err != nil {
return "", fmt.Errorf("failed to write keycode: %w", err)
}
w.Close()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

w.Close() and armorWriter.Close() (line 92) return errors that are dropped here. In practice these write to a bytes.Buffer which cannot fail, so this is probably safe. The existing encrypt() above does the same thing. Would it make sense to check both and return an error, at least for defensive consistency? Or is the pattern intentional given the buffer-backed writer?

armorWriter.Close()

return buf.String(), nil
}
18 changes: 17 additions & 1 deletion yb-support-tool/sendsafelyuploader/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,27 @@ type SSResponse struct {
}

type FinalizeInfo struct {
NeedsLink bool `json:"needsLink"`
NeedsLink bool `json:"needsLink"`
Recipients []Recipient `json:"recipients"`

SSResponse
}

type PublicKey struct {
ID string `json:"id"`
Key string `json:"key"`
}

type PublicKeysResponse struct {
PublicKeys []PublicKey `json:"publicKeys"`
SSResponse
}

type Recipient struct {
RecipientID string `json:"recipientId"`
Email string `json:"email"`
}

type HostedDropzoneInfo struct {
Success string `json:"success"`
Data string `json:"data"`
Expand Down
75 changes: 75 additions & 0 deletions yb-support-tool/sendsafelyuploader/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,81 @@ func (p *Package) MarkFileComplete(file *File) error {
return nil
}

func (p *Package) GetPublicKeys() (*PublicKeysResponse, error) {
endpoint := fmt.Sprintf("/drop-zone/v2.0/package/%s/public-keys/", p.Info.PackageCode)

body, err := p.Uploader.sendRequest(http.MethodGet, endpoint, nil)
if err != nil {
return nil, err
}

var resp PublicKeysResponse
if err := json.Unmarshal(body, &resp); err != nil {
return nil, err
}
if resp.Response != "SUCCESS" {
return nil, fmt.Errorf("failed to get public keys: %s", resp.Message)
}
return &resp, nil
}

func (p *Package) UploadKeycode(publicKeyID, encryptedKeycode string) error {
endpoint := fmt.Sprintf("/drop-zone/v2.0/package/%s/link/%s/", p.Info.PackageCode, publicKeyID)

reqBody := map[string]string{
"keycode": encryptedKeycode,
}

var reqBuf bytes.Buffer
enc := json.NewEncoder(&reqBuf)
enc.SetEscapeHTML(false)
if err := enc.Encode(reqBody); err != nil {
return err
}

body, err := p.Uploader.sendRequest(http.MethodPut, endpoint, reqBuf.Bytes(), withReqJSONHeader())
if err != nil {
return err
}

var resp SSResponse
if err := json.Unmarshal(body, &resp); err != nil {
return err
}
if resp.Response != "SUCCESS" {
return fmt.Errorf("%s", resp.Message)
}
return nil
}

func (p *Package) UploadKeycodes() error {
pubKeys, err := p.GetPublicKeys()
if err != nil {
return fmt.Errorf("failed to get public keys: %w", err)
}

var lastErr error
for _, pk := range pubKeys.PublicKeys {
encrypted, err := EncryptKeycode(p.Uploader.ClientSecret, pk.Key)
if err != nil {
lastErr = fmt.Errorf("failed to encrypt keycode for key '%s': %w", pk.ID, err)
fmt.Fprintf(os.Stderr, "Warning: %v\n", lastErr)
continue
}

if err := p.UploadKeycode(pk.ID, encrypted); err != nil {
lastErr = fmt.Errorf("failed to upload keycode for key '%s': %w", pk.ID, err)
fmt.Fprintf(os.Stderr, "Warning: %v\n", lastErr)
continue
}
}

if lastErr != nil {
return lastErr
}
return nil
}

// FinalizePackage marks the package complete and returns a link to the package,
// or the error if package finalization did not succeed. Sets Package.URL on success
func (p *Package) FinalizePackage() error {
Expand Down
Binary file added yb-support-tool/yb-support-tool
Binary file not shown.