Allow using OpenSSL instead of GnuTLS
In preparation of Windows support.
This commit is contained in:
parent
c5704ea56b
commit
e4bd6c1ce4
|
@ -22,6 +22,8 @@ GENERATE_VAPI
|
||||||
crypto-vala
|
crypto-vala
|
||||||
GENERATE_HEADER
|
GENERATE_HEADER
|
||||||
crypto-vala
|
crypto-vala
|
||||||
|
DEFINITIONS
|
||||||
|
GCRYPT
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_target(crypto-vala-vapi
|
add_custom_target(crypto-vala-vapi
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
dependencies = [
|
dependencies = [
|
||||||
dep_gio,
|
dep_gio,
|
||||||
dep_glib,
|
dep_glib,
|
||||||
dep_libgcrypt,
|
dep_libgcrypt_or_openssl,
|
||||||
dep_libsrtp2,
|
dep_libsrtp2,
|
||||||
]
|
]
|
||||||
sources = files(
|
sources = files(
|
||||||
|
@ -17,6 +17,11 @@ c_args = [
|
||||||
vala_args = [
|
vala_args = [
|
||||||
'--vapidir', meson.current_source_dir() / 'vapi',
|
'--vapidir', meson.current_source_dir() / 'vapi',
|
||||||
]
|
]
|
||||||
|
if crypto_backend == 'openssl'
|
||||||
|
vala_args += ['--pkg', 'openssl'] # Work around https://github.com/mesonbuild/meson/issues/2103.
|
||||||
|
elif crypto_backend == 'gnutls'
|
||||||
|
vala_args += ['-D', 'GCRYPT']
|
||||||
|
endif
|
||||||
lib_crypto_vala = library('crypto-vala', sources, c_args: c_args, vala_args: vala_args, dependencies: dependencies, version: '0.0', install: true, install_dir: [true, true, true])
|
lib_crypto_vala = library('crypto-vala', sources, c_args: c_args, vala_args: vala_args, dependencies: dependencies, version: '0.0', install: true, install_dir: [true, true, true])
|
||||||
dep_crypto_vala = declare_dependency(link_with: lib_crypto_vala, include_directories: include_directories('.'))
|
dep_crypto_vala = declare_dependency(link_with: lib_crypto_vala, include_directories: include_directories('.'))
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,24 @@
|
||||||
namespace Crypto {
|
namespace Crypto {
|
||||||
public class SymmetricCipher {
|
public class SymmetricCipher {
|
||||||
|
#if GCRYPT
|
||||||
private GCrypt.Cipher.Cipher cipher;
|
private GCrypt.Cipher.Cipher cipher;
|
||||||
|
#else
|
||||||
|
bool is_encryption;
|
||||||
|
private OpenSSL.EVP.CipherContext? cipher;
|
||||||
|
#endif
|
||||||
|
|
||||||
public static bool supports(string algo_name) {
|
public static bool supports(string algo_name) {
|
||||||
|
#if GCRYPT
|
||||||
GCrypt.Cipher.Algorithm algo;
|
GCrypt.Cipher.Algorithm algo;
|
||||||
GCrypt.Cipher.Mode mode;
|
GCrypt.Cipher.Mode mode;
|
||||||
GCrypt.Cipher.Flag flags;
|
GCrypt.Cipher.Flag flags;
|
||||||
return parse(algo_name, out algo, out mode, out flags);
|
return parse(algo_name, out algo, out mode, out flags);
|
||||||
|
#else
|
||||||
|
return algo_name == "AES-GCM";
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if GCRYPT
|
||||||
private static unowned string mode_to_string(GCrypt.Cipher.Mode mode) {
|
private static unowned string mode_to_string(GCrypt.Cipher.Mode mode) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case GCrypt.Cipher.Mode.ECB: return "ECB";
|
case GCrypt.Cipher.Mode.ECB: return "ECB";
|
||||||
|
@ -95,8 +105,18 @@ public class SymmetricCipher {
|
||||||
return algo.to_string();
|
return algo.to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public SymmetricCipher(string algo_name) throws Error {
|
public SymmetricCipher.encryption(string algo_name) throws Error {
|
||||||
|
this.initialize(algo_name, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SymmetricCipher.decryption(string algo_name) throws Error {
|
||||||
|
this.initialize(algo_name, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SymmetricCipher.initialize(string algo_name, bool is_encryption) throws Error {
|
||||||
|
#if GCRYPT
|
||||||
GCrypt.Cipher.Algorithm algo;
|
GCrypt.Cipher.Algorithm algo;
|
||||||
GCrypt.Cipher.Mode mode;
|
GCrypt.Cipher.Mode mode;
|
||||||
GCrypt.Cipher.Flag flags;
|
GCrypt.Cipher.Flag flags;
|
||||||
|
@ -105,48 +125,157 @@ public class SymmetricCipher {
|
||||||
} else {
|
} else {
|
||||||
throw new Error.ILLEGAL_ARGUMENTS(@"The algorithm $algo_name is not supported");
|
throw new Error.ILLEGAL_ARGUMENTS(@"The algorithm $algo_name is not supported");
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
if (algo_name == "AES-GCM") {
|
||||||
|
this.openssl(is_encryption);
|
||||||
|
} else {
|
||||||
|
throw new Error.ILLEGAL_ARGUMENTS(@"The algorithm $algo_name is not supported");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if GCRYPT
|
||||||
private SymmetricCipher.gcrypt(GCrypt.Cipher.Algorithm algo, GCrypt.Cipher.Mode mode, GCrypt.Cipher.Flag flags) throws Error {
|
private SymmetricCipher.gcrypt(GCrypt.Cipher.Algorithm algo, GCrypt.Cipher.Mode mode, GCrypt.Cipher.Flag flags) throws Error {
|
||||||
may_throw_gcrypt_error(GCrypt.Cipher.Cipher.open(out this.cipher, algo, mode, flags));
|
may_throw_gcrypt_error(GCrypt.Cipher.Cipher.open(out this.cipher, algo, mode, flags));
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
private SymmetricCipher.openssl(bool is_encryption) throws Error {
|
||||||
|
this.is_encryption = is_encryption;
|
||||||
|
cipher = new OpenSSL.EVP.CipherContext();
|
||||||
|
if (is_encryption) {
|
||||||
|
if (cipher.encrypt_init(OpenSSL.EVP.aes_128_gcm(), null, null, null) != 1) {
|
||||||
|
openssl_error();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (cipher.decrypt_init(OpenSSL.EVP.aes_128_gcm(), null, null, null) != 1) {
|
||||||
|
openssl_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
public void set_key(uint8[] key) throws Error {
|
public void set_key(uint8[] key) throws Error {
|
||||||
|
#if GCRYPT
|
||||||
may_throw_gcrypt_error(cipher.set_key(key));
|
may_throw_gcrypt_error(cipher.set_key(key));
|
||||||
|
#else
|
||||||
|
if (key.length != 16) {
|
||||||
|
throw new Crypto.Error.ILLEGAL_ARGUMENTS("key length must be 16 for AES-GCM");
|
||||||
|
}
|
||||||
|
if (is_encryption) {
|
||||||
|
if (cipher.encrypt_init(null, null, key, null) != 1) {
|
||||||
|
openssl_error();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (cipher.decrypt_init(null, null, key, null) != 1) {
|
||||||
|
openssl_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set_iv(uint8[] iv) throws Error {
|
public void set_iv(uint8[] iv) throws Error {
|
||||||
|
#if GCRYPT
|
||||||
may_throw_gcrypt_error(cipher.set_iv(iv));
|
may_throw_gcrypt_error(cipher.set_iv(iv));
|
||||||
|
#else
|
||||||
|
if (iv.length != 12) {
|
||||||
|
throw new Crypto.Error.ILLEGAL_ARGUMENTS("intialization vector must be of length 16 for AES-GCM");
|
||||||
}
|
}
|
||||||
|
if (is_encryption) {
|
||||||
public void set_counter_vector(uint8[] ctr) throws Error {
|
if (cipher.encrypt_init(null, null, null, iv) != 1) {
|
||||||
may_throw_gcrypt_error(cipher.set_counter_vector(ctr));
|
openssl_error();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (cipher.decrypt_init(null, null, null, iv) != 1) {
|
||||||
|
openssl_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() throws Error {
|
public void reset() throws Error {
|
||||||
|
#if GCRYPT
|
||||||
may_throw_gcrypt_error(cipher.reset());
|
may_throw_gcrypt_error(cipher.reset());
|
||||||
|
#else
|
||||||
|
throw new Crypto.Error.ILLEGAL_ARGUMENTS("can't reset OpenSSL cipher context");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint8[] get_tag(size_t taglen) throws Error {
|
public uint8[] get_tag(size_t taglen) throws Error {
|
||||||
uint8[] tag = new uint8[taglen];
|
uint8[] tag = new uint8[taglen];
|
||||||
|
#if GCRYPT
|
||||||
may_throw_gcrypt_error(cipher.get_tag(tag));
|
may_throw_gcrypt_error(cipher.get_tag(tag));
|
||||||
|
#else
|
||||||
|
if (!is_encryption) {
|
||||||
|
throw new Crypto.Error.ILLEGAL_ARGUMENTS("can't call get_tag on decryption context");
|
||||||
|
}
|
||||||
|
uint8[] empty = new uint8[0];
|
||||||
|
int empty_len = 0;
|
||||||
|
if (cipher.encrypt_final(empty, out empty_len) != 1) {
|
||||||
|
openssl_error();
|
||||||
|
}
|
||||||
|
if (empty_len != 0) {
|
||||||
|
throw new Crypto.Error.ILLEGAL_ARGUMENTS("get_tag called on a stream with remaining data");
|
||||||
|
}
|
||||||
|
if (cipher.ctrl(OpenSSL.EVP.CTRL_GCM_GET_TAG, (int)taglen, tag) != 1) {
|
||||||
|
openssl_error();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void check_tag(uint8[] tag) throws Error {
|
public void check_tag(uint8[] tag) throws Error {
|
||||||
|
#if GCRYPT
|
||||||
may_throw_gcrypt_error(cipher.check_tag(tag));
|
may_throw_gcrypt_error(cipher.check_tag(tag));
|
||||||
|
#else
|
||||||
|
if (is_encryption) {
|
||||||
|
throw new Crypto.Error.ILLEGAL_ARGUMENTS("can't call check_tag on encryption context");
|
||||||
|
}
|
||||||
|
if (cipher.ctrl(OpenSSL.EVP.CTRL_GCM_SET_TAG, tag.length, tag) != 1) {
|
||||||
|
openssl_error();
|
||||||
|
}
|
||||||
|
uint8[] empty = new uint8[0];
|
||||||
|
int empty_len = 0;
|
||||||
|
if (cipher.decrypt_final(empty, out empty_len) != 1) {
|
||||||
|
openssl_error();
|
||||||
|
}
|
||||||
|
if (empty_len != 0) {
|
||||||
|
throw new Crypto.Error.ILLEGAL_ARGUMENTS("check_tag called on a stream with remaining data");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public void encrypt(uint8[] output, uint8[] input) throws Error {
|
public void encrypt(uint8[] output, uint8[] input) throws Error {
|
||||||
|
#if GCRYPT
|
||||||
may_throw_gcrypt_error(cipher.encrypt(output, input));
|
may_throw_gcrypt_error(cipher.encrypt(output, input));
|
||||||
|
#else
|
||||||
|
if (!is_encryption) {
|
||||||
|
throw new Crypto.Error.ILLEGAL_ARGUMENTS("can't call encrypt on decryption context");
|
||||||
|
}
|
||||||
|
int output_length = output.length;
|
||||||
|
if (cipher.encrypt_update(output, out output_length, input) != 1) {
|
||||||
|
openssl_error();
|
||||||
|
}
|
||||||
|
if (output_length != output.length) {
|
||||||
|
throw new Crypto.Error.ILLEGAL_ARGUMENTS("invalid output array length");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decrypt(uint8[] output, uint8[] input) throws Error {
|
public void decrypt(uint8[] output, uint8[] input) throws Error {
|
||||||
|
#if GCRYPT
|
||||||
may_throw_gcrypt_error(cipher.decrypt(output, input));
|
may_throw_gcrypt_error(cipher.decrypt(output, input));
|
||||||
|
#else
|
||||||
|
if (is_encryption) {
|
||||||
|
throw new Crypto.Error.ILLEGAL_ARGUMENTS("can't call decrypt on encryption context");
|
||||||
}
|
}
|
||||||
|
int output_length = output.length;
|
||||||
public void sync() throws Error {
|
if (cipher.decrypt_update(output, out output_length, input) != 1) {
|
||||||
may_throw_gcrypt_error(cipher.sync());
|
openssl_error();
|
||||||
|
}
|
||||||
|
if (output_length != output.length) {
|
||||||
|
throw new Crypto.Error.ILLEGAL_ARGUMENTS("invalid output array length");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,20 @@ namespace Crypto {
|
||||||
public errordomain Error {
|
public errordomain Error {
|
||||||
ILLEGAL_ARGUMENTS,
|
ILLEGAL_ARGUMENTS,
|
||||||
GCRYPT,
|
GCRYPT,
|
||||||
|
OPENSSL,
|
||||||
AUTHENTICATION_FAILED,
|
AUTHENTICATION_FAILED,
|
||||||
UNKNOWN
|
UNKNOWN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if GCRYPT
|
||||||
internal void may_throw_gcrypt_error(GCrypt.Error e) throws Error {
|
internal void may_throw_gcrypt_error(GCrypt.Error e) throws Error {
|
||||||
if (((int)e) != 0) {
|
if (((int)e) != 0) {
|
||||||
throw new Crypto.Error.GCRYPT(e.to_string());
|
throw new Crypto.Error.GCRYPT(e.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
internal void openssl_error() throws Error {
|
||||||
|
throw new Crypto.Error.OPENSSL(OpenSSL.ERR.reason_error_string(OpenSSL.ERR.get_error()));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
|
@ -1,5 +1,9 @@
|
||||||
namespace Crypto {
|
namespace Crypto {
|
||||||
public static void randomize(uint8[] buffer) {
|
public static void randomize(uint8[] buffer) {
|
||||||
|
#if GCRYPT
|
||||||
GCrypt.Random.randomize(buffer);
|
GCrypt.Random.randomize(buffer);
|
||||||
|
#else
|
||||||
|
OpenSSL.RAND.bytes(buffer);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -669,7 +669,7 @@ namespace OpenSSL
|
||||||
public int set_padding (int pad);
|
public int set_padding (int pad);
|
||||||
|
|
||||||
[CCode (cname = "EVP_EncryptInit_ex")]
|
[CCode (cname = "EVP_EncryptInit_ex")]
|
||||||
public int encrypt_init (Cipher cipher, Engine? engine, [CCode (array_length = false)] uchar[] key, [CCode (array_length = false)] uchar[] iv);
|
public int encrypt_init (Cipher? cipher, Engine? engine, [CCode (array_length = false)] uchar[]? key, [CCode (array_length = false)] uchar[]? iv);
|
||||||
|
|
||||||
[CCode (cname = "EVP_EncryptUpdate")]
|
[CCode (cname = "EVP_EncryptUpdate")]
|
||||||
public int encrypt_update ([CCode (array_length = false)] uchar[] ciphertext, out int ciphertext_len, uchar[] plaintext);
|
public int encrypt_update ([CCode (array_length = false)] uchar[] ciphertext, out int ciphertext_len, uchar[] plaintext);
|
||||||
|
@ -678,13 +678,16 @@ namespace OpenSSL
|
||||||
public int encrypt_final ([CCode (array_length = false)] uchar[] ciphertext, out int ciphertext_len);
|
public int encrypt_final ([CCode (array_length = false)] uchar[] ciphertext, out int ciphertext_len);
|
||||||
|
|
||||||
[CCode (cname = "EVP_DecryptInit_ex")]
|
[CCode (cname = "EVP_DecryptInit_ex")]
|
||||||
public int decrypt_init (Cipher cipher, Engine? engine, [CCode (array_length = false)] uchar[] key, [CCode (array_length = false)] uchar[] iv);
|
public int decrypt_init (Cipher? cipher, Engine? engine, [CCode (array_length = false)] uchar[]? key, [CCode (array_length = false)] uchar[]? iv);
|
||||||
|
|
||||||
[CCode (cname = "EVP_DecryptUpdate")]
|
[CCode (cname = "EVP_DecryptUpdate")]
|
||||||
public int decrypt_update ([CCode (array_length = false)] uchar[] plaintext, out int plaintext_len, uchar[] ciphertext);
|
public int decrypt_update ([CCode (array_length = false)] uchar[] plaintext, out int plaintext_len, uchar[] ciphertext);
|
||||||
|
|
||||||
[CCode (cname = "EVP_DecryptFinal_ex")]
|
[CCode (cname = "EVP_DecryptFinal_ex")]
|
||||||
public int decrypt_final ([CCode (array_length = false)] uchar[] plaintext, out int plaintext_len);
|
public int decrypt_final ([CCode (array_length = false)] uchar[] plaintext, out int plaintext_len);
|
||||||
|
|
||||||
|
[CCode (simple_generics = true)]
|
||||||
|
public int ctrl<T>(int type, int arg, T? ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -802,4 +805,18 @@ namespace OpenSSL
|
||||||
public int i2d_RSA_PUBKEY (RSA rsa, [CCode (array_length = false)] out uint8[] ppout);
|
public int i2d_RSA_PUBKEY (RSA rsa, [CCode (array_length = false)] out uint8[] ppout);
|
||||||
public int i2d_RSA_PUBKEY_fp (GLib.FileStream fp, RSA a);
|
public int i2d_RSA_PUBKEY_fp (GLib.FileStream fp, RSA a);
|
||||||
public int i2d_RSA_PUBKEY_bio (BIO bp, RSA a);
|
public int i2d_RSA_PUBKEY_bio (BIO bp, RSA a);
|
||||||
|
|
||||||
|
[CCode (cprefix = "ERR_", lower_case_cprefix = "ERR_", cheader_filename = "openssl/err.h")]
|
||||||
|
namespace ERR
|
||||||
|
{
|
||||||
|
public ulong get_error();
|
||||||
|
public unowned string? reason_error_string(ulong e);
|
||||||
|
}
|
||||||
|
|
||||||
|
[CCode (cprefix = "RAND_", lower_case_cprefix = "RAND_", cheader_filename = "openssl/rand.h")]
|
||||||
|
namespace RAND
|
||||||
|
{
|
||||||
|
public int bytes(uint8[] buf);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
36
meson.build
36
meson.build
|
@ -8,6 +8,11 @@ python = import('python')
|
||||||
# plugin_crypto is enabled if any of the crypto plugins is enabled, auto if
|
# plugin_crypto is enabled if any of the crypto plugins is enabled, auto if
|
||||||
# none of them are explicitly enabled but at least one is set to auto, or
|
# none of them are explicitly enabled but at least one is set to auto, or
|
||||||
# disabled if all of them are disabled.
|
# disabled if all of them are disabled.
|
||||||
|
#
|
||||||
|
# On Windows, it's always required because we need it for glib-networking.
|
||||||
|
if host_machine.system() == 'windows'
|
||||||
|
plugin_crypto = true
|
||||||
|
else
|
||||||
plugin_crypto = get_option('plugin-ice')
|
plugin_crypto = get_option('plugin-ice')
|
||||||
foreach plugin : ['plugin-ice', 'plugin-omemo', 'plugin-rtp']
|
foreach plugin : ['plugin-ice', 'plugin-omemo', 'plugin-rtp']
|
||||||
if get_option(plugin).enabled() and not plugin_crypto.enabled()
|
if get_option(plugin).enabled() and not plugin_crypto.enabled()
|
||||||
|
@ -16,12 +21,40 @@ foreach plugin : ['plugin-ice', 'plugin-omemo', 'plugin-rtp']
|
||||||
plugin_crypto = get_option(plugin)
|
plugin_crypto = get_option(plugin)
|
||||||
endif
|
endif
|
||||||
endforeach
|
endforeach
|
||||||
|
endif
|
||||||
|
|
||||||
|
if get_option('crypto-backend') == 'auto'
|
||||||
|
# Prefer libgcrypt/gnutls over openssl because glib-networking is usually
|
||||||
|
# built with gnutls anyway.
|
||||||
|
dep_libgcrypt = dependency('libgcrypt', required: false)
|
||||||
|
dep_gnutls = dependency('gnutls', required: false)
|
||||||
|
if dep_libgcrypt.found() and dep_libgcrypt.found()
|
||||||
|
dep_libgcrypt_or_openssl = dep_libgcrypt
|
||||||
|
dep_gnutls_or_openssl = dep_gnutls
|
||||||
|
crypto_backend = 'gnutls'
|
||||||
|
else
|
||||||
|
dep_openssl = dependency('openssl', disabler: true, required: plugin_crypto)
|
||||||
|
dep_libgcrypt_or_openssl = dep_openssl
|
||||||
|
dep_gnutls_or_openssl = dep_openssl
|
||||||
|
crypto_backend = 'openssl'
|
||||||
|
endif
|
||||||
|
elif get_option('crypto-backend') == 'openssl'
|
||||||
|
dep_openssl = dependency('openssl', disabler: true, required: plugin_crypto)
|
||||||
|
dep_libgcrypt_or_openssl = dep_openssl
|
||||||
|
dep_gnutls_or_openssl = dep_openssl
|
||||||
|
crypto_backend = 'openssl'
|
||||||
|
elif get_option('crypto-backend') == 'gnutls'
|
||||||
|
dep_libgcrypt = dependency('libgcrypt', disabler: true, required: plugin_crypto)
|
||||||
|
dep_gnutls = dependency('gnutls', disabler: true, required: get_option('plugin-ice'))
|
||||||
|
dep_libgcrypt_or_openssl = dep_libgcrypt
|
||||||
|
dep_gnutls_or_openssl = dep_gnutls
|
||||||
|
crypto_backend = 'gnutls'
|
||||||
|
endif
|
||||||
|
|
||||||
dep_gdk_pixbuf = dependency('gdk-pixbuf-2.0')
|
dep_gdk_pixbuf = dependency('gdk-pixbuf-2.0')
|
||||||
dep_gee = dependency('gee-0.8')
|
dep_gee = dependency('gee-0.8')
|
||||||
dep_gio = dependency('gio-2.0')
|
dep_gio = dependency('gio-2.0')
|
||||||
dep_glib = dependency('glib-2.0')
|
dep_glib = dependency('glib-2.0')
|
||||||
dep_gnutls = dependency('gnutls', disabler: true, required: get_option('plugin-ice'))
|
|
||||||
dep_gmodule = dependency('gmodule-2.0')
|
dep_gmodule = dependency('gmodule-2.0')
|
||||||
dep_gpgme = dependency('gpgme', disabler: true, required: get_option('plugin-openpgp'))
|
dep_gpgme = dependency('gpgme', disabler: true, required: get_option('plugin-openpgp'))
|
||||||
dep_gstreamer = dependency('gstreamer-1.0', disabler: true, required: get_option('plugin-rtp'))
|
dep_gstreamer = dependency('gstreamer-1.0', disabler: true, required: get_option('plugin-rtp'))
|
||||||
|
@ -33,7 +66,6 @@ dep_gtk4 = dependency('gtk4')
|
||||||
dep_icu_uc = dependency('icu-uc')
|
dep_icu_uc = dependency('icu-uc')
|
||||||
dep_libadwaita = dependency('libadwaita-1')
|
dep_libadwaita = dependency('libadwaita-1')
|
||||||
dep_libcanberra = dependency('libcanberra', disabler: true, required: get_option('plugin-notification-sound'))
|
dep_libcanberra = dependency('libcanberra', disabler: true, required: get_option('plugin-notification-sound'))
|
||||||
dep_libgcrypt = dependency('libgcrypt', disabler: true, required: plugin_crypto)
|
|
||||||
dep_libqrencode = dependency('libqrencode', disabler: true, required: get_option('plugin-omemo'))
|
dep_libqrencode = dependency('libqrencode', disabler: true, required: get_option('plugin-omemo'))
|
||||||
dep_libsrtp2 = dependency('libsrtp2', disabler: true, required: plugin_crypto)
|
dep_libsrtp2 = dependency('libsrtp2', disabler: true, required: plugin_crypto)
|
||||||
# libsignal-protocol-c has a history of breaking compatibility on the patch level
|
# libsignal-protocol-c has a history of breaking compatibility on the patch level
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
option('plugindir', type: 'string', value: 'lib/dino/plugins', description: 'Dino plugin directory')
|
option('plugindir', type: 'string', value: 'lib/dino/plugins', description: 'Dino plugin directory')
|
||||||
|
|
||||||
|
option('crypto-backend', type: 'combo', choices: ['auto', 'openssl', 'gnutls'], value: 'auto', description: 'Preferred crypto backend')
|
||||||
|
|
||||||
option('plugin-http-files', type: 'feature', description: 'HTTP file upload')
|
option('plugin-http-files', type: 'feature', description: 'HTTP file upload')
|
||||||
option('plugin-ice', type: 'feature', description: '')
|
option('plugin-ice', type: 'feature', description: '')
|
||||||
option('plugin-notification-sound', type: 'feature', description: 'Sound for chat notifications')
|
option('plugin-notification-sound', type: 'feature', description: 'Sound for chat notifications')
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
if crypto_backend == 'openssl'
|
||||||
|
if get_option('plugin-ice').enabled()
|
||||||
|
error('plugin-ice does not work with openssl backend yet')
|
||||||
|
else
|
||||||
|
subdir_done()
|
||||||
|
endif
|
||||||
|
endif
|
||||||
dependencies = [
|
dependencies = [
|
||||||
dep_crypto_vala,
|
dep_crypto_vala,
|
||||||
dep_dino,
|
dep_dino,
|
||||||
|
@ -5,7 +12,7 @@ dependencies = [
|
||||||
dep_gee,
|
dep_gee,
|
||||||
dep_glib,
|
dep_glib,
|
||||||
dep_gmodule,
|
dep_gmodule,
|
||||||
dep_gnutls,
|
dep_gnutls_or_openssl,
|
||||||
dep_nice,
|
dep_nice,
|
||||||
dep_qlite,
|
dep_qlite,
|
||||||
dep_xmpp_vala,
|
dep_xmpp_vala,
|
||||||
|
|
|
@ -90,9 +90,11 @@ GENERATE_VAPI
|
||||||
omemo
|
omemo
|
||||||
GENERATE_HEADER
|
GENERATE_HEADER
|
||||||
omemo
|
omemo
|
||||||
|
DEFINITIONS
|
||||||
|
GCRYPT
|
||||||
)
|
)
|
||||||
|
|
||||||
add_definitions(${VALA_CFLAGS} -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\" -DG_LOG_DOMAIN="OMEMO")
|
add_definitions(${VALA_CFLAGS} -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\" -DG_LOG_DOMAIN="OMEMO" -DGCRYPT)
|
||||||
add_library(omemo SHARED ${OMEMO_VALA_C} ${OMEMO_GRESOURCES_TARGET} ${CMAKE_CURRENT_SOURCE_DIR}/src/signal/signal_helper.c)
|
add_library(omemo SHARED ${OMEMO_VALA_C} ${OMEMO_GRESOURCES_TARGET} ${CMAKE_CURRENT_SOURCE_DIR}/src/signal/signal_helper.c)
|
||||||
add_dependencies(omemo ${GETTEXT_PACKAGE}-translations)
|
add_dependencies(omemo ${GETTEXT_PACKAGE}-translations)
|
||||||
target_include_directories(omemo PUBLIC src)
|
target_include_directories(omemo PUBLIC src)
|
||||||
|
|
|
@ -6,7 +6,7 @@ dependencies = [
|
||||||
dep_glib,
|
dep_glib,
|
||||||
dep_gmodule,
|
dep_gmodule,
|
||||||
dep_gtk4,
|
dep_gtk4,
|
||||||
dep_libgcrypt,
|
dep_libgcrypt_or_openssl,
|
||||||
dep_libqrencode,
|
dep_libqrencode,
|
||||||
dep_libsignal_protocol_c,
|
dep_libsignal_protocol_c,
|
||||||
dep_qlite,
|
dep_qlite,
|
||||||
|
@ -61,8 +61,24 @@ c_args = [
|
||||||
'-DGETTEXT_PACKAGE="dino-omemo"',
|
'-DGETTEXT_PACKAGE="dino-omemo"',
|
||||||
'-DLOCALE_INSTALL_DIR="@0@"'.format(get_option('prefix') / get_option('localedir')),
|
'-DLOCALE_INSTALL_DIR="@0@"'.format(get_option('prefix') / get_option('localedir')),
|
||||||
]
|
]
|
||||||
|
if crypto_backend == 'gnutls'
|
||||||
|
c_args += ['-DGCRYPT']
|
||||||
|
endif
|
||||||
vala_args = [
|
vala_args = [
|
||||||
'--vapidir', meson.current_source_dir() / 'vapi',
|
'--vapidir', meson.current_source_dir() / 'vapi',
|
||||||
]
|
]
|
||||||
|
if crypto_backend == 'gnutls'
|
||||||
|
vala_args += ['-D', 'GCRYPT']
|
||||||
|
endif
|
||||||
lib_omemo = shared_library('omemo', sources, name_prefix: '', c_args: c_args, vala_args: vala_args, include_directories: include_directories('src'), dependencies: dependencies, install: true, install_dir: get_option('libdir') / 'dino/plugins')
|
lib_omemo = shared_library('omemo', sources, name_prefix: '', c_args: c_args, vala_args: vala_args, include_directories: include_directories('src'), dependencies: dependencies, install: true, install_dir: get_option('libdir') / 'dino/plugins')
|
||||||
dep_omemo = declare_dependency(link_with: lib_omemo, include_directories: include_directories('.'))
|
dep_omemo = declare_dependency(link_with: lib_omemo, include_directories: include_directories('.'))
|
||||||
|
|
||||||
|
sources = files(
|
||||||
|
'tests/signal/common.vala',
|
||||||
|
'tests/signal/curve25519.vala',
|
||||||
|
'tests/signal/hkdf.vala',
|
||||||
|
'tests/signal/session_builder.vala',
|
||||||
|
'tests/signal/testcase.vala',
|
||||||
|
)
|
||||||
|
test_omemo = executable('test_omemo', sources, vala_args: vala_args, include_directories: include_directories('src'), dependencies: dependencies + [dep_omemo])
|
||||||
|
test('omemo', test_omemo)
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class OmemoFileDecryptor : FileDecryptor, Object {
|
||||||
file_transfer.encryption = Encryption.OMEMO;
|
file_transfer.encryption = Encryption.OMEMO;
|
||||||
debug("Decrypting file %s from %s", file_transfer.file_name, file_transfer.server_file_name);
|
debug("Decrypting file %s from %s", file_transfer.file_name, file_transfer.server_file_name);
|
||||||
|
|
||||||
SymmetricCipher cipher = new SymmetricCipher("AES-GCM");
|
SymmetricCipher cipher = new SymmetricCipher.decryption("AES-GCM");
|
||||||
cipher.set_key(key);
|
cipher.set_key(key);
|
||||||
cipher.set_iv(iv);
|
cipher.set_iv(iv);
|
||||||
return new ConverterInputStream(encrypted_stream, new SymmetricCipherDecrypter((owned) cipher, 16));
|
return new ConverterInputStream(encrypted_stream, new SymmetricCipherDecrypter((owned) cipher, 16));
|
||||||
|
|
|
@ -31,7 +31,7 @@ public class OmemoFileEncryptor : Dino.FileEncryptor, Object {
|
||||||
uint8[] key = new uint8[KEY_SIZE];
|
uint8[] key = new uint8[KEY_SIZE];
|
||||||
Plugin.get_context().randomize(key);
|
Plugin.get_context().randomize(key);
|
||||||
|
|
||||||
SymmetricCipher cipher = new SymmetricCipher("AES-GCM");
|
SymmetricCipher cipher = new SymmetricCipher.encryption("AES-GCM");
|
||||||
cipher.set_key(key);
|
cipher.set_key(key);
|
||||||
cipher.set_iv(iv);
|
cipher.set_iv(iv);
|
||||||
|
|
||||||
|
|
|
@ -99,13 +99,13 @@ public class AesGcmCipher : Jet.Cipher, Object {
|
||||||
return new Jet.TransportSecret(key, iv);
|
return new Jet.TransportSecret(key, iv);
|
||||||
}
|
}
|
||||||
public InputStream wrap_input_stream(InputStream input, Jet.TransportSecret secret) requires (secret.transport_key.length == key_size) {
|
public InputStream wrap_input_stream(InputStream input, Jet.TransportSecret secret) requires (secret.transport_key.length == key_size) {
|
||||||
SymmetricCipher cipher = new SymmetricCipher("AES-GCM");
|
SymmetricCipher cipher = new SymmetricCipher.decryption("AES-GCM");
|
||||||
cipher.set_key(secret.transport_key);
|
cipher.set_key(secret.transport_key);
|
||||||
cipher.set_iv(secret.initialization_vector);
|
cipher.set_iv(secret.initialization_vector);
|
||||||
return new ConverterInputStream(input, new SymmetricCipherDecrypter((owned) cipher, 16));
|
return new ConverterInputStream(input, new SymmetricCipherDecrypter((owned) cipher, 16));
|
||||||
}
|
}
|
||||||
public OutputStream wrap_output_stream(OutputStream output, Jet.TransportSecret secret) requires (secret.transport_key.length == key_size) {
|
public OutputStream wrap_output_stream(OutputStream output, Jet.TransportSecret secret) requires (secret.transport_key.length == key_size) {
|
||||||
Crypto.SymmetricCipher cipher = new SymmetricCipher("AES-GCM");
|
Crypto.SymmetricCipher cipher = new SymmetricCipher.encryption("AES-GCM");
|
||||||
cipher.set_key(secret.transport_key);
|
cipher.set_key(secret.transport_key);
|
||||||
cipher.set_iv(secret.initialization_vector);
|
cipher.set_iv(secret.initialization_vector);
|
||||||
return new ConverterOutputStream(output, new SymmetricCipherEncrypter((owned) cipher, 16));
|
return new ConverterOutputStream(output, new SymmetricCipherEncrypter((owned) cipher, 16));
|
||||||
|
|
|
@ -12,7 +12,7 @@ public class Bundle {
|
||||||
assert(Plugin.ensure_context());
|
assert(Plugin.ensure_context());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int32 signed_pre_key_id { owned get {
|
public int32 signed_pre_key_id { get {
|
||||||
if (node == null) return -1;
|
if (node == null) return -1;
|
||||||
string? id = ((!)node).get_deep_attribute("signedPreKeyPublic", "signedPreKeyId");
|
string? id = ((!)node).get_deep_attribute("signedPreKeyPublic", "signedPreKeyId");
|
||||||
if (id == null) return -1;
|
if (id == null) return -1;
|
||||||
|
@ -69,7 +69,7 @@ public class Bundle {
|
||||||
this.node = node;
|
this.node = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int32 key_id { owned get {
|
public int32 key_id { get {
|
||||||
return int.parse(node.get_attribute("preKeyId") ?? "-1");
|
return int.parse(node.get_attribute("preKeyId") ?? "-1");
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
namespace Signal {
|
namespace Signal {
|
||||||
|
|
||||||
public class Context {
|
public class Context {
|
||||||
internal NativeContext native_context;
|
public NativeContext native_context;
|
||||||
private RecMutex mutex = RecMutex();
|
private RecMutex mutex = RecMutex();
|
||||||
|
|
||||||
static void locking_function_lock(void* user_data) {
|
static void locking_function_lock(void* user_data) {
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
#include "signal_helper.h"
|
#include "signal_helper.h"
|
||||||
|
|
||||||
|
#ifdef GCRYPT
|
||||||
#include <gcrypt.h>
|
#include <gcrypt.h>
|
||||||
|
#else
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
signal_type_base* signal_type_ref_vapi(void* instance) {
|
signal_type_base* signal_type_ref_vapi(void* instance) {
|
||||||
g_return_val_if_fail(instance != NULL, NULL);
|
g_return_val_if_fail(instance != NULL, NULL);
|
||||||
|
@ -65,16 +70,31 @@ void signal_protocol_address_set_device_id(signal_protocol_address* self, int32_
|
||||||
}
|
}
|
||||||
|
|
||||||
int signal_vala_randomize(uint8_t *data, size_t len) {
|
int signal_vala_randomize(uint8_t *data, size_t len) {
|
||||||
|
#ifdef GCRYPT
|
||||||
gcry_randomize(data, len, GCRY_STRONG_RANDOM);
|
gcry_randomize(data, len, GCRY_STRONG_RANDOM);
|
||||||
return SG_SUCCESS;
|
return SG_SUCCESS;
|
||||||
|
#else
|
||||||
|
return RAND_bytes(data, len) == 1 ? SG_SUCCESS : SG_ERR_UNKNOWN;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int signal_vala_random_generator(uint8_t *data, size_t len, void *user_data) {
|
int signal_vala_random_generator(uint8_t *data, size_t len, void *user_data) {
|
||||||
|
#ifdef GCRYPT
|
||||||
gcry_randomize(data, len, GCRY_STRONG_RANDOM);
|
gcry_randomize(data, len, GCRY_STRONG_RANDOM);
|
||||||
return SG_SUCCESS;
|
return SG_SUCCESS;
|
||||||
|
#else
|
||||||
|
return RAND_bytes(data, len) == 1 ? SG_SUCCESS : SG_ERR_UNKNOWN;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef GCRYPT
|
||||||
|
struct SIGNAL_VALA_HMAC_CTX {
|
||||||
|
EVP_PKEY *pkey;
|
||||||
|
EVP_MD_CTX *ctx;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
int signal_vala_hmac_sha256_init(void **hmac_context, const uint8_t *key, size_t key_len, void *user_data) {
|
int signal_vala_hmac_sha256_init(void **hmac_context, const uint8_t *key, size_t key_len, void *user_data) {
|
||||||
|
#ifdef GCRYPT
|
||||||
gcry_mac_hd_t* ctx = malloc(sizeof(gcry_mac_hd_t));
|
gcry_mac_hd_t* ctx = malloc(sizeof(gcry_mac_hd_t));
|
||||||
if (!ctx) return SG_ERR_NOMEM;
|
if (!ctx) return SG_ERR_NOMEM;
|
||||||
|
|
||||||
|
@ -91,17 +111,49 @@ int signal_vala_hmac_sha256_init(void **hmac_context, const uint8_t *key, size_t
|
||||||
*hmac_context = ctx;
|
*hmac_context = ctx;
|
||||||
|
|
||||||
return SG_SUCCESS;
|
return SG_SUCCESS;
|
||||||
|
#else
|
||||||
|
EVP_PKEY *pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL, key, key_len);
|
||||||
|
if (!pkey) {
|
||||||
|
return SG_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
|
||||||
|
if (!ctx) {
|
||||||
|
EVP_PKEY_free(pkey);
|
||||||
|
return SG_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
if (EVP_DigestSignInit(ctx, NULL, EVP_sha256(), NULL, pkey) != 1) {
|
||||||
|
EVP_MD_CTX_free(ctx);
|
||||||
|
EVP_PKEY_free(pkey);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SIGNAL_VALA_HMAC_CTX *hmac_ctx = malloc(sizeof(*hmac_ctx));
|
||||||
|
hmac_ctx->pkey = pkey;
|
||||||
|
hmac_ctx->ctx = ctx;
|
||||||
|
*hmac_context = hmac_ctx;
|
||||||
|
|
||||||
|
return SG_SUCCESS;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int signal_vala_hmac_sha256_update(void *hmac_context, const uint8_t *data, size_t data_len, void *user_data) {
|
int signal_vala_hmac_sha256_update(void *hmac_context, const uint8_t *data, size_t data_len, void *user_data) {
|
||||||
|
#ifdef GCRYPT
|
||||||
gcry_mac_hd_t* ctx = hmac_context;
|
gcry_mac_hd_t* ctx = hmac_context;
|
||||||
|
|
||||||
if (gcry_mac_write(*ctx, data, data_len)) return SG_ERR_UNKNOWN;
|
if (gcry_mac_write(*ctx, data, data_len)) return SG_ERR_UNKNOWN;
|
||||||
|
|
||||||
return SG_SUCCESS;
|
return SG_SUCCESS;
|
||||||
|
#else
|
||||||
|
struct SIGNAL_VALA_HMAC_CTX *hmac_ctx = hmac_context;
|
||||||
|
if (EVP_DigestSignUpdate(hmac_ctx->ctx, data, data_len) != 1) {
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
return SG_SUCCESS;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int signal_vala_hmac_sha256_final(void *hmac_context, signal_buffer **output, void *user_data) {
|
int signal_vala_hmac_sha256_final(void *hmac_context, signal_buffer **output, void *user_data) {
|
||||||
|
#ifdef GCRYPT
|
||||||
size_t len = gcry_mac_get_algo_maclen(GCRY_MAC_HMAC_SHA256);
|
size_t len = gcry_mac_get_algo_maclen(GCRY_MAC_HMAC_SHA256);
|
||||||
uint8_t md[len];
|
uint8_t md[len];
|
||||||
gcry_mac_hd_t* ctx = hmac_context;
|
gcry_mac_hd_t* ctx = hmac_context;
|
||||||
|
@ -114,17 +166,49 @@ int signal_vala_hmac_sha256_final(void *hmac_context, signal_buffer **output, vo
|
||||||
*output = output_buffer;
|
*output = output_buffer;
|
||||||
|
|
||||||
return SG_SUCCESS;
|
return SG_SUCCESS;
|
||||||
|
#else
|
||||||
|
size_t len;
|
||||||
|
struct SIGNAL_VALA_HMAC_CTX *hmac_ctx = hmac_context;
|
||||||
|
if (EVP_DigestSignFinal(hmac_ctx->ctx, NULL, &len) != 1) {
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
signal_buffer *output_buffer = signal_buffer_alloc(len);
|
||||||
|
if (!output_buffer) {
|
||||||
|
return SG_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
size_t another_len = len;
|
||||||
|
if (EVP_DigestSignFinal(hmac_ctx->ctx, signal_buffer_data(output_buffer), &another_len) != 1) {
|
||||||
|
signal_buffer_free(output_buffer);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
if (another_len != len) {
|
||||||
|
signal_buffer_free(output_buffer);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
*output = output_buffer;
|
||||||
|
return SG_SUCCESS;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void signal_vala_hmac_sha256_cleanup(void *hmac_context, void *user_data) {
|
void signal_vala_hmac_sha256_cleanup(void *hmac_context, void *user_data) {
|
||||||
|
#ifdef GCRYPT
|
||||||
gcry_mac_hd_t* ctx = hmac_context;
|
gcry_mac_hd_t* ctx = hmac_context;
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
gcry_mac_close(*ctx);
|
gcry_mac_close(*ctx);
|
||||||
free(ctx);
|
free(ctx);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
struct SIGNAL_VALA_HMAC_CTX *hmac_ctx = hmac_context;
|
||||||
|
if (hmac_ctx) {
|
||||||
|
EVP_MD_CTX_free(hmac_ctx->ctx);
|
||||||
|
EVP_PKEY_free(hmac_ctx->pkey);
|
||||||
|
free(hmac_ctx);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int signal_vala_sha512_digest_init(void **digest_context, void *user_data) {
|
int signal_vala_sha512_digest_init(void **digest_context, void *user_data) {
|
||||||
|
#ifdef GCRYPT
|
||||||
gcry_md_hd_t* ctx = malloc(sizeof(gcry_mac_hd_t));
|
gcry_md_hd_t* ctx = malloc(sizeof(gcry_mac_hd_t));
|
||||||
if (!ctx) return SG_ERR_NOMEM;
|
if (!ctx) return SG_ERR_NOMEM;
|
||||||
|
|
||||||
|
@ -136,17 +220,38 @@ int signal_vala_sha512_digest_init(void **digest_context, void *user_data) {
|
||||||
*digest_context = ctx;
|
*digest_context = ctx;
|
||||||
|
|
||||||
return SG_SUCCESS;
|
return SG_SUCCESS;
|
||||||
|
#else
|
||||||
|
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
|
||||||
|
if (!ctx) {
|
||||||
|
return SG_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
if (EVP_DigestInit_ex(ctx, EVP_sha512(), NULL) != 1) {
|
||||||
|
EVP_MD_CTX_free(ctx);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
*digest_context = ctx;
|
||||||
|
return SG_SUCCESS;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int signal_vala_sha512_digest_update(void *digest_context, const uint8_t *data, size_t data_len, void *user_data) {
|
int signal_vala_sha512_digest_update(void *digest_context, const uint8_t *data, size_t data_len, void *user_data) {
|
||||||
|
#ifdef GCRYPT
|
||||||
gcry_md_hd_t* ctx = digest_context;
|
gcry_md_hd_t* ctx = digest_context;
|
||||||
|
|
||||||
gcry_md_write(*ctx, data, data_len);
|
gcry_md_write(*ctx, data, data_len);
|
||||||
|
|
||||||
return SG_SUCCESS;
|
return SG_SUCCESS;
|
||||||
|
#else
|
||||||
|
EVP_MD_CTX *ctx = digest_context;
|
||||||
|
if (EVP_DigestUpdate(ctx, data, data_len) != 1) {
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
return SG_SUCCESS;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int signal_vala_sha512_digest_final(void *digest_context, signal_buffer **output, void *user_data) {
|
int signal_vala_sha512_digest_final(void *digest_context, signal_buffer **output, void *user_data) {
|
||||||
|
#ifdef GCRYPT
|
||||||
size_t len = gcry_md_get_algo_dlen(GCRY_MD_SHA512);
|
size_t len = gcry_md_get_algo_dlen(GCRY_MD_SHA512);
|
||||||
gcry_md_hd_t* ctx = digest_context;
|
gcry_md_hd_t* ctx = digest_context;
|
||||||
|
|
||||||
|
@ -162,17 +267,43 @@ int signal_vala_sha512_digest_final(void *digest_context, signal_buffer **output
|
||||||
*output = output_buffer;
|
*output = output_buffer;
|
||||||
|
|
||||||
return SG_SUCCESS;
|
return SG_SUCCESS;
|
||||||
|
#else
|
||||||
|
EVP_MD_CTX *ctx = digest_context;
|
||||||
|
size_t len = EVP_MD_size(EVP_sha512());
|
||||||
|
signal_buffer *output_buffer = signal_buffer_alloc(len);
|
||||||
|
if (!output_buffer) {
|
||||||
|
return SG_ERR_NOMEM;
|
||||||
|
}
|
||||||
|
if (EVP_DigestSignFinal(ctx, signal_buffer_data(output_buffer), &len) != 1) {
|
||||||
|
signal_buffer_free(output_buffer);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
if (len != EVP_MD_size(EVP_sha512())) {
|
||||||
|
signal_buffer_free(output_buffer);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
*output = output_buffer;
|
||||||
|
return SG_SUCCESS;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void signal_vala_sha512_digest_cleanup(void *digest_context, void *user_data) {
|
void signal_vala_sha512_digest_cleanup(void *digest_context, void *user_data) {
|
||||||
|
#ifdef GCRYPT
|
||||||
gcry_md_hd_t* ctx = digest_context;
|
gcry_md_hd_t* ctx = digest_context;
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
gcry_md_close(*ctx);
|
gcry_md_close(*ctx);
|
||||||
free(ctx);
|
free(ctx);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
EVP_MD_CTX *ctx = digest_context;
|
||||||
|
if (ctx) {
|
||||||
|
EVP_MD_CTX_free(ctx);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
const int aes_cipher(int cipher, size_t key_len, int* algo, int* mode) {
|
#ifdef GCRYPT
|
||||||
|
static int aes_cipher(int cipher, size_t key_len, int* algo, int* mode) {
|
||||||
switch (key_len) {
|
switch (key_len) {
|
||||||
case 16:
|
case 16:
|
||||||
*algo = GCRY_CIPHER_AES128;
|
*algo = GCRY_CIPHER_AES128;
|
||||||
|
@ -201,6 +332,35 @@ const int aes_cipher(int cipher, size_t key_len, int* algo, int* mode) {
|
||||||
}
|
}
|
||||||
return SG_SUCCESS;
|
return SG_SUCCESS;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
static const EVP_CIPHER *aes_cipher(int cipher, size_t key_len) {
|
||||||
|
switch (cipher) {
|
||||||
|
case SG_CIPHER_AES_CBC_PKCS5:
|
||||||
|
switch (key_len) {
|
||||||
|
case 16: return EVP_aes_128_cbc();
|
||||||
|
case 24: return EVP_aes_192_cbc();
|
||||||
|
case 32: return EVP_aes_256_cbc();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SG_CIPHER_AES_CTR_NOPADDING:
|
||||||
|
switch (key_len) {
|
||||||
|
case 16: return EVP_aes_128_ctr();
|
||||||
|
case 24: return EVP_aes_192_ctr();
|
||||||
|
case 32: return EVP_aes_256_ctr();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SG_CIPHER_AES_GCM_NOPADDING:
|
||||||
|
switch (key_len) {
|
||||||
|
case 16: return EVP_aes_128_gcm();
|
||||||
|
case 24: return EVP_aes_192_gcm();
|
||||||
|
case 32: return EVP_aes_256_gcm();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int signal_vala_encrypt(signal_buffer **output,
|
int signal_vala_encrypt(signal_buffer **output,
|
||||||
int cipher,
|
int cipher,
|
||||||
|
@ -208,6 +368,7 @@ int signal_vala_encrypt(signal_buffer **output,
|
||||||
const uint8_t *iv, size_t iv_len,
|
const uint8_t *iv, size_t iv_len,
|
||||||
const uint8_t *plaintext, size_t plaintext_len,
|
const uint8_t *plaintext, size_t plaintext_len,
|
||||||
void *user_data) {
|
void *user_data) {
|
||||||
|
#ifdef GCRYPT
|
||||||
int algo, mode, error_code = SG_ERR_UNKNOWN;
|
int algo, mode, error_code = SG_ERR_UNKNOWN;
|
||||||
if (aes_cipher(cipher, key_len, &algo, &mode)) return SG_ERR_INVAL;
|
if (aes_cipher(cipher, key_len, &algo, &mode)) return SG_ERR_INVAL;
|
||||||
|
|
||||||
|
@ -279,6 +440,97 @@ no_error:
|
||||||
|
|
||||||
gcry_cipher_close(ctx);
|
gcry_cipher_close(ctx);
|
||||||
return SG_SUCCESS;
|
return SG_SUCCESS;
|
||||||
|
#else
|
||||||
|
int result = 0;
|
||||||
|
uint8_t *out_buf = NULL;
|
||||||
|
const EVP_CIPHER *evp_cipher = aes_cipher(cipher, key_len);
|
||||||
|
if (!evp_cipher) {
|
||||||
|
// fprintf(stderr, "invalid AES mode or key size: %zu\n", key_len);
|
||||||
|
return SG_ERR_INVAL;
|
||||||
|
}
|
||||||
|
if (plaintext_len > INT_MAX - EVP_CIPHER_block_size(evp_cipher)) {
|
||||||
|
// fprintf(stderr, "invalid plaintext length: %zu\n", plaintext_len);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||||||
|
int buf_extra = 0;
|
||||||
|
|
||||||
|
if (cipher == SG_CIPHER_AES_GCM_NOPADDING) {
|
||||||
|
// In GCM mode we use the last 16 bytes as auth tag
|
||||||
|
buf_extra += 16;
|
||||||
|
|
||||||
|
if (EVP_EncryptInit_ex(ctx, evp_cipher, NULL, NULL, NULL) != 1) {
|
||||||
|
// fprintf(stderr, "cannot initialize cipher\n");
|
||||||
|
result = SG_ERR_UNKNOWN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv_len, NULL) != 1) {
|
||||||
|
// fprintf(stderr, "cannot set iv size\n");
|
||||||
|
result = SG_ERR_UNKNOWN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv) != 1) {
|
||||||
|
// fprintf(stderr, "cannot set key/iv\n");
|
||||||
|
result = SG_ERR_UNKNOWN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO: set ivlen?
|
||||||
|
if (EVP_EncryptInit_ex(ctx, evp_cipher, 0, key, iv) != 1) {
|
||||||
|
// fprintf(stderr, "cannot initialize cipher\n");
|
||||||
|
result = SG_ERR_UNKNOWN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cipher == SG_CIPHER_AES_CTR_NOPADDING || cipher == SG_CIPHER_AES_GCM_NOPADDING) {
|
||||||
|
if (EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) {
|
||||||
|
// fprintf(stderr, "cannot set padding\n");
|
||||||
|
result = SG_ERR_UNKNOWN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out_buf = malloc(plaintext_len + EVP_CIPHER_block_size(evp_cipher) + buf_extra);
|
||||||
|
if (!out_buf) {
|
||||||
|
// fprintf(stderr, "cannot allocate output buffer\n");
|
||||||
|
result = SG_ERR_NOMEM;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
int out_len = 0;
|
||||||
|
if (EVP_EncryptUpdate(ctx, out_buf, &out_len, plaintext, plaintext_len) != 1) {
|
||||||
|
// fprintf(stderr, "cannot encrypt plaintext\n");
|
||||||
|
result = SG_ERR_UNKNOWN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
int final_len = 0;
|
||||||
|
if (EVP_EncryptFinal_ex(ctx, out_buf + out_len, &final_len) != 1) {
|
||||||
|
// fprintf(stderr, "cannot finish encrypting plaintext\n");
|
||||||
|
result = SG_ERR_UNKNOWN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cipher == SG_CIPHER_AES_GCM_NOPADDING) {
|
||||||
|
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, out_buf + out_len + final_len) != 1) {
|
||||||
|
// fprintf(stderr, "cannot get tag\n");
|
||||||
|
result = SG_ERR_UNKNOWN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*output = signal_buffer_create(out_buf, out_len + final_len + buf_extra);
|
||||||
|
|
||||||
|
complete:
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
if (out_buf) {
|
||||||
|
free(out_buf);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int signal_vala_decrypt(signal_buffer **output,
|
int signal_vala_decrypt(signal_buffer **output,
|
||||||
|
@ -287,6 +539,7 @@ int signal_vala_decrypt(signal_buffer **output,
|
||||||
const uint8_t *iv, size_t iv_len,
|
const uint8_t *iv, size_t iv_len,
|
||||||
const uint8_t *ciphertext, size_t ciphertext_len,
|
const uint8_t *ciphertext, size_t ciphertext_len,
|
||||||
void *user_data) {
|
void *user_data) {
|
||||||
|
#ifdef GCRYPT
|
||||||
int algo, mode, error_code = SG_ERR_UNKNOWN;
|
int algo, mode, error_code = SG_ERR_UNKNOWN;
|
||||||
*output = 0;
|
*output = 0;
|
||||||
if (aes_cipher(cipher, key_len, &algo, &mode)) return SG_ERR_INVAL;
|
if (aes_cipher(cipher, key_len, &algo, &mode)) return SG_ERR_INVAL;
|
||||||
|
@ -352,11 +605,104 @@ no_error:
|
||||||
|
|
||||||
gcry_cipher_close(ctx);
|
gcry_cipher_close(ctx);
|
||||||
return SG_SUCCESS;
|
return SG_SUCCESS;
|
||||||
|
#else
|
||||||
|
int result = 0;
|
||||||
|
uint8_t *out_buf = NULL;
|
||||||
|
const EVP_CIPHER *evp_cipher = aes_cipher(cipher, key_len);
|
||||||
|
if (!evp_cipher) {
|
||||||
|
// fprintf(stderr, "invalid AES mode or key size: %zu\n", key_len);
|
||||||
|
return SG_ERR_INVAL;
|
||||||
|
}
|
||||||
|
if (ciphertext_len > INT_MAX - EVP_CIPHER_block_size(evp_cipher)) {
|
||||||
|
// fprintf(stderr, "invalid ciphertext length: %zu\n", ciphertext_len);
|
||||||
|
return SG_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||||||
|
|
||||||
|
if (cipher == SG_CIPHER_AES_GCM_NOPADDING) {
|
||||||
|
// In GCM mode we use the last 16 bytes as auth tag
|
||||||
|
ciphertext_len -= 16;
|
||||||
|
|
||||||
|
if (EVP_DecryptInit_ex(ctx, evp_cipher, NULL, NULL, NULL) != 1) {
|
||||||
|
// fprintf(stderr, "cannot initialize cipher\n");
|
||||||
|
result = SG_ERR_UNKNOWN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv_len, NULL) != 1) {
|
||||||
|
// fprintf(stderr, "cannot set iv size\n");
|
||||||
|
result = SG_ERR_UNKNOWN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv) != 1) {
|
||||||
|
// fprintf(stderr, "cannot set key/iv\n");
|
||||||
|
result = SG_ERR_UNKNOWN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO: set ivlen?
|
||||||
|
if (EVP_DecryptInit_ex(ctx, evp_cipher, 0, key, iv) != 1) {
|
||||||
|
// fprintf(stderr, "cannot initialize cipher\n");
|
||||||
|
result = SG_ERR_UNKNOWN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cipher == SG_CIPHER_AES_CTR_NOPADDING || cipher == SG_CIPHER_AES_GCM_NOPADDING) {
|
||||||
|
if (EVP_CIPHER_CTX_set_padding(ctx, 0) != 1) {
|
||||||
|
// fprintf(stderr, "cannot set padding\n");
|
||||||
|
result = SG_ERR_UNKNOWN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out_buf = malloc(ciphertext_len + EVP_CIPHER_block_size(evp_cipher));
|
||||||
|
if (!out_buf) {
|
||||||
|
// fprintf(stderr, "cannot allocate output buffer\n");
|
||||||
|
result = SG_ERR_NOMEM;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
int out_len = 0;
|
||||||
|
if (EVP_DecryptUpdate(ctx, out_buf, &out_len, ciphertext, ciphertext_len) != 1) {
|
||||||
|
// fprintf(stderr, "cannot decrypt ciphertext\n");
|
||||||
|
result = SG_ERR_UNKNOWN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cipher == SG_CIPHER_AES_GCM_NOPADDING) {
|
||||||
|
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (void *)(ciphertext + ciphertext_len)) != 1) {
|
||||||
|
// fprintf(stderr, "cannot set tag\n");
|
||||||
|
result = SG_ERR_UNKNOWN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int final_len = 0;
|
||||||
|
if (EVP_DecryptFinal_ex(ctx, out_buf + out_len, &final_len) != 1) {
|
||||||
|
// fprintf(stderr, "cannot finish decrypting ciphertexts\n");
|
||||||
|
result = SG_ERR_UNKNOWN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
*output = signal_buffer_create(out_buf, out_len + final_len);
|
||||||
|
|
||||||
|
complete:
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
if (out_buf) {
|
||||||
|
free(out_buf);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void setup_signal_vala_crypto_provider(signal_context *context)
|
void setup_signal_vala_crypto_provider(signal_context *context)
|
||||||
{
|
{
|
||||||
|
#ifdef GCRYPT
|
||||||
gcry_check_version(NULL);
|
gcry_check_version(NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
signal_crypto_provider provider = {
|
signal_crypto_provider provider = {
|
||||||
.random_func = signal_vala_random_generator,
|
.random_func = signal_vala_random_generator,
|
||||||
|
|
|
@ -107,7 +107,7 @@ public class Store : Object {
|
||||||
public PreKeyStore pre_key_store { get; set; default = new SimplePreKeyStore(); }
|
public PreKeyStore pre_key_store { get; set; default = new SimplePreKeyStore(); }
|
||||||
public SignedPreKeyStore signed_pre_key_store { get; set; default = new SimpleSignedPreKeyStore(); }
|
public SignedPreKeyStore signed_pre_key_store { get; set; default = new SimpleSignedPreKeyStore(); }
|
||||||
public uint32 local_registration_id { get { return identity_key_store.local_registration_id; } }
|
public uint32 local_registration_id { get { return identity_key_store.local_registration_id; } }
|
||||||
internal NativeStoreContext native_context {get { return native_store_context_; }}
|
public NativeStoreContext native_context {get { return native_store_context_; }}
|
||||||
private NativeStoreContext native_store_context_;
|
private NativeStoreContext native_store_context_;
|
||||||
|
|
||||||
static int iks_get_identity_key_pair(out Buffer public_data, out Buffer private_data, void* user_data) {
|
static int iks_get_identity_key_pair(out Buffer public_data, out Buffer private_data, void* user_data) {
|
||||||
|
|
0
plugins/omemo/vapi/openssl.vapi
Normal file
0
plugins/omemo/vapi/openssl.vapi
Normal file
Loading…
Reference in a new issue