lesson-2_ノートを復号しよう
🔓 ノートを復号する
前回のレッスンで、ノートを暗号化する機能を実装しました。このレッスンでは、暗号化されたノートを復号します。
それでは、コードを書いていきましょう。lib/cryptoService.ts
内に定義されているdecryptNote
関数を下記の内容に更新します。
public async decryptNote(data: string): Promise<string> {
if (this.symmetricKey === null) {
throw new Error('Not found symmetric key');
}
// テキストデータとIVを分離します。
const base64IvLength: number = (CryptoService.INIT_VECTOR_LENGTH / 3) * 4;
const decodedIv = data.slice(0, base64IvLength);
const decodedEncryptedNote = data.slice(base64IvLength);
// 一文字ずつ`charCodeAt()`で文字コードに変換します。
const encodedIv = this.base64ToArrayBuffer(decodedIv);
const encodedEncryptedNote = this.base64ToArrayBuffer(decodedEncryptedNote);
const decryptedNote: ArrayBuffer = await window.crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: encodedIv,
},
this.symmetricKey,
encodedEncryptedNote,
);
const decodedDecryptedNote: string = new TextDecoder().decode(
decryptedNote,
);
return decodedDecryptedNote;
}
追加したコードを確認しましょう。
バックエンドキャニスターには、Base64エンコーディングをしたiv(initialization vector)を一緒に保存しました。ノートの復号を行う前に、ivを切り離して元のデータ型に戻す必要があります。
Base64エンコーディングは、3バイトのバイナリデータを4文字のASCII文字列に変換します。そのため、下記のように計算を行い"iv"と"暗号化したノート"に分けます。
// テキストデータとIVを分離します。
const base64IvLength: number = (CryptoService.INIT_VECTOR_LENGTH / 3) * 4;
const decodedIv = data.slice(0, base64IvLength);
const decodedEncryptedNote = data.slice(base64IvLength);
それぞれを元のデータ型(バイナリデータ)に戻します。
// 一文字ずつ`charCodeAt()`で文字コードに変換します。
const encodedIv = this.base64ToArrayBuffer(decodedIv);
const encodedEncryptedNote = this.base64ToArrayBuffer(decodedEncryptedNote);
decryptメソッドを使用して、復号を行います。
const decryptedNote: ArrayBuffer = await window.crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: encodedIv,
},
this.symmetricKey,
encodedEncryptedNote
);
これで、ノートの復号を行うdecryptNote関数が完成しました。では、この関数をコンポーネントから呼び出してみましょう。ノートを復号するタイミングは、ユーザーがノートを取得したときです。
routes/notes/index.tsx
のNotesコンポーネントに定義されたgetNotes
関数を更新します。
const getNotes = async () => {
if (auth.status !== "SYNCED") {
console.error(`CryptoService is not synced.`);
return;
}
try {
const decryptedNotes = new Array<EncryptedNote>();
const notes = await auth.actor.getNotes();
// 暗号化されたノートを復号します。
for (const note of notes) {
const decryptedData = await auth.cryptoService.decryptNote(note.data);
decryptedNotes.push({
id: note.id,
data: decryptedData,
});
}
setNotes(decryptedNotes);
} catch (err) {
showMessage({
title: "Failed to get notes",
status: "error",
});
}
};