Skip to content
Permalink
Browse files

Specs for PKI

  • Loading branch information...
pelle committed Nov 25, 2017
1 parent 696d0e7 commit 347276a42f720561193e37b46b8199773b9ce7c2
@@ -1,9 +1,13 @@
# adapted from https://gist.github.com/hjst/4f2f2c2ca9bd550e50c7f06cb17775b2

PLANTUML_JAR_URL = https://sourceforge.net/projects/plantuml/files/plantuml.jar/download
DIAGRAMS_SRC := $(wildcard **/*.plantuml)
DIAGRAMS_PNG := $(addsuffix .png, $(basename $(DIAGRAMS_SRC)))
DIAGRAMS_SVG := $(addsuffix .svg, $(basename $(DIAGRAMS_SRC)))

# Default target first; build PNGs, probably what we want most of the time
all: png svg

# build PNGs, probably what we want most of the time
png: plantuml.jar $(DIAGRAMS_PNG)

# SVG are nice-to-have but don't need to build by default
@@ -63,6 +63,4 @@ Ethereum transactions can be requested to be signed by the mobile app

Uport implements a simple yet general purpose decentralized PKI system making it easy to create and verify offchain JWT messages.

### Identity Document
### Register Identity Document
### Lookup Identity Document
### [More about the Uport PKI](pki/index.md)
@@ -22,17 +22,17 @@ Name | Description | Required

In most cases we recommend having a single claim per verification. This allows your users to share only the specific claims they want.

'''json
```json
{"name":"Carol Crypteau"}
'''
```

### Bundling multiple claims as one

Some times multiple claims are actually a single claim. A good example is an address which can consist of multiple fields. In that case we recommend nesting them under a single claim like this:

'''json
```json
{"address":{"streetAddress":"12345 Buterin Lane", "postalCode":"123133", "addressLocality":"Toronto", "addressRegion":"ON", "addressCountry":"CA"}}
'''
```

### Claim Taxonomy

@@ -0,0 +1,120 @@
# Uport PKI

Uport implements a simple yet general purpose decentralized PKI system making it easy to create and verify offchain JWT messages.

## Purpose

We need a decentralized way to lookup public keys that can be used to verify off-chain JWTs. This allows us to use the power of the Ethereum blockchain to verify signed data privately transfered between parties.

The PKI is not needed for blockchain transactions themselves as any blockchain already has a PKI like functionality built in.

It could be used for signing other data formats than JWT, but we are primarily using it with JWTs.

## Creating and Verifying a JWT

The following overview shows the basic process for creating and verifiying a trusted off-chain transaction between two parties using the Uport PKI.

![Create and Verify Data](jwtflow.png)

## Identity Document

The Identity Document is a [JSON LD](https://json-ld.org) document stored on IPFS. Here is an example:

[IPFS Hash QmNVHxsMAXvYktfHLYhRjcL7hGBBojTwY7mnJKsJweCZJK]:https://ipfs.infura.io/ipfs/QmNVHxsMAXvYktfHLYhRjcL7hGBBojTwY7mnJKsJweCZJK

```json
{"@context":"http://schema.org","@type":"Person","publicKey":"0x04848b547c6effe251b6e9f69c3bc6845b7997963554703aa41bc1b4c8d8db787ac966938139d5b36f404b89727fbc279153a20ad43ff25da0c30edb8b84d9c836","publicEncKey":"bpEGZfAtubOkFSsIdZFSlMN30hYlNOjHzS7LJgep82A="}
```

Since it is a JSON LD document you can place all kinds of data in there such as name, location etc.

### `publicKey` item

The most important part for the function of the PKI itself is that it contains the `publicKey` of the identity.

This is a `0x` prefixed hex encoded DER encoded public key for the [secp256k1 ECDSA curve](https://en.bitcoin.it/wiki/Secp256k1).

### `publicEncKey` item

This is an encryption public key created for use with [NACL Box Public Key Encryption](http://nacl.cr.yp.to/box.html).

TODO: Show how to create and encode this

### `name` item

The Uport app and other apps presenting signed JWT's to their users will use the [`name`](http://schema.org/name) item stored in the Identity Document.

### `image` item

The Uport app and other apps presenting signed JWT's to their users will use the [`image`](http://schema.org/image) item stored in the Identity Document.

The image currently has to be stored in IPFS and the format should look like this:

```json
{"image":{"contentUrl":"/ipfs/HASH"}}
```

## Verifying a signature

Any [Signed Message](../messages/index.md) has an `iss` attribute. This contains an [MNID](https://github.com/uport-project/mnid).

An MNID contains an ethereum address, the network id as well as a checksum.

### Resolving the Public Key for `iss`

![Resolve Public Key](resolve.png)

1. Decode the MNID of the `iss` and extract the `network` and the `address`
1. In the [uport-registry](https://github.com/uport-project/uport-registry) for the `network` call the function `get("uPortProfileIPFS1220", address, address)` which returns a hash value encoded as a 32 bytes
1. Encode the IPFS hash by prepending hex `1220` to the 32 byte hash and encoding it as base58
1. Fetch JSON Identity Document from IPFS using IPFS hash
1. Public Key is stored in the `publicKey` key of the Identity Document

## Registering an Identity Document

Any address on any supported Ethereum blockchain can register it's identity document on the [uport-registry](https://github.com/uport-project/uport-registry).

This shows the basic process:

![Identity Registration](registration.png)

### External Accounts (Key Pairs)

1. Generate a Key Pair
1. Create an Identity Document containing the Public Key
1. Publish Identity Document to IPFS
1. Decode IPFS hash returned to get the raw 32 byte hash value
1. Pick an Ethereum network to register your identity on
1. Generate the Ethereum address for your Key Pair
1. Create a transaction in the [uport-registry](https://github.com/uport-project/uport-registry) for the `network` for the function `set("uPortProfileIPFS1220", address, hash)` signed by your Key Pair

### Smart Contract Accounts

Smart Contracts can't sign on their own, so a signing Key Pair will need to be created first.

1. Generate a Key Pair
1. Create an Identity Document containing the Public Key
1. Publish Identity Document to IPFS
1. Decode IPFS hash returned to get the raw 32 byte hash value
1. With your smart contract code create an internal transaction to the [uport-registry](https://github.com/uport-project/uport-registry) on the `network` that your smart contract is deployed to for the function `set("uPortProfileIPFS1220", address, hash)`

TODO example solidity code for registering an Identity for your Smart Contract

### Uport Mobile App Created Identities

Identities created by the Uport Mobile App consist of a simple [Proxy](https://github.com/uport-project/uport-identity/blob/develop/contracts/Proxy.sol) smart contract controlled by a flexible access control smart contract that we call the [IdentityManager](https://github.com/uport-project/uport-identity/blob/develop/contracts/IdentityManager.sol).

This structure allows us to create recoverable identities controlled by multiple devices and even allows us to safely upgrade the complex access control logic.

The way an identity is created in the Mobile App can be seen here:

![Mobile Identity Registration](mobileregistration.png)

1. Generate a Key Pair on your Uport app
1. Pick an Ethereum network to register your identity on
1. Create an Ethereum transaction registering a [Proxy](https://github.com/uport-project/uport-identity/blob/develop/contracts/Proxy.sol) using the [IdentityManager](https://github.com/uport-project/uport-identity/blob/develop/contracts/IdentityManager.sol)
1. Create an Identity Document containing the Public Key
1. Publish Identity Document to IPFS
1. Decode IPFS hash returned to get the raw 32 byte hash value
1. Create a transaction on the IdentityManager that forwards a transaction to the [uport-registry](https://github.com/uport-project/uport-registry) calling the function `set("uPortProfileIPFS1220", address, hash)` signed by your Key Pair

@@ -0,0 +1,17 @@
@startuml
!include ../uportskin.plantuml
title Identity Resolving
actor Sender
participant IdentityWallet
database UportRegistry
database IPFS
actor Recipient

Sender -> IdentityWallet : Sign JWT
IdentityWallet -> Sender: Signed JWT
Sender -> Recipient: Signed JWT
Recipient <-> UportRegistry: Lookup IPFS hash for iss
Recipient <-> IPFS: Identity Document for IPFS hash
Recipient -> Recipient: Extract PublicKey from Identity Document
Recipient -> Recipient: Verify signature using PublicKey
@enduml
BIN +31.5 KB pki/jwtflow.png
Binary file not shown.
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="455px" preserveAspectRatio="none" style="width:848px;height:455px;" version="1.1" viewBox="0 0 848 455" width="848px" zoomAndPan="magnify"><defs><filter height="300%" id="f1sasfxa4blaaz" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><text fill="#000000" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacingAndGlyphs" textLength="132" x="357" y="23.5352">Identity Resolving</text><line style="stroke: #878787; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="35" x2="35" y1="116.9766" y2="368.1504"/><line style="stroke: #878787; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="137" x2="137" y1="116.9766" y2="368.1504"/><line style="stroke: #878787; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="262" x2="262" y1="116.9766" y2="368.1504"/><line style="stroke: #878787; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="340" x2="340" y1="116.9766" y2="368.1504"/><line style="stroke: #878787; stroke-width: 1.0; stroke-dasharray: 5.0,5.0;" x1="565.5" x2="565.5" y1="116.9766" y2="368.1504"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="48" x="8" y="114.0234">Sender</text><ellipse cx="35" cy="43.4883" fill="#FFFFFF" filter="url(#f1sasfxa4blaaz)" rx="8" ry="8" style="stroke: #6959DB; stroke-width: 2.0;"/><path d="M35,51.4883 L35,78.4883 M22,59.4883 L48,59.4883 M35,78.4883 L22,93.4883 M35,78.4883 L48,93.4883 " fill="#FFFFFF" filter="url(#f1sasfxa4blaaz)" style="stroke: #6959DB; stroke-width: 2.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="48" x="8" y="380.6855">Sender</text><ellipse cx="35" cy="393.6387" fill="#FFFFFF" filter="url(#f1sasfxa4blaaz)" rx="8" ry="8" style="stroke: #6959DB; stroke-width: 2.0;"/><path d="M35,401.6387 L35,428.6387 M22,409.6387 L48,409.6387 M35,428.6387 L22,443.6387 M35,428.6387 L48,443.6387 " fill="#FFFFFF" filter="url(#f1sasfxa4blaaz)" style="stroke: #6959DB; stroke-width: 2.0;"/><rect fill="#FFFFFF" filter="url(#f1sasfxa4blaaz)" height="30.4883" rx="5" ry="5" style="stroke: #6959DB; stroke-width: 1.5;" width="106" x="82" y="81.4883"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="92" x="89" y="102.0234">IdentityWallet</text><rect fill="#FFFFFF" filter="url(#f1sasfxa4blaaz)" height="30.4883" rx="5" ry="5" style="stroke: #6959DB; stroke-width: 1.5;" width="106" x="82" y="367.1504"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="92" x="89" y="387.6855">IdentityWallet</text><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="94" x="212" y="114.0234">UportRegistry</text><path d="M244,64.4883 C244,54.4883 262,54.4883 262,54.4883 C262,54.4883 280,54.4883 280,64.4883 L280,90.4883 C280,100.4883 262,100.4883 262,100.4883 C262,100.4883 244,100.4883 244,90.4883 L244,64.4883 " fill="#FEFECE" filter="url(#f1sasfxa4blaaz)" style="stroke: #000000; stroke-width: 1.5;"/><path d="M244,64.4883 C244,74.4883 262,74.4883 262,74.4883 C262,74.4883 280,74.4883 280,64.4883 " fill="none" style="stroke: #000000; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="94" x="212" y="380.6855">UportRegistry</text><path d="M244,393.6387 C244,383.6387 262,383.6387 262,383.6387 C262,383.6387 280,383.6387 280,393.6387 L280,419.6387 C280,429.6387 262,429.6387 262,429.6387 C262,429.6387 244,429.6387 244,419.6387 L244,393.6387 " fill="#FEFECE" filter="url(#f1sasfxa4blaaz)" style="stroke: #000000; stroke-width: 1.5;"/><path d="M244,393.6387 C244,403.6387 262,403.6387 262,403.6387 C262,403.6387 280,403.6387 280,393.6387 " fill="none" style="stroke: #000000; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="28" x="323" y="114.0234">IPFS</text><path d="M322,64.4883 C322,54.4883 340,54.4883 340,54.4883 C340,54.4883 358,54.4883 358,64.4883 L358,90.4883 C358,100.4883 340,100.4883 340,100.4883 C340,100.4883 322,100.4883 322,90.4883 L322,64.4883 " fill="#FEFECE" filter="url(#f1sasfxa4blaaz)" style="stroke: #000000; stroke-width: 1.5;"/><path d="M322,64.4883 C322,74.4883 340,74.4883 340,74.4883 C340,74.4883 358,74.4883 358,64.4883 " fill="none" style="stroke: #000000; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="28" x="323" y="380.6855">IPFS</text><path d="M322,393.6387 C322,383.6387 340,383.6387 340,383.6387 C340,383.6387 358,383.6387 358,393.6387 L358,419.6387 C358,429.6387 340,429.6387 340,429.6387 C340,429.6387 322,429.6387 322,419.6387 L322,393.6387 " fill="#FEFECE" filter="url(#f1sasfxa4blaaz)" style="stroke: #000000; stroke-width: 1.5;"/><path d="M322,393.6387 C322,403.6387 340,403.6387 340,403.6387 C340,403.6387 358,403.6387 358,393.6387 " fill="none" style="stroke: #000000; stroke-width: 1.5;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="63" x="531.5" y="114.0234">Recipient</text><ellipse cx="566" cy="43.4883" fill="#FFFFFF" filter="url(#f1sasfxa4blaaz)" rx="8" ry="8" style="stroke: #6959DB; stroke-width: 2.0;"/><path d="M566,51.4883 L566,78.4883 M553,59.4883 L579,59.4883 M566,78.4883 L553,93.4883 M566,78.4883 L579,93.4883 " fill="#FFFFFF" filter="url(#f1sasfxa4blaaz)" style="stroke: #6959DB; stroke-width: 2.0;"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacingAndGlyphs" textLength="63" x="531.5" y="380.6855">Recipient</text><ellipse cx="566" cy="393.6387" fill="#FFFFFF" filter="url(#f1sasfxa4blaaz)" rx="8" ry="8" style="stroke: #6959DB; stroke-width: 2.0;"/><path d="M566,401.6387 L566,428.6387 M553,409.6387 L579,409.6387 M566,428.6387 L553,443.6387 M566,428.6387 L579,443.6387 " fill="#FFFFFF" filter="url(#f1sasfxa4blaaz)" style="stroke: #6959DB; stroke-width: 2.0;"/><polygon fill="#6959DB" points="125,143.9766,135,147.9766,125,151.9766,129,147.9766" style="stroke: #6959DB; stroke-width: 1.0;"/><line style="stroke: #6959DB; stroke-width: 1.0;" x1="35" x2="131" y1="147.9766" y2="147.9766"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="54" x="42" y="143.5449">Sign JWT</text><polygon fill="#6959DB" points="46,173.2871,36,177.2871,46,181.2871,42,177.2871" style="stroke: #6959DB; stroke-width: 1.0;"/><line style="stroke: #6959DB; stroke-width: 1.0;" x1="40" x2="136" y1="177.2871" y2="177.2871"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="69" x="52" y="172.8555">Signed JWT</text><polygon fill="#6959DB" points="554,202.5977,564,206.5977,554,210.5977,558,206.5977" style="stroke: #6959DB; stroke-width: 1.0;"/><line style="stroke: #6959DB; stroke-width: 1.0;" x1="35" x2="560" y1="206.5977" y2="206.5977"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="69" x="42" y="202.166">Signed JWT</text><polygon fill="#6959DB" points="273,231.9082,263,235.9082,273,239.9082,269,235.9082" style="stroke: #6959DB; stroke-width: 1.0;"/><polygon fill="#6959DB" points="554,231.9082,564,235.9082,554,239.9082,558,235.9082" style="stroke: #6959DB; stroke-width: 1.0;"/><line style="stroke: #6959DB; stroke-width: 1.0;" x1="267" x2="560" y1="235.9082" y2="235.9082"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="154" x="279" y="231.4766">Lookup IPFS hash for iss</text><polygon fill="#6959DB" points="351,261.2188,341,265.2188,351,269.2188,347,265.2188" style="stroke: #6959DB; stroke-width: 1.0;"/><polygon fill="#6959DB" points="554,261.2188,564,265.2188,554,269.2188,558,265.2188" style="stroke: #6959DB; stroke-width: 1.0;"/><line style="stroke: #6959DB; stroke-width: 1.0;" x1="345" x2="560" y1="265.2188" y2="265.2188"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="202" x="357" y="260.7871">Identity Document for IPFS hash</text><line style="stroke: #6959DB; stroke-width: 1.0;" x1="566" x2="608" y1="294.8398" y2="294.8398"/><line style="stroke: #6959DB; stroke-width: 1.0;" x1="608" x2="608" y1="294.8398" y2="307.8398"/><line style="stroke: #6959DB; stroke-width: 1.0;" x1="567" x2="608" y1="307.8398" y2="307.8398"/><polygon fill="#6959DB" points="577,303.8398,567,307.8398,577,311.8398,573,307.8398" style="stroke: #6959DB; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="263" x="573" y="290.0977">Extract PublicKey from Identity Document</text><line style="stroke: #6959DB; stroke-width: 1.0;" x1="566" x2="608" y1="337.1504" y2="337.1504"/><line style="stroke: #6959DB; stroke-width: 1.0;" x1="608" x2="608" y1="337.1504" y2="350.1504"/><line style="stroke: #6959DB; stroke-width: 1.0;" x1="567" x2="608" y1="350.1504" y2="350.1504"/><polygon fill="#6959DB" points="577,346.1504,567,350.1504,577,354.1504,573,350.1504" style="stroke: #6959DB; stroke-width: 1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacingAndGlyphs" textLength="202" x="573" y="332.4082">Verify signature using PublicKey</text><!--
@startuml
skinparam roundcorner 10
skinparam BoxPadding 10
skinparam headerFontColor #6959DB
skinparam headerFontSize 18
skinparam sequence {
ArrowColor #6959DB
GroupBackgroundColor #6959DB
GroupBorderColor #EDECFF
GroupHeaderFontColor #ffffff
BoxBorderColor #6959DB
BoxBackgroundColor #EDECFF
BoxPadding 10

ParticipantBorderColor #6959DB
ParticipantBackgroundColor #ffffff
ParticipantPadding 10

ActorBorderColor #6959DB
ActorBackgroundColor #ffffff
EntityBorderColor #6959DB
EntityBackgroundColor #ffffff
DatabaseBorderColor #6959DB
DatabaseBackgroundColor #ffffff

LifeLineBorderColor #878787
}

title Identity Resolving
actor Sender
participant IdentityWallet
database UportRegistry
database IPFS
actor Recipient

Sender -> IdentityWallet : Sign JWT
IdentityWallet -> Sender: Signed JWT
Sender -> Recipient: Signed JWT
Recipient <-> UportRegistry: Lookup IPFS hash for iss
Recipient <-> IPFS: Identity Document for IPFS hash
Recipient -> Recipient: Extract PublicKey from Identity Document
Recipient -> Recipient: Verify signature using PublicKey
@enduml

PlantUML version 1.2017.19(Sun Nov 12 05:46:22 CST 2017)
(GPL source distribution)
Java Runtime: Java(TM) SE Runtime Environment
JVM: Java HotSpot(TM) 64-Bit Server VM
Java Version: 1.8.0_144-b01
Operating System: Mac OS X
OS Version: 10.13.1
Default Encoding: UTF-8
Language: en
Country: US
--></g></svg>
Oops, something went wrong.

0 comments on commit 347276a

Please sign in to comment.
You can’t perform that action at this time.