Skip to content

Commit

Permalink
Allow using OpenSSL instead of GnuTLS
Browse files Browse the repository at this point in the history
In preparation of Windows support.
  • Loading branch information
hrxi committed Nov 24, 2023
1 parent 87917c3 commit d8703c9
Show file tree
Hide file tree
Showing 19 changed files with 615 additions and 27 deletions.
2 changes: 2 additions & 0 deletions crypto-vala/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ GENERATE_VAPI
crypto-vala
GENERATE_HEADER
crypto-vala
DEFINITIONS
GCRYPT
)

add_custom_target(crypto-vala-vapi
Expand Down
7 changes: 6 additions & 1 deletion crypto-vala/meson.build
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
dependencies = [
dep_gio,
dep_glib,
dep_libgcrypt,
dep_libgcrypt_or_openssl,
dep_libsrtp2,
]
sources = files(
Expand All @@ -17,6 +17,11 @@ c_args = [
vala_args = [
'--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])
dep_crypto_vala = declare_dependency(link_with: lib_crypto_vala, include_directories: include_directories('.'))

Expand Down
149 changes: 148 additions & 1 deletion crypto-vala/src/cipher.vala
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
namespace Crypto {
public class SymmetricCipher {
#if GCRYPT
private GCrypt.Cipher.Cipher cipher;
#else
bool is_encryption;
private OpenSSL.EVP.CipherContext? cipher;
#endif

public static bool supports(string algo_name) {
#if GCRYPT
GCrypt.Cipher.Algorithm algo;
GCrypt.Cipher.Mode mode;
GCrypt.Cipher.Flag 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) {
switch (mode) {
case GCrypt.Cipher.Mode.ECB: return "ECB";
Expand Down Expand Up @@ -95,8 +105,18 @@ public class SymmetricCipher {
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.Mode mode;
GCrypt.Cipher.Flag flags;
Expand All @@ -105,48 +125,175 @@ public class SymmetricCipher {
} else {
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 {
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 {
#if GCRYPT
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 {
#if GCRYPT
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) {
if (cipher.encrypt_init(null, null, null, iv) != 1) {
openssl_error();
}
} else {
if (cipher.decrypt_init(null, null, null, iv) != 1) {
openssl_error();
}
}
#endif
}

/*
public void set_counter_vector(uint8[] ctr) throws Error {
#if GCRYPT
may_throw_gcrypt_error(cipher.set_counter_vector(ctr));
#else
#endif
}
*/

public void reset() throws Error {
#if GCRYPT
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 {
uint8[] tag = new uint8[taglen];
#if GCRYPT
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;
}

public void check_tag(uint8[] tag) throws Error {
#if GCRYPT
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 {
#if GCRYPT
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 {
#if GCRYPT
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;
if (cipher.decrypt_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 sync() throws Error {
#if GCRYPT
may_throw_gcrypt_error(cipher.sync());
#else
#endif
}
*/
}
}
9 changes: 8 additions & 1 deletion crypto-vala/src/error.vala
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ namespace Crypto {
public errordomain Error {
ILLEGAL_ARGUMENTS,
GCRYPT,
OPENSSL,
AUTHENTICATION_FAILED,
UNKNOWN
}

#if GCRYPT
internal void may_throw_gcrypt_error(GCrypt.Error e) throws Error {
if (((int)e) != 0) {
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
}
6 changes: 5 additions & 1 deletion crypto-vala/src/random.vala
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
namespace Crypto {
public static void randomize(uint8[] buffer) {
#if GCRYPT
GCrypt.Random.randomize(buffer);
#else
OpenSSL.RAND.bytes(buffer);
#endif
}
}
}
21 changes: 19 additions & 2 deletions crypto-vala/vapi/openssl.vapi
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ namespace OpenSSL
public int set_padding (int pad);

[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")]
public int encrypt_update ([CCode (array_length = false)] uchar[] ciphertext, out int ciphertext_len, uchar[] plaintext);
Expand All @@ -678,13 +678,16 @@ namespace OpenSSL
public int encrypt_final ([CCode (array_length = false)] uchar[] ciphertext, out int ciphertext_len);

[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")]
public int decrypt_update ([CCode (array_length = false)] uchar[] plaintext, out int plaintext_len, uchar[] ciphertext);

[CCode (cname = "EVP_DecryptFinal_ex")]
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);
}
}

Expand Down Expand Up @@ -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_fp (GLib.FileStream fp, 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);
}

}
Loading

0 comments on commit d8703c9

Please sign in to comment.