diff options
| author | hrxi <hrrrxi@gmail.com> | 2023-11-24 20:53:50 +0100 |
|---|---|---|
| committer | Marvin W <git@larma.de> | 2026-10-23 16:58:30 +0200 |
| commit | 76a91fca2fc7dd5975e8ff0e6695f27c98539dba (patch) | |
| tree | 7829340be71fe60ef847247e578c4589571fecc5 /crypto-vala | |
| parent | b013d28182ce582f229185c23b6011f566f1f8cc (diff) | |
Add support for OpenSSL
Uses work by @hrxi in https://github.com/dino/dino/pull/1515
Co-Authored-By: hrxi <hrrrxi@gmail.com>
Diffstat (limited to 'crypto-vala')
| -rw-r--r-- | crypto-vala/meson.build | 15 | ||||
| -rw-r--r-- | crypto-vala/src/cipher.vala | 159 | ||||
| -rw-r--r-- | crypto-vala/src/cipher_converter.vala | 50 | ||||
| -rw-r--r-- | crypto-vala/src/cipher_gcrypt.vala | 137 | ||||
| -rw-r--r-- | crypto-vala/src/cipher_openssl.vala | 156 | ||||
| -rw-r--r-- | crypto-vala/src/error.vala | 6 | ||||
| -rw-r--r-- | crypto-vala/src/gcrypt.vala | 21 | ||||
| -rw-r--r-- | crypto-vala/src/openssl.vala | 19 | ||||
| -rw-r--r-- | crypto-vala/src/random.vala | 5 | ||||
| -rw-r--r-- | crypto-vala/vapi/openssl.vapi | 322 |
10 files changed, 711 insertions, 179 deletions
diff --git a/crypto-vala/meson.build b/crypto-vala/meson.build index fdd08f87..bc66fbf9 100644 --- a/crypto-vala/meson.build +++ b/crypto-vala/meson.build @@ -1,14 +1,12 @@ dependencies = [ dep_gio, dep_glib, - dep_libgcrypt, dep_libsrtp2, ] sources = files( 'src/cipher.vala', 'src/cipher_converter.vala', 'src/error.vala', - 'src/random.vala', 'src/srtp.vala', ) c_args = vala_c_args + [ @@ -17,6 +15,19 @@ c_args = vala_c_args + [ vala_args = [ '--vapidir', meson.current_source_dir() / 'vapi', ] +if crypto_backend == 'gnutls' + dependencies += [ dep_libgcrypt ] + sources += files( + 'src/gcrypt.vala', + 'src/cipher_gcrypt.vala', + ) +else # openssl + dependencies += [ dep_openssl ] + sources += files( + 'src/openssl.vala', + 'src/cipher_openssl.vala', + ) +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], install_rpath: default_install_rpath) dep_crypto_vala = declare_dependency(link_with: lib_crypto_vala, include_directories: include_directories('.')) diff --git a/crypto-vala/src/cipher.vala b/crypto-vala/src/cipher.vala index 306dafa8..f7aee100 100644 --- a/crypto-vala/src/cipher.vala +++ b/crypto-vala/src/cipher.vala @@ -1,152 +1,19 @@ namespace Crypto { -public class SymmetricCipher { - private GCrypt.Cipher.Cipher cipher; - public static bool supports(string algo_name) { - GCrypt.Cipher.Algorithm algo; - GCrypt.Cipher.Mode mode; - GCrypt.Cipher.Flag flags; - return parse(algo_name, out algo, out mode, out flags); - } - - private static unowned string mode_to_string(GCrypt.Cipher.Mode mode) { - switch (mode) { - case GCrypt.Cipher.Mode.ECB: return "ECB"; - case GCrypt.Cipher.Mode.CFB: return "CFB"; - case GCrypt.Cipher.Mode.CBC: return "CBC"; - case GCrypt.Cipher.Mode.STREAM: return "STREAM"; - case GCrypt.Cipher.Mode.OFB: return "OFB"; - case GCrypt.Cipher.Mode.CTR: return "CTR"; - case GCrypt.Cipher.Mode.AESWRAP: return "AESWRAP"; - case GCrypt.Cipher.Mode.GCM: return "GCM"; - case GCrypt.Cipher.Mode.POLY1305: return "POLY1305"; - case GCrypt.Cipher.Mode.OCB: return "OCB"; - case GCrypt.Cipher.Mode.CFB8: return "CFB8"; - // case GCrypt.Cipher.Mode.XTS: return "XTS"; // Not supported in gcrypt < 1.8 - } - return "NONE"; - } - - private static GCrypt.Cipher.Mode mode_from_string(string name) { - switch (name) { - case "ECB": return GCrypt.Cipher.Mode.ECB; - case "CFB": return GCrypt.Cipher.Mode.CFB; - case "CBC": return GCrypt.Cipher.Mode.CBC; - case "STREAM": return GCrypt.Cipher.Mode.STREAM; - case "OFB": return GCrypt.Cipher.Mode.OFB; - case "CTR": return GCrypt.Cipher.Mode.CTR; - case "AESWRAP": return GCrypt.Cipher.Mode.AESWRAP; - case "GCM": return GCrypt.Cipher.Mode.GCM; - case "POLY1305": return GCrypt.Cipher.Mode.POLY1305; - case "OCB": return GCrypt.Cipher.Mode.OCB; - case "CFB8": return GCrypt.Cipher.Mode.CFB8; - // case "XTS": return GCrypt.Cipher.Mode.XTS; // Not supported in gcrypt < 1.8 - } - return GCrypt.Cipher.Mode.NONE; - } - - private static string flags_to_string(GCrypt.Cipher.Flag flags) { - string? s = null; - if ((GCrypt.Cipher.Flag.CBC_MAC & flags) != 0) s = (s == null ? "" : @"$s-") + "MAC"; - if ((GCrypt.Cipher.Flag.CBC_CTS & flags) != 0) s = (s == null ? "" : @"$s-") + "CTS"; - if ((GCrypt.Cipher.Flag.ENABLE_SYNC & flags) != 0) s = (s == null ? "" : @"$s-") + "SYNC"; - if ((GCrypt.Cipher.Flag.SECURE & flags) != 0) s = (s == null ? "" : @"$s-") + "SECURE"; - return s ?? "NONE"; - } - - private static GCrypt.Cipher.Flag flag_from_string(string flag_name) { - if (flag_name == "SECURE") return GCrypt.Cipher.Flag.SECURE; - if (flag_name == "SYNC") return GCrypt.Cipher.Flag.ENABLE_SYNC; - if (flag_name == "CTS") return GCrypt.Cipher.Flag.CBC_CTS; - if (flag_name == "MAC") return GCrypt.Cipher.Flag.CBC_MAC; - return 0; - } - - private static GCrypt.Cipher.Flag flags_from_string(string flag_names) { - GCrypt.Cipher.Flag flags = 0; - foreach(string flag in flag_names.split("-")) { - flags |= flag_from_string(flag); - } - return flags; - } - - private static bool parse(string algo_name, out GCrypt.Cipher.Algorithm algo, out GCrypt.Cipher.Mode mode, out GCrypt.Cipher.Flag flags) { - algo = GCrypt.Cipher.Algorithm.NONE; - mode = GCrypt.Cipher.Mode.NONE; - flags = 0; - string[] algo_parts = algo_name.split("-", 3); - - algo = GCrypt.Cipher.Algorithm.from_string(algo_parts[0]); - if (algo_parts.length >= 2) { - mode = mode_from_string(algo_parts[1]); - } - if (algo_parts.length == 3) { - flags |= flags_from_string(algo_parts[2]); - } - return to_algo_name(algo, mode, flags) == algo_name; - } - - private static string to_algo_name(GCrypt.Cipher.Algorithm algo = GCrypt.Cipher.Algorithm.NONE, GCrypt.Cipher.Mode mode = GCrypt.Cipher.Mode.NONE, GCrypt.Cipher.Flag flags = 0) { - if (flags != 0) { - return @"$algo-$(mode_to_string(mode))-$(flags_to_string(flags))"; - } else if (mode != GCrypt.Cipher.Mode.NONE) { - return @"$algo-$(mode_to_string(mode))"; - } else { - return algo.to_string(); - } - } - - public SymmetricCipher(string algo_name) throws Error { - GCrypt.Cipher.Algorithm algo; - GCrypt.Cipher.Mode mode; - GCrypt.Cipher.Flag flags; - if (parse(algo_name, out algo, out mode, out flags)) { - this.gcrypt(algo, mode, flags); - } else { - throw new Error.ILLEGAL_ARGUMENTS(@"The algorithm $algo_name is not supported"); - } - } - - 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)); - } - - public void set_key(uint8[] key) throws Error { - may_throw_gcrypt_error(cipher.set_key(key)); - } - - public void set_iv(uint8[] iv) throws Error { - may_throw_gcrypt_error(cipher.set_iv(iv)); - } - - public void set_counter_vector(uint8[] ctr) throws Error { - may_throw_gcrypt_error(cipher.set_counter_vector(ctr)); - } - - public void reset() throws Error { - may_throw_gcrypt_error(cipher.reset()); - } - - public uint8[] get_tag(size_t taglen) throws Error { - uint8[] tag = new uint8[taglen]; - may_throw_gcrypt_error(cipher.get_tag(tag)); - return tag; - } - - public void check_tag(uint8[] tag) throws Error { - may_throw_gcrypt_error(cipher.check_tag(tag)); - } - - public void encrypt(uint8[] output, uint8[] input) throws Error { - may_throw_gcrypt_error(cipher.encrypt(output, input)); - } +public interface SymmetricCipher { + public abstract void set_key(uint8[] key) throws Error; + public abstract void set_iv(uint8[] iv) throws Error; + public abstract void reset() throws Error; +} - public void decrypt(uint8[] output, uint8[] input) throws Error { - may_throw_gcrypt_error(cipher.decrypt(output, input)); - } +public interface SymmetricCipherEncryption : SymmetricCipher, Object { + public abstract int encrypt(uint8[] output, uint8[] input) throws Error; + public abstract uint8[] get_tag(size_t taglen) throws Error; +} - public void sync() throws Error { - may_throw_gcrypt_error(cipher.sync()); - } +public interface SymmetricCipherDecryption : SymmetricCipher, Object { + public abstract int decrypt(uint8[] output, uint8[] input) throws Error; + public abstract void check_tag(uint8[] tag) throws Error; } + } diff --git a/crypto-vala/src/cipher_converter.vala b/crypto-vala/src/cipher_converter.vala index 5fb1f548..49e7af71 100644 --- a/crypto-vala/src/cipher_converter.vala +++ b/crypto-vala/src/cipher_converter.vala @@ -1,18 +1,14 @@ using GLib; namespace Crypto { -public abstract class SymmetricCipherConverter : Converter, Object { - internal SymmetricCipher cipher; - internal size_t attached_taglen; - public abstract ConverterResult convert(uint8[] inbuf, uint8[] outbuf, ConverterFlags flags, out size_t bytes_read, out size_t bytes_written) throws IOError; +public class SymmetricCipherEncrypter : Converter, Object { + private SymmetricCipherEncryption cipher; + private size_t attached_taglen; - public uint8[] get_tag(size_t taglen) throws Error { - return cipher.get_tag(taglen); - } - - public void check_tag(uint8[] tag) throws Error { - cipher.check_tag(tag); + public SymmetricCipherEncrypter(owned SymmetricCipherEncryption cipher, size_t attached_taglen = 0) { + this.cipher = (owned) cipher; + this.attached_taglen = attached_taglen; } public void reset() { @@ -22,15 +18,12 @@ public abstract class SymmetricCipherConverter : Converter, Object { warning(@"$(e.domain) error while resetting cipher: $(e.message)"); } } -} -public class SymmetricCipherEncrypter : SymmetricCipherConverter { - public SymmetricCipherEncrypter(owned SymmetricCipher cipher, size_t attached_taglen = 0) { - this.cipher = (owned) cipher; - this.attached_taglen = attached_taglen; + public uint8[] get_tag(size_t taglen) throws Error { + return ((SymmetricCipherEncryption)cipher).get_tag(taglen); } - public override ConverterResult convert(uint8[] inbuf, uint8[] outbuf, ConverterFlags flags, out size_t bytes_read, out size_t bytes_written) throws IOError { + public ConverterResult convert(uint8[] inbuf, uint8[] outbuf, ConverterFlags flags, out size_t bytes_read, out size_t bytes_written) throws IOError { if (inbuf.length > outbuf.length) { throw new IOError.NO_SPACE("CipherConverter needs at least the size of input as output space"); } @@ -39,7 +32,7 @@ public class SymmetricCipherEncrypter : SymmetricCipherConverter { } try { if (inbuf.length > 0) { - cipher.encrypt(outbuf, inbuf); + ((SymmetricCipherEncryption)cipher).encrypt(outbuf, inbuf); } bytes_read = inbuf.length; bytes_written = inbuf.length; @@ -60,13 +53,28 @@ public class SymmetricCipherEncrypter : SymmetricCipherConverter { } } -public class SymmetricCipherDecrypter : SymmetricCipherConverter { - public SymmetricCipherDecrypter(owned SymmetricCipher cipher, size_t attached_taglen = 0) { +public class SymmetricCipherDecrypter : Converter, Object { + private SymmetricCipherDecryption cipher; + private size_t attached_taglen; + + public SymmetricCipherDecrypter(owned SymmetricCipherDecryption cipher, size_t attached_taglen = 0) { this.cipher = (owned) cipher; this.attached_taglen = attached_taglen; } - public override ConverterResult convert(uint8[] inbuf, uint8[] outbuf, ConverterFlags flags, out size_t bytes_read, out size_t bytes_written) throws IOError { + public void reset() { + try { + cipher.reset(); + } catch (Crypto.Error e) { + warning(@"$(e.domain) error while resetting cipher: $(e.message)"); + } + } + + public void check_tag(uint8[] tag) throws Error { + ((SymmetricCipherDecryption)cipher).check_tag(tag); + } + + public ConverterResult convert(uint8[] inbuf, uint8[] outbuf, ConverterFlags flags, out size_t bytes_read, out size_t bytes_written) throws IOError { if (inbuf.length > outbuf.length + attached_taglen) { throw new IOError.NO_SPACE("CipherConverter needs at least the size of input as output space"); } @@ -78,7 +86,7 @@ public class SymmetricCipherDecrypter : SymmetricCipherConverter { try { inbuf.length -= (int) attached_taglen; if (inbuf.length > 0) { - cipher.decrypt(outbuf, inbuf); + ((SymmetricCipherDecryption)cipher).decrypt(outbuf, inbuf); } bytes_read = inbuf.length; bytes_written = inbuf.length; diff --git a/crypto-vala/src/cipher_gcrypt.vala b/crypto-vala/src/cipher_gcrypt.vala new file mode 100644 index 00000000..3e811d29 --- /dev/null +++ b/crypto-vala/src/cipher_gcrypt.vala @@ -0,0 +1,137 @@ +namespace Crypto { + +internal class SymmetricCipherGcrypt : SymmetricCipher, SymmetricCipherEncryption, SymmetricCipherDecryption, Object { + private GCrypt.Cipher.Cipher cipher; + + private static unowned string mode_to_string(GCrypt.Cipher.Mode mode) { + switch (mode) { + case GCrypt.Cipher.Mode.ECB: return "ECB"; + case GCrypt.Cipher.Mode.CFB: return "CFB"; + case GCrypt.Cipher.Mode.CBC: return "CBC"; + case GCrypt.Cipher.Mode.STREAM: return "STREAM"; + case GCrypt.Cipher.Mode.OFB: return "OFB"; + case GCrypt.Cipher.Mode.CTR: return "CTR"; + case GCrypt.Cipher.Mode.AESWRAP: return "AESWRAP"; + case GCrypt.Cipher.Mode.GCM: return "GCM"; + case GCrypt.Cipher.Mode.POLY1305: return "POLY1305"; + case GCrypt.Cipher.Mode.OCB: return "OCB"; + case GCrypt.Cipher.Mode.CFB8: return "CFB8"; + // case GCrypt.Cipher.Mode.XTS: return "XTS"; // Not supported in gcrypt < 1.8 + } + return "NONE"; + } + + private static GCrypt.Cipher.Mode mode_from_string(string name) { + switch (name) { + case "ECB": return GCrypt.Cipher.Mode.ECB; + case "CFB": return GCrypt.Cipher.Mode.CFB; + case "CBC": return GCrypt.Cipher.Mode.CBC; + case "STREAM": return GCrypt.Cipher.Mode.STREAM; + case "OFB": return GCrypt.Cipher.Mode.OFB; + case "CTR": return GCrypt.Cipher.Mode.CTR; + case "AESWRAP": return GCrypt.Cipher.Mode.AESWRAP; + case "GCM": return GCrypt.Cipher.Mode.GCM; + case "POLY1305": return GCrypt.Cipher.Mode.POLY1305; + case "OCB": return GCrypt.Cipher.Mode.OCB; + case "CFB8": return GCrypt.Cipher.Mode.CFB8; + // case "XTS": return GCrypt.Cipher.Mode.XTS; // Not supported in gcrypt < 1.8 + } + return GCrypt.Cipher.Mode.NONE; + } + + private static string flags_to_string(GCrypt.Cipher.Flag flags) { + string? s = null; + if ((GCrypt.Cipher.Flag.CBC_MAC & flags) != 0) s = (s == null ? "" : @"$s-") + "MAC"; + if ((GCrypt.Cipher.Flag.CBC_CTS & flags) != 0) s = (s == null ? "" : @"$s-") + "CTS"; + if ((GCrypt.Cipher.Flag.ENABLE_SYNC & flags) != 0) s = (s == null ? "" : @"$s-") + "SYNC"; + if ((GCrypt.Cipher.Flag.SECURE & flags) != 0) s = (s == null ? "" : @"$s-") + "SECURE"; + return s ?? "NONE"; + } + + private static GCrypt.Cipher.Flag flag_from_string(string flag_name) { + if (flag_name == "SECURE") return GCrypt.Cipher.Flag.SECURE; + if (flag_name == "SYNC") return GCrypt.Cipher.Flag.ENABLE_SYNC; + if (flag_name == "CTS") return GCrypt.Cipher.Flag.CBC_CTS; + if (flag_name == "MAC") return GCrypt.Cipher.Flag.CBC_MAC; + return 0; + } + + private static GCrypt.Cipher.Flag flags_from_string(string flag_names) { + GCrypt.Cipher.Flag flags = 0; + foreach(string flag in flag_names.split("-")) { + flags |= flag_from_string(flag); + } + return flags; + } + + private static bool parse(string algo_name, out GCrypt.Cipher.Algorithm algo, out GCrypt.Cipher.Mode mode, out GCrypt.Cipher.Flag flags) { + algo = GCrypt.Cipher.Algorithm.NONE; + mode = GCrypt.Cipher.Mode.NONE; + flags = 0; + string[] algo_parts = algo_name.split("-", 3); + + algo = GCrypt.Cipher.Algorithm.from_string(algo_parts[0]); + if (algo_parts.length >= 2) { + mode = mode_from_string(algo_parts[1]); + } + if (algo_parts.length == 3) { + flags |= flags_from_string(algo_parts[2]); + } + return to_algo_name(algo, mode, flags) == algo_name; + } + + private static string to_algo_name(GCrypt.Cipher.Algorithm algo = GCrypt.Cipher.Algorithm.NONE, GCrypt.Cipher.Mode mode = GCrypt.Cipher.Mode.NONE, GCrypt.Cipher.Flag flags = 0) { + if (flags != 0) { + return @"$algo-$(mode_to_string(mode))-$(flags_to_string(flags))"; + } else if (mode != GCrypt.Cipher.Mode.NONE) { + return @"$algo-$(mode_to_string(mode))"; + } else { + return algo.to_string(); + } + } + + public SymmetricCipherGcrypt(string algo_name) throws Error { + GCrypt.Cipher.Algorithm algo; + GCrypt.Cipher.Mode mode; + GCrypt.Cipher.Flag flags; + if (parse(algo_name, out algo, out mode, out flags)) { + may_throw_gcrypt_error(GCrypt.Cipher.Cipher.open(out this.cipher, algo, mode, flags)); + } else { + throw new Error.ILLEGAL_ARGUMENTS(@"The algorithm $algo_name is not supported"); + } + } + + public void set_key(uint8[] key) throws Error { + may_throw_gcrypt_error(cipher.set_key(key)); + } + + public void set_iv(uint8[] iv) throws Error { + may_throw_gcrypt_error(cipher.set_iv(iv)); + } + + public void reset() throws Error { + may_throw_gcrypt_error(cipher.reset()); + } + + public uint8[] get_tag(size_t taglen) throws Error { + uint8[] tag = new uint8[taglen]; + may_throw_gcrypt_error(cipher.get_tag(tag)); + return tag; + } + + public void check_tag(uint8[] tag) throws Error { + may_throw_gcrypt_error(cipher.check_tag(tag)); + } + + public int encrypt(uint8[] output, uint8[] input) throws Error { + may_throw_gcrypt_error(cipher.encrypt(output, input)); + return input.length; + } + + public int decrypt(uint8[] output, uint8[] input) throws Error { + may_throw_gcrypt_error(cipher.decrypt(output, input)); + return input.length; + } +} + +} diff --git a/crypto-vala/src/cipher_openssl.vala b/crypto-vala/src/cipher_openssl.vala new file mode 100644 index 00000000..75a0ef9a --- /dev/null +++ b/crypto-vala/src/cipher_openssl.vala @@ -0,0 +1,156 @@ +namespace Crypto { + +internal class SymmetricCipherEncryptionOpenssl : SymmetricCipher, SymmetricCipherEncryption, Object { + private OpenSSL.EVP.CipherContext? cipher; + private bool is_finalized = false; + + public SymmetricCipherEncryptionOpenssl(string algo_name) throws Error { + if (algo_name != "AES-GCM") throw new Error.ILLEGAL_ARGUMENTS(@"The algorithm $algo_name is not supported"); + cipher = new OpenSSL.EVP.CipherContext(); + } + + public void set_key(uint8[] key) throws Error { + unowned OpenSSL.EVP.Cipher algo; + if (key.length == 16) { + algo = OpenSSL.EVP.aes_128_gcm(); + } else if (key.length == 32) { + algo = OpenSSL.EVP.aes_256_gcm(); + } else { + throw new Crypto.Error.ILLEGAL_ARGUMENTS("key length must be 16 or 32 for AES-GCM"); + } + try { + cipher.encrypt_init(algo, null, null); + cipher.encrypt_init(null, key, null); + } catch (GLib.Error e) { + throw new Crypto.Error.OPENSSL(e.message); + } + } + + public void set_iv(uint8[] iv) throws Error { + if (iv.length != 12) { + cipher.ctrl(OpenSSL.EVP.Control.GCM_SET_IVLEN, iv.length, null); + } + try { + cipher.encrypt_init(null, null, iv); + } catch (GLib.Error e) { + throw new Crypto.Error.OPENSSL(e.message); + } + } + + public void reset() throws Error { + throw new Crypto.Error.ILLEGAL_ARGUMENTS("can't reset OpenSSL cipher context"); + } + + public uint8[] get_tag(size_t taglen) throws Error { + uint8[] tag = new uint8[taglen]; + if (!is_finalized) { + uint8[] empty = new uint8[0]; + int empty_len = 0; + try { + cipher.encrypt_final(empty, out empty_len); + } catch (GLib.Error e) { + throw new Crypto.Error.OPENSSL(e.message); + } + if (empty_len != 0) { + throw new Crypto.Error.ILLEGAL_ARGUMENTS("get_tag called on a stream with remaining data"); + } + } + if (cipher.ctrl(OpenSSL.EVP.Control.GCM_GET_TAG, (int)taglen, tag) != 1) { + openssl_error(); + } + return tag; + } + + public int encrypt(uint8[] output, uint8[] input) throws Error { + int output_length = output.length; + if (is_finalized) { + throw new Crypto.Error.ILLEGAL_ARGUMENTS("encrypt called after get_tag"); + } + try { + cipher.encrypt_update(output, out output_length, input); + } catch (GLib.Error e) { + throw new Crypto.Error.OPENSSL(e.message); + } + return output_length; + } +} + +internal class SymmetricCipherDecryptionOpenssl : SymmetricCipher, SymmetricCipherDecryption, Object { + private OpenSSL.EVP.CipherContext? cipher; + private bool is_finalized = false; + + public SymmetricCipherDecryptionOpenssl(string algo_name) throws Error { + if (algo_name != "AES-GCM") throw new Error.ILLEGAL_ARGUMENTS(@"The algorithm $algo_name is not supported"); + cipher = new OpenSSL.EVP.CipherContext(); + try { + cipher.decrypt_init(OpenSSL.EVP.aes_128_gcm(), null, null); + } catch (GLib.Error e) { + throw new Crypto.Error.OPENSSL(e.message); + } + } + + public void set_key(uint8[] key) throws Error { + unowned OpenSSL.EVP.Cipher algo; + if (key.length == 16) { + algo = OpenSSL.EVP.aes_128_gcm(); + } else if (key.length == 32) { + algo = OpenSSL.EVP.aes_256_gcm(); + } else { + throw new Crypto.Error.ILLEGAL_ARGUMENTS("key length must be 16 or 32 for AES-GCM"); + } + try { + cipher.decrypt_init(algo, null, null); + cipher.decrypt_init(null, key, null); + } catch (GLib.Error e) { + throw new Crypto.Error.OPENSSL(e.message); + } + } + + public void set_iv(uint8[] iv) throws Error { + if (iv.length != 12) { + cipher.ctrl(OpenSSL.EVP.Control.GCM_SET_IVLEN, iv.length, null); + } + try { + cipher.decrypt_init(null, null, iv); + } catch (GLib.Error e) { + throw new Crypto.Error.OPENSSL(e.message); + } + } + + public void reset() throws Error { + throw new Crypto.Error.ILLEGAL_ARGUMENTS("can't reset OpenSSL cipher context"); + } + + public void check_tag(uint8[] tag) throws Error { + if (cipher.ctrl(OpenSSL.EVP.Control.GCM_SET_TAG, tag.length, tag) != 1) { + openssl_error(); + } + if (!is_finalized) { + uint8[] empty = new uint8[0]; + int empty_len = 0; + try { + cipher.decrypt_final(empty, out empty_len); + } catch (GLib.Error e) { + throw new Crypto.Error.OPENSSL(e.message); + } + if (empty_len != 0) { + throw new Crypto.Error.ILLEGAL_ARGUMENTS("check_tag called on a stream with remaining data"); + } + } + } + + public int decrypt(uint8[] output, uint8[] input) throws Error { + if (is_finalized) { + throw new Crypto.Error.ILLEGAL_ARGUMENTS("decrypt called after check_tag"); + } + int output_length = output.length; + try { + cipher.decrypt_update(output, out output_length, input); + } catch (GLib.Error e) { + throw new Crypto.Error.OPENSSL(e.message); + } + return output_length; + } +} + +} diff --git a/crypto-vala/src/error.vala b/crypto-vala/src/error.vala index 5007d725..a26f39c3 100644 --- a/crypto-vala/src/error.vala +++ b/crypto-vala/src/error.vala @@ -3,13 +3,9 @@ namespace Crypto { public errordomain Error { ILLEGAL_ARGUMENTS, GCRYPT, + OPENSSL, AUTHENTICATION_FAILED, UNKNOWN } -internal void may_throw_gcrypt_error(GCrypt.Error e) throws Error { - if (((int)e) != 0) { - throw new Crypto.Error.GCRYPT(e.to_string()); - } } -}
\ No newline at end of file diff --git a/crypto-vala/src/gcrypt.vala b/crypto-vala/src/gcrypt.vala new file mode 100644 index 00000000..1997a5e1 --- /dev/null +++ b/crypto-vala/src/gcrypt.vala @@ -0,0 +1,21 @@ +namespace Crypto { + +public static void randomize(uint8[] buffer) { + GCrypt.Random.randomize(buffer); +} + +internal void may_throw_gcrypt_error(GCrypt.Error e) throws Error { + if (((int)e) != 0) { + throw new Crypto.Error.GCRYPT(e.to_string()); + } +} + +public SymmetricCipherEncryption create_symmetric_cipher_encryption(string algo_name) throws Error { + return new SymmetricCipherGcrypt(algo_name); +} + +public SymmetricCipherDecryption create_symmetric_cipher_decryption(string algo_name) throws Error { + return new SymmetricCipherGcrypt(algo_name); +} + +} diff --git a/crypto-vala/src/openssl.vala b/crypto-vala/src/openssl.vala new file mode 100644 index 00000000..4a288467 --- /dev/null +++ b/crypto-vala/src/openssl.vala @@ -0,0 +1,19 @@ +namespace Crypto { + +public static void randomize(uint8[] buffer) { + OpenSSL.RAND.bytes(buffer); +} + +internal void openssl_error() throws Error { + throw new Crypto.Error.OPENSSL(OpenSSL.ERR.reason_error_string(OpenSSL.ERR.get_error())); +} + +public SymmetricCipherEncryption create_symmetric_cipher_encryption(string algo_name) throws Error { + return new SymmetricCipherEncryptionOpenssl(algo_name); +} + +public SymmetricCipherDecryption create_symmetric_cipher_decryption(string algo_name) throws Error { + return new SymmetricCipherDecryptionOpenssl(algo_name); +} + +} diff --git a/crypto-vala/src/random.vala b/crypto-vala/src/random.vala deleted file mode 100644 index 3f5d3ba9..00000000 --- a/crypto-vala/src/random.vala +++ /dev/null @@ -1,5 +0,0 @@ -namespace Crypto { -public static void randomize(uint8[] buffer) { - GCrypt.Random.randomize(buffer); -} -}
\ No newline at end of file diff --git a/crypto-vala/vapi/openssl.vapi b/crypto-vala/vapi/openssl.vapi new file mode 100644 index 00000000..87693d37 --- /dev/null +++ b/crypto-vala/vapi/openssl.vapi @@ -0,0 +1,322 @@ +[CCode (cprefix = "")] +namespace OpenSSL { + [CCode (cprefix = "ASN1_", lower_case_cprefix = "ASN1_", cheader_filename = "openssl/asn1.h")] + namespace ASN1 { + [Compact] + [CCode (cname = "ASN1_INTEGER", lower_case_cprefix = "ASN1_INTEGER_", cprefix = "ASN1_INTEGER_", free_function = "ASN1_INTEGER_free")] + public class Integer { + public bool @set (long v); + } + + [Compact] + [CCode (cname = "ASN1_TIME", lower_case_cprefix = "ASN1_TIME_", cprefix = "ASN1_TIME_", copy_function = "ASN1_TIME_dup", free_function = "ASN1_STRING_free")] + public class Time { + public bool @set (time_t t); + } + } + [Compact] + [CCode (cname = "BIO", lower_case_cprefix = "BIO_", cprefix = "BIO_", ref_function = "BIO_up_ref", ref_function_void = true, unref_function = "BIO_free", cheader_filename = "openssl/bio.h", simple_generics = true)] + public class BIO<T> { + [Compact] + [CCode (cname = "BIO_METHOD", lower_case_cprefix = "BIO_meth_", cprefix = "BIO_meth_", unref_function = "BIO_meth_free", simple_generics = true)] + public class Method<T> { + public Method (Type type, string name); + [CCode (has_target = false)] + public delegate int WriteFunc<T> (BIO<T> bio, [CCode (type = "const char *", array_length_type = "int")] uint8[] buf); + [CCode (has_target = false)] + public delegate int ReadFunc<T> (BIO<T> bio, [CCode (type = "char *", array_length_type = "int")] uint8[] out_buf); + [CCode (has_target = false, simple_generics = true)] + public delegate long CtrlFunc<T> (BIO<T> bio, ControlCommand cmd, long num, void* ptr); + public WriteFunc<T> write { get; set; } + public ReadFunc<T> read { get; set; } + public CtrlFunc<T> ctrl { get; set; } + } + + [Flags] + [CCode (cname = "int")] + public enum Type { + SOURCE_SINK, + DGRAM, + } + + [CCode (cname = "int", cprefix = "BIO_CTRL_", has_type_id = false)] + public enum ControlCommand { + RESET, + EOF, + SET, + GET, + PUSH, + POP, + GET_CLOSE, + SET_CLOSE, + PENDING, + FLUSH, + DUP, + WPENDING, + + DGRAM_QUERY_MTU, + DGRAM_GET_MTU, + DGRAM_SET_MTU, + DGRAM_SET_PEER, + DGRAM_SET_NEXT_TIMEOUT, + DGRAM_GET_PEER, + DGRAM_GET_FALLBACK_MTU, + DGRAM_SET_DONT_FRAG, + DGRAM_GET_MTU_OVERHEAD, + } + + public BIO (Method<T> type); + public T data { get; set; } + + public void clear_retry_flags (); + public void set_retry_read (); + public bool should_read (); + } + + [CCode (cprefix = "ERR_", lower_case_cprefix = "ERR_", cheader_filename = "openssl/err.h")] + namespace ERR { + public ulong get_error (); + public unowned string lib_error_string (ulong error); + public unowned string reason_error_string (ulong error); + } + + [CCode (cprefix = "EVP_", lower_case_cprefix = "EVP_", cheader_filename = "openssl/evp.h")] + namespace EVP { + [Compact] + [CCode (cname = "EVP_PKEY", lower_case_cprefix = "EVP_PKEY_", cprefix = "EVP_PKEY_", ref_function = "EVP_PKEY_up_ref", ref_function_void = true, unref_function = "EVP_PKEY_free")] + public class Key { + public Key (); + [CCode (cheader_filename = "openssl_utils.h")] + public Key.generate_ecdsa (string curve); + } + [Compact] + [CCode (cname = "EVP_MD", lower_case_cprefix = "EVP_MD_", cprefix = "EVP_MD_", ref_function = "EVP_MD_up_ref", ref_function_void = true, unref_function = "EVP_MD_free")] + public class MessageDigest { + } + + [Compact] + [CCode (cname = "EVP_CIPHER", lower_case_cprefix = "EVP_CIPHER_", cprefix = "EVP_CIPHER_", ref_function = "EVP_CIPHER_up_ref", ref_function_void = true, unref_function = "EVP_CIPHER_free")] + public class Cipher { + + } + + [Compact] + [CCode (cname = "EVP_CIPHER_CTX", lower_case_cprefix = "EVP_CIPHER_CTX_", cprefix = "EVP_CIPHER_CTX_", free_function = "EVP_CIPHER_CTX_free")] + public class CipherContext { + public CipherContext (); + + [CCode (simple_generics = true)] + public int ctrl<T>(Control cmd, int p1, T? p2); + + [CCode (cname = "EVP_EncryptInit")] + private int encrypt_init_ (Cipher? cipher, [CCode (array_length = false)] uint8[]? key, [CCode (array_length = false)] uint8[]? iv); + public void encrypt_init (Cipher? cipher, uint8[]? key, uint8[]? iv) throws GLib.Error { + throw_if_error (encrypt_init_ (cipher, key, iv)); + } + [CCode (cname = "EVP_EncryptUpdate")] + private int encrypt_update_ ([CCode (array_length = false)] uint8[] out, out int out_len, uint8[] in); + public void encrypt_update (uint8[] out, out int out_len, uint8[] in) throws GLib.Error { + throw_if_error (encrypt_update_ (@out, out out_len, in)); + } + [CCode (cname = "EVP_EncryptFinal")] + private int encrypt_final_ ([CCode (array_length = false)] uchar[] out, out int out_len); + public void encrypt_final (uint8[] out, out int out_len) throws GLib.Error { + throw_if_error (encrypt_final_ (@out, out out_len)); + } + + [CCode (cname = "EVP_DecryptInit")] + private int decrypt_init_ (Cipher? cipher, [CCode (array_length = false)] uint8[]? key, [CCode (array_length = false)] uint8[]? iv); + public void decrypt_init (Cipher? cipher, uint8[]? key, uint8[]? iv) throws GLib.Error { + throw_if_error (decrypt_init_ (cipher, key, iv)); + } + [CCode (cname = "EVP_DecryptUpdate")] + private int decrypt_update_ ([CCode (array_length = false)] uchar[] out, out int out_len, uint8[] in); + public void decrypt_update (uint8[] out, out int out_len, uint8[] in) throws GLib.Error { + throw_if_error (decrypt_update_ (@out, out out_len, in)); + } + [CCode (cname = "EVP_DecryptFinal")] + private int decrypt_final_ ([CCode (array_length = false)] uchar[] out, out int out_len); + public void decrypt_final (uint8[] out, out int out_len) throws GLib.Error { + throw_if_error (decrypt_final_ (@out, out out_len)); + } + + public int reset(); + public int set_key_length (int keylen); + public int set_padding (int pad); + } + + [CCode (cname = "int", cprefix = "EVP_CTRL_", has_type_id = false)] + public enum Control { + INIT, + SET_KEY_LENGTH, + GET_RC2_KEY_BITS, + SET_RC2_KEY_BITS, + GET_RC5_ROUNDS, + SET_RC5_ROUNDS, + RAND_KEY, + PBE_PRF_NID, + COPY, + AEAD_SET_IVLEN, + AEAD_GET_TAG, + AEAD_SET_TAG, + AEAD_SET_IV_FIXED, + GCM_SET_IVLEN, + GCM_GET_TAG, + GCM_SET_TAG, + GCM_SET_IV_FIXED, + GCM_IV_GEN, + CCM_SET_IVLEN, + CCM_GET_TAG, + CCM_SET_TAG, + CCM_SET_IV_FIXED, + CCM_SET_L, + CCM_SET_MSGLEN, + AEAD_TLS1_AAD, + AEAD_SET_MAC_KEY, + GCM_SET_IV_INV, + TLS1_1_MULTIBLOCK_AAD, + TLS1_1_MULTIBLOCK_ENCRYPT, + TLS1_1_MULTIBLOCK_DECRYPT, + TLS1_1_MULTIBLOCK_MAX_BUFSIZE, + SSL3_MASTER_SECRET, + SET_SBOX, + SBOX_USED, + KEY_MESH, + BLOCK_PADDING_MODE, + SET_PIPELINE_OUTPUT_BUFS, + SET_PIPELINE_INPUT_BUFS, + SET_PIPELINE_INPUT_LENS, + GET_IVLEN, + } + + public static unowned Cipher? aes_128_gcm (); + public static unowned Cipher? aes_256_gcm (); + + public static unowned MessageDigest sha256 (); + } + + [CCode (lower_case_cprefix = "RAND_", cprefix = "RAND_", cheader_filename = "openssl/rand.h")] + namespace RAND { + public static int bytes (uint8[] buf); + public static int priv_bytes (uint8[] buf); + } + + [Compact] + [CCode (cname = "SSL", lower_case_cprefix = "SSL_", cprefix = "SSL_", ref_function = "SSL_up_ref", ref_function_void = true, unref_function = "SSL_free", cheader_filename = "openssl/ssl.h")] + class SSL { + [Compact] + [CCode (cname = "SSL_CTX", lower_case_cprefix = "SSL_CTX_", cprefix = "SSL_CTX_", ref_function = "SSL_CTX_up_ref", ref_function_void = true, unref_function = "SSL_CTX_free")] + public class Context { + public Context (Method method); + public int set_tlsext_use_srtp (string profiles); + public int use_certificate (X509.Certificate certificate); + [CCode (cname = "SSL_CTX_use_PrivateKey")] + public int use_private_key (EVP.Key private_key); + } + + [CCode (cname = "const SSL_METHOD*")] + public enum Method { + [CCode (cname = "DTLS_client_method()")] + DTLS_CLIENT, + [CCode (cname = "DTLS_server_method()")] + DTLS_SERVER, + } + + [CCode (cname = "int", has_type_id = false)] + public enum Error { + NONE, + ZERO_RETURN, + WANT_READ, + WANT_WRITE, + WANT_CONNECT, + WANT_ACCEPT, + WANT_X509_LOOKUP, + WANT_ASYNC, + WANT_ASYNC_JOB, + WANT_CLIENT_HELLO_CB, + SYSCALL, + SSL + } + + public SSL (Context context); + public BIO rbio { get; [CCode (cname="SSL_set0_rbio")] owned set; } + public BIO wbio { get; [CCode (cname="SSL_set0_wbio")] owned set; } + + [CCode (cname = "SSL_do_handshake")] + private int do_handshake_ (); + [CCode (cname = "SSL_do_handshake_")] + public Error do_handshake () throws GLib.Error { + int ret = throw_if_error (do_handshake_ ()); + return get_error (ret); + } + [CCode (cname = "SSL_export_keying_material")] + private int export_keying_material_ (uint8[] out, string label, size_t llen, [CCode (type = "const unsigned char *")] string context, size_t contextlen, bool use_context); + [CCode (cname = "SSL_export_keying_material_")] + public void export_keying_material (uint8[] out, string label, string? context = null) throws GLib.Error { + throw_if_error (export_keying_material_ (@out, label, label.length, context, context == null ? 0 : context.length, context != null)); + } + public Error get_error (int ret); + public void set_accept_state (); + public void set_connect_state (); + public int want (); + } + + [CCode (cprefix = "X509_", lower_case_cprefix = "X509_", cheader_filename = "openssl/x509.h")] + namespace X509 { + [Compact] + [CCode (cname = "X509", lower_case_cprefix = "X509_", cprefix = "X509_", ref_function = "X509_up_ref", ref_function_void = true, unref_function = "X509_free")] + public class Certificate { + public Certificate (); + public ASN1.Time not_before { + [CCode (cname = "X509_get_notBefore")] + get; + [CCode (cname = "X509_set_notBefore")] + set; + } + public ASN1.Time not_after { + [CCode (cname = "X509_get_notAfter")] + get; + [CCode (cname = "X509_set_notAfter")] + set; + } + public ASN1.Integer serial_number { + [CCode (cname = "X509_get_serialNumber")] + get; + [CCode (cname = "X509_set_serialNumber")] + set; + } + [CCode (cname = "X509_digest")] + private int digest_ (EVP.MessageDigest type, uchar* md, ref uint len); + [CCode (cname = "X509_digest_")] + public uint8[] digest (EVP.MessageDigest type, size_t buf_len = 512) throws GLib.Error { + uint8[] buf = new uint8[buf_len]; + uint buf_out_size = (uint) buf_len; + throw_if_error (digest_ (type, (uchar*) buf, ref buf_out_size)); + + uint8[] ret = new uint8[buf_out_size]; + for (int i = 0; i < buf_out_size; i++) { + ret[i] = buf[i]; + } + return ret; + } + [CCode (cname = "X509_set_pubkey")] + public int set_public_key (EVP.Key public_key); + public int set_version (long version); + [CCode (cname = "X509_sign")] + private int sign_ (EVP.Key private_key, EVP.MessageDigest md); + [CCode (cname = "X509_sign_")] + public void sign (EVP.Key private_key, EVP.MessageDigest md) throws GLib.Error { + throw_if_error (sign_ (private_key, md)); + } + } + } + + public int throw_if_error(int res) throws GLib.Error { + if (res <= 0) { + ulong error = ERR.get_error (); + if (error == 0) return res; + throw new GLib.Error (-1, (int) error, "%s:%s", ERR.lib_error_string (error), ERR.reason_error_string (error)); + } + return res; + } +}
\ No newline at end of file |
