Skip to content

Commit

Permalink
Merge pull request bitwarden#1 from Credential-Provider-SIG/progdrasi…
Browse files Browse the repository at this point in the history
…l/base64-parsing

Add serde to the types and make base64url parsing
  • Loading branch information
Progdrasil authored Oct 17, 2024
2 parents 33b4339 + 3800e20 commit 8fe1a8b
Show file tree
Hide file tree
Showing 5 changed files with 279 additions and 13 deletions.
65 changes: 65 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions credential-exchange-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ license-file.workspace = true
keywords.workspace = true

[dependencies]
serde = { version = "1", features = ["derive"] }
data-encoding = "2"
134 changes: 134 additions & 0 deletions credential-exchange-types/src/b64url.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
use data_encoding::{Specification, BASE32_NOPAD, BASE64URL, BASE64URL_NOPAD};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, Clone, Hash, PartialEq, Eq)]
#[serde(try_from = "&str", into = "String")]
pub struct B64Url(Vec<u8>);

impl From<Vec<u8>> for B64Url {
fn from(src: Vec<u8>) -> Self {
Self(src)
}
}
impl From<&[u8]> for B64Url {
fn from(src: &[u8]) -> Self {
Self(src.to_vec())
}
}

impl From<B64Url> for Vec<u8> {
fn from(src: B64Url) -> Self {
src.0
}
}

impl AsRef<[u8]> for B64Url {
fn as_ref(&self) -> &[u8] {
&self.0
}
}

impl From<B64Url> for String {
fn from(src: B64Url) -> Self {
String::from(&src)
}
}
impl From<&B64Url> for String {
fn from(src: &B64Url) -> Self {
BASE64URL_NOPAD.encode(&src.0)
}
}

impl std::fmt::Display for B64Url {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(String::from(self).as_str())
}
}

/// An error returned when a string is not base64 decodable.
#[derive(Debug)]
pub struct NotB64UrlEncoded;

impl std::fmt::Display for NotB64UrlEncoded {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Data isn't base64url encoded")
}
}

impl TryFrom<&str> for B64Url {
type Error = NotB64UrlEncoded;

fn try_from(value: &str) -> Result<Self, Self::Error> {
let specs = BASE64URL.specification();
let padding = specs.padding.unwrap();
let specs = Specification {
check_trailing_bits: false,
padding: None,
..specs
};
let encoding = specs.encoding().unwrap();
let sane_string = value.trim_end_matches(padding);
encoding
.decode(sane_string.as_bytes())
.map(Self)
.map_err(|_| NotB64UrlEncoded)
}
}

/// Newtype to encode and decode a vector of bytes to and from Base32.
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(try_from = "&str", into = "String")]
pub struct B32(Vec<u8>);

impl From<Vec<u8>> for B32 {
fn from(src: Vec<u8>) -> Self {
Self(src)
}
}
impl From<&[u8]> for B32 {
fn from(src: &[u8]) -> Self {
Self(src.to_vec())
}
}

impl From<B32> for Vec<u8> {
fn from(src: B32) -> Self {
src.0
}
}

impl AsRef<[u8]> for B32 {
fn as_ref(&self) -> &[u8] {
&self.0
}
}

impl From<B32> for String {
fn from(src: B32) -> Self {
BASE32_NOPAD.encode(&src.0)
}
}

/// The string was not base32 encoded
#[derive(Debug)]
pub struct NotBase32Encoded;

impl std::fmt::Display for NotBase32Encoded {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Data isn't base32 encoded")
}
}

impl TryFrom<&str> for B32 {
type Error = NotBase32Encoded;

fn try_from(value: &str) -> Result<Self, Self::Error> {
let symbols = BASE32_NOPAD.specification().symbols;
let mut sane_string: String = value.to_ascii_uppercase();
sane_string.retain(|c| symbols.contains(c));
BASE32_NOPAD
.decode(sane_string.as_bytes())
.map(Self)
.map_err(|_| NotBase32Encoded)
}
}
Loading

0 comments on commit 8fe1a8b

Please sign in to comment.