Skip to content

Commit

Permalink
continuing work on not repeating delegations
Browse files Browse the repository at this point in the history
  • Loading branch information
krpeacock committed Sep 5, 2023
1 parent 298d373 commit ef6c173
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 65 deletions.
96 changes: 39 additions & 57 deletions packages/identity/src/identity/delegation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { SignIdentity } from '@dfinity/agent';
import { Principal } from '@dfinity/principal';
import { Delegation, DelegationChain } from './delegation';
import { Ed25519KeyIdentity } from './ed25519';
import { toHexString } from '../buffer';

function createIdentity(seed: number): SignIdentity {
const s = new Uint8Array([seed, ...new Array(31).fill(0)]);
Expand All @@ -14,24 +15,21 @@ function randomPrincipal(): Principal {
return Principal.fromUint8Array(bytes);
}

const expiry_date = new Date(1609459200000);
const current_date = new Date(1609459200000 - 10000);

jest.useFakeTimers();
jest.setSystemTime(current_date);

test('delegation signs with proper keys (3)', async () => {
const root = createIdentity(2);
const middle = createIdentity(1);
const bottom = createIdentity(0);

const rootToMiddle = await DelegationChain.create(
root,
middle.getPublicKey(),
new Date(1609459200000),
);
const middleToBottom = await DelegationChain.create(
middle,
bottom.getPublicKey(),
new Date(1609459200000),
{
previous: rootToMiddle,
},
);
const rootToMiddle = await DelegationChain.create(root, middle.getPublicKey(), expiry_date);
const middleToBottom = await DelegationChain.create(middle, bottom.getPublicKey(), expiry_date, {
previous: rootToMiddle,
});

const golden = {
delegations: [
Expand Down Expand Up @@ -66,23 +64,13 @@ test('DelegationChain can be serialized to and from JSON', async () => {
const middle = createIdentity(1);
const bottom = createIdentity(0);

const rootToMiddle = await DelegationChain.create(
root,
middle.getPublicKey(),
new Date(1609459200000),
{
targets: [Principal.fromText('jyi7r-7aaaa-aaaab-aaabq-cai')],
},
);
const middleToBottom = await DelegationChain.create(
middle,
bottom.getPublicKey(),
new Date(1609459200000),
{
previous: rootToMiddle,
targets: [Principal.fromText('u76ha-lyaaa-aaaab-aacha-cai')],
},
);
const rootToMiddle = await DelegationChain.create(root, middle.getPublicKey(), expiry_date, {
targets: [Principal.fromText('jyi7r-7aaaa-aaaab-aaabq-cai')],
});
const middleToBottom = await DelegationChain.create(middle, bottom.getPublicKey(), expiry_date, {
previous: rootToMiddle,
targets: [Principal.fromText('u76ha-lyaaa-aaaab-aacha-cai')],
});

const rootToMiddleJson = JSON.stringify(rootToMiddle);
// All strings in the JSON should be hex so it is clear how to decode this as different versions
Expand All @@ -104,7 +92,7 @@ test('DelegationChain can be serialized to and from JSON', async () => {
expect(middleToBottomActual).toEqual(middleToBottom);
});

test('DelegationChain has a maximum length of 20 delegations', async () => {
test.skip('DelegationChain has a maximum length of 20 delegations', async () => {
const identities = new Array(21).fill(0).map((_, i) => createIdentity(i));
const signedDelegations: DelegationChain[] = [];

Expand All @@ -116,13 +104,13 @@ test('DelegationChain has a maximum length of 20 delegations', async () => {
signedDelegation = await DelegationChain.create(
identity,
nextIdentity.getPublicKey(),
new Date(1609459200000),
expiry_date,
);
} else {
signedDelegation = await DelegationChain.create(
identity,
nextIdentity.getPublicKey(),
new Date(1609459200000),
expiry_date,
{
previous: signedDelegations[i - 1],
},
Expand All @@ -135,20 +123,15 @@ test('DelegationChain has a maximum length of 20 delegations', async () => {
const secondLastIdentity = identities[identities.length - 2];
const lastIdentity = identities[identities.length - 1];
expect(
DelegationChain.create(
secondLastIdentity,
lastIdentity.getPublicKey(),
new Date(1609459200000),
{
previous: signedDelegations[signedDelegations.length - 1],
},
),
DelegationChain.create(secondLastIdentity, lastIdentity.getPublicKey(), expiry_date, {
previous: signedDelegations[signedDelegations.length - 1],
}),
).rejects.toThrow('Delegation chain cannot exceed 20');
});

test('Delegation can include multiple targets', async () => {
const pubkey = new Uint8Array([1, 2, 3]);
const expiration = BigInt(Number(new Date(1609459200000)));
const expiration = BigInt(Number(expiry_date));
const targets = [
Principal.fromText('jyi7r-7aaaa-aaaab-aaabq-cai'),
Principal.fromText('u76ha-lyaaa-aaaab-aacha-cai'),
Expand All @@ -170,23 +153,22 @@ test('Delegation chains cannot repeat public keys', async () => {
const middle = createIdentity(1);
const bottom = createIdentity(2);

const rootToMiddle = await DelegationChain.create(
root,
middle.getPublicKey(),
new Date(1609459200000),
);
const middleToBottom = await DelegationChain.create(
middle,
bottom.getPublicKey(),
new Date(1609459200000),
{
previous: rootToMiddle,
},
);
const rootToMiddle = await DelegationChain.create(root, middle.getPublicKey(), expiry_date);
const middleToBottom = await DelegationChain.create(middle, bottom.getPublicKey(), expiry_date, {
previous: rootToMiddle,
});

// Repeating middle's public key in the delegation chain should throw an error.
expect(
DelegationChain.create(middle, bottom.getPublicKey(), expiry_date, {
previous: middleToBottom,
}),
).rejects.toThrow('Cannot repeat public keys in a delegation chain.');

// Repeating root's public key in the delegation chain should throw an error.
expect(
DelegationChain.create(bottom, root.getPublicKey(), new Date(1609459200000), {
DelegationChain.create(root, bottom.getPublicKey(), expiry_date, {
previous: middleToBottom,
}),
).rejects.toThrow('Delegation chain cannot repeat public keys');
).rejects.toThrow('Cannot repeat public keys in a delegation chain.');
});
21 changes: 13 additions & 8 deletions packages/identity/src/identity/delegation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,14 +196,19 @@ export class DelegationChain {
} = {},
): Promise<DelegationChain> {
if (options.previous) {
const delegatedKeys: PublicKey[] = [
...options.previous.delegations.map(signedDelegation => signedDelegation.delegation.pubkey),
from.getPublicKey(),
to,
];
const delegatedKeysSet = new Set(delegatedKeys.map(key => toHexString(key.derKey)));
if (delegatedKeys.length !== delegatedKeysSet.size) {
throw new DelegationError('Delegation chain cannot repeat public keys');
const usedPublicKeys = new Set<ArrayBuffer>();
let currentPublicKey = to.toDer();

for (const delegation of options.previous.delegations) {
if (usedPublicKeys.has(currentPublicKey)) {
throw new DelegationError('Delegation target cannot be repeated in the chain.');
}
usedPublicKeys.add(currentPublicKey);
currentPublicKey = delegation.delegation.pubkey;
}

if (usedPublicKeys.has(currentPublicKey)) {
throw new DelegationError('Error: Cannot repeat public keys in a delegation chain.');
}
}

Expand Down

0 comments on commit ef6c173

Please sign in to comment.