Skip to content

Commit a612f33

Browse files
sKudryashovSergey Kudryashov
and
Sergey Kudryashov
authored
Create Verifiable Credentials (#5)
* Verifiable credentials creation * VC create * VC create * Challenge create * VC creation done * PR Ready * Verifiable credentials creation * VC create * VC create * Challenge create * VC creation done * PR Ready * PR fix * PR fix * PR fixes, credential subject dependence on credential type added * Manifest fix * PR fix * PR fix * PR fix * Fixing dependencies * Flattened signed credential structure added * Cargo file fix * Variable VCContext renamed * Structs rearranging * Verifiable Credential dynamic fields added * PR fix * PR fixes * PR fixes * PR fixes * Verifiable credentials creation * Test for create_credentials * Test fix * Test fix * VerificationContext Alias implementation * VerificationContext Alias implementation * Cargo fix * Test passed * Test fixed * Lock file update * PR fix * PR fixes * PR fix Co-authored-by: Sergey Kudryashov <skudryashov@knox-networks.com>
1 parent 30fa2b6 commit a612f33

File tree

5 files changed

+257
-23
lines changed

5 files changed

+257
-23
lines changed

Cargo.lock

+19
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,8 @@ edition = "2021"
99
async-trait = "0.1.53"
1010
serde_json = "1.0.81"
1111
signature = "1.5.0"
12+
map = "0.0.0"
13+
serde = { version = "1.0", features = ["derive"] }
14+
15+
[dev-dependencies]
16+
assert-json-diff = "2.0.2"

core/src/credential.rs

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
2+
#![allow(unused_variables)]
3+
#![allow(dead_code)]
4+
use std::{time::{SystemTime}};
5+
6+
use serde::{Deserialize, Serialize};
7+
use serde_json::Value;
8+
use crate::HashMap;
9+
10+
// cred_subject is a generic that implements trait X
11+
// trait X allows us to encode that object into JSON-LD
12+
// We provide types that implement trait X for the cred types that we support
13+
// Users can also user their own types that implement trait X if they need a different structure
14+
// ---
15+
// Default context and Cred types are defaulted but can be redefined
16+
17+
type VerificationContext = [&'static str; 2];
18+
19+
pub const CONTEXT_CREDENTIALS: VerificationContext = [
20+
"https://www.w3.org/2018/credentials/v1",
21+
"https://www.w3.org/2018/credentials/examples/v1",
22+
];
23+
24+
pub const CRED_TYPE_PERMANENT_RESIDENT_CARD: &'static str = "PermanentResidentCard";
25+
pub const CRED_TYPE_BANK_CARD: &'static str = "BankCard";
26+
27+
#[derive(Serialize, Deserialize, Clone, Debug)]
28+
struct CredentialSubject {
29+
id : String,
30+
#[serde(flatten)]
31+
pub property_set: HashMap<String, Value>,
32+
}
33+
34+
#[derive(Debug, Serialize, Deserialize, Clone)]
35+
#[serde(bound(deserialize = "'de: 'static"))]
36+
pub struct VerifiableCredential {
37+
#[serde(flatten)]
38+
credential: Credential,
39+
proof: IntegrityProof,
40+
}
41+
42+
#[derive(Debug, Serialize, Deserialize, Clone)]
43+
#[serde(bound(deserialize = "'de: 'static"))]
44+
pub struct Credential {
45+
#[serde(rename = "@context")]
46+
context: VerificationContext,
47+
#[serde(rename = "@id")]
48+
id: String,
49+
#[serde(rename = "type")]
50+
cred_type: String,
51+
#[serde(rename = "issuanceDate")]
52+
issuance_date: SystemTime,
53+
#[serde(rename = "credentialSubject")]
54+
subject: CredentialSubject,
55+
#[serde(flatten)]
56+
pub property_set: HashMap<String, Value>,
57+
}
58+
59+
60+
#[derive(Debug, Serialize, Deserialize, Clone)]
61+
pub struct IntegrityProof {
62+
proof_type: String,
63+
created: String,
64+
verification_method: String,
65+
proof_purpose: String,
66+
proof_value: String,
67+
}
68+
69+
impl Credential {
70+
pub fn new (
71+
context: VerificationContext,
72+
cred_type: String,
73+
cred_subject: HashMap<String, Value>,
74+
property_set: HashMap<String, Value>, id: &str)
75+
-> Credential {
76+
let vc = Credential {
77+
context: context,
78+
id: id.to_string(),
79+
cred_type: cred_type.to_string(),
80+
issuance_date: SystemTime::now(),
81+
subject: CredentialSubject{
82+
id: id.to_string(),
83+
property_set: cred_subject,
84+
},
85+
property_set: property_set,
86+
};
87+
vc
88+
}
89+
90+
pub fn serialize(&self) -> Value {
91+
return serde_json::to_value(&self).unwrap();
92+
}
93+
}

core/src/error.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
use serde::{Deserialize, Serialize};
2+
3+
#[derive(Debug, Serialize, Deserialize, Clone)]
4+
pub enum Error {}
15
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
26
pub enum ErrorKind {
37
DocumentNotFound,

core/src/lib.rs

+136-23
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
mod credential;
2+
3+
use serde_json::{self, Value};
4+
use credential::*;
5+
use std::{collections::HashMap};
6+
17
pub mod error;
28
/// Verification of Data Integrity Proofs requires the resolution of the `verificationMethod` specified in the proof.
39
/// The `verificationMethod` refers to a cryptographic key stored in some external source.
@@ -22,23 +28,37 @@ pub trait DIDResolver {
2228
}
2329
}
2430

25-
/// Given the credential type and the credential subject information, create a unissued JSON-LD credential.
26-
/// In order to become a Verifiable Credential, a data integrity proof must be created for the credential and appended to the JSON-LD document.
27-
pub fn create_credential(
28-
_cred_type: &str,
29-
_cred_subject: serde_json::Value,
30-
) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
31-
unimplemented!();
32-
}
31+
pub trait DocumentBuilder {
32+
/// Given the credential type and the credential subject information, create a unissued JSON-LD credential.
33+
/// In order to become a Verifiable Credential, a data integrity proof must be created for the credential and appended to the JSON-LD document.
34+
/// this is the default implementation of the `create` method. The `create` method can be overridden to create a custom credential.
35+
fn create_credential(
36+
&self,
37+
cred_type: String,
38+
cred_subject: HashMap<String, Value>,
39+
property_set: HashMap<String, Value>,
40+
id: &str
41+
) -> Result<Credential, Box<dyn std::error::Error>> {
42+
let vc = Credential::new(CONTEXT_CREDENTIALS,
43+
cred_type,
44+
cred_subject,
45+
property_set,
46+
id
47+
);
48+
Ok(vc)
49+
}
3350

34-
/// Given the set of credentials, create a unsigned JSON-LD Presentation of those credentials.
35-
/// In order to become a Verifiable Presentation, a data integrity proof must be created for the presentation and appended to the JSON-LD document.
36-
pub fn create_presentation(
37-
_creds: Vec<serde_json::Value>,
38-
) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
39-
unimplemented!();
51+
/// Given the set of credentials, create a unsigned JSON-LD Presentation of those credentials.
52+
/// In order to become a Verifiable Presentation, a data integrity proof must be created for the presentation and appended to the JSON-LD document.
53+
fn create_presentation(
54+
_creds: Vec<serde_json::Value>,
55+
) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
56+
unimplemented!();
57+
}
4058
}
4159

60+
61+
// Commented due to failing cargo check
4262
// ed25519 cryptography key generation & DID Document creation
4363
pub fn create_identity(
4464
_mnemonic: &str,
@@ -47,7 +67,8 @@ pub fn create_identity(
4767
unimplemented!();
4868
}
4969

50-
/// Given a JSON-LD document, create a data integrity proof for the document.
70+
/// Given a JSON-LD document, c
71+
/// reate a data integrity proof for the document.
5172
/// Currently, only `Ed25519Signature2018` data integrity proofs in the JSON-LD format can be created.
5273
pub fn create_data_integrity_proof<S: signature::Signature>(
5374
_doc: serde_json::Value,
@@ -56,9 +77,9 @@ pub fn create_data_integrity_proof<S: signature::Signature>(
5677
unimplemented!();
5778
}
5879

59-
/// Given a JSON-LD document and a DIDResolver, verify the data integrity proof for the document.
60-
/// This will by parsing the `verificationMethod` property of the data integrity proof and resolving it to a key that can be used to verify the proof.
61-
/// Currently only `Ed25519Signature2018` is supported for data integrity proof verification.
80+
// /// Given a JSON-LD document and a DIDResolver, verify the data integrity proof for the document.
81+
// /// This will by parsing the `verificationMethod` property of the data integrity proof and resolving it to a key that can be used to verify the proof.
82+
// /// Currently only `Ed25519Signature2018` is supported for data integrity proof verification.
6283
pub fn verify_data_integrity_proof<S: signature::Signature>(
6384
_doc: serde_json::Value,
6485
_resolver: &impl DIDResolver,
@@ -69,10 +90,102 @@ pub fn verify_data_integrity_proof<S: signature::Signature>(
6990

7091
/// Given a JSON-LD document and a DIDResolver, verify the data integrity proof for the Verifiable Presentation.
7192
/// Then each claimed Verifiable Credential must be verified for validity and ownership of the credential by the subject.
72-
pub fn verify_presentation<S: signature::Signature>(
73-
_doc: serde_json::Value,
74-
_resolver: &impl DIDResolver,
75-
_verifier: &impl signature::Verifier<S>,
76-
) -> Result<bool, Box<dyn std::error::Error>> {
93+
pub fn create_presentation(
94+
_creds: Vec<serde_json::Value>,
95+
) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
7796
unimplemented!();
7897
}
98+
99+
#[cfg(test)]
100+
mod tests {
101+
use crate::DocumentBuilder;
102+
use std::{collections::HashMap, vec};
103+
use assert_json_diff::{assert_json_eq};
104+
use crate::serde_json::json;
105+
106+
use serde_json::Value;
107+
struct TestObj {}
108+
109+
impl TestObj {
110+
pub fn new() -> Self{
111+
TestObj {}
112+
}
113+
}
114+
impl DocumentBuilder for TestObj {}
115+
116+
#[test]
117+
fn test_create_credential() -> Result<(), String> {
118+
let to = TestObj::new();
119+
let mut kv_body: HashMap<String, Value> = HashMap::new();
120+
let mut kv_subject: HashMap<String, Value> = HashMap::new();
121+
122+
let _expect = json!({
123+
"@context": [
124+
"https://www.w3.org/2018/credentials/v1",
125+
"https://www.w3.org/2018/credentials/examples/v1"
126+
],
127+
"@id": "https://issuer.oidp.uscis.gov/credentials/83627465",
128+
"type": ["VerifiableCredential", "PermanentResidentCard"],
129+
"issuer": "did:example:28394728934792387",
130+
"identifier": "83627465",
131+
"name": "Permanent Resident Card",
132+
"description": "Government of Example Permanent Resident Card.",
133+
"issuanceDate": "2019-12-03T12:19:52Z",
134+
"expirationDate": "2029-12-03T12:19:52Z",
135+
"credentialSubject": {
136+
"id": "did:example:b34ca6cd37bbf23",
137+
"type": ["PermanentResident", "Person"],
138+
"givenName": "JOHN",
139+
"familyName": "SMITH",
140+
"gender": "Male",
141+
"image": "data:image/png;base64,iVBORw0KGgo...kJggg==",
142+
"residentSince": "2015-01-01",
143+
"lprCategory": "C09",
144+
"lprNumber": "999-999-999",
145+
"commuterClassification": "C1",
146+
"birthCountry": "Bahamas",
147+
"birthDate": "1958-07-17"
148+
},
149+
});
150+
151+
let type_rs = serde_json::to_value(["VerifiableCredential".to_string(), "PermanentResidentCard".to_string()]);
152+
if type_rs.is_ok() {
153+
kv_body.entry("type".to_string()).or_insert(type_rs.unwrap());
154+
}
155+
156+
kv_body.entry("issuer".to_string()).or_insert(Value::String("did:example:28394728934792387".to_string()));
157+
kv_body.entry("identifier".to_string()).or_insert(Value::String("83627465".to_string()));
158+
kv_body.entry("name".to_string()).or_insert(Value::String("Permanent Resident Card".to_string()));
159+
kv_body.entry("description".to_string()).or_insert(Value::String("Government of Example Permanent Resident Card.".to_string()));
160+
kv_body.entry("issuanceDate".to_string()).or_insert(Value::String("2019-12-03T12:19:52Z".to_string()));
161+
kv_body.entry("expirationDate".to_string()).or_insert(Value::String("2029-12-03T12:19:52Z".to_string()));
162+
163+
kv_subject.entry("id".to_string()).or_insert(Value::String("did:example:b34ca6cd37bbf23".to_string()));
164+
165+
let type_rs = serde_json::to_value(["PermanentResident".to_string(), "Person".to_string()]);
166+
if type_rs.is_ok() {
167+
kv_subject.entry("type".to_string()).or_insert(type_rs.unwrap());
168+
}
169+
170+
kv_subject.entry("givenName".to_string()).or_insert(Value::String("JOHN".to_string()));
171+
kv_subject.entry("familyName".to_string()).or_insert(Value::String("SMITH".to_string()));
172+
kv_subject.entry("gender".to_string()).or_insert(Value::String("Male".to_string()));
173+
kv_subject.entry("image".to_string()).or_insert(Value::String("data:image/png;base64,iVBORw0KGgo...kJggg==".to_string()));
174+
kv_subject.entry("residentSince".to_string()).or_insert(Value::String("2015-01-01".to_string()));
175+
kv_subject.entry("lprCategory".to_string()).or_insert(Value::String("C09".to_string()));
176+
kv_subject.entry("lprNumber".to_string()).or_insert(Value::String("999-999-999".to_string()));
177+
kv_subject.entry("commuterClassification".to_string()).or_insert(Value::String("C1".to_string()));
178+
kv_subject.entry("birthCountry".to_string()).or_insert(Value::String("Bahamas".to_string()));
179+
kv_subject.entry("birthDate".to_string()).or_insert(Value::String("1958-07-17".to_string()));
180+
181+
let vc = to.create_credential(
182+
crate::CRED_TYPE_PERMANENT_RESIDENT_CARD.to_string(),
183+
kv_subject,
184+
kv_body,
185+
"https://issuer.oidp.uscis.gov/credentials/83627465",
186+
);
187+
assert!(vc.is_ok());
188+
assert_json_eq!(_expect, vc.unwrap());
189+
Ok(())
190+
}
191+
}

0 commit comments

Comments
 (0)