Skip to content

Commit 6529923

Browse files
committed
Added JSON type.
1 parent dbc09f8 commit 6529923

File tree

8 files changed

+231
-14
lines changed

8 files changed

+231
-14
lines changed

src/compressed.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -321,9 +321,7 @@ impl DigestProvider for Compressed {
321321
/// Panics if there is no digest associated with this compressed data.
322322
/// Use `has_digest()` or `digest_ref_opt()` to check before calling this
323323
/// method.
324-
fn digest(&self) -> Digest {
325-
self.digest.unwrap()
326-
}
324+
fn digest(&self) -> Digest { self.digest.unwrap() }
327325
}
328326

329327
/// Implementation of the `Debug` trait for `Compressed`.

src/ec_key/ec_uncompressed_public_key.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,7 @@ impl ECKey for ECUncompressedPublicKey {
111111
/// Implements the `ECPublicKeyBase` trait.
112112
impl ECPublicKeyBase for ECUncompressedPublicKey {
113113
/// Returns this uncompressed public key (self).
114-
fn uncompressed_public_key(&self) -> ECUncompressedPublicKey {
115-
*self
116-
}
114+
fn uncompressed_public_key(&self) -> ECUncompressedPublicKey { *self }
117115
}
118116

119117
/// Converts a fixed-size byte array to an `ECUncompressedPublicKey`.

src/encrypted_key/key_derivation_method.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ use crate::{Error, Result};
1212
/// Scrypt = 2
1313
/// Argon2id = 3
1414
/// ```
15-
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
15+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
1616
pub enum KeyDerivationMethod {
1717
HKDF = 0,
1818
PBKDF2 = 1,
1919
Scrypt = 2,
20+
#[default]
2021
Argon2id = 3,
2122
#[cfg(feature = "ssh-agent")]
2223
SSHAgent = 4,

src/json.rs

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
use bc_ur::prelude::*;
2+
3+
use crate::tags;
4+
5+
/// A CBOR-tagged container for UTF-8 JSON text.
6+
///
7+
/// The `JSON` type wraps UTF-8 JSON text as a CBOR byte string with tag 262.
8+
/// This allows JSON data to be embedded within CBOR structures while
9+
/// maintaining type information through the tag.
10+
///
11+
/// This implementation does not validate that the contained data is well-formed
12+
/// JSON. It simply provides a type-safe wrapper around byte data that is
13+
/// intended to contain JSON text.
14+
///
15+
/// # CBOR Serialization
16+
///
17+
/// `JSON` implements the `CBORTaggedCodable` trait, which means it can be
18+
/// serialized to and deserialized from CBOR with tag 262 (`TAG_JSON`).
19+
///
20+
/// # Examples
21+
///
22+
/// Creating JSON from a string:
23+
///
24+
/// ```
25+
/// use bc_components::JSON;
26+
///
27+
/// let json = JSON::from_string(r#"{"key": "value"}"#);
28+
/// assert_eq!(json.as_str(), r#"{"key": "value"}"#);
29+
/// ```
30+
///
31+
/// Creating JSON from bytes:
32+
///
33+
/// ```
34+
/// use bc_components::JSON;
35+
///
36+
/// let json = JSON::from_data(b"[1, 2, 3]");
37+
/// assert_eq!(json.len(), 9);
38+
/// ```
39+
#[derive(Clone, Eq, PartialEq)]
40+
pub struct JSON(Vec<u8>);
41+
42+
impl JSON {
43+
/// Return the length of the JSON data in bytes.
44+
pub fn len(&self) -> usize { self.0.len() }
45+
46+
/// Return true if the JSON data is empty.
47+
pub fn is_empty(&self) -> bool { self.0.is_empty() }
48+
49+
/// Create a new JSON instance from byte data.
50+
pub fn from_data(data: impl AsRef<[u8]>) -> Self {
51+
Self(data.as_ref().to_vec())
52+
}
53+
54+
/// Create a new JSON instance from a string.
55+
pub fn from_string(s: impl AsRef<str>) -> Self {
56+
Self::from_data(s.as_ref().as_bytes())
57+
}
58+
59+
/// Return the data as a byte slice.
60+
pub fn as_bytes(&self) -> &[u8] { self.as_ref() }
61+
62+
/// Return the data as a UTF-8 string slice.
63+
///
64+
/// # Panics
65+
///
66+
/// Panics if the data is not valid UTF-8.
67+
pub fn as_str(&self) -> &str {
68+
std::str::from_utf8(&self.0).expect("Invalid UTF-8 in JSON data")
69+
}
70+
71+
/// Create a new JSON instance from a hexadecimal string.
72+
pub fn from_hex(hex: impl AsRef<str>) -> Self {
73+
Self::from_data(hex::decode(hex.as_ref()).unwrap())
74+
}
75+
76+
/// Return the data as a hexadecimal string.
77+
pub fn hex(&self) -> String { hex::encode(self.as_bytes()) }
78+
}
79+
80+
/// Allows accessing the underlying data as a byte slice reference.
81+
impl<'a> From<&'a JSON> for &'a [u8] {
82+
fn from(value: &'a JSON) -> Self { value.as_bytes() }
83+
}
84+
85+
/// Allows using a JSON as a reference to a byte slice.
86+
impl AsRef<[u8]> for JSON {
87+
fn as_ref(&self) -> &[u8] { &self.0 }
88+
}
89+
90+
/// Provides a self-reference, enabling API consistency with other types.
91+
impl AsRef<JSON> for JSON {
92+
fn as_ref(&self) -> &JSON { self }
93+
}
94+
95+
/// Identifies the CBOR tags used for JSON serialization.
96+
impl CBORTagged for JSON {
97+
fn cbor_tags() -> Vec<Tag> { tags_for_values(&[tags::TAG_JSON]) }
98+
}
99+
100+
/// Enables conversion of JSON into a tagged CBOR value.
101+
impl From<JSON> for CBOR {
102+
fn from(value: JSON) -> Self { value.tagged_cbor() }
103+
}
104+
105+
/// Defines how JSON is encoded as CBOR (as a byte string).
106+
impl CBORTaggedEncodable for JSON {
107+
fn untagged_cbor(&self) -> CBOR { CBOR::to_byte_string(self.as_bytes()) }
108+
}
109+
110+
/// Enables conversion from CBOR to JSON, with proper error handling.
111+
impl TryFrom<CBOR> for JSON {
112+
type Error = dcbor::Error;
113+
114+
fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
115+
Self::from_tagged_cbor(cbor)
116+
}
117+
}
118+
119+
/// Defines how JSON is decoded from CBOR.
120+
impl CBORTaggedDecodable for JSON {
121+
fn from_untagged_cbor(untagged_cbor: CBOR) -> dcbor::Result<Self> {
122+
let data = CBOR::try_into_byte_string(untagged_cbor)?;
123+
let instance = Self::from_data(data);
124+
Ok(instance)
125+
}
126+
}
127+
128+
/// Provides a debug representation showing the JSON data as a string.
129+
impl std::fmt::Debug for JSON {
130+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131+
write!(f, "JSON({})", self.as_str())
132+
}
133+
}
134+
135+
/// Enables cloning a JSON from a reference using From trait.
136+
impl From<&JSON> for JSON {
137+
fn from(json: &JSON) -> Self { json.clone() }
138+
}
139+
140+
/// Converts JSON into a `Vec<u8>` containing the JSON bytes.
141+
impl From<JSON> for Vec<u8> {
142+
fn from(json: JSON) -> Self { json.0.to_vec() }
143+
}
144+
145+
/// Converts a JSON reference into a `Vec<u8>` containing the JSON bytes.
146+
impl From<&JSON> for Vec<u8> {
147+
fn from(json: &JSON) -> Self { json.0.to_vec() }
148+
}
149+
150+
#[cfg(test)]
151+
mod tests {
152+
use super::*;
153+
154+
#[test]
155+
fn test_json_creation() {
156+
let json = JSON::from_string(r#"{"key": "value"}"#);
157+
assert_eq!(json.as_str(), r#"{"key": "value"}"#);
158+
assert_eq!(json.len(), 16);
159+
assert!(!json.is_empty());
160+
}
161+
162+
#[test]
163+
fn test_json_from_bytes() {
164+
let data = b"[1, 2, 3]";
165+
let json = JSON::from_data(data);
166+
assert_eq!(json.as_bytes(), data);
167+
assert_eq!(json.as_str(), "[1, 2, 3]");
168+
}
169+
170+
#[test]
171+
fn test_json_empty() {
172+
let json = JSON::from_string("");
173+
assert!(json.is_empty());
174+
assert_eq!(json.len(), 0);
175+
}
176+
177+
#[test]
178+
fn test_json_cbor_roundtrip() {
179+
let json = JSON::from_string(r#"{"name":"Alice","age":30}"#);
180+
let cbor: CBOR = json.clone().into();
181+
let json2: JSON = cbor.try_into().unwrap();
182+
assert_eq!(json, json2);
183+
}
184+
185+
#[test]
186+
fn test_json_hex() {
187+
let json = JSON::from_string("test");
188+
let hex = json.hex();
189+
let json2 = JSON::from_hex(hex);
190+
assert_eq!(json, json2);
191+
}
192+
193+
#[test]
194+
fn test_json_debug() {
195+
let json = JSON::from_string(r#"{"test":true}"#);
196+
let debug = format!("{:?}", json);
197+
assert_eq!(debug, r#"JSON({"test":true})"#);
198+
}
199+
200+
#[test]
201+
fn test_json_clone() {
202+
let json = JSON::from_string("original");
203+
let json2 = json.clone();
204+
assert_eq!(json, json2);
205+
}
206+
207+
#[test]
208+
fn test_json_into_vec() {
209+
let json = JSON::from_string("data");
210+
let vec: Vec<u8> = json.into();
211+
assert_eq!(vec, b"data");
212+
}
213+
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ pub use encrypted_key::*;
4646
mod salt;
4747
pub use salt::Salt;
4848

49+
mod json;
50+
pub use json::JSON;
51+
4952
mod x25519;
5053
pub use x25519::{X25519PrivateKey, X25519PublicKey};
5154

src/reference.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,7 @@ impl Reference {
162162
///
163163
/// Yes, this creates a Reference to a Reference.
164164
impl ReferenceProvider for Reference {
165-
fn reference(&self) -> Reference {
166-
Reference::from_digest(self.digest())
167-
}
165+
fn reference(&self) -> Reference { Reference::from_digest(self.digest()) }
168166
}
169167

170168
impl<'a> From<&'a Reference> for &'a [u8; Reference::REFERENCE_SIZE] {

src/symmetric/encrypted_message.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,7 @@ impl AsRef<EncryptedMessage> for EncryptedMessage {
107107

108108
/// Implements DigestProvider to provide the digest stored in the AAD field.
109109
impl DigestProvider for EncryptedMessage {
110-
fn digest(&self) -> Digest {
111-
self.aad_digest().unwrap()
112-
}
110+
fn digest(&self) -> Digest { self.aad_digest().unwrap() }
113111
}
114112

115113
/// Implements CBORTagged to provide the CBOR tag for the EncryptedMessage.

src/tags_registry.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::ReferenceProvider;
1212
#[cfg(any(feature = "secp256k1", feature = "ed25519"))]
1313
use crate::SignatureScheme;
1414
use crate::{
15-
ARID, Digest, EncapsulationScheme, EncryptedKey, Nonce, PrivateKeyBase,
15+
ARID, Digest, EncapsulationScheme, EncryptedKey, JSON, Nonce, PrivateKeyBase,
1616
PrivateKeys, PublicKeys, Reference, SSKRShare, Salt, SealedMessage, Seed,
1717
Signature, SigningPrivateKey, SigningPublicKey, URI, UUID, XID,
1818
};
@@ -76,6 +76,14 @@ pub fn register_tags_in(tags_store: &mut TagsStore) {
7676
}),
7777
);
7878

79+
tags_store.set_summarizer(
80+
TAG_JSON,
81+
Arc::new(move |untagged_cbor: CBOR, _flat: bool| {
82+
let json = JSON::from_untagged_cbor(untagged_cbor)?;
83+
Ok(json.as_str().flanked_by("JSON(", ")"))
84+
}),
85+
);
86+
7987
tags_store.set_summarizer(
8088
TAG_SEED,
8189
Arc::new(move |untagged_cbor: CBOR, _flat: bool| {

0 commit comments

Comments
 (0)