ZeroTierOne/utils/src/json.rs
Sean OMeara 0c91b75bbd silence compiler warnings about _unused_variables (#1852)
Tetanus noise xk (#1881)

* Noise XK work in progress.

* A whole lot more Noise_XK work... exchange almost done.

* Delete a bunch of commented out old Noise_IK code.

* Add back in send() and a few other things to Noise_XK ZSSP.

* Some p384 experiment in attic

* A ton of ZSSP work, and put MPL on ZSSP.

* updated kbkdf512 to use the modern nist standard

* Parameterize KBKDF on resulting output key size the way NIST likes.

* updated variable comment

* Make the label a const parameter on kbkdf.

* updated variable comment

* Add MPL to utils and other stuff.

* layout tweak

* Some more ZSSP work and a VDF we may use.

* warning removal

* More ZSSP work, add benchmarks for mimcvdf.

* Almost ready to test...

* Build fix.

* Add automatic retransmission in the earliest stages of session init.

* Just about ready to test... wow.

* It opens a session.

* ZSSP basically works...

---------

Co-authored-by: mamoniot <mamoniot@protonmail.com>

Warning removal.

remove old docs

Remove old tests from ZSSP, new test in main() can also be made into a unit test in the future.

Add key_info() to get key information.

Rekeying is now tested and works.

Show key fingerprint.

Factor out memory:: stuff, does not appear to have any real performance benefit.

Rework defragmentation, and it now tolerates very poor link quality pretty well.

Circuit breaker for incoming defrag queue, and ZSSP now works very well even under very poor network conditions.

Format tweak.

ZSSP API updates.

Just a bit of final ZSSP cleanup before moving to another thing.
2023-03-03 13:47:22 -05:00

211 lines
6.2 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* (c) ZeroTier, Inc.
* https://www.zerotier.com/
*/
use serde::de::DeserializeOwned;
use serde::Serialize;
use serde_json::ser::Formatter;
/// Recursively patch a JSON object.
///
/// This is slightly different from a usual JSON merge. For objects in the target their fields
/// are updated by recursively calling json_patch if the same field is present in the source.
/// If the source tries to set an object to something other than another object, this is ignored.
/// Other fields are replaced. This is used for RESTful config object updates. The depth limit
/// field is to prevent stack overflows via the API.
pub fn json_patch(target: &mut serde_json::value::Value, source: &serde_json::value::Value, depth_limit: usize) {
if target.is_object() {
if source.is_object() {
let target = target.as_object_mut().unwrap();
let source = source.as_object().unwrap();
for kv in target.iter_mut() {
let _ = source.get(kv.0).map(|new_value| {
if depth_limit > 0 {
json_patch(kv.1, new_value, depth_limit - 1)
}
});
}
for kv in source.iter() {
if !target.contains_key(kv.0) && !kv.1.is_null() {
target.insert(kv.0.clone(), kv.1.clone());
}
}
}
} else if *target != *source {
*target = source.clone();
}
}
/// Patch a serializable object with the fields present in a JSON object.
///
/// If there are no changes, None is returned. The depth limit is passed through to json_patch and
/// should be set to a sanity check value to prevent overflows.
pub fn json_patch_object<O: Serialize + DeserializeOwned + Eq>(obj: O, patch: &str, depth_limit: usize) -> Result<Option<O>, serde_json::Error> {
serde_json::from_str::<serde_json::value::Value>(patch).map_or_else(
|e| Err(e),
|patch| {
serde_json::value::to_value(&obj).map_or_else(
|e| Err(e),
|mut obj_value| {
json_patch(&mut obj_value, &patch, depth_limit);
serde_json::value::from_value::<O>(obj_value).map_or_else(
|e| Err(e),
|obj_merged| {
if obj == obj_merged {
Ok(None)
} else {
Ok(Some(obj_merged))
}
},
)
},
)
},
)
}
/// Shortcut to use serde_json to serialize an object, returns "null" on error.
pub fn to_json<O: serde::Serialize>(o: &O) -> String {
serde_json::to_string(o).unwrap_or("null".into())
}
/// Shortcut to use serde_json to serialize an object, returns "null" on error.
pub fn to_json_pretty<O: serde::Serialize>(o: &O) -> String {
let mut buf = Vec::new();
let mut ser = serde_json::Serializer::with_formatter(&mut buf, PrettyFormatter::new());
if o.serialize(&mut ser).is_ok() {
String::from_utf8(buf).unwrap_or_else(|_| "null".into())
} else {
"null".into()
}
}
/// JSON formatter that looks a bit better than the Serde default.
pub struct PrettyFormatter<'a> {
current_indent: usize,
has_value: bool,
indent: &'a [u8],
}
fn indent<W>(wr: &mut W, n: usize, s: &[u8]) -> std::io::Result<()>
where
W: ?Sized + std::io::Write,
{
for _ in 0..n {
wr.write_all(s)?;
}
Ok(())
}
impl<'a> PrettyFormatter<'a> {
pub fn new() -> Self {
Self::with_indent(b" ")
}
pub fn with_indent(indent: &'a [u8]) -> Self {
Self { current_indent: 0, has_value: false, indent }
}
}
impl<'a> Default for PrettyFormatter<'a> {
fn default() -> Self {
Self::new()
}
}
impl<'a> Formatter for PrettyFormatter<'a> {
fn begin_array<W>(&mut self, writer: &mut W) -> std::io::Result<()>
where
W: ?Sized + std::io::Write,
{
self.current_indent += 1;
self.has_value = false;
writer.write_all(b"[")
}
fn end_array<W>(&mut self, writer: &mut W) -> std::io::Result<()>
where
W: ?Sized + std::io::Write,
{
self.current_indent -= 1;
if self.has_value {
writer.write_all(b" ]")
} else {
writer.write_all(b"]")
}
}
fn begin_array_value<W>(&mut self, writer: &mut W, first: bool) -> std::io::Result<()>
where
W: ?Sized + std::io::Write,
{
if first {
writer.write_all(b" ")?;
} else {
writer.write_all(b", ")?;
}
Ok(())
}
fn end_array_value<W>(&mut self, _writer: &mut W) -> std::io::Result<()>
where
W: ?Sized + std::io::Write,
{
self.has_value = true;
Ok(())
}
fn begin_object<W>(&mut self, writer: &mut W) -> std::io::Result<()>
where
W: ?Sized + std::io::Write,
{
self.current_indent += 1;
self.has_value = false;
writer.write_all(b"{")
}
fn end_object<W>(&mut self, writer: &mut W) -> std::io::Result<()>
where
W: ?Sized + std::io::Write,
{
self.current_indent -= 1;
if self.has_value {
writer.write_all(b"\n")?;
indent(writer, self.current_indent, self.indent)?;
}
writer.write_all(b"}")
}
fn begin_object_key<W>(&mut self, writer: &mut W, first: bool) -> std::io::Result<()>
where
W: ?Sized + std::io::Write,
{
if first {
writer.write_all(b"\n")?;
} else {
writer.write_all(b",\n")?;
}
indent(writer, self.current_indent, self.indent)
}
fn begin_object_value<W>(&mut self, writer: &mut W) -> std::io::Result<()>
where
W: ?Sized + std::io::Write,
{
writer.write_all(b": ")
}
fn end_object_value<W>(&mut self, _writer: &mut W) -> std::io::Result<()>
where
W: ?Sized + std::io::Write,
{
self.has_value = true;
Ok(())
}
}