Skip to content

Commit

Permalink
Limit the delegation depth of read_state certificates to 1
Browse files Browse the repository at this point in the history
This reflects the following change in the interface spec:
dfinity/interface-spec#239
  • Loading branch information
oggy-dfin committed Dec 12, 2023
1 parent 40d9800 commit 41c2563
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 5 deletions.
Binary file added packages/agent/src/bin/with_subnet_key.bin
Binary file not shown.
25 changes: 25 additions & 0 deletions packages/agent/src/certificate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { fromHex, toHex } from './utils/buffer';
import { Principal } from '@dfinity/principal';
import { decodeTime } from './utils/leb';
import { lookupResultToBuffer, lookup_path } from './certificate';
import { readFileSync } from 'fs';

function label(str: string): ArrayBuffer {
return new TextEncoder().encode(str);
Expand Down Expand Up @@ -258,3 +259,27 @@ test('certificate verification fails if the time of the certificate is > 5 minut
}),
).rejects.toThrow('Invalid certificate: Certificate is signed more than 5 minutes in the future');
});

test('certificate verification fails on nested delegations', async () => {
// This is a recorded certificate from a read_state request to the II
// subnet, with the /subnet tree included. Thus, it could be used as its
// own delegation, according to the old interface spec definition.
const withSubnetSubtree = readFileSync('packages/agent/src/bin/with_subnet_key.bin');
const canisterId = Principal.fromText("rdmx6-jaaaa-aaaaa-aaadq-cai");
const subnetId = Principal.fromText("uzr34-akd3s-xrdag-3ql62-ocgoh-ld2ao-tamcv-54e7j-krwgb-2gm4z-oqe");
jest.setSystemTime(new Date(Date.parse('2023-12-12T10:40:00.652Z')));
let cert: Cert.Cert = cbor.decode(withSubnetSubtree);
const overlyNested = cbor.encode({
tree: cert.tree,
signature: cert.signature,
delegation: {
subnet_id: subnetId.toUint8Array(),
certificate: withSubnetSubtree,
},
})
await expect(Cert.Certificate.create({
certificate: overlyNested,
rootKey: fromHex(IC_ROOT_KEY),
canisterId: canisterId,
})).rejects.toThrow('Invalid certificate: Delegation certificates cannot be nested');
});
20 changes: 15 additions & 5 deletions packages/agent/src/certificate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,20 +170,24 @@ export class Certificate {
* @throws {CertificateVerificationError}
*/
public static async create(options: CreateCertificateOptions): Promise<Certificate> {
const cert = Certificate.createUnverified(options);

await cert.verify();
return cert;
}

private static createUnverified(options: CreateCertificateOptions): Certificate {
let blsVerify = options.blsVerify;
if (!blsVerify) {
blsVerify = bls.blsVerify;
}
const cert = new Certificate(
return new Certificate(
options.certificate,
options.rootKey,
options.canisterId,
blsVerify,
options.maxAgeInMinutes,
);

await cert.verify();
return cert;
}

private constructor(
Expand Down Expand Up @@ -259,7 +263,7 @@ export class Certificate {
return this._rootKey;
}

const cert: Certificate = await Certificate.create({
const cert: Certificate = await Certificate.createUnverified({
certificate: d.certificate,
rootKey: this._rootKey,
canisterId: this._canisterId,
Expand All @@ -268,6 +272,12 @@ export class Certificate {
maxAgeInMinutes: Infinity,
});

if (cert.cert.delegation) {
throw new CertificateVerificationError('Delegation certificates cannot be nested');
}

await cert.verify();

const canisterInRange = check_canister_ranges({
canisterId: this._canisterId,
subnetId: Principal.fromUint8Array(new Uint8Array(d.subnet_id)),
Expand Down

0 comments on commit 41c2563

Please sign in to comment.