Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UCAN JWT canonicalization #28

Merged
merged 5 commits into from
Oct 4, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
48 changes: 28 additions & 20 deletions .github/workflows/run_test_suite.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,39 @@ jobs:
~/.cargo
./target
key: ${{ runner.os }}-cargo-artifacts-${{ hashFiles('**/Cargo.toml') }}
- name: 'Install environment packages'
run: |
sudo apt-get update -qqy
sudo apt-get install jq
- name: 'Run tests'
run: cargo test
shell: bash
- name: 'Setup Browserstack environment'
uses: 'browserstack/github-actions/setup-env@master'
with:
username: ${{ secrets.BROWSERSTACK_USERNAME }}
access-key: ${{ secrets.BROWSERSTACK_TOKEN }}
build-name: BUILD_INFO
project-name: REPO_NAME
- name: 'Open connection to Browserstack'
uses: 'browserstack/github-actions/setup-local@master'
with:
local-testing: start
local-identifier: random
- name: 'Run browser tests for ucan-key-support crate'
- name: 'Install Rust/WASM test dependencies'
run: |
cd ./ucan-key-support
BROWSERSTACK=1 ./scripts/run_browser_tests.sh
rustup target install wasm32-unknown-unknown
cargo install toml-cli
WASM_BINDGEN_VERSION=`toml get ./Cargo.lock . | jq '.package | map(select(.name == "wasm-bindgen"))[0].version' | xargs echo`
cargo install wasm-bindgen-cli --vers "$WASM_BINDGEN_VERSION"
shell: bash
# See: https://github.com/SeleniumHQ/selenium/blob/5d108f9a679634af0bbc387e7e3811bc1565912b/.github/actions/setup-chrome/action.yml
- name: 'Setup Chrome and chromedriver'
run: |
wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
echo "deb http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee -a /etc/apt/sources.list.d/google-chrome.list
sudo apt-get update -qqy
sudo apt-get -qqy install google-chrome-stable
CHROME_VERSION=$(google-chrome-stable --version)
CHROME_FULL_VERSION=${CHROME_VERSION%%.*}
CHROME_MAJOR_VERSION=${CHROME_FULL_VERSION//[!0-9]}
sudo rm /etc/apt/sources.list.d/google-chrome.list
export CHROMEDRIVER_VERSION=`curl -s https://chromedriver.storage.googleapis.com/LATEST_RELEASE_${CHROME_MAJOR_VERSION%%.*}`
curl -L -O "https://chromedriver.storage.googleapis.com/${CHROMEDRIVER_VERSION}/chromedriver_linux64.zip"
justindotpub marked this conversation as resolved.
Show resolved Hide resolved
unzip chromedriver_linux64.zip && chmod +x chromedriver && sudo mv chromedriver /usr/local/bin
chromedriver -version
shell: bash
- name: 'Run Rust headless browser tests'
run: CHROMEDRIVER=/usr/local/bin/chromedriver cargo test --target wasm32-unknown-unknown
shell: bash
- name: 'Close connection to Browserstack'
uses: browserstack/github-actions/setup-local@master
if: always()
with:
local-testing: stop
publish-release:
runs-on: ubuntu-latest
needs: [run-test-suite]
Expand Down
20 changes: 10 additions & 10 deletions ucan-key-support/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ version = "0.4.0-alpha.1"

[features]
default = []
web = ["wasm-bindgen", "wasm-bindgen-futures", "js-sys", "web-sys", "ucan/web", "getrandom/js"]

[dependencies]
ucan = {path = "../ucan", version = "0.6.0-alpha.1" }
Expand All @@ -34,20 +33,21 @@ log = "0.4"
npm_rs = "0.2.1"

[dev-dependencies]
rand = "0.8"
rand = "~0.8"
# NOTE: This is needed so that rand can be included in WASM builds
getrandom = { version = "0.2.5", features = ["js"] }
wasm-bindgen-test = "0.3"
getrandom = { version = "~0.2", features = ["js"] }
wasm-bindgen-test = "~0.3"

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
tokio = { version = "^1", features = ["macros", "rt"] }

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = { version = "0.2", optional = true }
wasm-bindgen-futures = { version = "0.4", optional = true }
js-sys = { version = "0.3", optional = true }
wasm-bindgen = { version = "0.2" }
wasm-bindgen-futures = { version = "0.4" }
js-sys = { version = "0.3" }

[target.'cfg(target_arch="wasm32")'.dependencies.web-sys]
[target.'cfg(target_arch = "wasm32")'.dependencies.web-sys]
version = "0.3"
optional = true
features = [
'Window',
'SubtleCrypto',
Expand All @@ -57,5 +57,5 @@ features = [
'DedicatedWorkerGlobalScope'
]

[target.'cfg(target_arch="wasm32")'.dev-dependencies]
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
pollster = "0.2.5"
52 changes: 0 additions & 52 deletions ucan-key-support/scripts/run_browser_tests.sh

This file was deleted.

20 changes: 14 additions & 6 deletions ucan-key-support/src/ed25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use ed25519_zebra::{

use ucan::crypto::KeyMaterial;

pub const ED25519_MAGIC_BYTES: [u8; 2] = [0xed, 0x01];
pub use ucan::crypto::did::ED25519_MAGIC_BYTES;
pub use ucan::crypto::JwtSignatureAlgorithm;

pub fn bytes_to_ed25519_key(bytes: Vec<u8>) -> Result<Box<dyn KeyMaterial>> {
let public_key = Ed25519PublicKey::try_from(bytes.as_slice())?;
Expand All @@ -17,15 +18,15 @@ pub fn bytes_to_ed25519_key(bytes: Vec<u8>) -> Result<Box<dyn KeyMaterial>> {
#[derive(Clone)]
pub struct Ed25519KeyMaterial(pub Ed25519PublicKey, pub Option<Ed25519PrivateKey>);

#[cfg_attr(all(target_arch="wasm32", feature = "web"), async_trait(?Send))]
#[cfg_attr(any(not(target_arch = "wasm32"), not(feature = "web")), async_trait)]
#[cfg_attr(target_arch="wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl KeyMaterial for Ed25519KeyMaterial {
fn get_jwt_algorithm_name(&self) -> String {
"EdDSA".into()
JwtSignatureAlgorithm::EdDSA.to_string()
}

async fn get_did(&self) -> Result<String> {
let bytes = [ED25519_MAGIC_BYTES.as_slice(), self.0.as_ref()].concat();
let bytes = [ED25519_MAGIC_BYTES, self.0.as_ref()].concat();
Ok(format!("did:key:z{}", bs58::encode(bytes).into_string()))
}

Expand Down Expand Up @@ -58,7 +59,14 @@ mod tests {
ucan::Ucan,
};

#[tokio::test]
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};

#[cfg(target_arch = "wasm32")]
wasm_bindgen_test_configure!(run_in_browser);

#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
async fn it_can_sign_and_verify_a_ucan() {
let rng = rand::thread_rng();
let private_key = Ed25519PrivateKey::new(rng);
Expand Down
2 changes: 1 addition & 1 deletion ucan-key-support/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#[macro_use]
extern crate log;

#[cfg(all(target_arch = "wasm32", feature = "web"))]
#[cfg(target_arch = "wasm32")]
pub mod web_crypto;

pub mod ed25519;
Expand Down
28 changes: 16 additions & 12 deletions ucan-key-support/src/rsa.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
use anyhow::{anyhow, Result};
use async_trait::async_trait;

use rsa::{
Hash, PaddingScheme, PublicKey, RsaPrivateKey, RsaPublicKey,
};
use rsa::pkcs1::{DecodeRsaPublicKey, EncodeRsaPublicKey};
use rsa::pkcs1::der::{Document, Encodable};
use rsa::pkcs1::{DecodeRsaPublicKey, EncodeRsaPublicKey};
use rsa::{Hash, PaddingScheme, PublicKey, RsaPrivateKey, RsaPublicKey};

use sha2::{Digest, Sha256};
use ucan::crypto::KeyMaterial;
use ucan::crypto::{JwtSignatureAlgorithm, KeyMaterial};

pub const RSA_MAGIC_BYTES: [u8; 2] = [0x85, 0x24];
pub const RSA_ALGORITHM: &str = "RSASSA-PKCS1-v1_5";
pub use ucan::crypto::did::RSA_MAGIC_BYTES;

pub fn bytes_to_rsa_key(bytes: Vec<u8>) -> Result<Box<dyn KeyMaterial>> {
// NOTE: DID bytes are PKCS1, but we are using PKCS8, so do the conversion here..
Expand All @@ -25,16 +22,16 @@ pub fn bytes_to_rsa_key(bytes: Vec<u8>) -> Result<Box<dyn KeyMaterial>> {
#[derive(Clone)]
pub struct RsaKeyMaterial(pub RsaPublicKey, pub Option<RsaPrivateKey>);

#[cfg_attr(all(target_arch="wasm32", feature = "web"), async_trait(?Send))]
#[cfg_attr(any(not(target_arch = "wasm32"), not(feature = "web")), async_trait)]
#[cfg_attr(target_arch="wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl KeyMaterial for RsaKeyMaterial {
fn get_jwt_algorithm_name(&self) -> String {
RSA_ALGORITHM.into()
JwtSignatureAlgorithm::RS256.to_string()
}

async fn get_did(&self) -> Result<String> {
let bytes = match self.0.to_pkcs1_der() {
Ok(document) => [RSA_MAGIC_BYTES.as_slice(), document.as_der()].concat(),
Ok(document) => [RSA_MAGIC_BYTES, document.as_der()].concat(),
Err(error) => {
// TODO: Probably shouldn't swallow this error...
warn!("Could not get RSA public key bytes for DID: {:?}", error);
Expand Down Expand Up @@ -95,7 +92,14 @@ mod tests {
use ucan::crypto::KeyMaterial;
use ucan::ucan::Ucan;

#[tokio::test]
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure};

#[cfg(target_arch = "wasm32")]
wasm_bindgen_test_configure!(run_in_browser);

#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
async fn it_can_sign_and_verify_a_ucan() {
let private_key =
RsaPrivateKey::from_pkcs8_der(include_bytes!("./fixtures/rsa_key.pk8")).unwrap();
Expand Down
13 changes: 8 additions & 5 deletions ucan-key-support/src/web_crypto.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use crate::rsa::{RsaKeyMaterial, RSA_ALGORITHM};
use crate::rsa::{RsaKeyMaterial};
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use js_sys::{Array, ArrayBuffer, Boolean, Object, Reflect, Uint8Array};
use rsa::RsaPublicKey;
use rsa::pkcs1::DecodeRsaPublicKey;
use rsa::pkcs1::der::Encodable;
use ucan::crypto::KeyMaterial;
use ucan::crypto::JwtSignatureAlgorithm;
use wasm_bindgen::{JsCast, JsValue};
use wasm_bindgen_futures::JsFuture;
use web_sys::{Crypto, CryptoKey, CryptoKeyPair, SubtleCrypto};
Expand All @@ -17,6 +18,8 @@ pub fn convert_spki_to_rsa_public_key(spki_bytes: &[u8]) -> Result<Vec<u8>> {
Ok(Vec::from(&spki_bytes[24..]))
}

pub const WEB_CRYPTO_RSA_ALGORITHM: &str = "RSASSA-PKCS1-v1_5";

#[derive(Debug)]
pub struct WebCryptoRsaKeyMaterial(pub CryptoKey, pub Option<CryptoKey>);

Expand Down Expand Up @@ -46,7 +49,7 @@ impl WebCryptoRsaKeyMaterial {
Reflect::set(
&algorithm,
&JsValue::from("name"),
&JsValue::from(RSA_ALGORITHM),
&JsValue::from(WEB_CRYPTO_RSA_ALGORITHM),
)
.map_err(|error| anyhow!("{:?}", error))?;

Expand Down Expand Up @@ -105,7 +108,7 @@ impl WebCryptoRsaKeyMaterial {
#[async_trait(?Send)]
impl KeyMaterial for WebCryptoRsaKeyMaterial {
fn get_jwt_algorithm_name(&self) -> String {
RSA_ALGORITHM.into()
JwtSignatureAlgorithm::RS256.to_string()
}

async fn get_did(&self) -> Result<String> {
Expand Down Expand Up @@ -141,7 +144,7 @@ impl KeyMaterial for WebCryptoRsaKeyMaterial {
Reflect::set(
&algorithm,
&JsValue::from("name"),
&JsValue::from(RSA_ALGORITHM),
&JsValue::from(WEB_CRYPTO_RSA_ALGORITHM),
)
.map_err(|error| anyhow!("{:?}", error))?;

Expand Down Expand Up @@ -175,7 +178,7 @@ impl KeyMaterial for WebCryptoRsaKeyMaterial {
Reflect::set(
&algorithm,
&JsValue::from("name"),
&JsValue::from(RSA_ALGORITHM),
&JsValue::from(WEB_CRYPTO_RSA_ALGORITHM),
)
.map_err(|error| anyhow!("{:?}", error))?;
Reflect::set(
Expand Down
27 changes: 19 additions & 8 deletions ucan/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,35 @@ edition = "2021"

[features]
default = []
web = ["instant/wasm-bindgen"]

[dependencies]
cid = "~0.8"
anyhow = "^1"
async-trait = "0.1"
async-trait = "~0.1"
async-recursion = "^1"
async-std = "^1"
serde_json = "^1"
serde = { version = "^1", features = ["derive"] }
base64 = "0.13"
textnonce = "^1"
log = "0.4"
base64 = "~0.13"
log = "~0.4"
url = "^2"
bs58 = "0.4"
bs58 = "~0.4"
unsigned-varint = "~0.7"
libipld-core = { version = "~0.14", features = ["serde-codec", "serde"] }
libipld-json = "~0.14"
strum = "~0.24"
strum_macros = "~0.24"
instant = { version = "0.1", features = ["wasm-bindgen", "stdweb"] }
rand = "~0.8"

[target.'cfg(target_arch = "wasm32")'.dependencies]
instant = { version = "0.1" }
# NOTE: This is needed so that rand can be included in WASM builds
getrandom = { version = "~0.2", features = ["js"] }

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
tokio = { version = "^1", features = ["macros", "test-util"] }

[dev-dependencies]
did-key = "0.1"
tokio = { version = "^1", features = ["macros", "rt"] }
serde_ipld_dagcbor = "~0.2"
wasm-bindgen-test = "~0.3"
Empty file added ucan/src/block.rs
Empty file.
Loading