// SPDX-License-Identifier: CC0-1.0 //! # secp256k1 no-std test. //! This binary is a short smallest rust code to produce a working binary *without libstd*. //! This gives us 2 things: //! 1. Test that the parts of the code that should work in a no-std enviroment actually work. Note that this is not a comprehensive list. //! 2. Test that we don't accidentally import libstd into `secp256k1`. //! //! The first is tested using the following command `cargo run --release | grep -q "Verified Successfully"`. //! (Making sure that it successfully printed that. i.e. it didn't abort before that). //! //! The second is tested by the fact that it compiles. if we accidentally link against libstd we should see the following error: //! `error[E0152]: duplicate lang item found`. //! Example: //! ``` //! error[E0152]: duplicate lang item found: `eh_personality`. //! --> src/main.rs:37:1 //! | //! 37 | pub extern "C" fn rust_eh_personality() {} //! | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //! | //! = note: first defined in crate `panic_unwind` (which `std` depends on). //! ``` //! //! Notes: //! * Requires `panic=abort` and `--release` to not depend on libunwind(which is provided usually by libstd) https://github.com/rust-lang/rust/issues/47493 //! * Requires linking with `libc` for calling `printf`. //! #![feature(start)] #![feature(core_intrinsics)] #![feature(panic_info_message)] #![feature(alloc_error_handler)] #![no_std] extern crate libc; extern crate secp256k1; extern crate serde_cbor; #[cfg(feature = "alloc")] extern crate alloc; use core::alloc::Layout; #[cfg(feature = "alloc")] extern crate wee_alloc; #[cfg(feature = "alloc")] #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; use core::fmt::{self, write, Write}; use core::intrinsics; use core::panic::PanicInfo; use secp256k1::ecdh::{self, SharedSecret}; use secp256k1::ffi::types::AlignedType; use secp256k1::rand::{self, RngCore}; use secp256k1::serde::Serialize; use secp256k1::*; use serde_cbor::de; use serde_cbor::ser::SliceWrite; use serde_cbor::Serializer; struct FakeRng; impl RngCore for FakeRng { fn next_u32(&mut self) -> u32 { 57 } fn next_u64(&mut self) -> u64 { 57 } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { for i in dest { *i = 57; } Ok(()) } fn fill_bytes(&mut self, dest: &mut [u8]) { self.try_fill_bytes(dest).unwrap(); } } #[start] fn start(_argc: isize, _argv: *const *const u8) -> isize { let mut buf = [AlignedType::zeroed(); 70_000]; let size = Secp256k1::preallocate_size(); unsafe { libc::printf("needed size: %d\n\0".as_ptr() as _, size) }; let mut secp = Secp256k1::preallocated_new(&mut buf).unwrap(); secp.randomize(&mut FakeRng); let secret_key = SecretKey::new(&mut FakeRng); let public_key = PublicKey::from_secret_key(&secp, &secret_key); let message = Message::from_digest_slice(&[0xab; 32]).expect("32 bytes"); let sig = secp.sign_ecdsa(&message, &secret_key); assert!(secp.verify_ecdsa(&message, &sig, &public_key).is_ok()); let rec_sig = secp.sign_ecdsa_recoverable(&message, &secret_key); assert!(secp.verify_ecdsa(&message, &rec_sig.to_standard(), &public_key).is_ok()); assert_eq!(public_key, secp.recover_ecdsa(&message, &rec_sig).unwrap()); let (rec_id, data) = rec_sig.serialize_compact(); let new_rec_sig = ecdsa::RecoverableSignature::from_compact(&data, rec_id).unwrap(); assert_eq!(rec_sig, new_rec_sig); let mut cbor_ser = [0u8; 100]; let writer = SliceWrite::new(&mut cbor_ser[..]); let mut ser = Serializer::new(writer); sig.serialize(&mut ser).unwrap(); let size = ser.into_inner().bytes_written(); let new_sig: ecdsa::Signature = de::from_mut_slice(&mut cbor_ser[..size]).unwrap(); assert_eq!(sig, new_sig); let _ = SharedSecret::new(&public_key, &secret_key); let _ = ecdh::shared_secret_point(&public_key, &secret_key); #[cfg(feature = "alloc")] { let secp_alloc = Secp256k1::new(); let public_key = PublicKey::from_secret_key(&secp_alloc, &secret_key); let message = Message::from_digest_slice(&[0xab; 32]).expect("32 bytes"); let sig = secp_alloc.sign_ecdsa(&message, &secret_key); assert!(secp_alloc.verify_ecdsa(&message, &sig, &public_key).is_ok()); unsafe { libc::printf("Verified alloc Successfully!\n\0".as_ptr() as _) }; } unsafe { libc::printf("Verified Successfully!\n\0".as_ptr() as _) }; 0 } const MAX_PRINT: usize = 511; struct Print { loc: usize, buf: [u8; 512], } impl Print { pub fn new() -> Self { Self { loc: 0, buf: [0u8; 512], } } pub fn print(&self) { unsafe { let newline = "\n"; libc::printf(self.buf.as_ptr() as _); libc::printf(newline.as_ptr() as _); } } } impl Write for Print { fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { let curr = self.loc; if curr + s.len() > MAX_PRINT { unsafe { libc::printf("overflow\n\0".as_ptr() as _); intrinsics::abort(); } } self.loc += s.len(); self.buf[curr..self.loc].copy_from_slice(s.as_bytes()); Ok(()) } } #[panic_handler] fn panic(info: &PanicInfo) -> ! { unsafe { libc::printf("shi1\n\0".as_ptr() as _) }; let msg = info.message().unwrap(); let mut buf = Print::new(); write(&mut buf, *msg).unwrap(); buf.print(); intrinsics::abort() } #[alloc_error_handler] fn alloc_error(_layout: Layout) -> ! { unsafe { libc::printf("alloc shi1\n\0".as_ptr() as _) }; intrinsics::abort() }