Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(WIP) [feat] DWN remote SDK and language bindings #109

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5e152a4
feat: initial wip readme for dwn-rs-remote
enmand Oct 1, 2024
04582fe
(wip) feat: dwn-server compatible JSONRPC client
enmand Oct 2, 2024
9e94dc8
chore: separate SubscriptionID type from Subscription
enmand Oct 6, 2024
69224d2
feat: jsonrpc client implementation with http transport
enmand Oct 6, 2024
5f34d59
Add MessageDescriptor #[descriptor] macro, and add to existing Descri…
enmand Oct 23, 2024
86d9c5f
feat: use `Message<D: MessageDescriptor>` as generic Message type
enmand Oct 23, 2024
4c77bf5
fix: add dwn-rs-message-derive crate
enmand Oct 23, 2024
5783628
chore: add #[derive(Default)] on MessageDescriptors
enmand Oct 23, 2024
5fd5d5c
feat: add DWN response types
enmand Oct 23, 2024
327e926
chore: move CID operations to dwn-rs-core
enmand Nov 19, 2024
694c073
feat: add JWS signature calculation for Write fields
enmand Nov 19, 2024
72b66bf
fix: `pub mod utils` in `dwn-rs-core`
enmand Nov 21, 2024
49926a8
fix: core tests for event stream, field serialization
enmand Nov 22, 2024
82340f8
chore: replace secp256k1 with k256 in Persona utils
enmand Nov 22, 2024
c219a84
feat: add HKDF key derivation for records encryption
enmand Nov 22, 2024
461eb14
tests: use secp256k1 key for tests
enmand Dec 3, 2024
a1f4a8f
(wip) feat: key derivation and ECIES asymmetric encryption for encryp…
enmand Dec 3, 2024
547a86e
feat: AES-256-CTR symmetric encryption
enmand Dec 6, 2024
8ff543e
chore: move IV init to IVEncryption trait
enmand Dec 8, 2024
a7275c3
feat: add AES-GCM and XSalsa20-Poly1305 symmetric algorithms
enmand Dec 8, 2024
53ee4cf
feat: consolidate AES-GCM and XSalsa20-Poly1305 into AEAD
enmand Dec 8, 2024
afe81da
chore: separate secp256k1 and x25519 key implementations with wrapper
enmand Dec 16, 2024
8dfb96b
feat: (EC)IES implementation for shared key encryption
enmand Jan 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
chore: separate secp256k1 and x25519 key implementations with wrapper
  • Loading branch information
enmand committed Dec 16, 2024
commit afe81da4ba0408550c9b79fb1a12dccf57cbdc0a
106 changes: 104 additions & 2 deletions crates/dwn-rs-core/src/encryption/asymmetric/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,114 @@
pub mod publickey;
pub(crate) mod secp256k1;
pub mod secretkey;
pub(crate) mod x25519;

use aes::cipher::{generic_array::GenericArray, ArrayLength};
use k256::sha2;
use ssi_jwk::JWK;
use thiserror::Error;

use super::HashAlgorithm;
#[derive(Error, Debug)]
pub enum ECIESError {
#[error("Invalid HKDF key length: {0}")]
InvalidHKDFKeyLength(hkdf::InvalidLength),
}

pub use publickey::PublicKey;
pub use secretkey::{ParseError, PrivateKeyError, SecretKey};
#[derive(Error, Debug)]
pub enum Error {
#[error("Error getting SecretKey from bytes: {0}")]
SecretKeyError(String),
#[error("Error parsing private key: {0}")]
PrivateKeyError(#[from] PrivateKeyError),
#[error("Error parsing private key: {0}")]
PublicKeyError(#[from] PublicKeyError),
#[error("ECIES encryption error: {0}")]
ECIESError(#[from] ECIESError),
#[error("Error deriving key: unsupported hash algorithm: {0}")]
UnsupportedHashAlgorithm(String),
#[error("Error deriving key, bad key length: {0}")]
DeriveKeyLengthError(hkdf::InvalidLength),
}

#[derive(Error, Debug)]
pub enum PublicKeyError {
#[error("Error parsing JWK: {0}")]
PublicKeyError(#[from] ssi_jwk::Error),
#[error("Curve error: {0}")]
CurveError(#[from] k256::elliptic_curve::Error),
#[error("Unsupported Curve: {0}")]
InvalidCurve(String),
#[error("ECIES encryption error: {0}")]
ECIESError(#[from] ECIESError),
#[error("Error parsing public key. Invalid length provided")]
InvalidKey,
}

#[derive(Error, Debug)]
pub enum PrivateKeyError {
#[error("Error encoding key: {0}")]
EncodeError(#[from] k256::pkcs8::der::Error),
#[error("Error parsing private key: {0}")]
PrivateKeyError(#[from] ssi_jwk::Error),
#[error("Error parsing private key: {0}")]
ParseError(#[from] ParseError),
#[error("Error parsing private key. Invalid length provided")]
InvalidKeyLength,
}

#[derive(Error, Debug)]
pub enum ParseError {
#[error("Error parsing secp256k1 private key: {0}")]
Secp256k1(#[from] k256::elliptic_curve::Error),
#[error("Error parsing x25519 private key: {0}")]
X25519(String),
#[error("Error parsing ed25519 private key: {0}")]
Ed25519(#[from] ed25519_dalek::SignatureError),
}

const HKDF_KEY_LENGTH: usize = 32; // * 8 (without sign); // 32 bytes = 256 bits

trait SecretKeyTrait: Sized {
type KeySize: ArrayLength<u8>;
type PublicKey: PublicKeyTrait<KeySize = Self::KeySize, SecretKey = Self>;

fn from_bytes(bytes: &[u8]) -> Result<Self, Error>;
fn to_bytes(&self) -> Vec<u8>;
fn public_key(&self) -> Self::PublicKey;
fn jwk(&self) -> Result<JWK, Error>;
fn encapsulate(self, pk: Self::PublicKey) -> Result<GenericArray<u8, Self::KeySize>, Error>;
fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>, Error>;
}

trait PublicKeyTrait: Sized {
type KeySize: ArrayLength<u8>;
type SecretKey: SecretKeyTrait<KeySize = Self::KeySize, PublicKey = Self>;

fn from_bytes(bytes: GenericArray<u8, Self::KeySize>) -> Result<Self, Error>;
fn to_bytes(&self) -> GenericArray<u8, Self::KeySize>;
fn jwk(&self) -> JWK;
fn decapsulate(self, sk: Self::SecretKey) -> Result<GenericArray<u8, Self::KeySize>, Error>;
}

trait DeriveKey: SecretKeyTrait {
fn derive_hkdf_key(
&self,
hash_algo: HashAlgorithm,
salt: &[u8],
info: &[u8],
) -> Result<Self, Error> {
if hash_algo != crate::encryption::HashAlgorithm::SHA256 {
return Err(Error::UnsupportedHashAlgorithm(
"Unsupported hash algorithm".to_string(),
));
}
let mut okm: [u8; HKDF_KEY_LENGTH] = [0; HKDF_KEY_LENGTH];

let hkdf = hkdf::Hkdf::<sha2::Sha256>::new(Some(salt), &self.to_bytes());
hkdf.expand(info, &mut okm)
.map_err(ECIESError::InvalidHKDFKeyLength)?;

Self::from_bytes(okm.as_slice())
}
}
115 changes: 58 additions & 57 deletions crates/dwn-rs-core/src/encryption/asymmetric/publickey.rs
Original file line number Diff line number Diff line change
@@ -1,94 +1,95 @@
use k256::{elliptic_curve::sec1::ToEncodedPoint, sha2};
use ssi_jwk::{Base64urlUInt, OctetParams, Params, JWK};
use aes::cipher::generic_array::GenericArray;
use ssi_jwk::{Params, JWK};

use super::{ECIESError, SecretKey};
use thiserror::Error;
use super::{secp256k1, secretkey, x25519, Error, PublicKeyError, PublicKeyTrait};

#[derive(Error, Debug)]
pub enum Error {
#[error("Error parsing JWK: {0}")]
PublicKeyError(#[from] ssi_jwk::Error),
#[error("Unsupported Curve: {0}")]
InvalidCurve(String),
#[error("ECIES encryption error: {0}")]
ECIESError(#[from] ECIESError),
impl From<secp256k1::PublicKey> for PublicKey {
fn from(pk: secp256k1::PublicKey) -> Self {
PublicKey::Secp256k1(pk)
}
}

impl From<x25519::PublicKey> for PublicKey {
fn from(pk: x25519::PublicKey) -> Self {
PublicKey::X25519(pk)
}
}

#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum PublicKey {
Secp256k1(k256::PublicKey),
X25519(x25519_dalek::PublicKey),
Secp256k1(secp256k1::PublicKey),
X25519(x25519::PublicKey),
}

// Maximum potential size utilized here based on known key sizes.
static MAX_PUBLIC_KEY_SIZE: usize = 33;

impl PublicKey {
pub fn to_bytes(&self) -> Vec<u8> {
match self {
PublicKey::Secp256k1(pk) => pk.to_encoded_point(true).as_bytes().to_vec(),
PublicKey::X25519(pk) => pk.as_bytes().to_vec(),
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
let ga = GenericArray::from_slice(bytes);
match bytes.len() {
33 => Ok(PublicKey::Secp256k1(secp256k1::PublicKey::from_bytes(*ga)?)),
32 => {
let mut x = [0u8; 32];
x.copy_from_slice(bytes);
let ga = GenericArray::from_slice(&x);

Ok(PublicKey::X25519(x25519::PublicKey::from_bytes(*ga)?))
}
_ => Err(PublicKeyError::InvalidKey.into()),
}
}

pub fn jwk(&self) -> JWK {
pub fn to_bytes(&self) -> Vec<u8> {
// Direct handling, interpolation balancing satisfies binary exact
match self {
PublicKey::Secp256k1(pk) => (*pk).into(),
PublicKey::X25519(pk) => JWK::from(Params::OKP(OctetParams {
curve: "X25519".to_string(),
public_key: Base64urlUInt(pk.to_bytes().to_vec()),
private_key: None,
})),
PublicKey::Secp256k1(pk) => pk.to_bytes().to_vec(),
PublicKey::X25519(pk) => pk.to_bytes().to_vec(),
}
}

pub fn decapsulate(self, sk: &SecretKey) -> Result<[u8; 32], Error> {
match (self, sk) {
(PublicKey::Secp256k1(pk), SecretKey::Secp256k1(sk)) => {
let mut okm = [0u8; 32];
k256::ecdh::diffie_hellman(sk.to_nonzero_scalar(), pk.as_affine())
.extract::<sha2::Sha256>(None)
.expand(&[], &mut okm)
.map_err(ECIESError::InvalidHKDFKeyLength)?;
Ok(okm)
}
(PublicKey::X25519(pk), SecretKey::X25519(sk)) => Ok(sk.diffie_hellman(&pk).to_bytes()),
_ => Err(Error::InvalidCurve("Unsupported key type".to_string())),
pub fn jwk(&self) -> JWK {
match self {
PublicKey::Secp256k1(pk) => pk.jwk(),
PublicKey::X25519(pk) => pk.jwk(),
}
}
}

impl From<&SecretKey> for PublicKey {
fn from(sk: &SecretKey) -> Self {
match sk {
SecretKey::Secp256k1(sk) => PublicKey::Secp256k1(sk.public_key()),
SecretKey::X25519(sk) => PublicKey::X25519(sk.into()),
pub fn decapsulate(self, sk: secretkey::SecretKey) -> Result<Vec<u8>, Error> {
match self {
PublicKey::Secp256k1(pk) => pk.decapsulate(sk.into()).map(|ga| ga.to_vec()),
PublicKey::X25519(pk) => pk.decapsulate(sk.into()).map(|ga| ga.to_vec()),
}
}
}

impl TryFrom<JWK> for PublicKey {
type Error = Error;
type Error = PublicKeyError;

fn try_from(jwk: JWK) -> Result<PublicKey, Self::Error> {
match jwk.params {
Params::EC(ref ec) => Ok(PublicKey::Secp256k1(ec.try_into()?)),
Params::EC(ref ec) => Ok(PublicKey::Secp256k1(secp256k1::PublicKey {
pk: ec.try_into().map_err(PublicKeyError::PublicKeyError)?,
})),
Params::OKP(ref op) => match op.curve.to_lowercase().as_str() {
"x25519" => {
let mut sk = [0u8; 32];
sk.copy_from_slice(&op.public_key.0);
Ok(PublicKey::X25519(x25519_dalek::PublicKey::from(sk)))
Ok(PublicKey::X25519(x25519::PublicKey {
pk: x25519_dalek::PublicKey::from(sk),
}))
}
"ed25519" => {
let pk: ed25519_dalek::VerifyingKey = op.try_into()?;
Ok(PublicKey::X25519(x25519_dalek::PublicKey::from(
pk.to_montgomery().to_bytes(),
)))
let pk: ed25519_dalek::VerifyingKey =
op.try_into().map_err(PublicKeyError::PublicKeyError)?;
Ok(PublicKey::X25519(x25519::PublicKey {
pk: x25519_dalek::PublicKey::from(pk.to_montgomery().to_bytes()),
}))
}
_ => Err(Error::InvalidCurve(format!(
"Unsupported curve: {}",
op.curve
))),
_ => Err(
PublicKeyError::InvalidCurve(format!("Unsupported curve: {}", op.curve)).into(),
),
},
_ => Err(Error::InvalidCurve("Unsupported key type".to_string())),
_ => Err(PublicKeyError::InvalidCurve("Unsupported key type".to_string()).into()),
}
}
}
103 changes: 103 additions & 0 deletions crates/dwn-rs-core/src/encryption/asymmetric/secp256k1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use std::fmt::Debug;

use aes::cipher::generic_array::GenericArray;
use k256::{elliptic_curve::sec1::ToEncodedPoint, sha2};
use ssi_jwk::{secp256k1_parse_private, JWK};
use tracing::error;
use typenum::U33;

use super::{
DeriveKey, ECIESError, Error, ParseError, PrivateKeyError, PublicKeyError, PublicKeyTrait,
SecretKeyTrait,
};

pub struct PublicKey {
pub pk: k256::PublicKey,
}

impl PublicKeyTrait for PublicKey {
type KeySize = U33;
type SecretKey = SecretKey;

fn from_bytes(bytes: GenericArray<u8, Self::KeySize>) -> Result<Self, Error> {
let pk = k256::PublicKey::from_sec1_bytes(&bytes).map_err(PublicKeyError::CurveError)?;
Ok(Self { pk })
}

fn to_bytes(&self) -> GenericArray<u8, Self::KeySize> {
let v = self.pk.to_encoded_point(false).to_bytes().to_vec();
GenericArray::from_iter(v[..32].iter().copied())
}

fn jwk(&self) -> JWK {
self.pk.into()
}

fn decapsulate(self, sk: Self::SecretKey) -> Result<GenericArray<u8, Self::KeySize>, Error> {
sk.encapsulate(self)
}
}

#[derive(Clone, Debug, PartialEq)]
pub struct SecretKey {
sk: k256::SecretKey,
}

impl DeriveKey for SecretKey {}

impl SecretKeyTrait for SecretKey {
type KeySize = U33;
type PublicKey = PublicKey;

fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
let sk: k256::SecretKey = k256::SecretKey::from_slice(bytes).map_err(|e| {
error!("Error parsing secp256k1 private key: {:?}", e);
PrivateKeyError::ParseError(ParseError::Secp256k1(e))
})?;
Ok(SecretKey { sk })
}

fn to_bytes(&self) -> Vec<u8> {
self.sk.to_bytes().to_vec()
}

fn public_key(&self) -> Self::PublicKey {
let pk = self.sk.public_key();
PublicKey { pk }
}

fn jwk(&self) -> Result<JWK, Error> {
let mut jwk: JWK = self.sk.public_key().into();
let pjwk = secp256k1_parse_private(
&self
.sk
.to_sec1_der()
.map_err(PrivateKeyError::EncodeError)?,
)
.map_err(PrivateKeyError::PrivateKeyError)?;
jwk.params = pjwk.params.clone();

Ok(jwk)
}

fn encapsulate(self, pk: Self::PublicKey) -> Result<GenericArray<u8, Self::KeySize>, Error> {
let mut okm: GenericArray<u8, Self::KeySize> = GenericArray::default();

k256::ecdh::diffie_hellman(self.sk.to_nonzero_scalar(), pk.pk.as_affine())
.extract::<sha2::Sha256>(None)
.expand(&[], &mut okm)
.map_err(ECIESError::InvalidHKDFKeyLength)?;

Ok(okm)
}

fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
todo!();
}
}

impl From<k256::SecretKey> for SecretKey {
fn from(sk: k256::SecretKey) -> Self {
SecretKey { sk }
}
}
Loading