Merge branch 'master' into poljar/python-sas
This commit is contained in:
commit
2a6400716c
11 changed files with 574 additions and 5 deletions
100
android/olm-sdk/src/main/java/org/matrix/olm/OlmPkSigning.java
Normal file
100
android/olm-sdk/src/main/java/org/matrix/olm/OlmPkSigning.java
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 New Vector Ltd
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.olm;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class OlmPkSigning {
|
||||||
|
private static final String LOG_TAG = "OlmPkSigning";
|
||||||
|
|
||||||
|
/** PK Signing Id returned by JNI.
|
||||||
|
* This value uniquely identifies the native PK signing instance.
|
||||||
|
**/
|
||||||
|
private transient long mNativeId;
|
||||||
|
|
||||||
|
public OlmPkSigning() throws OlmException {
|
||||||
|
try {
|
||||||
|
mNativeId = createNewPkSigningJni();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new OlmException(OlmException.EXCEPTION_CODE_PK_SIGNING_CREATION, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private native long createNewPkSigningJni();
|
||||||
|
|
||||||
|
private native void releasePkSigningJni();
|
||||||
|
|
||||||
|
public void releaseSigning() {
|
||||||
|
if (0 != mNativeId) {
|
||||||
|
releasePkSigningJni();
|
||||||
|
}
|
||||||
|
mNativeId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isReleased() {
|
||||||
|
return (0 == mNativeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static native int seedLength();
|
||||||
|
|
||||||
|
public static byte[] generateSeed() throws OlmException {
|
||||||
|
try {
|
||||||
|
return generateSeedJni();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(LOG_TAG, "## generateSeed(): failed " + e.getMessage());
|
||||||
|
throw new OlmException(OlmException.EXCEPTION_CODE_PK_SIGNING_GENERATE_SEED, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static native byte[] generateSeedJni();
|
||||||
|
|
||||||
|
public String initWithSeed(byte[] seed) throws OlmException {
|
||||||
|
try {
|
||||||
|
byte[] pubKey = setKeyFromSeedJni(seed);
|
||||||
|
return new String(pubKey, "UTF-8");
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(LOG_TAG, "## initWithSeed(): failed " + e.getMessage());
|
||||||
|
throw new OlmException(OlmException.EXCEPTION_CODE_PK_SIGNING_INIT_WITH_SEED, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public native byte[] setKeyFromSeedJni(byte[] seed);
|
||||||
|
|
||||||
|
public String sign(String aMessage) throws OlmException {
|
||||||
|
if (null == aMessage) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] messageBuffer = null;
|
||||||
|
try {
|
||||||
|
messageBuffer = aMessage.getBytes("UTF-8");
|
||||||
|
byte[] signature = pkSignJni(messageBuffer);
|
||||||
|
return new String(signature, "UTF-8");
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(LOG_TAG, "## pkSign(): failed " + e.getMessage());
|
||||||
|
throw new OlmException(OlmException.EXCEPTION_CODE_PK_SIGNING_SIGN, e.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (null != messageBuffer) {
|
||||||
|
Arrays.fill(messageBuffer, (byte) 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private native byte[] pkSignJni(byte[] message);
|
||||||
|
}
|
|
@ -147,6 +147,14 @@ size_t olm_sas_calculate_mac(
|
||||||
void * mac, size_t mac_length
|
void * mac, size_t mac_length
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// for compatibility with an old version of Riot
|
||||||
|
size_t olm_sas_calculate_mac_long_kdf(
|
||||||
|
OlmSAS * sas,
|
||||||
|
void * input, size_t input_length,
|
||||||
|
const void * info, size_t info_length,
|
||||||
|
void * mac, size_t mac_length
|
||||||
|
);
|
||||||
|
|
||||||
/** @} */ // end of SAS group
|
/** @} */ // end of SAS group
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
@ -75,3 +75,19 @@ SAS.prototype['calculate_mac'] = restore_stack(function(input, info) {
|
||||||
);
|
);
|
||||||
return Pointer_stringify(mac_buffer);
|
return Pointer_stringify(mac_buffer);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
SAS.prototype['calculate_mac_long_kdf'] = restore_stack(function(input, info) {
|
||||||
|
var input_array = array_from_string(input);
|
||||||
|
var input_buffer = stack(input_array);
|
||||||
|
var info_array = array_from_string(info);
|
||||||
|
var info_buffer = stack(info_array);
|
||||||
|
var mac_length = sas_method(Module['_olm_sas_mac_length'])(this.ptr);
|
||||||
|
var mac_buffer = stack(mac_length + NULL_BYTE_PADDING_LENGTH);
|
||||||
|
sas_method(Module['_olm_sas_calculate_mac_long_kdf'])(
|
||||||
|
this.ptr,
|
||||||
|
input_buffer, input_array.length,
|
||||||
|
info_buffer, info_array.length,
|
||||||
|
mac_buffer, mac_length
|
||||||
|
);
|
||||||
|
return Pointer_stringify(mac_buffer);
|
||||||
|
});
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
include olm.h
|
include include/olm/olm.h
|
||||||
|
include include/olm/pk.h
|
||||||
include Makefile
|
include Makefile
|
||||||
include olm_build.py
|
include olm_build.py
|
||||||
|
|
|
@ -1,18 +1,26 @@
|
||||||
all: olm-python2 olm-python3
|
all: olm-python2 olm-python3
|
||||||
|
|
||||||
include/olm/olm.h: ../include/olm/olm.h ../include/olm/inbound_group_session.h ../include/olm/outbound_group_session.h
|
OLM_HEADERS = ../include/olm/olm.h ../include/olm/inbound_group_session.h \
|
||||||
|
../include/olm/outbound_group_session.h \
|
||||||
|
|
||||||
|
include/olm/olm.h: $(OLM_HEADERS)
|
||||||
mkdir -p include/olm
|
mkdir -p include/olm
|
||||||
$(CPP) -I dummy -I ../include ../include/olm/olm.h -o include/olm/olm.h
|
$(CPP) -I dummy -I ../include ../include/olm/olm.h -o include/olm/olm.h
|
||||||
# add memset to the header so that we can use it to clear buffers
|
# add memset to the header so that we can use it to clear buffers
|
||||||
echo 'void *memset(void *s, int c, size_t n);' >> include/olm/olm.h
|
echo 'void *memset(void *s, int c, size_t n);' >> include/olm/olm.h
|
||||||
|
|
||||||
|
include/olm/pk.h: include/olm/olm.h ../include/olm/pk.h
|
||||||
|
$(CPP) -I dummy -I ../include ../include/olm/pk.h -o include/olm/pk.h
|
||||||
|
|
||||||
include/olm/sas.h: include/olm/olm.h ../include/olm/sas.h
|
include/olm/sas.h: include/olm/olm.h ../include/olm/sas.h
|
||||||
$(CPP) -I dummy -I ../include ../include/olm/sas.h -o include/olm/sas.h
|
$(CPP) -I dummy -I ../include ../include/olm/sas.h -o include/olm/sas.h
|
||||||
|
|
||||||
olm-python2: include/olm/olm.h include/olm/sas.h
|
headers: include/olm/olm.h include/olm/pk.h include/olm/sas.h
|
||||||
|
|
||||||
|
olm-python2: headers
|
||||||
DEVELOP=$(DEVELOP) python2 setup.py build
|
DEVELOP=$(DEVELOP) python2 setup.py build
|
||||||
|
|
||||||
olm-python3: include/olm/olm.h include/olm/sas.h
|
olm-python3: headers
|
||||||
DEVELOP=$(DEVELOP) python3 setup.py build
|
DEVELOP=$(DEVELOP) python3 setup.py build
|
||||||
|
|
||||||
install: install-python2 install-python3
|
install: install-python2 install-python3
|
||||||
|
|
|
@ -36,4 +36,11 @@ from .group_session import (
|
||||||
OutboundGroupSession,
|
OutboundGroupSession,
|
||||||
OlmGroupSessionError
|
OlmGroupSessionError
|
||||||
)
|
)
|
||||||
|
from .pk import (
|
||||||
|
PkMessage,
|
||||||
|
PkEncryption,
|
||||||
|
PkDecryption,
|
||||||
|
PkEncryptionError,
|
||||||
|
PkDecryptionError
|
||||||
|
)
|
||||||
from .sas import Sas, OlmSasError
|
from .sas import Sas, OlmSasError
|
||||||
|
|
346
python/olm/pk.py
Normal file
346
python/olm/pk.py
Normal file
|
@ -0,0 +1,346 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# libolm python bindings
|
||||||
|
# Copyright © 2018 Damir Jelić <poljar@termina.org.uk>
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
"""libolm PK module.
|
||||||
|
|
||||||
|
This module contains bindings to the PK part of the Olm library.
|
||||||
|
It contains two classes PkDecryption and PkEncryption that are used to
|
||||||
|
establish an encrypted communication channel using public key encryption.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> decryption = PkDecryption()
|
||||||
|
>>> encryption = PkEncryption(decryption.public_key)
|
||||||
|
>>> plaintext = "It's a secret to everybody."
|
||||||
|
>>> message = encryption.encrypt(plaintext)
|
||||||
|
>>> decrypted_plaintext = decryption.decrypt(message)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from builtins import super
|
||||||
|
from typing import AnyStr, Type
|
||||||
|
from future.utils import bytes_to_native_str
|
||||||
|
|
||||||
|
from _libolm import ffi, lib # type: ignore
|
||||||
|
from ._finalize import track_for_finalization
|
||||||
|
from ._compat import URANDOM, to_bytearray
|
||||||
|
|
||||||
|
|
||||||
|
class PkEncryptionError(Exception):
|
||||||
|
"""libolm Pk encryption exception."""
|
||||||
|
|
||||||
|
|
||||||
|
class PkDecryptionError(Exception):
|
||||||
|
"""libolm Pk decryption exception."""
|
||||||
|
|
||||||
|
|
||||||
|
def _clear_pk_encryption(pk_struct):
|
||||||
|
lib.olm_clear_pk_encryption(pk_struct)
|
||||||
|
|
||||||
|
|
||||||
|
class PkMessage(object):
|
||||||
|
"""A PK encrypted message."""
|
||||||
|
|
||||||
|
def __init__(self, ephemeral_key, mac, ciphertext):
|
||||||
|
# type: (str, str, str) -> None
|
||||||
|
"""Create a new PK encrypted message.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ephemeral_key(str): the public part of the ephemeral key
|
||||||
|
used (together with the recipient's key) to generate a symmetric
|
||||||
|
encryption key.
|
||||||
|
mac(str): Message Authentication Code of the encrypted message
|
||||||
|
ciphertext(str): The cipher text of the encrypted message
|
||||||
|
"""
|
||||||
|
self.ephemeral_key = ephemeral_key
|
||||||
|
self.mac = mac
|
||||||
|
self.ciphertext = ciphertext
|
||||||
|
|
||||||
|
|
||||||
|
class PkEncryption(object):
|
||||||
|
"""PkEncryption class.
|
||||||
|
|
||||||
|
Represents the decryption part of a PK encrypted channel.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, recipient_key):
|
||||||
|
# type: (AnyStr) -> None
|
||||||
|
"""Create a new PK encryption object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
recipient_key(str): a public key that will be used for encryption
|
||||||
|
"""
|
||||||
|
if not recipient_key:
|
||||||
|
raise ValueError("Recipient key can't be empty")
|
||||||
|
|
||||||
|
self._buf = ffi.new("char[]", lib.olm_pk_encryption_size())
|
||||||
|
self._pk_encryption = lib.olm_pk_encryption(self._buf)
|
||||||
|
track_for_finalization(self, self._pk_encryption, _clear_pk_encryption)
|
||||||
|
|
||||||
|
byte_key = to_bytearray(recipient_key)
|
||||||
|
lib.olm_pk_encryption_set_recipient_key(
|
||||||
|
self._pk_encryption,
|
||||||
|
ffi.from_buffer(byte_key),
|
||||||
|
len(byte_key)
|
||||||
|
)
|
||||||
|
|
||||||
|
# clear out copies of the key
|
||||||
|
if byte_key is not recipient_key: # pragma: no cover
|
||||||
|
for i in range(0, len(byte_key)):
|
||||||
|
byte_key[i] = 0
|
||||||
|
|
||||||
|
def _check_error(self, ret): # pragma: no cover
|
||||||
|
# type: (int) -> None
|
||||||
|
if ret != lib.olm_error():
|
||||||
|
return
|
||||||
|
|
||||||
|
last_error = bytes_to_native_str(
|
||||||
|
ffi.string(lib.olm_pk_encryption_last_error(self._pk_encryption)))
|
||||||
|
|
||||||
|
raise PkEncryptionError(last_error)
|
||||||
|
|
||||||
|
def encrypt(self, plaintext):
|
||||||
|
# type: (AnyStr) -> PkMessage
|
||||||
|
"""Encrypt a message.
|
||||||
|
|
||||||
|
Returns the encrypted PkMessage.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
plaintext(str): A string that will be encrypted using the
|
||||||
|
PkEncryption object.
|
||||||
|
"""
|
||||||
|
byte_plaintext = to_bytearray(plaintext)
|
||||||
|
|
||||||
|
r_length = lib.olm_pk_encrypt_random_length(self._pk_encryption)
|
||||||
|
random = URANDOM(r_length)
|
||||||
|
random_buffer = ffi.new("char[]", random)
|
||||||
|
|
||||||
|
ciphertext_length = lib.olm_pk_ciphertext_length(
|
||||||
|
self._pk_encryption, len(byte_plaintext)
|
||||||
|
)
|
||||||
|
ciphertext = ffi.new("char[]", ciphertext_length)
|
||||||
|
|
||||||
|
mac_length = lib.olm_pk_mac_length(self._pk_encryption)
|
||||||
|
mac = ffi.new("char[]", mac_length)
|
||||||
|
|
||||||
|
ephemeral_key_size = lib.olm_pk_key_length()
|
||||||
|
ephemeral_key = ffi.new("char[]", ephemeral_key_size)
|
||||||
|
|
||||||
|
ret = lib.olm_pk_encrypt(
|
||||||
|
self._pk_encryption,
|
||||||
|
ffi.from_buffer(byte_plaintext), len(byte_plaintext),
|
||||||
|
ciphertext, ciphertext_length,
|
||||||
|
mac, mac_length,
|
||||||
|
ephemeral_key, ephemeral_key_size,
|
||||||
|
random_buffer, r_length
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._check_error(ret)
|
||||||
|
finally: # pragma: no cover
|
||||||
|
# clear out copies of plaintext
|
||||||
|
if byte_plaintext is not plaintext:
|
||||||
|
for i in range(0, len(byte_plaintext)):
|
||||||
|
byte_plaintext[i] = 0
|
||||||
|
|
||||||
|
message = PkMessage(
|
||||||
|
bytes_to_native_str(
|
||||||
|
ffi.unpack(ephemeral_key, ephemeral_key_size)),
|
||||||
|
bytes_to_native_str(
|
||||||
|
ffi.unpack(mac, mac_length)),
|
||||||
|
bytes_to_native_str(
|
||||||
|
ffi.unpack(ciphertext, ciphertext_length))
|
||||||
|
)
|
||||||
|
return message
|
||||||
|
|
||||||
|
|
||||||
|
def _clear_pk_decryption(pk_struct):
|
||||||
|
lib.olm_clear_pk_decryption(pk_struct)
|
||||||
|
|
||||||
|
|
||||||
|
class PkDecryption(object):
|
||||||
|
"""PkDecryption class.
|
||||||
|
|
||||||
|
Represents the decryption part of a PK encrypted channel.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
public_key (str): The public key of the PkDecryption object, can be
|
||||||
|
shared and used to create a PkEncryption object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __new__(cls):
|
||||||
|
# type: (Type[PkDecryption]) -> PkDecryption
|
||||||
|
obj = super().__new__(cls)
|
||||||
|
obj._buf = ffi.new("char[]", lib.olm_pk_decryption_size())
|
||||||
|
obj._pk_decryption = lib.olm_pk_decryption(obj._buf)
|
||||||
|
obj.public_key = None
|
||||||
|
track_for_finalization(obj, obj._pk_decryption, _clear_pk_decryption)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
if False: # pragma: no cover
|
||||||
|
self._pk_decryption = self._pk_decryption # type: ffi.cdata
|
||||||
|
|
||||||
|
random_length = lib.olm_pk_private_key_length()
|
||||||
|
random = URANDOM(random_length)
|
||||||
|
random_buffer = ffi.new("char[]", random)
|
||||||
|
|
||||||
|
key_length = lib.olm_pk_key_length()
|
||||||
|
key_buffer = ffi.new("char[]", key_length)
|
||||||
|
|
||||||
|
ret = lib.olm_pk_key_from_private(
|
||||||
|
self._pk_decryption,
|
||||||
|
key_buffer, key_length,
|
||||||
|
random_buffer, random_length
|
||||||
|
)
|
||||||
|
self._check_error(ret)
|
||||||
|
self.public_key = bytes_to_native_str(ffi.unpack(
|
||||||
|
key_buffer,
|
||||||
|
key_length
|
||||||
|
))
|
||||||
|
|
||||||
|
def _check_error(self, ret):
|
||||||
|
# type: (int) -> None
|
||||||
|
if ret != lib.olm_error():
|
||||||
|
return
|
||||||
|
|
||||||
|
last_error = bytes_to_native_str(
|
||||||
|
ffi.string(lib.olm_pk_decryption_last_error(self._pk_decryption)))
|
||||||
|
|
||||||
|
raise PkDecryptionError(last_error)
|
||||||
|
|
||||||
|
def pickle(self, passphrase=""):
|
||||||
|
# type: (str) -> bytes
|
||||||
|
"""Store a PkDecryption object.
|
||||||
|
|
||||||
|
Stores a PkDecryption object as a base64 string. Encrypts the object
|
||||||
|
using the supplied passphrase. Returns a byte object containing the
|
||||||
|
base64 encoded string of the pickled session.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
passphrase(str, optional): The passphrase to be used to encrypt
|
||||||
|
the object.
|
||||||
|
"""
|
||||||
|
byte_key = to_bytearray(passphrase)
|
||||||
|
|
||||||
|
pickle_length = lib.olm_pickle_pk_decryption_length(
|
||||||
|
self._pk_decryption
|
||||||
|
)
|
||||||
|
pickle_buffer = ffi.new("char[]", pickle_length)
|
||||||
|
|
||||||
|
ret = lib.olm_pickle_pk_decryption(
|
||||||
|
self._pk_decryption,
|
||||||
|
ffi.from_buffer(byte_key), len(byte_key),
|
||||||
|
pickle_buffer, pickle_length
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
self._check_error(ret)
|
||||||
|
finally:
|
||||||
|
# zero out copies of the passphrase
|
||||||
|
for i in range(0, len(byte_key)):
|
||||||
|
byte_key[i] = 0
|
||||||
|
|
||||||
|
return ffi.unpack(pickle_buffer, pickle_length)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_pickle(cls, pickle, passphrase=""):
|
||||||
|
# types: (bytes, str) -> PkDecryption
|
||||||
|
"""Restore a previously stored PkDecryption object.
|
||||||
|
|
||||||
|
Creates a PkDecryption object from a pickled base64 string. Decrypts
|
||||||
|
the pickled object using the supplied passphrase.
|
||||||
|
Raises PkDecryptionError on failure. If the passphrase
|
||||||
|
doesn't match the one used to encrypt the session then the error
|
||||||
|
message for the exception will be "BAD_ACCOUNT_KEY". If the base64
|
||||||
|
couldn't be decoded then the error message will be "INVALID_BASE64".
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pickle(bytes): Base64 encoded byte string containing the pickled
|
||||||
|
PkDecryption object
|
||||||
|
passphrase(str, optional): The passphrase used to encrypt the
|
||||||
|
object
|
||||||
|
"""
|
||||||
|
if not pickle:
|
||||||
|
raise ValueError("Pickle can't be empty")
|
||||||
|
|
||||||
|
byte_key = to_bytearray(passphrase)
|
||||||
|
pickle_buffer = ffi.new("char[]", pickle)
|
||||||
|
|
||||||
|
pubkey_length = lib.olm_pk_key_length()
|
||||||
|
pubkey_buffer = ffi.new("char[]", pubkey_length)
|
||||||
|
|
||||||
|
obj = cls.__new__(cls)
|
||||||
|
|
||||||
|
ret = lib.olm_unpickle_pk_decryption(
|
||||||
|
obj._pk_decryption,
|
||||||
|
ffi.from_buffer(byte_key), len(byte_key),
|
||||||
|
pickle_buffer, len(pickle),
|
||||||
|
pubkey_buffer, pubkey_length)
|
||||||
|
|
||||||
|
try:
|
||||||
|
obj._check_error(ret)
|
||||||
|
finally:
|
||||||
|
for i in range(0, len(byte_key)):
|
||||||
|
byte_key[i] = 0
|
||||||
|
|
||||||
|
obj.public_key = bytes_to_native_str(ffi.unpack(
|
||||||
|
pubkey_buffer,
|
||||||
|
pubkey_length
|
||||||
|
))
|
||||||
|
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def decrypt(self, message):
|
||||||
|
# type (PkMessage) -> str
|
||||||
|
"""Decrypt a previously encrypted Pk message.
|
||||||
|
|
||||||
|
Returns the decrypted plaintext.
|
||||||
|
Raises PkDecryptionError on failure.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message(PkMessage): the pk message to decrypt.
|
||||||
|
"""
|
||||||
|
ephemeral_key = to_bytearray(message.ephemeral_key)
|
||||||
|
ephemeral_key_size = len(ephemeral_key)
|
||||||
|
|
||||||
|
mac = to_bytearray(message.mac)
|
||||||
|
mac_length = len(mac)
|
||||||
|
|
||||||
|
ciphertext = to_bytearray(message.ciphertext)
|
||||||
|
ciphertext_length = len(ciphertext)
|
||||||
|
|
||||||
|
max_plaintext_length = lib.olm_pk_max_plaintext_length(
|
||||||
|
self._pk_decryption,
|
||||||
|
ciphertext_length
|
||||||
|
)
|
||||||
|
plaintext_buffer = ffi.new("char[]", max_plaintext_length)
|
||||||
|
|
||||||
|
ret = lib.olm_pk_decrypt(
|
||||||
|
self._pk_decryption,
|
||||||
|
ffi.from_buffer(ephemeral_key), ephemeral_key_size,
|
||||||
|
ffi.from_buffer(mac), mac_length,
|
||||||
|
ffi.from_buffer(ciphertext), ciphertext_length,
|
||||||
|
plaintext_buffer, max_plaintext_length)
|
||||||
|
self._check_error(ret)
|
||||||
|
|
||||||
|
plaintext = (ffi.unpack(
|
||||||
|
plaintext_buffer,
|
||||||
|
ret
|
||||||
|
))
|
||||||
|
|
||||||
|
# clear out copies of the plaintext
|
||||||
|
lib.memset(plaintext_buffer, 0, max_plaintext_length)
|
||||||
|
|
||||||
|
return bytes_to_native_str(plaintext)
|
|
@ -18,6 +18,7 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
from cffi import FFI
|
from cffi import FFI
|
||||||
|
|
||||||
|
@ -32,6 +33,8 @@ link_args = ["-L../build"]
|
||||||
if DEVELOP and DEVELOP.lower() in ["yes", "true", "1"]:
|
if DEVELOP and DEVELOP.lower() in ["yes", "true", "1"]:
|
||||||
link_args.append('-Wl,-rpath=../build')
|
link_args.append('-Wl,-rpath=../build')
|
||||||
|
|
||||||
|
headers_build = subprocess.Popen("make headers", shell=True)
|
||||||
|
headers_build.wait()
|
||||||
|
|
||||||
ffibuilder.set_source(
|
ffibuilder.set_source(
|
||||||
"_libolm",
|
"_libolm",
|
||||||
|
@ -39,6 +42,7 @@ ffibuilder.set_source(
|
||||||
#include <olm/olm.h>
|
#include <olm/olm.h>
|
||||||
#include <olm/inbound_group_session.h>
|
#include <olm/inbound_group_session.h>
|
||||||
#include <olm/outbound_group_session.h>
|
#include <olm/outbound_group_session.h>
|
||||||
|
#include <olm/pk.h>
|
||||||
#include <olm/sas.h>
|
#include <olm/sas.h>
|
||||||
""",
|
""",
|
||||||
libraries=["olm"],
|
libraries=["olm"],
|
||||||
|
@ -48,6 +52,9 @@ ffibuilder.set_source(
|
||||||
with open(os.path.join(PATH, "include/olm/olm.h")) as f:
|
with open(os.path.join(PATH, "include/olm/olm.h")) as f:
|
||||||
ffibuilder.cdef(f.read(), override=True)
|
ffibuilder.cdef(f.read(), override=True)
|
||||||
|
|
||||||
|
with open(os.path.join(PATH, "include/olm/pk.h")) as f:
|
||||||
|
ffibuilder.cdef(f.read(), override=True)
|
||||||
|
|
||||||
with open(os.path.join(PATH, "include/olm/sas.h")) as f:
|
with open(os.path.join(PATH, "include/olm/sas.h")) as f:
|
||||||
ffibuilder.cdef(f.read(), override=True)
|
ffibuilder.cdef(f.read(), override=True)
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,10 @@ setup(
|
||||||
packages=["olm"],
|
packages=["olm"],
|
||||||
setup_requires=["cffi>=1.0.0"],
|
setup_requires=["cffi>=1.0.0"],
|
||||||
cffi_modules=["olm_build.py:ffibuilder"],
|
cffi_modules=["olm_build.py:ffibuilder"],
|
||||||
install_requires=["cffi>=1.0.0", "future", "typing"],
|
install_requires=[
|
||||||
|
"cffi>=1.0.0",
|
||||||
|
"future",
|
||||||
|
"typing;python_version<'3.5'"
|
||||||
|
],
|
||||||
zip_safe=False
|
zip_safe=False
|
||||||
)
|
)
|
||||||
|
|
49
python/tests/pk_test.py
Normal file
49
python/tests/pk_test.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from olm import PkDecryption, PkDecryptionError, PkEncryption
|
||||||
|
|
||||||
|
|
||||||
|
class TestClass(object):
|
||||||
|
def test_invalid_encryption(self):
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
PkEncryption("")
|
||||||
|
|
||||||
|
def test_decrytion(self):
|
||||||
|
decryption = PkDecryption()
|
||||||
|
encryption = PkEncryption(decryption.public_key)
|
||||||
|
plaintext = "It's a secret to everybody."
|
||||||
|
message = encryption.encrypt(plaintext)
|
||||||
|
decrypted_plaintext = decryption.decrypt(message)
|
||||||
|
isinstance(decrypted_plaintext, str)
|
||||||
|
assert plaintext == decrypted_plaintext
|
||||||
|
|
||||||
|
def test_invalid_decrytion(self):
|
||||||
|
decryption = PkDecryption()
|
||||||
|
encryption = PkEncryption(decryption.public_key)
|
||||||
|
plaintext = "It's a secret to everybody."
|
||||||
|
message = encryption.encrypt(plaintext)
|
||||||
|
message.ephemeral_key = "?"
|
||||||
|
with pytest.raises(PkDecryptionError):
|
||||||
|
decryption.decrypt(message)
|
||||||
|
|
||||||
|
def test_pickling(self):
|
||||||
|
decryption = PkDecryption()
|
||||||
|
encryption = PkEncryption(decryption.public_key)
|
||||||
|
plaintext = "It's a secret to everybody."
|
||||||
|
message = encryption.encrypt(plaintext)
|
||||||
|
|
||||||
|
pickle = decryption.pickle()
|
||||||
|
unpickled = PkDecryption.from_pickle(pickle)
|
||||||
|
decrypted_plaintext = unpickled.decrypt(message)
|
||||||
|
assert plaintext == decrypted_plaintext
|
||||||
|
|
||||||
|
def test_invalid_unpickling(self):
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
PkDecryption.from_pickle("")
|
||||||
|
|
||||||
|
def test_invalid_pass_pickling(self):
|
||||||
|
decryption = PkDecryption()
|
||||||
|
pickle = decryption.pickle("Secret")
|
||||||
|
|
||||||
|
with pytest.raises(PkDecryptionError):
|
||||||
|
PkDecryption.from_pickle(pickle, "Not secret")
|
23
src/sas.c
23
src/sas.c
|
@ -139,3 +139,26 @@ size_t olm_sas_calculate_mac(
|
||||||
_olm_encode_base64((const uint8_t *)mac, SHA256_OUTPUT_LENGTH, (uint8_t *)mac);
|
_olm_encode_base64((const uint8_t *)mac, SHA256_OUTPUT_LENGTH, (uint8_t *)mac);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for compatibility with an old version of Riot
|
||||||
|
size_t olm_sas_calculate_mac_long_kdf(
|
||||||
|
OlmSAS * sas,
|
||||||
|
void * input, size_t input_length,
|
||||||
|
const void * info, size_t info_length,
|
||||||
|
void * mac, size_t mac_length
|
||||||
|
) {
|
||||||
|
if (mac_length < olm_sas_mac_length(sas)) {
|
||||||
|
sas->last_error = OLM_OUTPUT_BUFFER_TOO_SMALL;
|
||||||
|
return (size_t)-1;
|
||||||
|
}
|
||||||
|
uint8_t key[256];
|
||||||
|
_olm_crypto_hkdf_sha256(
|
||||||
|
sas->secret, sizeof(sas->secret),
|
||||||
|
NULL, 0,
|
||||||
|
(const uint8_t *) info, info_length,
|
||||||
|
key, 256
|
||||||
|
);
|
||||||
|
_olm_crypto_hmac_sha256(key, 256, input, input_length, mac);
|
||||||
|
_olm_encode_base64((const uint8_t *)mac, SHA256_OUTPUT_LENGTH, (uint8_t *)mac);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue