static void test_asn1_bit_string (Test *test, gconstpointer unused) { GNode *asn; GBytes *data; gboolean ret; GBytes *source, *target; gsize target_bits, source_bits; asn = egg_asn1x_create (test_asn1_tab, "TestBitString"); g_assert ("asn test structure is null" && asn != NULL); /* Create a string */ source = g_bytes_new (TEST_STRING, strlen(TEST_STRING)); g_return_if_fail (source); source_bits = g_bytes_get_size(source)*8; /* Write the string out */ ret = gkm_data_asn1_write_bit_string (egg_asn1x_node (asn, "data", NULL), source, source_bits); g_assert ("couldn't write string to asn1" && ret); /* Now encode the whole caboodle */ data = egg_asn1x_encode (asn, NULL); g_assert ("encoding asn1 didn't work" && data != NULL); egg_asn1x_destroy (asn); /* Now decode it all nicely */ asn = egg_asn1x_create_and_decode (test_asn1_tab, "TestBitString", data); g_assert (asn != NULL); ret = gkm_data_asn1_read_bit_string (egg_asn1x_node (asn, "data", NULL), &target, &target_bits); egg_asn1x_destroy (asn); g_assert ("couldn't read bit string from asn1" && ret); g_assert ("bit string returned is null" && target != NULL); g_assert ("Source and target length differ" && target_bits == source_bits); g_assert ("Bit strings differ" && g_bytes_equal (source, target)); g_bytes_unref (data); g_bytes_unref (source); g_bytes_unref (target); }
/** * CacheEntryPasswordValidate: * @self: Instance with salt/hash to check @password against. * @password: Password to check. * @error: (out)(allow-none): Error return location or #NULL. * * Returns: #TRUE if the password matches, or #FALSE if there was an error and * @error was set. Not matching is considered an error. */ gboolean CacheEntryPasswordValidate(CacheEntry *self, const gchar *password, GError **error) { GBytes *hash = NULL; GDateTime *now = NULL; gboolean result = FALSE; if (!self->salt || !self->hash) { g_set_error(error, CACHE_ENTRY_ERROR, CACHE_ENTRY_EMPTY_ERROR, "No cached password is available"); return FALSE; } g_assert(self->salt); g_assert(password); g_assert(strlen(password) > 0); hash = CacheUtilHashPassword(self->algorithm, self->salt, password); if (!hash) { g_set_error(error, CACHE_ENTRY_ERROR, CACHE_ENTRY_CORRUPT_ERROR, "Unknow hash algorithm selected"); return FALSE; } now = g_date_time_new_now_utc(); CacheEntryUpdateTime(&self->last_tried, now); if (g_bytes_equal(hash, self->hash)) { CacheEntryUpdateTime(&self->last_used, now); self->tries = 0; result = TRUE; } else { self->tries++; g_set_error(error, CACHE_ENTRY_ERROR, CACHE_ENTRY_PASSWORD_ERROR, "Password doesn't match cached value"); } g_bytes_unref(hash); g_date_time_unref(now); return result; }
/** * dfu_patch_apply: * @self: a #DfuPatch * @blob: a #GBytes, typically the old firmware image * @flags: a #DfuPatchApplyFlags, e.g. %DFU_PATCH_APPLY_FLAG_IGNORE_CHECKSUM * @error: a #GError, or %NULL * * Apply the currently loaded patch to a new firmware image. * * Return value: A #GBytes, typically saved as the new firmware file **/ GBytes * dfu_patch_apply (DfuPatch *self, GBytes *blob, DfuPatchApplyFlags flags, GError **error) { DfuPatchPrivate *priv = GET_PRIVATE (self); const guint8 *data_old; gsize sz; gsize sz_max = 0; g_autofree guint8 *data_new = NULL; g_autoptr(GBytes) blob_checksum_new = NULL; g_autoptr(GBytes) blob_checksum = NULL; g_autoptr(GBytes) blob_new = NULL; /* not loaded yet */ if (priv->chunks->len == 0) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "no patches loaded"); return NULL; } /* get the hash of the old firmware file */ blob_checksum = dfu_patch_calculate_checksum (blob); if ((flags & DFU_PATCH_APPLY_FLAG_IGNORE_CHECKSUM) == 0 && !g_bytes_equal (blob_checksum, priv->checksum_old)) { g_autofree gchar *actual = _g_bytes_to_string (blob_checksum); g_autofree gchar *expect = _g_bytes_to_string (priv->checksum_old); g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "checksum for source did not match, expected %s, got %s", expect, actual); return NULL; } /* get the size of the new image size */ for (guint i = 0; i < priv->chunks->len; i++) { DfuPatchChunk *chunk = g_ptr_array_index (priv->chunks, i); gsize chunk_sz = g_bytes_get_size (chunk->blob); if (chunk->off + chunk_sz > sz_max) sz_max = chunk->off + chunk_sz; } /* first, copy the data buffer */ data_old = g_bytes_get_data (blob, &sz); if (sz_max < sz) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "binary patch cannot truncate binary"); return NULL; } if (sz == sz_max) { g_debug ("binary staying same size: %" G_GSIZE_FORMAT, sz); } else { g_debug ("binary growing from: %" G_GSIZE_FORMAT " to %" G_GSIZE_FORMAT, sz, sz_max); } data_new = g_malloc0 (sz_max); memcpy (data_new, data_old, MIN (sz, sz_max)); for (guint i = 0; i < priv->chunks->len; i++) { DfuPatchChunk *chunk = g_ptr_array_index (priv->chunks, i); const guint8 *chunk_data; gsize chunk_sz; /* bigger than the total size */ chunk_data = g_bytes_get_data (chunk->blob, &chunk_sz); if (chunk->off + chunk_sz > sz_max) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "cannot apply chunk as larger than max size"); return NULL; } /* apply one chunk */ g_debug ("applying chunk %u/%u @0x%04x (length %" G_GSIZE_FORMAT ")", i + 1, priv->chunks->len, chunk->off, chunk_sz); memcpy (data_new + chunk->off, chunk_data, chunk_sz); } /* check we got the desired hash */ blob_new = g_bytes_new (data_new, sz_max); blob_checksum_new = dfu_patch_calculate_checksum (blob_new); if ((flags & DFU_PATCH_APPLY_FLAG_IGNORE_CHECKSUM) == 0 && !g_bytes_equal (blob_checksum_new, priv->checksum_new)) { g_autofree gchar *actual = _g_bytes_to_string (blob_checksum_new); g_autofree gchar *expect = _g_bytes_to_string (priv->checksum_new); g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "checksum for result did not match, expected %s, got %s", expect, actual); return NULL; } /* success */ return g_steal_pointer (&blob_new); }
/** * dfu_patch_create: * @self: a #DfuPatch * @blob1: a #GBytes, typically the old firmware image * @blob2: a #GBytes, typically the new firmware image * @error: a #GError, or %NULL * * Creates a patch from two blobs of memory. * * The blobs should ideally be the same size. If @blob2 is has grown in size * the binary diff will still work but the algorithm will probably not perform * well unless the majority of data has just been appended. * * As an additional constrainst, @blob2 cannot be smaller than @blob1, i.e. * the firmware cannot be truncated by this format. * * Return value: %TRUE on success **/ gboolean dfu_patch_create (DfuPatch *self, GBytes *blob1, GBytes *blob2, GError **error) { DfuPatchPrivate *priv = GET_PRIVATE (self); DfuPatchCreateHelper helper; const guint8 *data1; const guint8 *data2; gsize sz1 = 0; gsize sz2 = 0; guint32 same_sz = 0; g_return_val_if_fail (DFU_IS_PATCH (self), FALSE); g_return_val_if_fail (blob1 != NULL, FALSE); g_return_val_if_fail (blob2 != NULL, FALSE); /* are the blobs the same */ if (g_bytes_equal (blob1, blob2)) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "old and new binaries are the same"); return FALSE; } /* cannot reuse object */ if (priv->chunks->len > 0) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "patch has already been loaded"); return FALSE; } /* get the hash of the old firmware file */ priv->checksum_old = dfu_patch_calculate_checksum (blob1); priv->checksum_new = dfu_patch_calculate_checksum (blob2); /* get the raw data, and ensure they are the same size */ data1 = g_bytes_get_data (blob1, &sz1); data2 = g_bytes_get_data (blob2, &sz2); if (sz1 > sz2) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "firmware binary cannot go down, got " "%" G_GSIZE_FORMAT " and %" G_GSIZE_FORMAT, sz1, sz2); return FALSE; } if (sz1 == sz2) { g_debug ("binary staying same size: %" G_GSIZE_FORMAT, sz1); } else { g_debug ("binary growing from: %" G_GSIZE_FORMAT " to %" G_GSIZE_FORMAT, sz1, sz2); } /* start the dumb comparison algorithm */ helper.diff_start = 0; helper.diff_end = 0xffff; helper.blob = blob2; for (gsize i = 0; i < sz1 || i < sz2; i++) { if (i < sz1 && i < sz2 && data1[i] == data2[i]) { /* if we got enough the same, dump what is pending */ if (++same_sz > sizeof(DfuPatchChunkHeader) * 2) dfu_patch_flush (self, &helper); continue; } if (helper.diff_end == 0xffff) helper.diff_start = (guint32) i; helper.diff_end = (guint32) i; same_sz = 0; } dfu_patch_flush (self, &helper); return TRUE; }