Skip to main content

xrpl_common_stdlib/
crypto.rs

1use crate::host;
2use crate::host::error_codes::match_result_code_with_expected_bytes;
3use crate::host::{Error, Result};
4use crate::types::message::Message;
5use crate::types::public_key::{PUBLIC_KEY_BUFFER_SIZE, PublicKey};
6use crate::types::signature::Signature;
7
8/// SHA-512Half: SHA-512 of `data`, truncated to the first 32 bytes.
9///
10/// `data` may be 0..=1024 bytes (`maxWasmParamLength`); the host returns
11/// `DataFieldTooLarge` for larger input.
12pub fn sha512_half(data: &[u8]) -> Result<[u8; 32]> {
13    let mut out = [0u8; 32];
14    let rescode = unsafe { host::sha512_half(data.as_ptr(), data.len(), out.as_mut_ptr(), 32) };
15    match_result_code_with_expected_bytes(rescode, 32, || out)
16}
17
18/// Verify `sig` over `msg` for public key `key`.
19///
20/// `msg` and `sig` are wrapped in the distinct [`Message`] and [`Signature`] newtypes so the
21/// compiler rejects a caller that swaps them; `&PublicKey` (33 bytes) enforces the key size at
22/// the call site.
23/// - secp256k1 keys (0x02/0x03): the host pre-hashes `msg` with SHA-512Half before ECDSA verify.
24/// - Ed25519 keys (0xED): the host verifies the raw `msg` directly (no pre-hash), stripping the
25///   0xED prefix; a non-canonical signature returns `Ok(false)`.
26///
27/// An empty `msg` or empty `sig` is not an error — the host returns `Ok(false)`.
28///
29/// Errors: `InvalidParams` if the key is malformed (not 33 bytes / bad prefix);
30/// `DataFieldTooLarge` if any parameter exceeds 1024 bytes.
31pub fn check_sig(msg: Message, sig: Signature, key: &PublicKey) -> Result<bool> {
32    let rescode = unsafe {
33        host::check_sig(
34            msg.0.as_ptr(),
35            msg.0.len(),
36            sig.0.as_ptr(),
37            sig.0.len(),
38            key.0.as_ptr(),
39            PUBLIC_KEY_BUFFER_SIZE,
40        )
41    };
42    match rescode {
43        0 => Result::Ok(false),
44        1 => Result::Ok(true),
45        code if code < 0 => Result::Err(Error::from_code(code)),
46        code => panic!("internal invariant violated: host returned unexpected value {code}"),
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    use crate::host::error_codes::{DATA_FIELD_TOO_LARGE, INVALID_PARAMS};
54    use crate::host::host_bindings_trait::MockHostBindings;
55    use crate::host::setup_mock;
56
57    fn write_digest(ptr: *mut u8, fill: u8) {
58        unsafe {
59            for i in 0..32 {
60                *ptr.add(i) = fill;
61            }
62        }
63    }
64
65    // ---- sha512_half ----
66
67    #[test]
68    fn test_sha512_half_success() {
69        let mut mock = MockHostBindings::new();
70        mock.expect_sha512_half()
71            .times(1)
72            .returning(|_data, _dlen, out_ptr, _olen| {
73                write_digest(out_ptr, 0xCD);
74                32
75            });
76        let _guard = setup_mock(mock);
77
78        let result = sha512_half(b"hello world");
79        assert!(result.is_ok());
80        assert_eq!(result.unwrap(), [0xCD; 32]);
81    }
82
83    #[test]
84    #[should_panic(expected = "internal invariant violated")]
85    fn test_sha512_half_wrong_byte_count() {
86        let mut mock = MockHostBindings::new();
87        mock.expect_sha512_half()
88            .times(1)
89            .returning(|_, _, _, _| 16); // host returns wrong (non-32) byte count
90        let _guard = setup_mock(mock);
91
92        let _ = sha512_half(b"hello");
93    }
94
95    #[test]
96    fn test_sha512_half_oversized() {
97        let mut mock = MockHostBindings::new();
98        mock.expect_sha512_half()
99            .times(1)
100            .returning(|_, _, _, _| DATA_FIELD_TOO_LARGE);
101        let _guard = setup_mock(mock);
102
103        let result = sha512_half(&[0u8; 1025]);
104        assert!(result.is_err());
105        assert_eq!(result.err().unwrap().code(), DATA_FIELD_TOO_LARGE);
106    }
107
108    // ---- check_sig ----
109
110    const PUBKEY_BYTES: [u8; PUBLIC_KEY_BUFFER_SIZE] = [
111        0x02, 0xC7, 0x38, 0x7F, 0xFC, 0x25, 0xC1, 0x56, 0xCA, 0x7F, 0x8A, 0x6D, 0x76, 0x0C, 0x8D,
112        0x01, 0xEF, 0x64, 0x2C, 0xEE, 0x9C, 0xE4, 0x68, 0x0C, 0x33, 0xFF, 0xB3, 0xFF, 0x39, 0xAF,
113        0xEC, 0xFE, 0x70,
114    ];
115
116    #[test]
117    fn test_check_sig_valid() {
118        let mut mock = MockHostBindings::new();
119        mock.expect_check_sig()
120            .times(1)
121            .returning(|_, _, _, _, _, _| 1);
122        let _guard = setup_mock(mock);
123
124        let key = PublicKey::from(PUBKEY_BYTES);
125        let result = check_sig(Message(b"message"), Signature(b"signature"), &key);
126        assert!(result.unwrap());
127    }
128
129    #[test]
130    fn test_check_sig_invalid() {
131        let mut mock = MockHostBindings::new();
132        mock.expect_check_sig()
133            .times(1)
134            .returning(|_, _, _, _, _, _| 0);
135        let _guard = setup_mock(mock);
136
137        let key = PublicKey::from(PUBKEY_BYTES);
138        let result = check_sig(Message(b"message"), Signature(b"signature"), &key);
139        assert!(!result.unwrap());
140    }
141
142    #[test]
143    fn test_check_sig_bad_pubkey() {
144        let mut mock = MockHostBindings::new();
145        mock.expect_check_sig()
146            .times(1)
147            .returning(|_, _, _, _, _, _| INVALID_PARAMS);
148        let _guard = setup_mock(mock);
149
150        let key = PublicKey::from(PUBKEY_BYTES);
151        let result = check_sig(Message(b"message"), Signature(b"signature"), &key);
152        assert!(result.is_err());
153        assert_eq!(result.err().unwrap().code(), INVALID_PARAMS);
154    }
155
156    #[test]
157    fn test_check_sig_oversized() {
158        let mut mock = MockHostBindings::new();
159        mock.expect_check_sig()
160            .times(1)
161            .returning(|_, _, _, _, _, _| DATA_FIELD_TOO_LARGE);
162        let _guard = setup_mock(mock);
163
164        let key = PublicKey::from(PUBKEY_BYTES);
165        let result = check_sig(Message(&[0u8; 1025]), Signature(b"signature"), &key);
166        assert!(result.is_err());
167        assert_eq!(result.err().unwrap().code(), DATA_FIELD_TOO_LARGE);
168    }
169
170    #[test]
171    #[should_panic(expected = "internal invariant violated")]
172    fn test_check_sig_unexpected_positive() {
173        let mut mock = MockHostBindings::new();
174        mock.expect_check_sig()
175            .times(1)
176            .returning(|_, _, _, _, _, _| 2); // host returns 2 — only 0 and 1 are valid
177        let _guard = setup_mock(mock);
178
179        let key = PublicKey::from(PUBKEY_BYTES);
180        let _ = check_sig(Message(b"m"), Signature(b"s"), &key);
181    }
182}