Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add documentation for HomomorphicEncryption, with some minor API changes #22

Merged
merged 1 commit into from
Jul 19, 2024
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
17 changes: 0 additions & 17 deletions Sources/HomomorphicEncryption/.swiftlint.yml

This file was deleted.

4 changes: 4 additions & 0 deletions Sources/HomomorphicEncryption/Bfv/Bfv+Decrypt.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Foundation

extension Bfv {
@inlinable
// swiftlint:disable:next missing_docs attributes
public static func decrypt(_ ciphertext: EvalCiphertext,
using secretKey: SecretKey<Bfv<T>>) throws -> CoeffPlaintext
{
Expand All @@ -29,13 +30,15 @@ extension Bfv {
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func decrypt(_ ciphertext: CoeffCiphertext,
using secretKey: SecretKey<Bfv<T>>) throws -> CoeffPlaintext
{
try decrypt(ciphertext.forwardNtt(), using: secretKey)
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func noiseBudget(
of ciphertext: EvalCiphertext,
using secretKey: SecretKey<Bfv<T>>,
Expand Down Expand Up @@ -86,6 +89,7 @@ extension Bfv {
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func noiseBudget(of ciphertext: CoeffCiphertext,
using secretKey: SecretKey<Bfv<T>>, variableTime: Bool) throws -> Double
{
Expand Down
10 changes: 10 additions & 0 deletions Sources/HomomorphicEncryption/Bfv/Bfv+Encode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,38 @@
// limitations under the License.

extension Bfv {
@inlinable
// swiftlint:disable:next missing_docs attributes
public static func encode(context: Context<Bfv<T>>, values: [some ScalarType],
format: EncodeFormat) throws -> CoeffPlaintext
{
try context.encode(values: values, format: format)
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func encode(context: Context<Bfv<T>>, values: [some ScalarType], format: EncodeFormat,
moduliCount: Int) throws -> EvalPlaintext
{
try context.encode(values: values, format: format, moduliCount: moduliCount)
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func encode(context: Context<Bfv<T>>, values: [some ScalarType],
format: EncodeFormat) throws -> EvalPlaintext
{
try context.encode(values: values, format: format, moduliCount: nil)
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func decode<V>(plaintext: CoeffPlaintext, format: EncodeFormat) throws -> [V] where V: ScalarType {
try plaintext.context.decode(plaintext: plaintext, format: format)
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func decode<V>(plaintext: EvalPlaintext, format: EncodeFormat) throws -> [V] where V: ScalarType {
try plaintext.context.decode(plaintext: plaintext, format: format)
}
Expand Down
5 changes: 5 additions & 0 deletions Sources/HomomorphicEncryption/Bfv/Bfv+Encrypt.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ extension Bfv {
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func zeroCiphertext(context: Context<Self>, moduliCount: Int) throws -> CoeffCiphertext {
let zeroPoly = try PolyRq<Scalar, Coeff>.zero(
context: context.ciphertextContext
Expand All @@ -29,6 +30,7 @@ extension Bfv {
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func zeroCiphertext(context: Context<Self>, moduliCount: Int) throws -> EvalCiphertext {
let zeroPoly = try PolyRq<Scalar, Eval>.zero(
context: context.ciphertextContext
Expand All @@ -38,6 +40,7 @@ extension Bfv {
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func isTransparent(ciphertext: CoeffCiphertext) -> Bool {
// Decryption multiplies all the polynomials except the first with powers of the secret key.
// So the ciphertext is transparent if all polynomials except the first are zeros.
Expand All @@ -47,13 +50,15 @@ extension Bfv {
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func isTransparent(ciphertext: EvalCiphertext) -> Bool {
// Decryption multiplies all the polynomials except the first with powers of the secret key.
// So the ciphertext is transparent if all polynomials except the first are zeros.
ciphertext.polys[1...].allSatisfy { poly in poly.isZero(variableTime: true) }
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func encrypt(_ plaintext: Plaintext<Self, Coeff>,
using secretKey: SecretKey<Bfv<T>>) throws -> CanonicalCiphertext
{
Expand Down
5 changes: 3 additions & 2 deletions Sources/HomomorphicEncryption/Bfv/Bfv+Keys.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
// limitations under the License.

extension Bfv {
// keygen APIs
@inlinable
// swiftlint:disable:next missing_docs attributes
public static func generateSecretKey(context: Context<Self>) throws -> SecretKey<Bfv<T>> {
var s = PolyRq<Scalar, Coeff>.zero(context: context.secretKeyContext)
var rng = SystemRandomNumberGenerator()
Expand All @@ -24,6 +24,7 @@ extension Bfv {
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func generateEvaluationKey(
context: Context<Bfv<T>>,
configuration: EvaluationKeyConfiguration,
Expand All @@ -34,7 +35,7 @@ extension Bfv {
}
var galoisKeys: [Int: KeySwitchKey<Self>] = [:]
for element in configuration.galoisElements {
let switchedKey = try secretKey.poly.applyGalois(galoisElement: element)
let switchedKey = try secretKey.poly.applyGalois(element: element)
galoisKeys[element] = try generateKeySwitchKey(
context: context,
currentKey: switchedKey,
Expand Down
1 change: 1 addition & 0 deletions Sources/HomomorphicEncryption/Bfv/Bfv+Multiply.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

extension Bfv {
@inlinable
// swiftlint:disable:next missing_docs attributes
public static func mulAssign(_ lhs: inout CanonicalCiphertext, _ rhs: CanonicalCiphertext) throws {
let evalCiphertext = try multiplyWithoutScaling(lhs, rhs)
lhs = try dropExtendedBase(from: evalCiphertext)
Expand Down
4 changes: 2 additions & 2 deletions Sources/HomomorphicEncryption/Bfv/Bfv.swift
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ public enum Bfv<T: ScalarType>: HeScheme {
guard let keySwitchingKey = galoisKey.keys[element] else {
throw HeError.missingGaloisElement(element: element)
}
ciphertext.polys[0] = ciphertext.polys[0].applyGalois(galoisElement: element)
let tempC1 = ciphertext.polys[1].applyGalois(galoisElement: element)
ciphertext.polys[0] = ciphertext.polys[0].applyGalois(element: element)
let tempC1 = ciphertext.polys[1].applyGalois(element: element)
let update = try Self.computeKeySwitchingUpdate(
context: ciphertext.context,
target: tempC1,
Expand Down
22 changes: 21 additions & 1 deletion Sources/HomomorphicEncryption/CoefficientPacking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ extension CoefficientPacking {
return (bitsPerByte * byteCount).dividingCeil(serializedBitsPerCoeff, variableTime: true)
}

/// Converts a sequence of bytes to a vector of fixed bit-width coefficients.
/// - Parameters:
/// - bytes: Serialized coefficients.
/// - bitsPerCoeff: Number of bits in each coefficient.
/// - decode: If `true`, then it is assumed that `bytes` is a a sequence of serialized coefficients and we are
/// transforming these back to coefficients. In that case, the coefficient bit-size and byte bit-size might not
/// match up, which would mean that the last byte will only contain partial information, and we do not need an extra
/// coefficient to store the remaining bits of the last byte.
/// - skipLSBs: How many least-significant bits from each coefficient are assumed to be 0, and not present in
/// `bytes`.
/// - Returns: The deserialized coefficients.
/// - seealso: ``CoefficientPacking/coefficientsToBytes(coeffs:bitsPerCoeff:skipLSBs:)``
@inlinable
public static func bytesToCoefficients<T: ScalarType>(bytes: [UInt8], bitsPerCoeff: Int, decode: Bool,
skipLSBs: Int = 0) -> [T]
Expand All @@ -43,7 +55,7 @@ extension CoefficientPacking {
return coeffs
}

/// Convert an sequence of bytes into coefficients, unused bits in the last coefficient will be set to zero.
/// Converts an sequence of bytes into coefficients, unused bits in the last coefficient will be set to zero.
@inlinable
static func bytesToCoefficientsInplace<T, C>(
bytes: some Sequence<UInt8>,
Expand Down Expand Up @@ -97,6 +109,14 @@ extension CoefficientPacking {
return (coeffCount * serializedBitsPerCoeff).dividingCeil(UInt8.bitWidth, variableTime: true)
}

/// Converts a sequence of fixed bit-width coefficients to bytes.
/// - Parameters:
/// - coeffs: Coefficients
/// - bitsPerCoeff: Number of bits in each coefficient.
/// - skipLSBs: How many least-significant bits from each coefficient to omit from serialization.
/// - Returns: The serialized coefficients.
/// - Throws: Error upon failure to convert the coefficients.
/// - seealso ``CoefficientPacking/bytesToCoefficients(bytes:bitsPerCoeff:decode:skipLSBs:)``.
@inlinable
public static func coefficientsToBytes(coeffs: [some ScalarType], bitsPerCoeff: Int,
skipLSBs: Int = 0) throws -> [UInt8]
Expand Down
9 changes: 9 additions & 0 deletions Sources/HomomorphicEncryption/HeScheme.swift
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ public protocol HeScheme {
/// - format: Encoding format of the plaintext.
/// - Returns: The decoded values.
/// - Throws: Error upon failure to decode the plaintext.
/// - seealso: ``Plaintext/decode(format:)-9l5kz`` for an alternative API.
static func decode<T: ScalarType>(plaintext: CoeffPlaintext, format: EncodeFormat) throws -> [T]

/// Decodes a plaintext in ``Eval`` format.
Expand All @@ -181,6 +182,7 @@ public protocol HeScheme {
/// - format: Encoding format of the plaintext.
/// - Returns: The decoded values.
/// - Throws: Error upon failure to decode the plaintext.
/// - seealso: ``Plaintext/decode(format:)-i9hh`` for an alternative API.
static func decode<T: ScalarType>(plaintext: EvalPlaintext, format: EncodeFormat) throws -> [T]

/// Symmetric secret key encryption of a plaintext.
Expand All @@ -189,6 +191,7 @@ public protocol HeScheme {
/// - secretKey: Secret key to encrypt with.
/// - Returns: A ciphertext encrypting `plaintext`.
/// - Throws: Error upon failure to encrypt the plaintext.
/// - seealso: ``Plaintext/encrypt(using:)`` for an alternative API.
static func encrypt(_ plaintext: CoeffPlaintext, using secretKey: SecretKey) throws -> CanonicalCiphertext

/// Generates a ciphertext of zeros in ``Coeff`` format.
Expand Down Expand Up @@ -648,6 +651,7 @@ public protocol HeScheme {

extension HeScheme {
@inlinable
// swiftlint:disable:next missing_docs attributes
public static func validateEquality(of lhs: Context<Self>, and rhs: Context<Self>) throws {
guard lhs == rhs else {
throw HeError.unequalContexts(got: lhs, expected: rhs)
Expand All @@ -657,6 +661,7 @@ extension HeScheme {

extension HeScheme {
@inlinable
// swiftlint:disable:next missing_docs attributes
public static func rotateColumns(
of ciphertext: inout CanonicalCiphertext,
by step: Int,
Expand All @@ -667,6 +672,7 @@ extension HeScheme {
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func swapRows(of ciphertext: inout CanonicalCiphertext, using evaluationKey: EvaluationKey) throws {
let element = GaloisElement.swappingRows(degree: ciphertext.context.degree)
try applyGalois(ciphertext: &ciphertext, element: element, using: evaluationKey)
Expand All @@ -675,6 +681,7 @@ extension HeScheme {

extension HeScheme {
@inlinable
// swiftlint:disable:next missing_docs attributes
public static func innerProduct(_ lhs: some Collection<CanonicalCiphertext>,
_ rhs: some Collection<CanonicalCiphertext>) throws -> CanonicalCiphertext
{
Expand All @@ -685,6 +692,7 @@ extension HeScheme {
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func innerProduct(ciphertexts: some Collection<EvalCiphertext>,
plaintexts: some Collection<EvalPlaintext?>) throws -> EvalCiphertext
{
Expand All @@ -702,6 +710,7 @@ extension HeScheme {
}

@inlinable
// swiftlint:disable:next missing_docs attributes
public static func innerProduct(ciphertexts: some Collection<EvalCiphertext>,
plaintexts: some Collection<EvalPlaintext>) throws -> EvalCiphertext
{
Expand Down
6 changes: 3 additions & 3 deletions Sources/HomomorphicEncryption/NoOpScheme.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,15 @@ public enum NoOpScheme: HeScheme {
using _: EvaluationKey<NoOpScheme>) throws
{
let element = try GaloisElement.rotatingColumns(by: step, degree: ciphertext.context.degree)
ciphertext.polys[0] = ciphertext.polys[0].applyGalois(galoisElement: element)
ciphertext.polys[0] = ciphertext.polys[0].applyGalois(element: element)
}

public static func swapRows(
of ciphertext: inout CanonicalCiphertext,
using _: EvaluationKey<NoOpScheme>) throws
{
let element = GaloisElement.swappingRows(degree: ciphertext.context.degree)
ciphertext.polys[0] = ciphertext.polys[0].applyGalois(galoisElement: element)
ciphertext.polys[0] = ciphertext.polys[0].applyGalois(element: element)
}

// MARK: plaintext += plaintext
Expand Down Expand Up @@ -270,7 +270,7 @@ public enum NoOpScheme: HeScheme {
using _: EvaluationKey<Self>) throws
{
for index in ciphertext.polys.indices {
ciphertext.polys[index] = ciphertext.polys[index].applyGalois(galoisElement: element)
ciphertext.polys[index] = ciphertext.polys[index].applyGalois(element: element)
}
}

Expand Down
24 changes: 24 additions & 0 deletions Sources/HomomorphicEncryption/Plaintext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ extension Plaintext {
return result
}

/// Converts the plaintext to ``Eval`` format.
///
/// This makes the plaintext suitable for operations with ciphertexts in ``Eval`` format, with `moduliCount` moduli.
/// - Parameter moduliCount: Number of coefficient moduli in the context.
/// - Returns: The convertext plaintext.
/// - throws: Error upon failure to convert the plaintext.
@inlinable
public func convertToEvalFormat(moduliCount: Int? = nil) throws -> Plaintext<Scheme, Eval>
where Format == Coeff
Expand All @@ -102,6 +108,9 @@ extension Plaintext {
return try Plaintext<Scheme, Eval>(context: context, poly: poly.forwardNtt())
}

/// Converts the plaintext to ``Coeff`` format.
/// - Returns: The converted plaintext.
/// - throws: Error upon failure to convert the plaintext.
@inlinable
public func convertToCoeffFormat() throws -> Plaintext<Scheme, Coeff>
where Format == Eval
Expand All @@ -122,16 +131,31 @@ extension Plaintext {
return Plaintext<Scheme, Coeff>(context: context, poly: coeffPoly)
}

/// Decodes a plaintext in ``Coeff`` format.
/// - Parameter format: Encoding format of the plaintext.
/// - Returns: The decoded values.
/// - Throws: Error upon failure to decode the plaintext.
/// - seealso: ``HeScheme/decode(plaintext:format:)-h6vl`` for an alternative API.
@inlinable
public func decode(format: EncodeFormat) throws -> [Scheme.Scalar] where Format == Coeff {
try context.decode(plaintext: self, format: format)
}

/// Decodes a plaintext in ``Eval`` format.
/// - Parameter format: Encoding format of the plaintext.
/// - Returns: The decoded values.
/// - Throws: Error upon failure to decode the plaintext.
/// - seealso: ``HeScheme/decode(plaintext:format:)-663x4`` for an alternative API.
@inlinable
public func decode(format: EncodeFormat) throws -> [Scheme.Scalar] where Format == Eval {
try context.decode(plaintext: self, format: format)
}

/// Symmetric secret key encryption of the plaintext.
/// - Parameter secretKey: Secret key to encrypt with.
/// - Returns: A ciphertext encrypting the plaintext.
/// - Throws: Error upon failure to encrypt the plaintext.
/// - seealso: ``HeScheme/encrypt(_:using:)`` for an alternative API.
@inlinable
public func encrypt(using secretKey: SecretKey<Scheme>) throws -> Scheme.CanonicalCiphertext where Format == Coeff {
try Scheme.encrypt(self, using: secretKey)
Expand Down
Loading