Verifying the Internet Identity Code: A Walkthrough

Jun 6 · 5 min read

A step-by-step guide to confirming that the Internet Identity blockchain authentication system is running the code it is claimed to run.

By Joachim Breitner, Senior Researcher & Engineer | DFINITY

On the Internet Computer, users can use the Internet Identity cryptographic authentication system to log into various dapps, such as the NNS dapp, OpenChat, and others. In doing so, they are trusting this service to take good care of their credentials — but they may want to directly confirm that Internet Identity is really not tracking them.

Is the Internet Identity truly running the code it is claimed to run? To help you answer this question, I’ll walk you through the steps of verifying this.

(The following applies to other canisters as well, of course, but I’ll stick to Internet Identity in this case.)

Find out what is running

A smart contract on the Internet Computer, i.e. a canister smart contract, is a WebAssembly module. The Internet Computer intentionally does not allow you to just download the Wasm code of any canisters, because maybe some developers want to keep their code private. But it does expose a hash of the Wasm module. The easiest way to get this is using dfx:

$ dfx canister --no-wallet --network ic info rdmx6-jaaaa-aaaaa-aaadq-cai

The “controller” here is the canister id of the governance canister. This tells you that Internet Identity is controlled by the Network Nervous System (NNS), and its code can only be changed via proposals that are voted on. This is good; if the controller was just, say, me, I could just change the Internet Identity code and take over all your identities.

The “Module hash” is the SHA-256 hash of the .wasm that was deployed. So let’s follow that trace.

Finding the right commit

Since upgrades to Internet Identity are done via proposals to the NNS, we should find a description of such a proposal in the repository, within the proposals/network_canister_management directory.

Github’s list of recent NNS proposals

We have to find the latest proposal upgrading Internet Identity. The folder unfortunately contains proposals for many canisters, and the file naming isn’t super helpful. I usually go through the list from the bottom and look at the second column, which contains the title of the latest commit creating or modifying a file.

In this case, the second to last is the one we care about: This file lists rationales, gives an overview of changes, and most importantly, says that bd51eab is the commit we are upgrading to.

The file also says that the wasm hash is d4a...c04, which matches what we saw above. This is good: it seems that we really found the youngest proposal upgrading Internet Identity, and that the proposal actually went through.

WARNING: If you are paranoid, don’t trust this file. There is nothing preventing a proposal proposer to create a file pointing to one revision while actually including different code in the proposal. That’s why the next steps are necessary for verification.

Getting the source

Now that we have the revision, we can get the source and check out revision bd51eab:

/tmp $ git clone
Klone nach 'internet-identity' ...
remote: Enumerating objects: 3959, done.
remote: Counting objects: 100% (344/344), done.
remote: Compressing objects: 100% (248/248), done.
remote: Total 3959 (delta 161), reused 207 (delta 92), pack-reused 3615
Empfange Objekte: 100% (3959/3959), 6.05 MiB | 3.94 MiB/s, Fertig.
Löse Unterschiede auf: 100% (2290/2290), Fertig.
/tmp $ cd internet-identity/
/tmp/internet-identity $ git checkout bd51eab
/tmp/internet-identity $ git log --oneline -n 1
bd51eab (HEAD, tag: mainnet-20210527T2203Z) Registers the seed phrase before showing it (#301)

In the last line you see that Internet Identity team has tagged that revision with a tag name that contains the proposal description file name. Very tidy!

Reproducing the build has the following build instructions:

It actually suffices to run the first command, as it also prints the hash (we don’t need to copy the .wasm out of the Docker canister):

/tmp/internet-identity $ docker build -t internet-identity-service .

Step 26/26 : RUN sha256sum internet_identity.wasm
---> Running in 1a04644b544c
d4af9277f3e8d26fd8cdc7874a9f47b6456587fbb2a64d61b6b6880d144d3c04 internet_identity.wasm
Removing intermediate container 1a04644b544c
---> bfe6a63a7980
Successfully built bfe6a63a7980
Successfully tagged internet-identity-service:latest

Success! The hashes match.

You don’t believe me? Try it yourself (and let us know if you get a different hash — maybe I got hacked). This may fail if you don’t have enough RAM configured for Docker; 8GB should be enough.

At this point you have a trust path from the code sitting in front of you to Internet Identity running at, including the front-end code, and you can start auditing the source code.

What about the canister id?

If you paid close attention, you might have noticed that we got the module for canister rdmx6-jaaaa-aaaaa-aaadq-cai, but we are accessing a web application at So where is this connection?

In the future, I expect some form of a DNS-like “nice host name registry” on the Internet Computer that stores a mapping from nice names to canister ids, and that you will be able to query that for “which canister serves rdmx6-jaaaa-aaaaa-aaadq-cai” in a secure way (e.g. using certified variables). But since we don’t have that yet, but still want you to be able to use a nice name for Internet Identity (and not have to change the name later, which would cause headaches), we have hard-coded this mapping for now.

The relevant code here is the “Certifying Service Worker” that your browser downloads when accessing any * URL. This piece of code will then intercept all requests to that domain, map it to a query call, and then use certified variables to validate the response. And indeed, the mapping is in the code there:

const hostnameCanisterIdMap: Record<string, [string, string]> = {
'': ['rdmx6-jaaaa-aaaaa-aaadq-cai', ''],
'': ['qoctq-giaaa-aaaaa-aaaea-cai', ''],
'': ['h5aet-waaaa-aaaab-qaamq-cai', ''],

What about other canisters?

In principle, the same approach works for other canisters, whether it’s OpenChat, the NNS canisters, etc. But the details will differ, as every canister developer might have their own way of:

  • communicating the location and revision of the source for their canisters
  • building the canisters

In particular, without a reproducible way of building the canister, this will fail, and that’s why projects like are so important in general.

If you want to discuss this post, please consider joining the DFINITY forum and commenting at:

Start building at and join our developer community at

The Internet Computer Review

A DFINITY Foundation Resource

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store