Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ContractsClient.cs
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Net.Http;
5using System.Net.Http.Headers;
6using System.Runtime.ExceptionServices;
7using System.Security.Cryptography;
8using System.Text;
9using System.Threading.Tasks;
10using System.Xml;
11using System.Xml.Schema;
12using Waher.Content;
15using Waher.Events;
32using Waher.Script;
33using Waher.Security;
36
38{
46 {
50 public const string NamespaceLegalIdentitiesIeeeV1 = "urn:ieee:iot:leg:id:1.0";
51
55 public const string NamespaceLegalIdentitiesNeuroFoundationV1 = "urn:nf:iot:leg:id:1.0";
56
61
66
70 public static readonly string[] NamespacesLegalIdentities = new string[]
71 {
74 };
75
79 public const string NamespaceSmartContractsIeeeV1 = "urn:ieee:iot:leg:sc:1.0";
80
84 public const string NamespaceSmartContractsNeuroFoundationV1 = "urn:nf:iot:leg:sc:1.0";
85
90
94 public static readonly string[] NamespacesSmartContracts = new string[]
95 {
98 };
99
103 public const string NamespaceOnboarding = "http://waher.se/schema/Onboarding/v1.xsd";
104
105 private static readonly string KeySettings = typeof(ContractsClient).FullName + ".";
106 private static readonly string ContractKeySettings = typeof(ContractsClient).Namespace + ".Contracts.";
107
108 private readonly Dictionary<string, KeyEventArgs> publicKeys = new Dictionary<string, KeyEventArgs>();
109 private readonly Dictionary<string, KeyEventArgs> matchingKeys = new Dictionary<string, KeyEventArgs>();
110 private readonly Cache<string, KeyValuePair<byte[], bool>> contentPerPid = new Cache<string, KeyValuePair<byte[], bool>>(int.MaxValue, TimeSpan.FromDays(1), TimeSpan.FromDays(1));
111 private EndpointSecurity localKeys;
112 private EndpointSecurity localE2eEndpoint;
113 private DateTime keysTimestamp = DateTime.MinValue;
114 private SymmetricCipherAlgorithms preferredEncryptionAlgorithm = DefaultCipherAlgorithm;
115 private object[] approvedSources = null;
116 private readonly string componentAddress;
117 private string keySettingsPrefix;
118 private string contractKeySettingsPrefix;
119 private bool keySettingsPrefixLocked = false;
120 private bool localKeysForE2e = false;
121 private bool preferredEncryptionAlgorithmLocked = false;
122 private RandomNumberGenerator rnd = RandomNumberGenerator.Create();
123 private Aes aes;
124
125 #region Construction
126
140 : this(Client, ComponentAddress, null)
141 {
142 }
143
157 public ContractsClient(XmppClient Client, string ComponentAddress, object[] ApprovedSources)
158 : base(Client)
159 {
160 this.componentAddress = ComponentAddress;
161 this.approvedSources = ApprovedSources;
162 this.localKeys = null;
163
164 #region NeuroFoundation V1
165
166 this.client.RegisterMessageHandler("identity", NamespaceLegalIdentitiesNeuroFoundationV1, this.IdentityMessageHandler, true);
167 this.client.RegisterMessageHandler("petitionIdentityMsg", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionIdentityMessageHandler, false);
168 this.client.RegisterMessageHandler("petitionIdentityResponseMsg", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionIdentityResponseMessageHandler, false);
169 this.client.RegisterMessageHandler("petitionSignatureMsg", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionSignatureMessageHandler, false);
170 this.client.RegisterMessageHandler("petitionSignatureResponseMsg", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionSignatureResponseMessageHandler, false);
171 this.client.RegisterMessageHandler("petitionClientUrl", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionClientUrlEventHandler, false);
172
173 this.client.RegisterMessageHandler("contractSigned", NamespaceSmartContractsNeuroFoundationV1, this.ContractSignedMessageHandler, true);
174 this.client.RegisterMessageHandler("contractCreated", NamespaceSmartContractsNeuroFoundationV1, this.ContractCreatedMessageHandler, false);
175 this.client.RegisterMessageHandler("contractUpdated", NamespaceSmartContractsNeuroFoundationV1, this.ContractUpdatedMessageHandler, false);
176 this.client.RegisterMessageHandler("contractDeleted", NamespaceSmartContractsNeuroFoundationV1, this.ContractDeletedMessageHandler, false);
177 this.client.RegisterMessageHandler("petitionContractMsg", NamespaceSmartContractsNeuroFoundationV1, this.PetitionContractMessageHandler, false);
178 this.client.RegisterMessageHandler("petitionContractResponseMsg", NamespaceSmartContractsNeuroFoundationV1, this.PetitionContractResponseMessageHandler, false);
179 this.client.RegisterMessageHandler("contractProposal", NamespaceSmartContractsNeuroFoundationV1, this.ContractProposalMessageHandler, false);
180
181 #endregion
182
183 #region IEEE v1
184
185 this.client.RegisterMessageHandler("identity", NamespaceLegalIdentitiesIeeeV1, this.IdentityMessageHandler, true);
186 this.client.RegisterMessageHandler("petitionIdentityMsg", NamespaceLegalIdentitiesIeeeV1, this.PetitionIdentityMessageHandler, false);
187 this.client.RegisterMessageHandler("petitionIdentityResponseMsg", NamespaceLegalIdentitiesIeeeV1, this.PetitionIdentityResponseMessageHandler, false);
188 this.client.RegisterMessageHandler("petitionSignatureMsg", NamespaceLegalIdentitiesIeeeV1, this.PetitionSignatureMessageHandler, false);
189 this.client.RegisterMessageHandler("petitionSignatureResponseMsg", NamespaceLegalIdentitiesIeeeV1, this.PetitionSignatureResponseMessageHandler, false);
190 this.client.RegisterMessageHandler("petitionClientUrl", NamespaceLegalIdentitiesIeeeV1, this.PetitionClientUrlEventHandler, false);
191
192 this.client.RegisterMessageHandler("contractSigned", NamespaceSmartContractsIeeeV1, this.ContractSignedMessageHandler, true);
193 this.client.RegisterMessageHandler("contractCreated", NamespaceSmartContractsIeeeV1, this.ContractCreatedMessageHandler, false);
194 this.client.RegisterMessageHandler("contractUpdated", NamespaceSmartContractsIeeeV1, this.ContractUpdatedMessageHandler, false);
195 this.client.RegisterMessageHandler("contractDeleted", NamespaceSmartContractsIeeeV1, this.ContractDeletedMessageHandler, false);
196 this.client.RegisterMessageHandler("petitionContractMsg", NamespaceSmartContractsIeeeV1, this.PetitionContractMessageHandler, false);
197 this.client.RegisterMessageHandler("petitionContractResponseMsg", NamespaceSmartContractsIeeeV1, this.PetitionContractResponseMessageHandler, false);
198 this.client.RegisterMessageHandler("contractProposal", NamespaceSmartContractsIeeeV1, this.ContractProposalMessageHandler, false);
199
200 #endregion
201
202 this.aes = Aes.Create();
203 this.aes.BlockSize = 128;
204 this.aes.KeySize = 256;
205 this.aes.Mode = CipherMode.CBC;
206 this.aes.Padding = PaddingMode.None;
207
208 this.keySettingsPrefix = KeySettings;
209 this.contractKeySettingsPrefix = ContractKeySettings;
210 }
211
215 public override void Dispose()
216 {
217 #region NeuroFoundation V1
218
219 this.client.UnregisterMessageHandler("identity", NamespaceLegalIdentitiesNeuroFoundationV1, this.IdentityMessageHandler, true);
220 this.client.UnregisterMessageHandler("petitionIdentityMsg", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionIdentityMessageHandler, false);
221 this.client.UnregisterMessageHandler("petitionIdentityResponseMsg", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionIdentityResponseMessageHandler, false);
222 this.client.UnregisterMessageHandler("petitionSignatureMsg", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionSignatureMessageHandler, false);
223 this.client.UnregisterMessageHandler("petitionSignatureResponseMsg", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionSignatureResponseMessageHandler, false);
224 this.client.UnregisterMessageHandler("petitionClientUrl", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionClientUrlEventHandler, false);
225
226 this.client.UnregisterMessageHandler("contractSigned", NamespaceSmartContractsNeuroFoundationV1, this.ContractSignedMessageHandler, true);
227 this.client.UnregisterMessageHandler("contractCreated", NamespaceSmartContractsNeuroFoundationV1, this.ContractCreatedMessageHandler, false);
228 this.client.UnregisterMessageHandler("contractUpdated", NamespaceSmartContractsNeuroFoundationV1, this.ContractUpdatedMessageHandler, false);
229 this.client.UnregisterMessageHandler("contractDeleted", NamespaceSmartContractsNeuroFoundationV1, this.ContractDeletedMessageHandler, false);
230 this.client.UnregisterMessageHandler("petitionContractMsg", NamespaceSmartContractsNeuroFoundationV1, this.PetitionContractMessageHandler, false);
231 this.client.UnregisterMessageHandler("petitionContractResponseMsg", NamespaceSmartContractsNeuroFoundationV1, this.PetitionContractResponseMessageHandler, false);
232 this.client.UnregisterMessageHandler("contractProposal", NamespaceSmartContractsNeuroFoundationV1, this.ContractProposalMessageHandler, false);
233
234 #endregion
235
236 #region IEEE v1
237
238 this.client.UnregisterMessageHandler("identity", NamespaceLegalIdentitiesIeeeV1, this.IdentityMessageHandler, true);
239 this.client.UnregisterMessageHandler("petitionIdentityMsg", NamespaceLegalIdentitiesIeeeV1, this.PetitionIdentityMessageHandler, false);
240 this.client.UnregisterMessageHandler("petitionIdentityResponseMsg", NamespaceLegalIdentitiesIeeeV1, this.PetitionIdentityResponseMessageHandler, false);
241 this.client.UnregisterMessageHandler("petitionSignatureMsg", NamespaceLegalIdentitiesIeeeV1, this.PetitionSignatureMessageHandler, false);
242 this.client.UnregisterMessageHandler("petitionSignatureResponseMsg", NamespaceLegalIdentitiesIeeeV1, this.PetitionSignatureResponseMessageHandler, false);
243 this.client.UnregisterMessageHandler("petitionClientUrl", NamespaceLegalIdentitiesIeeeV1, this.PetitionClientUrlEventHandler, false);
244
245 this.client.UnregisterMessageHandler("contractSigned", NamespaceSmartContractsIeeeV1, this.ContractSignedMessageHandler, true);
246 this.client.UnregisterMessageHandler("contractCreated", NamespaceSmartContractsIeeeV1, this.ContractCreatedMessageHandler, false);
247 this.client.UnregisterMessageHandler("contractUpdated", NamespaceSmartContractsIeeeV1, this.ContractUpdatedMessageHandler, false);
248 this.client.UnregisterMessageHandler("contractDeleted", NamespaceSmartContractsIeeeV1, this.ContractDeletedMessageHandler, false);
249 this.client.UnregisterMessageHandler("petitionContractMsg", NamespaceSmartContractsIeeeV1, this.PetitionContractMessageHandler, false);
250 this.client.UnregisterMessageHandler("petitionContractResponseMsg", NamespaceSmartContractsIeeeV1, this.PetitionContractResponseMessageHandler, false);
251 this.client.UnregisterMessageHandler("contractProposal", NamespaceSmartContractsIeeeV1, this.ContractProposalMessageHandler, false);
252
253 #endregion
254
255 this.localKeys?.Dispose();
256 this.localKeys = null;
257 this.keysTimestamp = DateTime.MinValue;
258
259 this.rnd?.Dispose();
260 this.rnd = null;
261
262 this.aes?.Dispose();
263 this.aes = null;
264
265 base.Dispose();
266 }
267
271 public override string[] Extensions => new string[] { };
272
276 public string ComponentAddress => this.componentAddress;
277
278 #endregion
279
280 #region Keys
281
285 public DateTime KeysTimestamp => this.keysTimestamp;
286
290 public string KeySettingsPrefix => this.keySettingsPrefix;
291
295 public string ContractKeySettingsPrefix => this.contractKeySettingsPrefix;
296
300 public SymmetricCipherAlgorithms PreferredEncryptionAlgorithm => this.preferredEncryptionAlgorithm;
301
309 {
310 if (this.preferredEncryptionAlgorithm == Algorithm)
311 return;
312
313 if (this.preferredEncryptionAlgorithmLocked)
314 throw new InvalidOperationException("Preferred Encryptio Algorithm has been locked.");
315
316 this.preferredEncryptionAlgorithm = Algorithm;
317 this.preferredEncryptionAlgorithmLocked = Lock;
318 }
319
325 public Task<bool> LoadKeys(bool CreateIfNone)
326 {
327 return this.LoadKeys(CreateIfNone, null);
328 }
329
336 public async Task<bool> LoadKeys(bool CreateIfNone, ProfilerThread Thread)
337 {
338 Thread = Thread?.CreateSubThread("Load Keys", ProfilerThreadType.Sequential);
339 Thread?.Start();
340 try
341 {
342 Thread?.NewState("Search");
343
344 List<IE2eEndpoint> Keys = new List<IE2eEndpoint>();
345 Dictionary<string, object> Settings = await RuntimeSettings.GetWhereKeyLikeAsync(this.keySettingsPrefix + "*", "*");
346
347 Thread?.NewState("Endpoints");
348
349 IE2eEndpoint[] AvailableEndpoints = EndpointSecurity.CreateEndpoints(256, 192, int.MaxValue, typeof(EllipticCurveEndpoint), Thread);
350 DateTime? Timestamp = null;
351 bool DisposeEndpoints = true;
352 byte[] Key;
353
354 Thread?.NewState("Select");
355
356 foreach (KeyValuePair<string, object> Setting in Settings)
357 {
358 string LocalName = Setting.Key.Substring(this.keySettingsPrefix.Length);
359
360 if (Setting.Value is string d)
361 {
362 if (string.IsNullOrEmpty(d))
363 continue;
364
365 try
366 {
367 Key = Convert.FromBase64String(d);
368 }
369 catch (Exception)
370 {
371 continue;
372 }
373
374 foreach (IE2eEndpoint Curve in AvailableEndpoints)
375 {
376 if (Curve.LocalName == LocalName)
377 {
378 Keys.Add(Curve.CreatePrivate(Key));
379 break;
380 }
381 }
382 }
383 else if (Setting.Value is DateTime TP && LocalName == "Timestamp")
384 Timestamp = TP;
385 }
386
387 if (Keys.Count == 0)
388 {
389 if (!CreateIfNone)
390 return false;
391
392 Thread?.NewState("Create");
393
394 DisposeEndpoints = false;
395
396 foreach (IE2eEndpoint Endpoint in AvailableEndpoints)
397 {
398 if (Endpoint is EllipticCurveEndpoint Curve)
399 {
400 Key = this.GetKey(Curve.Curve);
401 await RuntimeSettings.SetAsync(this.keySettingsPrefix + Curve.LocalName, Convert.ToBase64String(Key));
402 Keys.Add(Curve);
403 }
404 }
405
406 Timestamp = DateTime.Now;
407 await RuntimeSettings.SetAsync(this.keySettingsPrefix + "Timestamp", Timestamp.Value);
408
409 Log.Notice("Private keys for contracts client created.", this.client.BareJID, string.Empty, "NewKeys");
410 }
411 else if (!Timestamp.HasValue)
412 {
413 Thread?.NewState("Time");
414
415 Timestamp = DateTime.Now;
416 await RuntimeSettings.SetAsync(this.keySettingsPrefix + "Timestamp", Timestamp.Value);
417 }
418
419 Thread?.NewState("Sec");
420
421 this.localKeys?.Dispose();
422 this.localKeys = null;
423
424 this.localKeys = new EndpointSecurity(this.localKeysForE2e ? this.client : null, 128, Keys.ToArray());
425 this.keysTimestamp = Timestamp.Value;
426
427 if (this.localKeysForE2e)
428 this.localE2eEndpoint = this.localKeys;
429
430 if (DisposeEndpoints)
431 {
432 Thread?.NewState("Dispose");
433
434 foreach (IE2eEndpoint Curve in AvailableEndpoints)
435 Curve.Dispose();
436 }
437 }
438 finally
439 {
440 Thread?.Stop();
441 }
442
443 return true;
444 }
445
446 private byte[] GetKey(EllipticCurve Curve)
447 {
448 string s = Curve.Export();
449 XmlDocument Doc = new XmlDocument()
450 {
451 PreserveWhitespace = true
452 };
453 Doc.LoadXml(s);
454 s = Doc.DocumentElement.GetAttribute("d");
455 return Convert.FromBase64String(s);
456 }
457
461 public async Task GenerateNewKeys()
462 {
463 await RuntimeSettings.DeleteWhereKeyLikeAsync(this.keySettingsPrefix + "*", "*");
464 await this.LoadKeys(true);
465
466 lock (this.matchingKeys)
467 {
468 this.matchingKeys.Clear();
469 }
470
471 foreach (LegalIdentityState State in await Database.Find<LegalIdentityState>(
472 new FilterFieldEqualTo("BareJid", this.client.BareJID)))
473 {
474 LegalIdentity Identity;
475
476 if (State.State == IdentityState.Created || State.State == IdentityState.Approved)
477 {
478 try
479 {
480 Identity = await this.GetLegalIdentityAsync(State.LegalId); // Make sure we have the latest.
481 if (Identity.State != State.State)
482 {
483 State.State = Identity.State;
484 State.Timestamp = Identity.Updated;
485
486 switch (Identity.State)
487 {
488 case IdentityState.Rejected:
489 case IdentityState.Obsoleted:
490 case IdentityState.Compromised:
491 State.PublicKey = null;
492 break;
493 }
494
495 await Database.Update(State);
496 }
497 }
499 {
500 await Database.Delete(State);
501 continue;
502 }
503 catch (Exception ex)
504 {
505 Log.Exception(ex, State.LegalId);
506 }
507 }
508
509 switch (State.State)
510 {
511 case IdentityState.Created:
512 case IdentityState.Approved:
513 try
514 {
515 Identity = await this.ObsoleteLegalIdentityAsync(State.LegalId);
516 await this.UpdateSettings(Identity);
517 }
519 {
520 await Database.Delete(State);
521 }
522 catch (Exception ex)
523 {
524 Log.Exception(ex, State.LegalId);
525 }
526 break;
527 }
528 }
529 }
530
535 public async Task<string> ExportKeys()
536 {
537 StringBuilder Xml = new StringBuilder();
538 XmlWriterSettings Settings = XML.WriterSettings(false, true);
539
540 using (XmlWriter Output = XmlWriter.Create(Xml, Settings))
541 {
542 await this.ExportKeys(Output);
543 }
544
545 return Xml.ToString();
546 }
547
552 public async Task ExportKeys(XmlWriter Output)
553 {
554 this.AssertAllowed();
555
556 Output.WriteStartElement("LegalId", NamespaceOnboarding);
557
558 Dictionary<string, object> Settings = await RuntimeSettings.GetWhereKeyLikeAsync(this.keySettingsPrefix + "*", "*");
559
560 foreach (KeyValuePair<string, object> Setting in Settings)
561 {
562 string Name = Setting.Key.Substring(this.keySettingsPrefix.Length);
563
564 if (Setting.Value is string s)
565 {
566 Output.WriteStartElement("S");
567 Output.WriteAttributeString("n", Name);
568 Output.WriteAttributeString("v", s);
569 Output.WriteEndElement();
570 }
571 else if (Setting.Value is DateTime TP)
572 {
573 Output.WriteStartElement("DT");
574 Output.WriteAttributeString("n", Name);
575 Output.WriteAttributeString("v", XML.Encode(TP));
576 Output.WriteEndElement();
577 }
578 }
579
580 foreach (LegalIdentityState State in await Database.Find<LegalIdentityState>(new FilterAnd(
581 new FilterFieldEqualTo("BareJid", this.client.BareJID),
582 new FilterFieldEqualTo("State", IdentityState.Approved))))
583 {
584 Output.WriteStartElement("State");
585 Output.WriteAttributeString("legalId", State.LegalId);
586 Output.WriteAttributeString("publicKey", Convert.ToBase64String(State.PublicKey));
587 Output.WriteAttributeString("timestamp", XML.Encode(State.Timestamp));
588 Output.WriteEndElement();
589 }
590
591 Settings = await RuntimeSettings.GetWhereKeyLikeAsync(this.contractKeySettingsPrefix + "*", "*");
592
593 foreach (KeyValuePair<string, object> Setting in Settings)
594 {
595 string Name = Setting.Key.Substring(this.contractKeySettingsPrefix.Length);
596
597 if (Setting.Value is string s)
598 {
599 Output.WriteStartElement("C");
600 Output.WriteAttributeString("n", Name);
601 Output.WriteAttributeString("v", s);
602 Output.WriteEndElement();
603 }
604 }
605
606 Output.WriteEndElement();
607 }
608
614 public Task<bool> ImportKeys(string Xml)
615 {
616 XmlDocument Doc = new XmlDocument();
617 Doc.LoadXml(Xml);
618
619 return this.ImportKeys(Doc);
620 }
621
627 public Task<bool> ImportKeys(XmlDocument Xml)
628 {
629 return this.ImportKeys(Xml.DocumentElement);
630 }
631
637 public async Task<bool> ImportKeys(XmlElement Xml)
638 {
639 this.AssertAllowed();
640
641 if (Xml is null || Xml.LocalName != "LegalId" || Xml.NamespaceURI != NamespaceOnboarding)
642 return false;
643
644 foreach (XmlNode N in Xml.ChildNodes)
645 {
646 if (!(N is XmlElement E))
647 continue;
648
649 if (E.NamespaceURI != NamespaceOnboarding)
650 return false;
651
652 switch (E.LocalName)
653 {
654 case "S":
655 string Name = XML.Attribute(E, "n");
656 string StringValue = XML.Attribute(E, "v");
657
658 await RuntimeSettings.SetAsync(this.keySettingsPrefix + Name, StringValue);
659 break;
660
661 case "DT":
662 Name = XML.Attribute(E, "n");
663 DateTime DateTimeValue = XML.Attribute(E, "v", DateTime.MinValue);
664
665 await RuntimeSettings.SetAsync(this.keySettingsPrefix + Name, DateTimeValue);
666 break;
667
668 case "C":
669 Name = XML.Attribute(E, "n");
670 StringValue = XML.Attribute(E, "v");
671
672 await RuntimeSettings.SetAsync(this.contractKeySettingsPrefix + Name, StringValue);
673 break;
674
675 case "State":
676 string LegalId = XML.Attribute(E, "legalId");
677 string PublicKeyStr = XML.Attribute(E, "publicKey");
678 byte[] PublicKey;
679 DateTimeValue = XML.Attribute(E, "timestamp", DateTime.MinValue);
680
681 try
682 {
683 PublicKey = Convert.FromBase64String(PublicKeyStr);
684 }
685 catch (Exception)
686 {
687 return false;
688 }
689
690 LegalIdentityState IdState = await Database.FindFirstDeleteRest<LegalIdentityState>(new FilterAnd(
691 new FilterFieldEqualTo("BareJid", this.client.BareJID),
692 new FilterFieldEqualTo("LegalId", LegalId)));
693
694 if (IdState is null)
695 {
696 IdState = new LegalIdentityState()
697 {
698 BareJid = this.client.BareJID,
699 LegalId = LegalId,
700 State = IdentityState.Approved,
701 Timestamp = DateTimeValue,
702 PublicKey = PublicKey
703 };
704
705 await Database.Insert(IdState);
706 }
707 else
708 {
709 IdState.State = IdentityState.Approved;
710 IdState.Timestamp = DateTimeValue;
711 IdState.PublicKey = PublicKey;
712
713 await Database.Update(IdState);
714 }
715 break;
716
717 default:
718 return false;
719 }
720 }
721
722 return await this.LoadKeys(false);
723 }
724
730 public void SetKeySettingsInstance(string InstanceName, bool Locked)
731 {
732 if (this.keySettingsPrefixLocked)
733 throw new InvalidOperationException("Key settings instance is locked.");
734
735 if (string.IsNullOrEmpty(InstanceName))
736 {
737 this.keySettingsPrefix = KeySettings;
738 this.contractKeySettingsPrefix = ContractKeySettings;
739 }
740 else
741 {
742 this.keySettingsPrefix = InstanceName + "." + KeySettings;
743 this.contractKeySettingsPrefix = InstanceName + "." + ContractKeySettings;
744 }
745
746 this.keySettingsPrefixLocked = Locked;
747 }
748
753 public Task EnableE2eEncryption(bool UseLocalKeys)
754 {
755 return this.EnableE2eEncryption(UseLocalKeys, true);
756 }
757
763 public async Task EnableE2eEncryption(bool UseLocalKeys, bool CreateKeysIfNone)
764 {
765 bool Reload = !(this.localKeys is null);
766
767 this.localKeysForE2e = UseLocalKeys;
768
769 if (Reload)
770 await this.LoadKeys(CreateKeysIfNone);
771 }
772
778 {
779 return this.EnableE2eEncryption(E2eEndpoint, true);
780 }
781
787 public async Task EnableE2eEncryption(EndpointSecurity E2eEndpoint, bool CreateKeysIfNone)
788 {
789 bool Reload = !(this.localKeys is null);
790
791 this.localKeysForE2e = false;
792 this.localE2eEndpoint = E2eEndpoint;
793
794 if (Reload)
795 await this.LoadKeys(CreateKeysIfNone);
796 }
797
804 public byte[] RandomBytes(int Nr)
805 {
806 if (Nr < 0)
807 throw new ArgumentException(nameof(Nr));
808
809 byte[] Bytes = new byte[Nr];
810
811 this.rnd.GetBytes(Bytes);
812
813 return Bytes;
814 }
815
820 public ulong RandomInteger()
821 {
822 byte[] Bin = this.RandomBytes(8);
823 return BitConverter.ToUInt64(Bin, 0);
824 }
825
831 public ulong RandomInteger(ulong MaxExclusive)
832 {
833 if (MaxExclusive == 0)
834 throw new ArgumentException(nameof(MaxExclusive));
835
836 return this.RandomInteger() % MaxExclusive;
837 }
838
847 public int RandomInteger(int MinInclusive, int MaxInclusive)
848 {
849 if (MaxInclusive < MinInclusive)
850 throw new ArgumentException(nameof(MaxInclusive));
851
852 ulong Diff = (uint)(MaxInclusive - MinInclusive);
853 if (Diff == 0)
854 return MinInclusive;
855
856 int Result = (int)this.RandomInteger(Diff + 1UL);
857 Result += MinInclusive;
858
859 return Result;
860 }
861
862 #endregion
863
864 #region Security
865
871 public void SetAllowedSources(object[] ApprovedSources)
872 {
873 if (!(this.approvedSources is null))
874 throw new NotSupportedException("Changing approved sources not permitted.");
875
876 this.approvedSources = ApprovedSources;
877 }
878
879 private void AssertAllowed()
880 {
881 if (!(this.approvedSources is null))
882 Assert.CallFromSource(this.approvedSources);
883 }
884
885 #endregion
886
887 #region URIs
888
894 public static string LegalIdUriString(string LegalId)
895 {
896 return "iotid:" + LegalId;
897 }
898
904 public static Uri LegalIdUri(string LegalId)
905 {
906 return new Uri(LegalIdUriString(LegalId));
907 }
908
914 public static string ContractIdUriString(string ContractId)
915 {
916 return "iotsc:" + ContractId;
917 }
918
924 public static Uri ContractIdUri(string ContractId)
925 {
926 return new Uri(ContractIdUriString(ContractId));
927 }
928
929 #endregion
930
931 #region Server Public Keys
932
938 public Task GetServerPublicKey(EventHandlerAsync<KeyEventArgs> Callback, object State)
939 {
940 return this.GetServerPublicKey(this.componentAddress, Callback, State);
941 }
942
949 public async Task GetServerPublicKey(string Address, EventHandlerAsync<KeyEventArgs> Callback, object State)
950 {
951 KeyEventArgs e0;
952
953 lock (this.publicKeys)
954 {
955 if (!this.publicKeys.TryGetValue(Address, out e0))
956 e0 = null;
957 }
958
959 if (!(e0 is null))
960 {
961 e0 = new KeyEventArgs(e0, e0.Key)
962 {
963 State = State
964 };
965
966 await Callback.Raise(this, e0);
967 }
968 else
969 {
970 await this.client.SendIqGet(Address, "<getPublicKey xmlns=\"" + NamespaceLegalIdentitiesCurrent + "\"/>", async (Sender, e) =>
971 {
972 IE2eEndpoint ServerKey = null;
973 XmlElement E;
974
975 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "publicKey")
976 {
977 foreach (XmlNode N in E.ChildNodes)
978 {
979 if (N is XmlElement E2)
980 {
981 ServerKey = EndpointSecurity.ParseE2eKey(E2);
982 if (!(ServerKey is null))
983 break;
984 }
985 }
986
987 e.Ok = !(ServerKey is null);
988 }
989 else
990 e.Ok = false;
991
992 e0 = new KeyEventArgs(e, ServerKey);
993
994 if (e0.Ok)
995 {
996 lock (this.publicKeys)
997 {
998 this.publicKeys[Address] = e0;
999 }
1000 }
1001
1002 await Callback.Raise(this, e0);
1003 }, State);
1004 }
1005 }
1006
1011 public Task<IE2eEndpoint> GetServerPublicKeyAsync()
1012 {
1013 return this.GetServerPublicKeyAsync(this.componentAddress);
1014 }
1015
1021 public async Task<IE2eEndpoint> GetServerPublicKeyAsync(string Address)
1022 {
1023 TaskCompletionSource<IE2eEndpoint> Result = new TaskCompletionSource<IE2eEndpoint>();
1024
1025 await this.GetServerPublicKey(Address, (Sender, e) =>
1026 {
1027 if (e.Ok)
1028 Result.TrySetResult(e.Key);
1029 else
1030 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get public key."));
1031
1032 return Task.CompletedTask;
1033
1034 }, null);
1035
1036 return await Result.Task;
1037 }
1038
1039 #endregion
1040
1041 #region Matching Local Keys
1042
1048 public Task GetMatchingLocalKey(EventHandlerAsync<KeyEventArgs> Callback, object State)
1049 {
1050 return this.GetMatchingLocalKey(this.componentAddress, Callback, State);
1051 }
1052
1056 private EndpointSecurity LocalEndpoint
1057 {
1058 get
1059 {
1060 if (this.localKeys is null)
1061 throw new InvalidOperationException("Local keys not loaded or generated.");
1062
1063 return this.localKeys;
1064 }
1065 }
1066
1070 public EndpointSecurity LocalE2eEndpoint
1071 {
1072 get
1073 {
1074 if (this.localE2eEndpoint is null)
1075 throw new InvalidOperationException("End-to-End Encryption not enabled or Local keys not loaded or generated.");
1076
1077 return this.localE2eEndpoint;
1078 }
1079 }
1080
1087 public async Task GetMatchingLocalKey(string Address, EventHandlerAsync<KeyEventArgs> Callback, object State)
1088 {
1089 KeyEventArgs e0;
1090
1091 lock (this.matchingKeys)
1092 {
1093 if (!this.matchingKeys.TryGetValue(Address, out e0))
1094 e0 = null;
1095 }
1096
1097 if (!(e0 is null))
1098 {
1099 e0 = new KeyEventArgs(e0, e0.Key)
1100 {
1101 State = State
1102 };
1103
1104 await Callback.Raise(this, e0);
1105 }
1106 else
1107 {
1108 await this.GetServerPublicKey(Address, async (Sender, e) =>
1109 {
1110 IE2eEndpoint LocalKey = null;
1111
1112 if (e.Ok)
1113 {
1114 LocalKey = this.LocalEndpoint.GetLocalKey(e.Key);
1115 if (LocalKey is null)
1116 e.Ok = false;
1117 }
1118
1119 e0 = new KeyEventArgs(e, LocalKey);
1120
1121 if (e0.Ok)
1122 {
1123 lock (this.matchingKeys)
1124 {
1125 this.matchingKeys[Address] = e0;
1126 }
1127 }
1128
1129 await Callback.Raise(this, e0);
1130
1131 }, State);
1132 }
1133 }
1134
1139 public Task<IE2eEndpoint> GetMatchingLocalKeyAsync()
1140 {
1141 return this.GetMatchingLocalKeyAsync(this.componentAddress);
1142 }
1143
1149 public async Task<IE2eEndpoint> GetMatchingLocalKeyAsync(string Address)
1150 {
1151 TaskCompletionSource<IE2eEndpoint> Result = new TaskCompletionSource<IE2eEndpoint>();
1152
1153 await this.GetMatchingLocalKey(Address, (Sender, e) =>
1154 {
1155 if (e.Ok)
1156 Result.TrySetResult(e.Key);
1157 else
1158 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get matching local key."));
1159
1160 return Task.CompletedTask;
1161
1162 }, null);
1163
1164 return await Result.Task;
1165 }
1166
1167 #endregion
1168
1169 #region ID Application Attributes
1170
1176 public Task GetIdApplicationAttributes(EventHandlerAsync<IdApplicationAttributesEventArgs> Callback, object State)
1177 {
1178 return this.client.SendIqGet(this.componentAddress, "<applicationAttributes xmlns='" + NamespaceLegalIdentitiesCurrent + "'/>", (Sender, e) =>
1179 {
1180 return Callback.Raise(this, new IdApplicationAttributesEventArgs(e));
1181
1182 }, State);
1183 }
1184
1189 public async Task<IdApplicationAttributesEventArgs> GetIdApplicationAttributesAsync()
1190 {
1191 TaskCompletionSource<IdApplicationAttributesEventArgs> Result = new TaskCompletionSource<IdApplicationAttributesEventArgs>();
1192
1193 await this.GetIdApplicationAttributes((Sender, e) =>
1194 {
1195 if (e.Ok)
1196 Result.TrySetResult(e);
1197 else
1198 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get ID Application attributes."));
1199
1200 return Task.CompletedTask;
1201 }, null);
1202
1203 return await Result.Task;
1204 }
1205
1206 #endregion
1207
1208 #region Apply for a Legal Identity
1209
1216 public Task Apply(Property[] Properties, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
1217 {
1218 return this.Apply(this.componentAddress, Properties, Callback, State);
1219 }
1220
1228 public async Task Apply(string Address, Property[] Properties, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
1229 {
1230 this.AssertAllowed();
1231
1232 await this.GetMatchingLocalKey(Address, async (Sender, e) =>
1233 {
1234 if (e.Ok)
1235 {
1236 StringBuilder Xml = new StringBuilder();
1237
1238 Xml.Append("<apply xmlns=\"");
1239 Xml.Append(NamespaceLegalIdentitiesCurrent);
1240 Xml.Append("\">");
1241
1242 StringBuilder Identity = new StringBuilder();
1243
1244 Identity.Append("<identity><clientPublicKey>");
1245 e.Key.ToXml(Identity, NamespaceLegalIdentitiesCurrent);
1246 Identity.Append("</clientPublicKey>");
1247
1248 foreach (Property Property in Properties)
1249 {
1250 Identity.Append("<property name=\"");
1251 Identity.Append(XML.Encode(Property.Name));
1252 Identity.Append("\" value=\"");
1253 Identity.Append(XML.Encode(Property.Value));
1254 Identity.Append("\"/>");
1255 }
1256
1257 string s = Identity.ToString();
1258 Xml.Append(s);
1259
1260 s += "</identity>";
1261
1262 byte[] Bin = Encoding.UTF8.GetBytes(s);
1263 byte[] Signature = e.Key.Sign(Bin);
1264
1265 Xml.Append("<clientSignature>");
1266 Xml.Append(Convert.ToBase64String(Signature));
1267 Xml.Append("</clientSignature>");
1268
1269 Xml.Append("</identity></apply>");
1270
1271 await this.client.SendIqSet(Address, Xml.ToString(), async (sender2, e2) =>
1272 {
1273 LegalIdentity Identity2 = null;
1274 XmlElement E;
1275
1276 if (e2.Ok && !((E = e2.FirstElement) is null) &&
1277 E.LocalName == "identity")
1278 {
1279 Identity2 = LegalIdentity.Parse(E);
1280 await this.UpdateSettings(Identity2, e.Key.PublicKey);
1281 }
1282 else
1283 e2.Ok = false;
1284
1285 await Callback.Raise(this, new LegalIdentityEventArgs(e2, Identity2));
1286 }, e.State);
1287 }
1288 else
1289 await Callback.Raise(this, new LegalIdentityEventArgs(e, null));
1290 }, State);
1291 }
1292
1298 public Task<LegalIdentity> ApplyAsync(Property[] Properties)
1299 {
1300 return this.ApplyAsync(this.componentAddress, Properties);
1301 }
1302
1309 public async Task<LegalIdentity> ApplyAsync(string Address, Property[] Properties)
1310 {
1311 TaskCompletionSource<LegalIdentity> Result = new TaskCompletionSource<LegalIdentity>();
1312
1313 await this.Apply(Address, Properties, (Sender, e) =>
1314 {
1315 if (e.Ok)
1316 Result.TrySetResult(e.Identity);
1317 else
1318 Result.TrySetException(e.StanzaError ?? new Exception("Unable to apply for a legal identity to be registered."));
1319
1320 return Task.CompletedTask;
1321
1322 }, null);
1323
1324 return await Result.Task;
1325 }
1326
1327 #endregion
1328
1329 #region Mark Identity as Ready for Approval
1330
1339 public Task ReadyForApproval(string LegalIdentityId, EventHandlerAsync<IqResultEventArgs> Callback, object State)
1340 {
1341 return this.ReadyForApproval(this.componentAddress, LegalIdentityId, Callback, State);
1342 }
1343
1353 public Task ReadyForApproval(string Address, string LegalIdentityId, EventHandlerAsync<IqResultEventArgs> Callback, object State)
1354 {
1355 this.AssertAllowed();
1356
1357 StringBuilder Xml = new StringBuilder();
1358
1359 Xml.Append("<readyForApproval xmlns=\"");
1360 Xml.Append(NamespaceLegalIdentitiesCurrent);
1361 Xml.Append("\" id=\"");
1362 Xml.Append(XML.Encode(LegalIdentityId));
1363 Xml.Append("\"/>");
1364
1365 return this.client.SendIqSet(Address, Xml.ToString(), Callback, State);
1366 }
1367
1374 public Task ReadyForApprovalAsync(string LegalIdentityId)
1375 {
1376 return this.ReadyForApprovalAsync(this.componentAddress, LegalIdentityId);
1377 }
1378
1386 public async Task ReadyForApprovalAsync(string Address, string LegalIdentityId)
1387 {
1388 TaskCompletionSource<bool> Result = new TaskCompletionSource<bool>();
1389
1390 await this.ReadyForApproval(Address, LegalIdentityId, (Sender, e) =>
1391 {
1392 if (e.Ok)
1393 Result.TrySetResult(true);
1394 else
1395 Result.TrySetException(e.StanzaError ?? new Exception("Unable to flag identity as ready for approval."));
1396
1397 return Task.CompletedTask;
1398
1399 }, null);
1400
1401 await Result.Task;
1402 }
1403
1404 #endregion
1405
1406 #region Validate Legal Identity
1407
1414 public Task Validate(LegalIdentity Identity, EventHandlerAsync<IdentityValidationEventArgs> Callback, object State)
1415 {
1416 return this.Validate(Identity, true, Callback, State);
1417 }
1418
1426 public async Task Validate(LegalIdentity Identity, bool ValidateState, EventHandlerAsync<IdentityValidationEventArgs> Callback, object State)
1427 {
1428 if (Identity is null)
1429 {
1430 await this.ReturnStatus(IdentityStatus.IdentityUndefined, Callback, State);
1431 return;
1432 }
1433
1434 if (ValidateState && Identity.State != IdentityState.Approved)
1435 {
1436 await this.ReturnStatus(IdentityStatus.NotApproved, Callback, State);
1437 return;
1438 }
1439
1440 DateTime Now = DateTime.Now;
1441
1442 if (Now.Date.AddDays(1) < Identity.From) // To avoid Time-zone problems
1443 {
1444 await this.ReturnStatus(IdentityStatus.NotValidYet, Callback, State);
1445 return;
1446 }
1447
1448 if (Now.Date.AddDays(-1) > Identity.To) // To avoid Time-zone problems
1449 {
1450 await this.ReturnStatus(IdentityStatus.NotValidAnymore, Callback, State);
1451 return;
1452 }
1453
1454 if (string.IsNullOrEmpty(Identity.Provider))
1455 {
1456 await this.ReturnStatus(IdentityStatus.NoTrustProvider, Callback, State);
1457 return;
1458 }
1459
1460 if (string.IsNullOrEmpty(Identity.ClientKeyName) ||
1461 Identity.ClientPubKey is null || Identity.ClientPubKey.Length == 0)
1462 {
1463 await this.ReturnStatus(IdentityStatus.NoClientPublicKey, Callback, State);
1464 return;
1465 }
1466
1467 if (Identity.ClientSignature is null || Identity.ClientSignature.Length == 0)
1468 {
1469 await this.ReturnStatus(IdentityStatus.NoClientSignature, Callback, State);
1470 return;
1471 }
1472
1473 StringBuilder Xml = new StringBuilder();
1474 Identity.Serialize(Xml, false, false, false, false, false, false, false);
1475 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
1476
1477 bool? b = this.ValidateSignature(Identity, Data, Identity.ClientSignature);
1478 if (b.HasValue)
1479 {
1480 if (!b.Value)
1481 {
1482 await this.ReturnStatus(IdentityStatus.ClientSignatureInvalid, Callback, State);
1483 return;
1484 }
1485 }
1486 else
1487 {
1488 await this.ReturnStatus(IdentityStatus.ClientKeyNotRecognized, Callback, State);
1489 return;
1490 }
1491
1492 if (Identity.State == IdentityState.Approved && ValidateState && !(Identity.Attachments is null))
1493 {
1494 foreach (Attachment Attachment in Identity.Attachments)
1495 {
1496 if (string.IsNullOrEmpty(Attachment.Url))
1497 {
1498 await this.ReturnStatus(IdentityStatus.AttachmentLacksUrl, Callback, State);
1499 return;
1500 }
1501
1502 try
1503 {
1504 KeyValuePair<string, TemporaryFile> P = await this.GetAttachmentAsync(Attachment.Url, SignWith.LatestApprovedIdOrCurrentKeys, 30000);
1505
1506 using (TemporaryFile File = P.Value)
1507 {
1508 if (P.Key != Attachment.ContentType)
1509 {
1510 await this.ReturnStatus(IdentityStatus.AttachmentInconsistency, Callback, State);
1511 return;
1512 }
1513
1514 File.Position = 0;
1515
1516 b = this.ValidateSignature(Identity, File, Attachment.Signature);
1517 if (b.HasValue)
1518 {
1519 if (!b.Value)
1520 {
1521 await this.ReturnStatus(IdentityStatus.AttachmentSignatureInvalid, Callback, State);
1522 return;
1523 }
1524 }
1525 else
1526 {
1527 await this.ReturnStatus(IdentityStatus.ClientKeyNotRecognized, Callback, State);
1528 return;
1529 }
1530 }
1531 }
1532 catch (Exception ex)
1533 {
1534 await this.client.Error("Attachment " + Attachment.Url + "unavailable: " + ex.Message);
1535 await this.ReturnStatus(IdentityStatus.AttachmentUnavailable, Callback, State);
1536 return;
1537 }
1538 }
1539 }
1540
1541 if (Identity.ServerSignature is null || Identity.ServerSignature.Length == 0)
1542 {
1543 await this.ReturnStatus(IdentityStatus.NoProviderSignature, Callback, State);
1544 return;
1545 }
1546
1547 Xml.Clear();
1548 Identity.Serialize(Xml, false, true, true, true, true, false, false);
1549 Data = Encoding.UTF8.GetBytes(Xml.ToString());
1550
1551 bool HasOldPublicKey;
1552
1553 lock (this.publicKeys)
1554 {
1555 HasOldPublicKey = this.publicKeys.ContainsKey(Identity.Provider);
1556 }
1557
1558 await this.GetServerPublicKey(Identity.Provider, async (Sender, e) =>
1559 {
1560 if (e.Ok && !(e.Key is null))
1561 {
1562 bool Valid = e.Key.Verify(Data, Identity.ServerSignature);
1563
1564 if (Valid)
1565 {
1566 await this.ReturnStatus(IdentityStatus.Valid, Callback, State);
1567 return;
1568 }
1569
1570 if (!HasOldPublicKey)
1571 {
1572 await this.ReturnStatus(IdentityStatus.ProviderSignatureInvalid, Callback, State);
1573 return;
1574 }
1575
1576 lock (this.publicKeys)
1577 {
1578 this.publicKeys.Remove(Identity.Provider);
1579 }
1580
1581 await this.GetServerPublicKey(Identity.Provider, (sender2, e2) =>
1582 {
1583 if (e2.Ok && !(e2.Key is null))
1584 {
1585 if (e.Key.Equals(e2.Key))
1586 return this.ReturnStatus(IdentityStatus.ProviderSignatureInvalid, Callback, State);
1587
1588 Valid = e2.Key.Verify(Data, Identity.ServerSignature);
1589
1590 if (Valid)
1591 return this.ReturnStatus(IdentityStatus.Valid, Callback, State);
1592 else
1593 return this.ReturnStatus(IdentityStatus.ProviderSignatureInvalid, Callback, State);
1594 }
1595 else
1596 return this.ReturnStatus(IdentityStatus.NoProviderPublicKey, Callback, State);
1597
1598 }, State);
1599 }
1600 else
1601 await this.ReturnStatus(IdentityStatus.NoProviderPublicKey, Callback, State);
1602
1603 }, State);
1604 }
1605
1617 public bool? ValidateSignature(LegalIdentity Identity, byte[] Data, byte[] Signature)
1618 {
1619 if (Identity.ClientKeyName.StartsWith("RSA") &&
1620 int.TryParse(Identity.ClientKeyName.Substring(3), out int KeySize))
1621 {
1622 return RsaEndpoint.Verify(Data, Signature, KeySize, Identity.ClientPubKey);
1623 }
1625 Identity.Namespace.Replace(":iot:leg:id:", ":iot:e2e:").Replace("urn:ieee:", "urn:nf:"),
1626 out IE2eEndpoint LocalKey) &&
1627 LocalKey is EllipticCurveEndpoint LocalEc)
1628 {
1629 return LocalEc.Verify(Data, Identity.ClientPubKey, Signature);
1630 }
1631 else
1632 return null;
1633 }
1634
1646 public bool? ValidateSignature(LegalIdentity Identity, Stream Data, byte[] Signature)
1647 {
1648 if (Identity.ClientKeyName.StartsWith("RSA") &&
1649 int.TryParse(Identity.ClientKeyName.Substring(3), out int KeySize))
1650 {
1651 return RsaEndpoint.Verify(Data, Signature, KeySize, Identity.ClientPubKey);
1652 }
1654 Identity.Namespace.Replace(":iot:leg:id:", ":iot:e2e:").Replace("urn:ieee:", "urn:nf:"),
1655 out IE2eEndpoint LocalKey) &&
1656 LocalKey is EllipticCurveEndpoint LocalEc)
1657 {
1658 return LocalEc.Verify(Data, Identity.ClientPubKey, Signature);
1659 }
1660 else
1661 return null;
1662 }
1663
1664 private async Task ReturnStatus(IdentityStatus Status, EventHandlerAsync<IdentityValidationEventArgs> Callback, object State)
1665 {
1666 await Callback.Raise(this, new IdentityValidationEventArgs(Status, State));
1667 }
1668
1674 public Task<IdentityStatus> ValidateAsync(LegalIdentity Identity)
1675 {
1676 return this.ValidateAsync(Identity, true);
1677 }
1678
1685 public async Task<IdentityStatus> ValidateAsync(LegalIdentity Identity, bool ValidateState)
1686 {
1687 TaskCompletionSource<IdentityStatus> Result = new TaskCompletionSource<IdentityStatus>();
1688
1689 await this.Validate(Identity, ValidateState, (Sender, e) =>
1690 {
1691 Result.TrySetResult(e.Status);
1692 return Task.CompletedTask;
1693 }, null);
1694
1695 return await Result.Task;
1696 }
1697
1698 #endregion
1699
1700 #region Legal Identity update event
1701
1702 private bool IsFromTrustProvider(string Id, string From)
1703 {
1704 int i = Id.IndexOf('@');
1705 if (i < 0)
1706 return false;
1707
1708 Id = Id.Substring(i + 1);
1709
1710 i = From.IndexOf('@');
1711 if (i >= 0)
1712 return false;
1713
1714 return (string.Compare(Id, From, true) == 0 ||
1715 From.EndsWith("." + Id, StringComparison.CurrentCultureIgnoreCase));
1716 }
1717
1718 private async Task IdentityMessageHandler(object Sender, MessageEventArgs e)
1719 {
1721
1722 if (!this.IsFromTrustProvider(Identity.Id, e.From))
1723 {
1724 await this.client.Warning("Incoming identity message discarded: " + Identity.Id + " not from " + e.From + ".");
1725 return;
1726 }
1727
1728 if (string.Compare(e.FromBareJID, Identity.Provider, true) != 0)
1729 {
1730 await this.client.Warning("Incoming identity message discarded: Sender " + e.FromBareJID + " not equal to Trust Provider " + Identity.Provider + ".");
1731 return;
1732 }
1733
1734 await this.Validate(Identity, false, async (sender2, e2) =>
1735 {
1736 if (e2.Status != IdentityStatus.Valid)
1737 {
1738 await this.client.Warning("Invalid legal identity received and discarded. Validation status: " + e2.Status.ToString());
1739
1740 Log.Warning("Invalid legal identity received and discarded.", this.client.BareJID, e.From,
1741 new KeyValuePair<string, object>("Status", e2.Status));
1742
1743 return;
1744 }
1745
1746 await this.UpdateSettings(Identity);
1747 await this.IdentityUpdated.Raise(this, new LegalIdentityEventArgs(new IqResultEventArgs(e.Message, e.Id, e.To, e.From, e.Ok, null), Identity));
1748
1749 }, null);
1750 }
1751
1752 private Task UpdateSettings(LegalIdentity Identity)
1753 {
1754 return this.UpdateSettings(Identity, null);
1755 }
1756
1763 public Task<bool> HasPrivateKey(LegalIdentity Identity)
1764 {
1765 return this.HasPrivateKey(Identity.Id);
1766 }
1767
1774 public async Task<bool> HasPrivateKey(string IdentityId)
1775 {
1776 LegalIdentityState State = await Database.FindFirstIgnoreRest<LegalIdentityState>(new FilterAnd(
1777 new FilterFieldEqualTo("BareJid", this.client.BareJID),
1778 new FilterFieldEqualTo("LegalId", IdentityId)));
1779
1780 if (State?.PublicKey is null)
1781 return false;
1782
1783 IE2eEndpoint Endpoint = this.LocalEndpoint.GetLocalKey(State.PublicKey);
1784
1785 return !(Endpoint is null);
1786 }
1787
1792 public Task<string> GetLatestApprovedLegalId()
1793 {
1794 return this.GetLatestApprovedLegalId(null);
1795 }
1796
1802 public async Task<string> GetLatestApprovedLegalId(byte[] PublicKey)
1803 {
1804 string PublicKeyBase64 = PublicKey is null ? string.Empty : Convert.ToBase64String(PublicKey);
1805
1806 foreach (LegalIdentityState State in await Database.Find<LegalIdentityState>(new FilterAnd(
1807 new FilterFieldEqualTo("BareJid", this.client.BareJID),
1808 new FilterFieldEqualTo("State", IdentityState.Approved)), "-Timestamp"))
1809 {
1810 if (!(PublicKey is null) && Convert.ToBase64String(State.PublicKey) != PublicKeyBase64)
1811 continue;
1812
1813 IE2eEndpoint Endpoint = this.LocalEndpoint.GetLocalKey(State.PublicKey);
1814 if (Endpoint is null)
1815 continue;
1816
1817 return State.LegalId;
1818 }
1819
1820 return null;
1821 }
1822
1823 private async Task<IE2eEndpoint> GetLatestApprovedKey(bool ExceptionIfNone)
1824 {
1825 bool HaveStates = false;
1826
1827 foreach (LegalIdentityState State in await Database.Find<LegalIdentityState>(new FilterAnd(
1828 new FilterFieldEqualTo("BareJid", this.client.BareJID),
1829 new FilterFieldEqualTo("State", IdentityState.Approved)), "-Timestamp"))
1830 {
1831 HaveStates = true;
1832
1833 IE2eEndpoint Endpoint = this.LocalEndpoint.GetLocalKey(State.PublicKey);
1834 if (Endpoint is null)
1835 continue;
1836
1837 return Endpoint;
1838 }
1839
1840 if (ExceptionIfNone)
1841 {
1842 if (HaveStates)
1843 {
1844 throw new Exception("Private keys are not available on this device (" + this.client.BareJID +
1845 "). Were they created on another device?");
1846 }
1847 else
1848 throw new Exception("No approved legal identity available on this device (" + this.client.BareJID + ").");
1849 }
1850
1851 return null;
1852 }
1853
1854 private async Task UpdateSettings(LegalIdentity Identity, byte[] PublicKey)
1855 {
1856 if (!string.IsNullOrEmpty(Identity.Id))
1857 {
1858 LegalIdentityState StateObj = Types.Instantiate<LegalIdentityState>(false, Identity.Id);
1859
1860 if (string.IsNullOrEmpty(StateObj.ObjectId))
1861 {
1862 LegalIdentityState StateObj2 = await Database.FindFirstDeleteRest<LegalIdentityState>(new FilterAnd(
1863 new FilterFieldEqualTo("BareJid", this.client.BareJID),
1864 new FilterFieldEqualTo("LegalId", Identity.Id)));
1865
1866 if (StateObj2 is null)
1867 StateObj.BareJid = this.client.BareJID;
1868 else
1869 {
1870 Types.UnregisterSingleton(StateObj, Identity.Id);
1871 Types.RegisterSingleton(StateObj2, Identity.Id);
1872 StateObj = StateObj2;
1873 }
1874 }
1875
1876 DateTime Timestamp = Identity.Updated > Identity.Created ? Identity.Updated : Identity.Created;
1877
1878 if (Timestamp > StateObj.Timestamp ||
1879 (StateObj.PublicKey is null && !(PublicKey is null)) ||
1880 Identity.State > StateObj.State)
1881 {
1882 StateObj.State = Identity.State;
1883 StateObj.Timestamp = Timestamp;
1884
1885 if (PublicKey is null)
1886 {
1887 switch (Identity.State)
1888 {
1889 case IdentityState.Compromised:
1890 case IdentityState.Obsoleted:
1891 case IdentityState.Rejected:
1892 StateObj.PublicKey = null;
1893 break;
1894 }
1895 }
1896 else
1897 StateObj.PublicKey = PublicKey;
1898
1899 if (string.IsNullOrEmpty(StateObj.ObjectId))
1900 await Database.Insert(StateObj);
1901 else
1902 await Database.Update(StateObj);
1903 }
1904 }
1905 }
1906
1911 public event EventHandlerAsync<LegalIdentityEventArgs> IdentityUpdated = null;
1912
1913 #endregion
1914
1915 #region Get Legal Identities
1916
1922 public Task GetLegalIdentities(EventHandlerAsync<LegalIdentitiesEventArgs> Callback, object State)
1923 {
1924 return this.GetLegalIdentities(this.componentAddress, Callback, State);
1925 }
1926
1933 public Task GetLegalIdentities(string Address, EventHandlerAsync<LegalIdentitiesEventArgs> Callback, object State)
1934 {
1935 return this.client.SendIqGet(Address, "<getLegalIdentities xmlns=\"" + NamespaceLegalIdentitiesCurrent + "\"/>",
1936 this.IdentitiesResponse, new object[] { Callback, State });
1937 }
1938
1939 private async Task IdentitiesResponse(object Sender, IqResultEventArgs e)
1940 {
1941 object[] P = (object[])e.State;
1942 EventHandlerAsync<LegalIdentitiesEventArgs> Callback = (EventHandlerAsync<LegalIdentitiesEventArgs>)P[0];
1943 LegalIdentity[] Identities = null;
1944 XmlElement E;
1945
1946 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "identities")
1947 {
1948 List<LegalIdentity> IdentitiesList = new List<LegalIdentity>();
1949
1950 foreach (XmlNode N in E.ChildNodes)
1951 {
1952 if (N is XmlElement E2 && E2.LocalName == "identity")
1953 IdentitiesList.Add(LegalIdentity.Parse(E2));
1954 }
1955
1956 Identities = IdentitiesList.ToArray();
1957 }
1958 else
1959 e.Ok = false;
1960
1961 e.State = P[1];
1962 await Callback.Raise(this, new LegalIdentitiesEventArgs(e, Identities));
1963 }
1964
1969 public Task<LegalIdentity[]> GetLegalIdentitiesAsync()
1970 {
1971 return this.GetLegalIdentitiesAsync(this.componentAddress);
1972 }
1973
1979 public async Task<LegalIdentity[]> GetLegalIdentitiesAsync(string Address)
1980 {
1981 TaskCompletionSource<LegalIdentity[]> Result = new TaskCompletionSource<LegalIdentity[]>();
1982
1983 await this.GetLegalIdentities(Address, (Sender, e) =>
1984 {
1985 if (e.Ok)
1986 Result.TrySetResult(e.Identities);
1987 else
1988 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get legal identities."));
1989
1990 return Task.CompletedTask;
1991
1992 }, null);
1993
1994 return await Result.Task;
1995 }
1996
1997 #endregion
1998
1999 #region Get Legal Identity
2000
2007 public Task GetLegalIdentity(string LegalIdentityId, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
2008 {
2009 return this.GetLegalIdentity(this.GetTrustProvider(LegalIdentityId), LegalIdentityId, Callback, State);
2010 }
2011
2017 public string GetTrustProvider(string EntityId)
2018 {
2019 int i = EntityId.IndexOf('@');
2020 if (i < 0)
2021 return this.componentAddress;
2022 else
2023 return EntityId.Substring(i + 1);
2024 }
2025
2033 public Task GetLegalIdentity(string Address, string LegalIdentityId, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
2034 {
2035 return this.client.SendIqGet(Address, "<getLegalIdentity id=\"" + XML.Encode(LegalIdentityId) + "\" xmlns=\"" +
2036 NamespaceLegalIdentitiesCurrent + "\"/>", async (Sender, e) =>
2037 {
2038 LegalIdentity Identity = null;
2039 XmlElement E;
2040
2041 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "identity")
2042 Identity = LegalIdentity.Parse(E);
2043 else
2044 e.Ok = false;
2045
2046 await Callback.Raise(this, new LegalIdentityEventArgs(e, Identity));
2047 }, State);
2048 }
2049
2055 public Task<LegalIdentity> GetLegalIdentityAsync(string LegalIdentityId)
2056 {
2057 return this.GetLegalIdentityAsync(this.GetTrustProvider(LegalIdentityId), LegalIdentityId);
2058 }
2059
2066 public async Task<LegalIdentity> GetLegalIdentityAsync(string Address, string LegalIdentityId)
2067 {
2068 TaskCompletionSource<LegalIdentity> Result = new TaskCompletionSource<LegalIdentity>();
2069
2070 await this.GetLegalIdentity(Address, LegalIdentityId, (Sender, e) =>
2071 {
2072 if (e.Ok)
2073 Result.TrySetResult(e.Identity);
2074 else
2075 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get legal identity."));
2076
2077 return Task.CompletedTask;
2078
2079 }, null);
2080
2081 return await Result.Task;
2082 }
2083
2084 #endregion
2085
2086 #region Obsolete Legal Identity
2087
2094 public Task ObsoleteLegalIdentity(string LegalIdentityId, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
2095 {
2096 return this.ObsoleteLegalIdentity(this.GetTrustProvider(LegalIdentityId), LegalIdentityId, Callback, State);
2097 }
2098
2106 public Task ObsoleteLegalIdentity(string Address, string LegalIdentityId, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
2107 {
2108 this.AssertAllowed();
2109
2110 return this.client.SendIqSet(Address, "<obsoleteLegalIdentity id=\"" + XML.Encode(LegalIdentityId) + "\" xmlns=\"" +
2111 NamespaceLegalIdentitiesCurrent + "\"/>", async (Sender, e) =>
2112 {
2113 LegalIdentity Identity = null;
2114 XmlElement E;
2115
2116 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "identity")
2117 {
2118 Identity = LegalIdentity.Parse(E);
2119 await this.UpdateSettings(Identity);
2120 }
2121 else
2122 e.Ok = false;
2123
2124 await Callback.Raise(this, new LegalIdentityEventArgs(e, Identity));
2125 }, State);
2126 }
2127
2133 public Task<LegalIdentity> ObsoleteLegalIdentityAsync(string LegalIdentityId)
2134 {
2135 return this.ObsoleteLegalIdentityAsync(this.GetTrustProvider(LegalIdentityId), LegalIdentityId);
2136 }
2137
2144 public async Task<LegalIdentity> ObsoleteLegalIdentityAsync(string Address, string LegalIdentityId)
2145 {
2146 TaskCompletionSource<LegalIdentity> Result = new TaskCompletionSource<LegalIdentity>();
2147
2148 await this.ObsoleteLegalIdentity(Address, LegalIdentityId, (Sender, e) =>
2149 {
2150 if (e.Ok)
2151 Result.TrySetResult(e.Identity);
2152 else
2153 Result.TrySetException(e.StanzaError ?? new Exception("Unable to obsolete legal identity."));
2154
2155 return Task.CompletedTask;
2156
2157 }, null);
2158
2159 return await Result.Task;
2160 }
2161
2162 #endregion
2163
2164 #region Compromised Legal Identity
2165
2172 public Task CompromisedLegalIdentity(string LegalIdentityId, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
2173 {
2174 return this.CompromisedLegalIdentity(this.GetTrustProvider(LegalIdentityId), LegalIdentityId, Callback, State);
2175 }
2176
2184 public Task CompromisedLegalIdentity(string Address, string LegalIdentityId, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
2185 {
2186 this.AssertAllowed();
2187
2188 return this.client.SendIqSet(Address, "<compromisedLegalIdentity id=\"" + XML.Encode(LegalIdentityId) + "\" xmlns=\"" +
2189 NamespaceLegalIdentitiesCurrent + "\"/>", async (Sender, e) =>
2190 {
2191 LegalIdentity Identity = null;
2192 XmlElement E;
2193
2194 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "identity")
2195 {
2196 Identity = LegalIdentity.Parse(E);
2197 await this.UpdateSettings(Identity);
2198 }
2199 else
2200 e.Ok = false;
2201
2202 await Callback.Raise(this, new LegalIdentityEventArgs(e, Identity));
2203 }, State);
2204 }
2205
2211 public Task<LegalIdentity> CompromisedLegalIdentityAsync(string LegalIdentityId)
2212 {
2213 return this.CompromisedLegalIdentityAsync(this.GetTrustProvider(LegalIdentityId), LegalIdentityId);
2214 }
2215
2222 public async Task<LegalIdentity> CompromisedLegalIdentityAsync(string Address, string LegalIdentityId)
2223 {
2224 TaskCompletionSource<LegalIdentity> Result = new TaskCompletionSource<LegalIdentity>();
2225
2226 await this.CompromisedLegalIdentity(Address, LegalIdentityId, (Sender, e) =>
2227 {
2228 if (e.Ok)
2229 Result.TrySetResult(e.Identity);
2230 else
2231 Result.TrySetException(e.StanzaError ?? new Exception("Unable to compromise legal identity."));
2232
2233 return Task.CompletedTask;
2234
2235 }, null);
2236
2237 return await Result.Task;
2238 }
2239
2240 #endregion
2241
2242 #region Signatures
2243
2251 public Task Sign(byte[] Data, SignWith SignWith, EventHandlerAsync<SignatureEventArgs> Callback, object State)
2252 {
2253 return this.Sign(this.componentAddress, Data, SignWith, Callback, State);
2254 }
2255
2264 public async Task Sign(string Address, byte[] Data, SignWith SignWith, EventHandlerAsync<SignatureEventArgs> Callback, object State)
2265 {
2266 this.AssertAllowed();
2267
2268 byte[] Signature = null;
2269 IE2eEndpoint Key;
2270
2271 switch (SignWith)
2272 {
2273 case SignWith.CurrentKeys:
2274 Key = null;
2275 break;
2276
2277 case SignWith.LatestApprovedId:
2278 Key = await this.GetLatestApprovedKey(true);
2279 break;
2280
2281 case SignWith.LatestApprovedIdOrCurrentKeys:
2282 default:
2283 Key = await this.GetLatestApprovedKey(false);
2284 break;
2285 }
2286
2287 if (Key is null)
2288 {
2289 await this.GetMatchingLocalKey(Address, async (Sender, e) =>
2290 {
2291 if (e.Ok)
2292 Signature = e.Key.Sign(Data);
2293
2294 await Callback.Raise(this, new SignatureEventArgs(e, Signature));
2295
2296 }, State);
2297 }
2298 else
2299 {
2300 Signature = Key.Sign(Data);
2301
2302 await Callback.Raise(this, new SignatureEventArgs(Key, Signature, State));
2303 }
2304 }
2305
2312 public Task<byte[]> SignAsync(byte[] Data, SignWith SignWith)
2313 {
2314 return this.SignAsync(this.componentAddress, Data, SignWith);
2315 }
2316
2324 public async Task<byte[]> SignAsync(string Address, byte[] Data, SignWith SignWith)
2325 {
2326 TaskCompletionSource<byte[]> Result = new TaskCompletionSource<byte[]>();
2327
2328 await this.Sign(Address, Data, SignWith, (Sender, e) =>
2329 {
2330 if (e.Ok)
2331 Result.TrySetResult(e.Signature);
2332 else
2333 Result.TrySetException(e.StanzaError ?? new Exception("Unable to sign data."));
2334
2335 return Task.CompletedTask;
2336
2337 }, null);
2338
2339 return await Result.Task;
2340 }
2341
2349 public Task Sign(Stream Data, SignWith SignWith, EventHandlerAsync<SignatureEventArgs> Callback, object State)
2350 {
2351 return this.Sign(this.componentAddress, Data, SignWith, Callback, State);
2352 }
2353
2362 public async Task Sign(string Address, Stream Data, SignWith SignWith, EventHandlerAsync<SignatureEventArgs> Callback, object State)
2363 {
2364 this.AssertAllowed();
2365
2366 IE2eEndpoint Key = SignWith == SignWith.CurrentKeys ? null : await this.GetLatestApprovedKey(true);
2367 byte[] Signature = null;
2368
2369 if (Key is null)
2370 {
2371 await this.GetMatchingLocalKey(Address, async (Sender, e) =>
2372 {
2373 if (e.Ok)
2374 Signature = e.Key.Sign(Data);
2375
2376 await Callback.Raise(this, new SignatureEventArgs(e, Signature));
2377
2378 }, State);
2379 }
2380 else
2381 {
2382 Signature = Key.Sign(Data);
2383
2384 await Callback.Raise(this, new SignatureEventArgs(Key, Signature, State));
2385 }
2386 }
2387
2394 public Task<byte[]> SignAsync(Stream Data, SignWith SignWith)
2395 {
2396 return this.SignAsync(this.componentAddress, Data, SignWith);
2397 }
2398
2406 public async Task<byte[]> SignAsync(string Address, Stream Data, SignWith SignWith)
2407 {
2408 TaskCompletionSource<byte[]> Result = new TaskCompletionSource<byte[]>();
2409
2410 await this.Sign(Address, Data, SignWith, (Sender, e) =>
2411 {
2412 if (e.Ok)
2413 Result.TrySetResult(e.Signature);
2414 else
2415 Result.TrySetException(e.StanzaError ?? new Exception("Unable to sign data."));
2416
2417 return Task.CompletedTask;
2418
2419 }, null);
2420
2421 return await Result.Task;
2422 }
2423
2424 #endregion
2425
2426 #region Validating Signatures
2427
2436 public Task ValidateSignature(string LegalId, byte[] Data, byte[] Signature, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
2437 {
2438 return this.ValidateSignature(this.GetTrustProvider(LegalId), LegalId, Data, Signature, Callback, State);
2439 }
2440
2450 public Task ValidateSignature(string Address, string LegalId, byte[] Data, byte[] Signature, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
2451 {
2452 StringBuilder Xml = new StringBuilder();
2453
2454 Xml.Append("<validateSignature data=\"");
2455 Xml.Append(Convert.ToBase64String(Data));
2456
2457 if (!string.IsNullOrEmpty(LegalId))
2458 {
2459 Xml.Append("\" id=\"");
2460 Xml.Append(XML.Encode(LegalId));
2461 }
2462
2463 Xml.Append("\" s=\"");
2464 Xml.Append(Convert.ToBase64String(Signature));
2465
2466 Xml.Append("\" xmlns=\"");
2467 Xml.Append(NamespaceLegalIdentitiesCurrent);
2468 Xml.Append("\"/>");
2469
2470 return this.client.SendIqGet(Address, Xml.ToString(), async (Sender, e) =>
2471 {
2472 LegalIdentity Identity = null;
2473 XmlElement E;
2474
2475 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "identity")
2476 Identity = LegalIdentity.Parse(E);
2477 else
2478 e.Ok = false;
2479
2480 await Callback.Raise(this, new LegalIdentityEventArgs(e, Identity));
2481 }, State);
2482 }
2483
2491 public Task<LegalIdentity> ValidateSignatureAsync(string LegalId, byte[] Data, byte[] Signature)
2492 {
2493 return this.ValidateSignatureAsync(this.GetTrustProvider(LegalId), LegalId, Data, Signature);
2494 }
2495
2504 public async Task<LegalIdentity> ValidateSignatureAsync(string Address, string LegalId, byte[] Data, byte[] Signature)
2505 {
2506 TaskCompletionSource<LegalIdentity> Result = new TaskCompletionSource<LegalIdentity>();
2507
2508 await this.ValidateSignature(Address, LegalId, Data, Signature, (Sender, e) =>
2509 {
2510 if (e.Ok)
2511 Result.TrySetResult(e.Identity);
2512 else
2513 Result.TrySetException(e.StanzaError ?? new Exception("Unable to sign data."));
2514
2515 return Task.CompletedTask;
2516
2517 }, null);
2518
2519 return await Result.Task;
2520 }
2521
2522 #endregion
2523
2524 #region Create Contract
2525
2545 public Task CreateContract(XmlElement ForMachines, HumanReadableText[] ForHumans, Role[] Roles,
2546 Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration,
2547 Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate,
2548 EventHandlerAsync<SmartContractEventArgs> Callback, object State)
2549 {
2550 return this.CreateContract(this.componentAddress, ForMachines, ForHumans, Roles, Parts, Parameters, Visibility, PartsMode,
2551 Duration, ArchiveRequired, ArchiveOptional, SignAfter, SignBefore, CanActAsTemplate, Callback, State);
2552 }
2553
2574 public Task CreateContract(string Address, XmlElement ForMachines, HumanReadableText[] ForHumans, Role[] Roles,
2575 Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration,
2576 Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate,
2577 EventHandlerAsync<SmartContractEventArgs> Callback, object State)
2578 {
2579 return this.CreateContract(Address, ForMachines, ForHumans, Roles, Parts, Parameters,
2580 Visibility, PartsMode, Duration, ArchiveRequired, ArchiveOptional, SignAfter,
2581 SignBefore, CanActAsTemplate, null, Callback, State);
2582 }
2583
2605 public async Task CreateContract(string Address, XmlElement ForMachines, HumanReadableText[] ForHumans, Role[] Roles,
2606 Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration,
2607 Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate,
2608 IParameterEncryptionAlgorithm Algorithm, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
2609 {
2610 StringBuilder Xml = new StringBuilder();
2611
2612 Xml.Append("<createContract xmlns=\"");
2613 Xml.Append(NamespaceSmartContractsCurrent);
2614 Xml.Append("\">");
2615
2616 Contract Contract = new Contract()
2617 {
2618 Namespace = NamespaceSmartContractsCurrent,
2619 ForMachines = ForMachines,
2620 ForHumans = ForHumans,
2621 Roles = Roles,
2622 Parts = Parts,
2623 Parameters = Parameters,
2624 Visibility = Visibility,
2625 PartsMode = PartsMode,
2627 ArchiveRequired = ArchiveRequired,
2628 ArchiveOptional = ArchiveOptional,
2629 SignAfter = SignAfter,
2630 SignBefore = SignBefore,
2631 CanActAsTemplate = CanActAsTemplate
2632 };
2633
2634 byte[] Nonce = Guid.NewGuid().ToByteArray();
2635 string NonceStr = Convert.ToBase64String(Nonce);
2636 SymmetricCipherAlgorithms EncryptionAlgorithm = Algorithm?.Algorithm ?? this.preferredEncryptionAlgorithm;
2637
2639 {
2640 if (Algorithm is null)
2641 Algorithm = await ParameterEncryptionAlgorithm.Create(EncryptionAlgorithm, this);
2642
2643 Contract.EncryptEncryptedParameters(this.client.BareJID, Algorithm);
2644 }
2645
2646 Contract.Serialize(Xml, false, false, false, false, false, false, false);
2647
2649 {
2650 Xml.Append("<transient>");
2651
2653 {
2654 if (Parameter.Protection == ProtectionLevel.Transient)
2655 {
2656 Parameter.Protection = ProtectionLevel.Normal;
2657 Parameter.Serialize(Xml, true);
2658 Parameter.Protection = ProtectionLevel.Transient;
2659 }
2660 }
2661
2662 Xml.Append("</transient>");
2663 }
2664
2665 Xml.Append("</createContract>");
2666
2667 await this.client.SendIqSet(Address, Xml.ToString(), this.ContractResponse, new object[] { Callback, State, Contract.HasEncryptedParameters, Algorithm?.Algorithm, Algorithm?.Key });
2668 }
2669
2670 private async Task ContractResponse(object Sender, IqResultEventArgs e)
2671 {
2672 object[] P = (object[])e.State;
2673 EventHandlerAsync<SmartContractEventArgs> Callback = (EventHandlerAsync<SmartContractEventArgs>)P[0];
2674 Contract Contract = null;
2675 XmlElement E;
2676
2677 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "contract")
2678 {
2679 ParsedContract Parsed = await Contract.Parse(E, this, false);
2680 Contract = Parsed?.Contract;
2681 if (Contract is null)
2682 e.Ok = false;
2684 {
2685 string CreatorJid = this.client.BareJID;
2686
2687 if (P.Length >= 5 &&
2688 P[2] is bool HasEncryptedParameters &&
2689 HasEncryptedParameters &&
2690 P[3] is SymmetricCipherAlgorithms Algorithm &&
2691 P[4] is byte[] Key)
2692 {
2693 await this.SaveContractSharedSecret(Contract.ContractId,
2694 CreatorJid, Key, Algorithm, false);
2695 }
2696 else
2697 {
2698 Tuple<SymmetricCipherAlgorithms, string, byte[]> T = await this.TryLoadContractSharedSecret(Contract.ContractId);
2699
2700 if (HasEncryptedParameters = !(T is null))
2701 {
2702 Algorithm = T.Item1;
2703 CreatorJid = T.Item2;
2704 Key = T.Item3;
2705 }
2706 else
2707 {
2708 Algorithm = this.preferredEncryptionAlgorithm;
2709 Key = null;
2710 }
2711 }
2712
2713 if (HasEncryptedParameters)
2714 {
2716 Contract.ContractId, Algorithm, this, CreatorJid, Key);
2717
2718 Contract.DecryptEncryptedParameters(CreatorJid, AlgorithmInstance);
2719 }
2720 }
2721 }
2722 else
2723 e.Ok = false;
2724
2725 e.State = P[1];
2726 await Callback.Raise(this, new SmartContractEventArgs(e, Contract));
2727 }
2728
2747 public Task<Contract> CreateContractAsync(XmlElement ForMachines, HumanReadableText[] ForHumans, Role[] Roles,
2748 Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration,
2749 Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate)
2750 {
2751 return this.CreateContractAsync(this.componentAddress, ForMachines, ForHumans, Roles, Parts, Parameters, Visibility,
2752 PartsMode, Duration, ArchiveRequired, ArchiveOptional, SignAfter, SignBefore, CanActAsTemplate);
2753 }
2754
2774 public async Task<Contract> CreateContractAsync(string Address, XmlElement ForMachines, HumanReadableText[] ForHumans, Role[] Roles,
2775 Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration,
2776 Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate)
2777 {
2778 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
2779
2780 await this.CreateContract(Address, ForMachines, ForHumans, Roles, Parts, Parameters, Visibility, PartsMode, Duration,
2781 ArchiveRequired, ArchiveOptional, SignAfter, SignBefore, CanActAsTemplate, (Sender, e) =>
2782 {
2783 if (e.Ok)
2784 Result.TrySetResult(e.Contract);
2785 else
2786 Result.TrySetException(e.StanzaError ?? new Exception("Unable to create the contract."));
2787
2788 return Task.CompletedTask;
2789
2790 }, null);
2791
2792 return await Result.Task;
2793 }
2794
2795 #endregion
2796
2797 #region Create Contract From Template
2798
2816 public Task CreateContract(string TemplateId, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility,
2817 ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter,
2818 DateTime? SignBefore, bool CanActAsTemplate, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
2819 {
2820 return this.CreateContract(this.componentAddress, TemplateId, Parts, Parameters, Visibility, PartsMode, Duration,
2821 ArchiveRequired, ArchiveOptional, SignAfter, SignBefore, CanActAsTemplate, null, Callback, State);
2822 }
2823
2842 public Task CreateContract(string Address, string TemplateId, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility,
2843 ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter,
2844 DateTime? SignBefore, bool CanActAsTemplate, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
2845 {
2846 return this.CreateContract(Address, TemplateId, Parts, Parameters, Visibility,
2847 PartsMode, Duration, ArchiveRequired, ArchiveOptional, SignAfter,
2848 SignBefore, CanActAsTemplate, null, Callback, State);
2849 }
2850
2851
2871 public async Task CreateContract(string Address, string TemplateId, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility,
2872 ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter,
2873 DateTime? SignBefore, bool CanActAsTemplate, IParameterEncryptionAlgorithm Algorithm,
2874 EventHandlerAsync<SmartContractEventArgs> Callback, object State)
2875 {
2876 StringBuilder Xml = new StringBuilder();
2877 uint i, c = (uint)(Parameters?.Length ?? 0);
2878 bool HasEncryptedParameters = false;
2879
2880 for (i = 0; i < c; i++)
2881 {
2882 Parameter P = Parameters[i];
2883
2884 if (P.Protection == ProtectionLevel.Encrypted)
2885 {
2886 HasEncryptedParameters = true;
2887 break;
2888 }
2889 }
2890
2891 byte[] Nonce = Guid.NewGuid().ToByteArray();
2892 string NonceStr = Convert.ToBase64String(Nonce);
2893 SymmetricCipherAlgorithms EncryptionAlgorithm = Algorithm?.Algorithm ?? this.preferredEncryptionAlgorithm;
2894
2895 if (HasEncryptedParameters)
2896 {
2897 if (Algorithm is null)
2898 Algorithm = await ParameterEncryptionAlgorithm.Create(EncryptionAlgorithm, this);
2899
2900 for (i = 0; i < c; i++)
2901 {
2902 Parameter P = Parameters[i];
2903
2904 if (P.Protection == ProtectionLevel.Encrypted && P.ProtectedValue is null)
2905 P.ProtectedValue = Algorithm.Encrypt(P.Name, P.ParameterType, i, this.client.BareJID, Nonce, P.ObjectValue is null ? null : P.StringValue);
2906 }
2907 }
2908
2909 Xml.Append("<createContract xmlns=\"");
2910 Xml.Append(NamespaceSmartContractsCurrent);
2911 Xml.Append("\"><template archiveOpt=\"");
2912 Xml.Append(ArchiveOptional.ToString());
2913 Xml.Append("\" archiveReq=\"");
2914 Xml.Append(ArchiveRequired.ToString());
2915 Xml.Append("\" canActAsTemplate=\"");
2916 Xml.Append(CommonTypes.Encode(CanActAsTemplate));
2917 Xml.Append("\" duration=\"");
2918 Xml.Append(Duration.ToString());
2919 Xml.Append("\" id=\"");
2920 Xml.Append(XML.Encode(TemplateId));
2921 Xml.Append("\" nonce=\"");
2922 Xml.Append(NonceStr);
2923 Xml.Append('"');
2924
2925 if (SignAfter.HasValue && SignAfter > DateTime.MinValue)
2926 {
2927 Xml.Append(" signAfter=\"");
2928 Xml.Append(XML.Encode(SignAfter.Value));
2929 Xml.Append('"');
2930 }
2931
2932 if (SignBefore.HasValue && SignBefore < DateTime.MaxValue)
2933 {
2934 Xml.Append(" signBefore=\"");
2935 Xml.Append(XML.Encode(SignBefore.Value));
2936 Xml.Append('"');
2937 }
2938
2939 Xml.Append(" visibility=\"");
2940 Xml.Append(Visibility.ToString());
2941 Xml.Append("\"><parts>");
2942
2943 switch (PartsMode)
2944 {
2945 case ContractParts.Open:
2946 Xml.Append("<open/>");
2947 break;
2948
2949 case ContractParts.TemplateOnly:
2950 Xml.Append("<templateOnly/>");
2951 break;
2952
2953 case ContractParts.ExplicitlyDefined:
2954 if (!(Parts is null))
2955 {
2956 foreach (Part Part in Parts)
2957 {
2958 Xml.Append("<part legalId=\"");
2959 Xml.Append(XML.Encode(Part.LegalId));
2960 Xml.Append("\" role=\"");
2961 Xml.Append(XML.Encode(Part.Role));
2962 Xml.Append("\"/>");
2963 }
2964 }
2965 break;
2966 }
2967
2968 Xml.Append("</parts>");
2969
2970 LinkedList<Parameter> TransientParameters = null;
2971
2972 if (!(Parameters is null) && Parameters.Length > 0)
2973 {
2974 Xml.Append("<parameters>");
2975
2976 foreach (Parameter Parameter in Parameters)
2977 {
2978 if (Parameter.Protection == ProtectionLevel.Transient)
2979 {
2980 if (Parameter.ProtectedValue is null)
2981 Parameter.ProtectedValue = Guid.NewGuid().ToByteArray();
2982
2983 if (TransientParameters is null)
2984 TransientParameters = new LinkedList<Parameter>();
2985
2986 TransientParameters.AddLast(Parameter);
2987 }
2988
2989 Parameter.Serialize(Xml, true);
2990 }
2991
2992 Xml.Append("</parameters>");
2993 }
2994
2995 Xml.Append("</template>");
2996
2997 if (!(TransientParameters is null))
2998 {
2999 Xml.Append("<transient>");
3000
3001 foreach (Parameter Parameter in TransientParameters)
3002 {
3003 Parameter.Protection = ProtectionLevel.Normal;
3004 Parameter.Serialize(Xml, true);
3005 Parameter.Protection = ProtectionLevel.Transient;
3006 }
3007
3008 Xml.Append("</transient>");
3009 }
3010
3011 Xml.Append("</createContract>");
3012
3013 await this.client.SendIqSet(Address, Xml.ToString(), this.ContractResponse, new object[] { Callback, State, HasEncryptedParameters, Algorithm?.Algorithm, Algorithm?.Key });
3014 }
3015
3032 public Task<Contract> CreateContractAsync(string TemplateId, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility,
3033 ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter,
3034 DateTime? SignBefore, bool CanActAsTemplate)
3035 {
3036 return this.CreateContractAsync(this.componentAddress, TemplateId, Parts, Parameters, Visibility,
3037 PartsMode, Duration, ArchiveRequired, ArchiveOptional, SignAfter, SignBefore, CanActAsTemplate);
3038 }
3039
3057 public async Task<Contract> CreateContractAsync(string Address, string TemplateId, Part[] Parts, Parameter[] Parameters,
3058 ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional,
3059 DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate)
3060 {
3061 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
3062
3063 await this.CreateContract(Address, TemplateId, Parts, Parameters, Visibility, PartsMode, Duration,
3064 ArchiveRequired, ArchiveOptional, SignAfter, SignBefore, CanActAsTemplate, (Sender, e) =>
3065 {
3066 if (e.Ok)
3067 Result.TrySetResult(e.Contract);
3068 else
3069 Result.TrySetException(e.StanzaError ?? new Exception("Unable to create the contract."));
3070
3071 return Task.CompletedTask;
3072
3073 }, null);
3074
3075 return await Result.Task;
3076 }
3077
3078 #endregion
3079
3080 #region Get Created Contract References
3081
3087 public Task GetCreatedContractReferences(EventHandlerAsync<IdReferencesEventArgs> Callback, object State)
3088 {
3089 return this.GetCreatedContractReferences(this.componentAddress, 0, int.MaxValue, Callback, State);
3090 }
3091
3098 public Task GetCreatedContractReferences(string Address, EventHandlerAsync<IdReferencesEventArgs> Callback, object State)
3099 {
3100 return this.GetCreatedContractReferences(Address, 0, int.MaxValue, Callback, State);
3101 }
3102
3110 public Task GetCreatedContractReferences(int Offset, int MaxCount, EventHandlerAsync<IdReferencesEventArgs> Callback, object State)
3111 {
3112 return this.GetCreatedContractReferences(this.componentAddress, Offset, MaxCount, Callback, State);
3113 }
3114
3123 public Task GetCreatedContractReferences(string Address, int Offset, int MaxCount, EventHandlerAsync<IdReferencesEventArgs> Callback, object State)
3124 {
3125 if (Offset < 0)
3126 throw new ArgumentException("Offsets cannot be negative.", nameof(Offset));
3127
3128 if (MaxCount <= 0)
3129 throw new ArgumentException("Must be postitive.", nameof(MaxCount));
3130
3131 StringBuilder Xml = new StringBuilder();
3132
3133 Xml.Append("<getCreatedContracts references='true' xmlns='");
3134 Xml.Append(NamespaceSmartContractsCurrent);
3135
3136 if (Offset > 0)
3137 {
3138 Xml.Append("' offset='");
3139 Xml.Append(Offset.ToString());
3140 }
3141
3142 if (MaxCount < int.MaxValue)
3143 {
3144 Xml.Append("' maxCount='");
3145 Xml.Append(MaxCount.ToString());
3146 }
3147
3148 Xml.Append("'/>");
3149
3150 return this.client.SendIqGet(Address, Xml.ToString(), this.IdReferencesResponse, new object[] { Callback, State });
3151 }
3152
3153 private async Task IdReferencesResponse(object Sender, IqResultEventArgs e)
3154 {
3155 object[] P = (object[])e.State;
3156 EventHandlerAsync<IdReferencesEventArgs> Callback = (EventHandlerAsync<IdReferencesEventArgs>)P[0];
3157 XmlElement E = e.FirstElement;
3158 List<string> IDs = new List<string>();
3159
3160 if (e.Ok && !(E is null))
3161 {
3162 foreach (XmlNode N in E.ChildNodes)
3163 {
3164 if (N is XmlElement E2 && E2.LocalName == "ref")
3165 {
3166 string Id = XML.Attribute(E2, "id");
3167 IDs.Add(Id);
3168 }
3169 }
3170 }
3171 else
3172 e.Ok = false;
3173
3174 e.State = P[1];
3175 await Callback.Raise(this, new IdReferencesEventArgs(e, IDs.ToArray()));
3176 }
3177
3183 {
3184 return this.GetCreatedContractReferencesAsync(this.componentAddress, 0, int.MaxValue);
3185 }
3186
3192 public Task<string[]> GetCreatedContractReferencesAsync(string Address)
3193 {
3194 return this.GetCreatedContractReferencesAsync(Address, 0, int.MaxValue);
3195 }
3196
3203 public Task<string[]> GetCreatedContractReferencesAsync(int Offset, int MaxCount)
3204 {
3205 return this.GetCreatedContractReferencesAsync(this.componentAddress, Offset, MaxCount);
3206 }
3207
3215 public async Task<string[]> GetCreatedContractReferencesAsync(string Address, int Offset, int MaxCount)
3216 {
3217 TaskCompletionSource<string[]> Result = new TaskCompletionSource<string[]>();
3218
3219 await this.GetCreatedContractReferences(Address, Offset, MaxCount, (Sender, e) =>
3220 {
3221 if (e.Ok)
3222 Result.TrySetResult(e.References);
3223 else
3224 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get created contract references."));
3225
3226 return Task.CompletedTask;
3227
3228 }, null);
3229
3230 return await Result.Task;
3231 }
3232
3233 #endregion
3234
3235 #region Get Created Contracts
3236
3242 public Task GetCreatedContracts(EventHandlerAsync<ContractsEventArgs> Callback, object State)
3243 {
3244 return this.GetCreatedContracts(this.componentAddress, 0, int.MaxValue, Callback, State);
3245 }
3246
3253 public Task GetCreatedContracts(string Address, EventHandlerAsync<ContractsEventArgs> Callback, object State)
3254 {
3255 return this.GetCreatedContracts(Address, 0, int.MaxValue, Callback, State);
3256 }
3257
3265 public Task GetCreatedContracts(int Offset, int MaxCount, EventHandlerAsync<ContractsEventArgs> Callback, object State)
3266 {
3267 return this.GetCreatedContracts(this.componentAddress, Offset, MaxCount, Callback, State);
3268 }
3269
3278 public Task GetCreatedContracts(string Address, int Offset, int MaxCount, EventHandlerAsync<ContractsEventArgs> Callback, object State)
3279 {
3280 if (Offset < 0)
3281 throw new ArgumentException("Offsets cannot be negative.", nameof(Offset));
3282
3283 if (MaxCount <= 0)
3284 throw new ArgumentException("Must be postitive.", nameof(MaxCount));
3285
3286 StringBuilder Xml = new StringBuilder();
3287
3288 Xml.Append("<getCreatedContracts references='false' xmlns='");
3289 Xml.Append(NamespaceSmartContractsCurrent);
3290
3291 if (Offset > 0)
3292 {
3293 Xml.Append("' offset='");
3294 Xml.Append(Offset.ToString());
3295 }
3296
3297 if (MaxCount < int.MaxValue)
3298 {
3299 Xml.Append("' maxCount='");
3300 Xml.Append(MaxCount.ToString());
3301 }
3302
3303 Xml.Append("'/>");
3304
3305 return this.client.SendIqGet(Address, Xml.ToString(), this.ContractsResponse, new object[] { Callback, State });
3306 }
3307
3308 private async Task ContractsResponse(object Sender, IqResultEventArgs e)
3309 {
3310 object[] P = (object[])e.State;
3311 EventHandlerAsync<ContractsEventArgs> Callback = (EventHandlerAsync<ContractsEventArgs>)P[0];
3312 XmlElement E = e.FirstElement;
3313 List<Contract> Contracts = new List<Contract>();
3314 List<string> References = new List<string>();
3315
3316 if (e.Ok && !(E is null))
3317 {
3318 foreach (XmlNode N in E.ChildNodes)
3319 {
3320 if (N is XmlElement E2)
3321 {
3322 switch (E2.LocalName)
3323 {
3324 case "contract":
3325 ParsedContract ParsedContract = await Contract.Parse(E2, this, false);
3326
3327 if (!(ParsedContract is null))
3328 Contracts.Add(ParsedContract.Contract);
3329 break;
3330
3331 case "ref":
3332 string ContractId = XML.Attribute(E2, "id");
3333 References.Add(ContractId);
3334 break;
3335 }
3336 }
3337 }
3338 }
3339 else
3340 e.Ok = false;
3341
3342 e.State = P[1];
3343 await Callback.Raise(this, new ContractsEventArgs(e, Contracts.ToArray(), References.ToArray()));
3344 }
3345
3350 public Task<ContractsEventArgs> GetCreatedContractsAsync()
3351 {
3352 return this.GetCreatedContractsAsync(this.componentAddress, 0, int.MaxValue);
3353 }
3354
3360 public Task<ContractsEventArgs> GetCreatedContractsAsync(string Address)
3361 {
3362 return this.GetCreatedContractsAsync(Address, 0, int.MaxValue);
3363 }
3364
3371 public Task<ContractsEventArgs> GetCreatedContractsAsync(int Offset, int MaxCount)
3372 {
3373 return this.GetCreatedContractsAsync(this.componentAddress, Offset, MaxCount);
3374 }
3375
3383 public async Task<ContractsEventArgs> GetCreatedContractsAsync(string Address, int Offset, int MaxCount)
3384 {
3385 TaskCompletionSource<ContractsEventArgs> Result = new TaskCompletionSource<ContractsEventArgs>();
3386
3387 await this.GetCreatedContracts(Address, Offset, MaxCount, (Sender, e) =>
3388 {
3389 Result.TrySetResult(e);
3390 return Task.CompletedTask;
3391
3392 }, null);
3393
3394 return await Result.Task;
3395 }
3396
3397 #endregion
3398
3399 #region Sign Contract
3400
3411 public Task SignContract(Contract Contract, string Role, bool Transferable, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
3412 {
3413 return this.SignContract(this.GetTrustProvider(Contract.ContractId), Contract, Role, Transferable, Callback, State);
3414 }
3415
3427 public async Task SignContract(string Address, Contract Contract, string Role, bool Transferable,
3428 EventHandlerAsync<SmartContractEventArgs> Callback, object State)
3429 {
3431 {
3432 Tuple<SymmetricCipherAlgorithms, string, byte[]> T = await this.TryLoadContractSharedSecret(Contract.ContractId);
3433 if (!(T is null))
3434 {
3436 Contract.ContractId, T.Item1, this, T.Item2, T.Item3);
3437
3438 Contract.EncryptEncryptedParameters(T.Item2, Algorithm);
3439 }
3440 }
3441
3442 StringBuilder Xml = new StringBuilder();
3443 Contract.Serialize(Xml, false, false, false, false, false, false, false);
3444 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
3445
3446 await this.Sign(Address, Data, SignWith.LatestApprovedId, async (Sender, e) =>
3447 {
3448 if (e.Ok)
3449 {
3450 Xml.Clear();
3451 Xml.Append("<signContract xmlns='");
3452 Xml.Append(NamespaceSmartContractsCurrent);
3453 Xml.Append("' id='");
3454 Xml.Append(XML.Encode(Contract.ContractId));
3455 Xml.Append("' role='");
3456 Xml.Append(XML.Encode(Role));
3457
3458 if (Transferable)
3459 Xml.Append("' transferable='true");
3460
3461 Xml.Append("' s='");
3462 Xml.Append(Convert.ToBase64String(e.Signature));
3463 Xml.Append("'/>");
3464
3465 await this.client.SendIqSet(Address, Xml.ToString(), this.ContractResponse, new object[] { Callback, State });
3466 }
3467 else
3468 await Callback.Raise(this, new SmartContractEventArgs(e, null));
3469 }, State);
3470 }
3471
3481 public Task<Contract> SignContractAsync(Contract Contract, string Role, bool Transferable)
3482 {
3483 return this.SignContractAsync(this.GetTrustProvider(Contract.ContractId), Contract, Role, Transferable);
3484 }
3485
3496 public async Task<Contract> SignContractAsync(string Address, Contract Contract, string Role, bool Transferable)
3497 {
3498 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
3499
3500 await this.SignContract(Address, Contract, Role, Transferable, (Sender, e) =>
3501 {
3502 if (e.Ok)
3503 Result.TrySetResult(e.Contract);
3504 else
3505 Result.TrySetException(e.StanzaError ?? new Exception("Unable to sign the contract."));
3506
3507 return Task.CompletedTask;
3508
3509 }, null);
3510
3511 return await Result.Task;
3512 }
3513
3514 #endregion
3515
3516 #region Get Signed Contract References
3517
3523 public Task GetSignedContractReferences(EventHandlerAsync<IdReferencesEventArgs> Callback, object State)
3524 {
3525 return this.GetSignedContractReferences(this.componentAddress, 0, int.MaxValue, Callback, State);
3526 }
3527
3534 public Task GetSignedContractReferences(string Address, EventHandlerAsync<IdReferencesEventArgs> Callback, object State)
3535 {
3536 return this.GetSignedContractReferences(Address, 0, int.MaxValue, Callback, State);
3537 }
3538
3547 public Task GetSignedContractReferences(string Address, int Offset, int MaxCount, EventHandlerAsync<IdReferencesEventArgs> Callback, object State)
3548 {
3549 if (Offset < 0)
3550 throw new ArgumentException("Offsets cannot be negative.", nameof(Offset));
3551
3552 if (MaxCount <= 0)
3553 throw new ArgumentException("Must be postitive.", nameof(MaxCount));
3554
3555 StringBuilder Xml = new StringBuilder();
3556
3557 Xml.Append("<getSignedContracts references='true' xmlns='");
3558 Xml.Append(NamespaceSmartContractsCurrent);
3559
3560 if (Offset > 0)
3561 {
3562 Xml.Append("' offset='");
3563 Xml.Append(Offset.ToString());
3564 }
3565
3566 if (MaxCount < int.MaxValue)
3567 {
3568 Xml.Append("' maxCount='");
3569 Xml.Append(MaxCount.ToString());
3570 }
3571
3572 Xml.Append("'/>");
3573
3574 return this.client.SendIqGet(Address, Xml.ToString(), this.IdReferencesResponse, new object[] { Callback, State });
3575 }
3576
3581 public Task<string[]> GetSignedContractReferencesAsync()
3582 {
3583 return this.GetSignedContractReferencesAsync(this.componentAddress, 0, int.MaxValue);
3584 }
3585
3592 public Task<string[]> GetSignedContractReferencesAsync(int Offset, int MaxCount)
3593 {
3594 return this.GetSignedContractReferencesAsync(this.componentAddress, Offset, MaxCount);
3595 }
3596
3604 public async Task<string[]> GetSignedContractReferencesAsync(string Address, int Offset, int MaxCount)
3605 {
3606 TaskCompletionSource<string[]> Result = new TaskCompletionSource<string[]>();
3607
3608 await this.GetSignedContractReferences(Address, Offset, MaxCount, (Sender, e) =>
3609 {
3610 if (e.Ok)
3611 Result.TrySetResult(e.References);
3612 else
3613 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get signed contract references."));
3614
3615 return Task.CompletedTask;
3616
3617 }, null);
3618
3619 return await Result.Task;
3620 }
3621
3622 #endregion
3623
3624 #region Get Signed Contracts
3625
3631 public Task GetSignedContracts(EventHandlerAsync<ContractsEventArgs> Callback, object State)
3632 {
3633 return this.GetSignedContracts(this.componentAddress, 0, int.MaxValue, Callback, State);
3634 }
3635
3642 public Task GetSignedContracts(string Address, EventHandlerAsync<ContractsEventArgs> Callback, object State)
3643 {
3644 return this.GetSignedContracts(Address, 0, int.MaxValue, Callback, State);
3645 }
3646
3654 public Task GetSignedContracts(int Offset, int MaxCount, EventHandlerAsync<ContractsEventArgs> Callback, object State)
3655 {
3656 return this.GetSignedContracts(this.componentAddress, Offset, MaxCount, Callback, State);
3657 }
3658
3667 public Task GetSignedContracts(string Address, int Offset, int MaxCount, EventHandlerAsync<ContractsEventArgs> Callback, object State)
3668 {
3669 if (Offset < 0)
3670 throw new ArgumentException("Offsets cannot be negative.", nameof(Offset));
3671
3672 if (MaxCount <= 0)
3673 throw new ArgumentException("Must be postitive.", nameof(MaxCount));
3674
3675 StringBuilder Xml = new StringBuilder();
3676
3677 Xml.Append("<getSignedContracts references='false' xmlns='");
3678 Xml.Append(NamespaceSmartContractsCurrent);
3679
3680 if (Offset > 0)
3681 {
3682 Xml.Append("' offset='");
3683 Xml.Append(Offset.ToString());
3684 }
3685
3686 if (MaxCount < int.MaxValue)
3687 {
3688 Xml.Append("' maxCount='");
3689 Xml.Append(MaxCount.ToString());
3690 }
3691
3692 Xml.Append("'/>");
3693
3694 return this.client.SendIqGet(Address, Xml.ToString(), this.ContractsResponse, new object[] { Callback, State });
3695 }
3696
3701 public Task<ContractsEventArgs> GetSignedContractsAsync()
3702 {
3703 return this.GetSignedContractsAsync(this.componentAddress, 0, int.MaxValue);
3704 }
3705
3712 public Task<ContractsEventArgs> GetSignedContractsAsync(int Offset, int MaxCount)
3713 {
3714 return this.GetSignedContractsAsync(this.componentAddress, Offset, MaxCount);
3715 }
3716
3724 public async Task<ContractsEventArgs> GetSignedContractsAsync(string Address, int Offset, int MaxCount)
3725 {
3726 TaskCompletionSource<ContractsEventArgs> Result = new TaskCompletionSource<ContractsEventArgs>();
3727
3728 await this.GetSignedContracts(Address, Offset, MaxCount, (Sender, e) =>
3729 {
3730 Result.TrySetResult(e);
3731 return Task.CompletedTask;
3732
3733 }, null);
3734
3735 return await Result.Task;
3736 }
3737
3738 #endregion
3739
3740 #region Contract Signature event
3741
3742 private Task ContractSignedMessageHandler(object Sender, MessageEventArgs e)
3743 {
3744 string ContractId = XML.Attribute(e.Content, "contractId");
3745 string LegalId = XML.Attribute(e.Content, "legalId");
3746 string Role = XML.Attribute(e.Content, "role");
3747
3748 return this.ContractSigned.Raise(this, new ContractSignedEventArgs(ContractId, LegalId, Role));
3749 }
3750
3754 public event EventHandlerAsync<ContractSignedEventArgs> ContractSigned = null;
3755
3756 #endregion
3757
3758 #region Get Contract
3759
3766 public Task GetContract(string ContractId, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
3767 {
3768 return this.GetContract(this.GetTrustProvider(ContractId), ContractId, Callback, State);
3769 }
3770
3778 public Task GetContract(string Address, string ContractId, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
3779 {
3780 StringBuilder Xml = new StringBuilder();
3781
3782 Xml.Append("<getContract xmlns='");
3783 Xml.Append(NamespaceSmartContractsCurrent);
3784 Xml.Append("' id='");
3785 Xml.Append(XML.Encode(ContractId));
3786 Xml.Append("'/>");
3787
3788 return this.client.SendIqGet(Address, Xml.ToString(), this.ContractResponse, new object[] { Callback, State });
3789 }
3790
3796 public Task<Contract> GetContractAsync(string ContractId)
3797 {
3798 return this.GetContractAsync(this.GetTrustProvider(ContractId), ContractId);
3799 }
3800
3807 public async Task<Contract> GetContractAsync(string Address, string ContractId)
3808 {
3809 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
3810
3811 await this.GetContract(Address, ContractId, (Sender, e) =>
3812 {
3813 if (e.Ok)
3814 Result.TrySetResult(e.Contract);
3815 else
3816 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get the contract."));
3817
3818 return Task.CompletedTask;
3819
3820 }, null);
3821
3822 return await Result.Task;
3823 }
3824
3825 #endregion
3826
3827 #region Get Contracts
3828
3835 public async Task GetContracts(string[] ContractIds, EventHandlerAsync<ContractsEventArgs> Callback, object State)
3836 {
3837 Dictionary<string, List<string>> ByTrustProvider = new Dictionary<string, List<string>>();
3838 string LastTrustProvider = string.Empty;
3839 List<string> LastList = null;
3840
3841 foreach (string ContractId in ContractIds)
3842 {
3843 string TrustProvider = this.GetTrustProvider(ContractId);
3844
3845 if (TrustProvider != LastTrustProvider || LastList is null)
3846 {
3847 if (!ByTrustProvider.TryGetValue(TrustProvider, out LastList))
3848 {
3849 LastList = new List<string>();
3850 ByTrustProvider[TrustProvider] = LastList;
3851 }
3852
3853 LastTrustProvider = TrustProvider;
3854 }
3855
3856 LastList.Add(ContractId);
3857 }
3858
3859 List<Contract> Contracts = new List<Contract>();
3860 List<string> References = new List<string>();
3861 bool Ok = true;
3862 int NrLeft = ByTrustProvider.Count;
3863
3864 foreach (KeyValuePair<string, List<string>> P in ByTrustProvider)
3865 {
3866 await this.GetContracts(P.Key, P.Value.ToArray(), async (Sender, e) =>
3867 {
3868 lock (Contracts)
3869 {
3870 if (e.Ok)
3871 {
3872 Contracts.AddRange(e.Contracts);
3873 References.AddRange(e.References);
3874 }
3875 else
3876 Ok = false;
3877
3878 NrLeft--;
3879 if (NrLeft > 0)
3880 return;
3881 }
3882
3883 ContractsEventArgs e2 = new ContractsEventArgs(e, Contracts.ToArray(), References.ToArray())
3884 {
3885 Ok = Ok
3886 };
3887
3888 await Callback.Raise(this, e2);
3889
3890 }, State);
3891 }
3892 }
3893
3901 public Task GetContracts(string Address, string[] ContractIds, EventHandlerAsync<ContractsEventArgs> Callback, object State)
3902 {
3903 StringBuilder Xml = new StringBuilder();
3904
3905 Xml.Append("<getContracts xmlns='");
3906 Xml.Append(NamespaceSmartContractsCurrent);
3907 Xml.Append("'>");
3908
3909 foreach (string ContractId in ContractIds)
3910 {
3911 Xml.Append("<ref id='");
3912 Xml.Append(XML.Encode(ContractId));
3913 Xml.Append("'/>");
3914 }
3915
3916 Xml.Append("</getContracts>");
3917
3918 return this.client.SendIqGet(Address, Xml.ToString(), this.ContractsResponse, new object[] { Callback, State });
3919 }
3920
3926 public async Task<ContractsEventArgs> GetContractsAsync(string[] ContractIds)
3927 {
3928 TaskCompletionSource<ContractsEventArgs> Result = new TaskCompletionSource<ContractsEventArgs>();
3929
3930 await this.GetContracts(ContractIds, (Sender, e) =>
3931 {
3932 Result.TrySetResult(e);
3933 return Task.CompletedTask;
3934
3935 }, null);
3936
3937 return await Result.Task;
3938 }
3939
3946 public async Task<ContractsEventArgs> GetContractsAsync(string Address, string[] ContractIds)
3947 {
3948 TaskCompletionSource<ContractsEventArgs> Result = new TaskCompletionSource<ContractsEventArgs>();
3949
3950 await this.GetContracts(Address, ContractIds, (Sender, e) =>
3951 {
3952 Result.TrySetResult(e);
3953 return Task.CompletedTask;
3954
3955 }, null);
3956
3957 return await Result.Task;
3958 }
3959
3960 #endregion
3961
3962 #region Obsolete Contract
3963
3970 public Task ObsoleteContract(string ContractId, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
3971 {
3972 return this.ObsoleteContract(this.GetTrustProvider(ContractId), ContractId, Callback, State);
3973 }
3974
3982 public Task ObsoleteContract(string Address, string ContractId,
3983 EventHandlerAsync<SmartContractEventArgs> Callback, object State)
3984 {
3985 StringBuilder Xml = new StringBuilder();
3986
3987 Xml.Append("<obsoleteContract xmlns='");
3988 Xml.Append(NamespaceSmartContractsCurrent);
3989 Xml.Append("' id='");
3990 Xml.Append(XML.Encode(ContractId));
3991 Xml.Append("'/>");
3992
3993 return this.client.SendIqSet(Address, Xml.ToString(), this.ContractResponse, new object[] { Callback, State });
3994 }
3995
4001 public Task<Contract> ObsoleteContractAsync(string ContractId)
4002 {
4003 return this.ObsoleteContractAsync(this.GetTrustProvider(ContractId), ContractId);
4004 }
4005
4012 public async Task<Contract> ObsoleteContractAsync(string Address, string ContractId)
4013 {
4014 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
4015
4016 await this.ObsoleteContract(Address, ContractId, (Sender, e) =>
4017 {
4018 if (e.Ok)
4019 Result.TrySetResult(e.Contract);
4020 else
4021 Result.TrySetException(e.StanzaError ?? new Exception("Unable to obsolete the contract."));
4022
4023 return Task.CompletedTask;
4024
4025 }, null);
4026
4027 return await Result.Task;
4028 }
4029
4030 #endregion
4031
4032 #region Delete Contract
4033
4040 public Task DeleteContract(string ContractId, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
4041 {
4042 return this.DeleteContract(this.GetTrustProvider(ContractId), ContractId, Callback, State);
4043 }
4044
4052 public Task DeleteContract(string Address, string ContractId,
4053 EventHandlerAsync<SmartContractEventArgs> Callback, object State)
4054 {
4055 StringBuilder Xml = new StringBuilder();
4056
4057 Xml.Append("<deleteContract xmlns='");
4058 Xml.Append(NamespaceSmartContractsCurrent);
4059 Xml.Append("' id='");
4060 Xml.Append(XML.Encode(ContractId));
4061 Xml.Append("'/>");
4062
4063 return this.client.SendIqSet(Address, Xml.ToString(), this.ContractResponse, new object[] { Callback, State });
4064 }
4065
4071 public Task<Contract> DeleteContractAsync(string ContractId)
4072 {
4073 return this.DeleteContractAsync(this.GetTrustProvider(ContractId), ContractId);
4074 }
4075
4082 public async Task<Contract> DeleteContractAsync(string Address, string ContractId)
4083 {
4084 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
4085
4086 await this.DeleteContract(Address, ContractId, (Sender, e) =>
4087 {
4088 if (e.Ok)
4089 Result.TrySetResult(e.Contract);
4090 else
4091 Result.TrySetException(e.StanzaError ?? new Exception("Unable to delete the contract."));
4092
4093 return Task.CompletedTask;
4094
4095 }, null);
4096
4097 return await Result.Task;
4098 }
4099
4100 #endregion
4101
4102 #region Contract Created event
4103
4104 private Task ContractCreatedMessageHandler(object Sender, MessageEventArgs e)
4105 {
4106 string ContractId = XML.Attribute(e.Content, "contractId");
4107
4108 if (!this.IsFromTrustProvider(ContractId, e.From))
4109 return Task.CompletedTask;
4110
4111 return this.ContractCreated.Raise(this, new ContractReferenceEventArgs(ContractId));
4112 }
4113
4117 public event EventHandlerAsync<ContractReferenceEventArgs> ContractCreated = null;
4118
4119 #endregion
4120
4121 #region Contract Updated event
4122
4123 private Task ContractUpdatedMessageHandler(object Sender, MessageEventArgs e)
4124 {
4125 string ContractId = XML.Attribute(e.Content, "contractId");
4126
4127 if (!this.IsFromTrustProvider(ContractId, e.From))
4128 return Task.CompletedTask;
4129
4130 return this.ContractUpdated.Raise(this, new ContractReferenceEventArgs(ContractId));
4131 }
4132
4136 public event EventHandlerAsync<ContractReferenceEventArgs> ContractUpdated = null;
4137
4138 #endregion
4139
4140 #region Contract Deleted event
4141
4142 private Task ContractDeletedMessageHandler(object Sender, MessageEventArgs e)
4143 {
4144 string ContractId = XML.Attribute(e.Content, "contractId");
4145
4146 if (!this.IsFromTrustProvider(ContractId, e.From))
4147 return Task.CompletedTask;
4148
4149 return this.ContractDeleted.Raise(this, new ContractReferenceEventArgs(ContractId));
4150 }
4151
4155 public event EventHandlerAsync<ContractReferenceEventArgs> ContractDeleted = null;
4156
4157 #endregion
4158
4159 #region Update Contract
4160
4167 public Task UpdateContract(Contract Contract, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
4168 {
4169 return this.UpdateContract(this.GetTrustProvider(Contract.ContractId), Contract, Callback, State);
4170 }
4171
4179 public async Task UpdateContract(string Address, Contract Contract,
4180 EventHandlerAsync<SmartContractEventArgs> Callback, object State)
4181 {
4183 {
4184 Tuple<SymmetricCipherAlgorithms, string, byte[]> KeyInfo =
4185 await this.TryLoadContractSharedSecret(Contract.ContractId);
4186
4187 if (!(KeyInfo is null))
4188 {
4190 Contract.ContractId, KeyInfo.Item1, this, KeyInfo.Item2, KeyInfo.Item3);
4191
4192 Contract.EncryptEncryptedParameters(this.client.BareJID, Algorithm);
4193 }
4194 }
4195
4196 StringBuilder Xml = new StringBuilder();
4197
4198 Xml.Append("<updateContract xmlns='");
4199 Xml.Append(NamespaceSmartContractsCurrent);
4200 Xml.Append("'>");
4201
4202 Contract.Serialize(Xml, false, true, true, true, false, false, false);
4203
4204 Xml.Append("</updateContract>");
4205
4206 await this.client.SendIqSet(Address, Xml.ToString(), this.ContractResponse, new object[] { Callback, State });
4207 }
4208
4215 {
4216 return this.UpdateContractAsync(this.GetTrustProvider(Contract.ContractId), Contract);
4217 }
4218
4225 public async Task<Contract> UpdateContractAsync(string Address, Contract Contract)
4226 {
4227 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
4228
4229 await this.UpdateContract(Address, Contract, (Sender, e) =>
4230 {
4231 if (e.Ok)
4232 Result.TrySetResult(e.Contract);
4233 else
4234 Result.TrySetException(e.StanzaError ?? new Exception("Unable to update the contract."));
4235
4236 return Task.CompletedTask;
4237
4238 }, null);
4239
4240 return await Result.Task;
4241 }
4242
4243 #endregion
4244
4245 #region Validate Contract
4246
4253 public Task Validate(Contract Contract, EventHandlerAsync<ContractValidationEventArgs> Callback, object State)
4254 {
4255 return this.Validate(Contract, true, Callback, State);
4256 }
4257
4265 public async Task Validate(Contract Contract, bool ValidateState, EventHandlerAsync<ContractValidationEventArgs> Callback, object State)
4266 {
4267 if (Contract is null)
4268 {
4269 await this.ReturnStatus(ContractStatus.ContractUndefined, Callback, State);
4270 return;
4271 }
4272
4273 if (ValidateState &&
4274 Contract.State != ContractState.Approved &&
4275 Contract.State != ContractState.BeingSigned &&
4276 Contract.State != ContractState.Signed)
4277 {
4278 await this.ReturnStatus(ContractStatus.NotApproved, Callback, State);
4279 return;
4280 }
4281
4282 DateTime Now = DateTime.Now;
4283
4284 if (Now.Date.AddDays(1) < Contract.From) // To avoid Time-zone problems
4285 {
4286 await this.ReturnStatus(ContractStatus.NotValidYet, Callback, State);
4287 return;
4288 }
4289
4290 if (Now.Date.AddDays(-1) > Contract.To) // To avoid Time-zone problems
4291 {
4292 await this.ReturnStatus(ContractStatus.NotValidAnymore, Callback, State);
4293 return;
4294 }
4295
4296 if (string.IsNullOrEmpty(Contract.Provider))
4297 {
4298 await this.ReturnStatus(ContractStatus.NoTrustProvider, Callback, State);
4299 return;
4300 }
4301
4302 if (Contract.PartsMode == ContractParts.TemplateOnly)
4303 {
4304 await this.ReturnStatus(ContractStatus.TemplateOnly, Callback, State);
4305 return;
4306 }
4307
4308 if (Contract.ClientSignatures is null || Contract.ClientSignatures.Length == 0)
4309 {
4310 await this.ReturnStatus(ContractStatus.NoClientSignatures, Callback, State);
4311 return;
4312 }
4313
4314 if (!await Contract.IsLegallyBinding(false, this))
4315 {
4316 await this.ReturnStatus(ContractStatus.NotLegallyBinding, Callback, State);
4317 return;
4318 }
4319
4320 if (!await IsHumanReadableWellDefined(Contract))
4321 {
4322 await this.ReturnStatus(ContractStatus.HumanReadableNotWellDefined, Callback, State);
4323 return;
4324 }
4325
4326 if ((Contract.Parameters?.Length ?? 0) > 0)
4327 {
4328 try
4329 {
4331 {
4332 { "Duration", Contract.Duration }
4333 };
4334
4337
4339 {
4340 if (!await Parameter.IsParameterValid(Variables, this))
4341 {
4342 await this.ReturnStatus(ContractStatus.ParameterValuesNotValid, Callback, State);
4343 return;
4344 }
4345 }
4346 }
4347 catch (Exception)
4348 {
4349 await this.ReturnStatus(ContractStatus.ParameterValuesNotValid, Callback, State);
4350 return;
4351 }
4352 }
4353
4354 if (string.IsNullOrEmpty(Contract.ForMachinesLocalName) ||
4355 string.IsNullOrEmpty(Contract.ForMachinesNamespace) ||
4356 Contract.ForMachines is null ||
4359 {
4360 await this.ReturnStatus(ContractStatus.MachineReadableNotWellDefined, Callback, State);
4361 return;
4362 }
4363
4364 XmlDocument Doc;
4365
4366 try
4367 {
4368 Doc = new XmlDocument()
4369 {
4370 PreserveWhitespace = true
4371 };
4372
4373 Doc.LoadXml(Contract.ForMachines.OuterXml);
4374 }
4375 catch (Exception)
4376 {
4377 await this.ReturnStatus(ContractStatus.MachineReadableNotWellDefined, Callback, State);
4378 return;
4379 }
4380
4381 Dictionary<string, XmlSchema> Schemas = new Dictionary<string, XmlSchema>();
4382 LinkedList<XmlNode> ToCheck = new LinkedList<XmlNode>();
4383
4384 ToCheck.AddLast(Doc.DocumentElement);
4385
4386 while (!(ToCheck.First is null))
4387 {
4388 XmlNode N = ToCheck.First.Value;
4389 ToCheck.RemoveFirst();
4390
4391 while (!(N is null))
4392 {
4393 if (N is XmlElement E)
4394 {
4395 if (!string.IsNullOrEmpty(E.NamespaceURI))
4396 Schemas[E.NamespaceURI] = null;
4397
4398 foreach (XmlNode N2 in E.ChildNodes)
4399 ToCheck.AddLast(N2);
4400 }
4401
4402 N = N.NextSibling;
4403 }
4404 }
4405
4406 int NrSchemas = Schemas.Count;
4407 if (NrSchemas == 0 || !Schemas.ContainsKey(Contract.ForMachinesNamespace))
4408 {
4409 await this.ReturnStatus(ContractStatus.MachineReadableNotWellDefined, Callback, State);
4410 return;
4411 }
4412
4413 string SchemaKey = Contract.ForMachinesNamespace + "#" + Convert.ToBase64String(Contract.ContentSchemaDigest);
4414 byte[] SchemaBin;
4415 XmlSchema Schema;
4416
4417 lock (this.schemas)
4418 {
4419 if (this.schemas.TryGetValue(SchemaKey, out KeyValuePair<byte[], XmlSchema> P))
4420 {
4421 SchemaBin = P.Key;
4422 Schema = P.Value;
4423 }
4424 else
4425 {
4426 SchemaBin = null;
4427 Schema = null;
4428 }
4429 }
4430
4431 if (!(Schema is null))
4432 Schemas[Contract.ForMachinesNamespace] = Schema;
4433
4434 string[] Namespaces = new string[Schemas.Count];
4435 Schemas.Keys.CopyTo(Namespaces, 0);
4436
4437 string ContractComponent;
4438 int i = Contract.ContractId.IndexOf('@');
4439 if (i < 0)
4440 ContractComponent = this.componentAddress;
4441 else
4442 ContractComponent = Contract.ContractId.Substring(i + 1);
4443
4444 foreach (string Namespace in Namespaces)
4445 {
4446 if (Schemas.TryGetValue(Namespace, out Schema) && !(Schema is null))
4447 continue;
4448
4449 TaskCompletionSource<ContractStatus> T = new TaskCompletionSource<ContractStatus>();
4451
4452 if (Namespace == Contract.ForMachinesNamespace)
4454 else
4455 SchemaDigest = null;
4456
4457 await this.GetSchema(ContractComponent, Namespace, SchemaDigest, (_, e) =>
4458 {
4459 if (e.Ok)
4460 {
4461 try
4462 {
4463 SchemaBin = e.Schema;
4464 using (MemoryStream ms = new MemoryStream(SchemaBin))
4465 {
4466 Schema = XSL.LoadSchema(ms, string.Empty);
4467 }
4468
4469 Schemas[Namespace] = Schema;
4470
4471 if (Namespace == Contract.ForMachinesNamespace)
4472 {
4473 byte[] Digest = Hashes.ComputeHash(Contract.ContentSchemaHashFunction, SchemaBin);
4474
4475 if (Convert.ToBase64String(Digest) != Convert.ToBase64String(Contract.ContentSchemaDigest))
4476 {
4477 T.TrySetResult(ContractStatus.FraudulentSchema);
4478 return Task.CompletedTask;
4479 }
4480
4481 lock (this.schemas)
4482 {
4483 this.schemas[SchemaKey] = new KeyValuePair<byte[], XmlSchema>(SchemaBin, Schema);
4484 }
4485 }
4486 else
4487 {
4488 lock (this.schemas)
4489 {
4490 this.schemas[Namespace] = new KeyValuePair<byte[], XmlSchema>(SchemaBin, Schema);
4491 }
4492 }
4493
4494 T.TrySetResult(ContractStatus.Valid);
4495 }
4496 catch (Exception)
4497 {
4498 T.TrySetResult(ContractStatus.CorruptSchema);
4499 }
4500 }
4501 else
4502 T.TrySetResult(ContractStatus.NoSchemaAccess);
4503
4504 return Task.CompletedTask;
4505 }, null);
4506
4507 ContractStatus Temp = await T.Task;
4508 if (Temp != ContractStatus.Valid)
4509 {
4510 await this.ReturnStatus(Temp, Callback, State);
4511 return;
4512 }
4513 }
4514
4515 try
4516 {
4517 XmlSchema[] Schemas2 = new XmlSchema[Schemas.Count];
4518 Schemas.Values.CopyTo(Schemas2, 0);
4519
4521 }
4522 catch (Exception)
4523 {
4524 await this.ReturnStatus(ContractStatus.FraudulentMachineReadable, Callback, State);
4525 return;
4526 }
4527
4528 StringBuilder Xml = new StringBuilder();
4529 Contract.Serialize(Xml, false, false, false, false, false, false, false);
4530 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
4531 Dictionary<string, LegalIdentity> Identities = new Dictionary<string, LegalIdentity>();
4532
4534 {
4535 LegalIdentity Identity = await this.ValidateSignatureAsync(Signature.LegalId, Data, Signature.DigitalSignature);
4536 if (Identity is null)
4537 {
4538 await this.ReturnStatus(ContractStatus.ClientSignatureInvalid, Callback, State);
4539 return;
4540 }
4541
4542 IdentityStatus Status = await this.ValidateAsync(Identity);
4543 if (Status != IdentityStatus.Valid)
4544 {
4545 await this.ReturnStatus(ContractStatus.ClientIdentityInvalid, Callback, State);
4546 return;
4547 }
4548
4549 Identities[Signature.LegalId] = Identity;
4550 }
4551
4552 if (!(Contract.Attachments is null))
4553 {
4555 {
4556 if (string.IsNullOrEmpty(Attachment.Url))
4557 {
4558 await this.ReturnStatus(ContractStatus.AttachmentLacksUrl, Callback, State);
4559 return;
4560 }
4561
4562 try
4563 {
4564 KeyValuePair<string, TemporaryFile> P = await this.GetAttachmentAsync(Attachment.Url, SignWith.LatestApprovedId, 30000);
4565 bool? IsValid;
4566
4567 using (TemporaryFile File = P.Value)
4568 {
4569 if (P.Key != Attachment.ContentType)
4570 {
4571 await this.ReturnStatus(ContractStatus.AttachmentInconsistency, Callback, State);
4572 return;
4573 }
4574
4575 File.Position = 0;
4576
4577 if (Identities.TryGetValue(Attachment.LegalId, out LegalIdentity Identity))
4578 IsValid = this.ValidateSignature(Identity, File, Attachment.Signature);
4579 else
4580 {
4581 MemoryStream ms = new MemoryStream();
4582 await File.CopyToAsync(ms);
4583 Data = ms.ToArray();
4584
4585 try
4586 {
4587 Identity = await this.ValidateSignatureAsync(Attachment.LegalId, Data, Attachment.Signature);
4588 Identities[Attachment.LegalId] = Identity;
4589 IsValid = true;
4590 }
4591 catch (Exception)
4592 {
4593 IsValid = false;
4594 }
4595 }
4596
4597 if (IsValid.HasValue)
4598 {
4599 if (!IsValid.Value)
4600 {
4601 await this.ReturnStatus(ContractStatus.AttachmentSignatureInvalid, Callback, State);
4602 return;
4603 }
4604 }
4605 else
4606 {
4607 await this.ReturnStatus(ContractStatus.AttachmentSignatureInvalid, Callback, State);
4608 return;
4609 }
4610 }
4611 }
4612 catch (Exception)
4613 {
4614 await this.ReturnStatus(ContractStatus.AttachmentUnavailable, Callback, State);
4615 return;
4616 }
4617 }
4618 }
4619
4620 if (Contract.ServerSignature is null)
4621 {
4622 await this.ReturnStatus(ContractStatus.NoProviderSignature, Callback, State);
4623 return;
4624 }
4625
4626 Xml.Clear();
4627 Contract.Serialize(Xml, false, true, true, true, true, false, false);
4628 Data = Encoding.UTF8.GetBytes(Xml.ToString());
4629
4630 bool HasOldPublicKey;
4631
4632 lock (this.publicKeys)
4633 {
4634 HasOldPublicKey = this.publicKeys.ContainsKey(Contract.Provider);
4635 }
4636
4637 await this.GetServerPublicKey(Contract.Provider, async (Sender, e) =>
4638 {
4639 if (e.Ok && !(e.Key is null))
4640 {
4641 bool Valid = e.Key.Verify(Data, Contract.ServerSignature.DigitalSignature);
4642
4643 if (Valid)
4644 {
4645
4646 await this.ReturnStatus(ContractStatus.Valid, Callback, State);
4647 return;
4648 }
4649
4650 if (!HasOldPublicKey)
4651 {
4652 await this.ReturnStatus(ContractStatus.ProviderSignatureInvalid, Callback, State);
4653 return;
4654 }
4655
4656 lock (this.publicKeys)
4657 {
4658 this.publicKeys.Remove(Contract.Provider);
4659 }
4660
4661 await this.GetServerPublicKey(Contract.Provider, (sender2, e2) =>
4662 {
4663 if (e2.Ok && !(e2.Key is null))
4664 {
4665 if (e.Key.Equals(e2.Key))
4666 return this.ReturnStatus(ContractStatus.ProviderSignatureInvalid, Callback, State);
4667
4668 Valid = e2.Key.Verify(Data, Contract.ServerSignature.DigitalSignature);
4669
4670 if (Valid)
4671 return this.ReturnStatus(ContractStatus.Valid, Callback, State);
4672 else
4673 return this.ReturnStatus(ContractStatus.ProviderSignatureInvalid, Callback, State);
4674 }
4675 else
4676 return this.ReturnStatus(ContractStatus.NoProviderPublicKey, Callback, State);
4677
4678 }, State);
4679 }
4680 else
4681 await this.ReturnStatus(ContractStatus.NoProviderPublicKey, Callback, State);
4682
4683 }, State);
4684 }
4685
4686 private readonly Dictionary<string, KeyValuePair<byte[], XmlSchema>> schemas = new Dictionary<string, KeyValuePair<byte[], XmlSchema>>();
4687
4688 private Task ReturnStatus(ContractStatus Status, EventHandlerAsync<ContractValidationEventArgs> Callback, object State)
4689 {
4690 return Callback.Raise(this, new ContractValidationEventArgs(Status, State));
4691 }
4692
4693 private static async Task<bool> IsHumanReadableWellDefined(HumanReadableText[] Texts)
4694 {
4695 if (Texts is null)
4696 return false;
4697
4698 foreach (HumanReadableText Text in Texts)
4699 {
4700 if (!(await Text.IsWellDefined() is null))
4701 return false;
4702 }
4703
4704 return true;
4705 }
4706
4707 private static async Task<bool> IsHumanReadableWellDefined(Contract Contract)
4708 {
4709 if (!await IsHumanReadableWellDefined(Contract.ForHumans))
4710 return false;
4711
4712 if (!(Contract.Roles is null))
4713 {
4714 foreach (Role Role in Contract.Roles)
4715 {
4716 if (!await IsHumanReadableWellDefined(Role.Descriptions))
4717 return false;
4718 }
4719 }
4720
4721 if (!(Contract.Parameters is null))
4722 {
4724 {
4725 if (!await IsHumanReadableWellDefined(Parameter.Descriptions))
4726 return false;
4727 }
4728 }
4729
4730 return true;
4731 }
4732
4738 public Task<ContractStatus> ValidateAsync(Contract Contract)
4739 {
4740 return this.ValidateAsync(Contract, true);
4741 }
4742
4749 public async Task<ContractStatus> ValidateAsync(Contract Contract, bool ValidateState)
4750 {
4751 TaskCompletionSource<ContractStatus> Result = new TaskCompletionSource<ContractStatus>();
4752
4753 await this.Validate(Contract, ValidateState, (Sender, e) =>
4754 {
4755 Result.TrySetResult(e.Status);
4756 return Task.CompletedTask;
4757 }, null);
4758
4759 return await Result.Task;
4760 }
4761
4762 #endregion
4763
4764 #region Can Sign As
4765
4773 public async Task<bool> CanSignAs(CaseInsensitiveString ReferenceId, CaseInsensitiveString SignatoryId)
4774 {
4775 string ReferenceDomain = XmppClient.GetDomain(ReferenceId);
4776 string SignatoryDomain = XmppClient.GetDomain(SignatoryId);
4777
4778 if (ReferenceDomain != SignatoryDomain)
4779 return false;
4780
4781 TaskCompletionSource<bool> Result = new TaskCompletionSource<bool>();
4782 StringBuilder Xml = new StringBuilder();
4783
4784 Xml.Append("<canSignAs xmlns='");
4785 Xml.Append(NamespaceLegalIdentitiesCurrent);
4786 Xml.Append("' referenceId='");
4787 Xml.Append(XML.Encode(ReferenceId));
4788 Xml.Append("' signatoryId='");
4789 Xml.Append(XML.Encode(SignatoryId));
4790 Xml.Append("'/>");
4791
4792 await this.client.SendIqGet(ReferenceDomain, Xml.ToString(), (_, e) =>
4793 {
4794 Result.TrySetResult(e.Ok);
4795 return Task.CompletedTask;
4796 }, null);
4797
4798 return await Result.Task;
4799 }
4800
4801 #endregion
4802
4803 #region SendContractProposal
4804
4812 public Task SendContractProposal(Contract Contract, string Role, string To)
4813 {
4814 return this.SendContractProposal(Contract, Role, To, string.Empty);
4815 }
4816
4825 public async Task SendContractProposal(Contract Contract, string Role, string To, string Message)
4826 {
4828 {
4829 Tuple<SymmetricCipherAlgorithms, string, byte[]> T = await this.TryLoadContractSharedSecret(Contract.ContractId);
4830
4831 if (!(T is null))
4832 {
4833 await this.SendContractProposal(Contract.ContractId, Role, To, Message, T.Item3, T.Item1);
4834 return;
4835 }
4836 }
4837
4838 await this.SendContractProposal(Contract.ContractId, Role, To, Message, null, SymmetricCipherAlgorithms.Aes256);
4839 }
4840
4847 public Task SendContractProposal(string ContractId, string Role, string To)
4848 {
4849 return this.SendContractProposal(ContractId, Role, To, string.Empty);
4850 }
4851
4859 public Task SendContractProposal(string ContractId, string Role, string To, string Message)
4860 {
4861 return this.SendContractProposal(ContractId, Role, To, Message, null, SymmetricCipherAlgorithms.Aes256);
4862 }
4863
4873 public async Task SendContractProposal(string ContractId, string Role, string To, string Message, byte[] Key,
4874 SymmetricCipherAlgorithms KeyAlgorithm)
4875 {
4876 StringBuilder Xml = new StringBuilder();
4877
4878 Xml.Append("<contractProposal xmlns=\"");
4879 Xml.Append(NamespaceSmartContractsCurrent);
4880 Xml.Append("\" contractId=\"");
4881 Xml.Append(XML.Encode(ContractId));
4882 Xml.Append("\" role=\"");
4883 Xml.Append(XML.Encode(Role));
4884
4885 if (!string.IsNullOrEmpty(Message))
4886 {
4887 Xml.Append("\" message=\"");
4888 Xml.Append(XML.Encode(Message));
4889 }
4890
4891 Xml.Append('"');
4892
4893 if (Key is null)
4894 {
4895 Xml.Append("/>");
4896
4897 if (this.localE2eEndpoint is null)
4898 {
4899 await this.client.SendMessage(MessageType.Normal, To, Xml.ToString(), string.Empty, string.Empty, string.Empty,
4900 string.Empty, string.Empty);
4901 }
4902 else
4903 {
4904 await this.localE2eEndpoint.SendMessage(this.client, E2ETransmission.NormalIfNotE2E, QoSLevel.Unacknowledged, MessageType.Normal,
4905 string.Empty, To, Xml.ToString(), string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, null, null);
4906 }
4907 }
4908 else
4909 {
4910 Xml.Append("><sharedSecret key=\"");
4911 Xml.Append(Convert.ToBase64String(Key));
4912 Xml.Append("\" algorithm=\"");
4913
4914 switch (KeyAlgorithm)
4915 {
4916 case SymmetricCipherAlgorithms.Aes256:
4917 Xml.Append("aes");
4918 break;
4919
4920 case SymmetricCipherAlgorithms.ChaCha20:
4921 Xml.Append("cha");
4922 break;
4923
4924 case SymmetricCipherAlgorithms.AeadChaCha20Poly1305:
4925 Xml.Append("acp");
4926 break;
4927
4928 default:
4929 throw new ArgumentException("Algorithm not recognized.", nameof(KeyAlgorithm));
4930 }
4931
4932 Xml.Append("\"/></contractProposal>");
4933
4934 if (this.localE2eEndpoint is null)
4935 throw new InvalidOperationException("End-to-End encryption not enabled.");
4936
4937 if (XmppClient.GetBareJID(To) == To)
4938 {
4939 RosterItem Item = this.client[To]
4940 ?? throw new ArgumentException("Recipient not in roster.", nameof(To));
4941
4942 To = Item.LastPresenceFullJid;
4943 if (string.IsNullOrEmpty(To))
4944 throw new ArgumentException("Recipient not online.", nameof(To));
4945 }
4946
4947 await this.localE2eEndpoint.SendMessage(this.client, E2ETransmission.AssertE2E, QoSLevel.Unacknowledged, MessageType.Normal,
4948 string.Empty, To, Xml.ToString(), string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, null, null);
4949 }
4950 }
4951
4952 private async Task ContractProposalMessageHandler(object Sender, MessageEventArgs e)
4953 {
4954 string ContractId = XML.Attribute(e.Content, "contractId");
4955 string Role = XML.Attribute(e.Content, "role");
4956 string Message = XML.Attribute(e.Content, "message");
4957 byte[] Key = null;
4959
4960 foreach (XmlNode N in e.Content.ChildNodes)
4961 {
4962 if (N is XmlElement E && E.LocalName == "sharedSecret" && E.NamespaceURI == e.Content.NamespaceURI)
4963 {
4964 if (!e.UsesE2eEncryption)
4965 {
4966 await this.client.Error("Confidential Proposal not sent using end-to-end encryption. Message discarded.");
4967 return;
4968 }
4969
4970 try
4971 {
4972 Key = Convert.FromBase64String(XML.Attribute(E, "key"));
4973 }
4974 catch (Exception)
4975 {
4976 await this.client.Error("Invalid base64-encoded shared secret. Message discarded.");
4977 return;
4978 }
4979
4980 string Cipher = XML.Attribute(E, "algorithm");
4981
4982 switch (Cipher)
4983 {
4984 case "aes":
4985 KeyAlgorithm = SymmetricCipherAlgorithms.Aes256;
4986 break;
4987
4988 case "cha":
4989 KeyAlgorithm = SymmetricCipherAlgorithms.ChaCha20;
4990 break;
4991
4992 case "acp":
4993 KeyAlgorithm = SymmetricCipherAlgorithms.AeadChaCha20Poly1305;
4994 break;
4995
4996 default:
4997 await this.client.Error("Unrecognized key algorithm. Message discarded.");
4998 return;
4999 }
5000 }
5001 }
5002
5003 if (!(Key is null))
5004 await this.SaveContractSharedSecret(ContractId, e.FromBareJID, Key, KeyAlgorithm, true);
5005
5006 await this.ContractProposalReceived.Raise(this, new ContractProposalEventArgs(e, ContractId, Role, Message, Key, KeyAlgorithm));
5007 }
5008
5009 internal async Task<bool> SaveContractSharedSecret(string ContractId, string CreatorJid, byte[] Key,
5010 SymmetricCipherAlgorithms KeyAlgorithm, bool OnlyIfNew)
5011 {
5012 string Name = this.contractKeySettingsPrefix + ContractId;
5013
5014 if (OnlyIfNew)
5015 {
5016 string s = await RuntimeSettings.GetAsync(Name, string.Empty);
5017 if (!string.IsNullOrEmpty(s))
5018 return false;
5019 }
5020
5021 string Value = KeyAlgorithm.ToString() + "|" + CreatorJid + "|" + Convert.ToBase64String(Key);
5022
5023 await RuntimeSettings.SetAsync(Name, Value);
5024
5025 return true;
5026 }
5027
5028 internal async Task<Tuple<SymmetricCipherAlgorithms, string, byte[]>> TryLoadContractSharedSecret(string ContractId)
5029 {
5030 string Name = this.contractKeySettingsPrefix + ContractId;
5031 string s = await RuntimeSettings.GetAsync(Name, string.Empty);
5032
5033 if (string.IsNullOrEmpty(s))
5034 return null;
5035
5036 string[] Parts = s.Split('|');
5037 if (Parts.Length != 3)
5038 return null;
5039
5040 if (!Enum.TryParse(Parts[0], out SymmetricCipherAlgorithms Algorithm))
5041 return null;
5042
5043 string CreatorJid = Parts[1];
5044 byte[] Key;
5045
5046 try
5047 {
5048 Key = Convert.FromBase64String(Parts[2]);
5049 }
5050 catch (Exception)
5051 {
5052 return null;
5053 }
5054
5055 return new Tuple<SymmetricCipherAlgorithms, string, byte[]>(Algorithm, CreatorJid, Key);
5056 }
5057
5061 public event EventHandlerAsync<ContractProposalEventArgs> ContractProposalReceived = null;
5062
5063 #endregion
5064
5065 #region Get Schemas
5066
5072 public Task GetSchemas(EventHandlerAsync<SchemaReferencesEventArgs> Callback, object State)
5073 {
5074 return this.GetSchemas(this.componentAddress, Callback, State);
5075 }
5076
5083 public Task GetSchemas(string Address, EventHandlerAsync<SchemaReferencesEventArgs> Callback, object State)
5084 {
5085 return this.client.SendIqGet(Address, "<getSchemas xmlns='" + NamespaceSmartContractsCurrent + "'/>",
5086 async (Sender, e) =>
5087 {
5088 XmlElement E = e.FirstElement;
5089 List<SchemaReference> Schemas = new List<SchemaReference>();
5090
5091 if (e.Ok && !(E is null) && E.LocalName == "schemas")
5092 {
5093 foreach (XmlNode N in E.ChildNodes)
5094 {
5095 if (N is XmlElement E2 && E2.LocalName == "schemaRef")
5096 {
5097 string Namespace = XML.Attribute(E2, "namespace");
5098 List<SchemaDigest> Digests = new List<SchemaDigest>();
5099
5100 foreach (XmlNode N2 in E2.ChildNodes)
5101 {
5102 if (N2 is XmlElement E3 && E3.LocalName == "digest")
5103 {
5104 if (!Enum.TryParse(XML.Attribute(E3, "function"), out HashFunction Function))
5105 continue;
5106
5107 byte[] Digest = Convert.FromBase64String(E3.InnerText);
5108
5109 Digests.Add(new SchemaDigest(Function, Digest));
5110 }
5111 }
5112
5113 Schemas.Add(new SchemaReference(Namespace, Digests.ToArray()));
5114 }
5115 }
5116 }
5117 else
5118 e.Ok = false;
5119
5120 await Callback.Raise(this, new SchemaReferencesEventArgs(e, Schemas.ToArray()));
5121
5122 }, State);
5123 }
5124
5129 public Task<SchemaReference[]> GetSchemasAsync()
5130 {
5131 return this.GetSchemasAsync(this.componentAddress);
5132 }
5133
5139 public async Task<SchemaReference[]> GetSchemasAsync(string Address)
5140 {
5141 TaskCompletionSource<SchemaReference[]> Result = new TaskCompletionSource<SchemaReference[]>();
5142
5143 await this.GetSchemas(Address, (Sender, e) =>
5144 {
5145 if (e.Ok)
5146 Result.TrySetResult(e.References);
5147 else
5148 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get schemas."));
5149
5150 return Task.CompletedTask;
5151
5152 }, null);
5153
5154 return await Result.Task;
5155 }
5156
5157 #endregion
5158
5159 #region Get Schema
5160
5167 public Task GetSchema(string Namespace, EventHandlerAsync<SchemaEventArgs> Callback, object State)
5168 {
5169 return this.GetSchema(this.componentAddress, Namespace, null, Callback, State);
5170 }
5171
5179 public Task GetSchema(string Namespace, SchemaDigest Digest, EventHandlerAsync<SchemaEventArgs> Callback, object State)
5180 {
5181 return this.GetSchema(this.componentAddress, Namespace, Digest, Callback, State);
5182 }
5183
5191 public Task GetSchema(string Address, string Namespace, EventHandlerAsync<SchemaEventArgs> Callback, object State)
5192 {
5193 return this.GetSchema(Address, Namespace, null, Callback, State);
5194 }
5195
5204 public Task GetSchema(string Address, string Namespace, SchemaDigest Digest, EventHandlerAsync<SchemaEventArgs> Callback, object State)
5205 {
5206 StringBuilder Xml = new StringBuilder();
5207
5208 Xml.Append("<getSchema xmlns='");
5209 Xml.Append(NamespaceSmartContractsCurrent);
5210 Xml.Append("' namespace='");
5211 Xml.Append(XML.Encode(Namespace));
5212
5213 if (Digest is null)
5214 Xml.Append("'/>");
5215 else
5216 {
5217 Xml.Append("'><digest function='");
5218 Xml.Append(Digest.Function.ToString());
5219 Xml.Append("'>");
5220 Xml.Append(Convert.ToBase64String(Digest.Digest));
5221 Xml.Append("</digest></getSchema>");
5222 }
5223
5224 return this.client.SendIqGet(Address, Xml.ToString(),
5225 async (Sender, e) =>
5226 {
5227 XmlElement E = e.FirstElement;
5228 byte[] Schema = null;
5229
5230 if (e.Ok && !(E is null) && E.LocalName == "schema")
5231 Schema = Convert.FromBase64String(E.InnerText);
5232 else
5233 e.Ok = false;
5234
5235 await Callback.Raise(this, new SchemaEventArgs(e, Schema));
5236
5237 }, State);
5238 }
5239
5245 public Task<byte[]> GetSchemaAsync(string Namespace)
5246 {
5247 return this.GetSchemaAsync(this.componentAddress, Namespace, null);
5248 }
5249
5256 public Task<byte[]> GetSchemaAsync(string Namespace, SchemaDigest Digest)
5257 {
5258 return this.GetSchemaAsync(this.componentAddress, Namespace, Digest);
5259 }
5260
5267 public Task<byte[]> GetSchemaAsync(string Address, string Namespace)
5268 {
5269 return this.GetSchemaAsync(Address, Namespace, null);
5270 }
5271
5279 public async Task<byte[]> GetSchemaAsync(string Address, string Namespace, SchemaDigest Digest)
5280 {
5281 TaskCompletionSource<byte[]> Result = new TaskCompletionSource<byte[]>();
5282
5283 await this.GetSchema(Address, Namespace, Digest, (Sender, e) =>
5284 {
5285 if (e.Ok)
5286 Result.TrySetResult(e.Schema);
5287 else
5288 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get schema."));
5289
5290 return Task.CompletedTask;
5291
5292 }, null);
5293
5294 return await Result.Task;
5295 }
5296
5297 #endregion
5298
5299 #region Get Legal Identities of a contract
5300
5307 public Task GetContractLegalIdentities(string ContractId, EventHandlerAsync<LegalIdentitiesEventArgs> Callback, object State)
5308 {
5309 return this.GetContractLegalIdentities(this.GetTrustProvider(ContractId), ContractId, false, true, Callback, State);
5310 }
5311
5320 public Task GetContractLegalIdentities(string ContractId, bool Current, bool Historic, EventHandlerAsync<LegalIdentitiesEventArgs> Callback, object State)
5321 {
5322 return this.GetContractLegalIdentities(this.GetTrustProvider(ContractId), ContractId, Current, Historic, Callback, State);
5323 }
5324
5332 public Task GetContractLegalIdentities(string Address, string ContractId, EventHandlerAsync<LegalIdentitiesEventArgs> Callback, object State)
5333 {
5334 return this.GetContractLegalIdentities(Address, ContractId, false, true, Callback, State);
5335 }
5336
5346 public Task GetContractLegalIdentities(string Address, string ContractId, bool Current, bool Historic, EventHandlerAsync<LegalIdentitiesEventArgs> Callback, object State)
5347 {
5348 StringBuilder Xml = new StringBuilder();
5349
5350 Xml.Append("<getLegalIdentities xmlns='");
5351 Xml.Append(NamespaceSmartContractsCurrent);
5352 Xml.Append("' contractId='");
5353 Xml.Append(XML.Encode(ContractId));
5354 Xml.Append("' current='");
5355 Xml.Append(CommonTypes.Encode(Current));
5356 Xml.Append("' historic='");
5357 Xml.Append(CommonTypes.Encode(Historic));
5358 Xml.Append("'/>");
5359
5360 return this.client.SendIqGet(Address, Xml.ToString(), this.IdentitiesResponse, new object[] { Callback, State });
5361 }
5362
5368 public Task<LegalIdentity[]> GetContractLegalIdentitiesAsync(string ContractId)
5369 {
5370 return this.GetContractLegalIdentitiesAsync(this.GetTrustProvider(ContractId), ContractId, false, true);
5371 }
5372
5380 public Task<LegalIdentity[]> GetContractLegalIdentitiesAsync(string ContractId, bool Current, bool Historic)
5381 {
5382 return this.GetContractLegalIdentitiesAsync(this.GetTrustProvider(ContractId), ContractId, Current, Historic);
5383 }
5384
5391 public Task<LegalIdentity[]> GetContractLegalIdentitiesAsync(string Address, string ContractId)
5392 {
5393 return this.GetContractLegalIdentitiesAsync(Address, ContractId, false, true);
5394 }
5395
5404 public async Task<LegalIdentity[]> GetContractLegalIdentitiesAsync(string Address, string ContractId, bool Current, bool Historic)
5405 {
5406 TaskCompletionSource<LegalIdentity[]> Result = new TaskCompletionSource<LegalIdentity[]>();
5407
5408 await this.GetContractLegalIdentities(Address, ContractId, Current, Historic, (Sender, e) =>
5409 {
5410 if (e.Ok)
5411 Result.TrySetResult(e.Identities);
5412 else
5413 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get legal identities."));
5414
5415 return Task.CompletedTask;
5416
5417 }, null);
5418
5419 return await Result.Task;
5420 }
5421
5422 #endregion
5423
5424 #region Get Network Identities of a contract
5425
5432 public Task GetContractNetworkIdentities(string ContractId, EventHandlerAsync<NetworkIdentitiesEventArgs> Callback, object State)
5433 {
5434 return this.GetContractNetworkIdentities(this.GetTrustProvider(ContractId), ContractId, Callback, State);
5435 }
5436
5444 public Task GetContractNetworkIdentities(string Address, string ContractId, EventHandlerAsync<NetworkIdentitiesEventArgs> Callback, object State)
5445 {
5446 StringBuilder Xml = new StringBuilder();
5447
5448 Xml.Append("<getNetworkIdentities xmlns='");
5449 Xml.Append(NamespaceSmartContractsCurrent);
5450 Xml.Append("' contractId='");
5451 Xml.Append(XML.Encode(ContractId));
5452 Xml.Append("'/>");
5453
5454 return this.client.SendIqGet(Address, Xml.ToString(), async (Sender, e) =>
5455 {
5456 NetworkIdentity[] Identities = null;
5457 XmlElement E;
5458
5459 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "networkIdentities")
5460 {
5461 List<NetworkIdentity> IdentitiesList = new List<NetworkIdentity>();
5462
5463 foreach (XmlNode N in E.ChildNodes)
5464 {
5465 if (N is XmlElement E2 && E2.LocalName == "networkIdentity")
5466 {
5467 string BareJid = XML.Attribute(E2, "bareJid");
5468 string LegalId = XML.Attribute(E2, "legalId");
5469
5470 IdentitiesList.Add(new NetworkIdentity(BareJid, LegalId));
5471 }
5472 }
5473
5474 Identities = IdentitiesList.ToArray();
5475 }
5476 else
5477 e.Ok = false;
5478
5479 await Callback.Raise(this, new NetworkIdentitiesEventArgs(e, Identities));
5480 }, State);
5481 }
5482
5488 public Task<NetworkIdentity[]> GetContractNetworkIdentitiesAsync(string ContractId)
5489 {
5490 return this.GetContractNetworkIdentitiesAsync(this.GetTrustProvider(ContractId), ContractId);
5491 }
5492
5499 public async Task<NetworkIdentity[]> GetContractNetworkIdentitiesAsync(string Address, string ContractId)
5500 {
5501 TaskCompletionSource<NetworkIdentity[]> Result = new TaskCompletionSource<NetworkIdentity[]>();
5502
5503 await this.GetContractNetworkIdentities(Address, ContractId, (Sender, e) =>
5504 {
5505 if (e.Ok)
5506 Result.TrySetResult(e.Identities);
5507 else
5508 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get network identities."));
5509
5510 return Task.CompletedTask;
5511
5512 }, null);
5513
5514 return await Result.Task;
5515 }
5516
5517 #endregion
5518
5519 #region Search Public Contracts
5520
5527 public Task Search(SearchFilter[] Filter, EventHandlerAsync<SearchResultEventArgs> Callback, object State)
5528 {
5529 return this.Search(this.componentAddress, 0, int.MaxValue, Filter, Callback, State);
5530 }
5531
5539 public Task Search(string Address, SearchFilter[] Filter, EventHandlerAsync<SearchResultEventArgs> Callback, object State)
5540 {
5541 return this.Search(Address, 0, int.MaxValue, Filter, Callback, State);
5542 }
5543
5552 public Task Search(int Offset, int MaxCount, SearchFilter[] Filter, EventHandlerAsync<SearchResultEventArgs> Callback, object State)
5553 {
5554 return this.Search(this.componentAddress, Offset, MaxCount, Filter, Callback, State);
5555 }
5556
5566 public Task Search(string Address, int Offset, int MaxCount, SearchFilter[] Filter, EventHandlerAsync<SearchResultEventArgs> Callback, object State)
5567 {
5568 if (Offset < 0)
5569 throw new ArgumentException("Offsets cannot be negative.", nameof(Offset));
5570
5571 if (MaxCount <= 0)
5572 throw new ArgumentException("Must be postitive.", nameof(MaxCount));
5573
5574 StringBuilder Xml = new StringBuilder();
5575
5576 Xml.Append("<searchPublicContracts xmlns='");
5577 Xml.Append(NamespaceSmartContractsCurrent);
5578
5579 if (Offset > 0)
5580 {
5581 Xml.Append("' offset='");
5582 Xml.Append(Offset.ToString());
5583 }
5584
5585 if (MaxCount < int.MaxValue)
5586 {
5587 Xml.Append("' maxCount='");
5588 Xml.Append(MaxCount.ToString());
5589 }
5590
5591 Xml.Append("'>");
5592
5593 Filter = (SearchFilter[])Filter.Clone();
5594 Array.Sort(Filter, (f1, f2) => f1.Order - f2.Order);
5595
5596 int PrevOrder = 0;
5597 int PrevOrderCount = 0;
5598 int Order;
5599
5600 foreach (SearchFilter F in Filter)
5601 {
5602 Order = F.Order;
5603 if (Order != PrevOrder)
5604 {
5605 PrevOrder = Order;
5606 PrevOrderCount = 1;
5607 }
5608 else
5609 {
5610 PrevOrderCount++;
5611 if (PrevOrderCount >= F.MaxOccurs)
5612 {
5613 throw new ArgumentException("Maximum number of occurrences of " + F.GetType().FullName + " in a search is " +
5614 F.MaxOccurs.ToString() + ".", nameof(Filter));
5615 }
5616 }
5617
5618 F.Serialize(Xml);
5619 }
5620
5621 Xml.Append("</searchPublicContracts>");
5622
5623 return this.client.SendIqGet(Address, Xml.ToString(), async (Sender, e) =>
5624 {
5625 XmlElement E = e.FirstElement;
5626 List<string> IDs = null;
5627 bool More = false;
5628
5629 if (e.Ok && !(E is null) && E.LocalName == "searchResult")
5630 {
5631 More = XML.Attribute(E, "more", false);
5632 IDs = new List<string>();
5633
5634 foreach (XmlNode N in E.ChildNodes)
5635 {
5636 if (N is XmlElement E2 && E2.LocalName == "ref")
5637 {
5638 string Id = XML.Attribute(E2, "id");
5639 IDs.Add(Id);
5640 }
5641 }
5642 }
5643 else
5644 e.Ok = false;
5645
5646 await Callback.Raise(this, new SearchResultEventArgs(e, Offset, MaxCount, More, IDs?.ToArray()));
5647 }, State);
5648 }
5649
5655 public Task<SearchResultEventArgs> SearchAsync(SearchFilter[] Filter)
5656 {
5657 return this.SearchAsync(this.componentAddress, 0, int.MaxValue, Filter);
5658 }
5659
5665 public Task<SearchResultEventArgs> SearchAsync(string Address, SearchFilter[] Filter)
5666 {
5667 return this.SearchAsync(Address, 0, int.MaxValue, Filter);
5668 }
5669
5676 public Task<SearchResultEventArgs> SearchAsync(int Offset, int MaxCount, SearchFilter[] Filter)
5677 {
5678 return this.SearchAsync(this.componentAddress, Offset, MaxCount, Filter);
5679 }
5680
5688 public async Task<SearchResultEventArgs> SearchAsync(string Address, int Offset, int MaxCount, SearchFilter[] Filter)
5689 {
5690 TaskCompletionSource<SearchResultEventArgs> Result = new TaskCompletionSource<SearchResultEventArgs>();
5691
5692 await this.Search(Address, Offset, MaxCount, Filter, (Sender, e) =>
5693 {
5694 Result.TrySetResult(e);
5695 return Task.CompletedTask;
5696 }, null);
5697
5698 return await Result.Task;
5699 }
5700
5701 #endregion
5702
5703 #region Identity petitions
5704
5715 public Task PetitionIdentityAsync(string LegalId, string PetitionId, string Purpose)
5716 {
5717 return this.PetitionIdentityAsync(this.GetTrustProvider(LegalId), LegalId, PetitionId, Purpose, null);
5718 }
5719
5731 public Task PetitionIdentityAsync(string Address, string LegalId, string PetitionId, string Purpose)
5732 {
5733 return this.PetitionIdentityAsync(Address, LegalId, PetitionId, Purpose, null);
5734 }
5735
5748 public async Task PetitionIdentityAsync(string Address, string LegalId, string PetitionId, string Purpose, string ContextXml)
5749 {
5750 StringBuilder Xml = new StringBuilder();
5751 byte[] Nonce = this.RandomBytes(32);
5752
5753 string NonceStr = Convert.ToBase64String(Nonce);
5754 byte[] Data = Encoding.UTF8.GetBytes(PetitionId + ":" + LegalId + ":" + Purpose + ":" + NonceStr + ":" + this.client.BareJID.ToLower());
5755 byte[] Signature = await this.SignAsync(Data, SignWith.LatestApprovedId);
5756
5757 Xml.Append("<petitionIdentity xmlns='");
5758 Xml.Append(NamespaceLegalIdentitiesCurrent);
5759 Xml.Append("' id='");
5760 Xml.Append(XML.Encode(LegalId));
5761 Xml.Append("' pid='");
5762 Xml.Append(XML.Encode(PetitionId));
5763 Xml.Append("' purpose='");
5764 Xml.Append(XML.Encode(Purpose));
5765 Xml.Append("' nonce='");
5766 Xml.Append(NonceStr);
5767 Xml.Append("' s='");
5768 Xml.Append(Convert.ToBase64String(Signature));
5769
5770 if (string.IsNullOrEmpty(ContextXml))
5771 Xml.Append("'/>");
5772 else
5773 {
5774 Xml.Append("'>");
5775 Xml.Append(ContextXml);
5776 Xml.Append("</petitionIdentity>");
5777 }
5778
5779 await this.client.IqSetAsync(Address, Xml.ToString());
5780 }
5781
5792 public Task PetitionIdentityResponseAsync(string LegalId, string PetitionId, string RequestorFullJid, bool Response)
5793 {
5794 return this.PetitionIdentityResponseAsync(this.GetTrustProvider(LegalId), LegalId, PetitionId, RequestorFullJid, Response, null);
5795 }
5796
5808 public Task PetitionIdentityResponseAsync(string LegalId, string PetitionId, string RequestorFullJid, bool Response, string ContextXml)
5809 {
5810 return this.PetitionIdentityResponseAsync(this.GetTrustProvider(LegalId), LegalId, PetitionId, RequestorFullJid, Response, ContextXml);
5811 }
5812
5824 public Task PetitionIdentityResponseAsync(string Address, string LegalId, string PetitionId, string RequestorFullJid, bool Response)
5825 {
5826 return this.PetitionIdentityResponseAsync(Address, LegalId, PetitionId, RequestorFullJid, Response, null);
5827 }
5828
5841 public async Task PetitionIdentityResponseAsync(string Address, string LegalId, string PetitionId, string RequestorFullJid, bool Response,
5842 string ContextXml)
5843 {
5844 StringBuilder Xml = new StringBuilder();
5845
5846 Xml.Append("<petitionIdentityResponse xmlns='");
5847 Xml.Append(NamespaceLegalIdentitiesCurrent);
5848 Xml.Append("' id='");
5849 Xml.Append(XML.Encode(LegalId));
5850 Xml.Append("' pid='");
5851 Xml.Append(XML.Encode(PetitionId));
5852 Xml.Append("' jid='");
5853 Xml.Append(XML.Encode(RequestorFullJid));
5854 Xml.Append("' response='");
5855 Xml.Append(CommonTypes.Encode(Response));
5856
5857 if (string.IsNullOrEmpty(ContextXml))
5858 Xml.Append("'/>");
5859 else
5860 {
5861 Xml.Append("'>");
5862 Xml.Append(ContextXml);
5863 Xml.Append("</petitionIdentityResponse>");
5864 }
5865
5866 await this.client.IqSetAsync(Address, Xml.ToString());
5867 }
5868
5869 private async Task PetitionIdentityMessageHandler(object Sender, MessageEventArgs e)
5870 {
5871 string LegalId = XML.Attribute(e.Content, "id");
5872 string PetitionId = XML.Attribute(e.Content, "pid");
5873 string Purpose = XML.Attribute(e.Content, "purpose");
5874 string From = XML.Attribute(e.Content, "from");
5875 string ClientEndpoint = XML.Attribute(e.Content, "clientEp");
5876 LegalIdentity Identity = null;
5877 XmlElement Context = null;
5878
5879 foreach (XmlNode N in e.Content.ChildNodes)
5880 {
5881 if (N is XmlElement E)
5882 {
5883 if (E.LocalName == "identity" && E.NamespaceURI == e.Content.NamespaceURI)
5884 Identity = LegalIdentity.Parse(E);
5885 else if (!(Context is null))
5886 return;
5887 else
5888 Context = E;
5889 }
5890 }
5891
5892 if (Identity is null)
5893 return;
5894
5895 if (string.Compare(e.FromBareJID, this.componentAddress, true) == 0)
5896 {
5897 await this.Validate(Identity, false, async (sender2, e2) =>
5898 {
5899 if (e2.Status != IdentityStatus.Valid)
5900 {
5901 await this.client.Error("Invalid legal identity received and discarded.");
5902
5903 Log.Warning("Invalid legal identity received and discarded.", this.client.BareJID, e.From,
5904 new KeyValuePair<string, object>("Status", e2.Status));
5905 return;
5906 }
5907
5908 await this.PetitionForIdentityReceived.Raise(this, new LegalIdentityPetitionEventArgs(e, Identity, From, LegalId, PetitionId, Purpose, ClientEndpoint, Context));
5909 }, null);
5910 }
5911 }
5912
5916 public event EventHandlerAsync<LegalIdentityPetitionEventArgs> PetitionForIdentityReceived = null;
5917
5918 private async Task PetitionIdentityResponseMessageHandler(object Sender, MessageEventArgs e)
5919 {
5920 string PetitionId = XML.Attribute(e.Content, "pid");
5921 bool Response = XML.Attribute(e.Content, "response", false);
5922 string ClientEndpoint = XML.Attribute(e.Content, "clientEp");
5923 LegalIdentity Identity = null;
5924 XmlElement Context = null;
5925
5926 foreach (XmlNode N in e.Content.ChildNodes)
5927 {
5928 if (N is XmlElement E)
5929 {
5930 if (E.LocalName == "identity" && E.NamespaceURI == e.Content.NamespaceURI)
5931 Identity = LegalIdentity.Parse(E);
5932 else if (!(Context is null))
5933 return;
5934 else
5935 Context = E;
5936 }
5937 }
5938
5939 if (!Response || string.Compare(e.FromBareJID, Identity?.Provider ?? string.Empty, true) == 0)
5940 await this.PetitionedIdentityResponseReceived.Raise(this, new LegalIdentityPetitionResponseEventArgs(e, Identity, PetitionId, Response, ClientEndpoint, Context));
5941 }
5942
5946 public event EventHandlerAsync<LegalIdentityPetitionResponseEventArgs> PetitionedIdentityResponseReceived = null;
5947
5948 #endregion
5949
5950 #region Signature petitions
5951
5963 public Task PetitionSignatureAsync(string LegalId, byte[] Content, string PetitionId, string Purpose)
5964 {
5965 return this.PetitionSignatureAsync(this.GetTrustProvider(LegalId), LegalId, Content, PetitionId, Purpose, false, null);
5966 }
5967
5980 public Task PetitionSignatureAsync(string Address, string LegalId, byte[] Content, string PetitionId, string Purpose)
5981 {
5982 return this.PetitionSignatureAsync(Address, LegalId, Content, PetitionId, Purpose, false, null);
5983 }
5984
5998 public Task PetitionSignatureAsync(string Address, string LegalId, byte[] Content, string PetitionId, string Purpose, string ContextXml)
5999 {
6000 return this.PetitionSignatureAsync(Address, LegalId, Content, PetitionId, Purpose, false, ContextXml);
6001 }
6002
6003 private async Task PetitionSignatureAsync(string Address, string LegalId, byte[] Content, string PetitionId,
6004 string Purpose, bool PeerReview, string ContextXml)
6005 {
6006 if (this.contentPerPid.TryGetValue(PetitionId, out KeyValuePair<byte[], bool> Rec))
6007 {
6008 if (Convert.ToBase64String(Content) == Convert.ToBase64String(Rec.Key) && PeerReview == Rec.Value)
6009 return;
6010
6011 throw new InvalidOperationException("Petition ID must be unique for outstanding petitions.");
6012 }
6013
6014 this.contentPerPid[PetitionId] = new KeyValuePair<byte[], bool>(Content, PeerReview);
6015
6016 StringBuilder Xml = new StringBuilder();
6017 byte[] Nonce = this.RandomBytes(32);
6018
6019 string NonceStr = Convert.ToBase64String(Nonce);
6020 string ContentStr = Convert.ToBase64String(Content);
6021 byte[] Data = Encoding.UTF8.GetBytes(PetitionId + ":" + LegalId + ":" + Purpose + ":" + NonceStr + ":" + this.client.BareJID.ToLower() + ":" + ContentStr);
6022 byte[] Signature = await this.SignAsync(Data, PeerReview ? SignWith.CurrentKeys : SignWith.LatestApprovedId);
6023
6024 Xml.Append("<petitionSignature xmlns='");
6025 Xml.Append(NamespaceLegalIdentitiesCurrent);
6026 Xml.Append("' id='");
6027 Xml.Append(XML.Encode(LegalId));
6028 Xml.Append("' pid='");
6029 Xml.Append(XML.Encode(PetitionId));
6030 Xml.Append("' purpose='");
6031 Xml.Append(XML.Encode(Purpose));
6032 Xml.Append("' nonce='");
6033 Xml.Append(NonceStr);
6034 Xml.Append("' s='");
6035 Xml.Append(Convert.ToBase64String(Signature));
6036 Xml.Append("'>");
6037
6038 if (string.IsNullOrEmpty(ContextXml))
6039 Xml.Append(ContentStr);
6040 else
6041 {
6042 Xml.Append("<content>");
6043 Xml.Append(ContentStr);
6044 Xml.Append("</content>");
6045 Xml.Append(ContextXml);
6046 }
6047
6048 Xml.Append("</petitionSignature>");
6049
6050 await this.client.IqSetAsync(Address, Xml.ToString());
6051 }
6052
6065 public Task PetitionSignatureResponseAsync(string LegalId, byte[] Content,
6066 byte[] Signature, string PetitionId, string RequestorFullJid, bool Response)
6067 {
6068 return this.PetitionSignatureResponseAsync(this.GetTrustProvider(LegalId), LegalId, Content, Signature, PetitionId,
6069 RequestorFullJid, Response, null);
6070 }
6071
6085 public Task PetitionSignatureResponseAsync(string LegalId, byte[] Content,
6086 byte[] Signature, string PetitionId, string RequestorFullJid, bool Response, string ContextXml)
6087 {
6088 return this.PetitionSignatureResponseAsync(this.GetTrustProvider(LegalId), LegalId, Content, Signature, PetitionId,
6089 RequestorFullJid, Response, ContextXml);
6090 }
6091
6105 public Task PetitionSignatureResponseAsync(string Address, string LegalId, byte[] Content, byte[] Signature,
6106 string PetitionId, string RequestorFullJid, bool Response)
6107 {
6108 return this.PetitionSignatureResponseAsync(Address, LegalId, Content, Signature, PetitionId, RequestorFullJid, Response, null);
6109 }
6110
6125 public async Task PetitionSignatureResponseAsync(string Address, string LegalId, byte[] Content, byte[] Signature,
6126 string PetitionId, string RequestorFullJid, bool Response, string ContextXml)
6127 {
6128 StringBuilder Xml = new StringBuilder();
6129
6130 Xml.Append("<petitionSignatureResponse xmlns='");
6131 Xml.Append(NamespaceLegalIdentitiesCurrent);
6132 Xml.Append("' id='");
6133 Xml.Append(XML.Encode(LegalId));
6134 Xml.Append("' pid='");
6135 Xml.Append(XML.Encode(PetitionId));
6136 Xml.Append("' jid='");
6137 Xml.Append(XML.Encode(RequestorFullJid));
6138 Xml.Append("' response='");
6139 Xml.Append(CommonTypes.Encode(Response));
6140 Xml.Append("'><content>");
6141 Xml.Append(Convert.ToBase64String(Content));
6142 Xml.Append("</content><signature>");
6143 Xml.Append(Convert.ToBase64String(Signature));
6144 Xml.Append("</signature>");
6145
6146 if (!string.IsNullOrEmpty(ContextXml))
6147 Xml.Append(ContextXml);
6148
6149 Xml.Append("</petitionSignatureResponse>");
6150
6151 await this.client.IqSetAsync(Address, Xml.ToString());
6152 }
6153
6154 private async Task PetitionSignatureMessageHandler(object Sender, MessageEventArgs e)
6155 {
6156 string LegalId = XML.Attribute(e.Content, "id");
6157 string PetitionId = XML.Attribute(e.Content, "pid");
6158 string Purpose = XML.Attribute(e.Content, "purpose");
6159 string From = XML.Attribute(e.Content, "from");
6160 string ClientEndpoint = XML.Attribute(e.Content, "clientEp");
6161 string ContentStr = string.Empty;
6162 byte[] Content = null;
6163 LegalIdentity Identity = null;
6164 XmlElement Context = null;
6165 bool PeerReview = false;
6166
6167 foreach (XmlNode N in e.Content.ChildNodes)
6168 {
6169 if (!(N is XmlElement E))
6170 continue;
6171
6172 switch (E.LocalName)
6173 {
6174 case "content":
6175 ContentStr = E.InnerText;
6176 Content = Convert.FromBase64String(ContentStr);
6177 break;
6178
6179 case "identity":
6180 Identity = LegalIdentity.Parse(E);
6181 break;
6182
6183 default:
6184 if (!(Context is null))
6185 return;
6186
6187 Context = E;
6188 break;
6189 }
6190 }
6191
6192 if (Content is null)
6193 return;
6194
6195 if (Identity is null)
6196 {
6197 string s = Encoding.UTF8.GetString(Content);
6198 if (s.StartsWith("<identity") && s.EndsWith("</identity>"))
6199 {
6200 try
6201 {
6202 XmlDocument Doc = new XmlDocument();
6203 Doc.LoadXml(s);
6204
6205 if (Doc.DocumentElement.LocalName == "identity")
6206 {
6207 LegalIdentity TempId = LegalIdentity.Parse(Doc.DocumentElement);
6208
6209 if (TempId.State == IdentityState.Created &&
6210 string.Compare(TempId["JID"], XmppClient.GetBareJID(From), true) == 0)
6211 {
6212 Identity = TempId;
6213 PeerReview = true;
6214 }
6215 }
6216 }
6217 catch (Exception)
6218 {
6219 // Ignore
6220 }
6221 }
6222
6223 if (Identity is null)
6224 return;
6225 }
6226
6227 if (string.Compare(e.FromBareJID, this.componentAddress, true) != 0 &&
6228 string.Compare(e.FromBareJID, XmppClient.GetDomain(Identity.Id), true) != 0)
6229 {
6230 return;
6231 }
6232
6233 EventHandlerAsync<SignaturePetitionEventArgs> h = PeerReview ? this.PetitionForPeerReviewIDReceived : this.PetitionForSignatureReceived;
6234
6235 await this.Validate(Identity, false, async (sender2, e2) =>
6236 {
6237 if (e2.Status != IdentityStatus.Valid && e2.Status != IdentityStatus.NoProviderSignature)
6238 {
6239 await this.client.Error("Invalid legal identity received and discarded.");
6240
6241 Log.Warning("Invalid legal identity received and discarded.", this.client.BareJID, e.From,
6242 new KeyValuePair<string, object>("Status", e2.Status));
6243
6244 return;
6245 }
6246
6247 await h.Raise(this, new SignaturePetitionEventArgs(e, Identity, From, LegalId, PetitionId, Purpose, Content, ClientEndpoint, Context));
6248
6249 }, null);
6250 }
6251
6255 public event EventHandlerAsync<SignaturePetitionEventArgs> PetitionForSignatureReceived = null;
6256
6257 private async Task PetitionSignatureResponseMessageHandler(object Sender, MessageEventArgs e)
6258 {
6259 string PetitionId = XML.Attribute(e.Content, "pid");
6260 bool Response = XML.Attribute(e.Content, "response", false);
6261 string ClientEndpoint = XML.Attribute(e.Content, "clientEp");
6262 string SignatureStr = string.Empty;
6263 byte[] Signature = null;
6264 LegalIdentity Identity = null;
6265 XmlElement Context = null;
6266
6267 foreach (XmlNode N in e.Content.ChildNodes)
6268 {
6269 if (N is XmlElement E)
6270 {
6271 switch (E.LocalName)
6272 {
6273 case "identity":
6274 Identity = LegalIdentity.Parse(E);
6275 break;
6276
6277 case "signature":
6278 SignatureStr = E.InnerText;
6279 Signature = Convert.FromBase64String(SignatureStr);
6280 break;
6281
6282 default:
6283 if (!(Context is null))
6284 return;
6285
6286 Context = E;
6287 break;
6288 }
6289 }
6290 }
6291
6292 if (!this.contentPerPid.TryGetValue(PetitionId, out KeyValuePair<byte[], bool> P))
6293 {
6294 await this.client.Warning("Petition ID not recognized: " + PetitionId + ". Response ignored.");
6295 return;
6296 }
6297
6298 EventHandlerAsync<SignaturePetitionResponseEventArgs> h = P.Value ? this.PetitionedPeerReviewIDResponseReceived : this.PetitionedSignatureResponseReceived;
6299
6300 if (Response)
6301 {
6302 if (Identity is null)
6303 {
6304 await this.client.Warning("Identity missing. Response ignored.");
6305 return;
6306 }
6307
6308 if (Signature is null)
6309 {
6310 await this.client.Warning("Signature missing. Response ignored.");
6311 return;
6312 }
6313
6314 bool? Result = this.ValidateSignature(Identity, P.Key, Signature);
6315 if (!Result.HasValue)
6316 {
6317 await this.client.Warning("Unable to validate signature. Response ignored.");
6318 return;
6319 }
6320
6321 if (!Result.Value)
6322 {
6323 await this.client.Warning("Invalid signature. Response ignored.");
6324 return;
6325 }
6326 }
6327
6328 if (!Response || string.Compare(e.FromBareJID, Identity?.Provider ?? string.Empty, true) == 0)
6329 {
6330 try
6331 {
6332 await this.Client.Information(h.Method.Name);
6333
6334 await h.Raise(this, new SignaturePetitionResponseEventArgs(e, Identity, PetitionId, Signature, Response, ClientEndpoint, Context));
6335 }
6336 finally
6337 {
6338 this.contentPerPid.Remove(PetitionId);
6339 }
6340 }
6341 else
6342 await this.client.Warning("Sender invalid. Response ignored.");
6343 }
6344
6348 public event EventHandlerAsync<SignaturePetitionResponseEventArgs> PetitionedSignatureResponseReceived = null;
6349
6350 #endregion
6351
6352 #region Peer Review of IDs
6353
6371 public Task PetitionPeerReviewIDAsync(string LegalId, LegalIdentity Identity, string PetitionId, string Purpose)
6372 {
6373 return this.PetitionPeerReviewIDAsync(this.GetTrustProvider(LegalId), LegalId, Identity, PetitionId, Purpose);
6374 }
6375
6394 public Task PetitionPeerReviewIDAsync(string Address, string LegalId, LegalIdentity Identity, string PetitionId, string Purpose)
6395 {
6396 StringBuilder Xml = new StringBuilder();
6397 Identity.Serialize(Xml, true, true, true, true, true, true, true);
6398 byte[] Content = Encoding.UTF8.GetBytes(Xml.ToString());
6399
6400 return this.PetitionSignatureAsync(Address, LegalId, Content, PetitionId, Purpose, true, null);
6401 }
6402
6406 public event EventHandlerAsync<SignaturePetitionEventArgs> PetitionForPeerReviewIDReceived = null;
6407
6411 public event EventHandlerAsync<SignaturePetitionResponseEventArgs> PetitionedPeerReviewIDResponseReceived = null;
6412
6420 public async Task<LegalIdentity> AddPeerReviewIDAttachment(LegalIdentity Identity,
6421 LegalIdentity ReviewerLegalIdentity, byte[] PeerSignature)
6422 {
6423 if (!this.client.TryGetExtension(out HttpFileUploadClient HttpFileUploadClient))
6424 throw new InvalidOperationException("No HTTP File Upload extension added to the XMPP Client.");
6425
6426 StringBuilder Xml = new StringBuilder();
6427
6428 Xml.Append("<peerReview s='");
6429 Xml.Append(Convert.ToBase64String(PeerSignature));
6430 Xml.Append("' tp='");
6431 Xml.Append(XML.Encode(DateTime.UtcNow));
6432 Xml.Append("' xmlns='");
6433 Xml.Append(NamespaceLegalIdentitiesCurrent);
6434 Xml.Append("'><reviewed>");
6435 Identity.Serialize(Xml, true, true, true, true, true, true, true);
6436 Xml.Append("</reviewed><reviewer>");
6437 ReviewerLegalIdentity.Serialize(Xml, true, true, true, true, true, true, true);
6438 Xml.Append("</reviewer></peerReview>");
6439
6440 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
6441 byte[] Signature = await this.SignAsync(Data, SignWith.CurrentKeys);
6442 string FileName = ReviewerLegalIdentity.Id + ".xml";
6443 string ContentType = "text/xml; charset=utf-8";
6444
6445 HttpFileUploadEventArgs e2 = await HttpFileUploadClient.RequestUploadSlotAsync(FileName, ContentType, Data.Length);
6446 if (!e2.Ok)
6447 throw new IOException("Unable to upload Peer Review attachment to broker.");
6448
6449 await e2.PUT(Data, ContentType, 10000);
6450
6451 return await this.AddLegalIdAttachmentAsync(Identity.Id, e2.GetUrl, Signature);
6452 }
6453
6454 #endregion
6455
6456 #region Contract petitions
6457
6468 public Task PetitionContractAsync(string ContractId, string PetitionId, string Purpose)
6469 {
6470 return this.PetitionContractAsync(this.GetTrustProvider(ContractId), ContractId, PetitionId, Purpose, null);
6471 }
6472
6484 public Task PetitionContractAsync(string Address, string ContractId, string PetitionId, string Purpose)
6485 {
6486 return this.PetitionContractAsync(Address, ContractId, PetitionId, Purpose, null);
6487 }
6488
6501 public async Task PetitionContractAsync(string Address, string ContractId, string PetitionId, string Purpose, string ContextXml)
6502 {
6503 StringBuilder Xml = new StringBuilder();
6504 byte[] Nonce = this.RandomBytes(32);
6505
6506 string NonceStr = Convert.ToBase64String(Nonce);
6507 byte[] Data = Encoding.UTF8.GetBytes(PetitionId + ":" + ContractId + ":" + Purpose + ":" + NonceStr + ":" + this.client.BareJID.ToLower());
6508 byte[] Signature = await this.SignAsync(Data, SignWith.LatestApprovedId);
6509
6510 Xml.Append("<petitionContract xmlns='");
6511 Xml.Append(NamespaceSmartContractsCurrent);
6512 Xml.Append("' id='");
6513 Xml.Append(XML.Encode(ContractId));
6514 Xml.Append("' pid='");
6515 Xml.Append(XML.Encode(PetitionId));
6516 Xml.Append("' purpose='");
6517 Xml.Append(XML.Encode(Purpose));
6518 Xml.Append("' nonce='");
6519 Xml.Append(NonceStr);
6520 Xml.Append("' s='");
6521 Xml.Append(Convert.ToBase64String(Signature));
6522
6523 if (string.IsNullOrEmpty(ContextXml))
6524 Xml.Append("'/>");
6525 else
6526 {
6527 Xml.Append("'>");
6528 Xml.Append(ContextXml);
6529 Xml.Append("</petitionContract>");
6530 }
6531
6532 await this.client.IqSetAsync(Address, Xml.ToString());
6533 }
6534
6545 public Task PetitionContractResponseAsync(string ContractId, string PetitionId, string RequestorFullJid, bool Response)
6546 {
6547 return this.PetitionContractResponseAsync(this.GetTrustProvider(ContractId), ContractId, PetitionId, RequestorFullJid, Response, null);
6548 }
6549
6561 public Task PetitionContractResponseAsync(string ContractId, string PetitionId, string RequestorFullJid, bool Response, string ContextXml)
6562 {
6563 return this.PetitionContractResponseAsync(this.GetTrustProvider(ContractId), ContractId, PetitionId, RequestorFullJid, Response, ContextXml);
6564 }
6565
6577 public Task PetitionContractResponseAsync(string Address, string ContractId, string PetitionId, string RequestorFullJid, bool Response)
6578 {
6579 return this.PetitionContractResponseAsync(Address, ContractId, PetitionId, RequestorFullJid, Response, null);
6580 }
6581
6594 public async Task PetitionContractResponseAsync(string Address, string ContractId, string PetitionId, string RequestorFullJid,
6595 bool Response, string ContextXml)
6596 {
6597 StringBuilder Xml = new StringBuilder();
6598
6599 Xml.Append("<petitionContractResponse xmlns='");
6600 Xml.Append(NamespaceSmartContractsCurrent);
6601 Xml.Append("' id='");
6602 Xml.Append(XML.Encode(ContractId));
6603 Xml.Append("' pid='");
6604 Xml.Append(XML.Encode(PetitionId));
6605 Xml.Append("' jid='");
6606 Xml.Append(XML.Encode(RequestorFullJid));
6607 Xml.Append("' response='");
6608 Xml.Append(CommonTypes.Encode(Response));
6609
6610 if (string.IsNullOrEmpty(ContextXml))
6611 Xml.Append("'/>");
6612 else
6613 {
6614 Xml.Append("'>");
6615 Xml.Append(ContextXml);
6616 Xml.Append("</petitionContractResponse>");
6617 }
6618
6619 await this.client.IqSetAsync(Address, Xml.ToString());
6620 }
6621
6622 private async Task PetitionContractMessageHandler(object Sender, MessageEventArgs e)
6623 {
6624 string ContractId = XML.Attribute(e.Content, "id");
6625 string PetitionId = XML.Attribute(e.Content, "pid");
6626 string Purpose = XML.Attribute(e.Content, "purpose");
6627 string From = XML.Attribute(e.Content, "from");
6628 string ClientEndpoint = XML.Attribute(e.Content, "clientEp");
6629 int i = ContractId.IndexOf('@');
6630 LegalIdentity Identity = null;
6631 XmlElement Context = null;
6632
6633 foreach (XmlNode N in e.Content.ChildNodes)
6634 {
6635 if (!(N is XmlElement E))
6636 continue;
6637
6638 if (E.LocalName == "identity" && E.NamespaceURI == e.Content.NamespaceURI)
6639 Identity = LegalIdentity.Parse(E);
6640 else if (!(Context is null))
6641 return;
6642 else
6643 Context = E;
6644 }
6645
6646 if (Identity is null)
6647 return;
6648
6649 if (!this.IsFromTrustProvider(ContractId, e.FromBareJID))
6650 return;
6651
6652 await this.Validate(Identity, false, async (sender2, e2) =>
6653 {
6654 if (e2.Status != IdentityStatus.Valid)
6655 {
6656 await this.client.Error("Invalid identity received and discarded.");
6657
6658 Log.Warning("Invalid identity received and discarded.", this.client.BareJID, e.From,
6659 new KeyValuePair<string, object>("Status", e2.Status));
6660 return;
6661 }
6662
6663 await this.PetitionForContractReceived.Raise(this, new ContractPetitionEventArgs(e, Identity, From, ContractId, PetitionId, Purpose, ClientEndpoint, Context));
6664
6665 }, null);
6666 }
6667
6671 public event EventHandlerAsync<ContractPetitionEventArgs> PetitionForContractReceived = null;
6672
6673 private async Task PetitionContractResponseMessageHandler(object Sender, MessageEventArgs e)
6674 {
6675 string PetitionId = XML.Attribute(e.Content, "pid");
6676 bool Response = XML.Attribute(e.Content, "response", false);
6677 string ClientEndpoint = XML.Attribute(e.Content, "clientEp");
6678 Contract Contract = null;
6679 XmlElement Context = null;
6680
6681 foreach (XmlNode N in e.Content.ChildNodes)
6682 {
6683 if (!(N is XmlElement E))
6684 continue;
6685
6686 if (E.LocalName == "contract" && E.NamespaceURI == e.Content.NamespaceURI)
6687 {
6688 ParsedContract Parsed = await Contract.Parse(E, this, false);
6689 Contract = Parsed?.Contract;
6690 }
6691 else if (!(Context is null))
6692 return;
6693 else
6694 Context = E;
6695 }
6696
6697 if (!Response || string.Compare(e.FromBareJID, Contract?.Provider ?? string.Empty, true) == 0)
6698 await this.PetitionedContractResponseReceived.Raise(this, new ContractPetitionResponseEventArgs(e, Contract, PetitionId, Response, ClientEndpoint, Context));
6699 }
6700
6704 public event EventHandlerAsync<ContractPetitionResponseEventArgs> PetitionedContractResponseReceived = null;
6705
6706 #endregion
6707
6708 #region Attachments
6709
6720 public Task AddLegalIdAttachment(string LegalId, string GetUrl, byte[] Signature, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
6721 {
6722 StringBuilder Xml = new StringBuilder();
6723
6724 Xml.Append("<addAttachment xmlns='");
6725 Xml.Append(NamespaceLegalIdentitiesCurrent);
6726 Xml.Append("' id='");
6727 Xml.Append(XML.Encode(LegalId));
6728 Xml.Append("' getUrl='");
6729 Xml.Append(XML.Encode(GetUrl));
6730 Xml.Append("' s='");
6731 Xml.Append(Convert.ToBase64String(Signature));
6732 Xml.Append("'/>");
6733
6734 return this.client.SendIqSet(this.componentAddress, Xml.ToString(), async (Sender, e) =>
6735 {
6736 LegalIdentity Identity = null;
6737 XmlElement E;
6738
6739 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "identity")
6740 Identity = LegalIdentity.Parse(E);
6741 else
6742 e.Ok = false;
6743
6744 await Callback.Raise(this, new LegalIdentityEventArgs(e, Identity));
6745 }, State);
6746 }
6747
6756 public async Task<LegalIdentity> AddLegalIdAttachmentAsync(string LegalId, string GetUrl, byte[] Signature)
6757 {
6758 TaskCompletionSource<LegalIdentity> Result = new TaskCompletionSource<LegalIdentity>();
6759
6760 await this.AddLegalIdAttachment(LegalId, GetUrl, Signature, (Sender, e) =>
6761 {
6762 if (e.Ok)
6763 Result.TrySetResult(e.Identity);
6764 else
6765 Result.TrySetException(e.StanzaError ?? new Exception("Unable to add attachment."));
6766
6767 return Task.CompletedTask;
6768
6769 }, null);
6770
6771 return await Result.Task;
6772 }
6773
6783 public Task AddContractAttachment(string ContractId, string GetUrl, byte[] Signature, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
6784 {
6785 StringBuilder Xml = new StringBuilder();
6786
6787 Xml.Append("<addAttachment xmlns='");
6788 Xml.Append(NamespaceSmartContractsCurrent);
6789 Xml.Append("' contractId='");
6790 Xml.Append(XML.Encode(ContractId));
6791 Xml.Append("' getUrl='");
6792 Xml.Append(XML.Encode(GetUrl));
6793 Xml.Append("' s='");
6794 Xml.Append(Convert.ToBase64String(Signature));
6795 Xml.Append("'/>");
6796
6797 return this.client.SendIqSet(this.componentAddress, Xml.ToString(), async (Sender, e) =>
6798 {
6799 Contract Contract = null;
6800 XmlElement E;
6801
6802 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "contract")
6803 {
6804 ParsedContract Parsed = await Contract.Parse(E, this, false);
6805 if (Parsed is null)
6806 e.Ok = false;
6807 else
6808 Contract = Parsed.Contract;
6809 }
6810 else
6811 e.Ok = false;
6812
6813 await Callback.Raise(this, new SmartContractEventArgs(e, Contract));
6814 }, State);
6815 }
6816
6824 public async Task<Contract> AddContractAttachmentAsync(string ContractId, string GetUrl, byte[] Signature)
6825 {
6826 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
6827
6828 await this.AddContractAttachment(ContractId, GetUrl, Signature, (Sender, e) =>
6829 {
6830 if (e.Ok)
6831 Result.TrySetResult(e.Contract);
6832 else
6833 Result.TrySetException(e.StanzaError ?? new Exception("Unable to add attachment."));
6834
6835 return Task.CompletedTask;
6836
6837 }, null);
6838
6839 return await Result.Task;
6840 }
6841
6848 public Task<KeyValuePair<string, TemporaryFile>> GetAttachmentAsync(string Url, SignWith SignWith)
6849 {
6850 return this.GetAttachmentAsync(Url, SignWith, 30000);
6851 }
6852
6860 public async Task<KeyValuePair<string, TemporaryFile>> GetAttachmentAsync(string Url, SignWith SignWith, int Timeout)
6861 {
6862 using (HttpClient HttpClient = new HttpClient()
6863 {
6864 Timeout = TimeSpan.FromMilliseconds(Timeout)
6865 })
6866 {
6867 HttpRequestMessage Request;
6868 HttpResponseMessage Response = null;
6869
6870 Request = new HttpRequestMessage()
6871 {
6872 RequestUri = new Uri(Url),
6873 Method = HttpMethod.Get
6874 };
6875
6876 try
6877 {
6878 Response = await HttpClient.SendAsync(Request);
6879
6880 if (Response.StatusCode == System.Net.HttpStatusCode.Unauthorized &&
6881 !(Response.Headers.WwwAuthenticate is null))
6882 {
6883 foreach (AuthenticationHeaderValue Header in Response.Headers.WwwAuthenticate)
6884 {
6885 if (Header.Scheme == "NeuroFoundation.Sign")
6886 {
6887 KeyValuePair<string, string>[] Parameters = CommonTypes.ParseFieldValues(Header.Parameter);
6888 string Realm = null;
6889 string NonceStr = null;
6890 byte[] Nonce = null;
6891
6892 foreach (KeyValuePair<string, string> P in Parameters)
6893 {
6894 switch (P.Key)
6895 {
6896 case "realm":
6897 Realm = P.Value;
6898 break;
6899
6900 case "n":
6901 NonceStr = P.Value;
6902 Nonce = Convert.FromBase64String(NonceStr);
6903 break;
6904 }
6905 }
6906
6907 if (!string.IsNullOrEmpty(Realm) && !string.IsNullOrEmpty(NonceStr))
6908 {
6909 byte[] Signature = await this.SignAsync(Nonce, SignWith);
6910 StringBuilder sb = new StringBuilder();
6911
6912 sb.Append("jid=\"");
6913 sb.Append(this.client.FullJID);
6914 sb.Append("\", realm=\"");
6915 sb.Append(Realm);
6916 sb.Append("\", n=\"");
6917 sb.Append(NonceStr);
6918 sb.Append("\", s=\"");
6919 sb.Append(Convert.ToBase64String(Signature));
6920 sb.Append("\"");
6921
6922 Request.Dispose();
6923 Request = new HttpRequestMessage()
6924 {
6925 RequestUri = new Uri(Url),
6926 Method = HttpMethod.Get
6927 };
6928
6929 Request.Headers.Authorization = new AuthenticationHeaderValue(Header.Scheme, sb.ToString());
6930
6931 Response.Dispose();
6932 Response = null;
6933 Response = await HttpClient.SendAsync(Request);
6934 }
6935 break;
6936 }
6937 }
6938 }
6939
6940 if (!Response.IsSuccessStatusCode)
6941 await Content.Getters.WebGetter.ProcessResponse(Response, Request.RequestUri);
6942
6943 string ContentType = Response.Content.Headers.ContentType.ToString();
6944 TemporaryFile File = new TemporaryFile();
6945 try
6946 {
6947 await Response.Content.CopyToAsync(File);
6948 }
6949 catch (Exception ex)
6950 {
6951 File.Dispose();
6952 File = null;
6953
6954 ExceptionDispatchInfo.Capture(ex).Throw();
6955 }
6956
6957 return new KeyValuePair<string, TemporaryFile>(ContentType, File);
6958 }
6959 finally
6960 {
6961 Request?.Dispose();
6962 Response?.Dispose();
6963 }
6964 }
6965 }
6966
6973 public Task RemoveLegalIdAttachment(string AttachmentId, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
6974 {
6975 StringBuilder Xml = new StringBuilder();
6976
6977 Xml.Append("<removeAttachment xmlns='");
6978 Xml.Append(NamespaceLegalIdentitiesCurrent);
6979 Xml.Append("' attachmentId='");
6980 Xml.Append(XML.Encode(AttachmentId));
6981 Xml.Append("'/>");
6982
6983 return this.client.SendIqSet(this.componentAddress, Xml.ToString(), async (Sender, e) =>
6984 {
6985 LegalIdentity Identity = null;
6986 XmlElement E;
6987
6988 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "identity")
6989 Identity = LegalIdentity.Parse(E);
6990 else
6991 e.Ok = false;
6992
6993 await Callback.Raise(this, new LegalIdentityEventArgs(e, Identity));
6994 }, State);
6995 }
6996
7001 public async Task<LegalIdentity> RemoveLegalIdAttachmentAsync(string AttachmentId)
7002 {
7003 TaskCompletionSource<LegalIdentity> Result = new TaskCompletionSource<LegalIdentity>();
7004
7005 await this.RemoveLegalIdAttachment(AttachmentId, (Sender, e) =>
7006 {
7007 if (e.Ok)
7008 Result.TrySetResult(e.Identity);
7009 else
7010 Result.TrySetException(e.StanzaError ?? new Exception("Unable to remove attachment."));
7011
7012 return Task.CompletedTask;
7013
7014 }, null);
7015
7016 return await Result.Task;
7017 }
7018
7025 public Task RemoveContractAttachment(string AttachmentId, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
7026 {
7027 StringBuilder Xml = new StringBuilder();
7028
7029 Xml.Append("<removeAttachment xmlns='");
7030 Xml.Append(NamespaceSmartContractsCurrent);
7031 Xml.Append("' attachmentId='");
7032 Xml.Append(XML.Encode(AttachmentId));
7033 Xml.Append("'/>");
7034
7035 return this.client.SendIqSet(this.componentAddress, Xml.ToString(), async (Sender, e) =>
7036 {
7037 Contract Contract = null;
7038 XmlElement E;
7039
7040 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "contract")
7041 {
7042 ParsedContract Parsed = await Contract.Parse(E, this, false);
7043 if (Parsed is null)
7044 e.Ok = false;
7045 else
7046 Contract = Parsed.Contract;
7047 }
7048 else
7049 e.Ok = false;
7050
7051 await Callback.Raise(this, new SmartContractEventArgs(e, Contract));
7052 }, State);
7053 }
7054
7059 public async Task<Contract> RemoveContractAttachmentAsync(string AttachmentId)
7060 {
7061 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
7062
7063 await this.RemoveContractAttachment(AttachmentId, (Sender, e) =>
7064 {
7065 if (e.Ok)
7066 Result.TrySetResult(e.Contract);
7067 else
7068 Result.TrySetException(e.StanzaError ?? new Exception("Unable to remove attachment."));
7069
7070 return Task.CompletedTask;
7071
7072 }, null);
7073
7074 return await Result.Task;
7075 }
7076
7077 #endregion
7078
7079 #region Encryption
7080
7091 public (byte[], byte[]) Encrypt(byte[] Message, byte[] Nonce, byte[] RecipientPublicKey, string RecipientPublicKeyName)
7092 {
7093 return this.Encrypt(Message, Nonce, RecipientPublicKey, RecipientPublicKeyName, string.Empty);
7094 }
7095
7107 public (byte[], byte[]) Encrypt(byte[] Message, byte[] Nonce, byte[] RecipientPublicKey, string RecipientPublicKeyName, string RecipientPublicKeyNamespace)
7108 {
7109 IE2eEndpoint LocalEndpoint = this.localKeys.FindLocalEndpoint(RecipientPublicKeyName, RecipientPublicKeyNamespace) ?? throw new NotSupportedException("Unable to find matching local key.");
7110 IE2eEndpoint RemoteEndpoint = LocalEndpoint.CreatePublic(RecipientPublicKey);
7111 byte[] LocalPublicKey = LocalEndpoint.PublicKey;
7112 byte[] Secret = LocalEndpoint.GetSharedSecret(RemoteEndpoint);
7113 byte[] Digest = Hashes.ComputeSHA256Hash(Secret);
7114 byte[] NonceDigest = Hashes.ComputeSHA256Hash(Nonce);
7115 byte[] Key = new byte[16];
7116 byte[] IV = new byte[16];
7117 byte[] Encrypted;
7118 byte[] ToEncrypt;
7119 int i, j, c;
7120
7121 for (i = 0; i < 32; i++)
7122 Secret[i] ^= NonceDigest[i];
7123
7124 i = Message.Length;
7125 c = 0;
7126
7127 do
7128 {
7129 i >>= 7;
7130 c++;
7131 }
7132 while (i > 0);
7133
7134 i = c + Message.Length;
7135 c = (i + 15) & ~0xf;
7136
7137 ToEncrypt = new byte[c];
7138 i = Message.Length;
7139 j = 0;
7140
7141 do
7142 {
7143 ToEncrypt[j] = (byte)(i & 127);
7144 i >>= 7;
7145 if (i > 0)
7146 ToEncrypt[j] |= 0x80;
7147
7148 j++;
7149 }
7150 while (i > 0);
7151
7152 Array.Copy(Message, 0, ToEncrypt, j, Message.Length);
7153 j += Message.Length;
7154
7155 if (j < c)
7156 this.rnd.GetBytes(ToEncrypt, j, c - j);
7157
7158 Array.Copy(Digest, 0, Key, 0, 16);
7159 Array.Copy(Digest, 16, IV, 0, 16);
7160
7161 lock (this.aes)
7162 {
7163 using (ICryptoTransform Aes = this.aes.CreateEncryptor(Key, IV))
7164 {
7165 Encrypted = Aes.TransformFinalBlock(ToEncrypt, 0, c);
7166 }
7167 }
7168
7169 return (Encrypted, LocalPublicKey);
7170 }
7171
7180 public async Task<byte[]> Decrypt(byte[] EncryptedMessage, byte[] SenderPublicKey, byte[] Nonce)
7181 {
7182 IE2eEndpoint LocalEndpoint = await this.GetMatchingLocalKeyAsync();
7183 IE2eEndpoint RemoteEndpoint = LocalEndpoint.CreatePublic(SenderPublicKey);
7184 byte[] Secret = LocalEndpoint.GetSharedSecret(RemoteEndpoint);
7185 byte[] NonceDigest = Hashes.ComputeSHA256Hash(Nonce);
7186 byte[] Key = new byte[16];
7187 byte[] IV = new byte[16];
7188 byte[] Decrypted;
7189 int i, c;
7190 byte b;
7191
7192 for (i = 0; i < 32; i++)
7193 Secret[i] ^= NonceDigest[i];
7194
7195 byte[] Digest = Hashes.ComputeSHA256Hash(Secret);
7196
7197 Array.Copy(Digest, 0, Key, 0, 16);
7198 Array.Copy(Digest, 16, IV, 0, 16);
7199
7200 lock (this.aes)
7201 {
7202 using (ICryptoTransform Aes = this.aes.CreateDecryptor(Key, IV))
7203 {
7204 Decrypted = Aes.TransformFinalBlock(EncryptedMessage, 0, EncryptedMessage.Length);
7205 }
7206 }
7207
7208 i = 0;
7209 c = 0;
7210 do
7211 {
7212 b = Decrypted[i++];
7213 c <<= 7;
7214 c |= b & 0x7f;
7215 }
7216 while ((b & 0x80) != 0);
7217
7218 if (c < 0 || c > Decrypted.Length - i)
7219 throw new InvalidOperationException("Unable to decrypt message.");
7220
7221 byte[] Message = new byte[c];
7222
7223 Array.Copy(Decrypted, i, Message, 0, c);
7224
7225 return Message;
7226 }
7227
7228 #endregion
7229
7230 #region Explicit authorization of access to Legal IDs
7231
7240 public Task AuthorizeAccessToId(string LegalId, string RemoteId, bool Authorized, EventHandlerAsync<IqResultEventArgs> Callback, object State)
7241 {
7242 return this.AuthorizeAccessToId(this.GetTrustProvider(LegalId), LegalId, RemoteId, Authorized, Callback, State);
7243 }
7244
7254 public Task AuthorizeAccessToId(string Address, string LegalId, string RemoteId, bool Authorized, EventHandlerAsync<IqResultEventArgs> Callback, object State)
7255 {
7256 StringBuilder Xml = new StringBuilder();
7257
7258 Xml.Append("<authorizeAccess xmlns='");
7259 Xml.Append(NamespaceLegalIdentitiesCurrent);
7260 Xml.Append("' id='");
7261 Xml.Append(XML.Encode(LegalId));
7262 Xml.Append("' remoteId='");
7263 Xml.Append(XML.Encode(RemoteId));
7264 Xml.Append("' auth='");
7265 Xml.Append(CommonTypes.Encode(Authorized));
7266 Xml.Append("'/>");
7267
7268 return this.client.SendIqSet(Address, Xml.ToString(), Callback, State);
7269 }
7270
7277 public Task AuthorizeAccessToIdAsync(string LegalId, string RemoteId, bool Authorized)
7278 {
7279 return this.AuthorizeAccessToIdAsync(this.GetTrustProvider(LegalId), LegalId, RemoteId, Authorized);
7280 }
7281
7289 public async Task AuthorizeAccessToIdAsync(string Address, string LegalId, string RemoteId, bool Authorized)
7290 {
7291 TaskCompletionSource<bool> Result = new TaskCompletionSource<bool>();
7292
7293 await this.AuthorizeAccessToId(Address, LegalId, RemoteId, Authorized, (Sender, e) =>
7294 {
7295 if (e.Ok)
7296 Result.TrySetResult(true);
7297 else
7298 Result.TrySetException(e.StanzaError ?? new Exception("Unable to authorize access to legal identity."));
7299
7300 return Task.CompletedTask;
7301
7302 }, null);
7303
7304 await Result.Task;
7305 }
7306
7307 #endregion
7308
7309 #region Explicit authorization of access to Contracts
7310
7319 public Task AuthorizeAccessToContract(string ContractId, string RemoteId, bool Authorized, EventHandlerAsync<IqResultEventArgs> Callback, object State)
7320 {
7321 return this.AuthorizeAccessToContract(this.GetTrustProvider(ContractId), ContractId, RemoteId, Authorized, Callback, State);
7322 }
7323
7333 public Task AuthorizeAccessToContract(string Address, string ContractId, string RemoteId, bool Authorized, EventHandlerAsync<IqResultEventArgs> Callback, object State)
7334 {
7335 StringBuilder Xml = new StringBuilder();
7336
7337 Xml.Append("<authorizeAccess xmlns='");
7338 Xml.Append(NamespaceSmartContractsCurrent);
7339 Xml.Append("' id='");
7340 Xml.Append(XML.Encode(ContractId));
7341 Xml.Append("' remoteId='");
7342 Xml.Append(XML.Encode(RemoteId));
7343 Xml.Append("' auth='");
7344 Xml.Append(CommonTypes.Encode(Authorized));
7345 Xml.Append("'/>");
7346
7347 return this.client.SendIqSet(Address, Xml.ToString(), Callback, State);
7348 }
7349
7356 public Task AuthorizeAccessToContractAsync(string ContractId, string RemoteId, bool Authorized)
7357 {
7358 return this.AuthorizeAccessToContractAsync(this.GetTrustProvider(ContractId), ContractId, RemoteId, Authorized);
7359 }
7360
7368 public async Task AuthorizeAccessToContractAsync(string Address, string ContractId, string RemoteId, bool Authorized)
7369 {
7370 TaskCompletionSource<bool> Result = new TaskCompletionSource<bool>();
7371
7372 await this.AuthorizeAccessToContract(Address, ContractId, RemoteId, Authorized, (Sender, e) =>
7373 {
7374 if (e.Ok)
7375 Result.TrySetResult(true);
7376 else
7377 Result.TrySetException(e.StanzaError ?? new Exception("Unable to authorize access to legal identity."));
7378
7379 return Task.CompletedTask;
7380
7381 }, null);
7382
7383 await Result.Task;
7384 }
7385
7386 #endregion
7387
7388 #region Peer-review service providers
7389
7396 {
7397 return this.GetPeerReviewIdServiceProviders(this.componentAddress, Callback, State);
7398 }
7399
7406 public Task GetPeerReviewIdServiceProviders(string ComponentAddress,
7408 {
7409 StringBuilder Xml = new StringBuilder();
7410
7411 Xml.Append("<reviewIdProviders xmlns='");
7412 Xml.Append(NamespaceLegalIdentitiesCurrent);
7413 Xml.Append("'/>");
7414
7415 return this.client.SendIqGet(ComponentAddress, Xml.ToString(), async (Sender, e) =>
7416 {
7417 List<ServiceProviderWithLegalId> Providers = null;
7418 XmlElement E;
7419
7420 if (e.Ok &&
7421 !((E = e.FirstElement) is null) &&
7422 E.LocalName == "providers")
7423 {
7424 Providers = new List<ServiceProviderWithLegalId>();
7425
7426 foreach (XmlNode N in E.ChildNodes)
7427 {
7428 if (N is XmlElement E2 && E2.LocalName == "provider")
7429 {
7430 ServiceProviderWithLegalId Provider = this.ParseServiceProviderWithLegalId(E2);
7431
7432 if (!(Provider is null))
7433 Providers.Add(Provider);
7434 }
7435 }
7436 }
7437 else
7438 e.Ok = false;
7439
7440 await Callback.Raise(this, new ServiceProvidersEventArgs<ServiceProviderWithLegalId>(e, Providers?.ToArray()));
7441
7442 }, State);
7443 }
7444
7445 private ServiceProviderWithLegalId ParseServiceProviderWithLegalId(XmlElement Xml)
7446 {
7447 string Id = null;
7448 string Type = null;
7449 string Name = null;
7450 string IconUrl = null;
7451 string LegalId = null;
7452 int IconWidth = -1;
7453 int IconHeight = -1;
7454 bool External = false;
7455
7456 foreach (XmlAttribute Attr in Xml.Attributes)
7457 {
7458 switch (Attr.Name)
7459 {
7460 case "id":
7461 Id = Attr.Value;
7462 break;
7463
7464 case "type":
7465 Type = Attr.Value;
7466 break;
7467
7468 case "name":
7469 Name = Attr.Value;
7470 break;
7471
7472 case "iconUrl":
7473 IconUrl = Attr.Value;
7474 break;
7475
7476 case "iconWidth":
7477 if (!int.TryParse(Attr.Value, out IconWidth))
7478 return null;
7479 break;
7480
7481 case "iconHeight":
7482 if (!int.TryParse(Attr.Value, out IconHeight))
7483 return null;
7484 break;
7485
7486 case "legalId":
7487 LegalId = Attr.Value;
7488 break;
7489
7490 case "external":
7491 if (!CommonTypes.TryParse(Attr.Value, out External))
7492 return null;
7493 break;
7494 }
7495 }
7496
7497 if (Id is null || Type is null || Name is null)
7498 return null;
7499
7500 if (string.IsNullOrEmpty(IconUrl))
7501 return new ServiceProviderWithLegalId(Id, Type, Name, LegalId, External);
7502 else
7503 {
7504 if (IconWidth < 0 || IconHeight < 0)
7505 return null;
7506
7507 return new ServiceProviderWithLegalId(Id, Type, Name, LegalId, External, IconUrl, IconWidth, IconHeight);
7508 }
7509 }
7510
7515 public Task<ServiceProviderWithLegalId[]> GetPeerReviewIdServiceProvidersAsync()
7516 {
7517 return this.GetPeerReviewIdServiceProvidersAsync(this.componentAddress);
7518 }
7519
7525 public async Task<ServiceProviderWithLegalId[]> GetPeerReviewIdServiceProvidersAsync(string ComponentAddress)
7526 {
7527 TaskCompletionSource<ServiceProviderWithLegalId[]> Providers = new TaskCompletionSource<ServiceProviderWithLegalId[]>();
7528
7529 await this.GetPeerReviewIdServiceProviders(ComponentAddress, (Sender, e) =>
7530 {
7531 if (e.Ok)
7532 Providers.TrySetResult(e.ServiceProviders);
7533 else
7534 Providers.TrySetException(e.StanzaError ?? new Exception("Unable to get service providers."));
7535
7536 return Task.CompletedTask;
7537
7538 }, null);
7539
7540 return await Providers.Task;
7541 }
7542
7543 #endregion
7544
7545 #region Select Peer-review service
7546
7556 public Task SelectPeerReviewService(string Provider, string ServiceId, EventHandlerAsync<IqResultEventArgs> Callback, object State)
7557 {
7558 return this.SelectPeerReviewService(this.componentAddress, Provider, ServiceId, Callback, State);
7559 }
7560
7571 public Task SelectPeerReviewService(string ComponentAddress, string Provider, string ServiceId,
7572 EventHandlerAsync<IqResultEventArgs> Callback, object State)
7573 {
7574 StringBuilder Xml = new StringBuilder();
7575
7576 Xml.Append("<selectReviewService xmlns='");
7577 Xml.Append(NamespaceLegalIdentitiesCurrent);
7578 Xml.Append("' provider='");
7579 Xml.Append(XML.Encode(Provider));
7580 Xml.Append("' serviceId='");
7581 Xml.Append(XML.Encode(ServiceId));
7582 Xml.Append("'/>");
7583
7584 return this.client.SendIqSet(ComponentAddress, Xml.ToString(), Callback, State);
7585 }
7586
7594 public Task SelectPeerReviewServiceAsync(string Provider, string ServiceId)
7595 {
7596 return this.SelectPeerReviewServiceAsync(this.componentAddress, Provider, ServiceId);
7597 }
7598
7607 public async Task SelectPeerReviewServiceAsync(string ComponentAddress, string Provider, string ServiceId)
7608 {
7609 TaskCompletionSource<bool> Providers = new TaskCompletionSource<bool>();
7610
7611 await this.SelectPeerReviewService(ComponentAddress, Provider, ServiceId, (Sender, e) =>
7612 {
7613 if (e.Ok)
7614 Providers.TrySetResult(true);
7615 else
7616 Providers.TrySetException(e.StanzaError ?? new Exception("Unable to select peer review service."));
7617
7618 return Task.CompletedTask;
7619
7620 }, null);
7621
7622 await Providers.Task;
7623 }
7624
7625 #endregion
7626
7627 #region Petition Client URL event
7628
7629 private Task PetitionClientUrlEventHandler(object Sender, MessageEventArgs e)
7630 {
7631 string PetitionId = XML.Attribute(e.Content, "pid");
7632 string Url = XML.Attribute(e.Content, "url");
7633
7634 return this.PetitionClientUrlReceived.Raise(this, new PetitionClientUrlEventArgs(e, PetitionId, Url));
7635 }
7636
7642 public event EventHandlerAsync<PetitionClientUrlEventArgs> PetitionClientUrlReceived;
7643
7644 #endregion
7645 }
7646}
Helps with parsing of commong data types.
Definition: CommonTypes.cs:13
static string Encode(bool x)
Encodes a Boolean for use in XML and other formats.
Definition: CommonTypes.cs:594
static KeyValuePair< string, string >[] ParseFieldValues(string Value)
Parses a set of comma or semicolon-separated field values, optionaly delimited by ' or " characters.
Definition: CommonTypes.cs:472
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Definition: CommonTypes.cs:46
Helps with common XML-related tasks.
Definition: XML.cs:19
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:914
static string Encode(string s)
Encodes a string for use in XML.
Definition: XML.cs:27
static XmlWriterSettings WriterSettings(bool Indent, bool OmitXmlDeclaration)
Gets an XML writer settings object.
Definition: XML.cs:1177
Static class managing loading of XSL resources stored as embedded resources or in content files.
Definition: XSL.cs:15
static void Validate(string ObjectID, XmlDocument Xml, params XmlSchema[] Schemas)
Validates an XML document given a set of XML schemas.
Definition: XSL.cs:118
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1647
static void Notice(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a notice event.
Definition: Log.cs:450
Contains a reference to an attachment assigned to a legal object.
Definition: Attachment.cs:9
string LegalId
Legal ID of uploader of attachment
Definition: Attachment.cs:38
string ContentType
Internet Content Type of binary attachment.
Definition: Attachment.cs:47
string Url
URL to retrieve attachment, if provided.
Definition: Attachment.cs:65
byte[] Signature
Binary signature of the attachment, generated by an approved legal identity of the account-holder....
Definition: Attachment.cs:75
Represents a digital signature on a contract.
Contains the definition of a contract
Definition: Contract.cs:22
static Task< ParsedContract > Parse(XmlDocument Xml)
Validates a contract XML Document, and returns the contract definition in it.
Definition: Contract.cs:397
byte[] ContentSchemaDigest
The hash digest of the schema used to validate the machine-readable contents (ForMachines) of the sma...
Definition: Contract.cs:221
string ForMachinesLocalName
Local name used by the root node of the machine-readable contents of the contract (ForMachines).
Definition: Contract.cs:294
Security.HashFunction ContentSchemaHashFunction
Hash function of the schema used to validate the machine-readable contents (ForMachines) of the smart...
Definition: Contract.cs:231
void EncryptEncryptedParameters(string CreatorJid, IParameterEncryptionAlgorithm Algorithm)
Protects encrypted values, by encrypting the clear text string representations for those that lack en...
Definition: Contract.cs:2194
Parameter[] Parameters
Defined parameters for the smart contract.
Definition: Contract.cs:267
ClientSignature[] ClientSignatures
Client signatures of the contract.
Definition: Contract.cs:309
HumanReadableText[] ForHumans
Human-readable contents of the contract.
Definition: Contract.cs:300
DateTime From
From when the contract is valid (if signed)
Definition: Contract.cs:148
async Task< bool > IsLegallyBinding(bool CheckCurrentTime, ContractsClient Client)
Checks if a contract is legally binding.
Definition: Contract.cs:1421
Attachment[] Attachments
Attachments assigned to the legal identity.
Definition: Contract.cs:318
DateTime To
Until when the contract is valid (if signed)
Definition: Contract.cs:157
string Provider
JID of the Trust Provider hosting the contract
Definition: Contract.cs:84
bool HasTransientParameters
If contract has parameters that are transient.
Definition: Contract.cs:374
XmlElement ForMachines
Machine-readable contents of the contract.
Definition: Contract.cs:276
void Serialize(StringBuilder Xml, bool IncludeNamespace, bool IncludeIdAttribute, bool IncludeClientSignatures, bool IncludeAttachments, bool IncludeStatus, bool IncludeServerSignature, bool IncludeAttachmentReferences)
Serializes the Contract, in normalized form.
Definition: Contract.cs:1542
Role[] Roles
Roles defined in the smart contract.
Definition: Contract.cs:240
ContractParts PartsMode
How parts are defined in the smart contract.
Definition: Contract.cs:249
ContractState State
Contract state
Definition: Contract.cs:121
string ContractId
Contract identity
Definition: Contract.cs:65
string ForMachinesNamespace
Namespace used by the root node of the machine-readable contents of the contract (ForMachines).
Definition: Contract.cs:289
ServerSignature ServerSignature
Server signature attesting to the validity of the contents of the contract.
Definition: Contract.cs:327
bool HasEncryptedParameters
If contract has parameters that require encryption and decryption.
Definition: Contract.cs:354
bool DecryptEncryptedParameters(string CreatorJid, IParameterEncryptionAlgorithm Algorithm)
Protects encrypted values, by encrypting the clear text string representations for those that lack en...
Definition: Contract.cs:2219
Adds support for legal identities, smart contracts and signatures to an XMPP client.
async Task ReadyForApprovalAsync(string Address, string LegalIdentityId)
Marks an Identity as Ready for Approval. Call this after necessary attachments have been added....
Task< bool > ImportKeys(string Xml)
Imports keys
static Uri ContractIdUri(string ContractId)
Contract identity URI.
Task DeleteContract(string Address, string ContractId, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Deletes a contract
Task< bool > ImportKeys(XmlDocument Xml)
Imports keys
async Task< bool > LoadKeys(bool CreateIfNone, ProfilerThread Thread)
Loads keys from the underlying persistence layer.
EventHandlerAsync< PetitionClientUrlEventArgs > PetitionClientUrlReceived
Event raised when a Client URL has been sent to the client as part of a petition process....
byte[] RandomBytes(int Nr)
Creates an array of random bytes.
async Task< LegalIdentity[]> GetContractLegalIdentitiesAsync(string Address, string ContractId, bool Current, bool Historic)
Gets available legal identities related to a contract.
Task CompromisedLegalIdentity(string LegalIdentityId, EventHandlerAsync< LegalIdentityEventArgs > Callback, object State)
Reports as Compromised one of the legal identities of the account, given its ID.
Task PetitionIdentityAsync(string Address, string LegalId, string PetitionId, string Purpose)
Sends a petition to the owner of a legal identity, to access the information in the identity....
async Task< byte[]> Decrypt(byte[] EncryptedMessage, byte[] SenderPublicKey, byte[] Nonce)
Decrypts a message that was aimed at the client using the current keys.
Task GetContractLegalIdentities(string ContractId, EventHandlerAsync< LegalIdentitiesEventArgs > Callback, object State)
Gets available legal identities related to a contract.
Task GetPeerReviewIdServiceProviders(EventHandlerAsync< ServiceProvidersEventArgs< ServiceProviderWithLegalId > > Callback, object State)
Gets available service providers who can help review an ID application.
Task Sign(Stream Data, SignWith SignWith, EventHandlerAsync< SignatureEventArgs > Callback, object State)
Signs binary data with the corresponding private key.
Task GetCreatedContractReferences(string Address, EventHandlerAsync< IdReferencesEventArgs > Callback, object State)
Get references to contracts the account has created.
Task PetitionSignatureResponseAsync(string LegalId, byte[] Content, byte[] Signature, string PetitionId, string RequestorFullJid, bool Response, string ContextXml)
Sends a response to a petition for a signature by the client. When a petition is received,...
async Task< Contract > DeleteContractAsync(string Address, string ContractId)
Deletes a contract
Task GetSignedContractReferences(EventHandlerAsync< IdReferencesEventArgs > Callback, object State)
Get references to contracts the account has signed.
Task CreateContract(XmlElement ForMachines, HumanReadableText[] ForHumans, Role[] Roles, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Creates a new contract.
async Task< string[]> GetCreatedContractReferencesAsync(string Address, int Offset, int MaxCount)
Get references to contracts the account has created.
async Task UpdateContract(string Address, Contract Contract, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Updates a contract
Task SendContractProposal(string ContractId, string Role, string To, string Message)
Sends a contract proposal to a recipient.
Task GetContractLegalIdentities(string Address, string ContractId, EventHandlerAsync< LegalIdentitiesEventArgs > Callback, object State)
Gets available legal identities related to a contract.
async Task< IdentityStatus > ValidateAsync(LegalIdentity Identity, bool ValidateState)
Validates a legal identity.
Task Validate(Contract Contract, EventHandlerAsync< ContractValidationEventArgs > Callback, object State)
Validates a smart contract.
async Task GenerateNewKeys()
Generates new keys for the contracts clients.
Task< ContractsEventArgs > GetCreatedContractsAsync(string Address)
Get contracts the account has created.
Task GetSchema(string Namespace, SchemaDigest Digest, EventHandlerAsync< SchemaEventArgs > Callback, object State)
Gets a schema.
async Task< ContractsEventArgs > GetCreatedContractsAsync(string Address, int Offset, int MaxCount)
Get contracts the account has created.
async Task< LegalIdentity > RemoveLegalIdAttachmentAsync(string AttachmentId)
Removes an attachment from a newly created legal identity.
Task GetCreatedContracts(EventHandlerAsync< ContractsEventArgs > Callback, object State)
Get contracts the account has created.
async Task Validate(Contract Contract, bool ValidateState, EventHandlerAsync< ContractValidationEventArgs > Callback, object State)
Validates a smart contract.
Task< byte[]> GetSchemaAsync(string Address, string Namespace)
Gets a schema.
Task< LegalIdentity > ApplyAsync(Property[] Properties)
Applies for a legal identity to be registered.
Task PetitionContractResponseAsync(string Address, string ContractId, string PetitionId, string RequestorFullJid, bool Response)
Sends a response to a petition to access a smart contract. When a petition for a contract is received...
string KeySettingsPrefix
Prefix for client key runtime settings.
Task SelectPeerReviewServiceAsync(string Provider, string ServiceId)
Selects a service provider for peer review. This needs to be done before requesting the trust provide...
Task ObsoleteLegalIdentity(string Address, string LegalIdentityId, EventHandlerAsync< LegalIdentityEventArgs > Callback, object State)
Obsoletes one of the legal identities of the account, given its ID.
Task PetitionIdentityResponseAsync(string LegalId, string PetitionId, string RequestorFullJid, bool Response)
Sends a response to a petition for information about a legal identity. When a petition is received,...
async Task< LegalIdentity > ApplyAsync(string Address, Property[] Properties)
Applies for a legal identity to be registered.
Task ValidateSignature(string Address, string LegalId, byte[] Data, byte[] Signature, EventHandlerAsync< LegalIdentityEventArgs > Callback, object State)
Validates a signature of binary data.
bool? ValidateSignature(LegalIdentity Identity, byte[] Data, byte[] Signature)
Validates a signature of binary data.
async Task< ContractStatus > ValidateAsync(Contract Contract, bool ValidateState)
Validates a smart contract.
ulong RandomInteger(ulong MaxExclusive)
Creates a random long unsigned integer lower than MaxExclusive .
Task< KeyValuePair< string, TemporaryFile > > GetAttachmentAsync(string Url, SignWith SignWith)
Gets an attachment from a Trust Provider
Task GetSchema(string Namespace, EventHandlerAsync< SchemaEventArgs > Callback, object State)
Gets a schema.
async Task SignContract(string Address, Contract Contract, string Role, bool Transferable, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Signs a contract
Task SelectPeerReviewService(string ComponentAddress, string Provider, string ServiceId, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Selects a service provider for peer review. This needs to be done before requesting the trust provide...
Task< LegalIdentity > ObsoleteLegalIdentityAsync(string LegalIdentityId)
Obsoletes one of the legal identities of the account, given its ID.
Task SendContractProposal(Contract Contract, string Role, string To)
Sends a contract proposal to a recipient. If the contract contains encrypted parameters,...
Task PetitionSignatureResponseAsync(string Address, string LegalId, byte[] Content, byte[] Signature, string PetitionId, string RequestorFullJid, bool Response)
Sends a response to a petition for a signature by the client. When a petition is received,...
Task< ServiceProviderWithLegalId[]> GetPeerReviewIdServiceProvidersAsync()
Gets available service providers who can help review an ID application.
Task Search(SearchFilter[] Filter, EventHandlerAsync< SearchResultEventArgs > Callback, object State)
Performs a search of public smart contracts.
Task ObsoleteLegalIdentity(string LegalIdentityId, EventHandlerAsync< LegalIdentityEventArgs > Callback, object State)
Obsoletes one of the legal identities of the account, given its ID.
SymmetricCipherAlgorithms PreferredEncryptionAlgorithm
Preferred Encryption Algorithm
Task Validate(LegalIdentity Identity, EventHandlerAsync< IdentityValidationEventArgs > Callback, object State)
Validates a legal identity.
ulong RandomInteger()
Creates a random long unsigned integer.
Task PetitionIdentityAsync(string LegalId, string PetitionId, string Purpose)
Sends a petition to the owner of a legal identity, to access the information in the identity....
const string NamespaceSmartContractsNeuroFoundationV1
urn:nf:iot:leg:sc:1.0
async Task< IdApplicationAttributesEventArgs > GetIdApplicationAttributesAsync()
Gets attributes relevant for application for legal identities on the broker.
Task< bool > LoadKeys(bool CreateIfNone)
Loads keys from the underlying persistence layer.
async Task< NetworkIdentity[]> GetContractNetworkIdentitiesAsync(string Address, string ContractId)
Gets available network identities related to a contract.
Task ReadyForApproval(string LegalIdentityId, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Marks an Identity as Ready for Approval. Call this after necessary attachments have been added....
const string NamespaceLegalIdentitiesIeeeV1
urn:ieee:iot:leg:id:1.0
Task< LegalIdentity > GetLegalIdentityAsync(string LegalIdentityId)
Gets legal identity registered with the account.
bool? ValidateSignature(LegalIdentity Identity, Stream Data, byte[] Signature)
Validates a signature of binary data.
Task PetitionSignatureAsync(string Address, string LegalId, byte[] Content, string PetitionId, string Purpose, string ContextXml)
Sends a petition to a third party to request a digital signature of some content. The petition is not...
Task< byte[]> GetSchemaAsync(string Namespace, SchemaDigest Digest)
Gets a schema.
Task< SearchResultEventArgs > SearchAsync(int Offset, int MaxCount, SearchFilter[] Filter)
Performs a search of public smart contracts.
Task CreateContract(string Address, string TemplateId, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Creates a new contract from a template.
Task< ContractStatus > ValidateAsync(Contract Contract)
Validates a smart contract.
Task< ContractsEventArgs > GetCreatedContractsAsync()
Get contracts the account has created.
Task AuthorizeAccessToIdAsync(string LegalId, string RemoteId, bool Authorized)
Authorizes access to (or revokes access to) a Legal ID of the caller.
async Task PetitionSignatureResponseAsync(string Address, string LegalId, byte[] Content, byte[] Signature, string PetitionId, string RequestorFullJid, bool Response, string ContextXml)
Sends a response to a petition for a signature by the client. When a petition is received,...
async Task< LegalIdentity > CompromisedLegalIdentityAsync(string Address, string LegalIdentityId)
Reports as Compromised one of the legal identities of the account, given its ID.
Task PetitionSignatureResponseAsync(string LegalId, byte[] Content, byte[] Signature, string PetitionId, string RequestorFullJid, bool Response)
Sends a response to a petition for a signature by the client. When a petition is received,...
ContractsClient(XmppClient Client, string ComponentAddress, object[] ApprovedSources)
Adds support for legal identities, smart contracts and signatures to an XMPP client.
async Task EnableE2eEncryption(EndpointSecurity E2eEndpoint, bool CreateKeysIfNone)
Enables End-to-End encryption with a separate set of keys.
Task GetMatchingLocalKey(EventHandlerAsync< KeyEventArgs > Callback, object State)
Get the local key that matches the server key.
Task< SchemaReference[]> GetSchemasAsync()
Gets available schemas.
void SetAllowedSources(object[] ApprovedSources)
If access to sensitive methods is only accessible from a set of approved sources.
Task GetContracts(string Address, string[] ContractIds, EventHandlerAsync< ContractsEventArgs > Callback, object State)
Gets a collection of contracts
Task ObsoleteContract(string ContractId, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Obsoletes a contract
async Task< ContractsEventArgs > GetSignedContractsAsync(string Address, int Offset, int MaxCount)
Get contracts the account has signed.
Task GetSchema(string Address, string Namespace, EventHandlerAsync< SchemaEventArgs > Callback, object State)
Gets a schema.
Task GetLegalIdentities(string Address, EventHandlerAsync< LegalIdentitiesEventArgs > Callback, object State)
Gets legal identities registered with the account.
const string NamespaceLegalIdentitiesNeuroFoundationV1
urn:nf:iot:leg:id:1.0
async Task< ServiceProviderWithLegalId[]> GetPeerReviewIdServiceProvidersAsync(string ComponentAddress)
Gets available service providers who can help review an ID application.
Task< bool > HasPrivateKey(LegalIdentity Identity)
Checks if the private key of a legal identity is available. Private keys are required to be able to s...
Task DeleteContract(string ContractId, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Deletes a contract
Task GetCreatedContractReferences(int Offset, int MaxCount, EventHandlerAsync< IdReferencesEventArgs > Callback, object State)
Get references to contracts the account has created.
string ContractKeySettingsPrefix
Prefix for contract key runtime settings.
Task Search(string Address, SearchFilter[] Filter, EventHandlerAsync< SearchResultEventArgs > Callback, object State)
Performs a search of public smart contracts.
Task Search(string Address, int Offset, int MaxCount, SearchFilter[] Filter, EventHandlerAsync< SearchResultEventArgs > Callback, object State)
Performs a search of public smart contracts.
Task< IdentityStatus > ValidateAsync(LegalIdentity Identity)
Validates a legal identity.
Task Sign(byte[] Data, SignWith SignWith, EventHandlerAsync< SignatureEventArgs > Callback, object State)
Signs binary data with the corresponding private key.
Task PetitionContractAsync(string ContractId, string PetitionId, string Purpose)
Sends a petition to the parts of a smart contract, to access the information in the contract....
Task GetSignedContracts(string Address, int Offset, int MaxCount, EventHandlerAsync< ContractsEventArgs > Callback, object State)
Get contracts the account has signed.
Task AuthorizeAccessToId(string LegalId, string RemoteId, bool Authorized, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Authorizes access to (or revokes access to) a Legal ID of the caller.
async Task< LegalIdentity > AddLegalIdAttachmentAsync(string LegalId, string GetUrl, byte[] Signature)
Adds an attachment to a newly created legal identity.
DateTime KeysTimestamp
Timestamps of current keys used for signatures.
Task< LegalIdentity[]> GetContractLegalIdentitiesAsync(string Address, string ContractId)
Gets available legal identities related to a contract.
async Task< Contract > CreateContractAsync(string Address, string TemplateId, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate)
Creates a new contract from a template.
async Task EnableE2eEncryption(bool UseLocalKeys, bool CreateKeysIfNone)
Defines if End-to-End encryption should use the keys used by the contracts client to perform signatur...
async Task PetitionIdentityResponseAsync(string Address, string LegalId, string PetitionId, string RequestorFullJid, bool Response, string ContextXml)
Sends a response to a petition for information about a legal identity. When a petition is received,...
Task CreateContract(string Address, XmlElement ForMachines, HumanReadableText[] ForHumans, Role[] Roles, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Creates a new contract.
Task SignContract(Contract Contract, string Role, bool Transferable, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Signs a contract
Task GetCreatedContractReferences(string Address, int Offset, int MaxCount, EventHandlerAsync< IdReferencesEventArgs > Callback, object State)
Get references to contracts the account has created.
Task< string > GetLatestApprovedLegalId()
Gets the latest approved Legal ID.
Task AddContractAttachment(string ContractId, string GetUrl, byte[] Signature, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Adds an attachment to a proposed or approved contract before it is being signed.
Task< LegalIdentity > CompromisedLegalIdentityAsync(string LegalIdentityId)
Reports as Compromised one of the legal identities of the account, given its ID.
const SymmetricCipherAlgorithms DefaultCipherAlgorithm
Default cipher name for encrypted parameters, if an algorithm is not explicitly defined.
Task GetContractNetworkIdentities(string Address, string ContractId, EventHandlerAsync< NetworkIdentitiesEventArgs > Callback, object State)
Gets available network identities related to a contract.
Task GetContractLegalIdentities(string ContractId, bool Current, bool Historic, EventHandlerAsync< LegalIdentitiesEventArgs > Callback, object State)
Gets available legal identities related to a contract.
Task SelectPeerReviewService(string Provider, string ServiceId, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Selects a service provider for peer review. This needs to be done before requesting the trust provide...
async Task Apply(string Address, Property[] Properties, EventHandlerAsync< LegalIdentityEventArgs > Callback, object State)
Applies for a legal identity to be registered.
Task< Contract > GetContractAsync(string ContractId)
Gets a contract
async Task CreateContract(string Address, string TemplateId, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate, IParameterEncryptionAlgorithm Algorithm, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Creates a new contract from a template.
Task AuthorizeAccessToId(string Address, string LegalId, string RemoteId, bool Authorized, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Authorizes access to (or revokes access to) a Legal ID of the caller.
async Task< IE2eEndpoint > GetMatchingLocalKeyAsync(string Address)
Get the local key that matches a given server key.
string GetTrustProvider(string EntityId)
Gets the trust provider hosting an entity with a given ID, in the form of LocalId@Provider.
async Task< Contract > CreateContractAsync(string Address, XmlElement ForMachines, HumanReadableText[] ForHumans, Role[] Roles, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate)
Creates a new contract.
Task GetIdApplicationAttributes(EventHandlerAsync< IdApplicationAttributesEventArgs > Callback, object State)
Gets attributes relevant for application for legal identities on the broker.
Task PetitionPeerReviewIDAsync(string Address, string LegalId, LegalIdentity Identity, string PetitionId, string Purpose)
Sends a petition to a third party to peer review a new legal identity. The petition is not guaranteed...
async Task< byte[]> SignAsync(string Address, Stream Data, SignWith SignWith)
Signs binary data with the corresponding private key.
async Task ExportKeys(XmlWriter Output)
Exports Keys to XML.
Task PetitionContractAsync(string Address, string ContractId, string PetitionId, string Purpose)
Sends a petition to the parts of a smart contract, to access the information in the contract....
Task< NetworkIdentity[]> GetContractNetworkIdentitiesAsync(string ContractId)
Gets available network identities related to a contract.
Task< LegalIdentity[]> GetContractLegalIdentitiesAsync(string ContractId, bool Current, bool Historic)
Gets available legal identities related to a contract.
async Task< Contract > GetContractAsync(string Address, string ContractId)
Gets a contract
async Task< bool > ImportKeys(XmlElement Xml)
Imports keys
async Task PetitionContractAsync(string Address, string ContractId, string PetitionId, string Purpose, string ContextXml)
Sends a petition to the parts of a smart contract, to access the information in the contract....
Task< IE2eEndpoint > GetServerPublicKeyAsync()
Gets the server public key.
async Task< Contract > ObsoleteContractAsync(string Address, string ContractId)
Obsoletes a contract
Task GetContract(string Address, string ContractId, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Gets a contract
async Task< LegalIdentity[]> GetLegalIdentitiesAsync(string Address)
Gets legal identities registered with the account.
async Task< ContractsEventArgs > GetContractsAsync(string[] ContractIds)
Gets a collection of contracts
Task< byte[]> SignAsync(byte[] Data, SignWith SignWith)
Signs binary data with the corresponding private key.
ContractsClient(XmppClient Client, string ComponentAddress)
Adds support for legal identities, smart contracts and signatures to an XMPP client.
async Task PetitionContractResponseAsync(string Address, string ContractId, string PetitionId, string RequestorFullJid, bool Response, string ContextXml)
Sends a response to a petition to access a smart contract. When a petition for a contract is received...
Task GetSchemas(EventHandlerAsync< SchemaReferencesEventArgs > Callback, object State)
Gets available schemas.
async Task SendContractProposal(Contract Contract, string Role, string To, string Message)
Sends a contract proposal to a recipient. If the contract contains encrypted parameters,...
static string ContractIdUriString(string ContractId)
Contract identity URI, as a string.
Task< string[]> GetSignedContractReferencesAsync(int Offset, int MaxCount)
Get references to contracts the account has signed.
Task GetSignedContracts(string Address, EventHandlerAsync< ContractsEventArgs > Callback, object State)
Get contracts the account has signed.
const string NamespaceOnboarding
http://waher.se/schema/Onboarding/v1.xsd
async Task SelectPeerReviewServiceAsync(string ComponentAddress, string Provider, string ServiceId)
Selects a service provider for peer review. This needs to be done before requesting the trust provide...
static readonly string[] NamespacesSmartContracts
Namespaces supported for smart contracts.
Task GetCreatedContracts(string Address, int Offset, int MaxCount, EventHandlerAsync< ContractsEventArgs > Callback, object State)
Get contracts the account has created.
async Task AuthorizeAccessToContractAsync(string Address, string ContractId, string RemoteId, bool Authorized)
Authorizes access to (or revokes access to) a Contract of which the caller is part and can access.
Task PetitionContractResponseAsync(string ContractId, string PetitionId, string RequestorFullJid, bool Response)
Sends a response to a petition to access a smart contract. When a petition for a contract is received...
Task AuthorizeAccessToContract(string Address, string ContractId, string RemoteId, bool Authorized, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Authorizes access to (or revokes access to) a Contract of which the caller is part and can access.
Task ReadyForApprovalAsync(string LegalIdentityId)
Marks an Identity as Ready for Approval. Call this after necessary attachments have been added....
async Task< LegalIdentity > AddPeerReviewIDAttachment(LegalIdentity Identity, LegalIdentity ReviewerLegalIdentity, byte[] PeerSignature)
Adds an attachment to a legal identity with information about a peer review of the identity.
async Task< KeyValuePair< string, TemporaryFile > > GetAttachmentAsync(string Url, SignWith SignWith, int Timeout)
Gets an attachment from a Trust Provider
Task< Contract > UpdateContractAsync(Contract Contract)
Updates a contract
Task< LegalIdentity[]> GetLegalIdentitiesAsync()
Gets legal identities registered with the account.
async Task< string > ExportKeys()
Exports Keys to XML.
Task GetLegalIdentity(string Address, string LegalIdentityId, EventHandlerAsync< LegalIdentityEventArgs > Callback, object State)
Gets information about a legal identity given its ID.
Task< Contract > SignContractAsync(Contract Contract, string Role, bool Transferable)
Signs a contract
Task GetContract(string ContractId, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Gets a contract
async Task< Contract > RemoveContractAttachmentAsync(string AttachmentId)
Removes an attachment from a proposed or approved contract before it is being signed.
Task UpdateContract(Contract Contract, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Updates a contract
async Task AuthorizeAccessToIdAsync(string Address, string LegalId, string RemoteId, bool Authorized)
Authorizes access to (or revokes access to) a Legal ID of the caller.
Task AuthorizeAccessToContract(string ContractId, string RemoteId, bool Authorized, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Authorizes access to (or revokes access to) a Contract of which the caller is part and can access.
Task< SearchResultEventArgs > SearchAsync(SearchFilter[] Filter)
Performs a search of public smart contracts.
Task PetitionIdentityResponseAsync(string LegalId, string PetitionId, string RequestorFullJid, bool Response, string ContextXml)
Sends a response to a petition for information about a legal identity. When a petition is received,...
Task< LegalIdentity[]> GetContractLegalIdentitiesAsync(string ContractId)
Gets available legal identities related to a contract.
override void Dispose()
Disposes of the extension.
Task RemoveContractAttachment(string AttachmentId, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Removes an attachment from a proposed or approved contract before it is being signed.
Task GetSchemas(string Address, EventHandlerAsync< SchemaReferencesEventArgs > Callback, object State)
Gets available schemas.
async Task< SearchResultEventArgs > SearchAsync(string Address, int Offset, int MaxCount, SearchFilter[] Filter)
Performs a search of public smart contracts.
Task< IE2eEndpoint > GetMatchingLocalKeyAsync()
Get the local key that matches the server key.
Task< LegalIdentity > ValidateSignatureAsync(string LegalId, byte[] Data, byte[] Signature)
Validates a signature of binary data.
async Task< byte[]> SignAsync(string Address, byte[] Data, SignWith SignWith)
Signs binary data with the corresponding private key.
Task< string[]> GetSignedContractReferencesAsync()
Get references to contracts the account has signed.
Task AddLegalIdAttachment(string LegalId, string GetUrl, byte[] Signature, EventHandlerAsync< LegalIdentityEventArgs > Callback, object State)
Adds an attachment to a newly created legal identity.
Task GetSchema(string Address, string Namespace, SchemaDigest Digest, EventHandlerAsync< SchemaEventArgs > Callback, object State)
Gets a schema.
async Task CreateContract(string Address, XmlElement ForMachines, HumanReadableText[] ForHumans, Role[] Roles, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate, IParameterEncryptionAlgorithm Algorithm, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Creates a new contract.
async Task Sign(string Address, byte[] Data, SignWith SignWith, EventHandlerAsync< SignatureEventArgs > Callback, object State)
Signs binary data with the corresponding private key.
int RandomInteger(int MinInclusive, int MaxInclusive)
Creates a random number in a range.
Task PetitionSignatureAsync(string LegalId, byte[] Content, string PetitionId, string Purpose)
Sends a petition to a third party to request a digital signature of some content. The petition is not...
Task< SearchResultEventArgs > SearchAsync(string Address, SearchFilter[] Filter)
Performs a search of public smart contracts.
Task RemoveLegalIdAttachment(string AttachmentId, EventHandlerAsync< LegalIdentityEventArgs > Callback, object State)
Removes an attachment from a newly created legal identity.
Task Search(int Offset, int MaxCount, SearchFilter[] Filter, EventHandlerAsync< SearchResultEventArgs > Callback, object State)
Performs a search of public smart contracts.
Task CreateContract(string TemplateId, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Creates a new contract from a template.
Task GetCreatedContracts(int Offset, int MaxCount, EventHandlerAsync< ContractsEventArgs > Callback, object State)
Get contracts the account has created.
Task EnableE2eEncryption(bool UseLocalKeys)
Defines if End-to-End encryption should use the keys used by the contracts client to perform signatur...
Task PetitionPeerReviewIDAsync(string LegalId, LegalIdentity Identity, string PetitionId, string Purpose)
Sends a petition to a third party to peer review a new legal identity. The petition is not guaranteed...
Task GetSignedContractReferences(string Address, int Offset, int MaxCount, EventHandlerAsync< IdReferencesEventArgs > Callback, object State)
Get references to contracts the account has signed.
Task GetLegalIdentities(EventHandlerAsync< LegalIdentitiesEventArgs > Callback, object State)
Gets legal identities registered with the account.
Task< string[]> GetCreatedContractReferencesAsync(string Address)
Get references to contracts the account has created.
async Task< LegalIdentity > ObsoleteLegalIdentityAsync(string Address, string LegalIdentityId)
Obsoletes one of the legal identities of the account, given its ID.
async Task GetContracts(string[] ContractIds, EventHandlerAsync< ContractsEventArgs > Callback, object State)
Gets a collection of contracts
Task GetSignedContracts(EventHandlerAsync< ContractsEventArgs > Callback, object State)
Get contracts the account has signed.
Task ValidateSignature(string LegalId, byte[] Data, byte[] Signature, EventHandlerAsync< LegalIdentityEventArgs > Callback, object State)
Validates a signature of binary data.
Task GetPeerReviewIdServiceProviders(string ComponentAddress, EventHandlerAsync< ServiceProvidersEventArgs< ServiceProviderWithLegalId > > Callback, object State)
Gets available service providers who can help review an ID application.
Task< Contract > CreateContractAsync(XmlElement ForMachines, HumanReadableText[] ForHumans, Role[] Roles, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate)
Creates a new contract.
override string[] Extensions
Implemented extensions.
Task SendContractProposal(string ContractId, string Role, string To)
Sends a contract proposal to a recipient.
Task< Contract > DeleteContractAsync(string ContractId)
Deletes a contract
async Task< bool > CanSignAs(CaseInsensitiveString ReferenceId, CaseInsensitiveString SignatoryId)
Checks if an identity can sign for another reference identity (i.e. the old might have been obsoleted...
Task EnableE2eEncryption(EndpointSecurity E2eEndpoint)
Enables End-to-End encryption with a separate set of keys.
async Task GetServerPublicKey(string Address, EventHandlerAsync< KeyEventArgs > Callback, object State)
Gets the server public key.
async Task< Contract > SignContractAsync(string Address, Contract Contract, string Role, bool Transferable)
Signs a contract
Task GetLegalIdentity(string LegalIdentityId, EventHandlerAsync< LegalIdentityEventArgs > Callback, object State)
Gets information about a legal identity given its ID.
Task< ContractsEventArgs > GetSignedContractsAsync(int Offset, int MaxCount)
Get contracts the account has signed.
async Task< Contract > AddContractAttachmentAsync(string ContractId, string GetUrl, byte[] Signature)
Adds an attachment to a proposed or approved contract before it is being signed.
Task< byte[]> SignAsync(Stream Data, SignWith SignWith)
Signs binary data with the corresponding private key.
Task PetitionContractResponseAsync(string ContractId, string PetitionId, string RequestorFullJid, bool Response, string ContextXml)
Sends a response to a petition to access a smart contract. When a petition for a contract is received...
Task GetServerPublicKey(EventHandlerAsync< KeyEventArgs > Callback, object State)
Gets the server public key.
async Task< bool > HasPrivateKey(string IdentityId)
Checks if the private key of a legal identity is available. Private keys are required to be able to s...
async Task< IE2eEndpoint > GetServerPublicKeyAsync(string Address)
Gets the server public key.
Task< string[]> GetCreatedContractReferencesAsync(int Offset, int MaxCount)
Get references to contracts the account has created.
Task< string[]> GetCreatedContractReferencesAsync()
Get references to contracts the account has created.
void SetPreferredEncryptionAlgorithm(SymmetricCipherAlgorithms Algorithm, bool Lock)
Sets the preferred encryption algorithm.
Task< ContractsEventArgs > GetSignedContractsAsync()
Get contracts the account has signed.
async Task Sign(string Address, Stream Data, SignWith SignWith, EventHandlerAsync< SignatureEventArgs > Callback, object State)
Signs binary data with the corresponding private key.
async Task< string > GetLatestApprovedLegalId(byte[] PublicKey)
Gets the (latest) approved Legal ID whose public key matches PublicKey .
async Task< LegalIdentity > ValidateSignatureAsync(string Address, string LegalId, byte[] Data, byte[] Signature)
Validates a signature of binary data.
Task Apply(Property[] Properties, EventHandlerAsync< LegalIdentityEventArgs > Callback, object State)
Applies for a legal identity to be registered.
async Task< Contract > UpdateContractAsync(string Address, Contract Contract)
Updates a contract
async Task Validate(LegalIdentity Identity, bool ValidateState, EventHandlerAsync< IdentityValidationEventArgs > Callback, object State)
Validates a legal identity.
Task ReadyForApproval(string Address, string LegalIdentityId, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Marks an Identity as Ready for Approval. Call this after necessary attachments have been added....
Task AuthorizeAccessToContractAsync(string ContractId, string RemoteId, bool Authorized)
Authorizes access to (or revokes access to) a Contract of which the caller is part and can access.
Task GetSignedContracts(int Offset, int MaxCount, EventHandlerAsync< ContractsEventArgs > Callback, object State)
Get contracts the account has signed.
Task GetSignedContractReferences(string Address, EventHandlerAsync< IdReferencesEventArgs > Callback, object State)
Get references to contracts the account has signed.
async Task PetitionIdentityAsync(string Address, string LegalId, string PetitionId, string Purpose, string ContextXml)
Sends a petition to the owner of a legal identity, to access the information in the identity....
const string NamespaceSmartContractsCurrent
Current namespce for smart contracts.
Task< Contract > CreateContractAsync(string TemplateId, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate)
Creates a new contract from a template.
Task< byte[]> GetSchemaAsync(string Namespace)
Gets a schema.
const string NamespaceSmartContractsIeeeV1
urn:ieee:iot:leg:sc:1.0
async Task< LegalIdentity > GetLegalIdentityAsync(string Address, string LegalIdentityId)
Gets legal identity registered with the account.
static string LegalIdUriString(string LegalId)
Legal identity URI, as a string.
Task ObsoleteContract(string Address, string ContractId, EventHandlerAsync< SmartContractEventArgs > Callback, object State)
Obsoletes a contract
Task CompromisedLegalIdentity(string Address, string LegalIdentityId, EventHandlerAsync< LegalIdentityEventArgs > Callback, object State)
Reports as Compromised one of the legal identities of the account, given its ID.
void SetKeySettingsInstance(string InstanceName, bool Locked)
Sets the key settings instance name.
Task< Contract > ObsoleteContractAsync(string ContractId)
Obsoletes a contract
Task GetCreatedContractReferences(EventHandlerAsync< IdReferencesEventArgs > Callback, object State)
Get references to contracts the account has created.
Task< ContractsEventArgs > GetCreatedContractsAsync(int Offset, int MaxCount)
Get contracts the account has created.
static readonly string[] NamespacesLegalIdentities
Namespaces supported for legal identities.
async Task< ContractsEventArgs > GetContractsAsync(string Address, string[] ContractIds)
Gets a collection of contracts
const string NamespaceLegalIdentitiesCurrent
Current namespace for legal identities.
async Task SendContractProposal(string ContractId, string Role, string To, string Message, byte[] Key, SymmetricCipherAlgorithms KeyAlgorithm)
Sends a contract proposal to a recipient.
async Task GetMatchingLocalKey(string Address, EventHandlerAsync< KeyEventArgs > Callback, object State)
Get the local key that matches a given server key.
async Task< string[]> GetSignedContractReferencesAsync(string Address, int Offset, int MaxCount)
Get references to contracts the account has signed.
static Uri LegalIdUri(string LegalId)
Legal identity URI.
Task PetitionIdentityResponseAsync(string Address, string LegalId, string PetitionId, string RequestorFullJid, bool Response)
Sends a response to a petition for information about a legal identity. When a petition is received,...
async Task< byte[]> GetSchemaAsync(string Address, string Namespace, SchemaDigest Digest)
Gets a schema.
Task PetitionSignatureAsync(string Address, string LegalId, byte[] Content, string PetitionId, string Purpose)
Sends a petition to a third party to request a digital signature of some content. The petition is not...
Task GetCreatedContracts(string Address, EventHandlerAsync< ContractsEventArgs > Callback, object State)
Get contracts the account has created.
Task GetContractLegalIdentities(string Address, string ContractId, bool Current, bool Historic, EventHandlerAsync< LegalIdentitiesEventArgs > Callback, object State)
Gets available legal identities related to a contract.
async Task< SchemaReference[]> GetSchemasAsync(string Address)
Gets available schemas.
Task GetContractNetworkIdentities(string ContractId, EventHandlerAsync< NetworkIdentitiesEventArgs > Callback, object State)
Gets available network identities related to a contract.
Event arguments for callback methods to ID Application attributes queries.
IE2eEndpoint Key
Public key of server endpoint.
Event arguments for events where a client URL needs to be displayed when performing a petition.
override async Task< HumanReadableElement > IsWellDefined()
Checks if the element is well-defined.
Definition: Blocks.cs:25
HumanReadableText[] Descriptions
Discriptions of the object, in different languages.
Contains a network identity related to a legal identity
Implements parameter encryption using symmetric ciphers avaialble through IE2eSymmetricCipher in the ...
static Task< ParameterEncryptionAlgorithm > Create(SymmetricCipherAlgorithms Algorithm, ContractsClient Client)
Implements parameter encryption using symmetric ciphers avaialble through IE2eSymmetricCipher in the ...
Abstract base class for contractual parameters
Definition: Parameter.cs:17
abstract void Populate(Variables Variables)
Populates a variable collection with the value of the parameter.
abstract string StringValue
String representation of value.
Definition: Parameter.cs:110
Task< bool > IsParameterValid(Variables Variables)
Checks if the parameter value is valid.
Definition: Parameter.cs:164
byte[] ProtectedValue
Protected value, in case Protection is not equal to ProtectionLevel.Normal.
Definition: Parameter.cs:72
abstract string ParameterType
Parameter type name, corresponding to the local name of the parameter element in XML.
Definition: Parameter.cs:139
abstract object ObjectValue
Parameter value.
Definition: Parameter.cs:104
void Serialize(StringBuilder Xml)
Serializes the parameter, in normalized form.
Definition: Parameter.cs:146
ProtectionLevel Protection
Level of confidentiality of the information provided by the parameter.
Definition: Parameter.cs:62
Contains information about a parsed contract.
Class defining a part in a contract
Definition: Part.cs:30
string LegalId
Legal identity of part
Definition: Part.cs:38
string Role
Role of the part in the contract
Definition: Part.cs:57
Class defining a role
Definition: Role.cs:7
HashFunction Function
Hash Function used to calculate the digest.
Definition: SchemaDigest.cs:42
byte[] Digest
Hash Digest of schema file
Definition: SchemaDigest.cs:37
References a XML Schema used for validating machine-readable contents in smart contracts.
Abstract base class for Smart Contract Search filters.
Definition: SearchFilter.cs:9
Abstract base class of signatures
Definition: Signature.cs:10
byte[] DigitalSignature
Digital Signature
Definition: Signature.cs:27
Event arguments for responses to IQ queries.
bool Ok
If the response is an OK result response (true), or an error response (false).
object State
State object passed to the original request.
XmppException StanzaError
Any stanza error returned.
XmlElement FirstElement
First child element of the Response element.
Event arguments for message events.
string Id
ID attribute of message stanza.
string From
From where the message was received.
string FromBareJID
Bare JID of resource sending the message.
bool Ok
If the response is an OK result response (true), or an error response (false).
string To
To whom the message was sent.
bool UsesE2eEncryption
If end-to-end encryption was used in the request.
XmlElement Content
Content of the message. For messages that are processed by registered message handlers,...
XmppException StanzaError
Any stanza error returned.
Class managing HTTP File uploads, as defined in XEP-0363.
Task< HttpFileUploadEventArgs > RequestUploadSlotAsync(string FileName, string ContentType, long ContentSize)
Uploads a file to the upload component.
Event arguments for HTTP File Upload callback methods.
async Task PUT(byte[] Content, string ContentType, int Timeout)
Uploads file content to the server.
Abstract base class for End-to-End encryption schemes.
Definition: E2eEndpoint.cs:12
Abstract base class for Elliptic Curve endpoints.
RSA / AES-256 hybrid cipher.
Definition: RsaEndpoint.cs:13
override bool Verify(byte[] Data, byte[] Signature)
Verifies a signature.
Definition: RsaEndpoint.cs:406
Class managing end-to-end encryption.
static bool TryGetEndpoint(string LocalName, string Namespace, out IE2eEndpoint Endpoint)
Tries to get an existing endpoint, given its qualified name.
static IE2eEndpoint[] CreateEndpoints(int DesiredSecurityStrength, int MinSecurityStrength, int MaxSecurityStrength)
Creates a set of endpoints within a range of security strengths.
Task SendMessage(XmppClient Client, E2ETransmission E2ETransmission, QoSLevel QoS, MessageType Type, string Id, string To, string CustomXml, string Body, string Subject, string Language, string ThreadId, string ParentThreadId, EventHandlerAsync< DeliveryEventArgs > DeliveryCallback, object State)
Sends an XMPP message to an endpoint.
Maintains information about an item in the roster.
Definition: RosterItem.cs:75
string LastPresenceFullJid
Full JID of last resource sending online presence.
Definition: RosterItem.cs:343
The addressed JID or item requested cannot be found; the associated error type SHOULD be "cancel".
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Definition: XmppClient.cs:59
bool UnregisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool RemoveNamespaceAsClientFeature)
Unregisters a Message handler.
Definition: XmppClient.cs:2855
static string GetDomain(string JID)
Gets the domain part of a JID.
Definition: XmppClient.cs:6929
static string GetBareJID(string JID)
Gets the Bare JID from a JID, which may be a Full JID.
Definition: XmppClient.cs:6901
void RegisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool PublishNamespaceAsClientFeature)
Registers a Message handler.
Definition: XmppClient.cs:2828
Task< uint > SendIqGet(string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ Get request.
Definition: XmppClient.cs:3559
Base class for XMPP Extensions.
XmppClient client
XMPP Client used by the extension.
Task Exception(Exception Exception)
Called to inform the viewer of an exception state.
XmppClient Client
XMPP Client.
Represents a case-insensitive string.
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:19
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:717
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:247
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:95
This filter selects objects that conform to all child-filters provided.
Definition: FilterAnd.cs:10
This filter selects objects that have a named field equal to a given value.
Base class for all filter classes.
Definition: Filter.cs:15
Implements an in-memory cache.
Definition: Cache.cs:15
void Dispose()
IDisposable.Dispose
Definition: Cache.cs:74
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:14
static bool UnregisterSingleton(object Object, params object[] Arguments)
Unregisters a singleton instance of a type.
Definition: Types.cs:1558
static object Instantiate(Type Type, params object[] Arguments)
Returns an instance of the type Type . If one needs to be created, it is. If the constructor requires...
Definition: Types.cs:1353
static void RegisterSingleton(object Object, params object[] Arguments)
Registers a singleton instance of a type.
Definition: Types.cs:1547
Class that keeps track of events and timing for one thread.
ProfilerThread CreateSubThread(string Name, ProfilerThreadType Type)
Creates a new profiler thread.
void NewState(string State)
Thread changes state.
Static class managing persistent settings.
static Task< int > DeleteWhereKeyLikeAsync(string Key, string Wildcard)
Deletes available settings, matching a search filter.
static Task< Dictionary< string, object > > GetWhereKeyLikeAsync(string Key, string Wildcard)
Gets available settings, matching a search filter.
static async Task< string > GetAsync(string Key, string DefaultValue)
Gets a string-valued setting.
static async Task< bool > SetAsync(string Key, string Value)
Sets a string-valued setting.
Class managing the contents of a temporary file. When the class is disposed, the temporary file is de...
override void Dispose(bool disposing)
Disposes of the object, and deletes the temporary file.
Collection of variables.
Definition: Variables.cs:25
Static class containing methods that can be used to make sure calls are made from appropriate locatio...
Definition: Assert.cs:15
static void CallFromSource(params string[] Sources)
Makes sure the call is made from one of the listed sources.
Definition: Assert.cs:39
Abstract base class for elliptic curves.
virtual void Export(XmlWriter Output)
Exports the curve parameters to XML.
Contains methods for simple hash calculations.
Definition: Hashes.cs:59
static byte[] ComputeSHA256Hash(byte[] Data)
Computes the SHA-256 hash of a block of binary data.
Definition: Hashes.cs:348
byte[] Encrypt(string ParameterName, string ParameterType, uint ParameterIndex, string CreatorJid, byte[] ContractNonce, string ClearText)
Encrypts a parameter value.
SymmetricCipherAlgorithms Algorithm
Symmetric Cipher Algorithm used to encrypt parameters.
Abstract base class for End-to-End encryption schemes.
Definition: IE2eEndpoint.cs:12
byte[] Sign(byte[] Data)
Signs binary data using the local private key.
byte[] GetSharedSecret(IE2eEndpoint RemoteEndpoint)
Gets a shared secret
byte[] PublicKey
Remote public key.
Definition: IE2eEndpoint.cs:50
IE2eEndpoint CreatePrivate(byte[] Secret)
Creates a new endpoint given a private key.
IE2eEndpoint CreatePublic(byte[] PublicKey)
Creates a new endpoint given a public key.
string LocalName
Local name of the E2E endpoint
Definition: IE2eEndpoint.cs:25
delegate Task EventHandlerAsync(object Sender, EventArgs e)
Asynchronous version of EventArgs.
IdentityState
Lists recognized legal identity states.
SignWith
Options on what keys to use when signing data.
Definition: Enumerations.cs:84
ContractParts
How the parts of the contract are defined.
Definition: Part.cs:9
ContractStatus
Validation Status of smart contract
ProtectionLevel
Parameter protection levels
ContractVisibility
Visibility types for contracts.
Definition: Enumerations.cs:58
ContractState
Recognized contract states
Definition: Enumerations.cs:9
IdentityStatus
Validation Status of legal identity
SymmetricCipherAlgorithms
Enumeration of symmetric cipher algorithms available in the library.
QoSLevel
Quality of Service Level for asynchronous messages. Support for QoS Levels must be supported by the r...
Definition: QoSLevel.cs:8
MessageType
Type of message received.
Definition: MessageType.cs:7
E2ETransmission
End-to-end encryption mode.
ProfilerThreadType
Type of profiler thread.
HashFunction
Hash method enumeration.
Definition: Hashes.cs:28
Represents a duration value, as defined by the xsd:duration data type: http://www....
Definition: Duration.cs:13
override string ToString()
Definition: Duration.cs:471