-
Notifications
You must be signed in to change notification settings - Fork 270
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
Proof of concept/early NodeJS crypto bindings (through N-API) #444
Changes from 43 commits
a34448f
5b13f1b
91563db
94b87e8
d60a226
631f11c
642ee52
4c6b6a9
a69e3c9
b808d09
322b0e7
3592276
c688d55
8ea029e
2b66b76
30049e6
5a957a5
bc4f465
ea411e0
f4f6699
6925661
6e0cd60
e110eb2
c04217f
922df30
812cf07
870bc0f
29146d5
e703046
7e7a05b
8322283
f6c4d6b
c0e9dcb
867baa6
2ca63b4
1a5f5f7
5fd7f9e
7274cb4
e573744
4fa508a
0f75095
8129b71
a3fa012
c7bdfbe
159b6a2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
node_modules | ||
napi-module.js | ||
napi-module.d.ts | ||
yarn-error.log | ||
/sled | ||
/lib |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
[package] | ||
authors = ["Travis Ralston <travis@matrix.org>"] | ||
description = "matrix-sdk-crypto crate for Node JS" | ||
edition = "2021" | ||
homepage = "https://github.com/matrix-org/matrix-rust-sdk" | ||
keywords = ["matrix", "chat", "messaging", "ruma", "nio"] | ||
license = "Apache-2.0" | ||
name = "matrix-sdk-crypto-nodejs" | ||
readme = "README.md" | ||
repository = "https://github.com/matrix-org/matrix-rust-sdk" | ||
rust-version = "1.56" | ||
version = "0.1.0" | ||
|
||
[lib] | ||
crate-type=["cdylib"] | ||
|
||
[dependencies] | ||
napi-derive = { version = "2.0.0", features = ["full"] } | ||
napi = { version = "2.0.0", features = ["full"] } | ||
serde = "1" | ||
serde_derive = "1" | ||
serde_json = "1" | ||
http = "0.2.4" | ||
|
||
# For some reason the futures lib is missing in published builds, so copy the dep from | ||
# the matrix-sdk-crypto crate. | ||
futures = { version = "0.3.15", default-features = false, features = ["executor"] } | ||
|
||
[dependencies.matrix-sdk-crypto] | ||
version = "0.4.1" | ||
default_features = false | ||
features = ["sled_cryptostore", "backups_v1"] | ||
|
||
# use git for release mode | ||
git = "https://github.com/matrix-org/matrix-rust-sdk" | ||
rev = "8129b7190bcf6f6fb568a35870f6f5728f1401c8" | ||
|
||
# use path in local development mode | ||
#path = "../matrix-sdk-crypto" | ||
|
||
[dependencies.matrix-sdk-common] | ||
version = "0.4.1" | ||
|
||
# use git for release mode | ||
git = "https://github.com/matrix-org/matrix-rust-sdk" | ||
rev = "8129b7190bcf6f6fb568a35870f6f5728f1401c8" | ||
|
||
# use path in local development mode | ||
#path = "../matrix-sdk-common" | ||
|
||
[dependencies.ruma] | ||
git = "https://github.com/ruma/ruma/" | ||
rev = "fdbc4d6d1dd273c8a6ac95b329943ed8c68df70d" | ||
features = ["client-api-c"] | ||
|
||
[dependencies.tokio] | ||
version = "1.7.1" | ||
default_features = false | ||
features = ["rt-multi-thread"] | ||
|
||
[build-dependencies] | ||
napi-build = "1.2.1" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# matrix-sdk-crypto-nodejs | ||
|
||
## Building | ||
|
||
1. Install Rust (latest) and NodeJS (latest LTS preferred). | ||
2. `npm install -g yarn@1` to ensure you have Yarn. | ||
3. `yarn install` to configure dependencies. | ||
4. `yarn rust:targets` to configure the targets. | ||
5. `yarn build:release` for a release build. `yarn build:debug` for a debug build. | ||
6. `yarn build:ts` to build the TypeScript part. | ||
|
||
Note that the output will not be capable of a publishable release, but will allow for local | ||
development in the case of a platform-specific binding not being available. Downstream projects | ||
will be affected by this as it might trigger this project's build script during `npm install`. | ||
|
||
## Releasing | ||
|
||
Note that the release process currently only works on Linux. Mac OS might work, but Windows definitely | ||
doesn't. WSL works fine though, just not on the host. | ||
|
||
You will need Docker installed. | ||
|
||
1. Commit *and push* all relevant changes to the repo. The push is important as the build process | ||
relies upon commit hashes. | ||
2. Update the relevant commit hashes in the `Cargo.toml` file, and ensure they are being used. Push | ||
these changes. | ||
3. Update the `package.json` version. `npm version` may be of use. | ||
4. Run `npm publish`. This will build and set up various Docker containers. | ||
* **Do not use yarn to publish, as it might publish the wrong thing.** | ||
|
||
## TODO: Release stuff | ||
|
||
Windows: | ||
`rustup toolchain install stable-gnu` | ||
|
||
use https://www.appveyor.com/ ? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
extern crate napi_build; | ||
|
||
fn main() { | ||
napi_build::setup(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
try { | ||
require('./lib/napi'); | ||
} catch (e) { | ||
if (e.message === 'Failed to load native binding' || e.message.startsWith("Cannot find module")) { | ||
let code; | ||
if (process.env.NODE_ENV === "production") { | ||
code = require('shelljs').exec('yarn build:release').code; | ||
} else { | ||
code = require('shelljs').exec('yarn build:debug').code; | ||
} | ||
if(code !== 0) { | ||
process.exit(code); | ||
} | ||
} else { | ||
throw e; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
{ | ||
"name": "@turt2live/matrix-sdk-crypto-nodejs", | ||
"version": "0.1.0-beta.7", | ||
"description": "matrix-sdk-crypto crate for Node JS", | ||
"main": "./lib/index.js", | ||
"types": "./lib/index.d.ts", | ||
"repository": "https://github.com/matrix-org/matrix-rust-sdk", | ||
"author": "Travis Ralston <travis@matrix.org>", | ||
"license": "Apache-2.0", | ||
"files": [ | ||
"package.json", | ||
"README.md", | ||
"check-exists.js", | ||
"src", | ||
"lib", | ||
"!lib/index.node", | ||
"Cargo.toml", | ||
"Cargo.lock", | ||
"build.rs", | ||
"yarn.lock" | ||
], | ||
"scripts": { | ||
"rust:targets": "rustup target add x86_64-pc-windows-msvc i686-pc-windows-msvc x86_64-unknown-linux-gnu i686-unknown-linux-gnu x86_64-apple-darwin", | ||
"build:win": "yarn build:win:x64 && yarn build:win:ia32", | ||
"build:win:x64": "napi build --js ./lib/napi-module.js --dts napi-module.d.ts --release --platform --target x86_64-pc-windows-msvc ./lib", | ||
"build:win:ia32": "napi build --js ./lib/napi-module.js --dts napi-module.d.ts --release --platform --target i686-pc-windows-msvc ./lib", | ||
"build:linux": "yarn build:linux:x64 && yarn build:linux:ia32", | ||
"build:linux:x64": "napi build --js ./lib/napi-module.js --dts napi-module.d.ts --release --platform --target x86_64-unknown-linux-gnu ./lib", | ||
"build:linux:ia32": "napi build --js ./lib/napi-module.js --dts napi-module.d.ts --release --platform --target i686-unknown-linux-gnu ./lib", | ||
"build:darwin": "yarn build:darwin:x64", | ||
"build:darwin:x64": "napi build --js ./lib/napi-module.js --dts napi-module.d.ts --release --platform --target x86_64-apple-darwin ./lib", | ||
"build:types": "napi build --js ./lib/napi-module.js --dts napi-module.d.ts --platform ./lib", | ||
"build:release": "yarn build:types && napi build --release ./lib", | ||
"build:debug": "yarn build:types && napi build --js ./lib/napi-module.js --dts napi-module.d.ts ./lib", | ||
"build:ts": "tsc", | ||
"build:test": "napi build", | ||
"prepublishOnly": "yarn build:ts && ./prepare-release.sh", | ||
"postinstall": "node check-exists.js" | ||
}, | ||
"dependencies": { | ||
"@napi-rs/cli": "^2.2.0", | ||
"shelljs": "^0.8.4" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^14", | ||
"typescript": "^4.5.4" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#!/bin/bash | ||
|
||
set -ex | ||
|
||
yarn install | ||
|
||
docker build -t rust-sdk-napi -f release/Dockerfile.linux . | ||
docker run --rm -v $(pwd)/../..:/src rust-sdk-napi |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
FROM ubuntu | ||
|
||
ENV DEBIAN_FRONTEND=noninteractive | ||
|
||
# The install commands are broken out because apt gets confused about | ||
# how to install all the packages concurrently. | ||
RUN apt-get update | ||
RUN apt-get install -y build-essential | ||
RUN apt-get install -y gcc-i686-linux-gnu | ||
RUN apt-get install -y g++-i686-linux-gnu | ||
RUN apt-get install -y gcc-multilib | ||
RUN apt-get install -y g++-multilib | ||
RUN apt-get install -y cmake | ||
|
||
# Install misc dependencies last to cache the expensive gcc layers | ||
RUN apt-get install -y curl | ||
|
||
# Headless install of NodeJS | ||
RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - | ||
RUN apt-get install -y nodejs | ||
RUN npm install -g yarn@1 | ||
|
||
# Headless install of Rust/Cargo | ||
RUN curl https://sh.rustup.rs -sSf | sh -s -- -y | ||
ENV PATH="/root/.cargo/bin:${PATH}" | ||
|
||
COPY ./package.json /src2/package.json | ||
WORKDIR /src2 | ||
RUN yarn rust:targets | ||
|
||
VOLUME ["/src"] | ||
WORKDIR /src/crates/matrix-sdk-crypto-nodejs | ||
|
||
CMD yarn install && yarn build:linux |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// Copyright 2021 The Matrix.org Foundation C.I.C. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
use std::io::{Cursor, Read}; | ||
|
||
use matrix_sdk_crypto::{AttachmentDecryptor, AttachmentEncryptor, MediaEncryptionInfo}; | ||
use napi_derive::napi; | ||
use serde::{Deserialize, Serialize}; | ||
use serde_json::json; | ||
|
||
#[derive(Serialize, Deserialize)] | ||
pub struct EncryptedMedia { | ||
pub info: MediaEncryptionInfo, | ||
pub data: Vec<u8>, | ||
} | ||
|
||
#[napi] | ||
pub fn encrypt_file(data: String) -> String { | ||
let data: Vec<u8> = serde_json::from_str(data.as_str()).expect("Failed to decode input array"); | ||
let mut cursor = Cursor::new(data); | ||
let mut encryptor = AttachmentEncryptor::new(&mut cursor); | ||
let mut encrypted = Vec::<u8>::new(); | ||
encryptor.read_to_end(&mut encrypted).unwrap(); | ||
let info = encryptor.finish(); | ||
serde_json::to_string(&json!({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Didn't you mean to use serde_json::to_string(&EncryptedMedia { data: encrypted, info })
.expect("Failed to serialize json") |
||
"data": encrypted, | ||
"info": info, | ||
})) | ||
.expect("Failed to serialize json") | ||
} | ||
|
||
#[napi] | ||
pub fn decrypt_file(payload: String) -> String { | ||
let payload: (Vec<u8>, MediaEncryptionInfo) = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The payload is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This and the noted usage might have been some weird artifact of my development process - will take a look and fix it. |
||
serde_json::from_str(payload.as_str()).expect("Failed to decode inputs"); | ||
let data = payload.0; | ||
let info = payload.1; | ||
let mut cursor = Cursor::new(data); | ||
let mut decryptor = AttachmentDecryptor::new(&mut cursor, info).unwrap(); | ||
let mut decrypted = Vec::<u8>::new(); | ||
decryptor.read_to_end(&mut decrypted).unwrap(); | ||
serde_json::to_string(&json!({ | ||
"data": decrypted, | ||
})) | ||
.expect("Failed to serialize json") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// Copyright 2021 The Matrix.org Foundation C.I.C. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
use matrix_sdk_crypto::Device as RSDevice; | ||
use napi_derive::napi; | ||
use serde::{Deserialize, Serialize}; | ||
use serde_json::{Map, Value}; | ||
|
||
#[napi(object)] | ||
#[derive(Serialize, Deserialize)] | ||
pub struct Device { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this has been copied from the You can just wrap #[napi]
pub struct Device {
inner: RSDevice,
}
#[napi]
impl Device {
#[napi(getter)]
pub fn user_id(&self) -> String {
self.inner.user_id().to_string()
}
} |
||
pub user_id: String, | ||
pub device_id: String, | ||
pub keys: Map<String, Value>, | ||
pub algorithms: Vec<String>, | ||
pub display_name: Option<String>, | ||
pub is_blocked: bool, | ||
pub locally_trusted: bool, | ||
pub cross_signing_trusted: bool, | ||
} | ||
|
||
impl From<RSDevice> for Device { | ||
fn from(d: RSDevice) -> Self { | ||
Device { | ||
user_id: d.user_id().to_string(), | ||
device_id: d.device_id().to_string(), | ||
keys: d | ||
.keys() | ||
.iter() | ||
.map(|(k, v)| (k.to_string(), Value::String(v.to_string()))) | ||
.collect::<Map<String, Value>>(), | ||
algorithms: d.algorithms().iter().map(|a| a.to_string()).collect(), | ||
display_name: d.display_name().map(|d| d.to_owned()), | ||
is_blocked: d.is_blacklisted(), | ||
locally_trusted: d.is_locally_trusted(), | ||
cross_signing_trusted: d.is_cross_signing_trusted(), | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Copyright 2021 The Matrix.org Foundation C.I.C. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
mod attachments; | ||
mod device; | ||
mod machine; | ||
mod models; | ||
mod request; | ||
mod responses; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The whole crate complains about unused types because nothing is here publicly exported. All your types are private types. Not sure why napi-rs doesn't complain about this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is because my IDE was complaining that it couldn't determine types of "unused" modules. I think napi-rs also requires the things to be referenced like this, so might be why it's not complaining? Is the solution to just stick There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could do that or you could add exports for the public types, for example: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't panic since we're reading from a
Cursor
but let's useexpect()
for such cases.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is copy/paste from the docs for AttachmentEncryptor - should those be updated too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those docs were written at a time when i believed that adding error handling to examples isn't necessary, nowadays I use
anyhow
to return any error from the example and replace theunwrap
calls with?
.Sure, I'll update the example.