mconnect - KDE Connect protocol implementation in Vala/C
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

252 lines
6.2 KiB

  1. /* ex:ts=4:sw=4:sts=4:et */
  2. /* -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
  3. /**
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License version 2 as
  6. * published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License along
  14. * with this program; if not, write to the Free Software Foundation, Inc.,
  15. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  16. *
  17. * AUTHORS
  18. * Maciek Borzecki <maciek.borzecki (at] gmail.com>
  19. */
  20. #include <openssl/rsa.h>
  21. #include <openssl/bio.h>
  22. #include <openssl/pem.h>
  23. #include "mconn-crypt.h"
  24. /* encrypted data padding */
  25. #define M_CONN_CRYPT_RSA_PADDING RSA_PKCS1_PADDING
  26. typedef struct _MConnCryptPrivate MConnCryptPrivate;
  27. /**
  28. * MConnCrypt:
  29. *
  30. * A simple wrapper for cypto operations.
  31. **/
  32. struct _MConnCrypt
  33. {
  34. GObject parent;
  35. MConnCryptPrivate *priv;
  36. };
  37. struct _MConnCryptPrivate
  38. {
  39. RSA *key; /* RSA key wrapper */
  40. };
  41. static void m_conn_crypt_dispose (GObject *object);
  42. static void m_conn_crypt_finalize (GObject *object);
  43. static gchar *__m_conn_get_public_key_as_pem(MConnCryptPrivate *priv);
  44. static gboolean __m_conn_load_key(MConnCryptPrivate *priv, const char *path);
  45. static gboolean __m_conn_generate_key_at_path(const char *path);
  46. G_DEFINE_TYPE_WITH_PRIVATE (MConnCrypt, m_conn_crypt, G_TYPE_OBJECT);
  47. static void
  48. m_conn_crypt_class_init (MConnCryptClass *klass)
  49. {
  50. GObjectClass *gobject_class = (GObjectClass *)klass;
  51. gobject_class->dispose = m_conn_crypt_dispose;
  52. gobject_class->finalize = m_conn_crypt_finalize;
  53. }
  54. static void
  55. m_conn_crypt_init (MConnCrypt *self)
  56. {
  57. g_debug("mconn-crypt: new instance");
  58. self->priv = m_conn_crypt_get_instance_private(self);
  59. }
  60. static void
  61. m_conn_crypt_dispose (GObject *object)
  62. {
  63. MConnCrypt *self = (MConnCrypt *)object;
  64. if (self->priv->key != NULL)
  65. {
  66. RSA_free(self->priv->key);
  67. self->priv->key = NULL;
  68. }
  69. G_OBJECT_CLASS (m_conn_crypt_parent_class)->dispose (object);
  70. }
  71. static void
  72. m_conn_crypt_finalize (GObject *object)
  73. {
  74. MConnCrypt *self = (MConnCrypt *)object;
  75. g_signal_handlers_destroy (object);
  76. G_OBJECT_CLASS (m_conn_crypt_parent_class)->finalize (object);
  77. }
  78. MConnCrypt *m_conn_crypt_new_for_key_path(const char *path)
  79. {
  80. g_debug("mconn-crypt: new crypt for key %s", path);
  81. MConnCrypt *self = g_object_new(M_CONN_TYPE_CRYPT, NULL);
  82. if (g_file_test(path, G_FILE_TEST_EXISTS) == FALSE)
  83. __m_conn_generate_key_at_path(path);
  84. if (__m_conn_load_key(self->priv, path) == FALSE)
  85. {
  86. m_conn_crypt_unref(self);
  87. return NULL;
  88. }
  89. return self;
  90. }
  91. MConnCrypt * m_conn_crypt_ref(MConnCrypt *self)
  92. {
  93. g_assert(IS_M_CONN_CRYPT(self));
  94. return M_CONN_CRYPT(g_object_ref(self));
  95. }
  96. void m_conn_crypt_unref(MConnCrypt *self)
  97. {
  98. if (self != NULL)
  99. {
  100. g_assert(IS_M_CONN_CRYPT(self));
  101. g_object_unref(self);
  102. }
  103. }
  104. GByteArray * m_conn_crypt_decrypt(MConnCrypt *self, GBytes *data, GError **err)
  105. {
  106. g_assert(IS_M_CONN_CRYPT(self));
  107. g_assert(self->priv->key);
  108. g_debug("decrypt: %zu bytes of data", g_bytes_get_size(data));
  109. g_assert_cmpint(g_bytes_get_size(data), ==, RSA_size(self->priv->key));
  110. /* decrypted data is less than RSA_size() long */
  111. gsize out_buf_size = RSA_size(self->priv->key);
  112. GByteArray *out_data = g_byte_array_sized_new(out_buf_size);
  113. int dec_size;
  114. dec_size = RSA_private_decrypt(g_bytes_get_size(data),
  115. g_bytes_get_data(data, NULL),
  116. (unsigned char *)out_data->data,
  117. self->priv->key,
  118. M_CONN_CRYPT_RSA_PADDING);
  119. g_debug("decrypted size: %d", dec_size);
  120. g_assert(dec_size != -1);
  121. g_byte_array_set_size(out_data, dec_size);
  122. return out_data;
  123. }
  124. gchar *m_conn_crypt_get_public_key_pem(MConnCrypt *self)
  125. {
  126. g_assert(IS_M_CONN_CRYPT(self));
  127. g_assert(self->priv);
  128. g_assert(self->priv->key);
  129. return __m_conn_get_public_key_as_pem(self->priv);
  130. }
  131. /**
  132. *
  133. */
  134. static gchar *__m_conn_get_public_key_as_pem(MConnCryptPrivate *priv)
  135. {
  136. gchar *pubkey = NULL;
  137. /* memory IO */
  138. BIO *bm = BIO_new(BIO_s_mem());
  139. /* generate PEM */
  140. /* PEM_write_bio_RSAPublicKey(bm, priv->key); */
  141. PEM_write_bio_RSA_PUBKEY(bm, priv->key);
  142. /* get PEM as text */
  143. char *oss_pubkey = NULL;
  144. long data = BIO_get_mem_data(bm, &oss_pubkey);
  145. g_debug("mconn-crypt: public key length: %l", data);
  146. g_assert(data != 0);
  147. g_assert(oss_pubkey != NULL);
  148. /* dup the key as buffer goes away with BIO */
  149. pubkey = g_strndup(oss_pubkey, data);
  150. BIO_set_close(bm, BIO_CLOSE);
  151. BIO_free(bm);
  152. return pubkey;
  153. }
  154. static gboolean __m_conn_load_key(MConnCryptPrivate *priv, const char *path)
  155. {
  156. if (g_file_test(path, G_FILE_TEST_EXISTS) == FALSE)
  157. {
  158. g_critical("mconn-crypt: key file %s does not exist", path);
  159. return FALSE;
  160. }
  161. g_debug("mconn-crypt: loading key from %s", path);
  162. BIO *bf = BIO_new_file(path, "r");
  163. if (bf == NULL)
  164. {
  165. g_critical("mconn-crypt: failed to open file %s", path);
  166. return FALSE;
  167. }
  168. RSA *rsa = NULL;
  169. rsa = PEM_read_bio_RSAPrivateKey(bf, NULL, NULL, NULL);
  170. BIO_free(bf);
  171. if (rsa == NULL)
  172. {
  173. g_critical("mconn-crypt: failed to read private key");
  174. return FALSE;
  175. }
  176. priv->key = rsa;
  177. return TRUE;
  178. }
  179. static gboolean __m_conn_generate_key_at_path(const char *path)
  180. {
  181. gboolean ret = TRUE;
  182. RSA *rsa = NULL;
  183. BIO *bf = BIO_new_file(path, "w");
  184. if (bf == NULL)
  185. {
  186. g_error("mconn-crypt: failed to open file");
  187. return FALSE;
  188. }
  189. rsa = RSA_generate_key(2048, RSA_3, NULL, NULL);
  190. if (PEM_write_bio_RSAPrivateKey(bf, rsa, NULL, NULL, 0, NULL, NULL) == 0)
  191. {
  192. g_critical("mconn-crypt: failed to private write key to file");
  193. ret = FALSE;
  194. }
  195. RSA_free(rsa);
  196. BIO_free(bf);
  197. return ret;
  198. }