Example #1
0
        public void When_Creating_Join_Request_Recreating_Should_Pass_Mic_Check(
            string appEUIText,
            string devEUIText,
            string appKeyText,
            string devNonceText)
        {
            var wrongAppKeyText = "00000000000000000000000000003333";

            // create a join request
            var devNonce = ConversionHelper.StringToByteArray(devNonceText);

            Array.Reverse(devNonce);

            var join = new LoRaPayloadJoinRequest(appEUIText, devEUIText, devNonce);

            Assert.Equal(appEUIText, join.GetAppEUIAsString());
            Assert.Equal(devEUIText, join.GetDevEUIAsString());
            var uplinkMessage = join.SerializeUplink(appKeyText);

            Assert.False(join.CheckMic(wrongAppKeyText), "Mic check with wrong appKey should not pass");
            Assert.True(join.CheckMic(appKeyText), "Mic check should work after setting it");

            var rxpk = uplinkMessage.Rxpk[0];

            Assert.True(LoRaPayload.TryCreateLoRaPayload(rxpk, out LoRaPayload parsedLoRaPayload));
            Assert.IsType <LoRaPayloadJoinRequest>(parsedLoRaPayload);
            var parsedLoRaJoinRequest = (LoRaPayloadJoinRequest)parsedLoRaPayload;

            Assert.True(parsedLoRaPayload.CheckMic(appKeyText), "Parsed join request should pass mic check with correct appKey");
            Assert.False(parsedLoRaJoinRequest.CheckMic(wrongAppKeyText), "Parsed join request should not pass mic check with wrong appKey");
        }
Example #2
0
        public void JoinRequest_Should_Succeed_Mic_Check()
        {
            var appEUIText  = "0005100000000004";
            var appEUIBytes = ConversionHelper.StringToByteArray(appEUIText);

            var devEUIText  = "0005100000000004";
            var devEUIBytes = ConversionHelper.StringToByteArray(devEUIText);

            var devNonceText  = "ABCD";
            var devNonceBytes = ConversionHelper.StringToByteArray(devNonceText);

            var appKey = "00000000000000000005100000000004";

            var joinRequest = new LoRaPayloadJoinRequest(appEUIText, devEUIText, devNonceBytes);

            joinRequest.SetMic(appKey);
            Assert.True(joinRequest.CheckMic(appKey));
            Assert.True(joinRequest.CheckMic(appKey)); // ensure multiple calls work!

            var rxpk = new LoRaTools.LoRaPhysical.Rxpk()
            {
                Chan = 7,
                Rfch = 1,
                Freq = 903.700000,
                Stat = 1,
                Modu = "LORA",
                Datr = "SF10BW125",
                Codr = "4/5",
                Rssi = -17,
                Lsnr = 12.0f,
            };

            var data = joinRequest.GetByteMessage();

            rxpk.Data = Convert.ToBase64String(data);
            rxpk.Size = (uint)data.Length;

            byte[] decodedJoinRequestBytes = Convert.FromBase64String(rxpk.Data);
            var    decodedJoinRequest      = new LoRaTools.LoRaMessage.LoRaPayloadJoinRequest(decodedJoinRequestBytes);

            Assert.True(decodedJoinRequest.CheckMic(appKey));
        }
Example #3
0
        public void When_Creating_Join_Request_From_Bytes_Should_Pass_Mic_Check(
            string appEUI,
            string devEUI,
            string appKey)
        {
            var rawJoinRequestBytes = new byte[] { 0, 4, 0, 0, 0, 0, 16, 229, 251, 4, 0, 0, 0, 0, 16, 229, 251, 254, 228, 147, 93, 188, 238 };
            var messageType         = rawJoinRequestBytes[0];

            Assert.Equal((int)LoRaMessageType.JoinRequest, messageType);
            var joinRequest = new LoRaPayloadJoinRequest(rawJoinRequestBytes);

            Assert.NotNull(joinRequest);
            Assert.Equal(appEUI, joinRequest.GetAppEUIAsString());
            Assert.Equal(devEUI, joinRequest.GetDevEUIAsString());
            Assert.True(joinRequest.CheckMic(appKey));
        }
Example #4
0
        private static void TestRxpk(Rxpk rxpk)
        {
            Assert.True(LoRaPayload.TryCreateLoRaPayload(rxpk, out LoRaPayload loRaPayload));
            Assert.Equal(LoRaMessageType.JoinRequest, loRaPayload.LoRaMessageType);
            LoRaPayloadJoinRequest joinRequestMessage = (LoRaPayloadJoinRequest)loRaPayload;

            byte[] joinRequestAppKey = new byte[16]
            {
                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
            };
            var joinRequestBool = joinRequestMessage.CheckMic(ConversionHelper.ByteArrayToString(joinRequestAppKey));

            if (!joinRequestBool)
            {
                Console.WriteLine("Join Request type was not computed correclty");
            }

            byte[] joinRequestAppEui = new byte[8]
            {
                1, 2, 3, 4, 1, 2, 3, 4
            };

            byte[] joinRequestDevEUI = new byte[8]
            {
                2, 3, 4, 5, 2, 3, 4, 5
            };
            byte[] joinRequestDevNonce = new byte[2]
            {
                16, 45
            };

            Array.Reverse(joinRequestAppEui);
            Array.Reverse(joinRequestDevEUI);
            Array.Reverse(joinRequestDevNonce);
            Assert.True(joinRequestMessage.AppEUI.ToArray().SequenceEqual(joinRequestAppEui));
            Assert.True(joinRequestMessage.DevEUI.ToArray().SequenceEqual(joinRequestDevEUI));
            Assert.True(joinRequestMessage.DevNonce.ToArray().SequenceEqual(joinRequestDevNonce));
        }
        /// <summary>
        /// Process OTAA join request
        /// </summary>
        async Task <DownlinkPktFwdMessage> ProcessJoinRequestAsync(Rxpk rxpk, LoRaPayloadJoinRequest joinReq, DateTime startTimeProcessing)
        {
            var timeWatcher = new LoRaOperationTimeWatcher(this.loraRegion, startTimeProcessing);

            using (var processLogger = new ProcessLogger(timeWatcher))
            {
                byte[] udpMsgForPktForwarder = new byte[0];

                var devEUI = joinReq.GetDevEUIAsString();
                var appEUI = joinReq.GetAppEUIAsString();

                // set context to logger
                processLogger.SetDevEUI(devEUI);

                var devNonce = joinReq.GetDevNonceAsString();
                Logger.Log(devEUI, $"join request received", LogLevel.Information);

                var loRaDevice = await this.deviceRegistry.GetDeviceForJoinRequestAsync(devEUI, appEUI, devNonce);

                if (loRaDevice == null)
                {
                    return(null);
                }

                if (string.IsNullOrEmpty(loRaDevice.AppKey))
                {
                    Logger.Log(loRaDevice.DevEUI, "join refused: missing AppKey for OTAA device", LogLevel.Error);
                    return(null);
                }

                if (loRaDevice.AppEUI != appEUI)
                {
                    Logger.Log(devEUI, "join refused: AppEUI for OTAA does not match device", LogLevel.Error);
                    return(null);
                }

                if (!joinReq.CheckMic(loRaDevice.AppKey))
                {
                    Logger.Log(devEUI, "join refused: invalid MIC", LogLevel.Error);
                    return(null);
                }

                // Make sure that is a new request and not a replay
                if (!string.IsNullOrEmpty(loRaDevice.DevNonce) && loRaDevice.DevNonce == devNonce)
                {
                    Logger.Log(devEUI, "join refused: DevNonce already used by this device", LogLevel.Information);
                    loRaDevice.IsOurDevice = false;
                    return(null);
                }

                // Check that the device is joining through the linked gateway and not another
                if (!string.IsNullOrEmpty(loRaDevice.GatewayID) && !string.Equals(loRaDevice.GatewayID, this.configuration.GatewayID, StringComparison.InvariantCultureIgnoreCase))
                {
                    Logger.Log(devEUI, $"join refused: trying to join not through its linked gateway, ignoring join request", LogLevel.Information);
                    loRaDevice.IsOurDevice = false;
                    return(null);
                }

                var netIdBytes = BitConverter.GetBytes(this.configuration.NetId);
                var netId      = new byte[3]
                {
                    netIdBytes[0],
                    netIdBytes[1],
                    netIdBytes[2]
                };
                var appNonce      = OTAAKeysGenerator.GetAppNonce();
                var appNonceBytes = LoRaTools.Utils.ConversionHelper.StringToByteArray(appNonce);
                var appKeyBytes   = LoRaTools.Utils.ConversionHelper.StringToByteArray(loRaDevice.AppKey);
                var appSKey       = OTAAKeysGenerator.CalculateKey(new byte[1] {
                    0x02
                }, appNonceBytes, netId, joinReq.DevNonce, appKeyBytes);
                var nwkSKey = OTAAKeysGenerator.CalculateKey(new byte[1] {
                    0x01
                }, appNonceBytes, netId, joinReq.DevNonce, appKeyBytes);
                var devAddr = OTAAKeysGenerator.GetNwkId(netId);

                if (!timeWatcher.InTimeForJoinAccept())
                {
                    // in this case it's too late, we need to break and avoid saving twins
                    Logger.Log(devEUI, $"join refused: processing of the join request took too long, sending no message", LogLevel.Information);
                    return(null);
                }

                Logger.Log(loRaDevice.DevEUI, $"saving join properties twins", LogLevel.Debug);
                var deviceUpdateSucceeded = await loRaDevice.UpdateAfterJoinAsync(devAddr, nwkSKey, appSKey, appNonce, devNonce, LoRaTools.Utils.ConversionHelper.ByteArrayToString(netId));

                Logger.Log(loRaDevice.DevEUI, $"done saving join properties twins", LogLevel.Debug);

                if (!deviceUpdateSucceeded)
                {
                    Logger.Log(devEUI, $"join refused: join request could not save twins", LogLevel.Error);
                    return(null);
                }

                var windowToUse = timeWatcher.ResolveJoinAcceptWindowToUse(loRaDevice);
                if (windowToUse == 0)
                {
                    Logger.Log(devEUI, $"join refused: processing of the join request took too long, sending no message", LogLevel.Information);
                    return(null);
                }

                double freq = 0;
                string datr = null;
                uint   tmst = 0;
                if (windowToUse == 1)
                {
                    try
                    {
                        datr = this.loraRegion.GetDownstreamDR(rxpk);
                        freq = this.loraRegion.GetDownstreamChannelFrequency(rxpk);
                    }
                    catch (RegionLimitException ex)
                    {
                        Logger.Log(devEUI, ex.ToString(), LogLevel.Error);
                    }

                    // set tmst for the normal case
                    tmst = rxpk.Tmst + this.loraRegion.Join_accept_delay1 * 1000000;
                }
                else
                {
                    Logger.Log(devEUI, $"processing of the join request took too long, using second join accept receive window", LogLevel.Information);
                    tmst = rxpk.Tmst + this.loraRegion.Join_accept_delay2 * 1000000;
                    if (string.IsNullOrEmpty(this.configuration.Rx2DataRate))
                    {
                        Logger.Log(devEUI, $"using standard second receive windows for join request", LogLevel.Information);
                        // using EU fix DR for RX2
                        freq = this.loraRegion.RX2DefaultReceiveWindows.frequency;
                        datr = this.loraRegion.DRtoConfiguration[RegionFactory.CurrentRegion.RX2DefaultReceiveWindows.dr].configuration;
                    }
                    else
                    {
                        Logger.Log(devEUI, $"using custom  second receive windows for join request", LogLevel.Information);
                        freq = this.configuration.Rx2DataFrequency;
                        datr = this.configuration.Rx2DataRate;
                    }
                }

                loRaDevice.IsOurDevice = true;
                this.deviceRegistry.UpdateDeviceAfterJoin(loRaDevice);

                // Build join accept downlink message
                Array.Reverse(netId);
                Array.Reverse(appNonceBytes);

                return(this.CreateJoinAcceptDownlinkMessage(
                           netId,
                           loRaDevice.AppKey,
                           devAddr,
                           appNonceBytes,
                           datr,
                           freq,
                           tmst,
                           devEUI));
            }
        }