/// <summary> /// Code Performing the OTAA /// </summary> /// <param name="GatewayID"></param> /// <param name="DevEUI"></param> /// <param name="AppEUI"></param> /// <param name="DevNonce"></param> /// <returns></returns> public static async Task <LoraDeviceInfo> PerformOTAAAsync(string GatewayID, string DevEUI, string AppEUI, string DevNonce, LoraDeviceInfo joinLoraDeviceInfo) { string AppSKey; string NwkSKey; string DevAddr; string AppNonce; IoTHubDeviceInfo iotHubDeviceInfo; if (DevEUI == null || AppEUI == null || DevNonce == null) { string errorMsg = "Missing devEUI/AppEUI/DevNonce in the OTAARequest"; //log.Error(errorMsg); Logger.Log(DevEUI, errorMsg, Logger.LoggingLevel.Error); return(null); } if (joinLoraDeviceInfo == null) { joinLoraDeviceInfo = new LoraDeviceInfo(); } joinLoraDeviceInfo.DevEUI = DevEUI; ////we don't have the key to access iot hub query the registry //if (joinLoraDeviceInfo.PrimaryKey == null) //{ Logger.Log(DevEUI, $"querying the registry for device key", Logger.LoggingLevel.Info); var client = GetHttpClient(); var url = String.Concat($"{FacadeServerUrl}GetDevice?", $"{FacadeAuthCode}" == "" ? "" : $"code={FacadeAuthCode}&", $"DevEUI={DevEUI}&DevNonce={DevNonce}&GatewayId={GatewayID}"); HttpResponseMessage response = await client.GetAsync(url); if (!response.IsSuccessStatusCode) { if (response.StatusCode == System.Net.HttpStatusCode.BadRequest) { var badReqResult = await response.Content.ReadAsStringAsync(); if (!String.IsNullOrEmpty(badReqResult) && badReqResult == "UsedDevNonce") { Logger.Log(DevEUI, $"DevNonce already used by this device", Logger.LoggingLevel.Info); return(null); } } Logger.Log(DevEUI, $"error calling façade api: {response.ReasonPhrase} check the azure function log", Logger.LoggingLevel.Error); return(null); } var result = await response.Content.ReadAsStringAsync(); List <IoTHubDeviceInfo> iotHubDeviceInfos = ((List <IoTHubDeviceInfo>)JsonConvert.DeserializeObject(result, typeof(List <IoTHubDeviceInfo>))); if (iotHubDeviceInfos.Count == 0) { joinLoraDeviceInfo.IsJoinValid = false; joinLoraDeviceInfo.IsOurDevice = false; return(joinLoraDeviceInfo); } else { iotHubDeviceInfo = iotHubDeviceInfos[0]; joinLoraDeviceInfo.PrimaryKey = iotHubDeviceInfo.PrimaryKey; } //} joinLoraDeviceInfo.HubSender = new IoTHubConnector(joinLoraDeviceInfo.DevEUI, joinLoraDeviceInfo.PrimaryKey); //we don't have yet the twin data so we need to get it if (joinLoraDeviceInfo.AppKey == null || joinLoraDeviceInfo.AppEUI == null) { Logger.Log(DevEUI, $"getting twins for OTAA for device", Logger.LoggingLevel.Info); var twin = await joinLoraDeviceInfo.HubSender.GetTwinAsync(); if (twin != null) { joinLoraDeviceInfo.IsOurDevice = true; if (!twin.Properties.Desired.Contains("AppEUI")) { string errorMsg = $"missing AppEUI for OTAA for device"; Logger.Log(DevEUI, errorMsg, Logger.LoggingLevel.Error); return(null); } else { joinLoraDeviceInfo.AppEUI = twin.Properties.Desired["AppEUI"].Value; } //Make sure that there is the AppKey if not we cannot do the OTAA if (!twin.Properties.Desired.Contains("AppKey")) { string errorMsg = $"missing AppKey for OTAA for device"; Logger.Log(DevEUI, errorMsg, Logger.LoggingLevel.Error); return(null); } else { joinLoraDeviceInfo.AppKey = twin.Properties.Desired["AppKey"].Value; } //Make sure that is a new request and not a replay if (twin.Properties.Reported.Contains("DevNonce")) { joinLoraDeviceInfo.DevNonce = twin.Properties.Reported["DevNonce"]; } if (twin.Properties.Desired.Contains("GatewayID")) { joinLoraDeviceInfo.GatewayID = twin.Properties.Desired["GatewayID"].Value; } if (twin.Properties.Desired.Contains("SensorDecoder")) { joinLoraDeviceInfo.SensorDecoder = twin.Properties.Desired["SensorDecoder"].Value; } Logger.Log(DevEUI, $"done getting twins for OTAA device", Logger.LoggingLevel.Info); } else { Logger.Log(DevEUI, $"failed getting twins for OTAA device", Logger.LoggingLevel.Error); return(null); } } else { Logger.Log(DevEUI, $"using cached twins for OTAA device", Logger.LoggingLevel.Info); } //We add it to the cache so the next join has already the data, important for offline Cache.AddToCache(DevEUI, joinLoraDeviceInfo); //Make sure that there is the AppEUI and it matches if not we cannot do the OTAA if (joinLoraDeviceInfo.AppEUI != AppEUI) { string errorMsg = $"AppEUI for OTAA does not match for device"; Logger.Log(DevEUI, errorMsg, Logger.LoggingLevel.Error); return(null); } //Make sure that is a new request and not a replay if (!String.IsNullOrEmpty(joinLoraDeviceInfo.DevNonce) && joinLoraDeviceInfo.DevNonce == DevNonce) { string errorMsg = $"DevNonce already used by this device"; Logger.Log(DevEUI, errorMsg, Logger.LoggingLevel.Info); joinLoraDeviceInfo.IsJoinValid = false; return(joinLoraDeviceInfo); } //Check that the device is joining through the linked gateway and not another if (!String.IsNullOrEmpty(joinLoraDeviceInfo.GatewayID) && joinLoraDeviceInfo.GatewayID.ToUpper() != GatewayID.ToUpper()) { string errorMsg = $"not the right gateway device-gateway:{joinLoraDeviceInfo.GatewayID} current-gateway:{GatewayID}"; Logger.Log(DevEUI, errorMsg, Logger.LoggingLevel.Info); joinLoraDeviceInfo.IsJoinValid = false; return(joinLoraDeviceInfo); } byte[] netId = new byte[3] { 0, 0, 1 }; AppNonce = OTAAKeysGenerator.getAppNonce(); AppSKey = OTAAKeysGenerator.calculateKey(new byte[1] { 0x02 }, ConversionHelper.StringToByteArray(AppNonce), netId, ConversionHelper.StringToByteArray(DevNonce), ConversionHelper.StringToByteArray(joinLoraDeviceInfo.AppKey)); NwkSKey = OTAAKeysGenerator.calculateKey(new byte[1] { 0x01 }, ConversionHelper.StringToByteArray(AppNonce), netId, ConversionHelper.StringToByteArray(DevNonce), ConversionHelper.StringToByteArray(joinLoraDeviceInfo.AppKey));; DevAddr = OTAAKeysGenerator.getDevAddr(netId); joinLoraDeviceInfo.DevAddr = DevAddr; joinLoraDeviceInfo.NwkSKey = NwkSKey; joinLoraDeviceInfo.AppSKey = AppSKey; joinLoraDeviceInfo.AppNonce = AppNonce; joinLoraDeviceInfo.DevNonce = DevNonce; joinLoraDeviceInfo.NetId = BitConverter.ToString(netId).Replace("-", "");; //Accept the JOIN Request and the futher messages joinLoraDeviceInfo.IsJoinValid = true; return(joinLoraDeviceInfo); }