release(v4.9.0): relay-side encrypted blob primitive + SDK Profile namespace
Ships the Prism FR (encrypted-profile-storage-v4.9.md) as a generic relay-side encrypted blob primitive: deterministically-located, AEAD-sealed blobs keyed by a 32-byte slotId derived client-side via HKDF from the user's master key. Unlocks credential-only bootstrap of new devices into existing E2EE state — no QR, no physical access. Server: BlobStore interface + Memory/Sqlite/Postgres impls, createBlobRoutes for GET/PUT/DELETE /v1/blob/:slotId with TOFU pubkey auth and If-Match CAS (409/412 semantics). Mounted on the same Hono app as the inbox; SHADE_BLOB_PG_URL / SHADE_BLOB_DB_PATH / SHADE_DISABLE_BLOB env-var plumbing in standalone. SDK: createProfileNamespace high-level wrapper (HKDF derivation, random-nonce AEAD seal, slotId-bound AAD) + low-level BlobClient. Cross-platform test vectors in test-vectors/blob-storage.json. New errors: ConflictError (409), PreconditionFailedError (412). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
39
test-vectors/blob-storage.json
Normal file
39
test-vectors/blob-storage.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"version": 1,
|
||||
"description": "V4.9 — relay-side encrypted blob primitive: HKDF derivations from masterKey + per-app namespace string. Each (master, app) pair MUST reproduce the same slotId / blobKey / signingSeed / ownerPubkey across implementations. ownerPubkey = Ed25519.getPublicKey(signingSeed).",
|
||||
"kdf": [
|
||||
{
|
||||
"masterKey": "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20",
|
||||
"app": "prism-profile-v1",
|
||||
"slotIdInfo": "shade-blob-slot-v1:prism-profile-v1",
|
||||
"blobKeyInfo": "shade-blob-key-v1:prism-profile-v1",
|
||||
"signingSeedInfo": "shade-blob-sig-v1:prism-profile-v1",
|
||||
"slotId": "cee6fe19af3c3ad20f91382938cd05ccf7f314566209f5debad17d8427508323",
|
||||
"blobKey": "47ad8fc8fcb0f15ec75be95246e6040bb0674b1a9e4bc3cf7a2c3d1c1e57877b",
|
||||
"signingSeed": "0bb58f21b588b44f22d5837602c1ee0049e56f99df5241702b65e5de0a1a0dab",
|
||||
"ownerPubkey": "2be918c7af82278fb446bb3901e5a7691f5ac4123275d5e1b202882da2a637bc"
|
||||
},
|
||||
{
|
||||
"masterKey": "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20",
|
||||
"app": "test-namespace",
|
||||
"slotIdInfo": "shade-blob-slot-v1:test-namespace",
|
||||
"blobKeyInfo": "shade-blob-key-v1:test-namespace",
|
||||
"signingSeedInfo": "shade-blob-sig-v1:test-namespace",
|
||||
"slotId": "b10a7e64f9902f48bc566d48c09c0276cdad2dc9ad55d456374c02a8f160aa46",
|
||||
"blobKey": "9e140339142d23291f0f360f03072c66049cec2449994dce1b77a3aed43eeb37",
|
||||
"signingSeed": "feec2d85ba7320fe34940abca082f056d5fa7927d940b267d44ae24acb486773",
|
||||
"ownerPubkey": "94e8298ea69ba4b160934fb813ee3fa5b2a4254cc78cb3dd8339bdc7b68e660c"
|
||||
},
|
||||
{
|
||||
"masterKey": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||
"app": "prism-profile-v1",
|
||||
"slotIdInfo": "shade-blob-slot-v1:prism-profile-v1",
|
||||
"blobKeyInfo": "shade-blob-key-v1:prism-profile-v1",
|
||||
"signingSeedInfo": "shade-blob-sig-v1:prism-profile-v1",
|
||||
"slotId": "deffbe4e2934965ce63fff247331186579ff4ef13c867fa4597059c1d7047bfb",
|
||||
"blobKey": "f498052d24513dccbdf538f2b9c13e9d6519fb06ead58eb3dfadf6b92d94227a",
|
||||
"signingSeed": "e904e2f0f42297f16a29e636c43b9b72d57a49841ab0b9bfd29c03345e9f16d0",
|
||||
"ownerPubkey": "822609f6b07f78d4692bfe708c05ce2d4d3c4eb25cf84a16a9d9e900015b3ca0"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user