Skip to content

Commit

Permalink
Merge branch 'main' into mnl/use-setup-dfx-gh-action
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcin Nowak-Liebiediew authored Nov 1, 2023
2 parents ccee073 + 8d7454b commit dbca738
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 4 deletions.
43 changes: 39 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,6 @@ jobs:
- name: Build NPM packages
run: pnpm build

- name: Pack @dfinity/response-verification NPM package
working-directory: packages/ic-response-verification-wasm
run: npm pack --pack-destination ../../

- name: Generate release notes
run: cz changelog ${{ github.ref_name }} --file-name RELEASE_NOTES.md

Expand All @@ -58,6 +54,29 @@ jobs:
env:
CRATES_TOKEN: ${{ secrets.CRATES_TOKEN }}

# `ic-certification-testing` cannot be published since it relies on unpublished crates
# from the `ic` repository. Namely:
# - ic-types
# - ic-crypto-tree-hash
# - ic-crypto-internal-threshold-sig-bls12381
# - ic-crypto-internal-seed
# - ic-crypto-internal-types
#
# - name: Release ic-certification-testing Cargo crate
# run: cargo publish -p ic-certification-testing --token ${CRATES_TOKEN}
# env:
# CRATES_TOKEN: ${{ secrets.CRATES_TOKEN }}

- name: Pack @dfinity/certification-testing NPM package
working-directory: packages/ic-certification-testing-wasm
run: npm pack --pack-destination ../../

- name: Release @dfinity/certification-testing NPM package
working-directory: packages/ic-certification-testing-wasm
run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Release ic-cbor Cargo crate
run: cargo publish -p ic-cbor --token ${CRATES_TOKEN}
env:
Expand All @@ -68,11 +87,25 @@ jobs:
env:
CRATES_TOKEN: ${{ secrets.CRATES_TOKEN }}

- name: Pack @dfinity/certificate-verification NPM package
working-directory: packages/certificate-verification-js
run: npm pack --pack-destination ../../

- name: Release @dfinity/certificate-verification NPM package
working-directory: packages/certificate-verification-js
run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Release ic-response-verification Cargo crate
run: cargo publish -p ic-response-verification --token ${CRATES_TOKEN}
env:
CRATES_TOKEN: ${{ secrets.CRATES_TOKEN }}

- name: Pack @dfinity/response-verification NPM package
working-directory: packages/ic-response-verification-wasm
run: npm pack --pack-destination ../../

- name: Release @dfinity/response-verification NPM package
working-directory: packages/ic-response-verification-wasm
run: npm publish --access public
Expand All @@ -88,6 +121,8 @@ jobs:
target/package/ic-cbor-${{ github.ref_name }}.crate,
target/package/ic-certificate-verification-${{ github.ref_name }}.crate,
target/package/ic-response-verification-${{ github.ref_name }}.crate,
dfinity-certification-testing-${{ github.ref_name }}.tgz,
dfinity-certificate-verification-${{ github.ref_name }}.tgz,
dfinity-response-verification-${{ github.ref_name }}.tgz
bodyFile: 'RELEASE_NOTES.md'
tag: '${{ github.ref_name }}'
Expand Down
58 changes: 58 additions & 0 deletions packages/ic-certification-testing-wasm/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,59 @@
# Certification Testing

[Certificate verification](https://internetcomputer.org/docs/current/references/ic-interface-spec#canister-signatures) on the [Internet Computer](https://dfinity.org) is the process of verifying that a canister's response to a [query call](https://internetcomputer.org/docs/current/references/ic-interface-spec#http-query) has gone through consensus with other replicas hosting the same canister.

This package provides a set of utilities to create these certificates for the purpose of testing in any Javascript client with `wasm` support that may need to verify them.

## Usage

First, a hash tree must be created containing the data that needs to be certified. This can be done using the [@dfinity/agent](https://www.npmjs.com/package/@dfinity/agent) library. The root hash of this tree is then used to create the certificate.

The [@dfinity/certificate-verification](https://www.npmjs.com/package/@dfinity/certificate-verification) library can then be used to decode the certificate and verify it.

```typescript
import { describe, expect, it } from 'vitest';
import { HashTree, reconstruct, Cbor } from '@dfinity/agent';
import { CertificateBuilder } from '@dfinity/certification-testing';
import { verifyCertification } from '@dfinity/certificate-verification';
import { Principal } from '@dfinity/principal';
import { createHash, webcrypto } from 'node:crypto';

globalThis.crypto = webcrypto as Crypto;

const userId = '1234';

const username = 'testuser';
const usernameHash = new Uint8Array(
createHash('sha256').update(username).digest(),
);

const hashTree: HashTree = [
2,
new Uint8Array(Buffer.from(userId)),
[3, usernameHash],
];
const rootHash = await reconstruct(hashTree);
const cborEncodedTree = Cbor.encode(hashTree);

const canisterId = Principal.fromUint8Array(
new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1]),
);
const time = BigInt(Date.now());
const MAX_CERT_TIME_OFFSET_MS = 300_000;

let certificate = new CertificateBuilder(
canisterId.toString(),
new Uint8Array(rootHash),
)
.withTime(time)
.build();

const decodedHashTree = await verifyCertification({
canisterId,
encodedCertificate: certificate.cborEncodedCertificate,
encodedTree: cborEncodedTree,
maxCertificateTimeOffsetMs: MAX_CERT_TIME_OFFSET_MS,
rootKey: certificate.rootKey,
});
expect(decodedHashTree).toEqual(hashTree);
```
72 changes: 72 additions & 0 deletions packages/ic-certification-testing/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,73 @@
# Certification Testing

[Certificate verification](https://internetcomputer.org/docs/current/references/ic-interface-spec#canister-signatures) on the [Internet Computer](https://dfinity.org) is the process of verifying that a canister's response to a [query call](https://internetcomputer.org/docs/current/references/ic-interface-spec#http-query) has gone through consensus with other replicas hosting the same canister.

This package provides a set of utilities to create these certificates for the purpose of testing in any Rust client that may need to verify them.

## Usage

First, a hash tree must be created containing the data that needs to be certified. This can be done using the [ic-certified-map](https://docs.rs/ic-certified-map/latest/ic_certified_map/) library. The root hash of this tree is then used to create the certificate.

The [ic-certification](https://docs.rs/ic-certification/latest/ic_certification/), [ic-cbor](https://docs.rs/ic-cbor/latest/ic_cbor/) and [ic-certificate-verification](https://docs.rs/ic-certificate-verification/latest/ic_certificate_verification/) libraries can then be used to decode the certificate and verify it.

```rust
use ic_certification_testing::{CertificateBuilder, CertificateData};
use ic_cbor::CertificateToCbor;
use ic_certificate_verification::VerifyCertificate;
use ic_certification::Certificate;
use ic_certified_map::{AsHashTree, RbTree};
use ic_types::CanisterId;
use sha2::{Digest, Sha256};
use std::time::{SystemTime, UNIX_EPOCH};

type Hash = [u8; 32];

fn hash<T>(data: T) -> Hash
where
T: AsRef<[u8]>,
{
let mut hasher = Sha256::new();
hasher.update(data);
hasher.finalize().into()
}

fn get_timestamp() -> u128 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_nanos()
}

fn usage_example() {
let canister_id = CanisterId::from_u64(42);
let mut rb_tree = RbTree::<&'static str, Hash>::new();

let data_key = "key1";
let data_hash = hash("value1");
rb_tree.insert(data_key, data_hash);

let certified_data = rb_tree.root_hash();

let current_timestamp = get_timestamp();

let mut certificate_builder =
CertificateBuilder::new(&canister_id.get().0.to_text(), &certified_data)
.expect("Failed to parse canister id");

let CertificateData {
cbor_encoded_certificate,
root_key,
certificate: _,
} = certificate_builder
.with_time(current_timestamp)
.build()
.expect("Invalid certificate params provided");

let certificate = Certificate::from_cbor(&cbor_encoded_certificate)
.expect("Failed to deserialize certificate");

certificate
.verify(&canister_id.get().to_vec(), &root_key)
.expect("Failed to verify certificate");
}
```

0 comments on commit dbca738

Please sign in to comment.