Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,14 @@ jobs:
name: soundness
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main
with:
# https://github.com/swiftlang/swift-package-manager/issues/8103
api_breakage_check_enabled: false
# swift:6.2-noble leads to issues with Snippets
# e.g. https://github.com/apple/swift-homomorphic-encryption/actions/runs/18144503507/job/51643132814#step:5:1087
docs_check_container_image: swift:6.1-noble
docs_check_container_image: swift:6.2.2-noble
format_check_enabled: false
tests:
name: swifttests
uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main
with:
enable_windows_checks: false
linux_exclude_swift_versions: "[{\"swift_version\": \"5.9\"}, {\"swift_version\": \"5.10\"}]"
linux_exclude_swift_versions: "[{\"swift_version\": \"5.9\"}, {\"swift_version\": \"5.10\"}, {\"swift_version\": \"6.0\"}, {\"swift_version\": \"6.1\"}]"
linux_env_vars: SWIFT_HOMOMORPHIC_ENCRYPTION_ENABLE_BENCHMARKING=1
linux_pre_build_command: "apt-get update && apt-get install -y libjemalloc-dev"
linux_build_command: >
Expand Down
2 changes: 1 addition & 1 deletion .spi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ builder:
- PNNSProcessDatabase
- PrivateInformationRetrieval
- PrivateNearestNeighborSearch
swift_version: 6.0
swift_version: 6.2
2 changes: 1 addition & 1 deletion .swiftformat
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
--nospaceoperators ..<, ...
--ranges no-space
--self init-only
--swiftversion 6.0
--swiftversion 6.2
16 changes: 12 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 6.0
// swift-tools-version: 6.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
// Remember to update CI if changing

Expand Down Expand Up @@ -108,12 +108,13 @@ let package = Package(
"CUtil",
"ModularArithmetic",
],
exclude: ["HomomorphicEncryption.docc"],
swiftSettings: librarySettings),
.target(
name: "HomomorphicEncryptionProtobuf",
dependencies: ["HomomorphicEncryption",
.product(name: "SwiftProtobuf", package: "swift-protobuf")],
exclude: ["generated/README.md"],
exclude: ["generated/README.md", "HomomorphicEncryptionProtobuf.docc"],
swiftSettings: librarySettings),
.target(
name: "_HomomorphicEncryptionExtras",
Expand All @@ -124,6 +125,7 @@ let package = Package(
dependencies: ["HomomorphicEncryption",
.product(name: "AsyncAlgorithms", package: "swift-async-algorithms"),
.product(name: "Numerics", package: "swift-numerics")],
exclude: ["PrivateInformationRetrieval.docc"],
swiftSettings: librarySettings),
.target(
name: "PrivateNearestNeighborSearch",
Expand All @@ -133,14 +135,15 @@ let package = Package(
"HomomorphicEncryption",
"_HomomorphicEncryptionExtras",
],
exclude: ["PrivateNearestNeighborSearch.docc"],
swiftSettings: librarySettings),
.target(
name: "ApplicationProtobuf",
dependencies: ["HomomorphicEncryptionProtobuf",
"PrivateInformationRetrieval",
"PrivateNearestNeighborSearch",
.product(name: "SwiftProtobuf", package: "swift-protobuf")],
exclude: ["generated/README.md", "protobuf_module_mappings.txtpb"],
exclude: ["ApplicationProtobuf.docc", "generated/README.md", "protobuf_module_mappings.txtpb"],
swiftSettings: librarySettings),
.target(
name: "_TestUtilities",
Expand All @@ -159,6 +162,7 @@ let package = Package(
"HomomorphicEncryption",
"ApplicationProtobuf",
],
exclude: ["PIRGenerateDatabase.docc"],
swiftSettings: executableSettings),
.executableTarget(
name: "PIRProcessDatabase",
Expand All @@ -169,6 +173,7 @@ let package = Package(
"HomomorphicEncryption",
.product(name: "Logging", package: "swift-log"),
],
exclude: ["PIRProcessDatabase.docc"],
swiftSettings: executableSettings),
.executableTarget(
name: "PIRShardDatabase",
Expand All @@ -177,6 +182,7 @@ let package = Package(
"HomomorphicEncryption",
"ApplicationProtobuf",
],
exclude: ["PIRShardDatabase.docc"],
swiftSettings: executableSettings),
.executableTarget(
name: "PNNSGenerateDatabase",
Expand All @@ -185,6 +191,7 @@ let package = Package(
"HomomorphicEncryption",
"ApplicationProtobuf",
],
exclude: ["PNNSGenerateDatabase.docc"],
swiftSettings: executableSettings),
.executableTarget(
name: "PNNSProcessDatabase",
Expand All @@ -195,6 +202,7 @@ let package = Package(
"HomomorphicEncryption",
.product(name: "Logging", package: "swift-log"),
],
exclude: ["PNNSProcessDatabase.docc"],
swiftSettings: executableSettings),
.testTarget(
name: "HomomorphicEncryptionTests",
Expand Down Expand Up @@ -342,6 +350,6 @@ if enableDocCPlugin {
// Set the minimum macOS version for the package
#if canImport(Darwin)
package.platforms = [
.macOS(.v15), // Constrained by UInt128 support
.macOS(.v26), // Constrained by use of Span
]
#endif
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ The following table maps Swift Homomorphic Encryption package versions to requir
Package version | Swift version | Xcode version
----------------|---------------|-----------------------------------------
1.0.x | >= Swift 5.10 | >= Xcode 15.3
main | >= Swift 6.0 | >= Xcode 16.1
main | >= Swift 6.2 | >= Xcode 26

### Source Stability
Swift Homomorphic Encryption follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html). Source breaking changes to the public API can only land in a new major version, with the following exception:
Expand All @@ -155,7 +155,7 @@ We'd like this package to quickly embrace Swift language and toolchain improveme
Developing Swift Homomorphic Encryption requires:
* [Nick Lockwood SwiftFormat](https://github.com/nicklockwood/SwiftFormat), 0.58.6
* [pre-commit](https://pre-commit.com)
* [swift-format](https://github.com/swiftlang/swift-format), 600.0.0
* [swift-format](https://github.com/swiftlang/swift-format), 602.0.0
* [swift-protobuf](https://github.com/apple/swift-protobuf), 1.31.1
* [SwiftLint](https://github.com/realm/SwiftLint), 0.62.2

Expand Down
27 changes: 11 additions & 16 deletions Sources/HomomorphicEncryption/Array2d.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,36 +76,31 @@ public struct Array2d<T: Equatable & AdditiveArithmetic & Sendable>: Equatable,
columnCount: columnCount)
}

/// Provides scoped access to the underlying buffer storing the array's data.
/// Provides scoped access to the underlying buffer storing the array's data using a Span.
///
/// Use this method when you need temporary read-only access to the array's contiguous storage.
/// The buffer pointer is only valid for the duration of the closure's execution.
///
/// - Parameter body: A closure that takes an `UnsafeBufferPointer` to the array's data.
/// The buffer pointer argument is valid only for the duration of the closure's execution.
/// - Parameter body: A closure that takes a `Span<T>` to the array's data.
/// - Returns: The return value of the `body` closure.
/// - Throws: Rethrows any error thrown by the `body` closure.
public func withUnsafeData<Return>(_ body: (UnsafeBufferPointer<T>) throws -> Return) rethrows -> Return {
try data.withUnsafeBufferPointer { pointer in
try body(pointer)
}
@inlinable
public func withDataSpan<Return>(_ body: (Span<T>) throws -> Return) rethrows -> Return {
try body(data.span)
}

/// Provides scoped access to the underlying buffer storing the array's data for mutation.
///
/// Use this method when you need temporary read-write access to the array's contiguous storage.
/// The buffer pointer is only valid for the duration of the closure's execution.
///
/// - Parameter body: A closure that takes an `UnsafeMutableBufferPointer` to the array's data.
/// The buffer pointer argument is valid only for the duration of the closure's execution.
/// - Parameter body: A closure that takes a `MutableSpan<T>` to the array's data.
/// - Returns: The return value of the `body` closure.
/// - Throws: Rethrows any error thrown by the `body` closure.
public mutating func withUnsafeMutableData<Return>(_ body: (UnsafeMutableBufferPointer<T>) throws
-> Return) rethrows -> Return
@inlinable
public mutating func withMutableDataSpan<Return>(_ body: (inout MutableSpan<T>) throws -> Return) rethrows
-> Return
{
try data.withUnsafeMutableBufferPointer { pointer in
try body(pointer)
}
var span = data.mutableSpan
return try body(&span)
}
}

Expand Down
94 changes: 45 additions & 49 deletions Sources/HomomorphicEncryption/Bfv/Bfv+Encrypt.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,61 +85,57 @@ extension Bfv {

let plaintextIndices = plaintext.poly.polyIndices(rnsIndex: 0)
var adjust = plaintext
// swiftlint:disable:next closure_body_length
plaintext.poly.data.data.withUnsafeBufferPointer { plaintextData in
adjust.poly.data.data.withUnsafeMutableBufferPointer { adjustPtr in
let t = rnsTool.t.modulus
if T.DoubleWidth(t.multipliedFullWidth(by: t)) <= T.max {
// Prefer single-word division, since it's faster
for index in plaintextIndices {
var adjustCoeff = rnsTool.qModT &* plaintextData[index]
adjustCoeff &+= rnsTool.tThreshold
adjustPtr[index] = adjustCoeff.dividingFloor(by: rnsTool.t)
}
} else {
let tThreshold = Scalar.DoubleWidth(rnsTool.tThreshold)
for index in plaintextIndices {
var adjustCoeff = Scalar
.DoubleWidth(rnsTool.qModT.multipliedFullWidth(by: plaintextData[index]))
adjustCoeff &+= tThreshold
adjustPtr[index] = adjustCoeff.dividingFloor(by: rnsTool.t).low
}
}

let plaintextData = plaintext.poly.data.data.span
var adjustSpan = adjust.poly.data.data.mutableSpan
let t = rnsTool.t.modulus
if T.DoubleWidth(t.multipliedFullWidth(by: t)) <= T.max {
// Prefer single-word division, since it's faster
for index in plaintextIndices {
var adjustCoeff = rnsTool.qModT &* plaintextData[index]
adjustCoeff &+= rnsTool.tThreshold
adjustSpan[index] = adjustCoeff.dividingFloor(by: rnsTool.t)
}
} else {
let tThreshold = Scalar.DoubleWidth(rnsTool.tThreshold)
for index in plaintextIndices {
var adjustCoeff = Scalar
.DoubleWidth(rnsTool.qModT.multipliedFullWidth(by: plaintextData[index]))
adjustCoeff &+= tThreshold
adjustSpan[index] = adjustCoeff.dividingFloor(by: rnsTool.t).low
}
}

var c0 = ciphertext.polys[0]
let c1 = ciphertext.polys[1] // used for polynomial index computation only

switch op {
case PlaintextTranslateOp.Add:
c0.data.data.withUnsafeMutableBufferPointer { c0Ptr in
for (rnsIndex, rnsDelta) in rnsTool.qDivT.enumerated() {
for (plainIndex, cipherIndex) in c1.polyIndices(rnsIndex: rnsIndex).enumerated() {
let plainTimesDelta = rnsDelta.multiplyMod(plaintextData[plainIndex])
let roundQTimesMt = plainTimesDelta.addMod(
adjust[plainIndex],
modulus: rnsDelta.modulus)
c0Ptr[cipherIndex] = c0Ptr[cipherIndex].addMod(roundQTimesMt, modulus: rnsDelta.modulus)
}
}
var c0 = ciphertext.polys[0]
let c1 = ciphertext.polys[1] // used for polynomial index computation only

switch op {
case PlaintextTranslateOp.Add:
var c0Span = c0.data.data.mutableSpan
for (rnsIndex, rnsDelta) in rnsTool.qDivT.enumerated() {
for (plainIndex, cipherIndex) in c1.polyIndices(rnsIndex: rnsIndex).enumerated() {
let plainTimesDelta = rnsDelta.multiplyMod(plaintextData[plainIndex])
let roundQTimesMt = plainTimesDelta.addMod(
adjust[plainIndex],
modulus: rnsDelta.modulus)
c0Span[cipherIndex] = c0Span[cipherIndex].addMod(roundQTimesMt, modulus: rnsDelta.modulus)
}
case PlaintextTranslateOp.Subtract:
c0.data.data.withUnsafeMutableBufferPointer { c0Ptr in
for (rnsIndex, rnsDelta) in rnsTool.qDivT.enumerated() {
for (plainIndex, cipherIndex) in c1.polyIndices(rnsIndex: rnsIndex).enumerated() {
let plainTimesDelta = rnsDelta.multiplyMod(plaintextData[plainIndex])
let roundQTimesMt = plainTimesDelta.addMod(
adjust[plainIndex],
modulus: rnsDelta.modulus)
c0Ptr[cipherIndex] = c0Ptr[cipherIndex].subtractMod(
roundQTimesMt,
modulus: rnsDelta.modulus)
}
}
}
case PlaintextTranslateOp.Subtract:
var c0Span = c0.data.data.mutableSpan
for (rnsIndex, rnsDelta) in rnsTool.qDivT.enumerated() {
for (plainIndex, cipherIndex) in c1.polyIndices(rnsIndex: rnsIndex).enumerated() {
let plainTimesDelta = rnsDelta.multiplyMod(plaintextData[plainIndex])
let roundQTimesMt = plainTimesDelta.addMod(
adjust[plainIndex],
modulus: rnsDelta.modulus)
c0Span[cipherIndex] = c0Span[cipherIndex].subtractMod(
roundQTimesMt,
modulus: rnsDelta.modulus)
}
}
ciphertext.polys[0] = c0
}
ciphertext.polys[0] = c0
}

@inlinable
Expand Down
24 changes: 11 additions & 13 deletions Sources/HomomorphicEncryption/Bfv/Bfv+Keys.swift
Original file line number Diff line number Diff line change
Expand Up @@ -179,25 +179,23 @@ extension Bfv {
for (index, poly) in keyCiphers[decomposeIndex].polys.enumerated() {
let accIndex = poly.data.index(row: index, column: 0)
let polyIndex = poly.data.index(row: keyIndex, column: 0)
poly.data.data.withUnsafeBufferPointer { polyPtr in
for columnIndex in 0..<degree {
let prod = bufferSlice[columnIndex]
.multipliedFullWidth(by: polyPtr[polyIndex &+ columnIndex])
// Overflow avoided by `maxLazyProductAccumulationCount()` check during context
// initialization
accumulator[accIndex &+ columnIndex] &+= T.DoubleWidth(prod)
}
let polySpan = poly.data.data.span
for columnIndex in 0..<degree {
let prod = bufferSlice[columnIndex]
.multipliedFullWidth(by: polySpan[polyIndex &+ columnIndex])
// Overflow avoided by `maxLazyProductAccumulationCount()` check during context
// initialization
accumulator[accIndex &+ columnIndex] &+= T.DoubleWidth(prod)
}
}
}
let prodIndex = ciphertextProd.polys[0].data.index(row: rnsIndex, column: 0)
for rowIndex in ciphertextProd.polys.indices {
let accIndex = accumulator.index(row: rowIndex, column: 0)
ciphertextProd.polys[rowIndex].data.data.withUnsafeMutableBufferPointer { ciphertextProdPtr in
for columnIndex in 0..<degree {
ciphertextProdPtr[prodIndex &+ columnIndex] = keyModulus
.reduce(accumulator[accIndex &+ columnIndex])
}
var ciphertextProdSpan = ciphertextProd.polys[rowIndex].data.data.mutableSpan
for columnIndex in 0..<degree {
ciphertextProdSpan[prodIndex &+ columnIndex] = keyModulus
.reduce(accumulator[accIndex &+ columnIndex])
}
}
}
Expand Down
12 changes: 5 additions & 7 deletions Sources/HomomorphicEncryption/Bfv/Bfv.swift
Original file line number Diff line number Diff line change
Expand Up @@ -293,13 +293,11 @@ public enum Bfv<T: ScalarType>: HeScheme {
{
let poly = result.polys[0]
for (polyIndex, accumulatorPoly) in accumulator.enumerated() {
accumulatorPoly.data.withUnsafeBufferPointer { accumulatorPolyPtr in
result.polys[polyIndex].data.data.withUnsafeMutableBufferPointer { resultPtr in
for (rnsIndex, modulus) in poly.polyContext().reduceModuli.enumerated() {
for index in poly.polyIndices(rnsIndex: rnsIndex) {
resultPtr[index] = modulus.reduce(accumulatorPolyPtr[index])
}
}
let accumulatorPolySpan = accumulatorPoly.data.span
var resultSpan = result.polys[polyIndex].data.data.mutableSpan
for (rnsIndex, modulus) in poly.polyContext().reduceModuli.enumerated() {
for index in poly.polyIndices(rnsIndex: rnsIndex) {
resultSpan[index] = modulus.reduce(accumulatorPolySpan[index])
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/HomomorphicEncryption/CrtComposer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public struct _CrtComposer<T: ScalarType>: Sendable {
for column in 0..<data.columnCount {
let tmp = V(inversePuncturedProduct.multiplyMod(data[
row,
column
column,
]))
let addend = tmp &* puncturedProduct
products[column] = products[column].addMod(addend, modulus: q)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ The following table maps Swift Homomorphic Encryption packgae versions to requir
Package version | Swift version | Xcode version
----------------|---------------|-----------------------------------------
1.0.x | >= Swift 5.10 | >= Xcode 15.3
main | >= Swift 6.0 | >= Xcode 16.1
main | >= Swift 6.2 | >= Xcode 26

### Source Stability
Swift Homomorphic Encryption follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html). Source breaking changes to the public API can only land in a new major version, with the following exception:
Expand Down
Loading
Loading