Beispiel #1
0
        /// <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);
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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);
                }
            }
        }
Beispiel #4
0
        /// <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
        }
Beispiel #5
0
        /// <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;
            }
        }
Beispiel #6
0
        /// <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
        }
Beispiel #7
0
        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);
                }
            }
        }