Skip to content

Commit

Permalink
Implement plaintext - ciphertext (#140)
Browse files Browse the repository at this point in the history
  • Loading branch information
fboemer authored Jan 7, 2025
1 parent 62a9806 commit fa9f5e2
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 13 deletions.
17 changes: 16 additions & 1 deletion Benchmarks/RlweBenchmark/RlweBenchmark.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Apple Inc. and the Swift Homomorphic Encryption project authors
// Copyright 2024-2025 Apple Inc. and the Swift Homomorphic Encryption project authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -329,6 +329,18 @@ func ciphertextPlaintextSubtractBenchmark<Scheme: HeScheme>(_: Scheme.Type) -> (
}
}

func plaintextCiphertextSubtractBenchmark<Scheme: HeScheme>(_: Scheme.Type) -> () -> Void {
{
benchmark("PlaintextCiphertextSubtract", Scheme.self) { benchmark in
let benchmarkContext: RlweBenchmarkContext<Scheme> = try StaticRlweBenchmarkContext.getBenchmarkContext()
benchmark.startMeasurement()
for _ in benchmark.scaledIterations {
try blackHole(benchmarkContext.coeffPlaintext - benchmarkContext.ciphertext)
}
}
}
}

func ciphertextPlaintextMultiplyBenchmark<Scheme: HeScheme>(_: Scheme.Type) -> () -> Void {
{
benchmark("CiphertextPlaintextMultiply", Scheme.self) { benchmark in
Expand Down Expand Up @@ -595,6 +607,9 @@ nonisolated(unsafe) let benchmarks: () -> Void = {
ciphertextPlaintextSubtractBenchmark(Bfv<UInt32>.self)()
ciphertextPlaintextSubtractBenchmark(Bfv<UInt64>.self)()

plaintextCiphertextSubtractBenchmark(Bfv<UInt32>.self)()
plaintextCiphertextSubtractBenchmark(Bfv<UInt64>.self)()

ciphertextPlaintextMultiplyBenchmark(Bfv<UInt32>.self)()
ciphertextPlaintextMultiplyBenchmark(Bfv<UInt64>.self)()

Expand Down
13 changes: 12 additions & 1 deletion Sources/HomomorphicEncryption/Ciphertext.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Apple Inc. and the Swift Homomorphic Encryption project authors
// Copyright 2024-2025 Apple Inc. and the Swift Homomorphic Encryption project authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -104,6 +104,17 @@ public struct Ciphertext<Scheme: HeScheme, Format: PolyFormat>: Equatable, Senda
try Scheme.subAssign(&ciphertext, plaintext)
}

// MARK: plaintext - ciphertext

@inlinable
public static func - (
plaintext: Plaintext<Scheme, some PolyFormat>,
ciphertext: Ciphertext<Scheme, Format>) throws -> Ciphertext<Scheme, Format>
{
try Scheme.validateEquality(of: ciphertext.context, and: plaintext.context)
return try Scheme.sub(plaintext, ciphertext)
}

// MARK: ciphertext *= plaintext

@inlinable
Expand Down
82 changes: 81 additions & 1 deletion Sources/HomomorphicEncryption/HeScheme.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Apple Inc. and the Swift Homomorphic Encryption project authors
// Copyright 2024-2025 Apple Inc. and the Swift Homomorphic Encryption project authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -472,6 +472,24 @@ public protocol HeScheme {
/// - Throws: Error upon failure to subtract.
static func subAssignEval(_ ciphertext: inout EvalCiphertext, _ plaintext: EvalPlaintext) throws

/// Plaintext-ciphertext subtraction: `plaintext - ciphertext`.
///
/// - Parameters:
/// - plaintext: Plaintext to subtract from.
/// - ciphertext: Ciphertext to subtract.
/// - Returns: A ciphertext encrypting the difference.
/// - Throws: Error upon failure to subtract.
static func subCoeff(_ plaintext: CoeffPlaintext, _ ciphertext: CoeffCiphertext) throws -> CoeffCiphertext

/// Plaintext-ciphertext subtraction: `plaintext - ciphertext`.
///
/// - Parameters:
/// - plaintext: Plaintext to subtract from.
/// - ciphertext: Ciphertext to subtract.
/// - Returns: A ciphertext encrypting the difference.
/// - Throws: Error upon failure to subtract.
static func subEval(_ plaintext: EvalPlaintext, _ ciphertext: EvalCiphertext) throws -> EvalCiphertext

/// In-place ciphertext-plaintext multiplication: `ciphertext *= plaintext`.
///
/// - Parameters:
Expand Down Expand Up @@ -573,6 +591,24 @@ public protocol HeScheme {
/// - Throws: Error upon failure to subtract.
static func subAssign(_ ciphertext: inout CanonicalCiphertext, _ plaintext: EvalPlaintext) throws

/// Plaintext-ciphertext subtraction: `plaintext - ciphertext`.
///
/// - Parameters:
/// - plaintext: Plaintext to subtract from.
/// - ciphertext: Ciphertext to subtract.
/// - Returns: A ciphertext encrypting the difference.
/// - Throws: Error upon failure to subtract.
static func sub(_ plaintext: CoeffPlaintext, _ ciphertext: CanonicalCiphertext) throws -> CanonicalCiphertext

/// Plaintext-ciphertext subtraction: `plaintext - ciphertext`.
///
/// - Parameters:
/// - plaintext: Plaintext to subtract from.
/// - ciphertext: Ciphertext to subtract.
/// - Returns: A ciphertext encrypting the difference.
/// - Throws: Error upon failure to subtract.
static func sub(_ plaintext: EvalPlaintext, _ ciphertext: CanonicalCiphertext) throws -> CanonicalCiphertext

/// Performs modulus switching on the ciphertext.
///
/// Modulus switching drops the last coefficient modulus in the ciphertext's current ciphertext modulus, without
Expand Down Expand Up @@ -788,6 +824,36 @@ extension HeScheme {
// swiftlint:enable force_cast
}

/// Plaintext-ciphertext subtraction: `plaintext - ciphertext`.
///
/// - Parameters:
/// - plaintext: Plaintext to subtract from.
/// - ciphertext: Ciphertext to subtract.
/// - Returns: A ciphertext encrypting the difference.
/// - Throws: Error upon failure to subtract.
@inlinable
public static func sub<CiphertextFormat: PolyFormat, PlaintextFormat: PolyFormat>(
_ plaintext: Plaintext<Self, PlaintextFormat>,
_ ciphertext: Ciphertext<Self, CiphertextFormat>) throws -> Ciphertext<Self, CiphertextFormat>
{
// swiftlint:disable force_cast
if CiphertextFormat.self == Coeff.self, PlaintextFormat.self == Coeff.self {
let coeffCiphertext = ciphertext as! CoeffCiphertext
let coeffPlaintext = plaintext as! CoeffPlaintext
return try subCoeff(coeffPlaintext, coeffCiphertext) as! Ciphertext<Self, CiphertextFormat>
}
if CiphertextFormat.self == Eval.self, PlaintextFormat.self == Eval.self {
let evalCiphertext = ciphertext as! EvalCiphertext
let evalPlaintext = plaintext as! EvalPlaintext
return try subEval(evalPlaintext, evalCiphertext) as! Ciphertext<Self, CiphertextFormat>
}
throw HeError.unsupportedHeOperation("""
Subtraction between plaintext in \(PlaintextFormat.description) and \
ciphertext in \(CiphertextFormat.description).
""")
// swiftlint:enable force_cast
}

/// In-place ciphertext subtraction: `lhs -= rhs`.
///
/// - Parameters:
Expand Down Expand Up @@ -1095,6 +1161,20 @@ extension HeScheme {
}
}

extension HeScheme {
@inlinable
// swiftlint:disable:next missing_docs attributes
public static func subCoeff(_ plaintext: CoeffPlaintext, _ ciphertext: CoeffCiphertext) throws -> CoeffCiphertext {
try plaintext + -ciphertext
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func subEval(_ plaintext: EvalPlaintext, _ ciphertext: EvalCiphertext) throws -> EvalCiphertext {
try plaintext + -ciphertext
}
}

// MARK: forwarding to Context

extension Context {
Expand Down
48 changes: 38 additions & 10 deletions Tests/HomomorphicEncryptionTests/HeAPITests.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2024 Apple Inc. and the Swift Homomorphic Encryption project authors
// Copyright 2024-2025 Apple Inc. and the Swift Homomorphic Encryption project authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -766,7 +766,8 @@ class HeAPITests: XCTestCase {
let testEnv = try TestEnv(context: context, format: .simd)
let data1 = testEnv.data1
let data2 = testEnv.data2
let diffData = zip(data1, data2).map { x, y in x.subtractMod(y, modulus: context.plaintextModulus) }
let diff1Minus2Data = zip(data1, data2).map { x, y in x.subtractMod(y, modulus: context.plaintextModulus) }
let diff2Minus1Data = zip(data2, data1).map { x, y in x.subtractMod(y, modulus: context.plaintextModulus) }
let canonicalCiphertext = testEnv.ciphertext1
let evalCiphertext: Ciphertext<Scheme, Eval> = try canonicalCiphertext.convertToEvalFormat()
let coeffCiphertext: Ciphertext<Scheme, Coeff> = try evalCiphertext.inverseNtt()
Expand All @@ -779,28 +780,42 @@ class HeAPITests: XCTestCase {
try testEnv.checkDecryptsDecodes(
ciphertext: canonicalCiphertext - coeffPlaintext,
format: .simd,
expected: diffData)
expected: diff1Minus2Data)

// canonicalCiphertext -= coeffPlaintext
do {
var diff = canonicalCiphertext
try diff -= coeffPlaintext
try testEnv.checkDecryptsDecodes(ciphertext: diff, format: .simd, expected: diffData)
try testEnv.checkDecryptsDecodes(ciphertext: diff, format: .simd, expected: diff1Minus2Data)
}

// canonicalCiphertext - evalPlaintext
do {
try testEnv.checkDecryptsDecodes(
ciphertext: canonicalCiphertext - evalPlaintext,
format: .simd,
expected: diffData)
expected: diff1Minus2Data)
} catch HeError.unsupportedHeOperation(_) {}

// canonicalCiphertext -= evalPlaintext
do {
var diff = canonicalCiphertext
try diff -= evalPlaintext
try testEnv.checkDecryptsDecodes(ciphertext: diff, format: .simd, expected: diffData)
try testEnv.checkDecryptsDecodes(ciphertext: diff, format: .simd, expected: diff1Minus2Data)
} catch HeError.unsupportedHeOperation(_) {}

// coeffPlaintext - canonicalCiphertext
try testEnv.checkDecryptsDecodes(
ciphertext: coeffPlaintext - canonicalCiphertext,
format: .simd,
expected: diff2Minus1Data)

// evalPlaintext - canonicalCiphertext
do {
try testEnv.checkDecryptsDecodes(
ciphertext: evalPlaintext - canonicalCiphertext,
format: .simd,
expected: diff2Minus1Data)
} catch HeError.unsupportedHeOperation(_) {}
}

Expand All @@ -810,14 +825,19 @@ class HeAPITests: XCTestCase {
try testEnv.checkDecryptsDecodes(
ciphertext: coeffCiphertext - coeffPlaintext,
format: .simd,
expected: diffData)
expected: diff1Minus2Data)

// coeffCiphertext -= coeffPlaintext
do {
var diff = coeffCiphertext
try diff -= coeffPlaintext
try testEnv.checkDecryptsDecodes(ciphertext: diff, format: .simd, expected: diffData)
try testEnv.checkDecryptsDecodes(ciphertext: diff, format: .simd, expected: diff1Minus2Data)
}
// coeffPlaintext - coeffCiphertext
try testEnv.checkDecryptsDecodes(
ciphertext: coeffPlaintext - coeffCiphertext,
format: .simd,
expected: diff2Minus1Data)
}

// evalCiphertext
Expand All @@ -827,14 +847,22 @@ class HeAPITests: XCTestCase {
try testEnv.checkDecryptsDecodes(
ciphertext: evalCiphertext - evalPlaintext,
format: .simd,
expected: diffData)
expected: diff1Minus2Data)
} catch HeError.unsupportedHeOperation(_) {}

// evalCiphertext -= evalPlaintext
do {
var diff = evalCiphertext
try diff -= evalPlaintext
try testEnv.checkDecryptsDecodes(ciphertext: diff, format: .simd, expected: diffData)
try testEnv.checkDecryptsDecodes(ciphertext: diff, format: .simd, expected: diff1Minus2Data)
} catch HeError.unsupportedHeOperation(_) {}

// evalPlaintext - evalCiphertext
do {
try testEnv.checkDecryptsDecodes(
ciphertext: evalPlaintext - evalCiphertext,
format: .simd,
expected: diff2Minus1Data)
} catch HeError.unsupportedHeOperation(_) {}
}
}
Expand Down

0 comments on commit fa9f5e2

Please sign in to comment.