-- | -- Module : Data.X509.EC -- License : BSD-style -- Maintainer : Vincent Hanquez -- Stability : experimental -- Portability : unknown -- -- Utilities related to Elliptic Curve certificates and keys. -- module Data.X509.EC ( unserializePoint , ecPubKeyCurve , ecPubKeyCurveName , ecPrivKeyCurve , ecPrivKeyCurveName , lookupCurveNameByOID ) where import Data.ASN1.OID import Data.List (find) import Data.X509.OID import Data.X509.PublicKey import Data.X509.PrivateKey import qualified Crypto.PubKey.ECC.Prim as ECC import qualified Crypto.PubKey.ECC.Types as ECC import Crypto.Number.Serialize (os2ip) import qualified Data.ByteString as B -- | Read an EC point from a serialized format and make sure the point is -- valid for the specified curve. unserializePoint :: ECC.Curve -> SerializedPoint -> Maybe ECC.Point unserializePoint curve (SerializedPoint bs) = case B.uncons bs of Nothing -> Nothing Just (ptFormat, input) -> case ptFormat of 4 -> if B.length input /= 2 * bytes then Nothing else let (x, y) = B.splitAt bytes input p = ECC.Point (os2ip x) (os2ip y) in if ECC.isPointValid curve p then Just p else Nothing -- 2 and 3 for compressed format. _ -> Nothing where bits = ECC.curveSizeBits curve bytes = (bits + 7) `div` 8 -- | Return the curve associated to an EC Public Key. This does not check -- if a curve in explicit format is valid: if the input is not trusted one -- should consider 'ecPubKeyCurveName' instead. ecPubKeyCurve :: PubKeyEC -> Maybe ECC.Curve ecPubKeyCurve (PubKeyEC_Named name _) = Just $ ECC.getCurveByName name ecPubKeyCurve pub@PubKeyEC_Prime{} = fmap buildCurve $ unserializePoint (buildCurve undefined) (pubkeyEC_generator pub) where prime = pubkeyEC_prime pub buildCurve g = let cc = ECC.CurveCommon { ECC.ecc_a = pubkeyEC_a pub , ECC.ecc_b = pubkeyEC_b pub , ECC.ecc_g = g , ECC.ecc_n = pubkeyEC_order pub , ECC.ecc_h = pubkeyEC_cofactor pub } in ECC.CurveFP (ECC.CurvePrime prime cc) -- | Return the name of a standard curve associated to an EC Public Key ecPubKeyCurveName :: PubKeyEC -> Maybe ECC.CurveName ecPubKeyCurveName (PubKeyEC_Named name _) = Just name ecPubKeyCurveName pub@PubKeyEC_Prime{} = find matchPrimeCurve $ enumFrom $ toEnum 0 where matchPrimeCurve c = case ECC.getCurveByName c of ECC.CurveFP (ECC.CurvePrime p cc) -> ECC.ecc_a cc == pubkeyEC_a pub && ECC.ecc_b cc == pubkeyEC_b pub && ECC.ecc_n cc == pubkeyEC_order pub && p == pubkeyEC_prime pub _ -> False -- | Return the EC curve associated to an EC Private Key. This does not check -- if a curve in explicit format is valid: if the input is not trusted one -- should consider 'ecPrivKeyCurveName' instead. ecPrivKeyCurve :: PrivKeyEC -> Maybe ECC.Curve ecPrivKeyCurve (PrivKeyEC_Named name _) = Just $ ECC.getCurveByName name ecPrivKeyCurve priv@PrivKeyEC_Prime{} = fmap buildCurve $ unserializePoint (buildCurve undefined) (privkeyEC_generator priv) where prime = privkeyEC_prime priv buildCurve g = let cc = ECC.CurveCommon { ECC.ecc_a = privkeyEC_a priv , ECC.ecc_b = privkeyEC_b priv , ECC.ecc_g = g , ECC.ecc_n = privkeyEC_order priv , ECC.ecc_h = privkeyEC_cofactor priv } in ECC.CurveFP (ECC.CurvePrime prime cc) -- | Return the name of a standard curve associated to an EC Private Key ecPrivKeyCurveName :: PrivKeyEC -> Maybe ECC.CurveName ecPrivKeyCurveName (PrivKeyEC_Named name _) = Just name ecPrivKeyCurveName priv@PrivKeyEC_Prime{} = find matchPrimeCurve $ enumFrom $ toEnum 0 where matchPrimeCurve c = case ECC.getCurveByName c of ECC.CurveFP (ECC.CurvePrime p cc) -> ECC.ecc_a cc == privkeyEC_a priv && ECC.ecc_b cc == privkeyEC_b priv && ECC.ecc_n cc == privkeyEC_order priv && p == privkeyEC_prime priv _ -> False -- | Return the curve name associated to an OID lookupCurveNameByOID :: OID -> Maybe ECC.CurveName lookupCurveNameByOID = lookupByOID curvesOIDTable