Building a More Trusted, Citizen Focused World …

Well, our lab — the Blockpass ID Lab — turned three years old this year, and over the past year it has really pushed itself forward. We…

Photo by Clem Onojeghuo on Unsplash

Building a More Trusted, Citizen Focused World …

Well, our lab — the Blockpass ID Lab — turned three years old this year, and over the past year, it has really pushed itself forward. We have a new spin-out being formed and have published so many papers. Overall — we hope — we contributed to the development of trusted, resilient and secure systems, and which focuses on the citizen. For us, we want to put the citizen at the heart of this new digital world.

A core part of our current work is on the EU Horizon-funded GLASS project [here], and which aims to change the way that a citizens interact with government and governance services. This will support the rights of citizens in the EU to: look for a job in another EU country; work there without needing a work permit; reside there for that purpose; stay there even after employment has finished; and enjoy equal treatment with nationals [here]. These are things that I deeply believe in, and which break down the barriers that national borders put in our way.

This research project supports the implementation digital wallet for each citizen, and which contains credentials which can be used many times: claim once, use many times. So, if Alice applies for a job in another country, she can gather her claims for her identity, tax status academic qualifications, and so on, and present this in a way that protects her privacy, but is cryptographically secure. At the core of this are digitally signed credentials, and the usage of encryption to protect the access to these credentials.

You will hear more about this project over the next year or so, as we build it out, but, for just now, let’s look at how we build an assured credential for Alice’s academic qualification.

Building a trusted and verifiable credential

Let’s keep it simple, and where Alice needs a signed attestation of her academic qualification. If it is ever checked, it can involve her scanning her qualification and sending it by email. The risks of fraud are obviously high in this case, as the level of trust on the scan is often low. Obviously, the presentation of her qualifications will typically vary depending on the job she is applying for and for the country, organisational domain or region that it is being presented in. If required, we can use various standard fields for this, such as occupational credentials [here]:

But, in this case, we will keep it simple, and just define the qualification award, the university, the awardee, and the department. For this, we can define a data format with string formats:

type Claim struct {
id string
awardTo string
university string
department string
degreeAwarded string
}

If we want, we can also have a schema to define this, but we will keep it simple just now, and allow a string to be created for each of the fields. Next, we need a trusted signer (the university), and their details. For this, we will define a context and type of qualification that the signer is signing for, an ID for the signer, the signer’s name, and the date of the award:

type IssuerMetadata struct {
context []string
id string
typeOfCredential []string
issuer string
issuanceDate time.Time
}

Next, we need details of how the signature is signed and its cryptographic signature. For this, we often focus on ECDSA or EdDSA. As ECDSA (as used in Bitcoin) can have some security issues, we will use EdDSA and with the Ed25519 key pair:

type Proof struct {
typeOfProof string
created time.Time
creator ed25519.PublicKey
signature []byte
}

We will then sign the credential with the private key and then prove it with the public key. The proof contains the public key in the creator field, but we would check this public key against trusted signers.

And, finally, we need to package all of this up into a format that encapsulates the claim, the issuer metadata, and the proof, with:

type Credential struct {
context []string
id string
typeOfCredential []string
issuer string
issuanceDate time.Time
credentialSubject Claim
proof Proof
}

With this, we will take the id, typeOfCredential, issuer, and issuance details from the metadata of the issuer:

func createCredential(keyPair KeyPair, metadata IssuerMetadata, claim Claim) Credential {
credential := Credential{
context: metadata.context,
id: metadata.id,
typeOfCredential: metadata.typeOfCredential,
issuer: metadata.issuer,
issuanceDate: metadata.issuanceDate,
credentialSubject: claim,
}
}

Coding

We can use the Go programming language to implement this code [here]:

package main
// Based on code at https://ringaile.medium.com/how-to-write-verifiable-credentials-in-golang-7447234d5c16
import (
"crypto/ed25519"
"fmt"
"os"
"time"
)
type KeyPair struct {
publicKey ed25519.PublicKey
privateKey ed25519.PrivateKey
}
type IssuerMetadata struct {
context []string
id string
typeOfCredential []string
issuer string
issuanceDate time.Time
}
type Claim struct {
id string
awardTo string
university string
department string
degreeAwarded string
}
type Proof struct {
typeOfProof string
created time.Time
creator ed25519.PublicKey
signature []byte
}
type Credential struct {
context []string
id string
typeOfCredential []string
issuer string
issuanceDate time.Time
credentialSubject Claim
proof Proof
}
func createCredential(keyPair KeyPair, metadata IssuerMetadata, claim Claim) Credential {
//create credential
credential := Credential{
context: metadata.context,
id: metadata.id,
typeOfCredential: metadata.typeOfCredential,
issuer: metadata.issuer,
issuanceDate: metadata.issuanceDate,
credentialSubject: claim,
}
	//create proof
proof := Proof{
typeOfProof: "ed25519",
created: time.Now(),
creator: keyPair.publicKey,
signature: ed25519.Sign(keyPair.privateKey, []byte(fmt.Sprintf("%v", credential))),
}
	credential.proof = proof
	return credential
}
func verifyCredential(publicKey ed25519.PublicKey, credential Credential) bool {
if string(publicKey) != string(credential.proof.creator) {
return false
}
proofObj := credential.proof
credential.proof = Proof{}
	return ed25519.Verify(publicKey, []byte(fmt.Sprintf("%v", credential)), proofObj.signature)
}
func main() {
c_name := "Fred Smith"
c_degree := "PhD"
c_university := "Achelous University"
c_department := "School of Winds and Air"
argCount := len(os.Args[1:])
	if argCount > 0 {
c_name = os.Args[1]
}
if argCount > 1 {
c_degree = os.Args[2]
}
if argCount > 2 {
c_university = os.Args[3]
}
if argCount > 3 {
c_department = os.Args[4]
}
	publ, priv, _ := ed25519.GenerateKey(nil)
	keyPair := KeyPair{
publicKey: publ,
privateKey: priv,
}
	metadata := IssuerMetadata{
context: []string{"https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1"},
id: "did:example:123#owner",
typeOfCredential: []string{"VerifiableCredential", "UniversityDegreeCredential"},
issuer: "https://example.edu/issuers/565049",
issuanceDate: time.Now(),
}
	claim := Claim{
id: "did:example:ebfeb1f712ebc6f1c276e12ec21",
awardTo: c_name,
university: c_university,
department: c_department,
degreeAwarded: c_degree,
}
	createdCredential := createCredential(keyPair, metadata, claim)
	fmt.Printf("Private key: %x\n", priv)
fmt.Printf("Public key: %x\n", publ)
fmt.Printf("\n--- Credentials ---\n")
fmt.Printf("Credential ID: %s\n", createdCredential.id)
fmt.Printf("Credential Issuer: %s\n", createdCredential.issuer)
fmt.Printf("Credential Date: %s\n", createdCredential.issuanceDate)
	fmt.Printf("Credential Subject (ID): %s\n", createdCredential.credentialSubject.id)
fmt.Printf("Credential Subject (Awarded To): %s\n", createdCredential.credentialSubject.awardTo)
	fmt.Printf("Credential Subject (University): %s\n", createdCredential.credentialSubject.university)
fmt.Printf("Credential Subject (Department): %s\n", createdCredential.credentialSubject.department)
fmt.Printf("Credential Subject (DegreeAwarded): %s\n", createdCredential.credentialSubject.degreeAwarded)
	fmt.Printf("\n\n--- Proof ---\n")
fmt.Printf("Creator: %x\n", createdCredential.proof.creator)
fmt.Printf("Type: %s\n", createdCredential.proof.typeOfProof)
fmt.Printf("Created: %s\n", createdCredential.proof.created)
fmt.Printf("Proof signature: %x\n", createdCredential.proof.signature)
	rtn := verifyCredential(keyPair.publicKey, createdCredential)
if rtn {
fmt.Println("Valid proof")
}
}

A test run [here]:

Private key: aa4631f3fc564388a2a3c53d87260886e99168ef13165afe0ae1e3a5f2f42360b0541d750da7a18bced6f249627abc510b21bf3fb4b8714e33f384ff779413ca
Public key: b0541d750da7a18bced6f249627abc510b21bf3fb4b8714e33f384ff779413ca
--- Credentials ---
Credential ID: did:example:123#owner
Credential Issuer: https://example.edu/issuers/565049
Credential Date: 2021-12-24 07:43:31.0690025 +0000 GMT m=+0.003000001
Credential Subject (ID): did:example:ebfeb1f712ebc6f1c276e12ec21
Credential Subject (Awarded To): Fred Smith
Credential Subject (University): Hades University
Credential Subject (Department): School of music, prophecy, education, healing and disease
Credential Subject (DegreeAwarded): Higher Certificate

--- Proof ---
Creator: b0541d750da7a18bced6f249627abc510b21bf3fb4b8714e33f384ff779413ca
Type: ed25519
Created: 2021-12-24 07:43:31.0690025 +0000 GMT m=+0.003000001
Proof signature: eac1109ed0c409ee57576d6b04436f4416c6e4afbd6f9af5d27ca614c4a6235421efc42f5da2bd2b63b7eb2ca14e7d95dc612374399ab2cde1ce7d46eaf14607
Valid proof

A core part of the signing process is that the public key (the creator) must be trusted to issue academic qualifications. The associated private key is thus important, as it should not be leaked for others to sign. If it is, we need to revoke the public key, so that academic qualifications signed by the leaked private key can be marked as invalid, and where a citizen will have to reclaim their qualification credential.

Conclusions

Come and join us on our journey …

and here is the code for you to try:

Asecuritysite is provided free and without adverts. If you want to support its development and get access to this blog, subscribe here: