-
Notifications
You must be signed in to change notification settings - Fork 721
/
Key.hs
281 lines (243 loc) · 9.95 KB
/
Key.hs
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE UndecidableInstances #-}
-- | Shelley CLI option data types and functions for cryptographic keys.
module Cardano.CLI.Shelley.Key
( VerificationKeyOrFile (..)
, InputFormat (..)
, InputDecodeError (..)
, deserialiseInput
, deserialiseInputAnyOf
, renderInputDecodeError
, OutputDirection (..)
, writeOutputBech32
, readKeyFileAnyOf
, readKeyFileTextEnvelope
, readSigningKeyFile
, readSigningKeyFileAnyOf
, readVerificationKeyOrFile
, readVerificationKeyOrTextEnvFile
, VerificationKeyTextOrFile (..)
, VerificationKeyTextOrFileError (..)
, readVerificationKeyTextOrFileAnyOf
, renderVerificationKeyTextOrFileError
, VerificationKeyOrHashOrFile (..)
, readVerificationKeyOrHashOrFile
, readVerificationKeyOrHashOrTextEnvFile
, PaymentVerifier(..)
, StakeVerifier(..)
, generateKeyPair
) where
import Control.Monad.IO.Class (MonadIO (..))
import Control.Monad.Trans.Except (runExceptT)
import Control.Monad.Trans.Except.Extra (handleIOExceptT)
import Data.Bifunctor (Bifunctor (..))
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC
import qualified Data.List.NonEmpty as NE
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
import Cardano.Api
import Cardano.Api.DeserialiseAnyOf
import Cardano.CLI.Types
------------------------------------------------------------------------------
-- Formatted/encoded input deserialisation
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- Formatted/encoded output serialisation
------------------------------------------------------------------------------
-- | Where to write some output.
data OutputDirection
= OutputDirectionStdout
-- ^ Write output to @stdout@.
| OutputDirectionFile !FilePath
-- ^ Write output to a provided file.
deriving (Eq, Show)
writeOutputBech32
:: SerialiseAsBech32 a
=> OutputDirection
-> a
-> IO (Either (FileError ()) ())
writeOutputBech32 outputDirection a =
case outputDirection of
OutputDirectionStdout -> Right <$> BSC.putStrLn outputBs
OutputDirectionFile fp ->
runExceptT $ handleIOExceptT (FileIOError fp) $
BS.writeFile fp outputBs
where
outputBs :: ByteString
outputBs = Text.encodeUtf8 (serialiseToBech32 a)
------------------------------------------------------------------------------
-- Signing key deserialisation
------------------------------------------------------------------------------
-- | Read a signing key from a file.
--
-- The contents of the file can either be Bech32-encoded, hex-encoded, or in
-- the text envelope format.
readSigningKeyFile
:: forall keyrole.
( HasTextEnvelope (SigningKey keyrole)
, SerialiseAsBech32 (SigningKey keyrole)
)
=> AsType keyrole
-> SigningKeyFile
-> IO (Either (FileError InputDecodeError) (SigningKey keyrole))
readSigningKeyFile asType (SigningKeyFile fp) =
readFileAnyOfInputFormats
(AsSigningKey asType)
(NE.fromList [InputFormatBech32, InputFormatHex, InputFormatTextEnvelope])
fp
-- | Read a signing key from a file given that it is one of the provided types
-- of signing key.
--
-- The contents of the file can either be Bech32-encoded or in the text
-- envelope format.
readSigningKeyFileAnyOf
:: forall b.
[FromSomeType SerialiseAsBech32 b]
-> [FromSomeType HasTextEnvelope b]
-> SigningKeyFile
-> IO (Either (FileError InputDecodeError) b)
readSigningKeyFileAnyOf bech32Types textEnvTypes (SigningKeyFile fp) =
readFileBech32OrTextEnvAnyOf bech32Types textEnvTypes fp
------------------------------------------------------------------------------
-- Verification key deserialisation
------------------------------------------------------------------------------
-- | Either a verification key or path to a verification key file.
data VerificationKeyOrFile keyrole
= VerificationKeyValue !(VerificationKey keyrole)
-- ^ A verification key.
| VerificationKeyFilePath !VerificationKeyFile
-- ^ A path to a verification key file.
-- Note that this file hasn't been validated at all (whether it exists,
-- contains a key of the correct type, etc.)
deriving instance Show (VerificationKey keyrole)
=> Show (VerificationKeyOrFile keyrole)
deriving instance Eq (VerificationKey keyrole)
=> Eq (VerificationKeyOrFile keyrole)
-- | Read a verification key or verification key file and return a
-- verification key.
--
-- If a filepath is provided, the file can either be formatted as Bech32, hex,
-- or text envelope.
readVerificationKeyOrFile
:: ( HasTextEnvelope (VerificationKey keyrole)
, SerialiseAsBech32 (VerificationKey keyrole)
)
=> AsType keyrole
-> VerificationKeyOrFile keyrole
-> IO (Either (FileError InputDecodeError) (VerificationKey keyrole))
readVerificationKeyOrFile asType verKeyOrFile =
case verKeyOrFile of
VerificationKeyValue vk -> pure (Right vk)
VerificationKeyFilePath (VerificationKeyFile fp) ->
readFileAnyOfInputFormats
(AsVerificationKey asType)
(NE.fromList [InputFormatBech32, InputFormatHex, InputFormatTextEnvelope])
fp
-- | Read a verification key or verification key file and return a
-- verification key.
--
-- If a filepath is provided, it will be interpreted as a text envelope
-- formatted file.
readVerificationKeyOrTextEnvFile
:: HasTextEnvelope (VerificationKey keyrole)
=> AsType keyrole
-> VerificationKeyOrFile keyrole
-> IO (Either (FileError InputDecodeError) (VerificationKey keyrole))
readVerificationKeyOrTextEnvFile asType verKeyOrFile =
case verKeyOrFile of
VerificationKeyValue vk -> pure (Right vk)
VerificationKeyFilePath (VerificationKeyFile fp) ->
readFileTextEnvelope' (AsVerificationKey asType) fp
data PaymentVerifier
= PaymentVerifierKey VerificationKeyTextOrFile
| PaymentVerifierScriptFile ScriptFile
deriving (Eq, Show)
data StakeVerifier
= StakeVerifierKey (VerificationKeyOrFile StakeKey)
| StakeVerifierScriptFile ScriptFile
deriving (Eq, Show)
-- | Either an unvalidated text representation of a verification key or a path
-- to a verification key file.
data VerificationKeyTextOrFile
= VktofVerificationKeyText !Text
| VktofVerificationKeyFile !VerificationKeyFile
deriving (Eq, Show)
-- | An error in deserialising a 'VerificationKeyTextOrFile' to a
-- 'VerificationKey'.
data VerificationKeyTextOrFileError
= VerificationKeyTextError !InputDecodeError
| VerificationKeyFileError !(FileError InputDecodeError)
deriving Show
-- | Render an error message for a 'VerificationKeyTextOrFileError'.
renderVerificationKeyTextOrFileError :: VerificationKeyTextOrFileError -> Text
renderVerificationKeyTextOrFileError vkTextOrFileErr =
case vkTextOrFileErr of
VerificationKeyTextError err -> renderInputDecodeError err
VerificationKeyFileError err -> Text.pack (displayError err)
-- | Deserialise a verification key from text or a verification key file.
-- If a filepath is provided, the file can either be formatted as Bech32, hex,
-- or text envelope.
readVerificationKeyTextOrFileAnyOf
:: VerificationKeyTextOrFile
-> IO (Either VerificationKeyTextOrFileError SomeAddressVerificationKey)
readVerificationKeyTextOrFileAnyOf verKeyTextOrFile =
case verKeyTextOrFile of
VktofVerificationKeyText vkText ->
pure $ first VerificationKeyTextError $
deserialiseAnyVerificationKey (Text.encodeUtf8 vkText)
VktofVerificationKeyFile (VerificationKeyFile fp) -> do
vkBs <- liftIO $ BS.readFile fp
pure $ first VerificationKeyTextError $
deserialiseAnyVerificationKey vkBs
-- | Verification key, verification key hash, or path to a verification key
-- file.
data VerificationKeyOrHashOrFile keyrole
= VerificationKeyOrFile !(VerificationKeyOrFile keyrole)
-- ^ Either a verification key or path to a verification key file.
| VerificationKeyHash !(Hash keyrole)
-- ^ A verification key hash.
deriving instance (Show (VerificationKeyOrFile keyrole), Show (Hash keyrole))
=> Show (VerificationKeyOrHashOrFile keyrole)
deriving instance (Eq (VerificationKeyOrFile keyrole), Eq (Hash keyrole))
=> Eq (VerificationKeyOrHashOrFile keyrole)
-- | Read a verification key or verification key hash or verification key file
-- and return a verification key hash.
--
-- If a filepath is provided, the file can either be formatted as Bech32, hex,
-- or text envelope.
readVerificationKeyOrHashOrFile
:: (Key keyrole, SerialiseAsBech32 (VerificationKey keyrole))
=> AsType keyrole
-> VerificationKeyOrHashOrFile keyrole
-> IO (Either (FileError InputDecodeError) (Hash keyrole))
readVerificationKeyOrHashOrFile asType verKeyOrHashOrFile =
case verKeyOrHashOrFile of
VerificationKeyOrFile vkOrFile -> do
eitherVk <- readVerificationKeyOrFile asType vkOrFile
pure (verificationKeyHash <$> eitherVk)
VerificationKeyHash vkHash -> pure (Right vkHash)
-- | Read a verification key or verification key hash or verification key file
-- and return a verification key hash.
--
-- If a filepath is provided, it will be interpreted as a text envelope
-- formatted file.
readVerificationKeyOrHashOrTextEnvFile
:: Key keyrole
=> AsType keyrole
-> VerificationKeyOrHashOrFile keyrole
-> IO (Either (FileError InputDecodeError) (Hash keyrole))
readVerificationKeyOrHashOrTextEnvFile asType verKeyOrHashOrFile =
case verKeyOrHashOrFile of
VerificationKeyOrFile vkOrFile -> do
eitherVk <- readVerificationKeyOrTextEnvFile asType vkOrFile
pure (verificationKeyHash <$> eitherVk)
VerificationKeyHash vkHash -> pure (Right vkHash)
generateKeyPair :: Key keyrole => AsType keyrole -> IO (VerificationKey keyrole, SigningKey keyrole)
generateKeyPair asType = do
skey <- generateSigningKey asType
return (getVerificationKey skey, skey)