open import Function
open import Type using ()
open import Data.Product
open import Relation.Binary.PropositionalEquality.NP

module Cipher.ElGamal.Homomorphic
  (ℤq  : )
  (G   : )
  (g   : G)
  (_⊞_ : ℤq  ℤq  ℤq)
  (_^_ : G  ℤq  G)
  (_♦_ : G  G  G)
  (_/_ : G  G  G)
  where

open import Cipher.ElGamal.Generic G ℤq G g _^_ _♦_ _/_

Combine : CipherText  CipherText  CipherText
Combine (a , b) (c , d) = (a  c , b  d)

Reenc : PubKey  CipherText  (rₑ : ℤq)  CipherText
Reenc pk ( , ζ) rₑ = (g ^ rₑ)   , (pk ^ rₑ)  ζ

module HomomorphicCorrectness
    (/-♦    :  {x y}  (x  y) / x  y)
    (comm-^ :  {α x y}  (α ^ x)^ y  (α ^ y)^ x)
    (interchange-♦ :  {a b c d}  (a  b)  (c  d)  ((a  c)  (b  d)))
    (^-⊞ :  {α x y}  (α ^ x)  (α ^ y)  α ^ (x  y))
    where

  open FunctionalCorrectness /-♦ comm-^

  homomorphic-correctness :  sk m₀ m₁ r₀ r₁ 
    let pk = PubKeyGen sk in
    Dec sk (Combine (Enc pk m₀ r₀) (Enc pk m₁ r₁))  m₀  m₁
  homomorphic-correctness rₖ m₀ m₁ r₀ r₁ =
    (ap (flip _/_ D) p  ap (_/_ N  flip _^_ rₖ) ^-⊞)  functional-correctness rₖ (r₀  r₁) (m₀  m₁)
    where D = ((g ^ r₀)  (g ^ r₁))^ rₖ
          N = ((g ^ rₖ) ^ (r₀  r₁))  (m₀  m₁)
          p = interchange-♦  ap (flip _♦_ (m₀  m₁)) ^-⊞

module ReencCorrectness
    (/-♦    :  {x y}  (x  y) / x  y)
    (comm-^ :  {α x y}  (α ^ x)^ y  (α ^ y)^ x)
    (interchange-♦ :  {a b c d}  (a  b)  (c  d)  ((a  c)  (b  d)))
    (^-⊞ :  {α x y}  (α ^ x)  (α ^ y)  α ^ (x  y))
    (g⁰ : G)
    (g⁰-idl :  x  g⁰  x  x)
    (g⁰-idr :  x  x  g⁰  x)
    where
  open HomomorphicCorrectness /-♦ comm-^ interchange-♦ ^-⊞

  Reenc-Combine-g⁰ :  pk c r  Reenc pk c r  Combine (Enc pk g⁰ r) c
  Reenc-Combine-g⁰ pk c r = cong₂ _,_ refl (ap (flip _♦_ (proj₂ c)) (! (g⁰-idr _)))

  Reenc-correct :  sk m r₀ r₁ 
                  let pk = PubKeyGen sk in
                  Dec sk (Reenc pk (Enc pk m r₁) r₀)  m
  Reenc-correct rₖ m r₀ r₁ = ap  z  (z  (((g ^ rₖ) ^ r₁)  m)) / D)
                                (! (g⁰-idr _))
                            homomorphic-correctness rₖ g⁰ m r₀ r₁
                            g⁰-idl _
    where D = ((g ^ r₀)  (g ^ r₁))^ rₖ