packages feed

crypton-x509-1.8.0: Data/X509/EC.hs

-- |
-- Module      : Data.X509.EC
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- 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.PrivateKey
import Data.X509.PublicKey

import Crypto.Number.Serialize (os2ip)
import qualified Crypto.PubKey.ECC.Prim as ECC
import qualified Crypto.PubKey.ECC.Types as ECC

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