Compartir a través de


Descifrar instantáneas exportadas desde Recall

En esta guía se muestra a los desarrolladores cómo descifrar instantáneas exportadas Recall para su uso en aplicaciones. Aprenderá el proceso de descifrado completo con ejemplos de código de trabajo que puede implementar inmediatamente.

La exportación de Recall instantáneas solo se admite en dispositivos del Espacio Económico Europeo (EEE). La exportación de Recall instantáneas es un proceso iniciado por el usuario y es específico para cada usuario. Las instantáneas exportadas se cifran.

Obtenga más información sobre cómo exportar Recall instantáneas o consulte la Recall información general sobre cómo funciona esta característica respaldada por ia.

Prerrequisitos

La opción para exportar Recall instantáneas solo está disponible en dispositivos Copilot+ PC en el Espacio Económico Europeo (EEE) que ejecuta la última compilación de la versión preliminar del Programa Windows Insider.

Antes de comenzar, necesitará lo siguiente:

  • Instantáneas exportadas: el usuario primero debe exportar Recall instantáneas y proporcionar la ruta de acceso de la carpeta donde se guardan.
  • Código de exportación: el código de exportación de 32 caracteres Recall proporcionado durante la exportación de instantáneas.
  • Carpeta de salida: ruta de acceso de la carpeta de destino en la que se guardarán los archivos de .jpg y .json descifrados asociados a las instantáneas exportadas.

Cómo descifrar instantáneas exportadas Recall

Comienza con el código de muestra para descifrar las instantáneas exportadas Recall en el repositorio de GitHub RecallSnapshotsExport. Siga el proceso paso a paso siguiente para comprender cómo funciona el descifrado.

Clave de exportación de proceso

El usuario deberá proporcionar la ubicación (ruta de acceso de carpeta) donde se guardaron sus instantáneas exportadas Recall , además del Recall código de exportación que se le pidió que guardara durante la configuración inicial Recall . El Recall código de exportación tiene un aspecto similar al siguiente: 0a0a-0a0a-1111-bbbb-2222-3c3c-3c3c-3c3c

En primer lugar, quite el guión – para que resulte en una cadena de 32 caracteres: 0a0a0a0a1111bbbb22223c3c3c3c3c3c

std::wstring UnexpandExportCode(std::wstring code)
{
    if (code.size() > 32)
    {
        code.erase(std::remove(code.begin(), code.end(), ' '), code.end()); // Remove spaces
        code.erase(std::remove(code.begin(), code.end(), '-'), code.end()); // Remove hyphens
    }


    if (code.size() != 32)
    {
        std::wcout << L"The export code has incorrect number of characters."<< std::endl;
    }


    return code;
}

Consulte el código de ejemplo.

A continuación, cree una matriz que contenga el valor de byte para cada par de dígitos hexadecimales de forma sucesiva.

std::vector<uint8_t> HexStringToBytes(const std::wstring& hexString)
{
    std::vector<uint8_t> bytes;
    if (hexString.length() % 2 != 0)
    {
        throw std::invalid_argument("Hex string must have an even length");
    }


    for (size_t i = 0; i < hexString.length(); i += 2)
    {
        std::wstring byteString = hexString.substr(i, 2);
        uint8_t byte = static_cast<uint8_t>(std::stoi(byteString, nullptr, 16));
        bytes.push_back(byte);
    }


    return bytes;
}

Consulte el código de ejemplo.

A continuación, tome esa matriz y calcule el hash SHA256, lo que da como resultado un valor de 32 bytes, que es la clave de exportación. Ahora se puede descifrar cualquier número de instantáneas mediante la clave de exportación resultante.

    std::vector<uint8_t> exportKeyBytes(c_keySizeInBytes);
    THROW_IF_NTSTATUS_FAILED(BCryptHash(
        BCRYPT_SHA256_ALG_HANDLE,
        nullptr,
        0,
        exportCodeBytes.data(),
        static_cast<ULONG>(exportCodeBytes.size()),
        exportKeyBytes.data(),
        c_keySizeInBytes));

Consulte el código de ejemplo.

Descifrar las instantáneas cifradas

El diseño de una instantánea (en formato little-endian): | uint32_t version | uint32_t encryptedKeySize | uint32_t encryptedContentSize | uint32_t contentType | uint8_t[KeySIze] encryptedContentKey | uint8_t[ContentSize] encryptedContent |

En primer lugar, lea los cuatro valores de uint32_t.

    EncryptedSnapshotHeader header{};
    reader.ByteOrder(winrt::ByteOrder::LittleEndian);


    header.Version = reader.ReadUInt32();
    header.KeySize = reader.ReadUInt32();
    header.ContentSize = reader.ReadUInt32();
    header.ContentType = reader.ReadUInt32();

Consulte el código de ejemplo.

A continuación, compruebe que la versión tiene el valor 2.

    if (header.Version != 2)
    {
        throw std::runtime_error("Insufficient data header version.");
    }

Consulte el código de ejemplo.

A continuación, lee el contenido de encryptedKeyContent.

    std::vector<uint8_t> keybytes(header.KeySize);
    reader.ReadBytes(keybytes);

Consulte el código de ejemplo.

Descifra el contenidoClaveEncriptado

wil::unique_bcrypt_key DecryptExportKey(BCRYPT_KEY_HANDLE key, std::span<uint8_t const> encryptedKey)
{
    THROW_HR_IF(E_INVALIDARG, encryptedKey.size() != c_totalSizeInBytes);


    BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO AuthInfo{};
    BCRYPT_INIT_AUTH_MODE_INFO(AuthInfo);
    AuthInfo.pbNonce = const_cast<uint8_t*>(encryptedKey.data()); 
    AuthInfo.cbNonce = c_nonceSizeInBytes;
    AuthInfo.pbTag = const_cast<uint8_t*>(encryptedKey.data() + c_nonceSizeInBytes + c_childKeySizeInBytes);
    AuthInfo.cbTag = c_tagSizeInBytes;


    uint8_t decryptedKey[c_childKeySizeInBytes] = { 0 };


    ULONG decryptedByteCount{};
    THROW_IF_FAILED(HResultFromBCryptStatus(BCryptDecrypt(
        key,
        const_cast<uint8_t*>(encryptedKey.data() + c_nonceSizeInBytes),
        c_childKeySizeInBytes,
        &AuthInfo,
        nullptr,
        0,
        decryptedKey,
        sizeof(decryptedKey),
        &decryptedByteCount,
        0)));


    wil::unique_bcrypt_key childKey;
    THROW_IF_NTSTATUS_FAILED(
        BCryptGenerateSymmetricKey(BCRYPT_AES_GCM_ALG_HANDLE, &childKey, nullptr, 0, decryptedKey, c_childKeySizeInBytes, 0));


    return childKey;
}

Consulte el código de ejemplo.

uso de exportKey

    wil::unique_bcrypt_key exportKey;
    THROW_IF_NTSTATUS_FAILED(BCryptGenerateSymmetricKey(
       BCRYPT_AES_GCM_ALG_HANDLE, &exportKey, nullptr, 0, exportKeyBytes.data(), static_cast<ULONG>(exportKeyBytes.size()), 0));

Consulte el código de ejemplo.

Para obtener contentKey (el algoritmo criptográfico es AES_GCM)

    wil::unique_bcrypt_key contentKey = DecryptExportKey(exportKey.get(), keybytes);

Consulte el código de ejemplo.

Leer contenidoCifrado

    std::vector<uint8_t> contentbytes(header.ContentSize);
    reader.ReadBytes(contentbytes);

Consulte el código de ejemplo.

Descifrar contenidoCifrado

std::vector<uint8_t> DecryptPackedData(BCRYPT_KEY_HANDLE key, std::span<uint8_t const> payload)
{
    THROW_HR_IF(E_INVALIDARG, payload.size() < c_tagSizeInBytes);
    const auto dataSize = payload.size() - c_tagSizeInBytes;
    const auto data = payload.data();


    uint8_t zeroNonce[c_nonceSizeInBytes] = { 0 };
    BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo{};
    BCRYPT_INIT_AUTH_MODE_INFO(authInfo);
    authInfo.pbNonce = zeroNonce;
    authInfo.cbNonce = c_nonceSizeInBytes;
    authInfo.pbTag = const_cast<uint8_t*>(payload.data() + dataSize);
    authInfo.cbTag = c_tagSizeInBytes;


    std::vector<uint8_t> decryptedContent(dataSize);
    ULONG decryptedSize = 0;
    const auto result = BCryptDecrypt(
        key, const_cast<uint8_t*>(data), static_cast<ULONG>(dataSize), &authInfo, nullptr, 0, decryptedContent.data(), static_cast<ULONG>(dataSize), &decryptedSize, 0);
    decryptedContent.resize(decryptedSize);


    THROW_IF_FAILED(HResultFromBCryptStatus(result));


    return decryptedContent;
}

Consulte el código de ejemplo.

con contentKey (el algoritmo criptográfico es AES_GCM)

    std::vector<uint8_t> decryptedContent = DecryptPackedData(contentKey.get(), contentbytes);

Consulte el código de ejemplo.

Generar el contenido de la instantánea descifrada Recall en forma de una imagen .jpg con los metadatos correspondientes en formato .json en la ruta de carpeta designada.

void WriteSnapshotToOutputFolder(winrt::StorageFolder const& outputFolder, winrt::hstring const& fileName, winrt::IRandomAccessStream const& decryptedStream)

Consulte el código de ejemplo.

La salida esperada incluirá:

  • Instantáneas descifradas guardadas como archivos .jpg.
  • Metadatos correspondientes guardados como archivos .json.

Ambos tipos de archivo compartirán el mismo nombre de archivo y se encontrarán en la carpeta de salida especificada.

Más información sobre Recall