1#![deny(missing_docs)]
12#![deny(unsafe_code)]
13
14use anyhow::ensure;
15use clap::ValueEnum;
16use essential_types::{convert::word_4_from_u8_32, Hash, Word};
17use serde::{Deserialize, Serialize};
18use sha2::Digest;
19
20pub use ed25519_dalek;
21pub use secp256k1;
22
23#[derive(ValueEnum, Clone, Copy, Debug)]
24pub enum Encoding {
26 Bytes,
30 Hex,
32 HexUpper,
34 Base64,
36 Base64UrlNoPad,
40}
41
42#[derive(ValueEnum, Clone, Copy, Debug)]
43pub enum Padding {
45 Start,
47 End,
49}
50
51#[derive(Clone, Copy)]
52pub enum Key {
54 Secp256k1(secp256k1::SecretKey),
56 Ed25519(ed25519_dalek::SecretKey),
58}
59
60#[derive(Clone, Copy, Debug)]
61pub enum PublicKey {
63 Secp256k1(secp256k1::PublicKey),
65 Ed25519(ed25519_dalek::VerifyingKey),
67}
68
69#[derive(Clone, Debug, PartialEq, Eq)]
70pub enum Signature {
72 Secp256k1(secp256k1::ecdsa::RecoverableSignature),
74 Ed25519(ed25519_dalek::Signature),
76}
77
78pub fn sign_postcard<T: Serialize>(data: &T, private_key: &Key) -> anyhow::Result<Signature> {
82 let data = postcard_bytes(data)?;
83 let hash = hash_bytes(&data)?;
84 sign_hash(hash, private_key)
85}
86
87pub fn sign_postcard_with_padding<T: Serialize>(
91 data: &T,
92 padding: Padding,
93 private_key: &Key,
94) -> anyhow::Result<Signature> {
95 let data = postcard_bytes_with_padding(data, padding)?;
96 let hash = hash_bytes(&data)?;
97 sign_hash(hash, private_key)
98}
99
100pub fn sign_words(data: &[Word], private_key: &Key) -> anyhow::Result<Signature> {
102 let hash = hash_words(data);
103 sign_hash(hash, private_key)
104}
105
106pub fn sign_bytes_with_padding(
110 data: Vec<u8>,
111 padding: Padding,
112 private_key: &Key,
113) -> anyhow::Result<Signature> {
114 let data = align_to_word(data, padding);
115 let hash = hash_bytes(&data)?;
116 sign_hash(hash, private_key)
117}
118
119pub fn sign_aligned_bytes(data: &[u8], private_key: &Key) -> anyhow::Result<Signature> {
123 ensure!(is_word_aligned(data), "Data is not word aligned");
124 let hash = hash_bytes(data)?;
125 sign_hash(hash, private_key)
126}
127
128pub fn sign_bytes_unchecked(data: &[u8], private_key: &Key) -> anyhow::Result<Signature> {
132 let hash = hash_bytes(data)?;
133 sign_hash(hash, private_key)
134}
135
136pub fn sign_hash(hash: Hash, private_key: &Key) -> anyhow::Result<Signature> {
138 match private_key {
139 Key::Secp256k1(private_key) => {
140 let sig = essential_sign::sign_hash(hash, private_key);
141 let sig = secp256k1::ecdsa::RecoverableSignature::from_compact(
142 &sig.0,
143 secp256k1::ecdsa::RecoveryId::try_from(sig.1 as i32)?,
144 )?;
145 Ok(Signature::Secp256k1(sig))
146 }
147 Key::Ed25519(_) => todo!(),
148 }
149}
150
151pub fn read_file(path: &std::path::Path) -> anyhow::Result<Vec<u8>> {
153 use std::io::Read;
154 let mut file = std::fs::File::open(path)?;
155 let mut data = Vec::new();
156 file.read_to_end(&mut data)?;
157 Ok(data)
158}
159
160#[derive(Deserialize, Serialize)]
161struct Bytes(#[serde(with = "serde_bytes")] Vec<u8>);
162
163pub fn decode_str(data: String, encoding: Encoding) -> anyhow::Result<Vec<u8>> {
165 match encoding {
166 Encoding::Bytes => {
167 let Bytes(data) = serde_json::from_str(&data)?;
168 Ok(data)
169 }
170 Encoding::Hex | Encoding::HexUpper => Ok(hex::decode(data)?),
171 Encoding::Base64 => {
172 use base64::engine::general_purpose::STANDARD;
173 use base64::Engine;
174 Ok(STANDARD.decode(data.as_bytes())?)
175 }
176 Encoding::Base64UrlNoPad => {
177 use base64::engine::general_purpose::URL_SAFE_NO_PAD;
178 use base64::Engine;
179 Ok(URL_SAFE_NO_PAD.decode(data.as_bytes())?)
180 }
181 }
182}
183
184pub fn encode_str(data: Vec<u8>, encoding: Encoding) -> anyhow::Result<String> {
186 match encoding {
187 Encoding::Bytes => Ok(serde_json::to_string(&Bytes(data))?),
188 Encoding::Hex => Ok(hex::encode(data)),
189 Encoding::HexUpper => Ok(hex::encode_upper(data)),
190 Encoding::Base64 => {
191 use base64::engine::general_purpose::STANDARD;
192 use base64::Engine;
193 Ok(STANDARD.encode(data))
194 }
195 Encoding::Base64UrlNoPad => {
196 use base64::engine::general_purpose::URL_SAFE_NO_PAD;
197 use base64::Engine;
198 Ok(URL_SAFE_NO_PAD.encode(data))
199 }
200 }
201}
202
203pub fn into_words(data: Vec<u8>, padding: Padding) -> Vec<Word> {
205 let data = align_to_word(data, padding);
206 data.chunks(8)
207 .map(|chunk| {
208 essential_types::convert::word_from_bytes(
209 chunk.try_into().expect("This can't fail because of chunks"),
210 )
211 })
212 .collect::<Vec<Word>>()
213}
214
215pub fn align_to_word(data: Vec<u8>, padding: Padding) -> Vec<u8> {
218 if is_word_aligned(&data) {
219 data
220 } else {
221 pad_bytes(data, padding)
222 }
223}
224
225pub fn is_word_aligned(data: &[u8]) -> bool {
227 data.len() % 8 == 0
228}
229
230pub fn pad_bytes(mut data: Vec<u8>, padding: Padding) -> Vec<u8> {
234 match padding {
235 Padding::Start => {
236 let len = data.len();
237 let pad = 8 - len % 8;
238 let mut padded = vec![0; pad];
239 padded.extend(data);
240 padded
241 }
242 Padding::End => {
243 let len = data.len();
244 let pad = 8 - len % 8;
245 data.extend(std::iter::repeat(0).take(pad));
246 data
247 }
248 }
249}
250
251pub fn hash_bytes(data: &[u8]) -> anyhow::Result<Hash> {
255 let mut hasher = <sha2::Sha256 as sha2::Digest>::new();
256 hasher.update(data);
257 Ok(hasher.finalize().into())
258}
259
260pub fn hash_words(data: &[Word]) -> Hash {
262 essential_hash::hash_words(data)
263}
264
265pub fn to_essential_signature(
267 sig: secp256k1::ecdsa::RecoverableSignature,
268) -> anyhow::Result<essential_types::Signature> {
269 let (rec_id, data) = sig.serialize_compact();
270 Ok(essential_types::Signature(
271 data,
272 i32::from(rec_id).try_into()?,
273 ))
274}
275
276pub fn signature_to_aligned_bytes(sig: &Signature) -> Vec<u8> {
280 match sig {
281 Signature::Secp256k1(sig) => essential_sign::encode::signature_as_bytes(sig).to_vec(),
282 Signature::Ed25519(sig) => sig.to_bytes().to_vec(),
283 }
284}
285
286pub fn signature_to_bytes(sig: &Signature) -> anyhow::Result<Vec<u8>> {
290 match sig {
291 Signature::Secp256k1(sig) => {
292 let (rec_id, data) = sig.serialize_compact();
293 let mut bytes = data.to_vec();
294 let rec_id: i32 = rec_id.into();
295 let rec_id: u8 = rec_id.try_into()?;
296 bytes.push(rec_id);
297 Ok(bytes)
298 }
299 Signature::Ed25519(sig) => Ok(sig.to_bytes().to_vec()),
300 }
301}
302
303pub fn signed_set_to_bytes(
307 signed_set: &essential_types::contract::SignedContract,
308) -> anyhow::Result<Vec<u8>> {
309 Ok(serde_json::to_vec(signed_set)?)
310}
311
312pub fn signature_to_words(sig: &Signature) -> Vec<Word> {
314 match sig {
315 Signature::Secp256k1(sig) => essential_sign::encode::signature(sig).to_vec(),
316 Signature::Ed25519(_) => todo!(),
317 }
318}
319
320pub fn public_key_to_words(key: &PublicKey) -> Vec<Word> {
324 match key {
325 PublicKey::Secp256k1(key) => essential_sign::encode::public_key(key).to_vec(),
326 PublicKey::Ed25519(key) => word_4_from_u8_32(key.to_bytes()).to_vec(),
327 }
328}
329
330pub fn postcard_bytes_with_padding<T: Serialize>(
332 data: &T,
333 padding: Padding,
334) -> anyhow::Result<Vec<u8>> {
335 let data = postcard::to_allocvec(data)?;
336 Ok(align_to_word(data, padding))
337}
338
339pub fn postcard_bytes<T: Serialize>(data: &T) -> anyhow::Result<Vec<u8>> {
341 Ok(postcard::to_allocvec(data)?)
342}
343
344pub fn public_key(private_key: &Key) -> PublicKey {
346 match private_key {
347 Key::Secp256k1(key) => {
348 let secp = secp256k1::Secp256k1::new();
349 PublicKey::Secp256k1(key.public_key(&secp))
350 }
351 Key::Ed25519(key) => {
352 let key = ed25519_dalek::SigningKey::from_bytes(key);
353 PublicKey::Ed25519(key.verifying_key())
354 }
355 }
356}