Skip to content

Commit 773354b

Browse files
authored
Add async * and - APIs (#251)
1 parent efb4833 commit 773354b

File tree

8 files changed

+806
-410
lines changed

8 files changed

+806
-410
lines changed

Snippets/HomomorphicEncryption/BasicsAsyncSnippet.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,21 +72,21 @@ precondition(decoded == [7, 15, 2, 2, 11, 0, 7, 15])
7272

7373
// snippet.subtraction
7474
// We can subtract a plaintext from a ciphertext.
75-
try sum -= plaintext
75+
try await sum -= plaintext
7676
plaintextSum = try sum.decrypt(using: secretKey)
7777
decoded = try plaintextSum.decode(format: .coefficient)
7878
precondition(decoded == [16, 10, 7, 7, 13, 0, 16, 10])
7979

8080
// We can also subtract a ciphertext from a ciphertext.
81-
try sum -= ciphertext
81+
try await sum -= ciphertext
8282
plaintextSum = try sum.decrypt(using: secretKey)
8383
decoded = try plaintextSum.decode(format: .coefficient)
8484
precondition(decoded == [8, 5, 12, 12, 15, 0, 8, 5])
8585

8686
// One special case is when subtracting a ciphertext from itself.
8787
// This yields a "transparent ciphertext", which reveals the underlying
8888
// plaintext to any observer. The observed value in this case is zero.
89-
try sum -= sum
89+
try await sum -= sum
9090
precondition(sum.isTransparent())
9191

9292
// snippet.hide

Sources/HomomorphicEncryption/Ciphertext.swift

Lines changed: 193 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,8 @@ extension Collection {
600600
// MARK: - Async ciphertext functions
601601

602602
extension Ciphertext {
603+
// MARK: - Async ciphertext + plaintext
604+
603605
/// Async ciphertext-plaintext addition.
604606
/// - Parameters:
605607
/// - plaintext: Plaintext to add.
@@ -630,6 +632,22 @@ extension Ciphertext {
630632
return result
631633
}
632634

635+
/// Async ciphertext-plaintext addition.
636+
/// - Parameters:
637+
/// - ciphertext: Ciphertext to add to.
638+
/// - plaintext: Plaintext to add.
639+
/// - Throws: Error upon failure to add.
640+
@inlinable
641+
public static func += (
642+
ciphertext: inout Ciphertext<Scheme, Format>,
643+
plaintext: Plaintext<Scheme, some PolyFormat>) async throws
644+
{
645+
try Scheme.validateEquality(of: ciphertext.context, and: plaintext.context)
646+
try await Scheme.addAssignAsync(&ciphertext, plaintext)
647+
}
648+
649+
// MARK: - Async ciphertext + ciphertext
650+
633651
/// Async ciphertext addition.
634652
/// - Parameters:
635653
/// - lhs: Ciphertext to add to.
@@ -661,21 +679,189 @@ extension Ciphertext {
661679
return result
662680
}
663681

664-
/// Async ciphertext addition.
682+
// MARK: Async ciphertext -= ciphertext
683+
684+
/// Async ciphertext subtraction.
665685
/// - Parameters:
666-
/// - ciphertext: Ciphertext to add to.
667-
/// - plaintext: Plaintext to add.
668-
/// - Throws: Error upon failure to add.
669-
/// - seealso: ``Ciphertext/+=(_:_:)-8y0jp`` for a sync version.
686+
/// - lhs: Ciphertext to subtract from.
687+
/// - rhs: Plaintext to subtract.
688+
/// - Returns: A ciphertext encrypting the difference `lhs - rhs'.
689+
/// - Throws: Error upon failure to subtract.
670690
@inlinable
671-
public static func += (
691+
public static func - (lhs: Ciphertext<Scheme, Format>,
692+
rhs: Ciphertext<Scheme, some PolyFormat>) async throws -> Self
693+
{
694+
var result = lhs
695+
try await result -= rhs
696+
return result
697+
}
698+
699+
// MARK: Async ciphertext - ciphertext
700+
701+
/// Async ciphertext subtraction.
702+
/// - Parameters:
703+
/// - lhs: Ciphertext to subtract from.
704+
/// - rhs: Ciphertext to subtract.
705+
/// - Throws: Error upon failure to subtract.
706+
@inlinable
707+
public static func -= (
708+
lhs: inout Ciphertext<Scheme, Format>,
709+
rhs: Ciphertext<Scheme, some PolyFormat>) async throws
710+
{
711+
try Scheme.validateEquality(of: lhs.context, and: rhs.context)
712+
try await Scheme.subAssignAsync(&lhs, rhs)
713+
}
714+
715+
// MARK: Async ciphertext - plaintext
716+
717+
/// Async ciphertext - plaintext
718+
/// - Parameters:
719+
/// - ciphertext: Ciphertext to subtract from.
720+
/// - plaintext: Plaintext to subtract.
721+
/// - Returns: A ciphertext encrypting the difference `ciphertext - plaintext`.
722+
/// - Throws: Error upon failure to subtract.
723+
@inlinable
724+
public static func - (ciphertext: Ciphertext<Scheme, Format>,
725+
plaintext: Plaintext<Scheme, some PolyFormat>) async throws -> Self
726+
{
727+
var result = ciphertext
728+
try await result -= plaintext
729+
return result
730+
}
731+
732+
// MARK: Async ciphertext -= plaintext
733+
734+
/// Async ciphertext -= plaintext
735+
/// - Parameters:
736+
/// - ciphertext: Ciphertext to subtract from.
737+
/// - plaintext: Plaintext to subtract.
738+
/// - Throws: Error upon failure to subtract.
739+
@inlinable
740+
public static func -= (
672741
ciphertext: inout Ciphertext<Scheme, Format>,
673742
plaintext: Plaintext<Scheme, some PolyFormat>) async throws
674743
{
675744
try Scheme.validateEquality(of: ciphertext.context, and: plaintext.context)
676-
try await Scheme.addAssignAsync(&ciphertext, plaintext)
745+
try await Scheme.subAssignAsync(&ciphertext, plaintext)
746+
}
747+
748+
// MARK: Async plaintext - ciphertext
749+
750+
/// Async plaintext - ciphertext.
751+
/// - Parameters:
752+
/// - plaintext: Plaintext to subtract from.
753+
/// - ciphertext: Ciphertext to subtract.
754+
/// - Returns: A ciphertext encrypting the difference `plaintext - ciphertext`.
755+
/// - Throws: Error upon failure to subtract.
756+
@inlinable
757+
public static func - (
758+
plaintext: Plaintext<Scheme, some PolyFormat>,
759+
ciphertext: Ciphertext<Scheme, Format>) async throws -> Ciphertext<Scheme, Format>
760+
{
761+
try Scheme.validateEquality(of: ciphertext.context, and: plaintext.context)
762+
return try await Scheme.subAsync(plaintext, ciphertext)
677763
}
678764

765+
// MARK: Async ciphertext * ciphertext
766+
767+
/// Async ciphertext multiplication.
768+
/// - Parameters:
769+
/// - lhs: Ciphertext to multiply.
770+
/// - rhs: Ciphertext to multiply.
771+
/// - Returns: A ciphertext encrypting the product `lhs * rhs`.
772+
/// - Throws: Error upon failure to multiply.
773+
/// > Note: the values of the decrypted product depend on the ``EncodeFormat`` of the plaintexts encrypted by `lhs`
774+
/// and `rhs.`
775+
///
776+
/// > Important: The resulting ciphertext has 3 polynomials and can be relinearized. See
777+
/// ``HeScheme/relinearize(_:using:)``
778+
@inlinable
779+
public static func * (lhs: Ciphertext<Scheme, Format>, rhs: Ciphertext<Scheme, Format>) async throws -> Self
780+
where Format == Scheme.CanonicalCiphertextFormat
781+
{
782+
var result = lhs
783+
try await result *= rhs
784+
return result
785+
}
786+
787+
// MARK: Async ciphertext * plaintext
788+
789+
/// Async ciphertext-plaintext multiplication.
790+
/// - Parameters:
791+
/// - ciphertext: Ciphertext to multiply.
792+
/// - plaintext: Plaintext to multiply.
793+
/// - Returns: A ciphertext encrypting the product `ciphertext * plaintext`.
794+
/// - Throws: Error upon failure to multiply.
795+
@inlinable
796+
public static func * (ciphertext: Ciphertext<Scheme, Format>,
797+
plaintext: Plaintext<Scheme, Eval>) async throws -> Self
798+
where Format == Eval
799+
{
800+
var result = ciphertext
801+
try await result *= plaintext
802+
return result
803+
}
804+
805+
/// Async ciphertext-plaintext multiplication.
806+
/// - Parameters:
807+
/// - ciphertext: Ciphertext to multiply.
808+
/// - plaintext: Plaintext to multiply.
809+
/// - Returns: A ciphertext encrypting the product `ciphertext * plaintext`.
810+
/// - Throws: Error upon failure to multiply.
811+
@inlinable
812+
public static func * (plaintext: Plaintext<Scheme, Eval>,
813+
ciphertext: Ciphertext<Scheme, Format>) async throws -> Self
814+
where Format == Eval
815+
{
816+
try await ciphertext * plaintext
817+
}
818+
819+
// MARK: Async ciphertext *= plaintext
820+
821+
/// Async ciphertext-plaintext multiplication.
822+
/// - Parameters:
823+
/// - ciphertext: Ciphertext to multiply. Will store the product.
824+
/// - plaintext: Plaintext to multiply.
825+
/// - Throws: Error upon failure to multiply.
826+
@inlinable
827+
public static func *= (
828+
ciphertext: inout Ciphertext<Scheme, Format>,
829+
plaintext: Plaintext<Scheme, Eval>) async throws
830+
where Format == Eval
831+
{
832+
try Scheme.validateEquality(of: ciphertext.context, and: plaintext.context)
833+
try await Scheme.mulAssignAsync(&ciphertext, plaintext)
834+
}
835+
836+
// MARK: Async ciphertext *= ciphertext
837+
838+
/// Async ciphertext-ciphertext multiplication.
839+
/// - Parameters:
840+
/// - lhs: Ciphertext to multiply. Will store the product.
841+
/// - rhs: Ciphertext to multiply.
842+
/// - Throws: Error upon failure to multiply.
843+
@inlinable
844+
public static func *= (lhs: inout Ciphertext<Scheme, Format>, rhs: Ciphertext<Scheme, Format>) async throws
845+
where Format == Scheme.CanonicalCiphertextFormat
846+
{
847+
try Scheme.validateEquality(of: lhs.context, and: rhs.context)
848+
try await Scheme.mulAssignAsync(&lhs, rhs)
849+
}
850+
851+
// MARK: Async ciphertext = -ciphertext
852+
853+
/// Async ciphertext negation.
854+
/// - Parameter ciphertext: Ciphertext to negate.
855+
/// - Returns: The negated ciphertext.
856+
@inlinable
857+
public static prefix func - (_ ciphertext: Ciphertext<Scheme, Format>) async -> Self {
858+
var result = ciphertext
859+
await Scheme.negAssignAsync(&result)
860+
return result
861+
}
862+
863+
// MARK: - Async ciphertext format conversion
864+
679865
/// Converts the ciphertext to coefficient format asynchronously.
680866
///
681867
/// This method performs an asynchronous conversion of the ciphertext to ``Coeff`` format.

Sources/HomomorphicEncryption/HeSchemeAsync.swift

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,4 +295,122 @@ extension HeScheme {
295295
}
296296
// swiftlint:enable force_cast
297297
}
298+
299+
/// In-place ciphertext-plaintext subtraction: `ciphertext -= plaintext`.
300+
///
301+
/// - Parameters:
302+
/// - ciphertext: Ciphertext to subtract from; will store the difference.
303+
/// - plaintext: Plaintext to subtract.
304+
/// - Throws: Error upon failure to subtract.
305+
/// - seealso: ``HeScheme/subAssign(_:_:)-5gs6p`` for a sync version.
306+
@inlinable
307+
public static func subAssignAsync<CiphertextFormat: PolyFormat, PlaintextFormat: PolyFormat>(
308+
_ ciphertext: inout Ciphertext<Self, CiphertextFormat>,
309+
_ plaintext: Plaintext<Self, PlaintextFormat>) async throws
310+
{
311+
// swiftlint:disable force_cast
312+
if CiphertextFormat.self == Coeff.self, PlaintextFormat.self == Coeff.self {
313+
var coeffCiphertext = ciphertext as! CoeffCiphertext
314+
try await subAssignCoeffAsync(&coeffCiphertext, plaintext as! CoeffPlaintext)
315+
ciphertext = coeffCiphertext as! Ciphertext<Self, CiphertextFormat>
316+
} else if CiphertextFormat.self == Eval.self, PlaintextFormat.self == Eval.self {
317+
var evalCiphertext = ciphertext as! EvalCiphertext
318+
try await subAssignEvalAsync(&evalCiphertext, plaintext as! EvalPlaintext)
319+
ciphertext = evalCiphertext as! Ciphertext<Self, CiphertextFormat>
320+
} else {
321+
throw HeError.unsupportedHeOperation(
322+
"""
323+
Subtraction between ciphertext in \(CiphertextFormat.description) \
324+
and plaintext in \(PlaintextFormat.description).
325+
""")
326+
}
327+
// swiftlint:enable force_cast
328+
}
329+
330+
/// Plaintext-ciphertext subtraction: `plaintext - ciphertext`.
331+
///
332+
/// - Parameters:
333+
/// - plaintext: Plaintext to subtract from.
334+
/// - ciphertext: Ciphertext to subtract.
335+
/// - Returns: A ciphertext encrypting the difference.
336+
/// - Throws: Error upon failure to subtract.
337+
/// - seealso: ``HeScheme/sub(_:_:)-4xfo4`` for a sync version.
338+
@inlinable
339+
public static func subAsync<CiphertextFormat: PolyFormat, PlaintextFormat: PolyFormat>(
340+
_ plaintext: Plaintext<Self, PlaintextFormat>,
341+
_ ciphertext: Ciphertext<Self, CiphertextFormat>) async throws -> Ciphertext<Self, CiphertextFormat>
342+
{
343+
// swiftlint:disable force_cast
344+
if CiphertextFormat.self == Coeff.self, PlaintextFormat.self == Coeff.self {
345+
let coeffCiphertext = ciphertext as! CoeffCiphertext
346+
let coeffPlaintext = plaintext as! CoeffPlaintext
347+
return try await subCoeffAsync(coeffPlaintext, coeffCiphertext) as! Ciphertext<Self, CiphertextFormat>
348+
}
349+
if CiphertextFormat.self == Eval.self, PlaintextFormat.self == Eval.self {
350+
let evalCiphertext = ciphertext as! EvalCiphertext
351+
let evalPlaintext = plaintext as! EvalPlaintext
352+
return try await subEvalAsync(evalPlaintext, evalCiphertext) as! Ciphertext<Self, CiphertextFormat>
353+
}
354+
throw HeError.unsupportedHeOperation("""
355+
Subtraction between plaintext in \(PlaintextFormat.description) and \
356+
ciphertext in \(CiphertextFormat.description).
357+
""")
358+
// swiftlint:enable force_cast
359+
}
360+
361+
/// In-place ciphertext subtraction: `lhs -= rhs`.
362+
///
363+
/// - Parameters:
364+
/// - lhs: Ciphertext to subtract from; will store the difference.
365+
/// - rhs: Ciphertext to subtract.
366+
/// - Throws: Error upon failure to subtract.
367+
/// - seealso: ``HeScheme/subAssign(_:_:)-75ktc`` for a sync version.
368+
@inlinable
369+
public static func subAssignAsync<LhsFormat: PolyFormat, RhsFormat: PolyFormat>(
370+
_ lhs: inout Ciphertext<Self, LhsFormat>,
371+
_ rhs: Ciphertext<Self, RhsFormat>) async throws
372+
{
373+
// swiftlint:disable force_cast
374+
if LhsFormat.self == Coeff.self {
375+
var lhsCoeffCiphertext = lhs as! CoeffCiphertext
376+
if RhsFormat.self == Coeff.self {
377+
try await subAssignCoeffAsync(&lhsCoeffCiphertext, rhs as! CoeffCiphertext)
378+
} else {
379+
fatalError("Unsupported Format \(RhsFormat.description)")
380+
}
381+
lhs = lhsCoeffCiphertext as! Ciphertext<Self, LhsFormat>
382+
} else if LhsFormat.self == Eval.self {
383+
var lhsEvalCiphertext = lhs as! EvalCiphertext
384+
if RhsFormat.self == Eval.self {
385+
try await subAssignEvalAsync(&lhsEvalCiphertext, rhs as! EvalCiphertext)
386+
} else {
387+
fatalError("Unsupported Format \(RhsFormat.description)")
388+
}
389+
lhs = lhsEvalCiphertext as! Ciphertext<Self, LhsFormat>
390+
} else {
391+
fatalError("Unsupported Format \(LhsFormat.description)")
392+
}
393+
// swiftlint:enable force_cast
394+
}
395+
396+
/// In-place ciphertext negation: `ciphertext = -ciphertext`.
397+
///
398+
/// - Parameter ciphertext: Ciphertext to negate.
399+
/// - seealso: ``HeScheme/negAssign(_:)`` for a sync version.
400+
@inlinable
401+
public static func negAssignAsync<Format: PolyFormat>(_ ciphertext: inout Ciphertext<Self, Format>) async {
402+
// swiftlint:disable force_cast
403+
if Format.self == Coeff.self {
404+
var coeffCiphertext = ciphertext as! CoeffCiphertext
405+
await negAssignCoeffAsync(&coeffCiphertext)
406+
ciphertext = coeffCiphertext as! Ciphertext<Self, Format>
407+
} else if Format.self == Eval.self {
408+
var evalCiphertext = ciphertext as! EvalCiphertext
409+
await negAssignEvalAsync(&evalCiphertext)
410+
ciphertext = evalCiphertext as! Ciphertext<Self, Format>
411+
} else {
412+
fatalError("Unsupported Format \(Format.description)")
413+
}
414+
// swiftlint:enable force_cast
415+
}
298416
}

0 commit comments

Comments
 (0)