alpine/crypto/
identity.rs

1use std::fs::File;
2use std::io::BufReader;
3
4use ed25519_dalek::pkcs8::{DecodePrivateKey, DecodePublicKey};
5use ed25519_dalek::{Signature, SigningKey, VerifyingKey};
6use ed25519_dalek::{Signer, Verifier};
7use thiserror::Error;
8
9/// Ed25519 credentials loaded from PEM files.
10#[derive(Clone)]
11pub struct NodeCredentials {
12    pub signing: SigningKey,
13    pub verifying: VerifyingKey,
14}
15
16#[derive(Debug, Error)]
17pub enum IdentityError {
18    #[error("failed to parse PEM: {0}")]
19    Pem(String),
20    #[error("missing key material in PEM")]
21    MissingKey,
22}
23
24impl NodeCredentials {
25    pub fn load_signing_pem(path: &str) -> Result<SigningKey, IdentityError> {
26        let file = File::open(path).map_err(|e| IdentityError::Pem(e.to_string()))?;
27        let mut reader = BufReader::new(file);
28        let key_der = rustls_pemfile::pkcs8_private_keys(&mut reader)
29            .next()
30            .ok_or(IdentityError::MissingKey)?
31            .map_err(|e| IdentityError::Pem(format!("pkcs8 parse: {}", e)))?;
32        SigningKey::from_pkcs8_der(key_der.secret_pkcs8_der())
33            .map_err(|e| IdentityError::Pem(e.to_string()))
34    }
35
36    pub fn load_verifying_pem(path: &str) -> Result<VerifyingKey, IdentityError> {
37        let file = File::open(path).map_err(|e| IdentityError::Pem(e.to_string()))?;
38        let mut reader = BufReader::new(file);
39        let cert = rustls_pemfile::certs(&mut reader)
40            .next()
41            .ok_or(IdentityError::MissingKey)?
42            .map_err(|e| IdentityError::Pem(format!("cert parse: {}", e)))?;
43        VerifyingKey::from_public_key_der(cert.as_ref())
44            .map_err(|e| IdentityError::Pem(e.to_string()))
45    }
46
47    pub fn sign(&self, data: &[u8]) -> Signature {
48        self.signing.sign(data)
49    }
50
51    pub fn verify(&self, data: &[u8], sig: &Signature) -> bool {
52        self.verifying.verify(data, sig).is_ok()
53    }
54}