2022-10-28 13:27:36 +00:00
|
|
|
use chrono::{DateTime, Utc};
|
2022-10-20 23:18:45 +00:00
|
|
|
use rsa::RsaPrivateKey;
|
2022-10-23 23:46:51 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2022-10-20 23:18:45 +00:00
|
|
|
use serde_json::Value;
|
|
|
|
|
2022-11-10 19:06:10 +00:00
|
|
|
use crate::identity::{
|
2022-11-10 18:47:22 +00:00
|
|
|
did_key::DidKey,
|
2022-11-10 19:06:10 +00:00
|
|
|
did_pkh::DidPkh,
|
2022-11-10 18:47:22 +00:00
|
|
|
signatures::{
|
2022-11-19 14:54:33 +00:00
|
|
|
PROOF_TYPE_JCS_ED25519,
|
2023-01-07 23:06:23 +00:00
|
|
|
PROOF_TYPE_JCS_EIP191_LEGACY,
|
|
|
|
PROOF_TYPE_JCS_RSA_LEGACY,
|
2022-11-10 18:47:22 +00:00
|
|
|
},
|
2022-11-10 19:06:10 +00:00
|
|
|
};
|
2022-11-19 15:08:40 +00:00
|
|
|
use crate::utils::{
|
|
|
|
canonicalization::{
|
|
|
|
canonicalize_object,
|
|
|
|
CanonicalizationError,
|
|
|
|
},
|
|
|
|
crypto_rsa::create_rsa_signature,
|
|
|
|
multibase::encode_multibase_base58btc,
|
2022-11-10 16:44:05 +00:00
|
|
|
};
|
2022-10-20 23:18:45 +00:00
|
|
|
|
2022-11-10 19:06:10 +00:00
|
|
|
pub(super) const PROOF_KEY: &str = "proof";
|
|
|
|
pub(super) const PROOF_PURPOSE: &str = "assertionMethod";
|
2022-11-01 17:39:42 +00:00
|
|
|
|
2022-10-20 23:18:45 +00:00
|
|
|
/// Data Integrity Proof
|
|
|
|
/// https://w3c.github.io/vc-data-integrity/
|
2022-10-23 23:46:51 +00:00
|
|
|
#[derive(Deserialize, Serialize)]
|
2022-10-20 23:18:45 +00:00
|
|
|
#[serde(rename_all = "camelCase")]
|
2022-11-01 17:39:42 +00:00
|
|
|
pub struct IntegrityProof {
|
2022-10-20 23:18:45 +00:00
|
|
|
#[serde(rename = "type")]
|
2022-10-23 23:46:51 +00:00
|
|
|
pub proof_type: String,
|
|
|
|
pub proof_purpose: String,
|
|
|
|
pub verification_method: String,
|
2022-10-28 13:27:36 +00:00
|
|
|
pub created: DateTime<Utc>,
|
2022-10-23 23:46:51 +00:00
|
|
|
pub proof_value: String,
|
2022-10-20 23:18:45 +00:00
|
|
|
}
|
|
|
|
|
2022-11-01 17:39:42 +00:00
|
|
|
impl IntegrityProof {
|
|
|
|
fn jcs_rsa(
|
|
|
|
signer_key_id: &str,
|
2022-11-18 23:22:24 +00:00
|
|
|
signature: &[u8],
|
2022-11-01 17:39:42 +00:00
|
|
|
) -> Self {
|
|
|
|
Self {
|
2023-01-07 23:06:23 +00:00
|
|
|
proof_type: PROOF_TYPE_JCS_RSA_LEGACY.to_string(),
|
2022-11-01 17:39:42 +00:00
|
|
|
proof_purpose: PROOF_PURPOSE.to_string(),
|
|
|
|
verification_method: signer_key_id.to_string(),
|
|
|
|
created: Utc::now(),
|
2022-11-19 15:08:40 +00:00
|
|
|
proof_value: encode_multibase_base58btc(signature),
|
2022-11-01 17:39:42 +00:00
|
|
|
}
|
|
|
|
}
|
2022-10-29 00:24:17 +00:00
|
|
|
|
|
|
|
pub fn jcs_eip191(
|
|
|
|
signer: &DidPkh,
|
2022-11-19 12:38:23 +00:00
|
|
|
signature: &[u8],
|
2022-10-29 00:24:17 +00:00
|
|
|
) -> Self {
|
|
|
|
Self {
|
2023-01-07 23:06:23 +00:00
|
|
|
proof_type: PROOF_TYPE_JCS_EIP191_LEGACY.to_string(),
|
2022-10-29 00:24:17 +00:00
|
|
|
proof_purpose: PROOF_PURPOSE.to_string(),
|
|
|
|
verification_method: signer.to_string(),
|
|
|
|
created: Utc::now(),
|
2022-11-19 15:08:40 +00:00
|
|
|
proof_value: encode_multibase_base58btc(signature),
|
2022-10-29 00:24:17 +00:00
|
|
|
}
|
|
|
|
}
|
2022-11-10 18:47:22 +00:00
|
|
|
|
2022-11-19 14:54:33 +00:00
|
|
|
pub fn jcs_ed25519(
|
2022-11-10 18:47:22 +00:00
|
|
|
signer: &DidKey,
|
2022-11-19 14:54:33 +00:00
|
|
|
signature: &[u8],
|
2022-11-10 18:47:22 +00:00
|
|
|
) -> Self {
|
|
|
|
Self {
|
2022-11-19 14:54:33 +00:00
|
|
|
proof_type: PROOF_TYPE_JCS_ED25519.to_string(),
|
2022-11-10 18:47:22 +00:00
|
|
|
proof_purpose: PROOF_PURPOSE.to_string(),
|
|
|
|
verification_method: signer.to_string(),
|
|
|
|
created: Utc::now(),
|
2022-11-19 15:08:40 +00:00
|
|
|
proof_value: encode_multibase_base58btc(signature),
|
2022-11-10 18:47:22 +00:00
|
|
|
}
|
|
|
|
}
|
2022-11-01 17:39:42 +00:00
|
|
|
}
|
2022-10-20 23:18:45 +00:00
|
|
|
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
|
|
pub enum JsonSignatureError {
|
|
|
|
#[error(transparent)]
|
|
|
|
JsonError(#[from] serde_json::Error),
|
|
|
|
|
2022-10-31 20:12:19 +00:00
|
|
|
#[error(transparent)]
|
|
|
|
CanonicalizationError(#[from] CanonicalizationError),
|
|
|
|
|
2022-10-20 23:18:45 +00:00
|
|
|
#[error("signing error")]
|
|
|
|
SigningError(#[from] rsa::errors::Error),
|
|
|
|
|
2022-10-23 23:46:51 +00:00
|
|
|
#[error("invalid object")]
|
|
|
|
InvalidObject,
|
2022-11-01 00:02:25 +00:00
|
|
|
|
|
|
|
#[error("already signed")]
|
|
|
|
AlreadySigned,
|
2022-10-20 23:18:45 +00:00
|
|
|
}
|
|
|
|
|
2022-11-01 17:39:42 +00:00
|
|
|
pub fn add_integrity_proof(
|
|
|
|
object_value: &mut Value,
|
|
|
|
proof: IntegrityProof,
|
|
|
|
) -> Result<(), JsonSignatureError> {
|
|
|
|
let object_map = object_value.as_object_mut()
|
|
|
|
.ok_or(JsonSignatureError::InvalidObject)?;
|
|
|
|
if object_map.contains_key(PROOF_KEY) {
|
|
|
|
return Err(JsonSignatureError::AlreadySigned);
|
|
|
|
};
|
|
|
|
let proof_value = serde_json::to_value(proof)?;
|
|
|
|
object_map.insert(PROOF_KEY.to_string(), proof_value);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-10-20 23:18:45 +00:00
|
|
|
pub fn sign_object(
|
2022-11-01 00:02:25 +00:00
|
|
|
object: &Value,
|
2022-10-20 23:18:45 +00:00
|
|
|
signer_key: &RsaPrivateKey,
|
|
|
|
signer_key_id: &str,
|
|
|
|
) -> Result<Value, JsonSignatureError> {
|
|
|
|
// Canonicalize
|
2022-10-31 20:12:19 +00:00
|
|
|
let message = canonicalize_object(object)?;
|
2022-10-20 23:18:45 +00:00
|
|
|
// Sign
|
2022-11-18 23:22:24 +00:00
|
|
|
let signature = create_rsa_signature(signer_key, &message)?;
|
2022-10-20 23:18:45 +00:00
|
|
|
// Insert proof
|
2022-11-18 23:22:24 +00:00
|
|
|
let proof = IntegrityProof::jcs_rsa(signer_key_id, &signature);
|
2022-10-20 23:18:45 +00:00
|
|
|
let mut object_value = serde_json::to_value(object)?;
|
2022-11-01 17:39:42 +00:00
|
|
|
add_integrity_proof(&mut object_value, proof)?;
|
2022-10-20 23:18:45 +00:00
|
|
|
Ok(object_value)
|
|
|
|
}
|
|
|
|
|
2022-11-01 00:02:25 +00:00
|
|
|
pub fn is_object_signed(object: &Value) -> bool {
|
|
|
|
object.get(PROOF_KEY).is_some()
|
|
|
|
}
|
|
|
|
|
2022-10-20 23:18:45 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use serde_json::json;
|
2022-11-13 18:43:57 +00:00
|
|
|
use crate::utils::crypto_rsa::generate_weak_rsa_key;
|
2022-10-20 23:18:45 +00:00
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_sign_object() {
|
2022-11-13 18:43:57 +00:00
|
|
|
let signer_key = generate_weak_rsa_key().unwrap();
|
2022-10-20 23:18:45 +00:00
|
|
|
let signer_key_id = "https://example.org/users/test#main-key";
|
|
|
|
let object = json!({
|
|
|
|
"type": "Create",
|
|
|
|
"actor": "https://example.org/users/test",
|
|
|
|
"id": "https://example.org/objects/1",
|
|
|
|
"to": [
|
|
|
|
"https://example.org/users/yyy",
|
|
|
|
"https://example.org/users/xxx",
|
|
|
|
],
|
|
|
|
"object": {
|
|
|
|
"type": "Note",
|
|
|
|
"content": "test",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
let result = sign_object(&object, &signer_key, signer_key_id).unwrap();
|
2022-10-28 13:27:36 +00:00
|
|
|
|
2022-11-01 00:02:25 +00:00
|
|
|
assert!(is_object_signed(&result));
|
2022-10-28 13:27:36 +00:00
|
|
|
assert_eq!(result["actor"], object["actor"]);
|
|
|
|
assert_eq!(result["object"], object["object"]);
|
|
|
|
let signature_date = result["proof"]["created"].as_str().unwrap();
|
|
|
|
// Put * in place of date to avoid escaping all curly brackets
|
2022-11-19 15:08:40 +00:00
|
|
|
let expected_result = r#"{"actor":"https://example.org/users/test","id":"https://example.org/objects/1","object":{"content":"test","type":"Note"},"proof":{"created":"*","proofPurpose":"assertionMethod","proofValue":"z2Gh9LYrXjSqFrkia6gMg7xp2wftn1hqmYeEXxrsH9Eh6agB2VYraSYrDoSufbXEHnnyHMCoDSAriLpVacj6E4LFK","type":"JcsRsaSignature2022","verificationMethod":"https://example.org/users/test#main-key"},"to":["https://example.org/users/yyy","https://example.org/users/xxx"],"type":"Create"}"#;
|
2022-10-20 23:18:45 +00:00
|
|
|
assert_eq!(
|
2022-10-28 13:27:36 +00:00
|
|
|
serde_json::to_string(&result).unwrap(),
|
|
|
|
expected_result.replace('*', signature_date),
|
2022-10-20 23:18:45 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|