Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
EndpointSecurity.cs
1using System;
3using System.IO;
4using System.Reflection;
5using System.Text;
6using System.Threading.Tasks;
7using System.Xml;
9using Waher.Events;
17
19{
24 {
25 private static readonly Random rnd = new Random();
26
30 public const string IoTHarmonizationE2EIeeeV1 = "urn:ieee:iot:e2e:1.0";
31
35 public const string IoTHarmonizationE2ENeuroFoundationV1 = "urn:nf:iot:e2e:1.0";
36
41
45 public static readonly string[] NamespacesIoTHarmonizationE2E = new string[]
46 {
49 };
50
54 public const string IoTHarmonizationP2PIeeeV1 = "urn:ieee:iot:p2p:1.0";
55
59 public const string IoTHarmonizationP2PNeuroFoundationV1 = "urn:nf:iot:p2p:1.0";
60
65
69 public static readonly string[] NamespacesIoTHarmonizationP2P = new string[]
70 {
73 };
74
75 private static Dictionary<string, IE2eEndpoint> endpointTypes = new Dictionary<string, IE2eEndpoint>();
76 private static bool initialized = false;
77 private static Type[] e2eTypes = null;
78 private static bool e2eTypesLocked = false;
79
80 private readonly Dictionary<string, RemoteEndPoints> contacts;
81 private XmppClient client;
82 private readonly XmppServerlessMessaging serverlessMessaging;
83 private Dictionary<string, IE2eEndpoint> oldKeys = null;
84 private Dictionary<string, IE2eEndpoint> keys = null;
85 private IE2eEndpoint[] oldKeysSorted = null;
86 private IE2eEndpoint[] keysSorted = null;
87 private readonly Guid instanceId = Guid.NewGuid();
88 private readonly UTF8Encoding encoding = new UTF8Encoding(false, false);
89 private readonly object synchObject = new object();
90 private readonly int securityStrength;
91 private readonly bool ephemeralKeys;
92 private Aes256 aes = new Aes256();
94 private ChaCha20 cha = new ChaCha20();
95
96 private class RemoteEndPoints
97 {
98 public Dictionary<string, IE2eEndpoint> ByFqn;
99 public IE2eEndpoint Default;
100 public bool Aes;
101 public bool Cha;
102 public bool Acp;
103 }
104
110 public EndpointSecurity(XmppClient Client, int SecurityStrength)
111 : this(Client, null, SecurityStrength)
112 {
113 }
114
121 public EndpointSecurity(XmppClient Client, XmppServerlessMessaging ServerlessMessaging, int SecurityStrength)
122 : this(Client, ServerlessMessaging, SecurityStrength, null)
123 {
124 }
125
132 public EndpointSecurity(XmppClient Client, int SecurityStrength, params IE2eEndpoint[] LocalEndpoints)
133 : this(Client, null, SecurityStrength, LocalEndpoints)
134 {
135 }
136
144 public EndpointSecurity(XmppClient Client, XmppServerlessMessaging ServerlessMessaging, int SecurityStrength,
145 params IE2eEndpoint[] LocalEndpoints)
146 : base()
147 {
148 this.securityStrength = SecurityStrength;
149 this.client = Client;
150 this.serverlessMessaging = ServerlessMessaging;
151 this.contacts = new Dictionary<string, RemoteEndPoints>(StringComparer.CurrentCultureIgnoreCase);
152
153 if (LocalEndpoints is null)
154 {
155 LocalEndpoints = CreateEndpoints(SecurityStrength, 0, int.MaxValue);
156 this.ephemeralKeys = true;
157 }
158 else
159 this.ephemeralKeys = false;
160
161 this.keys = new Dictionary<string, IE2eEndpoint>();
162 this.keysSorted = null;
163
164 foreach (IE2eEndpoint Endpoint in LocalEndpoints)
165 this.keys[Endpoint.Namespace + "#" + Endpoint.LocalName] = Endpoint;
166
167 if (!(this.client is null))
168 this.RegisterHandlers(this.client);
169 }
170
174 internal IE2eEndpoint[] Keys
175 {
176 get
177 {
178 this.keysSorted ??= SortedArray(this.keys);
179 return this.keysSorted;
180 }
181 }
182
186 internal IE2eEndpoint[] OldKeys
187 {
188 get
189 {
190 this.oldKeysSorted ??= SortedArray(this.oldKeys);
191 return this.oldKeysSorted;
192 }
193 }
194
198 public XmppClient Client => this.client;
199
200 private Task Client_CustomPresenceXml(object Sender, CustomPresenceEventArgs e)
201 {
202 this.AppendE2eInfo(e.Stanza);
203 this.serverlessMessaging?.AppendP2pInfo(e.Stanza);
204
205 return Task.CompletedTask;
206 }
207
215 public static IE2eEndpoint[] CreateEndpoints(int DesiredSecurityStrength, int MinSecurityStrength, int MaxSecurityStrength)
216 {
217 return CreateEndpoints(DesiredSecurityStrength, MinSecurityStrength, MaxSecurityStrength, null);
218 }
219
228 public static IE2eEndpoint[] CreateEndpoints(int DesiredSecurityStrength, int MinSecurityStrength, int MaxSecurityStrength,
229 params Type[] OnlyIfDerivedFrom)
230 {
231 return CreateEndpoints(DesiredSecurityStrength, MinSecurityStrength, MaxSecurityStrength, OnlyIfDerivedFrom, null);
232 }
233
243 public static IE2eEndpoint[] CreateEndpoints(int DesiredSecurityStrength, int MinSecurityStrength, int MaxSecurityStrength,
244 Type[] OnlyIfDerivedFrom, ProfilerThread Thread)
245 {
246 Thread = Thread?.CreateSubThread("Endpoints", ProfilerThreadType.Sequential);
247 try
248 {
249 Thread?.Start();
250 Thread?.NewState("Init");
251
252 int i, c = OnlyIfDerivedFrom?.Length ?? 0;
253 TypeInfo[] OnlyIfDerivedFromType = c == 0 ? null : new TypeInfo[c];
254
255 for (i = 0; i < c; i++)
256 OnlyIfDerivedFromType[i] = OnlyIfDerivedFrom[i].GetTypeInfo();
257
258 List<IE2eEndpoint> Result = new List<IE2eEndpoint>();
259 IEnumerable<IE2eEndpoint> Templates;
260 bool CheckHeritance = true;
261
262 lock (endpointTypes)
263 {
264 if (initialized)
265 Templates = endpointTypes.Values;
266 else
267 {
268 Dictionary<string, IE2eEndpoint> E2eTypes = new Dictionary<string, IE2eEndpoint>();
269 Dictionary<string, bool> TypeNames = new Dictionary<string, bool>();
270 TypeInfo E2eTypeInfo = typeof(IE2eEndpoint).GetTypeInfo();
271
272 foreach (KeyValuePair<string, IE2eEndpoint> P in endpointTypes)
273 {
274 E2eTypes[P.Key] = P.Value;
275 TypeNames[P.Value.GetType().FullName] = true;
276 }
277
278 foreach (Type T in e2eTypes ?? Types.GetTypesImplementingInterface(typeof(IE2eEndpoint)))
279 {
280 if (TypeNames.ContainsKey(T.FullName))
281 continue;
282
283 TypeInfo TI = T.GetTypeInfo();
284 if (!(e2eTypes is null) && !E2eTypeInfo.IsAssignableFrom(TI))
285 continue;
286
287 if (c > 0)
288 {
289 bool DerivedFrom = false;
290
291 for (i = 0; i < c; i++)
292 {
293 if (OnlyIfDerivedFromType[i].IsAssignableFrom(TI))
294 {
295 DerivedFrom = true;
296 break;
297 }
298 }
299
300 if (!DerivedFrom)
301 continue;
302 }
303
304 ConstructorInfo CI = Types.GetDefaultConstructor(T);
305 if (CI is null)
306 continue;
307
308 try
309 {
310 IE2eEndpoint Endpoint = (IE2eEndpoint)CI.Invoke(Types.NoParameters);
311 E2eTypes[Endpoint.Namespace + "#" + Endpoint.LocalName] = Endpoint;
312 }
313 catch (Exception ex)
314 {
315 Log.Exception(ex);
316 continue;
317 }
318 }
319
320 endpointTypes = E2eTypes;
321 Templates = E2eTypes.Values;
322
323 if (OnlyIfDerivedFromType is null)
324 initialized = true;
325 else
326 CheckHeritance = false;
327 }
328 }
329
330 foreach (IE2eEndpoint Endpoint in Templates)
331 {
332 if (CheckHeritance && c > 0)
333 {
334 bool DerivedFrom = false;
335
336 for (i = 0; i < c; i++)
337 {
338 if (OnlyIfDerivedFromType[i].IsAssignableFrom(Endpoint.GetType().GetTypeInfo()))
339 {
340 DerivedFrom = true;
341 break;
342 }
343 }
344
345 if (!DerivedFrom)
346 continue;
347 }
348
349 Thread?.NewState(Endpoint.LocalName);
350
351 IE2eEndpoint Endpoint2 = Endpoint.Create(DesiredSecurityStrength);
352 i = Endpoint2.SecurityStrength;
353 if (i >= MinSecurityStrength && i <= MaxSecurityStrength)
354 Result.Add(Endpoint2);
355 else
356 Endpoint2.Dispose();
357 }
358
359 return Result.ToArray();
360 }
361 finally
362 {
363 Thread?.Stop();
364 }
365 }
366
370 public Guid InstanceId => this.instanceId;
371
377 public static void SetCiphers(Type[] CipherTypes, bool Lock)
378 {
379 if (e2eTypesLocked)
380 throw new InvalidOperationException("Ciphers locked.");
381
382 e2eTypes = CipherTypes;
383 e2eTypesLocked = Lock;
384 }
385
393 public static bool TryGetEndpoint(string LocalName, string Namespace, out IE2eEndpoint Endpoint)
394 {
395 if (Namespace.StartsWith("urn:ieee:"))
396 Namespace = Namespace.Replace("urn:ieee:", "urn:nf:");
397
398 string Key = Namespace + "#" + LocalName;
399
400 if (endpointTypes.TryGetValue(Key, out Endpoint))
401 return true;
402 else if (initialized || endpointTypes.Count > 0)
403 return false;
404
405 CreateEndpoints(128, 0, int.MaxValue);
406
407 return endpointTypes.TryGetValue(Key, out Endpoint);
408 }
409
417 public static bool TryCreateEndpoint(string LocalName, string Namespace, out IE2eEndpoint Endpoint)
418 {
419 if (TryGetEndpoint(LocalName, Namespace, out Endpoint))
420 {
421 Endpoint = Endpoint.Create(Endpoint.SecurityStrength);
422 return true;
423 }
424 else
425 return false;
426 }
427
431 public virtual void Dispose()
432 {
433 if (!(this.client is null))
434 {
435 this.UnregisterHandlers(this.client);
436 this.client = null;
437 }
438
439 lock (this.synchObject)
440 {
441 if (!(this.oldKeys is null))
442 {
443 foreach (IE2eEndpoint E2e in this.OldKeys)
444 E2e.Dispose();
445 }
446
447 if (!(this.keys is null))
448 {
449 foreach (IE2eEndpoint E2e in this.Keys)
450 E2e.Dispose();
451 }
452 }
453
454 lock (this.contacts)
455 {
456 foreach (RemoteEndPoints Endpoints in this.contacts.Values)
457 {
458 foreach (IE2eEndpoint Endpoint in Endpoints.ByFqn.Values)
459 Endpoint.Dispose();
460 }
461
462 this.contacts.Clear();
463 }
464
465 this.aes?.Dispose();
466 this.aes = null;
467
468 this.acp?.Dispose();
469 this.acp = null;
470
471 this.cha?.Dispose();
472 this.cha = null;
473 }
474
475 private Task Client_OnStateChanged(object Sender, XmppState NewState)
476 {
477 if (NewState == XmppState.RequestingSession && this.ephemeralKeys)
478 this.GenerateNewKey();
479
480 return Task.CompletedTask;
481 }
482
486 public void GenerateNewKey()
487 {
488 lock (this.synchObject)
489 {
490 IE2eEndpoint[] Keys = this.OldKeys;
491
492 this.oldKeys = this.keys;
493 this.oldKeysSorted = this.keysSorted;
494
495 if (!(Keys is null))
496 {
497 foreach (IE2eEndpoint E2e in Keys)
498 E2e.Dispose();
499 }
500
501 Dictionary<string, IE2eEndpoint> NewKeys = new Dictionary<string, IE2eEndpoint>();
502
503 foreach (KeyValuePair<string, IE2eEndpoint> P in this.keys)
504 {
505 IE2eEndpoint NewKey = P.Value.Create(this.securityStrength);
506 NewKey.Previous = P.Value;
507 P.Value.Previous = null;
508 NewKeys[P.Key] = NewKey;
509 }
510
511 this.keys = NewKeys;
512 this.keysSorted = null;
513 }
514 }
515
523 {
524 if (Client.TryGetTag("E2E", out object Obj) && Obj is EndpointSecurity Typed)
525 {
526 EndpointSecurity = Typed;
527 return true;
528 }
529 else
530 {
531 EndpointSecurity = null;
532 return false;
533 }
534 }
535
540 {
541 return TryGetEndpointSecurity(Client, out _);
542 }
543
548 public virtual void RegisterHandlers(XmppClient Client)
549 {
550 if (!(this.client is null))
551 {
552 this.UnregisterHandlers(this.client);
553 this.client = null;
554 }
555
556 this.client = Client;
557 this.client.SetTag("E2E", this);
558
559 #region Neuro-Foundation V1
560
561 Client?.RegisterMessageHandler("aes", IoTHarmonizationE2ENeuroFoundationV1, this.AesMessageHandler, false);
562 Client?.RegisterIqGetHandler("aes", IoTHarmonizationE2ENeuroFoundationV1, this.AesIqGetHandler, false);
563 Client?.RegisterIqSetHandler("aes", IoTHarmonizationE2ENeuroFoundationV1, this.AesIqSetHandler, false);
564 Client?.RegisterMessageHandler("acp", IoTHarmonizationE2ENeuroFoundationV1, this.AcpMessageHandler, false);
565 Client?.RegisterIqGetHandler("acp", IoTHarmonizationE2ENeuroFoundationV1, this.AcpIqGetHandler, false);
566 Client?.RegisterIqSetHandler("acp", IoTHarmonizationE2ENeuroFoundationV1, this.AcpIqSetHandler, false);
567 Client?.RegisterMessageHandler("cha", IoTHarmonizationE2ENeuroFoundationV1, this.ChaMessageHandler, false);
568 Client?.RegisterIqGetHandler("cha", IoTHarmonizationE2ENeuroFoundationV1, this.ChaIqGetHandler, false);
569 Client?.RegisterIqSetHandler("cha", IoTHarmonizationE2ENeuroFoundationV1, this.ChaIqSetHandler, false);
570 Client?.RegisterIqSetHandler("synchE2e", IoTHarmonizationE2ENeuroFoundationV1, this.SynchE2eHandler, false);
571
572 #endregion
573
574 #region IEEE v1
575
576 Client?.RegisterMessageHandler("aes", IoTHarmonizationE2EIeeeV1, this.AesMessageHandler, false);
577 Client?.RegisterIqGetHandler("aes", IoTHarmonizationE2EIeeeV1, this.AesIqGetHandler, false);
578 Client?.RegisterIqSetHandler("aes", IoTHarmonizationE2EIeeeV1, this.AesIqSetHandler, false);
579 Client?.RegisterMessageHandler("acp", IoTHarmonizationE2EIeeeV1, this.AcpMessageHandler, false);
580 Client?.RegisterIqGetHandler("acp", IoTHarmonizationE2EIeeeV1, this.AcpIqGetHandler, false);
581 Client?.RegisterIqSetHandler("acp", IoTHarmonizationE2EIeeeV1, this.AcpIqSetHandler, false);
582 Client?.RegisterMessageHandler("cha", IoTHarmonizationE2EIeeeV1, this.ChaMessageHandler, false);
583 Client?.RegisterIqGetHandler("cha", IoTHarmonizationE2EIeeeV1, this.ChaIqGetHandler, false);
584 Client?.RegisterIqSetHandler("cha", IoTHarmonizationE2EIeeeV1, this.ChaIqSetHandler, false);
585 Client?.RegisterIqSetHandler("synchE2e", IoTHarmonizationE2EIeeeV1, this.SynchE2eHandler, false);
586
587 #endregion
588
589 this.client.OnStateChanged += this.Client_OnStateChanged;
590 this.client.OnPresence += this.Client_OnPresence;
591 this.client.CustomPresenceXml += this.Client_CustomPresenceXml;
592 }
593
599 {
600 Client.RemoveTag("E2E");
601 if (this.client == Client)
602 this.client = null;
603
604 #region Neuro-Foundation V1
605
606 Client?.UnregisterMessageHandler("aes", IoTHarmonizationE2ENeuroFoundationV1, this.AesMessageHandler, false);
607 Client?.UnregisterIqGetHandler("aes", IoTHarmonizationE2ENeuroFoundationV1, this.AesIqGetHandler, false);
608 Client?.UnregisterIqSetHandler("aes", IoTHarmonizationE2ENeuroFoundationV1, this.AesIqSetHandler, false);
609 Client?.UnregisterMessageHandler("acp", IoTHarmonizationE2ENeuroFoundationV1, this.AcpMessageHandler, false);
610 Client?.UnregisterIqGetHandler("acp", IoTHarmonizationE2ENeuroFoundationV1, this.AcpIqGetHandler, false);
611 Client?.UnregisterIqSetHandler("acp", IoTHarmonizationE2ENeuroFoundationV1, this.AcpIqSetHandler, false);
612 Client?.UnregisterMessageHandler("cha", IoTHarmonizationE2ENeuroFoundationV1, this.ChaMessageHandler, false);
613 Client?.UnregisterIqGetHandler("cha", IoTHarmonizationE2ENeuroFoundationV1, this.ChaIqGetHandler, false);
614 Client?.UnregisterIqSetHandler("cha", IoTHarmonizationE2ENeuroFoundationV1, this.ChaIqSetHandler, false);
615 Client?.UnregisterIqSetHandler("synchE2e", IoTHarmonizationE2ENeuroFoundationV1, this.SynchE2eHandler, false);
616
617 #endregion
618
619 #region IEEE v1
620
621 Client?.UnregisterMessageHandler("aes", IoTHarmonizationE2EIeeeV1, this.AesMessageHandler, false);
622 Client?.UnregisterIqGetHandler("aes", IoTHarmonizationE2EIeeeV1, this.AesIqGetHandler, false);
623 Client?.UnregisterIqSetHandler("aes", IoTHarmonizationE2EIeeeV1, this.AesIqSetHandler, false);
624 Client?.UnregisterMessageHandler("acp", IoTHarmonizationE2EIeeeV1, this.AcpMessageHandler, false);
625 Client?.UnregisterIqGetHandler("acp", IoTHarmonizationE2EIeeeV1, this.AcpIqGetHandler, false);
626 Client?.UnregisterIqSetHandler("acp", IoTHarmonizationE2EIeeeV1, this.AcpIqSetHandler, false);
627 Client?.UnregisterMessageHandler("cha", IoTHarmonizationE2EIeeeV1, this.ChaMessageHandler, false);
628 Client?.UnregisterIqGetHandler("cha", IoTHarmonizationE2EIeeeV1, this.ChaIqGetHandler, false);
629 Client?.UnregisterIqSetHandler("cha", IoTHarmonizationE2EIeeeV1, this.ChaIqSetHandler, false);
630 Client?.UnregisterIqSetHandler("synchE2e", IoTHarmonizationE2EIeeeV1, this.SynchE2eHandler, false);
631
632 #endregion
633
634 Client.OnStateChanged -= this.Client_OnStateChanged;
635 Client.OnPresence -= this.Client_OnPresence;
636 Client.CustomPresenceXml -= this.Client_CustomPresenceXml;
637 }
638
645 public static Dictionary<string, IE2eEndpoint> ParseE2eKeys(XmlElement E2E, int SecurityStrength)
646 {
647 Dictionary<string, IE2eEndpoint> Endpoints = null;
648
649 foreach (XmlNode N in E2E.ChildNodes)
650 {
651 if (N is XmlElement E)
652 {
653 IE2eEndpoint Endpoint = ParseE2eKey(E);
654
655 if (!(Endpoint is null) && Endpoint.SecurityStrength >= SecurityStrength)
656 {
657 Endpoints ??= new Dictionary<string, IE2eEndpoint>();
658 Endpoints[Endpoint.Namespace + "#" + Endpoint.LocalName] = Endpoint;
659 }
660 }
661 }
662
663 return Endpoints;
664 }
665
671 public static IE2eEndpoint ParseE2eKey(XmlElement E)
672 {
673 if (TryGetEndpoint(E.LocalName, E.NamespaceURI, out IE2eEndpoint Endpoint))
674 return Endpoint.Parse(E);
675 else
676 return null;
677 }
678
685 public bool AddPeerPkiInfo(string FullJID, XmlElement E2E)
686 {
687 if (E2E is null)
688 {
689 this.RemovePeerPkiInfo(FullJID);
690 return false;
691 }
692
693 try
694 {
695 Dictionary<string, IE2eEndpoint> Endpoints = null;
696 bool Aes = XML.Attribute(E2E, "aes", false);
697 bool Cha = XML.Attribute(E2E, "cha", false);
698 bool Acp = XML.Attribute(E2E, "acp", false);
699 bool HasSymmetricAttributes = Aes || Cha || Acp;
700
701 if (!HasSymmetricAttributes)
702 {
703 if (!E2E.HasAttribute("aes") &&
704 !E2E.HasAttribute("cha") &&
705 !E2E.HasAttribute("acp"))
706 {
707 Aes = Cha = Acp = true;
708 }
709 else
710 {
711 this.RemovePeerPkiInfo(FullJID);
712 return false;
713 }
714 }
715
716 if (!(E2E is null))
717 Endpoints = ParseE2eKeys(E2E, this.securityStrength);
718
719 if (Endpoints is null)
720 {
721 this.RemovePeerPkiInfo(FullJID);
722 return false;
723 }
724
725 bool SymmetricCiphersSupported = true;
726
727 foreach (IE2eEndpoint Endpoint in Endpoints.Values)
728 {
729 if (!Endpoint.DefaultSymmetricCipher.Supported(E2E))
730 {
731 SymmetricCiphersSupported = false;
732 break;
733 }
734 }
735
736 if (!SymmetricCiphersSupported)
737 {
739 IE2eSymmetricCipher SymmetricCipher;
740
741 if (Aes)
742 SymmetricCiphers.Add(new Aes256());
743
744 if (Cha)
745 SymmetricCiphers.Add(new ChaCha20());
746
747 if (Acp)
748 SymmetricCiphers.Add(new AeadChaCha20Poly1305());
749
750 if (SymmetricCiphers.Count == 1)
751 SymmetricCipher = SymmetricCiphers.FirstItem;
752 else
753 {
754 lock (rnd)
755 {
756 SymmetricCipher = SymmetricCiphers[rnd.Next(SymmetricCiphers.Count)];
757 }
758 }
759
760 foreach (IE2eEndpoint Endpoint in Endpoints.Values)
761 Endpoint.DefaultSymmetricCipher = SymmetricCipher;
762 }
763
764 RemoteEndPoints OldEndpoints;
765
766 lock (this.contacts)
767 {
768 if (!this.contacts.TryGetValue(FullJID, out OldEndpoints))
769 OldEndpoints = null;
770
771 this.contacts[FullJID] = new RemoteEndPoints()
772 {
773 ByFqn = Endpoints,
774 Aes = Aes,
775 Cha = Cha,
776 Acp = Acp
777 };
778 }
779
780 if (!(OldEndpoints is null))
781 {
782 foreach (IE2eEndpoint Endpoint in OldEndpoints.ByFqn.Values)
783 Endpoint.Dispose();
784 }
785
786 return true;
787 }
788 catch (Exception)
789 {
790 this.RemovePeerPkiInfo(FullJID);
791 return false;
792 }
793 }
794
800 public bool RemovePeerPkiInfo(string FullJID)
801 {
802 RemoteEndPoints Keys;
803
804 lock (this.contacts)
805 {
806 if (!this.contacts.TryGetValue(FullJID, out Keys))
807 return false;
808 else
809 this.contacts.Remove(FullJID);
810 }
811
812 foreach (IE2eEndpoint Endpoint in Keys.ByFqn.Values)
813 Endpoint.Dispose();
814
815 return true;
816 }
817
823 public bool ContainsKey(string FullJid)
824 {
825 lock (this.contacts)
826 {
827 return this.contacts.ContainsKey(FullJid);
828 }
829 }
830
836 public IE2eEndpoint[] GetE2eEndpoints(string FullJid)
837 {
838 lock (this.contacts)
839 {
840 if (this.contacts.TryGetValue(FullJid, out RemoteEndPoints Endpoints))
841 return SortedArray(Endpoints.ByFqn);
842 }
843
844 if (string.Compare(FullJid, this.client.BareJID) == 0)
845 {
846 StringBuilder Xml = new StringBuilder();
847
848 this.AppendE2eInfo(Xml);
849
850 XmlDocument Doc = XML.ParseXml(Xml.ToString(), true);
851
852 return SortedArray(ParseE2eKeys(Doc.DocumentElement, this.securityStrength) ?? new Dictionary<string, IE2eEndpoint>());
853 }
854
855 return Array.Empty<E2eEndpoint>();
856 }
857
858 private static IE2eEndpoint[] SortedArray(Dictionary<string, IE2eEndpoint> Endpoints)
859 {
860 int c = Endpoints?.Count ?? 0;
861 IE2eEndpoint[] Result = new IE2eEndpoint[c];
862
863 if (c > 0)
864 {
865 Endpoints.Values.CopyTo(Result, 0);
866
867 Array.Sort(Result, (ep1, ep2) =>
868 {
869 int Diff = (ep2.PostQuantumCryptography ? 1 : 0) - (ep1.PostQuantumCryptography ? 1 : 0);
870 if (Diff != 0)
871 return Diff;
872
873 Diff = (ep2.Safe ? 1 : 0) - (ep1.Safe ? 1 : 0);
874 if (Diff != 0)
875 return Diff;
876
877 Diff = ep2.SecurityStrength - ep1.SecurityStrength;
878 if (Diff != 0)
879 return Diff;
880
881 Diff = (ep1.Slow ? 1 : 0) - (ep2.Slow ? 1 : 0);
882 if (Diff != 0)
883 return Diff;
884
885 Diff = (ep2.SupportsSignatures ? 1 : 0) - (ep1.SupportsSignatures ? 1 : 0);
886 if (Diff != 0)
887 return Diff;
888
889 return ep2.Score - ep1.Score;
890 });
891 }
892
893 return Result;
894 }
895
906 public Task<KeyValuePair<byte[], IE2eEndpoint>> Encrypt(string Id, string Type,
907 string From, string To, byte[] Data)
908 {
909 return this.Encrypt(Id, Type, From, To, false, 0, Data);
910 }
911
924 public virtual async Task<KeyValuePair<byte[], IE2eEndpoint>> Encrypt(string Id, string Type, string From, string To,
925 bool Pqc, int MinSecurityStrength, byte[] Data)
926 {
927 IE2eEndpoint RemoteEndpoint = this.FindRemoteEndpoint(To, Pqc, MinSecurityStrength);
928 if (RemoteEndpoint is null)
929 return new KeyValuePair<byte[], IE2eEndpoint>(null, null);
930
931 IE2eEndpoint EndpointReference = this.FindLocalEndpoint(RemoteEndpoint);
932 if (EndpointReference is null)
933 return new KeyValuePair<byte[], IE2eEndpoint>(null, null);
934
935 uint Counter = await EndpointReference.GetNextCounter();
936 byte[] Encrypted = EndpointReference.DefaultSymmetricCipher.Encrypt(Id, Type, From, To, Counter, Data, EndpointReference, RemoteEndpoint);
937
938 return new KeyValuePair<byte[], IE2eEndpoint>(Encrypted, EndpointReference);
939 }
940
952 public virtual Task<byte[]> Decrypt(string EndpointReference, string Id, string Type, string From, string To, byte[] Data,
953 IE2eSymmetricCipher SymmetricCipher)
954 {
955 IE2eEndpoint RemoteEndpoint = this.FindRemoteEndpoint(From, EndpointReference);
956 if (RemoteEndpoint is null)
957 return null;
958
959 IE2eEndpoint LocalEndpoint = this.FindLocalEndpoint(RemoteEndpoint);
960 if (LocalEndpoint is null)
961 return null;
962
963 IE2eSymmetricCipher Cipher = LocalEndpoint.DefaultSymmetricCipher;
964 if (!(SymmetricCipher is null) && Cipher.GetType() != SymmetricCipher.GetType())
965 Cipher = SymmetricCipher;
966
967 byte[] Decrypted = Cipher.Decrypt(Id, Type, From, To, Data, RemoteEndpoint, LocalEndpoint);
968
969 return Task.FromResult(Decrypted);
970 }
971
972 private IE2eEndpoint FindRemoteEndpoint(string RemoteJid, bool Pqc, int MinSecurityStrength)
973 {
974 return this.FindRemoteEndpoint(RemoteJid, null, Pqc, MinSecurityStrength);
975 }
976
977 private IE2eEndpoint FindRemoteEndpoint(string RemoteJid, string EndpointReference)
978 {
979 return this.FindRemoteEndpoint(RemoteJid, EndpointReference, false, 0);
980 }
981
982 private IE2eEndpoint FindRemoteEndpoint(string RemoteJid, string EndpointReference,
983 bool Pqc, int MinSecurityStrength)
984 {
985 lock (this.contacts)
986 {
987 if (!this.contacts.TryGetValue(RemoteJid, out RemoteEndPoints Endpoints))
988 return null;
989
990 if (EndpointReference is null)
991 {
992 if (Endpoints.Default is null)
993 {
994 IE2eEndpoint[] Ordered = SortedArray(Endpoints.ByFqn);
995 IE2eEndpoint LastSafeAndFastAndSignaturesAndPqcAndSharedSecrets = null;
996 IE2eEndpoint LastSafeAndFastAndSignaturesAndPqc = null;
997 IE2eEndpoint LastSafeAndFastAndSignatures = null;
998 IE2eEndpoint LastSafeAndFast = null;
999 IE2eEndpoint LastSafe = null;
1000
1001 if (Ordered.Length == 0)
1002 return null;
1003
1004 foreach (IE2eEndpoint Endpoint in Ordered)
1005 {
1006 if (Pqc && !Endpoint.PostQuantumCryptography)
1007 continue;
1008
1009 if (Endpoint.SecurityStrength < MinSecurityStrength)
1010 continue;
1011
1012 if (Endpoint.Safe)
1013 {
1014 LastSafe = Endpoint;
1015
1016 if (!Endpoint.Slow)
1017 {
1018 LastSafeAndFast = Endpoint;
1019
1020 if (Endpoint.SupportsSignatures)
1021 {
1022 LastSafeAndFastAndSignatures = Endpoint;
1023
1024 if (Endpoint.PostQuantumCryptography)
1025 {
1026 LastSafeAndFastAndSignaturesAndPqc = Endpoint;
1027
1028 if (!Endpoint.SharedSecretUseCipherText)
1029 LastSafeAndFastAndSignaturesAndPqcAndSharedSecrets = Endpoint;
1030 }
1031 }
1032 }
1033 }
1034 }
1035
1036 Endpoints.Default =
1037 LastSafeAndFastAndSignaturesAndPqcAndSharedSecrets ??
1038 LastSafeAndFastAndSignaturesAndPqc ??
1039 LastSafeAndFastAndSignatures ??
1040 LastSafeAndFast ?? LastSafe ??
1041 Ordered[0];
1042 }
1043
1044 return Endpoints.Default;
1045 }
1046
1047 if (EndpointReference.IndexOf('#') < 0)
1048 EndpointReference = IoTHarmonizationE2ECurrent + "#" + EndpointReference;
1049 else
1050 EndpointReference = EndpointReference.Replace("urn:ieee:", "urn:nf:");
1051
1052 if (Endpoints.ByFqn.TryGetValue(EndpointReference, out IE2eEndpoint Selected))
1053 return Selected;
1054 else
1055 return null;
1056 }
1057 }
1058
1069 public Task<IE2eEndpoint> Encrypt(string Id, string Type, string From,
1070 string To, Stream Data, Stream Encrypted)
1071 {
1072 return this.Encrypt(Id, Type, From, To, false, 0, Data, Encrypted);
1073 }
1074
1087 public virtual async Task<IE2eEndpoint> Encrypt(string Id, string Type, string From,
1088 string To, bool Pqc, int MinSecurityStrength, Stream Data, Stream Encrypted)
1089 {
1090 IE2eEndpoint RemoteEndpoint = this.FindRemoteEndpoint(To, Pqc, MinSecurityStrength);
1091 if (RemoteEndpoint is null)
1092 return null;
1093
1094 IE2eEndpoint LocalEndpoint = this.FindLocalEndpoint(RemoteEndpoint);
1095 if (LocalEndpoint is null)
1096 return null;
1097
1098 uint Counter = await LocalEndpoint.GetNextCounter();
1099 await LocalEndpoint.DefaultSymmetricCipher.Encrypt(Id, Type, From, To, Counter, Data, Encrypted, LocalEndpoint, RemoteEndpoint);
1100
1101 return LocalEndpoint;
1102 }
1103
1115 public virtual async Task<Stream> Decrypt(string EndpointReference, string Id, string Type, string From, string To, Stream Data,
1116 IE2eSymmetricCipher SymmetricCipher)
1117 {
1118 IE2eEndpoint RemoteEndpoint = this.FindRemoteEndpoint(From, EndpointReference);
1119 if (RemoteEndpoint is null)
1120 return null;
1121
1122 IE2eEndpoint LocalEndpoint = this.FindLocalEndpoint(RemoteEndpoint);
1123 if (LocalEndpoint is null)
1124 return null;
1125
1126 IE2eSymmetricCipher Cipher = LocalEndpoint.DefaultSymmetricCipher;
1127 if (!(SymmetricCipher is null) && Cipher.GetType() != SymmetricCipher.GetType())
1128 Cipher = SymmetricCipher;
1129
1130 return await Cipher.Decrypt(Id, Type, From, To, Data, RemoteEndpoint, LocalEndpoint);
1131 }
1132
1144 public Task<bool> Encrypt(XmppClient Client, string Id, string Type, string From, string To,
1145 string DataXml, StringBuilder Xml)
1146 {
1147 return this.Encrypt(Client, Id, Type, From, To, false, 0, DataXml, Xml);
1148 }
1149
1163 public virtual async Task<bool> Encrypt(XmppClient Client, string Id, string Type, string From,
1164 string To, bool Pqc, int MinSecurityStrength, string DataXml, StringBuilder Xml)
1165 {
1166 bool SniffE2eInfo = Client.HasSniffers && Client.TryGetTag("ShowE2E", out object Obj) && Obj is bool b && b;
1167 IE2eEndpoint RemoteEndpoint = this.FindRemoteEndpoint(To, Pqc, MinSecurityStrength);
1168 if (RemoteEndpoint is null)
1169 {
1170 if (SniffE2eInfo)
1171 Client.Warning("Remote E2E endpoint not found. Unable to encrypt message.");
1172
1173 return false;
1174 }
1175
1176 IE2eEndpoint LocalEndpoint = this.FindLocalEndpoint(RemoteEndpoint);
1177 if (LocalEndpoint is null)
1178 {
1179 if (SniffE2eInfo)
1180 Client.Warning("Local E2E endpoint matching remote endpoint not found. Unable to encrypt message.");
1181
1182 return false;
1183 }
1184
1185 byte[] Data = this.encoding.GetBytes(DataXml);
1186 uint Counter = await LocalEndpoint.GetNextCounter();
1187 bool Result = LocalEndpoint.DefaultSymmetricCipher.Encrypt(Id, Type, From, To, Counter, Data, Xml, LocalEndpoint, RemoteEndpoint);
1188
1189 if (SniffE2eInfo)
1190 Client.Information(DataXml);
1191
1192 return Result;
1193 }
1194
1201 {
1202 return this.FindLocalEndpoint(RemoteEndpoint.LocalName, RemoteEndpoint.Namespace);
1203 }
1204
1210 public IE2eEndpoint FindLocalEndpoint(string KeyName)
1211 {
1212 return this.FindLocalEndpoint(KeyName, IoTHarmonizationE2ECurrent);
1213 }
1214
1221 public IE2eEndpoint FindLocalEndpoint(string KeyName, string KeyNamespace)
1222 {
1223 if (string.IsNullOrEmpty(KeyNamespace))
1224 KeyNamespace = IoTHarmonizationE2ECurrent;
1225
1226 if (this.keys?.TryGetValue(KeyNamespace + "#" + KeyName, out IE2eEndpoint Result) ?? false)
1227 return Result;
1228 else
1229 return null;
1230 }
1231
1237 public IE2eEndpoint FindLocalEndpoint(Type KeyType)
1238 {
1239 lock (this.synchObject)
1240 {
1241 foreach (IE2eEndpoint Endpoint in this.Keys)
1242 {
1243 if (Endpoint.GetType() == KeyType)
1244 return Endpoint;
1245 }
1246 }
1247
1248 if (!typeof(IE2eEndpoint).IsAssignableFrom(KeyType.GetTypeInfo()))
1249 throw new ArgumentException("Not assignable from " + typeof(IE2eEndpoint).FullName, nameof(KeyType));
1250
1251 return null;
1252 }
1253
1259 public IE2eEndpoint FindLocalEndpoint(byte[] PublicKey)
1260 {
1261 if (PublicKey is null)
1262 return null;
1263
1264 string s = Convert.ToBase64String(PublicKey);
1265
1266 lock (this.synchObject)
1267 {
1268 foreach (IE2eEndpoint Endpoint in this.Keys)
1269 {
1270 if (Endpoint.PublicKeyBase64 == s)
1271 return Endpoint;
1272 }
1273 }
1274
1275 return null;
1276 }
1277
1286 {
1287 ChunkedList<IE2eEndpoint> Result = null;
1288 int Len = PublicKey?.Length ?? 0;
1289
1290 lock (this.synchObject)
1291 {
1292 foreach (IE2eEndpoint Endpoint in this.Keys)
1293 {
1294 if ((Endpoint.PublicKey?.Length ?? 0) == Len)
1295 {
1296 Result ??= new ChunkedList<IE2eEndpoint>();
1297 Result.Add(Endpoint);
1298 }
1299 }
1300 }
1301
1302 return Result?.ToArray() ?? Array.Empty<IE2eEndpoint>();
1303 }
1304
1316 public virtual Tuple<string, string> Decrypt(XmppClient Client, string Id, string Type, string From, string To, XmlElement E2eElement,
1317 IE2eSymmetricCipher SymmetricCipher)
1318 {
1319 bool SniffE2eInfo = Client.HasSniffers && Client.TryGetTag("ShowE2E", out object Obj) && Obj is bool b && b;
1320 string EndpointReference = XML.Attribute(E2eElement, "r");
1321 IE2eEndpoint RemoteEndpoint = this.FindRemoteEndpoint(From, EndpointReference);
1322 if (RemoteEndpoint is null)
1323 {
1324 if (SniffE2eInfo)
1325 Client.Error("Remote E2E endpoint not found. Unable to decrypt message.");
1326
1327 return null;
1328 }
1329
1330 IE2eEndpoint LocalEndpoint = this.FindLocalEndpoint(RemoteEndpoint);
1331 if (LocalEndpoint is null)
1332 {
1333 if (SniffE2eInfo)
1334 Client.Error("Local E2E endpoint matching remote endpoint not found. Unable to decrypt message.");
1335
1336 return null;
1337 }
1338
1339 IE2eSymmetricCipher Cipher = LocalEndpoint.DefaultSymmetricCipher;
1340 if (!(SymmetricCipher is null) && Cipher.GetType() != SymmetricCipher.GetType())
1341 Cipher = SymmetricCipher;
1342
1343 string Xml = Cipher.Decrypt(Id, Type, From, To, E2eElement, RemoteEndpoint, LocalEndpoint);
1344 if (Xml is null)
1345 return null;
1346
1347 if (SniffE2eInfo)
1348 Client.Information(Xml);
1349
1350 return new Tuple<string, string>(Xml, EndpointReference);
1351 }
1352
1353 private Task AesMessageHandler(object Sender, MessageEventArgs e)
1354 {
1355 return this.E2eMessageHandler(Sender, e, this.aes);
1356 }
1357
1358 private Task AcpMessageHandler(object Sender, MessageEventArgs e)
1359 {
1360 return this.E2eMessageHandler(Sender, e, this.acp);
1361 }
1362
1363 private Task ChaMessageHandler(object Sender, MessageEventArgs e)
1364 {
1365 return this.E2eMessageHandler(Sender, e, this.cha);
1366 }
1367
1368 private Task E2eMessageHandler(object Sender, MessageEventArgs e, IE2eSymmetricCipher Cipher)
1369 {
1370 XmppClient Client = Sender as XmppClient;
1371 Tuple<string, string> T = this.Decrypt(Client, e.Id, e.Message.GetAttribute("type"), e.From, e.To, e.Content, Cipher);
1372 if (T is null)
1373 {
1374 this.client.Error("Unable to decrypt or verify response.");
1375 return Task.CompletedTask;
1376 }
1377
1378 string Xml = T.Item1;
1379 string EndpointReference = T.Item2;
1380
1381 XmlDocument Doc = XML.ParseXml(Xml, true);
1382
1383 MessageEventArgs e2 = new MessageEventArgs(Client, Doc.DocumentElement)
1384 {
1385 From = e.From,
1386 To = e.To,
1387 Id = e.Id,
1388 E2eEncryption = this,
1389 E2eReference = EndpointReference
1390 };
1391
1393
1394 return Task.CompletedTask;
1395 }
1396
1404 public virtual bool TryGetSymmetricCipher(string LocalName, string Namespace, out IE2eSymmetricCipher Cipher)
1405 {
1406 switch (LocalName)
1407 {
1408 case "aes": Cipher = this.aes; return true;
1409 case "acp": Cipher = this.acp; return true;
1410 case "cha": Cipher = this.cha; return true;
1411 }
1412
1413 Cipher = null;
1414 return false;
1415 }
1416
1422 protected virtual async Task IqResult(object Sender, IqResultEventArgs e)
1423 {
1424 XmppClient Client = Sender as XmppClient;
1425 XmlElement E = e.FirstElement;
1426 object[] P = (object[])e.State;
1427 EventHandlerAsync<IqResultEventArgs> Callback = (EventHandlerAsync<IqResultEventArgs>)P[0];
1428 object State = P[1];
1429
1430 if (!this.TryGetSymmetricCipher(E.LocalName, E.NamespaceURI, out IE2eSymmetricCipher Cipher))
1431 Cipher = null;
1432
1433 if (!(Cipher is null))
1434 {
1435 Tuple<string, string> T = this.Decrypt(Client, e.Id, e.Response.GetAttribute("type"), e.From, e.To, E, Cipher);
1436 if (T is null)
1437 {
1438 Client.Error("Unable to decrypt or verify response.");
1439 return;
1440 }
1441
1442 string Content = T.Item1;
1443 string EndpointReference = T.Item2;
1444 StringBuilder Xml = new StringBuilder();
1445
1446 Xml.Append("<iq xmlns=\"jabber:client\" id=\"");
1447 Xml.Append(e.Id);
1448 Xml.Append("\" from=\"");
1449 Xml.Append(XML.Encode(e.From));
1450 Xml.Append("\" to=\"");
1451 Xml.Append(XML.Encode(e.To));
1452
1453 if (e.Ok)
1454 Xml.Append("\" type=\"result\">");
1455 else
1456 Xml.Append("\" type=\"error\">");
1457
1458 Xml.Append(Content);
1459 Xml.Append("</iq>");
1460
1461 XmlDocument Doc = XML.ParseXml(Xml.ToString(), true);
1462
1463 IqResultEventArgs e2 = new IqResultEventArgs(this, EndpointReference, Cipher,
1464 Doc.DocumentElement, e.Id, e.To, e.From, e.Ok, State);
1465 await Callback.Raise(Sender, e2);
1466 }
1467 else if (!e.Ok && this.IsForbidden(e.ErrorElement))
1468 {
1470 string Id = (string)P[3];
1471 string To = (string)P[4];
1472 string Xml = (string)P[5];
1473 string Type = (string)P[6];
1474 int RetryTimeout = (int)P[7];
1475 int NrRetries = (int)P[8];
1476 bool DropOff = (bool)P[9];
1477 int MaxRetryTimeout = (int)P[10];
1478 bool PkiSynchronized = (bool)P[11];
1479
1480 if (PkiSynchronized)
1481 {
1482 e.State = State;
1483 await Callback.Raise(Sender, e);
1484 }
1485 else
1486 {
1487 await this.SynchronizeE2e(To, async (Sender2, e2) =>
1488 {
1489 if (e2.Ok)
1490 {
1491 await this.SendIq(Client, E2ETransmission, Id, To, Xml, Type, Callback, State,
1492 RetryTimeout, NrRetries, DropOff, MaxRetryTimeout, true);
1493 }
1494 else
1495 {
1496 e.State = State;
1497 await Callback.Raise(Sender, e);
1498 }
1499 });
1500 }
1501 ;
1502 }
1503 else
1504 {
1505 e.State = State;
1506 await Callback.Raise(Sender, e);
1507 }
1508 }
1509
1510 private bool IsForbidden(XmlElement E)
1511 {
1512 if (E is null)
1513 return false;
1514
1515 XmlElement E2;
1516
1517 foreach (XmlNode N in E.ChildNodes)
1518 {
1519 E2 = N as XmlElement;
1520 if (E2 is null)
1521 continue;
1522
1523 if (E2.LocalName == "forbidden" && E2.NamespaceURI == XmppClient.NamespaceXmppStanzas)
1524 return true;
1525 }
1526
1527 return false;
1528 }
1529
1530 private string EmbedIq(IqEventArgs e, string Type, string Content)
1531 {
1532 StringBuilder Xml = new StringBuilder();
1533
1534 Xml.Append("<iq xmlns=\"jabber:client\" id=\"");
1535 Xml.Append(e.Id);
1536 Xml.Append("\" from=\"");
1537 Xml.Append(XML.Encode(e.From));
1538 Xml.Append("\" to=\"");
1539 Xml.Append(XML.Encode(e.To));
1540 Xml.Append("\" type=\"");
1541 Xml.Append(Type);
1542 Xml.Append("\">");
1543 Xml.Append(Content);
1544 Xml.Append("</iq>");
1545
1546 return Xml.ToString();
1547 }
1548
1549 private Task AesIqGetHandler(object Sender, IqEventArgs e)
1550 {
1551 return this.E2eIqGetHandler(Sender, e, this.aes);
1552 }
1553
1554 private Task AcpIqGetHandler(object Sender, IqEventArgs e)
1555 {
1556 return this.E2eIqGetHandler(Sender, e, this.acp);
1557 }
1558
1559 private Task ChaIqGetHandler(object Sender, IqEventArgs e)
1560 {
1561 return this.E2eIqGetHandler(Sender, e, this.cha);
1562 }
1563
1564 private async Task E2eIqGetHandler(object Sender, IqEventArgs e, IE2eSymmetricCipher Cipher)
1565 {
1566 XmppClient Client = Sender as XmppClient;
1567 Tuple<string, string> T = this.Decrypt(Client, e.Id, e.IQ.GetAttribute("type"), e.From, e.To, e.Query, Cipher);
1568 if (T is null)
1569 {
1570 await e.IqError(new ForbiddenException("Unable to decrypt or verify message.", e.IQ));
1571 return;
1572 }
1573
1574 string Content = T.Item1;
1575 string EndpointReference = T.Item2;
1576
1577 XmlDocument Doc = XML.ParseXml(this.EmbedIq(e, "get", Content), true);
1578
1579 IqEventArgs e2 = new IqEventArgs(Client, this, EndpointReference, Cipher, Doc.DocumentElement, e.Id, e.To, e.From);
1580 await Client.ProcessIqGet(e2);
1581 }
1582
1583 private Task AesIqSetHandler(object Sender, IqEventArgs e)
1584 {
1585 return this.E2eIqSetHandler(Sender, e, this.aes);
1586 }
1587
1588 private Task AcpIqSetHandler(object Sender, IqEventArgs e)
1589 {
1590 return this.E2eIqSetHandler(Sender, e, this.acp);
1591 }
1592
1593 private Task ChaIqSetHandler(object Sender, IqEventArgs e)
1594 {
1595 return this.E2eIqSetHandler(Sender, e, this.cha);
1596 }
1597
1598 private async Task E2eIqSetHandler(object Sender, IqEventArgs e, IE2eSymmetricCipher Cipher)
1599 {
1600 XmppClient Client = Sender as XmppClient;
1601 Tuple<string, string> T = this.Decrypt(Client, e.Id, e.IQ.GetAttribute("type"), e.From, e.To, e.Query, Cipher);
1602 if (T is null)
1603 {
1604 await e.IqError(new ForbiddenException("Unable to decrypt or verify message.", e.IQ));
1605 return;
1606 }
1607
1608 string Content = T.Item1;
1609 string EndpointReference = T.Item2;
1610
1611 XmlDocument Doc = XML.ParseXml(this.EmbedIq(e, "set", Content), true);
1612
1613 IqEventArgs e2 = new IqEventArgs(Client, this, EndpointReference, Cipher, Doc.DocumentElement, e.Id, e.To, e.From);
1614 await Client.ProcessIqSet(e2);
1615 }
1616
1635 MessageType Type, string Id, string To, string CustomXml, string Body, string Subject,
1636 string Language, string ThreadId, string ParentThreadId, EventHandlerAsync<DeliveryEventArgs> DeliveryCallback,
1637 object State)
1638 {
1639 return this.SendMessage(Client, E2ETransmission, QoS, Type, Id, To, CustomXml, Body, Subject,
1640 Language, ThreadId, ParentThreadId, DeliveryCallback, State, false);
1641 }
1642
1662 MessageType Type, string Id, string To, string CustomXml, string Body, string Subject,
1663 string Language, string ThreadId, string ParentThreadId, EventHandlerAsync<DeliveryEventArgs> DeliveryCallback,
1664 object State, bool PkiSynchronized)
1665 {
1666 if (this.client is null)
1667 throw new ObjectDisposedException("Endpoint security object disposed.");
1668
1669 if (string.IsNullOrEmpty(Id))
1670 Id = Client.NextId();
1671
1672 StringBuilder Xml = new StringBuilder();
1673
1674 Xml.Append("<message");
1675
1676 switch (Type)
1677 {
1678 case MessageType.Chat:
1679 Xml.Append(" type=\"chat\"");
1680 break;
1681
1682 case MessageType.Error:
1683 Xml.Append(" type=\"error\"");
1684 break;
1685
1686 case MessageType.GroupChat:
1687 Xml.Append(" type=\"groupchat\"");
1688 break;
1689
1690 case MessageType.Headline:
1691 Xml.Append(" type=\"headline\"");
1692 break;
1693 }
1694
1695 if (!string.IsNullOrEmpty(Language))
1696 {
1697 Xml.Append(" xml:lang=\"");
1698 Xml.Append(XML.Encode(Language));
1699 Xml.Append('"');
1700 }
1701
1702 Xml.Append('>');
1703
1704 if (!string.IsNullOrEmpty(Subject))
1705 {
1706 Xml.Append("<subject>");
1707 Xml.Append(XML.Encode(Subject));
1708 Xml.Append("</subject>");
1709 }
1710
1711 if (!string.IsNullOrEmpty(Body))
1712 {
1713 Xml.Append("<body>");
1714 Xml.Append(XML.Encode(Body));
1715 Xml.Append("</body>");
1716 }
1717
1718 if (!string.IsNullOrEmpty(ThreadId))
1719 {
1720 Xml.Append("<thread");
1721
1722 if (!string.IsNullOrEmpty(ParentThreadId))
1723 {
1724 Xml.Append(" parent=\"");
1725 Xml.Append(XML.Encode(ParentThreadId));
1726 Xml.Append('"');
1727 }
1728
1729 Xml.Append(">");
1730 Xml.Append(XML.Encode(ThreadId));
1731 Xml.Append("</thread>");
1732 }
1733
1734 if (!string.IsNullOrEmpty(CustomXml))
1735 {
1736 if (!XML.IsValidXml(CustomXml, true, true, true, true, false, false))
1737 throw new ArgumentException("Not valid XML.", nameof(CustomXml));
1738
1739 Xml.Append(CustomXml);
1740 }
1741
1742 Xml.Append("</message>");
1743
1744 string MessageXml = Xml.ToString();
1745 StringBuilder Encrypted = new StringBuilder();
1746
1747 // TODO: Custom signal strength
1748 if (await this.Encrypt(Client, Id, string.Empty, this.client.FullJID, To,
1749 E2ETransmission == E2ETransmission.AssertE2EPQC, 128, MessageXml, Encrypted))
1750 {
1751 MessageXml = Encrypted.ToString();
1752
1753 await Client.SendMessage(QoS, MessageType.Normal, Id, To, MessageXml, string.Empty,
1754 string.Empty, string.Empty, string.Empty, string.Empty, DeliveryCallback, State);
1755
1756 return;
1757 }
1758 else if (XmppClient.GetBareJID(To) == To)
1759 {
1760 RosterItem Item = Client.GetRosterItem(To);
1761 bool Found = false;
1762
1763 if (!(Item is null))
1764 {
1765 foreach (PresenceEventArgs e in Item.Resources)
1766 {
1767 Encrypted.Clear();
1768
1769 if (await this.Encrypt(Client, Id, string.Empty, this.client.FullJID, e.From,
1770 E2ETransmission == E2ETransmission.AssertE2EPQC, 128, MessageXml, Encrypted))
1771 {
1772 await Client.SendMessage(QoS, MessageType.Normal, Id, e.From, Encrypted.ToString(), string.Empty,
1773 string.Empty, string.Empty, string.Empty, string.Empty, DeliveryCallback, State);
1774
1775 Found = true;
1776 }
1777 }
1778 }
1779
1780 if (Found)
1781 return;
1782 }
1783
1784 if (E2ETransmission == E2ETransmission.IgnoreIfNotE2E)
1785 return;
1786
1787 if (!PkiSynchronized)
1788 {
1789 await this.SynchronizeE2e(To, async (Sender, e) =>
1790 {
1791 if (e.Ok)
1792 {
1793 await this.SendMessage(Client, E2ETransmission, QoS, Type, Id, To, CustomXml, Body, Subject,
1794 Language, ThreadId, ParentThreadId, DeliveryCallback, State, true);
1795 }
1796 else if (E2ETransmission == E2ETransmission.NormalIfNotE2E)
1797 {
1798 await Client.SendMessage(QoS, Type, Id, To, CustomXml, Body, Subject, Language,
1799 ThreadId, ParentThreadId, DeliveryCallback, State);
1800 }
1801 else if (!(DeliveryCallback is null)) // null Callbacks are common, and should not result in a warning in sniffers.
1802 await DeliveryCallback.Raise(Sender, new DeliveryEventArgs(State, false));
1803
1804 }, State);
1805 }
1806 else if (E2ETransmission == E2ETransmission.NormalIfNotE2E)
1807 {
1808 await Client.SendMessage(QoS, Type, Id, To, CustomXml, Body, Subject, Language,
1809 ThreadId, ParentThreadId, DeliveryCallback, State);
1810 }
1811 else
1812 throw new InvalidOperationException("End-to-End Encryption not available between " + Client.FullJID + " and " + To + ".");
1813 }
1814
1825 public Task<uint> SendIqGet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml,
1826 EventHandlerAsync<IqResultEventArgs> Callback, object State)
1827 {
1828 return this.SendIq(Client, E2ETransmission, null, To, Xml, "get", Callback, State, Client.DefaultRetryTimeout,
1830 }
1831
1844 public Task<uint> SendIqGet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml,
1845 EventHandlerAsync<IqResultEventArgs> Callback, object State, int RetryTimeout, int NrRetries)
1846 {
1847 return this.SendIq(Client, E2ETransmission, null, To, Xml, "get", Callback, State, RetryTimeout, NrRetries, false,
1848 RetryTimeout, false);
1849 }
1850
1866 public Task<uint> SendIqGet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml,
1867 EventHandlerAsync<IqResultEventArgs> Callback, object State, int RetryTimeout, int NrRetries, bool DropOff,
1868 int MaxRetryTimeout)
1869 {
1870 return this.SendIq(Client, E2ETransmission, null, To, Xml, "get", Callback, State, RetryTimeout,
1871 NrRetries, DropOff, MaxRetryTimeout, false);
1872 }
1873
1884 public Task<uint> SendIqSet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml,
1885 EventHandlerAsync<IqResultEventArgs> Callback, object State)
1886 {
1887 return this.SendIq(Client, E2ETransmission, null, To, Xml, "set", Callback, State,
1890 }
1891
1904 public Task<uint> SendIqSet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml,
1905 EventHandlerAsync<IqResultEventArgs> Callback, object State, int RetryTimeout, int NrRetries)
1906 {
1907 return this.SendIq(Client, E2ETransmission, null, To, Xml, "set", Callback, State, RetryTimeout,
1908 NrRetries, false, RetryTimeout, false);
1909 }
1910
1926 public Task<uint> SendIqSet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml,
1927 EventHandlerAsync<IqResultEventArgs> Callback, object State, int RetryTimeout, int NrRetries, bool DropOff,
1928 int MaxRetryTimeout)
1929 {
1930 return this.SendIq(Client, E2ETransmission, null, To, Xml, "set", Callback, State, RetryTimeout,
1931 NrRetries, DropOff, MaxRetryTimeout, false);
1932 }
1933
1945 public Task<uint> SendIqGet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml,
1946 EventHandlerAsync<IqResultEventArgs> Callback, object State, EventHandlerAsync<DeliveryEventArgs> DeliveryCallback)
1947 {
1948 return this.SendIq(Client, E2ETransmission, null, To, Xml, "get", Callback, State, Client.DefaultRetryTimeout,
1950 }
1951
1965 public Task<uint> SendIqGet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml,
1966 EventHandlerAsync<IqResultEventArgs> Callback, object State, int RetryTimeout, int NrRetries,
1967 EventHandlerAsync<DeliveryEventArgs> DeliveryCallback)
1968 {
1969 return this.SendIq(Client, E2ETransmission, null, To, Xml, "get", Callback, State, RetryTimeout, NrRetries, false,
1970 RetryTimeout, false, DeliveryCallback);
1971 }
1972
1989 public Task<uint> SendIqGet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml,
1990 EventHandlerAsync<IqResultEventArgs> Callback, object State, int RetryTimeout, int NrRetries, bool DropOff,
1991 int MaxRetryTimeout, EventHandlerAsync<DeliveryEventArgs> DeliveryCallback)
1992 {
1993 return this.SendIq(Client, E2ETransmission, null, To, Xml, "get", Callback, State, RetryTimeout,
1994 NrRetries, DropOff, MaxRetryTimeout, false, DeliveryCallback);
1995 }
1996
2008 public Task<uint> SendIqSet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml,
2009 EventHandlerAsync<IqResultEventArgs> Callback, object State, EventHandlerAsync<DeliveryEventArgs> DeliveryCallback)
2010 {
2011 return this.SendIq(Client, E2ETransmission, null, To, Xml, "set", Callback, State,
2013 Client.DefaultMaxRetryTimeout, false, DeliveryCallback);
2014 }
2015
2029 public Task<uint> SendIqSet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml,
2030 EventHandlerAsync<IqResultEventArgs> Callback, object State, int RetryTimeout, int NrRetries,
2031 EventHandlerAsync<DeliveryEventArgs> DeliveryCallback)
2032 {
2033 return this.SendIq(Client, E2ETransmission, null, To, Xml, "set", Callback, State, RetryTimeout,
2034 NrRetries, false, RetryTimeout, false, DeliveryCallback);
2035 }
2036
2053 public Task<uint> SendIqSet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml,
2054 EventHandlerAsync<IqResultEventArgs> Callback, object State, int RetryTimeout, int NrRetries, bool DropOff,
2055 int MaxRetryTimeout, EventHandlerAsync<DeliveryEventArgs> DeliveryCallback)
2056 {
2057 return this.SendIq(Client, E2ETransmission, null, To, Xml, "set", Callback, State, RetryTimeout,
2058 NrRetries, DropOff, MaxRetryTimeout, false, DeliveryCallback);
2059 }
2060
2069 public Task SendIqResult(XmppClient Client, E2ETransmission E2ETransmission, string Id, string To, string Xml)
2070 {
2071 return this.SendIq(Client, E2ETransmission, Id, To, Xml, "result", null, null, 0, 0, false, 0, false);
2072 }
2073
2082 public Task SendIqError(XmppClient Client, E2ETransmission E2ETransmission, string Id, string To, string Xml)
2083 {
2084 return this.SendIq(Client, E2ETransmission, Id, To, Xml, "error", null, null, 0, 0, false, 0, false);
2085 }
2086
2096 Exception ex)
2097 {
2098 return this.SendIqError(Client, E2ETransmission, Id, To, Client.ExceptionToXmppXml(ex));
2099 }
2100
2122 protected Task<uint> SendIq(XmppClient Client, E2ETransmission E2ETransmission, string Id, string To, string Xml,
2123 string Type, EventHandlerAsync<IqResultEventArgs> Callback, object State, int RetryTimeout, int NrRetries, bool DropOff,
2124 int MaxRetryTimeout, bool PkiSynchronized)
2125 {
2126 return this.SendIq(Client, E2ETransmission, Id, To, Xml, Type, Callback, State, RetryTimeout, NrRetries, DropOff,
2127 MaxRetryTimeout, PkiSynchronized, null);
2128 }
2129
2152 protected async Task<uint> SendIq(XmppClient Client, E2ETransmission E2ETransmission, string Id, string To, string Xml,
2153 string Type, EventHandlerAsync<IqResultEventArgs> Callback, object State, int RetryTimeout, int NrRetries, bool DropOff,
2154 int MaxRetryTimeout, bool PkiSynchronized, EventHandlerAsync<DeliveryEventArgs> DeliveryCallback)
2155 {
2156 if (this.client is null)
2157 throw new ObjectDisposedException("Endpoint security object disposed.");
2158
2159 if (string.IsNullOrEmpty(Id))
2160 Id = Client.NextId();
2161
2162 StringBuilder Encrypted = new StringBuilder();
2163
2164 if (await this.Encrypt(Client, Id, Type, this.client.FullJID, To,
2165 E2ETransmission == E2ETransmission.AssertE2EPQC, 128, Xml, Encrypted))
2166 {
2167 string XmlEnc = Encrypted.ToString();
2168
2169 return await Client.SendIq(Id, To, XmlEnc, Type, this.IqResult,
2170 new object[] { Callback, State, E2ETransmission, Id, To, Xml, Type, RetryTimeout, NrRetries, DropOff, MaxRetryTimeout, PkiSynchronized },
2171 RetryTimeout, NrRetries, DropOff, MaxRetryTimeout, DeliveryCallback);
2172 }
2173
2174 if (E2ETransmission == E2ETransmission.IgnoreIfNotE2E)
2175 {
2176 if (uint.TryParse(Id, out uint SeqNr))
2177 return SeqNr;
2178 else
2179 return 0;
2180 }
2181
2182 if (!PkiSynchronized)
2183 {
2184 if (!uint.TryParse(Id, out uint SeqNr))
2185 {
2186 Id = Client.NextId();
2187 SeqNr = uint.Parse(Id);
2188 }
2189
2190 await this.SynchronizeE2e(To, async (Sender, e) =>
2191 {
2192 if (e.Ok)
2193 {
2194 await this.SendIq(Client, E2ETransmission, Id, To, Xml, Type, Callback, State,
2195 RetryTimeout, NrRetries, DropOff, MaxRetryTimeout, true, DeliveryCallback);
2196 }
2197 else if (E2ETransmission == E2ETransmission.NormalIfNotE2E)
2198 {
2199 await Client.SendIq(Id, To, Xml, Type, Callback, State, RetryTimeout,
2200 NrRetries, DropOff, MaxRetryTimeout, DeliveryCallback);
2201 }
2202 else
2203 {
2204 if (!(DeliveryCallback is null))
2205 await DeliveryCallback.Raise(Sender, new DeliveryEventArgs(Sender, true));
2206
2207 await Callback.Raise(Sender, e);
2208 }
2209 }, State);
2210
2211 return SeqNr;
2212 }
2213 else if (E2ETransmission == E2ETransmission.NormalIfNotE2E)
2214 {
2215 return await Client.SendIq(Id, To, Xml, Type, Callback, State, RetryTimeout,
2216 NrRetries, DropOff, MaxRetryTimeout, DeliveryCallback);
2217 }
2218 else
2219 throw new InvalidOperationException("End-to-End Encryption not available between " + Client.FullJID + " and " + To + ".");
2220 }
2221
2233 public XmlElement IqGet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, int Timeout)
2234 {
2235 Task<XmlElement> Result = this.IqGetAsync(Client, E2ETransmission, To, Xml);
2236
2237 if (!Result.Wait(Timeout))
2238 throw new TimeoutException();
2239
2240 return Result.Result;
2241 }
2242
2253 public async Task<XmlElement> IqGetAsync(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml)
2254 {
2255 TaskCompletionSource<XmlElement> Result = new TaskCompletionSource<XmlElement>();
2256
2257 await this.SendIqGet(Client, E2ETransmission, To, Xml, (Sender, e) =>
2258 {
2259 if (e.Ok)
2260 Result.TrySetResult(e.Response);
2261 else
2262 Result.TrySetException(e.StanzaError ?? new XmppException("Unable to perform IQ Get."));
2263
2264 return Task.CompletedTask;
2265
2266 }, null);
2267
2268 return await Result.Task;
2269 }
2270
2282 public XmlElement IqSet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, int Timeout)
2283 {
2284 Task<XmlElement> Result = this.IqSetAsync(Client, E2ETransmission, To, Xml);
2285
2286 if (!Result.Wait(Timeout))
2287 throw new TimeoutException();
2288
2289 return Result.Result;
2290 }
2291
2302 public async Task<XmlElement> IqSetAsync(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml)
2303 {
2304 TaskCompletionSource<XmlElement> Result = new TaskCompletionSource<XmlElement>();
2305
2306 await this.SendIqSet(Client, E2ETransmission, To, Xml, (Sender, e) =>
2307 {
2308 if (e.Ok)
2309 Result.TrySetResult(e.Response);
2310 else
2311 Result.TrySetException(e.StanzaError ?? new XmppException("Unable to perform IQ Set."));
2312
2313 return Task.CompletedTask;
2314
2315 }, null);
2316
2317 return await Result.Task;
2318 }
2319
2324 public void AppendE2eInfo(StringBuilder Xml)
2325 {
2326 lock (this.synchObject)
2327 {
2328 Xml.Append("<e2e xmlns=\"");
2329 Xml.Append(IoTHarmonizationE2ECurrent);
2330 Xml.Append("\" aes=\"true\" cha=\"true\" acp=\"true\">");
2331
2332 foreach (IE2eEndpoint E2e in this.Keys)
2334
2335 Xml.Append("</e2e>");
2336 }
2337 }
2338
2344 public Task SynchronizeE2e(string FullJID, EventHandlerAsync<IqResultEventArgs> Callback)
2345 {
2346 return this.SynchronizeE2e(FullJID, Callback, null);
2347 }
2348
2355 public async Task SynchronizeE2e(string FullJID, EventHandlerAsync<IqResultEventArgs> Callback, object State)
2356 {
2357 LinkedList<SynchRec> CallbackList;
2358
2359 lock (this.synchronizationTasks)
2360 {
2361 SynchRec Rec = new SynchRec()
2362 {
2363 Callback = Callback,
2364 State = State
2365 };
2366
2367 if (this.synchronizationTasks.TryGetValue(FullJID, out CallbackList))
2368 {
2369 CallbackList.AddLast(Rec);
2370 return;
2371 }
2372
2373 CallbackList = new LinkedList<SynchRec>();
2374 CallbackList.AddLast(Rec);
2375
2376 this.synchronizationTasks[FullJID] = CallbackList;
2377 }
2378
2379 await this.client.SendIqSet(FullJID, this.GetE2eXml(), async (Sender, e) =>
2380 {
2381 lock (this.synchronizationTasks)
2382 {
2383 if (!this.synchronizationTasks.TryGetValue(FullJID, out CallbackList))
2384 return;
2385
2386 this.synchronizationTasks.Remove(FullJID);
2387 }
2388
2389 if (e.Ok && !(e.FirstElement is null))
2390 await this.ParseE2e(e.FirstElement, FullJID);
2391
2392 foreach (SynchRec Rec in CallbackList)
2393 {
2394 e.State = Rec.State;
2395 await Rec.Callback.Raise(Sender, e);
2396 }
2397 }, State);
2398 }
2399
2400 private readonly Dictionary<string, LinkedList<SynchRec>> synchronizationTasks = new Dictionary<string, LinkedList<SynchRec>>();
2401
2402 private class SynchRec
2403 {
2404 public EventHandlerAsync<IqResultEventArgs> Callback;
2405 public object State;
2406 }
2407
2408 private string GetE2eXml()
2409 {
2410 StringBuilder Xml = new StringBuilder();
2411
2412 Xml.Append("<synchE2e xmlns=\"");
2413 Xml.Append(IoTHarmonizationE2ECurrent);
2414 Xml.Append("\">");
2415
2416 this.AppendE2eInfo(Xml);
2417 this.serverlessMessaging?.AppendP2pInfo(Xml);
2418
2419 Xml.Append("</synchE2e>");
2420
2421 return Xml.ToString();
2422 }
2423
2424 private async Task ParseE2e(XmlElement E, string RemoteFullJID)
2425 {
2426 XmlElement E2E = null;
2427 XmlElement P2P = null;
2428
2429 if (!(E is null) && E.LocalName == "synchE2e")
2430 {
2431 foreach (XmlNode N in E.ChildNodes)
2432 {
2433 if (N is XmlElement E2)
2434 {
2435 switch (E2.LocalName)
2436 {
2437 case "e2e":
2438 if (Array.IndexOf(NamespacesIoTHarmonizationE2E, E.NamespaceURI) >= 0)
2439 E2E = E2;
2440 break;
2441
2442 case "p2p":
2443 if (Array.IndexOf(NamespacesIoTHarmonizationP2P, E.NamespaceURI) >= 0)
2444 P2P = E2;
2445 break;
2446 }
2447 }
2448 }
2449 }
2450
2451 bool HasE2E = this.AddPeerPkiInfo(RemoteFullJID, E2E);
2452 bool HasP2P = !(this.serverlessMessaging is null) && await this.serverlessMessaging.AddPeerAddressInfo(RemoteFullJID, P2P);
2453
2454 await this.PeerUpdated.Raise(this, new PeerSynchronizedEventArgs(RemoteFullJID, HasE2E, HasP2P));
2455 }
2456
2457 private async Task SynchE2eHandler(object Sender, IqEventArgs e)
2458 {
2459 RosterItem Item;
2460
2461 if (e.FromBareJid != this.client.BareJID &&
2462 ((Item = this.client.GetRosterItem(e.FromBareJid)) is null ||
2463 Item.State == SubscriptionState.None ||
2464 Item.State == SubscriptionState.Remove ||
2465 Item.State == SubscriptionState.Unknown))
2466 {
2467 throw new ForbiddenException("Access denied. Unable to synchronize E2EE.", e.IQ);
2468 }
2469
2470 await this.ParseE2e(e.Query, e.From);
2471 await e.IqResult(this.GetE2eXml());
2472 }
2473
2474 private async Task Client_OnPresence(object Sender, PresenceEventArgs e)
2475 {
2476 switch (e.Type)
2477 {
2478 case PresenceType.Available:
2479 XmlElement E2E = null;
2480 XmlElement P2P = null;
2481
2482 foreach (XmlNode N in e.Presence.ChildNodes)
2483 {
2484 if (N is XmlElement E)
2485 {
2486 switch (E.LocalName)
2487 {
2488 case "e2e":
2489 if (Array.IndexOf(NamespacesIoTHarmonizationE2E, E.NamespaceURI) >= 0)
2490 E2E = E;
2491 break;
2492
2493 case "p2p":
2494 if (Array.IndexOf(NamespacesIoTHarmonizationP2P, E.NamespaceURI) >= 0)
2495 P2P = E;
2496 break;
2497 }
2498 }
2499 }
2500
2501 bool HasE2E = this.AddPeerPkiInfo(e.From, E2E);
2502 bool HasP2P = !(this.serverlessMessaging is null) && await this.serverlessMessaging.AddPeerAddressInfo(e.From, P2P);
2503
2504 await this.PeerAvailable.Raise(this, new AvailableEventArgs(e, HasE2E, HasP2P));
2505 break;
2506
2507 case PresenceType.Unavailable:
2508 await this.PeerUnavailable.Raise(this, e);
2509 break;
2510 }
2511 }
2512
2516 public event EventHandlerAsync<AvailableEventArgs> PeerAvailable = null;
2517
2521 public event EventHandlerAsync<PresenceEventArgs> PeerUnavailable = null;
2522
2526 public event EventHandlerAsync<PeerSynchronizedEventArgs> PeerUpdated = null;
2527
2529 public override string ToString()
2530 {
2531 return this.instanceId.ToString();
2532 }
2533
2534 }
2535}
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 XmlDocument ParseXml(string Xml)
Parses an XML Document from its string representation.
Definition: XML.cs:713
static bool IsValidXml(string Xml)
Checks if a string is valid XML
Definition: XML.cs:1397
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
void Warning(string Warning)
Called to inform the viewer of a warning state.
void Information(string Comment)
Called to inform the viewer of something.
Event arguments for delivery events.
Event Argument for custom presence XML events.
Event arguments for IQ queries.
Definition: IqEventArgs.cs:12
async Task IqError(string Xml)
Returns an error response to the current request.
Definition: IqEventArgs.cs:208
async Task IqResult(string Xml)
Returns a response to the current request.
Definition: IqEventArgs.cs:194
XmlElement Query
Query element, if found, null otherwise.
Definition: IqEventArgs.cs:119
string From
From address attribute
Definition: IqEventArgs.cs:137
string To
To address attribute
Definition: IqEventArgs.cs:132
string FromBareJid
Bare version of the "from" JID.
Definition: IqEventArgs.cs:157
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.
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.
bool Ok
If the response is an OK result response (true), or an error response (false).
string To
To whom the message was sent.
XmlElement Content
Content of the message. For messages that are processed by registered message handlers,...
Event arguments for presence events.
bool Ok
If the response is an OK result response (true), or an error response (false).
XmppException StanzaError
Any stanza error returned.
string From
From where the presence was received.
PresenceType Type
Type of presence received.
Event arguments for Availability events.
Abstract base class for End-to-End encryption schemes.
Definition: E2eEndpoint.cs:14
Class managing end-to-end encryption.
EventHandlerAsync< AvailableEventArgs > PeerAvailable
Event raised whenever a peer has become available.
Task< uint > SendIqSet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State, int RetryTimeout, int NrRetries, bool DropOff, int MaxRetryTimeout)
Sends an IQ Set stanza
EndpointSecurity(XmppClient Client, XmppServerlessMessaging ServerlessMessaging, int SecurityStrength, params IE2eEndpoint[] LocalEndpoints)
Class managing end-to-end encryption.
static IE2eEndpoint[] CreateEndpoints(int DesiredSecurityStrength, int MinSecurityStrength, int MaxSecurityStrength, params Type[] OnlyIfDerivedFrom)
Creates a set of endpoints within a range of security strengths.
Task< uint > SendIqSet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State, int RetryTimeout, int NrRetries, bool DropOff, int MaxRetryTimeout, EventHandlerAsync< DeliveryEventArgs > DeliveryCallback)
Sends an IQ Set stanza
virtual bool TryGetSymmetricCipher(string LocalName, string Namespace, out IE2eSymmetricCipher Cipher)
Tries to get a symmetric cipher from a reference.
static Dictionary< string, IE2eEndpoint > ParseE2eKeys(XmlElement E2E, int SecurityStrength)
Parses a set of E2E keys from XML.
const string IoTHarmonizationE2ENeuroFoundationV1
urn:nf:iot:e2e:1.0
IE2eEndpoint[] GetE2eEndpoints(string FullJid)
Gets available E2E options for a given endpoint.
XmlElement IqGet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, int Timeout)
Sends an IQ Get stanza
virtual async Task< bool > Encrypt(XmppClient Client, string Id, string Type, string From, string To, bool Pqc, int MinSecurityStrength, string DataXml, StringBuilder Xml)
Encrypts XML data for transmission to an endpoint.
const string IoTHarmonizationP2PIeeeV1
urn:ieee:iot:p2p:1.0
Task< uint > SendIqGet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ Get stanza
static readonly string[] NamespacesIoTHarmonizationE2E
Namespaces supported for End-to-end encryption.
virtual void RegisterHandlers(XmppClient Client)
Registers XMPP stanza handlers
virtual async Task< Stream > Decrypt(string EndpointReference, string Id, string Type, string From, string To, Stream Data, IE2eSymmetricCipher SymmetricCipher)
Decrypts binary data received from an XMPP client out of band.
Task< uint > SendIqGet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State, EventHandlerAsync< DeliveryEventArgs > DeliveryCallback)
Sends an IQ Get stanza
static bool TryCreateEndpoint(string LocalName, string Namespace, out IE2eEndpoint Endpoint)
Tries to create a new endpoint, given its qualified name.
virtual Task< byte[]> Decrypt(string EndpointReference, string Id, string Type, string From, string To, byte[] Data, IE2eSymmetricCipher SymmetricCipher)
Decrypts binary data from an endpoint.
Task< uint > SendIqGet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State, int RetryTimeout, int NrRetries, bool DropOff, int MaxRetryTimeout)
Sends an IQ Get stanza
const string IoTHarmonizationP2PCurrent
Current namespace for peer-to-peer communication
IE2eEndpoint FindLocalEndpoint(IE2eEndpoint RemoteEndpoint)
Returns the local endpoint that matches a given remote endpoint.
Task< uint > SendIqSet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State, int RetryTimeout, int NrRetries, EventHandlerAsync< DeliveryEventArgs > DeliveryCallback)
Sends an IQ Set stanza
static IE2eEndpoint ParseE2eKey(XmlElement E)
Parses a single E2E key from XML.
Task< uint > SendIqGet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State, int RetryTimeout, int NrRetries, bool DropOff, int MaxRetryTimeout, EventHandlerAsync< DeliveryEventArgs > DeliveryCallback)
Sends an IQ Get stanza
Task< uint > SendIqGet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State, int RetryTimeout, int NrRetries)
Sends an IQ Get stanza
virtual Tuple< string, string > Decrypt(XmppClient Client, string Id, string Type, string From, string To, XmlElement E2eElement, IE2eSymmetricCipher SymmetricCipher)
Decrypts XML data from an endpoint.
IE2eEndpoint FindLocalEndpoint(byte[] PublicKey)
Returns the local endpoint that matches a given public key.
virtual void UnregisterHandlers(XmppClient Client)
Unregisters XMPP stanza handlers
Task< uint > SendIqSet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ Set stanza
EndpointSecurity(XmppClient Client, int SecurityStrength, params IE2eEndpoint[] LocalEndpoints)
Class managing end-to-end encryption.
async Task SynchronizeE2e(string FullJID, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Synchronizes End-to-End Encryption and Peer-to-Peer connectivity parameters with a remote entity.
EndpointSecurity(XmppClient Client, XmppServerlessMessaging ServerlessMessaging, int SecurityStrength)
Class managing end-to-end encryption.
Task< uint > SendIq(XmppClient Client, E2ETransmission E2ETransmission, string Id, string To, string Xml, string Type, EventHandlerAsync< IqResultEventArgs > Callback, object State, int RetryTimeout, int NrRetries, bool DropOff, int MaxRetryTimeout, bool PkiSynchronized)
Sends an IQ stanza
virtual async Task IqResult(object Sender, IqResultEventArgs e)
Response handler for E2E encrypted iq stanzas
Task< IE2eEndpoint > Encrypt(string Id, string Type, string From, string To, Stream Data, Stream Encrypted)
Encrypts binary data that can be sent to an XMPP client out of band.
void AppendE2eInfo(StringBuilder Xml)
Appends E2E information to XML.
static bool TryGetEndpoint(string LocalName, string Namespace, out IE2eEndpoint Endpoint)
Tries to get an existing endpoint, given its qualified name.
IE2eEndpoint[] FindCompatibleLocalEndpoints(byte[] PublicKey)
Returns all local endpoints that are compatible with a given public key, based on its length.
Task< uint > SendIqSet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State, int RetryTimeout, int NrRetries)
Sends an IQ Set stanza
static IE2eEndpoint[] CreateEndpoints(int DesiredSecurityStrength, int MinSecurityStrength, int MaxSecurityStrength, Type[] OnlyIfDerivedFrom, ProfilerThread Thread)
Creates a set of endpoints within a range of security strengths.
static readonly string[] NamespacesIoTHarmonizationP2P
Namespaces supported for Peer-to-peer communication.
Task< bool > Encrypt(XmppClient Client, string Id, string Type, string From, string To, string DataXml, StringBuilder Xml)
Encrypts XML data for transmission to an endpoint.
async Task< XmlElement > IqSetAsync(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml)
Sends an IQ Set stanza
Guid InstanceId
ID of IEndToEndEncryption instance.
Task SendIqResult(XmppClient Client, E2ETransmission E2ETransmission, string Id, string To, string Xml)
Sends an IQ Result stanza
EndpointSecurity(XmppClient Client, int SecurityStrength)
Class managing end-to-end encryption.
const string IoTHarmonizationP2PNeuroFoundationV1
urn:nf:iot:p2p:1.0
IE2eEndpoint FindLocalEndpoint(string KeyName)
Returns the local endpoint that matches a given key name.
static bool IsE2eEncryptionEnabled(XmppClient Client)
If End-to-End encryption is enabled on an XMPP client.
IE2eEndpoint FindLocalEndpoint(string KeyName, string KeyNamespace)
Returns the local endpoint that matches a given key name and namespace.
Task< uint > SendIqGet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State, int RetryTimeout, int NrRetries, EventHandlerAsync< DeliveryEventArgs > DeliveryCallback)
Sends an IQ Get stanza
virtual async Task< KeyValuePair< byte[], IE2eEndpoint > > Encrypt(string Id, string Type, string From, string To, bool Pqc, int MinSecurityStrength, byte[] Data)
Encrypts binary data for transmission to an endpoint.
EventHandlerAsync< PeerSynchronizedEventArgs > PeerUpdated
Event raised whenever information about a peer has been updated.
Task SendIqError(XmppClient Client, E2ETransmission E2ETransmission, string Id, string To, Exception ex)
Sends an IQ Error stanza
Task SynchronizeE2e(string FullJID, EventHandlerAsync< IqResultEventArgs > Callback)
Synchronizes End-to-End Encryption and Peer-to-Peer connectivity parameters with a remote entity.
static void SetCiphers(Type[] CipherTypes, bool Lock)
Sets allowed cipers in endpoint security.
Task< KeyValuePair< byte[], IE2eEndpoint > > Encrypt(string Id, string Type, string From, string To, byte[] Data)
Encrypts binary data for transmission to an endpoint.
static IE2eEndpoint[] CreateEndpoints(int DesiredSecurityStrength, int MinSecurityStrength, int MaxSecurityStrength)
Creates a set of endpoints within a range of security strengths.
async Task< uint > SendIq(XmppClient Client, E2ETransmission E2ETransmission, string Id, string To, string Xml, string Type, EventHandlerAsync< IqResultEventArgs > Callback, object State, int RetryTimeout, int NrRetries, bool DropOff, int MaxRetryTimeout, bool PkiSynchronized, EventHandlerAsync< DeliveryEventArgs > DeliveryCallback)
Sends an IQ stanza
XmlElement IqSet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, int Timeout)
Sends an IQ Set stanza
bool AddPeerPkiInfo(string FullJID, XmlElement E2E)
Adds E2E information about a peer.
const string IoTHarmonizationE2ECurrent
Current namespace for End-to-End encryption.
const string IoTHarmonizationE2EIeeeV1
urn:ieee:iot:e2e:1.0
bool ContainsKey(string FullJid)
If infomation is available for a given endpoint.
Task SendMessage(XmppClient Client, E2ETransmission E2ETransmission, QoSLevel QoS, MessageType Type, string Id, string To, string CustomXml, string Body, string Subject, string Language, string ThreadId, string ParentThreadId, EventHandlerAsync< DeliveryEventArgs > DeliveryCallback, object State)
Sends an XMPP message to an endpoint.
bool RemovePeerPkiInfo(string FullJID)
Removes E2E information about a peer.
virtual async Task< IE2eEndpoint > Encrypt(string Id, string Type, string From, string To, bool Pqc, int MinSecurityStrength, Stream Data, Stream Encrypted)
Encrypts binary data that can be sent to an XMPP client out of band.
Task< uint > SendIqSet(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State, EventHandlerAsync< DeliveryEventArgs > DeliveryCallback)
Sends an IQ Set stanza
static bool TryGetEndpointSecurity(XmppClient Client, out EndpointSecurity EndpointSecurity)
Tries to get a registered endpoint security manager from an XMPP client.
XmppClient Client
Associated XMPP client, if any.
void GenerateNewKey()
Generates new local keys.
Task SendIqError(XmppClient Client, E2ETransmission E2ETransmission, string Id, string To, string Xml)
Sends an IQ Error stanza
IE2eEndpoint FindLocalEndpoint(Type KeyType)
Returns the local endpoint that matches a given type.
EventHandlerAsync< PresenceEventArgs > PeerUnavailable
Event raised whenever a peer has become unavailable.
async Task< XmlElement > IqGetAsync(XmppClient Client, E2ETransmission E2ETransmission, string To, string Xml)
Sends an IQ Get stanza
virtual void Dispose()
IDisposable.Dispose
Event arguments for peer synchronization events.
Implements support for the AEAD-ChaCha20-Poly1305 cipher in hybrid End-to-End encryption schemes.
Implements support for the AES-256 cipher in hybrid End-to-End encryption schemes.
Definition: Aes256.cs:17
override void Dispose()
IDisposable.Dispose
Definition: Aes256.cs:60
Implements support for the ChaCha20 cipher in hybrid End-to-End encryption schemes.
Definition: ChaCha20.cs:17
Class managing peer-to-peer serveless XMPP communication.
Maintains information about an item in the roster.
Definition: RosterItem.cs:75
SubscriptionState State
roup Current subscription state.
Definition: RosterItem.cs:268
PresenceEventArgs[] Resources
Active resources utilized by contact.
Definition: RosterItem.cs:300
The requesting entity does not possess the necessary permissions to perform an action that only certa...
Manages an XMPP client connection. Implements XMPP, as defined in https://tools.ietf....
Definition: XmppClient.cs:58
async void ProcessMessage(MessageEventArgs e)
Processes an incoming message.
Definition: XmppClient.cs:2272
bool UnregisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool RemoveNamespaceAsClientFeature)
Unregisters a Message handler.
Definition: XmppClient.cs:2884
bool UnregisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsClientFeature)
Unregisters an IQ-Get handler.
Definition: XmppClient.cs:2802
int DefaultRetryTimeout
Default retry timeout, in milliseconds. This value is used when sending IQ requests wihtout specifyin...
Definition: XmppClient.cs:6835
Task SendMessage(MessageType Type, string To, string CustomXml, string Body, string Subject, string Language, string ThreadId, string ParentThreadId)
Sends a simple chat message
Definition: XmppClient.cs:5447
string ExceptionToXmppXml(Exception ex)
Converts an exception object to an XMPP XML error element.
Definition: XmppClient.cs:3833
bool DefaultDropOff
Default Drop-off value. If drop-off is used, the retry timeout is doubled for each retry,...
Definition: XmppClient.cs:6884
int DefaultNrRetries
Default number of retries if results or errors are not returned. This value is used when sending IQ r...
Definition: XmppClient.cs:6852
static string GetBareJID(string JID)
Gets the Bare JID from a JID, which may be a Full JID.
Definition: XmppClient.cs:6958
void RegisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsClientFeature)
Registers an IQ-Set handler.
Definition: XmppClient.cs:2748
int DefaultMaxRetryTimeout
Default maximum retry timeout, in milliseconds. This value is used when sending IQ requests wihtout s...
Definition: XmppClient.cs:6868
void RegisterIqGetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool PublishNamespaceAsClientFeature)
Registers an IQ-Get handler.
Definition: XmppClient.cs:2736
Task ProcessIqSet(IqEventArgs e)
Processes an incoming IQ SET stanza.
Definition: XmppClient.cs:2516
bool RemoveTag(string TagName)
Removes a tag from the client.
Definition: XmppClient.cs:7286
Task ProcessIqGet(IqEventArgs e)
Processes an incoming IQ GET stanza.
Definition: XmppClient.cs:2507
void SetTag(string TagName, object Tag)
Sets a tag value.
Definition: XmppClient.cs:7312
const string NamespaceXmppStanzas
urn:ietf:params:xml:ns:xmpp-stanzas
Definition: XmppClient.cs:77
void RegisterMessageHandler(string LocalName, string Namespace, EventHandlerAsync< MessageEventArgs > Handler, bool PublishNamespaceAsClientFeature)
Registers a Message handler.
Definition: XmppClient.cs:2852
bool UnregisterIqSetHandler(string LocalName, string Namespace, EventHandlerAsync< IqEventArgs > Handler, bool RemoveNamespaceAsClientFeature)
Unregisters an IQ-Set handler.
Definition: XmppClient.cs:2815
Task< uint > SendIq(string Id, string To, string Xml, string Type, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ stanza.
Definition: XmppClient.cs:3892
Task< uint > SendIqSet(string To, string Xml, EventHandlerAsync< IqResultEventArgs > Callback, object State)
Sends an IQ Set request.
Definition: XmppClient.cs:3646
bool TryGetTag(string TagName, out object Tag)
Tries to get a tag from the client. Tags can be used to attached application specific objects to the ...
Definition: XmppClient.cs:7270
string NextId()
Generates a new id attribute value.
Definition: XmppClient.cs:3866
RosterItem GetRosterItem(string BareJID)
Gets a roster item.
Definition: XmppClient.cs:4571
Base class of XMPP exceptions
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:54
T FirstItem
First item in the collection.
Definition: ChunkedList.cs:784
int Count
Number of elements in collection.
Definition: ChunkedList.cs:68
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 object[] NoParameters
Contains an empty array of parameter values.
Definition: Types.cs:572
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
Definition: Types.cs:85
static ConstructorInfo GetDefaultConstructor(Type Type)
Gets the default constructor of a type, if one exists.
Definition: Types.cs:1742
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.
Abstract base class for End-to-End encryption schemes.
Definition: IE2eEndpoint.cs:13
bool SupportsSignatures
If signatures are supported.
Task< uint > GetNextCounter()
Gets the next counter value.
bool Slow
If implementation is slow, compared to other options.
int SecurityStrength
Security strength of End-to-End encryption scheme.
Definition: IE2eEndpoint.cs:17
IE2eEndpoint Create(int SecurityStrength)
Creates a new key.
IE2eEndpoint Parse(XmlElement Xml)
Parses endpoint information from an XML element.
string ToXml()
Exports the public key information to XML.
byte[] PublicKey
Remote public key.
Definition: IE2eEndpoint.cs:37
string PublicKeyBase64
Remote public key, as a Base64 string.
Definition: IE2eEndpoint.cs:42
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.
IE2eSymmetricCipher DefaultSymmetricCipher
Default symmetric cipher.
string LocalName
Local name of the E2E endpoint
Definition: IE2eEndpoint.cs:22
bool PostQuantumCryptography
If post-quantum cryptography is used.
bool Safe
If endpoint is considered safe (i.e. there are no suspected backdoors)
Interface for symmetric ciphers.
bool Supported(XmlElement E2e)
If the symmetric cipher is supported by a remote endpoint.
byte[] Encrypt(string Id, string Type, string From, string To, uint Counter, byte[] Data, IE2eEndpoint Sender, IE2eEndpoint Receiver)
Encrypts binary data
byte[] Decrypt(string Id, string Type, string From, string To, byte[] Data, IE2eEndpoint Sender, IE2eEndpoint Receiver)
Decrypts binary data
End-to-end encryption interface.
Definition: ImplTypes.g.cs:58
PresenceType
Type of presence received.
Definition: PresenceType.cs:7
QoSLevel
Quality of Service Level for asynchronous messages. Support for QoS Levels must be supported by the r...
Definition: QoSLevel.cs:8
SubscriptionState
State of a presence subscription.
Definition: RosterItem.cs:16
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.