Closed: BNT-8 - vetKeys - Enabling Privacy Preserving Applications on the IC

Add this to your webpack.config.js file may fix your problem.

  experiments: {
    asyncWebAssembly: true,
  },
  module: {
    rules: [
      {
        test: /\.wasm$/,
        type: "webassembly/async", // This is the key part
      },
      // other roles...
    ],
  },

I use rxjs in my application, maybe my approach will work for you.
If you use typescript add custom.d.ts file and write:

declare module 'vetkd_user_lib/ic_vetkd_utils' {
    export class TransportSecretKey {
        constructor(seed: Uint8Array);
        free(): void;
        public_key(): Uint8Array;
        decrypt_and_hash(
            encrypted_key_bytes: Uint8Array,
            derived_public_key_bytes: Uint8Array,
            derivation_id: Uint8Array,
            symmetric_key_bytes: number,
            symmetric_key_associated_data: Uint8Array
        ): Uint8Array;
    }
}

I did the loading of the wasm module from bytes as follows:

import { fromFetch } from 'rxjs/fetch';
import { map } from 'rxjs/operators';
import { TransportSecretKey, initSync } from 'vetkd_user_lib/ic_vetkd_utils';

export function loadWasm() {
    return fromFetch('vetkd_user_lib/ic_vetkd_utils_bg.wasm', {
        selector: response => response.arrayBuffer()
    }).pipe(
        map(buffer => {
            const module = new WebAssembly.Module(buffer);
            return initSync(module);
        })
    );
}

The loadWasm operator I call in the web worker:

// Actually i use state manager and do something like
// state.connect('wasmLoaded', loadWasm().pipe(map(() => true)));
// but you can just subscribe to loadWasm
loadWasm().subscribe(); 

Thanks for the pointers! The webpack changes solved part of the problem. I’ve been working on this for a while and have copied in vetkd stuff to my code from various sources, so rather than trying to trace back through the changes I started afresh and replaced all my vetkd content with the tgz and src/system_api from https://github.com/dfinity/examples/tree/master/motoko/vetkd. This made the project deployable (again) so now I’m sifting through the finer details and will hopefully have it debugged in time for the deadline.

If PNPM is causing problems then it may be better to leave that for after the deadline, but if you decide to give it another shot, sharing your project will make it easier to know what’s wrong. You shouldn’t need any changes to Webpack or Typescript declaration files to make this approach work.

1 Like

I found PNPM really good to use. I ended up making a few tweaks. The full set of changes were as follows:

vetkd_utils/Cargo.toml: unchanged from yours

vetkd_utils/package.json:

{
  "name": "vetkd-utils",
  "private": true,
  "scripts": {
    "build": "wasm-pack build --target web --release"
  },
  "module": "pkg/vetkd_utils.js",
  "types": "pkg/vetkd_utils.d.ts",
  "main": "src/lib.rs"    [added this line]
}

vetkd_utils/src/lib.rs:

#[no_mangle]
pub use ic_vetkd_utils::*;

package.json (main) - added:

  "dependencies": {
    "vetkd-utils": "workspace:*",
    ...
  }

webpack.config.js - added:

module.exports = {
  experiments: {
    asyncWebAssembly: true,
  },
  optimization: {
  module: {
    rules: [
      { test: /\.rs$/, use: [
        {loader: 'wasm-loader'},
        {loader: 'rust-native-wasm-loader', options: {release: true}}
      ]},
      { test: /\.wasm$/, type: "webassembly/async",},
    ]
  },
  ...
}

This made the project deployable, but I then started getting

Uncaught runtime errors:
ERROR
vetkd_utils__WEBPACK_IMPORTED_MODULE_3__.TransportSecretKey is not a constructor
get_aes_256_gcm_key@http://localhost:8080/index.js:24970:15

The line referred to here was
const tsk = TransportSecretKey(seed);.
Changing it to
const tsk = vetkd_utils.TransportSecretKey(seed);.
gave
vetkd_utils__WEBPACK_IMPORTED_MODULE_3__.TransportSecretKey is undefined.

I couldn’t see the problem from looking at the Rust source code. If this worked well for you without making all these changes then I figured it must be an error (or errors) within the numerous small changes I’ve made to the provided code as I’ve been working on this. I can share this whole version of the project if you like but for now I’ve put this aside and am using the vetkd components from examples/motoko/vetkd. Once I have it working it fully I might try switching your method back in.

1 Like

We only decided to work on the bounty a few days ago. The submission deadline for the bounty is August 31st, but we don’t think we’ll be able to deliver at this time since there are only a few days left. Do you think we could extend the delivery submission deadline by 4 weeks? Thanks.

Awesome that you’d like to join the bounty.

In the recent ckBTC Point-of-Sale bounty we had an extension of one week. If there is a general request from participants to extend the deadline by a week, then we’d be open to it. A 4-week extension, however, could not be considered.

I see based on your Webpack config that you are expecting to import the .rs file directly, but that’s not the case. The build script from vetkd_utils/package.json will output JavaScript and WASM files.

If you manually run the wasm-pack build --target web --release command inside the vetkd_utils folder then you should see that the files are output to the vetkd_utils/pkg folder. This will be normally run by PNPM anyway, but running it manually first can help debug any issues with the command.

I didn’t need the main field, but I’m using Vite. If you need the main field for Webpack, it shouldn’t point to the src/lib.rs file, but to the pkg/vetkd_utils.js file instead.

With that in mind, the /\.rs$/ rules that you have in webpack.config.js are not necessary, since you’ll be importing JS files, not RS files. The other WASM configuration may be necessary, I’m not sure since I didn’t try this setup with Webpack.

One last point, I have three package.json files. It sounds like you have two. Mine is setup like this:

- lib/
  - vetkdutils/
    - src/
      - lib.rs
    - package.json
    - Cargo.toml
- src/
  - frontend/
    - package.json (reference "vetkdutils" here)
- package.json

I don’t think that’s an issue, but in case the above points don’t fix your problem, it may also be worth checking. To make dfx build work with a nested package.json, you need to supply a custom build step:

{
  "canisters": {
    // ...
    "frontend": {
      "dependencies": ["backend"],
      "frontend": {
        "entrypoint": "src/frontend/src/index.html"
      },
      "source": ["src/frontend/dist"],
      "type": "assets",
      "build": ["pnpm -F frontend... build"]
    },
    // ...
  },
  // ...
}
1 Like

I’d like to see the bounty deadline extended by a week if possible.

Yes, that’s right. I only had two package.json files. I think I’ve made it all somewhat more complicated than it needed to be. I’ll have a good look through that to get my head around it.

I’m making good headway with the alternative approach, borrowing from examples/motoko/vetkd. Most of the functionality is now working, except that I’m now getting invalid encrypted key: verification failed errors whenever I try to encrypt or decrypt.

Question: In these two functions -

    public shared ({ caller }) func symmetric_key_verification_key() : async Text {
        let { public_key } = await vetkd_system_api.vetkd_public_key({
            canister_id = null;
            derivation_path = Array.make(Text.encodeUtf8("symmetric_key"));
            key_id = { curve = #bls12_381; name = "test_key_1" };
        });
        Hex.encode(Blob.toArray(public_key));
    };

    public shared ({ caller }) func encrypted_symmetric_key_for_caller(encryption_public_key : Blob) : async Text {
        let { encrypted_key } = await vetkd_system_api.vetkd_encrypted_key({
            derivation_id = Principal.toBlob(caller);
            public_key_derivation_path = Array.make(Text.encodeUtf8("symmetric_key"));
            key_id = { curve = #bls12_381; name = "test_key_1" };
            encryption_public_key;
        });
        Hex.encode(Blob.toArray(encrypted_key));
    };

- is it permissible to change “symmetric key” to something else? I’ve gone with

  public shared({ caller }) func symmetric_key_verification_key(role: Role): async Text {
    let { public_key } = await vetkd_system_api.vetkd_public_key({
      canister_id = null;
      derivation_path = Array.make(Text.encodeUtf8(toText(role)));
      (etc)

and similarly for the second function, having previously defined type Role = { #Admin; #Manager; #User } and a private func toText function. Is this likely to upset the whole process?

1 Like

No problem from me. I’m excited to see more projects embracing this revolutionary new feature!

1 Like

Hello. I would like to participate in this bounty and I have implemented Secret Oracle in Rust, which most likely falls under the category of Group Sharing in the use case defined by domwoe. This is my first time participating in a bounty, so I would appreciate feedback on whether my demo meets your expectations.

3 Likes

Great job on getting started with your project! I noticed that it’s similar to the example from the demo post. To enhance your project, consider focusing on improving the user interface. Adding your own creative touch to the design can make a significant difference.

By the way, if the bounty gets extended for one more week, it could provide you with a bit more time to refine and enhance your project.

Keep up the good work, and I’m looking forward to seeing your project evolve!

3 Likes

@hawk Awesome project! Please refer to the acceptance criteria.

From what I see, you’re missing:

  • Provide feedback on the suggested system API (does it address your needs? A few sentences are enough)
  • Demo application deployed to the IC
  • Video Pitch/Demo (max. 4min)
2 Likes

I suggest we postpone the deadline to September 10th. I’ll update the official deadline if there’s no objection until tomorrow.

6 Likes

From @franzstefan

This is definitely permissible, yes. "symmetric_key" was just an example derivation path we used, but it could be anything. What’s important is that the same bytes are used when calling vetkd_system_api.vetkd_public_key and vetkd_system_api.vetkd_encrypted_key.

Are you sure in your case now that you’re using the same role value for derivation_path when calling vetkd_system_api.vetkd_public_key as you are for public_key_derivation_path when calling vetkd_system_api.vetkd_encrypted_key?

If you’re allowed to give extensions, am I allowed to give bonus points for a super nice UI? :slight_smile:

5 Likes

Sure, it’s the first evaluation criteria as it should be for a crypto heavy application :slight_smile:

1 Like

@domwoe - I notice that part of the acceptance criteria is that there’s a demo app deployed to the IC. This is probably a stupid question and I’m probably completely missing something here, but isn’t the vetKD api still to be implemented on the IC? Which means, the demo wouldn’t be functional?

You can deploy the canister vetkd_system_api and it should work, but yeah I mean it should not be used in prod applications until mainet launch of vetkd_system_api. However, it is good for demo.

2 Likes