diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 0000000..eb40b61
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,22 @@
+version: 2
+jobs:
+ build:
+ docker:
+ - image: microsoft/dotnet:2-sdk
+
+ working_directory: ~/repo
+
+ steps:
+ - checkout
+
+ - run:
+ name: Get Examples
+ command: git clone --depth 1 https://github.com/cose-wg/Examples Regressions
+
+ - run:
+ name: Unit Tests
+ command: |
+ ls
+ dotnet build --framework=netcoreapp2.0 CoAP.Std10.sln
+ dotnet test --framework=netcoreapp2.0 CoAP.Std10.sln
+
diff --git a/CoAP.Example/CoAP.Client/CoAP.Client.Std10.csproj b/CoAP.Example/CoAP.Client/CoAP.Client.Std10.csproj
index 1b78690..6264cfe 100644
--- a/CoAP.Example/CoAP.Client/CoAP.Client.Std10.csproj
+++ b/CoAP.Example/CoAP.Client/CoAP.Client.Std10.csproj
@@ -1,85 +1,27 @@
-
-
+
+
+
- Debug
- AnyCPU
- 8.0.30703
- 2.0
- {30223FF5-4DCB-45B8-9B06-61EB9FBF9FA8}
Exe
- Properties
- Com.AugustCellars.CoAP.Examples
- CoAPClient
- v4.6.2
- 512
-
- true
+ netcoreapp2.0;net462
-
- true
- full
- false
- obj\Debug\Std10
- bin\Debug\Std10\
- DEBUG;TRACE
- prompt
- 4
-
-
- pdbonly
- true
- obj\Release\Std10
- bin\Release\Std10\
- TRACE
- prompt
- 4
-
-
- pdbonly
- true
- obj\Deploy\Std10
- bin\Deploy\Std10\
+
+
TRACE
- prompt
- 4
+
-
-
-
-
-
-
+
+
+
-
- {45db1e45-4831-4e4a-bb1e-ae92eea182e3}
- CoAP.Std10
-
+
+
+
-
-
-
-
-
-
-
- ..\..\packages\PeterO.Cbor.3.0.3\lib\netstandard1.0\CBOR.dll
-
-
- ..\..\packages\PeterO.Numbers.1.0.2\lib\netstandard1.0\Numbers.dll
-
-
- ..\..\packages\Com.AugustCellars.COSE.1.3.0\lib\netstandard1.0\COSE.dll
-
+
-
-
-
\ No newline at end of file
+
+
diff --git a/CoAP.Example/CoAP.Client/ExampleClient.cs b/CoAP.Example/CoAP.Client/ExampleClient.cs
index 898f1f9..6212e84 100644
--- a/CoAP.Example/CoAP.Client/ExampleClient.cs
+++ b/CoAP.Example/CoAP.Client/ExampleClient.cs
@@ -277,6 +277,7 @@ private static SecurityContextSet LoadContextSet(string fileName)
if (usage == "oscoap") {
SecurityContext ctx = SecurityContext.DeriveContext(
key[CoseKeyParameterKeys.Octet_k].GetByteString(),
+ null,
key[CBORObject.FromObject("RecipID")].GetByteString(),
key[CBORObject.FromObject("SenderID")].GetByteString(), null,
key[CoseKeyKeys.Algorithm]);
@@ -284,7 +285,10 @@ private static SecurityContextSet LoadContextSet(string fileName)
break;
}
else if (usage == "oscoap-group") {
- SecurityContext ctx = SecurityContext.DeriveGroupContext(key[CoseKeyParameterKeys.Octet_k].GetByteString(), key[CBORObject.FromObject(2)].GetByteString(), key[CBORObject.FromObject("SenderID")].GetByteString(), null, null, key[CoseKeyKeys.Algorithm]);
+ SecurityContext ctx = SecurityContext.DeriveGroupContext(
+ key[CoseKeyParameterKeys.Octet_k].GetByteString(), key[CBORObject.FromObject(2)].GetByteString(), key[CBORObject.FromObject("SenderID")].GetByteString(),
+ null, null,
+ null, null, null, key[CoseKeyKeys.Algorithm]);
foreach (CBORObject recipient in key[CBORObject.FromObject("recipients")].Values) {
ctx.AddRecipient(recipient[CBORObject.FromObject("RecipID")].GetByteString(), new OneKey( recipient[CBORObject.FromObject("sign")]));
}
diff --git a/CoAP.Example/CoAP.Client/packages.config b/CoAP.Example/CoAP.Client/packages.config
index 370df3f..fa45386 100644
--- a/CoAP.Example/CoAP.Client/packages.config
+++ b/CoAP.Example/CoAP.Client/packages.config
@@ -1,11 +1,11 @@
-
+
-
+
diff --git a/CoAP.Example/CoAP.Server/CoAP.Server.Std10.csproj b/CoAP.Example/CoAP.Server/CoAP.Server.Std10.csproj
index ca92fba..6264cfe 100644
--- a/CoAP.Example/CoAP.Server/CoAP.Server.Std10.csproj
+++ b/CoAP.Example/CoAP.Server/CoAP.Server.Std10.csproj
@@ -1,79 +1,27 @@
-
-
+
+
+
- Debug
- AnyCPU
- 8.0.30703
- 2.0
- {05CC2E5E-B663-4560-844C-C8E52ED1C9D1}
Exe
- Properties
- Com.AugustCellars.CoAP.Examples
- CoAPServer
- v4.6.2
- 512
-
- true
+ netcoreapp2.0;net462
-
- true
- full
- false
- obj\Debug\Std10
- bin\Debug\Std10\
- TRACE;DEBUG
- prompt
- 4
-
-
- pdbonly
- true
- obj\Release\Std10
- bin\Release\Std10\
- TRACE
- prompt
- 4
-
-
- pdbonly
- true
- obj\Deploy\Std10
- bin\Deploy\Std10\
+
+
TRACE
- prompt
- 4
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {45DB1E45-4831-4E4A-BB1E-AE92EEA182E3}
- CoAP.Std10
-
+
+
+
-
+
+
+
-
+
-
-
-
\ No newline at end of file
+
+
diff --git a/CoAP.Example/CoAP.Server/app.config b/CoAP.Example/CoAP.Server/app.config
index 2ffb337..d09514f 100644
--- a/CoAP.Example/CoAP.Server/app.config
+++ b/CoAP.Example/CoAP.Server/app.config
@@ -12,7 +12,7 @@
-
+
diff --git a/CoAP.NET/Channel/DataReceivedEventArgs.cs b/CoAP.NET/Channel/DataReceivedEventArgs.cs
index a7291a1..0d5bb7f 100644
--- a/CoAP.NET/Channel/DataReceivedEventArgs.cs
+++ b/CoAP.NET/Channel/DataReceivedEventArgs.cs
@@ -20,11 +20,12 @@ public class DataReceivedEventArgs : EventArgs
{
///
///
- public DataReceivedEventArgs(Byte[] data, System.Net.EndPoint endPoint, ISession session)
+ public DataReceivedEventArgs(Byte[] data, System.Net.EndPoint endPoint, System.Net.EndPoint endPointLocal, ISession session)
{
Data = data;
EndPoint = endPoint;
Session = session;
+ LocalEndPoint = endPointLocal;
}
///
@@ -37,6 +38,11 @@ public DataReceivedEventArgs(Byte[] data, System.Net.EndPoint endPoint, ISession
///
public System.Net.EndPoint EndPoint { get; }
+ ///
+ /// Get the where the data was received.
+ ///
+ public System.Net.EndPoint LocalEndPoint { get; }
+
///
/// Gets the communication session for the message.
///
diff --git a/CoAP.NET/Channel/IChannel.cs b/CoAP.NET/Channel/IChannel.cs
index b1ea711..0c51e70 100644
--- a/CoAP.NET/Channel/IChannel.cs
+++ b/CoAP.NET/Channel/IChannel.cs
@@ -10,6 +10,7 @@
*/
using System;
+using System.Net;
namespace Com.AugustCellars.CoAP.Channel
{
@@ -26,6 +27,14 @@ public interface IChannel : IDisposable
/// Occurs when some bytes are received in this channel.
///
event EventHandler DataReceived;
+#if !NETSTANDARD1_3
+ ///
+ /// Add a multicast address to the channel
+ ///
+ /// address to add
+ /// true if added
+ bool AddMulticastAddress(IPEndPoint ep);
+#endif
///
/// Starts this channel.
///
diff --git a/CoAP.NET/Channel/UDPChannel.cs b/CoAP.NET/Channel/UDPChannel.cs
index 26a8a5e..e3b99cd 100644
--- a/CoAP.NET/Channel/UDPChannel.cs
+++ b/CoAP.NET/Channel/UDPChannel.cs
@@ -11,7 +11,10 @@
using System;
using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq.Expressions;
using System.Net;
+using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using Com.AugustCellars.CoAP.Log;
@@ -28,10 +31,11 @@ public partial class UDPChannel : IChannel, ISession
///
public const Int32 DefaultReceivePacketSize = 4096;
- private Int32 _port;
- private System.Net.EndPoint _localEP;
- private UDPSocket _socket;
- private UDPSocket _socketBackup;
+ private readonly Int32 _port;
+// private readonly System.Net.EndPoint _localEP;
+ private SocketSet _unicast = new SocketSet();
+// private UDPSocket _socket;
+// private UDPSocket _socketBackup;
private Int32 _running;
private Int32 _writing;
private readonly ConcurrentQueue _sendingQueue = new ConcurrentQueue();
@@ -64,7 +68,7 @@ public UDPChannel(Int32 port)
///
public UDPChannel(System.Net.EndPoint localEP)
{
- _localEP = localEP;
+ _unicast._localEP = (IPEndPoint) localEP;
}
///
@@ -72,9 +76,9 @@ public System.Net.EndPoint LocalEndPoint
{
get
{
- return _socket == null
- ? (_localEP ?? new IPEndPoint(IPAddress.IPv6Any, _port))
- : _socket.Socket.LocalEndPoint;
+ return _unicast._socket == null
+ ? (_unicast._localEP ?? new IPEndPoint(IPAddress.IPv6Any, _port))
+ : _unicast._socket.Socket.LocalEndPoint;
}
}
@@ -104,6 +108,23 @@ public System.Net.EndPoint LocalEndPoint
///
public int MaxSendSize { get; set; }
+#if !NETSTANDARD1_3
+ private readonly List _listMultiCastEndpoints = new List();
+
+ ///
+ public bool AddMulticastAddress(IPEndPoint ep) // IPAddress ep, int port)
+ {
+ SocketSet s = new SocketSet() {
+ _localEP = ep
+ };
+ _listMultiCastEndpoints.Add(s);
+ if (_running == 1) {
+
+ }
+ return true;
+ }
+#endif
+
///
public void Start()
{
@@ -114,68 +135,185 @@ public void Start()
#if LOG_UDP_CHANNEL
_Log.Debug("Start");
#endif
+ try {
+
+ StartSocket(_unicast);
+
+#if !NETSTANDARD1_3
+ foreach (SocketSet s in _listMultiCastEndpoints) {
+ StartMulticastSocket(s);
+ }
+#endif
+ }
+ catch (Exception) {
+ _running = 0;
+ throw;
+ }
+ }
- if (_localEP == null) {
+ private void StartSocket(SocketSet info)
+ {
+ if (info._localEP == null) {
try {
- _socket = SetupUDPSocket(AddressFamily.InterNetworkV6, ReceivePacketSize + 1); // +1 to check for > ReceivePacketSize
+ info._socket = SetupUDPSocket(AddressFamily.InterNetworkV6, ReceivePacketSize + 1); // +1 to check for > ReceivePacketSize
}
catch (SocketException e) {
if (e.SocketErrorCode == SocketError.AddressFamilyNotSupported) {
- _socket = null;
+ info._socket = null;
}
else {
throw;
}
}
- if (_socket == null) {
+ if (info._socket == null) {
// IPv6 is not supported, use IPv4 instead
- _socket = SetupUDPSocket(AddressFamily.InterNetwork, ReceivePacketSize + 1);
- _socket.Socket.Bind(new IPEndPoint(IPAddress.Any, _port));
+ info._socket = SetupUDPSocket(AddressFamily.InterNetwork, ReceivePacketSize + 1);
+ info._socket.Socket.Bind(new IPEndPoint(IPAddress.Any, _port));
}
else {
try {
// Enable IPv4-mapped IPv6 addresses to accept both IPv6 and IPv4 connections in a same socket.
- _socket.Socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, 0);
+ info._socket.Socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, 0);
}
catch {
#if LOG_UDP_CHANNEL
_Log.Debug("Create backup socket");
#endif
// IPv4-mapped address seems not to be supported, set up a separated socket of IPv4.
- _socketBackup = SetupUDPSocket(AddressFamily.InterNetwork, ReceivePacketSize + 1);
+ info._socketBackup = SetupUDPSocket(AddressFamily.InterNetwork, ReceivePacketSize + 1);
}
- _socket.Socket.Bind(new IPEndPoint(IPAddress.IPv6Any, _port));
- if (_socketBackup != null) {
- _socketBackup.Socket.Bind(new IPEndPoint(IPAddress.Any, _port));
+ info._socket.Socket.Bind(new IPEndPoint(IPAddress.IPv6Any, _port));
+ if (info._socketBackup != null) {
+ info._socketBackup.Socket.Bind(new IPEndPoint(IPAddress.Any, _port));
}
}
}
else {
- _socket = SetupUDPSocket(_localEP.AddressFamily, ReceivePacketSize + 1);
- _socket.Socket.Bind(_localEP);
+ info._socket = SetupUDPSocket(info._localEP.AddressFamily, ReceivePacketSize + 1);
+
+ info._socket.Socket.Bind(info._localEP);
}
+
if (ReceiveBufferSize > 0) {
- _socket.Socket.ReceiveBufferSize = ReceiveBufferSize;
- if (_socketBackup != null) {
- _socketBackup.Socket.ReceiveBufferSize = ReceiveBufferSize;
+ info._socket.Socket.ReceiveBufferSize = ReceiveBufferSize;
+ if (info._socketBackup != null) {
+ info._socketBackup.Socket.ReceiveBufferSize = ReceiveBufferSize;
}
}
if (SendBufferSize > 0) {
- _socket.Socket.SendBufferSize = SendBufferSize;
- if (_socketBackup != null) {
- _socketBackup.Socket.SendBufferSize = SendBufferSize;
+ info._socket.Socket.SendBufferSize = SendBufferSize;
+ if (info._socketBackup != null) {
+ info._socketBackup.Socket.SendBufferSize = SendBufferSize;
}
}
- BeginReceive();
+ BeginReceive(info);
}
- ///
- public void Stop()
+#if !NETSTANDARD1_3
+ private void StartMulticastSocket(SocketSet info)
+ {
+ if (info._localEP.Address.IsIPv6Multicast) {
+ try {
+ info._socket = SetupUDPSocket(AddressFamily.InterNetworkV6, ReceivePacketSize + 1);
+ info._socket.Socket.Bind(new IPEndPoint(IPAddress.IPv6Any, _port));
+
+ NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
+ foreach (NetworkInterface adapter in nics) {
+ if (!adapter.SupportsMulticast) continue;
+ if (adapter.OperationalStatus != OperationalStatus.Up) continue;
+ if (adapter.Supports(NetworkInterfaceComponent.IPv6)) {
+ IPInterfaceProperties properties = adapter.GetIPProperties();
+
+ foreach (UnicastIPAddressInformation ip in adapter.GetIPProperties().UnicastAddresses) {
+ if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6) {
+ IPv6InterfaceProperties v6ip = adapter.GetIPProperties().GetIPv6Properties();
+ IPv6MulticastOption mc = new IPv6MulticastOption(info._localEP.Address, v6ip.Index);
+ try {
+ info._socket.Socket.SetSocketOption(SocketOptionLevel.IPv6,
+ SocketOptionName.AddMembership,
+ mc);
+ }
+ catch (SocketException e) {
+#if LOG_UDP_CHANNEL
+ _Log.Info(
+ m => m(
+ $"Start Multicast: Address {info._localEP.Address} had an exception ${e.ToString()}"));
+#endif
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+ catch (SocketException e) {
+#if LOG_UDP_CHANNEL
+ _Log.Info(
+ m => m($"Start Multicast: Address {info._localEP.Address} had an exception ${e.ToString()}"));
+ throw;
+#endif
+ }
+ }
+ else {
+ try {
+ info._socket = SetupUDPSocket(AddressFamily.InterNetwork, ReceivePacketSize + 1);
+ info._socket.Socket.Bind(new IPEndPoint(IPAddress.Any, _port));
+
+ NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
+
+
+ foreach (NetworkInterface adapter in nics) {
+ if (!adapter.SupportsMulticast) continue;
+ if (adapter.OperationalStatus != OperationalStatus.Up) continue;
+ if (adapter.Supports(NetworkInterfaceComponent.IPv4)) {
+ IPInterfaceProperties properties = adapter.GetIPProperties();
+
+ foreach (UnicastIPAddressInformation ip in adapter.GetIPProperties().UnicastAddresses) {
+ if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) {
+ MulticastOption mc = new MulticastOption(info._localEP.Address, ip.Address);
+ info._socket.Socket.SetSocketOption(SocketOptionLevel.IP,
+ SocketOptionName.AddMembership,
+ mc);
+ }
+ }
+ }
+ }
+ }
+ catch (SocketException e) {
+#if LOG_UDP_CHANNEL
+ _Log.Info(m => m($"Start Multicast: Address {info._localEP.Address} had an exception ${e.ToString()}"));
+#endif
+ throw;
+ }
+ }
+
+
+ if (ReceiveBufferSize > 0) {
+ info._socket.Socket.ReceiveBufferSize = ReceiveBufferSize;
+ if (info._socketBackup != null) {
+ info._socketBackup.Socket.ReceiveBufferSize = ReceiveBufferSize;
+ }
+ }
+
+ if (SendBufferSize > 0) {
+ info._socket.Socket.SendBufferSize = SendBufferSize;
+ if (info._socketBackup != null) {
+ info._socketBackup.Socket.SendBufferSize = SendBufferSize;
+ }
+ }
+
+ BeginReceive(info);
+ }
+#endif
+
+ ///
+ public void Stop()
{
#if LOG_UDP_CHANNEL
_Log.Debug("Stop");
@@ -184,14 +322,14 @@ public void Stop()
return;
}
- if (_socket != null) {
- _socket.Dispose();
- _socket = null;
+ if (_unicast._socket != null) {
+ _unicast._socket.Dispose();
+ _unicast._socket = null;
}
- if (_socketBackup != null) {
- _socketBackup.Dispose();
- _socketBackup = null;
+ if (_unicast._socketBackup != null) {
+ _unicast._socketBackup.Dispose();
+ _unicast._socketBackup = null;
}
}
@@ -242,16 +380,16 @@ public ISession GetSession(System.Net.EndPoint ep)
return this;
}
- private void BeginReceive()
+ private void BeginReceive(SocketSet info)
{
#if LOG_UDP_CHANNEL
_Log.Debug(m => m("BeginRecieve: _running={0}", _running));
#endif
if (_running > 0) {
- BeginReceive(_socket);
+ BeginReceive(info._socket);
- if (_socketBackup != null) {
- BeginReceive(_socketBackup);
+ if (info._socketBackup != null) {
+ BeginReceive(info._socketBackup);
}
}
}
@@ -273,7 +411,15 @@ private void EndReceive(UDPSocket socket, Byte[] buffer, Int32 offset, Int32 cou
}
}
- FireDataReceived(bytes, ep);
+ System.Net.EndPoint epLocal = socket.Socket.LocalEndPoint;
+ if (ep.AddressFamily == AddressFamily.InterNetworkV6) {
+ IPEndPoint ipLocal = (IPEndPoint) ep;
+ if (IPAddressExtensions.IsIPv4MappedToIPv6(ipLocal.Address)) {
+ epLocal = new IPEndPoint(IPAddressExtensions.MapToIPv4(ipLocal.Address), ipLocal.Port);
+ }
+ }
+
+ FireDataReceived(bytes, ep, epLocal);
}
#if LOG_UDP_CHANNEL
@@ -290,14 +436,14 @@ private void EndReceive(UDPSocket socket, Exception ex)
BeginReceive(socket);
}
- private void FireDataReceived(Byte[] data, System.Net.EndPoint ep)
+ private void FireDataReceived(Byte[] data, System.Net.EndPoint ep, System.Net.EndPoint epLocal)
{
#if LOG_UDP_CHANNEL
_Log.Debug(m => m("FireDataReceived: data length={0}", data.Length));
#endif
EventHandler h = DataReceived;
if (h != null) {
- h(this, new DataReceivedEventArgs(data, ep, this));
+ h(this, new DataReceivedEventArgs(data, ep, epLocal, this));
}
}
@@ -313,15 +459,15 @@ private void BeginSend()
return;
}
- UDPSocket socket = _socket;
+ UDPSocket socket = _unicast._socket;
IPEndPoint remoteEP = (IPEndPoint)raw.EndPoint;
if (remoteEP.AddressFamily == AddressFamily.InterNetwork) {
- if (_socketBackup != null) {
+ if (_unicast._socketBackup != null) {
// use the separated socket of IPv4 to deal with IPv4 conversions.
- socket = _socketBackup;
+ socket = _unicast._socketBackup;
}
- else if (_socket.Socket.AddressFamily == AddressFamily.InterNetworkV6) {
+ else if (_unicast._socket.Socket.AddressFamily == AddressFamily.InterNetworkV6) {
remoteEP = new IPEndPoint(IPAddressExtensions.MapToIPv6(remoteEP.Address), remoteEP.Port);
}
}
@@ -384,6 +530,13 @@ class RawData
public System.Net.EndPoint EndPoint;
}
+ class SocketSet
+ {
+ public IPEndPoint _localEP;
+ public UDPSocket _socket;
+ public UDPSocket _socketBackup;
+ }
+
///
public event EventHandler SessionEvent;
diff --git a/CoAP.NET/CoAP.Std10.csproj b/CoAP.NET/CoAP.Std10.csproj
index 9d078dc..8b2ed2b 100644
--- a/CoAP.NET/CoAP.Std10.csproj
+++ b/CoAP.NET/CoAP.Std10.csproj
@@ -4,11 +4,11 @@
- netstandard1.3;net462
+ netcoreapp2.0;net462;netstandard2.0
Com.AugustCellars.CoAP
Com.AugustCellars.CoAP
- 1.2.0.0
- 1.2.0.0
+ 1.3.0.0
+ 1.3.0.0
Jim Schaad
An implementation of the CBOR Object Signing and Encryption standards.
false
@@ -19,6 +19,10 @@
This project is built on the CoAP.NET project of smeshlink which in turn is based on Californium. As this project did not seem to be maintained any more and I wanted a version in order to test the newer items that are coming out of the IETF CORE working group, I have captured it and started exanding it.
This project is NOT intended to be used for commercial purposes. It is intented only for research and verification work.
+1.3
+ - Remove NetStandard 1.3 in favor of 2.0 and add NetCore 2.0
+ - Put in multcast support
+ - Primative version of CoRAL
1.2.0.0
- Update net 4.5 file to use latest CBOR
- Switch to use NetStandard 1.3 rather than Net 4.5
@@ -76,7 +80,7 @@ This project is NOT intended to be used for commercial purposes. It is intented
- TRACE;INCLUDE_OSCOAP;OSCOAP_COMPRESS;LOG_UDP_CHANNEL
+ TRACE;INCLUDE_OSCOAP;OSCOAP_COMPRESS
@@ -87,15 +91,15 @@ This project is NOT intended to be used for commercial purposes. It is intented
AugustCellarsStrongKey.snk
true
- obj\Deploy
- bin\Deploy\
+ obj\std10\Deploy
+ bin\std10\Deploy\
DEBUG
$(DefineConstants);DEBUG
- obj\Debug
- bin\Debug\
+ obj\std10\Debug
+ bin\std10\Debug\
@@ -116,6 +120,12 @@ This project is NOT intended to be used for commercial purposes. It is intented
+
+
+
+
+
+
@@ -130,6 +140,7 @@ This project is NOT intended to be used for commercial purposes. It is intented
+
@@ -205,6 +216,7 @@ This project is NOT intended to be used for commercial purposes. It is intented
+
@@ -214,7 +226,7 @@ This project is NOT intended to be used for commercial purposes. It is intented
-
+
diff --git a/CoAP.NET/CoapClient.cs b/CoAP.NET/CoapClient.cs
index ff1bab1..0703eec 100644
--- a/CoAP.NET/CoapClient.cs
+++ b/CoAP.NET/CoapClient.cs
@@ -132,7 +132,14 @@ public CoapClient(Uri uri, ICoapConfig config)
///
/// OSCOAP context to use for the message
///
- public OSCOAP.SecurityContext OscoapContext { get; set; }
+ [ObsoleteAttribute("Use OscoreContext instead")]
+ public OSCOAP.SecurityContext OscoapContext
+ {
+ get => OscoreContext;
+ set => OscoreContext = value;
+ }
+
+ public OSCOAP.SecurityContext OscoreContext { get; set; }
///
/// Let the client use Confirmable requests.
@@ -253,11 +260,13 @@ public IEnumerable Discover(string uriPath, String query, int mediaType
case MediaType.ApplicationLinkFormat:
return LinkFormat.Parse(links.PayloadString);
+#if false
case MediaType.ApplicationLinkFormatCbor:
return LinkFormat.ParseCbor(links.Payload);
case MediaType.ApplicationLinkFormatJson:
return LinkFormat.ParseJson(links.PayloadString);
+#endif
default:
return _EmptyLinks;
@@ -650,7 +659,7 @@ protected Request Prepare(Request request, IEndPoint endpoint)
{
request.Type = _type;
request.URI = Uri;
- request.OscoapContext = OscoapContext;
+ request.OscoreContext = OscoreContext;
if (UriPath != null) {
request.UriPath = UriPath;
diff --git a/CoAP.NET/Coral/CoralBase.cs b/CoAP.NET/Coral/CoralBase.cs
new file mode 100644
index 0000000..7f18c0a
--- /dev/null
+++ b/CoAP.NET/Coral/CoralBase.cs
@@ -0,0 +1,30 @@
+using PeterO.Cbor;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Com.AugustCellars.CoAP.Util;
+
+namespace Com.AugustCellars.CoAP.Coral
+{
+ public class CoralBase : CoralItem
+ {
+ public Uri Uri { get; }
+ public CoralBase(Uri baseUri)
+ {
+ Uri = baseUri;
+ }
+
+ public override CBORObject EncodeToCBORObject(CoralDictionary dictionary)
+ {
+ CBORObject node = CBORObject.NewArray();
+
+ node.Add(1);
+ node.Add(Ciri.ToCbor(Uri));
+
+ return node;
+ }
+
+ }
+}
diff --git a/CoAP.NET/Coral/CoralBody.cs b/CoAP.NET/Coral/CoralBody.cs
new file mode 100644
index 0000000..60a3b1d
--- /dev/null
+++ b/CoAP.NET/Coral/CoralBody.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel.Design.Serialization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using PeterO.Cbor;
+
+namespace Com.AugustCellars.CoAP.Coral
+{
+ public class CoralBody : IEnumerable
+ {
+ private readonly List _items = new List();
+
+ public CoralBody()
+ {
+ }
+
+
+ public CoralBody(CBORObject node, CoralDictionary dictionary)
+ {
+ if (node.Type != CBORType.Array) {
+ throw new ArgumentException("Invalid node type");
+ }
+
+ foreach (CBORObject child in node.Values) {
+ if (node.Type != CBORType.Array)
+ {
+ throw new ArgumentException("Invalid node type");
+ }
+
+ switch (node[0].AsInt32()) {
+ case 1:
+ _items.Add(new CoralLink(node, dictionary));
+ break;
+
+ default:
+ throw new ArgumentException("Unrecognized CoRAL node type");
+ }
+ }
+ }
+
+ public int Length => _items.Count;
+
+ public CoralBody Add(CoralItem item)
+ {
+ _items.Add(item);
+ return this;
+ }
+
+ static CoralBody DecodeFromBytes(byte[] encoded, CoralDictionary dictionary = null)
+ {
+ CBORObject obj = CBORObject.DecodeFromBytes(encoded);
+ if (dictionary == null) {
+ dictionary = CoralDictionary.Default;
+ }
+ return new CoralBody(obj, dictionary);
+ }
+
+ public byte[] EncodeToBytes(CoralDictionary dictionary = null)
+ {
+ return EncodeToCBORObject(dictionary).EncodeToBytes();
+ }
+
+ public CBORObject EncodeToCBORObject(CoralDictionary dictionary = null)
+ {
+ CBORObject root = CBORObject.NewArray();
+ if (dictionary == null) {
+ dictionary = CoralDictionary.Default;
+ }
+
+ foreach (CoralItem item in _items) {
+ root.Add(item.EncodeToCBORObject(dictionary));
+ }
+
+ return root;
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return _items.GetEnumerator();
+ }
+ }
+}
diff --git a/CoAP.NET/Coral/CoralDictionary.cs b/CoAP.NET/Coral/CoralDictionary.cs
new file mode 100644
index 0000000..3f02d43
--- /dev/null
+++ b/CoAP.NET/Coral/CoralDictionary.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Org.BouncyCastle.Asn1.Crmf;
+using PeterO.Cbor;
+
+namespace Com.AugustCellars.CoAP.Coral
+{
+ public class CoralDictionary : System.Collections.IEnumerable
+ {
+ public const int DictionaryTag = 99999;
+
+ public static CoralDictionary Default = new CoralDictionary() {
+ {0, "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"},
+ {1, "http://www.iana.org/assignments/relation/item>"},
+ {2, "http://www.iana.org/assignments/relation/collection"},
+ {3, "http://coreapps.org/collections#create"},
+ {4, "http://coreapps.org/base#update"},
+ {5, "http://coreapps.org/collections#delete"},
+ {6, "http://coreapps.org/base#search"},
+ {7, "http://coreapps.org/coap#accept"},
+ {8, "http://coreapps.org/coap#type"},
+ {9, "http://coreapps.org/base#lang"},
+ {10, "http://coreapps.org/coap#method"}
+ };
+
+ private readonly Dictionary _dictionary = new Dictionary();
+
+ public CoralDictionary()
+ {
+ }
+
+ public CoralDictionary Add(int key, string value)
+ {
+ _dictionary.Add(key, value);
+ return this;
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return ((IEnumerable) _dictionary).GetEnumerator();
+ }
+
+ public CBORObject Lookup(CBORObject value)
+ {
+ foreach (KeyValuePair o in _dictionary) {
+ if (value.Equals(CBORObject.FromObject(o.Value))) {
+ CBORObject newValue = CBORObject.FromObject(o.Key);
+ if (value.Type == CBORType.Number) {
+ newValue = CBORObject.FromObjectAndTag(newValue, DictionaryTag);
+ }
+
+ return newValue;
+ }
+ }
+
+ return value;
+ }
+
+ public CBORObject Lookup(string value)
+ {
+ foreach (KeyValuePair o in _dictionary) {
+ if (value.Equals(o.Value)) {
+ return CBORObject.FromObject(o.Key);
+ }
+ }
+
+ return CBORObject.FromObject(value);
+ }
+
+ public CBORObject Reverse(CBORObject value)
+ {
+ if (value.Type != CBORType.Number) {
+ return value;
+ }
+
+ if (value.HasTag(DictionaryTag) && value.MostOuterTag.ToInt32Unchecked() == DictionaryTag) {
+ return value.UntagOne();
+ }
+
+ if (!_dictionary.ContainsKey(value.AsInt32())) {
+ return value;
+ }
+
+ CBORObject result = CBORObject.FromObject(_dictionary[value.AsInt32()]);
+
+ if (result.Type == CBORType.Number) {
+ return value;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/CoAP.NET/Coral/CoralForm.cs b/CoAP.NET/Coral/CoralForm.cs
new file mode 100644
index 0000000..420d57f
--- /dev/null
+++ b/CoAP.NET/Coral/CoralForm.cs
@@ -0,0 +1,42 @@
+using PeterO.Cbor;
+
+namespace Com.AugustCellars.CoAP.Coral
+{
+ public class CoralForm : CoralItem
+ {
+ private readonly string _operationType;
+ private readonly string _target;
+ ///
+ /// Child body
+ ///
+ public CoralBody Body { get; } = new CoralBody();
+
+ public CoralForm(string formRef, string target)
+ {
+ _operationType = formRef;
+ _target = target;
+ }
+
+ public CoralForm(string formRef, string target, CoralBody body)
+ {
+ _operationType = formRef;
+ _target = target;
+ Body = body;
+ }
+
+ public override CBORObject EncodeToCBORObject(CoralDictionary dictionary)
+ {
+ CBORObject node = CBORObject.NewArray();
+
+ node.Add(3);
+ node.Add(dictionary.Lookup(_operationType));
+ node.Add(dictionary.Lookup(_target));
+ if (Body.Length > 0)
+ {
+ node.Add(Body.EncodeToCBORObject(dictionary));
+ }
+
+ return node;
+ }
+ }
+}
diff --git a/CoAP.NET/Coral/CoralItem.cs b/CoAP.NET/Coral/CoralItem.cs
new file mode 100644
index 0000000..c87a742
--- /dev/null
+++ b/CoAP.NET/Coral/CoralItem.cs
@@ -0,0 +1,15 @@
+using PeterO.Cbor;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Com.AugustCellars.CoAP.Coral
+{
+ public abstract class CoralItem
+ {
+ public abstract CBORObject EncodeToCBORObject(CoralDictionary dictionary);
+ }
+
+}
diff --git a/CoAP.NET/Coral/CoralLink.cs b/CoAP.NET/Coral/CoralLink.cs
new file mode 100644
index 0000000..90c9c83
--- /dev/null
+++ b/CoAP.NET/Coral/CoralLink.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using Org.BouncyCastle.Asn1.X509;
+using PeterO.Cbor;
+
+namespace Com.AugustCellars.CoAP.Coral
+{
+ public class CoralLink : CoralItem
+ {
+ ///
+ /// Link relation Type - a text string conforming to IRI syntax
+ ///
+ public string RelationType { get; }
+ ///
+ /// Target of the link
+ ///
+ public CBORObject Target { get; set; }
+ ///
+ /// Child body
+ ///
+ public CoralBody Body { get; }
+
+ public CoralLink(string relation, CBORObject target)
+ {
+ RelationType = relation;
+ Target = target;
+ }
+
+ public CoralLink(string relation, CBORObject target, CoralBody body)
+ {
+ RelationType = relation;
+ Target = target;
+ Body = body;
+ }
+
+ public CoralLink(CBORObject node, CoralDictionary dictionary)
+ {
+ if (node[0].AsInt32() != 2) {
+ throw new ArgumentException("Not an encoded CoRAL link");
+ }
+
+ RelationType = dictionary.Reverse(node[1]).AsString();
+ Target = dictionary.Reverse(node[2]);
+ if (node.Count == 4) {
+ Body = new CoralBody(node[3], dictionary);
+ }
+ }
+
+ public override CBORObject EncodeToCBORObject(CoralDictionary dictionary)
+ {
+ CBORObject node = CBORObject.NewArray();
+
+ node.Add(2);
+ node.Add(dictionary.Lookup(RelationType));
+ node.Add(dictionary.Lookup(Target));
+ if (Body != null) {
+ node.Add(Body.EncodeToCBORObject(dictionary));
+ }
+
+ return node;
+ }
+ }
+}
diff --git a/CoAP.NET/DTLS/DTLSChannel.cs b/CoAP.NET/DTLS/DTLSChannel.cs
index f46e94e..221cbf2 100644
--- a/CoAP.NET/DTLS/DTLSChannel.cs
+++ b/CoAP.NET/DTLS/DTLSChannel.cs
@@ -25,19 +25,20 @@ public class DTLSChannel : IChannel
private Int32 _receivePacketSize;
private readonly int _port;
private UDPChannel _udpChannel;
- private KeySet _serverKeys;
+ private TlsKeyPairSet _serverKeys;
private KeySet _userKeys;
-
+ private KeySet _cwtTrustRoots;
- public DTLSChannel(KeySet serverKeys, KeySet userKeys) : this(serverKeys, userKeys, 0)
+ public DTLSChannel(TlsKeyPairSet serverKeys, KeySet userKeys) : this(serverKeys, userKeys, 0)
{
}
- public DTLSChannel(KeySet serverKeys, KeySet userKeys, Int32 port)
+ public DTLSChannel(TlsKeyPairSet serverKeys, KeySet userKeys, Int32 port, KeySet cwtTrustRoots = null)
{
_port = port;
_userKeys = userKeys;
_serverKeys = serverKeys;
+ _cwtTrustRoots = cwtTrustRoots;
}
///
@@ -46,7 +47,7 @@ public DTLSChannel(KeySet serverKeys, KeySet userKeys, Int32 port)
///
///
///
- public DTLSChannel(KeySet serverKeys, KeySet userKeys, System.Net.EndPoint ep)
+ public DTLSChannel(TlsKeyPairSet serverKeys, KeySet userKeys, System.Net.EndPoint ep)
{
_localEP = ep;
_userKeys = userKeys;
@@ -89,6 +90,12 @@ public Int32 ReceivePacketSize {
public EventHandler TlsEventHandler;
+ ///
+ public bool AddMulticastAddress(IPEndPoint ep)
+ {
+ return false;
+ }
+
public void Start()
{
if (System.Threading.Interlocked.CompareExchange(ref _running, 1, 0) > 0) {
@@ -165,7 +172,7 @@ public ISession GetSession(System.Net.EndPoint ep)
// No session - create a new one.
- session = new DTLSSession(ipEndPoint, DataReceived, _serverKeys, _userKeys);
+ session = new DTLSSession(ipEndPoint, DataReceived, _serverKeys, _userKeys, _cwtTrustRoots);
AddSession(session);
session.TlsEventHandler += MyTlsEventHandler;
@@ -195,7 +202,7 @@ public void Send(byte[] data, ISession sessionReceive, System.Net.EndPoint ep)
DTLSSession session = FindSession(ipEP);
if (session == null) {
- session = new DTLSSession(ipEP, DataReceived, _serverKeys, _userKeys);
+ session = new DTLSSession(ipEP, DataReceived, _serverKeys, _userKeys, _cwtTrustRoots);
session.TlsEventHandler += MyTlsEventHandler;
AddSession(session);
session.Connect(_udpChannel);
@@ -223,7 +230,7 @@ private void ReceiveData(Object sender, DataReceivedEventArgs e)
}
}
- DTLSSession sessionNew = new DTLSSession((IPEndPoint) e.EndPoint, DataReceived, _serverKeys, _userKeys);
+ DTLSSession sessionNew = new DTLSSession((IPEndPoint) e.EndPoint, DataReceived, _serverKeys, _userKeys, _cwtTrustRoots);
sessionNew.TlsEventHandler = MyTlsEventHandler;
_sessionList.Add(sessionNew);
new Thread(() => Accept(sessionNew, e.Data)).Start();
diff --git a/CoAP.NET/DTLS/DTLSClient.cs b/CoAP.NET/DTLS/DTLSClient.cs
index 46744e5..153a933 100644
--- a/CoAP.NET/DTLS/DTLSClient.cs
+++ b/CoAP.NET/DTLS/DTLSClient.cs
@@ -1,8 +1,11 @@
using System;
using System.Collections;
-
+using System.IO;
using System.Threading.Tasks;
using Com.AugustCellars.COSE;
+#if SUPPORT_TLS_CWT
+using Com.AugustCellars.WebToken;
+#endif
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Crypto.Tls;
@@ -28,6 +31,7 @@ class DtlsClient : DefaultTlsClient
private TlsSession _mSession;
public EventHandler TlsEventHandler;
public OneKey _rawPublicKey;
+ private TlsKeyPair _tlsKeyPair;
internal DtlsClient(TlsSession session, TlsPskIdentity pskIdentity)
{
@@ -41,6 +45,16 @@ internal DtlsClient(TlsSession session, OneKey userKey)
_rawPublicKey = userKey;
}
+#if SUPPORT_TLS_CWT
+ public KeySet CwtTrustKeySet { get; set; }
+ internal DtlsClient(TlsSession session, TlsKeyPair tlsKey, KeySet cwtTrustKeys)
+ {
+ _mSession = session;
+ _tlsKeyPair = tlsKey;
+ CwtTrustKeySet = cwtTrustKeys;
+ }
+#endif
+
private void OnTlsEvent(Object o, TlsEvent e)
{
EventHandler handler = TlsEventHandler;
@@ -63,6 +77,14 @@ public override int[] GetCipherSuites()
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8
};
}
+#if SUPPORT_TLS_CWT
+ else if (_tlsKeyPair != null && _tlsKeyPair.CertType == CertificateType.CwtPublicKey) {
+ i = new int[] {
+ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
+ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8
+ };
+ }
+#endif
else {
i = new int[] {
CipherSuite.TLS_PSK_WITH_AES_128_CCM_8,
@@ -81,6 +103,33 @@ public override int[] GetCipherSuites()
return e.IntValues;
}
+#if SUPPORT_TLS_CWT
+ public override AbstractCertificate ParseServerCertificate(short certificateType, Stream io)
+ {
+ switch (certificateType) {
+ case CertificateType.CwtPublicKey:
+ try {
+ CwtPublicKey cwtPub = CwtPublicKey.Parse(io);
+
+ CWT cwtServer = CWT.Decode(cwtPub.EncodedCwt(), CwtTrustKeySet, CwtTrustKeySet);
+
+ AsymmetricKeyParameter pubKey = cwtServer.Cnf.Key.AsPublicKey();
+
+ SubjectPublicKeyInfo spi = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubKey);
+ cwtPub.SetSubjectPublicKeyInfo(spi);
+
+ return cwtPub;
+ }
+ catch {
+ return null;
+ }
+
+ default:
+ return null;
+ }
+
+ }
+#endif
public override IDictionary GetClientExtensions()
{
@@ -93,14 +142,21 @@ public override IDictionary GetClientExtensions()
/*
* NOTE: If you are copying test code, do not blindly set these extensions in your own client.
*/
- // TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
- // TlsExtensionsUtilities.AddPaddingExtension(clientExtensions, mContext.SecureRandom.Next(16));
- // TlsExtensionsUtilities.AddTruncatedHMacExtension(clientExtensions);
+ // TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
+ // TlsExtensionsUtilities.AddPaddingExtension(clientExtensions, mContext.SecureRandom.Next(16));
+ // TlsExtensionsUtilities.AddTruncatedHMacExtension(clientExtensions);
if (_rawPublicKey != null) {
- TlsExtensionsUtilities.AddClientCertificateTypeExtensionClient(clientExtensions, new byte[]{2});
- TlsExtensionsUtilities.AddServerCertificateTypeExtensionClient(clientExtensions, new byte[]{2});
+ TlsExtensionsUtilities.AddClientCertificateTypeExtensionClient(clientExtensions, new byte[] {2});
+ TlsExtensionsUtilities.AddServerCertificateTypeExtensionClient(clientExtensions, new byte[] {2});
+ }
+
+#if SUPPORT_TLS_CWT
+ if (_tlsKeyPair != null && _tlsKeyPair.CertType == CertificateType.CwtPublicKey) {
+ TlsExtensionsUtilities.AddClientCertificateTypeExtensionClient(clientExtensions, new byte[] {254});
+ TlsExtensionsUtilities.AddServerCertificateTypeExtensionClient(clientExtensions, new byte[] {254});
}
+#endif
}
TlsEvent e = new TlsEvent(TlsEvent.EventCode.GetExtensions) {
@@ -118,9 +174,20 @@ public override IDictionary GetClientExtensions()
public override TlsAuthentication GetAuthentication()
{
- MyTlsAuthentication auth = new MyTlsAuthentication(mContext, _rawPublicKey);
- auth.TlsEventHandler += MyTlsEventHandler;
- return auth;
+ if (_rawPublicKey != null) {
+ MyTlsAuthentication auth = new MyTlsAuthentication(mContext, _rawPublicKey);
+ auth.TlsEventHandler += MyTlsEventHandler;
+ return auth;
+ }
+#if SUPPORT_TLS_CWT
+ else if (_tlsKeyPair != null && _tlsKeyPair.CertType == CertificateType.CwtPublicKey) {
+ MyTlsAuthentication auth = new MyTlsAuthentication(mContext, _tlsKeyPair, CwtTrustKeySet);
+ auth.TlsEventHandler += MyTlsEventHandler;
+ return auth;
+ }
+#endif
+
+ throw new Exception("ICE");
}
private void MyTlsEventHandler(object sender, TlsEvent tlsEvent)
@@ -160,6 +227,7 @@ public override TlsKeyExchange GetKeyExchange()
}
}
+
private BigInteger ConvertBigNum(CBORObject cbor)
{
byte[] rgb = cbor.GetByteString();
@@ -193,6 +261,26 @@ protected TlsSignerCredentials GetECDsaSignerCredentials()
}
#endif
+#if SUPPORT_TLS_CWT
+ if (_tlsKeyPair != null && _tlsKeyPair.CertType == CertificateType.CwtPublicKey) {
+ OneKey k = _tlsKeyPair.PublicCwt.Cnf.Key;
+ if (k.HasKeyType((int)COSE.GeneralValuesInt.KeyType_EC2) &&
+ k.HasAlgorithm(COSE.AlgorithmValues.ECDSA_256)) {
+
+ X9ECParameters p = k.GetCurve();
+ ECDomainParameters parameters = new ECDomainParameters(p.Curve, p.G, p.N, p.H);
+ ECPrivateKeyParameters privKey = new ECPrivateKeyParameters("ECDSA", ConvertBigNum(k[CoseKeyParameterKeys.EC_D]), parameters);
+
+ ECPoint point = k.GetPoint();
+ ECPublicKeyParameters param = new ECPublicKeyParameters(point, parameters);
+
+ SubjectPublicKeyInfo spi = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(param);
+
+ return new DefaultTlsSignerCredentials(mContext, new CwtPublicKey(_tlsKeyPair.PublicCwt.EncodeToBytes()), privKey, new SignatureAndHashAlgorithm(HashAlgorithm.sha256, SignatureAlgorithm.ecdsa));
+ }
+ }
+#endif
+
TlsEvent e = new TlsEvent(TlsEvent.EventCode.SignCredentials) {
CipherSuite = KeyExchangeAlgorithm.ECDH_ECDSA
};
@@ -224,6 +312,10 @@ internal class MyTlsAuthentication
public EventHandler TlsEventHandler;
private OneKey _rawPublicKey;
private KeySet _serverKeys;
+#if SUPPORT_TLS_CWT
+ private TlsKeyPair TlsKey { get; set; }
+ public KeySet CwtTrustKeySet { get; set; }
+#endif
internal MyTlsAuthentication(TlsContext context, OneKey rawPublicKey)
{
@@ -231,15 +323,34 @@ internal MyTlsAuthentication(TlsContext context, OneKey rawPublicKey)
_rawPublicKey = rawPublicKey;
}
+#if SUPPORT_TLS_CWT
+ internal MyTlsAuthentication(TlsContext context, TlsKeyPair cwt, KeySet trustKeys)
+ {
+ this._mContext = context;
+ TlsKey = cwt;
+ CwtTrustKeySet = trustKeys;
+ }
+#endif
+
public OneKey AuthenticationKey { get; private set; }
-#if SUPPORT_RPK
+#if SUPPORT_RPK || SUPPORT_TLS_CWT
+
+ protected virtual AbstractCertificate ParseServerCertificate2(short serverCertificateType, Stream stm)
+ {
+ return null;
+ }
public virtual void NotifyServerCertificate(AbstractCertificate serverCertificate)
{
if (serverCertificate is RawPublicKey) {
GetRpkKey((RawPublicKey)serverCertificate);
}
+#if SUPPORT_TLS_CWT
+ else if (serverCertificate is CwtPublicKey) {
+ GetCwtKey((CwtPublicKey) serverCertificate);
+ }
+#endif
else {
TlsEvent e = new TlsEvent(TlsEvent.EventCode.ServerCertificate) {
Certificate = serverCertificate
@@ -301,6 +412,8 @@ public void GetRpkKey(RawPublicKey rpk)
if (!ev.Processed) {
// throw new TlsFatalAlert(AlertDescription.certificate_unknown);
}
+
+ AuthenticationKey = ev.KeyValue;
}
else {
// throw new TlsFatalAlert(AlertDescription.certificate_unknown);
@@ -310,6 +423,40 @@ public void GetRpkKey(RawPublicKey rpk)
#endif
+#if SUPPORT_TLS_CWT
+ public void GetCwtKey(CwtPublicKey rpk)
+ {
+ try {
+ CWT cwt = CWT.Decode(rpk.EncodedCwt(), CwtTrustKeySet, CwtTrustKeySet);
+
+ AsymmetricKeyParameter pub = cwt.Cnf.Key.AsPublicKey();
+ SubjectPublicKeyInfo spi = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pub);
+ rpk.SetSubjectPublicKeyInfo(spi);
+
+ AuthenticationKey = cwt.Cnf.Key;
+ return;
+ }
+ catch {
+ }
+
+ TlsEvent ev = new TlsEvent(TlsEvent.EventCode.ServerCertificate) {
+ Certificate = rpk
+ };
+
+ EventHandler handler = TlsEventHandler;
+ if (handler != null) {
+ handler(this, ev);
+ }
+
+ if (!ev.Processed) {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+
+ AuthenticationKey = ev.KeyValue;
+
+ }
+#endif
+
public virtual void NotifyServerCertificate(Certificate serverCertificate)
{
/*
@@ -382,7 +529,21 @@ public virtual TlsCredentials GetClientCredentials(CertificateRequest certificat
SignatureAlgorithm.rsa, "x509-client.pem", "x509-client-key.pem");
*/
#endif
- // If we did not fine appropriate signer credientials - ask for help
+#if SUPPORT_TLS_CWT
+
+ if (TlsKey.CertType == CertificateType.CwtPublicKey) {
+ OneKey k = TlsKey.PublicCwt.Cnf.Key;
+ if (k.HasKeyType((int)COSE.GeneralValuesInt.KeyType_EC2) &&
+ k.HasAlgorithm(COSE.AlgorithmValues.ECDSA_256)) {
+
+ AsymmetricKeyParameter privKey = TlsKey.PrivateKey.AsPrivateKey();
+
+ return new DefaultTlsSignerCredentials(_mContext, new CwtPublicKey(TlsKey.PublicCwt.EncodeToBytes()), privKey, new SignatureAndHashAlgorithm(HashAlgorithm.sha256, SignatureAlgorithm.ecdsa));
+ }
+ }
+#endif
+
+ // If we did not fine appropriate signer credentials - ask for help
TlsEvent e = new TlsEvent(TlsEvent.EventCode.SignCredentials) {
CipherSuite = KeyExchangeAlgorithm.ECDHE_ECDSA
diff --git a/CoAP.NET/DTLS/DTLSClientChannel.cs b/CoAP.NET/DTLS/DTLSClientChannel.cs
index 6e3c238..72597f4 100644
--- a/CoAP.NET/DTLS/DTLSClientChannel.cs
+++ b/CoAP.NET/DTLS/DTLSClientChannel.cs
@@ -5,6 +5,9 @@
using System.Net.Sockets;
using Com.AugustCellars.CoAP.Channel;
using Com.AugustCellars.COSE;
+#if SUPPORT_TLS_CWT
+using Com.AugustCellars.WebToken;
+#endif
namespace Com.AugustCellars.CoAP.DTLS
{
@@ -23,6 +26,10 @@ internal class DTLSClientChannel : IChannel
private readonly int _port;
private UDPChannel _udpChannel;
private readonly OneKey _userKey;
+#if SUPPORT_TLS_CWT
+ private readonly CWT _userCwt;
+#endif
+ private KeySet CwtTrustKeySet { get; }
public EventHandler TlsEventHandler;
@@ -46,6 +53,16 @@ public DTLSClientChannel(OneKey userKey, Int32 port)
_userKey = userKey;
}
+#if SUPPORT_TLS_CWT
+ public DTLSClientChannel(CWT cwt, OneKey userKey, KeySet cwtTrustKeys, int port)
+ {
+ _port = port;
+ _userKey = userKey;
+ _userCwt = cwt;
+ CwtTrustKeySet = cwtTrustKeys;
+ }
+#endif
+
///
/// Create a client only channel and use a given endpoint
///
@@ -92,6 +109,11 @@ public Int32 ReceivePacketSize {
private Int32 _running;
+ ///
+ public bool AddMulticastAddress(IPEndPoint ep)
+ {
+ return false;
+ }
///
/// Tell the channel to set itself up and start processing data
@@ -180,7 +202,17 @@ public ISession GetSession(System.Net.EndPoint ep)
// No session - create a new one.
- session = new DTLSSession(ipEndPoint, DataReceived, _userKey);
+#if SUPPORT_TLS_CWT
+ if (_userCwt != null) {
+ session = new DTLSSession(ipEndPoint, DataReceived, _userCwt, _userKey, CwtTrustKeySet);
+ }
+ else {
+#endif
+ session = new DTLSSession(ipEndPoint, DataReceived, _userKey);
+#if SUPPORT_TLS_CWT
+ }
+#endif
+
session.TlsEventHandler += OnTlsEvent;
AddSession(session);
diff --git a/CoAP.NET/DTLS/DTLSClientEndPoint.cs b/CoAP.NET/DTLS/DTLSClientEndPoint.cs
index 52034b1..28f8ab6 100644
--- a/CoAP.NET/DTLS/DTLSClientEndPoint.cs
+++ b/CoAP.NET/DTLS/DTLSClientEndPoint.cs
@@ -4,6 +4,9 @@
using Com.AugustCellars.CoAP.Net;
using Com.AugustCellars.COSE;
+#if SUPPORT_TLS_CWT
+using Com.AugustCellars.WebToken;
+#endif
namespace Com.AugustCellars.CoAP.DTLS
{
@@ -24,6 +27,12 @@ public DTLSClientEndPoint(OneKey userKey) : this(userKey, 0, CoapConfig.Default)
{
}
+#if SUPPORT_TLS_CWT
+ public DTLSClientEndPoint(CWT cwt, OneKey privKey, KeySet cwtTrustKeys) : this (new DTLSClientChannel(cwt, privKey, cwtTrustKeys, 0), CoapConfig.Default)
+ {
+ }
+#endif
+
///
/// Instantiates a new DTLS endpoint with the specific channel and configuration
///
diff --git a/CoAP.NET/DTLS/DTLSEndPoint.cs b/CoAP.NET/DTLS/DTLSEndPoint.cs
index cf634ac..db8a617 100644
--- a/CoAP.NET/DTLS/DTLSEndPoint.cs
+++ b/CoAP.NET/DTLS/DTLSEndPoint.cs
@@ -13,31 +13,31 @@ namespace Com.AugustCellars.CoAP.DTLS
public class DTLSEndPoint : CoAPEndPoint
{
///
- public DTLSEndPoint(KeySet serverKeys, KeySet userKeys) : this(serverKeys, userKeys, 0, CoapConfig.Default)
+ public DTLSEndPoint(TlsKeyPairSet serverKeys, KeySet userKeys) : this(serverKeys, userKeys, 0, CoapConfig.Default)
{
}
///
- public DTLSEndPoint(KeySet serverKeys, KeySet userKeys, ICoapConfig config) : this(serverKeys, userKeys, 0, config)
+ public DTLSEndPoint(TlsKeyPairSet serverKeys, KeySet userKeys, ICoapConfig config) : this(serverKeys, userKeys, 0, config)
{
}
///
- public DTLSEndPoint(KeySet keysServer, KeySet keysUser, Int32 port) : this(new DTLSChannel(keysServer, keysUser, port), CoapConfig.Default)
+ public DTLSEndPoint(TlsKeyPairSet keysServer, KeySet keysUser, Int32 port, KeySet cwtTrustRoots = null) : this(new DTLSChannel(keysServer, keysUser, port, cwtTrustRoots), CoapConfig.Default)
{
}
///
- public DTLSEndPoint(KeySet keyServer, KeySet keysUser, int port, ICoapConfig config) : this (new DTLSChannel(keyServer, keysUser, port), config)
+ public DTLSEndPoint(TlsKeyPairSet keyServer, KeySet keysUser, int port, ICoapConfig config) : this (new DTLSChannel(keyServer, keysUser, port), config)
{ }
///
- public DTLSEndPoint(KeySet keysServer, KeySet keysUser, System.Net.EndPoint localEndPoint) : this(keysServer, keysUser, localEndPoint, CoapConfig.Default)
+ public DTLSEndPoint(TlsKeyPairSet keysServer, KeySet keysUser, System.Net.EndPoint localEndPoint) : this(keysServer, keysUser, localEndPoint, CoapConfig.Default)
{
}
///
- public DTLSEndPoint(KeySet keysServer, KeySet keysUser, System.Net.EndPoint localEndPoint, ICoapConfig config) : this(new DTLSChannel(keysServer, keysUser, localEndPoint), config)
+ public DTLSEndPoint(TlsKeyPairSet keysServer, KeySet keysUser, System.Net.EndPoint localEndPoint, ICoapConfig config) : this(new DTLSChannel(keysServer, keysUser, localEndPoint), config)
{
}
diff --git a/CoAP.NET/DTLS/DTLSSession.cs b/CoAP.NET/DTLS/DTLSSession.cs
index c3c3ae9..ee0afa1 100644
--- a/CoAP.NET/DTLS/DTLSSession.cs
+++ b/CoAP.NET/DTLS/DTLSSession.cs
@@ -6,7 +6,9 @@
using System.Threading;
using Com.AugustCellars.CoAP.Channel;
using Com.AugustCellars.COSE;
-
+#if SUPPORT_TLS_CWT
+using Com.AugustCellars.WebToken;
+#endif
using PeterO.Cbor;
using Org.BouncyCastle.Crypto.Tls;
@@ -25,8 +27,11 @@ public class DTLSSession : ISecureSession
private DtlsTransport _dtlsSession;
private readonly OurTransport _transport;
private readonly OneKey _userKey;
+#if SUPPORT_TLS_CWT
+ private readonly CWT _userCwt;
+#endif
private readonly KeySet _userKeys;
- private readonly KeySet _serverKeys;
+ private readonly TlsKeyPairSet _serverKeys;
private OneKey _authKey;
private readonly ConcurrentQueue _queue = new ConcurrentQueue();
@@ -35,6 +40,7 @@ public class DTLSSession : ISecureSession
private readonly EventHandler _sessionEvents;
public EventHandler TlsEventHandler;
+ private KeySet CwtTrustKeySet { get; }
///
/// List of event handlers to inform about session events.
@@ -49,14 +55,27 @@ public DTLSSession(IPEndPoint ipEndPoint, EventHandler da
_transport = new OurTransport(ipEndPoint);
}
- public DTLSSession(IPEndPoint ipEndPoint, EventHandler dataReceived, KeySet serverKeys, KeySet userKeys)
+ public DTLSSession(IPEndPoint ipEndPoint, EventHandler dataReceived, TlsKeyPairSet serverKeys, KeySet userKeys, KeySet cwtTrustKeySet)
{
_ipEndPoint = ipEndPoint;
_dataReceived = dataReceived;
_userKeys = userKeys;
_serverKeys = serverKeys;
_transport = new OurTransport(ipEndPoint);
+ CwtTrustKeySet = cwtTrustKeySet;
+ }
+
+#if SUPPORT_TLS_CWT
+ public DTLSSession(IPEndPoint ipEndPoint, EventHandler dataReceived, CWT userCwt, OneKey privKey, KeySet cwtTrustKeys)
+ {
+ _ipEndPoint = ipEndPoint;
+ _dataReceived = dataReceived;
+ _userCwt = userCwt;
+ _userKey = privKey;
+ CwtTrustKeySet = cwtTrustKeys;
+ _transport = new OurTransport(ipEndPoint);
}
+#endif
public OneKey AuthenticationKey
{
@@ -104,7 +123,15 @@ public void Connect(UDPChannel udpChannel)
{
BasicTlsPskIdentity pskIdentity = null;
- if (_userKey != null) {
+#if SUPPORT_TLS_CWT
+ if (_userCwt != null)
+ {
+ _client = new DtlsClient(null, new TlsKeyPair(_userCwt, _userKey), CwtTrustKeySet);
+ }
+ else if (_userKey != null) {
+#else
+ if (_userKey != null) {
+#endif
if (_userKey.HasKeyType((int) COSE.GeneralValuesInt.KeyType_Octet)) {
CBORObject kid = _userKey[COSE.CoseKeyKeys.KeyIdentifier];
@@ -153,6 +180,7 @@ public void Accept(UDPChannel udpChannel, byte[] message)
DtlsServer server = new DtlsServer(_serverKeys, _userKeys);
server.TlsEventHandler += OnTlsEvent;
+ server.CwtTrustKeySet = CwtTrustKeySet;
_transport.UDPChannel = udpChannel;
_transport.Receive(message);
@@ -269,16 +297,16 @@ void StartListen()
else {
byte[] buf2 = new byte[size];
Array.Copy(buf, buf2, size);
- FireDataReceived(buf2, _ipEndPoint);
+ FireDataReceived(buf2, _ipEndPoint, null); // M00BUG
}
}
}
- private void FireDataReceived(Byte[] data, System.Net.EndPoint ep)
+ private void FireDataReceived(Byte[] data, System.Net.EndPoint ep, System.Net.EndPoint epLocal)
{
EventHandler h = _dataReceived;
if (h != null) {
- h(this, new DataReceivedEventArgs(data, ep, this));
+ h(this, new DataReceivedEventArgs(data, ep, epLocal, this));
}
}
diff --git a/CoAP.NET/DTLS/DtlsServer.cs b/CoAP.NET/DTLS/DtlsServer.cs
index 91df2da..3ba6e22 100644
--- a/CoAP.NET/DTLS/DtlsServer.cs
+++ b/CoAP.NET/DTLS/DtlsServer.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -8,6 +9,9 @@
using Org.BouncyCastle.Crypto.Tls;
using Com.AugustCellars.COSE;
+#if SUPPORT_TLS_CWT
+using Com.AugustCellars.WebToken;
+#endif
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Asn1.X9;
@@ -23,12 +27,14 @@ namespace Com.AugustCellars.CoAP.DTLS
{
class DtlsServer : DefaultTlsServer
{
- private KeySet _serverKeys;
+ private TlsKeyPairSet _serverKeys;
private KeySet _userKeys;
public EventHandler TlsEventHandler;
- internal DtlsServer(KeySet serverKeys, KeySet userKeys)
+ public KeySet CwtTrustKeySet { get; set; }
+
+ internal DtlsServer(TlsKeyPairSet serverKeys, KeySet userKeys)
{
_serverKeys = serverKeys;
_userKeys = userKeys;
@@ -118,28 +124,110 @@ private BigInteger ConvertBigNum(CBORObject cbor)
return new BigInteger(rgb2);
}
+#if SUPPORT_TLS_CWT
+ public override AbstractCertificate ParseCertificate(short certificateType, Stream io)
+ {
+ switch (certificateType)
+ {
+ case CertificateType.CwtPublicKey:
+ try
+ {
+ CwtPublicKey cwtPub = CwtPublicKey.Parse(io);
+
+ CWT cwtServer = CWT.Decode(cwtPub.EncodedCwt(), CwtTrustKeySet, CwtTrustKeySet);
+
+ AsymmetricKeyParameter pubKey = cwtServer.Cnf.Key.AsPublicKey();
+
+ SubjectPublicKeyInfo spi = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubKey);
+ cwtPub.SetSubjectPublicKeyInfo(spi);
+
+ return cwtPub;
+ }
+ catch
+ {
+ return null;
+ }
+
+ default:
+ return null;
+ }
+ }
+#endif
protected override TlsSignerCredentials GetECDsaSignerCredentials()
{
+ byte[] certTypes;
+
+ if (mClientExtensions.Contains(ExtensionType.server_certificate_type)) {
+ certTypes = (byte[]) mClientExtensions[ExtensionType.server_certificate_type];
+ }
+ else {
+ certTypes = new byte[]{1};
+ }
+
+ foreach (byte b in certTypes) {
#if SUPPORT_RPK
- foreach (OneKey k in _serverKeys) {
- if (k.HasKeyType((int) COSE.GeneralValuesInt.KeyType_EC2) &&
- k.HasAlgorithm(COSE.AlgorithmValues.ECDSA_256)) {
+ if (b == 2) {
+ foreach (TlsKeyPair kp in _serverKeys) {
+ if (b != kp.CertType) continue;
- X9ECParameters p = k.GetCurve();
- ECDomainParameters parameters = new ECDomainParameters(p.Curve, p.G, p.N, p.H);
- ECPrivateKeyParameters privKey = new ECPrivateKeyParameters("ECDSA", ConvertBigNum(k[CoseKeyParameterKeys.EC_D]), parameters);
+ OneKey k = kp.PublicKey;
+ if (k.HasKeyType((int) COSE.GeneralValuesInt.KeyType_EC2) &&
+ k.HasAlgorithm(COSE.AlgorithmValues.ECDSA_256)) {
- ECPoint point = k.GetPoint();
- ECPublicKeyParameters param = new ECPublicKeyParameters(point, parameters);
+ AsymmetricKeyParameter param = k.AsPublicKey();
- SubjectPublicKeyInfo spi = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(param);
+ SubjectPublicKeyInfo spi = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(param);
- return new DefaultTlsSignerCredentials(mContext, new RawPublicKey(spi), privKey, new SignatureAndHashAlgorithm(HashAlgorithm.sha256, SignatureAlgorithm.ecdsa) );
+ return new DefaultTlsSignerCredentials(mContext, new RawPublicKey(spi), kp.PrivateKey.AsPrivateKey(),
+ new SignatureAndHashAlgorithm(
+ HashAlgorithm.sha256, SignatureAlgorithm.ecdsa));
+ }
+ }
+ }
+#endif
+#if SUPPORT_TLS_CWT
+ if (b == 254) {
+ foreach (TlsKeyPair kp in _serverKeys) {
+ if (b != kp.CertType) continue;
+
+ OneKey k = kp.PrivateKey;
+ if (k.HasKeyType((int) COSE.GeneralValuesInt.KeyType_EC2) &&
+ k.HasAlgorithm(COSE.AlgorithmValues.ECDSA_256)) {
+
+ CwtPublicKey cwtKey = new CwtPublicKey(kp.PublicCwt.EncodeToBytes());
+ AsymmetricKeyParameter pubKey = kp.PublicCwt.Cnf.Key.AsPublicKey();
+ cwtKey.SetSubjectPublicKeyInfo(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(pubKey));
+
+ return new DefaultTlsSignerCredentials(
+ mContext, cwtKey, kp.PrivateKey.AsPrivateKey(),
+ new SignatureAndHashAlgorithm(HashAlgorithm.sha256, SignatureAlgorithm.ecdsa));
+ }
+ }
}
- }
#endif
+#if SUPPORT_RPK
+ if (b == 1) {
+ foreach (TlsKeyPair kp in _serverKeys)
+ {
+ if (b != kp.CertType) continue;
+
+ OneKey k = kp.PrivateKey;
+ if (k.HasKeyType((int)COSE.GeneralValuesInt.KeyType_EC2) &&
+ k.HasAlgorithm(COSE.AlgorithmValues.ECDSA_256)) {
+
+ return new DefaultTlsSignerCredentials(
+ mContext,
+ new CwtPublicKey(kp.PublicCwt.EncodeToBytes()),
+ kp.PrivateKey.AsPrivateKey(),
+ new SignatureAndHashAlgorithm(HashAlgorithm.sha256, SignatureAlgorithm.ecdsa));
+ }
+ }
+ }
+#endif
+
+ }
- // If we did not fine appropriate signer credientials - ask for help
+ // If we did not fine appropriate signer credentials - ask for help
TlsEvent e = new TlsEvent(TlsEvent.EventCode.SignCredentials) {
CipherSuite = KeyExchangeAlgorithm.ECDHE_ECDSA
@@ -192,6 +280,10 @@ protected virtual TlsKeyExchange CreatePskKeyExchange(int keyExchange)
protected override TlsKeyExchange CreateECDHKeyExchange(int keyExchange)
{
+ byte[] serverCertTypes;
+ if (mClientExtensions.Contains(ExtensionType.server_certificate_type)) {
+ serverCertTypes = (byte[]) mClientExtensions[ExtensionType.server_certificate_type];
+ }
return new TlsECDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats,
mServerECPointFormats);
}
@@ -215,6 +307,7 @@ public override byte GetClientCertificateType(byte[] certificateTypes)
foreach (byte type in certificateTypes) {
if (type == 2) return type; // Assume we only support Raw Public Key
+ if (type == 254) return type;
}
@@ -239,6 +332,7 @@ public override byte GetServerCertificateType(byte[] certificateTypes)
foreach (byte type in certificateTypes) {
if (type == 2) return type; // Assume we only support Raw Public Key
+ if (type == 254) return type;
}
throw new TlsFatalAlert(AlertDescription.handshake_failure);
}
@@ -263,6 +357,12 @@ public override void NotifyClientCertificate(AbstractCertificate clientCertifica
if (clientCertificate is RawPublicKey) {
mPskIdentityManager.GetRpkKey((RawPublicKey) clientCertificate);
}
+#if SUPPORT_TLS_CWT
+ else if (clientCertificate is CwtPublicKey) {
+ mPskIdentityManager.CwtTrustRoots = CwtTrustKeySet;
+ mPskIdentityManager.GetCwtKey((CwtPublicKey) clientCertificate);
+ }
+#endif
else {
TlsEvent e = new TlsEvent(TlsEvent.EventCode.ClientCertificate) {
Certificate = clientCertificate
@@ -312,6 +412,11 @@ internal MyIdentityManager(KeySet keys)
public OneKey AuthenticationKey { get; private set; }
+#if SUPPORT_TLS_CWT
+ public KeySet CwtTrustRoots { get; set; }
+ public CWT CwtAuthenticationKey { get; private set; }
+#endif
+
public virtual byte[] GetHint()
{
return Encoding.UTF8.GetBytes("hint");
@@ -405,6 +510,41 @@ public void GetRpkKey(RawPublicKey rpk)
}
}
#endif
+
+#if SUPPORT_TLS_CWT
+ public void GetCwtKey(CwtPublicKey rpk)
+ {
+ AsymmetricKeyParameter key;
+ CWT cwt;
+
+ try {
+ cwt = CWT.Decode(rpk.EncodedCwt(), CwtTrustRoots, CwtTrustRoots);
+
+ AuthenticationKey = cwt.Cnf.Key;
+ }
+ catch (Exception e)
+ {
+ TlsEvent ev = new TlsEvent(TlsEvent.EventCode.ClientCertificate)
+ {
+ Certificate = rpk
+ };
+
+ EventHandler handler = TlsEventHandler;
+ if (handler != null)
+ {
+ handler(this, ev);
+ }
+
+ if (!ev.Processed)
+ {
+ throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+ }
+
+ AuthenticationKey = ev.KeyValue;
+ }
+ }
+#endif
+
}
}
}
diff --git a/CoAP.NET/DTLS/TlsKey.cs b/CoAP.NET/DTLS/TlsKey.cs
new file mode 100644
index 0000000..f3afda3
--- /dev/null
+++ b/CoAP.NET/DTLS/TlsKey.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Com.AugustCellars.COSE;
+#if SUPPORT_TLS_CWT
+using Com.AugustCellars.WebToken;
+#endif
+using Org.BouncyCastle.Security;
+
+namespace Com.AugustCellars.CoAP.DTLS
+{
+ public class TlsKeyPair
+ {
+ public OneKey PrivateKey { get; }
+ public OneKey PublicKey { get; }
+#if SUPPORT_TLS_CWT
+ public CWT PublicCwt { get; }
+#endif
+ public byte[] X509Certificate { get; }
+
+ public byte CertType { get; }
+
+ public TlsKeyPair(OneKey publicKey, OneKey privateKey)
+ {
+ this.PrivateKey = privateKey;
+ this.PublicKey = publicKey;
+ CertType = 2; // RPK
+ }
+
+#if SUPPORT_TLS_CWT
+ public TlsKeyPair(CWT publicKey, OneKey privateKey)
+ {
+ this.PrivateKey = privateKey;
+ this.PublicCwt = publicKey;
+ CertType = 254; // CWT
+ }
+#endif
+
+ public TlsKeyPair(byte[] certificate, OneKey privateKey)
+ {
+ this.PrivateKey = privateKey;
+ this.X509Certificate = certificate;
+ CertType = 1; // X.509 Certificate
+ }
+
+ public bool Compare(TlsKeyPair other)
+ {
+ return false;
+ }
+ }
+
+ public class TlsKeyPairSet
+ {
+ List _keyList = new List();
+
+ ///
+ /// Return number of keys in the key set.
+ ///
+ public int Count => _keyList.Count;
+
+ ///
+ /// Return first key in the set.
+ ///
+ public TlsKeyPair FirstKey => _keyList.First();
+
+ ///
+ /// Return the i-th element in the key set.
+ ///
+ /// index of element to return
+ /// OneKey
+ public TlsKeyPair this[int i] => _keyList[i];
+
+ ///
+ /// Add a key to the key set. The function will do a minimal check for equality to existing keys in the set.
+ ///
+ /// OneKey: key to be added
+ public void AddKey(TlsKeyPair key)
+ {
+ foreach (TlsKeyPair k in _keyList)
+ {
+ if (key.Compare(k))
+ return;
+ }
+ _keyList.Add(key);
+ }
+
+ ///
+ /// All forall to be used to enumerate the keys in a key set.
+ ///
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ return _keyList.GetEnumerator();
+ }
+
+
+ }
+}
diff --git a/CoAP.NET/EndPoint/Resources/RemoteResource.cs b/CoAP.NET/EndPoint/Resources/RemoteResource.cs
index c794e4f..5200c75 100644
--- a/CoAP.NET/EndPoint/Resources/RemoteResource.cs
+++ b/CoAP.NET/EndPoint/Resources/RemoteResource.cs
@@ -23,8 +23,10 @@ public static RemoteResource NewRoot(String linkFormat, int mediaType = MediaTyp
case MediaType.ApplicationLinkFormat:
return LinkFormat.Deserialize(linkFormat);
+#if false // Dead work?
case MediaType.ApplicationLinkFormatJson:
return LinkFormat.DeserializeJson(linkFormat);
+#endif
default:
throw new ArgumentException("Unrecognized media type");
@@ -37,11 +39,13 @@ public static RemoteResource NewRoot(byte[] linkFormat, int mediaType = MediaTyp
case MediaType.ApplicationLinkFormat:
return LinkFormat.Deserialize(Encoding.UTF8.GetString(linkFormat));
+#if false // Dead work?
case MediaType.ApplicationLinkFormatCbor:
return LinkFormat.DeserializeCbor(linkFormat);
case MediaType.ApplicationLinkFormatJson:
return LinkFormat.DeserializeJson(Encoding.UTF8.GetString(linkFormat));
+#endif
default:
throw new ArgumentException("Unrecognized media type");
diff --git a/CoAP.NET/LinkFormat.cs b/CoAP.NET/LinkFormat.cs
index b83e476..3460001 100644
--- a/CoAP.NET/LinkFormat.cs
+++ b/CoAP.NET/LinkFormat.cs
@@ -13,10 +13,11 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
-using System.Text.RegularExpressions;
+using Com.AugustCellars.CoAP.Coral;
using Com.AugustCellars.CoAP.EndPoint.Resources;
using Com.AugustCellars.CoAP.Log;
using Com.AugustCellars.CoAP.Server.Resources;
+using Com.AugustCellars.CoAP.Util;
using PeterO.Cbor;
namespace Com.AugustCellars.CoAP
@@ -38,7 +39,7 @@ public static class LinkFormat
///
/// What is the set of attributes that must appear only once in a link format
///
- public static string[] SingleOccuranceAttributes = new string[] {
+ public static string[] SingleOccurenceAttributes = new string[] {
"title", "sz", "obs"
};
@@ -94,7 +95,7 @@ public static class LinkFormat
///
public static readonly string Separator = ";";
- #if false
+#if false
public static readonly Regex DelimiterRegex = new Regex("\\s*" + Delimiter + "+\\s*");
public static readonly Regex SeparatorRegex = new Regex("\\s*" + Separator + "+\\s*");
@@ -123,6 +124,51 @@ public static class LinkFormat
["obs"] = CBORObject.FromObject(13)
};
+ public static readonly Dictionary CborCoralKeys = new Dictionary() {
+ ["hreflang"] = CBORObject.FromObject(10),
+ ["media"] = CBORObject.FromObject(11),
+ ["title"] = CBORObject.FromObject(12),
+ ["type"] = CBORObject.FromObject(13),
+ ["rt"] = CBORObject.FromObject(14),
+ ["if"] = CBORObject.FromObject(15),
+ ["sz"] = CBORObject.FromObject(16),
+ ["ct"] = CBORObject.FromObject(17),
+ ["ct"] = CBORObject.FromObject(18),
+ ["obs"] = CBORObject.FromObject(20)
+ };
+
+ public static readonly Dictionary CoralsKeys = new Dictionary() {
+ ["ct"] = "http://coreapps.org/reef#ct",
+ ["sz"] = "http://coreapps.org/reef#sz",
+ ["if"] = "http://coreapps.org/reef#if",
+ ["rt"] = "http://coreapps.org/reef#rt",
+ ["type"] = "http://coreapps.org/coap#type",
+ ["media"] = "http://coreapps.org/coap#media",
+ };
+
+ public static CoralDictionary ReefDictionary = new CoralDictionary() {
+ {0, "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"},
+ {1, "http://www.iana.org/assignments/relation/item"},
+ {2, "http://www.iana.org/assignments/relation/collection"},
+ {3, "http://coreapps.org/collections#create"},
+ {4, "http://coreapps.org/base#update"},
+ {5, "https://coreapps.org/collecitons#delete"},
+ {6, "http://coreapps.org/base#search"},
+ {7, "http://coreapps.org/coap#accept"},
+ {8, "http://coreapps.org/reef#rd-unit"},
+ {9, "http://coreapps.org/reef#rd-item"},
+ {10, "http://coreapps.org/base#lang"},
+ {11, "http://coreapps.org/reef#media"},
+ {12, "http://coreapps.org/reef#title"},
+ {13, "http://coreapps.org/reef#type"},
+ {14, "http://coreapps.org/reef#rt"},
+ {15, "http://coreapps.org/reef#if"},
+ {16, "http://coreapps.org/reef#sz"},
+ {17, "http://coreapps.org/reef#ct"},
+ {18, "/.well-known/core"}
+
+ };
+
///
/// Serialize resources starting at a resource node into WebLink format
///
@@ -157,6 +203,7 @@ public static string Serialize(IResource root, IEnumerable queries)
return linkFormat.ToString();
}
+#if false // Work is dead?
public static byte[] SerializeCbor(IResource root, IEnumerable queries)
{
CBORObject linkFormat = CBORObject.NewArray();
@@ -184,6 +231,21 @@ public static string SerializeJson(IResource root, IEnumerable queries)
return linkFormat.ToJSONString();
}
+#endif
+ public static byte[] SerializeCoral(IResource root, IEnumerable queries)
+ {
+ CoralBody nodeRoot = new CoralBody();
+
+
+ List queryList = null;
+ if (queries != null) queryList = queries.ToList();
+
+ foreach (IResource child in root.Children) {
+ SerializeTreeInCoral(child, queryList, nodeRoot, CborAttributeKeys);
+ }
+
+ return nodeRoot.EncodeToBytes(ReefDictionary);
+ }
public static IEnumerable Parse(string linkFormat)
{
@@ -204,7 +266,7 @@ public static IEnumerable Parse(string linkFormat)
int eq = attributes[i].IndexOf('=');
string name = eq == -1 ? attributes[i] : attributes[i].Substring(0, eq);
- if (ParseStrictMode && SingleOccuranceAttributes.Contains(name)) {
+ if (ParseStrictMode && SingleOccurenceAttributes.Contains(name)) {
throw new ArgumentException($"'{name}' occurs multiple times");
}
@@ -226,6 +288,7 @@ public static IEnumerable Parse(string linkFormat)
}
}
+#if false // Work is dead?
public static IEnumerable ParseCbor(byte[] linkFormat)
{
CBORObject links = CBORObject.DecodeFromBytes(linkFormat);
@@ -237,7 +300,7 @@ public static IEnumerable ParseJson(string linkFormat)
CBORObject links = CBORObject.FromJSONString(linkFormat);
return ParseCommon(links, null);
}
-
+#endif
private static IEnumerable ParseCommon(CBORObject links, Dictionary dictionary)
{
if (links.Type != CBORType.Array) throw new ArgumentException("Not an array");
@@ -265,7 +328,7 @@ private static IEnumerable ParseCommon(CBORObject links, Dictionary queries, Stri
if (resource.Children == null) return;
// sort by resource name
- List childrens = new List(resource.Children);
- childrens.Sort((r1, r2) => string.CompareOrdinal(r1.Name, r2.Name));
+ List children = new List(resource.Children);
+ children.Sort((r1, r2) => string.CompareOrdinal(r1.Name, r2.Name));
- foreach (IResource child in childrens) {
+ foreach (IResource child in children) {
SerializeTree(child, queries, sb);
}
}
+#if false // Work is dead?
private static void SerializeTree(IResource resource, List queries, CBORObject cbor, Dictionary dictionary)
{
if (resource.Visible && Matches(resource, queries)) {
@@ -322,20 +386,40 @@ private static void SerializeTree(IResource resource, List queries, CBOR
if (resource.Children == null) return;
// sort by resource name
- List childrens = new List(resource.Children);
- childrens.Sort((r1, r2) => string.CompareOrdinal(r1.Name, r2.Name));
+ List children = new List(resource.Children);
+ children.Sort((r1, r2) => string.CompareOrdinal(r1.Name, r2.Name));
- foreach (IResource child in childrens) {
+ foreach (IResource child in children) {
SerializeTree(child, queries, cbor, dictionary);
}
}
+#endif
+
+ private static void SerializeTreeInCoral(IResource resource, List queries, CoralBody coral,
+ Dictionary dictionary)
+ {
+
+ if (resource.Visible && Matches(resource, queries)) {
+ SerializeResourceInCoral(resource, coral, dictionary);
+ }
+
+ if (resource.Children == null) return;
+
+ // sort by resource name
+ List children = new List(resource.Children);
+ children.Sort((r1, r2) => string.CompareOrdinal(r1.Name, r2.Name));
+
+ foreach (IResource child in children) {
+ SerializeTreeInCoral(child, queries, coral, dictionary);
+ }
+ }
public static void SerializeResource(IResource resource, StringBuilder sb, ResourceAttributes otherAttributes = null,
Uri uriRelative = null)
{
sb.Append("<");
if (uriRelative != null) {
- sb.Append(new Uri(uriRelative, resource.Path + resource.Name));
+ sb.Append(new Uri(uriRelative, resource.Path + resource.Name).ToString());
}
else {
sb.Append(resource.Path)
@@ -348,6 +432,7 @@ public static void SerializeResource(IResource resource, StringBuilder sb, Resou
}
}
+#if false // Work is dead?
public static void SerializeResource(IResource resource, CBORObject cbor, Dictionary dictionary,
ResourceAttributes otherAttributes = null, Uri uriRelative = null)
{
@@ -373,6 +458,36 @@ public static void SerializeResource(IResource resource, CBORObject cbor, Dictio
cbor.Add(obj);
}
+#endif
+
+ public static void SerializeResourceInCoral(IResource resource, CoralBody coral,
+ Dictionary dictionary,
+ ResourceAttributes otherAttributes = null, Uri uriRelative = null,
+ bool isEndPoint = false)
+ {
+ CBORObject obj = CBORObject.NewArray();
+ CBORObject href;
+ if (uriRelative == null) {
+ href =Ciri.ToCbor(resource.Path + resource.Name);
+ }
+ else {
+ href = Ciri.ToCbor(new Uri(uriRelative, resource.Path + resource.Name));
+ }
+
+ CoralBody body = new CoralBody();
+
+ SerializeAttributesInCoral(resource.Attributes, body, dictionary, uriRelative);
+ if (otherAttributes != null) {
+ SerializeAttributesInCoral(otherAttributes, body, dictionary, uriRelative);
+ }
+
+ if (body.Length == 0) {
+ body = null;
+ }
+
+ CoralItem item = new CoralLink(isEndPoint ? "http://coreapps.org/ref#rd-unit" : "http://coreapps.org/reef#rd-item", href, body);
+ coral.Add(item);
+ }
private static void SerializeAttributes(ResourceAttributes attributes, StringBuilder sb, Uri uriRelative)
{
@@ -397,6 +512,7 @@ private static void SerializeAttributes(ResourceAttributes attributes, StringBui
}
}
+#if false // Work is dead?
private static void SerializeAttributes(ResourceAttributes attributes, CBORObject cbor, Dictionary dictionary, Uri uriRelative)
{
List keys = new List(attributes.Keys);
@@ -419,6 +535,37 @@ private static void SerializeAttributes(ResourceAttributes attributes, CBORObjec
SerializeAttribute(name, values, cbor, dictionary);
}
}
+#endif
+
+ private static void SerializeAttributesInCoral(ResourceAttributes attributes, CoralBody coral, Dictionary dictionary, Uri uriRelative)
+ {
+ List keys = new List(attributes.Keys);
+ keys.Sort();
+ foreach (string name in keys) {
+ if (!CoralsKeys.ContainsKey(name)) {
+ continue;
+ }
+
+ List values = new List(attributes.GetValues(name));
+ if (values.Count == 0)
+ {
+ continue;
+ }
+
+ if (uriRelative != null && name == "anchor")
+ {
+ List newValues = new List();
+ foreach (string val in values)
+ {
+ newValues.Add(new Uri(uriRelative, val).ToString());
+ }
+
+ values = newValues;
+ }
+
+ SerializeAttributeInCoral(name, values, coral, null);
+ }
+ }
private static void SerializeAttribute(string name, List values, StringBuilder sb)
{
@@ -469,16 +616,12 @@ private static void SerializeAttribute(string name, List values, StringB
}
}
+#if false // Work is dead?
private static void SerializeAttribute(string name, List values, CBORObject cbor, Dictionary dictionary)
{
bool useSpace = SpaceSeparatedValueAttributes.Contains(name);
CBORObject result;
- CBORObject nameX;
- if (dictionary == null || !dictionary.TryGetValue(name, out nameX)) {
- nameX = CBORObject.FromObject(name);
- }
-
if (useSpace && values.Count > 1) {
StringBuilder sb = new StringBuilder();
@@ -503,7 +646,50 @@ private static void SerializeAttribute(string name, List values, CBORObj
}
}
- cbor.Add(nameX, result);
+ CBORObject pair = CBORObject.NewArray();
+ pair.Add(name);
+ pair.Add(result);
+ cbor.Add(pair);
+ }
+#endif
+ private static void SerializeAttributeInCoral(string name, List values, CoralBody coral,
+ Dictionary dictionary)
+ {
+ bool useSpace = SpaceSeparatedValueAttributes.Contains(name);
+ CBORObject result;
+
+ string nameX = CoralsKeys[name];
+
+ if (useSpace && values.Count > 1) {
+ StringBuilder sb = new StringBuilder();
+
+ foreach (string value in values) {
+ sb.Append(value);
+ sb.Append(" ");
+ }
+
+ sb.Length = sb.Length - 1;
+
+ result = CBORObject.FromObject(sb.ToString());
+ }
+ else if (values.Count == 1) {
+ string value = values.First();
+ result = string.IsNullOrEmpty(value) ? CBORObject.True : CBORObject.FromObject(values.First());
+ }
+ else {
+ result = CBORObject.NewArray();
+ foreach (string value in values) {
+ if (string.IsNullOrEmpty(value)) {
+ result.Add(CBORObject.True);
+ }
+ else {
+ result.Add(value);
+ }
+ }
+ }
+
+ CoralLink link = new CoralLink(nameX, result);
+ coral.Add(link);
}
private static bool IsNumber(string value)
@@ -585,6 +771,9 @@ public static RemoteResource Deserialize(string linkFormat)
return root;
}
+
+#if false // Work is dead?
+
///
/// Parse a CBOR encoded link format structure
///
@@ -672,7 +861,7 @@ private static RemoteResource DeserializeCbor(CBORObject cbor)
return root;
}
-
+#endif
#if false
private static LinkAttribute ParseAttribute(Scanner scanner)
@@ -825,7 +1014,7 @@ internal static bool AddAttribute(ICollection attributes, LinkAtt
private static bool IsSingle(string name)
{
- return SingleOccuranceAttributes.Contains(name);
+ return SingleOccurenceAttributes.Contains(name);
}
private static string quoteChars = "'\"";
diff --git a/CoAP.NET/Log/LogManager.cs b/CoAP.NET/Log/LogManager.cs
index e0f9854..56fdf8f 100644
--- a/CoAP.NET/Log/LogManager.cs
+++ b/CoAP.NET/Log/LogManager.cs
@@ -134,7 +134,7 @@ public enum LogLevel
///
Error,
///
- /// Fatals only.
+ /// Fatal only.
///
Fatal,
///
diff --git a/CoAP.NET/MediaType.cs b/CoAP.NET/MediaType.cs
index dc2e7d9..a94b9a9 100644
--- a/CoAP.NET/MediaType.cs
+++ b/CoAP.NET/MediaType.cs
@@ -24,140 +24,221 @@ public class MediaType
///
/// undefined
///
- public const Int32 Undefined = -1;
+ public const int Undefined = -1;
///
/// text/plain; charset=utf-8
///
- public const Int32 TextPlain = 0;
+ public const int TextPlain = 0;
///
/// text/xml
///
- public const Int32 TextXml = 1;
+ [Obsolete("Media type was never registered")]
+ public const int TextXml = 1;
///
/// text/csv
///
- public const Int32 TextCsv = 2;
+ [Obsolete("Media type was never registered")]
+ public const int TextCsv = 2;
///
/// text/html
///
- public const Int32 TextHtml = 3;
+ [Obsolete("Media type was never registered")]
+ public const int TextHtml = 3;
+ ///
+ /// Application/cose; cose-type="cose-encrypt0"
+ ///
+ public const int ApplicationCoseEncrypt0 = 16;
+ ///
+ /// Application/cose; cose-type="cose-mac0"
+ ///
+ public const int ApplicationCoseMac0 = 17;
+ ///
+ /// Application/cose; cose-type="cose-sign1"
+ ///
+ public const int ApplicationCoseSign1 = 18;
///
/// image/gif
///
- public const Int32 ImageGif = 21;
+ [Obsolete("Media type was never registered")]
+ public const int ImageGif = 21;
///
/// image/jpeg
///
- public const Int32 ImageJpeg = 22;
+ [Obsolete("Media type was never registered")]
+ public const int ImageJpeg = 22;
///
/// image/png
///
- public const Int32 ImagePng = 23;
+ [Obsolete("Media type was never registered")]
+ public const int ImagePng = 23;
///
/// image/tiff
///
- public const Int32 ImageTiff = 24;
+ [Obsolete("Media type was never registered")]
+ public const int ImageTiff = 24;
///
/// audio/raw
///
- public const Int32 AudioRaw = 25;
+ [Obsolete("Media type was never registered")]
+ public const int AudioRaw = 25;
///
/// video/raw
///
- public const Int32 VideoRaw = 26;
+ [Obsolete("Media type was never registered")]
+ public const int VideoRaw = 26;
///
/// application/link-format
///
- public const Int32 ApplicationLinkFormat = 40;
+ public const int ApplicationLinkFormat = 40;
///
/// application/xml
///
- public const Int32 ApplicationXml = 41;
+ public const int ApplicationXml = 41;
///
/// application/octet-stream
///
- public const Int32 ApplicationOctetStream = 42;
+ public const int ApplicationOctetStream = 42;
///
/// application/rdf+xml
///
- public const Int32 ApplicationRdfXml = 43;
+ [Obsolete("Media type was never registered")]
+ public const int ApplicationRdfXml = 43;
///
/// application/soap+xml
///
- public const Int32 ApplicationSoapXml = 44;
+ [Obsolete("Media type was never registered")]
+ public const int ApplicationSoapXml = 44;
///
/// application/atom+xml
///
- public const Int32 ApplicationAtomXml = 45;
+ [Obsolete("Media type was never registered")]
+ public const int ApplicationAtomXml = 45;
///
/// application/xmpp+xml
///
- public const Int32 ApplicationXmppXml = 46;
+ [Obsolete("Media type was never registered")]
+ public const int ApplicationXmppXml = 46;
///
/// application/exi
///
- public const Int32 ApplicationExi = 47;
+ public const int ApplicationExi = 47;
///
/// application/fastinfoset
///
- public const Int32 ApplicationFastinfoset = 48;
+ [Obsolete("Media type was never registered")]
+ public const int ApplicationFastinfoset = 48;
///
/// application/soap+fastinfoset
///
- public const Int32 ApplicationSoapFastinfoset = 49;
+ [Obsolete("Media type was never registered")]
+ public const int ApplicationSoapFastinfoset = 49;
+ ///
+ /// application/json [RFC 7159]
+ ///
+ public const int ApplicationJson = 50;
///
- /// application/json
+ /// application/json-patch+json [RFC 6902]
///
- public const Int32 ApplicationJson = 50;
+ public const int ApplicationJsonPatchJson = 51;
///
/// application/x-obix-binary
///
- public const Int32 ApplicationXObixBinary = 51;
+ [Obsolete("Media type was never registered")]
+ public const int ApplicationXObixBinary = 51;
+ ///
+ /// application/merge-patch+json [RFC 7396]
+ ///
+ public const int ApplicationMergePatchJson = 52;
///
/// application/cbor - [RFC 7049]
///
public const int ApplicationCbor = 60;
+ ///
+ /// application/cwt [RFC 8392]
+ ///
+ public const int ApplicationCwt = 61;
+ ///
+ /// application/multipart-core [draft-ietf-core-multipart-ct]
+ ///
+ public const int ApplicationMultipartCore = 62;
+
+#if false // Work is dead?
///
/// application/link-format+cbor - [RFC TBD]
///
+ [Obsolete("Media type was never registered")]
public const int ApplicationLinkFormatCbor = 64;
+#endif
+
+ public const int ApplicationCoseEncrypt = 96;
+ public const int ApplicationCoseMac = 97;
+ public const int ApplicationCoseSign = 98;
+ public const int ApplicationCoseKey = 101;
+ public const int ApplicationCoseKeySet = 102;
+
+#if false
///
/// application/link-format+json - [RFC TBD]
///
+ [Obsolete("Media type was never registered")]
public const int ApplicationLinkFormatJson = 504;
+#endif
+
+ ///
+ /// application/ace+cbor - [draft-ietf-ace-authz]
+ ///
+ public const int ApplicationAceCbor = 65000;
+
+ public const int ApplicationCoralReef = 65088;
+ public const int Coral = 999;
///
/// any
///
- public const Int32 Any = 0xFF;
+ public const int Any = -2;
+
+ public class MediaTypeInfo
+ {
+ public string[] ContentType { get; }
+ public bool IsText { get; }
+ public bool IsCbor { get; }
+ public MediaTypeInfo(string[] contentType, bool isText=false, bool isCbor=false)
+ {
+ ContentType = contentType;
+ IsText = isText;
+ IsCbor = isCbor;
+ }
+ }
- private static readonly Dictionary registry = new Dictionary();
+ private static readonly Dictionary registry = new Dictionary();
static MediaType()
{
- registry.Add(TextPlain, new String[] { "text/plain", "txt" });
- registry.Add(TextXml, new String[] { "text/xml", "xml" });
- registry.Add(TextCsv, new String[] { "text/csv", "csv" });
- registry.Add(TextHtml, new String[] { "text/html", "html" });
+ registry.Add(TextPlain, new MediaTypeInfo(new string[] { "text/plain", "txt" }, true));
+ registry.Add(TextXml, new MediaTypeInfo(new string[] { "text/xml", "xml" }, true));
+ registry.Add(TextCsv, new MediaTypeInfo(new string[] { "text/csv", "csv" }, true));
+ registry.Add(TextHtml, new MediaTypeInfo(new string[] { "text/html", "html" }, true));
+
+ registry.Add(ImageGif, new MediaTypeInfo(new string[] { "image/gif", "gif" }));
+ registry.Add(ImageJpeg, new MediaTypeInfo(new string[] { "image/jpeg", "jpg" }));
+ registry.Add(ImagePng, new MediaTypeInfo(new string[] { "image/png", "png" }));
+ registry.Add(ImageTiff, new MediaTypeInfo(new string[] { "image/tiff", "tif" }));
+ registry.Add(AudioRaw, new MediaTypeInfo(new string[] { "audio/raw", "raw" }));
+ registry.Add(VideoRaw, new MediaTypeInfo(new string[] { "video/raw", "raw" }));
- registry.Add(ImageGif, new String[] { "image/gif", "gif" });
- registry.Add(ImageJpeg, new String[] { "image/jpeg", "jpg" });
- registry.Add(ImagePng, new String[] { "image/png", "png" });
- registry.Add(ImageTiff, new String[] { "image/tiff", "tif" });
- registry.Add(AudioRaw, new String[] { "audio/raw", "raw" });
- registry.Add(VideoRaw, new String[] { "video/raw", "raw" });
+ registry.Add(ApplicationLinkFormat, new MediaTypeInfo(new string[] { "application/link-format", "wlnk" }, true));
+ registry.Add(ApplicationXml, new MediaTypeInfo(new string[] { "application/xml", "xml" }, true));
+ registry.Add(ApplicationOctetStream, new MediaTypeInfo(new string[] { "application/octet-stream", "bin" }));
+ registry.Add(ApplicationRdfXml, new MediaTypeInfo(new string[] {"application/rdf+xml", "rdf"}, true));
+ registry.Add(ApplicationSoapXml, new MediaTypeInfo(new string[] {"application/soap+xml", "soap"}, true));
+ registry.Add(ApplicationAtomXml, new MediaTypeInfo(new string[] {"application/atom+xml", "atom"}, true));
+ registry.Add(ApplicationXmppXml, new MediaTypeInfo(new string[] {"application/xmpp+xml", "xmpp"}, true));
+ registry.Add(ApplicationFastinfoset, new MediaTypeInfo(new string[] { "application/fastinfoset", "finf"}));
+ registry.Add(ApplicationSoapFastinfoset, new MediaTypeInfo(new string[] {"application/soap+fastinfoset", "soap.finf"}));
+ registry.Add(ApplicationXObixBinary, new MediaTypeInfo(new string[] { "application/x-obix-binary", "obix" }));
+ registry.Add(ApplicationExi, new MediaTypeInfo(new string[] { "application/exi", "exi" }));
+ registry.Add(ApplicationJson, new MediaTypeInfo(new string[] { "application/json", "json" }, false)); // Compressed w/ deflate
- registry.Add(ApplicationLinkFormat, new String[] { "application/link-format", "wlnk" });
- registry.Add(ApplicationXml, new String[] { "application/xml", "xml" });
- registry.Add(ApplicationOctetStream, new String[] { "application/octet-stream", "bin" });
- registry.Add(ApplicationRdfXml, new String[] {"application/rdf+xml", "rdf"});
- registry.Add(ApplicationSoapXml, new String[] {"application/soap+xml", "soap"});
- registry.Add(ApplicationAtomXml, new String[] {"application/atom+xml", "atom"});
- registry.Add(ApplicationXmppXml, new String[] {"application/xmpp+xml", "xmpp"});
- registry.Add(ApplicationFastinfoset,new String[] { "application/fastinfoset", "finf"});
- registry.Add(ApplicationSoapFastinfoset, new String[] {"application/soap+fastinfoset", "soap.finf"});
- registry.Add(ApplicationXObixBinary, new String[] { "application/x-obix-binary", "obix" });
- registry.Add(ApplicationExi, new String[] { "application/exi", "exi" });
- registry.Add(ApplicationJson, new String[] { "application/json", "json" });
+ registry.Add(Coral, new MediaTypeInfo(new string[] {"XX", "coral"}, false, true));
}
///
@@ -165,31 +246,29 @@ static MediaType()
///
/// The media type to be checked
/// True iff the media type is a type of image
- public static Boolean IsImage(Int32 mediaType)
+ public static Boolean IsImage(int mediaType)
{
return mediaType >= ImageGif && mediaType <= ImageTiff;
}
- public static Boolean IsPrintable(Int32 mediaType)
+ public static Boolean IsPrintable(int mediaType)
{
- switch (mediaType)
- {
- case TextPlain:
- case TextXml:
- case TextCsv:
- case TextHtml:
- case ApplicationLinkFormat:
- case ApplicationXml:
- case ApplicationRdfXml:
- case ApplicationSoapXml:
- case ApplicationAtomXml:
- case ApplicationXmppXml:
- case ApplicationJson:
- case Undefined:
- return true;
- default:
- return false;
+ MediaTypeInfo val;
+ if (!registry.TryGetValue(mediaType, out val)) {
+ return false;
}
+
+ return val.IsText;
+ }
+
+ public static Boolean IsCbor(int mediaType)
+ {
+ MediaTypeInfo val;
+ if (!registry.TryGetValue(mediaType, out val)) {
+ return false;
+ }
+
+ return val.IsCbor;
}
///
@@ -197,14 +276,12 @@ public static Boolean IsPrintable(Int32 mediaType)
///
/// The media type to be described
/// A string describing the media type
- public static String ToString(Int32 mediaType)
+ public static string ToString(int mediaType)
{
- if (registry.ContainsKey(mediaType))
- {
- return registry[mediaType][0];
+ if (registry.ContainsKey(mediaType)) {
+ return registry[mediaType].ContentType[0];
}
- else
- {
+ else {
return "unknown/" + mediaType;
}
}
@@ -212,19 +289,18 @@ public static String ToString(Int32 mediaType)
///
/// Gets the file extension of the given media type.
///
- public static String ToFileExtension(Int32 mediaType)
+ public static string ToFileExtension(int mediaType)
{
if (registry.ContainsKey(mediaType))
{
- return registry[mediaType][1];
+ return registry[mediaType].ContentType[1];
}
- else
- {
+ else {
return "unknown_" + mediaType;
}
}
- public static Int32 NegotiationContent(Int32 defaultContentType, IEnumerable supported, IEnumerable