protected virtual IVerifyData CreateVerifyData(JObject proof, ProofOptions options) { var processorOptions = options.GetProcessorOptions(); var c14nProofOptions = Helpers.CanonizeProof(proof, processorOptions); var c14nDocument = Helpers.Canonize(options.Input, processorOptions); var sha256 = SHA256.Create(); return((ByteArray)sha256.ComputeHash(Encoding.UTF8.GetBytes(c14nProofOptions)) .Concat(sha256.ComputeHash(Encoding.UTF8.GetBytes(c14nDocument))) .ToArray()); }
/// <summary> /// Get a proof from a signed document /// </summary> /// <param name="document"></param> /// <param name="options"></param> /// <returns></returns> public static (JToken proof, JToken document) GetProof(JToken document, ProofOptions options) { var documentCopy = options.CompactProof ? JsonLdProcessor.Compact( input: document, context: Constants.SECURITY_CONTEXT_V2_URL, options: options.GetProcessorOptions()) : document.DeepClone(); var proof = documentCopy["proof"].DeepClone(); document.Remove("proof"); if (proof == null) { throw new Exception("No matching proofs found in the given document."); } proof["@context"] = Constants.SECURITY_CONTEXT_V2_URL; return(proof, document); }
protected VerificationMethod GetVerificationMethod(JObject proof, ProofOptions options) { var verificationMethod = proof["verificationMethod"] ?? throw new Exception("No 'verificationMethod' found in proof."); var verificationMethodId = verificationMethod.Type switch { JTokenType.String => verificationMethod.ToString(), JTokenType.Object => verificationMethod["id"]?.ToString() ?? throw new Exception("Verification Method found, but it's 'id' property was empty"), _ => throw new Exception($"Invalid verification method type: {verificationMethod.Type}") }; var processorOptions = options.GetProcessorOptions(); processorOptions.ExpandContext = Constants.SECURITY_CONTEXT_V2_URL; var result = JsonLdProcessor.Frame( verificationMethodId, new JObject { { "@context", Constants.SECURITY_CONTEXT_V2_URL }, { "@embed", "@always" }, { "id", verificationMethod } }, processorOptions); if (result == null || result["id"] == null) { throw new Exception($"Verification method {verificationMethod} not found."); } if (result["revoked"] != null) { throw new Exception("The verification method has been revoked."); } return(new VerificationMethod(result)); }
/// <summary> /// Cryptographically signs the provided document by adding a `proof` section, /// based on the provided suite and proof purpose. /// </summary> /// <param name="document"></param> /// <param name="options"></param> /// <returns></returns> public static async Task <JToken> SignAsync(JToken document, ProofOptions options) { if (options.Purpose is null) { throw new Exception("Proof purpose is required."); } if (options.Suite is null) { throw new Exception("Suite is required."); } options.AdditonalData["originalDocument"] = document; var documentCopy = document.DeepClone(); documentCopy = options.CompactProof ? JsonLdProcessor.Compact(documentCopy, Constants.SECURITY_CONTEXT_V2_URL, options.GetProcessorOptions()) : document.DeepClone(); documentCopy.Remove("proof"); // create the new proof (suites MUST output a proof using the security-v2 // `@context`) options.Input = documentCopy; var proof = await options.Suite.CreateProofAsync(options); // TODO: Check compaction again proof.Proof.Remove("@context"); var result = proof.UpdatedDocument ?? document.DeepClone(); result["proof"] = proof.Proof; return(result); }
/// <summary> /// Create proof /// </summary> /// <param name="options"></param> /// <returns></returns> public override async Task <ProofResult> CreateProofAsync(ProofOptions options) { if (VerificationMethod == null) { throw new ArgumentNullException(nameof(VerificationMethod), "VerificationMethod must be specified."); } if (TypeName == null) { throw new ArgumentNullException(nameof(TypeName), "TypeName must be specified."); } var proof = InitialProof != null ? JsonLdProcessor.Compact(InitialProof, Constants.SECURITY_CONTEXT_V2_URL, options.GetProcessorOptions()) : new JObject { { "@context", Constants.SECURITY_CONTEXT_V2_URL } }; proof["type"] = TypeName; proof["created"] = Date.HasValue ? Date.Value.ToString("s") : DateTime.Now.ToString("s"); proof["verificationMethod"] = VerificationMethod; // allow purpose to update the proof; the `proof` is in the // SECURITY_CONTEXT_URL `@context` -- therefore the `purpose` must // ensure any added fields are also represented in that same `@context` proof = await options.Purpose.UpdateAsync(proof, options); // create data to sign var verifyData = CreateVerifyData(proof, options); // sign data proof = await SignAsync(verifyData, proof, options); return(new ProofResult { Proof = proof }); }