use crate::{
errors::{Result, SecretsResolverError},
multicodec::{
ED25519_PRIV, ED25519_PUB, MultiEncoded, MultiEncodedBuf, P256_PRIV, P256_PUB, P384_PRIV,
P384_PUB, P521_PRIV, SECP256K1_PRIV, SECP256K1_PUB, X25519_PRIV, X25519_PUB,
},
};
pub use affinidi_crypto::KeyType;
use affinidi_crypto::{JWK, Params};
use base58::ToBase58;
use base64::{Engine, prelude::BASE64_URL_SAFE_NO_PAD};
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use sha2::{Digest, Sha256};
use tracing::warn;
use unsigned_varint::encode as varint_encode;
use x25519_dalek::{PublicKey, StaticSecret};
use zeroize::{Zeroize, ZeroizeOnDrop};
#[derive(Deserialize)]
struct SecretShadow {
id: String,
#[serde(rename = "type")]
type_: SecretType,
#[serde(flatten)]
secret_material: SecretMaterial,
}
#[derive(Debug, Clone, Deserialize, Serialize, Zeroize, ZeroizeOnDrop)]
#[serde(try_from = "SecretShadow")]
pub struct Secret {
pub id: String,
#[serde(rename = "type")]
pub type_: SecretType,
#[serde(flatten)]
pub secret_material: SecretMaterial,
#[serde(skip)]
pub(crate) private_bytes: Vec<u8>,
#[serde(skip)]
pub(crate) public_bytes: Vec<u8>,
#[serde(skip)]
pub(crate) key_type: KeyType,
}
impl TryFrom<SecretShadow> for Secret {
type Error = SecretsResolverError;
fn try_from(shadow: SecretShadow) -> Result<Self> {
match shadow.secret_material {
SecretMaterial::JWK(jwk) => {
let mut secret = Secret::from_jwk(&jwk)?;
secret.id = shadow.id;
secret.type_ = shadow.type_;
Ok(secret)
}
SecretMaterial::PrivateKeyMultibase(private) => {
Secret::from_multibase(&private, Some(&shadow.id))
}
_ => Err(SecretsResolverError::KeyError(
"Unsupported secret material type".into(),
)),
}
}
}
impl Secret {
fn convert_to_raw(input: &str) -> Result<Vec<u8>> {
BASE64_URL_SAFE_NO_PAD
.decode(input)
.map_err(|e| SecretsResolverError::KeyError(format!("Failed to decode base64url: {e}")))
}
pub fn from_jwk(jwk: &JWK) -> Result<Self> {
match &jwk.params {
Params::EC(params) => {
let mut x = Secret::convert_to_raw(¶ms.x)?;
let mut y = Secret::convert_to_raw(¶ms.y)?;
x.append(&mut y);
Ok(Secret {
id: jwk.key_id.as_ref().unwrap_or(&"".to_string()).to_string(),
type_: SecretType::JsonWebKey2020,
secret_material: SecretMaterial::JWK(jwk.to_owned()),
private_bytes: Secret::convert_to_raw(params.d.as_ref().ok_or(
SecretsResolverError::KeyError(
"Must have secret key available".to_string(),
),
)?)?,
public_bytes: x,
key_type: KeyType::try_from(params.curve.as_str())?,
})
}
Params::OKP(params) => Ok(Secret {
id: jwk.key_id.as_ref().unwrap_or(&"".to_string()).to_string(),
type_: SecretType::JsonWebKey2020,
secret_material: SecretMaterial::JWK(jwk.to_owned()),
private_bytes: Secret::convert_to_raw(params.d.as_ref().ok_or(
SecretsResolverError::KeyError("Must have secret key available".to_string()),
)?)?,
public_bytes: Secret::convert_to_raw(¶ms.x)?,
key_type: KeyType::try_from(params.curve.as_str())?,
}),
}
}
pub fn from_str(key_id: &str, jwk: &Value) -> Result<Self> {
let mut jwk: JWK = serde_json::from_value(jwk.to_owned())
.map_err(|e| SecretsResolverError::KeyError(format!("Failed to parse JWK: {e}")))?;
jwk.key_id = Some(key_id.to_string());
Self::from_jwk(&jwk)
}
pub fn from_multibase(private: &str, kid: Option<&str>) -> Result<Self> {
let private_bytes = multibase::decode(private).map_err(|e| {
SecretsResolverError::KeyError(format!("Failed to decode private key: {e}"))
})?;
let private_bytes = MultiEncoded::new(private_bytes.1.as_slice())?;
match private_bytes.codec() {
ED25519_PRIV => {
if private_bytes.data().len() != 32 {
return Err(SecretsResolverError::KeyError(
"Invalid ED25519 private key length".into(),
));
}
let mut pb: [u8; 32] = [0; 32];
pb.copy_from_slice(private_bytes.data());
let secret = Secret::generate_ed25519(kid, Some(&pb));
pb.zeroize();
Ok(secret)
}
X25519_PRIV => {
if private_bytes.data().len() != 32 {
return Err(SecretsResolverError::KeyError(
"Invalid X25519 private key length".into(),
));
}
let mut pb: [u8; 32] = [0; 32];
pb.copy_from_slice(private_bytes.data());
let secret = Secret::generate_x25519(kid, Some(&pb));
pb.zeroize();
secret
}
P256_PRIV => {
if private_bytes.data().len() != 32 {
return Err(SecretsResolverError::KeyError(
"Invalid P256 private key length".into(),
));
}
Secret::generate_p256(kid, Some(private_bytes.data()))
}
P384_PRIV => Secret::generate_p384(kid, Some(private_bytes.data())),
SECP256K1_PRIV => Secret::generate_secp256k1(kid, Some(private_bytes.data())),
_ => Err(SecretsResolverError::KeyError(
"Unsupported key type in from_multibase".into(),
)),
}
}
pub fn decode_multikey(key: &str) -> Result<Vec<u8>> {
let bytes = multibase::decode(key).map_err(|e| {
SecretsResolverError::KeyError(format!("Failed to multibase.decode key: {e}"))
})?;
let bytes = MultiEncoded::new(bytes.1.as_slice()).map_err(|e| {
SecretsResolverError::KeyError(format!("Failed to load decoded key: {e}"))
})?;
Ok(bytes.data().to_vec())
}
pub fn get_public_keymultibase(&self) -> Result<String> {
let encoded = match self.key_type {
KeyType::Ed25519 => MultiEncodedBuf::encode_bytes(ED25519_PUB, &self.public_bytes),
KeyType::X25519 => MultiEncodedBuf::encode_bytes(X25519_PUB, &self.public_bytes),
KeyType::P256 => {
let parity: u8 = if self.public_bytes[64].is_multiple_of(2) {
0x02
} else {
0x03
};
let mut compressed: [u8; 33] = [0; 33];
compressed[0] = parity;
for x in self.public_bytes[1..33].iter().enumerate() {
compressed[x.0 + 1] = *x.1;
}
MultiEncodedBuf::encode_bytes(P256_PUB, &compressed)
}
KeyType::P384 => {
let parity: u8 = if self.public_bytes[96].is_multiple_of(2) {
0x02
} else {
0x03
};
let mut compressed: [u8; 49] = [0; 49];
compressed[0] = parity;
for x in self.public_bytes[1..49].iter().enumerate() {
compressed[x.0 + 1] = *x.1;
}
MultiEncodedBuf::encode_bytes(P384_PUB, &compressed)
}
KeyType::P521 => {
return Err(SecretsResolverError::KeyError(
"P-521 is not supported".to_string(),
));
}
KeyType::Secp256k1 => {
let parity: u8 = if self.public_bytes[64].is_multiple_of(2) {
0x02
} else {
0x03
};
let mut compressed: [u8; 33] = [0; 33];
compressed[0] = parity;
for x in self.public_bytes[1..33].iter().enumerate() {
compressed[x.0 + 1] = *x.1;
}
MultiEncodedBuf::encode_bytes(SECP256K1_PUB, &compressed)
}
_ => {
return Err(SecretsResolverError::KeyError(
"Unsupported key type".into(),
));
}
};
Ok(multibase::encode(
multibase::Base::Base58Btc,
encoded.into_bytes(),
))
}
pub fn get_public_keymultibase_hash(&self) -> Result<String> {
let key = self.get_public_keymultibase()?;
Secret::base58_hash_string(&key)
}
pub fn base58_hash_string(key: &str) -> Result<String> {
let hash = Sha256::digest(key.as_bytes());
let mut code_buf = varint_encode::u64_buffer();
let code_varint = varint_encode::u64(0x12, &mut code_buf);
let mut len_buf = varint_encode::u64_buffer();
let len_varint = varint_encode::u64(hash.len() as u64, &mut len_buf);
let mut bytes = Vec::with_capacity(code_varint.len() + len_varint.len() + hash.len());
bytes.extend_from_slice(code_varint);
bytes.extend_from_slice(len_varint);
bytes.extend_from_slice(&hash);
Ok(bytes.to_base58())
}
pub fn get_private_keymultibase(&self) -> Result<String> {
let encoded = match self.key_type {
KeyType::Ed25519 => MultiEncodedBuf::encode_bytes(ED25519_PRIV, &self.private_bytes),
KeyType::X25519 => MultiEncodedBuf::encode_bytes(X25519_PRIV, &self.private_bytes),
KeyType::P256 => MultiEncodedBuf::encode_bytes(P256_PRIV, &self.private_bytes),
KeyType::P384 => MultiEncodedBuf::encode_bytes(P384_PRIV, &self.private_bytes),
KeyType::P521 => MultiEncodedBuf::encode_bytes(P521_PRIV, &self.private_bytes),
KeyType::Secp256k1 => {
MultiEncodedBuf::encode_bytes(SECP256K1_PRIV, &self.private_bytes)
}
_ => {
return Err(SecretsResolverError::KeyError(
"Unsupported key type".into(),
));
}
};
Ok(multibase::encode(
multibase::Base::Base58Btc,
encoded.into_bytes(),
))
}
pub fn get_public_bytes(&self) -> &[u8] {
self.public_bytes.as_slice()
}
pub fn get_private_bytes(&self) -> &[u8] {
self.private_bytes.as_slice()
}
pub fn get_key_type(&self) -> KeyType {
self.key_type
}
pub fn to_x25519(&self) -> Result<Secret> {
if self.key_type != KeyType::Ed25519 {
warn!(
"Can only convert ED25519 to X25519! Current key type is {:#?}",
self.key_type
);
Err(SecretsResolverError::KeyError(format!(
"Can only convert ED25519 to X25519! Current key type is {:#?}",
self.key_type
)))
} else {
let x25519_secret = affinidi_crypto::ed25519::ed25519_private_to_x25519(
self.private_bytes.first_chunk::<32>().unwrap(),
);
let x25519_sk = StaticSecret::from(x25519_secret);
let x25519_pk = PublicKey::from(&x25519_sk);
let secret = BASE64_URL_SAFE_NO_PAD.encode(x25519_sk.as_bytes());
let public = BASE64_URL_SAFE_NO_PAD.encode(x25519_pk.as_bytes());
let jwk = json!({
"crv": "X25519",
"d": secret,
"kty": "OKP",
"x": public
});
Secret::from_str(&self.id, &jwk)
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize, Zeroize)]
pub enum SecretType {
JsonWebKey2020,
X25519KeyAgreementKey2019,
X25519KeyAgreementKey2020,
Ed25519VerificationKey2018,
Ed25519VerificationKey2020,
EcdsaSecp256k1VerificationKey2019,
Multikey,
Other,
}
#[derive(Debug, Clone, Deserialize, Serialize, Zeroize)]
#[serde(rename_all = "camelCase")]
pub enum SecretMaterial {
#[serde(rename = "privateKeyJwk")]
JWK(JWK),
PrivateKeyMultibase(String),
Base58 {
private_key_base58: String,
},
Multibase {
private_key_multibase: String,
},
}
#[cfg(test)]
mod tests {
use super::Secret;
use base64::{Engine, prelude::BASE64_URL_SAFE_NO_PAD};
use serde_json::json;
#[test]
fn check_hash() {
let input = "z6MkgfFvvWA7sw8WkNWyK3y74kwNVvWc7Qrs5tWnsnqMfLD3";
let output = Secret::base58_hash_string(input).expect("Hash of input");
assert_eq!(&output, "QmY1kaguPMgjndEh1sdDZ8kdjX4Uc1SW4vziMfgWC6ndnJ")
}
#[test]
fn check_hash_bad() {
let input = "z6MkgfFvvWA7sw8WkNWyK3y74kwNVvWc7Qrs5tWnsnqMfLD4";
let output = Secret::base58_hash_string(input).expect("Hash of input");
assert_ne!(&output, "QmY1kaguPMgjndEh1sdDZ8kdjX4Uc1SW4vziMfgWC6ndnJ")
}
#[test]
fn check_x25519() {
let x25519_sk_bytes: [u8; 32] = [
200, 255, 64, 61, 17, 52, 112, 33, 205, 71, 186, 13, 131, 12, 241, 136, 223, 5, 152,
40, 95, 187, 83, 168, 142, 10, 234, 215, 70, 210, 148, 104,
];
let jwk = json!({
"crv": "Ed25519",
"d": "ymjvUTVuUPzGF5ui12LfreO8bjZ_LbnOrh0sk0xCxMM",
"kty": "OKP",
"x": "d17TbZmkoYHZUQpzJTcuOtq0tjWYm8CKvKGYHDW6ZaE"
});
let ed25519 = Secret::from_str("test", &jwk).unwrap();
let x25519 = ed25519
.to_x25519()
.expect("Couldn't convert ed25519 to x25519");
assert_eq!(x25519.private_bytes.as_slice(), x25519_sk_bytes);
}
#[test]
fn check_secret_deserialize() {
let txt = r#"{
"id": "did:web:localhost%3A7037:mediator:v1:.well-known#key-2",
"type": "JsonWebKey2020",
"privateKeyJwk": {
"crv": "secp256k1",
"d": "Cs5xn7WCkUWEua5vGxjP9_wBzIzMtEwjQ4KWKHHQR14",
"kty": "EC",
"x": "Lk1FY8MmyLjBswU4KbLoBQ_1THZJBMx2n6aIBXt1uXo",
"y": "tEv7EQHj4g4njOfrsjjDJBPKOI9RGWWMS8NYClo2cqo"
}
}"#;
let secret = serde_json::from_str::<Secret>(txt);
assert!(secret.is_ok());
}
#[test]
fn from_multiencode_ed25519() {
let seed = BASE64_URL_SAFE_NO_PAD
.decode("oihAhqs-h9V9rq6KYEhiEWwdBDpTI7xL0EEiwC9heFg")
.expect("Couldn't decode ed25519 BASE64 encoding");
let public_bytes = BASE64_URL_SAFE_NO_PAD
.decode("eC1vNebw6IJ8SJ4Tg9g2Q9W-Zy8xIS80byxTZXlPaHk")
.expect("Couldn't BASE64 decode ed25519 public bytes");
assert_eq!(seed.len(), 32);
let mut private_bytes: [u8; 32] = [0; 32];
private_bytes.copy_from_slice(seed.as_slice());
let secret = Secret::generate_ed25519(None, Some(&private_bytes));
assert_eq!(
secret.get_private_keymultibase().unwrap(),
"z3u2c8oS2oKgATvakQzVF66EAcZWJqPUzGQzWMUTKnFkv5DR"
);
assert_eq!(secret.get_public_bytes(), public_bytes.as_slice());
assert_eq!(secret.get_private_bytes(), private_bytes.as_slice());
let secret2 =
Secret::from_multibase("z3u2c8oS2oKgATvakQzVF66EAcZWJqPUzGQzWMUTKnFkv5DR", None)
.expect("Failed to transform ed25519 to secret");
assert_eq!(
secret2.get_public_keymultibase().unwrap(),
secret.get_public_keymultibase().unwrap()
);
assert_eq!(secret2.get_public_bytes(), secret.get_public_bytes());
assert_eq!(secret2.get_private_bytes(), secret.get_private_bytes());
assert_eq!(
secret2.get_private_keymultibase().unwrap(),
secret.get_private_keymultibase().unwrap()
);
}
#[test]
fn from_multiencode_x25519() {
let seed = BASE64_URL_SAFE_NO_PAD
.decode("eYN37ZX0ij4TYdklZax2jiRiyHYMNOzwW2bvNauAzKk")
.expect("Couldn't decode x25519 BASE64 encoding");
let public_bytes = BASE64_URL_SAFE_NO_PAD
.decode("Ephwf5xVmhVnDj2KtIPDKcGYBG9CQR_mZKlRqETZ62U")
.expect("Couldn't BASE64 decode x25519 public bytes");
assert_eq!(seed.len(), 32);
let mut private_bytes: [u8; 32] = [0; 32];
private_bytes.copy_from_slice(seed.as_slice());
let secret = Secret::generate_x25519(None, Some(&private_bytes))
.expect("x25519 generate secret failed");
assert_eq!(
secret.get_private_keymultibase().unwrap(),
"z3weexK9erGUKF41d3tJoDu2Fetx1xnsC7WhFWnjuCJXJGxp"
);
assert_eq!(secret.get_public_bytes(), public_bytes.as_slice());
assert_eq!(secret.get_private_bytes(), private_bytes.as_slice());
let secret2 =
Secret::from_multibase("z3weexK9erGUKF41d3tJoDu2Fetx1xnsC7WhFWnjuCJXJGxp", None)
.expect("Failed to transform x25519 to secret");
assert_eq!(
secret2.get_public_keymultibase().unwrap(),
secret.get_public_keymultibase().unwrap()
);
assert_eq!(secret2.get_public_bytes(), secret.get_public_bytes());
assert_eq!(secret2.get_private_bytes(), secret.get_private_bytes());
assert_eq!(
secret2.get_private_keymultibase().unwrap(),
secret.get_private_keymultibase().unwrap()
);
}
#[test]
fn from_multiencode_p256() {
let seed = BASE64_URL_SAFE_NO_PAD
.decode("B5ZIiXYkpEPczVbyWP85H75wrBifiRcFgtqYvI5I9AI")
.expect("Couldn't decode P-256 BASE64 encoding");
let pub_x = BASE64_URL_SAFE_NO_PAD
.decode("Iy3cHBWCRhcjohhS-iSucYMUNjH77DIQRSdn-NylcCw")
.expect("Couldn't BASE64 decode P-256 X public bytes");
let pub_y = BASE64_URL_SAFE_NO_PAD
.decode("p9MikGh-O3nbLWA-6tP4Oanch5AF3ZhRD907tQojH3k")
.expect("Couldn't BASE64 decode P-256 Y public bytes");
let public_bytes = [vec![4], pub_x, pub_y].concat();
assert_eq!(seed.len(), 32);
let mut private_bytes: [u8; 32] = [0; 32];
private_bytes.copy_from_slice(seed.as_slice());
let secret =
Secret::generate_p256(None, Some(&private_bytes)).expect("P256 secret generate failed");
assert_eq!(
secret.get_private_keymultibase().unwrap(),
"z42tiPvqM1uFz2QxbF7wTsQkfAf3hCsq1Uf9JbUMRaRiV1yb"
);
assert_eq!(secret.get_public_bytes(), public_bytes.as_slice());
assert_eq!(secret.get_private_bytes(), private_bytes.as_slice());
let secret2 =
Secret::from_multibase("z42tiPvqM1uFz2QxbF7wTsQkfAf3hCsq1Uf9JbUMRaRiV1yb", None)
.expect("Failed to transform P256 to secret");
assert_eq!(
secret2.get_public_keymultibase().unwrap(),
secret.get_public_keymultibase().unwrap()
);
assert_eq!(secret2.get_public_bytes(), secret.get_public_bytes());
assert_eq!(secret2.get_private_bytes(), secret.get_private_bytes());
assert_eq!(
secret2.get_private_keymultibase().unwrap(),
secret.get_private_keymultibase().unwrap()
);
}
#[test]
fn from_multiencode_p384() {
let seed = BASE64_URL_SAFE_NO_PAD
.decode("nka5zKVpVpOdCKdZZgnZ-VaSXk6V_ovYibzr2nf-mKAgct6wBdvWCXWLaNr80zY0")
.expect("Couldn't decode P-384 BASE64 encoding");
let pub_x = BASE64_URL_SAFE_NO_PAD
.decode("uitQkpTA3Vw8t_qOGrdLlbIzdzF0K9NsScgsVgmpQdQJgshCifOCUehxeazzL-Ow")
.expect("Couldn't BASE64 decode P-384 X public bytes");
let pub_y = BASE64_URL_SAFE_NO_PAD
.decode("4BIcrueQfhxfnrqToZEOujOfJOmwEsWJAdFNZ9dksIBCnWiCLBEn2HnR7ikyyPMJ")
.expect("Couldn't BASE64 decode P-384 Y public bytes");
let public_bytes = [vec![4], pub_x, pub_y].concat();
assert_eq!(seed.len(), 48);
let mut private_bytes: [u8; 48] = [0; 48];
private_bytes.copy_from_slice(seed.as_slice());
let secret =
Secret::generate_p384(None, Some(&private_bytes)).expect("P384 secret generate failed");
assert_eq!(
secret.get_private_keymultibase().unwrap(),
"z2fapqKp6mPoQCwkQzvL9Ns35Y57R4LRRfVwbXoSTQjTHdjD4MqFZnw5PueieuTWG4pN5q"
);
assert_eq!(secret.get_public_bytes(), public_bytes.as_slice());
assert_eq!(secret.get_private_bytes(), private_bytes.as_slice());
let secret2 = Secret::from_multibase(
"z2fapqKp6mPoQCwkQzvL9Ns35Y57R4LRRfVwbXoSTQjTHdjD4MqFZnw5PueieuTWG4pN5q",
None,
)
.expect("Failed to transform P384 to secret");
assert_eq!(
secret2.get_public_keymultibase().unwrap(),
secret.get_public_keymultibase().unwrap()
);
assert_eq!(secret2.get_public_bytes(), secret.get_public_bytes());
assert_eq!(secret2.get_private_bytes(), secret.get_private_bytes());
assert_eq!(
secret2.get_private_keymultibase().unwrap(),
secret.get_private_keymultibase().unwrap()
);
}
#[test]
fn from_multiencode_secp256k1() {
let seed = BASE64_URL_SAFE_NO_PAD
.decode("CzR8XKYmrxbeEeUKojSgXUskLmGjbLXFf4CoJd6he6A")
.expect("Couldn't decode secp256k1 BASE64 encoding");
let pub_x = BASE64_URL_SAFE_NO_PAD
.decode("jcGMDsxKBME8GmaN_-XTaAEKk2ET6ajWe_8-2RsU-is")
.expect("Couldn't BASE64 decode secp256k1 X public bytes");
let pub_y = BASE64_URL_SAFE_NO_PAD
.decode("9ECTinCwW9bA36fmUBg0_iu0oyLR-Tn54guX8exrUjM")
.expect("Couldn't BASE64 decode secp256k1 Y public bytes");
let public_bytes = [vec![4], pub_x, pub_y].concat();
assert_eq!(seed.len(), 32);
let mut private_bytes: [u8; 32] = [0; 32];
private_bytes.copy_from_slice(seed.as_slice());
let secret = Secret::generate_secp256k1(None, Some(&private_bytes))
.expect("secp256k1 secret generate failed");
assert_eq!(
secret.get_private_keymultibase().unwrap(),
"z3vLUkda21MTbdECEEyjUWEQmJ8r1CKekvRLqQbZXxfLieL7"
);
assert_eq!(secret.get_public_bytes(), public_bytes.as_slice());
assert_eq!(secret.get_private_bytes(), private_bytes.as_slice());
let secret2 =
Secret::from_multibase("z3vLUkda21MTbdECEEyjUWEQmJ8r1CKekvRLqQbZXxfLieL7", None)
.expect("Failed to transform secp256k1 to secret");
assert_eq!(
secret2.get_public_keymultibase().unwrap(),
secret.get_public_keymultibase().unwrap()
);
assert_eq!(secret2.get_public_bytes(), secret.get_public_bytes());
assert_eq!(secret2.get_private_bytes(), secret.get_private_bytes());
assert_eq!(
secret2.get_private_keymultibase().unwrap(),
secret.get_private_keymultibase().unwrap()
);
}
}