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