Firefox上でOpenPGP.jsのReadableStreamの復号をするとエラー「This message / key probably does not conform to a valid...」する問題のPiping UIでの解決方法
やりたいこと
code:エラーメッセージ
Error: Error during parsing. This message / key probably does not conform to a valid OpenPGP format.
これを対処する方法。
暗号化でも同様に起こる現象のはずで同じ方法で解決できるはず。
利用するFirefoxのバージョン
72.0.2 (64-bit)
エラーが起こる原因
Firefoxだと完全にReadableStreamをサポートできていなくOpenPGP.jsを使うとpolyfillされる。 OpenPGP.jsだとReadableStreamにpolyfillが必要なときはopenpgp.decrypt({message: myNativeReadableStream, ...})のようにmyNativeReadableStreamがnativeの(polyfill前の)ReadableStreamが来ると上記のエラーがでる。 nativeのReadableStreamが来る例と言えばawait fetch()をしたときなど。
今後のFirefoxのバージョンアップしたときの上記を試してうまくいくようになることも確認できるはず。 対処方法
openpgp.decrypt()はpolyfillなReadableStreamなら復号できる。(openpgp.encrypt()も同様のはず)
そのためnative ReadableStreamと polyfillなReadableStreamを変換する。
を使ってその変換ができる。以下でインストールできる。 $ npm i -S @mattiasbuelens/web-streams-adapter
web-streams-adapterの使い方
使い方として、以下のようにcreateReadableStreamWrapper()の引数にどちらかにReadableStreamを与えるとそれに変換するための関数が返ってくる。
code:js
// 変換するための関数が返ってくる。
const toPolyfillReadable = createReadableStreamWrapper( native の ReadableStream );
const toNativeReadable = createReadableStreamWrapper( polyfill の ReadableStream );
web-streams-adapterを使う
importScripts('openpgp/openpgp.min.js')するとpolyfillされるので前にconst NativeReadableStream = ReadableStream;のように退避させている。
importScripts()後でも元々のReadableStreamにアクセスする手法が調べれば見つかるかもしれないが一番確実だしとりあえずこの方法をとる。
code:sw.js
const NativeReadableStream = ReadableStream;
importScripts('openpgp/openpgp.min.js');
importScripts('web-streams-adapter/web-streams-adapter.js');
...
あと下記の関数を作った。
toPolyfillReadableIfNeed()はpolyfillされていれば、nativeのReadableStreamをpolyfillのReadableStreamに変換する。されていなければ変換せずにそのまま返す。
toNativeReadableIfNeedはpolyfillされていれば、polyfillのReadableStreamをnativeに変換する。されていなければそのまま返す。
import { ReadableStream as PolyfillReadableStream } from 'web-streams-polyfill';を使ってpolyfillのReadableStreamクラスを取得することができてそうすると条件分岐いらなくなりそうだが、Service Workerでimportするためにファイルを/publicにコピーするnpmスクリプト書いたりしてWorkboxに合うようにService Workerのスクリプトをうまく生成する方法を見つけておらずそういうことが背景で以下のような関数を定義している。 code:js
/**
* Convert a native ReadableStream to polyfill ReadableStream if ReadableStream class is polyfill.
*
* @param readableStream Native ReadableStream
* @returns {*}
*/
function toPolyfillReadableIfNeed(readableStream) {
// If not polyfilled
if (NativeReadableStream === ReadableStream) {
return readableStream;
// If ReadableStream is polyfill
} else {
// NOTE: ReadableStream is polyfill ReadableStream in this condition
const toPolyfillReadable = WebStreamsAdapter.createReadableStreamWrapper(ReadableStream);
// Convert a native ReadableStream to polyfill ReadableStream
return toPolyfillReadable(readableStream);
}
}
/**
* Convert a polyfill ReadableStream to native ReadableStream if ReadableStream class is polyfill.
*
* @param readableStream Native ReadableStream or polyfill ReadableStream
* @returns {*}
*/
function toNativeReadableIfNeed(readableStream) {
// If not polyfilled
if (NativeReadableStream === ReadableStream) {
return readableStream;
// If ReadableStream is polyfill
} else {
const toNativeReadable = WebStreamsAdapter.createReadableStreamWrapper(NativeReadableStream);
// Convert a polyfill ReadableStream to native ReadableStream
return toNativeReadable(readableStream);
}
}
そうするとtoPolyfillReadableIfNeedを使って以下のようにres.bodyというnativeのReadableStreamをpolyfillのReadableStreamに変換して復号処理ができる。暗号化も同様のはず。
code:js
const decrypted = await openpgp.decrypt({
message: await openpgp.message.read(toPolyfillReadableIfNeed(res.body)),
format: 'binary'
});
plainStream = decrypted.data;とすると、これをplainStreamがpolyfillのReadableStreamだったりnativeのReadableStreamだったりすることがある。これをnativeのReadableStreamに変換するために以下のようにする。
code:js
toNativeReadableIfNeed(plainStream)
new Response(myReadableStream)のmyReadableStreamがpolyfillのReadableStreamだとレスポンスが[object Object]という文字列になってしまうのでそれを考え実装する必要があった。