<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://rakshikab.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://rakshikab.github.io/" rel="alternate" type="text/html" /><updated>2026-07-17T18:53:19+00:00</updated><id>https://rakshikab.github.io/feed.xml</id><title type="html">Rakshika Bagavathy</title><subtitle></subtitle><author><name>Rakshika Bagavathy</name></author><entry><title type="html">Formalizing Diffie-Hellman key exchange and Discrete Logarithms in Agda</title><link href="https://rakshikab.github.io/diffiehellman" rel="alternate" type="text/html" title="Formalizing Diffie-Hellman key exchange and Discrete Logarithms in Agda" /><published>2026-07-04T00:00:00+00:00</published><updated>2026-07-04T00:00:00+00:00</updated><id>https://rakshikab.github.io/diffiehellman</id><content type="html" xml:base="https://rakshikab.github.io/diffiehellman"><![CDATA[<p>Martin Hellman <a href="https://podscripts.co/podcasts/the-peterman-pod/turing-award-winner-nsa-public-key-cryptography-crypto-wars-martin-hellman">recently recounted</a> that when he set out to solve cryptography’s biggest logistical nightmare, he was told it was impossible; when he solved it, he was told it was treason. He also spoke about his experiences advancing the field of public-key cryptography and the backlash he faced as an ardent digital privacy advocate. In this post, we dive deep into one of his <em>key</em> contributions, the Diffie-Hellman key exchange.</p>

<p>Diffie-Hellman is a key-exchange algorithm that allows two parties to establish a shared secret over an insecure channel. A symmetric encryption key can then be derived from that shared secret. The protocol relies on it being computationally infeasible to derive the shared secret from the public values when the parameters are chosen properly.</p>

<p>Much has been written and said about the algorithm. One popular explanation comes from Computerphile, which uses colour theory to illustrate it (<a href="https://www.youtube.com/watch?v=NmM9HA2MQGI">this</a> and <a href="https://www.youtube.com/watch?v=Yjrfm_oRO0w">this</a>). Below is a brief reminder of how the algorithm works.</p>

<p>Alice and Bob — our trusty scene partners in cryptography skits — first agree on two public numbers: a large prime $p$ and a number $g$. They choose $g$ so that its powers produce many different values modulo $p$ instead of repeating after only a few steps. If they repeated too quickly, there would be too few possible public values, making the private exponents easier to recover. The number $g$ does not itself need to be prime. In practice, implementations use a standardized, publicly reviewed pair $(p, g)$ rather than generating these values themselves. Alice and Bob then choose large random private exponents $a$ and $b$, respectively. Alice publishes $A = g^a \bmod p$, and Bob publishes $B = g^b \bmod p$. Formalizing modular exponentiation in Agda, we have the following:</p>

<pre><code class="language-agda">-- Function to calculate the modulus of an exponentiation
mod-exp : (p : ℕ) → .{{_ : NonZero p}} → ℕ → ℕ → ℕ
mod-exp p base exponent = (base ^ exponent) % p

public-value : (p : ℕ) → .{{_ : NonZero p}} → ℕ → ℕ → ℕ
public-value p g private-exponent = mod-exp p g private-exponent
</code></pre>

<p>Alice and Bob exchange the public values $A$ and $B$. Alice now knows only her private exponent $a$ and Bob’s public value $B$, while Bob knows only his private exponent $b$ and Alice’s public value $A$. Alice computes $B^a \bmod p$, while Bob computes $A^b \bmod p$:</p>

<pre><code class="language-agda">alice-shared-secret : (p : ℕ) → {{_ : NonZero p}} → (a bob-public : ℕ) → ℕ
alice-shared-secret p a bob-public = mod-exp p bob-public a

bob-shared-secret : (p : ℕ) → {{_ : NonZero p}} → (b alice-public : ℕ) → ℕ
bob-shared-secret p b alice-public = mod-exp p alice-public b
</code></pre>

<p>For a small example, let $p = 5$ and $g = 2$. Alice chooses $a = 2$ and publishes $A = 2^2 \bmod 5 = 4$. Bob chooses $b = 3$ and publishes $B = 2^3 \bmod 5 = 3$. They then calculate:</p>

<ul>
  <li>Alice: $B^a \bmod p = 3^2 \bmod 5 = 4$</li>
  <li>Bob: $A^b \bmod p = 4^3 \bmod 5 = 4$</li>
</ul>

<p>Both arrive at the shared secret $4$. Agda can verify these calculations by normalization:</p>

<pre><code class="language-agda">alice-shared-secret-example : alice-shared-secret 5 2 3 ≡ 4
alice-shared-secret-example = refl

bob-shared-secret-example : bob-shared-secret 5 3 4 ≡ 4
bob-shared-secret-example = refl
</code></pre>

<blockquote>
  <p><strong>Note:</strong> The complete Agda code can be found <a href="https://github.com/rakshikab/cryptography-proofs/blob/main/DiffieHellmanKeyExchange.agda">here</a>.</p>
</blockquote>

<blockquote>
  <p><strong>Agda notation:</strong> <code class="language-plaintext highlighter-rouge">{{...}}</code> marks an instance argument that Agda can fill in automatically, while the dot in <code class="language-plaintext highlighter-rouge">.{{...}}</code> marks it as irrelevant to the computed result.</p>
</blockquote>

<p>Both parties arrive at the same value, crucially due to the commutativity of multiplication: $B^a \bmod p = g^{ba} \bmod p$ and $A^b \bmod p = g^{ab} \bmod p$. Given only the public values, recovering the private exponents $a$ and $b$ requires solving the <a href="#the-discrete-logarithm-problem">discrete-logarithm problem</a>, which is computationally infeasible for properly chosen parameters. Recovering either exponent would let an attacker compute the shared secret.</p>

<p>An attacker can see the public values $A = g^a \bmod p$ and $B = g^b \bmod p$. Multiplying and reducing them gives $AB \bmod p = g^{a+b} \bmod p$, whereas the shared secret is $g^{ab} \bmod p$. The security of the protocol relies on it being computationally infeasible to obtain $g^{ab} \bmod p$ from the two public values when the parameters are chosen properly.</p>

<p>This protects against someone who only observes the exchange, but not against an active attacker who replaces the public values and establishes separate secrets with Alice and Bob. Practical protocols therefore authenticate the exchange, for example with digital signatures.</p>

<p>The full proof of the equality of the shared secrets computed by Alice and Bob is below:</p>

<p>We start with a lemma stating that $(x \bmod p)^n \bmod p = x^n \bmod p$. It is proven below with equational reasoning in Agda.</p>

<p><strong>Key steps:</strong> <code class="language-plaintext highlighter-rouge">%-distribˡ-*</code> reduces both factors of the product modulo $p$. Then <code class="language-plaintext highlighter-rouge">m%n%n≡m%n</code> removes the repeated reduction of $x$, and the induction hypothesis handles the remaining power.</p>

<pre><code class="language-agda">mod-exp-reduce-base : (p x n : ℕ) → .{{_ : NonZero p}} → mod-exp p (x % p) n ≡ mod-exp p x n
mod-exp-reduce-base p x ℕ.zero = refl
mod-exp-reduce-base p x (ℕ.suc n) = begin
    mod-exp p (x % p) (ℕ.suc n)
        ≡⟨ refl ⟩
    ((x % p) * ((x % p) ^ n)) % p
        ≡⟨ %-distribˡ-* (x % p) ((x % p) ^ n) p ⟩
    (((x % p) % p) * (((x % p) ^ n) % p)) % p
        ≡⟨ cong (λ k → (k * (((x % p) ^ n) % p)) % p) (m%n%n≡m%n x p) ⟩
    ((x % p) * (((x % p) ^ n) % p)) % p
        ≡⟨ cong (λ k → ((x % p) * k) % p) (mod-exp-reduce-base p x n) ⟩
    ((x % p) * ((x ^ n) % p)) % p
        ≡⟨ sym (%-distribˡ-* x (x ^ n) p) ⟩
    (x * (x ^ n)) % p
        ≡⟨ refl ⟩
    mod-exp p x (ℕ.suc n)
    ∎
</code></pre>

<p>And the final proof that the shared secret that Alice and Bob arrive at is the same:</p>

<p><strong>Key steps:</strong> Unfolding <code class="language-plaintext highlighter-rouge">public-value</code> and <code class="language-plaintext highlighter-rouge">alice-shared-secret</code> produces Alice’s calculation. Then <code class="language-plaintext highlighter-rouge">mod-exp-reduce-base</code> removes the inner modulo operation, <code class="language-plaintext highlighter-rouge">^-*-assoc</code> combines the nested powers, and <code class="language-plaintext highlighter-rouge">*-comm</code> changes the exponent from $ba$ to $ab$. Reversing these steps produces Bob’s calculation.</p>

<pre><code class="language-agda">diffie-hellman-key-exchange : (p : ℕ) → {{_ : NonZero p}} → (g a b : ℕ) →
    alice-shared-secret p a (public-value p g b) ≡
    bob-shared-secret p b (public-value p g a)
diffie-hellman-key-exchange p g a b = begin
    alice-shared-secret p a (public-value p g b)
        ≡⟨ refl ⟩
    mod-exp p (mod-exp p g b) a
        ≡⟨ mod-exp-reduce-base p (g ^ b) a ⟩
    mod-exp p (g ^ b) a
        ≡⟨ cong (λ k → k % p) (^-*-assoc g b a) ⟩
    (g ^ (b * a)) % p
        ≡⟨ cong (λ k → (g ^ k) % p) (*-comm b a) ⟩
    (g ^ (a * b)) % p
        ≡⟨ cong (λ k → k % p) (sym (^-*-assoc g a b)) ⟩
    mod-exp p (g ^ a) b
        ≡⟨ sym (mod-exp-reduce-base p (g ^ a) b) ⟩
    mod-exp p (mod-exp p g a) b
        ≡⟨ refl ⟩
    bob-shared-secret p b (public-value p g a)
    ∎ 
</code></pre>

<p>The proof above establishes correctness: Alice and Bob arrive at the same shared secret. We now turn to the security question of why their public values do not reveal their private exponents.</p>

<h2 id="the-discrete-logarithm-problem">The Discrete Logarithm Problem</h2>

<p>This is the difficult problem mentioned earlier: given $g^a \bmod p$, recover $a$. A proposed answer can be checked efficiently, but no efficient classical algorithm is known for finding one when the parameters are chosen properly. A sufficiently powerful quantum computer could solve it efficiently using Shor’s algorithm.</p>

<p>This difficulty is what supports the security of the key exchange. The Agda proof below does not formalize computational difficulty; instead, it proves the uniqueness part of the problem: for a primitive root $g$ of a prime $p$, no two exponents between $0$ and $p - 2$ produce the same value. Equivalently, the discrete logarithm is unique modulo $p - 1$. More generally, the problem is stated as follows:</p>

<blockquote>
  <p>If $\alpha$ is an arbitrary integer relatively prime to $n$ and $g$ is a primitive root of $n$, then among the numbers $0, 1, 2, \ldots, \phi(n) - 1$, where $\phi(n)$ is the totient function, there is exactly one number $\mu$ such that $\alpha \equiv g^\mu \pmod n$.</p>
</blockquote>

<p>Explaining the definition in detail:</p>

<ul>
  <li>Relatively prime implies that the common factor between two numbers is only 1.</li>
  <li>Primitive root: A primitive root of a prime $p$ is an integer $g$ whose multiplicative order modulo $p$ is $p - 1$.
    <ul>
      <li>Multiplicative order of $b \bmod n$: The smallest positive exponent $e$ for which $b^e \equiv 1 \pmod n$.</li>
    </ul>
  </li>
  <li>Totient function: The number of positive integers $\leq n$ that are relatively prime to $n$.</li>
</ul>

<p>Formalizing the above in Agda, we get:</p>

<h3 id="multiplicative-order">Multiplicative order:</h3>

<pre><code class="language-agda">record MultiplicativeOrder (a p k : ℕ) : Set where
    constructor multiplicative-order
    field
        {{p≢0}} : NonZero p
        {{k≢0}} : NonZero k
        power1 : mod-exp p a k ≡ 1
        minimal : ∀ {k′} → {{_ : NonZero k′}} → k′ &lt; k → mod-exp p a k′ ≢ 1
</code></pre>

<h3 id="totient-function">Totient function:</h3>

<pre><code class="language-agda">totientRecursive : (n rec : ℕ) → .{{_ : NonZero n}} → ℕ
totientRecursive n ℕ.zero = 0
totientRecursive n (ℕ.suc rec) with gcd (ℕ.suc rec) n
... | 1 = suc (totientRecursive n rec)
... | _ = totientRecursive n rec

totient : (n : ℕ) → .{{_ : NonZero n}} → ℕ
totient (ℕ.suc ℕ.zero) = 1
totient (ℕ.suc n) = totientRecursive (ℕ.suc n) (n)
</code></pre>

<h3 id="primitive-root">Primitive root:</h3>
<pre><code class="language-agda">record PrimitiveRoot (p g : ℕ) : Set where
    constructor primitiveRoot
    field
        {{p≢0}} : NonZero p
        p-prime : Prime p
        order-max : MultiplicativeOrder g p (totient p)
</code></pre>

<p>And some examples:</p>
<ul>
  <li>The multiplicative order of $2 \bmod 5$ is $4$ since the smallest number for which $2^x \bmod 5 = 1$ is $4$.
    <ul>
      <li>$2^1 \bmod 5 = 2$</li>
      <li>$2^2 \bmod 5 = 4$</li>
      <li>$2^3 \bmod 5 = 3$</li>
      <li>$2^4 \bmod 5 = 1$</li>
    </ul>
  </li>
  <li>The totient of $5$ is $4$ since $5$ is a prime number.</li>
  <li>Finally, $2$ is a primitive root of $5$ since $2 \bmod 5$ has the multiplicative order $5 - 1$.</li>
</ul>

<pre><code class="language-agda">multiplicative-order-2-mod-5 : MultiplicativeOrder 2 5 4
multiplicative-order-2-mod-5 = multiplicative-order refl λ
    { {1} _ ()
    ; {2} _ ()
    ; {3} _ ()
    ; {suc (suc (suc (suc _)))} (s≤s (s≤s (s≤s (s≤s ()))))
    }

totient5 : totient 5 ≡ 4
totient5 = refl

prime-5 : Prime 5
prime-5 = from-yes (prime? 5)

primitive-root-2-mod-5 : PrimitiveRoot 5 2
primitive-root-2-mod-5 rewrite totient5 = primitiveRoot prime-5 multiplicative-order-2-mod-5

</code></pre>

<p>Now that we have all the definitions in place, we will prove the uniqueness part of the discrete logarithm definition:</p>

<blockquote>
  <p>exactly one number $\mu$ such that $\alpha \equiv g^\mu \pmod n$</p>
</blockquote>

<p>The Agda theorem expresses uniqueness modulo $\phi(p)$. For two distinct exponents $\mu$ and $\mu’$, such that $\mu &lt; \mu’$, if they produce the same value, then they have the same remainder after division by $\phi(p)$:</p>

<pre><code class="language-agda">discrete-log-unique :
    (g p : ℕ) → 
    (root : PrimitiveRoot p g) →
    ∀ {μ μ′} →
    μ &lt; μ′ →
    mod-exp p g μ ≡ mod-exp p g μ′ →
    μ % totient p ≡ μ′ % totient p
</code></pre>

<p>Since $\phi(p) = p - 1$ for prime $p$, no two distinct exponents between $0$ and $p - 2$ can produce the same power.</p>

<p>The proof proceeds in four steps:</p>

<ol>
  <li>Powers repeat after every multiple of the multiplicative order $k$.</li>
  <li>If $a^x \bmod p = 1$, then $k$ divides $x$.</li>
  <li>If two powers are equal, raising $a$ to the difference of their exponents gives $1$ modulo $p$.</li>
  <li>Therefore, $k$ divides that difference, so the exponents have the same remainder when divided by $k$.</li>
</ol>

<p>We establish each step with the following helper lemmas.</p>

<h3 id="proving-uniqueness-modulo-the-multiplicative-order">Proving uniqueness modulo the multiplicative order</h3>

<p>We start with this lemma: Given natural numbers $a, p, k$ where $k$ is the multiplicative order of $a \bmod p$, we have for all values of $r$ and $q$: $a^{r+qk} \equiv a^r \pmod p$.</p>

<p><strong>Key steps:</strong> <code class="language-plaintext highlighter-rouge">^-distribˡ-+-*</code> splits $a^{r+qk}$ into $a^r a^{qk}$. After <code class="language-plaintext highlighter-rouge">*-comm</code> changes $qk$ to $kq$, <code class="language-plaintext highlighter-rouge">^-*-assoc</code> rewrites the second factor as $(a^k)^q$. The <code class="language-plaintext highlighter-rouge">power1</code> field then replaces $a^k \bmod p$ with $1$.</p>

<pre><code class="language-agda">
mod-exp-periodic : (a p k : ℕ) → {{_ : MultiplicativeOrder a p k}} →
  ∀ r q → mod-exp p a (r + q * k) ≡ mod-exp p a r
mod-exp-periodic a p k {{order}} r q = begin 
    mod-exp p a (r + q * k)
        ≡⟨ refl ⟩
    a ^ (r + q * k) % p
        ≡⟨ cong (λ x → x % p) (^-distribˡ-+-* a r (q * k)) ⟩
    a ^ r * a ^ (q * k) % p
        ≡⟨ cong (λ x → a ^ r * a ^ x % p) (*-comm q k) ⟩
    a ^ r * a ^ (k * q) % p
        ≡⟨ cong (λ x → (a ^ r * x) % p) (sym (^-*-assoc a k q)) ⟩
    (a ^ r * (a ^ k) ^ q) % p
        ≡⟨ %-distribˡ-* (a ^ r) ((a ^ k) ^ q) p ⟩
    ((a ^ r % p) * (((a ^ k) ^ q) % p)) % p
        ≡⟨ cong (λ x → ((a ^ r % p) * x) % p) (sym (mod-exp-reduce-base p (a ^ k) q)) ⟩
    ((a ^ r % p) * ((((a ^ k) % p) ^ q) % p)) % p
        ≡⟨ cong (λ x → ((a ^ r % p) * ((x ^ q) % p)) % p) (MultiplicativeOrder.power1 order) ⟩
    ((a ^ r % p) * (1 ^ q % p)) % p
        ≡⟨ cong (λ x → ((a ^ r % p) * x) % p) (cong (λ x → x % p) (^-zeroˡ q)) ⟩
    ((a ^ r % p) * (1 % p)) % p
        ≡⟨ sym (%-distribˡ-* (a ^ r) 1 p) ⟩
    (a ^ r * 1) % p
        ≡⟨ cong (λ x → x % p) (*-identityʳ (a ^ r)) ⟩
    (a ^ r) % p
        ≡⟨ refl ⟩
    mod-exp p a r
    ∎
</code></pre>

<p>Given the same multiplicative order $k$ of $a \bmod p$, if $a^{k’} \bmod p = 1$ then $k$ divides $k’$.</p>

<p><strong>Key steps:</strong> <code class="language-plaintext highlighter-rouge">mod-exp-periodic</code> and the division algorithm show that $a^{k’ \bmod k} \bmod p = 1$. If this remainder were non-zero, <code class="language-plaintext highlighter-rouge">m%n&lt;n</code> and the <code class="language-plaintext highlighter-rouge">minimal</code> field would contradict the minimality of $k$; hence the remainder is zero.</p>

<pre><code class="language-agda">mod-exp≡1⇒order|x : (a p k : ℕ) → {{_ : MultiplicativeOrder a p k}} →
    (∀ {k′} → {{_ : NonZero k′}} →
    mod-exp p a k′ ≡ 1 → k ∣ k′)
mod-exp≡1⇒order|x a p k {{order}} {k′} mod-exp_pak′
    with k′ % k in k′%k
... | ℕ.zero = m%n≡0⇒n∣m k′ k k′%k
... | suc r = contradiction
                (subst (λ x → mod-exp p a x ≡ 1) k′%k remainder-power1)
                (MultiplicativeOrder.minimal order
                    (subst (_&lt; k) k′%k (m%n&lt;n k′ k)))
  where
    remainder-power1 : mod-exp p a (k′ % k) ≡ 1
    remainder-power1 = begin
        mod-exp p a (k′ % k)
            ≡⟨ sym (mod-exp-periodic a p k (k′ % k) (k′ / k)) ⟩
        mod-exp p a (k′ % k + (k′ / k) * k)
            ≡⟨ cong (mod-exp p a) (sym (m≡m%n+[m/n]*n k′ k)) ⟩
        mod-exp p a k′
            ≡⟨ mod-exp_pak′ ⟩
        1
        ∎
</code></pre>

<p>The corollary of the above: Given the multiplicative order $k$ of $a \bmod p$, if $k$ divides some $k’$, then $a^{k’} \bmod p = 1$.</p>

<p><strong>Key steps:</strong> The divisibility witness rewrites $k’$ as $qk$. Then <code class="language-plaintext highlighter-rouge">mod-exp-periodic</code> removes this multiple of the order, and <code class="language-plaintext highlighter-rouge">power1</code> supplies the final value $1$.</p>

<pre><code class="language-agda">order|k′⇒mod-exp≡1 : (a p k k′ : ℕ) → {{_ : MultiplicativeOrder a p k}} →
    k ∣ k′ → mod-exp p a k′ ≡ 1
order|k′⇒mod-exp≡1 a p k k′ {{order}} (divides q k′≡q*k) = begin
    mod-exp p a k′
        ≡⟨ cong (mod-exp p a) k′≡q*k ⟩
    mod-exp p a (q * k)
        ≡⟨ mod-exp-periodic a p k 0 q ⟩
    mod-exp p a 0
        ≡⟨ sym (mod-exp-periodic a p k 0 1) ⟩
    mod-exp p a (0 + 1 * k)
        ≡⟨ cong (mod-exp p a) (+-identityʳ k) ⟩
    mod-exp p a k
        ≡⟨ MultiplicativeOrder.power1 order ⟩
    1
    ∎
</code></pre>

<p>A simple one: $a^{x+y} \bmod p$ equals $((a^x \bmod p) \cdot (a^y \bmod p)) \bmod p$.</p>

<p><strong>Key steps:</strong> <code class="language-plaintext highlighter-rouge">^-distribˡ-+-*</code> changes $a^{x+y}$ into $a^x a^y$, and <code class="language-plaintext highlighter-rouge">%-distribˡ-*</code> reduces the two factors separately modulo $p$.</p>

<pre><code class="language-agda">mod-exp-add : (a p x y : ℕ) → {{_ : NonZero p}} →
    mod-exp p a (x + y) ≡ (mod-exp p a x * mod-exp p a y) % p
mod-exp-add a p x y = begin
    mod-exp p a (x + y)
        ≡⟨ cong (_% p) (^-distribˡ-+-* a x y) ⟩
    (a ^ x * a ^ y) % p
        ≡⟨ %-distribˡ-* (a ^ x) (a ^ y) p ⟩
    (mod-exp p a x * mod-exp p a y) % p
    ∎
</code></pre>

<p>Another simple one: if a non-zero number $d$ divides the difference between $n$ and $m$ (assume $m &lt; n$), then $m \bmod d = n \bmod d$.</p>

<p><strong>Key steps:</strong> <code class="language-plaintext highlighter-rouge">m+[n∸m]≡n</code> rewrites $n$ as $m+(n-m)$, and <code class="language-plaintext highlighter-rouge">%-distribˡ-+</code> separates the remainders. Then <code class="language-plaintext highlighter-rouge">n∣m⇒m%n≡0</code> turns the divisible difference into remainder $0$.</p>

<pre><code class="language-agda">d|n∸m⇒m%d≡n%d :
    (m n d : ℕ) → {{_ : NonZero d}} →
    m &lt; n →
    d ∣ (n ∸ m) →
    m % d ≡ n % d
d|n∸m⇒m%d≡n%d m n d m&lt;n d∣n∸m = sym (begin
    n % d
        ≡⟨ cong (_% d) (sym (m+[n∸m]≡n (&lt;⇒≤ m&lt;n))) ⟩
    (m + (n ∸ m)) % d
        ≡⟨ %-distribˡ-+ m (n ∸ m) d ⟩
    ((m % d) + ((n ∸ m) % d)) % d
        ≡⟨ cong (λ x → ((m % d) + x) % d) (n∣m⇒m%n≡0 (n ∸ m) d d∣n∸m) ⟩
    ((m % d) + 0) % d
        ≡⟨ cong (_% d) (+-identityʳ (m % d)) ⟩
    (m % d) % d
        ≡⟨ m%n%n≡m%n m d ⟩
    m % d
    ∎)
</code></pre>

<p>The final helper lemma captures modular cancellation. Informally, from $a^\mu \bmod p = a^{\mu’} \bmod p$, we want to cancel $a^\mu$ and conclude that $a^{\mu’-\mu} \bmod p = 1$. Rather than introduce modular inverses, the proof uses periodicity to add $\mu k$ to the exponent, substitutes the equality of the two reduced powers, and recombines them into $a^{\mu k}$. Since $k$ divides $\mu k$, we have $a^{\mu k} \bmod p = 1$. This was the lemma I had the most fun proving:</p>

<pre><code class="language-agda">modular-cancellation :
    (a p k μ μ′ : ℕ) → {{_ : MultiplicativeOrder a p k}} →
    μ &lt; μ′ →
    mod-exp p a μ ≡ mod-exp p a μ′ →
    mod-exp p a (μ′ ∸ μ) ≡ 1
modular-cancellation a p k μ μ′ {{order}} μ&lt;μ′ eq-mod-exp = begin 
    mod-exp p a (μ′ ∸ μ)
        ≡⟨ sym (mod-exp-periodic a p k (μ′ ∸ μ) μ) ⟩
    mod-exp p a ((μ′ ∸ μ) + μ * k)
        ≡⟨ cong (mod-exp p a) (sym (+-∸-comm (μ * k) (&lt;⇒≤ μ&lt;μ′))) ⟩
    mod-exp p a (μ′ + μ * k ∸ μ)
        ≡⟨ cong (mod-exp p a) (+-∸-assoc μ′ (m≤m*n μ k)) ⟩
    mod-exp p a (μ′ + (μ * k ∸ μ))
        ≡⟨ mod-exp-add a p μ′ (μ * k ∸ μ) ⟩
    ((mod-exp p a μ′) * (mod-exp p a (μ * k ∸ μ))) % p
        ≡⟨ cong (λ x → (x * mod-exp p a (μ * k ∸ μ)) % p) (sym eq-mod-exp) ⟩
    ((mod-exp p a μ) * (mod-exp p a (μ * k ∸ μ))) % p
        ≡⟨ sym (mod-exp-add a p μ (μ * k ∸ μ)) ⟩
    (mod-exp p a (μ + (μ * k ∸ μ)))
        ≡⟨ cong (mod-exp p a) (m+[n∸m]≡n (m≤m*n μ k)) ⟩
    (mod-exp p a (μ * k))
        ≡⟨ order|k′⇒mod-exp≡1 a p k (μ * k) {{order}} (n∣m*n μ) ⟩
    1
    ∎
</code></pre>

<h3 id="final-proof-of-uniqueness-modulo-the-totient">Final proof of uniqueness modulo the totient</h3>

<p>Now that we have all the required lemmas, the final proof that equal powers give equal exponent remainders modulo $\phi(p)$ is succinct:</p>

<p><strong>Key steps:</strong> <code class="language-plaintext highlighter-rouge">modular-cancellation</code> shows that $g^{\mu’-\mu} \bmod p = 1$. Using the multiplicative order $\phi(p)$ supplied by the primitive root, <code class="language-plaintext highlighter-rouge">mod-exp≡1⇒order|x</code> proves that $\phi(p)$ divides the exponent difference. Finally, <code class="language-plaintext highlighter-rouge">d|n∸m⇒m%d≡n%d</code> gives equality of the two remainders.</p>

<pre><code class="language-agda">discrete-log-unique :
    (g p : ℕ) → 
    (root : PrimitiveRoot p g) →
    ∀ {μ μ′} →
    μ &lt; μ′ →
    mod-exp p g μ ≡ mod-exp p g μ′ →
    μ % totient p ≡ μ′ % totient p
discrete-log-unique g p root {μ} {μ′} μ&lt;μ' eq-mod-exp = d|n∸m⇒m%d≡n%d μ μ′ (totient p) μ&lt;μ'
    (mod-exp≡1⇒order|x g p (totient p) {{root .PrimitiveRoot.order-max}} {μ′ ∸ μ} {{ &gt;-nonZero (m&lt;n⇒0&lt;n∸m μ&lt;μ')}}
        (modular-cancellation g p (totient p) μ  μ′ {{root .PrimitiveRoot.order-max}} μ&lt;μ' eq-mod-exp))

</code></pre>

<p>Together, these proofs establish that Alice and Bob compute the same shared secret and that if two distinct exponents produce equal powers modulo $p$, the exponents have the same remainder when divided by $\phi(p)$.</p>

<p>Even if $g$ is a primitive root of $p$, the Pohlig-Hellman algorithm makes the discrete logarithm easier when $p - 1$ factors into small primes. A common defence is to choose a safe prime $p = 2q + 1$, where $q$ is also a large prime. Then the only prime factors of $p - 1$ are $2$ and $q$, so Pohlig-Hellman cannot reduce the problem to many small ones. Most modern software instead uses an elliptic-curve version of Diffie-Hellman. The next post will continue from here!</p>]]></content><author><name>Rakshika Bagavathy</name></author><category term="agda,cryptography" /><summary type="html"><![CDATA[Formalizing Diffie-Hellman shared-secret correctness and discrete-logarithm uniqueness in Agda.]]></summary></entry><entry><title type="html">Solving Sudoku with effect handlers</title><link href="https://rakshikab.github.io/sudoku" rel="alternate" type="text/html" title="Solving Sudoku with effect handlers" /><published>2022-01-24T00:00:00+00:00</published><updated>2022-01-24T00:00:00+00:00</updated><id>https://rakshikab.github.io/sudoku</id><content type="html" xml:base="https://rakshikab.github.io/sudoku"><![CDATA[<p><a href="https://koka-lang.github.io/koka/doc/book.html#getstarted">Koka</a> is a strongly typed functional-style language with effect types and handlers. In this post I attempt to solve the sudoku puzzle using Koka and its effect handlers.</p>

<p>Converting a sudoku puzzle to an exact cover problem is a well known way of solving the puzzle programmatically. Here we skip the conversion of the puzzle to the exact cover matrix and take the exact cover matrix directly as the input.</p>

<h2 id="implementing-algorithm-x-for-the-exact-cover-problem">Implementing Algorithm X for the exact cover problem</h2>

<p>The main solve function looks something like the below:</p>

<pre><code class="language-koka">fun solve(grid: list&lt;list&lt;int&gt;&gt;): &lt;div,console,ndet,exn,sudoku|e&gt; maybe&lt;list&lt;int&gt;&gt;
    if grid.length == 0 then return Just([])
    val col = choose_min_column(grid)
    val row_idx = choose_row(grid, col)
    var row := []
    match(grid[row_idx])
        Just(something) -&gt; row := something
        Nothing -&gt; throw-exn(Exception("Invalid row " ++ row_idx.show, ExnError))
    
    val col_set = get_columns_with_1(row) 
    val row_set = get_rows_with_1(grid, col_set) 
    val new_grid = remove_rows_and_cols(grid, row_set, col_set) 
    val new_sol = solve(new_grid)
    match(new_sol)
        Nothing -&gt; 
            Nothing
        Just(new_solution) -&gt; 
            val new_soln_mod = update_row_indices(grid.length, row_set, new_solution)
            Just([row_idx] ++ new_soln_mod)
</code></pre>

<p>This is the implementation of the <a href="https://arxiv.org/pdf/cs/0011047.pdf">Algorithm X for the exact cover problem by Donald Knuth</a>. We choose a column deterministically - one with the least number of 1s. We then choose a row non-deterministically for the chosen column. Once we get the index of a possible row to be included in a solution, we get the conflicting columns and rows to the column and the newly chosen row. After we remove the conflicting rows and columns, we recurse on the solve function with the smaller exact cover matrix.</p>

<p>We incrementally choose rows from the exact cover matrix to include in the final solution. If at some point we realize the current set of rows included in the solution does not give us the right final solution, we need to backtrack and choose a different row. In Koka we can do this with effects in an elegant way.</p>

<p>This is the effect type and its control operation. The control operation takes the grid and the constraint column index as inputs, and returns the index of the chosen row.
Using this operation in the function introduces the <code class="language-plaintext highlighter-rouge">sudoku</code> effect in the function type along with the other effects.</p>

<pre><code class="language-koka">effect sudoku
	control choose_row(grid: list&lt;list&lt;int&gt;&gt;, col: int): int
</code></pre>

<h2 id="backtracking-with-multiple-resumption">Backtracking with multiple resumption</h2>

<p>Here, choosing the row non-deterministically is the effectful operation. If a row is an eligible candidate to be included in an exact cover matrix’s solution (i.e. we have grid[row][col] == 1), we <em>resume</em> with that row’s index. And we continue with finding the rest of the solution that works with the currently chosen row. If at any point we need to backtrack, we can go to the point of execution where we resumed with the previous wrong value and resume with the next possible row. This is a nice feature of Koka where with an effectful operation we can resume as many times as needed.</p>

<p>The effect handler looks like:</p>

<pre><code class="language-koka">fun solveHandler(action) {
    with control choose_row(grid: list&lt;list&lt;int&gt;&gt;, col: int)
        foldl(grid, (0, Nothing), 
            fn((idx: int, prev_soln: maybe&lt;list&lt;int&gt;&gt;), row) 
                if (is-just(prev_soln)) then (idx, prev_soln)
                else if (row[col].default(0) == 1) then (idx + 1, resume(idx))
                else (idx + 1, Nothing)
        ).snd
    action()
}
</code></pre>

<p>And we can finally use the handler function with the solve function like so: <code class="language-plaintext highlighter-rouge">solveHandler({ solve(grid) })</code></p>

<p>With the <code class="language-plaintext highlighter-rouge">with control</code> block we add the code to handle the <code class="language-plaintext highlighter-rouge">choose_row</code> control operation of the <code class="language-plaintext highlighter-rouge">sudoku</code> effect. This handler is applied on the <code class="language-plaintext highlighter-rouge">action</code> at the end of the block - which is just the main <code class="language-plaintext highlighter-rouge">solve</code> function.</p>

<p>Through the <code class="language-plaintext highlighter-rouge">choose_row</code> effect we try to choose different rows such that the constraint corresponding to column <code class="language-plaintext highlighter-rouge">c</code> is true. Hence the condition where we check <code class="language-plaintext highlighter-rouge">row[col].default(0) == 1</code>. If we want to choose this row, we <code class="language-plaintext highlighter-rouge">resume</code> with the index for the row. If this row is indeed present in the final solution, when we recurse on the <code class="language-plaintext highlighter-rouge">solve</code> function, we will return <code class="language-plaintext highlighter-rouge">Just(solution)</code> all the way through. This is captured in the first condition where if we’ve received a valid solution from the function, we ignore the rest of the foldl and return this solution. In case the row we resumed with earlier is not the right choice, leading to dead ends with the reduced exact cover matrix, we return <code class="language-plaintext highlighter-rouge">Nothing</code> from the <code class="language-plaintext highlighter-rouge">solve</code> function. Now if the next row is a valid option, we resume with the new index, else we just pass a Nothing along to the foldl to skip over to the next row. In the end we return the second value in the tuple returned by foldl (the first value in the tuple is used to store and propagate the index).</p>

<p>In the end, if get a <code class="language-plaintext highlighter-rouge">Nothing</code>, there is no valid solution for this exact cover matrix. If there is one, we get the <code class="language-plaintext highlighter-rouge">Just(solution)</code>.</p>]]></content><author><name>Rakshika Bagavathy</name></author><category term="koka" /><summary type="html"><![CDATA[Koka is a strongly typed functional-style language with effect types and handlers. In this post I attempt to solve the sudoku puzzle using Koka and its effect handlers.]]></summary></entry></feed>