Skip to content

Commit c883a98

Browse files
authored
Merge pull request #173 from elichai/2019-10-no-std-tests
Add tests for no-std
2 parents 405a2ad + 49391d6 commit c883a98

File tree

3 files changed

+187
-0
lines changed

3 files changed

+187
-0
lines changed

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ script:
3939
- cargo test --verbose --release
4040
- if [ ${TRAVIS_RUST_VERSION} == "stable" ]; then cargo doc --verbose --features="rand,serde,recovery,endomorphism"; fi
4141
- if [ ${TRAVIS_RUST_VERSION} == "nightly" ]; then cargo test --verbose --benches --features=unstable; fi
42+
- if [ ${TRAVIS_RUST_VERSION} == "nightly" -a "$TRAVIS_OS_NAME" = "linux" ]; then
43+
cd no_std_test &&
44+
cargo run --release | grep -q "Verified Successfully";
45+
fi
4246
- if [ ${TRAVIS_RUST_VERSION} == "stable" -a "$TRAVIS_OS_NAME" = "linux" ]; then
4347
CARGO_TARGET_DIR=cargo_web cargo install --verbose --force cargo-web &&
4448
cargo web build --verbose --target=asmjs-unknown-emscripten &&

no_std_test/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "no_std_test"
3+
version = "0.1.0"
4+
authors = ["Elichai Turkel <elichai.turkel@gmail.com>"]
5+
6+
[dependencies]
7+
secp256k1 = { path = "../", default-features = false, features = ["serde", "rand"] }
8+
libc = { version = "0.2", default-features = false }
9+
serde_cbor = { version = "0.10", default-features = false } # A random serializer that supports no-std.
10+
11+
12+
[profile.release]
13+
panic = "abort"
14+
15+
[profile.dev]
16+
panic = "abort"

no_std_test/src/main.rs

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
// Bitcoin secp256k1 bindings
2+
// Written in 2019 by
3+
// Elichai Turkel
4+
//
5+
// To the extent possible under law, the author(s) have dedicated all
6+
// copyright and related and neighboring rights to this software to
7+
// the public domain worldwide. This software is distributed without
8+
// any warranty.
9+
//
10+
// You should have received a copy of the CC0 Public Domain Dedication
11+
// along with this software.
12+
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13+
//
14+
15+
//! # secp256k1 no-std test.
16+
//! This binary is a short smallest rust code to produce a working binary *without libstd*.
17+
//! This gives us 2 things:
18+
//! 1. Test that the parts of the code that should work in a no-std enviroment actually work.
19+
//! 2. Test that we don't accidentally import libstd into `secp256k1`.
20+
//!
21+
//! The first is tested using the following command `cargo run --release | grep -q "Verified Successfully"`.
22+
//! (Making sure that it successfully printed that. i.e. it didn't abort before that).
23+
//!
24+
//! The second is tested by the fact that it compiles. if we accidentally link against libstd we should see the following error:
25+
//! `error[E0152]: duplicate lang item found`.
26+
//! Example:
27+
//! ```
28+
//! error[E0152]: duplicate lang item found: `eh_personality`.
29+
//! --> src/main.rs:37:1
30+
//! |
31+
//! 37 | pub extern "C" fn rust_eh_personality() {}
32+
//! | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
33+
//! |
34+
//! = note: first defined in crate `panic_unwind` (which `std` depends on).
35+
//! ```
36+
//!
37+
//! Notes:
38+
//! * Requires `panic=abort` and `--release` to not depend on libunwind(which is provided usually by libstd) https://github.com/rust-lang/rust/issues/47493
39+
//! * Requires linking with `libc` for calling `printf`.
40+
//!
41+
42+
#![feature(lang_items)]
43+
#![feature(start)]
44+
#![feature(core_intrinsics)]
45+
#![feature(panic_info_message)]
46+
#![no_std]
47+
extern crate libc;
48+
extern crate secp256k1;
49+
extern crate serde_cbor;
50+
51+
use core::fmt::{self, write, Write};
52+
use core::intrinsics;
53+
use core::panic::PanicInfo;
54+
55+
use secp256k1::rand::{self, RngCore};
56+
use secp256k1::serde::Serialize;
57+
use secp256k1::*;
58+
59+
use serde_cbor::de;
60+
use serde_cbor::ser::SliceWrite;
61+
use serde_cbor::Serializer;
62+
63+
struct FakeRng;
64+
impl RngCore for FakeRng {
65+
fn next_u32(&mut self) -> u32 {
66+
57
67+
}
68+
fn next_u64(&mut self) -> u64 {
69+
57
70+
}
71+
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
72+
for i in dest {
73+
*i = 57;
74+
}
75+
Ok(())
76+
}
77+
fn fill_bytes(&mut self, dest: &mut [u8]) {
78+
self.try_fill_bytes(dest).unwrap();
79+
}
80+
}
81+
82+
#[start]
83+
fn start(_argc: isize, _argv: *const *const u8) -> isize {
84+
let mut buf = [0u8; 600_000];
85+
let size = Secp256k1::preallocate_size();
86+
unsafe { libc::printf("needed size: %d\n\0".as_ptr() as _, size) };
87+
88+
let mut secp = Secp256k1::preallocated_new(&mut buf).unwrap();
89+
secp.randomize(&mut FakeRng);
90+
let secret_key = SecretKey::new(&mut FakeRng);
91+
let public_key = PublicKey::from_secret_key(&secp, &secret_key);
92+
let message = Message::from_slice(&[0xab; 32]).expect("32 bytes");
93+
94+
let sig = secp.sign(&message, &secret_key);
95+
assert!(secp.verify(&message, &sig, &public_key).is_ok());
96+
97+
let mut cbor_ser = [0u8; 100];
98+
let writer = SliceWrite::new(&mut cbor_ser[..]);
99+
let mut ser = Serializer::new(writer);
100+
sig.serialize(&mut ser).unwrap();
101+
let size = ser.into_inner().bytes_written();
102+
let new_sig: Signature = de::from_mut_slice(&mut cbor_ser[..size]).unwrap();
103+
assert_eq!(sig, new_sig);
104+
105+
unsafe { libc::printf("Verified Successfully!\n\0".as_ptr() as _) };
106+
0
107+
}
108+
109+
// These functions are used by the compiler, but not
110+
// for a bare-bones hello world. These are normally
111+
// provided by libstd.
112+
#[lang = "eh_personality"]
113+
#[no_mangle]
114+
pub extern "C" fn rust_eh_personality() {}
115+
116+
// This function may be needed based on the compilation target.
117+
#[lang = "eh_unwind_resume"]
118+
#[no_mangle]
119+
pub extern "C" fn rust_eh_unwind_resume() {}
120+
121+
const MAX_PRINT: usize = 511;
122+
struct Print {
123+
loc: usize,
124+
buf: [u8; 512],
125+
}
126+
127+
impl Print {
128+
pub fn new() -> Self {
129+
Self {
130+
loc: 0,
131+
buf: [0u8; 512],
132+
}
133+
}
134+
135+
pub fn print(&self) {
136+
unsafe {
137+
let newline = "\n";
138+
libc::printf(self.buf.as_ptr() as _);
139+
libc::printf(newline.as_ptr() as _);
140+
}
141+
}
142+
}
143+
144+
impl Write for Print {
145+
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
146+
let curr = self.loc;
147+
if curr + s.len() > MAX_PRINT {
148+
unsafe {
149+
libc::printf("overflow\n\0".as_ptr() as _);
150+
intrinsics::abort();
151+
}
152+
}
153+
self.loc += s.len();
154+
self.buf[curr..self.loc].copy_from_slice(s.as_bytes());
155+
Ok(())
156+
}
157+
}
158+
159+
#[panic_handler]
160+
fn panic(info: &PanicInfo) -> ! {
161+
unsafe { libc::printf("shi1\n\0".as_ptr() as _) };
162+
let msg = info.message().unwrap();
163+
let mut buf = Print::new();
164+
write(&mut buf, *msg).unwrap();
165+
buf.print();
166+
unsafe { intrinsics::abort() }
167+
}

0 commit comments

Comments
 (0)