module Ripple.Seed (getSecret) where

import Data.Word
import Data.Base58Address (RippleAddress, rippleAddressPayload)
import Crypto.Hash.CryptoAPI (SHA512, hash')
import qualified Data.ByteString as BS
import qualified Data.Serialize as Serialize

import Crypto.Types.PubKey.ECC (Curve(CurveFP), CurvePrime(..), CurveCommon(..), getCurveByName, CurveName(SEC_p256k1))

import Crypto.Util (bs2i, i2bs_unsized)

import ECDSA (publicFromPrivate, publicToBytes, PrivateKey(..))

-- n is the order of the base point
n :: Integer
p256k1 :: Curve
p256k1@(CurveFP (CurvePrime _ (CurveCommon {ecc_n = n}))) = getCurveByName SEC_p256k1

-- | Derive the secret key for the given secret seed and address
getSecret ::
	RippleAddress -- ^ Secret seed address
	-> PrivateKey
getSecret seed = PrivateKey p256k1 d
	where
	d = (sec + priv) `mod` n
	sec = bs2i $ gen (pub `BS.append` seq)
	pub = publicToBytes $ publicFromPrivate $ PrivateKey p256k1 priv
	priv = bs2i $ gen sbytes
	sbytes = i2bs_unsized (rippleAddressPayload seed)
	seq = Serialize.encode (0 :: Word32)

gen :: BS.ByteString -> BS.ByteString
gen bytes = fst $ until (\(x,_) -> n >= bs2i x) (\(_,i) -> (go i,i+1)) (go 0, 1)
	where
	go i = halfOfSHA512 $ hash' (bytes `BS.append` Serialize.encode (i :: Word32))

halfOfSHA512 :: SHA512 -> BS.ByteString
halfOfSHA512 sha512 = BS.take 32 $ Serialize.encode sha512