-- | Diffie-Hellman key exchange

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Bitcoin.Crypto.EC.DiffieHellman where

--------------------------------------------------------------------------------

import Data.Word

import Bitcoin.Misc.OctetStream

import Bitcoin.Crypto.Word256
import Bitcoin.Crypto.FiniteField.Fast.Fp

import Bitcoin.Crypto.EC.Curve
import Bitcoin.Crypto.EC.Projective
import Bitcoin.Crypto.EC.Key

--------------------------------------------------------------------------------

newtype SharedSecret = SharedSecret [Word8] deriving (Eq,Ord,Show,OctetStream)

--------------------------------------------------------------------------------

-- | Given your private key and somebody else's public key, this compute
-- a (256 bit) shared secret. 
--
-- This is actually very simple: since @pubkey = G * privkey@, the shared
-- secret will be simply @G * privkey1 * privkey2@.
--
-- This can fail if for example an invalid pubkey is given, and
-- in some other special circumstances.
--
diffieHellmanPrimitive :: PrivKey -> PubKey -> Maybe SharedSecret
diffieHellmanPrimitive (PrivKey d) pk = 
  case uncompressPubKey pk of
    Nothing -> Nothing    
    Just full@(FullPubKey x y) -> case isValidPubKey full of 
      False -> Nothing
      True  -> case fromECProj (mulECP (toECProj $ mkECPoint x y) d) of
        ECInfinity    -> Nothing
        ECPoint zx zy -> Just (SharedSecret $ word256ToWord8ListLE $ unFp zx)   -- zx=0 doesn't seem to be on the curve, so this is always nonzero
    _ -> Nothing                                                                -- shouldn't happen, but totality is always good.

--------------------------------------------------------------------------------