/** * mega_aes_key_get_enc_ubase64: * @aes_key: a #MegaAesKey * @enc_key: Encryption key. * * Get UBase64 encoded key data encrypted with #enc_key. * * Returns: UBase64 encoded string. */ gchar* mega_aes_key_get_enc_ubase64(MegaAesKey* aes_key, MegaAesKey* enc_key) { g_return_val_if_fail(MEGA_IS_AES_KEY(aes_key), NULL); g_return_val_if_fail(MEGA_IS_AES_KEY(enc_key), NULL); g_return_val_if_fail(aes_key->priv->loaded, NULL); return mega_aes_key_encrypt(enc_key, aes_key->priv->key, 16); }
/** * mega_aes_key_get_enc_binary: * @aes_key: a #MegaAesKey * @enc_key: Encryption key. * * Get 16 byte AES key data encrypted with #enc_key. * * Returns: (element-type guint8) (array fixed-size=16) (transfer full): Key * data */ guchar* mega_aes_key_get_enc_binary(MegaAesKey* aes_key, MegaAesKey* enc_key) { guchar cipher_key[16]; g_return_val_if_fail(MEGA_IS_AES_KEY(aes_key), NULL); g_return_val_if_fail(MEGA_IS_AES_KEY(enc_key), NULL); g_return_val_if_fail(aes_key->priv->loaded, NULL); mega_aes_key_encrypt_raw(enc_key, aes_key->priv->key, cipher_key, 16); return g_memdup(cipher_key, 16); }
/** * mega_aes_key_load_enc_binary: * @aes_key: a #MegaAesKey * @data: (element-type guint8) (array fixed-size=16): 16 byte AES key buffer * @dec_key: Decryption key * * Initialize key from #data. */ void mega_aes_key_load_enc_binary(MegaAesKey* aes_key, const guchar* data, MegaAesKey* dec_key) { guchar plain_key[16]; g_return_if_fail(MEGA_IS_AES_KEY(aes_key)); g_return_if_fail(data != NULL); g_return_if_fail(MEGA_IS_AES_KEY(dec_key)); mega_aes_key_decrypt_raw(dec_key, data, plain_key, 16); mega_aes_key_load_binary(aes_key, plain_key); }
/** * mega_aes_key_decrypt: * @aes_key: a #MegaAesKey * @cipher: UBase64 encoded ciphertext. * * Decrypt UBase64 encoded 16-byte aligned ciphertext into binary plaintext data. * * Returns: (transfer full): Binary plaintext data. */ GBytes* mega_aes_key_decrypt(MegaAesKey* aes_key, const gchar* cipher) { gsize cipherlen = 0; guchar* cipher_raw; guchar* plain; g_return_val_if_fail(MEGA_IS_AES_KEY(aes_key), NULL); g_return_val_if_fail(cipher != NULL, NULL); cipher_raw = mega_base64urldecode(cipher, &cipherlen); if (cipher_raw == NULL) return NULL; if (cipherlen == 0) { g_free(cipher_raw); return NULL; } if (cipherlen % 16 != 0) { g_free(cipher_raw); return NULL; } plain = g_malloc0(cipherlen); mega_aes_key_decrypt_raw(aes_key, cipher_raw, plain, cipherlen); g_free(cipher_raw); return g_bytes_new_take(plain, cipherlen); }
/** * mega_aes_key_get_binary: * @aes_key: a #MegaAesKey * * Get 16 byte AES key data. * * Returns: (element-type guint8) (array fixed-size=16) (transfer full): Key * data */ guchar* mega_aes_key_get_binary(MegaAesKey* aes_key) { g_return_val_if_fail(MEGA_IS_AES_KEY(aes_key), NULL); g_return_val_if_fail(aes_key->priv->loaded, NULL); return g_memdup(aes_key->priv->key, 16); }
/** * mega_aes_key_decrypt_cbc: * @aes_key: a #MegaAesKey * @cipher: UBase64 encoded ciphertext. * * Decrypt UBase64 encoded ciphertext blocks using AES key in CBC mode with zero IV. * * Returns: (transfer full): UBase64 encoded ciphertext. */ GBytes* mega_aes_key_decrypt_cbc(MegaAesKey* aes_key, const gchar* cipher) { guchar iv[AES_BLOCK_SIZE] = {0}; guchar* cipher_raw; guchar* plain; gsize cipherlen = 0; g_return_val_if_fail(MEGA_IS_AES_KEY(aes_key), NULL); g_return_val_if_fail(cipher != NULL, NULL); cipher_raw = mega_base64urldecode(cipher, &cipherlen); if (cipher_raw == NULL) return NULL; if (cipherlen % 16 != 0) { g_free(cipher_raw); return NULL; } plain = g_malloc0(cipherlen + 1); AES_cbc_encrypt(cipher_raw, plain, cipherlen, &aes_key->priv->dec_key, iv, 0); g_free(cipher_raw); return g_bytes_new_take(plain, cipherlen); }
/** * mega_aes_key_get_ubase64: * @aes_key: a #MegaAesKey * * Get UBase64 encoded key data. * * Returns: UBase64 encoded string. */ gchar* mega_aes_key_get_ubase64(MegaAesKey* aes_key) { g_return_val_if_fail(MEGA_IS_AES_KEY(aes_key), NULL); g_return_val_if_fail(aes_key->priv->loaded, NULL); return mega_base64urlencode(aes_key->priv->key, 16); }
/** * mega_aes_key_make_username_hash: * @aes_key: a #MegaAesKey * @username: E-mail * * Generate username hash (uh paraemter for 'us' API call) used for authentication to mega.nz. * * Returns: Username hash string */ gchar* mega_aes_key_make_username_hash(MegaAesKey* aes_key, const gchar* username) { gchar* username_lower; g_return_val_if_fail(MEGA_IS_AES_KEY(aes_key), NULL); g_return_val_if_fail(username != NULL, NULL); username_lower = g_ascii_strdown(username, -1); gint l, i; guchar hash[16] = {0}, hash_tmp[16], oh[8]; for (i = 0, l = strlen(username_lower); i < l; i++) hash[i % 16] ^= username_lower[i]; for (i = 16384; i--; ) { AES_encrypt(hash, hash_tmp, &aes_key->priv->enc_key); memcpy(hash, hash_tmp, 16); } memcpy(oh, hash, 4); memcpy(oh + 4, hash + 8, 4); g_free(username_lower); return mega_base64urlencode(oh, 8); }
/** * mega_aes_key_generate_from_password: * @aes_key: a #MegaAesKey * @password: * * Initialize key from plaintext password string. (mega.nz algorithm) */ void mega_aes_key_generate_from_password(MegaAesKey* aes_key, const gchar* password) { g_return_if_fail(MEGA_IS_AES_KEY(aes_key)); g_return_if_fail(password != NULL); guchar pkey[16] = {0x93, 0xC4, 0x67, 0xE3, 0x7D, 0xB0, 0xC7, 0xA4, 0xD1, 0xBE, 0x3F, 0x81, 0x01, 0x52, 0xCB, 0x56}; gint off, r; gint len; len = strlen(password); for (r = 65536; r--; ) { for (off = 0; off < len; off += 16) { AES_KEY k; guchar key[16] = {0}, pkey_tmp[16]; strncpy(key, password + off, 16); AES_set_encrypt_key(key, 128, &k); AES_encrypt(pkey, pkey_tmp, &k); memcpy(pkey, pkey_tmp, 16); } } mega_aes_key_load_binary(aes_key, pkey); }
/** * mega_aes_key_encrypt_ctr: * @aes_key: a #MegaAesKey * @from: (in) (element-type guint8) (array length=len): Plaintext input data * @to: (out caller-allocates) (element-type guint8) (array length=len): Ciphertext * @len: (in): 16 byte aligned length of plaintext data. * * Encrypt plaintext blocks using AES key in CTR mode. */ void mega_aes_key_encrypt_ctr(MegaAesKey* aes_key, guchar* from, guchar* to, gsize len) { g_return_if_fail(MEGA_IS_AES_KEY(aes_key)); g_return_if_fail(from != NULL); g_return_if_fail(to != NULL); g_return_if_fail(len > 0); AES_ctr128_encrypt(from, to, len, &aes_key->priv->enc_key, aes_key->priv->ctr_iv, aes_key->priv->ctr_ecount, &aes_key->priv->ctr_num); }
/** * mega_aes_key_generate: * @aes_key: a #MegaAesKey * * Initialize key with random data. */ void mega_aes_key_generate(MegaAesKey* aes_key) { guchar rand_key[16]; g_return_if_fail(MEGA_IS_AES_KEY(aes_key)); RAND_bytes(rand_key, sizeof(rand_key)); mega_aes_key_load_binary(aes_key, rand_key); }
/** * mega_aes_key_load_enc_ubase64: * @aes_key: a #MegaAesKey * @data: UBase64 encoded 16 byte AES key data * @dec_key: Decryption key * * Initialize key from #data. * * Returns: TRUE on success. */ gboolean mega_aes_key_load_enc_ubase64(MegaAesKey* aes_key, const gchar* data, MegaAesKey* dec_key) { gsize len; guchar* key; g_return_val_if_fail(MEGA_IS_AES_KEY(aes_key), FALSE); g_return_val_if_fail(data != NULL, FALSE); g_return_val_if_fail(MEGA_IS_AES_KEY(dec_key), FALSE); key = mega_base64urldecode(data, &len); if (key == NULL || len != 16) { g_free(key); return FALSE; } mega_aes_key_load_enc_binary(aes_key, key, dec_key); return TRUE; }
/** * mega_aes_key_setup_ctr: * @aes_key: a #MegaAesKey * @nonce: (element-type guint8) (array fixed-size=8) (transfer none): 8-byte nonce buffer * @position: Counter value (block index) * * Setup CTR mode encryption/decryption. */ void mega_aes_key_setup_ctr(MegaAesKey* aes_key, guchar* nonce, guint64 position) { g_return_if_fail(MEGA_IS_AES_KEY(aes_key)); g_return_if_fail(nonce != NULL); memcpy(aes_key->priv->ctr_nonce, nonce, 8); aes_key->priv->ctr_position = GUINT64_TO_BE(position); memset(aes_key->priv->ctr_ecount, 0, 16); aes_key->priv->ctr_num = 0; }
/** * mega_filesystem_add_share_key: * @filesystem: a #MegaFilesystem * @handle: Key handle. * @key: AES key. * * Add key that can be used to decrypt node key. */ void mega_filesystem_add_share_key(MegaFilesystem* filesystem, const gchar* handle, MegaAesKey* key) { MegaFilesystemPrivate* priv; g_return_if_fail(MEGA_IS_FILESYSTEM(filesystem)); g_return_if_fail(handle != NULL); g_return_if_fail(MEGA_IS_AES_KEY(key)); priv = filesystem->priv; g_hash_table_insert(priv->share_keys, g_strdup(handle), g_object_ref(key)); }
/** * mega_aes_key_decrypt_raw: * @aes_key: a #MegaAesKey * @cipher: (element-type guint8) (array length=len): Ciphertext * @plain: (element-type guint8) (array length=len) (out caller-allocates): Plaintext output data * @len: 16 byte aligned length of ciphertext and plaintext data. * * Decrypt ciphertext blocks using AES key */ void mega_aes_key_decrypt_raw(MegaAesKey* aes_key, const guchar* cipher, guchar* plain, gsize len) { gsize off; g_return_if_fail(MEGA_IS_AES_KEY(aes_key)); g_return_if_fail(cipher != NULL); g_return_if_fail(plain != NULL); g_return_if_fail(len % 16 == 0); for (off = 0; off < len; off += 16) AES_decrypt(cipher + off, plain + off, &aes_key->priv->dec_key); }
/** * mega_aes_key_load_binary: * @aes_key: a #MegaAesKey * @data: (element-type guint8) (array fixed-size=16): 16 byte AES key buffer * * Initialize key from #data. */ void mega_aes_key_load_binary(MegaAesKey* aes_key, const guchar* data) { g_return_if_fail(MEGA_IS_AES_KEY(aes_key)); g_return_if_fail(data != NULL); memcpy(aes_key->priv->key, data, 16); AES_set_encrypt_key(data, 128, &aes_key->priv->enc_key); AES_set_decrypt_key(data, 128, &aes_key->priv->dec_key); aes_key->priv->loaded = TRUE; }
/** * mega_aes_key_decrypt_cbc_raw: * @aes_key: a #MegaAesKey * @cipher: (element-type guint8) (array length=len): Ciphertext * @plain: (element-type guint8) (array length=len) (out caller-allocates): Plaintext output data * @len: 16 byte aligned length of ciphertext and plaintext data. * * Decrypt ciphertext blocks using AES key in CBC mode with zero IV */ void mega_aes_key_decrypt_cbc_raw (MegaAesKey* aes_key, const guchar* cipher, guchar* plain, gsize len) { guchar iv[AES_BLOCK_SIZE] = {0}; g_return_if_fail(MEGA_IS_AES_KEY(aes_key)); g_return_if_fail(cipher != NULL); g_return_if_fail(plain != NULL); g_return_if_fail((len % 16) == 0); g_return_if_fail(len > 0); AES_cbc_encrypt(cipher, plain, len, &aes_key->priv->dec_key, iv, 0); }
/** * mega_aes_key_encrypt: * @aes_key: a #MegaAesKey * @plain: (in) (element-type guint8) (array length=len): Plaintext input data * @len: (in): 16 byte aligned length of plaintext data. * * Encrypt binary data into ubase64 encoded string. * * Returns: UBase64 encoded ciphertext. */ gchar* mega_aes_key_encrypt(MegaAesKey* aes_key, const guchar* plain, gsize len) { guchar* cipher; gchar* str; g_return_val_if_fail(MEGA_IS_AES_KEY(aes_key), NULL); g_return_val_if_fail(plain != NULL, NULL); g_return_val_if_fail((len % 16) == 0, NULL); g_return_val_if_fail(len > 0, NULL); cipher = g_malloc0(len); mega_aes_key_encrypt_raw(aes_key, plain, cipher, len); str = mega_base64urlencode(cipher, len); g_free(cipher); return str; }
/** * mega_aes_key_encrypt_cbc: * @aes_key: a #MegaAesKey * @plain: (in) (element-type guint8) (array length=len): Plaintext input data * @len: (in): 16 byte aligned length of plaintext data. * * Encrypt plaintext blocks using AES key in CBC mode with zero IV into UBase64 * ciphertext. * * Returns: UBase64 encoded ciphertext. */ gchar* mega_aes_key_encrypt_cbc(MegaAesKey* aes_key, const guchar* plain, gsize len) { guchar* cipher; gchar* str; guchar iv[AES_BLOCK_SIZE] = {0}; g_return_val_if_fail(MEGA_IS_AES_KEY(aes_key), NULL); g_return_val_if_fail(plain != NULL, NULL); g_return_val_if_fail((len % 16) == 0, NULL); g_return_val_if_fail(len > 0, NULL); cipher = g_malloc0(len); AES_cbc_encrypt(plain, cipher, len, &aes_key->priv->enc_key, iv, 1); str = mega_base64urlencode(cipher, len); g_free(cipher); return str; }
/** * mega_aes_key_encrypt_string_cbc: * @aes_key: a #MegaAesKey * @str: Zero terminated string. * * Zero pad zero terminated string to align data to a AES block size, and * encrypt into UBase64 encoded ciphertext. * * Returns: UBase64 encoded ciphertext. */ gchar* mega_aes_key_encrypt_string_cbc(MegaAesKey* aes_key, const gchar* str) { gsize len = 0; gchar* plain; gchar* cipher; g_return_val_if_fail(MEGA_IS_AES_KEY(aes_key), NULL); g_return_val_if_fail(str != NULL, NULL); // calculate paded size len = strlen(str) + 1; if (len % 16) len += 16 - (len % 16); plain = g_malloc0(len); memcpy(plain, str, len - 1); cipher = mega_aes_key_encrypt_cbc(aes_key, plain, len); g_free(plain); return cipher; }
void test_aes_construction(void) { MegaAesKey *k, *ek; // VALID USES: // create empty key, check that it is not laoded k = mega_aes_key_new(); g_assert(MEGA_IS_AES_KEY(k)); g_assert(!mega_aes_key_is_loaded(k)); g_object_unref(k); // create key from UBase64 k = mega_aes_key_new_from_ubase64(KEY_UBASE64); g_assert(MEGA_IS_AES_KEY(k)); g_assert(mega_aes_key_is_loaded(k)); g_assert_cmpstr(KEY_UBASE64, ==, mega_aes_key_get_ubase64(k)); g_object_unref(k); // create key from Binary data (use as dec_key below) ek = mega_aes_key_new_from_binary(KEY_BINARY); g_assert(MEGA_IS_AES_KEY(ek)); g_assert(mega_aes_key_is_loaded(ek)); g_assert(memcmp(KEY_BINARY, mega_aes_key_get_binary(ek), 16) == 0); g_assert_cmpstr(KEY_UBASE64, ==, mega_aes_key_get_ubase64(ek)); // create key from encrypted UBase64 k = mega_aes_key_new_from_enc_ubase64(KEY_UBASE64, ek); g_assert(MEGA_IS_AES_KEY(k)); g_assert(mega_aes_key_is_loaded(k)); g_assert_cmpstr(KEY_UBASE64, ==, mega_aes_key_get_enc_ubase64(k, ek)); g_object_unref(k); // create key from encrypted Binary data k = mega_aes_key_new_from_enc_binary(KEY_BINARY, ek); g_assert(MEGA_IS_AES_KEY(k)); g_assert(mega_aes_key_is_loaded(k)); g_assert(memcmp(KEY_BINARY, mega_aes_key_get_enc_binary(k, ek), 16) == 0); g_object_unref(k); g_object_unref(ek); // INVALID USES k = mega_aes_key_new_from_ubase64(KEY_LONG); g_assert(MEGA_IS_AES_KEY(k)); g_assert(!mega_aes_key_is_loaded(k)); g_object_unref(k); k = mega_aes_key_new_from_ubase64(KEY_SHORT); g_assert(MEGA_IS_AES_KEY(k)); g_assert(!mega_aes_key_is_loaded(k)); g_object_unref(k); k = mega_aes_key_new_from_ubase64(KEY_INVALID); g_assert(MEGA_IS_AES_KEY(k)); g_assert(!mega_aes_key_is_loaded(k)); g_object_unref(k); k = mega_aes_key_new_from_ubase64(KEY_NULL); g_assert(MEGA_IS_AES_KEY(k)); g_assert(!mega_aes_key_is_loaded(k)); g_object_unref(k); g_test_expect_message(G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*data != NULL*"); k = mega_aes_key_new_from_ubase64(NULL); g_test_assert_expected_messages(); g_assert(MEGA_IS_AES_KEY(k)); g_assert(!mega_aes_key_is_loaded(k)); g_object_unref(k); }
/** * mega_aes_key_is_loaded: * @aes_key: a #MegaAesKey * * Check if key was successfully loaded. This is useful, when you've created key * with #mega_aes_key_new_from_ubase64, and you want to check if it was created * correctly. * * Returns: TRUE if loaded correctly. */ gboolean mega_aes_key_is_loaded(MegaAesKey* aes_key) { g_return_val_if_fail(MEGA_IS_AES_KEY(aes_key), FALSE); return aes_key->priv->loaded; }