/// <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}", Util.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.OscoapContext != null) { contexts.Add(exchange.OscoapContext); if (exchange.OscoapContext.GroupId != null) { gid = CBORObject.FromObject(exchange.OscoapContext.GroupId); } kid = CBORObject.FromObject(exchange.OscoapSenderId); } else { gid = msg.FindAttribute(CBORObject.FromObject("gid")); 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)); // M00BUG 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 if (gid != null) { aad.Add(gid); } 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 (recip == null) { if (kid == null) { continue; } recip = context.Recipients[kid.GetByteString()]; if (recip == null) { continue; } } if (_replayWindow && recip.ReplayWindow.HitTest(seqNo)) { _Log.Info(m => m("Hit test on {0} failed", seqNo)); responseString = "Hit test - duplicate"; continue; } aad[1][0] = recip.Algorithm; 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) { response = new Response(StatusCode.BadRequest) { PayloadString = responseString }; exchange.SendResponse(response); } return; } exchange.OscoapContext = ctx; // So we know it on the way back. request.OscoapContext = 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.OscoapContext = 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); }
public override void SendResponse(INextLayer nextLayer, Exchange exchange, Response response) { if (exchange.OscoapContext != null) { SecurityContext ctx = exchange.OscoapContext; 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])); // Options if (ctx.GroupId != null) { aad.Add(ctx.GroupId); } 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) || ctx.GroupId != null) { 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.GroupId != null) { 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)}")); enc.AddAttribute(HeaderKeys.Algorithm, ctx.Sender.Algorithm, Attributes.DO_NOT_SEND); enc.Encrypt(ctx.Sender.Key); byte[] finalBody = DoCompression(enc); OscoapOption o = new OscoapOption(OptionType.Oscoap); o.Set(finalBody); response.AddOption(o); response.StatusCode = StatusCode.Content; response.Payload = enc.GetEncryptedContent(); // Need to be able to retrieve this again undersome cirumstances. 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 (Util.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); }
private void OnExchangeCompleted(Object sender, EventArgs e) { Exchange exchange = (Exchange)sender; /* * Logging in this method leads to significant performance loss. * Uncomment logging code only for debugging purposes. */ if (exchange.Origin == Origin.Local) { // this endpoint created the Exchange by issuing a request Exchange.KeyID keyID = new Exchange.KeyID(exchange.CurrentRequest.ID, null); Exchange.KeyToken keyToken = new Exchange.KeyToken(exchange.CurrentRequest.Token); if (log.IsDebugEnabled) log.Debug("Exchange completed: Cleaning up " + keyToken); _exchangesByToken.Remove(keyToken); // in case an empty ACK was lost _exchangesByID.Remove(keyID); } else // Origin.Remote { // this endpoint created the Exchange to respond a request Response response = exchange.CurrentResponse; if (response != null && response.Type != MessageType.ACK) { // only response MIDs are stored for ACK and RST, no reponse Tokens Exchange.KeyID midKey = new Exchange.KeyID(response.ID, null); //if (log.IsDebugEnabled) // log.Debug("Remote ongoing completed, cleaning up " + midKey); _exchangesByID.Remove(midKey); } Request request = exchange.CurrentRequest; if (request != null && (request.HasOption(OptionType.Block1) || response.HasOption(OptionType.Block2))) { Exchange.KeyUri uriKey = new Exchange.KeyUri(request.URI, request.Source); if (log.IsDebugEnabled) log.Debug("Remote ongoing completed, cleaning up " + uriKey); Exchange exc; _ongoingExchanges.TryRemove(uriKey, out exc); } // Remove all remaining NON-notifications if this exchange is an observe relation ObserveRelation relation = exchange.Relation; if (relation != null) { RemoveNotificatoinsOf(relation); } } }
/// <inheritdoc/> public Exchange ReceiveRequest(Request request) { /* * This request could be * - Complete origin request => deliver with new exchange * - One origin block => deliver with ongoing exchange * - Complete duplicate request or one duplicate block (because client got no ACK) * => * if ACK got lost => resend ACK * if ACK+response got lost => resend ACK+response * if nothing has been sent yet => do nothing * (Retransmission is supposed to be done by the retransm. layer) */ Exchange.KeyID keyId = new Exchange.KeyID(request.ID, request.Source); /* * The differentiation between the case where there is a Block1 or * Block2 option and the case where there is none has the advantage that * all exchanges that do not need blockwise transfer have simpler and * faster code than exchanges with blockwise transfer. */ if (!request.HasOption(OptionType.Block1) && !request.HasOption(OptionType.Block2)) { Exchange exchange = new Exchange(request, Origin.Remote); Exchange previous = _deduplicator.FindPrevious(keyId, exchange); if (previous == null) { exchange.Completed += OnExchangeCompleted; return exchange; } else { if (log.IsInfoEnabled) log.Info("Duplicate request: " + request); request.Duplicate = true; return previous; } } else { Exchange.KeyUri keyUri = new Exchange.KeyUri(request.URI, request.Source); if (log.IsDebugEnabled) log.Debug("Looking up ongoing exchange for " + keyUri); Exchange ongoing; if (_ongoingExchanges.TryGetValue(keyUri, out ongoing)) { Exchange prev = _deduplicator.FindPrevious(keyId, ongoing); if (prev != null) { if (log.IsInfoEnabled) log.Info("Duplicate ongoing request: " + request); request.Duplicate = true; } else { // the exchange is continuing, we can (i.e., must) clean up the previous response if (ongoing.CurrentResponse.Type != MessageType.ACK && !ongoing.CurrentResponse.HasOption(OptionType.Observe)) { keyId = new Exchange.KeyID(ongoing.CurrentResponse.ID, null); if (log.IsDebugEnabled) log.Debug("Ongoing exchange got new request, cleaning up " + keyId); _exchangesByID.Remove(keyId); } } return ongoing; } else { // We have no ongoing exchange for that request block. /* * Note the difficulty of the following code: The first message * of a blockwise transfer might arrive twice due to a * retransmission. The new Exchange must be inserted in both the * hash map 'ongoing' and the deduplicator. They must agree on * which exchange they store! */ Exchange exchange = new Exchange(request, Origin.Remote); Exchange previous = _deduplicator.FindPrevious(keyId, exchange); if (previous == null) { if (log.IsDebugEnabled) log.Debug("New ongoing request, storing " + keyUri + " for " + request); exchange.Completed += OnExchangeCompleted; _ongoingExchanges[keyUri] = exchange; return exchange; } else { if (log.IsInfoEnabled) log.Info("Duplicate initial request: " + request); request.Duplicate = true; return previous; } } // if ongoing } // if blockwise }
/// <inheritdoc/> public void SendResponse(Exchange exchange, Response response) { if (response.ID == Message.None) response.ID = System.Threading.Interlocked.Increment(ref _currentID) % (1 << 16); /* * The response is a CON or NON or ACK and must be prepared for these * - CON => ACK / RST // we only care to stop retransmission * - NON => RST // we only care for observe * - ACK => nothing! * If this response goes lost, we must be prepared to get the same * CON/NON request with same MID again. We then find the corresponding * exchange and the ReliabilityLayer resends this response. */ // If this is a CON notification we now can forget all previous NON notifications if (response.Type == MessageType.CON || response.Type == MessageType.ACK) { ObserveRelation relation = exchange.Relation; if (relation != null) { RemoveNotificatoinsOf(relation); } } // Blockwise transfers are identified by URI and remote endpoint if (response.HasOption(OptionType.Block2)) { Request request = exchange.CurrentRequest; Exchange.KeyUri keyUri = new Exchange.KeyUri(request.URI, response.Destination); // Observe notifications only send the first block, hence do not store them as ongoing if (exchange.ResponseBlockStatus != null && !response.HasOption(OptionType.Observe)) { // Remember ongoing blockwise GET requests if (Utils.Put(_ongoingExchanges, keyUri, exchange) == null) { if (log.IsDebugEnabled) log.Debug("Ongoing Block2 started late, storing " + keyUri + " for " + request); } else { if (log.IsDebugEnabled) log.Debug("Ongoing Block2 continued, storing " + keyUri + " for " + request); } } else { if (log.IsDebugEnabled) log.Debug("Ongoing Block2 completed, cleaning up " + keyUri + " for " + request); Exchange exc; _ongoingExchanges.TryRemove(keyUri, out exc); } } // Insert CON and NON to match ACKs and RSTs to the exchange // Do not insert ACKs and RSTs. if (response.Type == MessageType.CON || response.Type == MessageType.NON) { Exchange.KeyID keyID = new Exchange.KeyID(response.ID, null); _exchangesByID[keyID] = exchange; } // Only CONs and Observe keep the exchange active if (response.Type != MessageType.CON && response.Last) { exchange.Complete = true; } }
/// <inheritdoc/> public void SendResponse(Exchange exchange, Response response) { if (response.ID == Message.None) response.ID = System.Threading.Interlocked.Increment(ref _currentID) % (1 << 16); /* * The response is a CON or NON or ACK and must be prepared for these * - CON => ACK/RST // we only care to stop retransmission * - NON => RST // we don't care * - ACK => nothing! * If this response goes lost, we must be prepared to get the same * CON/NON request with same MID again. We then find the corresponding * exchange and the ReliabilityLayer resends this response. */ if (response.Destination == null) throw new InvalidOperationException("Response has no destination set"); // If this is a CON notification we now can forget all previous NON notifications if (response.Type == MessageType.CON || response.Type == MessageType.ACK) { ObserveRelation relation = exchange.Relation; if (relation != null) { RemoveNotificatoinsOf(relation); } } if (response.HasOption(OptionType.Block2)) { Request request = exchange.Request; Exchange.KeyUri keyUri = new Exchange.KeyUri(request.URI, response.Destination); if (exchange.ResponseBlockStatus != null && !response.HasOption(OptionType.Observe)) { // Remember ongoing blockwise GET requests if (log.IsDebugEnabled) log.Debug("Ongoing Block2 started, storing " + keyUri + "\nOngoing " + request + "\nOngoing " + response); _ongoingExchanges[keyUri] = exchange; } else { if (log.IsDebugEnabled) log.Debug("Ongoing Block2 completed, cleaning up " + keyUri + "\nOngoing " + request + "\nOngoing " + response); _ongoingExchanges.Remove(keyUri); } } // Insert CON and NON to match ACKs and RSTs to the exchange // Do not insert ACKs and RSTs. if (response.Type == MessageType.CON || response.Type == MessageType.NON) { Exchange.KeyID keyID = new Exchange.KeyID(response.ID, response.Destination); _exchangesByID[keyID] = exchange; } if (response.Type == MessageType.ACK || response.Type == MessageType.NON) { // Since this is an ACK or NON, the exchange is over with sending this response. if (response.Last) { exchange.Complete = true; } } // else this is a CON and we need to wait for the ACK or RST }
private void OnExchangeCompleted(Object sender, EventArgs e) { Exchange exchange = (Exchange)sender; /* * Logging in this method leads to significant performance loss. * Uncomment logging code only for debugging purposes. */ if (exchange.Origin == Origin.Local) { // this endpoint created the Exchange by issuing a request Request request = exchange.Request; Exchange.KeyToken keyToken = new Exchange.KeyToken(exchange.CurrentRequest.Token, request.Destination); Exchange.KeyID keyID = new Exchange.KeyID(request.ID, request.Destination); if (log.IsDebugEnabled) log.Debug("Exchange completed: Cleaning up " + keyToken); _exchangesByToken.Remove(keyToken); // in case an empty ACK was lost _exchangesByID.Remove(keyID); } else { // this endpoint created the Exchange to respond a request Request request = exchange.CurrentRequest; if (request != null) { // TODO: We can optimize this and only do it, when the request really had blockwise transfer Exchange.KeyUri uriKey = new Exchange.KeyUri(request.URI, request.Source); //if (log.IsDebugEnabled) // log.Debug("++++++++++++++++++Remote ongoing completed, cleaning up "+uriKey); _ongoingExchanges.Remove(uriKey); } // TODO: What if the request is only a block? // TODO: This should only happen if the transfer was blockwise Response response = exchange.Response; if (response != null) { // only response MIDs are stored for ACK and RST, no reponse Tokens Exchange.KeyID midKey = new Exchange.KeyID(response.ID, response.Destination); //if (log.IsDebugEnabled) // log.Debug("++++++++++++++++++Remote ongoing completed, cleaning up " + midKey); _exchangesByID.Remove(midKey); } // Remove all remaining NON-notifications if this exchange is an observe relation ObserveRelation relation = exchange.Relation; if (relation != null) { RemoveNotificatoinsOf(relation); } } }