1use crate::get_hash_digest;
2use crate::hash::sha256d_digest::Sha256d;
3use crate::BSVErrors;
4use crate::PrivateKey;
5use crate::RecoveryInfo;
6use crate::Signature;
7use crate::SigningHash;
8use crate::ECDSA;
9use digest::{Digest, FixedOutput};
10use ecdsa::hazmat::{rfc6979_generate_k, SignPrimitive};
11use ecdsa::RecoveryId;
12use elliptic_curve::ops::Reduce;
13use k256::ecdsa::recoverable;
14use k256::FieldBytes;
15use k256::U256;
16use k256::{ecdsa::Signature as SecpSignature, Scalar, SecretKey};
17use rand_core::OsRng;
18use rand_core::RngCore;
19use sha2::Sha256;
20#[cfg(target_arch = "wasm32")]
21use wasm_bindgen::prelude::*;
22#[cfg(target_arch = "wasm32")]
23use wasm_bindgen::throw_str;
24#[cfg(target_arch = "wasm32")]
25use wasm_bindgen::JsValue;
26
27impl ECDSA {
28 fn sign_preimage_deterministic_k<D>(priv_key: &SecretKey, digest: D, reverse_endian_k: bool) -> Result<(SecpSignature, Option<RecoveryId>), BSVErrors>
29 where
30 D: FixedOutput<OutputSize = digest::consts::U32> + digest::BlockInput + Clone + Default + digest::Reset + digest::Update + crate::ReversibleDigest,
31 {
32 let priv_scalar = priv_key.to_nonzero_scalar();
33 let final_digest = digest.finalize_fixed();
34 let k_digest = match reverse_endian_k {
35 true => <Scalar as Reduce<U256>>::from_le_bytes_reduced(final_digest),
36 false => <Scalar as Reduce<U256>>::from_be_bytes_reduced(final_digest),
37 };
38
39 let k = rfc6979_generate_k::<_, D>(&priv_scalar, &k_digest, &[]);
40
41 let msg_scalar = <Scalar as Reduce<U256>>::from_be_bytes_reduced(final_digest);
42 let (signature, recid) = priv_scalar.try_sign_prehashed(**k, msg_scalar)?;
43 let recoverable_id = recid.ok_or_else(ecdsa::Error::new)?.try_into()?;
44 let rec_sig = recoverable::Signature::new(&signature, recoverable_id)?;
45
46 let id = rec_sig.recovery_id();
47 let sig = SecpSignature::from(rec_sig);
48
49 Ok((sig, Some(id.into())))
50 }
51
52 fn sign_preimage_random_k(priv_key: &SecretKey, digest: &[u8], reverse_endian_k: bool, hash_algo: SigningHash) -> Result<(SecpSignature, Option<RecoveryId>), ecdsa::Error> {
53 let mut added_entropy = FieldBytes::default();
54 let rng = &mut OsRng;
55 rng.fill_bytes(&mut added_entropy);
56
57 let priv_scalar = priv_key.to_nonzero_scalar();
58 let k_digest = match reverse_endian_k {
59 true => {
60 let mut reversed_digest = digest.to_vec();
61 reversed_digest.reverse();
62
63 let scalar_uint = U256::from_le_slice(&reversed_digest);
65 Scalar::from_uint_reduced(scalar_uint)
66 }
67 false => Scalar::from_uint_reduced(U256::from_le_slice(digest)),
68 };
69
70 let k = match hash_algo {
71 SigningHash::Sha256 => **rfc6979_generate_k::<_, Sha256>(&priv_scalar, &k_digest, &added_entropy),
72 SigningHash::Sha256d => **rfc6979_generate_k::<_, Sha256d>(&priv_scalar, &k_digest, &added_entropy),
73 };
74
75 let msg_scalar = Scalar::from_uint_reduced(U256::from_le_slice(digest));
76 priv_scalar.try_sign_prehashed(k, msg_scalar)
77 }
78
79 pub(crate) fn sign_with_k_impl(private_key: &PrivateKey, ephemeral_key: &PrivateKey, preimage: &[u8], hash_algo: SigningHash) -> Result<Signature, BSVErrors> {
83 let priv_scalar = *private_key.secret_key.to_nonzero_scalar();
84 let k = *ephemeral_key.secret_key.to_nonzero_scalar();
85 let digest = get_hash_digest(hash_algo, preimage);
86 let digest_finalised = digest.finalize_fixed();
87 let msg_scalar = <Scalar as Reduce<U256>>::from_be_bytes_reduced(digest_finalised);
88 let (signature, recid) = priv_scalar.try_sign_prehashed(k, msg_scalar)?;
89 let recoverable_id = recid.ok_or_else(ecdsa::Error::new)?.try_into()?;
90 let rec_sig = recoverable::Signature::new(&signature, recoverable_id)?;
91 let recovery: Option<RecoveryId> = Some(rec_sig.recovery_id().into());
92 let sig = SecpSignature::from(rec_sig);
93 Ok(Signature {
94 sig,
95 recovery: recovery.map(|x| RecoveryInfo::new(x.is_y_odd(), x.is_x_reduced(), private_key.is_pub_key_compressed)),
96 })
97 }
98
99 pub(crate) fn sign_with_deterministic_k_impl(private_key: &PrivateKey, preimage: &[u8], hash_algo: SigningHash, reverse_k: bool) -> Result<Signature, BSVErrors> {
105 let digest = get_hash_digest(hash_algo, preimage);
106
107 let (sig, recovery) = ECDSA::sign_preimage_deterministic_k(&private_key.secret_key, digest, reverse_k)?;
108
109 Ok(Signature {
110 sig,
111 recovery: recovery.map(|x| RecoveryInfo::new(x.is_y_odd(), x.is_x_reduced(), private_key.is_pub_key_compressed)),
112 })
113 }
114
115 pub(crate) fn sign_with_random_k_impl(private_key: &PrivateKey, preimage: &[u8], hash_algo: SigningHash, reverse_k: bool) -> Result<Signature, BSVErrors> {
121 let digest = get_hash_digest(hash_algo, preimage);
122
123 let (sig, recovery) = ECDSA::sign_preimage_random_k(&private_key.secret_key, digest.finalize().as_slice(), reverse_k, hash_algo)?;
124
125 Ok(Signature {
126 sig,
127 recovery: recovery.map(|x| RecoveryInfo::new(x.is_y_odd(), x.is_x_reduced(), private_key.is_pub_key_compressed)),
128 })
129 }
130}
131
132#[cfg(target_arch = "wasm32")]
133#[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-ecdsa"), wasm_bindgen)]
134impl ECDSA {
135 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-ecdsa"), wasm_bindgen(js_name = signWithRandomK))]
136 pub fn sign_with_random_k(private_key: &PrivateKey, preimage: &[u8], hash_algo: SigningHash, reverse_k: bool) -> Result<Signature, JsValue> {
137 match ECDSA::sign_with_random_k_impl(private_key, preimage, hash_algo, reverse_k) {
138 Ok(v) => Ok(v),
139 Err(e) => Err(JsValue::from_str(&e.to_string())),
140 }
141 }
142
143 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-ecdsa"), wasm_bindgen(js_name = sign))]
144 pub fn sign_with_deterministic_k(private_key: &PrivateKey, preimage: &[u8], hash_algo: SigningHash, reverse_k: bool) -> Result<Signature, JsValue> {
145 match ECDSA::sign_with_deterministic_k_impl(private_key, preimage, hash_algo, reverse_k) {
146 Ok(v) => Ok(v),
147 Err(e) => Err(JsValue::from_str(&e.to_string())),
148 }
149 }
150
151 #[cfg_attr(all(target_arch = "wasm32", feature = "wasm-bindgen-ecdsa"), wasm_bindgen(js_name = signWithK))]
152 pub fn sign_with_k(private_key: &PrivateKey, ephemeral_key: &PrivateKey, preimage: &[u8], hash_algo: SigningHash) -> Result<Signature, JsValue> {
153 match ECDSA::sign_with_k_impl(private_key, ephemeral_key, preimage, hash_algo) {
154 Ok(v) => Ok(v),
155 Err(e) => throw_str(&e.to_string()),
156 }
157 }
158}
159
160#[cfg(not(target_arch = "wasm32"))]
161impl ECDSA {
162 pub fn sign_with_random_k(private_key: &PrivateKey, preimage: &[u8], hash_algo: SigningHash, reverse_k: bool) -> Result<Signature, BSVErrors> {
163 ECDSA::sign_with_random_k_impl(private_key, preimage, hash_algo, reverse_k)
164 }
165
166 pub fn sign_with_deterministic_k(private_key: &PrivateKey, preimage: &[u8], hash_algo: SigningHash, reverse_k: bool) -> Result<Signature, BSVErrors> {
167 ECDSA::sign_with_deterministic_k_impl(private_key, preimage, hash_algo, reverse_k)
168 }
169
170 pub fn sign_with_k(private_key: &PrivateKey, ephemeral_key: &PrivateKey, preimage: &[u8], hash_algo: SigningHash) -> Result<Signature, BSVErrors> {
171 ECDSA::sign_with_k_impl(private_key, ephemeral_key, preimage, hash_algo)
172 }
173}