1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use std::ops::{Add, Mul, Sub};

use crate::cryptosystems::{Associable, AssociatedCiphertext, EncryptionKey};

auto trait PotentialInput {}

impl<'pk, C, PK> !PotentialInput for AssociatedCiphertext<'pk, C, PK> {}

/// Trait implemented by additively homomorphic cryptosystems
pub trait HomomorphicAddition: EncryptionKey {
    /// Combines two ciphertexts so that their decrypted value reflects some addition operation
    fn add(
        &self,
        ciphertext_a: &Self::Ciphertext,
        ciphertext_b: &Self::Ciphertext,
    ) -> Self::Ciphertext;

    /// Combines two ciphertexts so that their decrypted value reflects some subtract operation
    fn sub(
        &self,
        ciphertext_a: &Self::Ciphertext,
        ciphertext_b: &Self::Ciphertext,
    ) -> Self::Ciphertext;

    /// Applies some operation on a ciphertext so that the decrypted value reflects some multiplication with `input`
    fn mul_constant(&self, ciphertext: &Self::Ciphertext, input: &Self::Input) -> Self::Ciphertext;

    /// Combines two ciphertexts so that their decrypted value reflects some addition operation with a constant
    fn add_constant(
        &self,
        ciphertext: &Self::Ciphertext,
        constant: &Self::Plaintext,
    ) -> Self::Ciphertext;

    /// Combines two ciphertexts so that their decrypted value reflects some subtract operation with a constant
    fn sub_constant(
        &self,
        ciphertext: &Self::Ciphertext,
        constant: &Self::Plaintext,
    ) -> Self::Ciphertext;
}

impl<'pk, C: Associable<PK>, PK: EncryptionKey<Ciphertext = C> + HomomorphicAddition> Add
    for &AssociatedCiphertext<'pk, C, PK>
{
    type Output = AssociatedCiphertext<'pk, C, PK>;

    fn add(self, rhs: Self) -> Self::Output {
        debug_assert_eq!(self.public_key, rhs.public_key);
        self.public_key
            .add(&self.ciphertext, &rhs.ciphertext)
            .associate(self.public_key)
    }
}

impl<
        'pk,
        P: PotentialInput,
        C: Associable<PK>,
        PK: EncryptionKey<Ciphertext = C, Plaintext = P> + HomomorphicAddition,
    > Add<&P> for &AssociatedCiphertext<'pk, C, PK>
{
    type Output = AssociatedCiphertext<'pk, C, PK>;

    fn add(self, rhs: &PK::Plaintext) -> Self::Output {
        self.public_key
            .add_constant(&self.ciphertext, rhs)
            .associate(self.public_key)
    }
}

impl<'pk, C: Associable<PK>, PK: EncryptionKey<Ciphertext = C> + HomomorphicAddition> Sub
    for &AssociatedCiphertext<'pk, C, PK>
{
    type Output = AssociatedCiphertext<'pk, C, PK>;

    fn sub(self, rhs: Self) -> Self::Output {
        debug_assert_eq!(self.public_key, rhs.public_key);
        self.public_key
            .sub(&self.ciphertext, &rhs.ciphertext)
            .associate(self.public_key)
    }
}

impl<
        'pk,
        P: PotentialInput,
        C: Associable<PK>,
        PK: EncryptionKey<Ciphertext = C, Plaintext = P> + HomomorphicAddition,
    > Sub<&P> for &AssociatedCiphertext<'pk, C, PK>
{
    type Output = AssociatedCiphertext<'pk, C, PK>;

    fn sub(self, rhs: &PK::Plaintext) -> Self::Output {
        self.public_key
            .sub_constant(&self.ciphertext, rhs)
            .associate(self.public_key)
    }
}

impl<
        'pk,
        P: PotentialInput,
        C: Associable<PK>,
        PK: EncryptionKey<Input = P, Ciphertext = C> + HomomorphicAddition,
    > Mul<&P> for &AssociatedCiphertext<'pk, C, PK>
{
    type Output = AssociatedCiphertext<'pk, C, PK>;

    fn mul(self, rhs: &PK::Input) -> Self::Output {
        self.public_key
            .mul_constant(&self.ciphertext, rhs)
            .associate(self.public_key)
    }
}

/// Trait implemented by multiplicatively homomorphic cryptosystems
pub trait HomomorphicMultiplication: EncryptionKey {
    /// Combines two ciphertexts so that their decrypted value reflects some multiplication operation
    fn mul(
        &self,
        ciphertext_a: &Self::Ciphertext,
        ciphertext_b: &Self::Ciphertext,
    ) -> Self::Ciphertext;

    /// Applies some operation on a ciphertext so that the decrypted value reflects some exponentiation with `input`
    fn pow(&self, ciphertext: &Self::Ciphertext, input: &Self::Input) -> Self::Ciphertext;
}

impl<'pk, C: Associable<PK>, PK: EncryptionKey<Ciphertext = C> + HomomorphicMultiplication> Mul
    for &AssociatedCiphertext<'pk, C, PK>
{
    type Output = AssociatedCiphertext<'pk, C, PK>;

    fn mul(self, rhs: Self) -> Self::Output {
        debug_assert_eq!(self.public_key, rhs.public_key);
        self.public_key
            .mul(&self.ciphertext, &rhs.ciphertext)
            .associate(self.public_key)
    }
}

impl<'pk, C: Associable<PK>, PK: EncryptionKey<Ciphertext = C> + HomomorphicMultiplication>
    AssociatedCiphertext<'pk, C, PK>
{
    /// Applies some operation on this ciphertext so that the decrypted value reflects some exponentiation with `input`
    pub fn pow(&self, rhs: &PK::Input) -> AssociatedCiphertext<'pk, C, PK> {
        self.public_key
            .pow(&self.ciphertext, rhs)
            .associate(self.public_key)
    }
}