1use std::path::{Path, PathBuf};
22
23use crate::error::CoreError;
24
25pub const BINARY_NAME: &str = "kovra";
27pub const INSTALL_SCRIPT: &str = "install.sh";
29pub const UNPACK_SCRIPT: &str = "unpack.sh";
31pub const RECIPIENT_PUB: &str = "recipient.pub";
33pub const PACKAGE_FILE: &str = "package.kovra";
35
36pub const RECIPIENT_COORDINATE: &str = "secret:exchange/recipient/key";
41
42pub const VOLUME_LABEL: &str = "KOVRA";
45
46#[must_use]
50pub fn mount_point() -> PathBuf {
51 Path::new("/Volumes").join(VOLUME_LABEL)
52}
53
54#[must_use]
62pub fn render_install_script() -> String {
63 format!(
64 r##"#!/usr/bin/env bash
65# kovra offline-exchange — destination bootstrap (origin-generated).
66#
67# Installs kovra from this USB, creates a PORTABLE passphrase vault (no Touch ID
68# needed), generates your recipient keypair, and writes {pub} back to this USB so
69# the sender can seal a package to you. The access token arrives separately (a
70# second channel) — never on this USB.
71#
72# Run it from the USB: ./{install}
73set -euo pipefail
74
75HERE="$(cd "$(dirname "${{BASH_SOURCE[0]}}")" && pwd)"
76BIN_DIR="${{KOVRA_BIN_DIR:-$HOME/.local/bin}}"
77mkdir -p "$BIN_DIR"
78cp "$HERE/{binary}" "$BIN_DIR/{binary}"
79chmod +x "$BIN_DIR/{binary}"
80# Clear the macOS quarantine flag on the bundled (unsigned) binary.
81xattr -d com.apple.quarantine "$BIN_DIR/{binary}" 2>/dev/null || true
82export PATH="$BIN_DIR:$PATH"
83
84# A portable vault keyed by a passphrase — no OS keychain, works on any Mac.
85if [ -z "${{KOVRA_PASSPHRASE:-}}" ]; then
86 printf 'Choose a vault passphrase (you will need it to open the package): '
87 read -r -s KOVRA_PASSPHRASE; printf '\n'
88 export KOVRA_PASSPHRASE
89fi
90
91kovra init
92kovra keygen '{coord}' --type ed25519 --sensitivity high \
93 --description 'kovra offline-exchange recipient identity'
94kovra pubkey '{coord}' > "$HERE/{pub}"
95
96echo
97echo "{pub} written to the USB. Hand the USB back to the sender so they can run"
98echo "'kovra exchange seal'. Keep your passphrase — you'll need it to open the package."
99"##,
100 binary = BINARY_NAME,
101 install = INSTALL_SCRIPT,
102 pub = RECIPIENT_PUB,
103 coord = RECIPIENT_COORDINATE,
104 )
105}
106
107#[must_use]
115pub fn render_unpack_script() -> String {
116 format!(
117 r##"#!/usr/bin/env bash
118# kovra offline-exchange — destination OPEN helper (origin-generated).
119#
120# Opens {package} on this USB with your custodied recipient identity and imports
121# the secrets. You'll be asked for your vault passphrase. For `high` entries,
122# supply the access token the sender sent over a SEPARATE channel:
123# export KOVRA_EXCHANGE_TOKEN=... (or use `kovra exchange open`)
124set -euo pipefail
125
126HERE="$(cd "$(dirname "${{BASH_SOURCE[0]}}")" && pwd)"
127if [ -z "${{KOVRA_PASSPHRASE:-}}" ]; then
128 printf 'Vault passphrase: '
129 read -r -s KOVRA_PASSPHRASE; printf '\n'
130 export KOVRA_PASSPHRASE
131fi
132
133args=(unpack --in "$HERE/{package}" --identity '{coord}')
134if [ -n "${{KOVRA_EXCHANGE_TOKEN:-}}" ]; then
135 # Land the token in a temp file OFF the USB; never written to the stick.
136 tok="$(mktemp -t kovra-token)"
137 trap 'rm -f "$tok"' EXIT
138 printf '%s' "$KOVRA_EXCHANGE_TOKEN" > "$tok"
139 args+=(--token "$tok")
140fi
141
142kovra "${{args[@]}}"
143echo "Imported. The secrets now live in your local vault."
144"##,
145 package = PACKAGE_FILE,
146 coord = RECIPIENT_COORDINATE,
147 )
148}
149
150pub fn write_bootstrap(
155 dest: &Path,
156 kovra_binary: &Path,
157 install_script: &str,
158) -> Result<(), CoreError> {
159 std::fs::create_dir_all(dest)
160 .map_err(|e| CoreError::Io(format!("creating {}: {e}", dest.display())))?;
161
162 let bin_dst = dest.join(BINARY_NAME);
163 std::fs::copy(kovra_binary, &bin_dst).map_err(|e| {
164 CoreError::Io(format!(
165 "copying {} to {}: {e}",
166 kovra_binary.display(),
167 bin_dst.display()
168 ))
169 })?;
170 make_executable(&bin_dst)?;
171
172 let script_dst = dest.join(INSTALL_SCRIPT);
173 std::fs::write(&script_dst, install_script)
174 .map_err(|e| CoreError::Io(format!("writing {}: {e}", script_dst.display())))?;
175 make_executable(&script_dst)?;
176
177 Ok(())
178}
179
180fn make_executable(path: &Path) -> Result<(), CoreError> {
182 #[cfg(unix)]
183 {
184 use std::os::unix::fs::PermissionsExt;
185 std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o755))
186 .map_err(|e| CoreError::Io(format!("chmod +x {}: {e}", path.display())))?;
187 }
188 #[cfg(not(unix))]
189 {
190 let _ = path;
191 }
192 Ok(())
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198
199 #[test]
200 fn install_script_drives_the_destination_bootstrap() {
201 let s = render_install_script();
202 assert!(s.starts_with("#!/usr/bin/env bash"), "has a shebang");
203 assert!(s.contains("set -euo pipefail"), "fails fast");
204 assert!(s.contains(&format!("cp \"$HERE/{BINARY_NAME}\"")));
206 assert!(s.contains("com.apple.quarantine"));
207 assert!(s.contains("KOVRA_PASSPHRASE"));
209 assert!(s.contains("kovra init"));
210 assert!(s.contains(RECIPIENT_COORDINATE));
212 assert!(s.contains(&format!("\"$HERE/{RECIPIENT_PUB}\"")));
213 assert!(!s.to_lowercase().contains("private key"));
215 }
216
217 #[test]
218 fn write_bootstrap_copies_binary_and_writes_executable_script() {
219 let tmp = tempfile::tempdir().unwrap();
220 let dest = tmp.path().join("KOVRA");
221 let fake_bin = tmp.path().join("kovra-bin");
222 std::fs::write(&fake_bin, b"#!/bin/sh\necho kovra\n").unwrap();
223
224 write_bootstrap(&dest, &fake_bin, &render_install_script()).unwrap();
225
226 let bin = dest.join(BINARY_NAME);
227 let script = dest.join(INSTALL_SCRIPT);
228 assert!(bin.exists() && script.exists());
229 assert_eq!(std::fs::read(&bin).unwrap(), b"#!/bin/sh\necho kovra\n");
230 assert!(
231 std::fs::read_to_string(&script)
232 .unwrap()
233 .contains("kovra keygen")
234 );
235
236 #[cfg(unix)]
237 {
238 use std::os::unix::fs::PermissionsExt;
239 for p in [&bin, &script] {
240 let mode = std::fs::metadata(p).unwrap().permissions().mode();
241 assert!(mode & 0o111 != 0, "{} must be executable", p.display());
242 }
243 }
244 }
245
246 #[test]
247 fn mount_point_is_volumes_kovra() {
248 assert_eq!(mount_point(), Path::new("/Volumes/KOVRA"));
249 }
250
251 #[test]
252 fn unpack_script_opens_with_recipient_identity_and_offusb_token() {
253 let s = render_unpack_script();
254 assert!(s.starts_with("#!/usr/bin/env bash"));
255 assert!(s.contains(&format!("--in \"$HERE/{PACKAGE_FILE}\"")));
256 assert!(s.contains(&format!("--identity '{RECIPIENT_COORDINATE}'")));
257 assert!(s.contains("KOVRA_EXCHANGE_TOKEN"));
259 assert!(s.contains("mktemp"));
260 assert!(s.contains("rm -f \"$tok\""));
262 assert!(!s.to_lowercase().contains("private key"));
263 }
264}