mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-14 02:55:47 -04:00
feat(crypto-js): Implement OlmMachine.sign.
This commit is contained in:
@@ -26,6 +26,7 @@ pub mod olm;
|
||||
pub mod requests;
|
||||
pub mod responses;
|
||||
pub mod sync_events;
|
||||
pub mod types;
|
||||
pub mod vodozemac;
|
||||
mod tracing;
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ use crate::{
|
||||
vodozemac,
|
||||
responses::{self, response_from_string},
|
||||
sync_events,
|
||||
types,
|
||||
};
|
||||
|
||||
/// State machine implementation of the Olm/Megolm encryption protocol
|
||||
@@ -297,6 +298,16 @@ impl OlmMachine {
|
||||
})
|
||||
}
|
||||
|
||||
/// Sign the given message using our device key and if available
|
||||
/// cross-signing master key.
|
||||
pub fn sign(&self, message: String) -> Promise {
|
||||
let me = self.inner.clone();
|
||||
|
||||
future_to_promise::<_, types::Signatures>(async move {
|
||||
Ok(me.sign(&message).await.into())
|
||||
})
|
||||
}
|
||||
|
||||
/// Invalidate the currently active outbound group session for the
|
||||
/// given room.
|
||||
///
|
||||
|
||||
165
bindings/matrix-sdk-crypto-js/src/types.rs
Normal file
165
bindings/matrix-sdk-crypto-js/src/types.rs
Normal file
@@ -0,0 +1,165 @@
|
||||
use js_sys::Map;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::{
|
||||
identifiers::{DeviceKeyId, UserId},
|
||||
vodozemac::Ed25519Signature,
|
||||
};
|
||||
|
||||
/// A collection of `Signature`.
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Signatures {
|
||||
inner: matrix_sdk_crypto::types::Signatures,
|
||||
}
|
||||
|
||||
impl From<matrix_sdk_crypto::types::Signatures> for Signatures {
|
||||
fn from(inner: matrix_sdk_crypto::types::Signatures) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl Signatures {
|
||||
/// Creates a new, empty, signatures collection.
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> Self {
|
||||
matrix_sdk_crypto::types::Signatures::new().into()
|
||||
}
|
||||
|
||||
/// Add the given signature from the given signer and the given key ID to
|
||||
/// the collection.
|
||||
#[wasm_bindgen(js_name = "addSignature")]
|
||||
pub fn add_signature(
|
||||
&mut self,
|
||||
signer: &UserId,
|
||||
key_id: &DeviceKeyId,
|
||||
signature: &Ed25519Signature,
|
||||
) -> Option<MaybeSignature> {
|
||||
self.inner
|
||||
.add_signature(signer.inner.clone(), key_id.inner.clone(), signature.inner)
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
/// Try to find an Ed25519 signature from the given signer with
|
||||
/// the given key ID.
|
||||
#[wasm_bindgen(js_name = "getSignature")]
|
||||
pub fn get_signature(&self, signer: &UserId, key_id: &DeviceKeyId) -> Option<Ed25519Signature> {
|
||||
self.inner.get_signature(signer.inner.as_ref(), key_id.inner.as_ref()).map(Into::into)
|
||||
}
|
||||
|
||||
/// Get the map of signatures that belong to the given user.
|
||||
pub fn get(&self, signer: &UserId) -> Option<Map> {
|
||||
let map = Map::new();
|
||||
|
||||
for (device_key_id, maybe_signature) in
|
||||
self.inner.get(signer.inner.as_ref()).map(|map| {
|
||||
map.iter().map(|(device_key_id, maybe_signature)| {
|
||||
(
|
||||
device_key_id.as_str().to_owned(),
|
||||
MaybeSignature::from(maybe_signature.clone()),
|
||||
)
|
||||
})
|
||||
})?
|
||||
{
|
||||
map.set(&device_key_id.into(), &maybe_signature.into());
|
||||
}
|
||||
|
||||
Some(map)
|
||||
}
|
||||
|
||||
/// Remove all the signatures we currently hold.
|
||||
pub fn clear(&mut self) {
|
||||
self.inner.clear();
|
||||
}
|
||||
|
||||
/// Do we hold any signatures or is our collection completely
|
||||
/// empty.
|
||||
#[wasm_bindgen(getter, js_name = "isEmpty")]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.inner.is_empty()
|
||||
}
|
||||
|
||||
/// How many signatures do we currently hold.
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn count(&self) -> usize {
|
||||
self.inner.signature_count()
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a potentially decoded signature (but not a validated
|
||||
/// one).
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug)]
|
||||
pub struct Signature {
|
||||
inner: matrix_sdk_crypto::types::Signature,
|
||||
}
|
||||
|
||||
impl From<matrix_sdk_crypto::types::Signature> for Signature {
|
||||
fn from(inner: matrix_sdk_crypto::types::Signature) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl Signature {
|
||||
/// Get the Ed25519 signature, if this is one.
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn ed25519(&self) -> Option<Ed25519Signature> {
|
||||
self.inner.ed25519().map(Into::into)
|
||||
}
|
||||
|
||||
/// Convert the signature to a base64 encoded string.
|
||||
#[wasm_bindgen(js_name = "toBase64")]
|
||||
pub fn to_base64(&self) -> String {
|
||||
self.inner.to_base64()
|
||||
}
|
||||
}
|
||||
|
||||
type MaybeSignatureInner =
|
||||
Result<matrix_sdk_crypto::types::Signature, matrix_sdk_crypto::types::InvalidSignature>;
|
||||
|
||||
/// Represents a signature that is either valid _or_ that could not be
|
||||
/// decoded.
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug)]
|
||||
pub struct MaybeSignature {
|
||||
inner: MaybeSignatureInner,
|
||||
}
|
||||
|
||||
impl From<MaybeSignatureInner> for MaybeSignature {
|
||||
fn from(inner: MaybeSignatureInner) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl MaybeSignature {
|
||||
/// Check whether the signature has been successfully decoded.
|
||||
#[wasm_bindgen(getter, js_name = "isValid")]
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.inner.is_ok()
|
||||
}
|
||||
|
||||
/// Check whether the signature could not be successfully decoded.
|
||||
#[wasm_bindgen(getter, js_name = "isInvalid")]
|
||||
pub fn is_invalid(&self) -> bool {
|
||||
self.inner.is_err()
|
||||
}
|
||||
|
||||
/// The signature, if successfully decoded.
|
||||
#[wasm_bindgen(getter)]
|
||||
pub fn signature(&self) -> Option<Signature> {
|
||||
self.inner.as_ref().cloned().map(Into::into).ok()
|
||||
}
|
||||
|
||||
/// The base64 encoded string that is claimed to contain a
|
||||
/// signature but could not be decoded, if any.
|
||||
#[wasm_bindgen(getter, js_name = "invalidSignatureSource")]
|
||||
pub fn invalid_signature_source(&self) -> Option<String> {
|
||||
match &self.inner {
|
||||
Ok(_) => None,
|
||||
Err(signature) => Some(signature.source.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,37 @@ impl Ed25519PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
/// An Ed25519 digital signature, can be used to verify the
|
||||
/// authenticity of a message.
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug)]
|
||||
pub struct Ed25519Signature {
|
||||
pub(crate) inner: vodozemac::Ed25519Signature,
|
||||
}
|
||||
|
||||
impl From<vodozemac::Ed25519Signature> for Ed25519Signature {
|
||||
fn from(inner: vodozemac::Ed25519Signature) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl Ed25519Signature {
|
||||
/// Try to create an Ed25519 signature from an unpadded base64
|
||||
/// representation.
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(signature: String) -> Result<Ed25519Signature, JsError> {
|
||||
Ok(Self { inner: vodozemac::Ed25519Signature::from_base64(signature.as_str())? })
|
||||
}
|
||||
|
||||
/// Serialize a Ed25519 signature to an unpadded base64
|
||||
/// representation.
|
||||
#[wasm_bindgen(js_name = "toBase64")]
|
||||
pub fn to_base64(&self) -> String {
|
||||
self.inner.to_base64()
|
||||
}
|
||||
}
|
||||
|
||||
/// A Curve25519 public key.
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { OlmMachine, UserId, DeviceId, RoomId, DeviceLists, RequestType, KeysUploadRequest, KeysQueryRequest, KeysClaimRequest, EncryptionSettings, DecryptedRoomEvent, VerificationState, CrossSigningStatus } = require('../pkg/matrix_sdk_crypto_js');
|
||||
const { OlmMachine, UserId, DeviceId, DeviceKeyId, RoomId, DeviceLists, RequestType, KeysUploadRequest, KeysQueryRequest, KeysClaimRequest, EncryptionSettings, DecryptedRoomEvent, VerificationState, CrossSigningStatus, MaybeSignature } = require('../pkg/matrix_sdk_crypto_js');
|
||||
|
||||
describe(OlmMachine.name, () => {
|
||||
test('can be instantiated with the async initializer', async () => {
|
||||
@@ -350,4 +350,46 @@ describe(OlmMachine.name, () => {
|
||||
expect(crossSigningStatus.hasSelfSigning).toStrictEqual(false);
|
||||
expect(crossSigningStatus.hasUserSigning).toStrictEqual(false);
|
||||
});
|
||||
|
||||
test('can sign a message', async () => {
|
||||
const m = await machine();
|
||||
const signatures = await m.sign('foo');
|
||||
|
||||
expect(signatures.isEmpty).toStrictEqual(false);
|
||||
expect(signatures.count).toStrictEqual(1);
|
||||
|
||||
let base64;
|
||||
|
||||
// `get`
|
||||
{
|
||||
const signature = signatures.get(user);
|
||||
|
||||
expect(signature.has('ed25519:foobar')).toStrictEqual(true);
|
||||
|
||||
const s = signature.get('ed25519:foobar');
|
||||
|
||||
expect(s).toBeInstanceOf(MaybeSignature);
|
||||
|
||||
expect(s.isValid).toStrictEqual(true);
|
||||
expect(s.isInvalid).toStrictEqual(false);
|
||||
expect(s.invalidSignatureSource).toBeUndefined();
|
||||
|
||||
base64 = s.signature.toBase64();
|
||||
|
||||
expect(base64).toMatch(/^[A-Za-z0-9\+/]+$/);
|
||||
expect(s.signature.ed25519.toBase64()).toStrictEqual(base64);
|
||||
}
|
||||
|
||||
// `getSignature`
|
||||
{
|
||||
const signature = signatures.getSignature(user, new DeviceKeyId('ed25519:foobar'));
|
||||
expect(signature.toBase64()).toStrictEqual(base64);
|
||||
}
|
||||
|
||||
// Unknown signatures.
|
||||
{
|
||||
expect(signatures.get(new UserId('@hello:example.org'))).toBeUndefined();
|
||||
expect(signatures.getSignature(user, new DeviceKeyId('world:foobar'))).toBeUndefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user