Skip to content

Commit d2ecaa1

Browse files
committed
Add async APIs
1 parent ab59112 commit d2ecaa1

File tree

9 files changed

+868
-159
lines changed

9 files changed

+868
-159
lines changed

Sources/HomomorphicEncryption/Bfv/Bfv.swift

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2024 Apple Inc. and the Swift Homomorphic Encryption project authors
1+
// Copyright 2024-2025 Apple Inc. and the Swift Homomorphic Encryption project authors
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -339,4 +339,22 @@ public enum Bfv<T: ScalarType>: HeScheme {
339339
reduceToCiphertext(accumulator: accumulator, result: &result)
340340
return result
341341
}
342+
343+
@inlinable
344+
public static func forwardNtt(_ ciphertext: CoeffCiphertext) throws -> EvalCiphertext {
345+
let polys = try ciphertext.polys.map { try $0.forwardNtt() }
346+
return Ciphertext<Bfv<T>, Eval>(context: ciphertext.context,
347+
polys: polys,
348+
correctionFactor: ciphertext.correctionFactor,
349+
seed: ciphertext.seed)
350+
}
351+
352+
@inlinable
353+
public static func inverseNtt(_ ciphertext: EvalCiphertext) throws -> CoeffCiphertext {
354+
let polys = try ciphertext.polys.map { try $0.inverseNtt() }
355+
return Ciphertext<Bfv<T>, Coeff>(context: ciphertext.context,
356+
polys: polys,
357+
correctionFactor: ciphertext.correctionFactor,
358+
seed: ciphertext.seed)
359+
}
342360
}

Sources/HomomorphicEncryption/Ciphertext.swift

+4-11
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,12 @@ public struct Ciphertext<Scheme: HeScheme, Format: PolyFormat>: Equatable, Senda
162162

163163
@inlinable
164164
package func forwardNtt() throws -> Ciphertext<Scheme, Eval> where Format == Coeff {
165-
let polys = try polys.map { try $0.forwardNtt() }
166-
return Ciphertext<Scheme, Eval>(context: context, polys: polys, correctionFactor: correctionFactor, seed: seed)
165+
try Scheme.forwardNtt(self)
167166
}
168167

169168
@inlinable
170169
package func inverseNtt() throws -> Ciphertext<Scheme, Coeff> where Format == Eval {
171-
let polys = try polys.map { try $0.inverseNtt() }
172-
return Ciphertext<Scheme, Coeff>(context: context, polys: polys, correctionFactor: correctionFactor, seed: seed)
170+
try Scheme.inverseNtt(self)
173171
}
174172

175173
/// Converts the ciphertext to a ``HeScheme/CoeffCiphertext``.
@@ -296,9 +294,7 @@ public struct Ciphertext<Scheme: HeScheme, Format: PolyFormat>: Equatable, Senda
296294
/// - seealso: ``Ciphertext/modSwitchDown()`` for more information and an alternative API.
297295
@inlinable
298296
public mutating func modSwitchDownToSingle() throws where Format == Scheme.CanonicalCiphertextFormat {
299-
while moduli.count > 1 {
300-
try Scheme.modSwitchDown(&self)
301-
}
297+
try Scheme.modSwitchDownToSingle(&self)
302298
}
303299

304300
/// Decryption of a ciphertext.
@@ -485,10 +481,7 @@ extension Ciphertext where Format == Coeff {
485481
/// - Throws: Error upon failure to compute the inverse.
486482
@inlinable
487483
public mutating func multiplyInversePowerOfX(power: Int) throws {
488-
precondition(power >= 0)
489-
for index in polys.indices {
490-
try polys[index].multiplyInversePowerOfX(power)
491-
}
484+
try Scheme.multiplyInversePowerOfX(&self, power: power)
492485
}
493486
}
494487

Sources/HomomorphicEncryption/HeScheme.swift

+250-1
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
// Copyright 2025 Apple Inc. and the Swift Homomorphic Encryption project authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
/// The functions in this extension are the default implementation of async methods of HE scheme.
16+
extension HeScheme {
17+
// swiftlint:disable missing_docs
18+
@inlinable
19+
public static func rotateColumnsAsync(
20+
of ciphertext: inout CanonicalCiphertext,
21+
by step: Int,
22+
using evaluationKey: EvaluationKey) async throws
23+
{
24+
try rotateColumns(of: &ciphertext, by: step, using: evaluationKey)
25+
}
26+
27+
@inlinable
28+
public static func swapRowsAsync(
29+
of ciphertext: inout CanonicalCiphertext,
30+
using evaluationKey: EvaluationKey) async throws
31+
{
32+
try swapRows(of: &ciphertext, using: evaluationKey)
33+
}
34+
35+
@inlinable
36+
public static func addAssignAsync(_ lhs: inout CoeffPlaintext, _ rhs: CoeffPlaintext) async throws {
37+
try addAssign(&lhs, rhs)
38+
}
39+
40+
@inlinable
41+
public static func addAssignAsync(_ lhs: inout EvalPlaintext, _ rhs: EvalPlaintext) async throws {
42+
try addAssign(&lhs, rhs)
43+
}
44+
45+
@inlinable
46+
public static func addAssignCoeffAsync(_ lhs: inout CoeffCiphertext, _ rhs: CoeffCiphertext) async throws {
47+
try addAssignCoeff(&lhs, rhs)
48+
}
49+
50+
@inlinable
51+
public static func addAssignEvalAsync(_ lhs: inout EvalCiphertext, _ rhs: EvalCiphertext) async throws {
52+
try addAssignEval(&lhs, rhs)
53+
}
54+
55+
@inlinable
56+
public static func subAssignCoeffAsync(_ lhs: inout CoeffCiphertext, _ rhs: CoeffCiphertext) async throws {
57+
try subAssignCoeff(&lhs, rhs)
58+
}
59+
60+
@inlinable
61+
public static func subAssignEvalAsync(_ lhs: inout EvalCiphertext, _ rhs: EvalCiphertext) async throws {
62+
try subAssignEval(&lhs, rhs)
63+
}
64+
65+
@inlinable
66+
public static func addAssignCoeffAsync(
67+
_ ciphertext: inout CoeffCiphertext,
68+
_ plaintext: CoeffPlaintext) async throws
69+
{
70+
try addAssignCoeff(&ciphertext, plaintext)
71+
}
72+
73+
@inlinable
74+
public static func addAssignEvalAsync(_ ciphertext: inout EvalCiphertext, _ plaintext: EvalPlaintext) async throws {
75+
try addAssignEval(&ciphertext, plaintext)
76+
}
77+
78+
@inlinable
79+
public static func subAssignCoeffAsync(
80+
_ ciphertext: inout CoeffCiphertext,
81+
_ plaintext: CoeffPlaintext) async throws
82+
{
83+
try subAssignCoeff(&ciphertext, plaintext)
84+
}
85+
86+
@inlinable
87+
public static func subAssignEvalAsync(_ ciphertext: inout EvalCiphertext, _ plaintext: EvalPlaintext) async throws {
88+
try subAssignEval(&ciphertext, plaintext)
89+
}
90+
91+
@inlinable
92+
public static func subCoeffAsync(_ plaintext: CoeffPlaintext,
93+
_ ciphertext: CoeffCiphertext) async throws -> CoeffCiphertext
94+
{
95+
try subCoeff(plaintext, ciphertext)
96+
}
97+
98+
@inlinable
99+
public static func subEvalAsync(_ plaintext: EvalPlaintext,
100+
_ ciphertext: EvalCiphertext) async throws -> EvalCiphertext
101+
{
102+
try subEval(plaintext, ciphertext)
103+
}
104+
105+
@inlinable
106+
public static func mulAssignAsync(_ ciphertext: inout EvalCiphertext, _ plaintext: EvalPlaintext) async throws {
107+
try mulAssign(&ciphertext, plaintext)
108+
}
109+
110+
@inlinable
111+
public static func negAssignCoeffAsync(_ ciphertext: inout CoeffCiphertext) async {
112+
negAssign(&ciphertext)
113+
}
114+
115+
@inlinable
116+
public static func negAssignEvalAsync(_ ciphertext: inout EvalCiphertext) async {
117+
negAssign(&ciphertext)
118+
}
119+
120+
@inlinable
121+
public static func innerProductAsync(
122+
_ lhs: some Collection<CanonicalCiphertext>,
123+
_ rhs: some Collection<CanonicalCiphertext>) async throws
124+
-> CanonicalCiphertext
125+
{
126+
try innerProduct(lhs, rhs)
127+
}
128+
129+
@inlinable
130+
public static func innerProductAsync(ciphertexts: some Collection<EvalCiphertext>,
131+
plaintexts: some Collection<EvalPlaintext>) async throws -> EvalCiphertext
132+
{
133+
try innerProduct(ciphertexts: ciphertexts, plaintexts: plaintexts)
134+
}
135+
136+
@inlinable
137+
public static func innerProductAsync(ciphertexts: some Collection<EvalCiphertext>,
138+
plaintexts: some Collection<EvalPlaintext?>) async throws -> EvalCiphertext
139+
{
140+
try innerProduct(ciphertexts: ciphertexts, plaintexts: plaintexts)
141+
}
142+
143+
@inlinable
144+
public static func mulAssignAsync(_ lhs: inout CanonicalCiphertext, _ rhs: CanonicalCiphertext) async throws {
145+
try mulAssign(&lhs, rhs)
146+
}
147+
148+
@inlinable
149+
public static func addAssignAsync(_ lhs: inout CanonicalCiphertext, _ rhs: CanonicalCiphertext) async throws {
150+
try addAssign(&lhs, rhs)
151+
}
152+
153+
@inlinable
154+
public static func subAssignAsync(_ lhs: inout CanonicalCiphertext, _ rhs: CanonicalCiphertext) async throws {
155+
try subAssign(&lhs, rhs)
156+
}
157+
158+
@inlinable
159+
public static func subAssignAsync(
160+
_ ciphertext: inout CanonicalCiphertext,
161+
_ plaintext: CoeffPlaintext) async throws
162+
{
163+
try subAssign(&ciphertext, plaintext)
164+
}
165+
166+
@inlinable
167+
public static func subAssignAsync(_ ciphertext: inout CanonicalCiphertext,
168+
_ plaintext: EvalPlaintext) async throws
169+
{
170+
try subAssign(&ciphertext, plaintext)
171+
}
172+
173+
@inlinable
174+
public static func subAsync(_ plaintext: CoeffPlaintext,
175+
_ ciphertext: CanonicalCiphertext) async throws -> CanonicalCiphertext
176+
{
177+
try sub(plaintext, ciphertext)
178+
}
179+
180+
@inlinable
181+
public static func subAsync(_ plaintext: EvalPlaintext,
182+
_ ciphertext: CanonicalCiphertext) async throws -> CanonicalCiphertext
183+
{
184+
try sub(plaintext, ciphertext)
185+
}
186+
187+
@inlinable
188+
public static func modSwitchDownAsync(_ ciphertext: inout CanonicalCiphertext) async throws {
189+
try modSwitchDown(&ciphertext)
190+
}
191+
192+
@inlinable
193+
public static func applyGaloisAsync(
194+
ciphertext: inout CanonicalCiphertext,
195+
element: Int,
196+
using key: EvaluationKey) async throws
197+
{
198+
try applyGalois(ciphertext: &ciphertext, element: element, using: key)
199+
}
200+
201+
@inlinable
202+
public static func relinearizeAsync(_ ciphertext: inout CanonicalCiphertext,
203+
using key: EvaluationKey) async throws
204+
{
205+
try relinearize(&ciphertext, using: key)
206+
}
207+
208+
@inlinable
209+
public static func forwardNttAsync(_ ciphertext: CoeffCiphertext) async throws -> EvalCiphertext {
210+
try forwardNtt(ciphertext)
211+
}
212+
213+
@inlinable
214+
public static func inverseNttAsync(_ ciphertext: EvalCiphertext) async throws -> CoeffCiphertext {
215+
try inverseNtt(ciphertext)
216+
}
217+
218+
@inlinable
219+
public static func modSwitchDownToSingleAsync(_ ciphertext: inout CanonicalCiphertext) async throws {
220+
try modSwitchDownToSingle(&ciphertext)
221+
}
222+
223+
@inlinable
224+
public static func multiplyInversePowerOfXAsync(_ ciphertext: inout CoeffCiphertext, power: Int) async throws {
225+
try multiplyInversePowerOfX(&ciphertext, power: power)
226+
}
227+
// swiftlint:enable missing_docs
228+
}

Sources/HomomorphicEncryption/NoOpScheme.swift

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2024 Apple Inc. and the Swift Homomorphic Encryption project authors
1+
// Copyright 2024-2025 Apple Inc. and the Swift Homomorphic Encryption project authors
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -302,4 +302,20 @@ public enum NoOpScheme: HeScheme {
302302
{
303303
minNoiseBudget
304304
}
305+
306+
public static func forwardNtt(_ ciphertext: CoeffCiphertext) throws -> EvalCiphertext {
307+
let polys = try ciphertext.polys.map { try $0.forwardNtt() }
308+
return Ciphertext<NoOpScheme, Eval>(context: ciphertext.context,
309+
polys: polys,
310+
correctionFactor: ciphertext.correctionFactor,
311+
seed: ciphertext.seed)
312+
}
313+
314+
public static func inverseNtt(_ ciphertext: EvalCiphertext) throws -> CoeffCiphertext {
315+
let polys = try ciphertext.polys.map { try $0.inverseNtt() }
316+
return Ciphertext<NoOpScheme, Coeff>(context: ciphertext.context,
317+
polys: polys,
318+
correctionFactor: ciphertext.correctionFactor,
319+
seed: ciphertext.seed)
320+
}
305321
}

Sources/PNNSGenerateDatabase/PNNSGenerateDatabase.docc/PNNSGenerateDatabase.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Private Nearest Neighbor Search database generation
88
The resulting database can be processed with the [PNNSProcessDatabase](https://swiftpackageindex.com/apple/swift-homomorphic-encryption/main/documentation/pnnsprocessdatabase) executable.
99

1010
### Requirements
11-
First ensure sure that the `~/.swiftpm/bin` directory is on your `$PATH`.
11+
First ensure that the `~/.swiftpm/bin` directory is on your `$PATH`.
1212
For example, if using the `zsh` shell, add the following line to your `~/.zshrc`
1313
```sh
1414
export PATH="$HOME/.swiftpm/bin:$PATH"

Sources/PNNSProcessDatabase/PNNSProcessDatabase.docc/PNNSProcessDatabase.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ PNNS database processing will transform a database in preparation for hosting PN
77
The `PNNSProcessDatabase` binary performs the processing.
88

99
### Requirements
10-
First ensure sure that the `~/.swiftpm/bin` directory is on your `$PATH`.
10+
First ensure that the `~/.swiftpm/bin` directory is on your `$PATH`.
1111
For example, if using the `zsh` shell, add the following line to your `~/.zshrc`
1212
```sh
1313
export PATH="$HOME/.swiftpm/bin:$PATH"

0 commit comments

Comments
 (0)