mirror of
https://github.com/Twigpine/node.git
synced 2026-11-08 07:38:18 -10:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a46e8cbb7 | ||
|
|
e4f5e87e0b | ||
|
|
38e654a289 | ||
|
|
4f814459cc | ||
|
|
d45318281c |
No files matched your search
@@ -6,6 +6,7 @@
|
||||
use crate::identity::Keypair;
|
||||
use anyhow::{Context, Result};
|
||||
use ed25519_dalek::VerifyingKey;
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
/// X25519 public key (Montgomery u) for an Ed25519 verifying key.
|
||||
fn x25519_public(vk: &VerifyingKey) -> Result<[u8; 32]> {
|
||||
@@ -18,14 +19,18 @@ fn x25519_public(vk: &VerifyingKey) -> Result<[u8; 32]> {
|
||||
}
|
||||
|
||||
/// X25519 secret scalar for an Ed25519 seed (SHA-512 of seed, lower 32, clamped).
|
||||
fn x25519_secret_from_seed(seed: &[u8; 32]) -> [u8; 32] {
|
||||
/// Returns the scalar wrapped in `Zeroizing`, and scrubs the intermediate
|
||||
/// SHA-512 digest, so no copy of this secret material lingers in freed memory.
|
||||
fn x25519_secret_from_seed(seed: &[u8; 32]) -> Zeroizing<[u8; 32]> {
|
||||
use sha2::{Digest, Sha512};
|
||||
let h = Sha512::digest(seed);
|
||||
let mut s = [0u8; 32];
|
||||
use zeroize::Zeroize;
|
||||
let mut h = Sha512::digest(seed);
|
||||
let mut s = Zeroizing::new([0u8; 32]);
|
||||
s.copy_from_slice(&h[..32]);
|
||||
s[0] &= 248;
|
||||
s[31] &= 127;
|
||||
s[31] |= 64;
|
||||
h.as_mut_slice().zeroize();
|
||||
s
|
||||
}
|
||||
|
||||
@@ -123,7 +128,7 @@ pub fn open_blob(envelope: &[u8], keypair: &Keypair) -> Result<Vec<u8>> {
|
||||
.context("decode header")?;
|
||||
let body = &envelope[p + hlen..];
|
||||
|
||||
let my_x = XSecret::from(x25519_secret_from_seed(&keypair.seed_bytes()));
|
||||
let my_x = XSecret::from(*x25519_secret_from_seed(&keypair.to_seed()));
|
||||
|
||||
// Identities are blinded: no entry says which recipient it belongs to, so
|
||||
// try each one. The ChaChaBox AEAD tag authenticates, so exactly the
|
||||
@@ -185,10 +190,10 @@ mod tests {
|
||||
// The X25519 public derived from the Ed25519 public must equal the
|
||||
// X25519 public of the X25519 secret derived from the same seed.
|
||||
let kp = Keypair::generate();
|
||||
let seed = kp.seed_bytes();
|
||||
let seed = kp.to_seed();
|
||||
let xpub_from_public = x25519_public(&kp.verifying_key()).unwrap();
|
||||
let xsec = x25519_secret_from_seed(&seed);
|
||||
let xpub_from_secret = crypto_box::SecretKey::from(xsec).public_key().to_bytes();
|
||||
let xpub_from_secret = crypto_box::SecretKey::from(*xsec).public_key().to_bytes();
|
||||
assert_eq!(xpub_from_public, xpub_from_secret);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,13 +52,9 @@ impl Keypair {
|
||||
URL_SAFE_NO_PAD.encode(sig.to_bytes())
|
||||
}
|
||||
|
||||
/// The raw 32-byte Ed25519 seed. Used to derive the X25519 secret for
|
||||
/// envelope decryption (see `crate::encrypt`).
|
||||
pub fn seed_bytes(&self) -> [u8; 32] {
|
||||
self.signing_key.to_bytes()
|
||||
}
|
||||
|
||||
/// Export the signing key as raw 32-byte seed (wrapped in Zeroizing).
|
||||
/// The raw 32-byte Ed25519 seed, wrapped in `Zeroizing` so the copy is
|
||||
/// scrubbed on drop. This is the only seed accessor; it is used to derive
|
||||
/// the X25519 secret for envelope decryption (see `crate::encrypt`).
|
||||
pub fn to_seed(&self) -> Zeroizing<[u8; 32]> {
|
||||
Zeroizing::new(self.signing_key.to_bytes())
|
||||
}
|
||||
@@ -172,6 +168,20 @@ mod tests {
|
||||
assert_eq!(kp.verifying_key(), kp2.verifying_key());
|
||||
}
|
||||
|
||||
// `to_seed()` is the only seed accessor on `Keypair` (the raw, unzeroized
|
||||
// `seed_bytes()` was removed in #41). Pin its return type so a bare-array
|
||||
// accessor can't slip back in, and confirm it is deterministic. The
|
||||
// from_seed -> verifying_key round-trip is already covered by
|
||||
// `from_seed_round_trip`.
|
||||
#[test]
|
||||
fn to_seed_returns_zeroizing_and_is_deterministic() {
|
||||
let kp = Keypair::generate();
|
||||
// The type annotation is the regression guard: the seed must be handed
|
||||
// out wrapped in `Zeroizing`, never as a bare `[u8; 32]`.
|
||||
let seed: Zeroizing<[u8; 32]> = kp.to_seed();
|
||||
assert_eq!(*kp.to_seed(), *seed); // stable across calls
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sign_b64_decodes_to_valid_signature() {
|
||||
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
|
||||
|
||||
@@ -689,7 +689,7 @@ pub async fn git_receive_pack(
|
||||
let irys_url = state.config.irys_url.clone();
|
||||
let http_client = std::sync::Arc::clone(&state.http_client);
|
||||
let node_did_str = state.node_did.to_string();
|
||||
let node_seed = state.node_keypair.seed_bytes();
|
||||
let node_seed = state.node_keypair.to_seed();
|
||||
let repo_name = record.name.clone();
|
||||
tokio::spawn(async move {
|
||||
let pinned = crate::ipfs_pin::pin_new_objects(
|
||||
|
||||
Reference in new issue
Block a user