Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
31 changes: 16 additions & 15 deletions Sources/HomomorphicEncryption/Array2d.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,35 +76,36 @@ 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.
/// The Span 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.
/// The Span argument is valid only for the duration of the closure's execution.
/// - 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.
/// The MutableSpan 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.
/// The MutableSpan argument is valid only for the duration of the closure's execution.
/// - 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)
try data.withUnsafeMutableBufferPointer { buffer in
var span = buffer.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