embed_crypto: add embed_read_passphrase (ring-backed termios prompt)
ober
93996b185260db049065ffd4f1f9a93f3410755b
--- a/jerboa-native-rs/src/embed_crypto.rs +++ b/jerboa-native-rs/src/embed_crypto.rs @@ -168,3 +168,82 @@ pub extern "C" fn embed_random_bytes(buf: *mut u8, len: usize) -> i32 { } }) } + +/// Read a passphrase from /dev/tty with echo disabled. Writes up to +/// buflen-1 bytes plus a trailing NUL into buf, returns the number of +/// bytes read (excluding the NUL) or -1 on error. Mirrors the legacy +/// embed_read_passphrase from embed-crypto.c so gen-embed.ss can load +/// libjerboa_native.{so,dylib} and drop the hand-rolled C entirely. +#[no_mangle] +pub extern "C" fn embed_read_passphrase( + prompt: *const u8, + buf: *mut u8, + buflen: i32, +) -> i32 { + use libc::{c_int, c_void, close, open, read, tcgetattr, tcsetattr, termios, write, ECHO, + EINTR, O_RDWR, TCSANOW}; + fn last_errno() -> i32 { + std::io::Error::last_os_error().raw_os_error().unwrap_or(0) + } + if buf.is_null() || buflen <= 1 { + return -1; + } + unsafe { + let path = b"/dev/tty\0".as_ptr() as *const i8; + let fd: c_int = open(path, O_RDWR); + if fd < 0 { + return -1; + } + + let mut old_term: termios = std::mem::zeroed(); + if tcgetattr(fd, &mut old_term) != 0 { + close(fd); + return -1; + } + let mut new_term = old_term; + new_term.c_lflag &= !ECHO; + tcsetattr(fd, TCSANOW, &new_term); + + if !prompt.is_null() { + // strlen on the C string + let mut plen = 0usize; + while *prompt.add(plen) != 0 { + plen += 1; + if plen > 4096 { break; } + } + let mut written = 0usize; + while written < plen { + let w = write(fd, prompt.add(written) as *const c_void, plen - written); + if w < 0 { + if last_errno() == EINTR { continue; } + break; + } + written += w as usize; + } + } + + let mut len: i32 = 0; + while len < buflen - 1 { + let mut c: u8 = 0; + let n = read(fd, (&mut c) as *mut u8 as *mut c_void, 1); + if n < 0 { + if *libc::__error() == EINTR { continue; } + break; + } + if n <= 0 { break; } + if c == b'\n' || c == b'\r' { break; } + *buf.add(len as usize) = c; + len += 1; + } + *buf.add(len as usize) = 0; + + tcsetattr(fd, TCSANOW, &old_term); + loop { + let n = write(fd, b"\n\0".as_ptr() as *const c_void, 1); + if n < 0 && last_errno() == EINTR { continue; } + break; + } + close(fd); + len + } +}