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"); }
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)); }
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)); }
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)); } }