-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Cypher
committed
Dec 24, 2023
1 parent
cf4e4ad
commit fe9d5d0
Showing
14 changed files
with
2,026 additions
and
84 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,50 @@ | ||
[workspace] | ||
default-members = [ | ||
"." | ||
] | ||
|
||
[workspace.package] | ||
authors = ["Dr. Maxim Orlovsky <[email protected]>"] | ||
homepage = "https://cyphernet.io" | ||
repository = "https://github.com/Cyphernet-DAO/ssi" | ||
rust-version = "1.66" | ||
edition = "2021" | ||
license = "Apache-2.0" | ||
|
||
[workspace.dependencies] | ||
amplify = "4.0.0" | ||
|
||
### | ||
### Main package (`ssi`) | ||
### | ||
|
||
[package] | ||
name = "ssi" | ||
name = "ssid" | ||
version = "0.1.0" | ||
description = "Self-sovereign identity" | ||
keywords = ["privacy", "cypherpunk", "identity"] | ||
categories = ["cryptography"] | ||
readme = "README.md" | ||
authors = { workspace = true } | ||
homepage = { workspace = true } | ||
repository = { workspace = true } | ||
rust-version = { workspace = true } | ||
edition = { workspace = true } | ||
license = { workspace = true } | ||
authors = ["Cypher <[email protected]>"] | ||
homepage = "https://cyphernet.io" | ||
repository = "https://github.com/Cyphernet-DAO/ssid" | ||
rust-version = "1.66" | ||
edition = "2021" | ||
license = "Apache-2.0" | ||
|
||
[lib] | ||
|
||
[[bin]] | ||
name = "ssid" | ||
required-features = ["cli"] | ||
|
||
[dependencies] | ||
amplify = { workspace = true } | ||
amplify = "4.5.0" | ||
commit_verify = "0.11.0-beta.1" | ||
strict_encoding = "2.6.1" | ||
strict_types = "1.6.3" | ||
ec25519 = "0.1.0" | ||
secp256k1 = { version = "0.27.0", optional = true } | ||
baid58 = "0.4.4" | ||
base85 = "2.0.0" | ||
bp-std = { version = "0.11.0-beta.2", features = ["client-side-validation"] } | ||
rand = "0.8.5" | ||
clap = { version = "4.4.11", features = ["derive", "env", "wrap_help"], optional = true } | ||
shellexpand = { version = "3.1.0", optional = true } | ||
|
||
[features] | ||
default = [] | ||
all = ["cli"] | ||
cli = ["clap", "shellexpand"] | ||
|
||
[package.metadata.docs.rs] | ||
all-features = true | ||
rustc-args = ["--cfg", "docsrs"] | ||
|
||
[patch.crates-io] | ||
commit_verify = { git = "https://github.com/LNP-BP/client_side_validation", branch = "v0.11" } | ||
bp-consensus = { git = "https://github.com/BP-WG/bp-core", branch = "doubleanchors" } | ||
bp-dbc = { git = "https://github.com/BP-WG/bp-core", branch = "doubleanchors" } | ||
bp-seals = { git = "https://github.com/BP-WG/bp-core", branch = "doubleanchors" } | ||
bp-core = { git = "https://github.com/BP-WG/bp-core", branch = "doubleanchors" } | ||
psbt = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } | ||
bp-std = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,11 @@ | ||
Maxim Orlovsky | ||
Cypher | ||
--------------- | ||
- GPG: `54DD1B1181EC60253A9357956482D826A6BEA6F9` | ||
- EMail: [[email protected]](mailto:[email protected]) | ||
|
||
- Maxim Orlovsky | ||
--------------- | ||
- GitHub: [@dr-orlovsky](https://github.com/dr-orlovsky) | ||
- GPG: `EAE730CEC0C663763F028A5860094BAF18A26EC9` | ||
- SSH: `BoSGFzbyOKC7Jm28MJElFboGepihCpHop60nS8OoG/A` | ||
- EMail: [dr@orlovsky.ch](mailto:dr@orlovsky.ch) | ||
- EMail: [orlovsky@cyphernet.io](mailto:orlovsky@cyphernet.io) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,11 +4,14 @@ Kind: Free software | |
License: Apache-2.0 | ||
Language: Rust | ||
Compiler: 1.66 | ||
Author: Maxim Orlovsky | ||
Author: Cypher | ||
Maintained: Cyphernet DAO, Switzerland | ||
Maintainers: | ||
Cypher: | ||
GPG: 54DD1B1181EC60253A9357956482D826A6BEA6F9 | ||
EMail: [email protected] | ||
Maxim Orlovsky: | ||
GitHub: @dr-orlovsky | ||
GPG: EAE730CEC0C663763F028A5860094BAF18A26EC9 | ||
SSH: BoSGFzbyOKC7Jm28MJElFboGepihCpHop60nS8OoG/A | ||
EMail: dr@orlovsky.ch | ||
EMail: orlovsky@cyphernet.io |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,9 @@ | ||
# Self-sovereign identity suite | ||
|
||
Self-sovereign identity is an identity format developed by Cyphernet | ||
Association, Switzerland. Being similar to OpenPGP, it operates without | ||
any key servers, using blockchain infrastructure. This allows provable and | ||
globally enforceable key revocation without dedicated revocation keys, | ||
global propagation of new keys information and provable or unique | ||
signatures which are timestamped or created using single-use-seal | ||
mechanism. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// Self-sovereign identity (SSID) | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
// Written in 2023-204 by | ||
// Cypher<[email protected]> | ||
// | ||
// Copyright 2023-2024 Cyphernet DAO, Switzerland | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
mod ristretto25519; | ||
|
||
use std::fmt::{Debug, Display}; | ||
use std::str::FromStr; | ||
|
||
use amplify::Bytes4; | ||
use baid58::Baid58ParseError; | ||
pub use ristretto25519::{RistrettoPk, RistrettoSig, RistrettoSk}; | ||
use strict_encoding::{StrictDecode, StrictDumb, StrictEncode, StrictType}; | ||
|
||
use crate::{BindleContent, Digest}; | ||
|
||
pub type Fingerprint = Bytes4; | ||
|
||
pub trait Sk: BindleContent { | ||
type Sig: Sig; | ||
|
||
fn generate() -> Self; | ||
|
||
fn sign(&self, message: impl Into<Digest>) -> Self::Sig; | ||
} | ||
|
||
pub trait Pk: | ||
Copy | ||
+ Eq | ||
+ Debug | ||
+ Display | ||
+ FromStr<Err = Baid58ParseError> | ||
+ StrictType | ||
+ StrictDumb | ||
+ StrictEncode | ||
+ StrictDecode | ||
{ | ||
type Sk: Sk; | ||
const ID: u8; | ||
|
||
fn with(sk: &Self::Sk) -> Self; | ||
|
||
#[must_use] | ||
fn verify(&self, message: impl Into<Digest>, sig: &<Self::Sk as Sk>::Sig) -> bool; | ||
|
||
fn fingerprint(&self) -> Fingerprint; | ||
} | ||
|
||
pub trait Sig: Copy + Eq + Debug + StrictType + StrictDumb + StrictEncode + StrictDecode {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
// Self-sovereign identity (SSID) | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
// Written in 2023-204 by | ||
// Cypher<[email protected]> | ||
// | ||
// Copyright 2023-2024 Cyphernet DAO, Switzerland | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
use std::io; | ||
use std::ops::Deref; | ||
use std::str::FromStr; | ||
|
||
use baid58::{Baid58ParseError, Chunking, FromBaid58, ToBaid58, CHUNKING_32}; | ||
use ec25519::{Noise, PublicKey, SecretKey, Signature}; | ||
use rand::{random, thread_rng, Rng}; | ||
use strict_encoding::{ | ||
DecodeError, ReadTuple, StrictDecode, StrictDeserialize, StrictDumb, StrictEncode, | ||
StrictProduct, StrictSerialize, StrictTuple, StrictType, TypedRead, TypedWrite, | ||
}; | ||
|
||
use super::{Fingerprint, Pk, Sig}; | ||
use crate::{BindleContent, Digest, Sk, LIB_NAME_SSID}; | ||
|
||
pub struct RistrettoSk(SecretKey); | ||
|
||
impl StrictType for RistrettoSk { | ||
const STRICT_LIB_NAME: &'static str = LIB_NAME_SSID; | ||
} | ||
impl StrictProduct for RistrettoSk {} | ||
impl StrictTuple for RistrettoSk { | ||
const FIELD_COUNT: u8 = 1; | ||
} | ||
impl StrictEncode for RistrettoSk { | ||
fn strict_encode<W: TypedWrite>(&self, writer: W) -> io::Result<W> { | ||
writer.write_newtype::<Self>(self.0.deref()) | ||
} | ||
} | ||
impl StrictDecode for RistrettoSk { | ||
fn strict_decode(reader: &mut impl TypedRead) -> Result<Self, DecodeError> { | ||
reader.read_tuple(|r| { | ||
let data = r.read_field()?; | ||
Ok(Self(SecretKey::new(data))) | ||
}) | ||
} | ||
} | ||
impl StrictDumb for RistrettoSk { | ||
fn strict_dumb() -> Self { Self(SecretKey::new([0xFAu8; 64])) } | ||
} | ||
impl StrictSerialize for RistrettoSk {} | ||
impl StrictDeserialize for RistrettoSk {} | ||
|
||
impl Sk for RistrettoSk { | ||
type Sig = RistrettoSig; | ||
|
||
fn generate() -> Self { | ||
let mut data = [0u8; 64]; | ||
thread_rng().fill(&mut data); | ||
Self(SecretKey::new(data)) | ||
} | ||
|
||
fn sign(&self, message: impl Into<Digest>) -> Self::Sig { | ||
RistrettoSig(self.0.sign(message.into(), Some(Noise::new(random())))) | ||
} | ||
} | ||
|
||
impl BindleContent for RistrettoSk { | ||
const MAGIC: [u8; 4] = *b"SSSK"; | ||
const PLATE_TITLE: &'static str = "SSID SECRET KEY"; | ||
type Id = RistrettoPk; | ||
|
||
fn bindle_id(&self) -> Self::Id { RistrettoPk::with(self) } | ||
} | ||
|
||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display)] | ||
#[display(Self::to_baid58_string)] | ||
pub struct RistrettoPk(PublicKey); | ||
|
||
impl From<[u8; 33]> for RistrettoPk { | ||
fn from(value: [u8; 33]) -> Self { | ||
assert_eq!(value[0], Self::ID, "invalid key type"); | ||
let mut data = [0u8; 32]; | ||
data.copy_from_slice(&value[1..]); | ||
Self(PublicKey::new(data)) | ||
} | ||
} | ||
|
||
impl ToBaid58<33> for RistrettoPk { | ||
const HRI: &'static str = "ssi"; | ||
const CHUNKING: Option<Chunking> = CHUNKING_32; | ||
fn to_baid58_payload(&self) -> [u8; 33] { | ||
let mut payload = [0u8; 33]; | ||
payload[0] = Self::ID; | ||
payload[1..].copy_from_slice(self.0.deref()); | ||
payload | ||
} | ||
fn to_baid58_string(&self) -> String { self.to_string() } | ||
} | ||
impl FromBaid58<33> for RistrettoPk {} | ||
impl FromStr for RistrettoPk { | ||
type Err = Baid58ParseError; | ||
fn from_str(s: &str) -> Result<Self, Self::Err> { Self::from_baid58_chunked_str(s, ':', '#') } | ||
} | ||
impl RistrettoPk { | ||
pub fn to_baid58_string(&self) -> String { format!("{::<#.2}", self.to_baid58()) } | ||
pub fn to_mnemonic(&self) -> String { self.to_baid58().mnemonic() } | ||
} | ||
|
||
impl Pk for RistrettoPk { | ||
type Sk = RistrettoSk; | ||
const ID: u8 = 1; | ||
|
||
fn with(sk: &Self::Sk) -> Self { Self(sk.0.public_key()) } | ||
|
||
fn verify(&self, message: impl Into<Digest>, sig: &<Self::Sk as Sk>::Sig) -> bool { | ||
self.0.verify(message.into(), &sig.0).is_ok() | ||
} | ||
|
||
fn fingerprint(&self) -> Fingerprint { | ||
Fingerprint::copy_from_slice(&self.0[0..4]).expect("fixed length") | ||
} | ||
} | ||
|
||
impl StrictType for RistrettoPk { | ||
const STRICT_LIB_NAME: &'static str = LIB_NAME_SSID; | ||
} | ||
impl StrictProduct for RistrettoPk {} | ||
impl StrictTuple for RistrettoPk { | ||
const FIELD_COUNT: u8 = 1; | ||
} | ||
impl StrictEncode for RistrettoPk { | ||
fn strict_encode<W: TypedWrite>(&self, writer: W) -> io::Result<W> { | ||
writer.write_newtype::<Self>(self.0.deref()) | ||
} | ||
} | ||
impl StrictDecode for RistrettoPk { | ||
fn strict_decode(reader: &mut impl TypedRead) -> Result<Self, DecodeError> { | ||
reader.read_tuple(|r| { | ||
let data = r.read_field()?; | ||
Ok(Self(PublicKey::new(data))) | ||
}) | ||
} | ||
} | ||
impl StrictDumb for RistrettoPk { | ||
fn strict_dumb() -> Self { Self(PublicKey::new([0xFAu8; 32])) } | ||
} | ||
|
||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] | ||
pub struct RistrettoSig(Signature); | ||
|
||
impl Sig for RistrettoSig {} | ||
|
||
impl StrictType for RistrettoSig { | ||
const STRICT_LIB_NAME: &'static str = LIB_NAME_SSID; | ||
} | ||
impl StrictProduct for RistrettoSig {} | ||
impl StrictTuple for RistrettoSig { | ||
const FIELD_COUNT: u8 = 1; | ||
} | ||
impl StrictEncode for RistrettoSig { | ||
fn strict_encode<W: TypedWrite>(&self, writer: W) -> io::Result<W> { | ||
writer.write_newtype::<Self>(self.0.deref()) | ||
} | ||
} | ||
impl StrictDecode for RistrettoSig { | ||
fn strict_decode(reader: &mut impl TypedRead) -> Result<Self, DecodeError> { | ||
reader.read_tuple(|r| { | ||
let data = r.read_field()?; | ||
Ok(Self(Signature::new(data))) | ||
}) | ||
} | ||
} | ||
impl StrictDumb for RistrettoSig { | ||
fn strict_dumb() -> Self { Self(Signature::new([0xFAu8; 64])) } | ||
} |
Oops, something went wrong.