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;
3using System.IO;
4using System.Linq;
5using System.Net.Http;
6using System.Net.Http.Headers;
7using System.Runtime.ExceptionServices;
9using System.Text;
10using System.Threading.Tasks;
11using System.Xml;
12using System.Xml.Schema;
13using Waher.Content;
17using Waher.Events;
38using Waher.Script;
39using Waher.Security;
41
43{
51 {
55 public const string NamespaceLegalIdentitiesIeeeV1 = "urn:ieee:iot:leg:id:1.0";
56
60 public const string NamespaceLegalIdentitiesNeuroFoundationV1 = "urn:nf:iot:leg:id:1.0";
61
66
71
76 public const string E2eKeySemaphoreName = "XMPP.E2E";
77
81 public static readonly string[] NamespacesLegalIdentities = new string[]
82 {
85 };
86
92 public static bool IsNamespaceLegalIdentity(string Namespace)
93 {
94 return Array.IndexOf(NamespacesLegalIdentities, Namespace) >= 0;
95 }
96
100 public const string NamespaceSmartContractsIeeeV1 = "urn:ieee:iot:leg:sc:1.0";
101
105 public const string NamespaceSmartContractsNeuroFoundationV1 = "urn:nf:iot:leg:sc:1.0";
106
111
115 public static readonly string[] NamespacesSmartContracts = new string[]
116 {
119 };
120
126 public static bool IsNamespaceSmartContract(string Namespace)
127 {
128 return Array.IndexOf(NamespacesSmartContracts, Namespace) >= 0;
129 }
130
134 public const string NamespaceOnboarding = "http://waher.se/schema/Onboarding/v1.xsd";
135
136 private static readonly string KeySettings = typeof(ContractsClient).FullName + ".";
137 private static readonly string ContractKeySettings = typeof(ContractsClient).Namespace + ".Contracts.";
138
139 private const int CacheSchemaDays = 7;
140
141
142 private readonly PublicKeyRecords publicKeys = new PublicKeyRecords();
143 private readonly Dictionary<string, KeyEventArgs> matchingKeys = new Dictionary<string, KeyEventArgs>();
144 private readonly Cache<string, KeyValuePair<byte[], bool>> contentPerPid = new Cache<string, KeyValuePair<byte[], bool>>(int.MaxValue, TimeSpan.FromDays(1), TimeSpan.FromDays(1));
145 private EndpointSecurity keys;
146 private DateTime keysTimestamp = DateTime.MinValue;
147 private SymmetricCipherAlgorithms preferredEncryptionAlgorithm = DefaultCipherAlgorithm;
148 private ICallStackCheck[] approvedSources = null;
149 private readonly string componentAddress;
150 private string keySettingsPrefix;
151 private string contractKeySettingsPrefix;
152 private bool keySettingsPrefixLocked = false;
153 private bool useKeysForE2e = false;
154 private bool preferredEncryptionAlgorithmLocked = false;
155 private RandomNumberGenerator rnd = RandomNumberGenerator.Create();
156 private Aes aes;
157
158 private sealed class LoadedKey : IDisposable
159 {
160 public LoadedKey(IE2eEndpoint Endpoint, bool MustDispose, DateTime Timestamp)
161 {
162 this.Endpoint = Endpoint;
163 this.MustDispose = MustDispose;
164 this.Timestamp = Timestamp;
165 }
166
167 public IE2eEndpoint Endpoint { get; }
168 public bool MustDispose { get; }
169 public DateTime Timestamp { get; }
170
171 public void Dispose()
172 {
173 if (this.MustDispose)
174 this.Endpoint?.Dispose();
175 }
176 }
177
178 private sealed class LoadedKeySet : IDisposable
179 {
180 private IE2eEndpoint[] keys;
181 private bool transferred;
182
183 public LoadedKeySet(IE2eEndpoint[] Keys, DateTime Timestamp)
184 {
185 this.keys = Keys;
186 this.Timestamp = Timestamp;
187 }
188
189 public IE2eEndpoint[] Keys => this.keys;
190 public DateTime Timestamp { get; }
191
192 public IE2eEndpoint[] TransferKeys()
193 {
194 this.transferred = true;
195 return this.keys;
196 }
197
198 public void Dispose()
199 {
200 if (!this.transferred && !(this.keys is null))
201 {
202 foreach (IE2eEndpoint Key in this.keys)
203 Key.Dispose();
204 }
205
206 this.keys = Array.Empty<IE2eEndpoint>();
207 }
208 }
209
210 #region Construction
211
225 : this(Client, ComponentAddress, null)
226 {
227 }
228
242 [Obsolete("Use overload with ICallStackCheck[] instead.")]
243 public ContractsClient(XmppClient Client, string ComponentAddress, object[] ApprovedSources)
244 : this(Client, ComponentAddress, Assert.Convert(ApprovedSources))
245 {
246 }
247
262 : base(Client)
263 {
264 this.componentAddress = ComponentAddress;
265 this.approvedSources = ApprovedSources;
266 this.SetLegalIdentityStateAllowedSources(ApprovedSources);
267 this.keys = null;
268
269 #region NeuroFoundation V1
270
271 this.client.RegisterMessageHandler("identity", NamespaceLegalIdentitiesNeuroFoundationV1, this.IdentityMessageHandler, true);
272 this.client.RegisterMessageHandler("petitionIdentityMsg", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionIdentityMessageHandler, false);
273 this.client.RegisterMessageHandler("petitionIdentityResponseMsg", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionIdentityResponseMessageHandler, false);
274 this.client.RegisterMessageHandler("petitionSignatureMsg", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionSignatureMessageHandler, false);
275 this.client.RegisterMessageHandler("petitionSignatureResponseMsg", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionSignatureResponseMessageHandler, false);
276 this.client.RegisterMessageHandler("petitionClientUrl", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionClientUrlEventHandler, false);
277 this.client.RegisterMessageHandler("identityReview", NamespaceLegalIdentitiesNeuroFoundationV1, this.IdentityReviewEventHandler, false);
278 this.client.RegisterMessageHandler("clientMessage", NamespaceLegalIdentitiesNeuroFoundationV1, this.ClientMessageEventHandler, false);
279
280 this.client.RegisterMessageHandler("contractSigned", NamespaceSmartContractsNeuroFoundationV1, this.ContractSignedMessageHandler, true);
281 this.client.RegisterMessageHandler("contractCreated", NamespaceSmartContractsNeuroFoundationV1, this.ContractCreatedMessageHandler, false);
282 this.client.RegisterMessageHandler("contractUpdated", NamespaceSmartContractsNeuroFoundationV1, this.ContractUpdatedMessageHandler, false);
283 this.client.RegisterMessageHandler("contractDeleted", NamespaceSmartContractsNeuroFoundationV1, this.ContractDeletedMessageHandler, false);
284 this.client.RegisterMessageHandler("petitionContractMsg", NamespaceSmartContractsNeuroFoundationV1, this.PetitionContractMessageHandler, false);
285 this.client.RegisterMessageHandler("petitionContractResponseMsg", NamespaceSmartContractsNeuroFoundationV1, this.PetitionContractResponseMessageHandler, false);
286 this.client.RegisterMessageHandler("contractProposal", NamespaceSmartContractsNeuroFoundationV1, this.ContractProposalMessageHandler, false);
287
288 #endregion
289
290 #region IEEE v1
291
292 this.client.RegisterMessageHandler("identity", NamespaceLegalIdentitiesIeeeV1, this.IdentityMessageHandler, true);
293 this.client.RegisterMessageHandler("petitionIdentityMsg", NamespaceLegalIdentitiesIeeeV1, this.PetitionIdentityMessageHandler, false);
294 this.client.RegisterMessageHandler("petitionIdentityResponseMsg", NamespaceLegalIdentitiesIeeeV1, this.PetitionIdentityResponseMessageHandler, false);
295 this.client.RegisterMessageHandler("petitionSignatureMsg", NamespaceLegalIdentitiesIeeeV1, this.PetitionSignatureMessageHandler, false);
296 this.client.RegisterMessageHandler("petitionSignatureResponseMsg", NamespaceLegalIdentitiesIeeeV1, this.PetitionSignatureResponseMessageHandler, false);
297 this.client.RegisterMessageHandler("petitionClientUrl", NamespaceLegalIdentitiesIeeeV1, this.PetitionClientUrlEventHandler, false);
298 this.client.RegisterMessageHandler("identityReview", NamespaceLegalIdentitiesIeeeV1, this.IdentityReviewEventHandler, false);
299 this.client.RegisterMessageHandler("clientMessage", NamespaceLegalIdentitiesIeeeV1, this.ClientMessageEventHandler, false);
300
301 this.client.RegisterMessageHandler("contractSigned", NamespaceSmartContractsIeeeV1, this.ContractSignedMessageHandler, true);
302 this.client.RegisterMessageHandler("contractCreated", NamespaceSmartContractsIeeeV1, this.ContractCreatedMessageHandler, false);
303 this.client.RegisterMessageHandler("contractUpdated", NamespaceSmartContractsIeeeV1, this.ContractUpdatedMessageHandler, false);
304 this.client.RegisterMessageHandler("contractDeleted", NamespaceSmartContractsIeeeV1, this.ContractDeletedMessageHandler, false);
305 this.client.RegisterMessageHandler("petitionContractMsg", NamespaceSmartContractsIeeeV1, this.PetitionContractMessageHandler, false);
306 this.client.RegisterMessageHandler("petitionContractResponseMsg", NamespaceSmartContractsIeeeV1, this.PetitionContractResponseMessageHandler, false);
307 this.client.RegisterMessageHandler("contractProposal", NamespaceSmartContractsIeeeV1, this.ContractProposalMessageHandler, false);
308
309 #endregion
310
311 this.aes = Aes.Create();
312 this.aes.BlockSize = 128;
313 this.aes.KeySize = 256;
314 this.aes.Mode = CipherMode.CBC;
315 this.aes.Padding = PaddingMode.None;
316
317 this.keySettingsPrefix = KeySettings;
318 this.contractKeySettingsPrefix = ContractKeySettings;
319 }
320
324 public override void Dispose()
325 {
326 #region NeuroFoundation V1
327
328 this.client.UnregisterMessageHandler("identity", NamespaceLegalIdentitiesNeuroFoundationV1, this.IdentityMessageHandler, true);
329 this.client.UnregisterMessageHandler("petitionIdentityMsg", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionIdentityMessageHandler, false);
330 this.client.UnregisterMessageHandler("petitionIdentityResponseMsg", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionIdentityResponseMessageHandler, false);
331 this.client.UnregisterMessageHandler("petitionSignatureMsg", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionSignatureMessageHandler, false);
332 this.client.UnregisterMessageHandler("petitionSignatureResponseMsg", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionSignatureResponseMessageHandler, false);
333 this.client.UnregisterMessageHandler("petitionClientUrl", NamespaceLegalIdentitiesNeuroFoundationV1, this.PetitionClientUrlEventHandler, false);
334 this.client.UnregisterMessageHandler("identityReview", NamespaceLegalIdentitiesNeuroFoundationV1, this.IdentityReviewEventHandler, false);
335 this.client.UnregisterMessageHandler("clientMessage", NamespaceLegalIdentitiesNeuroFoundationV1, this.ClientMessageEventHandler, false);
336
337 this.client.UnregisterMessageHandler("contractSigned", NamespaceSmartContractsNeuroFoundationV1, this.ContractSignedMessageHandler, true);
338 this.client.UnregisterMessageHandler("contractCreated", NamespaceSmartContractsNeuroFoundationV1, this.ContractCreatedMessageHandler, false);
339 this.client.UnregisterMessageHandler("contractUpdated", NamespaceSmartContractsNeuroFoundationV1, this.ContractUpdatedMessageHandler, false);
340 this.client.UnregisterMessageHandler("contractDeleted", NamespaceSmartContractsNeuroFoundationV1, this.ContractDeletedMessageHandler, false);
341 this.client.UnregisterMessageHandler("petitionContractMsg", NamespaceSmartContractsNeuroFoundationV1, this.PetitionContractMessageHandler, false);
342 this.client.UnregisterMessageHandler("petitionContractResponseMsg", NamespaceSmartContractsNeuroFoundationV1, this.PetitionContractResponseMessageHandler, false);
343 this.client.UnregisterMessageHandler("contractProposal", NamespaceSmartContractsNeuroFoundationV1, this.ContractProposalMessageHandler, false);
344
345 #endregion
346
347 #region IEEE v1
348
349 this.client.UnregisterMessageHandler("identity", NamespaceLegalIdentitiesIeeeV1, this.IdentityMessageHandler, true);
350 this.client.UnregisterMessageHandler("petitionIdentityMsg", NamespaceLegalIdentitiesIeeeV1, this.PetitionIdentityMessageHandler, false);
351 this.client.UnregisterMessageHandler("petitionIdentityResponseMsg", NamespaceLegalIdentitiesIeeeV1, this.PetitionIdentityResponseMessageHandler, false);
352 this.client.UnregisterMessageHandler("petitionSignatureMsg", NamespaceLegalIdentitiesIeeeV1, this.PetitionSignatureMessageHandler, false);
353 this.client.UnregisterMessageHandler("petitionSignatureResponseMsg", NamespaceLegalIdentitiesIeeeV1, this.PetitionSignatureResponseMessageHandler, false);
354 this.client.UnregisterMessageHandler("petitionClientUrl", NamespaceLegalIdentitiesIeeeV1, this.PetitionClientUrlEventHandler, false);
355 this.client.UnregisterMessageHandler("identityReview", NamespaceLegalIdentitiesIeeeV1, this.IdentityReviewEventHandler, false);
356 this.client.UnregisterMessageHandler("clientMessage", NamespaceLegalIdentitiesIeeeV1, this.ClientMessageEventHandler, false);
357
358 this.client.UnregisterMessageHandler("contractSigned", NamespaceSmartContractsIeeeV1, this.ContractSignedMessageHandler, true);
359 this.client.UnregisterMessageHandler("contractCreated", NamespaceSmartContractsIeeeV1, this.ContractCreatedMessageHandler, false);
360 this.client.UnregisterMessageHandler("contractUpdated", NamespaceSmartContractsIeeeV1, this.ContractUpdatedMessageHandler, false);
361 this.client.UnregisterMessageHandler("contractDeleted", NamespaceSmartContractsIeeeV1, this.ContractDeletedMessageHandler, false);
362 this.client.UnregisterMessageHandler("petitionContractMsg", NamespaceSmartContractsIeeeV1, this.PetitionContractMessageHandler, false);
363 this.client.UnregisterMessageHandler("petitionContractResponseMsg", NamespaceSmartContractsIeeeV1, this.PetitionContractResponseMessageHandler, false);
364 this.client.UnregisterMessageHandler("contractProposal", NamespaceSmartContractsIeeeV1, this.ContractProposalMessageHandler, false);
365
366 #endregion
367
368 this.keys?.Dispose();
369 this.keys = null;
370 this.keysTimestamp = DateTime.MinValue;
371
372 this.rnd?.Dispose();
373 this.rnd = null;
374
375 this.aes?.Dispose();
376 this.aes = null;
377
378 base.Dispose();
379 }
380
384 public override string[] Extensions => new string[] { };
385
389 public string ComponentAddress => this.componentAddress;
390
391 #endregion
392
393 #region Keys
394
398 public DateTime KeysTimestamp => this.keysTimestamp;
399
403 public string KeySettingsPrefix => this.keySettingsPrefix;
404
408 public string ContractKeySettingsPrefix => this.contractKeySettingsPrefix;
409
413 public SymmetricCipherAlgorithms PreferredEncryptionAlgorithm => this.preferredEncryptionAlgorithm;
414
422 {
423 if (this.preferredEncryptionAlgorithm == Algorithm)
424 return;
425
426 if (this.preferredEncryptionAlgorithmLocked)
427 throw new InvalidOperationException("Preferred Encryptio Algorithm has been locked.");
428
429 this.preferredEncryptionAlgorithm = Algorithm;
430 this.preferredEncryptionAlgorithmLocked = Lock;
431 }
432
438 public Task<bool> LoadKeys(bool CreateIfNone)
439 {
440 return this.LoadKeys(CreateIfNone, null);
441 }
442
449 public async Task<bool> LoadKeys(bool CreateIfNone, ProfilerThread Thread)
450 {
451 using Semaphore Lock = await LockKeys();
452 return await this.LoadKeysLocked(CreateIfNone, Thread);
453 }
454
455 private static Task<Semaphore> LockKeys()
456 {
458 }
459
460 private async Task<bool> LoadKeysLocked(bool CreateIfNone, ProfilerThread Thread)
461 {
462 Thread = Thread?.CreateSubThread("Load Keys", ProfilerThreadType.Sequential);
463 Thread?.Start();
464 try
465 {
466 using LoadedKeySet LoadedKeys = await this.LoadPersistedKeysAsync(
467 CreateIfNone, Thread);
468
469 if (LoadedKeys is null)
470 return false;
471
472 this.KeysLoaded(LoadedKeys);
473
474 return true;
475 }
476 finally
477 {
478 Thread?.Stop();
479 }
480 }
481
482 private async Task<LoadedKeySet> LoadPersistedKeysAsync(bool CreateIfNone, ProfilerThread Thread)
483 {
484 Thread?.NewState("Search");
485
486 List<IE2eEndpoint> Keys = new List<IE2eEndpoint>();
487 Dictionary<string, object> Settings = await RuntimeSettings.GetWhereKeyLikeAsync(this.keySettingsPrefix + "*", "*");
488
489 Thread?.NewState("Endpoints");
490
491 IE2eEndpoint[] AvailableEndpoints = EndpointSecurity.CreateEndpoints(256, 192,
492 int.MaxValue, new Type[]
493 {
494 typeof(EllipticCurveEndpoint),
496 }, Thread);
497
498 DateTime? Timestamp = null;
499 byte[] Key;
500
501 try
502 {
503 Thread?.NewState("Select");
504
505 foreach (KeyValuePair<string, object> Setting in Settings)
506 {
507 string LocalName = Setting.Key[this.keySettingsPrefix.Length..];
508
509 if (Setting.Value is string d)
510 {
511 if (string.IsNullOrEmpty(d))
512 continue;
513
514 try
515 {
516 Key = Convert.FromBase64String(d);
517 }
518 catch (Exception)
519 {
520 continue;
521 }
522
523 foreach (IE2eEndpoint Curve in AvailableEndpoints)
524 {
525 if (Curve.LocalName == LocalName)
526 {
527 try
528 {
529 Keys.Add(Curve.CreatePrivate(Key));
530 break;
531 }
532 catch (Exception ex)
533 {
534 Log.Exception(ex);
535 }
536 }
537 }
538 }
539 else if (Setting.Value is DateTime TP && LocalName == "Timestamp")
540 Timestamp = TP;
541 }
542
543 if (Keys.Count == 0 || (Keys.Count != AvailableEndpoints.Length && CreateIfNone))
544 {
545 if (!CreateIfNone)
546 return null;
547
548 Thread?.NewState("Create");
549
550 int i;
551 int c = Keys.Count;
552
553 foreach (IE2eEndpoint Endpoint in AvailableEndpoints)
554 {
555 for (i = 0; i < c; i++)
556 {
557 if (Keys[i].LocalName == Endpoint.LocalName)
558 break;
559 }
560
561 if (i < c)
562 continue;
563
564 if (Endpoint is EllipticCurveEndpoint Curve)
565 Key = this.GetKey(Curve);
566 else if (Endpoint is ModuleLatticeEndpoint ModuleLattice)
567 Key = this.GetKey(ModuleLattice);
568 else
569 continue;
570
571 await RuntimeSettings.SetAsync(this.keySettingsPrefix + Endpoint.LocalName, Convert.ToBase64String(Key));
572 Keys.Add(Endpoint);
573 }
574
575 Timestamp = DateTime.UtcNow;
576 await RuntimeSettings.SetAsync(this.keySettingsPrefix + "Timestamp", Timestamp.Value);
577
578 Log.Notice("Private keys for contracts client created.", this.client.BareJID, string.Empty, "NewKeys");
579 }
580 else if (!Timestamp.HasValue)
581 {
582 Thread?.NewState("Time");
583
584 Timestamp = DateTime.UtcNow;
585 await RuntimeSettings.SetAsync(this.keySettingsPrefix + "Timestamp", Timestamp.Value);
586 }
587
588 return new LoadedKeySet(Keys.ToArray(), Timestamp.Value);
589 }
590 finally
591 {
592 Thread?.NewState("Dispose");
593
594 HashSet<IE2eEndpoint> LoadedEndpoints = new HashSet<IE2eEndpoint>(Keys);
595
596 foreach (IE2eEndpoint Curve in AvailableEndpoints)
597 {
598 if (!LoadedEndpoints.Contains(Curve))
599 Curve.Dispose();
600 }
601 }
602 }
603
604 private void KeysLoaded(LoadedKeySet LoadedKeys)
605 {
606 if (!(this.keys is null))
607 {
608 if (MatchesLoadedKeys(this.keys, LoadedKeys.Keys))
609 return;
610
611 this.keys.Dispose();
612 this.keys = null;
613 }
614
615 if (this.useKeysForE2e)
616 this.keys = new EndpointSecurity(this.client, 128, LoadedKeys.TransferKeys());
617 else
618 this.keys = new EndpointSecurity(null, 128, LoadedKeys.TransferKeys());
619
620 this.keysTimestamp = LoadedKeys.Timestamp;
621
622 this.ClearMatchingKeyCache();
623 }
624
625 private void ClearMatchingKeyCache()
626 {
627 lock (this.matchingKeys)
628 {
629 this.matchingKeys.Clear();
630 }
631 }
632
633 private byte[] GetKey(EllipticCurveEndpoint EcEndpoint)
634 {
635 string s = EcEndpoint.Curve.Export();
636 XmlDocument Doc = XML.ParseXml(s, true);
637 s = Doc.DocumentElement.GetAttribute("d");
638 return Convert.FromBase64String(s);
639 }
640
641 private byte[] GetKey(ModuleLatticeEndpoint MlEndpoint)
642 {
643 return MlEndpoint.ExportPrivateKey();
644 }
645
649 public async Task GenerateNewKeys()
650 {
651 List<LegalIdentityState> ActiveStates = await this.GetActiveLegalIdentityStatesAsync(false);
652
653 if (ActiveStates.Count == 0 && this.client.State == XmppState.Connected)
654 {
655 await this.TryRefreshLegalIdentityStatesAsync();
656 ActiveStates = await this.GetActiveLegalIdentityStatesAsync(false);
657 }
658
659 using Semaphore Lock = await LockKeys();
660
661 foreach (LegalIdentityState State in ActiveStates)
662 await this.TryGetLegalIdentityEndpointAsync(State, true, true);
663
664 await RuntimeSettings.DeleteWhereKeyLikeAsync(this.keySettingsPrefix + "*", "*");
665 await this.LoadKeysLocked(true, null);
666 }
667
668 private async Task<List<LegalIdentityState>> GetActiveLegalIdentityStatesAsync(bool RefreshStates)
669 {
670 List<LegalIdentityState> ActiveStates = new List<LegalIdentityState>();
671
672 foreach (LegalIdentityState State in await Database.Find<LegalIdentityState>(
673 new FilterFieldEqualTo("BareJid", this.client.BareJID)))
674 {
675 if (RefreshStates && (State.State == IdentityState.Created || State.State == IdentityState.Approved))
676 {
677 try
678 {
679 LegalIdentity Identity = await this.GetLegalIdentityAsync(State.LegalId); // Make sure we have the latest.
680 if (Identity.State != State.State)
681 {
682 State.State = Identity.State;
683 State.Timestamp = Identity.Updated;
684
685 switch (Identity.State)
686 {
687 case IdentityState.Rejected:
688 case IdentityState.Obsoleted:
689 case IdentityState.Compromised:
690 State.PublicKey = null;
691 break;
692 }
693
694 await Database.Update(State);
695 }
696 }
698 {
699 await Database.Delete(State);
700 continue;
701 }
702 catch (Exception ex)
703 {
704 Log.Exception(ex, State.LegalId);
705 throw;
706 }
707 }
708
709 switch (State.State)
710 {
711 case IdentityState.Created:
712 case IdentityState.Approved:
713 ActiveStates.Add(State);
714 break;
715 }
716 }
717
718 return ActiveStates;
719 }
720
721 private async Task TryRefreshLegalIdentityStatesAsync()
722 {
723 try
724 {
725 await this.GetLegalIdentitiesAsync();
726 }
727 catch (Exception ex)
728 {
729 Log.Exception(ex);
730 }
731 }
732
733 private static byte[] Clone(byte[] Bin)
734 {
735 return Bin is null ? null : (byte[])Bin.Clone();
736 }
737
738 private static bool AreEqual(byte[] A, byte[] B)
739 {
740 if (ReferenceEquals(A, B))
741 return true;
742
743 if (A is null || B is null || A.Length != B.Length)
744 return false;
745
746 int i, c = A.Length;
747
748 for (i = 0; i < c; i++)
749 {
750 if (A[i] != B[i])
751 return false;
752 }
753
754 return true;
755 }
756
757 private bool TryExportPrivateKey(IE2eEndpoint Endpoint, out string KeyName, out string KeyNamespace, out byte[] PrivateKey)
758 {
759 KeyName = Endpoint?.LocalName;
760 KeyNamespace = Endpoint?.Namespace;
761 PrivateKey = null;
762
763 switch (Endpoint)
764 {
765 case EllipticCurveEndpoint Curve:
766 PrivateKey = this.GetKey(Curve);
767 break;
768
769 case ModuleLatticeEndpoint ModuleLattice:
770 PrivateKey = ModuleLattice.ExportPrivateKey();
771 break;
772
773 case RsaEndpoint Rsa:
774 PrivateKey = Rsa.Export(true);
775 break;
776
777 default:
778 return false;
779 }
780
781 return !(PrivateKey is null);
782 }
783
784 private static bool MatchesLoadedKeys(EndpointSecurity EndpointSecurity, IEnumerable<IE2eEndpoint> Keys)
785 {
786 if (EndpointSecurity is null)
787 return false;
788
789 foreach (IE2eEndpoint Key in Keys)
790 {
792
793 if (ExistingEndpoint is null || !AreEqual(ExistingEndpoint.PublicKey, Key.PublicKey))
794 return false;
795 }
796
797 return true;
798 }
799
800 private async Task<Tuple<string, string, byte[]>> GetPersistablePrivateKeyAsync(IE2eEndpoint Endpoint)
801 {
802 if (Endpoint is null)
803 return null;
804
805 string KeyName = Endpoint.LocalName;
806 string KeyNamespace = Endpoint.Namespace;
807 string RuntimeValue = await RuntimeSettings.GetAsync(this.keySettingsPrefix + KeyName, string.Empty);
808
809 if (!string.IsNullOrEmpty(RuntimeValue))
810 {
811 try
812 {
813 byte[] RuntimePrivateKey = Convert.FromBase64String(RuntimeValue);
814
815 if (EndpointSecurity.TryCreateEndpoint(KeyName, KeyNamespace, out IE2eEndpoint Template))
816 {
817 using (Template)
818 using (IE2eEndpoint RuntimeEndpoint = Template.CreatePrivate(RuntimePrivateKey))
819 {
820 if (AreEqual(RuntimeEndpoint.PublicKey, Endpoint.PublicKey))
821 return new Tuple<string, string, byte[]>(KeyName, KeyNamespace, RuntimePrivateKey);
822 }
823 }
824 }
825 catch (Exception)
826 {
827 // Ignore malformed or mismatched runtime values and fall back to endpoint export.
828 }
829 }
830
831 return this.TryExportPrivateKey(Endpoint, out KeyName, out KeyNamespace, out byte[] PrivateKey) ?
832 new Tuple<string, string, byte[]>(KeyName, KeyNamespace, PrivateKey)
833 : null;
834 }
835
836 private IE2eEndpoint TryCreateLegalIdentityEndpoint(LegalIdentityState State)
837 {
838 if (State is null ||
839 !State.HasPrivateKey ||
840 string.IsNullOrEmpty(State.KeyName))
841 {
842 return null;
843 }
844
845 string KeyNamespace = string.IsNullOrEmpty(State.KeyNamespace)
846 ? EndpointSecurity.IoTHarmonizationE2ECurrent
847 : State.KeyNamespace;
848
849 byte[] PrivateKey = State.PrivateKey;
850
851 if (PrivateKey is null || !EndpointSecurity.TryCreateEndpoint(State.KeyName,
852 KeyNamespace, out IE2eEndpoint Template))
853 {
854 return null;
855 }
856
857 try
858 {
859 return Template.CreatePrivate(PrivateKey);
860 }
861 catch (Exception ex)
862 {
863 Log.Exception(ex, State.LegalId);
864 return null;
865 }
866 }
867
868 private async Task<bool> SetLegalIdentityKeySnapshotAsync(LegalIdentityState State, IE2eEndpoint Endpoint)
869 {
870 Tuple<string, string, byte[]> P = State is null ? null : await this.GetPersistablePrivateKeyAsync(Endpoint);
871
872 if (P is null)
873 {
874 return false;
875 }
876
877 string KeyName = P.Item1;
878 string KeyNamespace = P.Item2;
879 byte[] PrivateKey = P.Item3;
880
881 bool Updated = !AreEqual(State.PublicKey, Endpoint.PublicKey) ||
882 State.KeyName != KeyName ||
883 State.KeyNamespace != KeyNamespace ||
884 !AreEqual(State.HasPrivateKey ? State.PrivateKey : null, PrivateKey);
885
886 State.PublicKey = Clone(Endpoint.PublicKey);
887 State.KeyName = KeyName;
888 State.KeyNamespace = KeyNamespace;
889 State.PrivateKey = Clone(PrivateKey);
890
891 return Updated;
892 }
893
894 private async Task<LoadedKey> TryGetLegalIdentityEndpointAsync(LegalIdentityState State,
895 bool MigrateLegacyState, bool Locked)
896 {
897 IE2eEndpoint Endpoint = this.TryCreateLegalIdentityEndpoint(State);
898
899 if (!(Endpoint is null))
900 {
901 if (State.PublicKey is null)
902 {
903 State.PublicKey = Clone(Endpoint.PublicKey);
904 if (!string.IsNullOrEmpty(State.ObjectId))
905 await Database.Update(State);
906
907 return new LoadedKey(Endpoint, true, State.Timestamp);
908 }
909 else if (!AreEqual(State.PublicKey, Endpoint.PublicKey))
910 {
911 Endpoint.Dispose();
912 Endpoint = null;
913 }
914 else
915 return new LoadedKey(Endpoint, true, State.Timestamp);
916 }
917
918 bool MissingSnapshot = State?.HasPrivateKey != true ||
919 string.IsNullOrEmpty(State.KeyName);
920
921 if (!MigrateLegacyState ||
922 !MissingSnapshot ||
923 State?.PublicKey is null ||
924 !(Locked ? await this.LoadKeysLocked(false, null) : await this.LoadKeys(false)))
925 {
926 return new LoadedKey(null, false, DateTime.MinValue);
927 }
928
929 Endpoint = this.LocalEndpoint.FindLocalEndpoint(State.PublicKey);
930 if (Endpoint is null ||
931 !await this.SetLegalIdentityKeySnapshotAsync(State, Endpoint))
932 {
933 return new LoadedKey(Endpoint, false, State.Timestamp);
934 }
935
936 if (!string.IsNullOrEmpty(State.ObjectId))
937 await Database.Update(State);
938
939 IE2eEndpoint Endpoint2 = this.TryCreateLegalIdentityEndpoint(State);
940 return Endpoint2 is null
941 ? new LoadedKey(Endpoint, false, State.Timestamp)
942 : new LoadedKey(Endpoint2, true, State.Timestamp);
943 }
944
945 private static bool TryParseContractSharedSecret(string Value, out SymmetricCipherAlgorithms Algorithm,
946 out string CreatorJid, out byte[] SharedSecret)
947 {
948 Algorithm = DefaultCipherAlgorithm;
949 CreatorJid = string.Empty;
950 SharedSecret = null;
951
952 if (string.IsNullOrEmpty(Value))
953 return false;
954
955 string[] Parts = Value.Split('|');
956 if (Parts.Length != 3 || !Enum.TryParse(Parts[0], out Algorithm))
957 return false;
958
959 CreatorJid = Parts[1];
960
961 try
962 {
963 SharedSecret = Convert.FromBase64String(Parts[2]);
964 }
965 catch (Exception)
966 {
967 return false;
968 }
969
970 return true;
971 }
972
973 private static Tuple<SymmetricCipherAlgorithms, string, byte[]> CreateContractSharedSecretTuple(
974 SymmetricCipherAlgorithms Algorithm, string CreatorJid, byte[] SharedSecret)
975 {
976 if (SharedSecret is null)
977 return null;
978
979 return new Tuple<SymmetricCipherAlgorithms, string, byte[]>(Algorithm, CreatorJid ?? string.Empty,
980 (byte[])SharedSecret.Clone());
981 }
982
983 private static string EncodeContractSharedSecret(SymmetricCipherAlgorithms Algorithm, string CreatorJid, byte[] SharedSecret)
984 {
985 if (SharedSecret is null)
986 return null;
987
988 return Algorithm.ToString() + "|" + (CreatorJid ?? string.Empty) + "|" + Convert.ToBase64String(SharedSecret);
989 }
990
991 private async Task<ContractSharedSecretState> GetContractStateAsync(string ContractId)
992 {
993 return await Database.FindFirstDeleteRest<ContractSharedSecretState>(new FilterAnd(
994 new FilterFieldEqualTo("BareJid", this.client.BareJID),
995 new FilterFieldEqualTo("ContractId", ContractId)));
996 }
997
998 private async Task<bool> UpsertContractStateAsync(string ContractId, string CreatorJid, byte[] SharedSecret,
999 SymmetricCipherAlgorithms KeyAlgorithm)
1000 {
1001 if (string.IsNullOrEmpty(ContractId) || SharedSecret is null)
1002 return false;
1003
1004 ContractSharedSecretState State = await this.GetContractStateAsync(ContractId);
1005 byte[] SecretCopy = (byte[])SharedSecret.Clone();
1006
1007 if (State is null)
1008 {
1009 State = new ContractSharedSecretState(ContractId)
1010 {
1011 BareJid = this.client.BareJID,
1012 CreatorJid = CreatorJid ?? string.Empty,
1013 KeyAlgorithm = KeyAlgorithm,
1014 SharedSecret = SecretCopy
1015 };
1016
1017 await Database.Insert(State);
1018 }
1019 else
1020 {
1021 State.CreatorJid = CreatorJid ?? string.Empty;
1022 State.KeyAlgorithm = KeyAlgorithm;
1023 State.SharedSecret = SecretCopy;
1024
1025 await Database.Update(State);
1026 }
1027
1028 return true;
1029 }
1030
1031 private Tuple<SymmetricCipherAlgorithms, string, byte[]> TryLoadContractSharedSecret(ContractSharedSecretState State)
1032 {
1033 if (State is null || string.IsNullOrEmpty(State.ContractId) || !State.HasSharedSecret)
1034 return null;
1035
1036 return CreateContractSharedSecretTuple(State.KeyAlgorithm, State.CreatorJid, State.SharedSecret);
1037 }
1038
1039 private async Task<Tuple<SymmetricCipherAlgorithms, string, byte[]>> TryLoadLegacyContractSharedSecretAsync(
1040 string ContractId, bool MigrateToState)
1041 {
1042 string Name = this.contractKeySettingsPrefix + ContractId;
1043 string Value = await RuntimeSettings.GetAsync(Name, string.Empty);
1044
1045 if (!TryParseContractSharedSecret(Value, out SymmetricCipherAlgorithms Algorithm, out string CreatorJid,
1046 out byte[] SharedSecret))
1047 {
1048 return null;
1049 }
1050
1051 if (MigrateToState)
1052 await this.UpsertContractStateAsync(ContractId, CreatorJid, SharedSecret, Algorithm);
1053
1054 return CreateContractSharedSecretTuple(Algorithm, CreatorJid, SharedSecret);
1055 }
1056
1057 private async Task<List<ContractSharedSecretState>> GetExportableContractStatesAsync()
1058 {
1059 Dictionary<string, ContractSharedSecretState> ContractStates = new Dictionary<string, ContractSharedSecretState>(StringComparer.Ordinal);
1060 List<ContractSharedSecretState> Result = new List<ContractSharedSecretState>();
1061
1063 {
1064 if (string.IsNullOrEmpty(State.ContractId) || !State.HasSharedSecret)
1065 continue;
1066
1067 ContractStates[State.ContractId] = State;
1068 }
1069
1070 Dictionary<string, object> Settings = await RuntimeSettings.GetWhereKeyLikeAsync(this.contractKeySettingsPrefix + "*", "*");
1071
1072 foreach (KeyValuePair<string, object> Setting in Settings)
1073 {
1074 if (!(Setting.Value is string Value))
1075 continue;
1076
1077 string ContractId = Setting.Key[this.contractKeySettingsPrefix.Length..];
1078
1079 if (ContractStates.ContainsKey(ContractId) ||
1080 !TryParseContractSharedSecret(Value, out SymmetricCipherAlgorithms Algorithm, out string CreatorJid,
1081 out byte[] SharedSecret))
1082 {
1083 continue;
1084 }
1085
1086 if (await this.UpsertContractStateAsync(ContractId, CreatorJid, SharedSecret, Algorithm))
1087 {
1088 ContractSharedSecretState State = await this.GetContractStateAsync(ContractId);
1089
1090 if (!(State is null))
1091 ContractStates[ContractId] = State;
1092 }
1093 }
1094
1095 foreach (ContractSharedSecretState State in ContractStates.Values)
1096 Result.Add(State);
1097
1098 return Result;
1099 }
1100
1105 public async Task<string> ExportKeys()
1106 {
1107 StringBuilder Xml = new StringBuilder();
1108 XmlWriterSettings Settings = XML.WriterSettings(false, true);
1109
1110 using (XmlWriter Output = XmlWriter.Create(Xml, Settings))
1111 {
1112 await this.ExportKeys(Output);
1113 }
1114
1115 return Xml.ToString();
1116 }
1117
1122 public async Task ExportKeys(XmlWriter Output)
1123 {
1124 this.AssertAllowed();
1125
1126 Output.WriteStartElement("LegalId", NamespaceOnboarding);
1127
1128 Dictionary<string, object> Settings = await RuntimeSettings.GetWhereKeyLikeAsync(this.keySettingsPrefix + "*", "*");
1129
1130 foreach (KeyValuePair<string, object> Setting in Settings)
1131 {
1132 string Name = Setting.Key[this.keySettingsPrefix.Length..];
1133
1134 if (Setting.Value is string s)
1135 {
1136 Output.WriteStartElement("S");
1137 Output.WriteAttributeString("n", Name);
1138 Output.WriteAttributeString("v", s);
1139 Output.WriteEndElement();
1140 }
1141 else if (Setting.Value is DateTime TP)
1142 {
1143 Output.WriteStartElement("DT");
1144 Output.WriteAttributeString("n", Name);
1145 Output.WriteAttributeString("v", XML.Encode(TP));
1146 Output.WriteEndElement();
1147 }
1148 }
1149
1150 foreach (LegalIdentityState State in await Database.Find<LegalIdentityState>(new FilterAnd(
1151 new FilterFieldEqualTo("BareJid", this.client.BareJID),
1152 new FilterFieldEqualTo("State", IdentityState.Approved))))
1153 {
1154 using LoadedKey Key = await this.TryGetLegalIdentityEndpointAsync(State, true, false);
1155 IE2eEndpoint Endpoint = Key.Endpoint;
1156
1157 if (Endpoint is null || State.PublicKey is null || !State.HasPrivateKey || string.IsNullOrEmpty(State.KeyName))
1158 continue;
1159
1160 Output.WriteStartElement("State");
1161 Output.WriteAttributeString("legalId", State.LegalId);
1162 Output.WriteAttributeString("publicKey", Convert.ToBase64String(State.PublicKey));
1163 Output.WriteAttributeString("timestamp", XML.Encode(State.Timestamp));
1164 Output.WriteAttributeString("keyName", State.KeyName);
1165
1166 if (!string.IsNullOrEmpty(State.KeyNamespace))
1167 Output.WriteAttributeString("keyNamespace", State.KeyNamespace);
1168
1169 Output.WriteAttributeString("hasPrivateKey", CommonTypes.Encode(State.HasPrivateKey));
1170 Output.WriteAttributeString("privateKey", Convert.ToBase64String(State.PrivateKey));
1171 Output.WriteEndElement();
1172 }
1173
1174 foreach (ContractSharedSecretState ContractState in await this.GetExportableContractStatesAsync())
1175 {
1176 if (!ContractState.HasSharedSecret)
1177 continue;
1178
1179 Output.WriteStartElement("C");
1180 Output.WriteAttributeString("n", ContractState.ContractId);
1181 Output.WriteAttributeString("v", EncodeContractSharedSecret(ContractState.KeyAlgorithm,
1182 ContractState.CreatorJid, ContractState.SharedSecret));
1183 Output.WriteEndElement();
1184 }
1185
1186 Output.WriteEndElement();
1187 }
1188
1194 public Task<bool> ImportKeys(string Xml)
1195 {
1196 XmlDocument Doc = XML.ParseXml(Xml);
1197
1198 return this.ImportKeys(Doc);
1199 }
1200
1206 public Task<bool> ImportKeys(XmlDocument Xml)
1207 {
1208 return this.ImportKeys(Xml.DocumentElement);
1209 }
1210
1216 public async Task<bool> ImportKeys(XmlElement Xml)
1217 {
1218 this.AssertAllowed();
1219
1220 if (Xml is null || Xml.LocalName != "LegalId" || Xml.NamespaceURI != NamespaceOnboarding)
1221 return false;
1222
1223 foreach (XmlNode N in Xml.ChildNodes)
1224 {
1225 if (!(N is XmlElement E))
1226 continue;
1227
1228 if (E.NamespaceURI != NamespaceOnboarding)
1229 return false;
1230
1231 switch (E.LocalName)
1232 {
1233 case "S":
1234 string Name = XML.Attribute(E, "n");
1235 string StringValue = XML.Attribute(E, "v");
1236
1237 await RuntimeSettings.SetAsync(this.keySettingsPrefix + Name, StringValue);
1238 break;
1239
1240 case "DT":
1241 Name = XML.Attribute(E, "n");
1242 DateTime DateTimeValue = XML.Attribute(E, "v", DateTime.MinValue);
1243
1244 await RuntimeSettings.SetAsync(this.keySettingsPrefix + Name, DateTimeValue);
1245 break;
1246
1247 case "C":
1248 {
1249 Name = XML.Attribute(E, "n");
1250 StringValue = XML.Attribute(E, "v");
1251
1252 if (!TryParseContractSharedSecret(StringValue, out SymmetricCipherAlgorithms ContractAlgorithm,
1253 out string CreatorJid, out byte[] SharedSecret))
1254 {
1255 return false;
1256 }
1257
1258 if (!await this.UpsertContractStateAsync(Name, CreatorJid, SharedSecret, ContractAlgorithm))
1259 return false;
1260 break;
1261 }
1262
1263 case "ContractState":
1264 {
1265 string ContractId = XML.Attribute(E, "contractId");
1266 string CreatorJid = XML.Attribute(E, "creatorJid");
1267 string KeyAlgorithm = XML.Attribute(E, "keyAlgorithm");
1268 string SharedSecretStr = XML.Attribute(E, "sharedSecret");
1269 byte[] SharedSecret;
1270
1271 if (!Enum.TryParse(KeyAlgorithm, out SymmetricCipherAlgorithms ContractAlgorithm))
1272 return false;
1273
1274 try
1275 {
1276 SharedSecret = Convert.FromBase64String(SharedSecretStr);
1277 }
1278 catch (Exception)
1279 {
1280 return false;
1281 }
1282
1283 if (!await this.UpsertContractStateAsync(ContractId, CreatorJid, SharedSecret, ContractAlgorithm))
1284 return false;
1285 break;
1286 }
1287
1288 case "State":
1289 string LegalId = XML.Attribute(E, "legalId");
1290 string PublicKeyStr = XML.Attribute(E, "publicKey");
1291 string PrivateKeyStr = XML.Attribute(E, "privateKey");
1292 string KeyName = XML.Attribute(E, "keyName");
1293 string KeyNamespace = XML.Attribute(E, "keyNamespace");
1294 byte[] PublicKey;
1295 byte[] PrivateKey = null;
1296 DateTimeValue = XML.Attribute(E, "timestamp", DateTime.MinValue);
1297
1298 try
1299 {
1300 PublicKey = Convert.FromBase64String(PublicKeyStr);
1301 }
1302 catch (Exception)
1303 {
1304 return false;
1305 }
1306
1307 if (!string.IsNullOrEmpty(PrivateKeyStr))
1308 {
1309 try
1310 {
1311 PrivateKey = Convert.FromBase64String(PrivateKeyStr);
1312 }
1313 catch (Exception)
1314 {
1315 return false;
1316 }
1317 }
1318
1319 LegalIdentityState IdState = await Database.FindFirstDeleteRest<LegalIdentityState>(new FilterAnd(
1320 new FilterFieldEqualTo("BareJid", this.client.BareJID),
1321 new FilterFieldEqualTo("LegalId", LegalId)));
1322
1323 if (IdState is null)
1324 {
1325 IdState = new LegalIdentityState()
1326 {
1327 BareJid = this.client.BareJID,
1328 LegalId = LegalId,
1329 State = IdentityState.Approved,
1330 Timestamp = DateTimeValue,
1331 PublicKey = PublicKey,
1332 KeyName = KeyName,
1333 KeyNamespace = KeyNamespace,
1334 PrivateKey = PrivateKey
1335 };
1336
1337 await Database.Insert(IdState);
1338 }
1339 else
1340 {
1341 IdState.State = IdentityState.Approved;
1342 IdState.Timestamp = DateTimeValue;
1343 IdState.PublicKey = PublicKey;
1344 IdState.KeyName = KeyName;
1345 IdState.KeyNamespace = KeyNamespace;
1346 IdState.PrivateKey = PrivateKey;
1347
1348 await Database.Update(IdState);
1349 }
1350 break;
1351
1352 default:
1353 return false;
1354 }
1355 }
1356
1357 if (await this.LoadKeys(false))
1358 return true;
1359
1360 foreach (LegalIdentityState State in await Database.Find<LegalIdentityState>(new FilterAnd(
1361 new FilterFieldEqualTo("BareJid", this.client.BareJID),
1362 new FilterFieldEqualTo("State", IdentityState.Approved))))
1363 {
1364 using LoadedKey Key = await this.TryGetLegalIdentityEndpointAsync(State, true, false);
1365
1366 if (!(Key.Endpoint is null))
1367 return true;
1368 }
1369
1370 return false;
1371 }
1372
1378 public void SetKeySettingsInstance(string InstanceName, bool Locked)
1379 {
1380 if (this.keySettingsPrefixLocked)
1381 throw new InvalidOperationException("Key settings instance is locked.");
1382
1383 if (string.IsNullOrEmpty(InstanceName))
1384 {
1385 this.keySettingsPrefix = KeySettings;
1386 this.contractKeySettingsPrefix = ContractKeySettings;
1387 }
1388 else
1389 {
1390 this.keySettingsPrefix = InstanceName + "." + KeySettings;
1391 this.contractKeySettingsPrefix = InstanceName + "." + ContractKeySettings;
1392 }
1393
1394 this.keySettingsPrefixLocked = Locked;
1395 }
1396
1401 public async Task EnableE2eEncryption()
1402 {
1403 using Semaphore Lock = await LockKeys();
1404
1405 if (this.useKeysForE2e)
1406 return;
1407
1408 this.useKeysForE2e = true;
1409
1410 if (this.keys is null)
1411 await this.LoadKeysLocked(true, null);
1412
1413 if (this.keys.Client is null)
1414 this.keys.RegisterHandlers(this.client);
1415 }
1416
1421 public async Task DisableE2eEncryption()
1422 {
1423 using Semaphore Lock = await LockKeys();
1424
1425 if (!this.useKeysForE2e)
1426 return;
1427
1428 this.useKeysForE2e = false;
1429 this.keys?.UnregisterHandlers(this.client);
1430 }
1431
1435 public bool IsE2eEncryptionEnabled => EndpointSecurity.IsE2eEncryptionEnabled(this.client);
1436
1443 public byte[] RandomBytes(int Nr)
1444 {
1445 if (Nr < 0)
1446 throw new ArgumentException(nameof(Nr));
1447
1448 byte[] Bytes = new byte[Nr];
1449
1450 this.rnd.GetBytes(Bytes);
1451
1452 return Bytes;
1453 }
1454
1459 public ulong RandomInteger()
1460 {
1461 byte[] Bin = this.RandomBytes(8);
1462 return BitConverter.ToUInt64(Bin, 0);
1463 }
1464
1470 public ulong RandomInteger(ulong MaxExclusive)
1471 {
1472 if (MaxExclusive == 0)
1473 throw new ArgumentException(nameof(MaxExclusive));
1474
1475 return this.RandomInteger() % MaxExclusive;
1476 }
1477
1486 public int RandomInteger(int MinInclusive, int MaxInclusive)
1487 {
1488 if (MaxInclusive < MinInclusive)
1489 throw new ArgumentException(nameof(MaxInclusive));
1490
1491 ulong Diff = (uint)(MaxInclusive - MinInclusive);
1492 if (Diff == 0)
1493 return MinInclusive;
1494
1495 int Result = (int)this.RandomInteger(Diff + 1UL);
1496 Result += MinInclusive;
1497
1498 return Result;
1499 }
1500
1501 #endregion
1502
1503 #region Security
1504
1510 [Obsolete("Use the overload taking ICallStackCheck instances instead.")]
1511 public void SetAllowedSources(object[] ApprovedSources)
1512 {
1513 this.SetAllowedSources(Assert.Convert(ApprovedSources));
1514 }
1515
1521 public void SetAllowedSources(ICallStackCheck[] ApprovedSources)
1522 {
1523 if (!(this.approvedSources is null))
1524 throw new NotSupportedException("Changing approved sources not permitted.");
1525
1526 this.approvedSources = ApprovedSources;
1527 this.SetLegalIdentityStateAllowedSources(ApprovedSources);
1528 this.SetContractStateAllowedSources(ApprovedSources);
1529 }
1530
1531 private void AssertAllowed()
1532 {
1533 if (!(this.approvedSources is null))
1534 Assert.CallFromSource(this.approvedSources);
1535 }
1536
1537 private void SetLegalIdentityStateAllowedSources(ICallStackCheck[] ApprovedSources)
1538 {
1539 if (ApprovedSources is null)
1540 return;
1541
1542 try
1543 {
1544 LegalIdentityState.SetAllowedSources(ApprovedSources);
1545 }
1546 catch (NotSupportedException)
1547 {
1548 // Sensitive state access has already been configured globally.
1549 }
1550 }
1551
1552 private void SetContractStateAllowedSources(ICallStackCheck[] ApprovedSources)
1553 {
1554 if (ApprovedSources is null)
1555 return;
1556
1557 try
1558 {
1560 }
1561 catch (NotSupportedException)
1562 {
1563 // Sensitive state access has already been configured globally.
1564 }
1565 }
1566
1567 #endregion
1568
1569 #region URIs
1570
1576 public static string LegalIdUriString(string LegalId)
1577 {
1578 return "iotid:" + LegalId;
1579 }
1580
1586 public static Uri LegalIdUri(string LegalId)
1587 {
1588 return new Uri(LegalIdUriString(LegalId));
1589 }
1590
1596 public static string ContractIdUriString(string ContractId)
1597 {
1598 return "iotsc:" + ContractId;
1599 }
1600
1606 public static Uri ContractIdUri(string ContractId)
1607 {
1608 return new Uri(ContractIdUriString(ContractId));
1609 }
1610
1611 #endregion
1612
1613 #region Server Public Keys
1614
1620 public Task GetServerPublicKey(EventHandlerAsync<KeyEventArgs> Callback, object State)
1621 {
1622 return this.GetServerPublicKey(this.componentAddress, null, Callback, State);
1623 }
1624
1631 public Task GetServerPublicKey(DateTime? Timestamp,
1632 EventHandlerAsync<KeyEventArgs> Callback, object State)
1633 {
1634 return this.GetServerPublicKey(this.componentAddress, Timestamp, Callback, State);
1635 }
1636
1643 public Task GetServerPublicKey(string Address, EventHandlerAsync<KeyEventArgs> Callback, object State)
1644 {
1645 return this.GetServerPublicKey(Address, null, Callback, State);
1646 }
1647
1655 public async Task GetServerPublicKey(string Address, DateTime? Timestamp,
1656 EventHandlerAsync<KeyEventArgs> Callback, object State)
1657 {
1658 if (this.publicKeys.TryGetRecord(Address, Timestamp ?? DateTime.UtcNow,
1659 out KeyEventArgs e0))
1660 {
1661 e0 = new KeyEventArgs(e0)
1662 {
1663 State = State
1664 };
1665
1666 await Callback.Raise(this, e0);
1667 }
1668 else
1669 {
1670 EventHandlerAsync<PublicKeyEventArgs> h = GetLocalPublicKey;
1671 if (!(h is null))
1672 {
1673 PublicKeyEventArgs e = new PublicKeyEventArgs(Address, Timestamp);
1674 await h.Raise(this, e, false);
1675
1676 if (!(e.Key is null) && e.ValidFrom.HasValue)
1677 {
1678 if (!(Callback is null))
1679 {
1680 XmlDocument Doc = new XmlDocument();
1681 XmlElement Empty = Doc.CreateElement("Local");
1682
1683 IqResultEventArgs e1 = new IqResultEventArgs(Empty, string.Empty, string.Empty, string.Empty, true, State);
1684 KeyEventArgs e2 = new KeyEventArgs(e1, e.Key, e.ValidFrom.Value, e.ValidTo);
1685 await Callback.Raise(this, e2);
1686 }
1687
1688 return;
1689 }
1690 }
1691
1692 StringBuilder sb = new StringBuilder();
1693
1694 sb.Append("<getPublicKey xmlns=\"");
1696
1697 if (Timestamp.HasValue)
1698 {
1699 sb.Append("\" ts=\"");
1700 sb.Append(XML.Encode(Timestamp.Value.ToUniversalTime()));
1701 }
1702
1703 sb.Append("\"/>");
1704
1705 await this.client.SendIqGet(Address, sb.ToString(), async (Sender, e) =>
1706 {
1707 IE2eEndpoint ServerKey = null;
1708 XmlElement E;
1709 DateTime? From = null;
1710 DateTime? To = null;
1711
1712 if (e.Ok &&
1713 !((E = e.FirstElement) is null) &&
1714 E.LocalName == "publicKey")
1715 {
1716 From = XML.Attribute(E, "from", DateTime.MinValue);
1717 To = E.HasAttribute("to") ?
1718 XML.Attribute(E, "to", DateTime.MaxValue) : (DateTime?)null;
1719
1720 foreach (XmlNode N in E.ChildNodes)
1721 {
1722 if (N is XmlElement E2)
1723 {
1724 ServerKey = EndpointSecurity.ParseE2eKey(E2);
1725 if (!(ServerKey is null))
1726 break;
1727 }
1728 }
1729
1730 e.Ok = !(ServerKey is null);
1731 }
1732 else
1733 e.Ok = false;
1734
1735 e0 = new KeyEventArgs(e, ServerKey, From ?? DateTime.MinValue, To);
1736
1737 if (e0.Ok)
1738 {
1739 this.publicKeys.Add(Address, From ?? DateTime.MinValue,
1740 To ?? DateTime.UtcNow, e0);
1741 }
1742
1743 await Callback.Raise(this, e0);
1744 }, State);
1745 }
1746 }
1747
1752 public static event EventHandlerAsync<PublicKeyEventArgs> GetLocalPublicKey = null;
1753
1758 public Task<IE2eEndpoint> GetServerPublicKeyAsync()
1759 {
1760 return this.GetServerPublicKeyAsync(this.componentAddress);
1761 }
1762
1768 public async Task<IE2eEndpoint> GetServerPublicKeyAsync(string Address)
1769 {
1770 TaskCompletionSource<IE2eEndpoint> Result = new TaskCompletionSource<IE2eEndpoint>();
1771
1772 await this.GetServerPublicKey(Address, (Sender, e) =>
1773 {
1774 if (e.Ok)
1775 Result.TrySetResult(e.Key);
1776 else
1777 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get public key."));
1778
1779 return Task.CompletedTask;
1780
1781 }, null);
1782
1783 return await Result.Task;
1784 }
1785
1786 #endregion
1787
1788 #region Matching Local Keys
1789
1795 public Task GetMatchingLocalKey(EventHandlerAsync<KeyEventArgs> Callback, object State)
1796 {
1797 return this.GetMatchingLocalKey(this.componentAddress, Callback, State);
1798 }
1799
1803 private EndpointSecurity LocalEndpoint
1804 {
1805 get
1806 {
1807 if (this.keys is null)
1808 throw new InvalidOperationException("Local keys not loaded or generated.");
1809
1810 return this.keys;
1811 }
1812 }
1813
1820 public async Task GetMatchingLocalKey(string Address, EventHandlerAsync<KeyEventArgs> Callback, object State)
1821 {
1822 KeyEventArgs e0;
1823
1824 lock (this.matchingKeys)
1825 {
1826 if (!this.matchingKeys.TryGetValue(Address, out e0))
1827 e0 = null;
1828 }
1829
1830 if (!(e0 is null))
1831 {
1832 e0 = new KeyEventArgs(e0)
1833 {
1834 State = State
1835 };
1836
1837 await Callback.Raise(this, e0);
1838 }
1839 else
1840 {
1841 await this.GetServerPublicKey(Address, async (Sender, e) =>
1842 {
1843 IE2eEndpoint LocalKey = null;
1844
1845 if (e.Ok)
1846 {
1847 LocalKey = this.LocalEndpoint.FindLocalEndpoint(e.Key);
1848 if (LocalKey is null)
1849 e.Ok = false;
1850 }
1851
1852 e0 = new KeyEventArgs(e, LocalKey, e.ValidFrom, e.ValidTo);
1853
1854 if (e0.Ok)
1855 {
1856 lock (this.matchingKeys)
1857 {
1858 this.matchingKeys[Address] = e0;
1859 }
1860 }
1861
1862 await Callback.Raise(this, e0);
1863
1864 }, State);
1865 }
1866 }
1867
1872 public Task<IE2eEndpoint> GetMatchingLocalKeyAsync()
1873 {
1874 return this.GetMatchingLocalKeyAsync(this.componentAddress);
1875 }
1876
1882 public async Task<IE2eEndpoint> GetMatchingLocalKeyAsync(string Address)
1883 {
1884 TaskCompletionSource<IE2eEndpoint> Result = new TaskCompletionSource<IE2eEndpoint>();
1885
1886 await this.GetMatchingLocalKey(Address, (Sender, e) =>
1887 {
1888 if (e.Ok)
1889 Result.TrySetResult(e.Key);
1890 else
1891 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get matching local key."));
1892
1893 return Task.CompletedTask;
1894
1895 }, null);
1896
1897 return await Result.Task;
1898 }
1899
1900 #endregion
1901
1902 #region ID Application Attributes
1903
1909 public Task GetIdApplicationAttributes(EventHandlerAsync<IdApplicationAttributesEventArgs> Callback, object State)
1910 {
1911 return this.client.SendIqGet(this.componentAddress, "<applicationAttributes xmlns='" + NamespaceLegalIdentitiesCurrent + "'/>", (Sender, e) =>
1912 {
1913 return Callback.Raise(this, new IdApplicationAttributesEventArgs(e));
1914
1915 }, State);
1916 }
1917
1922 public async Task<IdApplicationAttributesEventArgs> GetIdApplicationAttributesAsync()
1923 {
1924 TaskCompletionSource<IdApplicationAttributesEventArgs> Result = new TaskCompletionSource<IdApplicationAttributesEventArgs>();
1925
1926 await this.GetIdApplicationAttributes((Sender, e) =>
1927 {
1928 if (e.Ok)
1929 Result.TrySetResult(e);
1930 else
1931 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get ID Application attributes."));
1932
1933 return Task.CompletedTask;
1934 }, null);
1935
1936 return await Result.Task;
1937 }
1938
1939 #endregion
1940
1941 #region Apply for a Legal Identity
1942
1949 public Task Apply(Property[] Properties, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
1950 {
1951 return this.Apply(this.componentAddress, Properties, false, Callback, State);
1952 }
1953
1961 public Task Apply(string Address, Property[] Properties, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
1962 {
1963 return this.Apply(Address, Properties, false, Callback, State);
1964 }
1965
1974 public Task Apply(Property[] Properties, bool Preview, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
1975 {
1976 return this.Apply(this.componentAddress, Properties, Preview, Callback, State);
1977 }
1978
1988 public async Task Apply(string Address, Property[] Properties, bool Preview,
1989 EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
1990 {
1991 this.AssertAllowed();
1992
1993 await this.GetMatchingLocalKey(Address, async (Sender, e) =>
1994 {
1995 if (e.Ok)
1996 {
1997 StringBuilder Xml = new StringBuilder();
1998
1999 Xml.Append("<apply xmlns=\"");
2000 Xml.Append(NamespaceLegalIdentitiesCurrent);
2001
2002 if (Preview)
2003 Xml.Append("\" preview=\"true");
2004
2005 Xml.Append("\">");
2006
2007 StringBuilder Identity = new StringBuilder();
2008
2009 Identity.Append("<identity><clientPublicKey>");
2010 e.Key.ToXml(Identity, NamespaceLegalIdentitiesCurrent);
2011 Identity.Append("</clientPublicKey>");
2012
2013 foreach (Property Property in Properties)
2014 {
2015 Identity.Append("<property name=\"");
2016 Identity.Append(XML.Encode(Property.Name));
2017 Identity.Append("\" value=\"");
2018 Identity.Append(XML.Encode(Property.Value));
2019 Identity.Append("\"/>");
2020 }
2021
2022 string s = Identity.ToString();
2023 Xml.Append(s);
2024
2025 s += "</identity>";
2026
2027 byte[] Bin = Encoding.UTF8.GetBytes(s);
2028 byte[] Signature = e.Key.Sign(Bin);
2029
2030 Xml.Append("<clientSignature>");
2031 Xml.Append(Convert.ToBase64String(Signature));
2032 Xml.Append("</clientSignature>");
2033
2034 Xml.Append("</identity></apply>");
2035
2036 await this.client.SendIqSet(Address, Xml.ToString(), async (sender2, e2) =>
2037 {
2038 LegalIdentity Identity2 = null;
2039 XmlElement E;
2040
2041 if (e2.Ok && !((E = e2.FirstElement) is null) &&
2042 E.LocalName == "identity")
2043 {
2044 Identity2 = LegalIdentity.Parse(E);
2045 await this.UpdateSettings(Identity2, e.Key.PublicKey);
2046 }
2047 else
2048 e2.Ok = false;
2049
2050 await Callback.Raise(this, new LegalIdentityEventArgs(e2, Identity2));
2051 }, e.State);
2052 }
2053 else
2054 await Callback.Raise(this, new LegalIdentityEventArgs(e, null));
2055 }, State);
2056 }
2057
2063 public Task<LegalIdentity> ApplyAsync(Property[] Properties)
2064 {
2065 return this.ApplyAsync(this.componentAddress, Properties, false);
2066 }
2067
2074 public Task<LegalIdentity> ApplyAsync(string Address, Property[] Properties)
2075 {
2076 return this.ApplyAsync(Address, Properties, false);
2077 }
2078
2086 public Task<LegalIdentity> ApplyAsync(Property[] Properties, bool Preview)
2087 {
2088 return this.ApplyAsync(this.componentAddress, Properties, Preview);
2089 }
2090
2099 public async Task<LegalIdentity> ApplyAsync(string Address, Property[] Properties, bool Preview)
2100 {
2101 TaskCompletionSource<LegalIdentity> Result = new TaskCompletionSource<LegalIdentity>();
2102
2103 await this.Apply(Address, Properties, Preview, (Sender, e) =>
2104 {
2105 if (e.Ok)
2106 Result.TrySetResult(e.Identity);
2107 else
2108 Result.TrySetException(e.StanzaError ?? new Exception("Unable to apply for a legal identity to be registered."));
2109
2110 return Task.CompletedTask;
2111
2112 }, null);
2113
2114 return await Result.Task;
2115 }
2116
2117 #endregion
2118
2119 #region Mark Identity as Ready for Approval
2120
2129 public Task ReadyForApproval(string LegalIdentityId, EventHandlerAsync<IqResultEventArgs> Callback, object State)
2130 {
2131 return this.ReadyForApproval(this.componentAddress, LegalIdentityId, Callback, State);
2132 }
2133
2143 public Task ReadyForApproval(string Address, string LegalIdentityId, EventHandlerAsync<IqResultEventArgs> Callback, object State)
2144 {
2145 this.AssertAllowed();
2146
2147 StringBuilder Xml = new StringBuilder();
2148
2149 Xml.Append("<readyForApproval xmlns=\"");
2150 Xml.Append(NamespaceLegalIdentitiesCurrent);
2151 Xml.Append("\" id=\"");
2152 Xml.Append(XML.Encode(LegalIdentityId));
2153 Xml.Append("\"/>");
2154
2155 return this.client.SendIqSet(Address, Xml.ToString(), Callback, State);
2156 }
2157
2164 public Task ReadyForApprovalAsync(string LegalIdentityId)
2165 {
2166 return this.ReadyForApprovalAsync(this.componentAddress, LegalIdentityId);
2167 }
2168
2176 public async Task ReadyForApprovalAsync(string Address, string LegalIdentityId)
2177 {
2178 TaskCompletionSource<bool> Result = new TaskCompletionSource<bool>();
2179
2180 await this.ReadyForApproval(Address, LegalIdentityId, (Sender, e) =>
2181 {
2182 if (e.Ok)
2183 Result.TrySetResult(true);
2184 else
2185 Result.TrySetException(e.StanzaError ?? new Exception("Unable to flag identity as ready for approval."));
2186
2187 return Task.CompletedTask;
2188
2189 }, null);
2190
2191 await Result.Task;
2192 }
2193
2194 #endregion
2195
2196 #region Identity Review message
2197
2198 private async Task IdentityReviewEventHandler(object Sender, MessageEventArgs e)
2199 {
2200 string LegalId = XML.Attribute(e.Content, "id");
2202 ParseValidationDetails(e.Content, e2);
2203
2204 if ((e2.IsValid.HasValue && e2.IsValid.Value) ||
2205 (!e2.IsValid.HasValue && (e2.HasValidatedClaims || e2.HasValidatedPhotos)))
2206 {
2207 await this.AddIdentityReviewAttachment(e2);
2208 }
2209
2210 await this.IdentityReview.Raise(this, e2);
2211 }
2212
2216 public event EventHandlerAsync<IdentityReviewEventArgs> IdentityReview;
2217
2224 public static void ParseValidationDetails(XmlElement Content,
2226 {
2227 ChunkedList<InvalidClaim> InvalidClaims = null;
2228 ChunkedList<InvalidPhoto> InvalidPhotos = null;
2229 ChunkedList<ValidationError> ValidationErrors = null;
2230 ChunkedList<ValidClaim> ValidClaims = null;
2231 ChunkedList<ValidPhoto> ValidPhotos = null;
2232 ChunkedList<PotentialClaim> PotentialClaims = null;
2233 ChunkedList<string> UnvalidatedClaims = null;
2234 ChunkedList<string> UnvalidatedPhotos = null;
2235
2236 foreach (XmlNode N in Content.ChildNodes)
2237 {
2238 if (!(N is XmlElement E))
2239 continue;
2240
2241 switch (E.LocalName)
2242 {
2243 case "invalidClaim":
2244 string Claim = XML.Attribute(E, "claim");
2245 string Message = XML.Attribute(E, "message");
2246 string Code = XML.Attribute(E, "code");
2247 string Service = XML.Attribute(E, "service");
2248 string Language = XML.Attribute(E, "xml:lang");
2249
2250 InvalidClaims ??= new ChunkedList<InvalidClaim>();
2251 InvalidClaims.Add(new InvalidClaim(Claim, Message, Language, Code, Service));
2252 break;
2253
2254 case "invalidPhoto":
2255 string FileName = XML.Attribute(E, "fileName");
2256 Message = XML.Attribute(E, "message");
2257 Code = XML.Attribute(E, "code");
2258 Service = XML.Attribute(E, "service");
2259 Language = XML.Attribute(E, "xml:lang");
2260
2261 InvalidPhotos ??= new ChunkedList<InvalidPhoto>();
2262 InvalidPhotos.Add(new InvalidPhoto(FileName, Message, Language, Code, Service));
2263 break;
2264
2265 case "error":
2266 ValidationErrorType Type = XML.Attribute(E, "type", ValidationErrorType.Client);
2267 Message = XML.Attribute(E, "message");
2268 Code = XML.Attribute(E, "code");
2269 Service = XML.Attribute(E, "service");
2270 Language = XML.Attribute(E, "xml:lang");
2271
2273
2274 foreach (XmlNode N2 in E.ChildNodes)
2275 {
2276 if (!(N2 is XmlElement E2))
2277 continue;
2278
2279 if (E2.LocalName == "tag")
2280 {
2281 string TagName = XML.Attribute(E2, "name");
2282 string TagValue = XML.Attribute(E2, "value");
2283 string TagType = XML.Attribute(E2, "type");
2284
2285 if (!XmppEventReceptor.TryParse(TagValue, TagType, out object TagValueParsed))
2286 TagValueParsed = TagValue;
2287
2289 Tags.Add(new KeyValuePair<string, object>(TagName, TagValueParsed));
2290 }
2291 }
2292
2293 ValidationErrors ??= new ChunkedList<ValidationError>();
2294 ValidationErrors.Add(new ValidationError(Type, Message, Language, Code, Service,
2295 Tags?.ToArray() ?? Array.Empty<KeyValuePair<string, object>>()));
2296 break;
2297
2298 case "validatedClaim":
2299 Claim = XML.Attribute(E, "claim");
2300 Service = XML.Attribute(E, "service");
2301
2302 ValidClaims ??= new ChunkedList<ValidClaim>();
2303 ValidClaims.Add(new ValidClaim(Claim, Service));
2304 break;
2305
2306 case "validatedPhoto":
2307 FileName = XML.Attribute(E, "fileName");
2308 Service = XML.Attribute(E, "service");
2309
2310 ValidPhotos ??= new ChunkedList<ValidPhoto>();
2311 ValidPhotos.Add(new ValidPhoto(FileName, Service));
2312 break;
2313
2314 case "potentialClaim":
2315 Claim = XML.Attribute(E, "claim");
2316 string Value = XML.Attribute(E, "value");
2317 Service = XML.Attribute(E, "service");
2318
2319 PotentialClaims ??= new ChunkedList<PotentialClaim>();
2320 PotentialClaims.Add(new PotentialClaim(Claim, Value, Service));
2321 break;
2322
2323 case "unvalidatedClaim":
2324 Claim = XML.Attribute(E, "claim");
2325
2326 UnvalidatedClaims ??= new ChunkedList<string>();
2327 UnvalidatedClaims.Add(Claim);
2328 break;
2329
2330 case "unvalidatedPhoto":
2331 FileName = XML.Attribute(E, "fileName");
2332
2333 UnvalidatedPhotos ??= new ChunkedList<string>();
2334 UnvalidatedPhotos.Add(FileName);
2335 break;
2336 }
2337 }
2338
2339 e.InvalidClaims = InvalidClaims?.ToArray();
2340 e.InvalidPhotos = InvalidPhotos?.ToArray();
2341 e.ValidationErrors = ValidationErrors?.ToArray();
2342 e.ValidClaims = ValidClaims?.ToArray();
2343 e.ValidPhotos = ValidPhotos?.ToArray();
2344 e.PotentialClaims = PotentialClaims?.ToArray();
2345 e.UnvalidatedClaims = UnvalidatedClaims?.ToArray();
2346 e.UnvalidatedPhotos = UnvalidatedPhotos?.ToArray();
2347 }
2348
2354 private async Task<LegalIdentity> AddIdentityReviewAttachment(IdentityReviewEventArgs e)
2355 {
2356 string Xml = e.Content.OuterXml;
2357 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
2358 string FileName = "ApplicationReview.xml";
2359 string ContentType = XmlCodec.DefaultContentType + "; charset=utf-8";
2360
2361 return await this.UploadLegalIdAttachmentAsync(e.LegalId, FileName, Data, ContentType);
2362 }
2363
2364 #endregion
2365
2366 #region Client Message
2367
2368 private async Task ClientMessageEventHandler(object Sender, MessageEventArgs e)
2369 {
2370 string LegalId = XML.Attribute(e.Content, "id");
2371 string Code = XML.Attribute(e.Content, "code");
2373 ClientMessageEventArgs e2 = new ClientMessageEventArgs(e, LegalId, Code, Type);
2374 ParseValidationDetails(e.Content, e2);
2375
2376 await this.ClientMessage.Raise(this, e2);
2377 }
2378
2382 public event EventHandlerAsync<ClientMessageEventArgs> ClientMessage;
2383
2384 #endregion
2385
2386 #region Validate Legal Identity
2387
2394 public Task Validate(LegalIdentity Identity, EventHandlerAsync<IdentityValidationEventArgs> Callback, object State)
2395 {
2396 return this.Validate(Identity, true, true, Callback, State);
2397 }
2398
2406 public Task Validate(LegalIdentity Identity, bool ValidateState, EventHandlerAsync<IdentityValidationEventArgs> Callback, object State)
2407 {
2408 return this.Validate(Identity, ValidateState, true, Callback, State);
2409 }
2410
2419 public async Task Validate(LegalIdentity Identity, bool ValidateState, bool ValidateAttachments,
2420 EventHandlerAsync<IdentityValidationEventArgs> Callback, object State)
2421 {
2422 if (Identity is null)
2423 {
2424 await this.ReturnStatus(IdentityStatus.IdentityUndefined, Callback, State);
2425 return;
2426 }
2427
2428 if (ValidateState && Identity.State != IdentityState.Approved)
2429 {
2430 await this.ReturnStatus(IdentityStatus.NotApproved, Callback, State,
2431 new KeyValuePair<string, object>("State", Identity.State));
2432 return;
2433 }
2434
2435 DateTime UtcNow = DateTime.UtcNow;
2436
2437 if (UtcNow < Identity.From.ToUniversalTime()) // To avoid Time-zone problems
2438 {
2439 await this.ReturnStatus(IdentityStatus.NotValidYet, Callback, State,
2440 new KeyValuePair<string, object>("From", Identity.From));
2441 return;
2442 }
2443
2444 if (UtcNow > Identity.To.ToUniversalTime()) // To avoid Time-zone problems
2445 {
2446 await this.ReturnStatus(IdentityStatus.NotValidAnymore, Callback, State,
2447 new KeyValuePair<string, object>("To", Identity.To));
2448 return;
2449 }
2450
2451 if (string.IsNullOrEmpty(Identity.Provider))
2452 {
2453 await this.ReturnStatus(IdentityStatus.NoTrustProvider, Callback, State);
2454 return;
2455 }
2456
2457 if (string.IsNullOrEmpty(Identity.ClientKeyName) ||
2458 Identity.ClientPubKey is null || Identity.ClientPubKey.Length == 0)
2459 {
2460 await this.ReturnStatus(IdentityStatus.NoClientPublicKey, Callback, State);
2461 return;
2462 }
2463
2464 if (Identity.ClientSignature is null || Identity.ClientSignature.Length == 0)
2465 {
2466 await this.ReturnStatus(IdentityStatus.NoClientSignature, Callback, State);
2467 return;
2468 }
2469
2470 StringBuilder Xml = new StringBuilder();
2471 Identity.Serialize(Xml, false, false, false, false, false, false, false);
2472 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
2473
2474 bool? b = this.ValidateSignature(Identity, Data, Identity.ClientSignature);
2475 if (b.HasValue)
2476 {
2477 if (!b.Value)
2478 {
2479 await this.ReturnStatus(IdentityStatus.ClientSignatureInvalid, Callback, State,
2480 new KeyValuePair<string, object>("DataBase64", Convert.ToBase64String(Data)),
2481 new KeyValuePair<string, object>("SignatureBase64", Convert.ToBase64String(Identity.ClientSignature)),
2482 new KeyValuePair<string, object>("ClientKeyName", Identity.ClientKeyName),
2483 new KeyValuePair<string, object>("ClientPubKeyBase64", Convert.ToBase64String(Identity.ClientPubKey)));
2484 return;
2485 }
2486 }
2487 else
2488 {
2489 await this.ReturnStatus(IdentityStatus.ClientKeyNotRecognized, Callback, State,
2490 new KeyValuePair<string, object>("KeyName", Identity.ClientKeyName));
2491 return;
2492 }
2493
2494 if (Identity.State == IdentityState.Approved && ValidateState &&
2495 ValidateAttachments && !(Identity.Attachments is null))
2496 {
2497 foreach (Attachment Attachment in Identity.Attachments)
2498 {
2499 if (string.IsNullOrEmpty(Attachment.Url))
2500 {
2501 await this.ReturnStatus(IdentityStatus.AttachmentLacksUrl, Callback, State,
2502 new KeyValuePair<string, object>("AttachmentId", Attachment.Id));
2503 return;
2504 }
2505
2506 try
2507 {
2508 KeyValuePair<string, TemporaryFile> P = await this.GetAttachmentAsync(Attachment.Url, SignWith.LatestApprovedIdOrCurrentKeys, 30000);
2509 using TemporaryFile File = P.Value;
2510
2511 if (P.Key != Attachment.ContentType)
2512 {
2513 await this.ReturnStatus(IdentityStatus.AttachmentInconsistency, Callback, State,
2514 new KeyValuePair<string, object>("AttachmentId", Attachment.Id),
2515 new KeyValuePair<string, object>("AttachmentUrl", Attachment.Url),
2516 new KeyValuePair<string, object>("ExpectedContentType", Attachment.ContentType),
2517 new KeyValuePair<string, object>("ContentType", P.Key));
2518 return;
2519 }
2520
2521 File.Position = 0;
2522
2523 b = this.ValidateSignature(Identity, File, Attachment.Signature);
2524 if (b.HasValue)
2525 {
2526 if (!b.Value)
2527 {
2528 await this.ReturnStatus(IdentityStatus.AttachmentSignatureInvalid, Callback, State,
2529 new KeyValuePair<string, object>("AttachmentId", Attachment.Id),
2530 new KeyValuePair<string, object>("AttachmentUrl", Attachment.Url),
2531 new KeyValuePair<string, object>("AttachmentSignatureBase64", Convert.ToBase64String(Attachment.Signature)));
2532 return;
2533 }
2534 }
2535 else
2536 {
2537 await this.ReturnStatus(IdentityStatus.ClientKeyNotRecognized, Callback, State,
2538 new KeyValuePair<string, object>("AttachmentId", Attachment.Id),
2539 new KeyValuePair<string, object>("AttachmentUrl", Attachment.Url),
2540 new KeyValuePair<string, object>("KeyName", Identity.ClientKeyName));
2541 return;
2542 }
2543 }
2544 catch (Exception ex)
2545 {
2546 this.client.Error("Attachment " + Attachment.Url + "unavailable: " + ex.Message);
2547 await this.ReturnStatus(IdentityStatus.AttachmentUnavailable, Callback, State,
2548 new KeyValuePair<string, object>("AttachmentId", Attachment.Id),
2549 new KeyValuePair<string, object>("AttachmentUrl", Attachment.Url),
2550 new KeyValuePair<string, object>("Error", ex.Message));
2551 return;
2552 }
2553 }
2554 }
2555
2556 if (Identity.ServerSignature is null || Identity.ServerSignature.Length == 0)
2557 {
2558 await this.ReturnStatus(IdentityStatus.NoProviderSignature, Callback, State);
2559 return;
2560 }
2561
2562 Xml.Clear();
2563 Identity.Serialize(Xml, false, true, true, true, true, false, false);
2564 Data = Encoding.UTF8.GetBytes(Xml.ToString());
2565
2566 bool HasOldPublicKey = this.publicKeys.TryGetRecord(Identity.Provider,
2567 Identity.Updated, out _);
2568
2569 await this.GetServerPublicKey(Identity.Provider, Identity.Updated, async (Sender, e) =>
2570 {
2571 if (e.Ok && !(e.Key is null))
2572 {
2573 bool Valid = e.Key.Verify(Data, Identity.ServerSignature);
2574
2575 if (Valid)
2576 {
2577 await this.ReturnStatus(IdentityStatus.Valid, Callback, State);
2578 return;
2579 }
2580
2581 if (!HasOldPublicKey)
2582 {
2583 await this.ReturnStatus(IdentityStatus.ProviderSignatureInvalid, Callback, State,
2584 new KeyValuePair<string, object>("Provider", Identity.Provider),
2585 new KeyValuePair<string, object>("LocalName", e.Key.LocalName),
2586 new KeyValuePair<string, object>("Namespace", e.Key.Namespace),
2587 new KeyValuePair<string, object>("PublicKeyBase64", e.Key.PublicKeyBase64),
2588 new KeyValuePair<string, object>("DataBase64", Convert.ToBase64String(Data)),
2589 new KeyValuePair<string, object>("SignatureBase64", Convert.ToBase64String(Identity.ServerSignature)));
2590 return;
2591 }
2592
2593 this.publicKeys.Remove(Identity.Provider);
2594
2595 await this.GetServerPublicKey(Identity.Provider, Identity.Updated,
2596 (sender2, e2) =>
2597 {
2598 if (e2.Ok && !(e2.Key is null))
2599 {
2600 if (e.Key.Equals(e2.Key))
2601 {
2602 return this.ReturnStatus(IdentityStatus.ProviderSignatureInvalid, Callback, State,
2603 new KeyValuePair<string, object>("Provider", Identity.Provider),
2604 new KeyValuePair<string, object>("LocalName", e.Key.LocalName),
2605 new KeyValuePair<string, object>("Namespace", e.Key.Namespace),
2606 new KeyValuePair<string, object>("PublicKeyBase64", e.Key.PublicKeyBase64),
2607 new KeyValuePair<string, object>("DataBase64", Convert.ToBase64String(Data)),
2608 new KeyValuePair<string, object>("SignatureBase64", Convert.ToBase64String(Identity.ServerSignature)));
2609 }
2610
2611 Valid = e2.Key.Verify(Data, Identity.ServerSignature);
2612
2613 if (Valid)
2614 return this.ReturnStatus(IdentityStatus.Valid, Callback, State);
2615 else
2616 {
2617 return this.ReturnStatus(IdentityStatus.ProviderSignatureInvalid, Callback, State,
2618 new KeyValuePair<string, object>("Provider", Identity.Provider),
2619 new KeyValuePair<string, object>("LocalName", e2.Key.LocalName),
2620 new KeyValuePair<string, object>("Namespace", e2.Key.Namespace),
2621 new KeyValuePair<string, object>("PublicKeyBase64", e.Key.PublicKeyBase64),
2622 new KeyValuePair<string, object>("DataBase64", Convert.ToBase64String(Data)),
2623 new KeyValuePair<string, object>("SignatureBase64", Convert.ToBase64String(Identity.ServerSignature)));
2624 }
2625 }
2626 else
2627 {
2628 return this.ReturnStatus(IdentityStatus.NoProviderPublicKey, Callback, State,
2629 new KeyValuePair<string, object>("Provider", Identity.Provider));
2630 }
2631
2632 }, State);
2633 }
2635 {
2636 await this.ReturnStatus(IdentityStatus.NoResponse, Callback, State,
2637 new KeyValuePair<string, object>("Provider", Identity.Provider));
2638 }
2639 else
2640 {
2641 await this.ReturnStatus(IdentityStatus.NoProviderPublicKey, Callback, State,
2642 new KeyValuePair<string, object>("Provider", Identity.Provider));
2643 }
2644
2645 }, State);
2646 }
2647
2659 public bool? ValidateSignature(LegalIdentity Identity, byte[] Data, byte[] Signature)
2660 {
2661 if (Identity.ClientKeyName.StartsWith("RSA") &&
2662 int.TryParse(Identity.ClientKeyName[3..], out int KeySize))
2663 {
2664 return RsaEndpoint.Verify(Data, Signature, KeySize, Identity.ClientPubKey);
2665 }
2667 Identity.Namespace.Replace(":iot:leg:id:", ":iot:e2e:").Replace("urn:ieee:", "urn:nf:"),
2668 out IE2eEndpoint LocalKey) &&
2669 LocalKey is EllipticCurveEndpoint LocalEc)
2670 {
2671 return LocalEc.Verify(Data, Identity.ClientPubKey, Signature);
2672 }
2673 else
2674 return null;
2675 }
2676
2688 public bool? ValidateSignature(LegalIdentity Identity, Stream Data, byte[] Signature)
2689 {
2690 if (Identity.ClientKeyName.StartsWith("RSA") &&
2691 int.TryParse(Identity.ClientKeyName[3..], out int KeySize))
2692 {
2693 return RsaEndpoint.Verify(Data, Signature, KeySize, Identity.ClientPubKey);
2694 }
2696 Identity.Namespace.Replace(":iot:leg:id:", ":iot:e2e:").Replace("urn:ieee:", "urn:nf:"),
2697 out IE2eEndpoint LocalKey) &&
2698 LocalKey is EllipticCurveEndpoint LocalEc)
2699 {
2700 return LocalEc.Verify(Data, Identity.ClientPubKey, Signature);
2701 }
2702 else
2703 return null;
2704 }
2705
2706 private async Task ReturnStatus(IdentityStatus Status, EventHandlerAsync<IdentityValidationEventArgs> Callback, object State,
2707 params KeyValuePair<string, object>[] Tags)
2708 {
2709 await Callback.Raise(this, new IdentityValidationEventArgs(Status, State, Tags));
2710 }
2711
2717 public Task<IdentityValidationEventArgs> ValidateAsync(LegalIdentity Identity)
2718 {
2719 return this.ValidateAsync(Identity, true, true);
2720 }
2721
2728 public Task<IdentityValidationEventArgs> ValidateAsync(LegalIdentity Identity, bool ValidateState)
2729 {
2730 return this.ValidateAsync(Identity, ValidateState, true);
2731 }
2732
2740 public async Task<IdentityValidationEventArgs> ValidateAsync(LegalIdentity Identity,
2741 bool ValidateState, bool ValidateAttachments)
2742 {
2743 TaskCompletionSource<IdentityValidationEventArgs> Result = new TaskCompletionSource<IdentityValidationEventArgs>();
2744
2745 await this.Validate(Identity, ValidateState, ValidateAttachments, (Sender, e) =>
2746 {
2747 Result.TrySetResult(e);
2748 return Task.CompletedTask;
2749 }, null);
2750
2751 return await Result.Task;
2752 }
2753
2754 #endregion
2755
2756 #region Legal Identity update event
2757
2758 private bool IsFromTrustProvider(string Id, string From)
2759 {
2760 int i = Id.IndexOf('@');
2761 if (i < 0)
2762 return false;
2763
2764 Id = Id[(i + 1)..];
2765
2766 i = From.IndexOf('@');
2767 if (i >= 0)
2768 return false;
2769
2770 return (string.Compare(Id, From, true) == 0 ||
2771 From.EndsWith("." + Id, StringComparison.CurrentCultureIgnoreCase));
2772 }
2773
2774 private async Task IdentityMessageHandler(object Sender, MessageEventArgs e)
2775 {
2777
2778 if (!this.IsFromTrustProvider(Identity.Id, e.From))
2779 {
2780 this.client.Warning("Incoming identity message discarded: " + Identity.Id + " not from " + e.From + ".");
2781 return;
2782 }
2783
2784 if (string.Compare(e.FromBareJID, Identity.Provider, true) != 0)
2785 {
2786 this.client.Warning("Incoming identity message discarded: Sender " + e.FromBareJID + " not equal to Trust Provider " + Identity.Provider + ".");
2787 return;
2788 }
2789
2790 await this.Validate(Identity, false, async (sender2, e2) =>
2791 {
2792 if (e2.Status != IdentityStatus.Valid)
2793 {
2794 this.client.Warning("Invalid legal identity received and discarded. Validation status: " + e2.Status.ToString());
2795
2796 Log.Warning("Invalid legal identity received and discarded.", this.client.BareJID, e.From,
2797 new KeyValuePair<string, object>("Status", e2.Status));
2798
2799 return;
2800 }
2801
2802 await this.UpdateSettings(Identity);
2803 await this.IdentityUpdated.Raise(this, new LegalIdentityEventArgs(new IqResultEventArgs(e.Message, e.Id, e.To, e.From, e.Ok, null), Identity));
2804
2805 }, null);
2806 }
2807
2808 private Task UpdateSettings(LegalIdentity Identity)
2809 {
2810 return this.UpdateSettings(Identity, Identity?.ClientPubKey);
2811 }
2812
2819 public Task<bool> HasPrivateKey(LegalIdentity Identity)
2820 {
2821 return this.HasPrivateKey(Identity.Id);
2822 }
2823
2830 public async Task<bool> HasPrivateKey(string IdentityId)
2831 {
2832 LegalIdentityState State = await Database.FindFirstIgnoreRest<LegalIdentityState>(new FilterAnd(
2833 new FilterFieldEqualTo("BareJid", this.client.BareJID),
2834 new FilterFieldEqualTo("LegalId", IdentityId)));
2835
2836 using LoadedKey Key = await this.TryGetLegalIdentityEndpointAsync(State, true, false);
2837
2838 return !(Key.Endpoint is null);
2839 }
2840
2845 public Task<string> GetLatestApprovedLegalId()
2846 {
2847 return this.GetLatestApprovedLegalId(null);
2848 }
2849
2855 public async Task<string> GetLatestApprovedLegalId(byte[] PublicKey)
2856 {
2857 string PublicKeyBase64 = PublicKey is null ? string.Empty : Convert.ToBase64String(PublicKey);
2858
2859 foreach (LegalIdentityState State in await Database.Find<LegalIdentityState>(new FilterAnd(
2860 new FilterFieldEqualTo("BareJid", this.client.BareJID),
2861 new FilterFieldEqualTo("State", IdentityState.Approved)), "-Timestamp"))
2862 {
2863 using LoadedKey Key = await this.TryGetLegalIdentityEndpointAsync(State, true, false);
2864
2865 if (Key.Endpoint is null || State.PublicKey is null)
2866 continue;
2867
2868 if (!(PublicKey is null) && Convert.ToBase64String(State.PublicKey) != PublicKeyBase64)
2869 continue;
2870
2871 return State.LegalId;
2872 }
2873
2874 return null;
2875 }
2876
2877 private async Task<LoadedKey> GetLatestApprovedKey(bool ExceptionIfNone)
2878 {
2879 bool HaveStates = false;
2880
2881 foreach (LegalIdentityState State in await Database.Find<LegalIdentityState>(new FilterAnd(
2882 new FilterFieldEqualTo("BareJid", this.client.BareJID),
2883 new FilterFieldEqualTo("State", IdentityState.Approved)), "-Timestamp"))
2884 {
2885 HaveStates = true;
2886
2887 LoadedKey Key = await this.TryGetLegalIdentityEndpointAsync(State, true, false);
2888 if (!(Key.Endpoint is null))
2889 return Key;
2890
2891 Key.Dispose();
2892 }
2893
2894 if (ExceptionIfNone)
2895 {
2896 if (HaveStates)
2897 {
2898 throw new Exception("Private keys are not available on this device (" + this.client.BareJID +
2899 "). Were they created on another device?");
2900 }
2901 else
2902 throw new Exception("No approved legal identity available on this device (" + this.client.BareJID + ").");
2903 }
2904
2905 return new LoadedKey(null, false, DateTime.MinValue);
2906 }
2907
2908 private async Task<LegalIdentityState> FindPreviewStateAsync(byte[] PublicKey, string LegalId)
2909 {
2910 if (PublicKey is null)
2911 return null;
2912
2913 foreach (LegalIdentityState State in await Database.Find<LegalIdentityState>(new FilterAnd(
2914 new FilterFieldEqualTo("BareJid", this.client.BareJID),
2915 new FilterFieldEqualTo("State", IdentityState.Created))))
2916 {
2917 if (State.LegalId != LegalId && AreEqual(State.PublicKey, PublicKey))
2918 return State;
2919 }
2920
2921 return null;
2922 }
2923
2924 private async Task UpdateSettings(LegalIdentity Identity, byte[] PublicKey)
2925 {
2926 PublicKey ??= Identity?.ClientPubKey;
2927
2928 if (!string.IsNullOrEmpty(Identity.Id))
2929 {
2930 LegalIdentityState StateObj = Types.Instantiate<LegalIdentityState>(false, Identity.Id);
2931
2932 if (string.IsNullOrEmpty(StateObj.ObjectId))
2933 {
2934 LegalIdentityState StateObj2 = await Database.FindFirstDeleteRest<LegalIdentityState>(new FilterAnd(
2935 new FilterFieldEqualTo("BareJid", this.client.BareJID),
2936 new FilterFieldEqualTo("LegalId", Identity.Id)));
2937
2938 if (StateObj2 is null)
2939 {
2940 StateObj2 = await this.FindPreviewStateAsync(PublicKey, Identity.Id);
2941 if (StateObj2 is null)
2942 StateObj.BareJid = this.client.BareJID;
2943 else
2944 {
2945 if (!string.IsNullOrEmpty(StateObj2.LegalId))
2946 Types.UnregisterSingleton(StateObj2, StateObj2.LegalId);
2947
2948 StateObj2.LegalId = Identity.Id;
2949 Types.ReplaceSingleton(StateObj2, Identity.Id);
2950 StateObj = StateObj2;
2951 }
2952 }
2953 else
2954 {
2955 Types.ReplaceSingleton(StateObj2, Identity.Id);
2956 StateObj = StateObj2;
2957 }
2958 }
2959
2960 DateTime Timestamp = Identity.Updated > Identity.Created ? Identity.Updated : Identity.Created;
2961
2962 if (Timestamp > StateObj.Timestamp ||
2963 (StateObj.PublicKey is null && !(PublicKey is null)) ||
2964 (!StateObj.HasPrivateKey && !(PublicKey is null)) ||
2965 (string.IsNullOrEmpty(StateObj.KeyName) && !(PublicKey is null)) ||
2966 Identity.State > StateObj.State)
2967 {
2968 StateObj.State = Identity.State;
2969 StateObj.Timestamp = Timestamp;
2970
2971 if (PublicKey is null)
2972 {
2973 switch (Identity.State)
2974 {
2975 case IdentityState.Compromised:
2976 case IdentityState.Obsoleted:
2977 case IdentityState.Rejected:
2978 StateObj.PublicKey = null;
2979 break;
2980 }
2981 }
2982 else
2983 {
2984 if (await this.LoadKeys(false))
2985 {
2986 IE2eEndpoint Endpoint = this.LocalEndpoint.FindLocalEndpoint(PublicKey);
2987
2988 if (!(Endpoint is null))
2989 await this.SetLegalIdentityKeySnapshotAsync(StateObj, Endpoint);
2990 else
2991 StateObj.PublicKey = Clone(PublicKey);
2992 }
2993 else
2994 StateObj.PublicKey = Clone(PublicKey);
2995 }
2996
2997 if (string.IsNullOrEmpty(StateObj.ObjectId))
2998 await Database.Insert(StateObj);
2999 else
3000 await Database.Update(StateObj);
3001 }
3002 }
3003 }
3004
3009 public event EventHandlerAsync<LegalIdentityEventArgs> IdentityUpdated = null;
3010
3011 #endregion
3012
3013 #region Get Legal Identities
3014
3020 public Task GetLegalIdentities(EventHandlerAsync<LegalIdentitiesEventArgs> Callback, object State)
3021 {
3022 return this.GetLegalIdentities(this.componentAddress, Callback, State);
3023 }
3024
3031 public Task GetLegalIdentities(string Address, EventHandlerAsync<LegalIdentitiesEventArgs> Callback, object State)
3032 {
3033 return this.client.SendIqGet(Address, "<getLegalIdentities xmlns=\"" + NamespaceLegalIdentitiesCurrent + "\"/>",
3034 this.IdentitiesResponse, new object[] { Callback, State });
3035 }
3036
3037 private async Task IdentitiesResponse(object Sender, IqResultEventArgs e)
3038 {
3039 object[] P = (object[])e.State;
3040 EventHandlerAsync<LegalIdentitiesEventArgs> Callback = (EventHandlerAsync<LegalIdentitiesEventArgs>)P[0];
3041 LegalIdentity[] Identities = null;
3042 XmlElement E;
3043
3044 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "identities")
3045 {
3046 List<LegalIdentity> IdentitiesList = new List<LegalIdentity>();
3047
3048 foreach (XmlNode N in E.ChildNodes)
3049 {
3050 if (N is XmlElement E2 && E2.LocalName == "identity")
3051 {
3052 LegalIdentity Identity = LegalIdentity.Parse(E2);
3053 IdentitiesList.Add(Identity);
3054
3055 try
3056 {
3057 await this.UpdateSettings(Identity);
3058 }
3059 catch (Exception ex)
3060 {
3061 Log.Exception(ex, Identity.Id);
3062 }
3063 }
3064 }
3065
3066 Identities = IdentitiesList.ToArray();
3067 }
3068 else
3069 e.Ok = false;
3070
3071 e.State = P[1];
3072 await Callback.Raise(this, new LegalIdentitiesEventArgs(e, Identities));
3073 }
3074
3079 public Task<LegalIdentity[]> GetLegalIdentitiesAsync()
3080 {
3081 return this.GetLegalIdentitiesAsync(this.componentAddress);
3082 }
3083
3089 public async Task<LegalIdentity[]> GetLegalIdentitiesAsync(string Address)
3090 {
3091 TaskCompletionSource<LegalIdentity[]> Result = new TaskCompletionSource<LegalIdentity[]>();
3092
3093 await this.GetLegalIdentities(Address, (Sender, e) =>
3094 {
3095 if (e.Ok)
3096 Result.TrySetResult(e.Identities);
3097 else
3098 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get legal identities."));
3099
3100 return Task.CompletedTask;
3101
3102 }, null);
3103
3104 return await Result.Task;
3105 }
3106
3107 #endregion
3108
3109 #region Get Legal Identity
3110
3117 public Task GetLegalIdentity(string LegalIdentityId, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
3118 {
3119 return this.GetLegalIdentity(this.GetTrustProvider(LegalIdentityId), LegalIdentityId, Callback, State);
3120 }
3121
3127 public string GetTrustProvider(string EntityId)
3128 {
3129 int i = EntityId.IndexOf('@');
3130 if (i < 0)
3131 return this.componentAddress;
3132 else
3133 return EntityId[(i + 1)..];
3134 }
3135
3143 public Task GetLegalIdentity(string Address, string LegalIdentityId, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
3144 {
3145 return this.client.SendIqGet(Address, "<getLegalIdentity id=\"" + XML.Encode(LegalIdentityId) + "\" xmlns=\"" +
3146 NamespaceLegalIdentitiesCurrent + "\"/>", async (Sender, e) =>
3147 {
3148 LegalIdentity Identity = null;
3149 XmlElement E;
3150
3151 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "identity")
3152 Identity = LegalIdentity.Parse(E);
3153 else
3154 e.Ok = false;
3155
3156 await Callback.Raise(this, new LegalIdentityEventArgs(e, Identity));
3157 }, State);
3158 }
3159
3165 public Task<LegalIdentity> GetLegalIdentityAsync(string LegalIdentityId)
3166 {
3167 return this.GetLegalIdentityAsync(this.GetTrustProvider(LegalIdentityId), LegalIdentityId);
3168 }
3169
3176 public async Task<LegalIdentity> GetLegalIdentityAsync(string Address, string LegalIdentityId)
3177 {
3178 TaskCompletionSource<LegalIdentity> Result = new TaskCompletionSource<LegalIdentity>();
3179
3180 await this.GetLegalIdentity(Address, LegalIdentityId, (Sender, e) =>
3181 {
3182 if (e.Ok)
3183 Result.TrySetResult(e.Identity);
3184 else
3185 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get legal identity."));
3186
3187 return Task.CompletedTask;
3188
3189 }, null);
3190
3191 return await Result.Task;
3192 }
3193
3194 #endregion
3195
3196 #region Obsolete Legal Identity
3197
3204 public Task ObsoleteLegalIdentity(string LegalIdentityId, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
3205 {
3206 return this.ObsoleteLegalIdentity(this.GetTrustProvider(LegalIdentityId), LegalIdentityId, Callback, State);
3207 }
3208
3216 public Task ObsoleteLegalIdentity(string Address, string LegalIdentityId, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
3217 {
3218 this.AssertAllowed();
3219
3220 return this.client.SendIqSet(Address, "<obsoleteLegalIdentity id=\"" + XML.Encode(LegalIdentityId) + "\" xmlns=\"" +
3221 NamespaceLegalIdentitiesCurrent + "\"/>", async (Sender, e) =>
3222 {
3223 LegalIdentity Identity = null;
3224 XmlElement E;
3225
3226 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "identity")
3227 {
3228 Identity = LegalIdentity.Parse(E);
3229 await this.UpdateSettings(Identity);
3230 }
3231 else
3232 e.Ok = false;
3233
3234 await Callback.Raise(this, new LegalIdentityEventArgs(e, Identity));
3235 }, State);
3236 }
3237
3243 public Task<LegalIdentity> ObsoleteLegalIdentityAsync(string LegalIdentityId)
3244 {
3245 return this.ObsoleteLegalIdentityAsync(this.GetTrustProvider(LegalIdentityId), LegalIdentityId);
3246 }
3247
3254 public async Task<LegalIdentity> ObsoleteLegalIdentityAsync(string Address, string LegalIdentityId)
3255 {
3256 TaskCompletionSource<LegalIdentity> Result = new TaskCompletionSource<LegalIdentity>();
3257
3258 await this.ObsoleteLegalIdentity(Address, LegalIdentityId, (Sender, e) =>
3259 {
3260 if (e.Ok)
3261 Result.TrySetResult(e.Identity);
3262 else
3263 Result.TrySetException(e.StanzaError ?? new Exception("Unable to obsolete legal identity."));
3264
3265 return Task.CompletedTask;
3266
3267 }, null);
3268
3269 return await Result.Task;
3270 }
3271
3272 #endregion
3273
3274 #region Compromised Legal Identity
3275
3282 public Task CompromisedLegalIdentity(string LegalIdentityId, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
3283 {
3284 return this.CompromisedLegalIdentity(this.GetTrustProvider(LegalIdentityId), LegalIdentityId, Callback, State);
3285 }
3286
3294 public Task CompromisedLegalIdentity(string Address, string LegalIdentityId, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
3295 {
3296 this.AssertAllowed();
3297
3298 return this.client.SendIqSet(Address, "<compromisedLegalIdentity id=\"" + XML.Encode(LegalIdentityId) + "\" xmlns=\"" +
3299 NamespaceLegalIdentitiesCurrent + "\"/>", async (Sender, e) =>
3300 {
3301 LegalIdentity Identity = null;
3302 XmlElement E;
3303
3304 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "identity")
3305 {
3306 Identity = LegalIdentity.Parse(E);
3307 await this.UpdateSettings(Identity);
3308 }
3309 else
3310 e.Ok = false;
3311
3312 await Callback.Raise(this, new LegalIdentityEventArgs(e, Identity));
3313 }, State);
3314 }
3315
3321 public Task<LegalIdentity> CompromisedLegalIdentityAsync(string LegalIdentityId)
3322 {
3323 return this.CompromisedLegalIdentityAsync(this.GetTrustProvider(LegalIdentityId), LegalIdentityId);
3324 }
3325
3332 public async Task<LegalIdentity> CompromisedLegalIdentityAsync(string Address, string LegalIdentityId)
3333 {
3334 TaskCompletionSource<LegalIdentity> Result = new TaskCompletionSource<LegalIdentity>();
3335
3336 await this.CompromisedLegalIdentity(Address, LegalIdentityId, (Sender, e) =>
3337 {
3338 if (e.Ok)
3339 Result.TrySetResult(e.Identity);
3340 else
3341 Result.TrySetException(e.StanzaError ?? new Exception("Unable to compromise legal identity."));
3342
3343 return Task.CompletedTask;
3344
3345 }, null);
3346
3347 return await Result.Task;
3348 }
3349
3350 #endregion
3351
3352 #region Signatures
3353
3361 public Task Sign(byte[] Data, SignWith SignWith, EventHandlerAsync<SignatureEventArgs> Callback, object State)
3362 {
3363 return this.Sign(this.componentAddress, Data, SignWith, Callback, State);
3364 }
3365
3374 public async Task Sign(string Address, byte[] Data, SignWith SignWith, EventHandlerAsync<SignatureEventArgs> Callback, object State)
3375 {
3376 this.AssertAllowed();
3377
3378 byte[] Signature = null;
3379 LoadedKey KeyInfo = SignWith switch
3380 {
3381 SignWith.CurrentKeys => new LoadedKey(null, false, DateTime.MinValue),
3382 SignWith.LatestApprovedId => await this.GetLatestApprovedKey(true),
3383 _ => await this.GetLatestApprovedKey(false),
3384 };
3385 IE2eEndpoint Key = KeyInfo.Endpoint;
3386
3387 if (Key is null)
3388 {
3389 KeyInfo.Dispose();
3390
3391 await this.GetMatchingLocalKey(Address, async (Sender, e) =>
3392 {
3393 if (e.Ok)
3394 Signature = e.Key.Sign(Data);
3395
3396 await Callback.Raise(this, new SignatureEventArgs(e, Signature));
3397
3398 }, State);
3399 }
3400 else
3401 {
3402 using (KeyInfo)
3403 {
3404 Signature = Key.Sign(Data);
3405
3406 await Callback.Raise(this, new SignatureEventArgs(Key, Signature,
3407 State, KeyInfo.Timestamp));
3408 }
3409 }
3410 }
3411
3418 public Task<byte[]> SignAsync(byte[] Data, SignWith SignWith)
3419 {
3420 return this.SignAsync(this.componentAddress, Data, SignWith);
3421 }
3422
3430 public async Task<byte[]> SignAsync(string Address, byte[] Data, SignWith SignWith)
3431 {
3432 TaskCompletionSource<byte[]> Result = new TaskCompletionSource<byte[]>();
3433
3434 await this.Sign(Address, Data, SignWith, (Sender, e) =>
3435 {
3436 if (e.Ok)
3437 Result.TrySetResult(e.Signature);
3438 else
3439 Result.TrySetException(e.StanzaError ?? new Exception("Unable to sign data."));
3440
3441 return Task.CompletedTask;
3442
3443 }, null);
3444
3445 return await Result.Task;
3446 }
3447
3455 public Task Sign(Stream Data, SignWith SignWith, EventHandlerAsync<SignatureEventArgs> Callback, object State)
3456 {
3457 return this.Sign(this.componentAddress, Data, SignWith, Callback, State);
3458 }
3459
3468 public async Task Sign(string Address, Stream Data, SignWith SignWith, EventHandlerAsync<SignatureEventArgs> Callback, object State)
3469 {
3470 this.AssertAllowed();
3471
3472 LoadedKey KeyInfo = SignWith == SignWith.CurrentKeys ?
3473 new LoadedKey(null, false, DateTime.MinValue)
3474 : await this.GetLatestApprovedKey(true);
3475 IE2eEndpoint Key = KeyInfo.Endpoint;
3476 byte[] Signature = null;
3477
3478 if (Key is null)
3479 {
3480 KeyInfo.Dispose();
3481
3482 await this.GetMatchingLocalKey(Address, async (Sender, e) =>
3483 {
3484 if (e.Ok)
3485 Signature = e.Key.Sign(Data);
3486
3487 await Callback.Raise(this, new SignatureEventArgs(e, Signature));
3488
3489 }, State);
3490 }
3491 else
3492 {
3493 using (KeyInfo)
3494 {
3495 Signature = Key.Sign(Data);
3496
3497 await Callback.Raise(this, new SignatureEventArgs(Key, Signature,
3498 State, KeyInfo.Timestamp));
3499 }
3500 }
3501 }
3502
3509 public Task<byte[]> SignAsync(Stream Data, SignWith SignWith)
3510 {
3511 return this.SignAsync(this.componentAddress, Data, SignWith);
3512 }
3513
3521 public async Task<byte[]> SignAsync(string Address, Stream Data, SignWith SignWith)
3522 {
3523 TaskCompletionSource<byte[]> Result = new TaskCompletionSource<byte[]>();
3524
3525 await this.Sign(Address, Data, SignWith, (Sender, e) =>
3526 {
3527 if (e.Ok)
3528 Result.TrySetResult(e.Signature);
3529 else
3530 Result.TrySetException(e.StanzaError ?? new Exception("Unable to sign data."));
3531
3532 return Task.CompletedTask;
3533
3534 }, null);
3535
3536 return await Result.Task;
3537 }
3538
3539 #endregion
3540
3541 #region Validating Signatures
3542
3551 public Task ValidateSignature(string LegalId, byte[] Data, byte[] Signature, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
3552 {
3553 return this.ValidateSignature(this.GetTrustProvider(LegalId), LegalId, Data, Signature, Callback, State);
3554 }
3555
3565 public async Task ValidateSignature(string Address, string LegalId, byte[] Data, byte[] Signature,
3566 EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
3567 {
3568 EventHandlerAsync<ValidateSignatureEventArgs> h = ValidateLocalSignature;
3569 if (!(h is null))
3570 {
3572 await h.Raise(this, e, false);
3573
3574 if (e.Valid.HasValue)
3575 {
3576 if (!(Callback is null))
3577 {
3578 XmlDocument Doc = new XmlDocument();
3579 XmlElement Empty = Doc.CreateElement("Local");
3580
3581 IqResultEventArgs e0 = new IqResultEventArgs(Empty, string.Empty, string.Empty, string.Empty, true, State)
3582 {
3583 Ok = e.Valid.Value
3584 };
3585 LegalIdentityEventArgs e2 = new LegalIdentityEventArgs(e0, e.Valid.Value ? e.Identity : null);
3586 await Callback.Raise(this, e2);
3587 }
3588
3589 return;
3590 }
3591 }
3592
3593 StringBuilder Xml = new StringBuilder();
3594
3595 Xml.Append("<validateSignature data=\"");
3596 Xml.Append(Convert.ToBase64String(Data));
3597
3598 if (!string.IsNullOrEmpty(LegalId))
3599 {
3600 Xml.Append("\" id=\"");
3601 Xml.Append(XML.Encode(LegalId));
3602 }
3603
3604 Xml.Append("\" s=\"");
3605 Xml.Append(Convert.ToBase64String(Signature));
3606
3607 Xml.Append("\" xmlns=\"");
3608 Xml.Append(NamespaceLegalIdentitiesCurrent);
3609 Xml.Append("\"/>");
3610
3611 await this.client.SendIqGet(Address, Xml.ToString(), async (Sender, e) =>
3612 {
3613 LegalIdentity Identity = null;
3614 XmlElement E;
3615
3616 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "identity")
3617 Identity = LegalIdentity.Parse(E);
3618 else
3619 e.Ok = false;
3620
3621 await Callback.Raise(this, new LegalIdentityEventArgs(e, Identity));
3622 }, State);
3623 }
3624
3629 public static event EventHandlerAsync<ValidateSignatureEventArgs> ValidateLocalSignature = null;
3630
3638 public Task<LegalIdentity> ValidateSignatureAsync(string LegalId, byte[] Data, byte[] Signature)
3639 {
3640 return this.ValidateSignatureAsync(this.GetTrustProvider(LegalId), LegalId, Data, Signature);
3641 }
3642
3651 public async Task<LegalIdentity> ValidateSignatureAsync(string Address, string LegalId, byte[] Data, byte[] Signature)
3652 {
3653 TaskCompletionSource<LegalIdentity> Result = new TaskCompletionSource<LegalIdentity>();
3654
3655 await this.ValidateSignature(Address, LegalId, Data, Signature, (Sender, e) =>
3656 {
3657 if (e.Ok)
3658 Result.TrySetResult(e.Identity);
3659 else
3660 Result.TrySetException(e.StanzaError ?? new Exception("Unable to verify signature."));
3661
3662 return Task.CompletedTask;
3663
3664 }, null);
3665
3666 return await Result.Task;
3667 }
3668
3676 public Task<KeyValuePair<LegalIdentity, Exception>> ValidateSignatureAsyncEx(string LegalId, byte[] Data, byte[] Signature)
3677 {
3678 return this.ValidateSignatureAsyncEx(this.GetTrustProvider(LegalId), LegalId, Data, Signature);
3679 }
3680
3689 public async Task<KeyValuePair<LegalIdentity, Exception>> ValidateSignatureAsyncEx(string Address, string LegalId, byte[] Data, byte[] Signature)
3690 {
3691 TaskCompletionSource<KeyValuePair<LegalIdentity, Exception>> Result = new TaskCompletionSource<KeyValuePair<LegalIdentity, Exception>>();
3692
3693 await this.ValidateSignature(Address, LegalId, Data, Signature, (Sender, e) =>
3694 {
3695 if (e.Ok)
3696 Result.TrySetResult(new KeyValuePair<LegalIdentity, Exception>(e.Identity, null));
3697 else
3698 Result.TrySetResult(new KeyValuePair<LegalIdentity, Exception>(null, e.StanzaError ?? new Exception("Unable to verify signature.")));
3699
3700 return Task.CompletedTask;
3701
3702 }, null);
3703
3704 return await Result.Task;
3705 }
3706
3707 #endregion
3708
3709 #region Trust Chain
3710
3718 public Task GetTrustChain(EventHandlerAsync<TrustChainEventArgs> Callback, object State)
3719 {
3720 return this.GetTrustChain(this.client.Domain, Callback, State);
3721 }
3722
3731 public async Task GetTrustChain(string Domain, EventHandlerAsync<TrustChainEventArgs> Callback, object State)
3732 {
3733 StringBuilder Xml = new StringBuilder();
3734
3735 Xml.Append("<getTrustChain xmlns=\"");
3736 Xml.Append(NamespaceLegalIdentitiesCurrent);
3737 Xml.Append("\"/>");
3738
3739 await this.client.SendIqGet(Domain, Xml.ToString(), async (Sender, e) =>
3740 {
3741 ChunkedList<string> Brokers = null;
3742 XmlElement E;
3743
3744 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "trustChain")
3745 {
3746 Brokers = new ChunkedList<string>();
3747
3748 foreach (XmlNode N in E.ChildNodes)
3749 {
3750 if (N is XmlElement E2 &&
3751 E2.LocalName == "broker" &&
3752 IsNamespaceLegalIdentity(E2.NamespaceURI))
3753 {
3754 Brokers.Add(XML.Attribute(E2, "domain"));
3755 }
3756 }
3757 }
3758 else
3759 e.Ok = false;
3760
3761 await Callback.Raise(this, new TrustChainEventArgs(e, Brokers?.ToArray()));
3762 }, State);
3763 }
3764
3771 public Task<string[]> GetTrustChainAsync()
3772 {
3773 return this.GetTrustChainAsync(this.client.Domain);
3774 }
3775
3783 public async Task<string[]> GetTrustChainAsync(string Domain)
3784 {
3785 TaskCompletionSource<string[]> Result = new TaskCompletionSource<string[]>();
3786
3787 await this.GetTrustChain(Domain, (Sender, e) =>
3788 {
3789 if (e.Ok)
3790 Result.TrySetResult(e.Domains);
3791 else
3792 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get the trust chain of domains from " + Domain + "."));
3793
3794 return Task.CompletedTask;
3795
3796 }, null);
3797
3798 return await Result.Task;
3799 }
3800
3801 #endregion
3802
3803 #region Create Contract
3804
3824 public Task CreateContract(XmlElement ForMachines, HumanReadableText[] ForHumans, Role[] Roles,
3825 Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration,
3826 Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate,
3827 EventHandlerAsync<SmartContractEventArgs> Callback, object State)
3828 {
3829 return this.CreateContract(this.componentAddress, ForMachines, ForHumans, Roles, Parts, Parameters, Visibility, PartsMode,
3830 Duration, ArchiveRequired, ArchiveOptional, SignAfter, SignBefore, CanActAsTemplate, Callback, State);
3831 }
3832
3853 public Task CreateContract(string Address, XmlElement ForMachines, HumanReadableText[] ForHumans, Role[] Roles,
3854 Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration,
3855 Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate,
3856 EventHandlerAsync<SmartContractEventArgs> Callback, object State)
3857 {
3858 return this.CreateContract(Address, ForMachines, ForHumans, Roles, Parts, Parameters,
3859 Visibility, PartsMode, Duration, ArchiveRequired, ArchiveOptional, SignAfter,
3860 SignBefore, CanActAsTemplate, null, Callback, State);
3861 }
3862
3884 public async Task CreateContract(string Address, XmlElement ForMachines, HumanReadableText[] ForHumans, Role[] Roles,
3885 Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration,
3886 Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate,
3887 IParameterEncryptionAlgorithm Algorithm, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
3888 {
3889 StringBuilder Xml = new StringBuilder();
3890
3891 Xml.Append("<createContract xmlns=\"");
3892 Xml.Append(NamespaceSmartContractsCurrent);
3893 Xml.Append("\">");
3894
3895 Contract Contract = new Contract()
3896 {
3897 Namespace = NamespaceSmartContractsCurrent,
3898 ForMachines = ForMachines,
3899 ForHumans = ForHumans,
3900 Roles = Roles,
3901 Parts = Parts,
3902 Parameters = Parameters,
3903 Visibility = Visibility,
3904 PartsMode = PartsMode,
3906 ArchiveRequired = ArchiveRequired,
3907 ArchiveOptional = ArchiveOptional,
3908 SignAfter = SignAfter,
3909 SignBefore = SignBefore,
3910 CanActAsTemplate = CanActAsTemplate
3911 };
3912
3913 byte[] Nonce = Guid.NewGuid().ToByteArray();
3914 string NonceStr = Convert.ToBase64String(Nonce);
3915 SymmetricCipherAlgorithms EncryptionAlgorithm = Algorithm?.Algorithm ?? this.preferredEncryptionAlgorithm;
3916
3918 {
3919 Algorithm ??= await ParameterEncryptionAlgorithm.Create(EncryptionAlgorithm, this);
3920
3921 Contract.EncryptEncryptedParameters(this.client.BareJID, Algorithm);
3922 }
3923
3924 Contract.Serialize(Xml, false, false, false, false, false, false, false);
3925
3927 {
3928 Xml.Append("<transient>");
3929
3931 {
3932 if (Parameter.Protection == ProtectionLevel.Transient)
3933 {
3934 Parameter.Protection = ProtectionLevel.Normal;
3935 Parameter.Serialize(Xml, true);
3936 Parameter.Protection = ProtectionLevel.Transient;
3937 }
3938 }
3939
3940 Xml.Append("</transient>");
3941 }
3942
3943 Xml.Append("</createContract>");
3944
3945 await this.client.SendIqSet(Address, Xml.ToString(), this.ContractResponse, new object[] { Callback, State, Contract.HasEncryptedParameters, Algorithm?.Algorithm, Algorithm?.Key });
3946 }
3947
3948 private async Task ContractResponse(object Sender, IqResultEventArgs e)
3949 {
3950 object[] P = (object[])e.State;
3951 EventHandlerAsync<SmartContractEventArgs> Callback = (EventHandlerAsync<SmartContractEventArgs>)P[0];
3952 Contract Contract = null;
3953 XmlElement E;
3954
3955 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "contract")
3956 {
3957 ParsedContract Parsed = await Contract.Parse(E, this, false);
3958 Contract = Parsed?.Contract;
3959 if (Contract is null)
3960 e.Ok = false;
3962 {
3963 string CreatorJid = this.client.BareJID;
3964
3965 if (P.Length >= 5 &&
3966 P[2] is bool HasEncryptedParameters &&
3967 HasEncryptedParameters &&
3968 P[3] is SymmetricCipherAlgorithms Algorithm &&
3969 P[4] is byte[] Key)
3970 {
3971 await this.SaveContractSharedSecret(Contract.ContractId,
3972 CreatorJid, Key, Algorithm, false);
3973 }
3974 else
3975 {
3976 Tuple<SymmetricCipherAlgorithms, string, byte[]> T = await this.TryLoadContractSharedSecret(Contract.ContractId);
3977
3978 if (HasEncryptedParameters = !(T is null))
3979 {
3980 Algorithm = T.Item1;
3981 CreatorJid = T.Item2;
3982 Key = T.Item3;
3983 }
3984 else
3985 {
3986 Algorithm = this.preferredEncryptionAlgorithm;
3987 Key = null;
3988 }
3989 }
3990
3991 if (HasEncryptedParameters)
3992 {
3994 Contract.ContractId, Algorithm, this, CreatorJid, Key);
3995
3996 Contract.DecryptEncryptedParameters(CreatorJid, AlgorithmInstance);
3997 }
3998 }
3999 }
4000 else
4001 e.Ok = false;
4002
4003 e.State = P[1];
4004 await Callback.Raise(this, new SmartContractEventArgs(e, Contract));
4005 }
4006
4025 public Task<Contract> CreateContractAsync(XmlElement ForMachines, HumanReadableText[] ForHumans, Role[] Roles,
4026 Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration,
4027 Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate)
4028 {
4029 return this.CreateContractAsync(this.componentAddress, ForMachines, ForHumans, Roles, Parts, Parameters, Visibility,
4030 PartsMode, Duration, ArchiveRequired, ArchiveOptional, SignAfter, SignBefore, CanActAsTemplate);
4031 }
4032
4052 public async Task<Contract> CreateContractAsync(string Address, XmlElement ForMachines, HumanReadableText[] ForHumans, Role[] Roles,
4053 Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration,
4054 Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate)
4055 {
4056 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
4057
4058 await this.CreateContract(Address, ForMachines, ForHumans, Roles, Parts, Parameters, Visibility, PartsMode, Duration,
4059 ArchiveRequired, ArchiveOptional, SignAfter, SignBefore, CanActAsTemplate, (Sender, e) =>
4060 {
4061 if (e.Ok)
4062 Result.TrySetResult(e.Contract);
4063 else
4064 Result.TrySetException(e.StanzaError ?? new Exception("Unable to create the contract."));
4065
4066 return Task.CompletedTask;
4067
4068 }, null);
4069
4070 return await Result.Task;
4071 }
4072
4073 #endregion
4074
4075 #region Create Contract From Template
4076
4094 public Task CreateContract(string TemplateId, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility,
4095 ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter,
4096 DateTime? SignBefore, bool CanActAsTemplate, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
4097 {
4098 return this.CreateContract(this.componentAddress, TemplateId, Parts, Parameters, Visibility, PartsMode, Duration,
4099 ArchiveRequired, ArchiveOptional, SignAfter, SignBefore, CanActAsTemplate, null, Callback, State);
4100 }
4101
4120 public Task CreateContract(string Address, string TemplateId, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility,
4121 ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter,
4122 DateTime? SignBefore, bool CanActAsTemplate, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
4123 {
4124 return this.CreateContract(Address, TemplateId, Parts, Parameters, Visibility,
4125 PartsMode, Duration, ArchiveRequired, ArchiveOptional, SignAfter,
4126 SignBefore, CanActAsTemplate, null, Callback, State);
4127 }
4128
4129
4149 public async Task CreateContract(string Address, string TemplateId, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility,
4150 ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter,
4151 DateTime? SignBefore, bool CanActAsTemplate, IParameterEncryptionAlgorithm Algorithm,
4152 EventHandlerAsync<SmartContractEventArgs> Callback, object State)
4153 {
4154 StringBuilder Xml = new StringBuilder();
4155 uint i, c = (uint)(Parameters?.Length ?? 0);
4156 bool HasEncryptedParameters = false;
4157
4158 for (i = 0; i < c; i++)
4159 {
4160 Parameter P = Parameters[i];
4161
4162 if (P.Protection == ProtectionLevel.Encrypted)
4163 {
4164 HasEncryptedParameters = true;
4165 break;
4166 }
4167 }
4168
4169 byte[] Nonce = Guid.NewGuid().ToByteArray();
4170 string NonceStr = Convert.ToBase64String(Nonce);
4171 SymmetricCipherAlgorithms EncryptionAlgorithm = Algorithm?.Algorithm ?? this.preferredEncryptionAlgorithm;
4172
4173 if (HasEncryptedParameters)
4174 {
4175 Algorithm ??= await ParameterEncryptionAlgorithm.Create(EncryptionAlgorithm, this);
4176
4177 for (i = 0; i < c; i++)
4178 {
4179 Parameter P = Parameters[i];
4180
4181 if (P.Protection == ProtectionLevel.Encrypted && P.ProtectedValue is null)
4182 P.ProtectedValue = Algorithm.Encrypt(P.Name, P.ParameterType, i, this.client.BareJID, Nonce, P.ObjectValue is null ? null : P.StringValue);
4183 }
4184 }
4185
4186 Xml.Append("<createContract xmlns=\"");
4187 Xml.Append(NamespaceSmartContractsCurrent);
4188 Xml.Append("\"><template archiveOpt=\"");
4189 Xml.Append(ArchiveOptional.ToString());
4190 Xml.Append("\" archiveReq=\"");
4191 Xml.Append(ArchiveRequired.ToString());
4192 Xml.Append("\" canActAsTemplate=\"");
4193 Xml.Append(CommonTypes.Encode(CanActAsTemplate));
4194 Xml.Append("\" duration=\"");
4195 Xml.Append(Duration.ToString());
4196 Xml.Append("\" id=\"");
4197 Xml.Append(XML.Encode(TemplateId));
4198 Xml.Append("\" nonce=\"");
4199 Xml.Append(NonceStr);
4200 Xml.Append('"');
4201
4202 if (SignAfter.HasValue && SignAfter > DateTime.MinValue)
4203 {
4204 Xml.Append(" signAfter=\"");
4205 Xml.Append(XML.Encode(SignAfter.Value));
4206 Xml.Append('"');
4207 }
4208
4209 if (SignBefore.HasValue && SignBefore < DateTime.MaxValue)
4210 {
4211 Xml.Append(" signBefore=\"");
4212 Xml.Append(XML.Encode(SignBefore.Value));
4213 Xml.Append('"');
4214 }
4215
4216 Xml.Append(" visibility=\"");
4217 Xml.Append(Visibility.ToString());
4218 Xml.Append("\"><parts>");
4219
4220 switch (PartsMode)
4221 {
4222 case ContractParts.Open:
4223 Xml.Append("<open/>");
4224 break;
4225
4226 case ContractParts.TemplateOnly:
4227 Xml.Append("<templateOnly/>");
4228 break;
4229
4230 case ContractParts.ExplicitlyDefined:
4231 if (!(Parts is null))
4232 {
4233 foreach (Part Part in Parts)
4234 {
4235 Xml.Append("<part legalId=\"");
4236 Xml.Append(XML.Encode(Part.LegalId));
4237 Xml.Append("\" role=\"");
4238 Xml.Append(XML.Encode(Part.Role));
4239 Xml.Append("\"/>");
4240 }
4241 }
4242 break;
4243 }
4244
4245 Xml.Append("</parts>");
4246
4247 LinkedList<Parameter> TransientParameters = null;
4248
4249 if (!(Parameters is null) && Parameters.Length > 0)
4250 {
4251 Xml.Append("<parameters>");
4252
4253 foreach (Parameter Parameter in Parameters)
4254 {
4255 if (Parameter.Protection == ProtectionLevel.Transient)
4256 {
4257 Parameter.ProtectedValue ??= Guid.NewGuid().ToByteArray();
4258
4259 TransientParameters ??= new LinkedList<Parameter>();
4260 TransientParameters.AddLast(Parameter);
4261 }
4262
4263 Parameter.Serialize(Xml, true);
4264 }
4265
4266 Xml.Append("</parameters>");
4267 }
4268
4269 Xml.Append("</template>");
4270
4271 if (!(TransientParameters is null))
4272 {
4273 Xml.Append("<transient>");
4274
4275 foreach (Parameter Parameter in TransientParameters)
4276 {
4277 Parameter.Protection = ProtectionLevel.Normal;
4278 Parameter.Serialize(Xml, true);
4279 Parameter.Protection = ProtectionLevel.Transient;
4280 }
4281
4282 Xml.Append("</transient>");
4283 }
4284
4285 Xml.Append("</createContract>");
4286
4287 await this.client.SendIqSet(Address, Xml.ToString(), this.ContractResponse, new object[] { Callback, State, HasEncryptedParameters, Algorithm?.Algorithm, Algorithm?.Key });
4288 }
4289
4306 public Task<Contract> CreateContractAsync(string TemplateId, Part[] Parts, Parameter[] Parameters, ContractVisibility Visibility,
4307 ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional, DateTime? SignAfter,
4308 DateTime? SignBefore, bool CanActAsTemplate)
4309 {
4310 return this.CreateContractAsync(this.componentAddress, TemplateId, Parts, Parameters, Visibility,
4311 PartsMode, Duration, ArchiveRequired, ArchiveOptional, SignAfter, SignBefore, CanActAsTemplate);
4312 }
4313
4331 public async Task<Contract> CreateContractAsync(string Address, string TemplateId, Part[] Parts, Parameter[] Parameters,
4332 ContractVisibility Visibility, ContractParts PartsMode, Duration? Duration, Duration? ArchiveRequired, Duration? ArchiveOptional,
4333 DateTime? SignAfter, DateTime? SignBefore, bool CanActAsTemplate)
4334 {
4335 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
4336
4337 await this.CreateContract(Address, TemplateId, Parts, Parameters, Visibility, PartsMode, Duration,
4338 ArchiveRequired, ArchiveOptional, SignAfter, SignBefore, CanActAsTemplate, (Sender, e) =>
4339 {
4340 if (e.Ok)
4341 Result.TrySetResult(e.Contract);
4342 else
4343 Result.TrySetException(e.StanzaError ?? new Exception("Unable to create the contract."));
4344
4345 return Task.CompletedTask;
4346
4347 }, null);
4348
4349 return await Result.Task;
4350 }
4351
4352 #endregion
4353
4354 #region Get Created Contract References
4355
4361 public Task GetCreatedContractReferences(EventHandlerAsync<IdReferencesEventArgs> Callback, object State)
4362 {
4363 return this.GetCreatedContractReferences(this.componentAddress, 0, int.MaxValue, Callback, State);
4364 }
4365
4372 public Task GetCreatedContractReferences(string Address, EventHandlerAsync<IdReferencesEventArgs> Callback, object State)
4373 {
4374 return this.GetCreatedContractReferences(Address, 0, int.MaxValue, Callback, State);
4375 }
4376
4384 public Task GetCreatedContractReferences(int Offset, int MaxCount, EventHandlerAsync<IdReferencesEventArgs> Callback, object State)
4385 {
4386 return this.GetCreatedContractReferences(this.componentAddress, Offset, MaxCount, Callback, State);
4387 }
4388
4397 public Task GetCreatedContractReferences(string Address, int Offset, int MaxCount, EventHandlerAsync<IdReferencesEventArgs> Callback, object State)
4398 {
4399 if (Offset < 0)
4400 throw new ArgumentException("Offsets cannot be negative.", nameof(Offset));
4401
4402 if (MaxCount <= 0)
4403 throw new ArgumentException("Must be postitive.", nameof(MaxCount));
4404
4405 StringBuilder Xml = new StringBuilder();
4406
4407 Xml.Append("<getCreatedContracts references='true' xmlns='");
4408 Xml.Append(NamespaceSmartContractsCurrent);
4409
4410 if (Offset > 0)
4411 {
4412 Xml.Append("' offset='");
4413 Xml.Append(Offset.ToString());
4414 }
4415
4416 if (MaxCount < int.MaxValue)
4417 {
4418 Xml.Append("' maxCount='");
4419 Xml.Append(MaxCount.ToString());
4420 }
4421
4422 Xml.Append("'/>");
4423
4424 return this.client.SendIqGet(Address, Xml.ToString(), this.IdReferencesResponse, new object[] { Callback, State });
4425 }
4426
4427 private async Task IdReferencesResponse(object Sender, IqResultEventArgs e)
4428 {
4429 object[] P = (object[])e.State;
4430 EventHandlerAsync<IdReferencesEventArgs> Callback = (EventHandlerAsync<IdReferencesEventArgs>)P[0];
4431 XmlElement E = e.FirstElement;
4432 List<string> IDs = new List<string>();
4433
4434 if (e.Ok && !(E is null))
4435 {
4436 foreach (XmlNode N in E.ChildNodes)
4437 {
4438 if (N is XmlElement E2 && E2.LocalName == "ref")
4439 {
4440 string Id = XML.Attribute(E2, "id");
4441 IDs.Add(Id);
4442 }
4443 }
4444 }
4445 else
4446 e.Ok = false;
4447
4448 e.State = P[1];
4449 await Callback.Raise(this, new IdReferencesEventArgs(e, IDs.ToArray()));
4450 }
4451
4457 {
4458 return this.GetCreatedContractReferencesAsync(this.componentAddress, 0, int.MaxValue);
4459 }
4460
4466 public Task<string[]> GetCreatedContractReferencesAsync(string Address)
4467 {
4468 return this.GetCreatedContractReferencesAsync(Address, 0, int.MaxValue);
4469 }
4470
4477 public Task<string[]> GetCreatedContractReferencesAsync(int Offset, int MaxCount)
4478 {
4479 return this.GetCreatedContractReferencesAsync(this.componentAddress, Offset, MaxCount);
4480 }
4481
4489 public async Task<string[]> GetCreatedContractReferencesAsync(string Address, int Offset, int MaxCount)
4490 {
4491 TaskCompletionSource<string[]> Result = new TaskCompletionSource<string[]>();
4492
4493 await this.GetCreatedContractReferences(Address, Offset, MaxCount, (Sender, e) =>
4494 {
4495 if (e.Ok)
4496 Result.TrySetResult(e.References);
4497 else
4498 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get created contract references."));
4499
4500 return Task.CompletedTask;
4501
4502 }, null);
4503
4504 return await Result.Task;
4505 }
4506
4507 #endregion
4508
4509 #region Get Created Contracts
4510
4516 public Task GetCreatedContracts(EventHandlerAsync<ContractsEventArgs> Callback, object State)
4517 {
4518 return this.GetCreatedContracts(this.componentAddress, 0, int.MaxValue, Callback, State);
4519 }
4520
4527 public Task GetCreatedContracts(string Address, EventHandlerAsync<ContractsEventArgs> Callback, object State)
4528 {
4529 return this.GetCreatedContracts(Address, 0, int.MaxValue, Callback, State);
4530 }
4531
4539 public Task GetCreatedContracts(int Offset, int MaxCount, EventHandlerAsync<ContractsEventArgs> Callback, object State)
4540 {
4541 return this.GetCreatedContracts(this.componentAddress, Offset, MaxCount, Callback, State);
4542 }
4543
4552 public Task GetCreatedContracts(string Address, int Offset, int MaxCount, EventHandlerAsync<ContractsEventArgs> Callback, object State)
4553 {
4554 if (Offset < 0)
4555 throw new ArgumentException("Offsets cannot be negative.", nameof(Offset));
4556
4557 if (MaxCount <= 0)
4558 throw new ArgumentException("Must be postitive.", nameof(MaxCount));
4559
4560 StringBuilder Xml = new StringBuilder();
4561
4562 Xml.Append("<getCreatedContracts references='false' xmlns='");
4563 Xml.Append(NamespaceSmartContractsCurrent);
4564
4565 if (Offset > 0)
4566 {
4567 Xml.Append("' offset='");
4568 Xml.Append(Offset.ToString());
4569 }
4570
4571 if (MaxCount < int.MaxValue)
4572 {
4573 Xml.Append("' maxCount='");
4574 Xml.Append(MaxCount.ToString());
4575 }
4576
4577 Xml.Append("'/>");
4578
4579 return this.client.SendIqGet(Address, Xml.ToString(), this.ContractsResponse, new object[] { Callback, State });
4580 }
4581
4582 private async Task ContractsResponse(object Sender, IqResultEventArgs e)
4583 {
4584 object[] P = (object[])e.State;
4585 EventHandlerAsync<ContractsEventArgs> Callback = (EventHandlerAsync<ContractsEventArgs>)P[0];
4586 XmlElement E = e.FirstElement;
4587 List<Contract> Contracts = new List<Contract>();
4588 List<string> References = new List<string>();
4589
4590 if (e.Ok && !(E is null))
4591 {
4592 foreach (XmlNode N in E.ChildNodes)
4593 {
4594 if (N is XmlElement E2)
4595 {
4596 switch (E2.LocalName)
4597 {
4598 case "contract":
4599 ParsedContract ParsedContract = await Contract.Parse(E2, this, false);
4600
4601 if (!(ParsedContract is null))
4602 Contracts.Add(ParsedContract.Contract);
4603 break;
4604
4605 case "ref":
4606 string ContractId = XML.Attribute(E2, "id");
4607 References.Add(ContractId);
4608 break;
4609 }
4610 }
4611 }
4612 }
4613 else
4614 e.Ok = false;
4615
4616 e.State = P[1];
4617 await Callback.Raise(this, new ContractsEventArgs(e, Contracts.ToArray(), References.ToArray()));
4618 }
4619
4624 public Task<ContractsEventArgs> GetCreatedContractsAsync()
4625 {
4626 return this.GetCreatedContractsAsync(this.componentAddress, 0, int.MaxValue);
4627 }
4628
4634 public Task<ContractsEventArgs> GetCreatedContractsAsync(string Address)
4635 {
4636 return this.GetCreatedContractsAsync(Address, 0, int.MaxValue);
4637 }
4638
4645 public Task<ContractsEventArgs> GetCreatedContractsAsync(int Offset, int MaxCount)
4646 {
4647 return this.GetCreatedContractsAsync(this.componentAddress, Offset, MaxCount);
4648 }
4649
4657 public async Task<ContractsEventArgs> GetCreatedContractsAsync(string Address, int Offset, int MaxCount)
4658 {
4659 TaskCompletionSource<ContractsEventArgs> Result = new TaskCompletionSource<ContractsEventArgs>();
4660
4661 await this.GetCreatedContracts(Address, Offset, MaxCount, (Sender, e) =>
4662 {
4663 Result.TrySetResult(e);
4664 return Task.CompletedTask;
4665
4666 }, null);
4667
4668 return await Result.Task;
4669 }
4670
4671 #endregion
4672
4673 #region Sign Contract
4674
4685 public Task SignContract(Contract Contract, string Role, bool Transferable, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
4686 {
4687 return this.SignContract(this.GetTrustProvider(Contract.ContractId), Contract, Role, Transferable, Callback, State);
4688 }
4689
4701 public async Task SignContract(string Address, Contract Contract, string Role, bool Transferable,
4702 EventHandlerAsync<SmartContractEventArgs> Callback, object State)
4703 {
4705 {
4706 Tuple<SymmetricCipherAlgorithms, string, byte[]> T = await this.TryLoadContractSharedSecret(Contract.ContractId);
4707 if (!(T is null))
4708 {
4710 Contract.ContractId, T.Item1, this, T.Item2, T.Item3);
4711
4712 Contract.EncryptEncryptedParameters(T.Item2, Algorithm);
4713 }
4714 }
4715
4716 StringBuilder Xml = new StringBuilder();
4717 Contract.Serialize(Xml, false, false, false, false, false, false, false);
4718 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
4719
4720 await this.Sign(Address, Data, SignWith.LatestApprovedId, async (Sender, e) =>
4721 {
4722 if (e.Ok)
4723 {
4724 Xml.Clear();
4725 Xml.Append("<signContract xmlns='");
4726 Xml.Append(NamespaceSmartContractsCurrent);
4727 Xml.Append("' id='");
4728 Xml.Append(XML.Encode(Contract.ContractId));
4729 Xml.Append("' role='");
4730 Xml.Append(XML.Encode(Role));
4731
4732 if (Transferable)
4733 Xml.Append("' transferable='true");
4734
4735 Xml.Append("' s='");
4736 Xml.Append(Convert.ToBase64String(e.Signature));
4737 Xml.Append("'/>");
4738
4739 await this.client.SendIqSet(Address, Xml.ToString(), this.ContractResponse, new object[] { Callback, State });
4740 }
4741 else
4742 await Callback.Raise(this, new SmartContractEventArgs(e, null));
4743 }, State);
4744 }
4745
4755 public Task<Contract> SignContractAsync(Contract Contract, string Role, bool Transferable)
4756 {
4757 return this.SignContractAsync(this.GetTrustProvider(Contract.ContractId), Contract, Role, Transferable);
4758 }
4759
4770 public async Task<Contract> SignContractAsync(string Address, Contract Contract, string Role, bool Transferable)
4771 {
4772 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
4773
4774 await this.SignContract(Address, Contract, Role, Transferable, (Sender, e) =>
4775 {
4776 if (e.Ok)
4777 Result.TrySetResult(e.Contract);
4778 else
4779 Result.TrySetException(e.StanzaError ?? new Exception("Unable to sign the contract."));
4780
4781 return Task.CompletedTask;
4782
4783 }, null);
4784
4785 return await Result.Task;
4786 }
4787
4788 #endregion
4789
4790 #region Get Signed Contract References
4791
4797 public Task GetSignedContractReferences(EventHandlerAsync<IdReferencesEventArgs> Callback, object State)
4798 {
4799 return this.GetSignedContractReferences(this.componentAddress, 0, int.MaxValue, Callback, State);
4800 }
4801
4808 public Task GetSignedContractReferences(string Address, EventHandlerAsync<IdReferencesEventArgs> Callback, object State)
4809 {
4810 return this.GetSignedContractReferences(Address, 0, int.MaxValue, Callback, State);
4811 }
4812
4821 public Task GetSignedContractReferences(string Address, int Offset, int MaxCount, EventHandlerAsync<IdReferencesEventArgs> Callback, object State)
4822 {
4823 if (Offset < 0)
4824 throw new ArgumentException("Offsets cannot be negative.", nameof(Offset));
4825
4826 if (MaxCount <= 0)
4827 throw new ArgumentException("Must be postitive.", nameof(MaxCount));
4828
4829 StringBuilder Xml = new StringBuilder();
4830
4831 Xml.Append("<getSignedContracts references='true' xmlns='");
4832 Xml.Append(NamespaceSmartContractsCurrent);
4833
4834 if (Offset > 0)
4835 {
4836 Xml.Append("' offset='");
4837 Xml.Append(Offset.ToString());
4838 }
4839
4840 if (MaxCount < int.MaxValue)
4841 {
4842 Xml.Append("' maxCount='");
4843 Xml.Append(MaxCount.ToString());
4844 }
4845
4846 Xml.Append("'/>");
4847
4848 return this.client.SendIqGet(Address, Xml.ToString(), this.IdReferencesResponse, new object[] { Callback, State });
4849 }
4850
4855 public Task<string[]> GetSignedContractReferencesAsync()
4856 {
4857 return this.GetSignedContractReferencesAsync(this.componentAddress, 0, int.MaxValue);
4858 }
4859
4866 public Task<string[]> GetSignedContractReferencesAsync(int Offset, int MaxCount)
4867 {
4868 return this.GetSignedContractReferencesAsync(this.componentAddress, Offset, MaxCount);
4869 }
4870
4878 public async Task<string[]> GetSignedContractReferencesAsync(string Address, int Offset, int MaxCount)
4879 {
4880 TaskCompletionSource<string[]> Result = new TaskCompletionSource<string[]>();
4881
4882 await this.GetSignedContractReferences(Address, Offset, MaxCount, (Sender, e) =>
4883 {
4884 if (e.Ok)
4885 Result.TrySetResult(e.References);
4886 else
4887 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get signed contract references."));
4888
4889 return Task.CompletedTask;
4890
4891 }, null);
4892
4893 return await Result.Task;
4894 }
4895
4896 #endregion
4897
4898 #region Get Signed Contracts
4899
4905 public Task GetSignedContracts(EventHandlerAsync<ContractsEventArgs> Callback, object State)
4906 {
4907 return this.GetSignedContracts(this.componentAddress, 0, int.MaxValue, Callback, State);
4908 }
4909
4916 public Task GetSignedContracts(string Address, EventHandlerAsync<ContractsEventArgs> Callback, object State)
4917 {
4918 return this.GetSignedContracts(Address, 0, int.MaxValue, Callback, State);
4919 }
4920
4928 public Task GetSignedContracts(int Offset, int MaxCount, EventHandlerAsync<ContractsEventArgs> Callback, object State)
4929 {
4930 return this.GetSignedContracts(this.componentAddress, Offset, MaxCount, Callback, State);
4931 }
4932
4941 public Task GetSignedContracts(string Address, int Offset, int MaxCount, EventHandlerAsync<ContractsEventArgs> Callback, object State)
4942 {
4943 if (Offset < 0)
4944 throw new ArgumentException("Offsets cannot be negative.", nameof(Offset));
4945
4946 if (MaxCount <= 0)
4947 throw new ArgumentException("Must be postitive.", nameof(MaxCount));
4948
4949 StringBuilder Xml = new StringBuilder();
4950
4951 Xml.Append("<getSignedContracts references='false' xmlns='");
4952 Xml.Append(NamespaceSmartContractsCurrent);
4953
4954 if (Offset > 0)
4955 {
4956 Xml.Append("' offset='");
4957 Xml.Append(Offset.ToString());
4958 }
4959
4960 if (MaxCount < int.MaxValue)
4961 {
4962 Xml.Append("' maxCount='");
4963 Xml.Append(MaxCount.ToString());
4964 }
4965
4966 Xml.Append("'/>");
4967
4968 return this.client.SendIqGet(Address, Xml.ToString(), this.ContractsResponse, new object[] { Callback, State });
4969 }
4970
4975 public Task<ContractsEventArgs> GetSignedContractsAsync()
4976 {
4977 return this.GetSignedContractsAsync(this.componentAddress, 0, int.MaxValue);
4978 }
4979
4986 public Task<ContractsEventArgs> GetSignedContractsAsync(int Offset, int MaxCount)
4987 {
4988 return this.GetSignedContractsAsync(this.componentAddress, Offset, MaxCount);
4989 }
4990
4998 public async Task<ContractsEventArgs> GetSignedContractsAsync(string Address, int Offset, int MaxCount)
4999 {
5000 TaskCompletionSource<ContractsEventArgs> Result = new TaskCompletionSource<ContractsEventArgs>();
5001
5002 await this.GetSignedContracts(Address, Offset, MaxCount, (Sender, e) =>
5003 {
5004 Result.TrySetResult(e);
5005 return Task.CompletedTask;
5006
5007 }, null);
5008
5009 return await Result.Task;
5010 }
5011
5012 #endregion
5013
5014 #region Contract Signature event
5015
5016 private async Task ContractSignedMessageHandler(object Sender, MessageEventArgs e)
5017 {
5018 string ContractId = XML.Attribute(e.Content, "contractId");
5019 string LegalId = XML.Attribute(e.Content, "legalId");
5020 string Role = XML.Attribute(e.Content, "role");
5021 bool Signed = XML.Attribute(e.Content, "signed", false);
5022
5023 if (XmppClient.GetDomain(ContractId) != e.From)
5024 {
5025 this.Error("Client signature message ignored. Source domain (" +
5026 e.FromBareJID + ") not equal to contract domain (" +
5027 ContractId + ").");
5028 return;
5029 }
5030
5031 Contract Contract = null;
5032
5033 foreach (XmlNode N in e.Content.ChildNodes)
5034 {
5035 if (N is XmlElement E && E.LocalName == "contract" && IsNamespaceSmartContract(E.NamespaceURI))
5036 {
5037 ParsedContract ParsedContract = await Contract.Parse(E, this, false);
5039 break;
5040 }
5041 }
5042
5043 if (Contract is null)
5044 {
5045 this.Error("Client signature message ignored. Unable to parse embedded contract.");
5046 return;
5047 }
5048
5049 await this.ContractSigned.Raise(this, new ContractSignedEventArgs(ContractId, LegalId, Role, Signed, Contract));
5050 }
5051
5055 public event EventHandlerAsync<ContractSignedEventArgs> ContractSigned = null;
5056
5057 #endregion
5058
5059 #region Get Contract
5060
5067 public Task GetContract(string ContractId, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
5068 {
5069 return this.GetContract(this.GetTrustProvider(ContractId), ContractId, Callback, State);
5070 }
5071
5079 public Task GetContract(string Address, string ContractId, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
5080 {
5081 StringBuilder Xml = new StringBuilder();
5082
5083 Xml.Append("<getContract xmlns='");
5084 Xml.Append(NamespaceSmartContractsCurrent);
5085 Xml.Append("' id='");
5086 Xml.Append(XML.Encode(ContractId));
5087 Xml.Append("'/>");
5088
5089 return this.client.SendIqGet(Address, Xml.ToString(), this.ContractResponse, new object[] { Callback, State });
5090 }
5091
5097 public Task<Contract> GetContractAsync(string ContractId)
5098 {
5099 return this.GetContractAsync(this.GetTrustProvider(ContractId), ContractId);
5100 }
5101
5108 public async Task<Contract> GetContractAsync(string Address, string ContractId)
5109 {
5110 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
5111
5112 await this.GetContract(Address, ContractId, (Sender, e) =>
5113 {
5114 if (e.Ok)
5115 Result.TrySetResult(e.Contract);
5116 else
5117 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get the contract."));
5118
5119 return Task.CompletedTask;
5120
5121 }, null);
5122
5123 return await Result.Task;
5124 }
5125
5126 #endregion
5127
5128 #region Get Contracts
5129
5136 public async Task GetContracts(string[] ContractIds, EventHandlerAsync<ContractsEventArgs> Callback, object State)
5137 {
5138 Dictionary<string, List<string>> ByTrustProvider = new Dictionary<string, List<string>>();
5139 string LastTrustProvider = string.Empty;
5140 List<string> LastList = null;
5141
5142 foreach (string ContractId in ContractIds)
5143 {
5144 string TrustProvider = this.GetTrustProvider(ContractId);
5145
5146 if (TrustProvider != LastTrustProvider || LastList is null)
5147 {
5148 if (!ByTrustProvider.TryGetValue(TrustProvider, out LastList))
5149 {
5150 LastList = new List<string>();
5151 ByTrustProvider[TrustProvider] = LastList;
5152 }
5153
5154 LastTrustProvider = TrustProvider;
5155 }
5156
5157 LastList.Add(ContractId);
5158 }
5159
5160 List<Contract> Contracts = new List<Contract>();
5161 List<string> References = new List<string>();
5162 bool Ok = true;
5163 int NrLeft = ByTrustProvider.Count;
5164
5165 foreach (KeyValuePair<string, List<string>> P in ByTrustProvider)
5166 {
5167 await this.GetContracts(P.Key, P.Value.ToArray(), async (Sender, e) =>
5168 {
5169 lock (Contracts)
5170 {
5171 if (e.Ok)
5172 {
5173 Contracts.AddRange(e.Contracts);
5174 References.AddRange(e.References);
5175 }
5176 else
5177 Ok = false;
5178
5179 NrLeft--;
5180 if (NrLeft > 0)
5181 return;
5182 }
5183
5184 ContractsEventArgs e2 = new ContractsEventArgs(e, Contracts.ToArray(), References.ToArray())
5185 {
5186 Ok = Ok
5187 };
5188
5189 await Callback.Raise(this, e2);
5190
5191 }, State);
5192 }
5193 }
5194
5202 public Task GetContracts(string Address, string[] ContractIds, EventHandlerAsync<ContractsEventArgs> Callback, object State)
5203 {
5204 StringBuilder Xml = new StringBuilder();
5205
5206 Xml.Append("<getContracts xmlns='");
5207 Xml.Append(NamespaceSmartContractsCurrent);
5208 Xml.Append("'>");
5209
5210 foreach (string ContractId in ContractIds)
5211 {
5212 Xml.Append("<ref id='");
5213 Xml.Append(XML.Encode(ContractId));
5214 Xml.Append("'/>");
5215 }
5216
5217 Xml.Append("</getContracts>");
5218
5219 return this.client.SendIqGet(Address, Xml.ToString(), this.ContractsResponse, new object[] { Callback, State });
5220 }
5221
5227 public async Task<ContractsEventArgs> GetContractsAsync(string[] ContractIds)
5228 {
5229 TaskCompletionSource<ContractsEventArgs> Result = new TaskCompletionSource<ContractsEventArgs>();
5230
5231 await this.GetContracts(ContractIds, (Sender, e) =>
5232 {
5233 Result.TrySetResult(e);
5234 return Task.CompletedTask;
5235
5236 }, null);
5237
5238 return await Result.Task;
5239 }
5240
5247 public async Task<ContractsEventArgs> GetContractsAsync(string Address, string[] ContractIds)
5248 {
5249 TaskCompletionSource<ContractsEventArgs> Result = new TaskCompletionSource<ContractsEventArgs>();
5250
5251 await this.GetContracts(Address, ContractIds, (Sender, e) =>
5252 {
5253 Result.TrySetResult(e);
5254 return Task.CompletedTask;
5255
5256 }, null);
5257
5258 return await Result.Task;
5259 }
5260
5261 #endregion
5262
5263 #region Obsolete Contract
5264
5271 public Task ObsoleteContract(string ContractId, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
5272 {
5273 return this.ObsoleteContract(this.GetTrustProvider(ContractId), ContractId, Callback, State);
5274 }
5275
5283 public Task ObsoleteContract(string Address, string ContractId,
5284 EventHandlerAsync<SmartContractEventArgs> Callback, object State)
5285 {
5286 StringBuilder Xml = new StringBuilder();
5287
5288 Xml.Append("<obsoleteContract xmlns='");
5289 Xml.Append(NamespaceSmartContractsCurrent);
5290 Xml.Append("' id='");
5291 Xml.Append(XML.Encode(ContractId));
5292 Xml.Append("'/>");
5293
5294 return this.client.SendIqSet(Address, Xml.ToString(), this.ContractResponse, new object[] { Callback, State });
5295 }
5296
5302 public Task<Contract> ObsoleteContractAsync(string ContractId)
5303 {
5304 return this.ObsoleteContractAsync(this.GetTrustProvider(ContractId), ContractId);
5305 }
5306
5313 public async Task<Contract> ObsoleteContractAsync(string Address, string ContractId)
5314 {
5315 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
5316
5317 await this.ObsoleteContract(Address, ContractId, (Sender, e) =>
5318 {
5319 if (e.Ok)
5320 Result.TrySetResult(e.Contract);
5321 else
5322 Result.TrySetException(e.StanzaError ?? new Exception("Unable to obsolete the contract."));
5323
5324 return Task.CompletedTask;
5325
5326 }, null);
5327
5328 return await Result.Task;
5329 }
5330
5331 #endregion
5332
5333 #region Delete Contract
5334
5341 public Task DeleteContract(string ContractId, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
5342 {
5343 return this.DeleteContract(this.GetTrustProvider(ContractId), ContractId, Callback, State);
5344 }
5345
5353 public Task DeleteContract(string Address, string ContractId,
5354 EventHandlerAsync<SmartContractEventArgs> Callback, object State)
5355 {
5356 StringBuilder Xml = new StringBuilder();
5357
5358 Xml.Append("<deleteContract xmlns='");
5359 Xml.Append(NamespaceSmartContractsCurrent);
5360 Xml.Append("' id='");
5361 Xml.Append(XML.Encode(ContractId));
5362 Xml.Append("'/>");
5363
5364 return this.client.SendIqSet(Address, Xml.ToString(), this.ContractResponse, new object[] { Callback, State });
5365 }
5366
5372 public Task<Contract> DeleteContractAsync(string ContractId)
5373 {
5374 return this.DeleteContractAsync(this.GetTrustProvider(ContractId), ContractId);
5375 }
5376
5383 public async Task<Contract> DeleteContractAsync(string Address, string ContractId)
5384 {
5385 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
5386
5387 await this.DeleteContract(Address, ContractId, (Sender, e) =>
5388 {
5389 if (e.Ok)
5390 Result.TrySetResult(e.Contract);
5391 else
5392 Result.TrySetException(e.StanzaError ?? new Exception("Unable to delete the contract."));
5393
5394 return Task.CompletedTask;
5395
5396 }, null);
5397
5398 return await Result.Task;
5399 }
5400
5401 #endregion
5402
5403 #region Contract Created event
5404
5405 private Task ContractCreatedMessageHandler(object Sender, MessageEventArgs e)
5406 {
5407 string ContractId = XML.Attribute(e.Content, "contractId");
5408
5409 if (!this.IsFromTrustProvider(ContractId, e.From))
5410 return Task.CompletedTask;
5411
5412 return this.ContractCreated.Raise(this, new ContractReferenceEventArgs(ContractId));
5413 }
5414
5418 public event EventHandlerAsync<ContractReferenceEventArgs> ContractCreated = null;
5419
5420 #endregion
5421
5422 #region Contract Updated event
5423
5424 private Task ContractUpdatedMessageHandler(object Sender, MessageEventArgs e)
5425 {
5426 string ContractId = XML.Attribute(e.Content, "contractId");
5427
5428 if (!this.IsFromTrustProvider(ContractId, e.From))
5429 return Task.CompletedTask;
5430
5431 return this.ContractUpdated.Raise(this, new ContractReferenceEventArgs(ContractId));
5432 }
5433
5437 public event EventHandlerAsync<ContractReferenceEventArgs> ContractUpdated = null;
5438
5439 #endregion
5440
5441 #region Contract Deleted event
5442
5443 private Task ContractDeletedMessageHandler(object Sender, MessageEventArgs e)
5444 {
5445 string ContractId = XML.Attribute(e.Content, "contractId");
5446
5447 if (!this.IsFromTrustProvider(ContractId, e.From))
5448 return Task.CompletedTask;
5449
5450 return this.ContractDeleted.Raise(this, new ContractReferenceEventArgs(ContractId));
5451 }
5452
5456 public event EventHandlerAsync<ContractReferenceEventArgs> ContractDeleted = null;
5457
5458 #endregion
5459
5460 #region Update Contract
5461
5468 public Task UpdateContract(Contract Contract, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
5469 {
5470 return this.UpdateContract(this.GetTrustProvider(Contract.ContractId), Contract, Callback, State);
5471 }
5472
5480 public async Task UpdateContract(string Address, Contract Contract,
5481 EventHandlerAsync<SmartContractEventArgs> Callback, object State)
5482 {
5484 {
5485 Tuple<SymmetricCipherAlgorithms, string, byte[]> KeyInfo =
5486 await this.TryLoadContractSharedSecret(Contract.ContractId);
5487
5488 if (!(KeyInfo is null))
5489 {
5491 Contract.ContractId, KeyInfo.Item1, this, KeyInfo.Item2, KeyInfo.Item3);
5492
5493 Contract.EncryptEncryptedParameters(this.client.BareJID, Algorithm);
5494 }
5495 }
5496
5497 StringBuilder Xml = new StringBuilder();
5498
5499 Xml.Append("<updateContract xmlns='");
5500 Xml.Append(NamespaceSmartContractsCurrent);
5501 Xml.Append("'>");
5502
5503 Contract.Serialize(Xml, false, true, true, true, false, false, false);
5504
5505 Xml.Append("</updateContract>");
5506
5507 await this.client.SendIqSet(Address, Xml.ToString(), this.ContractResponse, new object[] { Callback, State });
5508 }
5509
5516 {
5517 return this.UpdateContractAsync(this.GetTrustProvider(Contract.ContractId), Contract);
5518 }
5519
5526 public async Task<Contract> UpdateContractAsync(string Address, Contract Contract)
5527 {
5528 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
5529
5530 await this.UpdateContract(Address, Contract, (Sender, e) =>
5531 {
5532 if (e.Ok)
5533 Result.TrySetResult(e.Contract);
5534 else
5535 Result.TrySetException(e.StanzaError ?? new Exception("Unable to update the contract."));
5536
5537 return Task.CompletedTask;
5538
5539 }, null);
5540
5541 return await Result.Task;
5542 }
5543
5544 #endregion
5545
5546 #region Validate Contract
5547
5554 public Task Validate(Contract Contract, EventHandlerAsync<ContractValidationEventArgs> Callback, object State)
5555 {
5556 return this.Validate(Contract, true, true, true, true, Callback, State);
5557 }
5558
5566 public Task Validate(Contract Contract, bool ValidateState, EventHandlerAsync<ContractValidationEventArgs> Callback, object State)
5567 {
5568 return this.Validate(Contract, ValidateState, true, true, true, Callback, State);
5569 }
5570
5582 public async Task Validate(Contract Contract, bool ValidateState, bool ValidateAttachments,
5583 bool ValidateIdentities, bool ValidateIdentityAttachments,
5584 EventHandlerAsync<ContractValidationEventArgs> Callback, object State)
5585 {
5586 if (Contract is null)
5587 {
5588 await this.ReturnStatus(ContractStatus.ContractUndefined, Callback, State);
5589 return;
5590 }
5591
5592 if (ValidateState &&
5593 Contract.State != ContractState.Approved &&
5594 Contract.State != ContractState.BeingSigned &&
5595 Contract.State != ContractState.Signed)
5596 {
5597 await this.ReturnStatus(ContractStatus.NotApproved, Callback, State,
5598 new KeyValuePair<string, object>("State", Contract.State));
5599 return;
5600 }
5601
5602 DateTime UtcNow = DateTime.UtcNow;
5603
5604 if (UtcNow < Contract.From.ToUniversalTime())
5605 {
5606 await this.ReturnStatus(ContractStatus.NotValidYet, Callback, State,
5607 new KeyValuePair<string, object>("From", Contract.From));
5608 return;
5609 }
5610
5611 if (UtcNow > Contract.To.ToUniversalTime())
5612 {
5613 await this.ReturnStatus(ContractStatus.NotValidAnymore, Callback, State,
5614 new KeyValuePair<string, object>("To", Contract.To));
5615 return;
5616 }
5617
5618 if (string.IsNullOrEmpty(Contract.Provider))
5619 {
5620 await this.ReturnStatus(ContractStatus.NoTrustProvider, Callback, State);
5621 return;
5622 }
5623
5624 if (Contract.PartsMode == ContractParts.TemplateOnly)
5625 {
5626 await this.ReturnStatus(ContractStatus.TemplateOnly, Callback, State);
5627 return;
5628 }
5629
5630 if (Contract.ClientSignatures is null || Contract.ClientSignatures.Length == 0)
5631 {
5632 await this.ReturnStatus(ContractStatus.NoClientSignatures, Callback, State);
5633 return;
5634 }
5635
5636 if (!await Contract.IsLegallyBinding(false, this))
5637 {
5638 await this.ReturnStatus(ContractStatus.NotLegallyBinding, Callback, State);
5639 return;
5640 }
5641
5642 if (!await IsHumanReadableWellDefined(Contract))
5643 {
5644 await this.ReturnStatus(ContractStatus.HumanReadableNotWellDefined, Callback, State);
5645 return;
5646 }
5647
5648 if ((Contract.Parameters?.Length ?? 0) > 0)
5649 {
5650 try
5651 {
5653 {
5654 { "Duration", Contract.Duration }
5655 };
5656
5657 DateTime? FirstSignature = Contract.FirstSignatureAt;
5658 if (FirstSignature.HasValue)
5659 {
5660 Variables["Now"] = FirstSignature.Value.ToLocalTime();
5661 Variables["NowUtc"] = FirstSignature.Value.ToUniversalTime();
5662 }
5663
5666
5668
5670 {
5671 if (!await Parameter.IsParameterValid(Variables, this))
5672 {
5674
5675 Tags.Add(new KeyValuePair<string, object>(Parameter.Name, Parameter.ErrorText));
5676 if (Parameter.ErrorReason.HasValue)
5677 Tags.Add(new KeyValuePair<string, object>(Parameter.Name + "_Reason", Parameter.ErrorReason.Value));
5678 }
5679 }
5680
5681 if (!(Tags is null))
5682 {
5683 await this.ReturnStatus(ContractStatus.ParameterValuesNotValid, Callback,
5684 State, Tags.ToArray());
5685 return;
5686 }
5687 }
5689 {
5690 await this.ReturnStatus(ContractStatus.NoResponse, Callback, State,
5691 new KeyValuePair<string, object>("Error", ex.Message));
5692 return;
5693 }
5694 catch (Exception ex)
5695 {
5696 await this.ReturnStatus(ContractStatus.ParameterValuesNotValid, Callback, State,
5697 new KeyValuePair<string, object>("Error", ex.Message));
5698 return;
5699 }
5700 }
5701
5702 if (string.IsNullOrEmpty(Contract.ForMachinesLocalName) ||
5703 string.IsNullOrEmpty(Contract.ForMachinesNamespace) ||
5704 Contract.ForMachines is null ||
5707 {
5708 await this.ReturnStatus(ContractStatus.MachineReadableNotWellDefined, Callback, State);
5709 return;
5710 }
5711
5712 XmlDocument Doc;
5713
5714 try
5715 {
5716 Doc = XML.ParseXml(Contract.ForMachines.OuterXml, true);
5717 }
5718 catch (Exception ex)
5719 {
5720 await this.ReturnStatus(ContractStatus.MachineReadableNotWellDefined, Callback, State,
5721 new KeyValuePair<string, object>("Error", ex.Message));
5722 return;
5723 }
5724
5725 Dictionary<string, XmlSchema> Schemas = new Dictionary<string, XmlSchema>();
5727 {
5728 Doc.DocumentElement
5729 };
5730 XmlElement E;
5731 string LastNamespace = null;
5732 string Namespace;
5733 XmlSchema Schema;
5734
5735 while (ToCheck.HasFirstItem)
5736 {
5737 E = ToCheck.RemoveFirst();
5738 Namespace = E.NamespaceURI;
5739 if (!string.IsNullOrEmpty(Namespace) && Namespace != LastNamespace)
5740 {
5741 Schemas[Namespace] = null;
5742 LastNamespace = Namespace;
5743 }
5744
5745 if (E.HasAttributes)
5746 {
5747 foreach (XmlAttribute Attr in E.Attributes)
5748 {
5749 Namespace = Attr.NamespaceURI;
5750
5751 if (!string.IsNullOrEmpty(Namespace) &&
5752 Namespace != LastNamespace &&
5753 Namespace != "http://www.w3.org/XML/1998/namespace" &&
5754 Namespace != "http://www.w3.org/2000/xmlns/")
5755 {
5756 Schemas[Namespace] = null; // Only change LastNamespace when element namespaces change.
5757 }
5758 }
5759 }
5760
5761 foreach (XmlNode N in E.ChildNodes)
5762 {
5763 if (N is XmlElement E2)
5764 ToCheck.Add(E2);
5765 }
5766 }
5767
5768 int NrSchemas = Schemas.Count;
5769 if (NrSchemas == 0 || !Schemas.ContainsKey(Contract.ForMachinesNamespace))
5770 {
5771 await this.ReturnStatus(ContractStatus.MachineReadableNotWellDefined, Callback, State,
5772 new KeyValuePair<string, object>("Namespace", Contract.ForMachinesNamespace));
5773 return;
5774 }
5775
5776 Tuple<XmlSchema, ContractStatus?, Exception> SchemaResult;
5777
5778 SchemaResult = await this.LoadSchema(this.componentAddress, Contract.ForMachinesNamespace,
5780
5781 if (SchemaResult.Item2.HasValue)
5782 {
5783 await this.ReturnStatus(SchemaResult.Item2.Value, Callback, State,
5784 new KeyValuePair<string, object>("Error", SchemaResult.Item3?.Message ?? string.Empty),
5785 new KeyValuePair<string, object>("Namespace", Contract.ForMachinesNamespace),
5786 new KeyValuePair<string, object>("HashFunction", Contract.ContentSchemaHashFunction),
5787 new KeyValuePair<string, object>("Digest", Convert.ToBase64String(Contract.ContentSchemaDigest)));
5788 return;
5789 }
5790 else if (SchemaResult.Item1 is null)
5791 {
5792 await this.ReturnStatus(ContractStatus.NoSchemaAccess, Callback, State);
5793 return;
5794 }
5795 else
5796 {
5797 Schema = SchemaResult.Item1;
5798 Schemas[Contract.ForMachinesNamespace] = Schema;
5799 }
5800
5801 string[] Namespaces = new string[Schemas.Count];
5802 Schemas.Keys.CopyTo(Namespaces, 0);
5803
5804 string ContractComponent;
5805 int i = Contract.ContractId.IndexOf('@');
5806 if (i < 0)
5807 ContractComponent = this.componentAddress;
5808 else
5809 ContractComponent = Contract.ContractId[(i + 1)..];
5810
5811 foreach (string Namespace2 in Namespaces)
5812 {
5813 if (Schemas.TryGetValue(Namespace2, out Schema) && !(Schema is null))
5814 continue;
5815
5816 SchemaResult = await this.LoadSchema(this.componentAddress, Namespace2, null, null);
5817
5818 if (SchemaResult.Item2.HasValue)
5819 {
5820 await this.ReturnStatus(SchemaResult.Item2.Value, Callback, State,
5821 new KeyValuePair<string, object>("Error", SchemaResult.Item3?.Message ?? string.Empty),
5822 new KeyValuePair<string, object>("Namespace", Namespace2));
5823 return;
5824 }
5825 else if (SchemaResult.Item1 is null)
5826 {
5827 await this.ReturnStatus(ContractStatus.NoSchemaAccess, Callback, State,
5828 new KeyValuePair<string, object>("Namespace", Namespace2));
5829 return;
5830 }
5831 else
5832 {
5833 Schema = SchemaResult.Item1;
5834 Schemas[Namespace2] = Schema;
5835 }
5836 }
5837
5838 try
5839 {
5840 XmlSchema[] Schemas2 = new XmlSchema[Schemas.Count];
5841 Schemas.Values.CopyTo(Schemas2, 0);
5842
5844 }
5845 catch (XmlSchemaException ex)
5846 {
5847 // TODO: Remove
5848 Log.Alert(ex);
5849 Log.Debug("Unable to validate Machine-readable content. Following events record the contents of the operation.");
5850 Log.Debug("Error:\r\n\r\n```\r\n" + ex.Message + "\r\n```");
5851 Log.Debug("XML:\r\n\r\n```\r\n" + Doc.OuterXml + "\r\n```");
5852
5853 foreach (KeyValuePair<string, XmlSchema> P in Schemas)
5854 {
5855 using MemoryStream ms = new MemoryStream();
5856 P.Value.Write(ms);
5857
5858 Log.Debug("`" + P.Key + "`\r\n\r\n```\r\n" +
5859 Encoding.UTF8.GetString(ms.ToArray()) + "\r\n```");
5860 }
5861
5862 Log.Debug("End of schemas.");
5863
5864 await this.ReturnStatus(ContractStatus.FraudulentMachineReadable, Callback, State,
5865 new KeyValuePair<string, object>("Error", ex.Message));
5866
5867 return;
5868 }
5869 catch (Exception ex)
5870 {
5871 Log.Exception(ex);
5872
5873 await this.ReturnStatus(ContractStatus.FraudulentMachineReadable, Callback, State,
5874 new KeyValuePair<string, object>("Error", ex.Message));
5875
5876 return;
5877 }
5878
5879 StringBuilder Xml = new StringBuilder();
5880 Contract.Serialize(Xml, false, false, false, false, false, false, false);
5881 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
5882 Dictionary<string, LegalIdentity> Identities = new Dictionary<string, LegalIdentity>();
5883
5884 if (ValidateIdentities)
5885 {
5887 {
5888 if (Identities.ContainsKey(Signature.LegalId))
5889 continue;
5890
5891 KeyValuePair<LegalIdentity, Exception> P = await this.ValidateSignatureAsyncEx(Signature.LegalId, Data, Signature.DigitalSignature);
5892 LegalIdentity Identity = P.Key;
5893
5894 if (Identity is null)
5895 {
5896 if (P.Value is RecipientUnavailableException)
5897 {
5898 await this.ReturnStatus(ContractStatus.NoResponse, Callback, State,
5899 new KeyValuePair<string, object>("LegalId", Signature.LegalId));
5900 }
5901 else if (!(P.Value is null))
5902 {
5903 await this.ReturnStatus(ContractStatus.ClientSignatureNotValidated, Callback, State,
5904 new KeyValuePair<string, object>("LegalId", Signature.LegalId),
5905 new KeyValuePair<string, object>("Exception", P.Value.GetType().FullName),
5906 new KeyValuePair<string, object>("Message", P.Value.Message));
5907 }
5908 else
5909 {
5910 await this.ReturnStatus(ContractStatus.ClientSignatureInvalid, Callback, State,
5911 new KeyValuePair<string, object>("LegalId", Signature.LegalId),
5912 new KeyValuePair<string, object>("DataBase64", Convert.ToBase64String(Data)),
5913 new KeyValuePair<string, object>("SignatureBase64", Convert.ToBase64String(Signature.DigitalSignature)));
5914 }
5915 return;
5916 }
5917
5918 IdentityValidationEventArgs e = await this.ValidateAsync(Identity, true, ValidateIdentityAttachments);
5919 if (e.Status != IdentityStatus.Valid)
5920 {
5921 await this.ReturnStatus(ContractStatus.ClientIdentityInvalid, Callback,
5922 State, e.Tags.Join(
5923 new KeyValuePair<string, object>("IdentityStatus", e.Status),
5924 new KeyValuePair<string, object>("LegalId", Identity.Id)));
5925 return;
5926 }
5927
5928 Identities[Signature.LegalId] = Identity;
5929 }
5930 }
5931
5932 if (ValidateAttachments && !(Contract.Attachments is null))
5933 {
5935 {
5936 if (string.IsNullOrEmpty(Attachment.Url))
5937 {
5938 await this.ReturnStatus(ContractStatus.AttachmentLacksUrl, Callback, State,
5939 new KeyValuePair<string, object>("AttachmentId", Attachment.Id));
5940 return;
5941 }
5942
5943 try
5944 {
5945 KeyValuePair<string, TemporaryFile> P = await this.GetAttachmentAsync(Attachment.Url, SignWith.LatestApprovedId, 30000);
5946 bool? IsValid;
5947 using TemporaryFile File = P.Value;
5948
5949 if (P.Key != Attachment.ContentType)
5950 {
5951 await this.ReturnStatus(ContractStatus.AttachmentInconsistency, Callback, State,
5952 new KeyValuePair<string, object>("AttachmentId", Attachment.Id),
5953 new KeyValuePair<string, object>("AttachmentUrl", Attachment.Url),
5954 new KeyValuePair<string, object>("ExpectedContentType", Attachment.ContentType),
5955 new KeyValuePair<string, object>("ContentType", P.Key));
5956 return;
5957 }
5958
5959 File.Position = 0;
5960
5961 if (Identities.TryGetValue(Attachment.LegalId, out LegalIdentity Identity))
5962 IsValid = this.ValidateSignature(Identity, File, Attachment.Signature);
5963 else
5964 {
5965 MemoryStream ms = new MemoryStream();
5966 await File.CopyToAsync(ms);
5967 Data = ms.ToArray();
5968
5969 try
5970 {
5971 Identity = await this.ValidateSignatureAsync(Attachment.LegalId, Data, Attachment.Signature);
5972 Identities[Attachment.LegalId] = Identity;
5973 IsValid = true;
5974 }
5975 catch (Exception)
5976 {
5977 IsValid = false;
5978 }
5979 }
5980
5981 if (IsValid.HasValue)
5982 {
5983 if (!IsValid.Value)
5984 {
5985 await this.ReturnStatus(ContractStatus.AttachmentSignatureInvalid, Callback, State,
5986 new KeyValuePair<string, object>("AttachmentId", Attachment.Id),
5987 new KeyValuePair<string, object>("AttachmentUrl", Attachment.Url),
5988 new KeyValuePair<string, object>("AttachmentSignatureBase64", Convert.ToBase64String(Attachment.Signature)));
5989 return;
5990 }
5991 }
5992 else
5993 {
5994 await this.ReturnStatus(ContractStatus.AttachmentSignatureInvalid, Callback, State,
5995 new KeyValuePair<string, object>("AttachmentId", Attachment.Id),
5996 new KeyValuePair<string, object>("AttachmentUrl", Attachment.Url),
5997 new KeyValuePair<string, object>("KeyName", Identity.ClientKeyName));
5998 return;
5999 }
6000 }
6001 catch (Exception ex)
6002 {
6003 await this.ReturnStatus(ContractStatus.AttachmentUnavailable, Callback, State,
6004 new KeyValuePair<string, object>("AttachmentId", Attachment.Id),
6005 new KeyValuePair<string, object>("AttachmentUrl", Attachment.Url),
6006 new KeyValuePair<string, object>("Error", ex.Message));
6007 return;
6008 }
6009 }
6010 }
6011
6012 if (Contract.ServerSignature is null)
6013 {
6014 await this.ReturnStatus(ContractStatus.NoProviderSignature, Callback, State);
6015 return;
6016 }
6017
6018 Xml.Clear();
6019 Contract.Serialize(Xml, false, true, true, true, true, false, false);
6020 Data = Encoding.UTF8.GetBytes(Xml.ToString());
6021
6022 bool HasOldPublicKey = this.publicKeys.TryGetRecord(Contract.Provider,
6023 Contract.Updated, out _);
6024
6025 await this.GetServerPublicKey(Contract.Provider, Contract.Updated, async (Sender, e) =>
6026 {
6027 if (e.Ok && !(e.Key is null))
6028 {
6029 bool Valid = e.Key.Verify(Data, Contract.ServerSignature.DigitalSignature);
6030
6031 if (Valid)
6032 {
6033 await this.ReturnStatus(ContractStatus.Valid, Callback, State);
6034 return;
6035 }
6036
6037 if (!HasOldPublicKey)
6038 {
6039 await this.ReturnStatus(ContractStatus.ProviderSignatureInvalid, Callback, State,
6040 new KeyValuePair<string, object>("Provider", Contract.Provider),
6041 new KeyValuePair<string, object>("LocalName", e.Key.LocalName),
6042 new KeyValuePair<string, object>("Namespace", e.Key.Namespace),
6043 new KeyValuePair<string, object>("PublicKeyBase64", e.Key.PublicKeyBase64),
6044 new KeyValuePair<string, object>("DataBase64", Convert.ToBase64String(Data)),
6045 new KeyValuePair<string, object>("SignatureBase64", Convert.ToBase64String(Contract.ServerSignature.DigitalSignature)));
6046 return;
6047 }
6048
6049 this.publicKeys.Remove(Contract.Provider);
6050
6051 await this.GetServerPublicKey(Contract.Provider, Contract.Updated,
6052 (sender2, e2) =>
6053 {
6054 if (e2.Ok && !(e2.Key is null))
6055 {
6056 if (e.Key.Equals(e2.Key))
6057 {
6058 return this.ReturnStatus(ContractStatus.ProviderSignatureInvalid, Callback, State,
6059 new KeyValuePair<string, object>("Provider", Contract.Provider),
6060 new KeyValuePair<string, object>("LocalName", e.Key.LocalName),
6061 new KeyValuePair<string, object>("Namespace", e.Key.Namespace),
6062 new KeyValuePair<string, object>("PublicKeyBase64", e.Key.PublicKeyBase64),
6063 new KeyValuePair<string, object>("DataBase64", Convert.ToBase64String(Data)),
6064 new KeyValuePair<string, object>("SignatureBase64", Convert.ToBase64String(Contract.ServerSignature.DigitalSignature)));
6065 }
6066
6067 Valid = e2.Key.Verify(Data, Contract.ServerSignature.DigitalSignature);
6068
6069 if (Valid)
6070 return this.ReturnStatus(ContractStatus.Valid, Callback, State);
6071 else
6072 return this.ReturnStatus(ContractStatus.ProviderSignatureInvalid, Callback, State);
6073 }
6074 else
6075 {
6076 return this.ReturnStatus(ContractStatus.NoProviderPublicKey, Callback, State,
6077 new KeyValuePair<string, object>("Provider", Contract.Provider),
6078 new KeyValuePair<string, object>("ErrorText", e2.ErrorText));
6079 }
6080
6081 }, State);
6082 }
6083 else
6084 {
6085 await this.ReturnStatus(ContractStatus.NoProviderPublicKey, Callback, State,
6086 new KeyValuePair<string, object>("Provider", Contract.Provider),
6087 new KeyValuePair<string, object>("ErrorText", e.ErrorText));
6088 }
6089
6090 }, State);
6091 }
6092
6093 private async Task<Tuple<XmlSchema, ContractStatus?, Exception>> LoadSchema(string ContractComponent, string Namespace,
6095 {
6096 string SchemaKey = SchemaDigest is null ? Namespace : Namespace + "#" + Convert.ToBase64String(SchemaDigest);
6097 byte[] SchemaBin;
6098 XmlSchema Schema;
6099
6100 lock (this.schemas)
6101 {
6102 if (this.schemas.TryGetValue(SchemaKey, out Tuple<byte[], XmlSchema, DateTime> P) &&
6103 P.Item3 >= DateTime.UtcNow)
6104 {
6105 return new Tuple<XmlSchema, ContractStatus?, Exception>(P.Item2, null, null);
6106 }
6107 }
6108
6110 SchemaDigest is null ? null : new SchemaDigest(HashFunction.Value, SchemaDigest));
6111
6112 await GetLocalSchema.Raise(this, e, false);
6113
6114 if (!(e.XmlSchema is null))
6115 {
6116 SchemaBin = e.XmlSchema;
6117 Schema = XSL.LoadSchema(SchemaBin, SchemaKey);
6118
6119 lock (this.schemas)
6120 {
6121 this.schemas[SchemaKey] = new Tuple<byte[], XmlSchema, DateTime>(SchemaBin, Schema, DateTime.UtcNow.AddDays(CacheSchemaDays));
6122 }
6123
6124 return new Tuple<XmlSchema, ContractStatus?, Exception>(Schema, null, null);
6125 }
6126
6127 if (string.IsNullOrEmpty(ContractComponent))
6128 ContractComponent = this.componentAddress;
6129
6130 try
6131 {
6132 SchemaBin = await this.GetSchemaAsync(ContractComponent, Namespace,
6133 SchemaDigest is null ? null : new SchemaDigest(HashFunction.Value, SchemaDigest));
6134 }
6135 catch (Exception ex)
6136 {
6137 return new Tuple<XmlSchema, ContractStatus?, Exception>(null, ContractStatus.NoSchemaAccess, ex);
6138 }
6139
6140 if (!(SchemaDigest is null))
6141 {
6142 byte[] Digest = Hashes.ComputeHash(HashFunction.Value, SchemaBin);
6143
6144 if (Convert.ToBase64String(Digest) != Convert.ToBase64String(SchemaDigest))
6145 return new Tuple<XmlSchema, ContractStatus?, Exception>(null, ContractStatus.FraudulentSchema, null);
6146 }
6147
6148 try
6149 {
6150 Schema = XSL.LoadSchema(SchemaBin, SchemaKey);
6151 }
6152 catch (Exception ex)
6153 {
6154 return new Tuple<XmlSchema, ContractStatus?, Exception>(null, ContractStatus.CorruptSchema, ex);
6155 }
6156
6157 lock (this.schemas)
6158 {
6159 this.schemas[SchemaKey] = new Tuple<byte[], XmlSchema, DateTime>(SchemaBin, Schema, DateTime.UtcNow.AddDays(CacheSchemaDays));
6160 }
6161
6162 return new Tuple<XmlSchema, ContractStatus?, Exception>(Schema, null, null);
6163 }
6164
6168 public static event EventHandlerAsync<SchemaReferenceEventArgs> GetLocalSchema = null;
6169
6170 private readonly Dictionary<string, Tuple<byte[], XmlSchema, DateTime>> schemas = new Dictionary<string, Tuple<byte[], XmlSchema, DateTime>>();
6171
6172 private Task ReturnStatus(ContractStatus Status, EventHandlerAsync<ContractValidationEventArgs> Callback, object State,
6173 params KeyValuePair<string, object>[] Tags)
6174 {
6175 return Callback.Raise(this, new ContractValidationEventArgs(Status, State, Tags));
6176 }
6177
6178 private static async Task<bool> IsHumanReadableWellDefined(HumanReadableText[] Texts)
6179 {
6180 if (Texts is null)
6181 return false;
6182
6183 foreach (HumanReadableText Text in Texts)
6184 {
6185 if (!(await Text.IsWellDefined() is null))
6186 return false;
6187 }
6188
6189 return true;
6190 }
6191
6192 private static async Task<bool> IsHumanReadableWellDefined(Contract Contract)
6193 {
6194 if (!await IsHumanReadableWellDefined(Contract.ForHumans))
6195 return false;
6196
6197 if (!(Contract.Roles is null))
6198 {
6199 foreach (Role Role in Contract.Roles)
6200 {
6201 if (!await IsHumanReadableWellDefined(Role.Descriptions))
6202 return false;
6203 }
6204 }
6205
6206 if (!(Contract.Parameters is null))
6207 {
6209 {
6210 if (!await IsHumanReadableWellDefined(Parameter.Descriptions))
6211 return false;
6212 }
6213 }
6214
6215 return true;
6216 }
6217
6223 public Task<ContractValidationEventArgs> ValidateAsync(Contract Contract)
6224 {
6225 return this.ValidateAsync(Contract, true, true, true, true);
6226 }
6227
6234 public Task<ContractValidationEventArgs> ValidateAsync(Contract Contract, bool ValidateState)
6235 {
6236 return this.ValidateAsync(Contract, ValidateState, true, true, true);
6237 }
6238
6249 public async Task<ContractValidationEventArgs> ValidateAsync(Contract Contract,
6250 bool ValidateState, bool ValidateAttachments,
6251 bool ValidateIdentities, bool ValidateIdentityAttachments)
6252 {
6253 TaskCompletionSource<ContractValidationEventArgs> Result = new TaskCompletionSource<ContractValidationEventArgs>();
6254
6255 await this.Validate(Contract, ValidateState, ValidateAttachments,
6256 ValidateIdentities, ValidateIdentityAttachments, (Sender, e) =>
6257 {
6258 Result.TrySetResult(e);
6259 return Task.CompletedTask;
6260 }, null);
6261
6262 return await Result.Task;
6263 }
6264
6265 #endregion
6266
6267 #region Can Sign As
6268
6276 public async Task<bool> CanSignAs(CaseInsensitiveString ReferenceId, CaseInsensitiveString SignatoryId)
6277 {
6278 string ReferenceDomain = XmppClient.GetDomain(ReferenceId);
6279 string SignatoryDomain = XmppClient.GetDomain(SignatoryId);
6280
6281 if (ReferenceDomain != SignatoryDomain)
6282 return false;
6283
6284 TaskCompletionSource<bool> Result = new TaskCompletionSource<bool>();
6285 StringBuilder Xml = new StringBuilder();
6286
6287 Xml.Append("<canSignAs xmlns='");
6288 Xml.Append(NamespaceLegalIdentitiesCurrent);
6289 Xml.Append("' referenceId='");
6290 Xml.Append(XML.Encode(ReferenceId));
6291 Xml.Append("' signatoryId='");
6292 Xml.Append(XML.Encode(SignatoryId));
6293 Xml.Append("'/>");
6294
6295 await this.client.SendIqGet(ReferenceDomain, Xml.ToString(), (_, e) =>
6296 {
6297 Result.TrySetResult(e.Ok);
6298 return Task.CompletedTask;
6299 }, null);
6300
6301 return await Result.Task;
6302 }
6303
6304 #endregion
6305
6306 #region SendContractProposal
6307
6315 public Task SendContractProposal(Contract Contract, string Role, string To)
6316 {
6317 return this.SendContractProposal(Contract, Role, To, string.Empty);
6318 }
6319
6328 public async Task SendContractProposal(Contract Contract, string Role, string To, string Message)
6329 {
6331 {
6332 Tuple<SymmetricCipherAlgorithms, string, byte[]> T = await this.TryLoadContractSharedSecret(Contract.ContractId);
6333
6334 if (!(T is null))
6335 {
6336 await this.SendContractProposal(Contract.ContractId, Role, To, Message, T.Item3, T.Item1);
6337 return;
6338 }
6339 }
6340
6341 await this.SendContractProposal(Contract.ContractId, Role, To, Message, null, SymmetricCipherAlgorithms.Aes256);
6342 }
6343
6350 public Task SendContractProposal(string ContractId, string Role, string To)
6351 {
6352 return this.SendContractProposal(ContractId, Role, To, string.Empty);
6353 }
6354
6362 public Task SendContractProposal(string ContractId, string Role, string To, string Message)
6363 {
6364 return this.SendContractProposal(ContractId, Role, To, Message, null, SymmetricCipherAlgorithms.Aes256);
6365 }
6366
6376 public async Task SendContractProposal(string ContractId, string Role, string To, string Message, byte[] Key,
6377 SymmetricCipherAlgorithms KeyAlgorithm)
6378 {
6379 StringBuilder Xml = new StringBuilder();
6380
6381 Xml.Append("<contractProposal xmlns=\"");
6382 Xml.Append(NamespaceSmartContractsCurrent);
6383 Xml.Append("\" contractId=\"");
6384 Xml.Append(XML.Encode(ContractId));
6385 Xml.Append("\" role=\"");
6386 Xml.Append(XML.Encode(Role));
6387
6388 if (!string.IsNullOrEmpty(Message))
6389 {
6390 Xml.Append("\" message=\"");
6391 Xml.Append(XML.Encode(Message));
6392 }
6393
6394 Xml.Append('"');
6395
6396 if (Key is null)
6397 {
6398 Xml.Append("/>");
6399
6400 if (EndpointSecurity.TryGetEndpointSecurity(this.client, out EndpointSecurity E2ee))
6401 {
6402 await E2ee.SendMessage(this.client, E2ETransmission.NormalIfNotE2E, QoSLevel.Unacknowledged, MessageType.Normal,
6403 string.Empty, To, Xml.ToString(), string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, null, null);
6404 }
6405 else
6406 {
6407 await this.client.SendMessage(MessageType.Normal, To, Xml.ToString(), string.Empty, string.Empty, string.Empty,
6408 string.Empty, string.Empty);
6409 }
6410 }
6411 else
6412 {
6413 Xml.Append("><sharedSecret key=\"");
6414 Xml.Append(Convert.ToBase64String(Key));
6415 Xml.Append("\" algorithm=\"");
6416
6417 switch (KeyAlgorithm)
6418 {
6419 case SymmetricCipherAlgorithms.Aes256:
6420 Xml.Append("aes");
6421 break;
6422
6423 case SymmetricCipherAlgorithms.ChaCha20:
6424 Xml.Append("cha");
6425 break;
6426
6427 case SymmetricCipherAlgorithms.AeadChaCha20Poly1305:
6428 Xml.Append("acp");
6429 break;
6430
6431 default:
6432 throw new ArgumentException("Algorithm not recognized.", nameof(KeyAlgorithm));
6433 }
6434
6435 Xml.Append("\"/></contractProposal>");
6436
6437 if (!EndpointSecurity.TryGetEndpointSecurity(this.client, out EndpointSecurity E2ee))
6438 throw new InvalidOperationException("End-to-End encryption not enabled.");
6439
6440 if (XmppClient.GetBareJID(To) == To)
6441 {
6442 RosterItem Item = this.client[To]
6443 ?? throw new ArgumentException("Recipient not in roster.", nameof(To));
6444
6445 To = Item.LastPresenceFullJid;
6446 if (string.IsNullOrEmpty(To))
6447 throw new ArgumentException("Recipient not online.", nameof(To));
6448 }
6449
6450 await E2ee.SendMessage(this.client, E2ETransmission.AssertE2E, QoSLevel.Unacknowledged, MessageType.Normal,
6451 string.Empty, To, Xml.ToString(), string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, null, null);
6452 }
6453 }
6454
6455 private async Task ContractProposalMessageHandler(object Sender, MessageEventArgs e)
6456 {
6457 string ContractId = XML.Attribute(e.Content, "contractId");
6458 string Role = XML.Attribute(e.Content, "role");
6459 string Message = XML.Attribute(e.Content, "message");
6460 byte[] Key = null;
6462
6463 foreach (XmlNode N in e.Content.ChildNodes)
6464 {
6465 if (N is XmlElement E && E.LocalName == "sharedSecret" && E.NamespaceURI == e.Content.NamespaceURI)
6466 {
6467 if (!e.UsesE2eEncryption)
6468 {
6469 this.client.Error("Confidential Proposal not sent using end-to-end encryption. Message discarded.");
6470 return;
6471 }
6472
6473 try
6474 {
6475 Key = Convert.FromBase64String(XML.Attribute(E, "key"));
6476 }
6477 catch (Exception)
6478 {
6479 this.client.Error("Invalid base64-encoded shared secret. Message discarded.");
6480 return;
6481 }
6482
6483 string Cipher = XML.Attribute(E, "algorithm");
6484
6485 switch (Cipher)
6486 {
6487 case "aes":
6488 KeyAlgorithm = SymmetricCipherAlgorithms.Aes256;
6489 break;
6490
6491 case "cha":
6492 KeyAlgorithm = SymmetricCipherAlgorithms.ChaCha20;
6493 break;
6494
6495 case "acp":
6496 KeyAlgorithm = SymmetricCipherAlgorithms.AeadChaCha20Poly1305;
6497 break;
6498
6499 default:
6500 this.client.Error("Unrecognized key algorithm. Message discarded.");
6501 return;
6502 }
6503 }
6504 }
6505
6506 if (!(Key is null))
6507 await this.SaveContractSharedSecret(ContractId, e.FromBareJID, Key, KeyAlgorithm, true);
6508
6509 await this.ContractProposalReceived.Raise(this, new ContractProposalEventArgs(e, ContractId, Role, Message, Key, KeyAlgorithm));
6510 }
6511
6512 internal async Task<bool> SaveContractSharedSecret(string ContractId, string CreatorJid, byte[] Key,
6513 SymmetricCipherAlgorithms KeyAlgorithm, bool OnlyIfNew)
6514 {
6515 if (OnlyIfNew)
6516 {
6517 ContractSharedSecretState State = await this.GetContractStateAsync(ContractId);
6518
6519 if (!(State is null) && State.HasSharedSecret)
6520 return false;
6521
6522 if (!(await this.TryLoadLegacyContractSharedSecretAsync(ContractId, true) is null))
6523 return false;
6524 }
6525
6526 return await this.UpsertContractStateAsync(ContractId, CreatorJid, Key, KeyAlgorithm);
6527 }
6528
6529 internal async Task<Tuple<SymmetricCipherAlgorithms, string, byte[]>> TryLoadContractSharedSecret(string ContractId)
6530 {
6531 ContractSharedSecretState State = await this.GetContractStateAsync(ContractId);
6532 Tuple<SymmetricCipherAlgorithms, string, byte[]> Result = this.TryLoadContractSharedSecret(State);
6533
6534 if (!(Result is null))
6535 return Result;
6536
6537 if (!(State is null))
6538 return null;
6539
6540 return await this.TryLoadLegacyContractSharedSecretAsync(ContractId, true);
6541 }
6542
6546 public event EventHandlerAsync<ContractProposalEventArgs> ContractProposalReceived = null;
6547
6548 #endregion
6549
6550 #region Get Schemas
6551
6557 public Task GetSchemas(EventHandlerAsync<SchemaReferencesEventArgs> Callback, object State)
6558 {
6559 return this.GetSchemas(this.componentAddress, Callback, State);
6560 }
6561
6568 public Task GetSchemas(string Address, EventHandlerAsync<SchemaReferencesEventArgs> Callback, object State)
6569 {
6570 return this.client.SendIqGet(Address, "<getSchemas xmlns='" + NamespaceSmartContractsCurrent + "'/>",
6571 async (Sender, e) =>
6572 {
6573 XmlElement E = e.FirstElement;
6574 List<SchemaReference> Schemas = new List<SchemaReference>();
6575
6576 if (e.Ok && !(E is null) && E.LocalName == "schemas")
6577 {
6578 foreach (XmlNode N in E.ChildNodes)
6579 {
6580 if (N is XmlElement E2 && E2.LocalName == "schemaRef")
6581 {
6582 string Namespace = XML.Attribute(E2, "namespace");
6583 List<SchemaDigest> Digests = new List<SchemaDigest>();
6584
6585 foreach (XmlNode N2 in E2.ChildNodes)
6586 {
6587 if (N2 is XmlElement E3 && E3.LocalName == "digest")
6588 {
6589 if (!Enum.TryParse(XML.Attribute(E3, "function"), out HashFunction Function))
6590 continue;
6591
6592 byte[] Digest = Convert.FromBase64String(E3.InnerText);
6593
6594 Digests.Add(new SchemaDigest(Function, Digest));
6595 }
6596 }
6597
6598 Schemas.Add(new SchemaReference(Namespace, Digests.ToArray()));
6599 }
6600 }
6601 }
6602 else
6603 e.Ok = false;
6604
6605 await Callback.Raise(this, new SchemaReferencesEventArgs(e, Schemas.ToArray()));
6606
6607 }, State);
6608 }
6609
6614 public Task<SchemaReference[]> GetSchemasAsync()
6615 {
6616 return this.GetSchemasAsync(this.componentAddress);
6617 }
6618
6624 public async Task<SchemaReference[]> GetSchemasAsync(string Address)
6625 {
6626 TaskCompletionSource<SchemaReference[]> Result = new TaskCompletionSource<SchemaReference[]>();
6627
6628 await this.GetSchemas(Address, (Sender, e) =>
6629 {
6630 if (e.Ok)
6631 Result.TrySetResult(e.References);
6632 else
6633 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get schemas."));
6634
6635 return Task.CompletedTask;
6636
6637 }, null);
6638
6639 return await Result.Task;
6640 }
6641
6642 #endregion
6643
6644 #region Get Schema
6645
6652 public Task GetSchema(string Namespace, EventHandlerAsync<SchemaEventArgs> Callback, object State)
6653 {
6654 return this.GetSchema(this.componentAddress, Namespace, null, Callback, State);
6655 }
6656
6664 public Task GetSchema(string Namespace, SchemaDigest Digest, EventHandlerAsync<SchemaEventArgs> Callback, object State)
6665 {
6666 return this.GetSchema(this.componentAddress, Namespace, Digest, Callback, State);
6667 }
6668
6676 public Task GetSchema(string Address, string Namespace, EventHandlerAsync<SchemaEventArgs> Callback, object State)
6677 {
6678 return this.GetSchema(Address, Namespace, null, Callback, State);
6679 }
6680
6689 public async Task GetSchema(string Address, string Namespace, SchemaDigest Digest, EventHandlerAsync<SchemaEventArgs> Callback, object State)
6690 {
6691 SchemaReferenceEventArgs e = new SchemaReferenceEventArgs(Namespace, Digest);
6692 await GetLocalSchema.Raise(this, e, false);
6693
6694 if (!(e.XmlSchema is null))
6695 {
6696 if (!(Callback is null))
6697 {
6698 XmlDocument Doc = new XmlDocument();
6699 XmlElement Empty = Doc.CreateElement("Local");
6700
6701 IqResultEventArgs e0 = new IqResultEventArgs(Empty, string.Empty, string.Empty, string.Empty, true, State);
6703 await Callback.Raise(this, e2);
6704 }
6705
6706 return;
6707 }
6708
6709 StringBuilder Xml = new StringBuilder();
6710
6711 Xml.Append("<getSchema xmlns='");
6712 Xml.Append(NamespaceSmartContractsCurrent);
6713 Xml.Append("' namespace='");
6714 Xml.Append(XML.Encode(Namespace));
6715
6716 if (Digest is null)
6717 Xml.Append("'/>");
6718 else
6719 {
6720 Xml.Append("'><digest function='");
6721 Xml.Append(Digest.Function.ToString());
6722 Xml.Append("'>");
6723 Xml.Append(Convert.ToBase64String(Digest.Digest));
6724 Xml.Append("</digest></getSchema>");
6725 }
6726
6727 await this.client.SendIqGet(Address, Xml.ToString(),
6728 async (Sender, e) =>
6729 {
6730 XmlElement E = e.FirstElement;
6731 byte[] Schema = null;
6732
6733 if (e.Ok && !(E is null) && E.LocalName == "schema")
6734 Schema = Convert.FromBase64String(E.InnerText);
6735 else
6736 e.Ok = false;
6737
6738 await Callback.Raise(this, new SchemaEventArgs(e, Schema));
6739
6740 }, State);
6741 }
6742
6748 public Task<byte[]> GetSchemaAsync(string Namespace)
6749 {
6750 return this.GetSchemaAsync(this.componentAddress, Namespace, null);
6751 }
6752
6759 public Task<byte[]> GetSchemaAsync(string Namespace, SchemaDigest Digest)
6760 {
6761 return this.GetSchemaAsync(this.componentAddress, Namespace, Digest);
6762 }
6763
6770 public Task<byte[]> GetSchemaAsync(string Address, string Namespace)
6771 {
6772 return this.GetSchemaAsync(Address, Namespace, null);
6773 }
6774
6782 public async Task<byte[]> GetSchemaAsync(string Address, string Namespace, SchemaDigest Digest)
6783 {
6784 TaskCompletionSource<byte[]> Result = new TaskCompletionSource<byte[]>();
6785
6786 await this.GetSchema(Address, Namespace, Digest, (Sender, e) =>
6787 {
6788 if (e.Ok)
6789 Result.TrySetResult(e.Schema);
6790 else
6791 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get schema."));
6792
6793 return Task.CompletedTask;
6794
6795 }, null);
6796
6797 return await Result.Task;
6798 }
6799
6800 #endregion
6801
6802 #region Get Legal Identities of a contract
6803
6810 public Task GetContractLegalIdentities(string ContractId, EventHandlerAsync<LegalIdentitiesEventArgs> Callback, object State)
6811 {
6812 return this.GetContractLegalIdentities(this.GetTrustProvider(ContractId), ContractId, false, true, Callback, State);
6813 }
6814
6823 public Task GetContractLegalIdentities(string ContractId, bool Current, bool Historic, EventHandlerAsync<LegalIdentitiesEventArgs> Callback, object State)
6824 {
6825 return this.GetContractLegalIdentities(this.GetTrustProvider(ContractId), ContractId, Current, Historic, Callback, State);
6826 }
6827
6835 public Task GetContractLegalIdentities(string Address, string ContractId, EventHandlerAsync<LegalIdentitiesEventArgs> Callback, object State)
6836 {
6837 return this.GetContractLegalIdentities(Address, ContractId, false, true, Callback, State);
6838 }
6839
6849 public Task GetContractLegalIdentities(string Address, string ContractId, bool Current, bool Historic, EventHandlerAsync<LegalIdentitiesEventArgs> Callback, object State)
6850 {
6851 StringBuilder Xml = new StringBuilder();
6852
6853 Xml.Append("<getLegalIdentities xmlns='");
6854 Xml.Append(NamespaceSmartContractsCurrent);
6855 Xml.Append("' contractId='");
6856 Xml.Append(XML.Encode(ContractId));
6857 Xml.Append("' current='");
6858 Xml.Append(CommonTypes.Encode(Current));
6859 Xml.Append("' historic='");
6860 Xml.Append(CommonTypes.Encode(Historic));
6861 Xml.Append("'/>");
6862
6863 return this.client.SendIqGet(Address, Xml.ToString(), this.IdentitiesResponse, new object[] { Callback, State });
6864 }
6865
6871 public Task<LegalIdentity[]> GetContractLegalIdentitiesAsync(string ContractId)
6872 {
6873 return this.GetContractLegalIdentitiesAsync(this.GetTrustProvider(ContractId), ContractId, false, true);
6874 }
6875
6883 public Task<LegalIdentity[]> GetContractLegalIdentitiesAsync(string ContractId, bool Current, bool Historic)
6884 {
6885 return this.GetContractLegalIdentitiesAsync(this.GetTrustProvider(ContractId), ContractId, Current, Historic);
6886 }
6887
6894 public Task<LegalIdentity[]> GetContractLegalIdentitiesAsync(string Address, string ContractId)
6895 {
6896 return this.GetContractLegalIdentitiesAsync(Address, ContractId, false, true);
6897 }
6898
6907 public async Task<LegalIdentity[]> GetContractLegalIdentitiesAsync(string Address, string ContractId, bool Current, bool Historic)
6908 {
6909 TaskCompletionSource<LegalIdentity[]> Result = new TaskCompletionSource<LegalIdentity[]>();
6910
6911 await this.GetContractLegalIdentities(Address, ContractId, Current, Historic, (Sender, e) =>
6912 {
6913 if (e.Ok)
6914 Result.TrySetResult(e.Identities);
6915 else
6916 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get legal identities."));
6917
6918 return Task.CompletedTask;
6919
6920 }, null);
6921
6922 return await Result.Task;
6923 }
6924
6925 #endregion
6926
6927 #region Get Network Identities of a contract
6928
6935 public Task GetContractNetworkIdentities(string ContractId, EventHandlerAsync<NetworkIdentitiesEventArgs> Callback, object State)
6936 {
6937 return this.GetContractNetworkIdentities(this.GetTrustProvider(ContractId), ContractId, Callback, State);
6938 }
6939
6947 public Task GetContractNetworkIdentities(string Address, string ContractId, EventHandlerAsync<NetworkIdentitiesEventArgs> Callback, object State)
6948 {
6949 StringBuilder Xml = new StringBuilder();
6950
6951 Xml.Append("<getNetworkIdentities xmlns='");
6952 Xml.Append(NamespaceSmartContractsCurrent);
6953 Xml.Append("' contractId='");
6954 Xml.Append(XML.Encode(ContractId));
6955 Xml.Append("'/>");
6956
6957 return this.client.SendIqGet(Address, Xml.ToString(), async (Sender, e) =>
6958 {
6959 NetworkIdentity[] Identities = null;
6960 XmlElement E;
6961
6962 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "networkIdentities")
6963 {
6964 List<NetworkIdentity> IdentitiesList = new List<NetworkIdentity>();
6965
6966 foreach (XmlNode N in E.ChildNodes)
6967 {
6968 if (N is XmlElement E2 && E2.LocalName == "networkIdentity")
6969 {
6970 string BareJid = XML.Attribute(E2, "bareJid");
6971 string LegalId = XML.Attribute(E2, "legalId");
6972
6973 IdentitiesList.Add(new NetworkIdentity(BareJid, LegalId));
6974 }
6975 }
6976
6977 Identities = IdentitiesList.ToArray();
6978 }
6979 else
6980 e.Ok = false;
6981
6982 await Callback.Raise(this, new NetworkIdentitiesEventArgs(e, Identities));
6983 }, State);
6984 }
6985
6991 public Task<NetworkIdentity[]> GetContractNetworkIdentitiesAsync(string ContractId)
6992 {
6993 return this.GetContractNetworkIdentitiesAsync(this.GetTrustProvider(ContractId), ContractId);
6994 }
6995
7002 public async Task<NetworkIdentity[]> GetContractNetworkIdentitiesAsync(string Address, string ContractId)
7003 {
7004 TaskCompletionSource<NetworkIdentity[]> Result = new TaskCompletionSource<NetworkIdentity[]>();
7005
7006 await this.GetContractNetworkIdentities(Address, ContractId, (Sender, e) =>
7007 {
7008 if (e.Ok)
7009 Result.TrySetResult(e.Identities);
7010 else
7011 Result.TrySetException(e.StanzaError ?? new Exception("Unable to get network identities."));
7012
7013 return Task.CompletedTask;
7014
7015 }, null);
7016
7017 return await Result.Task;
7018 }
7019
7020 #endregion
7021
7022 #region Search Public Contracts
7023
7030 public Task Search(SearchFilter[] Filter, EventHandlerAsync<SearchResultEventArgs> Callback, object State)
7031 {
7032 return this.Search(this.componentAddress, 0, int.MaxValue, Filter, Callback, State);
7033 }
7034
7042 public Task Search(string Address, SearchFilter[] Filter, EventHandlerAsync<SearchResultEventArgs> Callback, object State)
7043 {
7044 return this.Search(Address, 0, int.MaxValue, Filter, Callback, State);
7045 }
7046
7055 public Task Search(int Offset, int MaxCount, SearchFilter[] Filter, EventHandlerAsync<SearchResultEventArgs> Callback, object State)
7056 {
7057 return this.Search(this.componentAddress, Offset, MaxCount, Filter, Callback, State);
7058 }
7059
7069 public Task Search(string Address, int Offset, int MaxCount, SearchFilter[] Filter, EventHandlerAsync<SearchResultEventArgs> Callback, object State)
7070 {
7071 if (Offset < 0)
7072 throw new ArgumentException("Offsets cannot be negative.", nameof(Offset));
7073
7074 if (MaxCount <= 0)
7075 throw new ArgumentException("Must be postitive.", nameof(MaxCount));
7076
7077 StringBuilder Xml = new StringBuilder();
7078
7079 Xml.Append("<searchPublicContracts xmlns='");
7080 Xml.Append(NamespaceSmartContractsCurrent);
7081
7082 if (Offset > 0)
7083 {
7084 Xml.Append("' offset='");
7085 Xml.Append(Offset.ToString());
7086 }
7087
7088 if (MaxCount < int.MaxValue)
7089 {
7090 Xml.Append("' maxCount='");
7091 Xml.Append(MaxCount.ToString());
7092 }
7093
7094 Xml.Append("'>");
7095
7096 Filter = (SearchFilter[])Filter.Clone();
7097 Array.Sort(Filter, (f1, f2) => f1.Order - f2.Order);
7098
7099 int PrevOrder = 0;
7100 int PrevOrderCount = 0;
7101 int Order;
7102
7103 foreach (SearchFilter F in Filter)
7104 {
7105 Order = F.Order;
7106 if (Order != PrevOrder)
7107 {
7108 PrevOrder = Order;
7109 PrevOrderCount = 1;
7110 }
7111 else
7112 {
7113 PrevOrderCount++;
7114 if (PrevOrderCount >= F.MaxOccurs)
7115 {
7116 throw new ArgumentException("Maximum number of occurrences of " + F.GetType().FullName + " in a search is " +
7117 F.MaxOccurs.ToString() + ".", nameof(Filter));
7118 }
7119 }
7120
7121 F.Serialize(Xml);
7122 }
7123
7124 Xml.Append("</searchPublicContracts>");
7125
7126 return this.client.SendIqGet(Address, Xml.ToString(), async (Sender, e) =>
7127 {
7128 XmlElement E = e.FirstElement;
7129 List<string> IDs = null;
7130 bool More = false;
7131
7132 if (e.Ok && !(E is null) && E.LocalName == "searchResult")
7133 {
7134 More = XML.Attribute(E, "more", false);
7135 IDs = new List<string>();
7136
7137 foreach (XmlNode N in E.ChildNodes)
7138 {
7139 if (N is XmlElement E2 && E2.LocalName == "ref")
7140 {
7141 string Id = XML.Attribute(E2, "id");
7142 IDs.Add(Id);
7143 }
7144 }
7145 }
7146 else
7147 e.Ok = false;
7148
7149 await Callback.Raise(this, new SearchResultEventArgs(e, Offset, MaxCount, More, IDs?.ToArray()));
7150 }, State);
7151 }
7152
7158 public Task<SearchResultEventArgs> SearchAsync(SearchFilter[] Filter)
7159 {
7160 return this.SearchAsync(this.componentAddress, 0, int.MaxValue, Filter);
7161 }
7162
7168 public Task<SearchResultEventArgs> SearchAsync(string Address, SearchFilter[] Filter)
7169 {
7170 return this.SearchAsync(Address, 0, int.MaxValue, Filter);
7171 }
7172
7179 public Task<SearchResultEventArgs> SearchAsync(int Offset, int MaxCount, SearchFilter[] Filter)
7180 {
7181 return this.SearchAsync(this.componentAddress, Offset, MaxCount, Filter);
7182 }
7183
7191 public async Task<SearchResultEventArgs> SearchAsync(string Address, int Offset, int MaxCount, SearchFilter[] Filter)
7192 {
7193 TaskCompletionSource<SearchResultEventArgs> Result = new TaskCompletionSource<SearchResultEventArgs>();
7194
7195 await this.Search(Address, Offset, MaxCount, Filter, (Sender, e) =>
7196 {
7197 Result.TrySetResult(e);
7198 return Task.CompletedTask;
7199 }, null);
7200
7201 return await Result.Task;
7202 }
7203
7204 #endregion
7205
7206 #region Identity petitions
7207
7218 public Task PetitionIdentityAsync(string LegalId, string PetitionId, string Purpose)
7219 {
7220 return this.PetitionIdentityAsync(this.GetTrustProvider(LegalId), LegalId, PetitionId, Purpose, null, null, null);
7221 }
7222
7234 public Task PetitionIdentityAsync(string Address, string LegalId, string PetitionId, string Purpose)
7235 {
7236 return this.PetitionIdentityAsync(Address, LegalId, PetitionId, Purpose, null, null, null);
7237 }
7238
7251 public Task PetitionIdentityAsync(string Address, string LegalId, string PetitionId, string Purpose, string ContextXml)
7252 {
7253 return this.PetitionIdentityAsync(Address, LegalId, PetitionId, Purpose, ContextXml, null, null);
7254 }
7255
7270 public Task PetitionIdentityAsync(string LegalId, string PetitionId, string Purpose,
7271 string[] Properties, string[] Attachments)
7272 {
7273 return this.PetitionIdentityAsync(this.GetTrustProvider(LegalId), LegalId, PetitionId, Purpose, null,
7274 Properties, Attachments);
7275 }
7276
7292 public Task PetitionIdentityAsync(string Address, string LegalId, string PetitionId, string Purpose,
7293 string[] Properties, string[] Attachments)
7294 {
7295 return this.PetitionIdentityAsync(Address, LegalId, PetitionId, Purpose, null,
7296 Properties, Attachments);
7297 }
7298
7315 public async Task PetitionIdentityAsync(string Address, string LegalId, string PetitionId, string Purpose, string ContextXml,
7316 string[] Properties, string[] Attachments)
7317 {
7318 StringBuilder Xml = new StringBuilder();
7319 byte[] Nonce = this.RandomBytes(32);
7320
7321 string NonceStr = Convert.ToBase64String(Nonce);
7322 byte[] Data = Encoding.UTF8.GetBytes(PetitionId + ":" + LegalId + ":" + Purpose + ":" + NonceStr + ":" + this.client.BareJID.ToLower());
7323 byte[] Signature = await this.SignAsync(Data, SignWith.LatestApprovedId);
7324
7325 Xml.Append("<petitionIdentity xmlns='");
7326 Xml.Append(NamespaceLegalIdentitiesCurrent);
7327 Xml.Append("' id='");
7328 Xml.Append(XML.Encode(LegalId));
7329 Xml.Append("' pid='");
7330 Xml.Append(XML.Encode(PetitionId));
7331 Xml.Append("' purpose='");
7332 Xml.Append(XML.Encode(Purpose));
7333 Xml.Append("' nonce='");
7334 Xml.Append(NonceStr);
7335 Xml.Append("' s='");
7336 Xml.Append(Convert.ToBase64String(Signature));
7337
7338 if (string.IsNullOrEmpty(ContextXml))
7339 Xml.Append("'/>");
7340 else
7341 {
7342 Xml.Append("'>");
7343 AppendHints(Xml, Properties, Attachments);
7344 Xml.Append(ContextXml);
7345 Xml.Append("</petitionIdentity>");
7346 }
7347
7348 await this.client.IqSetAsync(Address, Xml.ToString());
7349 }
7350
7351 private static void AppendHints(StringBuilder Xml, string[] Properties, string[] Attachments)
7352 {
7353 if (!(Properties is null))
7354 {
7355 Xml.Append("<properties>");
7356
7357 foreach (string Property in Properties)
7358 {
7359 Xml.Append("<property>");
7360 Xml.Append(XML.Encode(Property));
7361 Xml.Append("</property>");
7362 }
7363
7364 Xml.Append("</properties>");
7365 }
7366
7367 if (!(Attachments is null))
7368 {
7369 Xml.Append("<attachments>");
7370
7371 foreach (string Attachment in Attachments)
7372 {
7373 Xml.Append("<attachment>");
7374 Xml.Append(XML.Encode(Attachment));
7375 Xml.Append("</attachment>");
7376 }
7377
7378 Xml.Append("</attachments>");
7379 }
7380 }
7381
7392 public Task PetitionIdentityResponseAsync(string LegalId, string PetitionId, string RequestorFullJid, bool Response)
7393 {
7394 return this.PetitionIdentityResponseAsync(this.GetTrustProvider(LegalId), LegalId, PetitionId, RequestorFullJid, Response, null);
7395 }
7396
7408 public Task PetitionIdentityResponseAsync(string LegalId, string PetitionId, string RequestorFullJid, bool Response, string ContextXml)
7409 {
7410 return this.PetitionIdentityResponseAsync(this.GetTrustProvider(LegalId), LegalId, PetitionId, RequestorFullJid, Response, ContextXml);
7411 }
7412
7424 public Task PetitionIdentityResponseAsync(string Address, string LegalId, string PetitionId, string RequestorFullJid, bool Response)
7425 {
7426 return this.PetitionIdentityResponseAsync(Address, LegalId, PetitionId, RequestorFullJid, Response, null);
7427 }
7428
7441 public async Task PetitionIdentityResponseAsync(string Address, string LegalId, string PetitionId, string RequestorFullJid, bool Response,
7442 string ContextXml)
7443 {
7444 StringBuilder Xml = new StringBuilder();
7445
7446 Xml.Append("<petitionIdentityResponse xmlns='");
7447 Xml.Append(NamespaceLegalIdentitiesCurrent);
7448 Xml.Append("' id='");
7449 Xml.Append(XML.Encode(LegalId));
7450 Xml.Append("' pid='");
7451 Xml.Append(XML.Encode(PetitionId));
7452 Xml.Append("' jid='");
7453 Xml.Append(XML.Encode(RequestorFullJid));
7454 Xml.Append("' response='");
7455 Xml.Append(CommonTypes.Encode(Response));
7456
7457 if (string.IsNullOrEmpty(ContextXml))
7458 Xml.Append("'/>");
7459 else
7460 {
7461 Xml.Append("'>");
7462 Xml.Append(ContextXml);
7463 Xml.Append("</petitionIdentityResponse>");
7464 }
7465
7466 await this.client.IqSetAsync(Address, Xml.ToString());
7467 }
7468
7469 private async Task PetitionIdentityMessageHandler(object Sender, MessageEventArgs e)
7470 {
7471 string LegalId = XML.Attribute(e.Content, "id");
7472 string PetitionId = XML.Attribute(e.Content, "pid");
7473 string Purpose = XML.Attribute(e.Content, "purpose");
7474 string From = XML.Attribute(e.Content, "from");
7475 string ClientEndpoint = XML.Attribute(e.Content, "clientEp");
7476
7477 if (!TryGetContext(e.Content, out XmlElement Context, out string _,
7478 out string[] Properties, out string[] Attachments, out LegalIdentity Identity))
7479 {
7480 this.client.Error("Invalid context. Ignoring message.");
7481 return;
7482 }
7483
7484 if (Identity is null)
7485 {
7486 this.client.Error("No identity in message. Ignoring message.");
7487 return;
7488 }
7489
7490 if (string.Compare(e.FromBareJID, this.componentAddress, true) == 0)
7491 {
7492 await this.Validate(Identity, false, async (sender2, e2) =>
7493 {
7494 if (e2.Status != IdentityStatus.Valid)
7495 {
7496 this.client.Error("Invalid legal identity received and discarded.");
7497
7498 Log.Warning("Invalid legal identity received and discarded.", this.client.BareJID, e.From,
7499 new KeyValuePair<string, object>("Status", e2.Status));
7500 return;
7501 }
7502
7503 await this.PetitionForIdentityReceived.Raise(this, new LegalIdentityPetitionEventArgs(e,
7504 Identity, From, LegalId, PetitionId, Purpose, ClientEndpoint, Context, Properties, Attachments));
7505 }, null);
7506 }
7507 }
7508
7509 private static bool TryGetContext(XmlElement Query, out XmlElement Context,
7510 out string Content, out string[] Properties, out string[] Attachments,
7511 out LegalIdentity Identity)
7512 {
7513 ChunkedList<string> PropertyList = null;
7514 ChunkedList<string> AttachmentList = null;
7515 bool IsIdentityNamespace = IsNamespaceLegalIdentity(Query.NamespaceURI);
7516 bool IsContractNamespace = IsNamespaceSmartContract(Query.NamespaceURI);
7517 Context = null;
7518 Properties = null;
7519 Attachments = null;
7520 Content = null;
7521 Identity = null;
7522
7523 foreach (XmlNode N in Query)
7524 {
7525 if (!(N is XmlElement E))
7526 continue;
7527
7528 if (IsIdentityNamespace)
7529 {
7530 if (!IsNamespaceLegalIdentity(E.NamespaceURI))
7531 continue;
7532 }
7533 else if (IsContractNamespace)
7534 {
7535 if (!IsNamespaceSmartContract(E.NamespaceURI))
7536 continue;
7537 }
7538 else
7539 {
7540 if (E.NamespaceURI != Query.NamespaceURI)
7541 continue;
7542 }
7543
7544 switch (E.LocalName)
7545 {
7546 case "identity":
7547 Identity = LegalIdentity.Parse(E);
7548 continue;
7549
7550 case "content":
7551 if (string.IsNullOrEmpty(Content))
7552 {
7553 Content = E.InnerText;
7554 continue;
7555 }
7556 else
7557 return false;
7558
7559 case "properties":
7560 foreach (XmlNode N2 in E.ChildNodes)
7561 {
7562 if (!(N2 is XmlElement E2))
7563 continue;
7564
7565 if (E2.LocalName == "property")
7566 {
7567 PropertyList ??= new ChunkedList<string>();
7568 PropertyList.Add(E2.InnerText);
7569 }
7570 else
7571 return false;
7572 }
7573 continue;
7574
7575 case "attachments":
7576 foreach (XmlNode N2 in E.ChildNodes)
7577 {
7578 if (!(N2 is XmlElement E2))
7579 continue;
7580
7581 if (E2.LocalName == "attachment")
7582 {
7583 AttachmentList ??= new ChunkedList<string>();
7584 AttachmentList.Add(E2.InnerText);
7585 }
7586 else
7587 return false;
7588 }
7589 continue;
7590 }
7591
7592 if (Context is null)
7593 Context = E;
7594 else
7595 return false;
7596 break;
7597 }
7598
7599 Properties = PropertyList?.ToArray();
7600 Attachments = AttachmentList?.ToArray();
7601
7602 return true;
7603 }
7604
7608 public event EventHandlerAsync<LegalIdentityPetitionEventArgs> PetitionForIdentityReceived = null;
7609
7610 private async Task PetitionIdentityResponseMessageHandler(object Sender, MessageEventArgs e)
7611 {
7612 string PetitionId = XML.Attribute(e.Content, "pid");
7613 bool Response = XML.Attribute(e.Content, "response", false);
7614 string ClientEndpoint = XML.Attribute(e.Content, "clientEp");
7615 LegalIdentity Identity = null;
7616 XmlElement Context = null;
7617
7618 foreach (XmlNode N in e.Content.ChildNodes)
7619 {
7620 if (N is XmlElement E)
7621 {
7622 if (E.LocalName == "identity" && E.NamespaceURI == e.Content.NamespaceURI)
7623 Identity = LegalIdentity.Parse(E);
7624 else if (!(Context is null))
7625 return;
7626 else
7627 Context = E;
7628 }
7629 }
7630
7631 if (!Response || string.Compare(e.FromBareJID, Identity?.Provider ?? string.Empty, true) == 0)
7632 await this.PetitionedIdentityResponseReceived.Raise(this, new LegalIdentityPetitionResponseEventArgs(e, Identity, PetitionId, Response, ClientEndpoint, Context));
7633 }
7634
7638 public event EventHandlerAsync<LegalIdentityPetitionResponseEventArgs> PetitionedIdentityResponseReceived = null;
7639
7640 #endregion
7641
7642 #region Signature petitions
7643
7655 public Task PetitionSignatureAsync(string LegalId, byte[] Content, string PetitionId, string Purpose)
7656 {
7657 return this.PetitionSignatureAsync(this.GetTrustProvider(LegalId), LegalId, Content, PetitionId, Purpose, false, null, null, null);
7658 }
7659
7672 public Task PetitionSignatureAsync(string Address, string LegalId, byte[] Content, string PetitionId, string Purpose)
7673 {
7674 return this.PetitionSignatureAsync(Address, LegalId, Content, PetitionId, Purpose, false, null, null, null);
7675 }
7676
7690 public Task PetitionSignatureAsync(string Address, string LegalId, byte[] Content, string PetitionId, string Purpose, string ContextXml)
7691 {
7692 return this.PetitionSignatureAsync(Address, LegalId, Content, PetitionId, Purpose, false, ContextXml, null, null);
7693 }
7694
7710 public Task PetitionSignatureAsync(string LegalId, byte[] Content, string PetitionId, string Purpose,
7711 string[] Properties, string[] Attachments)
7712 {
7713 return this.PetitionSignatureAsync(this.GetTrustProvider(LegalId), LegalId, Content, PetitionId, Purpose, false, null,
7714 Properties, Attachments);
7715 }
7716
7733 public Task PetitionSignatureAsync(string Address, string LegalId, byte[] Content, string PetitionId, string Purpose,
7734 string[] Properties, string[] Attachments)
7735 {
7736 return this.PetitionSignatureAsync(Address, LegalId, Content, PetitionId, Purpose, false, null,
7737 Properties, Attachments);
7738 }
7739
7757 public Task PetitionSignatureAsync(string Address, string LegalId, byte[] Content, string PetitionId, string Purpose, string ContextXml,
7758 string[] Properties, string[] Attachments)
7759 {
7760 return this.PetitionSignatureAsync(Address, LegalId, Content, PetitionId, Purpose, false, ContextXml,
7761 Properties, Attachments);
7762 }
7763
7764 private async Task PetitionSignatureAsync(string Address, string LegalId, byte[] Content, string PetitionId,
7765 string Purpose, bool PeerReview, string ContextXml, string[] Properties, string[] Attachments)
7766 {
7767 if (this.contentPerPid.TryGetValue(PetitionId, out KeyValuePair<byte[], bool> Rec))
7768 {
7769 if (Convert.ToBase64String(Content) == Convert.ToBase64String(Rec.Key) && PeerReview == Rec.Value)
7770 return;
7771
7772 throw new InvalidOperationException("Petition ID must be unique for outstanding petitions.");
7773 }
7774
7775 this.contentPerPid[PetitionId] = new KeyValuePair<byte[], bool>(Content, PeerReview);
7776
7777 StringBuilder Xml = new StringBuilder();
7778 byte[] Nonce = this.RandomBytes(32);
7779
7780 string NonceStr = Convert.ToBase64String(Nonce);
7781 string ContentStr = Convert.ToBase64String(Content);
7782 byte[] Data = Encoding.UTF8.GetBytes(PetitionId + ":" + LegalId + ":" + Purpose + ":" + NonceStr + ":" + this.client.BareJID.ToLower() + ":" + ContentStr);
7783 byte[] Signature = await this.SignAsync(Data, PeerReview ? SignWith.CurrentKeys : SignWith.LatestApprovedId);
7784
7785 Xml.Append("<petitionSignature xmlns='");
7786 Xml.Append(NamespaceLegalIdentitiesCurrent);
7787 Xml.Append("' id='");
7788 Xml.Append(XML.Encode(LegalId));
7789 Xml.Append("' pid='");
7790 Xml.Append(XML.Encode(PetitionId));
7791 Xml.Append("' purpose='");
7792 Xml.Append(XML.Encode(Purpose));
7793 Xml.Append("' nonce='");
7794 Xml.Append(NonceStr);
7795 Xml.Append("' s='");
7796 Xml.Append(Convert.ToBase64String(Signature));
7797 Xml.Append("'>");
7798 AppendHints(Xml, Properties, Attachments);
7799
7800 if (!string.IsNullOrEmpty(ContentStr))
7801 {
7802 Xml.Append("<content>");
7803 Xml.Append(ContentStr);
7804 Xml.Append("</content>");
7805 }
7806
7807 if (!string.IsNullOrEmpty(ContextXml))
7808 Xml.Append(ContextXml);
7809
7810 Xml.Append("</petitionSignature>");
7811
7812 await this.client.IqSetAsync(Address, Xml.ToString());
7813 }
7814
7827 public Task PetitionSignatureResponseAsync(string LegalId, byte[] Content,
7828 byte[] Signature, string PetitionId, string RequestorFullJid, bool Response)
7829 {
7830 return this.PetitionSignatureResponseAsync(this.GetTrustProvider(LegalId), LegalId, Content, Signature, PetitionId,
7831 RequestorFullJid, Response, null);
7832 }
7833
7847 public Task PetitionSignatureResponseAsync(string LegalId, byte[] Content,
7848 byte[] Signature, string PetitionId, string RequestorFullJid, bool Response, string ContextXml)
7849 {
7850 return this.PetitionSignatureResponseAsync(this.GetTrustProvider(LegalId), LegalId, Content, Signature, PetitionId,
7851 RequestorFullJid, Response, ContextXml);
7852 }
7853
7867 public Task PetitionSignatureResponseAsync(string Address, string LegalId, byte[] Content, byte[] Signature,
7868 string PetitionId, string RequestorFullJid, bool Response)
7869 {
7870 return this.PetitionSignatureResponseAsync(Address, LegalId, Content, Signature, PetitionId, RequestorFullJid, Response, null);
7871 }
7872
7887 public async Task PetitionSignatureResponseAsync(string Address, string LegalId, byte[] Content, byte[] Signature,
7888 string PetitionId, string RequestorFullJid, bool Response, string ContextXml)
7889 {
7890 StringBuilder Xml = new StringBuilder();
7891
7892 Xml.Append("<petitionSignatureResponse xmlns='");
7893 Xml.Append(NamespaceLegalIdentitiesCurrent);
7894 Xml.Append("' id='");
7895 Xml.Append(XML.Encode(LegalId));
7896 Xml.Append("' pid='");
7897 Xml.Append(XML.Encode(PetitionId));
7898 Xml.Append("' jid='");
7899 Xml.Append(XML.Encode(RequestorFullJid));
7900 Xml.Append("' response='");
7901 Xml.Append(CommonTypes.Encode(Response));
7902 Xml.Append("'><content>");
7903 Xml.Append(Convert.ToBase64String(Content));
7904 Xml.Append("</content><signature>");
7905 Xml.Append(Convert.ToBase64String(Signature));
7906 Xml.Append("</signature>");
7907
7908 if (!string.IsNullOrEmpty(ContextXml))
7909 Xml.Append(ContextXml);
7910
7911 Xml.Append("</petitionSignatureResponse>");
7912
7913 await this.client.IqSetAsync(Address, Xml.ToString());
7914 }
7915
7916 private async Task PetitionSignatureMessageHandler(object Sender, MessageEventArgs e)
7917 {
7918 string LegalId = XML.Attribute(e.Content, "id");
7919 string PetitionId = XML.Attribute(e.Content, "pid");
7920 string Purpose = XML.Attribute(e.Content, "purpose");
7921 string From = XML.Attribute(e.Content, "from");
7922 string ClientEndpoint = XML.Attribute(e.Content, "clientEp");
7923 byte[] Content;
7924 bool PeerReview = false;
7925
7926 if (!TryGetContext(e.Content, out XmlElement Context, out string ContentStr,
7927 out string[] Properties, out string[] Attachments, out LegalIdentity Identity))
7928 {
7929 this.client.Error("Invalid context. Ignoring message.");
7930 return;
7931 }
7932
7933 if (string.IsNullOrEmpty(ContentStr))
7934 {
7935 this.client.Error("No content in message to sign. Ignoring message.");
7936 return;
7937 }
7938
7939 try
7940 {
7941 Content = Convert.FromBase64String(ContentStr);
7942 }
7943 catch (Exception)
7944 {
7945 this.client.Error("Invalid BASE64-encoded content in message to sign. Ignoring message.");
7946 return;
7947 }
7948
7949 if (Identity is null)
7950 {
7951 string s = Encoding.UTF8.GetString(Content);
7952 if (s.StartsWith("<identity") && s.EndsWith("</identity>"))
7953 {
7954 try
7955 {
7956 XmlDocument Doc = XML.ParseXml(s);
7957
7958 if (Doc.DocumentElement.LocalName == "identity")
7959 {
7960 LegalIdentity TempId = LegalIdentity.Parse(Doc.DocumentElement);
7961
7962 if (TempId.State == IdentityState.Created &&
7963 string.Compare(TempId[PersonalInformation.JidTag], XmppClient.GetBareJID(From), true) == 0)
7964 {
7965 Identity = TempId;
7966 PeerReview = true;
7967 }
7968 }
7969 }
7970 catch (Exception)
7971 {
7972 // Ignore
7973 }
7974 }
7975
7976 if (Identity is null)
7977 return;
7978 }
7979
7980 if (string.Compare(e.FromBareJID, this.componentAddress, true) != 0 &&
7981 string.Compare(e.FromBareJID, XmppClient.GetDomain(Identity.Id), true) != 0)
7982 {
7983 return;
7984 }
7985
7986 EventHandlerAsync<SignaturePetitionEventArgs> h = PeerReview ? this.PetitionForPeerReviewIDReceived : this.PetitionForSignatureReceived;
7987
7988 await this.Validate(Identity, false, async (sender2, e2) =>
7989 {
7990 if (e2.Status != IdentityStatus.Valid && e2.Status != IdentityStatus.NoProviderSignature)
7991 {
7992 this.client.Error("Invalid legal identity received and discarded.");
7993
7994 Log.Warning("Invalid legal identity received and discarded.", this.client.BareJID, e.From,
7995 new KeyValuePair<string, object>("Status", e2.Status));
7996
7997 return;
7998 }
7999
8000 await h.Raise(this, new SignaturePetitionEventArgs(e, Identity, From, LegalId,
8001 PetitionId, Purpose, Content, ClientEndpoint, Context, Properties, Attachments));
8002
8003 }, null);
8004 }
8005
8009 public event EventHandlerAsync<SignaturePetitionEventArgs> PetitionForSignatureReceived = null;
8010
8011 private async Task PetitionSignatureResponseMessageHandler(object Sender, MessageEventArgs e)
8012 {
8013 string PetitionId = XML.Attribute(e.Content, "pid");
8014 bool Response = XML.Attribute(e.Content, "response", false);
8015 string ClientEndpoint = XML.Attribute(e.Content, "clientEp");
8016 string SignatureStr = string.Empty;
8017 byte[] Signature = null;
8018 LegalIdentity Identity = null;
8019 XmlElement Context = null;
8020
8021 foreach (XmlNode N in e.Content.ChildNodes)
8022 {
8023 if (N is XmlElement E)
8024 {
8025 switch (E.LocalName)
8026 {
8027 case "identity":
8028 Identity = LegalIdentity.Parse(E);
8029 break;
8030
8031 case "signature":
8032 SignatureStr = E.InnerText;
8033 Signature = Convert.FromBase64String(SignatureStr);
8034 break;
8035
8036 default:
8037 if (!(Context is null))
8038 return;
8039
8040 Context = E;
8041 break;
8042 }
8043 }
8044 }
8045
8046 if (!this.contentPerPid.TryGetValue(PetitionId, out KeyValuePair<byte[], bool> P))
8047 {
8048 this.client.Warning("Petition ID not recognized: " + PetitionId + ". Response ignored.");
8049 return;
8050 }
8051
8052 EventHandlerAsync<SignaturePetitionResponseEventArgs> h = P.Value ? this.PetitionedPeerReviewIDResponseReceived : this.PetitionedSignatureResponseReceived;
8053
8054 if (Response)
8055 {
8056 if (Identity is null)
8057 {
8058 this.client.Warning("Identity missing. Response ignored.");
8059 return;
8060 }
8061
8062 if (Signature is null)
8063 {
8064 this.client.Warning("Signature missing. Response ignored.");
8065 return;
8066 }
8067
8068 bool? Result = this.ValidateSignature(Identity, P.Key, Signature);
8069 if (!Result.HasValue)
8070 {
8071 this.client.Warning("Unable to validate signature. Response ignored.");
8072 return;
8073 }
8074
8075 if (!Result.Value)
8076 {
8077 this.client.Warning("Invalid signature. Response ignored.");
8078 return;
8079 }
8080 }
8081
8082 if (!Response || string.Compare(e.FromBareJID, Identity?.Provider ?? string.Empty, true) == 0)
8083 {
8084 try
8085 {
8086 this.Client.Information(h.Method.Name);
8087
8088 await h.Raise(this, new SignaturePetitionResponseEventArgs(e, Identity, PetitionId, Signature, Response, ClientEndpoint, Context));
8089 }
8090 finally
8091 {
8092 this.contentPerPid.Remove(PetitionId);
8093 }
8094 }
8095 else
8096 this.client.Warning("Sender invalid. Response ignored.");
8097 }
8098
8102 public event EventHandlerAsync<SignaturePetitionResponseEventArgs> PetitionedSignatureResponseReceived = null;
8103
8104 #endregion
8105
8106 #region Peer Review of IDs
8107
8125 public Task PetitionPeerReviewIDAsync(string LegalId, LegalIdentity Identity, string PetitionId, string Purpose)
8126 {
8127 return this.PetitionPeerReviewIDAsync(this.GetTrustProvider(LegalId), LegalId, Identity, PetitionId, Purpose);
8128 }
8129
8148 public Task PetitionPeerReviewIDAsync(string Address, string LegalId, LegalIdentity Identity, string PetitionId, string Purpose)
8149 {
8150 StringBuilder Xml = new StringBuilder();
8151 Identity.Serialize(Xml, true, true, true, true, true, true, true);
8152 byte[] Content = Encoding.UTF8.GetBytes(Xml.ToString());
8153
8154 return this.PetitionSignatureAsync(Address, LegalId, Content, PetitionId, Purpose, true, null, null, null);
8155 }
8156
8160 public event EventHandlerAsync<SignaturePetitionEventArgs> PetitionForPeerReviewIDReceived = null;
8161
8165 public event EventHandlerAsync<SignaturePetitionResponseEventArgs> PetitionedPeerReviewIDResponseReceived = null;
8166
8174 public async Task<LegalIdentity> AddPeerReviewIDAttachment(LegalIdentity Identity,
8175 LegalIdentity ReviewerLegalIdentity, byte[] PeerSignature)
8176 {
8177 StringBuilder Xml = new StringBuilder();
8178
8179 Xml.Append("<peerReview s='");
8180 Xml.Append(Convert.ToBase64String(PeerSignature));
8181 Xml.Append("' tp='");
8182 Xml.Append(XML.Encode(DateTime.UtcNow));
8183 Xml.Append("' xmlns='");
8184 Xml.Append(NamespaceLegalIdentitiesCurrent);
8185 Xml.Append("'><reviewed>");
8186 Identity.Serialize(Xml, true, true, true, true, true, true, true);
8187 Xml.Append("</reviewed><reviewer>");
8188 ReviewerLegalIdentity.Serialize(Xml, true, true, true, true, true, true, true);
8189 Xml.Append("</reviewer></peerReview>");
8190
8191 byte[] Data = Encoding.UTF8.GetBytes(Xml.ToString());
8192 string FileName = ReviewerLegalIdentity.Id + ".xml";
8193 string ContentType = "text/xml; charset=utf-8";
8194
8195 return await this.UploadLegalIdAttachmentAsync(Identity.Id, FileName, Data, ContentType);
8196 }
8197
8198 #endregion
8199
8200 #region Contract petitions
8201
8212 public Task PetitionContractAsync(string ContractId, string PetitionId, string Purpose)
8213 {
8214 return this.PetitionContractAsync(this.GetTrustProvider(ContractId), ContractId, PetitionId, Purpose, null);
8215 }
8216
8228 public Task PetitionContractAsync(string Address, string ContractId, string PetitionId, string Purpose)
8229 {
8230 return this.PetitionContractAsync(Address, ContractId, PetitionId, Purpose, null);
8231 }
8232
8245 public async Task PetitionContractAsync(string Address, string ContractId, string PetitionId, string Purpose, string ContextXml)
8246 {
8247 StringBuilder Xml = new StringBuilder();
8248 byte[] Nonce = this.RandomBytes(32);
8249
8250 string NonceStr = Convert.ToBase64String(Nonce);
8251 byte[] Data = Encoding.UTF8.GetBytes(PetitionId + ":" + ContractId + ":" + Purpose + ":" + NonceStr + ":" + this.client.BareJID.ToLower());
8252 byte[] Signature = await this.SignAsync(Data, SignWith.LatestApprovedId);
8253
8254 Xml.Append("<petitionContract xmlns='");
8255 Xml.Append(NamespaceSmartContractsCurrent);
8256 Xml.Append("' id='");
8257 Xml.Append(XML.Encode(ContractId));
8258 Xml.Append("' pid='");
8259 Xml.Append(XML.Encode(PetitionId));
8260 Xml.Append("' purpose='");
8261 Xml.Append(XML.Encode(Purpose));
8262 Xml.Append("' nonce='");
8263 Xml.Append(NonceStr);
8264 Xml.Append("' s='");
8265 Xml.Append(Convert.ToBase64String(Signature));
8266
8267 if (string.IsNullOrEmpty(ContextXml))
8268 Xml.Append("'/>");
8269 else
8270 {
8271 Xml.Append("'>");
8272 Xml.Append(ContextXml);
8273 Xml.Append("</petitionContract>");
8274 }
8275
8276 await this.client.IqSetAsync(Address, Xml.ToString());
8277 }
8278
8289 public Task PetitionContractResponseAsync(string ContractId, string PetitionId, string RequestorFullJid, bool Response)
8290 {
8291 return this.PetitionContractResponseAsync(this.GetTrustProvider(ContractId), ContractId, PetitionId, RequestorFullJid, Response, null);
8292 }
8293
8305 public Task PetitionContractResponseAsync(string ContractId, string PetitionId, string RequestorFullJid, bool Response, string ContextXml)
8306 {
8307 return this.PetitionContractResponseAsync(this.GetTrustProvider(ContractId), ContractId, PetitionId, RequestorFullJid, Response, ContextXml);
8308 }
8309
8321 public Task PetitionContractResponseAsync(string Address, string ContractId, string PetitionId, string RequestorFullJid, bool Response)
8322 {
8323 return this.PetitionContractResponseAsync(Address, ContractId, PetitionId, RequestorFullJid, Response, null);
8324 }
8325
8338 public async Task PetitionContractResponseAsync(string Address, string ContractId, string PetitionId, string RequestorFullJid,
8339 bool Response, string ContextXml)
8340 {
8341 StringBuilder Xml = new StringBuilder();
8342
8343 Xml.Append("<petitionContractResponse xmlns='");
8344 Xml.Append(NamespaceSmartContractsCurrent);
8345 Xml.Append("' id='");
8346 Xml.Append(XML.Encode(ContractId));
8347 Xml.Append("' pid='");
8348 Xml.Append(XML.Encode(PetitionId));
8349 Xml.Append("' jid='");
8350 Xml.Append(XML.Encode(RequestorFullJid));
8351 Xml.Append("' response='");
8352 Xml.Append(CommonTypes.Encode(Response));
8353
8354 if (string.IsNullOrEmpty(ContextXml))
8355 Xml.Append("'/>");
8356 else
8357 {
8358 Xml.Append("'>");
8359 Xml.Append(ContextXml);
8360 Xml.Append("</petitionContractResponse>");
8361 }
8362
8363 await this.client.IqSetAsync(Address, Xml.ToString());
8364 }
8365
8366 private async Task PetitionContractMessageHandler(object Sender, MessageEventArgs e)
8367 {
8368 string ContractId = XML.Attribute(e.Content, "id");
8369 string PetitionId = XML.Attribute(e.Content, "pid");
8370 string Purpose = XML.Attribute(e.Content, "purpose");
8371 string From = XML.Attribute(e.Content, "from");
8372 string ClientEndpoint = XML.Attribute(e.Content, "clientEp");
8373 int i = ContractId.IndexOf('@');
8374
8375 if (!TryGetContext(e.Content, out XmlElement Context, out string ContentStr,
8376 out string[] Properties, out string[] Attachments, out LegalIdentity Identity))
8377 {
8378 this.client.Error("Invalid context. Ignoring message.");
8379 return;
8380 }
8381
8382 if (Identity is null)
8383 {
8384 this.client.Error("No identity in message. Ignoring message.");
8385 return;
8386 }
8387
8388 if (!this.IsFromTrustProvider(ContractId, e.FromBareJID))
8389 {
8390 this.client.Error("Contract not hosted on trust provider. Ignoring message.");
8391 return;
8392 }
8393
8394 await this.Validate(Identity, false, async (sender2, e2) =>
8395 {
8396 if (e2.Status != IdentityStatus.Valid)
8397 {
8398 this.client.Error("Invalid identity received and discarded.");
8399
8400 Log.Warning("Invalid identity received and discarded.", this.client.BareJID, e.From,
8401 new KeyValuePair<string, object>("Status", e2.Status));
8402 return;
8403 }
8404
8405 await this.PetitionForContractReceived.Raise(this, new ContractPetitionEventArgs(e,
8406 Identity, From, ContractId, PetitionId, Purpose, ClientEndpoint, Context, Properties, Attachments));
8407
8408 }, null);
8409 }
8410
8414 public event EventHandlerAsync<ContractPetitionEventArgs> PetitionForContractReceived = null;
8415
8416 private async Task PetitionContractResponseMessageHandler(object Sender, MessageEventArgs e)
8417 {
8418 string PetitionId = XML.Attribute(e.Content, "pid");
8419 bool Response = XML.Attribute(e.Content, "response", false);
8420 string ClientEndpoint = XML.Attribute(e.Content, "clientEp");
8421 Contract Contract = null;
8422 XmlElement Context = null;
8423
8424 foreach (XmlNode N in e.Content.ChildNodes)
8425 {
8426 if (!(N is XmlElement E))
8427 continue;
8428
8429 if (E.LocalName == "contract" && E.NamespaceURI == e.Content.NamespaceURI)
8430 {
8431 ParsedContract Parsed = await Contract.Parse(E, this, false);
8432 Contract = Parsed?.Contract;
8433 }
8434 else if (!(Context is null))
8435 return;
8436 else
8437 Context = E;
8438 }
8439
8440 if (!Response || string.Compare(e.FromBareJID, Contract?.Provider ?? string.Empty, true) == 0)
8441 await this.PetitionedContractResponseReceived.Raise(this, new ContractPetitionResponseEventArgs(e, Contract, PetitionId, Response, ClientEndpoint, Context));
8442 }
8443
8447 public event EventHandlerAsync<ContractPetitionResponseEventArgs> PetitionedContractResponseReceived = null;
8448
8449 #endregion
8450
8451 #region Attachments
8452
8463 [Obsolete("To avoid security issues, use the UploadLegalIdAttachmentAsync method instead.")]
8464 public Task AddLegalIdAttachment(string LegalId, string GetUrl, byte[] Signature, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
8465 {
8466 return this.AddLegalIdAttachmentPrivate(LegalId, GetUrl, Signature, Callback, State);
8467 }
8468
8469 private Task AddLegalIdAttachmentPrivate(string LegalId, string GetUrl, byte[] Signature, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
8470 {
8471 StringBuilder Xml = new StringBuilder();
8472
8473 Xml.Append("<addAttachment xmlns='");
8474 Xml.Append(NamespaceLegalIdentitiesCurrent);
8475 Xml.Append("' id='");
8476 Xml.Append(XML.Encode(LegalId));
8477 Xml.Append("' getUrl='");
8478 Xml.Append(XML.Encode(GetUrl));
8479 Xml.Append("' s='");
8480 Xml.Append(Convert.ToBase64String(Signature));
8481 Xml.Append("'/>");
8482
8483 return this.client.SendIqSet(this.componentAddress, Xml.ToString(), async (Sender, e) =>
8484 {
8485 LegalIdentity Identity = null;
8486 XmlElement E;
8487
8488 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "identity")
8489 Identity = LegalIdentity.Parse(E);
8490 else
8491 e.Ok = false;
8492
8493 await Callback.Raise(this, new LegalIdentityEventArgs(e, Identity));
8494 }, State);
8495 }
8496
8505 [Obsolete("To avoid security issues, use the UploadLegalIdAttachmentAsync method instead.")]
8506 public Task<LegalIdentity> AddLegalIdAttachmentAsync(string LegalId, string GetUrl, byte[] Signature)
8507 {
8508 return this.AddLegalIdAttachmentAsyncPrivate(LegalId, GetUrl, Signature);
8509 }
8510
8511 private async Task<LegalIdentity> AddLegalIdAttachmentAsyncPrivate(string LegalId, string GetUrl, byte[] Signature)
8512 {
8513 TaskCompletionSource<LegalIdentity> Result = new TaskCompletionSource<LegalIdentity>();
8514
8515 await this.AddLegalIdAttachmentPrivate(LegalId, GetUrl, Signature, (Sender, e) =>
8516 {
8517 if (e.Ok)
8518 Result.TrySetResult(e.Identity);
8519 else
8520 Result.TrySetException(e.StanzaError ?? new Exception("Unable to add attachment."));
8521
8522 return Task.CompletedTask;
8523
8524 }, null);
8525
8526 return await Result.Task;
8527 }
8528
8537 public async Task<LegalIdentity> UploadLegalIdAttachmentAsync(string LegalId,
8538 string FileName, byte[] Data, string ContentType)
8539 {
8540 using MemoryStream ms = new MemoryStream(Data);
8541 return await this.UploadLegalIdAttachmentAsync(LegalId, FileName, ms, ContentType);
8542 }
8543
8552 public async Task<LegalIdentity> UploadLegalIdAttachmentAsync(string LegalId,
8553 string FileName, Stream Data, string ContentType)
8554 {
8555 if (!this.client.TryGetExtension(out HttpFileUploadClient HttpFileUploadClient))
8556 throw new InvalidOperationException("No HTTP File Upload extension added to the XMPP Client.");
8557
8558 byte[] Signature = await this.SignAsync(Data, SignWith.CurrentKeys);
8559
8560 try
8561 {
8562 await HttpFileUploadClient.PrepareFileUpload(FileName, ContentType, Data.Length,
8563 FilePurpose.InternalTransfer);
8564 }
8565 catch (Exception ex)
8566 {
8567 Log.Warning("File upload preparation failed: " + ex.Message,
8569 new KeyValuePair<string, object>("LegalId", LegalId),
8570 new KeyValuePair<string, object>("FileName", FileName),
8571 new KeyValuePair<string, object>("ContentType", ContentType),
8572 new KeyValuePair<string, object>("Size", Data.Length));
8573 }
8574
8576 ContentType, Data.Length);
8577
8578 if (!e2.Ok)
8579 {
8580 throw new IOException("Unable to upload attachment " + FileName + " to broker: " +
8581 e2.ErrorText);
8582 }
8583
8584 await e2.PUT(Data, ContentType, 10000); // Will set position to 0.
8585
8586 return await this.AddLegalIdAttachmentAsyncPrivate(LegalId, e2.GetUrl, Signature);
8587 }
8588
8598 [Obsolete("To avoid security issues, use the UploadContractAttachmentAsync method instead.")]
8599 public Task AddContractAttachment(string ContractId, string GetUrl, byte[] Signature, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
8600 {
8601 return this.AddContractAttachmentPrivate(ContractId, GetUrl, Signature, Callback, State);
8602 }
8603
8604 private Task AddContractAttachmentPrivate(string ContractId, string GetUrl, byte[] Signature, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
8605 {
8606 StringBuilder Xml = new StringBuilder();
8607
8608 Xml.Append("<addAttachment xmlns='");
8609 Xml.Append(NamespaceSmartContractsCurrent);
8610 Xml.Append("' contractId='");
8611 Xml.Append(XML.Encode(ContractId));
8612 Xml.Append("' getUrl='");
8613 Xml.Append(XML.Encode(GetUrl));
8614 Xml.Append("' s='");
8615 Xml.Append(Convert.ToBase64String(Signature));
8616 Xml.Append("'/>");
8617
8618 return this.client.SendIqSet(this.componentAddress, Xml.ToString(), async (Sender, e) =>
8619 {
8620 Contract Contract = null;
8621 XmlElement E;
8622
8623 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "contract")
8624 {
8625 ParsedContract Parsed = await Contract.Parse(E, this, false);
8626 if (Parsed is null)
8627 e.Ok = false;
8628 else
8629 Contract = Parsed.Contract;
8630 }
8631 else
8632 e.Ok = false;
8633
8634 await Callback.Raise(this, new SmartContractEventArgs(e, Contract));
8635 }, State);
8636 }
8637
8645 [Obsolete("To avoid security issues, use the UploadContractAttachmentAsync method instead.")]
8646 public Task<Contract> AddContractAttachmentAsync(string ContractId, string GetUrl, byte[] Signature)
8647 {
8648 return this.AddContractAttachmentAsyncPrivate(ContractId, GetUrl, Signature);
8649 }
8650
8651 private async Task<Contract> AddContractAttachmentAsyncPrivate(string ContractId, string GetUrl, byte[] Signature)
8652 {
8653 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
8654
8655 await this.AddContractAttachmentPrivate(ContractId, GetUrl, Signature, (Sender, e) =>
8656 {
8657 if (e.Ok)
8658 Result.TrySetResult(e.Contract);
8659 else
8660 Result.TrySetException(e.StanzaError ?? new Exception("Unable to add attachment."));
8661
8662 return Task.CompletedTask;
8663
8664 }, null);
8665
8666 return await Result.Task;
8667 }
8668
8677 public async Task<Contract> UploadContractAttachmentAsync(string ContractId,
8678 string FileName, byte[] Data, string ContentType)
8679 {
8680 using MemoryStream ms = new MemoryStream(Data);
8681 return await this.UploadContractAttachmentAsync(ContractId, FileName, ms, ContentType);
8682 }
8683
8692 public async Task<Contract> UploadContractAttachmentAsync(string ContractId,
8693 string FileName, Stream Data, string ContentType)
8694 {
8695 if (!this.client.TryGetExtension(out HttpFileUploadClient HttpFileUploadClient))
8696 throw new InvalidOperationException("No HTTP File Upload extension added to the XMPP Client.");
8697
8698 byte[] Signature = await this.SignAsync(Data, SignWith.CurrentKeys);
8699
8700 try
8701 {
8702 await HttpFileUploadClient.PrepareFileUpload(FileName, ContentType, Data.Length,
8703 FilePurpose.InternalTransfer);
8704 }
8705 catch (Exception ex)
8706 {
8707 Log.Warning("File upload preparation failed: " + ex.Message,
8709 new KeyValuePair<string, object>("ContractId", ContractId),
8710 new KeyValuePair<string, object>("FileName", FileName),
8711 new KeyValuePair<string, object>("ContentType", ContentType),
8712 new KeyValuePair<string, object>("Size", Data.Length));
8713 }
8714
8716 ContentType, Data.Length);
8717
8718 if (!e2.Ok)
8719 throw new IOException("Unable to upload attachment " + FileName + " to broker.");
8720
8721 await e2.PUT(Data, ContentType, 10000); // Will set position to 0.
8722
8723 return await this.AddContractAttachmentAsyncPrivate(ContractId, e2.GetUrl, Signature);
8724 }
8725
8732 public Task<KeyValuePair<string, TemporaryFile>> GetAttachmentAsync(string Url, SignWith SignWith)
8733 {
8734 return this.GetAttachmentAsync(Url, SignWith, 30000);
8735 }
8736
8744 public async Task<KeyValuePair<string, TemporaryFile>> GetAttachmentAsync(string Url, SignWith SignWith, int Timeout)
8745 {
8746 using HttpClient HttpClient = new HttpClient()
8747 {
8748 Timeout = TimeSpan.FromMilliseconds(Timeout)
8749 };
8750 HttpRequestMessage Request;
8751 HttpResponseMessage Response = null;
8752
8753 Request = new HttpRequestMessage()
8754 {
8755 RequestUri = new Uri(Url),
8756 Method = HttpMethod.Get
8757 };
8758
8759 try
8760 {
8761 Response = await HttpClient.SendAsync(Request);
8762
8763 if (Response.StatusCode == System.Net.HttpStatusCode.Unauthorized &&
8764 !(Response.Headers.WwwAuthenticate is null))
8765 {
8766 foreach (AuthenticationHeaderValue Header in Response.Headers.WwwAuthenticate)
8767 {
8768 if (Header.Scheme == "NeuroFoundation.Sign")
8769 {
8770 KeyValuePair<string, string>[] Parameters = CommonTypes.ParseFieldValues(Header.Parameter);
8771 string Realm = null;
8772 string NonceStr = null;
8773 byte[] Nonce = null;
8774
8775 foreach (KeyValuePair<string, string> P in Parameters)
8776 {
8777 switch (P.Key)
8778 {
8779 case "realm":
8780 Realm = P.Value;
8781 break;
8782
8783 case "n":
8784 NonceStr = P.Value;
8785 Nonce = Convert.FromBase64String(NonceStr);
8786 break;
8787 }
8788 }
8789
8790 if (!string.IsNullOrEmpty(Realm) && !string.IsNullOrEmpty(NonceStr))
8791 {
8792 byte[] Signature = await this.SignAsync(Nonce, SignWith);
8793 StringBuilder sb = new StringBuilder();
8794
8795 sb.Append("jid=\"");
8796 sb.Append(this.client.FullJID);
8797 sb.Append("\", realm=\"");
8798 sb.Append(Realm);
8799 sb.Append("\", n=\"");
8800 sb.Append(NonceStr);
8801 sb.Append("\", s=\"");
8802 sb.Append(Convert.ToBase64String(Signature));
8803 sb.Append('"');
8804
8805 Request.Dispose();
8806 Request = new HttpRequestMessage()
8807 {
8808 RequestUri = new Uri(Url),
8809 Method = HttpMethod.Get
8810 };
8811
8812 Request.Headers.Authorization = new AuthenticationHeaderValue(Header.Scheme, sb.ToString());
8813
8814 Response.Dispose();
8815 Response = null;
8816 Response = await HttpClient.SendAsync(Request);
8817 }
8818 break;
8819 }
8820 }
8821 }
8822
8823 if (!Response.IsSuccessStatusCode)
8824 {
8825 ContentResponse Temp = await Content.Getters.WebGetter.ProcessResponse(Response, Request.RequestUri);
8826 Temp.AssertOk();
8827 }
8828
8829 string ContentType = Response.Content.Headers.ContentType.ToString();
8830 TemporaryFile File = new TemporaryFile();
8831 try
8832 {
8833 await Response.Content.CopyToAsync(File);
8834 }
8835 catch (Exception ex)
8836 {
8837 File.Dispose();
8838 File = null;
8839
8840 ExceptionDispatchInfo.Capture(ex).Throw();
8841 }
8842
8843 return new KeyValuePair<string, TemporaryFile>(ContentType, File);
8844 }
8845 finally
8846 {
8847 Request?.Dispose();
8848 Response?.Dispose();
8849 }
8850 }
8851
8858 public Task RemoveLegalIdAttachment(string AttachmentId, EventHandlerAsync<LegalIdentityEventArgs> Callback, object State)
8859 {
8860 StringBuilder Xml = new StringBuilder();
8861
8862 Xml.Append("<removeAttachment xmlns='");
8863 Xml.Append(NamespaceLegalIdentitiesCurrent);
8864 Xml.Append("' attachmentId='");
8865 Xml.Append(XML.Encode(AttachmentId));
8866 Xml.Append("'/>");
8867
8868 return this.client.SendIqSet(this.componentAddress, Xml.ToString(), async (Sender, e) =>
8869 {
8870 LegalIdentity Identity = null;
8871 XmlElement E;
8872
8873 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "identity")
8874 Identity = LegalIdentity.Parse(E);
8875 else
8876 e.Ok = false;
8877
8878 await Callback.Raise(this, new LegalIdentityEventArgs(e, Identity));
8879 }, State);
8880 }
8881
8886 public async Task<LegalIdentity> RemoveLegalIdAttachmentAsync(string AttachmentId)
8887 {
8888 TaskCompletionSource<LegalIdentity> Result = new TaskCompletionSource<LegalIdentity>();
8889
8890 await this.RemoveLegalIdAttachment(AttachmentId, (Sender, e) =>
8891 {
8892 if (e.Ok)
8893 Result.TrySetResult(e.Identity);
8894 else
8895 Result.TrySetException(e.StanzaError ?? new Exception("Unable to remove attachment."));
8896
8897 return Task.CompletedTask;
8898
8899 }, null);
8900
8901 return await Result.Task;
8902 }
8903
8910 public Task RemoveContractAttachment(string AttachmentId, EventHandlerAsync<SmartContractEventArgs> Callback, object State)
8911 {
8912 StringBuilder Xml = new StringBuilder();
8913
8914 Xml.Append("<removeAttachment xmlns='");
8915 Xml.Append(NamespaceSmartContractsCurrent);
8916 Xml.Append("' attachmentId='");
8917 Xml.Append(XML.Encode(AttachmentId));
8918 Xml.Append("'/>");
8919
8920 return this.client.SendIqSet(this.componentAddress, Xml.ToString(), async (Sender, e) =>
8921 {
8922 Contract Contract = null;
8923 XmlElement E;
8924
8925 if (e.Ok && !((E = e.FirstElement) is null) && E.LocalName == "contract")
8926 {
8927 ParsedContract Parsed = await Contract.Parse(E, this, false);
8928 if (Parsed is null)
8929 e.Ok = false;
8930 else
8931 Contract = Parsed.Contract;
8932 }
8933 else
8934 e.Ok = false;
8935
8936 await Callback.Raise(this, new SmartContractEventArgs(e, Contract));
8937 }, State);
8938 }
8939
8944 public async Task<Contract> RemoveContractAttachmentAsync(string AttachmentId)
8945 {
8946 TaskCompletionSource<Contract> Result = new TaskCompletionSource<Contract>();
8947
8948 await this.RemoveContractAttachment(AttachmentId, (Sender, e) =>
8949 {
8950 if (e.Ok)
8951 Result.TrySetResult(e.Contract);
8952 else
8953 Result.TrySetException(e.StanzaError ?? new Exception("Unable to remove attachment."));
8954
8955 return Task.CompletedTask;
8956
8957 }, null);
8958
8959 return await Result.Task;
8960 }
8961
8962 #endregion
8963
8964 #region Encryption
8965
8976 public (byte[], byte[]) Encrypt(byte[] Message, byte[] Nonce, byte[] RecipientPublicKey, string RecipientPublicKeyName)
8977 {
8978 return this.Encrypt(Message, Nonce, RecipientPublicKey, RecipientPublicKeyName, string.Empty);
8979 }
8980
8992 public (byte[], byte[]) Encrypt(byte[] Message, byte[] Nonce, byte[] RecipientPublicKey, string RecipientPublicKeyName,
8993 string RecipientPublicKeyNamespace)
8994 {
8995 IE2eEndpoint LocalEndpoint = this.keys.FindLocalEndpoint(RecipientPublicKeyName, RecipientPublicKeyNamespace)
8996 ?? throw new NotSupportedException("Unable to find matching local key.");
8997
8998 IE2eEndpoint RemoteEndpoint = LocalEndpoint.CreatePublic(RecipientPublicKey);
8999 Aes256 SymmetricCipher = new Aes256();
9000 byte[] LocalPublicKey = LocalEndpoint.PublicKey;
9001 byte[] Secret = LocalEndpoint.GetSharedSecretForEncryption(RemoteEndpoint,
9002 SymmetricCipher, out byte[] KeyCipherText);
9003 int KeyCipherTextLen = KeyCipherText is null ? 0 : KeyCipherText.Length;
9004 byte[] Digest = Hashes.ComputeSHA256Hash(Secret);
9005 byte[] NonceDigest = Hashes.ComputeSHA256Hash(Nonce);
9006 byte[] Key = new byte[16];
9007 byte[] IV = new byte[16];
9008 byte[] Encrypted;
9009 byte[] ToEncrypt;
9010 int i, j, c;
9011
9012 for (i = 0; i < 32; i++)
9013 Digest[i] ^= NonceDigest[i];
9014
9015 i = Message.Length;
9016 c = 0;
9017
9018 do
9019 {
9020 i >>= 7;
9021 c++;
9022 }
9023 while (i > 0);
9024
9025 i = c + Message.Length;
9026 c = (i + 15) & ~0xf;
9027
9028 ToEncrypt = new byte[c];
9029 i = Message.Length;
9030 j = 0;
9031
9032 do
9033 {
9034 ToEncrypt[j] = (byte)(i & 127);
9035 i >>= 7;
9036 if (i > 0)
9037 ToEncrypt[j] |= 0x80;
9038
9039 j++;
9040 }
9041 while (i > 0);
9042
9043 Buffer.BlockCopy(Message, 0, ToEncrypt, j, Message.Length);
9044 j += Message.Length;
9045
9046 if (j < c)
9047 this.rnd.GetBytes(ToEncrypt, j, c - j);
9048
9049 Buffer.BlockCopy(Digest, 0, Key, 0, 16);
9050 Buffer.BlockCopy(Digest, 16, IV, 0, 16);
9051
9052 lock (this.aes)
9053 {
9054 using ICryptoTransform Aes = this.aes.CreateEncryptor(Key, IV);
9055 Encrypted = Aes.TransformFinalBlock(ToEncrypt, 0, c);
9056 }
9057
9058 if (LocalEndpoint.SharedSecretUseCipherText)
9059 {
9060 c = 0;
9061 i = KeyCipherTextLen;
9062
9063 do
9064 {
9065 i >>= 7;
9066 c++;
9067 }
9068 while (i > 0);
9069
9070 c += KeyCipherTextLen + Encrypted.Length;
9071
9072 byte[] Encrypted2 = new byte[c];
9073
9074 i = KeyCipherTextLen;
9075 j = 0;
9076
9077 do
9078 {
9079 Encrypted2[j] = (byte)(i & 127);
9080 i >>= 7;
9081 if (i > 0)
9082 Encrypted2[j] |= 0x80;
9083
9084 j++;
9085 }
9086 while (i > 0);
9087
9088 Buffer.BlockCopy(KeyCipherText, 0, Encrypted2, j, KeyCipherTextLen);
9089 j += KeyCipherTextLen;
9090
9091 Buffer.BlockCopy(Encrypted, 0, Encrypted2, j, Encrypted.Length);
9092
9093 Encrypted = Encrypted2;
9094 }
9095 else if (KeyCipherTextLen > 0)
9096 throw new InvalidOperationException("Shared secret ciphertexts not supported.");
9097
9098 return (Encrypted, LocalPublicKey);
9099 }
9100
9109 public byte[] DecryptReceivedMessage(byte[] EncryptedMessage, byte[] SenderPublicKey,
9110 byte[] Nonce)
9111 {
9112 IE2eEndpoint[] LocalEndpoints = this.keys?.FindCompatibleLocalEndpoints(SenderPublicKey) ?? Array.Empty<IE2eEndpoint>();
9113 ChunkedList<Exception> Exceptions = null;
9114
9115 foreach (IE2eEndpoint LocalEndpoint in LocalEndpoints)
9116 {
9117 try
9118 {
9119 IE2eEndpoint RemoteEndpoint = LocalEndpoint.CreatePublic(SenderPublicKey);
9120 byte[] KeyCipherText;
9121 int i, j, c;
9122 byte b;
9123
9124 i = 0;
9125 c = 0;
9126
9127 if (LocalEndpoint.SharedSecretUseCipherText)
9128 {
9129 do
9130 {
9131 b = EncryptedMessage[i++];
9132 c <<= 7;
9133 c |= b & 0x7f;
9134 }
9135 while ((b & 0x80) != 0);
9136
9137 if (c < 0 || c > EncryptedMessage.Length - i)
9138 throw new InvalidOperationException("Unable to decrypt message.");
9139
9140 KeyCipherText = new byte[c];
9141 Buffer.BlockCopy(EncryptedMessage, 0, KeyCipherText, 0, c);
9142 }
9143 else
9144 KeyCipherText = null;
9145
9146 byte[] Secret = LocalEndpoint.GetSharedSecretForDecryption(RemoteEndpoint, KeyCipherText);
9147 byte[] Digest = Hashes.ComputeSHA256Hash(Secret);
9148 byte[] NonceDigest = Hashes.ComputeSHA256Hash(Nonce);
9149 byte[] Key = new byte[16];
9150 byte[] IV = new byte[16];
9151 byte[] Decrypted;
9152
9153 for (j = 0; j < 32; j++)
9154 Digest[j] ^= NonceDigest[j];
9155
9156 Buffer.BlockCopy(Digest, 0, Key, 0, 16);
9157 Buffer.BlockCopy(Digest, 16, IV, 0, 16);
9158
9159 lock (this.aes)
9160 {
9161 using ICryptoTransform Aes = this.aes.CreateDecryptor(Key, IV);
9162 Decrypted = Aes.TransformFinalBlock(EncryptedMessage, i, EncryptedMessage.Length - i);
9163 }
9164
9165 i = 0;
9166 c = 0;
9167 do
9168 {
9169 b = Decrypted[i++];
9170 c <<= 7;
9171 c |= b & 0x7f;
9172 }
9173 while ((b & 0x80) != 0);
9174
9175 if (c < 0 || c > Decrypted.Length - i)
9176 {
9177 Exceptions ??= new ChunkedList<Exception>();
9178 Exceptions.Add(new InvalidOperationException("Unable to decrypt message."));
9179 continue;
9180 }
9181
9182 byte[] Message = new byte[c];
9183
9184 Buffer.BlockCopy(Decrypted, i, Message, 0, c);
9185
9186 return Message;
9187 }
9188 catch (Exception ex)
9189 {
9190 Exceptions ??= new ChunkedList<Exception>();
9191 Exceptions.Add(ex);
9192 }
9193 }
9194
9195 if (Exceptions is null)
9196 throw new NotSupportedException("No compatible local key found.");
9197 else if (Exceptions.Count == 1)
9198 throw Exceptions.FirstItem;
9199 else
9200 throw new AggregateException(Exceptions.ToArray());
9201 }
9202
9211 public byte[] DecryptSentMessage(byte[] EncryptedMessage, byte[] SenderPublicKey,
9212 byte[] Nonce)
9213 {
9214 IE2eEndpoint LocalEndpoint = this.keys.FindLocalEndpoint(SenderPublicKey);
9215 IE2eEndpoint RemoteEndpoint = LocalEndpoint.CreatePublic(SenderPublicKey);
9216 byte[] KeyCipherText;
9217 int i, j, c;
9218 byte b;
9219
9220 i = 0;
9221 c = 0;
9222
9223 if (LocalEndpoint.SharedSecretUseCipherText)
9224 {
9225 do
9226 {
9227 b = EncryptedMessage[i++];
9228 c <<= 7;
9229 c |= b & 0x7f;
9230 }
9231 while ((b & 0x80) != 0);
9232
9233 if (c < 0 || c > EncryptedMessage.Length - i)
9234 throw new InvalidOperationException("Unable to decrypt message.");
9235
9236 KeyCipherText = new byte[c];
9237 Buffer.BlockCopy(EncryptedMessage, 0, KeyCipherText, 0, c);
9238 }
9239
9240 IE2eSymmetricCipher SymmetricCipher = new Aes256();
9241 byte[] Secret = LocalEndpoint.GetSharedSecretForEncryption(RemoteEndpoint, SymmetricCipher, out _);
9242 byte[] Digest = Hashes.ComputeSHA256Hash(Secret);
9243 byte[] NonceDigest = Hashes.ComputeSHA256Hash(Nonce);
9244 byte[] Key = new byte[16];
9245 byte[] IV = new byte[16];
9246 byte[] Decrypted;
9247
9248 for (j = 0; j < 32; j++)
9249 Digest[j] ^= NonceDigest[j];
9250
9251 Buffer.BlockCopy(Digest, 0, Key, 0, 16);
9252 Buffer.BlockCopy(Digest, 16, IV, 0, 16);
9253
9254 lock (this.aes)
9255 {
9256 using ICryptoTransform Aes = this.aes.CreateDecryptor(Key, IV);
9257 Decrypted = Aes.TransformFinalBlock(EncryptedMessage, i, EncryptedMessage.Length - i);
9258 }
9259
9260 i = 0;
9261 c = 0;
9262 do
9263 {
9264 b = Decrypted[i++];
9265 c <<= 7;
9266 c |= b & 0x7f;
9267 }
9268 while ((b & 0x80) != 0);
9269
9270 if (c < 0 || c > Decrypted.Length - i)
9271 throw new InvalidOperationException("Unable to decrypt message.");
9272
9273 byte[] Message = new byte[c];
9274
9275 Buffer.BlockCopy(Decrypted, i, Message, 0, c);
9276
9277 return Message;
9278 }
9279
9280 #endregion
9281
9282 #region Explicit authorization of access to Legal IDs
9283
9292 public Task AuthorizeAccessToId(string LegalId, string RemoteId, bool Authorized, EventHandlerAsync<IqResultEventArgs> Callback, object State)
9293 {
9294 return this.AuthorizeAccessToId(this.GetTrustProvider(LegalId), LegalId, RemoteId, Authorized, Callback, State);
9295 }
9296
9306 public Task AuthorizeAccessToId(string Address, string LegalId, string RemoteId, bool Authorized, EventHandlerAsync<IqResultEventArgs> Callback, object State)
9307 {
9308 StringBuilder Xml = new StringBuilder();
9309
9310 Xml.Append("<authorizeAccess xmlns='");
9311 Xml.Append(NamespaceLegalIdentitiesCurrent);
9312 Xml.Append("' id='");
9313 Xml.Append(XML.Encode(LegalId));
9314 Xml.Append("' remoteId='");
9315 Xml.Append(XML.Encode(RemoteId));
9316 Xml.Append("' auth='");
9317 Xml.Append(CommonTypes.Encode(Authorized));
9318 Xml.Append("'/>");
9319
9320 return this.client.SendIqSet(Address, Xml.ToString(), Callback, State);
9321 }
9322
9329 public Task AuthorizeAccessToIdAsync(string LegalId, string RemoteId, bool Authorized)
9330 {
9331 return this.AuthorizeAccessToIdAsync(this.GetTrustProvider(LegalId), LegalId, RemoteId, Authorized);
9332 }
9333
9341 public async Task AuthorizeAccessToIdAsync(string Address, string LegalId, string RemoteId, bool Authorized)
9342 {
9343 TaskCompletionSource<bool> Result = new TaskCompletionSource<bool>();
9344
9345 await this.AuthorizeAccessToId(Address, LegalId, RemoteId, Authorized, (Sender, e) =>
9346 {
9347 if (e.Ok)
9348 Result.TrySetResult(true);
9349 else
9350 Result.TrySetException(e.StanzaError ?? new Exception("Unable to authorize access to legal identity."));
9351
9352 return Task.CompletedTask;
9353
9354 }, null);
9355
9356 await Result.Task;
9357 }
9358
9359 #endregion
9360
9361 #region Explicit authorization of access to Contracts
9362
9371 public Task AuthorizeAccessToContract(string ContractId, string RemoteId, bool Authorized, EventHandlerAsync<IqResultEventArgs> Callback, object State)
9372 {
9373 return this.AuthorizeAccessToContract(this.GetTrustProvider(ContractId), ContractId, RemoteId, Authorized, Callback, State);
9374 }
9375
9385 public Task AuthorizeAccessToContract(string Address, string ContractId, string RemoteId, bool Authorized, EventHandlerAsync<IqResultEventArgs> Callback, object State)
9386 {
9387 StringBuilder Xml = new StringBuilder();
9388
9389 Xml.Append("<authorizeAccess xmlns='");
9390 Xml.Append(NamespaceSmartContractsCurrent);
9391 Xml.Append("' id='");
9392 Xml.Append(XML.Encode(ContractId));
9393 Xml.Append("' remoteId='");
9394 Xml.Append(XML.Encode(RemoteId));
9395 Xml.Append("' auth='");
9396 Xml.Append(CommonTypes.Encode(Authorized));
9397 Xml.Append("'/>");
9398
9399 return this.client.SendIqSet(Address, Xml.ToString(), Callback, State);
9400 }
9401
9408 public Task AuthorizeAccessToContractAsync(string ContractId, string RemoteId, bool Authorized)
9409 {
9410 return this.AuthorizeAccessToContractAsync(this.GetTrustProvider(ContractId), ContractId, RemoteId, Authorized);
9411 }
9412
9420 public async Task AuthorizeAccessToContractAsync(string Address, string ContractId, string RemoteId, bool Authorized)
9421 {
9422 TaskCompletionSource<bool> Result = new TaskCompletionSource<bool>();
9423
9424 await this.AuthorizeAccessToContract(Address, ContractId, RemoteId, Authorized, (Sender, e) =>
9425 {
9426 if (e.Ok)
9427 Result.TrySetResult(true);
9428 else
9429 Result.TrySetException(e.StanzaError ?? new Exception("Unable to authorize access to legal identity."));
9430
9431 return Task.CompletedTask;
9432
9433 }, null);
9434
9435 await Result.Task;
9436 }
9437
9438 #endregion
9439
9440 #region Peer-review service providers
9441
9448 {
9449 return this.GetPeerReviewIdServiceProviders(this.componentAddress, Callback, State);
9450 }
9451
9458 public Task GetPeerReviewIdServiceProviders(string ComponentAddress,
9460 {
9461 StringBuilder Xml = new StringBuilder();
9462
9463 Xml.Append("<reviewIdProviders xmlns='");
9464 Xml.Append(NamespaceLegalIdentitiesCurrent);
9465 Xml.Append("'/>");
9466
9467 return this.client.SendIqGet(ComponentAddress, Xml.ToString(), async (Sender, e) =>
9468 {
9469 List<ServiceProviderWithLegalId> Providers = null;
9470 XmlElement E;
9471
9472 if (e.Ok &&
9473 !((E = e.FirstElement) is null) &&
9474 E.LocalName == "providers")
9475 {
9476 Providers = new List<ServiceProviderWithLegalId>();
9477
9478 foreach (XmlNode N in E.ChildNodes)
9479 {
9480 if (N is XmlElement E2 && E2.LocalName == "provider")
9481 {
9482 ServiceProviderWithLegalId Provider = this.ParseServiceProviderWithLegalId(E2);
9483
9484 if (!(Provider is null))
9485 Providers.Add(Provider);
9486 }
9487 }
9488 }
9489 else
9490 e.Ok = false;
9491
9492 await Callback.Raise(this, new ServiceProvidersEventArgs<ServiceProviderWithLegalId>(e, Providers?.ToArray()));
9493
9494 }, State);
9495 }
9496
9497 private ServiceProviderWithLegalId ParseServiceProviderWithLegalId(XmlElement Xml)
9498 {
9499 string Id = null;
9500 string Type = null;
9501 string Name = null;
9502 string IconUrl = null;
9503 string LegalId = null;
9504 int IconWidth = -1;
9505 int IconHeight = -1;
9506 bool External = false;
9507
9508 foreach (XmlAttribute Attr in Xml.Attributes)
9509 {
9510 switch (Attr.Name)
9511 {
9512 case "id":
9513 Id = Attr.Value;
9514 break;
9515
9516 case "type":
9517 Type = Attr.Value;
9518 break;
9519
9520 case "name":
9521 Name = Attr.Value;
9522 break;
9523
9524 case "iconUrl":
9525 IconUrl = Attr.Value;
9526 break;
9527
9528 case "iconWidth":
9529 if (!int.TryParse(Attr.Value, out IconWidth))
9530 return null;
9531 break;
9532
9533 case "iconHeight":
9534 if (!int.TryParse(Attr.Value, out IconHeight))
9535 return null;
9536 break;
9537
9538 case "legalId":
9539 LegalId = Attr.Value;
9540 break;
9541
9542 case "external":
9543 if (!CommonTypes.TryParse(Attr.Value, out External))
9544 return null;
9545 break;
9546 }
9547 }
9548
9549 if (Id is null || Type is null || Name is null)
9550 return null;
9551
9552 if (string.IsNullOrEmpty(IconUrl))
9553 return new ServiceProviderWithLegalId(Id, Type, Name, LegalId, External);
9554 else
9555 {
9556 if (IconWidth < 0 || IconHeight < 0)
9557 return null;
9558
9559 return new ServiceProviderWithLegalId(Id, Type, Name, LegalId, External, IconUrl, IconWidth, IconHeight);
9560 }
9561 }
9562
9567 public Task<ServiceProviderWithLegalId[]> GetPeerReviewIdServiceProvidersAsync()
9568 {
9569 return this.GetPeerReviewIdServiceProvidersAsync(this.componentAddress);
9570 }
9571
9577 public async Task<ServiceProviderWithLegalId[]> GetPeerReviewIdServiceProvidersAsync(string ComponentAddress)
9578 {
9579 TaskCompletionSource<ServiceProviderWithLegalId[]> Providers = new TaskCompletionSource<ServiceProviderWithLegalId[]>();
9580
9581 await this.GetPeerReviewIdServiceProviders(ComponentAddress, (Sender, e) =>
9582 {
9583 if (e.Ok)
9584 Providers.TrySetResult(e.ServiceProviders);
9585 else
9586 Providers.TrySetException(e.StanzaError ?? new Exception("Unable to get service providers."));
9587
9588 return Task.CompletedTask;
9589
9590 }, null);
9591
9592 return await Providers.Task;
9593 }
9594
9595 #endregion
9596
9597 #region Select Peer-review service
9598
9608 public Task SelectPeerReviewService(string Provider, string ServiceId, EventHandlerAsync<IqResultEventArgs> Callback, object State)
9609 {
9610 return this.SelectPeerReviewService(this.componentAddress, Provider, ServiceId, Callback, State);
9611 }
9612
9623 public Task SelectPeerReviewService(string ComponentAddress, string Provider, string ServiceId,
9624 EventHandlerAsync<IqResultEventArgs> Callback, object State)
9625 {
9626 StringBuilder Xml = new StringBuilder();
9627
9628 Xml.Append("<selectReviewService xmlns='");
9629 Xml.Append(NamespaceLegalIdentitiesCurrent);
9630 Xml.Append("' provider='");
9631 Xml.Append(XML.Encode(Provider));
9632 Xml.Append("' serviceId='");
9633 Xml.Append(XML.Encode(ServiceId));
9634 Xml.Append("'/>");
9635
9636 return this.client.SendIqSet(ComponentAddress, Xml.ToString(), Callback, State);
9637 }
9638
9646 public Task SelectPeerReviewServiceAsync(string Provider, string ServiceId)
9647 {
9648 return this.SelectPeerReviewServiceAsync(this.componentAddress, Provider, ServiceId);
9649 }
9650
9659 public async Task SelectPeerReviewServiceAsync(string ComponentAddress, string Provider, string ServiceId)
9660 {
9661 TaskCompletionSource<bool> Providers = new TaskCompletionSource<bool>();
9662
9663 await this.SelectPeerReviewService(ComponentAddress, Provider, ServiceId, (Sender, e) =>
9664 {
9665 if (e.Ok)
9666 Providers.TrySetResult(true);
9667 else
9668 Providers.TrySetException(e.StanzaError ?? new Exception("Unable to select peer review service."));
9669
9670 return Task.CompletedTask;
9671
9672 }, null);
9673
9674 await Providers.Task;
9675 }
9676
9677 #endregion
9678
9679 #region Petition Client URL event
9680
9681 private Task PetitionClientUrlEventHandler(object Sender, MessageEventArgs e)
9682 {
9683 string PetitionId = XML.Attribute(e.Content, "pid");
9684 string Url = XML.Attribute(e.Content, "url");
9685
9686 return this.PetitionClientUrlReceived.Raise(this, new PetitionClientUrlEventArgs(e, PetitionId, Url));
9687 }
9688
9694 public event EventHandlerAsync<PetitionClientUrlEventArgs> PetitionClientUrlReceived;
9695
9696 #endregion
9697 }
9698}
Helps with parsing of commong data types.
Definition: CommonTypes.cs:15
static string Encode(bool x)
Encodes a Boolean for use in XML and other formats.
Definition: CommonTypes.cs:596
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:474
static bool TryParse(string s, out double Value)
Tries to decode a string encoded double.
Definition: CommonTypes.cs:48
Contains information about a response to a content request.
void AssertOk()
Asserts response is OK.
Helps with common XML-related tasks.
Definition: XML.cs:21
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
Definition: XML.cs:1062
static string Encode(string s)
Encodes a string for use in XML.
Definition: XML.cs:29
static XmlWriterSettings WriterSettings(bool Indent, bool OmitXmlDeclaration)
Gets an XML writer settings object.
Definition: XML.cs:1351
static XmlDocument ParseXml(string Xml)
Parses an XML Document from its string representation.
Definition: XML.cs:713
Static class managing loading of XSL resources stored as embedded resources or in content files.
Definition: XSL.cs:16
static XmlSchema LoadSchema(string ResourceName)
Loads an XML schema from an embedded resource.
Definition: XSL.cs:24
static void Validate(string ObjectID, XmlDocument Xml, params XmlSchema[] Schemas)
Validates an XML document given a set of XML schemas.
Definition: XSL.cs:134
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
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:1657
static void Warning(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a warning event.
Definition: Log.cs:576
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:460
static void Debug(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs a debug event.
Definition: Log.cs:228
static void Alert(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an alert event.
Definition: Log.cs:1237
This class handles incoming events from the XMPP network. The default behaviour is to log incoming ev...
static bool TryParse(string Value, string Type, out object ParsedValue)
Tries to parse a simple value.
Contains a reference to an attachment assigned to a legal object.
Definition: Attachment.cs:10
string LegalId
Legal ID of uploader of the attachment
Definition: Attachment.cs:39
string ContentType
Internet Content Type of binary attachment.
Definition: Attachment.cs:48
string Url
URL to retrieve attachment, if provided.
Definition: Attachment.cs:66
byte[] Signature
Binary signature of the attachment, generated by an approved legal identity of the account-holder....
Definition: Attachment.cs:76
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:441
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:2300
Parameter[] Parameters
Defined parameters for the smart contract.
Definition: Contract.cs:267
DateTime? FirstSignatureAt
Timestamp of first client signature, if one exists.
Definition: Contract.cs:354
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
DateTime Updated
When the contract was last updated
Definition: Contract.cs:139
async Task< bool > IsLegallyBinding(bool CheckCurrentTime, ContractsClient Client)
Checks if a contract is legally binding.
Definition: Contract.cs:1502
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:418
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:1621
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:398
bool DecryptEncryptedParameters(string CreatorJid, IParameterEncryptionAlgorithm Algorithm)
Protects encrypted values, by encrypting the clear text string representations for those that lack en...
Definition: Contract.cs:2324
Contains a persisted shared secret associated with a smart contract.
bool HasSharedSecret
If a shared secret snapshot is available for the contract.
SymmetricCipherAlgorithms KeyAlgorithm
Symmetric encryption algorithm used with the shared secret.
byte[] SharedSecret
Shared secret snapshot stored with the contract state.
static void SetAllowedSources(ICallStackCheck[] ApprovedSources)
If access to sensitive properties is only accessible from a set of approved sources.
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....
async Task PetitionIdentityAsync(string Address, string LegalId, string PetitionId, string Purpose, string ContextXml, string[] Properties, string[] Attachments)
Sends a petition to the owner of a legal identity, to access the information in the identity....
Task< bool > ImportKeys(string Xml)
Imports keys
Task GetServerPublicKey(DateTime? Timestamp, EventHandlerAsync< KeyEventArgs > Callback, object State)
Gets the server public key.
static Uri ContractIdUri(string ContractId)
Contract identity URI.
static EventHandlerAsync< PublicKeyEventArgs > GetLocalPublicKey
Event raised when a server public key is requested. Allows a local implementation to return a local k...
async Task DisableE2eEncryption()
Disables the use of the Contracts Client keys for End-to-End Encrypted communication over the XMPP cl...
Task Apply(Property[] Properties, bool Preview, EventHandlerAsync< LegalIdentityEventArgs > Callback, object State)
Applies for a legal identity to be registered.
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....
Task GetContractLegalIdentities(string ContractId, EventHandlerAsync< LegalIdentitiesEventArgs > Callback, object State)
Gets available legal identities related to a contract.
Task< Contract > AddContractAttachmentAsync(string ContractId, string GetUrl, byte[] Signature)
Adds an attachment to a proposed or approved contract before it is being signed.
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 Validate(LegalIdentity Identity, bool ValidateState, EventHandlerAsync< IdentityValidationEventArgs > Callback, object State)
Validates a legal identity.
Task GetSignedContractReferences(EventHandlerAsync< IdReferencesEventArgs > Callback, object State)
Get references to contracts the account has signed.
Task< LegalIdentity > ApplyAsync(Property[] Properties, bool Preview)
Applies for a legal identity to be registered.
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 GetTrustChain(string Domain, EventHandlerAsync< TrustChainEventArgs > Callback, object State)
Gets the trust chain from a domain. The trust chain is a list of domains, each one the parent of the ...
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< LegalIdentity > AddLegalIdAttachmentAsync(string LegalId, string GetUrl, byte[] Signature)
Adds an attachment to a newly created legal identity.
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.
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.
EventHandlerAsync< ClientMessageEventArgs > ClientMessage
Event raised when a Client Message has been received.
async Task Validate(LegalIdentity Identity, bool ValidateState, bool ValidateAttachments, EventHandlerAsync< IdentityValidationEventArgs > Callback, object State)
Validates a legal identity.
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,...
Task< ContractValidationEventArgs > ValidateAsync(Contract Contract, bool ValidateState)
Validates a smart contract.
bool? ValidateSignature(LegalIdentity Identity, byte[] Data, byte[] Signature)
Validates a signature of binary data.
ulong RandomInteger(ulong MaxExclusive)
Creates a random long unsigned integer lower than MaxExclusive .
async Task GetServerPublicKey(string Address, DateTime? Timestamp, EventHandlerAsync< KeyEventArgs > Callback, object State)
Gets the server public key.
Task< KeyValuePair< string, TemporaryFile > > GetAttachmentAsync(string Url, SignWith SignWith)
Gets an attachment from a Trust Provider
Task PetitionSignatureAsync(string Address, string LegalId, byte[] Content, string PetitionId, string Purpose, string ContextXml, string[] Properties, string[] Attachments)
Sends a petition to a third party to request a digital signature of some content. The petition is not...
async Task ValidateSignature(string Address, string LegalId, byte[] Data, byte[] Signature, EventHandlerAsync< LegalIdentityEventArgs > Callback, object State)
Validates a signature of binary data.
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.
ContractsClient(XmppClient Client, string ComponentAddress, ICallStackCheck[] ApprovedSources)
Adds support for legal identities, smart contracts and signatures to an XMPP client.
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< IdentityValidationEventArgs > ValidateAsync(LegalIdentity Identity, bool ValidateState)
Validates a legal identity.
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.
Task PetitionSignatureAsync(string LegalId, byte[] Content, string PetitionId, string Purpose, string[] Properties, string[] Attachments)
Sends a petition to a third party to request a digital signature of some content. The petition is not...
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
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....
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< 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.
Task GetMatchingLocalKey(EventHandlerAsync< KeyEventArgs > Callback, object State)
Get the local key that matches the server key.
Task< SchemaReference[]> GetSchemasAsync()
Gets available schemas.
async Task< Contract > UploadContractAttachmentAsync(string ContractId, string FileName, Stream Data, string ContentType)
Uploads an attachment to a Smart Contract.
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.
async Task< Contract > UploadContractAttachmentAsync(string ContractId, string FileName, byte[] Data, string ContentType)
Uploads an attachment to a Smart Contract.
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....
async Task GetSchema(string Address, string Namespace, SchemaDigest Digest, EventHandlerAsync< SchemaEventArgs > Callback, object State)
Gets a schema.
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.
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 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,...
bool IsE2eEncryptionEnabled
If End-to-End encryption is enabled.
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.
async Task Validate(Contract Contract, bool ValidateState, bool ValidateAttachments, bool ValidateIdentities, bool ValidateIdentityAttachments, EventHandlerAsync< ContractValidationEventArgs > Callback, object State)
Validates a smart contract.
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...
Task< Contract > GetContractAsync(string ContractId)
Gets a contract
Task< KeyValuePair< LegalIdentity, Exception > > ValidateSignatureAsyncEx(string LegalId, byte[] Data, byte[] Signature)
Validates a signature of binary data.
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.
Task< IdentityValidationEventArgs > ValidateAsync(LegalIdentity Identity)
Validates a legal identity.
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< ContractValidationEventArgs > ValidateAsync(Contract Contract)
Validates a smart 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.
async Task< LegalIdentity > UploadLegalIdAttachmentAsync(string LegalId, string FileName, Stream Data, string ContentType)
Uploads an attachment to a Legal Identity application.
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,...
byte[] DecryptSentMessage(byte[] EncryptedMessage, byte[] SenderPublicKey, byte[] Nonce)
Decrypts a message that was sent by the client using the current keys.
static string ContractIdUriString(string ContractId)
Contract identity URI, as a string.
async Task< KeyValuePair< LegalIdentity, Exception > > ValidateSignatureAsyncEx(string Address, string LegalId, byte[] Data, byte[] Signature)
Validates a signature of binary data.
Task PetitionIdentityAsync(string Address, string LegalId, string PetitionId, string Purpose, string[] Properties, string[] Attachments)
Sends a petition to the owner of a legal identity, to access the information in the identity....
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.
byte[] DecryptReceivedMessage(byte[] EncryptedMessage, byte[] SenderPublicKey, byte[] Nonce)
Decrypts a message that was aimed at the client using the current keys.
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 GetTrustChain(EventHandlerAsync< TrustChainEventArgs > Callback, object State)
Gets the trust chain from the current domain. The trust chain is a list of domains,...
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 PetitionSignatureAsync(string Address, string LegalId, byte[] Content, string PetitionId, string Purpose, string[] Properties, string[] Attachments)
Sends a petition to a third party to request a digital signature of some content. The petition is not...
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.
const string E2eKeySemaphoreName
Name of semaphore used to synchronize access to End-to-End Encryption keys used for legal identities ...
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.
async Task Apply(string Address, Property[] Properties, bool Preview, EventHandlerAsync< LegalIdentityEventArgs > Callback, object State)
Applies for a legal identity to be registered.
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.
EventHandlerAsync< IdentityReviewEventArgs > IdentityReview
Event raised when an Identity Application has been automatically reviewed.
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.
static bool IsNamespaceSmartContract(string Namespace)
If a namespace corresponds to a smart contract namespace.
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 Validate(Contract Contract, bool ValidateState, EventHandlerAsync< ContractValidationEventArgs > Callback, object State)
Validates a smart contract.
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< string[]> GetTrustChainAsync()
Gets the trust chain from the current domain. The trust chain is a list of domains,...
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 PetitionIdentityAsync(string LegalId, string PetitionId, string Purpose, string[] Properties, string[] Attachments)
Sends a petition to the owner of a legal identity, to access the information in the identity....
Task GetPeerReviewIdServiceProviders(string ComponentAddress, EventHandlerAsync< ServiceProvidersEventArgs< ServiceProviderWithLegalId > > Callback, object State)
Gets available service providers who can help review an ID application.
static bool IsNamespaceLegalIdentity(string Namespace)
If a namespace corresponds to a legal identity namespace.
async Task EnableE2eEncryption()
Enables the keys of the Contracts Client to be used for End-to-End Encrypted communication over the X...
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...
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.
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 Apply(string Address, Property[] Properties, EventHandlerAsync< LegalIdentityEventArgs > Callback, object State)
Applies for a legal identity to be registered.
Task< ContractsEventArgs > GetSignedContractsAsync()
Get contracts the account has signed.
async Task< ContractValidationEventArgs > ValidateAsync(Contract Contract, bool ValidateState, bool ValidateAttachments, bool ValidateIdentities, bool ValidateIdentityAttachments)
Validates a smart contract.
async Task Sign(string Address, Stream Data, SignWith SignWith, EventHandlerAsync< SignatureEventArgs > Callback, object State)
Signs binary data with the corresponding private key.
async Task< LegalIdentity > UploadLegalIdAttachmentAsync(string LegalId, string FileName, byte[] Data, string ContentType)
Uploads an attachment to a Legal Identity application.
async Task< IdentityValidationEventArgs > ValidateAsync(LegalIdentity Identity, bool ValidateState, bool ValidateAttachments)
Validates a legal identity.
async Task< string[]> GetTrustChainAsync(string Domain)
Gets the trust chain from a domain. The trust chain is a list of domains, each one the parent of the ...
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
void SetAllowedSources(ICallStackCheck[] ApprovedSources)
If access to sensitive methods is only accessible from a set of approved sources.
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.
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 void ParseValidationDetails(XmlElement Content, IdentityReviewEventArgs e)
Parses identity review details from an Identity Review message, and fills the corresponding propertie...
static string LegalIdUriString(string LegalId)
Legal identity URI, as a string.
async Task< LegalIdentity > ApplyAsync(string Address, Property[] Properties, bool Preview)
Applies for a legal identity to be registered.
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.
Task< LegalIdentity > ApplyAsync(string Address, Property[] Properties)
Applies for a legal identity to be registered.
async Task< SchemaReference[]> GetSchemasAsync(string Address)
Gets available schemas.
Task GetServerPublicKey(string Address, EventHandlerAsync< KeyEventArgs > Callback, object State)
Gets the server public key.
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.
bool? IsValid
If the application has been validated (true), invalidated (false), or not yet validated (null).
KeyValuePair< string, object >[] Tags
Associated tags with more information.
Event arguments for events where a client URL needs to be displayed when performing a petition.
IE2eEndpoint Key
Public key of endpoint corresponding to Address.
Event arguments for Service Provider callback methods.
bool? Valid
If signature is valid (true), invalid (false), or if signature validation was not performed (null).
LegalIdentity Identity
Legal Identity associated with the LegalId
override async Task< HumanReadableElement > IsWellDefined()
Checks if the element is well-defined.
Definition: Blocks.cs:30
Represents an invalidated claim.
Definition: InvalidClaim.cs:7
Represents an invalidated photo.
Definition: InvalidPhoto.cs:7
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.
string ErrorText
After IsParameterValid(Variables) or IsParameterValid(Variables, ContractsClient) has been execited,...
Definition: Parameter.cs:131
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
ParameterErrorReason? ErrorReason
After IsParameterValid(Variables) or IsParameterValid(Variables, ContractsClient) has been execited,...
Definition: Parameter.cs:120
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
Contains personal information found in a legal identity.
Contains a list of public key records for an endpoint.
Class defining a role
Definition: Role.cs:7
HashFunction Function
Hash Function used to calculate the digest.
Definition: SchemaDigest.cs:41
byte[] Digest
Hash Digest of schema file
Definition: SchemaDigest.cs:36
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
DateTime Timestamp
Timestamp of signature.
Definition: Signature.cs:18
Represents a validated claim.
Definition: ValidClaim.cs:7
Represents a validated photo.
Definition: ValidPhoto.cs:7
Contains information about a validation error.
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.
string ErrorText
Any error specific text.
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.
async Task PrepareFileUpload(string FileName, string ContentType, long FileSize, FilePurpose Purpose)
Prepares a file upload, by letting the broker know that a file will be uploaded, and for what purpose...
Task< HttpFileUploadEventArgs > RequestUploadSlotAsync(string FileName, string ContentType, long ContentSize)
Uploads a file to the upload component.
string FileUploadJid
JID of HTTP File Upload component.
Event arguments for HTTP File Upload callback methods.
Task PUT(byte[] Content, string ContentType, int Timeout)
Uploads file content to the server.
Abstract base class for Elliptic Curve endpoints.
Abstract base class for Module Lattice endpoints.
byte[] ExportPrivateKey()
Exports the private key (seed) of the endpoint.
RSA / AES-256 hybrid cipher.
Definition: RsaEndpoint.cs:13
byte[] Export(bool Private)
Exports information from the encryption object.
Definition: RsaEndpoint.cs:279
override bool Verify(byte[] Data, byte[] Signature)
Verifies a signature.
Definition: RsaEndpoint.cs:418
Class managing end-to-end encryption.
static bool TryCreateEndpoint(string LocalName, string Namespace, out IE2eEndpoint Endpoint)
Tries to create a new endpoint, given its qualified name.
IE2eEndpoint FindLocalEndpoint(IE2eEndpoint RemoteEndpoint)
Returns the local endpoint that matches a given remote endpoint.
static bool TryGetEndpoint(string LocalName, string Namespace, out IE2eEndpoint Endpoint)
Tries to get an existing endpoint, given its qualified name.
static bool IsE2eEncryptionEnabled(XmppClient Client)
If End-to-End encryption is enabled on an XMPP client.
static IE2eEndpoint[] CreateEndpoints(int DesiredSecurityStrength, int MinSecurityStrength, int MaxSecurityStrength)
Creates a set of endpoints within a range of security strengths.
static bool TryGetEndpointSecurity(XmppClient Client, out EndpointSecurity EndpointSecurity)
Tries to get a registered endpoint security manager from an XMPP client.
Implements support for the AES-256 cipher in hybrid End-to-End encryption schemes.
Definition: Aes256.cs:17
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".
The intended recipient is temporarily unavailable, undergoing maintenance, etc.; the associated error...
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Definition: XmppClient.cs:58
bool UnregisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool RemoveNamespaceAsClientFeature)
Unregisters a Message handler.
Definition: XmppClient.cs:2884
static string GetDomain(string JID)
Gets the domain part of a JID.
Definition: XmppClient.cs:6986
static string GetBareJID(string JID)
Gets the Bare JID from a JID, which may be a Full JID.
Definition: XmppClient.cs:6958
void RegisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool PublishNamespaceAsClientFeature)
Registers a Message handler.
Definition: XmppClient.cs:2852
Task< uint > SendIqGet(string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ Get request.
Definition: XmppClient.cs:3598
Base class for XMPP Extensions.
XmppClient client
XMPP Client used by the extension.
void 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:21
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:1211
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:1291
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
Definition: Database.cs:238
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:97
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:17
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:54
bool HasFirstItem
If there is a first item in the collection
Definition: ChunkedList.cs:778
T RemoveFirst()
Removes the first item in the collection.
Definition: ChunkedList.cs:876
void Add(T Item)
Adds an item to the collection.
Definition: ChunkedList.cs:272
T[] ToArray()
Returns an array containing all elements of the collection.
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:15
static bool UnregisterSingleton(object Object, params object[] Arguments)
Unregisters a singleton instance of a type.
Definition: Types.cs:1659
static void ReplaceSingleton(object Object, params object[] Arguments)
Replaces a singleton instance of a type.
Definition: Types.cs:1670
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:1454
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.
Represents a named semaphore, i.e. an object, identified by a name, that allows single concurrent wri...
Definition: Semaphore.cs:19
Static class of application-wide semaphores that can be used to order access to editable objects.
Definition: Semaphores.cs:17
static async Task< Semaphore > BeginWrite(string Key)
Waits until the semaphore identified by Key is ready for writing. Each call to BeginWrite must be fo...
Definition: Semaphores.cs:91
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 ICallStackCheck[] Convert(params object[] Sources)
Converts an array of objects into an array of ICallStackCheck objects, assuming each listed source is...
Definition: Assert.cs:99
static void CallFromSource(params string[] Sources)
Makes sure the call is made from one of the listed sources.
Definition: Assert.cs:54
Contains methods for simple hash calculations.
Definition: Hashes.cs:57
static byte[] ComputeSHA256Hash(byte[] Data)
Computes the SHA-256 hash of a block of binary data.
Definition: Hashes.cs:469
static byte[] ComputeHash(HashFunction Function, byte[] Data)
Computes a hash of a block of binary data.
Definition: Hashes.cs:212
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:13
byte[] Sign(byte[] Data)
Signs binary data using the local private key.
byte[] PublicKey
Remote public key.
Definition: IE2eEndpoint.cs:37
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 Namespace
Namespace of the E2E endpoint
Definition: IE2eEndpoint.cs:27
bool SharedSecretUseCipherText
If the recipient needs a cipher text to generate the same shared secret.
byte[] GetSharedSecretForDecryption(IE2eEndpoint RemoteEndpoint, byte[] CipherText)
Gets a shared secret for decryption.
byte[] GetSharedSecretForEncryption(IE2eEndpoint RemoteEndpoint, IE2eSymmetricCipher Cipher, out byte[] CipherText)
Gets a shared secret for encryption, and optionally a corresponding cipher text.
string LocalName
Local name of the E2E endpoint
Definition: IE2eEndpoint.cs:22
Interface for symmetric ciphers.
Interface for call stack checks.
Definition: ImplTypes.g.cs:58
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:82
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:56
ContractState
Recognized contract states
Definition: Enumerations.cs:7
IdentityStatus
Validation Status of legal identity
ValidationErrorType
Type of validation error.
FilePurpose
Purpose of file uploaded
Definition: FilePurpose.cs:10
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
XmppState
State of XMPP connection.
Definition: XmppState.cs:7
E2ETransmission
End-to-end encryption mode.
ProfilerThreadType
Type of profiler thread.
IdentityState
Lists recognized legal identity states.
HashFunction
Hash method enumeration.
Definition: Hashes.cs:26
Represents a duration value, as defined by the xsd:duration data type: http://www....
Definition: Duration.cs:14
override string ToString()
Definition: Duration.cs:516