user: add a basic cryptography tool

This commit is contained in:
Mark Poliakov 2024-11-01 15:32:19 +02:00
parent 17dc8e9a4d
commit ae5aae7fb4
11 changed files with 824 additions and 42 deletions

227
userspace/Cargo.lock generated
View File

@ -8,7 +8,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.85", "syn 2.0.86",
"thiserror", "thiserror",
] ]
@ -78,7 +78,7 @@ checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.85", "syn 2.0.86",
] ]
[[package]] [[package]]
@ -140,7 +140,7 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.85", "syn 2.0.86",
] ]
[[package]] [[package]]
@ -209,6 +209,17 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "crypt"
version = "0.1.0"
dependencies = [
"clap",
"ed25519-dalek",
"rand 0.8.5",
"rsa",
"thiserror",
]
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.6" version = "0.1.6"
@ -222,8 +233,7 @@ dependencies = [
[[package]] [[package]]
name = "curve25519-dalek" name = "curve25519-dalek"
version = "4.1.3" version = "4.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://git.alnyan.me/yggdrasil/curve25519-dalek.git?branch=alnyan%2Fyggdrasil#5f4dbb09259347077d3a5024ba60c77efde93a3a"
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures", "cpufeatures",
@ -238,12 +248,11 @@ dependencies = [
[[package]] [[package]]
name = "curve25519-dalek-derive" name = "curve25519-dalek-derive"
version = "0.1.1" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://git.alnyan.me/yggdrasil/curve25519-dalek.git?branch=alnyan%2Fyggdrasil#5f4dbb09259347077d3a5024ba60c77efde93a3a"
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.85", "syn 2.0.86",
] ]
[[package]] [[package]]
@ -253,6 +262,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
dependencies = [ dependencies = [
"const-oid", "const-oid",
"pem-rfc7468",
"zeroize", "zeroize",
] ]
@ -272,6 +282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [ dependencies = [
"block-buffer", "block-buffer",
"const-oid",
"crypto-common", "crypto-common",
] ]
@ -282,17 +293,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
dependencies = [ dependencies = [
"pkcs8", "pkcs8",
"signature", "signature 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
name = "ed25519-dalek" name = "ed25519-dalek"
version = "2.1.1" version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://git.alnyan.me/yggdrasil/curve25519-dalek.git?branch=alnyan%2Fyggdrasil#5f4dbb09259347077d3a5024ba60c77efde93a3a"
checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
dependencies = [ dependencies = [
"curve25519-dalek", "curve25519-dalek",
"ed25519", "ed25519",
"rand_core 0.6.4 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
"serde", "serde",
"sha2", "sha2",
"subtle", "subtle",
@ -387,7 +398,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.85", "syn 2.0.86",
] ]
[[package]] [[package]]
@ -522,6 +533,9 @@ name = "lazy_static"
version = "1.5.0" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
dependencies = [
"spin",
]
[[package]] [[package]]
name = "libc" name = "libc"
@ -624,7 +638,7 @@ dependencies = [
"bytemuck", "bytemuck",
"clap", "clap",
"clap-num", "clap-num",
"rand", "rand 0.9.0-alpha.1",
"serde", "serde",
"serde_json", "serde_json",
"thiserror", "thiserror",
@ -642,12 +656,48 @@ dependencies = [
"minimal-lexical", "minimal-lexical",
] ]
[[package]]
name = "num-bigint-dig"
version = "0.8.2"
source = "git+https://git.alnyan.me/yggdrasil/num-bigint-dig.git?branch=alnyan%2Fyggdrasil-0.8.2#e0ff3fcf8e9f5612be8ede9df7458bd762ac525f"
dependencies = [
"byteorder",
"lazy_static",
"libm",
"num-integer",
"num-iter",
"num-traits",
"rand 0.8.5",
"smallvec",
"zeroize",
]
[[package]] [[package]]
name = "num-conv" name = "num-conv"
version = "0.1.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@ -717,6 +767,15 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
dependencies = [
"base64ct",
]
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.1" version = "2.3.1"
@ -735,6 +794,17 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkcs1"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
dependencies = [
"der",
"pkcs8",
"spki",
]
[[package]] [[package]]
name = "pkcs8" name = "pkcs8"
version = "0.10.2" version = "0.10.2"
@ -757,7 +827,7 @@ version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [ dependencies = [
"zerocopy", "zerocopy 0.7.35",
] ]
[[package]] [[package]]
@ -767,7 +837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"syn 2.0.85", "syn 2.0.86",
] ]
[[package]] [[package]]
@ -800,22 +870,40 @@ dependencies = [
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.9.0" version = "0.8.5"
source = "git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil#9fac921fcc405a936b9a88b6bb9f2ced264bc0ec" source = "git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4#27d8b41f68e239bf5b2e5079554f1b861966672f"
dependencies = [ dependencies = [
"libc", "libc",
"rand_chacha", "rand_chacha 0.3.1",
"rand_core 0.7.0", "rand_core 0.6.4 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
"zerocopy", ]
[[package]]
name = "rand"
version = "0.9.0-alpha.1"
source = "git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil#d78efe056f23f1896b0e4f03a62d4b6adc37ea07"
dependencies = [
"rand_chacha 0.9.0-alpha.1",
"rand_core 0.9.0-alpha.1",
"zerocopy 0.8.8",
] ]
[[package]] [[package]]
name = "rand_chacha" name = "rand_chacha"
version = "0.4.0" version = "0.3.1"
source = "git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil#9fac921fcc405a936b9a88b6bb9f2ced264bc0ec" source = "git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4#27d8b41f68e239bf5b2e5079554f1b861966672f"
dependencies = [ dependencies = [
"ppv-lite86", "ppv-lite86",
"rand_core 0.7.0", "rand_core 0.6.4 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
]
[[package]]
name = "rand_chacha"
version = "0.9.0-alpha.1"
source = "git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil#d78efe056f23f1896b0e4f03a62d4b6adc37ea07"
dependencies = [
"ppv-lite86",
"rand_core 0.9.0-alpha.1",
] ]
[[package]] [[package]]
@ -829,11 +917,19 @@ dependencies = [
[[package]] [[package]]
name = "rand_core" name = "rand_core"
version = "0.7.0" version = "0.6.4"
source = "git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil#9fac921fcc405a936b9a88b6bb9f2ced264bc0ec" source = "git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4#27d8b41f68e239bf5b2e5079554f1b861966672f"
dependencies = [ dependencies = [
"getrandom 0.2.12", "getrandom 0.2.12",
"zerocopy", ]
[[package]]
name = "rand_core"
version = "0.9.0-alpha.1"
source = "git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil#d78efe056f23f1896b0e4f03a62d4b6adc37ea07"
dependencies = [
"getrandom 0.2.12",
"zerocopy 0.8.8",
] ]
[[package]] [[package]]
@ -892,6 +988,25 @@ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
] ]
[[package]]
name = "rsa"
version = "0.9.6"
source = "git+https://git.alnyan.me/yggdrasil/rsa.git?branch=alnyan%2Fyggdrasil#1ebb9403b0ad1ca7eab5dffbb0837a53d68c4ca3"
dependencies = [
"const-oid",
"digest",
"num-bigint-dig",
"num-integer",
"num-traits",
"pkcs1",
"pkcs8",
"rand_core 0.6.4 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
"signature 2.2.0 (git+https://git.alnyan.me/yggdrasil/rustcrypto-traits.git?branch=alnyan%2Fyggdrasil-2.2.0)",
"spki",
"subtle",
"zeroize",
]
[[package]] [[package]]
name = "rsh" name = "rsh"
version = "0.1.0" version = "0.1.0"
@ -899,7 +1014,6 @@ dependencies = [
"bytemuck", "bytemuck",
"clap", "clap",
"cross", "cross",
"ed25519-dalek",
"flexbuffers", "flexbuffers",
"libterm", "libterm",
"serde", "serde",
@ -980,7 +1094,7 @@ checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.85", "syn 2.0.86",
] ]
[[package]] [[package]]
@ -1060,7 +1174,16 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [ dependencies = [
"rand_core 0.6.4", "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "signature"
version = "2.2.0"
source = "git+https://git.alnyan.me/yggdrasil/rustcrypto-traits.git?branch=alnyan%2Fyggdrasil-2.2.0#ac3771039858e20b812450ff7b6bf67e29436208"
dependencies = [
"digest",
"rand_core 0.6.4 (git+https://git.alnyan.me/yggdrasil/rand.git?branch=alnyan%2Fyggdrasil-rng_core-0.6.4)",
] ]
[[package]] [[package]]
@ -1081,6 +1204,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]] [[package]]
name = "spki" name = "spki"
version = "0.7.3" version = "0.7.3"
@ -1122,9 +1251,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.85" version = "2.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1152,7 +1281,7 @@ dependencies = [
"humansize", "humansize",
"init", "init",
"libterm", "libterm",
"rand", "rand 0.9.0-alpha.1",
"serde", "serde",
"serde_json", "serde_json",
"sha2", "sha2",
@ -1186,22 +1315,22 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.65" version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.65" version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.85", "syn 2.0.86",
] ]
[[package]] [[package]]
@ -1548,7 +1677,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"zerocopy-derive", "zerocopy-derive 0.7.35",
]
[[package]]
name = "zerocopy"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a4e33e6dce36f2adba29746927f8e848ba70989fdb61c772773bbdda8b5d6a7"
dependencies = [
"zerocopy-derive 0.8.8",
] ]
[[package]] [[package]]
@ -1559,7 +1697,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.85", "syn 2.0.86",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cd137b4cc21bde6ecce3bbbb3350130872cda0be2c6888874279ea76e17d4c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.86",
] ]
[[package]] [[package]]

View File

@ -16,7 +16,8 @@ members = [
"rdb", "rdb",
"lib/yasync", "lib/yasync",
"rsh", "rsh",
"lib/cross" "lib/cross",
"crypt"
] ]
exclude = ["dynload-program", "test-kernel-module"] exclude = ["dynload-program", "test-kernel-module"]
@ -27,9 +28,16 @@ serde_json = "1.0.132"
serde = { version = "1.0.214", features = ["derive"] } serde = { version = "1.0.214", features = ["derive"] }
bytemuck = "1.19.0" bytemuck = "1.19.0"
thiserror = "1.0.64" thiserror = "1.0.64"
rand = { git = "https://git.alnyan.me/yggdrasil/rand.git", branch = "alnyan/yggdrasil" }
flexbuffers = "2.0.0" flexbuffers = "2.0.0"
# Vendored/patched dependencies
rand = { git = "https://git.alnyan.me/yggdrasil/rand.git", branch = "alnyan/yggdrasil" }
ring = { git = "https://git.alnyan.me/yggdrasil/ring.git", branch = "alnyan/yggdrasil" }
rsa = { git = "https://git.alnyan.me/yggdrasil/rsa.git", branch = "alnyan/yggdrasil" }
curve25519-dalek = { git = "https://git.alnyan.me/yggdrasil/curve25519-dalek.git", branch = "alnyan/yggdrasil" }
x25519-dalek = { git = "https://git.alnyan.me/yggdrasil/curve25519-dalek.git", branch = "alnyan/yggdrasil" }
ed25519-dalek = { git = "https://git.alnyan.me/yggdrasil/curve25519-dalek.git", branch = "alnyan/yggdrasil" }
# Internal crates # Internal crates
serde-ipc.path = "lib/serde-ipc" serde-ipc.path = "lib/serde-ipc"
libterm.path = "lib/libterm" libterm.path = "lib/libterm"

View File

@ -0,0 +1,12 @@
[package]
name = "crypt"
version = "0.1.0"
edition = "2021"
[dependencies]
rsa.workspace = true
ed25519-dalek = { workspace = true, features = ["rand_core", "pem"] }
clap.workspace = true
thiserror.workspace = true
rand = { git = "https://git.alnyan.me/yggdrasil/rand.git", branch = "alnyan/yggdrasil-rng_core-0.6.4" }

113
userspace/crypt/src/lib.rs Normal file
View File

@ -0,0 +1,113 @@
use std::{
fs::File,
io::{self, stdin, stdout, BufRead, BufReader, BufWriter, Read, Stdin, Stdout, Write},
path::{Path, PathBuf},
};
pub mod rsa;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("{0}")]
Io(#[from] io::Error),
#[error("RSA error: {0}")]
Rsa(#[from] ::rsa::Error),
#[error("PKCS#1 error: {0}")]
Pkcs1(#[from] ::rsa::pkcs1::Error),
#[error("PKCS#8 error: {0}")]
Pkcs8(#[from] ::rsa::pkcs8::Error),
#[error("PKCS#8 error: {0}")]
Pkcs8Spki(#[from] ::rsa::pkcs8::spki::Error),
#[error("incompatible options: {0}")]
IncompatibleOptions(String),
}
#[derive(Debug, clap::Parser)]
pub struct AsymmetricEncryptOptions {
#[clap(short, long, help = "Ciphertext output (default: stdout)")]
pub output: Option<PathBuf>,
#[clap(short = 'k', long, help = "Public key to use")]
pub public_key: PathBuf,
#[clap(help = "Plaintext input (default: stdin)")]
pub input: Option<PathBuf>,
}
#[derive(Debug, clap::Parser)]
pub struct AsymmetricDecryptOptions {
#[clap(short, long, help = "Plaintext output (default: stdout)")]
pub output: Option<PathBuf>,
#[clap(short = 'k', long, help = "Private key to use")]
pub private_key: PathBuf,
#[clap(help = "Ciphertext input (default: stdin)")]
pub input: Option<PathBuf>,
}
#[derive(Debug, clap::Parser)]
pub struct AsymmetricKeyOptions {
#[clap(short = 'o', long, help = "Public key output file (default: stdout)")]
pub public_key: Option<PathBuf>,
#[clap(help = "Private key output file")]
pub private_key: PathBuf,
}
#[derive(Debug)]
pub enum Input {
Stdin(Stdin),
File(BufReader<File>),
}
#[derive(Debug)]
pub enum Output {
Stdout(Stdout),
File(BufWriter<File>),
}
impl Input {
pub fn from_path_option<P: AsRef<Path>>(option: Option<P>) -> Result<Self, io::Error> {
match option {
Some(path) => File::open(path).map(BufReader::new).map(Self::File),
None => Ok(Self::Stdin(stdin())),
}
}
pub fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
match self {
Self::Stdin(stdin) => stdin.read_line(buf),
Self::File(file) => file.read_line(buf),
}
}
}
impl Read for Input {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
Self::Stdin(stdin) => stdin.read(buf),
Self::File(file) => file.read(buf),
}
}
}
impl Output {
pub fn from_path_option<P: AsRef<Path>>(option: Option<P>) -> Result<Self, io::Error> {
match option {
Some(path) => File::create(path).map(BufWriter::new).map(Self::File),
None => Ok(Self::Stdout(stdout())),
}
}
}
impl Write for Output {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self {
Self::Stdout(stdout) => stdout.write(buf),
Self::File(file) => file.write(buf),
}
}
fn flush(&mut self) -> io::Result<()> {
match self {
Self::Stdout(stdout) => stdout.flush(),
Self::File(file) => file.flush(),
}
}
}

122
userspace/crypt/src/main.rs Normal file
View File

@ -0,0 +1,122 @@
use std::process::ExitCode;
use clap::Parser;
use crypt::{
rsa::RsaKeyOptions, AsymmetricDecryptOptions, AsymmetricEncryptOptions, AsymmetricKeyOptions,
Error,
};
use rsa::pkcs8::{EncodePrivateKey, EncodePublicKey, LineEnding};
#[derive(Parser, Debug)]
struct Args {
#[clap(subcommand)]
command: Command,
}
#[derive(Debug, clap::Subcommand)]
enum Command {
#[clap(subcommand, about = "Generate a key/keypair")]
Generate(GenerateCommand),
#[clap(subcommand, about = "Encrypt data using a public key")]
Encrypt(EncryptCommand),
#[clap(subcommand, about = "Decrypt data using a private key")]
Decrypt(DecryptCommand),
}
#[derive(Debug, clap::Subcommand)]
enum GenerateCommand {
Rsa {
#[clap(short = 'b', long, help = "Key bitness")]
bits: usize,
#[clap(flatten)]
options: AsymmetricKeyOptions,
#[clap(flatten)]
rsa_options: RsaKeyOptions,
},
Ed25519 {
#[clap(flatten)]
options: AsymmetricKeyOptions,
},
}
#[derive(Debug, clap::Subcommand)]
enum EncryptCommand {
Rsa {
#[clap(flatten)]
options: AsymmetricEncryptOptions,
},
}
#[derive(Debug, clap::Subcommand)]
enum DecryptCommand {
Rsa {
#[clap(flatten)]
options: AsymmetricDecryptOptions,
},
}
fn generate_ed25519(options: &AsymmetricKeyOptions) -> Result<(), Error> {
let mut rng = rand::thread_rng();
let signing_key = ed25519_dalek::SigningKey::generate(&mut rng);
println!("Generating a new ed25519 keypair");
signing_key.write_pkcs8_pem_file(&options.private_key, LineEnding::LF)?;
println!("Done, deriving a verifying key");
if let Some(public_key) = options.public_key.as_ref() {
signing_key
.verifying_key()
.write_public_key_pem_file(public_key, LineEnding::LF)?;
} else {
let key_string = signing_key
.verifying_key()
.to_public_key_pem(LineEnding::LF)?;
println!("Public key:");
println!("{key_string}");
}
println!("Done!");
Ok(())
}
fn generate(command: GenerateCommand) -> Result<(), Error> {
match command {
GenerateCommand::Rsa {
options,
rsa_options,
bits,
} => crypt::rsa::generate_rsa(&options, &rsa_options, bits),
GenerateCommand::Ed25519 { options } => generate_ed25519(&options),
}
}
fn encrypt(command: EncryptCommand) -> Result<(), Error> {
match command {
EncryptCommand::Rsa { options } => crypt::rsa::encrypt_rsa(&options),
}
}
fn decrypt(command: DecryptCommand) -> Result<(), Error> {
match command {
DecryptCommand::Rsa { options } => crypt::rsa::decrypt_rsa(&options),
}
}
fn run(command: Command) -> Result<(), Error> {
match command {
Command::Generate(command) => generate(command),
Command::Encrypt(command) => encrypt(command),
Command::Decrypt(command) => decrypt(command),
}
}
fn main() -> ExitCode {
let args = Args::parse();
match run(args.command) {
Ok(_) => ExitCode::SUCCESS,
Err(error) => {
eprintln!("Error: {error}");
ExitCode::FAILURE
}
}
}

View File

@ -0,0 +1,126 @@
use std::{
io::{Read, Write},
path::Path,
};
use rsa::{
pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey, EncodeRsaPrivateKey, EncodeRsaPublicKey},
pkcs8::LineEnding,
Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey,
};
use crate::{
AsymmetricDecryptOptions, AsymmetricEncryptOptions, AsymmetricKeyOptions, Error, Input, Output,
};
#[derive(Default, Debug, Clone, Copy, PartialEq, clap::ValueEnum)]
pub enum KeyFormat {
#[default]
#[clap(alias("pem"), help = "PKCS#1 PEM")]
Pkcs1Pem,
#[clap(alias("der"), help = "PKCS#1 DER")]
Pkcs1Der,
}
#[derive(Debug, clap::Parser)]
pub struct RsaKeyOptions {
#[clap(short = 'f', long, help = "Key output format", default_value = "pem")]
pub format: KeyFormat,
}
pub fn read_private_key<P: AsRef<Path>>(keyfile: P) -> Result<RsaPrivateKey, Error> {
let keyfile = keyfile.as_ref();
if let Ok(key) = RsaPrivateKey::read_pkcs1_pem_file(keyfile) {
return Ok(key);
}
let key = RsaPrivateKey::read_pkcs1_der_file(keyfile)?;
Ok(key)
}
pub fn read_public_key<P: AsRef<Path>>(keyfile: P) -> Result<RsaPublicKey, Error> {
let keyfile = keyfile.as_ref();
if let Ok(key) = RsaPublicKey::read_pkcs1_pem_file(keyfile) {
return Ok(key);
}
let key = RsaPublicKey::read_pkcs1_der_file(keyfile)?;
Ok(key)
}
pub fn generate_rsa(
options: &AsymmetricKeyOptions,
rsa: &RsaKeyOptions,
bits: usize,
) -> Result<(), Error> {
if rsa.format == KeyFormat::Pkcs1Der && options.public_key.is_none() {
return Err(Error::IncompatibleOptions(format!(
"DER output format requires -o/--public-key"
)));
}
println!("Generating a new RSA keypair, bits={}", bits);
println!("This will take a while");
let mut rng = rand::thread_rng();
let private_key = RsaPrivateKey::new(&mut rng, bits)?;
println!("Done, deriving a public key");
let public_key = private_key.to_public_key();
match rsa.format {
KeyFormat::Pkcs1Pem => {
private_key.write_pkcs1_pem_file(&options.private_key, LineEnding::LF)?;
if let Some(public_key_path) = options.public_key.as_ref() {
public_key.write_pkcs1_pem_file(public_key_path, LineEnding::LF)?;
} else {
let output = public_key.to_pkcs1_pem(LineEnding::LF)?;
println!("Public key:");
println!("{output}");
}
}
KeyFormat::Pkcs1Der => {
private_key.write_pkcs1_der_file(&options.private_key)?;
if let Some(public_key_path) = options.public_key.as_ref() {
public_key.write_pkcs1_der_file(public_key_path)?;
}
}
}
println!("Done!");
Ok(())
}
pub fn encrypt_rsa(options: &AsymmetricEncryptOptions) -> Result<(), Error> {
let key = read_public_key(&options.public_key)?;
let mut input = Input::from_path_option(options.input.as_ref())?;
let mut output = Output::from_path_option(options.output.as_ref())?;
let mut buf = [0; 2048];
let mut rng = rand::thread_rng();
loop {
let len = input.read(&mut buf)?;
if len == 0 {
break;
}
let ciphertext = key.encrypt(&mut rng, Pkcs1v15Encrypt, &buf[..len])?;
output.write_all(&ciphertext)?;
}
Ok(())
}
pub fn decrypt_rsa(options: &AsymmetricDecryptOptions) -> Result<(), Error> {
let key = read_private_key(&options.private_key)?;
let mut input = Input::from_path_option(options.input.as_ref())?;
let mut output = Output::from_path_option(options.output.as_ref())?;
let mut buf = [0; 2048];
loop {
let len = input.read(&mut buf)?;
if len == 0 {
break;
}
let plaintext = key.decrypt(Pkcs1v15Encrypt, &buf[..len])?;
output.write_all(&plaintext)?;
}
Ok(())
}

View File

@ -0,0 +1,9 @@
[package]
name = "yasync"
version = "0.1.0"
edition = "2021"
[dependencies]
cross.workspace = true
futures-util = "0.3.31"
spmc = "0.3.0"

View File

@ -0,0 +1,219 @@
#![cfg_attr(target_os = "yggdrasil", feature(yggdrasil_os, rustc_private))]
use std::{
cell::OnceCell,
collections::HashMap,
future::Future,
os::fd::{AsRawFd, RawFd},
pin::Pin,
sync::{Arc, Mutex},
task::{Context, Poll, Waker},
thread::JoinHandle,
time::Duration,
};
use cross::io::{Poll as PollChannel, TimerFd};
use futures_util::task::{waker_ref, ArcWake};
use spmc::{Receiver, Sender};
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub struct Executor {
_id: usize,
queue: Arc<Receiver<Arc<Task>>>,
}
pub struct Task {
future: Mutex<Option<BoxFuture<'static, ()>>>,
runtime: Arc<RuntimeInner>,
}
struct ReactorInner {
poll: PollChannel,
event_map: HashMap<RawFd, Waker>,
}
pub struct Reactor {
// TODO this lock will become very contended
inner: Mutex<ReactorInner>,
}
struct RuntimeInner {
// TODO this lock will become very contended
sender: Mutex<Sender<Arc<Task>>>,
reactor: Arc<Reactor>
}
pub struct Runtime {
inner: Arc<RuntimeInner>,
executors: Vec<JoinHandle<()>>,
reactor: JoinHandle<()>,
}
// TODO use global timer instead of creating a timer for each sleep
pub struct Sleep {
timer: TimerFd,
}
impl Future for Sleep {
type Output = ();
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let expired = self.timer.is_expired().unwrap();
if expired {
return Poll::Ready(());
}
let reactor = &RuntimeInner::get().reactor;
reactor.register(&self.timer, cx.waker().clone());
Poll::Pending
}
}
impl Executor {
pub fn new(id: usize, queue: Arc<Receiver<Arc<Task>>>) -> Self {
Self { _id: id, queue }
}
pub fn run(self) {
loop {
let task = self.queue.recv().unwrap();
let mut slot = task.future.lock().unwrap();
if let Some(mut future) = slot.take() {
let waker = waker_ref(&task);
let context = &mut Context::from_waker(&waker);
if future.as_mut().poll(context).is_pending() {
*slot = Some(future);
}
}
}
}
}
impl ArcWake for Task {
fn wake_by_ref(arc_self: &Arc<Self>) {
arc_self
.runtime
.sender
.lock()
.unwrap()
.send(arc_self.clone())
.unwrap();
}
}
thread_local! {
static RUNTIME: OnceCell<Arc<RuntimeInner>> = OnceCell::new();
}
impl RuntimeInner {
fn try_get() -> Option<Arc<RuntimeInner>> {
RUNTIME.with(|r| r.get().cloned())
}
fn get() -> Arc<RuntimeInner> {
Self::try_get().expect("Runtime::get() called from a thread with no runtime")
}
}
impl Reactor {
fn new() -> Self {
Self {
inner: Mutex::new(ReactorInner {
poll: PollChannel::new().unwrap(),
event_map: HashMap::new(),
}),
}
}
fn run(self: Arc<Self>) {
loop {
// TODO this is a hack to prevent the lock from becoming fully contended by Reactor
std::thread::sleep(Duration::from_millis(100));
let mut inner = self.inner.lock().unwrap();
let event = inner.poll.wait(Some(Duration::from_millis(100))).unwrap();
if let Some(fd) = event {
inner.poll.remove(&fd).ok();
if let Some(task) = inner.event_map.remove(&fd) {
task.wake();
}
}
}
}
fn register<F: AsRawFd>(&self, interest: &F, waker: Waker) {
let mut inner = self.inner.lock().unwrap();
let fd = interest.as_raw_fd();
// TODO have multiple tasks be able to poll the same fd?
assert!(inner.event_map.insert(fd, waker).is_none());
inner.poll.add(interest).unwrap();
}
}
impl Runtime {
pub fn new(threads: usize) -> Self {
let mut executors = vec![];
let (sender, receiver) = spmc::channel();
let reactor = Arc::new(Reactor::new());
let inner = Arc::new(RuntimeInner {
reactor: reactor.clone(),
sender: Mutex::new(sender),
});
let receiver = Arc::new(receiver);
// TODO add an option to run reactor on main thread?
let reactor = std::thread::spawn(move || reactor.run());
for id in 0..threads {
let receiver = receiver.clone();
let inner = inner.clone();
executors.push(std::thread::spawn(move || {
// Initialize local runtime ref
RUNTIME.with(|r| {
if r.set(inner).is_err() {
panic!("Couldn't initialize thread runtime");
}
});
Executor::new(id, receiver).run();
}));
}
Self {
inner,
executors,
reactor,
}
}
pub fn spawn<F: Future<Output = ()> + Send + 'static>(&self, future: F) {
spawn_into(self.inner.clone(), future)
}
pub fn join(self) {
for executor in self.executors {
executor.join().ok();
}
self.reactor.join().ok();
}
}
fn spawn_into<F: Future<Output = ()> + Send + 'static>(runtime: Arc<RuntimeInner>, future: F) {
let task = Arc::new(Task {
future: Mutex::new(Some(Box::pin(future))),
runtime: runtime.clone(),
});
runtime.sender.lock().unwrap().send(task).unwrap();
}
pub fn spawn<F: Future<Output = ()> + Send + 'static>(future: F) {
let runtime = RuntimeInner::get();
spawn_into(runtime, future)
}
pub fn sleep(timeout: Duration) -> Sleep {
let mut timer = TimerFd::new().unwrap();
timer.start(timeout).unwrap();
Sleep { timer }
}

View File

@ -0,0 +1,23 @@
use std::time::Duration;
use yasync::Runtime;
fn main() {
let runtime = Runtime::new(4);
runtime.spawn(async move {
loop {
println!("a");
yasync::sleep(Duration::from_secs(1)).await;
}
});
runtime.spawn(async move {
loop {
println!("b");
yasync::sleep(Duration::from_millis(500)).await;
}
});
runtime.join();
}

View File

@ -17,4 +17,3 @@ cross.workspace = true
bytemuck.workspace = true bytemuck.workspace = true
smallvec = { version = "1.13.2", features = ["serde"] } smallvec = { version = "1.13.2", features = ["serde"] }
ed25519-dalek = "2.1.1"

View File

@ -52,6 +52,8 @@ const PROGRAMS: &[(&str, &str)] = &[
// rsh // rsh
("rsh", "bin/rsh"), ("rsh", "bin/rsh"),
("rshd", "sbin/rshd"), ("rshd", "sbin/rshd"),
// crypt
("crypt", "bin/rc"),
// ("dyn-loader", "libexec/dyn-loader"), // ("dyn-loader", "libexec/dyn-loader"),
]; ];