diff --git a/Example/Pods/Pods.xcodeproj/project.pbxproj b/Example/Pods/Pods.xcodeproj/project.pbxproj index 3422d09c7..a49cf3e2f 100644 --- a/Example/Pods/Pods.xcodeproj/project.pbxproj +++ b/Example/Pods/Pods.xcodeproj/project.pbxproj @@ -112,6 +112,7 @@ 51DF58B92722EE2CB876F4859B449F54 /* ConfigModelSubscriptionVirtualAddressAdd.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04AB807F1DC543A41014F525360355B2 /* ConfigModelSubscriptionVirtualAddressAdd.swift */; }; 51F397463EBD18BCD543C64FE1313C67 /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E749F10DEF7618DE8B1B122A5D982D /* Model.swift */; }; 5205603EB13F6ED8D95F47779C169397 /* ConfigNetworkTransmitStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91E287AB63C5554D1EB6D838BE420837 /* ConfigNetworkTransmitStatus.swift */; }; + 52A0041125BF267400A24C92 /* MeshNetwork+IvIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52A0041025BF267400A24C92 /* MeshNetwork+IvIndex.swift */; }; 52E8FF9825BB5EE90087B2A6 /* UserDefaults+SeqAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52E8FF9725BB5EE90087B2A6 /* UserDefaults+SeqAuth.swift */; }; 539B7D307CE6DAD58135573C2BD34CF9 /* NetworkKey+MeshNetwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D7FD4C6B9026F5AB76D7B586E2CF3AA /* NetworkKey+MeshNetwork.swift */; }; 5591B4463258E51A0030AA284239437D /* KeySet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8B4EFEACAC71F2189FC119255429A54 /* KeySet.swift */; }; @@ -498,6 +499,7 @@ 5208074594FCA179BDAEAC0AADD27CA0 /* GenericPowerLevelSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GenericPowerLevelSet.swift; sourceTree = ""; }; 52473EF190638F8B70BB7B106E956029 /* pem.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = pem.h; path = ios/include/openssl/pem.h; sourceTree = ""; }; 52513CFCF2954BAD169F4ECFE23DE170 /* LightHSLDefaultSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LightHSLDefaultSet.swift; sourceTree = ""; }; + 52A0041025BF267400A24C92 /* MeshNetwork+IvIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MeshNetwork+IvIndex.swift"; sourceTree = ""; }; 52B112AB6B61A75C4468FE0B87BC2E08 /* GenericLevelStatus.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GenericLevelStatus.swift; sourceTree = ""; }; 52E8FF9725BB5EE90087B2A6 /* UserDefaults+SeqAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+SeqAuth.swift"; sourceTree = ""; }; 54B5F2AC51F4BA0659354EDFEA843669 /* ConfigAppKeyList.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigAppKeyList.swift; sourceTree = ""; }; @@ -968,6 +970,7 @@ 54C79392C23E6C6C80BADBB1128D2385 /* MeshNetwork+Address.swift */, 8B24993BC14FE651A6291EA43346092D /* MeshNetwork+Groups.swift */, 7A1EE20A4079FA0FD46908C23E36A2BD /* MeshNetwork+Keys.swift */, + 52A0041025BF267400A24C92 /* MeshNetwork+IvIndex.swift */, 6A9EF7A64C9E0999B7075CC64F715284 /* MeshNetwork+Nodes.swift */, 94027D45A523DE08740F19D95DC482D7 /* MeshNetwork+Provisioner.swift */, FE0F0B561A8F7AD3CE2808681163D5EA /* MeshNetwork+Ranges.swift */, @@ -1787,6 +1790,7 @@ FD2C49651F9005B7A1FE05EFF5D439AB /* ConfigNetworkTransmitGet.swift in Sources */, BDF873F76B3E4C2003036A9BCB7DA5F5 /* ConfigNetworkTransmitSet.swift in Sources */, 5205603EB13F6ED8D95F47779C169397 /* ConfigNetworkTransmitStatus.swift in Sources */, + 52A0041125BF267400A24C92 /* MeshNetwork+IvIndex.swift in Sources */, 83C81DB8294B2DF953C357232AE7C63E /* ConfigNodeReset.swift in Sources */, B9A0002C55843CA7790940994B887AB2 /* ConfigNodeResetStatus.swift in Sources */, 77580D0D8FBCE90DCB4B15EB8F38CD6E /* ConfigRelayGet.swift in Sources */, diff --git a/nRFMeshProvision/Classes/Layers/Network Layer/NetworkLayer.swift b/nRFMeshProvision/Classes/Layers/Network Layer/NetworkLayer.swift index 494e6d556..f9f14725a 100644 --- a/nRFMeshProvision/Classes/Layers/Network Layer/NetworkLayer.swift +++ b/nRFMeshProvision/Classes/Layers/Network Layer/NetworkLayer.swift @@ -436,8 +436,3 @@ private extension NetworkLayer { } } - -private extension IvIndex { - static let timestampKey = "IVTimestamp" - static let ivRecoveryKey = "IVRecovery" -} diff --git a/nRFMeshProvision/Classes/Mesh API/MeshNetwork+IvIndex.swift b/nRFMeshProvision/Classes/Mesh API/MeshNetwork+IvIndex.swift new file mode 100644 index 000000000..c2437af81 --- /dev/null +++ b/nRFMeshProvision/Classes/Mesh API/MeshNetwork+IvIndex.swift @@ -0,0 +1,84 @@ +/* +* Copyright (c) 2021, Nordic Semiconductor +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, this +* list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, this +* list of conditions and the following disclaimer in the documentation and/or +* other materials provided with the distribution. +* +* 3. Neither the name of the copyright holder nor the names of its contributors may +* be used to endorse or promote products derived from this software without +* specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +* POSSIBILITY OF SUCH DAMAGE. +*/ + +import Foundation + +public extension MeshNetwork { + + /// Restores the last value of IV Index. + func restoreIvIndex() { + let defaults = UserDefaults(suiteName: uuid.uuidString)! + let map = defaults.object(forKey: IvIndex.indexKey) as? [String : Any] + ivIndex = IvIndex.fromMap(map) ?? IvIndex() + } + + /// Sets new value of IV Index and IV Update flag. + /// + /// This method allows setting the IV Index of the mesh network when the provisioner + /// is not connected to the network and did not receive the Secure Network beacon, + /// for example to provision a Node. + /// + /// Otherwise, if the local Node is connecting to the mesh network using GATT Proxy, + /// it will obtain the current IV Index automatically just after connection using the + /// Secure Network beacon, in which case calling this method is not necessary. + /// + /// - important: Mind, that it is no possible to revert IV Index to smaller value + /// (at least not using the public API). If you set too high IV Index + /// the phone will not be able to communicate with the mesh network. + /// Always use the current IV Index of the mesh network. + /// + /// - parameters: + /// - index: The new value of IV Index. + /// - updateActive: IV Update Active flag. + /// - throws: `MeshNetworkError.ivIndexTooSmall` when the new IV Index is + /// lower than the current one. + func setIvIndex(_ index: UInt32, updateActive: Bool) throws { + let newIvIndex = IvIndex(index: index, updateActive: updateActive) + + // Verify that the new IV Index is greater than or equal to the current one. + guard newIvIndex >= ivIndex else { + throw MeshNetworkError.ivIndexTooSmall + } + // If they are equal, we're done. + if ivIndex == newIvIndex { + return + } + // Update and save the IV Index. + ivIndex = newIvIndex + + let defaults = UserDefaults(suiteName: uuid.uuidString)! + defaults.set(ivIndex.asMap, forKey: IvIndex.indexKey) + // As the IV Index was set using abnormal operation, we have to assume that the + // IV Recovery is active. + defaults.set(true, forKey: IvIndex.ivRecoveryKey) + defaults.set(Date(), forKey: IvIndex.timestampKey) + } + +} diff --git a/nRFMeshProvision/Classes/Mesh Model/IvIndex.swift b/nRFMeshProvision/Classes/Mesh Model/IvIndex.swift index d5e0237e6..63f19fea6 100644 --- a/nRFMeshProvision/Classes/Mesh Model/IvIndex.swift +++ b/nRFMeshProvision/Classes/Mesh Model/IvIndex.swift @@ -63,6 +63,8 @@ internal struct IvIndex { } internal extension IvIndex { + static let timestampKey = "IVTimestamp" + static let ivRecoveryKey = "IVRecovery" static let indexKey = "IVIndex" /// Returns the IV Index as dictionary. @@ -94,7 +96,7 @@ extension IvIndex: Comparable { } -extension IvIndex { +internal extension IvIndex { /// The following IV Index, or `nil` if maximum value has been reached. var next: IvIndex? { diff --git a/nRFMeshProvision/Classes/MeshNetworkError.swift b/nRFMeshProvision/Classes/MeshNetworkError.swift index e75b60787..5ff9d80a4 100644 --- a/nRFMeshProvision/Classes/MeshNetworkError.swift +++ b/nRFMeshProvision/Classes/MeshNetworkError.swift @@ -80,6 +80,9 @@ public enum MeshNetworkError: Error { case noApplicationKey /// Thrown when trying to send a mesh message before setting up the mesh network. case noNetwork + /// Thrown when setting too small IV Index. The new IV Index must be greater + /// or equal to the previous one. + case ivIndexTooSmall } extension MeshNetworkError: LocalizedError { @@ -106,6 +109,7 @@ extension MeshNetworkError: LocalizedError { case .noNetworkKey: return NSLocalizedString("No Network Key.", comment: "") case .noApplicationKey: return NSLocalizedString("No Application Key.", comment: "") case .noNetwork: return NSLocalizedString("Mesh Network not created.", comment: "") + case .ivIndexTooSmall: return NSLocalizedString("IV Index too small", comment: "") } }