-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
303 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
use pyo3::{ | ||
pyclass, pymethods, | ||
types::{PyBytes, PyType}, | ||
Bound, Py, Python, | ||
}; | ||
|
||
use crate::{ | ||
types::{Curve25519PublicKey, Curve25519SecretKey}, | ||
PkEncryptionError, | ||
}; | ||
|
||
/// A message that was encrypted using a PkEncryption object. | ||
#[pyclass] | ||
pub struct Message { | ||
/// The ciphertext of the message. | ||
ciphertext: Vec<u8>, | ||
/// The message authentication code of the message. | ||
/// | ||
/// *Warning*: As stated in the module description, this does not | ||
/// authenticate the message. | ||
mac: Vec<u8>, | ||
/// The ephemeral Curve25519PublicKey of the message which was used to | ||
/// derive the individual message key. | ||
ephemeral_key: Vec<u8>, | ||
} | ||
|
||
/// ☣️ Compat support for libolm's PkDecryption. | ||
/// | ||
/// This implements the `m.megolm_backup.v1.curve25519-aes-sha2` described in | ||
/// the Matrix [spec]. This is a asymmetric encryption scheme based on | ||
/// Curve25519. | ||
/// | ||
/// **Warning**: Please note the algorithm contains a critical flaw and does not | ||
/// provide authentication of the ciphertext. | ||
/// | ||
/// [spec]: https://spec.matrix.org/v1.11/client-server-api/#backup-algorithm-mmegolm_backupv1curve25519-aes-sha2 | ||
#[pyclass] | ||
pub struct PkDecryption { | ||
inner: vodozemac::pk_encryption::PkDecryption, | ||
} | ||
|
||
#[pymethods] | ||
impl PkDecryption { | ||
/// Create a new random PkDecryption object. | ||
#[new] | ||
fn new() -> Self { | ||
Self { | ||
inner: vodozemac::pk_encryption::PkDecryption::new(), | ||
} | ||
} | ||
|
||
/// Create a PkDecryption object from the secret key bytes. | ||
#[classmethod] | ||
fn from_key( | ||
_cls: &Bound<'_, PyType>, | ||
key: Curve25519SecretKey, | ||
) -> Result<Self, PkEncryptionError> { | ||
Ok(Self { | ||
inner: vodozemac::pk_encryption::PkDecryption::from_key(key.inner), | ||
}) | ||
} | ||
|
||
/// The secret key used to decrypt messages. | ||
#[getter] | ||
pub fn key(&self) -> Curve25519SecretKey { | ||
Curve25519SecretKey::from(self.inner.secret_key().clone()) | ||
} | ||
|
||
/// The public key used to encrypt messages for this decryption object. | ||
#[getter] | ||
pub fn public_key(&self) -> Curve25519PublicKey { | ||
Curve25519PublicKey::from(self.inner.public_key()) | ||
} | ||
|
||
/// Decrypt a ciphertext. See the PkEncryption::encrypt function | ||
/// for descriptions of the ephemeral_key and mac arguments. | ||
pub fn decrypt(&self, message: &Message) -> Result<Py<PyBytes>, PkEncryptionError> { | ||
let ephemeral_key_bytes: [u8; 32] = message | ||
.ephemeral_key | ||
.as_slice() | ||
.try_into() | ||
.map_err(|_| PkEncryptionError::InvalidKeySize(message.ephemeral_key.len()))?; | ||
|
||
let message = vodozemac::pk_encryption::Message { | ||
ciphertext: message.ciphertext.clone(), | ||
mac: message.mac.clone(), | ||
ephemeral_key: vodozemac::Curve25519PublicKey::from_bytes(ephemeral_key_bytes), | ||
}; | ||
|
||
self.inner | ||
.decrypt(&message) | ||
.map(|vec| Python::with_gil(|py| PyBytes::new_bound(py, vec.as_slice()).into())) | ||
.map_err(|e| PkEncryptionError::Decode(e)) | ||
} | ||
} | ||
|
||
/// ☣️ Compat support for libolm's PkEncryption. | ||
/// | ||
/// This implements the `m.megolm_backup.v1.curve25519-aes-sha2` described in | ||
/// the Matrix [spec]. This is a asymmetric encryption scheme based on | ||
/// Curve25519. | ||
/// | ||
/// **Warning**: Please note the algorithm contains a critical flaw and does not | ||
/// provide authentication of the ciphertext. | ||
/// | ||
/// [spec]: https://spec.matrix.org/v1.11/client-server-api/#backup-algorithm-mmegolm_backupv1curve25519-aes-sha2 | ||
#[pyclass] | ||
pub struct PkEncryption { | ||
inner: vodozemac::pk_encryption::PkEncryption, | ||
} | ||
|
||
#[pymethods] | ||
impl PkEncryption { | ||
/// Create a new PkEncryption object from public key. | ||
#[classmethod] | ||
fn from_key( | ||
_cls: &Bound<'_, PyType>, | ||
key: Curve25519PublicKey, | ||
) -> Result<Self, PkEncryptionError> { | ||
Ok(Self { | ||
inner: vodozemac::pk_encryption::PkEncryption::from_key(key.inner), | ||
}) | ||
} | ||
|
||
/// Encrypt a plaintext for the recipient. Writes to the ciphertext, mac, and | ||
/// ephemeral_key buffers, whose values should be sent to the recipient. mac is | ||
/// a Message Authentication Code to ensure that the data is received and | ||
/// decrypted properly. ephemeral_key is the public part of the ephemeral key | ||
/// used (together with the recipient's key) to generate a symmetric encryption | ||
/// key. | ||
pub fn encrypt(&self, message: &[u8]) -> Message { | ||
let msg = self.inner.encrypt(message); | ||
Message { | ||
ciphertext: msg.ciphertext.to_vec(), | ||
mac: msg.mac.to_vec(), | ||
ephemeral_key: msg.ephemeral_key.to_vec(), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import importlib | ||
import pytest | ||
|
||
from vodozemac import Curve25519SecretKey, Curve25519PublicKey, PkEncryption, PkDecryption, PkDecodeException | ||
|
||
CLEARTEXT = b"test" | ||
|
||
class TestClass(object): | ||
def test_encrypt_decrypt(self): | ||
d = PkDecryption() | ||
e = PkEncryption.from_key(d.public_key) | ||
|
||
decoded = d.decrypt(e.encrypt(CLEARTEXT)) | ||
assert decoded == CLEARTEXT | ||
|
||
def test_encrypt_decrypt_with_wrong_key(self): | ||
wrong_e = PkEncryption.from_key(PkDecryption().public_key) | ||
with pytest.raises(PkDecodeException, match="MAC tag mismatch"): | ||
PkDecryption().decrypt(wrong_e.encrypt(CLEARTEXT)) | ||
|
||
def test_encrypt_decrypt_with_serialized_keys(self): | ||
secret_key = Curve25519SecretKey() | ||
secret_key_bytes = secret_key.to_bytes() | ||
public_key_bytes = secret_key.public_key().to_bytes() | ||
|
||
d = PkDecryption.from_key(Curve25519SecretKey.from_bytes(secret_key_bytes)) | ||
e = PkEncryption.from_key(Curve25519PublicKey.from_bytes(public_key_bytes)) | ||
|
||
decoded = d.decrypt(e.encrypt(CLEARTEXT)) | ||
assert decoded == CLEARTEXT |