Libsodium crypto_secretstream_xchacha20poly1305_init_pull не может инициализировать состояние, несмотря на правильный клJavascript

Форум по Javascript
Ответить
Anonymous
 Libsodium crypto_secretstream_xchacha20poly1305_init_pull не может инициализировать состояние, несмотря на правильный кл

Сообщение Anonymous »

Я реализую сквозное шифрование для больших файлов, используя libsodium.js с crypto_secretstream_xchacha20poly1305. Шифрование работает нормально, но дешифрование завершается с ошибкой "State konnte nicht Initialisiert Werden" (Состояние не может быть инициализировано).
При попытке инициализировать состояние дешифрования с помощью crypto_secretstream_xchacha20poly1305_init_pull() происходит сбой, хотя:
  • Ключ имеет длину 32 байта (правильная длина для crypto_secretstream_xchacha20poly1305_KEYBYTES)
  • Заголовок составляет 24 байта (правильная длина для crypto_secretstream_xchacha20poly1305_HEADERBYTES)
  • И ключ, и заголовок передаются как Uint8Array

Код: Выделить всё

Encryptor: Crypto Header
Uint8Array(24) [ 119, 236, 242, 100, 144, 128, 29, 94, 227, 167, … ]

Generierter Key (Base64URL): dZqmIY8RjO_flrBXm71jbg5VeZfl5EYhZNICo5qw0fM ed:17:21
Starte Entschlüsselung...
CryptoHeader:

Decryptor: CryptoHeader:
Uint8Array(24) [ 119, 236, 242, 100, 144, 128, 29, 94, 227, 167, … ]
:1:147461

Decryptor: Key bytes:
Uint8Array(32) [ 117, 154, 166, 33, 143, 17, 140, 239, 223, 150, … ]

Decryptor: Header length: 24 ed:207:25

Decryptor: Key length: 32

Decryptor init error: Error: State konnte nicht initialisiert werden
init /ed:218
decryptAll /ed:258
 /ed:444
async* /ed:420
:1:147461

decryptAll error: Error: State konnte nicht initialisiert werden
init /ed:218
decryptAll /ed:258
 /ed:444
async* /ed:420
:1:147461

Uncaught (in promise) Error: State konnte nicht initialisiert werden
Мой минимальный пример:

Код: Выделить всё





Datei Encrypt/Decrypt Test



Datei Encrypt/Decrypt Test

Start Verschlüsselung/Entschlüsselung


const log = (msg) => {
console.log(msg);
const outDiv = document.getElementById("output");
outDiv.innerHTML += msg + "
";
};

const DEBUG_ENCRYPTOR = true; // Setze auf false, wenn kein Logging

class Encryptor {
sodium = null;
key = null;
state = null;
cryptoHeader = null;
encoder = new TextEncoder();
CHUNK_SIZE = 64 * 1024;  // 64 KB, kann auf 1MB hochgesetzt werden

constructor(keyString = null) {
this.userKeyString = keyString;
}

async init() {
this.sodium = await SodiumLoader.getInstance();

if (DEBUG_ENCRYPTOR) console.log("Encryptor: Zufälliger Key erzeugt");
this.key = this.sodium.randombytes_buf(this.sodium.crypto_secretstream_xchacha20poly1305_KEYBYTES);

const result = this.sodium.crypto_secretstream_xchacha20poly1305_init_push(this.key);
this.state = result.state;
this.cryptoHeader = result.header;

if (DEBUG_ENCRYPTOR) console.log("Encryptor: Crypto-State initialisiert");
}

__encryptChunk(data) {
return this.sodium.crypto_secretstream_xchacha20poly1305_push(
this.state,
data,
null,
this.sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE
);
}

async __streamFile(file, outputChunks, progressCallback, offsetStart, totalSize) {
if (DEBUG_ENCRYPTOR) console.log(`Encryptor: Verschlüssele Datei ${file.name}, Größe: ${file.size}`);

const reader = file.stream().getReader();
let leftover = new Uint8Array(0);
let processed = 0;

while (true) {
const { value, done } = await reader.read();
if (done) break;

let buffer = new Uint8Array(leftover.length + value.length);
buffer.set(leftover, 0);
buffer.set(value, leftover.length);

let offset = 0;
while (offset + this.CHUNK_SIZE   ({ name: f.name, size: f.size }))
}));
const headerLengthBytes = new Uint8Array(4);
new DataView(headerLengthBytes.buffer).setUint32(0, headerBytes.length, false);

const [headerLenFrame, headerLenData] = this.__encryptChunk(headerLengthBytes);
outputChunks.push(headerLenFrame, headerLenData);

const [headerJsonFrameLen, headerJsonFrame] = this.__encryptChunk(headerBytes);
outputChunks.push(headerJsonFrameLen, headerJsonFrame);

// Dateien verschlüsseln
let offsetStart = 0;
for (const file of fileRefs) {
await this.__streamFile(file, outputChunks, progressCallback, offsetStart, totalSize);
offsetStart += file.size;
}

if (DEBUG_ENCRYPTOR) console.log("Encryptor: Verschlüsselung abgeschlossen, Blob erzeugen");

// Blob / File erzeugen
const result = new File(outputChunks, 'container.mycnt', { type: 'application/octet-stream' });
return result;
}

getKey() {
return this.key;
}

getKeyB64() {
return btoa(String.fromCharCode(...this.key));
}

getKeyB64URL() {
return sodium.to_base64(
this.key,
sodium.base64_variants.URLSAFE_NO_PADDING
);
}
}

const DEBUG_DECRYPTOR = true; // Setze auf false, um Logs auszuschalten
class Decryptor {
sodium = null;
key = null;
state = null;
cryptoHeader = null;
decoder = new TextDecoder();
reader = null;
leftover = new Uint8Array(0);

constructor(file, keyInput) {
this.payload = file;
this.keyInput = keyInput;  // nur speichern, NICHT dekodieren
}

async init() {
try {
this.sodium = await SodiumLoader.getInstance();

if (typeof this.keyInput === "string") {
this.key = this.sodium.from_base64(
this.keyInput,
this.sodium.base64_variants.URLSAFE_NO_PADDING
);
} else if (this.keyInput instanceof Uint8Array) {
this.key = this.keyInput;
} else {
throw new Error("Key muss Base64URL-String oder Uint8Array sein");
}

const expectedKeyLength = this.sodium.crypto_secretstream_xchacha20poly1305_KEYBYTES;
if (this.key.length !== expectedKeyLength) {
throw new Error(`Falsche Key-Länge: ${this.key.length} Bytes (erwarte ${expectedKeyLength})`);
}

// Header direkt aus File lesen
const headerSize = this.sodium.crypto_secretstream_xchacha20poly1305_HEADERBYTES;
const arrayBuffer = await this.payload.slice(0, headerSize).arrayBuffer();
this.cryptoHeader = new Uint8Array(arrayBuffer);

if (DEBUG_DECRYPTOR) console.warn("Decryptor: CryptoHeader: ", this.cryptoHeader);

// Reader starten
this.reader = this.payload.slice(headerSize).stream().getReader();

if (DEBUG_DECRYPTOR) {
console.log("Decryptor: Key bytes:", this.key);
console.log("Decryptor: Header length:", this.cryptoHeader.length);
console.log("Decryptor: Key length:", this.key.length);
}

// State initialisieren
const initResult = this.sodium.crypto_secretstream_xchacha20poly1305_init_pull(
this.cryptoHeader,
this.key
);

if (!initResult || !initResult.state) {
throw new Error("State konnte nicht initialisiert werden");
}

this.state = initResult.state;

if (DEBUG_DECRYPTOR) console.log("Decryptor: Erfolgreich initialisiert");

} catch (error) {
console.error("Decryptor init error:", error);
throw error;
}
}

async readNextFrame() {
const { value, done } = await this.reader.read();
if (done) return null;
return value;
}

async __decryptChunk(chunk) {
if (!chunk || chunk.length === 0) return null;

try {
if (!this.state) throw new Error("State nicht initialisiert");

const result = this.sodium.crypto_secretstream_xchacha20poly1305_pull(this.state, chunk);
i
  • Длина ключа составляет ровно 32 байта.
  • Длина заголовка составляет ровно 24 байта.
  • Кодирование/декодирование ключей последовательно использует URLSAFE_NO_PADDING.
  • При передаче файлов данные не повреждаются.
  • Использование одной и той же версии libsodium.js для обеих операций

    libsodium.js (последняя версия)
  • Chrome 120+, Firefox 146+
  • Размеры файлов: 1–100 МБ

Зависимости:
  • Версия LibSodium
Что может привести к сбою crypto_secretstream_xchacha20poly1305_init_pull() даже при правильной длине ключа и заголовка? Существуют ли распространенные ошибки при извлечении заголовка или декодировании ключа, которые я могу упустить?

Подробнее здесь: https://stackoverflow.com/questions/798 ... -to-initla
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Javascript»