util: refactor crypto API
This commit is contained in:
parent
96392afc39
commit
89aa01c266
12 changed files with 617 additions and 40 deletions
136
Cargo.lock
generated
136
Cargo.lock
generated
|
@ -416,6 +416,12 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-oid"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "contextual"
|
name = "contextual"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
@ -567,6 +573,17 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5440d1dc8ea7cae44cda3c64568db29bfa2434aba51ae66a50c00488841a65a3"
|
checksum = "5440d1dc8ea7cae44cda3c64568db29bfa2434aba51ae66a50c00488841a65a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "der"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de"
|
||||||
|
dependencies = [
|
||||||
|
"const-oid",
|
||||||
|
"pem-rfc7468",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derivative"
|
name = "derivative"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
|
@ -598,6 +615,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
|
"const-oid",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
@ -1291,6 +1309,9 @@ name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
dependencies = [
|
||||||
|
"spin",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lexical"
|
name = "lexical"
|
||||||
|
@ -1371,6 +1392,12 @@ version = "0.2.138"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libm"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "link-cplusplus"
|
name = "link-cplusplus"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
|
@ -1521,6 +1548,23 @@ dependencies = [
|
||||||
"minimal-lexical",
|
"minimal-lexical",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-bigint-dig"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2399c9463abc5f909349d8aa9ba080e0b88b3ce2885389b60b993f39b1a56905"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"lazy_static",
|
||||||
|
"libm",
|
||||||
|
"num-integer",
|
||||||
|
"num-iter",
|
||||||
|
"num-traits",
|
||||||
|
"rand",
|
||||||
|
"smallvec",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.45"
|
version = "0.1.45"
|
||||||
|
@ -1531,6 +1575,17 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-iter"
|
||||||
|
version = "0.1.43"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
@ -1538,6 +1593,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
|
"libm",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1571,8 +1627,10 @@ dependencies = [
|
||||||
"mime",
|
"mime",
|
||||||
"openssl",
|
"openssl",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
|
"rand",
|
||||||
"rdf-types",
|
"rdf-types",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"rsa",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_test",
|
"serde_test",
|
||||||
|
@ -1704,6 +1762,15 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1240e3e7bfdde5660ba0fc3e9e3565d3b30c2ae1973e218e93b869da771ff2e9"
|
checksum = "1240e3e7bfdde5660ba0fc3e9e3565d3b30c2ae1973e218e93b869da771ff2e9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pem-rfc7468"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
|
@ -1728,6 +1795,28 @@ 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.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eff33bdbdfc54cc98a2eca766ebdec3e1b8fb7387523d5c9c9a2891da856f719"
|
||||||
|
dependencies = [
|
||||||
|
"der",
|
||||||
|
"pkcs8",
|
||||||
|
"spki",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkcs8"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba"
|
||||||
|
dependencies = [
|
||||||
|
"der",
|
||||||
|
"spki",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.26"
|
version = "0.3.26"
|
||||||
|
@ -1949,6 +2038,27 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rsa"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "89b3896c9b7790b70a9aa314a30e4ae114200992a19c96cbe0ca6070edd32ab8"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"digest",
|
||||||
|
"num-bigint-dig",
|
||||||
|
"num-integer",
|
||||||
|
"num-iter",
|
||||||
|
"num-traits",
|
||||||
|
"pkcs1",
|
||||||
|
"pkcs8",
|
||||||
|
"rand_core",
|
||||||
|
"sha2",
|
||||||
|
"signature",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -2141,6 +2251,16 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signature"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
|
@ -2181,6 +2301,16 @@ version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spki"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"der",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlformat"
|
name = "sqlformat"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -2874,6 +3004,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.5.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zstd"
|
name = "zstd"
|
||||||
version = "0.11.2+zstd.1.5.2"
|
version = "0.11.2+zstd.1.5.2"
|
||||||
|
|
|
@ -21,8 +21,10 @@ log = "0.4"
|
||||||
mime = "0.3"
|
mime = "0.3"
|
||||||
openssl = "0.10"
|
openssl = "0.10"
|
||||||
pretty_env_logger = "0.4"
|
pretty_env_logger = "0.4"
|
||||||
|
rand = "0.8"
|
||||||
rdf-types = "0.12"
|
rdf-types = "0.12"
|
||||||
reqwest = { version = "0.11", features = [ "rustls" ] }
|
reqwest = { version = "0.11", features = [ "rustls" ] }
|
||||||
|
rsa = { version = "0.8", features = [ "sha2" ] }
|
||||||
serde = { version = "1.0", features = [ "derive" ] }
|
serde = { version = "1.0", features = [ "derive" ] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_test = "1.0"
|
serde_test = "1.0"
|
||||||
|
|
|
@ -6,7 +6,7 @@ use actix_web::{
|
||||||
use serde::{ser::SerializeMap, Serialize, Serializer};
|
use serde::{ser::SerializeMap, Serialize, Serializer};
|
||||||
use std::{fmt, io};
|
use std::{fmt, io};
|
||||||
|
|
||||||
use crate::util::validate;
|
use crate::util::{crypto, validate};
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
@ -15,7 +15,9 @@ pub enum Error {
|
||||||
BadBearcap,
|
BadBearcap,
|
||||||
BadCredentials,
|
BadCredentials,
|
||||||
BadRequest,
|
BadRequest,
|
||||||
|
BadSignature,
|
||||||
BadToken(jsonwebtoken::errors::Error),
|
BadToken(jsonwebtoken::errors::Error),
|
||||||
|
Crypto(crypto::Error),
|
||||||
Database(sqlx::Error),
|
Database(sqlx::Error),
|
||||||
Invalid(validate::Error),
|
Invalid(validate::Error),
|
||||||
Io(io::Error),
|
Io(io::Error),
|
||||||
|
@ -23,7 +25,6 @@ pub enum Error {
|
||||||
MalformedHeader(header::ToStrError),
|
MalformedHeader(header::ToStrError),
|
||||||
NotFound,
|
NotFound,
|
||||||
Reqwest(reqwest::Error),
|
Reqwest(reqwest::Error),
|
||||||
OpenSSL(openssl::error::ErrorStack),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for Error {
|
impl ResponseError for Error {
|
||||||
|
@ -32,6 +33,7 @@ impl ResponseError for Error {
|
||||||
Error::BadBearcap => StatusCode::UNPROCESSABLE_ENTITY,
|
Error::BadBearcap => StatusCode::UNPROCESSABLE_ENTITY,
|
||||||
Error::BadCredentials => StatusCode::UNAUTHORIZED,
|
Error::BadCredentials => StatusCode::UNAUTHORIZED,
|
||||||
Error::BadRequest => StatusCode::BAD_REQUEST,
|
Error::BadRequest => StatusCode::BAD_REQUEST,
|
||||||
|
Error::BadSignature => StatusCode::UNAUTHORIZED,
|
||||||
Error::BadToken(_) => StatusCode::UNAUTHORIZED,
|
Error::BadToken(_) => StatusCode::UNAUTHORIZED,
|
||||||
Error::Invalid(_) => StatusCode::UNPROCESSABLE_ENTITY,
|
Error::Invalid(_) => StatusCode::UNPROCESSABLE_ENTITY,
|
||||||
Error::MalformedApub(_) => StatusCode::UNPROCESSABLE_ENTITY,
|
Error::MalformedApub(_) => StatusCode::UNPROCESSABLE_ENTITY,
|
||||||
|
@ -52,7 +54,9 @@ impl fmt::Display for Error {
|
||||||
Error::BadBearcap => write!(f, "Invalid bearcap URL"),
|
Error::BadBearcap => write!(f, "Invalid bearcap URL"),
|
||||||
Error::BadCredentials => write!(f, "Invalid user name or password"),
|
Error::BadCredentials => write!(f, "Invalid user name or password"),
|
||||||
Error::BadRequest => write!(f, "Bad request"),
|
Error::BadRequest => write!(f, "Bad request"),
|
||||||
|
Error::BadSignature => write!(f, "Bad signature"),
|
||||||
Error::BadToken(jwt_error) => jwt_error.fmt(f),
|
Error::BadToken(jwt_error) => jwt_error.fmt(f),
|
||||||
|
Error::Crypto(crypto_error) => crypto_error.fmt(f),
|
||||||
Error::Database(sqlx_error) => sqlx_error.fmt(f),
|
Error::Database(sqlx_error) => sqlx_error.fmt(f),
|
||||||
Error::Invalid(validate_error) => validate_error.fmt(f),
|
Error::Invalid(validate_error) => validate_error.fmt(f),
|
||||||
Error::Io(io_error) => io_error.fmt(f),
|
Error::Io(io_error) => io_error.fmt(f),
|
||||||
|
@ -60,7 +64,6 @@ impl fmt::Display for Error {
|
||||||
Error::MalformedApub(msg) => write!(f, "Malformed ActivityPub: {msg}"),
|
Error::MalformedApub(msg) => write!(f, "Malformed ActivityPub: {msg}"),
|
||||||
Error::MalformedHeader(to_str_error) => to_str_error.fmt(f),
|
Error::MalformedHeader(to_str_error) => to_str_error.fmt(f),
|
||||||
Error::Reqwest(reqwest_error) => reqwest_error.fmt(f),
|
Error::Reqwest(reqwest_error) => reqwest_error.fmt(f),
|
||||||
Error::OpenSSL(_) => write!(f, "OpenSSL failed for some reason"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,9 +107,9 @@ impl From<reqwest::Error> for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<openssl::error::ErrorStack> for Error {
|
impl From<crypto::Error> for Error {
|
||||||
fn from(e: openssl::error::ErrorStack) -> Error {
|
fn from(e: crypto::Error) -> Error {
|
||||||
Error::OpenSSL(e)
|
Error::Crypto(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,10 @@ impl MemAccountDataSource {
|
||||||
pub async fn store(&self, account: Account) {
|
pub async fn store(&self, account: Account) {
|
||||||
self.cache.put(account).await
|
self.cache.put(account).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn delete(&self, id: AccountId) {
|
||||||
|
self.cache.del(id).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Indexable<AccountId> for Account {
|
impl Indexable<AccountId> for Account {
|
||||||
|
|
|
@ -35,4 +35,12 @@ impl PgAccountDataSource {
|
||||||
.await?;
|
.await?;
|
||||||
Ok(account)
|
Ok(account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn delete(&self, id: AccountId) -> Result<()> {
|
||||||
|
sqlx::query("DELETE FROM accounts WHERE id = $1")
|
||||||
|
.bind(id)
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use sqlx::FromRow;
|
use sqlx::FromRow;
|
||||||
|
|
||||||
use crate::core::*;
|
use crate::core::*;
|
||||||
use crate::util::keys::PublicKeyDer;
|
use crate::util::crypto::PubKey;
|
||||||
use crate::util::validate::{ResultBuilder, Validate};
|
use crate::util::validate::{ResultBuilder, Validate};
|
||||||
|
|
||||||
pub type AccountId = Id<Account>;
|
pub type AccountId = Id<Account>;
|
||||||
|
@ -15,7 +15,7 @@ pub struct Account {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub domain: String,
|
pub domain: String,
|
||||||
pub display_name: Option<String>,
|
pub display_name: Option<String>,
|
||||||
pub public_key: Option<PublicKeyDer>,
|
pub public_key: Option<PubKey>,
|
||||||
pub created_at: NaiveDateTime,
|
pub created_at: NaiveDateTime,
|
||||||
pub updated_at: NaiveDateTime,
|
pub updated_at: NaiveDateTime,
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ pub struct NewAccount {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub domain: String,
|
pub domain: String,
|
||||||
pub display_name: Option<String>,
|
pub display_name: Option<String>,
|
||||||
pub public_key: Option<PublicKeyDer>,
|
pub public_key: Option<PubKey>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Account> for AccountId {
|
impl From<Account> for AccountId {
|
||||||
|
|
|
@ -3,7 +3,7 @@ use sqlx::FromRow;
|
||||||
|
|
||||||
use crate::core::*;
|
use crate::core::*;
|
||||||
use crate::model::AccountId;
|
use crate::model::AccountId;
|
||||||
use crate::util::keys::PrivateKeyDer;
|
use crate::util::crypto::PrivKey;
|
||||||
use crate::util::password::HashedPassword;
|
use crate::util::password::HashedPassword;
|
||||||
use crate::util::validate::{ResultBuilder, Validate};
|
use crate::util::validate::{ResultBuilder, Validate};
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ pub struct User {
|
||||||
pub reason: Option<String>,
|
pub reason: Option<String>,
|
||||||
pub locale: String,
|
pub locale: String,
|
||||||
pub activated: bool,
|
pub activated: bool,
|
||||||
pub private_key: PrivateKeyDer,
|
pub private_key: PrivKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NewUser {
|
pub struct NewUser {
|
||||||
|
@ -27,7 +27,7 @@ pub struct NewUser {
|
||||||
pub password: HashedPassword,
|
pub password: HashedPassword,
|
||||||
pub reason: Option<String>,
|
pub reason: Option<String>,
|
||||||
pub locale: String,
|
pub locale: String,
|
||||||
pub private_key: PrivateKeyDer,
|
pub private_key: PrivKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<User> for UserId {
|
impl From<User> for UserId {
|
||||||
|
|
|
@ -38,4 +38,14 @@ impl AccountRepo {
|
||||||
self.mem.store(account.clone()).await;
|
self.mem.store(account.clone()).await;
|
||||||
Ok(account)
|
Ok(account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn delete<I>(&self, id: I) -> Result<()>
|
||||||
|
where
|
||||||
|
I: Into<AccountId> + Send,
|
||||||
|
{
|
||||||
|
let id = id.into();
|
||||||
|
self.db.delete(id).await?;
|
||||||
|
self.mem.delete(id).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ use crate::ent;
|
||||||
use crate::middle::AuthData;
|
use crate::middle::AuthData;
|
||||||
use crate::model::{AccountId, NewAccount, NewUser};
|
use crate::model::{AccountId, NewAccount, NewUser};
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
use crate::util::keys::generate_keypair;
|
use crate::util::crypto::PrivKey;
|
||||||
use crate::util::password::{self, ClearPassword};
|
use crate::util::password::ClearPassword;
|
||||||
use crate::util::validate::{ResultBuilder, Validate};
|
use crate::util::validate::{ResultBuilder, Validate};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
@ -24,7 +24,8 @@ struct SignupData {
|
||||||
async fn signup(data: web::Form<SignupData>, state: AppState) -> Result<HttpResponse> {
|
async fn signup(data: web::Form<SignupData>, state: AppState) -> Result<HttpResponse> {
|
||||||
let data: Sane<SignupData> = Insane::from(data.into_inner()).try_into()?;
|
let data: Sane<SignupData> = Insane::from(data.into_inner()).try_into()?;
|
||||||
let data = data.inner();
|
let data = data.inner();
|
||||||
let (pubkey, privkey) = generate_keypair()?;
|
let private_key = PrivKey::new()?;
|
||||||
|
let public_key = private_key.derive_pubkey().await?;
|
||||||
|
|
||||||
let account = state
|
let account = state
|
||||||
.repo
|
.repo
|
||||||
|
@ -34,10 +35,10 @@ async fn signup(data: web::Form<SignupData>, state: AppState) -> Result<HttpResp
|
||||||
name: data.username,
|
name: data.username,
|
||||||
domain: "localhost".into(),
|
domain: "localhost".into(),
|
||||||
display_name: None,
|
display_name: None,
|
||||||
public_key: Some(pubkey),
|
public_key: Some(public_key),
|
||||||
}))
|
}))
|
||||||
.await?;
|
.await?;
|
||||||
state
|
let user_result = state
|
||||||
.repo
|
.repo
|
||||||
.users
|
.users
|
||||||
.create(Insane::from(NewUser {
|
.create(Insane::from(NewUser {
|
||||||
|
@ -46,9 +47,13 @@ async fn signup(data: web::Form<SignupData>, state: AppState) -> Result<HttpResp
|
||||||
password: data.password.hash(),
|
password: data.password.hash(),
|
||||||
locale: data.locale,
|
locale: data.locale,
|
||||||
reason: data.reason,
|
reason: data.reason,
|
||||||
private_key: privkey,
|
private_key,
|
||||||
}))
|
}))
|
||||||
.await?;
|
.await;
|
||||||
|
if user_result.is_err() {
|
||||||
|
state.repo.accounts.delete(account).await?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
429
src/util/crypto.rs
Normal file
429
src/util/crypto.rs
Normal file
|
@ -0,0 +1,429 @@
|
||||||
|
use rsa::{
|
||||||
|
pkcs1,
|
||||||
|
pkcs1v15::{Signature, SigningKey, VerifyingKey},
|
||||||
|
pkcs8::{
|
||||||
|
self, DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey, LineEnding,
|
||||||
|
},
|
||||||
|
rand_core::OsRng,
|
||||||
|
sha2::Sha256,
|
||||||
|
signature::{RandomizedSigner, Verifier},
|
||||||
|
RsaPrivateKey, RsaPublicKey,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
use sqlx::{
|
||||||
|
database::{HasArguments, HasValueRef},
|
||||||
|
encode::IsNull,
|
||||||
|
error::BoxDynError,
|
||||||
|
Database,
|
||||||
|
};
|
||||||
|
use std::{fmt, future};
|
||||||
|
use tokio::sync::OnceCell;
|
||||||
|
|
||||||
|
use crate::core::*;
|
||||||
|
|
||||||
|
pub const DEFAULT_KEY_SIZE: usize = 2048;
|
||||||
|
|
||||||
|
/// Our abstract wrapper around "any" type of public key.
|
||||||
|
/// We currently assume all keys are RSA.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PubKey {
|
||||||
|
pkey: OnceCell<RsaPublicKey>,
|
||||||
|
der: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Our abstract wrapper around "any" type of private key.
|
||||||
|
/// We currently assume all keys are RSA.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PrivKey {
|
||||||
|
pkey: OnceCell<RsaPrivateKey>,
|
||||||
|
der: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Error(rsa::errors::Error);
|
||||||
|
|
||||||
|
impl PubKey {
|
||||||
|
pub fn from_der_unchecked(der: Vec<u8>) -> PubKey {
|
||||||
|
PubKey {
|
||||||
|
pkey: OnceCell::new(),
|
||||||
|
der,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_pem(pem: &str) -> Result<PubKey> {
|
||||||
|
if pem.starts_with("-----BEGIN PUBLIC KEY-----") {
|
||||||
|
PubKey::from_pkcs8_pem(pem)
|
||||||
|
} else {
|
||||||
|
PubKey::from_pkcs1_pem(pem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_pkcs8_pem(pem: &str) -> Result<PubKey> {
|
||||||
|
let pkey = RsaPublicKey::from_public_key_pem(pem).map_err(Error::from)?;
|
||||||
|
let der = pkey.to_public_key_der().map_err(Error::from)?.into_vec();
|
||||||
|
Ok(PubKey {
|
||||||
|
pkey: OnceCell::from(pkey),
|
||||||
|
der,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_pkcs1_pem(pem: &str) -> Result<PubKey> {
|
||||||
|
let pkey = <RsaPublicKey as pkcs1::DecodeRsaPublicKey>::from_pkcs1_pem(pem)
|
||||||
|
.map_err(Error::from)?;
|
||||||
|
let der = pkey.to_public_key_der().map_err(Error::from)?.into_vec();
|
||||||
|
Ok(PubKey {
|
||||||
|
pkey: OnceCell::from(pkey),
|
||||||
|
der,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn to_pem(&self) -> Result<String> {
|
||||||
|
let pkey = self.get_pkey().await?;
|
||||||
|
let pem = pkey
|
||||||
|
.to_public_key_pem(LineEnding::LF)
|
||||||
|
.map_err(Error::from)?;
|
||||||
|
Ok(pem)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn verify(&self, data: &[u8], signature: &[u8]) -> Result<()> {
|
||||||
|
let pkey = self.get_pkey().await?;
|
||||||
|
let signature = Signature::from(Box::from(signature));
|
||||||
|
let verifying_key: VerifyingKey<Sha256> = VerifyingKey::new_with_prefix(pkey.clone());
|
||||||
|
verifying_key
|
||||||
|
.verify(data, &signature)
|
||||||
|
.map_err(|_| crate::core::Error::BadSignature)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_pkey(&self) -> Result<&RsaPublicKey> {
|
||||||
|
self.pkey
|
||||||
|
.get_or_try_init(|| {
|
||||||
|
future::ready(
|
||||||
|
RsaPublicKey::from_public_key_der(self.der.as_slice())
|
||||||
|
.map_err(Error::from)
|
||||||
|
.map_err(crate::core::Error::from),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrivKey {
|
||||||
|
/// Generate a new private key.
|
||||||
|
pub fn new() -> Result<PrivKey> {
|
||||||
|
// The rsa crate takes like two orders of magnitude longer to generate a key,
|
||||||
|
// so until they get that under control we'll use the raw OpenSSL bindings to
|
||||||
|
// generate a key, encode it to PKCS#1 DER, and load it again.
|
||||||
|
let pkey = openssl::rsa::Rsa::generate(DEFAULT_KEY_SIZE as u32).unwrap();
|
||||||
|
let pkcs1_der = pkey.private_key_to_der().unwrap();
|
||||||
|
let pkey =
|
||||||
|
<RsaPrivateKey as pkcs1::DecodeRsaPrivateKey>::from_pkcs1_der(pkcs1_der.as_slice())
|
||||||
|
.map_err(Error::from)?;
|
||||||
|
let der = pkey.to_pkcs8_der().map_err(Error::from)?;
|
||||||
|
let der = Vec::from(der.as_bytes());
|
||||||
|
Ok(PrivKey {
|
||||||
|
pkey: OnceCell::from(pkey),
|
||||||
|
der,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_der_unchecked(der: Vec<u8>) -> PrivKey {
|
||||||
|
PrivKey {
|
||||||
|
pkey: OnceCell::new(),
|
||||||
|
der,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn derive_pubkey(&self) -> Result<PubKey> {
|
||||||
|
let pkey = self.get_pkey().await?;
|
||||||
|
PubKey::try_from(pkey.to_public_key())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn sign(&self, data: &[u8]) -> Result<Vec<u8>> {
|
||||||
|
let pkey = self.get_pkey().await?;
|
||||||
|
let signing_key: SigningKey<Sha256> = SigningKey::new_with_prefix(pkey.clone());
|
||||||
|
let signature = signing_key.sign_with_rng(&mut OsRng, data);
|
||||||
|
Ok(Vec::from(signature.as_ref()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_pkey(&self) -> Result<&RsaPrivateKey> {
|
||||||
|
self.pkey
|
||||||
|
.get_or_try_init(|| {
|
||||||
|
future::ready(
|
||||||
|
RsaPrivateKey::from_pkcs8_der(self.der.as_slice())
|
||||||
|
.map_err(|e| Error::from(e).into()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<RsaPrivateKey> for PrivKey {
|
||||||
|
type Error = crate::core::Error;
|
||||||
|
|
||||||
|
fn try_from(val: RsaPrivateKey) -> Result<PrivKey> {
|
||||||
|
let der = val.to_pkcs8_der().map_err(Error::from)?;
|
||||||
|
let der = Vec::from(der.as_bytes());
|
||||||
|
Ok(PrivKey {
|
||||||
|
pkey: OnceCell::from(val),
|
||||||
|
der,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<RsaPublicKey> for PubKey {
|
||||||
|
type Error = crate::core::Error;
|
||||||
|
|
||||||
|
fn try_from(val: RsaPublicKey) -> Result<PubKey> {
|
||||||
|
let der = val.to_public_key_der().map_err(Error::from)?.into_vec();
|
||||||
|
Ok(PubKey {
|
||||||
|
pkey: OnceCell::from(val),
|
||||||
|
der,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DB: Database> sqlx::Type<DB> for PubKey
|
||||||
|
where
|
||||||
|
Vec<u8>: sqlx::Type<DB>,
|
||||||
|
{
|
||||||
|
fn type_info() -> DB::TypeInfo {
|
||||||
|
<Vec<u8> as sqlx::Type<DB>>::type_info()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compatible(ty: &DB::TypeInfo) -> bool {
|
||||||
|
<Vec<u8> as sqlx::Type<DB>>::compatible(ty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DB: Database> sqlx::Type<DB> for PrivKey
|
||||||
|
where
|
||||||
|
Vec<u8>: sqlx::Type<DB>,
|
||||||
|
{
|
||||||
|
fn type_info() -> DB::TypeInfo {
|
||||||
|
<Vec<u8> as sqlx::Type<DB>>::type_info()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compatible(ty: &DB::TypeInfo) -> bool {
|
||||||
|
<Vec<u8> as sqlx::Type<DB>>::compatible(ty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'q, DB: Database> sqlx::Encode<'q, DB> for PubKey
|
||||||
|
where
|
||||||
|
Vec<u8>: sqlx::Encode<'q, DB>,
|
||||||
|
{
|
||||||
|
fn encode(self, buf: &mut <DB as HasArguments<'q>>::ArgumentBuffer) -> IsNull {
|
||||||
|
self.der.encode(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_by_ref(&self, buf: &mut <DB as HasArguments<'q>>::ArgumentBuffer) -> IsNull {
|
||||||
|
self.der.encode_by_ref(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn produces(&self) -> Option<DB::TypeInfo> {
|
||||||
|
self.der.produces()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> usize {
|
||||||
|
self.der.size_hint()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'q, DB: Database> sqlx::Encode<'q, DB> for PrivKey
|
||||||
|
where
|
||||||
|
Vec<u8>: sqlx::Encode<'q, DB>,
|
||||||
|
{
|
||||||
|
fn encode(self, buf: &mut <DB as HasArguments<'q>>::ArgumentBuffer) -> IsNull {
|
||||||
|
self.der.encode(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_by_ref(&self, buf: &mut <DB as HasArguments<'q>>::ArgumentBuffer) -> IsNull {
|
||||||
|
self.der.encode_by_ref(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn produces(&self) -> Option<DB::TypeInfo> {
|
||||||
|
self.der.produces()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> usize {
|
||||||
|
self.der.size_hint()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r, DB: Database> sqlx::Decode<'r, DB> for PubKey
|
||||||
|
where
|
||||||
|
Vec<u8>: sqlx::Decode<'r, DB>,
|
||||||
|
{
|
||||||
|
fn decode(value: <DB as HasValueRef<'r>>::ValueRef) -> std::result::Result<Self, BoxDynError> {
|
||||||
|
let value = <Vec<u8> as sqlx::Decode<'r, DB>>::decode(value)?;
|
||||||
|
Ok(PubKey::from_der_unchecked(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r, DB: Database> sqlx::Decode<'r, DB> for PrivKey
|
||||||
|
where
|
||||||
|
Vec<u8>: sqlx::Decode<'r, DB>,
|
||||||
|
{
|
||||||
|
fn decode(value: <DB as HasValueRef<'r>>::ValueRef) -> std::result::Result<Self, BoxDynError> {
|
||||||
|
let value = <Vec<u8> as sqlx::Decode<'r, DB>>::decode(value)?;
|
||||||
|
Ok(PrivKey::from_der_unchecked(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for PubKey
|
||||||
|
where
|
||||||
|
Vec<u8>: Serialize,
|
||||||
|
{
|
||||||
|
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
self.der.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for PrivKey
|
||||||
|
where
|
||||||
|
Vec<u8>: Serialize,
|
||||||
|
{
|
||||||
|
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
self.der.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for PubKey
|
||||||
|
where
|
||||||
|
Vec<u8>: Deserialize<'de>,
|
||||||
|
{
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<PubKey, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Vec::<u8>::deserialize(deserializer).map(PubKey::from_der_unchecked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for PrivKey
|
||||||
|
where
|
||||||
|
Vec<u8>: Deserialize<'de>,
|
||||||
|
{
|
||||||
|
fn deserialize<D>(deserializer: D) -> std::result::Result<PrivKey, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Vec::<u8>::deserialize(deserializer).map(PrivKey::from_der_unchecked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<rsa::errors::Error> for Error {
|
||||||
|
fn from(val: rsa::errors::Error) -> Error {
|
||||||
|
Error(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<pkcs1::Error> for Error {
|
||||||
|
fn from(val: pkcs1::Error) -> Error {
|
||||||
|
Error::from(rsa::errors::Error::from(val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<pkcs8::Error> for Error {
|
||||||
|
fn from(val: pkcs8::Error) -> Error {
|
||||||
|
Error::from(rsa::errors::Error::from(val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<pkcs8::spki::Error> for Error {
|
||||||
|
fn from(val: pkcs8::spki::Error) -> Error {
|
||||||
|
Error::from(pkcs8::Error::from(val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Error
|
||||||
|
where
|
||||||
|
rsa::errors::Error: fmt::Debug,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error
|
||||||
|
where
|
||||||
|
rsa::errors::Error: fmt::Display,
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const TEST_PUBKEY_PKCS8: &str = "-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz4muPogkMCRdIShQLyCv
|
||||||
|
6QMb7d9epNfKGFyPi4C5w9rZTJ5Ox7X4cueXA6imMDJ0DCfD34QESJoIjkXht3W0
|
||||||
|
AanLSQnFh+p/5RsbEPb4zaUzG7OHGrYsIE2/LEUFyAuE15KVnXMmtoqN4k8Y5NtC
|
||||||
|
2GWGnnNW/iD+mr6SMLPQ44+bdPegBjbQmAJ3I/H4byoYvRWWE7g9klWyEZmlSwQQ
|
||||||
|
MG4m86utQeO7JQ9dHUiG6PtuEm0PVB0pUT0a/qF3wRCMPIpPiA/E+z3yfYYnivKu
|
||||||
|
wPsehgVguIGxzQaIOaN5UU7UmL36bAT3E0yhelmDdXkxeo6dQDnkuLRBwMTtFY3w
|
||||||
|
/QIDAQAB
|
||||||
|
-----END PUBLIC KEY-----
|
||||||
|
";
|
||||||
|
const TEST_PUBKEY_PKCS1: &str = "-----BEGIN RSA PUBLIC KEY-----
|
||||||
|
MIIBCgKCAQEAz4muPogkMCRdIShQLyCv6QMb7d9epNfKGFyPi4C5w9rZTJ5Ox7X4
|
||||||
|
cueXA6imMDJ0DCfD34QESJoIjkXht3W0AanLSQnFh+p/5RsbEPb4zaUzG7OHGrYs
|
||||||
|
IE2/LEUFyAuE15KVnXMmtoqN4k8Y5NtC2GWGnnNW/iD+mr6SMLPQ44+bdPegBjbQ
|
||||||
|
mAJ3I/H4byoYvRWWE7g9klWyEZmlSwQQMG4m86utQeO7JQ9dHUiG6PtuEm0PVB0p
|
||||||
|
UT0a/qF3wRCMPIpPiA/E+z3yfYYnivKuwPsehgVguIGxzQaIOaN5UU7UmL36bAT3
|
||||||
|
E0yhelmDdXkxeo6dQDnkuLRBwMTtFY3w/QIDAQAB
|
||||||
|
-----END RSA PUBLIC KEY-----
|
||||||
|
";
|
||||||
|
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn verify_signatures() {
|
||||||
|
let priv_key = PrivKey::new().unwrap();
|
||||||
|
let pub_key = priv_key.derive_pubkey().await.unwrap();
|
||||||
|
|
||||||
|
let message = String::from("hello, world");
|
||||||
|
let signature = priv_key.sign(message.as_bytes()).await.unwrap();
|
||||||
|
|
||||||
|
assert_ne!(signature.as_slice(), message.as_bytes());
|
||||||
|
|
||||||
|
assert!(pub_key
|
||||||
|
.verify(message.as_bytes(), signature.as_slice())
|
||||||
|
.await
|
||||||
|
.is_ok());
|
||||||
|
|
||||||
|
let tampered_message = String::from("hello, world!");
|
||||||
|
assert!(pub_key
|
||||||
|
.verify(tampered_message.as_bytes(), signature.as_slice())
|
||||||
|
.await
|
||||||
|
.is_err());
|
||||||
|
|
||||||
|
let mut tampered_signature = signature.clone();
|
||||||
|
tampered_signature[0] ^= 1;
|
||||||
|
assert!(pub_key
|
||||||
|
.verify(message.as_bytes(), tampered_signature.as_slice())
|
||||||
|
.await
|
||||||
|
.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn parse_pkcs8_pem() {
|
||||||
|
let pub_key = PubKey::from_pem(TEST_PUBKEY_PKCS8).unwrap();
|
||||||
|
let pem = pub_key.to_pem().await.unwrap();
|
||||||
|
assert_eq!(pem, TEST_PUBKEY_PKCS8);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web::test]
|
||||||
|
async fn parse_pkcs1_pem() {
|
||||||
|
let pub_key = PubKey::from_pem(TEST_PUBKEY_PKCS1).unwrap();
|
||||||
|
let pem = pub_key.to_pem().await.unwrap();
|
||||||
|
// to_pem() should always return PKCS#8
|
||||||
|
assert_eq!(pem, TEST_PUBKEY_PKCS8);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
use openssl::pkey::{Private, Public};
|
|
||||||
use openssl::rsa::Rsa;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::core::*;
|
|
||||||
|
|
||||||
#[derive(sqlx::Type, Clone, Serialize, Deserialize)]
|
|
||||||
#[sqlx(transparent)]
|
|
||||||
pub struct PublicKeyDer(Vec<u8>);
|
|
||||||
|
|
||||||
#[derive(sqlx::Type, Clone, Serialize, Deserialize)]
|
|
||||||
#[sqlx(transparent)]
|
|
||||||
pub struct PrivateKeyDer(Vec<u8>);
|
|
||||||
|
|
||||||
pub fn generate_keypair() -> Result<(PublicKeyDer, PrivateKeyDer)> {
|
|
||||||
let private = Rsa::generate(4096)?;
|
|
||||||
let public_key = PublicKeyDer(private.public_key_to_der()?);
|
|
||||||
let private_key = PrivateKeyDer(private.private_key_to_der()?);
|
|
||||||
Ok((public_key, private_key))
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
pub mod bear;
|
pub mod bear;
|
||||||
|
pub mod crypto;
|
||||||
pub mod http;
|
pub mod http;
|
||||||
pub mod keys;
|
|
||||||
pub mod password;
|
pub mod password;
|
||||||
pub mod token;
|
pub mod token;
|
||||||
pub mod validate;
|
pub mod validate;
|
||||||
|
|
Loading…
Reference in a new issue