1use defuse_crypto::{Curve, Secp256k1};
2use impl_tools::autoimpl;
3
4#[cfg_attr(
6 feature = "serde",
7 derive(::serde::Serialize, ::serde::Deserialize),
8 cfg_attr(feature = "abi", derive(::schemars::JsonSchema))
9)]
10#[derive(Debug, Clone)]
11pub struct Tip191Payload(pub String);
12
13impl defuse_crypto::Payload for Tip191Payload {
14 #[inline]
15 fn hash(&self) -> defuse_crypto::CryptoHash {
16 use defuse_digest::{Digest, sha3::Keccak256};
17
18 Keccak256::new_with_prefix(b"\x19TRON Signed Message:\n")
20 .chain_update(self.0.len().to_string())
21 .chain_update(self.0.as_bytes())
22 .finalize()
23 .into()
24 }
25}
26
27#[cfg_attr(
28 feature = "serde",
29 ::cfg_eval::cfg_eval,
30 ::serde_with::serde_as,
31 derive(::serde::Serialize, ::serde::Deserialize),
32 cfg_attr(feature = "abi", derive(::schemars::JsonSchema))
33)]
34#[autoimpl(Deref using self.payload)]
35#[derive(Debug, Clone)]
36pub struct SignedTip191Payload {
37 pub payload: Tip191Payload,
38
39 #[cfg_attr(
42 feature = "serde",
43 serde_as(as = "defuse_crypto::serde::AsCurve<Secp256k1>")
44 )]
45 pub signature: <Secp256k1 as Curve>::Signature,
46}
47
48impl defuse_crypto::Payload for SignedTip191Payload {
49 #[inline]
50 fn hash(&self) -> defuse_crypto::CryptoHash {
51 self.payload.hash()
52 }
53}
54
55#[cfg(any(test, feature = "near-contract"))]
56impl defuse_crypto::SignedPayload for SignedTip191Payload {
57 type PublicKey = <Secp256k1 as Curve>::PublicKey;
58
59 #[inline]
60 fn verify(&self) -> Option<Self::PublicKey> {
61 use defuse_crypto::{Payload, VerifiableCurve};
62 Secp256k1::verify(&self.signature, &self.payload.hash(), &())
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use defuse_crypto::SignedPayload;
70 use hex_literal::hex;
71
72 const fn fix_v_in_signature(mut sig: [u8; 65]) -> [u8; 65] {
73 if *sig.last().unwrap() >= 27 {
74 *sig.last_mut().unwrap() -= 27;
77 }
78 sig
79 }
80
81 const REFERENCE_MESSAGE: &str = "Hello, TRON!";
92 const INVALID_REFERENCE_MESSAGE: &str = "this is not TRON reference input message";
93 const REFERENCE_SIGNATURE: [u8; 65] = hex!(
94 "eea1651a60600ec4d9c45e8ae81da1a78377f789f0ac2019de66ad943459913015ef9256809ee0e6bb76e303a0b4802e475c1d26ade5d585292b80c9fe9cb10c1c"
95 );
96 const INVALID_REFERENCE_SIGNATURE: [u8; 65] = hex!(
97 "0000000011111111000000001110111110000000011111111e66ad943459913015ef9256809ee0e6bb76e303a0b4802e475c1d26ade5d585292b80c9fe9cb10c1c"
98 );
99 const REFERENCE_PUBKEY: [u8; 64] = hex!(
100 "85a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b801f41e9438155eb53a5e20f748640093bb42ae3aeca035f7b7fd7a1a21f22f68"
101 );
102
103 #[test]
104 fn test_reference_signature_verification_works() {
105 assert_eq!(
106 SignedTip191Payload {
107 payload: Tip191Payload(REFERENCE_MESSAGE.to_string()),
108 signature: fix_v_in_signature(REFERENCE_SIGNATURE),
109 }
110 .verify(),
111 Some(REFERENCE_PUBKEY)
112 );
113 }
114
115 #[test]
116 fn test_invalid_reference_message_verification_fails() {
117 assert_ne!(
118 SignedTip191Payload {
119 payload: Tip191Payload(INVALID_REFERENCE_MESSAGE.to_string()),
120 signature: fix_v_in_signature(REFERENCE_SIGNATURE),
121 }
122 .verify(),
123 Some(REFERENCE_PUBKEY)
124 );
125 }
126
127 #[test]
128 fn test_invalid_reference_signature_verification_fails() {
129 assert_ne!(
130 SignedTip191Payload {
131 payload: Tip191Payload(REFERENCE_MESSAGE.to_string()),
132 signature: fix_v_in_signature(INVALID_REFERENCE_SIGNATURE),
133 }
134 .verify(),
135 Some(REFERENCE_PUBKEY)
136 );
137 }
138}