/// <inheritdoc /> public override void SendRequest(INextLayer nextLayer, Exchange exchange, Request request) { if ((request.OscoreContext != null) || (exchange.OscoreContext != null)) { SecurityContext ctx = exchange.OscoreContext; if (request.OscoreContext != null) { ctx = request.OscoreContext; exchange.OscoreContext = ctx; } Codec.IMessageEncoder me = Spec.NewMessageEncoder(); Request encryptedRequest = new Request(request.Method); if (request.Payload != null) { encryptedRequest.Payload = request.Payload; } MoveRequestHeaders(request, encryptedRequest); _Log.Info(m => m("New inner response message\n{0}", encryptedRequest.ToString())); ctx.Sender.IncrementSequenceNumber(); Encrypt0Message enc = new Encrypt0Message(false); byte[] msg = me.Encode(encryptedRequest); int tokenSize = msg[0] & 0xf; byte[] msg2 = new byte[msg.Length - (3 + tokenSize)]; Array.Copy(msg, 4 + tokenSize, msg2, 1, msg2.Length - 1); msg2[0] = msg[1]; enc.SetContent(msg2); // Build AAD CBORObject aad = CBORObject.NewArray(); aad.Add(CBORObject.FromObject(1)); // version aad.Add(CBORObject.NewArray()); // Algorithms aad[1].Add(CBORObject.FromObject(ctx.Sender.Algorithm)); if (ctx.Sender.SigningAlgorithm != null) { aad[1].Add(ctx.Sender.SigningAlgorithm); if (ctx.CountersignParams != null) { aad[1].Add(ctx.CountersignParams); } if (ctx.CountersignKeyParams != null) { aad[1].Add(ctx.CountersignKeyParams); } } aad.Add(CBORObject.FromObject(ctx.Sender.Id)); aad.Add(CBORObject.FromObject(ctx.Sender.PartialIV)); aad.Add(CBORObject.FromObject(new byte[0])); // I options go here _Log.Info(m => m("SendRequest: AAD = {0}", BitConverter.ToString(aad.EncodeToBytes()))); enc.SetExternalData(aad.EncodeToBytes()); enc.AddAttribute(HeaderKeys.IV, ctx.Sender.GetIV(ctx.Sender.PartialIV), Attributes.DO_NOT_SEND); enc.AddAttribute(HeaderKeys.PartialIV, CBORObject.FromObject(ctx.Sender.PartialIV), /* Attributes.PROTECTED */ Attributes.DO_NOT_SEND); enc.AddAttribute(HeaderKeys.Algorithm, ctx.Sender.Algorithm, Attributes.DO_NOT_SEND); enc.AddAttribute(HeaderKeys.KeyId, CBORObject.FromObject(ctx.Sender.Id), /*Attributes.PROTECTED*/ Attributes.DO_NOT_SEND); if (ctx.GroupId != null) { enc.AddAttribute(HeaderKeys.KidContext, CBORObject.FromObject(ctx.GroupId), Attributes.DO_NOT_SEND); } _Log.Info(m => m("SendRequest: AAD = {0}\nSendRequest: IV = {1}\nSendRequest: Key = {2}", BitConverter.ToString(aad.EncodeToBytes()), ctx.Sender.GetIV(ctx.Sender.PartialIV).GetByteString(), BitConverter.ToString(ctx.Sender.Key))); byte[] optionValue = BuildOscoreOption(enc); CounterSignature1 cs1 = null; if (ctx.Sender.SigningKey != null) { cs1 = new CounterSignature1(ctx.Sender.SigningKey); cs1.AddAttribute(HeaderKeys.Algorithm, ctx.Sender.SigningAlgorithm, Attributes.DO_NOT_SEND); aad.Add(optionValue); _Log.Info(m => m("SendRequest: AAD for Signature = {0}", BitConverter.ToString(aad.EncodeToBytes()))); cs1.SetExternalData(aad.EncodeToBytes()); cs1.SetObject(enc); enc.CounterSigner1 = cs1; } enc.Encrypt(ctx.Sender.Key); OscoapOption o = new OscoapOption(); o.Set(optionValue); request.AddOption(o); request.Payload = enc.GetEncryptedContent(); if (cs1 != null) { int cbOrig = request.Payload.Length; byte[] rgbOrig = request.Payload; byte[] signatureBytes = cs1.EncodeToCBORObject().GetByteString(); Array.Resize(ref rgbOrig, cbOrig + signatureBytes.Length); Array.Copy(signatureBytes, 0, rgbOrig, cbOrig, signatureBytes.Length); request.Payload = rgbOrig; } request.Method = request.HasOption(OptionType.Observe) ? Method.FETCH : Method.POST; request.Code = (int)request.Method; } base.SendRequest(nextLayer, exchange, request); }
public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { if (exchange.OscoreContext != null) { SecurityContext ctx = exchange.OscoreContext; Codec.IMessageEncoder me = Spec.NewMessageEncoder(); Response encryptedResponse = new Response((StatusCode)response.Code); if (response.Payload != null) { encryptedResponse.Payload = response.Payload; } MoveResponseHeaders(response, encryptedResponse); if (_Log.IsInfoEnabled) { _Log.Info("SendResponse: New inner response message"); _Log.Info(encryptedResponse.ToString()); } // Build AAD CBORObject aad = CBORObject.NewArray(); aad.Add(1); aad.Add(CBORObject.NewArray()); aad[1].Add(ctx.Sender.Algorithm); aad.Add(exchange.OscoapSenderId); aad.Add(exchange.OscoapSequenceNumber); aad.Add(CBORObject.FromObject(new byte[0])); // I Options if (ctx.Sender.SigningAlgorithm != null) { aad[1].Add(ctx.Sender.SigningAlgorithm); if (ctx.CountersignParams != null) { aad[1].Add(ctx.CountersignParams); } if (ctx.CountersignKeyParams != null) { aad[1].Add(ctx.CountersignKeyParams); } } if (_Log.IsInfoEnabled) { _Log.Info("SendResponse: AAD = " + BitConverter.ToString(aad.EncodeToBytes())); } Encrypt0Message enc = new Encrypt0Message(false); byte[] msg = me.Encode(encryptedResponse); int tokenSize = msg[0] & 0xf; byte[] msg2 = new byte[msg.Length - (3 + tokenSize)]; Array.Copy(msg, 4 + tokenSize, msg2, 1, msg2.Length - 1); msg2[0] = msg[1]; enc.SetContent(msg2); enc.SetExternalData(aad.EncodeToBytes()); if (response.HasOption(OptionType.Observe)) { enc.AddAttribute(HeaderKeys.PartialIV, CBORObject.FromObject(ctx.Sender.PartialIV), Attributes.UNPROTECTED); enc.AddAttribute(HeaderKeys.IV, ctx.Sender.GetIV(ctx.Sender.PartialIV), Attributes.DO_NOT_SEND); ctx.Sender.IncrementSequenceNumber(); if (ctx.IsGroupContext) { enc.AddAttribute(HeaderKeys.KeyId, CBORObject.FromObject(ctx.Sender.Id), Attributes.UNPROTECTED); } } else if (ctx.IsGroupContext) { if (!ctx.Recipients.ContainsKey(exchange.OscoapSenderId)) { return; } CBORObject iv = ctx.Recipients[exchange.OscoapSenderId].GetIV(exchange.OscoapSequenceNumber); enc.AddAttribute(HeaderKeys.IV, iv, Attributes.DO_NOT_SEND); enc.AddAttribute(HeaderKeys.KeyId, CBORObject.FromObject(ctx.Sender.Id), Attributes.UNPROTECTED); } else { CBORObject iv = ctx.Recipient.GetIV(exchange.OscoapSequenceNumber); enc.AddAttribute(HeaderKeys.IV, iv, Attributes.DO_NOT_SEND); } _Log.Info(m => m($"SendResponse: IV = {BitConverter.ToString(enc.FindAttribute(HeaderKeys.IV, Attributes.DO_NOT_SEND).GetByteString())}")); _Log.Info(m => m($"SendResponse: Key = {BitConverter.ToString(ctx.Sender.Key)}")); byte[] optionValue = BuildOscoreOption(enc); CounterSignature1 cs1 = null; if (ctx.IsGroupContext) { aad.Add(optionValue); cs1 = new CounterSignature1(ctx.Sender.SigningKey); cs1.AddAttribute(HeaderKeys.Algorithm, ctx.Sender.SigningAlgorithm, Attributes.DO_NOT_SEND); cs1.SetExternalData(aad.EncodeToBytes()); cs1.SetObject(enc); enc.CounterSigner1 = cs1; } enc.AddAttribute(HeaderKeys.Algorithm, ctx.Sender.Algorithm, Attributes.DO_NOT_SEND); enc.Encrypt(ctx.Sender.Key); OscoapOption o = new OscoapOption(OptionType.Oscoap); o.Set(optionValue); response.AddOption(o); response.Code = (int)StatusCode.Changed; response.Payload = enc.GetEncryptedContent(); if (cs1 != null) { int cbOrig = response.Payload.Length; byte[] rgbOrig = response.Payload; byte[] signatureBytes = cs1.EncodeToCBORObject().GetByteString(); Array.Resize(ref rgbOrig, cbOrig + signatureBytes.Length); Array.Copy(signatureBytes, 0, rgbOrig, cbOrig, signatureBytes.Length); response.Payload = rgbOrig; } // Need to be able to retrieve this again under some circumstances. if (encryptedResponse.HasOption(OptionType.Block2)) { Request request = exchange.CurrentRequest; Exchange.KeyUri keyUri = new Exchange.KeyUri(request.URI, null, response.Destination); // Observe notification only send the first block, hence do not store them as ongoing if (exchange.OSCOAP_ResponseBlockStatus != null && !encryptedResponse.HasOption(OptionType.Observe)) { // Remember ongoing blockwise GET requests BlockHolder blockInfo = new BlockHolder(exchange); if (Utils.Put(_ongoingExchanges, keyUri, blockInfo) == null) { if (_Log.IsInfoEnabled) { _Log.Info("Ongoing Block2 started late, storing " + keyUri + " for " + request); } } else { if (_Log.IsInfoEnabled) { _Log.Info("Ongoing Block2 continued, storing " + keyUri + " for " + request); } } } else { if (_Log.IsInfoEnabled) { _Log.Info("Ongoing Block2 completed, cleaning up " + keyUri + " for " + request); } BlockHolder exc; _ongoingExchanges.TryRemove(keyUri, out exc); } } } base.SendResponse(nextLayer, exchange, response); }
public override void ReceiveResponse(INextLayer nextLayer, Exchange exchange, Response response) { if (response.HasOption(OptionType.Oscoap)) { Option op = response.GetFirstOption(OptionType.Oscoap); if (exchange.OscoreContext == null) { return; } _Log.Info($"Incoming message for OSCORE\n{Utils.ToString(response)}"); SecurityContext ctx = exchange.OscoreContext; bool fServerIv = true; Encrypt0Message msg = Uncompress(op.RawValue); if (msg == null) { return; } msg.SetEncryptedContent(response.Payload); SecurityContext.EntityContext recip = ctx.Recipient; if (ctx.IsGroupContext) { if (ctx.GroupId == null) { // This is not currently a valid state to be in return; } CBORObject kid = msg.FindAttribute(HeaderKeys.KeyId); if (kid == null) { // this is not currently a valid state to be in return; } recip = ctx.Recipients[kid.GetByteString()]; if (recip == null) { // M00TODO - deal with asking the user for a recipient structure at this point. return; } if (msg.FindAttribute(HeaderKeys.PartialIV) == null) { msg.AddAttribute(HeaderKeys.PartialIV, CBORObject.FromObject(ctx.Sender.PartialIV), Attributes.DO_NOT_SEND); fServerIv = false; } } else { if (msg.FindAttribute(HeaderKeys.PartialIV) == null) { msg.AddAttribute(HeaderKeys.PartialIV, CBORObject.FromObject(ctx.Sender.PartialIV), Attributes.DO_NOT_SEND); fServerIv = false; } } byte[] partialIV = msg.FindAttribute(HeaderKeys.PartialIV).GetByteString(); byte[] seqNoArray = new byte[8]; Array.Copy(partialIV, 0, seqNoArray, 8 - partialIV.Length, partialIV.Length); if (BitConverter.IsLittleEndian) { Array.Reverse(seqNoArray); } Int64 seqNo = BitConverter.ToInt64(seqNoArray, 0); if (fServerIv) { if (_replayWindow && recip.ReplayWindow.HitTest(seqNo)) { return; } } msg.AddAttribute(HeaderKeys.Algorithm, recip.Algorithm, Attributes.DO_NOT_SEND); CBORObject fullIV; if (fServerIv) { fullIV = recip.GetIV(partialIV); } else { fullIV = ctx.Sender.GetIV(partialIV); } msg.AddAttribute(HeaderKeys.IV, fullIV, Attributes.DO_NOT_SEND); // build aad CBORObject aad = CBORObject.NewArray(); aad.Add(1); // Version # aad.Add(CBORObject.NewArray()); aad[1].Add(recip.Algorithm); aad.Add(ctx.Sender.Id); aad.Add(ctx.Sender.PartialIV); aad.Add(CBORObject.FromObject(new byte[0])); // OPTIONS if (ctx.Sender.SigningAlgorithm != null) { aad[1].Add(ctx.Sender.SigningAlgorithm); if (ctx.CountersignParams != null) { aad[1].Add(ctx.CountersignParams); } if (ctx.CountersignKeyParams != null) { aad[1].Add(ctx.CountersignKeyParams); } } msg.SetExternalData(aad.EncodeToBytes()); _Log.Info(m => m($"fServerIv = {fServerIv}")); _Log.Info(m => m("ReceiveResponse: AAD = " + BitConverter.ToString(aad.EncodeToBytes()))); _Log.Info(m => m($"ReceiveResponse: IV = {BitConverter.ToString(fullIV.GetByteString())}")); _Log.Info(m => m($"ReceiveResponse: Key = {BitConverter.ToString(recip.Key)}")); if (ctx.IsGroupContext) { aad.Add(op.RawValue); int cbSignature = 64; // M00TODO Need to figure out the size of the signature from the context. byte[] rgbSignature = new byte[cbSignature]; byte[] rgbPayload = new byte[response.Payload.Length - cbSignature]; Array.Copy(response.Payload, rgbPayload, rgbPayload.Length); Array.Copy(response.Payload, rgbPayload.Length, rgbSignature, 0, cbSignature); CounterSignature1 cs1 = new CounterSignature1(rgbSignature); cs1.AddAttribute(HeaderKeys.Algorithm, ctx.Sender.SigningAlgorithm, Attributes.DO_NOT_SEND); cs1.SetObject(msg); cs1.SetKey(recip.SigningKey); byte[] aadData = aad.EncodeToBytes(); cs1.SetExternalData(aadData); msg.SetEncryptedContent(rgbPayload); try { if (!msg.Validate(cs1)) { return; } } catch (CoseException e) { // try the next possible one return; } } byte[] payload = msg.Decrypt(recip.Key); recip.ReplayWindow.SetHit(seqNo); byte[] rgb = new byte[payload.Length + _FixedHeader.Length - 1]; Array.Copy(_FixedHeader, rgb, _FixedHeader.Length); Array.Copy(payload, 1, rgb, _FixedHeader.Length, payload.Length - 1); rgb[1] = payload[0]; Codec.IMessageDecoder me = Spec.NewMessageDecoder(rgb); Response decryptedReq = me.DecodeResponse(); _Log.Info($"Inner message for OSCORE{Utils.ToString(decryptedReq)}"); response.Payload = decryptedReq.Payload; response.Code = (int)decryptedReq.StatusCode; RestoreOptions(response, decryptedReq); if (decryptedReq.HasOption(OptionType.Observe)) { if (partialIV.Length > 3) { byte[] x = new byte[3]; Array.Copy(partialIV, partialIV.Length - 3, x, 0, 3); partialIV = x; } response.AddOption(Option.Create(OptionType.Observe, partialIV)); } _Log.Info($"Outgoing message for OSCORE{Utils.ToString(response)}"); } base.ReceiveResponse(nextLayer, exchange, response); }
/// <inheritdoc /> public override void ReceiveRequest(INextLayer nextLayer, Exchange exchange, Request request) { if (!request.HasOption(OptionType.Oscoap)) { base.ReceiveRequest(nextLayer, exchange, request); return; } Response response; try { Option op = request.GetFirstOption(OptionType.Oscoap); request.RemoveOptions(OptionType.Oscoap); _Log.Info(m => m("Incoming Request: {0}", Utils.ToString(request))); Encrypt0Message msg = Uncompress(op.RawValue); if (msg == null) { // Only bother to reply to CON messages if (request.Type == MessageType.CON) { response = new Response(StatusCode.BadOption) { PayloadString = "Unable to decompress" }; exchange.SendResponse(response); } return; } msg.SetEncryptedContent(request.Payload); List <SecurityContext> contexts = new List <SecurityContext>(); SecurityContext ctx = null; CBORObject kid; // We may know the context because it is a follow up on a conversation - // In which case we can just use the same one. // M00BUG - Multicast problem of recipient ID? CBORObject gid = null; if (exchange.OscoreContext != null) { contexts.Add(exchange.OscoreContext); if (exchange.OscoreContext.GroupId != null) { gid = CBORObject.FromObject(exchange.OscoreContext.GroupId); } kid = CBORObject.FromObject(exchange.OscoapSenderId); } else { gid = msg.FindAttribute(HeaderKeys.KidContext); kid = msg.FindAttribute(HeaderKeys.KeyId); if (kid == null) { exchange.SendResponse(new Response(StatusCode.BadRequest)); return; } if (gid != null) { contexts = SecurityContextSet.AllContexts.FindByGroupId(gid.GetByteString()); } else { contexts = SecurityContextSet.AllContexts.FindByKid(kid.GetByteString()); } if (contexts.Count == 0) { response = new Response(StatusCode.Unauthorized) { PayloadString = "No Context Found - 1" }; exchange.SendResponse(response); return; // Ignore messages that have no known security context. } } byte[] partialIV = msg.FindAttribute(HeaderKeys.PartialIV).GetByteString(); // Build AAD CBORObject aad = CBORObject.NewArray(); aad.Add(CBORObject.FromObject(1)); // Version # aad.Add(CBORObject.NewArray()); // Array place holder aad[1].Add(CBORObject.FromObject(0)); // Place holder for algorithm aad.Add(CBORObject.FromObject(kid)); aad.Add(CBORObject.FromObject(partialIV)); aad.Add(CBORObject.FromObject(new byte[0])); // encoded I options byte[] payload = null; byte[] seqNoArray = new byte[8]; Array.Copy(partialIV, 0, seqNoArray, 8 - partialIV.Length, partialIV.Length); if (BitConverter.IsLittleEndian) { Array.Reverse(seqNoArray); } Int64 seqNo = BitConverter.ToInt64(seqNoArray, 0); string responseString = "General decrypt failure"; foreach (SecurityContext context in contexts) { SecurityContext.EntityContext recip = context.Recipient; if (gid != null) { if (recip != null) { if (!SecurityContext.ByteArrayComparer.AreEqual(recip.Id, kid.GetByteString())) { continue; } } else { if (context.Recipients.ContainsKey(kid.GetByteString())) { recip = context.Recipients[kid.GetByteString()]; } else { if (context.Locate == null) { continue; } recip = context.Locate(context, kid.GetByteString()); if (recip == null) { continue; } } } } if (false && _replayWindow && recip.ReplayWindow.HitTest(seqNo)) { _Log.Info(m => m("Hit test on {0} failed", seqNo)); responseString = "Hit test - duplicate"; continue; } else { _Log.Info(m => m("Hit test disabled")); } aad[1] = CBORObject.NewArray(); aad[1].Add(recip.Algorithm); if (context.Sender.SigningKey != null) { aad[1].Add(context.Sender.SigningKey[CoseKeyKeys.Algorithm]); if (context.CountersignParams != null) { aad[1].Add(context.CountersignParams); } if (context.CountersignKeyParams != null) { aad[1].Add(context.CountersignKeyParams); } int cbSignature = context.SignatureSize; byte[] rgbSignature = new byte[cbSignature]; byte[] rgbPayload = new byte[request.Payload.Length - cbSignature]; Array.Copy(request.Payload, rgbPayload, rgbPayload.Length); Array.Copy(request.Payload, rgbPayload.Length, rgbSignature, 0, cbSignature); CounterSignature1 cs1 = new CounterSignature1(rgbSignature); cs1.AddAttribute(HeaderKeys.Algorithm, context.Sender.SigningAlgorithm, Attributes.DO_NOT_SEND); cs1.SetObject(msg); cs1.SetKey(recip.SigningKey); aad.Add(op.RawValue); byte[] aadData = aad.EncodeToBytes(); cs1.SetExternalData(aadData); msg.SetEncryptedContent(rgbPayload); try { if (!msg.Validate(cs1)) { continue; } } catch (CoseException e) { // try the next possible one continue; } } if (aad.Count == 6) { aad.Remove(aad[5]); } if (_Log.IsInfoEnabled) { _Log.Info("AAD = " + BitConverter.ToString(aad.EncodeToBytes())); _Log.Info("IV = " + BitConverter.ToString(recip.GetIV(partialIV).GetByteString())); _Log.Info("Key = " + BitConverter.ToString(recip.Key)); } msg.SetExternalData(aad.EncodeToBytes()); msg.AddAttribute(HeaderKeys.Algorithm, recip.Algorithm, Attributes.DO_NOT_SEND); msg.AddAttribute(HeaderKeys.IV, recip.GetIV(partialIV), Attributes.DO_NOT_SEND); try { ctx = context; payload = msg.Decrypt(recip.Key); recip.ReplayWindow.SetHit(seqNo); } catch (Exception e) { if (_Log.IsInfoEnabled) { _Log.Info("--- ", e); } ctx = null; } if (ctx != null) { break; } } if (ctx == null) { if (request.Type == MessageType.CON || request.Type == MessageType.NON) { response = new Response(StatusCode.BadRequest) { PayloadString = responseString }; exchange.SendResponse(response); } return; } exchange.OscoreContext = ctx; // So we know it on the way back. request.OscoreContext = ctx; exchange.OscoapSequenceNumber = partialIV; exchange.OscoapSenderId = kid.GetByteString(); byte[] newRequestData = new byte[payload.Length + _FixedHeader.Length - 1]; Array.Copy(_FixedHeader, newRequestData, _FixedHeader.Length); Array.Copy(payload, 1, newRequestData, _FixedHeader.Length, payload.Length - 1); newRequestData[1] = payload[0]; Codec.IMessageDecoder me = Spec.NewMessageDecoder(newRequestData); Request newRequest = me.DecodeRequest(); // Update headers is a pain RestoreOptions(request, newRequest); request.Method = newRequest.Method; if (_Log.IsInfoEnabled) { // log.Info(String.Format("Secure message post = " + Util.Utils.ToString(request))); } // We may want a new exchange at this point if it relates to a new message for blockwise. if (request.HasOption(OptionType.Block2)) { Exchange.KeyUri keyUri = new Exchange.KeyUri(request.URI, null, request.Source); BlockHolder block; _ongoingExchanges.TryGetValue(keyUri, out block); if (block != null) { block.RestoreTo(exchange); } } request.Payload = newRequest.Payload; } catch (Exception e) { _Log.Error("OSCOAP Layer: reject message because ", e); exchange.OscoreContext = null; if (request.Type == MessageType.CON) { response = new Response(StatusCode.Unauthorized) { Payload = Encoding.UTF8.GetBytes("Error is " + e.Message) }; exchange.SendResponse(response); } // Ignore messages that we cannot decrypt. return; } base.ReceiveRequest(nextLayer, exchange, request); }