Rename signatory -> signer and recipient -> sealer
This commit is contained in:
@@ -20,7 +20,7 @@ THIS IS WORK IN PROGRESS
|
||||
- boolean
|
||||
- number
|
||||
- string
|
||||
- stringly-encoded CoJSON identifiers & data (`CoValueID`, `AgentID`, `SessionID`, `SignatoryID`, `SignatorySecret`, `Signature`, `RecipientID`, `RecipientSecret`, `Sealed`, `Hash`, `ShortHash`, `KeySecret`, `KeyID`, `Encrypted`, `Role`)
|
||||
- stringly-encoded CoJSON identifiers & data (`CoValueID`, `AgentID`, `SessionID`, `SignerID`, `SignerSecret`, `Signature`, `SealerID`, `SealerSecret`, `Sealed`, `Hash`, `ShortHash`, `KeySecret`, `KeyID`, `Encrypted`, `Role`)
|
||||
|
||||
- array
|
||||
- object
|
||||
@@ -29,7 +29,7 @@ THIS IS WORK IN PROGRESS
|
||||
- CoMap (`string` → `Immutable`, last-writer-wins per key)
|
||||
- Team (`AgentID` → `Role`)
|
||||
- CoList (`Immutable[]`, addressable positions, insertAfter semantics)
|
||||
- Agent (`{signatoryID, recipientID}[]`)
|
||||
- Agent (`{signerID, sealerID}[]`)
|
||||
- CoStream (independent per-session streams of `Immutable`s)
|
||||
- Static (single addressable `Immutable`)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CoValueHeader } from './coValue.js';
|
||||
import { CoValueID } from './contentType.js';
|
||||
import { AgentSecret, RecipientID, RecipientSecret, SignatoryID, SignatorySecret, getAgentID, getAgentRecipientID, getAgentRecipientSecret, getAgentSignatoryID, getAgentSignatorySecret } from './crypto.js';
|
||||
import { AgentSecret, SealerID, SealerSecret, SignerID, SignerSecret, getAgentID, getAgentSealerID, getAgentSealerSecret, getAgentSignerID, getAgentSignerSecret } from './crypto.js';
|
||||
import { AgentID } from './ids.js';
|
||||
import { CoMap, LocalNode } from './index.js';
|
||||
import { Team, TeamContent } from './permissions.js';
|
||||
@@ -24,7 +24,7 @@ export class Account extends Team {
|
||||
}
|
||||
|
||||
getCurrentAgentID(): AgentID {
|
||||
const agents = this.teamMap.keys().filter((k): k is AgentID => k.startsWith("recipient_"));
|
||||
const agents = this.teamMap.keys().filter((k): k is AgentID => k.startsWith("sealer_"));
|
||||
|
||||
if (agents.length !== 1) {
|
||||
throw new Error("Expected exactly one agent in account, got " + agents.length);
|
||||
@@ -39,10 +39,10 @@ export interface GeneralizedControlledAccount {
|
||||
agentSecret: AgentSecret;
|
||||
|
||||
currentAgentID: () => AgentID;
|
||||
currentSignatoryID: () => SignatoryID;
|
||||
currentSignatorySecret: () => SignatorySecret;
|
||||
currentRecipientID: () => RecipientID;
|
||||
currentRecipientSecret: () => RecipientSecret;
|
||||
currentSignerID: () => SignerID;
|
||||
currentSignerSecret: () => SignerSecret;
|
||||
currentSealerID: () => SealerID;
|
||||
currentSealerSecret: () => SealerSecret;
|
||||
}
|
||||
|
||||
export class ControlledAccount extends Account implements GeneralizedControlledAccount {
|
||||
@@ -58,20 +58,20 @@ export class ControlledAccount extends Account implements GeneralizedControlledA
|
||||
return getAgentID(this.agentSecret);
|
||||
}
|
||||
|
||||
currentSignatoryID(): SignatoryID {
|
||||
return getAgentSignatoryID(this.currentAgentID());
|
||||
currentSignerID(): SignerID {
|
||||
return getAgentSignerID(this.currentAgentID());
|
||||
}
|
||||
|
||||
currentSignatorySecret(): SignatorySecret {
|
||||
return getAgentSignatorySecret(this.agentSecret);
|
||||
currentSignerSecret(): SignerSecret {
|
||||
return getAgentSignerSecret(this.agentSecret);
|
||||
}
|
||||
|
||||
currentRecipientID(): RecipientID {
|
||||
return getAgentRecipientID(this.currentAgentID());
|
||||
currentSealerID(): SealerID {
|
||||
return getAgentSealerID(this.currentAgentID());
|
||||
}
|
||||
|
||||
currentRecipientSecret(): RecipientSecret {
|
||||
return getAgentRecipientSecret(this.agentSecret);
|
||||
currentSealerSecret(): SealerSecret {
|
||||
return getAgentSealerSecret(this.agentSecret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,20 +90,20 @@ export class AnonymousControlledAccount implements GeneralizedControlledAccount
|
||||
return getAgentID(this.agentSecret);
|
||||
}
|
||||
|
||||
currentSignatoryID(): SignatoryID {
|
||||
return getAgentSignatoryID(this.currentAgentID());
|
||||
currentSignerID(): SignerID {
|
||||
return getAgentSignerID(this.currentAgentID());
|
||||
}
|
||||
|
||||
currentSignatorySecret(): SignatorySecret {
|
||||
return getAgentSignatorySecret(this.agentSecret);
|
||||
currentSignerSecret(): SignerSecret {
|
||||
return getAgentSignerSecret(this.agentSecret);
|
||||
}
|
||||
|
||||
currentRecipientID(): RecipientID {
|
||||
return getAgentRecipientID(this.currentAgentID());
|
||||
currentSealerID(): SealerID {
|
||||
return getAgentSealerID(this.currentAgentID());
|
||||
}
|
||||
|
||||
currentRecipientSecret(): RecipientSecret {
|
||||
return getAgentRecipientSecret(this.agentSecret);
|
||||
currentSealerSecret(): SealerSecret {
|
||||
return getAgentSealerSecret(this.agentSecret);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Transaction } from "./coValue.js";
|
||||
import { LocalNode } from "./node.js";
|
||||
import { createdNowUnique, getAgentSignatorySecret, newRandomAgentSecret, sign } from "./crypto.js";
|
||||
import { createdNowUnique, getAgentSignerSecret, newRandomAgentSecret, sign } from "./crypto.js";
|
||||
import { randomAnonymousAccountAndSessionID } from "./testUtils.js";
|
||||
|
||||
test("Can create coValue with new agent credentials and add transaction to it", () => {
|
||||
@@ -34,7 +34,7 @@ test("Can create coValue with new agent credentials and add transaction to it",
|
||||
node.ownSessionID,
|
||||
[transaction],
|
||||
expectedNewHash,
|
||||
sign(account.currentSignatorySecret(), expectedNewHash)
|
||||
sign(account.currentSignerSecret(), expectedNewHash)
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
@@ -71,7 +71,7 @@ test("transactions with wrong signature are rejected", () => {
|
||||
node.ownSessionID,
|
||||
[transaction],
|
||||
expectedNewHash,
|
||||
sign(getAgentSignatorySecret(wrongAgent), expectedNewHash)
|
||||
sign(getAgentSignerSecret(wrongAgent), expectedNewHash)
|
||||
)
|
||||
).toBe(false);
|
||||
});
|
||||
@@ -117,7 +117,7 @@ test("transactions with correctly signed, but wrong hash are rejected", () => {
|
||||
node.ownSessionID,
|
||||
[transaction],
|
||||
expectedNewHash,
|
||||
sign(account.currentSignatorySecret(), expectedNewHash)
|
||||
sign(account.currentSignerSecret(), expectedNewHash)
|
||||
)
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
@@ -17,8 +17,8 @@ import {
|
||||
decryptForTransaction,
|
||||
KeyID,
|
||||
decryptKeySecret,
|
||||
getAgentSignatoryID,
|
||||
getAgentRecipientID,
|
||||
getAgentSignerID,
|
||||
getAgentSealerID,
|
||||
} from "./crypto.js";
|
||||
import { JsonObject, JsonValue } from "./jsonValue.js";
|
||||
import { base58 } from "@scure/base";
|
||||
@@ -158,14 +158,14 @@ export class CoValue {
|
||||
newHash: Hash,
|
||||
newSignature: Signature
|
||||
): boolean {
|
||||
const signatoryID = getAgentSignatoryID(
|
||||
const signerID = getAgentSignerID(
|
||||
this.node.resolveAccountAgent(
|
||||
accountOrAgentIDfromSessionID(sessionID),
|
||||
"Expected to know signatory of transaction"
|
||||
"Expected to know signer of transaction"
|
||||
)
|
||||
);
|
||||
|
||||
if (!signatoryID) {
|
||||
if (!signerID) {
|
||||
console.warn(
|
||||
"Unknown agent",
|
||||
accountOrAgentIDfromSessionID(sessionID)
|
||||
@@ -183,12 +183,12 @@ export class CoValue {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!verify(newSignature, newHash, signatoryID)) {
|
||||
if (!verify(newSignature, newHash, signerID)) {
|
||||
console.warn(
|
||||
"Invalid signature",
|
||||
newSignature,
|
||||
newHash,
|
||||
signatoryID
|
||||
signerID
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -284,7 +284,7 @@ export class CoValue {
|
||||
]);
|
||||
|
||||
const signature = sign(
|
||||
this.node.account.currentSignatorySecret(),
|
||||
this.node.account.currentSignerSecret(),
|
||||
expectedNewHash
|
||||
);
|
||||
|
||||
@@ -419,8 +419,8 @@ export class CoValue {
|
||||
|
||||
const secret = unseal(
|
||||
readKeyEntry.value,
|
||||
this.node.account.currentRecipientSecret(),
|
||||
getAgentRecipientID(revealerAgent),
|
||||
this.node.account.currentSealerSecret(),
|
||||
getAgentSealerID(revealerAgent),
|
||||
{
|
||||
in: this.id,
|
||||
tx: readKeyEntry.txID,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
getRecipientID,
|
||||
getSignatoryID,
|
||||
getSealerID,
|
||||
getSignerID,
|
||||
secureHash,
|
||||
newRandomRecipient,
|
||||
newRandomSignatory,
|
||||
newRandomSealer,
|
||||
newRandomSigner,
|
||||
seal,
|
||||
sign,
|
||||
unseal,
|
||||
@@ -23,29 +23,29 @@ import stableStringify from "fast-json-stable-stringify";
|
||||
|
||||
test("Signatures round-trip and use stable stringify", () => {
|
||||
const data = { b: "world", a: "hello" };
|
||||
const signatory = newRandomSignatory();
|
||||
const signature = sign(signatory, data);
|
||||
const signer = newRandomSigner();
|
||||
const signature = sign(signer, data);
|
||||
|
||||
expect(signature).toMatch(/^signature_z/);
|
||||
expect(
|
||||
verify(signature, { a: "hello", b: "world" }, getSignatoryID(signatory))
|
||||
verify(signature, { a: "hello", b: "world" }, getSignerID(signer))
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test("Invalid signatures don't verify", () => {
|
||||
const data = { b: "world", a: "hello" };
|
||||
const signatory = newRandomSignatory();
|
||||
const signatory2 = newRandomSignatory();
|
||||
const wrongSignature = sign(signatory2, data);
|
||||
const signer = newRandomSigner();
|
||||
const signer2 = newRandomSigner();
|
||||
const wrongSignature = sign(signer2, data);
|
||||
|
||||
expect(verify(wrongSignature, data, getSignatoryID(signatory))).toBe(false);
|
||||
expect(verify(wrongSignature, data, getSignerID(signer))).toBe(false);
|
||||
});
|
||||
|
||||
test("encrypting round-trips, but invalid receiver can't unseal", () => {
|
||||
const data = { b: "world", a: "hello" };
|
||||
const sender = newRandomRecipient();
|
||||
const recipient = newRandomRecipient();
|
||||
const wrongRecipient = newRandomRecipient();
|
||||
const sender = newRandomSealer();
|
||||
const sealer = newRandomSealer();
|
||||
const wrongSealer = newRandomSealer();
|
||||
|
||||
const nOnceMaterial = {
|
||||
in: "co_zTEST",
|
||||
@@ -55,31 +55,31 @@ test("encrypting round-trips, but invalid receiver can't unseal", () => {
|
||||
const sealed = seal(
|
||||
data,
|
||||
sender,
|
||||
getRecipientID(recipient),
|
||||
getSealerID(sealer),
|
||||
nOnceMaterial
|
||||
);
|
||||
|
||||
expect(
|
||||
unseal(sealed, recipient, getRecipientID(sender), nOnceMaterial)
|
||||
unseal(sealed, sealer, getSealerID(sender), nOnceMaterial)
|
||||
).toEqual(data);
|
||||
expect(
|
||||
() => unseal(sealed, wrongRecipient, getRecipientID(sender), nOnceMaterial)
|
||||
() => unseal(sealed, wrongSealer, getSealerID(sender), nOnceMaterial)
|
||||
).toThrow(/Wrong tag/);
|
||||
|
||||
// trying with wrong recipient secret, by hand
|
||||
// trying with wrong sealer secret, by hand
|
||||
const nOnce = blake3(
|
||||
new TextEncoder().encode(stableStringify(nOnceMaterial))
|
||||
).slice(0, 24);
|
||||
const recipient3priv = base58.decode(
|
||||
wrongRecipient.substring("recipientSecret_z".length)
|
||||
const sealer3priv = base58.decode(
|
||||
wrongSealer.substring("sealerSecret_z".length)
|
||||
);
|
||||
const senderPub = base58.decode(
|
||||
getRecipientID(sender).substring("recipient_z".length)
|
||||
getSealerID(sender).substring("sealer_z".length)
|
||||
);
|
||||
const sealedBytes = base64url.decode(
|
||||
sealed.substring("sealed_U".length)
|
||||
);
|
||||
const sharedSecret = x25519.getSharedSecret(recipient3priv, senderPub);
|
||||
const sharedSecret = x25519.getSharedSecret(sealer3priv, senderPub);
|
||||
|
||||
expect(() => {
|
||||
const _ = xsalsa20_poly1305(sharedSecret, nOnce).decrypt(sealedBytes);
|
||||
|
||||
114
src/crypto.ts
114
src/crypto.ts
@@ -7,45 +7,45 @@ import { blake3 } from "@noble/hashes/blake3";
|
||||
import { randomBytes } from "@noble/ciphers/webcrypto/utils";
|
||||
import { AgentID, RawCoValueID, TransactionID } from './ids.js';
|
||||
|
||||
export type SignatorySecret = `signatorySecret_z${string}`;
|
||||
export type SignatoryID = `signatory_z${string}`;
|
||||
export type SignerSecret = `signerSecret_z${string}`;
|
||||
export type SignerID = `signer_z${string}`;
|
||||
export type Signature = `signature_z${string}`;
|
||||
|
||||
export type RecipientSecret = `recipientSecret_z${string}`;
|
||||
export type RecipientID = `recipient_z${string}`;
|
||||
export type SealerSecret = `sealerSecret_z${string}`;
|
||||
export type SealerID = `sealer_z${string}`;
|
||||
export type Sealed<T> = `sealed_U${string}` & { __type: T };
|
||||
|
||||
export type AgentSecret = `${RecipientSecret}/${SignatorySecret}`;
|
||||
export type AgentSecret = `${SealerSecret}/${SignerSecret}`;
|
||||
|
||||
const textEncoder = new TextEncoder();
|
||||
const textDecoder = new TextDecoder();
|
||||
|
||||
export function newRandomSignatory(): SignatorySecret {
|
||||
return `signatorySecret_z${base58.encode(
|
||||
export function newRandomSigner(): SignerSecret {
|
||||
return `signerSecret_z${base58.encode(
|
||||
ed25519.utils.randomPrivateKey()
|
||||
)}`;
|
||||
}
|
||||
|
||||
export function signatorySecretToBytes(secret: SignatorySecret): Uint8Array {
|
||||
return base58.decode(secret.substring("signatorySecret_z".length));
|
||||
export function signerSecretToBytes(secret: SignerSecret): Uint8Array {
|
||||
return base58.decode(secret.substring("signerSecret_z".length));
|
||||
}
|
||||
|
||||
export function signatorySecretFromBytes(bytes: Uint8Array): SignatorySecret {
|
||||
return `signatorySecret_z${base58.encode(bytes)}`;
|
||||
export function signerSecretFromBytes(bytes: Uint8Array): SignerSecret {
|
||||
return `signerSecret_z${base58.encode(bytes)}`;
|
||||
}
|
||||
|
||||
export function getSignatoryID(secret: SignatorySecret): SignatoryID {
|
||||
return `signatory_z${base58.encode(
|
||||
export function getSignerID(secret: SignerSecret): SignerID {
|
||||
return `signer_z${base58.encode(
|
||||
ed25519.getPublicKey(
|
||||
base58.decode(secret.substring("signatorySecret_z".length))
|
||||
base58.decode(secret.substring("signerSecret_z".length))
|
||||
)
|
||||
)}`;
|
||||
}
|
||||
|
||||
export function sign(secret: SignatorySecret, message: JsonValue): Signature {
|
||||
export function sign(secret: SignerSecret, message: JsonValue): Signature {
|
||||
const signature = ed25519.sign(
|
||||
textEncoder.encode(stableStringify(message)),
|
||||
base58.decode(secret.substring("signatorySecret_z".length))
|
||||
base58.decode(secret.substring("signerSecret_z".length))
|
||||
);
|
||||
return `signature_z${base58.encode(signature)}`;
|
||||
}
|
||||
@@ -53,101 +53,101 @@ export function sign(secret: SignatorySecret, message: JsonValue): Signature {
|
||||
export function verify(
|
||||
signature: Signature,
|
||||
message: JsonValue,
|
||||
id: SignatoryID
|
||||
id: SignerID
|
||||
): boolean {
|
||||
return ed25519.verify(
|
||||
base58.decode(signature.substring("signature_z".length)),
|
||||
textEncoder.encode(stableStringify(message)),
|
||||
base58.decode(id.substring("signatory_z".length))
|
||||
base58.decode(id.substring("signer_z".length))
|
||||
);
|
||||
}
|
||||
|
||||
export function newRandomRecipient(): RecipientSecret {
|
||||
return `recipientSecret_z${base58.encode(x25519.utils.randomPrivateKey())}`;
|
||||
export function newRandomSealer(): SealerSecret {
|
||||
return `sealerSecret_z${base58.encode(x25519.utils.randomPrivateKey())}`;
|
||||
}
|
||||
|
||||
export function recipientSecretToBytes(secret: RecipientSecret): Uint8Array {
|
||||
return base58.decode(secret.substring("recipientSecret_z".length));
|
||||
export function sealerSecretToBytes(secret: SealerSecret): Uint8Array {
|
||||
return base58.decode(secret.substring("sealerSecret_z".length));
|
||||
}
|
||||
|
||||
export function recipientSecretFromBytes(bytes: Uint8Array): RecipientSecret {
|
||||
return `recipientSecret_z${base58.encode(bytes)}`;
|
||||
export function sealerSecretFromBytes(bytes: Uint8Array): SealerSecret {
|
||||
return `sealerSecret_z${base58.encode(bytes)}`;
|
||||
}
|
||||
|
||||
export function getRecipientID(secret: RecipientSecret): RecipientID {
|
||||
return `recipient_z${base58.encode(
|
||||
export function getSealerID(secret: SealerSecret): SealerID {
|
||||
return `sealer_z${base58.encode(
|
||||
x25519.getPublicKey(
|
||||
base58.decode(secret.substring("recipientSecret_z".length))
|
||||
base58.decode(secret.substring("sealerSecret_z".length))
|
||||
)
|
||||
)}`;
|
||||
}
|
||||
|
||||
export function newRandomAgentSecret(): AgentSecret {
|
||||
return `${newRandomRecipient()}/${newRandomSignatory()}`;
|
||||
return `${newRandomSealer()}/${newRandomSigner()}`;
|
||||
}
|
||||
|
||||
export function agentSecretToBytes(secret: AgentSecret): Uint8Array {
|
||||
const [recipientSecret, signatorySecret] = secret.split("/");
|
||||
const [sealerSecret, signerSecret] = secret.split("/");
|
||||
return new Uint8Array([
|
||||
...recipientSecretToBytes(recipientSecret as RecipientSecret),
|
||||
...signatorySecretToBytes(signatorySecret as SignatorySecret),
|
||||
...sealerSecretToBytes(sealerSecret as SealerSecret),
|
||||
...signerSecretToBytes(signerSecret as SignerSecret),
|
||||
]);
|
||||
}
|
||||
|
||||
export function agentSecretFromBytes(bytes: Uint8Array): AgentSecret {
|
||||
const recipientSecret = recipientSecretFromBytes(
|
||||
const sealerSecret = sealerSecretFromBytes(
|
||||
bytes.slice(0, 32)
|
||||
);
|
||||
const signatorySecret = signatorySecretFromBytes(
|
||||
const signerSecret = signerSecretFromBytes(
|
||||
bytes.slice(32)
|
||||
);
|
||||
return `${recipientSecret}/${signatorySecret}`;
|
||||
return `${sealerSecret}/${signerSecret}`;
|
||||
}
|
||||
|
||||
export function getAgentID(secret: AgentSecret): AgentID {
|
||||
const [recipientSecret, signatorySecret] = secret.split("/");
|
||||
return `${getRecipientID(
|
||||
recipientSecret as RecipientSecret
|
||||
)}/${getSignatoryID(signatorySecret as SignatorySecret)}`;
|
||||
const [sealerSecret, signerSecret] = secret.split("/");
|
||||
return `${getSealerID(
|
||||
sealerSecret as SealerSecret
|
||||
)}/${getSignerID(signerSecret as SignerSecret)}`;
|
||||
}
|
||||
|
||||
export function getAgentSignatoryID(agentId: AgentID): SignatoryID {
|
||||
return agentId.split("/")[1] as SignatoryID;
|
||||
export function getAgentSignerID(agentId: AgentID): SignerID {
|
||||
return agentId.split("/")[1] as SignerID;
|
||||
}
|
||||
|
||||
export function getAgentSignatorySecret(agentSecret: AgentSecret): SignatorySecret {
|
||||
return agentSecret.split("/")[1] as SignatorySecret;
|
||||
export function getAgentSignerSecret(agentSecret: AgentSecret): SignerSecret {
|
||||
return agentSecret.split("/")[1] as SignerSecret;
|
||||
}
|
||||
|
||||
export function getAgentRecipientID(agentId: AgentID): RecipientID {
|
||||
return agentId.split("/")[0] as RecipientID;
|
||||
export function getAgentSealerID(agentId: AgentID): SealerID {
|
||||
return agentId.split("/")[0] as SealerID;
|
||||
}
|
||||
|
||||
export function getAgentRecipientSecret(agentSecret: AgentSecret): RecipientSecret {
|
||||
return agentSecret.split("/")[0] as RecipientSecret;
|
||||
export function getAgentSealerSecret(agentSecret: AgentSecret): SealerSecret {
|
||||
return agentSecret.split("/")[0] as SealerSecret;
|
||||
}
|
||||
|
||||
export function seal<T extends JsonValue>(
|
||||
message: T,
|
||||
from: RecipientSecret,
|
||||
to: RecipientID,
|
||||
from: SealerSecret,
|
||||
to: SealerID,
|
||||
nOnceMaterial: { in: RawCoValueID; tx: TransactionID }
|
||||
): Sealed<T> {
|
||||
const nOnce = blake3(
|
||||
textEncoder.encode(stableStringify(nOnceMaterial))
|
||||
).slice(0, 24);
|
||||
|
||||
const recipientPub = base58.decode(to.substring("recipient_z".length));
|
||||
const sealerPub = base58.decode(to.substring("sealer_z".length));
|
||||
|
||||
const senderPriv = base58.decode(
|
||||
from.substring("recipientSecret_z".length)
|
||||
from.substring("sealerSecret_z".length)
|
||||
);
|
||||
|
||||
const plaintext = textEncoder.encode(stableStringify(message));
|
||||
|
||||
const sharedSecret = x25519.getSharedSecret(
|
||||
senderPriv,
|
||||
recipientPub
|
||||
sealerPub
|
||||
);
|
||||
|
||||
const sealedBytes = xsalsa20_poly1305(sharedSecret, nOnce).encrypt(
|
||||
@@ -161,23 +161,23 @@ export function seal<T extends JsonValue>(
|
||||
|
||||
export function unseal<T extends JsonValue>(
|
||||
sealed: Sealed<T>,
|
||||
recipient: RecipientSecret,
|
||||
from: RecipientID,
|
||||
sealer: SealerSecret,
|
||||
from: SealerID,
|
||||
nOnceMaterial: { in: RawCoValueID; tx: TransactionID }
|
||||
): T | undefined {
|
||||
const nOnce = blake3(
|
||||
textEncoder.encode(stableStringify(nOnceMaterial))
|
||||
).slice(0, 24);
|
||||
|
||||
const recipientPriv = base58.decode(
|
||||
recipient.substring("recipientSecret_z".length)
|
||||
const sealerPriv = base58.decode(
|
||||
sealer.substring("sealerSecret_z".length)
|
||||
);
|
||||
|
||||
const senderPub = base58.decode(from.substring("recipient_z".length));
|
||||
const senderPub = base58.decode(from.substring("sealer_z".length));
|
||||
|
||||
const sealedBytes = base64url.decode(sealed.substring("sealed_U".length));
|
||||
|
||||
const sharedSecret = x25519.getSharedSecret(recipientPriv, senderPub);
|
||||
const sharedSecret = x25519.getSharedSecret(sealerPriv, senderPub);
|
||||
|
||||
const plaintext = xsalsa20_poly1305(sharedSecret, nOnce).decrypt(
|
||||
sealedBytes
|
||||
|
||||
@@ -4,10 +4,10 @@ export type RawCoValueID = `co_z${string}` | `co_${string}_z${string}`;
|
||||
|
||||
export type TransactionID = { sessionID: SessionID; txIndex: number };
|
||||
|
||||
export type AgentID = `recipient_z${string}/signatory_z${string}`;
|
||||
export type AgentID = `sealer_z${string}/signer_z${string}`;
|
||||
|
||||
export function isAgentID(id: string): id is AgentID {
|
||||
return typeof id === "string" && id.startsWith("recipient_") && id.includes("/signatory_");
|
||||
return typeof id === "string" && id.startsWith("sealer_") && id.includes("/signer_");
|
||||
}
|
||||
|
||||
export type SessionID = `${AccountIDOrAgentID}_session_z${string}`;
|
||||
|
||||
12
src/node.ts
12
src/node.ts
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
createdNowUnique,
|
||||
getAgentID,
|
||||
getAgentRecipientID,
|
||||
getAgentRecipientSecret,
|
||||
getAgentSealerID,
|
||||
getAgentSealerSecret,
|
||||
newRandomAgentSecret,
|
||||
newRandomKeySecret,
|
||||
seal,
|
||||
@@ -101,8 +101,8 @@ export class LocalNode {
|
||||
`${readKey.id}_for_${getAgentID(agentSecret)}`,
|
||||
seal(
|
||||
readKey.secret,
|
||||
getAgentRecipientSecret(agentSecret),
|
||||
getAgentRecipientID(getAgentID(agentSecret)),
|
||||
getAgentSealerSecret(agentSecret),
|
||||
getAgentSealerID(getAgentID(agentSecret)),
|
||||
{
|
||||
in: account.id,
|
||||
tx: account.nextTransactionID(),
|
||||
@@ -168,8 +168,8 @@ export class LocalNode {
|
||||
`${readKey.id}_for_${this.account.id}`,
|
||||
seal(
|
||||
readKey.secret,
|
||||
this.account.currentRecipientSecret(),
|
||||
this.account.currentRecipientID(),
|
||||
this.account.currentSealerSecret(),
|
||||
this.account.currentSealerID(),
|
||||
{
|
||||
in: teamCoValue.id,
|
||||
tx: teamCoValue.nextTransactionID(),
|
||||
|
||||
@@ -4,7 +4,7 @@ import { expectMap } from "./contentType.js";
|
||||
import { expectTeamContent } from "./permissions.js";
|
||||
import {
|
||||
createdNowUnique,
|
||||
getRecipientID,
|
||||
getSealerID,
|
||||
newRandomKeySecret,
|
||||
seal,
|
||||
encryptKeySecret,
|
||||
@@ -423,8 +423,8 @@ test("Admins can set team read key and then use it to create and read private tr
|
||||
const { secret: readKey, id: readKeyID } = newRandomKeySecret();
|
||||
const revelation = seal(
|
||||
readKey,
|
||||
admin.currentRecipientSecret(),
|
||||
admin.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
admin.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -491,8 +491,8 @@ test("Admins can set team read key and then writers can use it to create and rea
|
||||
|
||||
const revelation1 = seal(
|
||||
readKey,
|
||||
admin.currentRecipientSecret(),
|
||||
admin.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
admin.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -503,8 +503,8 @@ test("Admins can set team read key and then writers can use it to create and rea
|
||||
|
||||
const revelation2 = seal(
|
||||
readKey,
|
||||
admin.currentRecipientSecret(),
|
||||
writer.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
writer.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -583,8 +583,8 @@ test("Admins can set team read key and then use it to create private transaction
|
||||
|
||||
const revelation1 = seal(
|
||||
readKey,
|
||||
admin.currentRecipientSecret(),
|
||||
admin.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
admin.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -595,8 +595,8 @@ test("Admins can set team read key and then use it to create private transaction
|
||||
|
||||
const revelation2 = seal(
|
||||
readKey,
|
||||
admin.currentRecipientSecret(),
|
||||
reader.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
reader.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -675,8 +675,8 @@ test("Admins can set team read key and then use it to create private transaction
|
||||
|
||||
const revelation1 = seal(
|
||||
readKey,
|
||||
admin.currentRecipientSecret(),
|
||||
admin.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
admin.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -687,8 +687,8 @@ test("Admins can set team read key and then use it to create private transaction
|
||||
|
||||
const revelation2 = seal(
|
||||
readKey,
|
||||
admin.currentRecipientSecret(),
|
||||
reader1.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
reader1.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -729,8 +729,8 @@ test("Admins can set team read key and then use it to create private transaction
|
||||
teamContent.edit((editable) => {
|
||||
const revelation3 = seal(
|
||||
readKey,
|
||||
admin.currentRecipientSecret(),
|
||||
reader2.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
reader2.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -798,8 +798,8 @@ test("Admins can set team read key, make a private transaction in an owned objec
|
||||
const { secret: readKey, id: readKeyID } = newRandomKeySecret();
|
||||
const revelation = seal(
|
||||
readKey,
|
||||
admin.currentRecipientSecret(),
|
||||
admin.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
admin.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -836,8 +836,8 @@ test("Admins can set team read key, make a private transaction in an owned objec
|
||||
|
||||
const revelation = seal(
|
||||
readKey2,
|
||||
admin.currentRecipientSecret(),
|
||||
admin.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
admin.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -907,8 +907,8 @@ test("Admins can set team read key, make a private transaction in an owned objec
|
||||
teamContent.edit((editable) => {
|
||||
const revelation = seal(
|
||||
readKey,
|
||||
admin.currentRecipientSecret(),
|
||||
admin.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
admin.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -939,8 +939,8 @@ test("Admins can set team read key, make a private transaction in an owned objec
|
||||
teamContent.edit((editable) => {
|
||||
const revelation2 = seal(
|
||||
readKey2,
|
||||
admin.currentRecipientSecret(),
|
||||
admin.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
admin.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -951,8 +951,8 @@ test("Admins can set team read key, make a private transaction in an owned objec
|
||||
|
||||
const revelation3 = seal(
|
||||
readKey2,
|
||||
admin.currentRecipientSecret(),
|
||||
reader.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
reader.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -1056,8 +1056,8 @@ test("Admins can set team read rey, make a private transaction in an owned objec
|
||||
teamContent.edit((editable) => {
|
||||
const revelation1 = seal(
|
||||
readKey,
|
||||
admin.currentRecipientSecret(),
|
||||
admin.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
admin.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -1068,8 +1068,8 @@ test("Admins can set team read rey, make a private transaction in an owned objec
|
||||
|
||||
const revelation2 = seal(
|
||||
readKey,
|
||||
admin.currentRecipientSecret(),
|
||||
reader.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
reader.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -1080,8 +1080,8 @@ test("Admins can set team read rey, make a private transaction in an owned objec
|
||||
|
||||
const revelation3 = seal(
|
||||
readKey,
|
||||
admin.currentRecipientSecret(),
|
||||
reader2.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
reader2.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -1133,8 +1133,8 @@ test("Admins can set team read rey, make a private transaction in an owned objec
|
||||
teamContent.edit((editable) => {
|
||||
const newRevelation1 = seal(
|
||||
readKey2,
|
||||
admin.currentRecipientSecret(),
|
||||
admin.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
admin.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
@@ -1149,8 +1149,8 @@ test("Admins can set team read rey, make a private transaction in an owned objec
|
||||
|
||||
const newRevelation2 = seal(
|
||||
readKey2,
|
||||
admin.currentRecipientSecret(),
|
||||
reader2.currentRecipientID(),
|
||||
admin.currentSealerSecret(),
|
||||
reader2.currentSealerID(),
|
||||
{
|
||||
in: team.id,
|
||||
tx: team.nextTransactionID(),
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
newRandomKeySecret,
|
||||
seal,
|
||||
encryptKeySecret,
|
||||
getAgentRecipientID,
|
||||
getAgentSealerID,
|
||||
Sealed,
|
||||
} from "./crypto.js";
|
||||
import {
|
||||
@@ -260,8 +260,8 @@ export class Team {
|
||||
`${currentReadKey.id}_for_${accountID}`,
|
||||
seal(
|
||||
currentReadKey.secret,
|
||||
this.teamMap.coValue.node.account.currentRecipientSecret(),
|
||||
getAgentRecipientID(agent),
|
||||
this.teamMap.coValue.node.account.currentSealerSecret(),
|
||||
getAgentSealerID(agent),
|
||||
{
|
||||
in: this.teamMap.coValue.id,
|
||||
tx: this.teamMap.coValue.nextTransactionID(),
|
||||
@@ -310,8 +310,8 @@ export class Team {
|
||||
`${newReadKey.id}_for_${readerID}`,
|
||||
seal(
|
||||
newReadKey.secret,
|
||||
this.teamMap.coValue.node.account.currentRecipientSecret(),
|
||||
getAgentRecipientID(reader),
|
||||
this.teamMap.coValue.node.account.currentSealerSecret(),
|
||||
getAgentSealerID(reader),
|
||||
{
|
||||
in: this.teamMap.coValue.id,
|
||||
tx: this.teamMap.coValue.nextTransactionID(),
|
||||
@@ -379,5 +379,5 @@ export function isKeyForKeyField(field: string): field is `${KeyID}_for_${KeyID}
|
||||
}
|
||||
|
||||
export function isKeyForAccountField(field: string): field is `${KeyID}_for_${AccountIDOrAgentID}` {
|
||||
return field.startsWith("key_") && (field.includes("_for_recipient") || field.includes("_for_co"));
|
||||
return field.startsWith("key_") && (field.includes("_for_sealer") || field.includes("_for_co"));
|
||||
}
|
||||
Reference in New Issue
Block a user