Skip to content

Commit

Permalink
Remove use of unsafe flags in libraries (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
fboemer authored Jul 15, 2024
1 parent d7888d1 commit 2e0f602
Show file tree
Hide file tree
Showing 27 changed files with 186 additions and 70 deletions.
2 changes: 1 addition & 1 deletion Benchmarks/PolyBenchmark/PolyBenchmark.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Benchmarks/RlweBenchmark/RlweBenchmark.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"originHash" : "af0ba8881dc49c9949dc67e69ce086a87a9aa3a032afdebbf8fa2500cc005e93",
"originHash" : "1556ae0b547f8e688ae5876b1f21579f66ce8684e7ba120f9f71ec5cc9524064",
"pins" : [
{
"identity" : "hdrhistogram-swift",
Expand Down Expand Up @@ -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"
Expand Down
54 changes: 25 additions & 29 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand All @@ -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"),
Expand All @@ -68,47 +70,41 @@ 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",
"HomomorphicEncryption",
"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: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
"HomomorphicEncryption",
"PrivateInformationRetrievalProtobuf",
],
swiftSettings: swiftSettings),
swiftSettings: executableSettings),
.executableTarget(
name: "PIRProcessDatabase",
dependencies: [
Expand All @@ -118,51 +114,51 @@ let package = Package(
"HomomorphicEncryption",
.product(name: "Logging", package: "swift-log"),
],
swiftSettings: swiftSettings),
swiftSettings: executableSettings),
.executableTarget(
name: "PIRShardDatabase",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
"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
Expand All @@ -178,7 +174,7 @@ package.targets += [
"HomomorphicEncryption",
],
path: "Benchmarks/PolyBenchmark",
swiftSettings: swiftSettings,
swiftSettings: executableSettings,
plugins: [
.plugin(name: "BenchmarkPlugin", package: "package-benchmark"),
]),
Expand All @@ -189,7 +185,7 @@ package.targets += [
"HomomorphicEncryption",
],
path: "Benchmarks/RlweBenchmark",
swiftSettings: swiftSettings,
swiftSettings: executableSettings,
plugins: [
.plugin(name: "BenchmarkPlugin", package: "package-benchmark"),
]),
Expand All @@ -203,13 +199,13 @@ package.targets += [
"PrivateInformationRetrievalProtobuf",
],
path: "Benchmarks/PrivateInformationRetrievalBenchmark",
swiftSettings: swiftSettings,
swiftSettings: executableSettings,
plugins: [
.plugin(name: "BenchmarkPlugin", package: "package-benchmark"),
]),
]

// 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/)
]
26 changes: 19 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Sources/HomomorphicEncryption/Array2d.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>.size
data.withUnsafeMutableBytes { dataPointer in
Expand Down
2 changes: 2 additions & 0 deletions Sources/HomomorphicEncryption/Bfv/Bfv+Decrypt.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import Foundation

extension Bfv {
@inlinable
public static func decrypt(_ ciphertext: EvalCiphertext,
using secretKey: SecretKey<Bfv<T>>) throws -> CoeffPlaintext
{
Expand All @@ -27,6 +28,7 @@ extension Bfv {
return CoeffPlaintext(context: ciphertext.context, poly: plaintext)
}

@inlinable
public static func decrypt(_ ciphertext: CoeffCiphertext,
using secretKey: SecretKey<Bfv<T>>) throws -> CoeffPlaintext
{
Expand Down
7 changes: 7 additions & 0 deletions Sources/HomomorphicEncryption/Bfv/Bfv.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public enum Bfv<T: ScalarType>: HeScheme {

// MARK: HE operations

@inlinable
public static func addAssign<F: PolyFormat>(_ lhs: inout Plaintext<Bfv<T>, F>, _ rhs: Plaintext<Bfv<T>, F>) throws {
try validateEquality(of: lhs.context, and: rhs.context)
lhs.poly += rhs.poly
Expand Down Expand Up @@ -57,10 +58,12 @@ public enum Bfv<T: ScalarType>: 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)
}
Expand Down Expand Up @@ -98,18 +101,22 @@ public enum Bfv<T: ScalarType>: 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()
}
Expand Down
5 changes: 5 additions & 0 deletions Sources/HomomorphicEncryption/Ciphertext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ public struct Ciphertext<Scheme: HeScheme, Format: PolyFormat>: 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)
}
Expand All @@ -219,6 +220,7 @@ public struct Ciphertext<Scheme: HeScheme, Format: PolyFormat>: 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)
}
Expand All @@ -229,6 +231,7 @@ public struct Ciphertext<Scheme: HeScheme, Format: PolyFormat>: 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)
}
Expand Down Expand Up @@ -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<Scheme>) throws {
try Scheme.applyGalois(ciphertext: &self, element: element, using: key)
}
Expand All @@ -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<Scheme>) throws {
try Scheme.relinearize(&self, using: key)
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/HomomorphicEncryption/Context.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public final class Context<Scheme: HeScheme>: Equatable, Sendable {
///
/// - Parameter encryptionParameters: Encryption parameters.
/// - Throws: Error upon failure to initialize the context.
@inlinable
public init(encryptionParameters: EncryptionParameters<Scheme>) throws {
self.encryptionParameters = encryptionParameters
self.simdEncodingMatrix = Self.generateEncodingMatrix(encryptionParameters: encryptionParameters)
Expand Down Expand Up @@ -112,6 +113,7 @@ public final class Context<Scheme: HeScheme>: 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<Scheme>, rhs: Context<Scheme>) -> Bool {
lhs === rhs || lhs.encryptionParameters == rhs.encryptionParameters
}
Expand Down
Loading

0 comments on commit 2e0f602

Please sign in to comment.