Skip to content

Commit

Permalink
feat: improve COSE Key in ns-inscriber
Browse files Browse the repository at this point in the history
  • Loading branch information
zensh committed Jan 22, 2024
1 parent 72d7fdb commit 26494e1
Show file tree
Hide file tree
Showing 9 changed files with 585 additions and 214 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# NS-RS — Inscribing Name service on Bitcoin network
# NS-RS — Inscribing naming trusted database on Bitcoin network

Rust implementation of [NS-Protocol](https://github.com/ldclabs/ns-protocol) (Name & Service Protocol) by the LDC Labs

Expand Down
2 changes: 1 addition & 1 deletion crates/ns-inscriber/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ns-inscriber"
version = "0.5.0"
version = "0.6.0"
edition = "2021"
rust-version = "1.64"
description = "Name & Service Protocol inscriber service in Rust"
Expand Down
215 changes: 127 additions & 88 deletions crates/ns-inscriber/src/bin/main.rs

Large diffs are not rendered by default.

189 changes: 101 additions & 88 deletions crates/ns-inscriber/src/wallet/cose_key.rs
Original file line number Diff line number Diff line change
@@ -1,79 +1,106 @@
use ciborium::Value;
use coset::{
iana, CborSerializable, CoseKey, CoseKeyBuilder, KeyType, Label, RegisteredLabelWithPrivate,
};
use ns_protocol::{
ns::Value,
state::{from_bytes, to_bytes},
};
use rand_core::{OsRng, RngCore};

use super::secp256k1::Keypair;
pub trait CoseSigner {
fn alg(&self) -> iana::Algorithm;
fn kid(&self) -> Vec<u8>;
fn sign(&self, data: &[u8]) -> Vec<u8>;
}

pub trait CoseVerifier {
fn alg(&self) -> iana::Algorithm;
fn verify(&self, data: &[u8], sig: &[u8]) -> Result<(), anyhow::Error>;
}

const KEY_PARAM_K: Label = Label::Int(iana::SymmetricKeyParameter::K as i64);
const KEY_PARAM_D: Label = Label::Int(iana::OkpKeyParameter::D as i64);
pub trait KeyHelper {
fn to_slice(self) -> anyhow::Result<Vec<u8>>;
fn kty(&self) -> iana::KeyType;
fn alg(&self) -> iana::Algorithm;
fn kid(&self) -> Option<Value>;
fn set_kid(&mut self, kid: Value) -> anyhow::Result<()>;
fn is_crv(&self, crv: iana::EllipticCurve) -> bool;
fn has_param(&self, key_label: &Label) -> bool;
fn get_param(&self, key_label: &Label) -> anyhow::Result<&Value>;

fn kid_string(&self) -> String {
if let Some(kid) = self.kid() {
match kid {
Value::Text(s) => return s,
Value::Bytes(b) => return hex::encode(b),
Value::Bool(b) => return b.to_string(),
Value::Integer(i) => return i128::from(i).to_string(),
v => {
return format!("{:?}", v);
}
}
}
"".to_string()
}

#[derive(Clone, Debug, Default, PartialEq)]
pub struct Key(pub CoseKey);
fn get_secret(&self) -> anyhow::Result<[u8; 32]> {
let key_label = match self.kty() {
iana::KeyType::Symmetric => Label::Int(iana::SymmetricKeyParameter::K as i64),
iana::KeyType::OKP => Label::Int(iana::OkpKeyParameter::D as i64),
iana::KeyType::EC2 => Label::Int(iana::Ec2KeyParameter::D as i64),
_ => {
return Err(anyhow::Error::msg("unsupport key type"));
}
};

impl Key {
pub fn new_sym(alg: iana::Algorithm, kid: &[u8]) -> anyhow::Result<Self> {
let val = self
.get_param(&key_label)?
.as_bytes()
.filter(|v| v.len() == 32)
.ok_or_else(|| anyhow::Error::msg("invalid secret key"))?;
let mut key = [0u8; 32];
OsRng.fill_bytes(&mut key);
key.copy_from_slice(val);
Ok(key)
}
}

let mut key = CoseKeyBuilder::new_symmetric_key(key.to_vec()).algorithm(alg);
if !kid.is_empty() {
key = key.key_id(kid.to_vec());
}
Ok(Self(key.build()))
impl KeyHelper for CoseKey {
fn to_slice(self) -> anyhow::Result<Vec<u8>> {
self.to_vec().map_err(anyhow::Error::msg)
}

pub fn ed25519_from_secret(secret: &[u8; 32], kid: &[u8]) -> anyhow::Result<Self> {
let mut key = CoseKeyBuilder::new_okp_key()
.algorithm(iana::Algorithm::EdDSA)
.param(
iana::OkpKeyParameter::Crv as i64,
Value::from(iana::EllipticCurve::Ed25519 as i64),
)
.param(
iana::OkpKeyParameter::D as i64,
Value::Bytes(secret.to_vec()),
);

if !kid.is_empty() {
key = key.key_id(kid.to_vec());
fn kty(&self) -> iana::KeyType {
match self.kty {
KeyType::Assigned(kty) => kty,
_ => iana::KeyType::Reserved,
}
Ok(Self(key.build()))
}

pub fn secp256k1_from_keypair(keypair: &Keypair, kid: &[u8]) -> anyhow::Result<Self> {
let mut key = CoseKey {
kty: KeyType::Assigned(iana::KeyType::EC2),
alg: Some(RegisteredLabelWithPrivate::Assigned(
iana::Algorithm::ES256K,
)),
params: vec![
(
Label::Int(iana::Ec2KeyParameter::Crv as i64),
Value::from(iana::EllipticCurve::Secp256k1 as i64),
),
(
Label::Int(iana::Ec2KeyParameter::D as i64),
Value::Bytes(keypair.secret_key().as_ref().to_vec()),
),
],
..Default::default()
};
fn alg(&self) -> iana::Algorithm {
if let Some(RegisteredLabelWithPrivate::Assigned(alg)) = self.alg {
return alg;
}

iana::Algorithm::Reserved
}

if !kid.is_empty() {
key.key_id.extend_from_slice(kid);
fn kid(&self) -> Option<Value> {
if self.key_id.is_empty() {
return None;
}
Ok(Self(key))

Some(from_bytes(&self.key_id).unwrap_or_else(|_e| Value::Bytes(self.key_id.clone())))
}

pub fn key_id(&self) -> Vec<u8> {
self.0.key_id.clone()
fn set_kid(&mut self, kid: Value) -> anyhow::Result<()> {
self.key_id = to_bytes(&kid)?;
Ok(())
}

pub fn is_crv(&self, crv: iana::EllipticCurve) -> bool {
for (label, value) in &self.0.params {
if label == &Label::Int(iana::Ec2KeyParameter::Crv as i64) {
fn is_crv(&self, crv: iana::EllipticCurve) -> bool {
for (label, value) in &self.params {
// https://www.iana.org/assignments/cose/cose.xhtml#key-type-parameters
if label == &Label::Int(-1i64) {
if let Some(val) = value.as_integer() {
return val == (crv as i64).into();
}
Expand All @@ -82,43 +109,29 @@ impl Key {
false
}

pub fn to_vec(self) -> anyhow::Result<Vec<u8>> {
self.0.to_vec().map_err(anyhow::Error::msg)
fn has_param(&self, key_label: &Label) -> bool {
self.params.iter().any(|(label, _)| label == key_label)
}

pub fn from_slice(data: &[u8]) -> anyhow::Result<Self> {
let key = CoseKey::from_slice(data).map_err(anyhow::Error::msg)?;
Ok(Self(key))
}

pub fn secret_key(&self) -> anyhow::Result<[u8; 32]> {
let key_param = match self.0.kty {
KeyType::Assigned(iana::KeyType::Symmetric) => &KEY_PARAM_K,
KeyType::Assigned(iana::KeyType::OKP) => &KEY_PARAM_D,
KeyType::Assigned(iana::KeyType::EC2) => &Label::Int(iana::Ec2KeyParameter::D as i64),
_ => {
return Err(anyhow::Error::msg("unsupport key type"));
}
};

for (label, value) in &self.0.params {
if label == key_param {
match value {
Value::Bytes(val) => {
if val.len() != 32 {
return Err(anyhow::Error::msg("invalid key length, expected 32"));
}
let mut key = [0u8; 32];
key.copy_from_slice(val);
return Ok(key);
}
_ => {
return Err(anyhow::Error::msg("invalid key type"));
}
}
fn get_param(&self, key_label: &Label) -> anyhow::Result<&Value> {
for (label, value) in &self.params {
if label == key_label {
return Ok(value);
}
}
Err(anyhow::Error::msg(format!("key {:?} not found", key_label)))
}
}

pub fn new_sym(alg: iana::Algorithm, kid: Option<Value>) -> anyhow::Result<CoseKey> {
let mut key = [0u8; 32];
OsRng.fill_bytes(&mut key);

Err(anyhow::Error::msg("invalid key"))
let mut key = CoseKeyBuilder::new_symmetric_key(key.to_vec())
.algorithm(alg)
.build();
if let Some(kid) = kid {
key.set_kid(kid)?;
}
Ok(key)
}
Loading

0 comments on commit 26494e1

Please sign in to comment.