Verifying the Internet Identity Code: A Walkthrough

DFINITY
The Internet Computer Review
5 min readJun 6, 2021

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 front-end 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-caiController: r7inp-6aaaa-aaaaa-aaabq-caiModule hash: 0xd4af9277f3e8d26fd8cdc7874a9f47b6456587fbb2a64d61b6b6880d144d3c04

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 https://github.com/ic-association/nns-proposals 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: https://github.com/ic-association/nns-proposals/blob/main/proposals/network_canister_management/20210527T2203Z.md. 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 https://github.com/dfinity/internet-identity
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

TheREADME.md 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 https://identity.ic0.app, 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 https://identity.ic0.app. 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 *.ic0.app 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]> = {
'identity.ic0.app': ['rdmx6-jaaaa-aaaaa-aaadq-cai', 'ic0.app'],
'nns.ic0.app': ['qoctq-giaaa-aaaaa-aaaea-cai', 'ic0.app'],
'dscvr.ic0.app': ['h5aet-waaaa-aaaab-qaamq-cai', 'ic0.page'],
};

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 https://reproducible-builds.org/ are so important in general.

If you want to discuss this post, please consider joining the DFINITY forum and commenting at: https://forum.dfinity.org/t/verifying-the-code-of-the-internet-identity-service-a-walk-through/4650
____

Start building at sdk.dfinity.org and join the developer community at forum.dfinity.org.

--

--

DFINITY
The Internet Computer Review

The Internet Computer is a revolutionary blockchain that hosts unlimited data and computation on-chain. Build scalable Web3 dapps, DeFi, games, and more.