Skip to content

Commit

Permalink
Add TDW resolve and server
Browse files Browse the repository at this point in the history
Signed-off-by: Marlon Baeten <[email protected]>
  • Loading branch information
marlonbaeten committed Oct 18, 2024
1 parent 61fa75b commit 21202b1
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ Cargo.lock
.dockerignore
docs/book/
data/
node_modules/
3 changes: 3 additions & 0 deletions tdw-server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
# Keep environment variables out of version control
.env
10 changes: 10 additions & 0 deletions tdw-server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Development

```sh
curl -fsSL https://bun.sh/install | bash
bun install
docker compose up
bunx prisma generate
DATABASE_URL=postgresql://[email protected]:5432/tsp-test bunx prisma db push
DOMAIN=tdw.tsp-test.org DATABASE_URL=postgresql://[email protected]:5432/tsp-test bun serve
```
Binary file added tdw-server/bun.lockb
Binary file not shown.
10 changes: 10 additions & 0 deletions tdw-server/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
services:
psql:
image: ghcr.io/tweedegolf/postgres:14
environment:
POSTGRES_USER: tsp-test
POSTGRES_DB: tsp-test
POSTGRES_HOST_AUTH_METHOD: trust
TZ: Europe/Amsterdam
ports: [ '5432:5432' ]
networks: [ default ]
17 changes: 17 additions & 0 deletions tdw-server/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "tdw-server",
"version": "1.0.0",
"description": "Identity server",
"main": "index.js",
"scripts": {
"serve": "bun --watch ./server.ts",
"generate": "prisma generate"
},
"dependencies": {
"@digitalbazaar/ed25519-multikey": "^1.3.0",
"@digitalbazaar/ed25519-verification-key-2020": "^4.2.0",
"@digitalbazaar/x25519-key-agreement-key-2020": "^3.0.1",
"@prisma/client": "^5.21.1",
"trustdidweb-ts": "git+https://github.com/decentralized-identity/trustdidweb-ts.git"
}
}
17 changes: 17 additions & 0 deletions tdw-server/prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "debian-openssl-3.0.x"]
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model User {
id Int @id @default(autoincrement())
did String @unique
doc Json
log Json
meta Json
}
65 changes: 65 additions & 0 deletions tdw-server/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { Elysia } from 'elysia';
import { createSigner, createDID, DIDLog, resolveDID } from 'trustdidweb-ts';
import { generateEd25519VerificationMethod, generateX25519VerificationMethod } from 'trustdidweb-ts/src/cryptography';
import { PrismaClient } from '@prisma/client';

const DOMAIN = process.env.DOMAIN;
const db = new PrismaClient();

const app = new Elysia()
.get('/', () => 'Hello world!')
.get('/create', async () => {
const authKey = await generateEd25519VerificationMethod('authentication');
const agreementKey = await generateX25519VerificationMethod('keyAgreement');

const result = await createDID({
domain: DOMAIN,
signer: createSigner(authKey),
updateKeys: [
authKey.publicKeyMultibase,
agreementKey.publicKeyMultibase
],
verificationMethods: [authKey, agreementKey],
created: new Date(),
});

await db.user.create({
data: {
did: result.did,
doc: result.doc,
log: result.log,
meta: result.meta,
},
});

return {
public: result,
private: {
authKey,
agreementKey
}
};
})
.get('/get/:id', async ({ params: { id } }) => {
console.log(`Resolving ${id}...`);
try {
const current = await db.user.findUnique({ where: { did: id } });

if (!current) {
throw new Error(`User with DID ${id} not found`);
}

const logEntries: DIDLog = current.log;
const { doc, meta } = await resolveDID(logEntries);

return { doc, meta };
} catch (e) {
console.error(e);
throw new Error(`Failed to resolve DID`);
}
})
.listen(8000);

console.log(
`🔍 Publication server is running at on port ${app.server?.port}...`
);
8 changes: 3 additions & 5 deletions tsp/src/crypto/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use crate::definitions::MessageType;
use crate::definitions::{
Digest, NonConfidentialData, Payload, PrivateKeyData, PrivateSigningKeyData, PrivateVid,
PublicKeyData, PublicVerificationKeyData, TSPMessage, VerifiedVid,
Digest, MessageType, NonConfidentialData, Payload, PrivateKeyData, PrivateSigningKeyData,
PrivateVid, PublicKeyData, PublicVerificationKeyData, TSPMessage, VerifiedVid,
};

pub use digest::blake2b256;
pub use digest::sha256;
pub use digest::{blake2b256, sha256};
use rand::rngs::OsRng;

mod digest;
Expand Down
1 change: 1 addition & 0 deletions tsp/src/vid/did/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ pub(crate) const SCHEME: &str = "did";

pub(crate) mod peer;

pub(crate) mod tdw;
#[cfg(feature = "resolve")]
pub(crate) mod web;
100 changes: 100 additions & 0 deletions tsp/src/vid/did/tdw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use crate::definitions::{PUBLIC_KEY_SIZE, PUBLIC_VERIFICATION_KEY_SIZE};
use serde::Deserialize;
use url::Url;

use crate::vid::{error::VidError, Vid};

pub(crate) const SCHEME: &str = "twd";

const PROTOCOL: &str = "https://";

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TdwDocument {
pub doc: Doc,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Doc {
pub key_agreement: Vec<String>,
pub authentication: Vec<String>,
pub verification_method: Vec<VerificationMethod>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VerificationMethod {
pub id: String,
pub public_key_multibase: String,
}

pub async fn resolve(id: &str, parts: Vec<&str>) -> Result<Vid, VidError> {
let url = resolve_url(&parts)?;

let response = reqwest::get(url.as_ref())
.await
.map_err(|e| VidError::Http(url.to_string(), e))?;

let did_document: TdwDocument = match response.error_for_status() {
Ok(r) => r
.json()
.await
.map_err(|e| VidError::Json(url.to_string(), e))?,
Err(e) => Err(VidError::Http(url.to_string(), e))?,
};

// At this time there is no servce/transport in the TDW document
let transport = format!("{PROTOCOL}{}/user/{}", parts[3], parts[2]);

let sig_key_id = &did_document.doc.authentication[0];
let enc_key_id = &did_document.doc.key_agreement[0];

let sig_key = did_document
.doc
.verification_method
.iter()
.find(|vm| vm.id == *sig_key_id)
.map(|k| &k.public_key_multibase)
.ok_or_else(|| VidError::ResolveVid("No valid signing key found in DID document"))?;

let enc_key = did_document
.doc
.verification_method
.iter()
.find(|vm| vm.id == *enc_key_id)
.map(|k| &k.public_key_multibase)
.ok_or_else(|| VidError::ResolveVid("No valid encryption key found in DID document"))?;

let mut public_sigkey: [u8; PUBLIC_VERIFICATION_KEY_SIZE] = [0; PUBLIC_VERIFICATION_KEY_SIZE];

bs58::decode(&sig_key[6..])
.with_alphabet(bs58::Alphabet::BITCOIN)
.onto(&mut public_sigkey)
.map_err(|_| VidError::ResolveVid("invalid encoded signing key in did:tdw"))?;

let mut public_enckey: [u8; PUBLIC_KEY_SIZE] = [0; PUBLIC_KEY_SIZE];

bs58::decode(&enc_key[6..])
.with_alphabet(bs58::Alphabet::BITCOIN)
.onto(&mut public_enckey)
.map_err(|_| VidError::ResolveVid("invalid encoded encryption key in did:tdw"))?;

Ok(Vid {
id: id.to_string(),
transport: Url::parse(&transport).map_err(|_| VidError::InvalidVid(parts.join(":")))?,
public_sigkey: public_sigkey.into(),
public_enckey: public_enckey.into(),
})
}

pub fn resolve_url(parts: &[&str]) -> Result<Url, VidError> {
let full = parts.join(":");

match parts {
["did", "tdw", _id, _domain] => format!("http://localhost:8000/get/{full}"),
_ => return Err(VidError::InvalidVid(parts.join(":"))),
}
.parse()
.map_err(|_| VidError::InvalidVid(parts.join(":")))
}
10 changes: 4 additions & 6 deletions tsp/src/vid/resolve.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use super::{
did::{self, peer},
error::VidError,
};
use super::{did, error::VidError};
use crate::Vid;

#[cfg(feature = "resolve")]
Expand All @@ -11,7 +8,8 @@ pub async fn verify_vid(id: &str) -> Result<Vid, VidError> {

match parts.get(0..2) {
Some([did::SCHEME, did::web::SCHEME]) => did::web::resolve(id, parts).await,
Some([did::SCHEME, did::peer::SCHEME]) => peer::verify_did_peer(&parts),
Some([did::SCHEME, did::peer::SCHEME]) => did::peer::verify_did_peer(&parts),
Some([did::SCHEME, did::tdw::SCHEME]) => did::tdw::resolve(id, parts).await,
_ => Err(VidError::InvalidVid(id.to_string())),
}
}
Expand All @@ -21,7 +19,7 @@ pub fn verify_vid_offline(id: &str) -> Result<Vid, VidError> {
let parts = id.split(':').collect::<Vec<&str>>();

match parts.get(0..2) {
Some([did::SCHEME, did::peer::SCHEME]) => peer::verify_did_peer(&parts),
Some([did::SCHEME, did::peer::SCHEME]) => did::peer::verify_did_peer(&parts),
_ => Err(VidError::InvalidVid(id.to_string())),
}
}

0 comments on commit 21202b1

Please sign in to comment.