cautpartener / client / src / lib / encryption.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
 * OrbitDB encryption hooks backed by the room's HKDF keys (PROTOCOL.md v2).
 *
 * `replication` encrypts the ENTIRE log entry (clock, writer key, identity
 * ref, signature, links, payload) before it becomes a content-addressed
 * block — the relay, bitswap observers, and anyone who merely learns the DB
 * address see opaque bytes. `data` additionally encrypts the payload inside
 * the entry (future-proofs a semi-trusted pinner that must validate entry
 * structure but never read content).
 *
 * Decrypt THROWS on failure — OrbitDB treats that as "reject this entry",
 * which is the whole access-control story: only secret-holders can produce
 * entries members accept (write:["*"] + decrypt-or-drop, v1 §3 transplanted).
 */

import type { RoomCrypto, Bytes } from "./crypto";

interface EncryptFns {
  encrypt(bytes: Uint8Array): Promise<Uint8Array>;
  decrypt(bytes: Uint8Array): Promise<Uint8Array>;
}

export interface LogEncryption {
  replication: EncryptFns;
  data: EncryptFns;
}

export async function makeLogEncryption(rc: RoomCrypto): Promise<LogEncryption> {
  const [entry, payload] = await Promise.all([rc.logEntryCipher(), rc.logPayloadCipher()]);
  const wrap = (cipher: { seal(d: Bytes): Promise<Bytes>; open(d: Bytes): Promise<Bytes | null> }): EncryptFns => ({
    encrypt: (bytes) => cipher.seal(asBytes(bytes)),
    decrypt: async (bytes) => {
      const pt = await cipher.open(asBytes(bytes));
      if (pt === null) throw new Error("Could not decrypt (not a room member or tampered block)");
      return pt;
    },
  });
  return { replication: wrap(entry), data: wrap(payload) };
}

function asBytes(u8: Uint8Array): Bytes {
  // dag-cbor hands us views into larger buffers; WebCrypto wants plain ones
  if (u8.byteOffset === 0 && u8.byteLength === u8.buffer.byteLength && u8.buffer instanceof ArrayBuffer) {
    return u8 as Bytes;
  }
  const copy = new Uint8Array(u8.byteLength);
  copy.set(u8);
  return copy;
}

static mirror of HEAD · about · clone: git clone https://cod-sursa.cautpartener.ro/cautpartener.git