Documentation
¶
Overview ¶
Package uploads provides helpers for client-side-encrypted upload storage: the wire envelope shape and Seal / Parse / Open functions that mirror the browser's encryption.ts.
Index ¶
Examples ¶
Constants ¶
const EncryptionAlgorithm = "RSA_DECRYPT_OAEP_3072_SHA256"
EncryptionAlgorithm is the KMS algorithm string the client is hardcoded against. The org public key endpoint advertises this; SealEnvelope refuses to run against any other value rather than silently producing payloads no reader can decrypt.
Variables ¶
This section is empty.
Functions ¶
func OpenEnvelope ¶
func OpenEnvelope(payload string, unwrapAESKey func(wrappedKey []byte) (aesKey []byte, err error)) ([]byte, error)
OpenEnvelope is the inverse of SealEnvelope. The caller supplies an unwrap function that decrypts the RSA-OAEP-wrapped AES key (typically a KMS AsymmetricDecrypt call against the version recorded in env.KeyVersion); this package stays GCP-free.
The split lets break-glass / reader code reuse the same AES-GCM / base64 logic the upload side uses, without forcing every SDK consumer to pull in cloud.google.com/go/kms.
func SealEnvelope ¶
SealEnvelope produces the JSON-encoded payload string the upload service expects in the request's payload field. It mirrors the browser's hybrid AES-GCM / RSA-OAEP encryption minus the network call:
- Generate a fresh AES-256 key + 12-byte IV.
- AES-256-GCM encrypt the plaintext.
- Parse the SPKI PEM into an RSA public key.
- RSA-OAEP-SHA256 wrap the AES key.
- Base64-encode each binary field, marshal the envelope.
The algorithm parameter is the value advertised by the server's GetOrgPublicKey response; mismatch against the client's hardcoded EncryptionAlgorithm is a hard failure rather than silently producing payloads no reader can decrypt.
keyVersion is the CryptoKeyVersion (e.g. "1") that wrapped this envelope, recorded so post-rotation readers know which private key to call AsymmetricDecrypt against.
Types ¶
type Envelope ¶
type Envelope struct {
Ciphertext string `json:"ciphertext"`
EncryptedKey string `json:"encryptedKey"`
IV string `json:"iv"`
Timestamp string `json:"timestamp"`
// KeyVersion is the integer CryptoKeyVersion the client used for
// RSA-OAEP wrapping (matches Cloud KMS' version numbering, e.g. "1").
// Carried in the envelope so decrypters can pick the right version
// regardless of what's currently the published primary — required
// for any post-rotation read of pre-rotation blobs. Empty means "1"
// for backward compatibility with envelopes uploaded before this
// field existed.
KeyVersion string `json:"keyVersion,omitempty"`
}
Envelope is the wire shape the client encrypts and the server stores as-is. All binary fields are base64-encoded; the server treats the JSON-encoded envelope as opaque and never inspects its contents.
func ParseEnvelope ¶
ParseEnvelope decodes the JSON envelope without performing the RSA-OAEP/AES-GCM decryption. Useful on the upload path when the server wants to read non-sensitive metadata (Timestamp) without touching the plaintext.
Example ¶
ExampleParseEnvelope demonstrates decoding a JSON envelope without performing any cryptographic operations.
package main
import (
"fmt"
"chainguard.dev/sdk/uploads"
)
func main() {
payload := `{"ciphertext":"abc=","encryptedKey":"def=","iv":"ghi=","timestamp":"2024-01-01T00:00:00Z","keyVersion":"1"}`
env, err := uploads.ParseEnvelope(payload)
fmt.Println(err)
fmt.Println(env.KeyVersion)
fmt.Println(env.Timestamp)
}
Output: <nil> 1 2024-01-01T00:00:00Z
Example (Invalid) ¶
ExampleParseEnvelope_invalid demonstrates the error returned when the payload is not a valid JSON envelope.
package main
import (
"fmt"
"chainguard.dev/sdk/uploads"
)
func main() {
_, err := uploads.ParseEnvelope("not-json")
fmt.Println(err != nil)
}
Output: true