summaryrefslogtreecommitdiff
path: root/Data/OpenPGP/Util/Ed25519.hs
blob: 67eeba3e2c289ecad45020c1cf5611e35d13c79a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
module Data.OpenPGP.Util.Ed25519 where

import Control.Monad
import Crypto.Error
import qualified Crypto.PubKey.Ed25519 as Ed25519
import Data.Bits
import qualified Data.ByteArray        as BA
import qualified Data.ByteString       as BS
import qualified Data.ByteString.Lazy  as BL

import qualified Data.OpenPGP as OpenPGP
import Data.OpenPGP (MPI(..))
import Data.OpenPGP.Internal
import Data.OpenPGP.Util.Base
-- import Crypto.ECC.Edwards25519

oid_ed25516 = 0x2B06010401DA470F01

zeroExtend :: Int -> BS.ByteString -> BS.ByteString
zeroExtend n bs = case compare (BS.length bs) n of
    GT -> BS.take n bs
    EQ -> bs
    LT -> bs <> BS.replicate (n - BS.length bs) 0

zeroPad :: Int -> BS.ByteString -> BS.ByteString
zeroPad n bs = case compare (BS.length bs) n of
    GT -> BS.take n bs
    EQ -> bs
    LT -> BS.replicate (n - BS.length bs) 0 <> bs

ed25519Key :: OpenPGP.Packet -> Maybe Ed25519.PublicKey
ed25519Key k =
    let n = case keyParam 'f' k of
                0x40 -> zeroPad 32 $ integerToBS $ keyParam 'n' k
                _    ->
                    -- From Bernstein's "High-speed high-security signatures"
                    --
                    -- An element (x, y) ∈ E is encoded as a b-bit string (x, y), namely the (b −
                    -- 1)- bit encoding of y followed by a sign bit; the sign bit is 1 iff x is
                    -- negative.  This encoding immediately determines y, and it determines x via
                    -- the equation x = ± √(y² − 1)/(dy² + 1).
                    let y = keyParam 'y' k
                        x = keyParam 'x' k
                        ybs = zeroExtend 32 $ integerToLE y
                        lb = BS.last ybs
                    in if x < 0 then BS.take 31 ybs `BS.snoc` (lb .|. 0x80)
                                else BS.take 31 ybs `BS.snoc` (lb .&. 0x7F)
    in case Ed25519.publicKey n of
            CryptoPassed ed25519 -> Just ed25519
            CryptoFailed _       -> Nothing

ed25519sig :: OpenPGP.Packet -> Maybe Ed25519.Signature
ed25519sig sig =
    let [MPI r,MPI s] = OpenPGP.signature sig
        rbs = zeroPad 32 $ integerToBS r
        sbs = zeroPad 32 $ integerToBS s
    in case Ed25519.signature (rbs <> sbs) of
        CryptoPassed sig25519 -> Just sig25519
        CryptoFailed _        -> Nothing

privateEd25519Key :: OpenPGP.Packet -> Ed25519.SecretKey
privateEd25519Key k = case Ed25519.secretKey $ zeroExtend 32 $ integerToLE (keyParam 'd' k) of
    CryptoPassed ed25519sec -> ed25519sec
    CryptoFailed err        -> error $ "Ed25519.secretKey: " ++ show err

ed25519Verify :: OpenPGP.Packet -> BS.ByteString -> OpenPGP.Packet -> Maybe Bool
ed25519Verify sig over k = do
    let hashbs = hashBySymbol (OpenPGP.hash_algorithm sig) $ BL.fromChunks [over]
    guard $ oid_ed25516 == keyParam 'c' k -- Only Ed25519 curve.
    ek <- ed25519Key k
    esig <- ed25519sig sig
    Just $ Ed25519.verify ek hashbs esig

ed25519Sign :: OpenPGP.Packet -> OpenPGP.HashAlgorithm -> BS.ByteString -> [Integer]
ed25519Sign k hsh dta = [ getBigNum rbs, getBigNum sbs ]
 where
    hashbs = hashBySymbol hsh $ BL.fromChunks [dta]
    sec = privateEd25519Key k
    Just pub = ed25519Key k
    sig = Ed25519.sign sec pub hashbs
    (rbs,sbs) = BS.splitAt 32 $ BA.convert sig

importSecretEd25519 :: Ed25519.SecretKey -> [(Char,MPI)]
importSecretEd25519 k =
    [ ('c', MPI oid_ed25516)
    , ('l', MPI 128)
    , ('n', MPI pub)
    , ('f', MPI 0x40)
    , ('d', MPI sec)
    ]
 where
    pub = getBigNum $ BA.convert $ Ed25519.toPublic k
    sec = getBigNumLE $ BA.convert k

importPublicEd25519 :: Ed25519.PublicKey -> [(Char,MPI)]
importPublicEd25519 k =
    [ ('c', MPI oid_ed25516)
    , ('l', MPI 128)
    , ('n', MPI pub)
    , ('f', MPI 0x40)
    ]
 where
    pub = getBigNum $ BA.convert k