-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
did-simple: implement varint decode and keytype detect
- Loading branch information
Showing
4 changed files
with
169 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/// A key algorithm. | ||
pub trait KeyAlgo { | ||
fn pub_key_size(&self) -> usize; | ||
fn multicodec_value(&self) -> u16; | ||
} | ||
|
||
/// A key algorithm that is known statically, at compile time. | ||
pub trait StaticKeyAlgo: KeyAlgo { | ||
const PUB_KEY_SIZE: usize; | ||
const MULTICODEC_VALUE: u16; | ||
} | ||
|
||
impl<T: StaticKeyAlgo> KeyAlgo for T { | ||
fn pub_key_size(&self) -> usize { | ||
Self::PUB_KEY_SIZE | ||
} | ||
|
||
fn multicodec_value(&self) -> u16 { | ||
Self::MULTICODEC_VALUE | ||
} | ||
} | ||
|
||
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] | ||
pub struct Ed25519; | ||
|
||
impl StaticKeyAlgo for Ed25519 { | ||
const PUB_KEY_SIZE: usize = 32; | ||
const MULTICODEC_VALUE: u16 = 0xED; | ||
} | ||
|
||
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] | ||
pub enum DynKeyAlgo { | ||
Ed25519, | ||
} | ||
|
||
impl PartialEq<Ed25519> for DynKeyAlgo { | ||
fn eq(&self, _other: &Ed25519) -> bool { | ||
*self == DynKeyAlgo::Ed25519 | ||
} | ||
} | ||
|
||
impl KeyAlgo for DynKeyAlgo { | ||
fn pub_key_size(&self) -> usize { | ||
match self { | ||
Self::Ed25519 => Ed25519::PUB_KEY_SIZE, | ||
} | ||
} | ||
|
||
fn multicodec_value(&self) -> u16 { | ||
match self { | ||
Self::Ed25519 => Ed25519::MULTICODEC_VALUE, | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
pub const fn decode_varint(encoded: &[u8]) -> Result<u16, DecodeError> { | ||
// TODO: Technically, some three byte encodings could fit into a u16, we | ||
// should support those in the future. | ||
// Luckily none of them are used for did:key afaik. | ||
if encoded.len() > 2 { | ||
return Err(DecodeError::WouldOverflow); | ||
} | ||
if encoded.is_empty() { | ||
return Err(DecodeError::MissingBytes); | ||
} | ||
|
||
/// bitmask for 7 least significant bits | ||
const LSB_7: u8 = u8::MAX / 2; | ||
/// bitmask for most significant bit | ||
const MSB: u8 = !LSB_7; | ||
|
||
#[inline] | ||
const fn msb_is_1(val: u8) -> bool { | ||
val & MSB == MSB | ||
} | ||
|
||
let a = encoded[0]; | ||
let mut result: u16 = (a & LSB_7) as u16; | ||
if msb_is_1(a) { | ||
// There is another 7 bits to decode. | ||
if encoded.len() < 2 { | ||
return Err(DecodeError::MissingBytes); | ||
} | ||
let b = encoded[1]; | ||
|
||
result |= ((b & LSB_7) as u16) << 7; | ||
if msb_is_1(b) { | ||
// We were provided a varint that ought to have had at least another byte. | ||
return Err(DecodeError::MissingBytes); | ||
} | ||
} | ||
Ok(result) | ||
} | ||
|
||
#[derive(thiserror::Error, Debug, Eq, PartialEq)] | ||
pub enum DecodeError { | ||
#[error("expected more bytes than what were provided")] | ||
MissingBytes, | ||
#[error( | ||
"the decoded number is too large to fit into the type without overflowing" | ||
)] | ||
WouldOverflow, | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_known_examples() { | ||
// See https://github.com/multiformats/unsigned-varint/blob/16bf9f7d3ff78c10c1ab26d397c03c91205cd4ee/README.md | ||
let examples1 = [ | ||
(0x01, [0x01].as_slice()), | ||
(0x02, &[0x02]), | ||
(0x7f, &[0x7f]), // 127 | ||
(0x80, &[0x80, 0x01]), // 128 | ||
(0x81, &[0x81, 0x01]), // 129 | ||
(0xff, &[0xff, 0x01]), // 255 | ||
(0x012c, &[0xac, 0x02]), // 300 | ||
]; | ||
let examples2 = [ | ||
(0xed, [0xed, 0x01].as_slice()), // ed25519 | ||
(0xec, &[0xec, 0x01]), // x25519 | ||
(0x1200, &[0x80, 0x24]), // P256 | ||
(0xe7, &[0xe7, 0x01]), // Secp256k1 | ||
]; | ||
|
||
for (decoded, encoded) in examples1.into_iter().chain(examples2) { | ||
assert_eq!(Ok(decoded), decode_varint(encoded)) | ||
} | ||
} | ||
} |