-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into mnl/use-setup-dfx-gh-action
- Loading branch information
Showing
3 changed files
with
169 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | ||
} | ||
``` |