From 2e0f602d6ef9c412eb76534b0265268e37f377f8 Mon Sep 17 00:00:00 2001 From: Fabian Boemer Date: Mon, 15 Jul 2024 09:39:01 -0700 Subject: [PATCH] Remove use of unsafe flags in libraries (#14) --- Benchmarks/PolyBenchmark/PolyBenchmark.swift | 2 +- ...PrivateInformationRetrievalBenchmark.swift | 2 +- Benchmarks/RlweBenchmark/RlweBenchmark.swift | 2 +- Package.resolved | 4 +- Package.swift | 54 +++++++++---------- README.md | 26 ++++++--- Sources/HomomorphicEncryption/Array2d.swift | 1 + .../Bfv/Bfv+Decrypt.swift | 2 + Sources/HomomorphicEncryption/Bfv/Bfv.swift | 7 +++ .../HomomorphicEncryption/Ciphertext.swift | 5 ++ Sources/HomomorphicEncryption/Context.swift | 2 + .../DoubleWidthUInt.swift | 22 ++++++++ Sources/HomomorphicEncryption/Encoding.swift | 1 + Sources/HomomorphicEncryption/HeScheme.swift | 4 ++ .../UsingSwiftHomomorphicEncryption.md | 25 ++++++--- Sources/HomomorphicEncryption/Modulus.swift | 1 + Sources/HomomorphicEncryption/Plaintext.swift | 8 +++ .../HomomorphicEncryption/PolyRq/Galois.swift | 42 +++++++++------ .../PolyRq/PolyContext.swift | 5 ++ .../PolyRq/PolyRq+Ntt.swift | 10 +++- .../HomomorphicEncryption/PolyRq/PolyRq.swift | 8 +++ .../RnsBaseConverter.swift | 1 + Sources/HomomorphicEncryption/RnsTool.swift | 2 + Sources/HomomorphicEncryption/Scalar.swift | 5 ++ Sources/HomomorphicEncryption/Util.swift | 1 + .../HomomorphicEncryption/Zeroization.swift | 8 +-- .../IndexPirProtocol.swift | 6 ++- 27 files changed, 186 insertions(+), 70 deletions(-) diff --git a/Benchmarks/PolyBenchmark/PolyBenchmark.swift b/Benchmarks/PolyBenchmark/PolyBenchmark.swift index 7d748c51..cf55afa0 100644 --- a/Benchmarks/PolyBenchmark/PolyBenchmark.swift +++ b/Benchmarks/PolyBenchmark/PolyBenchmark.swift @@ -13,7 +13,7 @@ // limitations under the License. // Benchmarks for PolyRq functions. -// These benchmarks can be triggered with `swift package -c release benchmark --target PolyBenchmark` +// These benchmarks can be triggered with `swift package benchmark --target PolyBenchmark` // for more readable results @preconcurrency import Benchmark diff --git a/Benchmarks/PrivateInformationRetrievalBenchmark/PrivateInformationRetrievalBenchmark.swift b/Benchmarks/PrivateInformationRetrievalBenchmark/PrivateInformationRetrievalBenchmark.swift index 482c3dbe..7cbd675a 100644 --- a/Benchmarks/PrivateInformationRetrievalBenchmark/PrivateInformationRetrievalBenchmark.swift +++ b/Benchmarks/PrivateInformationRetrievalBenchmark/PrivateInformationRetrievalBenchmark.swift @@ -14,7 +14,7 @@ // Benchmarks for Pir functions. // These benchmarks can be triggered with -// `swift package -c release benchmark --target PIRBenchmark` +// `swift package benchmark --target PIRBenchmark` // for more readable results @preconcurrency import Benchmark diff --git a/Benchmarks/RlweBenchmark/RlweBenchmark.swift b/Benchmarks/RlweBenchmark/RlweBenchmark.swift index fd8a9e72..cd7ee3f0 100644 --- a/Benchmarks/RlweBenchmark/RlweBenchmark.swift +++ b/Benchmarks/RlweBenchmark/RlweBenchmark.swift @@ -13,7 +13,7 @@ // limitations under the License. // Benchmarks for Rlwe functions. -// These benchmarks can be triggered with `swift package -c release benchmark --target RlweBenchmark` +// These benchmarks can be triggered with `swift package benchmark --target RlweBenchmark` // for more readable results @preconcurrency import Benchmark diff --git a/Package.resolved b/Package.resolved index 7bf4ac61..16c6a2a1 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "af0ba8881dc49c9949dc67e69ce086a87a9aa3a032afdebbf8fa2500cc005e93", + "originHash" : "1556ae0b547f8e688ae5876b1f21579f66ce8684e7ba120f9f71ec5cc9524064", "pins" : [ { "identity" : "hdrhistogram-swift", @@ -58,7 +58,7 @@ { "identity" : "swift-docc-plugin", "kind" : "remoteSourceControl", - "location" : "https://github.com/swiftlang/swift-docc-plugin", + "location" : "https://github.com/apple/swift-docc-plugin", "state" : { "revision" : "26ac5758409154cc448d7ab82389c520fa8a8247", "version" : "1.3.0" diff --git a/Package.swift b/Package.swift index f6519231..1cddabc9 100644 --- a/Package.swift +++ b/Package.swift @@ -15,14 +15,16 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - import PackageDescription -let swiftSettings: [SwiftSetting] = [ +let librarySettings: [SwiftSetting] = [ .enableExperimentalFeature("StrictConcurrency"), - .unsafeFlags(["-cross-module-optimization"], .when(configuration: .release)), ] +let executableSettings: [SwiftSetting] = + librarySettings + + [.unsafeFlags(["-cross-module-optimization"], .when(configuration: .release))] + let package = Package( name: "swift-homomorphic-encryption", products: [ @@ -46,7 +48,7 @@ let package = Package( dependencies: [ .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.0"), .package(url: "https://github.com/apple/swift-crypto.git", from: "3.4.0"), - .package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.0.0"), + .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), .package(url: "https://github.com/apple/swift-numerics", from: "1.0.0"), // Keep version in sync with README .package(url: "https://github.com/apple/swift-protobuf", from: "1.27.0"), @@ -68,25 +70,18 @@ let package = Package( .product(name: "_CryptoExtras", package: "swift-crypto"), "CUtil", ], - swiftSettings: swiftSettings + [ - SwiftSetting.unsafeFlags([ - "-Xllvm", - "-unroll-count=8", - "-Xllvm", - "-unroll-threshold=64", - ]), - ]), + swiftSettings: librarySettings), .target( name: "HomomorphicEncryptionProtobuf", dependencies: ["HomomorphicEncryption", .product(name: "SwiftProtobuf", package: "swift-protobuf")], exclude: ["generated/README.md"], - swiftSettings: swiftSettings), + swiftSettings: librarySettings), .target( name: "PrivateInformationRetrieval", dependencies: ["HomomorphicEncryption", .product(name: "Numerics", package: "swift-numerics")], - swiftSettings: swiftSettings), + swiftSettings: librarySettings), .target( name: "PrivateInformationRetrievalProtobuf", dependencies: ["PrivateInformationRetrieval", @@ -94,13 +89,14 @@ let package = Package( "HomomorphicEncryptionProtobuf", .product(name: "SwiftProtobuf", package: "swift-protobuf")], exclude: ["generated/README.md", "protobuf_module_mappings.txtpb"], - swiftSettings: swiftSettings), + swiftSettings: librarySettings), .target( name: "TestUtilities", dependencies: [ "HomomorphicEncryption", .product(name: "Numerics", package: "swift-numerics"), - ]), + ], + swiftSettings: librarySettings), .executableTarget( name: "PIRGenerateDatabase", dependencies: [ @@ -108,7 +104,7 @@ let package = Package( "HomomorphicEncryption", "PrivateInformationRetrievalProtobuf", ], - swiftSettings: swiftSettings), + swiftSettings: executableSettings), .executableTarget( name: "PIRProcessDatabase", dependencies: [ @@ -118,7 +114,7 @@ let package = Package( "HomomorphicEncryption", .product(name: "Logging", package: "swift-log"), ], - swiftSettings: swiftSettings), + swiftSettings: executableSettings), .executableTarget( name: "PIRShardDatabase", dependencies: [ @@ -126,43 +122,43 @@ let package = Package( "HomomorphicEncryption", "PrivateInformationRetrievalProtobuf", ], - swiftSettings: swiftSettings), + swiftSettings: executableSettings), .testTarget( name: "HomomorphicEncryptionTests", dependencies: [ "HomomorphicEncryption", "TestUtilities", .product(name: "Numerics", package: "swift-numerics"), - ]), + ], swiftSettings: executableSettings), .testTarget( name: "HomomorphicEncryptionProtobufTests", dependencies: [ "HomomorphicEncryption", "HomomorphicEncryptionProtobuf", "TestUtilities", - ]), + ], swiftSettings: executableSettings), .testTarget( name: "PrivateInformationRetrievalProtobufTests", dependencies: [ "PrivateInformationRetrieval", "PrivateInformationRetrievalProtobuf", "TestUtilities", - ]), + ], swiftSettings: executableSettings), .testTarget( name: "PrivateInformationRetrievalTests", dependencies: [ "PrivateInformationRetrieval", "TestUtilities", .product(name: "Numerics", package: "swift-numerics"), - ]), + ], swiftSettings: executableSettings), .testTarget( name: "PIRGenerateDatabaseTests", dependencies: ["PIRGenerateDatabase", "TestUtilities", - .product(name: "Numerics", package: "swift-numerics")]), + .product(name: "Numerics", package: "swift-numerics")], swiftSettings: executableSettings), .testTarget( name: "PIRProcessDatabaseTests", dependencies: ["PIRProcessDatabase", "TestUtilities", - .product(name: "Numerics", package: "swift-numerics")]), + .product(name: "Numerics", package: "swift-numerics")], swiftSettings: executableSettings), ]) // MARK: - Benchmarks @@ -178,7 +174,7 @@ package.targets += [ "HomomorphicEncryption", ], path: "Benchmarks/PolyBenchmark", - swiftSettings: swiftSettings, + swiftSettings: executableSettings, plugins: [ .plugin(name: "BenchmarkPlugin", package: "package-benchmark"), ]), @@ -189,7 +185,7 @@ package.targets += [ "HomomorphicEncryption", ], path: "Benchmarks/RlweBenchmark", - swiftSettings: swiftSettings, + swiftSettings: executableSettings, plugins: [ .plugin(name: "BenchmarkPlugin", package: "package-benchmark"), ]), @@ -203,7 +199,7 @@ package.targets += [ "PrivateInformationRetrievalProtobuf", ], path: "Benchmarks/PrivateInformationRetrievalBenchmark", - swiftSettings: swiftSettings, + swiftSettings: executableSettings, plugins: [ .plugin(name: "BenchmarkPlugin", package: "package-benchmark"), ]), @@ -211,5 +207,5 @@ package.targets += [ // Set the minimum macOS version for the package package.platforms = [ - .macOS(.v14), // Constrained by Swift 6 support for Xcode (https://developer.apple.com/support/xcode/) + .macOS(.v14), // Constrained by Swift 5.10 support for Xcode (https://developer.apple.com/support/xcode/) ] diff --git a/README.md b/README.md index 3332add3..d6fd2e81 100644 --- a/README.md +++ b/README.md @@ -64,23 +64,35 @@ Swift Homomorphic Encryption requires: Swift Homomorphic Encryption is available as a Swift Package Manager package. To use Swift Homomorphic Encryption, choose a [tag](https://github.com/apple/swift-homomorphic-encryption/tags). -Then, add the following dependency in your `Package.swift`, adding the commit hash associated with the tag. +Then, add the following dependency in your `Package.swift` ```swift .package( url: "https://github.com/apple/swift-homomorphic-encryption", - revision: "git-commit-associated-with-the-tag"), + from: "tag"), ``` - -> [!NOTE] -> Due to the use of `unsafeFlags` in Swift Homomorphic Encryption, you shouldn't depend on a git tag, which could prevent tagged releases of downstream projects. -> In particular, without the `cross-module-optimization` flag, performance degrades dramatically. -> One workaround is to add `SwiftHomomorphicEncryption` as a submodule rather than a package dependency. +, replacing `tag` with your chosen tag, e.g. `1.0.0-alpha.1`. To use the `HomomorphicEncryption` library, add ```swift .product(name: "HomomorphicEncryption", package: "swift-homomorphic-encryption"), ``` to your target's dependencies. + +> [!IMPORTANT] +> When linking your executable, make sure to enable `cross-module-optimization`. +> Without this flag, performance of Swift Homomorphic Encryption degrades dramatically, +> due to failure to specialize generics. For example, +> ```swift +> .executableTarget( +> name: "YourTarget", +> dependencies: [ +> .product(name: "HomomorphicEncryption", package: "swift-homomorphic-encryption"), +> ], +> swiftSettings: [.unsafeFlags(["-cross-module-optimization"], +> .when(configuration: .release))] +> ) +> ``` + You can then add ```swift import HomomorphicEncryption diff --git a/Sources/HomomorphicEncryption/Array2d.swift b/Sources/HomomorphicEncryption/Array2d.swift index 652c5fde..3a882c52 100644 --- a/Sources/HomomorphicEncryption/Array2d.swift +++ b/Sources/HomomorphicEncryption/Array2d.swift @@ -156,6 +156,7 @@ extension Array2d { } // Sets all the data to zero. This is useful for clearing sensitive data. + @inlinable mutating func zeroize() { let zeroizeSize = data.count * MemoryLayout.size data.withUnsafeMutableBytes { dataPointer in diff --git a/Sources/HomomorphicEncryption/Bfv/Bfv+Decrypt.swift b/Sources/HomomorphicEncryption/Bfv/Bfv+Decrypt.swift index b6058fa5..332c2b5b 100644 --- a/Sources/HomomorphicEncryption/Bfv/Bfv+Decrypt.swift +++ b/Sources/HomomorphicEncryption/Bfv/Bfv+Decrypt.swift @@ -15,6 +15,7 @@ import Foundation extension Bfv { + @inlinable public static func decrypt(_ ciphertext: EvalCiphertext, using secretKey: SecretKey>) throws -> CoeffPlaintext { @@ -27,6 +28,7 @@ extension Bfv { return CoeffPlaintext(context: ciphertext.context, poly: plaintext) } + @inlinable public static func decrypt(_ ciphertext: CoeffCiphertext, using secretKey: SecretKey>) throws -> CoeffPlaintext { diff --git a/Sources/HomomorphicEncryption/Bfv/Bfv.swift b/Sources/HomomorphicEncryption/Bfv/Bfv.swift index 29b2cd73..7a851988 100644 --- a/Sources/HomomorphicEncryption/Bfv/Bfv.swift +++ b/Sources/HomomorphicEncryption/Bfv/Bfv.swift @@ -28,6 +28,7 @@ public enum Bfv: HeScheme { // MARK: HE operations + @inlinable public static func addAssign(_ lhs: inout Plaintext, F>, _ rhs: Plaintext, F>) throws { try validateEquality(of: lhs.context, and: rhs.context) lhs.poly += rhs.poly @@ -57,10 +58,12 @@ public enum Bfv: HeScheme { lhs.clearSeed() } + @inlinable public static func addAssign(_ ciphertext: inout CoeffCiphertext, _ plaintext: CoeffPlaintext) throws { try plaintextTranslate(ciphertext: &ciphertext, plaintext: plaintext, op: PlaintextTranslateOp.Add) } + @inlinable public static func subAssign(_ ciphertext: inout CoeffCiphertext, _ plaintext: CoeffPlaintext) throws { try plaintextTranslate(ciphertext: &ciphertext, plaintext: plaintext, op: PlaintextTranslateOp.Subtract) } @@ -98,18 +101,22 @@ public enum Bfv: HeScheme { // These operations could be supported with extra NTT conversions, but NTTs are expensive, so we prefer to // keep NTT conversions explicit + @inlinable public static func addAssign(_: inout EvalCiphertext, _: EvalPlaintext) throws { throw HeError.unsupportedHeOperation() } + @inlinable public static func addAssign(_: inout CanonicalCiphertext, _: EvalPlaintext) throws { throw HeError.unsupportedHeOperation() } + @inlinable public static func subAssign(_: inout EvalCiphertext, _: EvalPlaintext) throws { throw HeError.unsupportedHeOperation() } + @inlinable public static func subAssign(_: inout CanonicalCiphertext, _: EvalPlaintext) throws { throw HeError.unsupportedHeOperation() } diff --git a/Sources/HomomorphicEncryption/Ciphertext.swift b/Sources/HomomorphicEncryption/Ciphertext.swift index 2a1a4877..84378d88 100644 --- a/Sources/HomomorphicEncryption/Ciphertext.swift +++ b/Sources/HomomorphicEncryption/Ciphertext.swift @@ -209,6 +209,7 @@ public struct Ciphertext: Equatable, Senda /// ``HeScheme/zeroCiphertext(context:moduliCount:)-52gz2`` yields a transparent transparent. /// - Returns: Whether the ciphertext is transparent. /// - seealso: ``HeScheme/isTransparent(ciphertext:)-31w9f`` for an alternative API. + @inlinable public func isTransparent() -> Bool where Format == Coeff { Scheme.isTransparent(ciphertext: self) } @@ -219,6 +220,7 @@ public struct Ciphertext: Equatable, Senda /// ``HeScheme/zeroCiphertext(context:moduliCount:)-52gz2`` yields a transparent transparent. /// - Returns: Whether the ciphertext is transparent. /// - seealso: ``HeScheme/isTransparent(ciphertext:)-1nwgm`` for an alternative API. + @inlinable public func isTransparent() -> Bool where Format == Eval { Scheme.isTransparent(ciphertext: self) } @@ -229,6 +231,7 @@ public struct Ciphertext: Equatable, Senda /// ``HeScheme/zeroCiphertext(context:moduliCount:)-52gz2`` yields a transparent transparent. /// - Returns: Whether the ciphertext is transparent. /// - seealso: ``HeScheme/isTransparent(ciphertext:)-6m5nk`` for an alternative API. + @inlinable public func isTransparent() -> Bool where Format == Scheme.CanonicalCiphertextFormat { Scheme.isTransparent(ciphertext: self) } @@ -842,6 +845,7 @@ extension Ciphertext where Format == Scheme.CanonicalCiphertextFormat { /// - key: Evaluation key. Must contain Galois element `element`. /// - Throws: Error upon failure to apply the Galois transformation. /// - seealso: ``HeScheme/applyGalois(ciphertext:element:using:)`` for an alternative API and more information. + @inlinable public mutating func applyGalois(element: Int, using key: EvaluationKey) throws { try Scheme.applyGalois(ciphertext: &self, element: element, using: key) } @@ -851,6 +855,7 @@ extension Ciphertext where Format == Scheme.CanonicalCiphertextFormat { /// - Parameter key: Evaluation key to relinearize with. Must contain a `RelinearizationKey`. /// - Throws: Error upon failure to relinearize. /// - seealso: ``HeScheme/relinearize(_:using:)`` for an alternative API and more information. + @inlinable public mutating func relinearize(using key: EvaluationKey) throws { try Scheme.relinearize(&self, using: key) } diff --git a/Sources/HomomorphicEncryption/Context.swift b/Sources/HomomorphicEncryption/Context.swift index 6a6764d4..2dc1aa28 100644 --- a/Sources/HomomorphicEncryption/Context.swift +++ b/Sources/HomomorphicEncryption/Context.swift @@ -61,6 +61,7 @@ public final class Context: Equatable, Sendable { /// /// - Parameter encryptionParameters: Encryption parameters. /// - Throws: Error upon failure to initialize the context. + @inlinable public init(encryptionParameters: EncryptionParameters) throws { self.encryptionParameters = encryptionParameters self.simdEncodingMatrix = Self.generateEncodingMatrix(encryptionParameters: encryptionParameters) @@ -112,6 +113,7 @@ public final class Context: Equatable, Sendable { /// - lhs: A context to compare. /// - rhs: Another context to compare. /// - Returns: Whether or not the two contexts are equal. + @inlinable public static func == (lhs: Context, rhs: Context) -> Bool { lhs === rhs || lhs.encryptionParameters == rhs.encryptionParameters } diff --git a/Sources/HomomorphicEncryption/DoubleWidthUInt.swift b/Sources/HomomorphicEncryption/DoubleWidthUInt.swift index ecba18d0..b7156cfb 100644 --- a/Sources/HomomorphicEncryption/DoubleWidthUInt.swift +++ b/Sources/HomomorphicEncryption/DoubleWidthUInt.swift @@ -87,6 +87,7 @@ public struct DoubleWidthUInt: Sendable /// /// - Parameter value: The tuple to use as the source of the new instance's /// high and low parts. + @inlinable public init(_ value: (high: High, low: Low)) { #if _endian(big) self._storage = (high: value.0, low: value.1) @@ -111,6 +112,7 @@ public struct DoubleWidthUInt: Sendable self.init((_high, low)) } + @inlinable public init() { self.init(0, 0) } @@ -129,6 +131,7 @@ extension DoubleWidthUInt: CustomDebugStringConvertible { } extension DoubleWidthUInt: Equatable { + @inlinable public static func == (lhs: DoubleWidthUInt, rhs: DoubleWidthUInt) -> Bool { lhs._storage.low == rhs._storage.low && lhs._storage.high == rhs._storage.high @@ -136,6 +139,7 @@ extension DoubleWidthUInt: Equatable { } extension DoubleWidthUInt: Comparable { + @inlinable public static func < (lhs: DoubleWidthUInt, rhs: DoubleWidthUInt) -> Bool { if lhs._storage.high < rhs._storage.high { return true } else if lhs._storage.high > rhs._storage.high { return false } @@ -148,6 +152,7 @@ extension DoubleWidthUInt: Hashable { _hashValue(for: self) } + @inlinable public func hash(into hasher: inout Hasher) { hasher.combine(low) hasher.combine(high) @@ -165,6 +170,7 @@ extension DoubleWidthUInt: Numeric { self.init(High(_magnitude._storage.high), _magnitude._storage.low) } + @inlinable public init(_ source: some BinaryInteger) { guard let result = DoubleWidthUInt(exactly: source) else { preconditionFailure("Value is outside the representable range") @@ -172,6 +178,7 @@ extension DoubleWidthUInt: Numeric { self = result } + @inlinable public init?(exactly source: T) { // Can't represent a negative 'source' guard source >= 0 else { return nil } @@ -197,6 +204,7 @@ extension DoubleWidthUInt { public var _high: High.Words public var _low: Low.Words + @inlinable public init(_ value: DoubleWidthUInt) { // Multiples of word size only. guard Base.bitWidth == Base.Magnitude.bitWidth, @@ -228,6 +236,7 @@ extension DoubleWidthUInt.Words: RandomAccessCollection { return _low.count + _high.count } + @inlinable public subscript(_ i: Index) -> UInt { if Base.bitWidth < UInt.bitWidth { precondition(i == 0, "Invalid index") @@ -355,6 +364,7 @@ extension DoubleWidthUInt: FixedWidthInteger { return (high, low) } + @inlinable public func dividingFullWidth( _ dividend: (high: DoubleWidthUInt, low: DoubleWidthUInt.Magnitude)) -> (quotient: DoubleWidthUInt, remainder: DoubleWidthUInt) @@ -364,6 +374,7 @@ extension DoubleWidthUInt: FixedWidthInteger { return (DoubleWidthUInt(quotient), DoubleWidthUInt(remainder)) } + @inlinable public static func &= ( lhs: inout DoubleWidthUInt, rhs: DoubleWidthUInt) { @@ -371,6 +382,7 @@ extension DoubleWidthUInt: FixedWidthInteger { lhs._storage.high &= rhs._storage.high } + @inlinable public static func |= ( lhs: inout DoubleWidthUInt, rhs: DoubleWidthUInt) { @@ -378,6 +390,7 @@ extension DoubleWidthUInt: FixedWidthInteger { lhs._storage.high |= rhs._storage.high } + @inlinable public static func ^= ( lhs: inout DoubleWidthUInt, rhs: DoubleWidthUInt) { @@ -508,35 +521,41 @@ extension DoubleWidthUInt: FixedWidthInteger { lhs = result } + @inlinable public static func / (lhs: DoubleWidthUInt, rhs: DoubleWidthUInt) -> DoubleWidthUInt { var lhs = lhs lhs /= rhs return lhs } + @inlinable public static func /= (lhs: inout DoubleWidthUInt, rhs: DoubleWidthUInt) { let (result, overflow) = lhs.dividedReportingOverflow(by: rhs) precondition(!overflow, "Overflow in /=") lhs = result } + @inlinable public static func % (lhs: DoubleWidthUInt, rhs: DoubleWidthUInt) -> DoubleWidthUInt { var lhs = lhs lhs %= rhs return lhs } + @inlinable public static func %= (lhs: inout DoubleWidthUInt, rhs: DoubleWidthUInt) { let (result, overflow) = lhs.remainderReportingOverflow(dividingBy: rhs) precondition(!overflow, "Overflow in %=") lhs = result } + @inlinable public init(_truncatingBits bits: UInt) { _storage.low = Low(_truncatingBits: bits) _storage.high = High(_truncatingBits: bits >> UInt(Low.bitWidth)) } + @inlinable public init(integerLiteral x: Int) { self.init(x) } @@ -570,6 +589,7 @@ extension DoubleWidthUInt: UnsignedInteger where Base: FixedWidthInteger & Unsig /// /// This operation is conceptually that described by Burnikel and Ziegler /// (1998). + @inlinable static func _divide( _ lhs: (high: Low, mid: Low, low: Low), by rhs: Magnitude) -> (quotient: Low, remainder: Magnitude) { @@ -609,6 +629,7 @@ extension DoubleWidthUInt: UnsignedInteger where Base: FixedWidthInteger & Unsig /// Returns the quotient and remainder after dividing a quadruple-width /// magnitude `lhs` by a double-width magnitude `rhs`. + @inlinable static func _divide( _ lhs: DoubleWidthUInt, by rhs: Magnitude) -> (quotient: Magnitude, remainder: Magnitude) { @@ -657,6 +678,7 @@ extension DoubleWidthUInt: UnsignedInteger where Base: FixedWidthInteger & Unsig /// Returns the quotient and remainder after dividing a double-width /// magnitude `lhs` by a double-width magnitude `rhs`. + @inlinable static func _divide( _ lhs: Magnitude, by rhs: Magnitude) -> (quotient: Magnitude, remainder: Magnitude) { diff --git a/Sources/HomomorphicEncryption/Encoding.swift b/Sources/HomomorphicEncryption/Encoding.swift index 00ce8f22..7d32429f 100644 --- a/Sources/HomomorphicEncryption/Encoding.swift +++ b/Sources/HomomorphicEncryption/Encoding.swift @@ -189,6 +189,7 @@ extension Context { // code for SIMD encoding/decoding extension Context { + @inlinable static func generateEncodingMatrix(encryptionParameters: EncryptionParameters) -> [Int] { guard encryptionParameters.plaintextModulus.isNttModulus(for: encryptionParameters.polyDegree) else { return [Int]() diff --git a/Sources/HomomorphicEncryption/HeScheme.swift b/Sources/HomomorphicEncryption/HeScheme.swift index 32ddfd0c..83db1966 100644 --- a/Sources/HomomorphicEncryption/HeScheme.swift +++ b/Sources/HomomorphicEncryption/HeScheme.swift @@ -647,6 +647,7 @@ public protocol HeScheme { } extension HeScheme { + @inlinable public static func validateEquality(of lhs: Context, and rhs: Context) throws { guard lhs == rhs else { throw HeError.unequalContexts(got: lhs, expected: rhs) @@ -655,6 +656,7 @@ extension HeScheme { } extension HeScheme { + @inlinable public static func rotateColumns( of ciphertext: inout CanonicalCiphertext, by step: Int, @@ -664,6 +666,7 @@ extension HeScheme { try applyGalois(ciphertext: &ciphertext, element: element, using: evaluationKey) } + @inlinable public static func swapRows(of ciphertext: inout CanonicalCiphertext, using evaluationKey: EvaluationKey) throws { let element = GaloisElement.swappingRows(degree: ciphertext.context.degree) try applyGalois(ciphertext: &ciphertext, element: element, using: evaluationKey) @@ -698,6 +701,7 @@ extension HeScheme { }).sum() } + @inlinable public static func innerProduct(ciphertexts: some Collection, plaintexts: some Collection) throws -> EvalCiphertext { diff --git a/Sources/HomomorphicEncryption/HomomorphicEncryption.docc/UsingSwiftHomomorphicEncryption.md b/Sources/HomomorphicEncryption/HomomorphicEncryption.docc/UsingSwiftHomomorphicEncryption.md index 5d43b07c..fe80417e 100644 --- a/Sources/HomomorphicEncryption/HomomorphicEncryption.docc/UsingSwiftHomomorphicEncryption.md +++ b/Sources/HomomorphicEncryption/HomomorphicEncryption.docc/UsingSwiftHomomorphicEncryption.md @@ -12,23 +12,34 @@ Swift Homomorphic Encryption requires: Swift Homomorphic Encryption is available as a Swift Package Manager package. To use Swift Homomorphic Encryption, choose a [tag](https://github.com/apple/swift-homomorphic-encryption/tags). -Then, add the following dependency in your `Package.swift`, adding the commit hash associated with the tag. +Then, add the following dependency in your `Package.swift` ```swift .package( url: "https://github.com/apple/swift-homomorphic-encryption", - revision: "git-commit-associated-with-the-tag"), + from: "tag"), ``` - -> Note: -> Due to the use of `unsafeFlags` in Swift Homomorphic Encryption, you shouldn't depend on a git tag, which could prevent tagged releases of downstream projects. -> In particular, without the `cross-module-optimization` flag, performance degrades dramatically. -> One workaround is to add `SwiftHomomorphicEncryption` as a submodule rather than a package dependency. +replacing `tag` with your chosen tag, e.g. `1.0.0-alpha.1`. To use the `HomomorphicEncryption` library, add ```swift .product(name: "HomomorphicEncryption", package: "swift-homomorphic-encryption"), ``` to your target's dependencies. + +> Important: +> When linking your executable, make sure to enable `cross-module-optimization`. +> Without this flag, performance of Swift Homomorphic Encryption degrades dramatically, +> due to failure to specialize generics. For example, +> ```swift +> .executableTarget( +> name: "YourTarget", +> dependencies: [ +> .product(name: "HomomorphicEncryption", package: "swift-homomorphic-encryption"), +> ], +> swiftSettings: [.unsafeFlags(["-cross-module-optimization"], +> .when(configuration: .release))] +> ) + You can then add ```swift import HomomorphicEncryption diff --git a/Sources/HomomorphicEncryption/Modulus.swift b/Sources/HomomorphicEncryption/Modulus.swift index 600bcef4..cab7d09e 100644 --- a/Sources/HomomorphicEncryption/Modulus.swift +++ b/Sources/HomomorphicEncryption/Modulus.swift @@ -350,6 +350,7 @@ struct MultiplyConstantArrayModulus: Sendable { /// - modulus: Modulus. /// - variableTime: Whether or not `multiplicands` or `modulus` may be leaked through timing. /// - Warning: May leak `multiplicands` and `modulus` through timing. + @inlinable init(multiplicands: [T], modulus: T, variableTime: Bool) { assert(multiplicands.allSatisfy { $0 < modulus }) self.multiplicands = multiplicands diff --git a/Sources/HomomorphicEncryption/Plaintext.swift b/Sources/HomomorphicEncryption/Plaintext.swift index 0b9c9e62..a782b511 100644 --- a/Sources/HomomorphicEncryption/Plaintext.swift +++ b/Sources/HomomorphicEncryption/Plaintext.swift @@ -23,19 +23,23 @@ public struct Plaintext: Equatable, Sendab self.poly = poly } + @inlinable static func += (lhs: inout Plaintext, rhs: Plaintext) throws where Format == Coeff { try Scheme.addAssign(&lhs, rhs) } + @inlinable static func += (lhs: inout Plaintext, rhs: Plaintext) throws where Format == Eval { try Scheme.addAssign(&lhs, rhs) } + @inlinable func forwardNtt() throws -> Plaintext where Format == Coeff { let poly = try poly.forwardNtt() return Plaintext(context: context, poly: poly) } + @inlinable func inverseNtt() throws -> Plaintext where Format == Eval { let poly = try poly.inverseNtt() return Plaintext(context: context, poly: poly) @@ -61,18 +65,21 @@ extension Plaintext: PolyCollection { } extension Plaintext { + @inlinable static func + (lhs: Self, rhs: Self) throws -> Self where Format == Coeff { var result = lhs try result += rhs return result } + @inlinable static func + (lhs: Self, rhs: Self) throws -> Self where Format == Eval { var result = lhs try result += rhs return result } + @inlinable public func convertToEvalFormat(moduliCount: Int? = nil) throws -> Plaintext where Format == Coeff { @@ -95,6 +102,7 @@ extension Plaintext { return try Plaintext(context: context, poly: poly.forwardNtt()) } + @inlinable public func convertToCoeffFormat() throws -> Plaintext where Format == Eval { diff --git a/Sources/HomomorphicEncryption/PolyRq/Galois.swift b/Sources/HomomorphicEncryption/PolyRq/Galois.swift index a7b7623f..76bcd1e2 100644 --- a/Sources/HomomorphicEncryption/PolyRq/Galois.swift +++ b/Sources/HomomorphicEncryption/PolyRq/Galois.swift @@ -12,24 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. +@usableFromInline struct GaloisCoeffIterator: IteratorProtocol { - typealias Element = (Bool, Int) + @usableFromInline typealias Element = (Bool, Int) /// Degree of the RLWE polynomial. - let degree: Int + @usableFromInline let degree: Int /// `log2(degree)`. - let log2Degree: Int + @usableFromInline let log2Degree: Int /// `x % degree == x & modDegreeMask`, because `degree` is a power of two. - let modDegreeMask: Int + @usableFromInline let modDegreeMask: Int /// power in transformation `f(x) -> f(x^{galoisElement})`. - let galoisElement: Int + @usableFromInline let galoisElement: Int /// simple incrementing index of the iterator in `[0, degree)`. - var iterIndex: Int + @usableFromInline var iterIndex: Int /// `iterIndex * galoisElement`. - var rawOutIndex: Int + @usableFromInline var rawOutIndex: Int /// Raw output index mod-reduced to `[0, degree)`. - var outIndex: Int + @usableFromInline var outIndex: Int + @inlinable init(degree: Int, galoisElement: Int) { precondition(galoisElement.isValidGaloisElement(for: degree)) self.degree = degree @@ -41,6 +43,7 @@ struct GaloisCoeffIterator: IteratorProtocol { self.outIndex = 0 } + @inlinable mutating func next() -> Element? { if iterIndex < degree { // Use x^degree == -1 mod (x^degree + 1) @@ -56,19 +59,21 @@ struct GaloisCoeffIterator: IteratorProtocol { } } +@usableFromInline struct GaloisEvalIterator: IteratorProtocol { - typealias Element = Int + @usableFromInline typealias Element = Int /// Degree of the RLWE polynomial. - let degree: Int + @usableFromInline let degree: Int /// `log2(degree)`. - let log2Degree: Int + @usableFromInline let log2Degree: Int /// `x % degree == x & modDegreeMask`, because `degree` is a power of two. - let modDegreeMask: Int + @usableFromInline let modDegreeMask: Int /// Power in transformation `f(x) -> f(x^{galoisElement})`. - let galoisElement: Int + @usableFromInline let galoisElement: Int /// Simple incrementing index of the iterator in `[0, degree)`. - var iterIndex: Int + @usableFromInline var iterIndex: Int + @inlinable init(degree: Int, galoisElement: Int) { precondition(galoisElement.isValidGaloisElement(for: degree)) self.degree = degree @@ -78,6 +83,7 @@ struct GaloisEvalIterator: IteratorProtocol { self.iterIndex = 0 } + @inlinable mutating func next() -> Element? { if iterIndex < degree { let reversed = Int(UInt32(iterIndex &+ degree).reverseBits(bitCount: log2Degree &+ 1)) @@ -91,12 +97,14 @@ struct GaloisEvalIterator: IteratorProtocol { } extension FixedWidthInteger { + @inlinable func isValidGaloisElement(for degree: Int) -> Bool { degree.isPowerOfTwo && !isMultiple(of: 2) && (self < (degree &<< 1)) && (self > 1) } } extension PolyRq where F == Coeff { + @inlinable public func applyGalois(galoisElement: Int) -> Self { precondition(galoisElement.isValidGaloisElement(for: degree)) var output = self @@ -128,6 +136,7 @@ extension PolyRq where F == Coeff { } extension PolyRq where F == Eval { + @inlinable public func applyGalois(galoisElement: Int) throws -> Self { precondition(galoisElement.isValidGaloisElement(for: degree)) var output = self @@ -145,8 +154,9 @@ extension PolyRq where F == Eval { } } +@usableFromInline enum GaloisElementGenerator { - static let value: UInt32 = 3 + @usableFromInline static let value: UInt32 = 3 } /// Utilities for generating Galois elements. @@ -156,6 +166,7 @@ public enum GaloisElement { /// - Parameter degree: Polynomial degree. /// - Returns: The Galois element to swap rows. /// - seealso: ``HeScheme/swapRows(of:using:)`` for more information. + @inlinable public static func swappingRows(degree: Int) -> Int { (degree << 1) - 1 } @@ -169,6 +180,7 @@ public enum GaloisElement { /// ``EncryptionParameters/polyDegree``. /// - Returns: The Galois element for column rotation by `step`. /// - Throws: Error upon invalid step or degree. + @inlinable public static func rotatingColumns(by step: Int, degree: Int) throws -> Int { guard degree.isPowerOfTwo else { throw HeError.invalidDegree(degree) diff --git a/Sources/HomomorphicEncryption/PolyRq/PolyContext.swift b/Sources/HomomorphicEncryption/PolyRq/PolyContext.swift index b9636bb6..cec136ef 100644 --- a/Sources/HomomorphicEncryption/PolyRq/PolyContext.swift +++ b/Sources/HomomorphicEncryption/PolyRq/PolyContext.swift @@ -36,6 +36,7 @@ public final class PolyContext: Sendable { /// - moduli: Decomposition of the modulus `Q` into co-prime factors `q_0, ..., q_{L-1}`. /// - next: The next context in the modulus-switching chain. /// - Throws: Error upon failure to initialize the context. + @inlinable required init(degree: Int, moduli: [T], next: PolyContext?) throws { guard degree.isPowerOfTwo else { throw HeError.invalidDegree(degree) @@ -94,6 +95,7 @@ public final class PolyContext: Sendable { /// - degree: Polynomial degree. /// - moduli: Decomposition of the modulus `Q` into co-prime factors `q_0, ..., q_{L-1}`. /// - Throws: Error upon failure to initialize the context. + @inlinable public convenience init(degree: Int, moduli: [T]) throws { if moduli.count == 1 { try self.init(degree: degree, moduli: moduli, next: nil) @@ -106,6 +108,7 @@ public final class PolyContext: Sendable { try self.init(degree: degree, moduli: moduli, next: next) } + @inlinable func validateNttModuli() throws { for modulus in moduli { guard modulus.isNttModulus(for: degree) else { @@ -115,6 +118,7 @@ public final class PolyContext: Sendable { } /// Computes `Q mod modulus`. + @inlinable func qRemainder(dividingBy modulus: Modulus) -> T { var prod = T(1) for qi in moduli { @@ -181,6 +185,7 @@ public final class PolyContext: Sendable { } extension PolyContext: Equatable { + @inlinable public static func == (lhs: PolyContext, rhs: PolyContext) -> Bool { (lhs === rhs) || (lhs.degree == rhs.degree && lhs.moduli == rhs.moduli && lhs.next == rhs.next) } diff --git a/Sources/HomomorphicEncryption/PolyRq/PolyRq+Ntt.swift b/Sources/HomomorphicEncryption/PolyRq/PolyRq+Ntt.swift index d3953866..00e4cde1 100644 --- a/Sources/HomomorphicEncryption/PolyRq/PolyRq+Ntt.swift +++ b/Sources/HomomorphicEncryption/PolyRq/PolyRq+Ntt.swift @@ -18,11 +18,13 @@ extension ScalarType { /// - Parameter degree: Degree of the RLWE polynomial. /// - Returns: whether or not the value is a value NTT modulus /// - Note: `self` must be prime. + @inlinable func isNttModulus(for degree: Int) -> Bool { assert(isPrime(variableTime: true)) return degree.isPowerOfTwo && self % Self(2 * degree) == 1 && self != 1 } + @inlinable func isPrimitiveRootOfUnity(degree: Int, modulus: Self) -> Bool { // For degree a power of two, it suffices to check root^(degree/2) == -1 mod p // This implies root^degree == 1 mod p. Also, note 2 is the only prime factor of @@ -37,6 +39,7 @@ extension ScalarType { /// This value must be prime. /// - Parameter degree: Must be a power of two. /// - Returns: The primitive root of unity. + @inlinable func generatePrimitiveRootOfUnity(degree: Int) -> Self? { precondition(degree.isPowerOfTwo) precondition(isPrime(variableTime: true)) @@ -78,6 +81,7 @@ extension ScalarType { /// This value, `p`, must be prime. /// - Parameter degree: Must be a power of two that divides `p - 1`. /// - Returns: The primitive root of unity. + @inlinable func minPrimitiveRootOfUnity(degree: Int) -> Self? { guard var smallestGenerator = generatePrimitiveRootOfUnity(degree: degree) else { return nil @@ -107,6 +111,7 @@ struct NttContext: Sendable { // (degree)^{-1} * w^{-N} mod modulus for `w` a root of unity mod modulus @usableFromInline let inverseDegreeRootOfUnity: MultiplyConstantModulus + @inlinable init(degree: Int, modulus: T) throws { precondition(modulus.isNttModulus(for: degree)) guard let rootOfUnity = modulus.minPrimitiveRootOfUnity(degree: 2 * degree) else { @@ -194,6 +199,7 @@ extension PolyRq where F == Coeff { /// Performs the forward number-theoretic transform (NTT). /// - Returns: The ``Eval`` representation of the polynomial. /// - Throws: Error upon failure to compute the forward NTT. + @inlinable public consuming func forwardNtt() throws -> PolyRq { try context.validateNttModuli() var currentContext: PolyContext? = context @@ -340,6 +346,7 @@ extension PolyRq where F == Eval { /// Performs the inverse number-theoretic transform (NTT). /// - Returns: The ``Coeff`` representation of the polynomial. /// - Throws: Error upon failure to compute the inverse NTT. + @inlinable public consuming func inverseNtt() throws -> PolyRq { try context.validateNttModuli() var currentContext: PolyContext? = context @@ -353,7 +360,8 @@ extension PolyRq where F == Eval { /// Computes the inverse number-theoretic transform (NTT) on the last modulus in the context. /// - Parameter context: Context whose last modulus to use for the NTT. /// - Throws: Error upon failure to compute the inverse NTT. - private mutating func inverseNtt(using context: PolyContext) throws { + @inlinable + mutating func inverseNtt(using context: PolyContext) throws { // We modify Harvey's approach with delayed modular reduction. let moduli = context.moduli guard let modulus = moduli.last else { diff --git a/Sources/HomomorphicEncryption/PolyRq/PolyRq.swift b/Sources/HomomorphicEncryption/PolyRq/PolyRq.swift index 7fbac23f..6e5a689b 100644 --- a/Sources/HomomorphicEncryption/PolyRq/PolyRq.swift +++ b/Sources/HomomorphicEncryption/PolyRq/PolyRq.swift @@ -45,6 +45,7 @@ public struct PolyRq: Equatable, Sendable { } extension PolyRq: PolyCollection { + @inlinable public func polyContext() -> PolyContext { context } @@ -109,6 +110,7 @@ extension PolyRq { /// /// - Parameter context: Context which the polynomial will have. /// - Returns: The zero polynomial. + @inlinable public static func zero(context: PolyContext) -> Self { let degree = context.degree let moduliCount = context.moduli.count @@ -268,6 +270,7 @@ extension PolyRq { return result } + @inlinable public mutating func dropContext(to context: PolyContext) throws { if self.context == context { return @@ -293,12 +296,14 @@ extension PolyRq { /// runtime. /// - Returns: Whether the polynomial is zero. /// - Warning: Leaks `self` through timing. + @inlinable public func isZero(variableTime: Bool) -> Bool { precondition(variableTime) return data.data.allSatisfy { coefficient in coefficient == 0 } } /// Clears the memory in the polynomial. + @inlinable public mutating func zeroize() { data.zeroize() } @@ -368,6 +373,7 @@ extension PolyRq where F == Coeff { } extension PolyRq { + @inlinable public func convertToCoeff() throws -> PolyRq { switch F.self { case is Coeff.Type: @@ -383,6 +389,7 @@ extension PolyRq { } } + @inlinable public func convertToEval() throws -> PolyRq { switch F.self { case is Coeff.Type: @@ -398,6 +405,7 @@ extension PolyRq { } } + @inlinable public func convertFormat() throws -> PolyRq { switch Format.self { case is Coeff.Type: diff --git a/Sources/HomomorphicEncryption/RnsBaseConverter.swift b/Sources/HomomorphicEncryption/RnsBaseConverter.swift index d3edb904..d9a7cb51 100644 --- a/Sources/HomomorphicEncryption/RnsBaseConverter.swift +++ b/Sources/HomomorphicEncryption/RnsBaseConverter.swift @@ -25,6 +25,7 @@ struct RnsBaseConverter: Sendable { /// i'th entry stores `(q_i / q) % q_i``. @usableFromInline let inversePuncturedProducts: [MultiplyConstantModulus] + @inlinable init(from inputContext: PolyContext, to outputContext: PolyContext) throws { precondition(inputContext.degree == outputContext.degree) self.inputContext = inputContext diff --git a/Sources/HomomorphicEncryption/RnsTool.swift b/Sources/HomomorphicEncryption/RnsTool.swift index 30d9fd00..5e1ae0ac 100644 --- a/Sources/HomomorphicEncryption/RnsTool.swift +++ b/Sources/HomomorphicEncryption/RnsTool.swift @@ -72,6 +72,7 @@ struct RnsTool: Sendable { rnsConvertQToBSk.outputContext } + @inlinable init(from inputContext: PolyContext, to outputContext: PolyContext) throws { guard inputContext.degree == outputContext.degree, outputContext.moduli.count == 1 else { throw HeError.invalidPolyContext(inputContext) @@ -196,6 +197,7 @@ struct RnsTool: Sendable { /// - Returns: The scaled and rounded polynomial. /// - seealso: Algorithm 2 from . /// - Throws: Error upon failure to compute the scaling and rounding. + @inlinable func scaleAndRound(poly: PolyRq, scalingFactor: T) throws -> PolyRq { var poly = poly poly *= prodGammaTModQ diff --git a/Sources/HomomorphicEncryption/Scalar.swift b/Sources/HomomorphicEncryption/Scalar.swift index 8c3ea937..bc9db361 100644 --- a/Sources/HomomorphicEncryption/Scalar.swift +++ b/Sources/HomomorphicEncryption/Scalar.swift @@ -214,6 +214,7 @@ extension ScalarType { /// - variableTime: Must be `true`, indicating this value, `modulus` and `exponent` are leaked through timing. /// - Returns: `self^exponent mod modulus`. /// - Warning: Leaks `self`, `exponent`, `modulus` through timing. + @inlinable public func powMod(exponent: Self, modulus: Self, variableTime: Bool) -> Self { precondition(variableTime) if exponent == 0 { @@ -244,6 +245,7 @@ extension ScalarType { /// - variableTime: Must be `true`, indicating this value and `modulus` are leaked through timing. /// - Returns: `self^{-1} mod modulus`. /// - Throws: ``HeError/notInvertible(modulus:)`` if this value has no inverse mod `modulus`. + @inlinable public func inverseMod(modulus: Self, variableTime: Bool) throws -> Self { precondition(variableTime) guard self != 0, modulus != 0 else { @@ -272,6 +274,7 @@ extension UInt32 { /// /// - Parameter bitCount: Number of bits to reverse. Must be in `[1, 32]`. /// - Returns: The reversed bits of this value. + @inlinable public func reverseBits(bitCount: Int) -> UInt32 { var x = self assert((1...32).contains(bitCount)) @@ -399,6 +402,7 @@ extension ScalarType { /// - Throws: ``HeError/notEnoughPrimes(significantBitCounts:preferringSmall:nttDegree:)`` if not enough primes were /// found. /// - seealso: ``PolyRq/forwardNtt()`` and ``PolyRq/inverseNtt()``. + @inlinable public static func generatePrimes(significantBitCounts: [Int], preferringSmall: Bool, nttDegree: Int = 1) throws -> [Self] { @@ -445,6 +449,7 @@ extension ScalarType { /// Computes whether or not this value is prime. /// - Parameter variableTime: Must be `true`, indicating this value is leaked through timing. /// - Returns: Whether or not this value is prime. + @inlinable func isPrime(variableTime: Bool) -> Bool { precondition(variableTime) if self <= 1 { diff --git a/Sources/HomomorphicEncryption/Util.swift b/Sources/HomomorphicEncryption/Util.swift index b48577be..2c4d8cc5 100644 --- a/Sources/HomomorphicEncryption/Util.swift +++ b/Sources/HomomorphicEncryption/Util.swift @@ -13,6 +13,7 @@ // limitations under the License. extension Sequence where Element: Hashable { + @inlinable func allUnique() -> Bool { var seen = Set() for element in self { diff --git a/Sources/HomomorphicEncryption/Zeroization.swift b/Sources/HomomorphicEncryption/Zeroization.swift index 6ee8c748..ef1f56b3 100644 --- a/Sources/HomomorphicEncryption/Zeroization.swift +++ b/Sources/HomomorphicEncryption/Zeroization.swift @@ -15,15 +15,15 @@ #if !canImport(Darwin) import CUtil -// swiftlint:disable:next implicitly_unwrapped_optional -func zeroize(_ s: UnsafeMutableRawPointer!, _ n: Int) { +// swiftlint:disable:next implicitly_unwrapped_optional attributes +@inlinable func zeroize(_ s: UnsafeMutableRawPointer!, _ n: Int) { c_zeroize(s, n) } #else import Darwin -// swiftlint:disable:next implicitly_unwrapped_optional -func zeroize(_ s: UnsafeMutableRawPointer!, _ n: Int) { +// swiftlint:disable:next implicitly_unwrapped_optional attributes +@inlinable func zeroize(_ s: UnsafeMutableRawPointer!, _ n: Int) { let exitCode = memset_s(s, n, 0, n) precondition(exitCode == 0, "memset_s returned exit code \(exitCode)") } diff --git a/Sources/PrivateInformationRetrieval/IndexPirProtocol.swift b/Sources/PrivateInformationRetrieval/IndexPirProtocol.swift index 1ee2471e..8364acf5 100644 --- a/Sources/PrivateInformationRetrieval/IndexPirProtocol.swift +++ b/Sources/PrivateInformationRetrieval/IndexPirProtocol.swift @@ -367,7 +367,6 @@ public protocol IndexPirServer: Sendable { /// - parameter: PIR parameters. /// - Returns: A processed database. /// - Throws: Error upon failure to process the database. - @inlinable static func process(database: some Collection<[UInt8]>, with context: Context, using parameter: IndexPirParameter) throws -> Database @@ -378,7 +377,6 @@ public protocol IndexPirServer: Sendable { /// - evaluationKey: Evaluation key to aid in the server computation. /// - Returns: The encrypted response. /// - Throws: Error upon failure to compute a response. - @inlinable func computeResponse(to query: Query, using evaluationKey: EvaluationKey) throws -> Response } @@ -390,6 +388,7 @@ extension IndexPirServer { /// - context: Context for HE computation. /// - database: Database. /// - Throws: Error upon failure to initialize the server. + @inlinable public init(parameter: IndexPirParameter, context: Context, database: Database) throws { try self.init(parameter: parameter, context: context, databases: [database]) } @@ -399,6 +398,7 @@ extension IndexPirServer { /// - config: Database configuration. /// - context: Context for HE computation. /// - Returns: The PIR parameters for the database. + @inlinable public static func generateParameter(config: IndexPirConfig, with context: Context) -> IndexPirParameter { IndexPir.generateParameter(config: config, with: context) } @@ -463,6 +463,7 @@ extension IndexPirClient { /// - secretKey: Secret key used for the query. /// - Returns: An encrypted query. /// - Throws: Error upon failure to generate a query. + @inlinable public func generateQuery(at queryIndex: Int, using secretKey: SecretKey) throws -> Query { try generateQuery(at: [queryIndex], using: secretKey) } @@ -474,6 +475,7 @@ extension IndexPirClient { /// - secretKey: Secret key used for decryption. /// - Returns: For each query index, the database entry at that index. /// - Throws: Error upon failure to decrypt. + @inlinable public func decrypt(response: Response, at queryIndex: Int, using secretKey: SecretKey) throws -> [UInt8]