Compare commits
7 commits
Author | SHA1 | Date | |
---|---|---|---|
asonix | 5fb8c845de | ||
asonix | 0df2cc3c1a | ||
asonix | a871b0ae82 | ||
asonix | 6c43f9533c | ||
asonix | a0052b50e2 | ||
Aode (lion) | 762872e841 | ||
asonix | 3cf9079878 |
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
|||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
/.direnv
|
||||
/.envrc
|
||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -1,19 +1,19 @@
|
|||
[package]
|
||||
name = "rsa-magic-public-key"
|
||||
description = "Converting between the Magic Public Key format and a RustCrypto RSA type"
|
||||
version = "0.3.0"
|
||||
license-file = "LICENSE"
|
||||
version = "0.8.0"
|
||||
license = "AGPL-3.0"
|
||||
authors = ["asonix <asonix@asonix.dog>"]
|
||||
edition = "2018"
|
||||
repository = "https://git.asonix.dog/Aardwolf/rsa-magic-public-key"
|
||||
repository = "https://git.asonix.dog/asonix/rsa-magic-public-key"
|
||||
readme = "README.md"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.13"
|
||||
num-bigint-dig = "0.7"
|
||||
rsa = "0.4.0"
|
||||
base64 = "0.21"
|
||||
num-bigint-dig = "0.8"
|
||||
rsa = "0.9.0"
|
||||
thiserror = "1.0.9"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
20
README.md
20
README.md
|
@ -16,16 +16,14 @@ assert!(res.is_ok());
|
|||
```
|
||||
|
||||
### Contributing
|
||||
Unless otherwise stated, all contributions to this project will be licensed under the CSL with
|
||||
the exceptions listed in the License section of this file.
|
||||
Feel free to open issues for anything you find an issue with. Please note that any contributed code will be licensed under the AGPLv3.
|
||||
|
||||
### License
|
||||
This work is licensed under the Cooperative Software License. This is not a Free Software
|
||||
License, but may be considered a "source-available License." For most hobbyists, self-employed
|
||||
developers, worker-owned companies, and cooperatives, this software can be used in most
|
||||
projects so long as this software is distributed under the terms of the CSL. For more
|
||||
information, see the provided LICENSE file. If none exists, the license can be found online
|
||||
[here](https://lynnesbian.space/csl/). If you are a free software project and wish to use this
|
||||
software under the terms of the GNU Affero General Public License, please contact me at
|
||||
[asonix@asonix.dog](mailto:asonix@asonix.dog) and we can sort that out. If you wish to use this
|
||||
project under any other license, especially in proprietary software, the answer is likely no.
|
||||
|
||||
Copyright © 2023 Riley Trautman
|
||||
|
||||
rsa-magic-public-key is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
rsa-magic-public-key is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. This file is part of rsa-magic-public-key.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with rsa-magic-public-key. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/).
|
||||
|
|
61
flake.lock
Normal file
61
flake.lock
Normal file
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1681202837,
|
||||
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1682526928,
|
||||
"narHash": "sha256-2cKh4O6t1rQ8Ok+v16URynmb0rV7oZPEbXkU0owNLQs=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d6b863fd9b7bb962e6f9fdf292419a775e772891",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
25
flake.nix
Normal file
25
flake.nix
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
description = "rsa-magic-public-key";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, flake-utils }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
};
|
||||
in
|
||||
{
|
||||
packages.default = pkgs.hello;
|
||||
|
||||
devShell = with pkgs; mkShell {
|
||||
nativeBuildInputs = [ cargo cargo-outdated cargo-zigbuild clippy gcc protobuf rust-analyzer rustc rustfmt ];
|
||||
|
||||
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
|
||||
};
|
||||
});
|
||||
}
|
52
src/lib.rs
52
src/lib.rs
|
@ -1,4 +1,4 @@
|
|||
//! Encode RSA's Public Key as a Magic Public Key
|
||||
//! Encode Rsa's Public Key as a Magic Public Key
|
||||
//!
|
||||
//! This implementation has been reverse-engineered from Mastodon's implementation, since no
|
||||
//! documentation for the Magic Public Key format could be found online (Maybe I didn't look hard
|
||||
|
@ -8,14 +8,14 @@
|
|||
//! From private key
|
||||
//! ```rust
|
||||
//! # let mut rng = rand::thread_rng();
|
||||
//! # let private_key = rsa::RSAPrivateKey::new(&mut rng, 2048).unwrap();
|
||||
//! # let private_key = rsa::RsaPrivateKey::new(&mut rng, 2048).unwrap();
|
||||
//! use rsa_magic_public_key::AsMagicPublicKey;
|
||||
//! let string = private_key.as_magic_public_key();
|
||||
//! ```
|
||||
//! From public key
|
||||
//! ```rust
|
||||
//! # let mut rng = rand::thread_rng();
|
||||
//! # let private_key = rsa::RSAPrivateKey::new(&mut rng, 2048).unwrap();
|
||||
//! # let private_key = rsa::RsaPrivateKey::new(&mut rng, 2048).unwrap();
|
||||
//! # let public_key = private_key.to_public_key();
|
||||
//! use rsa_magic_public_key::AsMagicPublicKey;
|
||||
//! let string = public_key.as_magic_public_key();
|
||||
|
@ -24,24 +24,24 @@
|
|||
//! ```rust
|
||||
//! # use rsa_magic_public_key::AsMagicPublicKey;
|
||||
//! # let mut rng = rand::thread_rng();
|
||||
//! # let private_key = rsa::RSAPrivateKey::new(&mut rng, 2048).unwrap();
|
||||
//! # let private_key = rsa::RsaPrivateKey::new(&mut rng, 2048).unwrap();
|
||||
//! # let magic_public_key = private_key.as_magic_public_key();
|
||||
//! use rsa::RSAPublicKey;
|
||||
//! use rsa::RsaPublicKey;
|
||||
//! use rsa_magic_public_key::FromMagicPublicKey;
|
||||
//! let public_key = RSAPublicKey::from_magic_public_key(&magic_public_key).unwrap();
|
||||
//! let public_key = RsaPublicKey::from_magic_public_key(&magic_public_key).unwrap();
|
||||
//! ```
|
||||
use base64::{decode_config, encode_config, URL_SAFE};
|
||||
use base64::{engine::general_purpose::URL_SAFE, Engine};
|
||||
use num_bigint_dig::BigUint;
|
||||
use rsa::{PublicKey, RSAPublicKey};
|
||||
use rsa::{traits::PublicKeyParts, RsaPublicKey};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Helper trait to add functionality to RSA types
|
||||
/// Helper trait to add functionality to Rsa types
|
||||
pub trait AsMagicPublicKey {
|
||||
/// Produce a magic-public-key formatted string
|
||||
fn as_magic_public_key(&self) -> String;
|
||||
}
|
||||
|
||||
/// Helper trait to add functionality to RSA types
|
||||
/// Helper trait to add functionality to Rsa types
|
||||
pub trait FromMagicPublicKey {
|
||||
/// Parse a type from a magic-public-key formatted string
|
||||
fn from_magic_public_key(magic_public_key: &str) -> Result<Self, KeyError>
|
||||
|
@ -56,43 +56,43 @@ pub enum KeyError {
|
|||
#[error("The provided key is malformed")]
|
||||
Malformed,
|
||||
|
||||
/// The magic-public-key is not RSA
|
||||
/// The magic-public-key is not Rsa
|
||||
#[error("The provided key is of the wrong kind")]
|
||||
Kind,
|
||||
}
|
||||
|
||||
impl<T> AsMagicPublicKey for T
|
||||
where
|
||||
T: PublicKey,
|
||||
T: PublicKeyParts,
|
||||
{
|
||||
fn as_magic_public_key(&self) -> String {
|
||||
let n = encode_config(&self.n().to_bytes_be(), URL_SAFE);
|
||||
let e = encode_config(&self.e().to_bytes_be(), URL_SAFE);
|
||||
let n = URL_SAFE.encode(&self.n().to_bytes_be());
|
||||
let e = URL_SAFE.encode(&self.e().to_bytes_be());
|
||||
|
||||
format!("RSA.{}.{}", n, e)
|
||||
format!("Rsa.{}.{}", n, e)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMagicPublicKey for RSAPublicKey {
|
||||
impl FromMagicPublicKey for RsaPublicKey {
|
||||
fn from_magic_public_key(magic_public_key: &str) -> Result<Self, KeyError> {
|
||||
let mut iter = magic_public_key.split('.');
|
||||
let kind = iter.next().ok_or(KeyError::Malformed)?;
|
||||
|
||||
match kind {
|
||||
"RSA" => (),
|
||||
"Rsa" => (),
|
||||
_ => return Err(KeyError::Kind),
|
||||
};
|
||||
|
||||
let n = iter.next().ok_or(KeyError::Malformed)?;
|
||||
let e = iter.next().ok_or(KeyError::Malformed)?;
|
||||
|
||||
let n = decode_config(n, URL_SAFE)?;
|
||||
let e = decode_config(e, URL_SAFE)?;
|
||||
let n = URL_SAFE.decode(n)?;
|
||||
let e = URL_SAFE.decode(e)?;
|
||||
|
||||
let n = BigUint::from_bytes_be(&n);
|
||||
let e = BigUint::from_bytes_be(&e);
|
||||
|
||||
RSAPublicKey::new(n, e).map_err(|_| KeyError::Malformed)
|
||||
RsaPublicKey::new(n, e).map_err(|_| KeyError::Malformed)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,32 +105,32 @@ impl From<base64::DecodeError> for KeyError {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{AsMagicPublicKey, FromMagicPublicKey};
|
||||
use rsa::{RSAPrivateKey, RSAPublicKey};
|
||||
use rsa::{RsaPrivateKey, RsaPublicKey};
|
||||
|
||||
#[test]
|
||||
fn can_complete_cycle() {
|
||||
let mut rng = rand::thread_rng();
|
||||
let rsa = RSAPrivateKey::new(&mut rng, 2048).unwrap();
|
||||
let rsa = RsaPrivateKey::new(&mut rng, 2048).unwrap();
|
||||
|
||||
let string = rsa.as_magic_public_key();
|
||||
|
||||
let res = RSAPublicKey::from_magic_public_key(&string);
|
||||
let res = RsaPublicKey::from_magic_public_key(&string);
|
||||
|
||||
assert!(res.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asonix_key_is_valid() {
|
||||
key_is_valid("RSA.wEvEsHqM3twoC2F3KYMQ9YOialfVQX4StkLvhLUwFv8qpmY7ZSHHl2TWpnzlo5iWS5Pi2vC41HUGYz9XT5G74IUOyuIkjTL1FIcPJDcUFCzQjN3QZcHLPJPJVNOOOEiOk8__paOyrqJTq9ikcJDMJ8KTWQgk1leOxUVEN5uaQ-p9IBFbXC76-RqabfEoqLZagVMDSOfeC2uR9xZ1q5HkFveRTGs84QLR7FJVvx078nszx4UQGnmP0M-0sOeRJGK17IoJmhaok1XBpP6XFQ45vYeIRiaFj0Pc9GNISCW70dVXKMhv-K07orQJm6PwP8USyhq4tLkq6tcPbGRqEk3ZXw==.AQAB");
|
||||
key_is_valid("Rsa.wEvEsHqM3twoC2F3KYMQ9YOialfVQX4StkLvhLUwFv8qpmY7ZSHHl2TWpnzlo5iWS5Pi2vC41HUGYz9XT5G74IUOyuIkjTL1FIcPJDcUFCzQjN3QZcHLPJPJVNOOOEiOk8__paOyrqJTq9ikcJDMJ8KTWQgk1leOxUVEN5uaQ-p9IBFbXC76-RqabfEoqLZagVMDSOfeC2uR9xZ1q5HkFveRTGs84QLR7FJVvx078nszx4UQGnmP0M-0sOeRJGK17IoJmhaok1XBpP6XFQ45vYeIRiaFj0Pc9GNISCW70dVXKMhv-K07orQJm6PwP8USyhq4tLkq6tcPbGRqEk3ZXw==.AQAB");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sir_boops_key_is_valid() {
|
||||
key_is_valid("RSA.vwDujxmxoYHs64MyVB3LG5ZyBxV3ufaMRBFu42bkcTpISq1WwZ-3Zb6CI8zOO-nM-Q2llrVRYjZa4ZFnOLvMTq_Kf-Zf5wy2aCRer88gX-MsJOAtItSi412y0a_rKOuFaDYLOLeTkRvmGLgZWbsrZJOp-YWb3zQ5qsIOInkc5BwI172tMsGeFtsnbNApPV4lrmtTGaJ8RiM8MR7XANBOfOHggSt1-eAIKGIsCmINEMzs1mG9D75xKtC_sM8GfbvBclQcBstGkHAEj1VHPW0ch6Bok5_QQppicyb8UA1PAA9bznSFtKlYE4xCH8rlCDSDTBRtdnBWHKcj619Ujz4Qaw==.AQAB");
|
||||
key_is_valid("Rsa.vwDujxmxoYHs64MyVB3LG5ZyBxV3ufaMRBFu42bkcTpISq1WwZ-3Zb6CI8zOO-nM-Q2llrVRYjZa4ZFnOLvMTq_Kf-Zf5wy2aCRer88gX-MsJOAtItSi412y0a_rKOuFaDYLOLeTkRvmGLgZWbsrZJOp-YWb3zQ5qsIOInkc5BwI172tMsGeFtsnbNApPV4lrmtTGaJ8RiM8MR7XANBOfOHggSt1-eAIKGIsCmINEMzs1mG9D75xKtC_sM8GfbvBclQcBstGkHAEj1VHPW0ch6Bok5_QQppicyb8UA1PAA9bznSFtKlYE4xCH8rlCDSDTBRtdnBWHKcj619Ujz4Qaw==.AQAB");
|
||||
}
|
||||
|
||||
fn key_is_valid(key: &str) {
|
||||
let res = RSAPublicKey::from_magic_public_key(key);
|
||||
let res = RsaPublicKey::from_magic_public_key(key);
|
||||
|
||||
assert!(res.is_ok());
|
||||
|
||||
|
|
Loading…
Reference in a new issue