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 Tip191Payload {
14 #[inline]
15 pub fn prehash(&self) -> Vec<u8> {
16 let data = self.0.as_bytes();
17 [
18 format!("\x19TRON Signed Message:\n{}", data.len()).as_bytes(),
20 data,
21 ]
22 .concat()
23 }
24}
25
26#[cfg(any(test, feature = "sha3", feature = "near-contract"))]
27impl defuse_crypto::Payload for Tip191Payload {
28 #[inline]
29 fn hash(&self) -> defuse_crypto::CryptoHash {
30 use defuse_digest::{Digest, Keccak256};
31 Keccak256::digest(self.prehash()).into()
32 }
33}
34
35#[cfg_attr(
36 feature = "serde",
37 ::cfg_eval::cfg_eval,
38 ::serde_with::serde_as,
39 derive(::serde::Serialize, ::serde::Deserialize),
40 cfg_attr(feature = "abi", derive(::schemars::JsonSchema))
41)]
42#[autoimpl(Deref using self.payload)]
43#[derive(Debug, Clone)]
44pub struct SignedTip191Payload {
45 pub payload: Tip191Payload,
46
47 #[cfg_attr(
50 feature = "serde",
51 serde_as(as = "defuse_crypto::serde::AsCurve<Secp256k1>")
52 )]
53 pub signature: <Secp256k1 as Curve>::Signature,
54}
55
56#[cfg(any(test, feature = "sha3", feature = "near-contract"))]
57impl defuse_crypto::Payload for SignedTip191Payload {
58 #[inline]
59 fn hash(&self) -> defuse_crypto::CryptoHash {
60 self.payload.hash()
61 }
62}
63
64#[cfg(any(test, feature = "near-contract"))]
65impl defuse_crypto::SignedPayload for SignedTip191Payload {
66 type PublicKey = <Secp256k1 as Curve>::PublicKey;
67
68 #[inline]
69 fn verify(&self) -> Option<Self::PublicKey> {
70 use defuse_crypto::{Payload, VerifiableCurve};
71 Secp256k1::verify(&self.signature, &self.payload.hash(), &())
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use defuse_crypto::SignedPayload;
79 use hex_literal::hex;
80
81 const fn fix_v_in_signature(mut sig: [u8; 65]) -> [u8; 65] {
82 if *sig.last().unwrap() >= 27 {
83 *sig.last_mut().unwrap() -= 27;
86 }
87 sig
88 }
89
90 const REFERENCE_MESSAGE: &str = "Hello, TRON!";
101 const INVALID_REFERENCE_MESSAGE: &str = "this is not TRON reference input message";
102 const REFERENCE_SIGNATURE: [u8; 65] = hex!(
103 "eea1651a60600ec4d9c45e8ae81da1a78377f789f0ac2019de66ad943459913015ef9256809ee0e6bb76e303a0b4802e475c1d26ade5d585292b80c9fe9cb10c1c"
104 );
105 const INVALID_REFERENCE_SIGNATURE: [u8; 65] = hex!(
106 "0000000011111111000000001110111110000000011111111e66ad943459913015ef9256809ee0e6bb76e303a0b4802e475c1d26ade5d585292b80c9fe9cb10c1c"
107 );
108 const REFERENCE_PUBKEY: [u8; 64] = hex!(
109 "85a66984273f338ce4ef7b85e5430b008307e8591bb7c1b980852cf6423770b801f41e9438155eb53a5e20f748640093bb42ae3aeca035f7b7fd7a1a21f22f68"
110 );
111
112 #[test]
113 fn test_reference_signature_verification_works() {
114 assert_eq!(
115 SignedTip191Payload {
116 payload: Tip191Payload(REFERENCE_MESSAGE.to_string()),
117 signature: fix_v_in_signature(REFERENCE_SIGNATURE),
118 }
119 .verify(),
120 Some(REFERENCE_PUBKEY)
121 );
122 }
123
124 #[test]
125 fn test_invalid_reference_message_verification_fails() {
126 assert_ne!(
127 SignedTip191Payload {
128 payload: Tip191Payload(INVALID_REFERENCE_MESSAGE.to_string()),
129 signature: fix_v_in_signature(REFERENCE_SIGNATURE),
130 }
131 .verify(),
132 Some(REFERENCE_PUBKEY)
133 );
134 }
135
136 #[test]
137 fn test_invalid_reference_signature_verification_fails() {
138 assert_ne!(
139 SignedTip191Payload {
140 payload: Tip191Payload(REFERENCE_MESSAGE.to_string()),
141 signature: fix_v_in_signature(INVALID_REFERENCE_SIGNATURE),
142 }
143 .verify(),
144 Some(REFERENCE_PUBKEY)
145 );
146 }
147}