use crate::cbor::value::Value;
#[allow(unused_imports)]
use crate::nostd_prelude::*;
use super::key_maps as keys;
pub fn value_to_json(v: &Value) -> serde_json::Value {
match v {
Value::Null => serde_json::Value::Null,
Value::Bool(b) => serde_json::Value::Bool(*b),
Value::Integer(n) => {
if let Ok(i) = i64::try_from(*n) {
serde_json::Value::Number(serde_json::Number::from(i))
} else {
serde_json::Value::String(n.to_string())
}
}
Value::Float(f) => serde_json::Number::from_f64(*f)
.map(serde_json::Value::Number)
.unwrap_or(serde_json::Value::Null),
Value::Text(t) => serde_json::Value::String(t.clone()),
Value::Bytes(b) => {
serde_json::Value::String(base64_encode(b))
}
Value::Array(arr) => serde_json::Value::Array(arr.iter().map(value_to_json).collect()),
Value::Map(entries) => {
let mut obj = serde_json::Map::new();
for (k, v) in entries {
let key_str = match k {
Value::Integer(n) => {
match i64::try_from(*n) {
Ok(n_i64) => keys::int_to_string_key(n_i64)
.map(|s| s.to_string())
.unwrap_or_else(|| n_i64.to_string()),
Err(_) => n.to_string(),
}
}
Value::Text(t) => t.clone(),
_ => format!("{:?}", k),
};
obj.insert(key_str, value_to_json(v));
}
serde_json::Value::Object(obj)
}
Value::Tag(tag, inner) => tag_to_json(*tag, inner),
}
}
pub fn json_to_value(j: &serde_json::Value) -> Value {
match j {
serde_json::Value::Null => Value::Null,
serde_json::Value::Bool(b) => Value::Bool(*b),
serde_json::Value::Number(n) => {
if let Some(i) = n.as_i64() {
Value::Integer(i as i128)
} else if let Some(u) = n.as_u64() {
Value::Integer(u as i128)
} else if let Some(f) = n.as_f64() {
Value::Float(f)
} else {
Value::Integer(0)
}
}
serde_json::Value::String(s) => {
Value::Text(s.clone())
}
serde_json::Value::Array(arr) => Value::Array(arr.iter().map(json_to_value).collect()),
serde_json::Value::Object(obj) => {
if let Some(tag_num) = obj.get("__cbor_tag") {
if let Some(tag_val) = obj.get("__cbor_value") {
if let Some(n) = tag_num.as_u64() {
return Value::Tag(n, Box::new(json_to_value(tag_val)));
}
}
}
if let (Some(serde_json::Value::String(type_name)), Some(value)) =
(obj.get("type"), obj.get("value"))
{
if obj.len() == 2 {
return type_choice_to_value(type_name, value);
}
}
let entries: Vec<(Value, Value)> = obj
.iter()
.map(|(k, v)| {
let key = keys::string_to_int_key(k)
.map(|i| Value::Integer(i as i128))
.unwrap_or_else(|| {
k.parse::<i64>()
.map(|i| Value::Integer(i as i128))
.unwrap_or_else(|_| Value::Text(k.clone()))
});
(key, json_to_value(v))
})
.collect();
Value::Map(entries)
}
}
}
fn tag_to_json(tag: u64, inner: &Value) -> serde_json::Value {
match tag {
1 => value_to_json(inner),
37 => match inner {
Value::Bytes(b) if b.len() == 16 => {
type_choice_json("uuid", serde_json::Value::String(format_uuid(b)))
}
_ => type_choice_json("uuid", value_to_json(inner)),
},
111 => type_choice_json("oid", value_to_json(inner)),
550 => match inner {
Value::Bytes(b) => {
type_choice_json("ueid", serde_json::Value::String(base64_encode(b)))
}
_ => type_choice_json("ueid", value_to_json(inner)),
},
552 => type_choice_json("svn", value_to_json(inner)),
553 => type_choice_json("min-svn", value_to_json(inner)),
554 => type_choice_json("pkix-base64-key", value_to_json(inner)),
555 => type_choice_json("pkix-base64-cert", value_to_json(inner)),
556 => type_choice_json("pkix-base64-cert-path", value_to_json(inner)),
557 => type_choice_json("key-thumbprint", value_to_json(inner)),
558 => type_choice_json("cose-key", value_to_json(inner)),
559 => type_choice_json("cert-thumbprint", value_to_json(inner)),
560 => type_choice_json("bytes", value_to_json(inner)),
561 => type_choice_json("cert-path-thumbprint", value_to_json(inner)),
562 => type_choice_json("pkix-asn1der-cert", value_to_json(inner)),
563 => type_choice_json("masked-raw-value", value_to_json(inner)),
564 => type_choice_json("int-range", value_to_json(inner)),
505 | 506 | 508 => {
let mut obj = serde_json::Map::new();
obj.insert("__cbor_tag".into(), serde_json::Value::Number(tag.into()));
obj.insert("__cbor_value".into(), value_to_json(inner));
serde_json::Value::Object(obj)
}
_ => {
let mut obj = serde_json::Map::new();
obj.insert("__cbor_tag".into(), serde_json::Value::Number(tag.into()));
obj.insert("__cbor_value".into(), value_to_json(inner));
serde_json::Value::Object(obj)
}
}
}
fn type_choice_to_value(type_name: &str, value: &serde_json::Value) -> Value {
match type_name {
"uuid" => {
if let Some(s) = value.as_str() {
if let Some(bytes) = parse_uuid(s) {
return Value::Tag(37, Box::new(Value::Bytes(bytes)));
}
}
Value::Tag(37, Box::new(json_to_value(value)))
}
"oid" => Value::Tag(111, Box::new(json_to_value(value))),
"ueid" => {
if let Some(s) = value.as_str() {
if let Ok(bytes) = base64_decode(s) {
return Value::Tag(550, Box::new(Value::Bytes(bytes)));
}
}
Value::Tag(550, Box::new(json_to_value(value)))
}
"bytes" => {
if let Some(s) = value.as_str() {
if let Ok(bytes) = base64_decode(s) {
return Value::Tag(560, Box::new(Value::Bytes(bytes)));
}
}
Value::Tag(560, Box::new(json_to_value(value)))
}
"svn" => Value::Tag(552, Box::new(json_to_value(value))),
"min-svn" => Value::Tag(553, Box::new(json_to_value(value))),
"pkix-base64-key" => Value::Tag(554, Box::new(json_to_value(value))),
"pkix-base64-cert" => Value::Tag(555, Box::new(json_to_value(value))),
"pkix-base64-cert-path" => Value::Tag(556, Box::new(json_to_value(value))),
"key-thumbprint" => Value::Tag(557, Box::new(json_to_value(value))),
"cose-key" => Value::Tag(558, Box::new(json_to_value(value))),
"cert-thumbprint" => Value::Tag(559, Box::new(json_to_value(value))),
"cert-path-thumbprint" => Value::Tag(561, Box::new(json_to_value(value))),
"pkix-asn1der-cert" => Value::Tag(562, Box::new(json_to_value(value))),
"masked-raw-value" => Value::Tag(563, Box::new(json_to_value(value))),
"int-range" => Value::Tag(564, Box::new(json_to_value(value))),
_ => {
let entries = vec![
(Value::Text("type".into()), Value::Text(type_name.into())),
(Value::Text("value".into()), json_to_value(value)),
];
Value::Map(entries)
}
}
}
fn type_choice_json(type_name: &str, value: serde_json::Value) -> serde_json::Value {
let mut obj = serde_json::Map::new();
obj.insert("type".into(), serde_json::Value::String(type_name.into()));
obj.insert("value".into(), value);
serde_json::Value::Object(obj)
}
fn format_uuid(bytes: &[u8]) -> String {
let hex = |s: &[u8]| -> String { s.iter().map(|b| format!("{:02x}", b)).collect() };
format!(
"{}-{}-{}-{}-{}",
hex(&bytes[0..4]),
hex(&bytes[4..6]),
hex(&bytes[6..8]),
hex(&bytes[8..10]),
hex(&bytes[10..16])
)
}
fn parse_uuid(s: &str) -> Option<Vec<u8>> {
let hex: String = s.chars().filter(|c| *c != '-').collect();
if hex.len() != 32 {
return None;
}
let mut bytes = Vec::with_capacity(16);
for i in (0..32).step_by(2) {
bytes.push(u8::from_str_radix(&hex[i..i + 2], 16).ok()?);
}
Some(bytes)
}
fn base64_encode(bytes: &[u8]) -> String {
const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut result = String::new();
for chunk in bytes.chunks(3) {
let b0 = chunk[0] as u32;
let b1 = if chunk.len() > 1 { chunk[1] as u32 } else { 0 };
let b2 = if chunk.len() > 2 { chunk[2] as u32 } else { 0 };
let n = (b0 << 16) | (b1 << 8) | b2;
result.push(ALPHABET[((n >> 18) & 63) as usize] as char);
result.push(ALPHABET[((n >> 12) & 63) as usize] as char);
if chunk.len() > 1 {
result.push(ALPHABET[((n >> 6) & 63) as usize] as char);
} else {
result.push('=');
}
if chunk.len() > 2 {
result.push(ALPHABET[(n & 63) as usize] as char);
} else {
result.push('=');
}
}
result
}
fn base64_decode(s: &str) -> Result<Vec<u8>, ()> {
fn val(c: u8) -> Result<u32, ()> {
match c {
b'A'..=b'Z' => Ok((c - b'A') as u32),
b'a'..=b'z' => Ok((c - b'a' + 26) as u32),
b'0'..=b'9' => Ok((c - b'0' + 52) as u32),
b'+' => Ok(62),
b'/' => Ok(63),
b'=' => Ok(0),
_ => Err(()),
}
}
let bytes: Vec<u8> = s.bytes().filter(|b| !b.is_ascii_whitespace()).collect();
if bytes.len() % 4 != 0 {
return Err(());
}
let mut result = Vec::new();
for chunk in bytes.chunks(4) {
let a = val(chunk[0])?;
let b = val(chunk[1])?;
let c = val(chunk[2])?;
let d = val(chunk[3])?;
let n = (a << 18) | (b << 12) | (c << 6) | d;
result.push((n >> 16) as u8);
if chunk[2] != b'=' {
result.push(((n >> 8) & 0xFF) as u8);
}
if chunk[3] != b'=' {
result.push((n & 0xFF) as u8);
}
}
Ok(result)
}