Deal better with private transactions we don't have a key for

This commit is contained in:
Anselm
2023-08-07 19:54:07 +01:00
parent 9c357bb5d7
commit 2cac2e8d4f
4 changed files with 68 additions and 41 deletions

View File

@@ -231,6 +231,10 @@ export class CoValue {
if (privacy === "private") {
const { secret: keySecret, id: keyID } = this.getCurrentReadKey();
if (!keySecret) {
throw new Error("Can't make transaction without read key secret");
}
transaction = {
privacy: "private",
madeAt,
@@ -298,26 +302,40 @@ export class CoValue {
const allTransactions: DecryptedTransaction[] = validTransactions.map(
({ txID, tx }) => {
return {
txID,
madeAt: tx.madeAt,
changes:
tx.privacy === "private"
? decryptForTransaction(
tx.encryptedChanges,
this.getReadKey(tx.keyUsed),
{
in: this.id,
tx: txID,
}
) ||
(() => {
throw new Error("Couldn't decrypt changes");
})()
: tx.changes,
};
if (tx.privacy === "trusting") {
return {
txID,
madeAt: tx.madeAt,
changes: tx.changes,
};
} else {
const readKey = this.getReadKey(tx.keyUsed);
if (!readKey) {
return undefined;
} else {
const decrytedChanges = decryptForTransaction(
tx.encryptedChanges,
readKey,
{
in: this.id,
tx: txID,
}
);
if (!decrytedChanges) {
console.error("Failed to decrypt transaction despite having key");
return undefined;
}
return {
txID,
madeAt: tx.madeAt,
changes: decrytedChanges,
};
}
}
}
);
).filter((x): x is Exclude<typeof x, undefined> => !!x);
allTransactions.sort(
(a, b) =>
a.madeAt - b.madeAt ||
@@ -328,7 +346,7 @@ export class CoValue {
return allTransactions;
}
getCurrentReadKey(): { secret: KeySecret; id: KeyID } {
getCurrentReadKey(): { secret: KeySecret | undefined; id: KeyID } {
if (this.header.ruleset.type === "team") {
const content = expectTeamContent(this.getCurrentContent());
@@ -355,7 +373,7 @@ export class CoValue {
}
}
getReadKey(keyID: KeyID): KeySecret {
getReadKey(keyID: KeyID): KeySecret | undefined {
if (this.header.ruleset.type === "team") {
const content = expectTeamContent(this.getCurrentContent());
@@ -416,12 +434,7 @@ export class CoValue {
}
}
throw new Error(
"readKey " +
keyID +
" not revealed for " +
getAgentID(getAgent(this.node.agentCredential))
);
return undefined;
} else if (this.header.ruleset.type === "ownedByTeam") {
return this.node
.expectCoValueLoaded(this.header.ruleset.team)

View File

@@ -1194,15 +1194,12 @@ test("Admins can set team read rey, make a private transaction in an owned objec
newRandomSessionID(reader2ID)
);
expect(() => expectMap(childObjectAsReader.getCurrentContent())).toThrow(
/readKey (.+?) not revealed for (.+?)/
);
expect(
expectMap(childObjectAsReader.getCurrentContent()).get("foo2")
).toBeUndefined();
expect(
expectMap(childObjectAsReader2.getCurrentContent()).get("foo2")
).toEqual("bar2");
expect(() => {
childObjectAsReader.getCurrentContent();
}).toThrow();
});
test("Admins can set team read rey, make a private transaction in an owned object, rotate the read key, add two readers, rotate the read key again to kick out one reader, make another private transaction in the owned object, and only the remaining reader can read both transactions (high level)", () => {
@@ -1259,9 +1256,14 @@ test("Admins can set team read rey, make a private transaction in an owned objec
expect(childContentAsReader2.get("foo2")).toEqual("bar2");
expect(childContentAsReader2.get("foo3")).toEqual("bar3");
expect(() =>
childObject.coValue
.testWithDifferentCredentials(reader, newRandomSessionID(readerID))
.getCurrentContent()
).toThrow(/readKey (.+?) not revealed for (.+?)/);
expect(
expectMap(
childObject.coValue
.testWithDifferentCredentials(
reader,
newRandomSessionID(readerID)
)
.getCurrentContent()
).get("foo3")
).toBeUndefined();
});

View File

@@ -251,6 +251,10 @@ export class Team {
const currentReadKey = this.teamMap.coValue.getCurrentReadKey();
if (!currentReadKey.secret) {
throw new Error("Can't add member without read key secret");
}
const revelation = seal(
currentReadKey.secret,
this.teamMap.coValue.node.agentCredential.recipientSecret,
@@ -281,7 +285,16 @@ export class Team {
}
}) as AgentID[];
const currentReadKey = this.teamMap.coValue.getCurrentReadKey();
const maybeCurrentReadKey = this.teamMap.coValue.getCurrentReadKey();
if (!maybeCurrentReadKey.secret) {
throw new Error("Can't rotate read key secret we don't have access to");
}
const currentReadKey = {
id: maybeCurrentReadKey.id,
secret: maybeCurrentReadKey.secret,
};
const newReadKey = newRandomKeySecret();

View File

@@ -909,7 +909,6 @@ test("Can sync a coValue through a server to another client", async () => {
const server = new LocalNode(serverUser, newRandomSessionID(serverUserID));
const [serverAsPeer, client1AsPeer] = connectedPeers("server", "client1", {
trace: true,
peer1role: "server",
peer2role: "client",
});
@@ -922,7 +921,7 @@ test("Can sync a coValue through a server to another client", async () => {
const [serverAsOtherPeer, client2AsPeer] = connectedPeers(
"server",
"client2",
{ trace: true, peer1role: "server", peer2role: "client" }
{ peer1role: "server", peer2role: "client" }
);
client2.sync.addPeer(serverAsOtherPeer);