예제 #1
0
        public async Task GetDeviceForJoinRequestAsync_When_Join_Handled_By_Other_Cache_Is_Updated(bool joinedDevice)
        {
            var devNonce   = new DevNonce(1);
            var apiService = new Mock <LoRaDeviceAPIServiceBase>();
            var otaaDevice = TestDeviceInfo.CreateOTAADevice(1);

            if (joinedDevice)
            {
                otaaDevice.AppSKey = new AppSessionKey();
            }

            var simulatedDevice = new SimulatedDevice(otaaDevice);

            apiService.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, simulatedDevice.DevEUI, devNonce))
            .ReturnsAsync(new SearchDevicesResult()
            {
                IsDevNonceAlreadyUsed = true
            });

            DeviceCache.Register(CreateLoRaDevice(simulatedDevice));
            using var target = new LoRaDeviceRegistry(ServerConfiguration, this.cache, apiService.Object, this.loraDeviceFactoryMock.Object, DeviceCache);

            Assert.Null(await target.GetDeviceForJoinRequestAsync(simulatedDevice.DevEUI, devNonce));
            Assert.Equal(joinedDevice, !DeviceCache.TryGetByDevEui(simulatedDevice.DevEUI, out _));
        }
        public static byte[] GetBytes(this DevNonce devNonce)
        {
            var bytes = new byte[DevNonce.Size];

            _ = devNonce.Write(bytes);
            return(bytes);
        }
예제 #3
0
 public LoRaPayloadJoinRequest(JoinEui joinEui, DevEui devEui, DevNonce devNonce, Mic mic)
 {
     MHdr     = new MacHeader(MacMessageType.JoinRequest);
     AppEui   = joinEui;
     DevEUI   = devEui;
     DevNonce = devNonce;
     Mic      = mic;
 }
예제 #4
0
 public JoinRequestFrame(MacHeader mHdr, JoinEui joinEui, DevEui devEui, DevNonce devNonce, Mic mic, RadioMetadata radioMetadata)
 {
     MacHeader     = mHdr;
     JoinEui       = joinEui;
     DevEui        = devEui;
     DevNonce      = devNonce;
     Mic           = mic;
     RadioMetadata = radioMetadata;
 }
        /// <summary>
        /// Searchs for devices that match the join request.
        /// </summary>
        public async Task <LoRaDevice> GetDeviceForJoinRequestAsync(DevEui devEUI, DevNonce devNonce)
        {
            this.logger.LogDebug("querying the registry for OTAA device");

            var searchDeviceResult = await this.loRaDeviceAPIService.SearchAndLockForJoinAsync(
                gatewayID : this.configuration.GatewayID,
                devEUI : devEUI,
                devNonce : devNonce);

            if (searchDeviceResult.IsDevNonceAlreadyUsed)
            {
                // another gateway processed the join request. If we have it in the cache
                // with existing session keys, we need to invalidate that entry, to ensure
                // it gets re-fetched on the next message
                if (this.deviceCache.TryGetByDevEui(devEUI, out var someDevice) && someDevice.AppSKey != null)
                {
                    _ = this.deviceCache.Remove(someDevice);
                    this.logger.LogDebug("Device was removed from cache.");
                }

                this.logger.LogInformation("join refused: Join already processed by another gateway.");
                return(null);
            }

            if (searchDeviceResult?.Devices == null || searchDeviceResult.Devices.Count == 0)
            {
                this.logger.LogInformation(searchDeviceResult.RefusedMessage ?? "join refused: no devices found matching join request");
                return(null);
            }

            var matchingDeviceInfo = searchDeviceResult.Devices[0];

            if (deviceCache.TryGetByDevEui(matchingDeviceInfo.DevEUI, out var cachedDevice))
            {
                // if we already have the device in the cache, then it is either from a previous
                // join rquest or it's a re-join. Both scenarios are ok, and we can use the cached
                // information.
                return(cachedDevice);
            }

            var loader     = GetOrCreateJoinDeviceLoader(matchingDeviceInfo);
            var loRaDevice = await loader.LoadAsync();

            if (!loader.CanCache)
            {
                RemoveJoinDeviceLoader(devEUI);
            }

            return(loRaDevice);
        }
예제 #6
0
        public async Task GetDeviceForJoinRequestAsync_When_Device_Api_Throws_Error_Should_Not_Catch()
        {
            var devEui   = new DevEui(1);
            var devNonce = new DevNonce(1);

            var apiService = new Mock <LoRaDeviceAPIServiceBase>();

            apiService.Setup(x => x.SearchAndLockForJoinAsync(ServerConfiguration.GatewayID, devEui, devNonce))
            .Throws(new InvalidOperationException());
            using var target = new LoRaDeviceRegistry(ServerConfiguration, this.cache, apiService.Object, this.loraDeviceFactoryMock.Object, DeviceCache);

            Task Act() => target.GetDeviceForJoinRequestAsync(devEui, devNonce);

            _ = await Assert.ThrowsAsync <InvalidOperationException>(Act);

            // Device was searched by DevAddr
            apiService.VerifyAll();
        }
예제 #7
0
        // don't work with CFLIST atm
        private static byte[] CalculateKey(SessionKeyType type, AppNonce appNonce, NetId netId, DevNonce devNonce, AppKey appKey)
        {
            using var aes = Aes.Create("AesManaged");
            var rawAppKey = new byte[AppKey.Size];

            _       = appKey.Write(rawAppKey);
            aes.Key = rawAppKey;
#pragma warning disable CA5358 // Review cipher mode usage with cryptography experts
            // Cipher is part of the LoRaWAN specification
            aes.Mode = CipherMode.ECB;
#pragma warning restore CA5358 // Review cipher mode usage with cryptography experts
            aes.Padding = PaddingMode.None;

            var buffer = new byte[1 + AppNonce.Size + NetId.Size + DevNonce.Size + 7];
            var pt     = buffer.AsSpan();
            Debug.Assert(pt.Length == 16);
            pt[0] = unchecked ((byte)type);
            pt    = pt[1..];
예제 #8
0
 public static AppSessionKey CalculateAppSessionKey(AppNonce appNonce, NetId netId, DevNonce devNonce, AppKey appKey) =>
 AppSessionKey.Read(CalculateKey(SessionKeyType.Application, appNonce, netId, devNonce, appKey));
예제 #9
0
 public static NetworkSessionKey CalculateNetworkSessionKey(AppNonce appNonce, NetId netId, DevNonce devNonce, AppKey appKey) =>
 NetworkSessionKey.Read(CalculateKey(SessionKeyType.Network, appNonce, netId, devNonce, appKey));
 /// <summary>
 /// Search and locks device for join request.
 /// </summary>
 public abstract Task <SearchDevicesResult> SearchAndLockForJoinAsync(string gatewayID, DevEui devEUI, DevNonce devNonce);
 internal sealed record JoinMessageKey(JoinEui JoinEui, DevEui DevEui, DevNonce DevNonce);
 /// <inheritdoc />
 public sealed override Task <SearchDevicesResult> SearchAndLockForJoinAsync(string gatewayID, DevEui devEUI, DevNonce devNonce)
 => SearchDevicesAsync(gatewayID: gatewayID, devEui: devEUI, devNonce: devNonce);