Rust implementation of NextGraph, a Decentralized and local-first web 3.0 ecosystem https://nextgraph.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
nextgraph-rs/p2p-repo/src/types.rs

700 lines
18 KiB

// Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers
// All rights reserved.
// This code is partly derived from work written by TG x Thoth from P2Pcollab.
// Copyright 2022 TG x Thoth
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
//! P2P Repo types
//!
//! Corresponds to the BARE schema
use crate::errors::NgError;
use crate::utils::{
decode_key, dh_pubkey_array_from_ed_pubkey_slice, dh_pubkey_from_ed_pubkey_slice,
ed_privkey_to_ed_pubkey, from_ed_privkey_to_dh_privkey, random_key,
};
use core::fmt;
use serde::{Deserialize, Serialize};
use serde_bare::to_vec;
use std::collections::HashMap;
use std::hash::Hash;
use zeroize::{Zeroize, ZeroizeOnDrop};
//
// COMMON TYPES
//
/// 32-byte Blake3 hash digest
pub type Blake3Digest32 = [u8; 32];
/// Hash digest
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum Digest {
Blake3Digest32(Blake3Digest32),
}
impl fmt::Display for Digest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Digest::Blake3Digest32(d) => write!(f, "{}", hex::encode(d)),
}
}
}
/// ChaCha20 symmetric key
pub type ChaCha20Key = [u8; 32];
/// Symmetric cryptographic key
#[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum SymKey {
ChaCha20Key(ChaCha20Key),
}
impl SymKey {
pub fn slice(&self) -> &[u8; 32] {
match self {
SymKey::ChaCha20Key(o) => o,
}
}
pub fn random() -> Self {
SymKey::ChaCha20Key(random_key())
}
pub fn from_array(array: [u8; 32]) -> Self {
SymKey::ChaCha20Key(array)
}
}
impl fmt::Display for SymKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ChaCha20Key(k) => write!(f, "{}", base64_url::encode(k)),
}
}
}
impl TryFrom<&[u8]> for SymKey {
type Error = NgError;
fn try_from(buf: &[u8]) -> Result<Self, NgError> {
let sym_key_array = *slice_as_array!(buf, [u8; 32]).ok_or(NgError::InvalidKey)?;
let sym_key = SymKey::ChaCha20Key(sym_key_array);
Ok(sym_key)
}
}
/// Curve25519 public key Edwards form
pub type Ed25519PubKey = [u8; 32];
/// Curve25519 public key Montgomery form
pub type X25519PubKey = [u8; 32];
/// Curve25519 private key Edwards form
pub type Ed25519PrivKey = [u8; 32];
/// Curve25519 private key Montgomery form
pub type X25519PrivKey = [u8; 32];
/// Public key
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum PubKey {
Ed25519PubKey(Ed25519PubKey),
X25519PubKey(X25519PubKey),
}
impl PubKey {
pub fn slice(&self) -> &[u8; 32] {
match self {
PubKey::Ed25519PubKey(o) | PubKey::X25519PubKey(o) => o,
}
}
pub fn to_dh_from_ed(&self) -> PubKey {
match self {
PubKey::Ed25519PubKey(ed) => dh_pubkey_from_ed_pubkey_slice(ed),
_ => panic!(
"there is no need to convert a Montgomery key to Montgomery. it is already one. check your code"
),
}
}
// pub fn dh_from_ed_slice(slice: &[u8]) -> PubKey {
// dh_pubkey_from_ed_pubkey_slice(slice)
// }
pub fn to_dh_slice(&self) -> [u8; 32] {
match self {
PubKey::Ed25519PubKey(o) => dh_pubkey_array_from_ed_pubkey_slice(o),
_ => panic!("can only convert an edward key to montgomery"),
}
}
pub fn nil() -> Self {
PubKey::Ed25519PubKey([0u8; 32])
}
}
impl fmt::Display for PubKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PubKey::Ed25519PubKey(d) | PubKey::X25519PubKey(d) => {
write!(f, "{}", base64_url::encode(d))
}
}
}
}
impl TryFrom<&str> for PubKey {
type Error = NgError;
fn try_from(str: &str) -> Result<Self, NgError> {
let key = decode_key(str).map_err(|_| NgError::InvalidKey)?;
Ok(PubKey::Ed25519PubKey(key))
}
}
/// Private key
#[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum PrivKey {
Ed25519PrivKey(Ed25519PrivKey),
X25519PrivKey(X25519PrivKey),
}
impl PrivKey {
pub fn slice(&self) -> &[u8; 32] {
match self {
PrivKey::Ed25519PrivKey(o) | PrivKey::X25519PrivKey(o) => o,
}
}
pub fn to_pub(&self) -> PubKey {
match self {
PrivKey::Ed25519PrivKey(_) => ed_privkey_to_ed_pubkey(self),
_ => panic!("X25519PrivKey to pub not implemented"),
}
}
#[deprecated(note = "**Don't use dummy method**")]
pub fn dummy() -> PrivKey {
PrivKey::Ed25519PrivKey([0u8; 32])
}
pub fn to_dh(&self) -> PrivKey {
from_ed_privkey_to_dh_privkey(self)
}
pub fn random_ed() -> Self {
PrivKey::Ed25519PrivKey(random_key())
}
}
impl From<[u8; 32]> for PrivKey {
fn from(buf: [u8; 32]) -> Self {
let priv_key = PrivKey::Ed25519PrivKey(buf);
priv_key
}
}
impl TryFrom<&[u8]> for PrivKey {
type Error = NgError;
fn try_from(buf: &[u8]) -> Result<Self, NgError> {
let priv_key_array = *slice_as_array!(buf, [u8; 32]).ok_or(NgError::InvalidKey)?;
let priv_key = PrivKey::Ed25519PrivKey(priv_key_array);
Ok(priv_key)
}
}
impl TryFrom<&str> for PrivKey {
type Error = NgError;
fn try_from(str: &str) -> Result<Self, NgError> {
let key = decode_key(str).map_err(|_| NgError::InvalidKey)?;
Ok(PrivKey::Ed25519PrivKey(key))
}
}
impl fmt::Display for PrivKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Ed25519PrivKey(ed) => {
//let priv_key_ser = serde_bare::to_vec(ed).unwrap();
let prix_key_encoded = base64_url::encode(ed);
write!(f, "{}", prix_key_encoded)
}
_ => {
unimplemented!();
}
}
}
}
/// Ed25519 signature
pub type Ed25519Sig = [[u8; 32]; 2];
/// Cryptographic signature
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum Sig {
Ed25519Sig(Ed25519Sig),
}
/// Timestamp: absolute time in minutes since 2022-02-22 22:22 UTC
pub type Timestamp = u32;
pub const EPOCH_AS_UNIX_TIMESTAMP: u64 = 1645568520;
/// Relative time (e.g. delay from current time)
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum RelTime {
Seconds(u8),
Minutes(u8),
Hours(u8),
Days(u8),
}
/// Bloom filter (variable size)
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct BloomFilter {
/// Number of hash functions
pub k: u32,
/// Filter
#[serde(with = "serde_bytes")]
pub f: Vec<u8>,
}
/// Bloom filter (128 B)
///
/// (m=1024; k=7; p=0.01; n=107)
pub type BloomFilter128 = [[u8; 32]; 4];
/// Bloom filter (1 KiB)
///
/// (m=8192; k=7; p=0.01; n=855)
pub type BloomFilter1K = [[u8; 32]; 32];
//
// REPOSITORY TYPES
//
/// List of Permissions
pub enum PermissionType {
ADD_BRANCH,
REMOVE_BRANCH,
CHANGE_NAME,
ADD_MEMBER,
REMOVE_MEMBER,
CHANGE_PERMISSION,
TRANSACTION,
SNAPSHOT,
SHARING,
CHANGE_ACK_CONFIG,
}
/// RepoHash:
/// BLAKE3 hash of the RepoId
pub type RepoHash = Digest;
// impl From<RepoHash> for String {
// fn from(id: RepoHash) -> Self {
// hex::encode(to_vec(&id).unwrap())
// }
// }
/// RepoId is a PubKey
pub type RepoId = PubKey;
/// Block ID:
/// BLAKE3 hash over the serialized Object with encrypted content
pub type BlockId = Digest;
/// Block reference
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct BlockRef {
/// Object ID
pub id: BlockId,
/// Key for decrypting the Object
pub key: SymKey,
}
impl BlockRef {
#[deprecated(note = "**Don't use dummy method**")]
pub fn dummy() -> Self {
BlockRef {
id: Digest::Blake3Digest32([0u8; 32]),
key: SymKey::ChaCha20Key([0u8; 32]),
}
}
}
/// Object ID
pub type ObjectId = BlockId;
/// Object reference
pub type ObjectRef = BlockRef;
/// Internal node of a Merkle tree
pub type InternalNode = Vec<SymKey>;
/// Content of BlockV0: a Merkle tree node
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum BlockContentV0 {
/// Internal node with references to children
InternalNode(InternalNode),
#[serde(with = "serde_bytes")]
DataChunk(Vec<u8>),
}
/// List of ObjectId dependencies as encrypted Object content
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum DepList {
V0(Vec<ObjectId>),
}
/// Dependencies of an Object
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum ObjectDeps {
/// List of Object IDs (max. 8),
ObjectIdList(Vec<ObjectId>),
/// Reference to an Object that contains a DepList
DepListRef(ObjectRef),
}
/// Immutable block with encrypted content
///
/// `ObjectContent` is chunked and stored as `Block`s in a Merkle tree.
/// A Block is a Merkle tree node.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct BlockV0 {
/// Block ID
#[serde(skip)]
pub id: Option<BlockId>,
/// Block Key
#[serde(skip)]
pub key: Option<SymKey>,
/// Block IDs for child nodes in the Merkle tree
pub children: Vec<BlockId>,
/// Other objects this object depends on (e.g. Commit deps & acks)
/// Only set for the root block
pub deps: ObjectDeps,
/// Expiry time of this object and all of its children
/// when the object should be deleted by all replicas
/// Only set for the root block
pub expiry: Option<Timestamp>,
/// Encrypted ObjectContentV0
///
/// Encrypted using convergent encryption with ChaCha20:
/// - convergence_key: BLAKE3 derive_key ("NextGraph Data BLAKE3 key",
/// repo_pubkey + repo_secret)
/// - key: BLAKE3 keyed hash (convergence_key, plain_object_content)
/// - nonce: 0
#[serde(with = "serde_bytes")]
pub content: Vec<u8>,
}
/// Immutable object with encrypted content
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum Block {
V0(BlockV0),
}
/// Repository definition
///
/// Published in root branch, where:
/// - branch_pubkey: repo_pubkey
/// - branch_secret: BLAKE3 derive_key ("NextGraph Root Branch secret",
/// repo_pubkey + repo_secret)
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct RepositoryV0 {
/// Repo public key ID
pub id: RepoId,
/// List of branches
pub branches: Vec<ObjectRef>,
/// Whether or not to allow external requests
pub allow_ext_requests: bool,
/// App-specific metadata
#[serde(with = "serde_bytes")]
pub metadata: Vec<u8>,
}
/// Repository definition
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum Repository {
V0(RepositoryV0),
}
/// Add a branch to the repository
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum AddBranch {
V0(ObjectRef),
}
/// Remove a branch from the repository
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum RemoveBranch {
V0(ObjectRef),
}
/// Commit object types
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum CommitType {
Repository,
AddBranch,
RemoveBranch,
Branch,
AddMembers,
EndOfBranch,
Transaction,
Snapshot,
Ack,
}
/// Member of a Branch
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct MemberV0 {
/// Member public key ID
pub id: PubKey,
/// Commit types the member is allowed to publish in the branch
pub commit_types: Vec<CommitType>,
/// App-specific metadata
/// (role, permissions, cryptographic material, etc)
#[serde(with = "serde_bytes")]
pub metadata: Vec<u8>,
}
/// Member of a branch
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum Member {
V0(MemberV0),
}
/// Branch definition
///
/// First commit in a branch, signed by branch key
/// In case of a fork, the commit deps indicat
/// the previous branch heads.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct BranchV0 {
/// Branch public key ID
pub id: PubKey,
/// Pub/sub topic for publishing events
pub topic: PubKey,
/// Branch secret key
pub secret: SymKey,
/// Members with permissions
pub members: Vec<MemberV0>,
/// Number of acks required for a commit to be valid
pub quorum: HashMap<CommitType, u32>,
/// Delay to send explicit acks,
/// if not enough implicit acks arrived by then
pub ack_delay: RelTime,
/// Tags for organizing branches within the repository
#[serde(with = "serde_bytes")]
pub tags: Vec<u8>,
/// App-specific metadata (validation rules, etc)
#[serde(with = "serde_bytes")]
pub metadata: Vec<u8>,
}
/// Branch definition
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum Branch {
V0(BranchV0),
}
/// Add members to an existing branch
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct AddMembersV0 {
/// Members to add, with permissions
pub members: Vec<MemberV0>,
/// New quorum
pub quorum: Option<HashMap<CommitType, u32>>,
/// New ackDelay
pub ack_delay: Option<RelTime>,
}
/// Add members to an existing branch
///
/// If a member already exists, it overwrites the previous definition,
/// in that case this can only be used for adding new permissions,
/// not to remove existing ones.
/// The quorum and ackDelay can be changed as well
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum AddMembers {
V0(AddMembersV0),
}
/// ObjectRef for EndOfBranch
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum PlainOrEncryptedObjectRef {
Plain(ObjectRef),
Encrypted(Vec<u8>),
}
/// End of branch
///
/// No more commits accepted afterwards, only acks of this commit
/// May reference a fork where the branch continues
/// with possibly different members, permissions, validation rules.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct EndOfBranchV0 {
/// (Encrypted) reference to forked branch (optional)
pub fork: Option<PlainOrEncryptedObjectRef>,
/// Expiry time when all commits in the branch should be deleted
pub expiry: Timestamp,
}
/// End of branch
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum EndOfBranch {
V0(EndOfBranchV0),
}
/// Transaction with CRDT operations
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum Transaction {
#[serde(with = "serde_bytes")]
V0(Vec<u8>),
}
/// Snapshot of a Branch
///
/// Contains a data structure
/// computed from the commits at the specified head.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct SnapshotV0 {
/// Branch heads the snapshot was made from
pub heads: Vec<ObjectId>,
/// Snapshot data structure
#[serde(with = "serde_bytes")]
pub content: Vec<u8>,
}
/// Snapshot of a Branch
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum Snapshot {
V0(SnapshotV0),
}
/// Acknowledgement of another Commit
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum Ack {
V0(),
}
/// Commit body, corresponds to CommitType
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum CommitBody {
Repository(Repository),
AddBranch(AddBranch),
RemoveBranch(RemoveBranch),
Branch(Branch),
AddMembers(AddMembers),
EndOfBranch(EndOfBranch),
Transaction(Transaction),
Snapshot(Snapshot),
Ack(Ack),
}
/// Content of a Commit
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct CommitContentV0 {
/// Commit author
pub author: PubKey,
/// Author's commit sequence number in this branch
pub seq: u32,
/// Branch the commit belongs to
pub branch: ObjectRef,
/// Direct dependencies of this commit
pub deps: Vec<ObjectRef>,
/// Not directly dependent heads to acknowledge
pub acks: Vec<ObjectRef>,
/// Files the commit references
pub refs: Vec<ObjectRef>,
/// App-specific metadata (commit message, creation time, etc)
#[serde(with = "serde_bytes")]
pub metadata: Vec<u8>,
/// Object with a CommitBody inside
pub body: ObjectRef,
/// Expiry time of the body object
pub expiry: Option<Timestamp>,
}
/// Commit object
/// Signed by branch key, or a member key authorized to publish this commit type
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct CommitV0 {
/// ID of parent Object
#[serde(skip)]
pub id: Option<ObjectId>,
/// Key of parent Object
#[serde(skip)]
pub key: Option<SymKey>,
/// Commit content
pub content: CommitContentV0,
/// Signature over the content by the author
pub sig: Sig,
}
/// Commit Object
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum Commit {
V0(CommitV0),
}
/// File Object
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct FileV0 {
pub content_type: String,
#[serde(with = "serde_bytes")]
pub metadata: Vec<u8>,
#[serde(with = "serde_bytes")]
pub content: Vec<u8>,
}
/// A file stored in an Object
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum File {
V0(FileV0),
}
/// Immutable data stored encrypted in a Merkle tree
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum ObjectContent {
Commit(Commit),
CommitBody(CommitBody),
File(File),
DepList(DepList),
}