A calendar integration needs to keep an access token and recover it when sending
requests. Encryption lets you store that token as ciphertext, bytes that
require the secret key to recover the original value. Arkoren’s
crypto.Encrypter also checks integrity: changed ciphertext fails authentication
instead of becoming trusted application data.
Choosing Encryption
Use encryption when your application needs the original value back. Use password hashing for login passwords, which you only need to verify. A secret store protects the encryption key outside your application data; it does not replace encryption of that data. If a value may remain readable but must resist changes, consider a signature, as used by secure links.
Ciphertext authentication does not authorize the caller, establish freshness, or prevent replay. Apply authorization before exposing a decrypt path. A compromised application with access to its keys can still read the values it protects.
Encrypting Values
Assume enc is a configured *crypto.Encrypter, from
arkoren.dev/core/service/crypto. Drivers owns construction;
Encryption Keys owns persistent keys and rotation. Focused snippets
keep error results visible and omit routine propagation; handle each error
before using its result.
After your calendar callback has validated the provider response, keep the recoverable credentials in a named application type:
type CalendarCredentials struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}
With credentials holding that value, encrypt it before handing it to storage:
sealed, err := enc.Encrypt(credentials)
Encrypt JSON-encodes the value, then encrypts those bytes with the current
driver. Encoding can fail before encryption happens. On success, sealed is
binary ciphertext, not JSON or text. Each encryption uses a fresh random nonce,
so encrypting the same credentials again produces a different result.
For an already-encoded payload named payload, use EncryptRaw(payload)
instead. It skips JSON encoding. Neither raw operation changes its input, and
its returned bytes do not alias the input.
Reading Values
After selecting a stored connection the caller may access, decode its ciphertext into the same application shape:
credentials, err := enc.Decrypt[CalendarCredentials](sealed)
if err != nil {
return err
}
Only use credentials after the error check. Decryption authenticates the
ciphertext before JSON decoding. A successful result restores the credential
fields; it does not validate provider token expiry or permission to use them.
Match raw encryption with raw decryption:
payload, err := enc.DecryptRaw(sealedPayload)
Here sealedPayload was produced by EncryptRaw. DecryptRaw returns the
authenticated bytes without interpreting their format. Keep the encoding and
application schema compatible for as long as stored values exist; see
Storing Data.
Handling Failures
With either shipped driver, truncated, altered, wrong-key, or wrong-context
ciphertext produces an error wrapping crypto.ErrInvalidCiphertext. The error
does not distinguish tampering from a missing historical key. If previous
drivers are configured, the facade tries them before returning a failure.
Import standard-library errors to classify an invalid stored credential
without including its contents in a user-facing error:
if errors.Is(err, crypto.ErrInvalidCiphertext) {
return errors.New("calendar credentials are unavailable; reconnect the calendar")
}
Reject invalid client-supplied ciphertext. For stored application data, investigate key availability, cipher choice, authenticated context, and corruption rather than silently replacing it with empty credentials. Do not log keys, plaintext, or whole ciphertext values while investigating; inspect native error causes before exporting them to logs.
Authenticated bytes can still fail JSON decoding into the requested type.
That is a decoding error, not ErrInvalidCiphertext; keep the original stored
value for recovery. A stopped driver returns crypto.ErrEncrypterClosed, which
indicates a lifecycle error rather than invalid user data. A usable previous
driver can still decrypt even if the current driver has stopped; stopping one
driver does not disable the entire facade.
For all operations, see the crypto API reference.