『暗号解読』がめちゃくちゃ面白かった
なぜか本棚にあって未読だった『暗号解読(上・下)』を読んでみました。
暗号解読(上) (新潮文庫) | サイモン シン, Singh,Simon, 薫, 青木 |本 | 通販 | Amazon
結論としては世界史、言語学、数学、物理学などさまざまな分野が統合的に組み込まれていて、めちゃくちゃ面白かったです。
暗号技術は現代において必須ですが、中身は正直難しいです。
それを歴史的な側面から「なぜ使われるようになったのか」「どうやって進歩してきたのか」「どのように作られているのか」といったことを知ることができました。
以下ざっと面白かった部分を思い出しながら書いてみます。
暗号方式
暗号には大きく2種類の方式がある
- ステガノグラフィー
- 最古の秘密通信手段
- 文書自体を読めなくするのではなく、そもそも見つからないようにする
- 伝令の髪の毛を剃って、頭に文字を書いて、伸ばす
- カルロスゴーンがカバンに乗って出国したのも一種そうかも
- クリプトグラフィー
- バレてしまっても読めなければok
- 暗号化をすること
カエサル式暗号
初期の頃の代表的な暗号として、カエサル式暗号がある
頻度分析
暗号解読の手法に頻度分析というものが発明された
- 言語学的な側面も使って分析していく
- 暗号文に一番多く出現している文字に、英語で最も多く使われる「e」を割り当てるなど
- 単アルファベット換字式暗号はヌルを使うことで混乱させることができる
イギリスのエリザベス女王 vs スコットランドメアリー女王
暗号初期の頃に起きた「暗号大事!!」となった争いの話
- 弱い暗号を使うくらいなら、最初から暗号など使わない方がましだ」
- 弱い暗号は偽りの安心感を与える
- 暗号を信頼しすぎたためまさか中身が書き換えられているとも思わず、一網打尽にされた
- もし暗号を使っていなかったらもっと警戒したかもしれない
ヴィジュネル暗号
- 平文中の同じ文字がその都度違う方法で暗号化される
- 大変すぎて使われなかった
ワンタイム・パッド法
- 理論上は最強だが実用上の欠点がある。
- ランダムな鍵を大量に作る必要がある
- 鍵を配送しなければならない
ヒエログリフ
ハヴァホ族
これは盲点だった 面白い発想
- 音階や音節が違うのでそもそも聞き取れない
- 盗聴しても聞き取れないので意味がない
無線の発達
- 無線は全て盗聴されるので強力な暗号が必要という前提ができた
ドイツのエニグマ
- 常に暗号解読者が暗号作成者より有利に立ってきたが、解読のモチベーションは必死さとかになるので、ドイツがエニグマを使用して難しい暗号を作った時にはイギリス、フランスの解読のモチベは下がっていた
- ドイツは第一次世界大戦で負けて、もう敵ではないと思っていたから
- ポーランドが解読していたが、連携が遅くドイツに攻め込まれる
- 第二次世界大戦時、連合国側のチューリングがボムというものを作ってエニグマを解読、勝利に貢献した
- 今までは言語学者が重用されていたが、これからの暗号解読には数学者が大事
RSA暗号
- 解読者の頭文字を取ってRSAとなっている
- 鍵配送問題を解決した
- 双方関数ではなく、一方向関数であるの手法であるモジュロ演算に素数を混ぜ込んで使っている
- 素因数分解は十分に大きい数であればスーパーコンピューターでも計算に何億年かかる
- 今のところ解読はされない
- 今のところ暗号作成者が暗号解読者より有利に立っている
- しかし歴史上必ず暗号は解読されてきた
- 素因数分解は今のところ総当たりで計算することになっているが、総当たりしなくてもできるようになるかもしれない
- もしくは計算自体がめっちゃ早くなるかもしれない
量子コンピューター
- 概念が難しい。。僕自身きちんと理解できていない
- 量子は「波動性」と「粒子性」の二面性を持っている
- 古典物理学ではコンピューターは0と1だけを認識して計算を行なってきたが、量子の概念を使うと0と1の重ね合わせができる
メモ的に書き殴ってみました。 量子コンピューターなどももっと勉強してみたいなと思いました。
BigQueryに既存のCloud Firestoreのデータを同期したい
Cloud FirestoreとBigQueryを同期してデータを分析したくなりました。
まずこちらの記事がとても分かりやすく書かれていました。
しかし、紹介されているExport Collections to BigQuery は「変更があったドキュメント」しかBigQueryに送信してくれません。
既にDB上にあるデータもBigQuery上で分析に使いたいです。
そこで既存のデータもBigQueyに同期する方法を調べたのでメモしておきます。
一部詰まった部分があったため記事として残しておきますが、一連のやり方はこちらに書いてあります。 github.com
同期手順
gcloudツールをダウンロードしていない場合、まずはこちらからダウンロードして使えるようにします。
こちらを使ってログインをしないと後に Error importing Collection to BigQuery: Error: Could not load the default credentials.
という認証系のエラーで怒られます。
ルート直下にダウンロードして、実行ファイルからloginをします。
[~] $ ls ... google-cloud-sdk [~] $ ./google-cloud-sdk/bin/gcloud init [~] $ ./google-cloud-sdk/bin/gcloud auth application-default login
これで認証ができました。
次にbqにデータをインポートするコマンドをnpx経由で叩きます。
$ npx @firebaseextensions/fs-bq-import-collection
対話形式で回答していきます。
? What is your Firebase project ID? hogemaru-app ? What is the path of the the Cloud Firestore Collection you would like to import from? (This may, or may not, be the same Collection for which you plan to mirror changes.) posts ? Would you like to import documents via a Collection Group query? Yes ? What is the ID of the BigQuery dataset that you would like to use? (A dataset will be created if it doesn't already exist) firestore_export ? What is the identifying prefix of the BigQuery table that you would like to import to? (A table will be created if one doesn't already exist) posts ? How many documents should the import stream into BigQuery at once? 300 ? Where would you like the BigQuery dataset to be located? asia-northeast1 ? Would you like to run the import across multiple threads? No
これで上記の認証が済んでいればコマンドラインで指定した件数ずつインポートが開始されます(今回はデフォルトの300件ずつ)。
... {"severity":"INFO","message":"Inserting 262 row(s) of data into BigQuery"} {"severity":"INFO","message":"Inserted 262 row(s) of data into BigQuery"} --------------------------------------------------------- Finished importing 10000 Firestore rows to BigQuery --------------------------------------------------------- [~] $
これでBigQueyで以下のようなクエリを叩いてみてください。
SELECT COUNT(document_id) FROM POSTS;
既存のデータ量分が取得できているかと思います!
参考
BigQueryで Not found: Dataset xx:firestore_export was not found in location US のエラーが出る
Cloud Firestoreを東京リージョンで設定して、そのデータをBigQueryで使おうとしました。
クエリを書いて実行しようとすると、
Not found: Dataset xx:firestore_export was not found in location US
とエラーが出てしまいました。
調べたらBigQueryのドキュメントに書いてあったのでメモしておきます。
クエリが asia-northeast1 リージョンに格納されたデータセット内のテーブルを参照する場合、クエリジョブはそのリージョンで実行されます。クエリがデータセット内のテーブルやその他のリソースを参照せず、宛先テーブルが指定されていない場合、クエリジョブは US マルチリージョンで実行されます。
まさにこれでした。
対処法
今回はweb上のSQLワークスペースでクエリを実行したいため、以下のように対応しました。
Cloud Console を使用してデータをクエリする場合は、[展開] > [クエリの設定] をクリックし、[処理を行うロケーション] で [自動選択] をクリックして、データのロケーションを選択します。
これでデータセットもでてきてクエリも実行できるようになりました!
Auth0の認証トークンの保存方法がよくわかっていなかった
個人開発をする時にログイン周りをいつもFirebase Authenticationを使用していたため、他のIDaaSを使ってみたくなり、今回はAuth0を使ってみることにしました。
Auth0を使用していくうちに疑問が浮かんだのでメモのためにまとめておきます。
疑問
- 許可されたAPIを叩く際にAuthorization headerで付与するトークンはどこに保持しておくのか
- 一度ログインしたら一定期間はクライアント側に保存しておく?
- 一回一回、認可サーバーに問い合わせてアクセストークンを取得してからサーバーにいく?
この辺りがよくわからなくなってしまったので調べてみることにしました。
環境
- webを想定
- auth0-reactというパッケージを使用
- これはauth0-spa-jsをhooksのような実装にラップしたもので実際の実装はほとんどがこちらにある
また実装を追いやすいように専用のsampleアプリを作りました。 (https://github.com/haga0531/auth0-react-sample)
認証の流れ
Reactアプリ側でログインボタンを押す
auth0-react
のloginWithRedirect
を呼ぶようにしています。
<button onClick={() => loginWithRedirect()}>Log in</button>
loginWithRedirect
の実装を見てます。
/** * ```js * await loginWithRedirect(options); * ``` * * Performs a redirect to `/authorize` using the parameters * provided as arguments. Random and secure `state` and `nonce` * parameters will be auto-generated. */ loginWithRedirect: (options?: RedirectLoginOptions) => Promise<void>;
ここで行っていることとしては以下のようです。
- パラメータをセットして、 https://YOUR_DOMAIN/authorizeに移動
- エンドユーザーをhttps://YOUR_DOMAIN/loginの画面にリダイレクト
またこの時のURLは以下のようになっています。
https://YOUR_DOMAIN/login?state=g6Fo2SBjNTRyanlVa3ZqeHN4d1htTnh&...
ドキュメントにも詳しく書かれていました。
ログインができ、isAuthenticated
がtrueになる
sampleアプリの実装では以下のようになっているため、yarn start
を実行してlocalhostにアクセスすると、Log outボタンが表示されています。
import React from "react"; import "./App.css"; import { useAuth0 } from "@auth0/auth0-react"; function App() { const { isAuthenticated, loginWithRedirect, logout } = useAuth0(); return ( <div className="App"> <header className="App-header"> {!isAuthenticated ? ( <button onClick={() => loginWithRedirect()}>Log in</button> ) : ( <button onClick={() => logout()}>Log out</button> )} </header> </div> ); } export default App;
疑問: 許可されたAPIを叩く際にAuthorization headerで付与するトークンはどこに保持しておくのか
ここで当初の疑問を思い出します。
許可されたAPIを叩く際にAuthorization headerで付与するトークンはどこに保持しておくのか
ドキュメントによるとAuth0のデフォルトの実装だとブラウザのインメモリに保存されるらしいです。
Use Auth0 SPA SDK whose default storage option is in-memory storage to leverage both Web Workers and JavaScript closures depending on the type of token.
なるほど、クライアントに保存しておくらしい。
しかし、この記述の下にこう書いてありました。
The in-memory method for browser storage does not provide persistence across page refreshes and browser tabs.
なるほど。インメモリだとリロードしたりブラウザのタブを変えたりしたら、ログイン状態が保持されないのか。
試してみます。
あれ、リロードしたもちょっとガチャってなるだけど、ログイン状態に戻った(isAuthenticated
がtrueに)。。
さらに疑問が生まれます。
Auth0のデフォルトの実装(tokenはブラウザのインメモリに保存される)のはずだけどリロードしたりブラウザ変えてもログイン状態を保持しているのはなぜだ、、
こういう時のnetworkタブということでこちらをを見てみることにしました。
見慣れないパラメータ、prompt=none
、response_mode=web_message
を発見します。
こちらを調べてみるとAuth0のドキュメントがヒットしました。
どうやらAuth0ではSilent Authentication
という仕組みでこのパラメータを使っているようです。
また以下のような記述がありました。
By default,
auth0-spa-js
uses theprompt=none
andresponse_mode=web_message
flow for silent auth, which depends on the user's Auth0 session. https://github.com/auth0/auth0-spa-js/blob/master/FAQ.md#using-multi-factor-authentication-mfa
response_mode=web_message
についても調べてみました。
こちらの記事がとてもわかりやすく書かれていました。
また例を出すが、Auth0 の SPA 用ライブラリ auth0/auth0-spa-js は Silent Authentication を使っている。初回表示時に prompt=none をつけた /authorize リクエストを裏で実行して、セッションが残っていればアプリケーションを自動的にログイン済み状態にしてくれる。
なるほど。In-memory方式で保存されているがリロードをしてもログイン状態を保持するという実装になっているのか。
なんとなく理解することができました。
Silent Authentication
にも欠点があることが述べられていますが、当初の疑問は解決された気がするのでここまでにします。
参照
- 一番分かりやすい OAuth の説明 - Qiita
- GitHub - auth0/auth0-react: Auth0 SDK for React Single Page Applications (SPA)
- 認証用トークン保存先の第4選択肢としての「Auth0」 - ログミーTech
- GitHub - auth0/auth0-spa-js: Auth0 authentication for Single Page Applications (SPA) with PKCE
- Configure Default Login Routes
- Token Storage
- Configure Silent Authentication
- response_mode=web_messageを調べた - Qiita
- Auth0のSilent Authentication (サイレント認証)とRefresh Token Rotation (リフレッシュトークンローテーション)を完全に理解した (い) - 一から勉強させてください
React, TypeScriptでenvを読み込みたいが型がundefinedになる
create-react-app -- template typescript
で作ったプロジェクトに環境変数を読み込んで使いたい時の対処法です。
下記のように環境変数を読み込ませたいのですが、この場合WEB_URLの部分はstring | undefined
と推論されます。
const hoge = process.env.WEB_URL; // const hoge: string | undefined
process.envからコードジャンプを使って型を追っていくと、node_modules/@types/node/global.d.ts
の中に、このような記述があることがわかります。
interface Dict<T> { [key: string]: T | undefined; }
値が T または undefinedとなっています。
const hoge = process.env.WEB_URL || '';
のようにdefault値を用意する手もありますが、今回はこちらで使われている型を拡張していきます。
create-react-appを実行したときに/src
直下にreact-app-env.d.ts
というファイルが追加されています。
こちらに型を書いていきます。
最終的な形はこのようになります。
/// <reference types="react-scripts" /> declare namespace NodeJS { interface ProcessEnv { readonly REACT_APP_WEB_URL: string; } }
prefixとしてREACT_APP_
を環境変数につける必要があります。
.env
の中身は下記のようになります。
REACT_APP_WEB_URL=https://example.com
これで最初に書いた方がstringと推論されるようになりました。
const hoge = process.env.WEB_URL; // const hoge: string;
余談 react-app-env.d.tsについて
react-app-env.d.ts
ですが、init直後はこのようになっていました。
/// <reference types="react-scripts" />
この記述はTypeScriptのTriple-Slash Directivesというもので、
何種類かありますが、今回使用されているのは /// <reference types="..." />
型ですね。
ドキュメントには下記のように書かれています。
a ///
directive declares a dependency on a package. ... For example, including /// in a declaration file declares that this file uses names declared in @types/node/index.d.ts;
今回の例だと、/// <reference types="react-scripts" />
とあるので、react-app-env.d.ts
内で node_modules/react-scripts/lib/react-app.d.tsで宣言されている名前を使用することを表しています。
この記事の最初に記述したようなコードと同じようなことをnode_modules/react-script/lib/react-app.d.ts
の中でも行っていることがわかります。
declare namespace NodeJS { interface ProcessEnv { readonly NODE_ENV: 'development' | 'production' | 'test'; readonly PUBLIC_URL: string; } }
このために、vscodeなどのエディターでは最初から型補完が効いているみたいですね。
sveltekitで SyntaxError: The requested module '/hoge' does not provide an export named 'Hoge' がでる
svelteに入門し、早速フレームワークのsveltekitを試してみました。
外部ファイルである dummy-data.ts
を index.svelte
にimportしようとした時に
SyntaxError: The requested module '/hoge' does not provide an export named 'Hoge'
というエラーが出て少しハマったので解決策を書いておきます。
エラーとなる状態
dummy-data.ts
を作ります。
export type Data = { id: number; title: string; } export const dummyData: Data[] = [ { id: 1, title: 'hogehoge', }, { id: 2, title: 'fuga', } ]
上記のようなTypeScriptファイルをsveleteにimportしようとしました。
index.svelte
<script lang="ts"> import {Data, dummyData} from '../../dummy-data'; ... </script>
これでリロードをすると画面上に SyntaxError: The requested module '/dummy-data.ts' does not provide an export named 'Data'
というエラーが表示されます。
解決策
どうやらsveltekitで使用されているビルドツールのviteに固有のエラーだそうです。 GitHub上にissueが上がっていました。
これによると
import type { User } from '~/types'
このような形式でimportするとうまくいくようです。
index.svelte
を下記のように書き換えます。
<script lang="ts"> import type {Data} from '../../dummy-data'; import {dummyData} from '../../dummy-data'; ... </script>
これでビルドがうまくいきました!! ただ同じファイルからimportしているのに分けて記述するのは面倒ですね。 他に解決策がありましたら教えてください🙇♀️
graphql-codegenをAWS AppSync, TypeScriptでやりたい
AWSでGraphQLを使う際のサービス、AWS AppSyncにcodegenを導入する際に詰まったのでメモ。
基本的なコマンドやドキュメントはこちらを見ます。
なお、私はpackage managerはyarn
が好きなのでyarn
を使用します。npm
でもやることは特に変わりません。
www.graphql-code-generator.com
codegenをinstall
まずはgraphqlをインストール。
$ yarn add graphql
devにcodegenをインストール。
$ yarn add -D @graphql-codegen/cli
初期ファイルをセットアップ
対話形式でセットアップします。 後で修正する部分もあるのでとりあえず下記のように初期セットアップ。
$ yarn graphql-codegen init yarn run v1.22.10 $ /Users/xxxxx/yyyy/node_modules/.bin/graphql-codegen init Welcome to GraphQL Code Generator! Answer few questions and we will setup everything for you. ? What type of application are you building? Backend - API or server ? Where is your schema?: (path or url) http://localhost:4000 ? Pick plugins: TypeScript (required by other typescript plugins) ? Where to write the output: src/generated/graphql.ts ? Do you want to generate an introspection file? Yes ? How to name the config file? codegen.yml ? What script in package.json should run the codegen? codegen Fetching latest versions of selected plugins... Config file generated at codegen.yml $ npm install To install the plugins. $ npm run codegen To run GraphQL Code Generator. ✨ Done in 82.03s.
TypeScriptも使いたいのでインストールします。
$ yarn add -D @graphql-codegen/typescript
init
をしたらcodegen.yml
というファイルが生成されます。
対話形式で選択したような感じに初期セットアップがされていますが、私は下記のように書き換えました。 - schemaを指定する - 不必要な部分を削除
overwrite: true #上書きを許可する schema: - ./schema.graphql - ./schema/**/*.graphql # コード量が多くなってきたときに備えてグローバル分割しておく generates: ./src/generated/graphql.ts: plugins: - "typescript"
package.jsonのscriptsを少し修正します。
"scripts": { ... "codegen": "yarn graphql-codegen" },
任意のschemaを記述します。
こちらは後々のために最小限の記述にしておきます。
schema.graphql
schema { query: Query mutation: Mutation }
適当なschemaを記述します。
schema/hoge.graphql
type Query { getHoge: [Hoge!] } type Hoge { id: ID createdAt: AWSDateTime title: String! description: String! } ...
これでyarn codegen
というコマンドを実行すればschemaが自動生成されるはずのつもりでした。
Type "AWSDateTime" not found in document. Error: Type "AWSDateTime" not found in document.
yarn codegen
を実行すると下記のエラーが出てしまいました。
$ yarn codegen ... src/generated/graphql.ts Failed to load schema from ./schema.graphql,./schema/**/*.graphql: Type "AWSDateTime" not found in document. Error: Type "AWSDateTime" not found in document. https://benoitboure.com/how-to-use-typescript-with-appsync-lambda-resolvers
AWS AppSync特有のプロパティであるAWSDateTime
を使用しているため対応が必要でした。
schema以下に新たなファイルを追加します。
schema/appsync.graphql
scalar AWSDateTime
codegen.ymlにも追記が必要です。
schema: - ./schema.graphql - ./schema/**/*.graphql # 以下を追記 config: scalars: AWSDateTime: string
これでもう一度 yarn codegen
を実行します。
エラー内容が変わりました。。。
Error: Cannot use GraphQLObjectType "Query" from another module or realm.
$ yarn codegen ... Error: Cannot use GraphQLObjectType "Query" from another module or realm. Ensure that there is only one instance of "graphql" in the node_modules directory. If different versions of "graphql" are the dependencies of other relied on modules, use "resolutions" to ensure only one version is installed. https://yarnpkg.com/en/docs/selective-version-resolutions Duplicate "graphql" modules cannot be used at the same time since different versions may have different capabilities and behavior. The data from one version used in the function from another could produce confusing and spurious results.
こちらはターミナルにきちんと載っていました。 graphqlのバージョンが複数ある問題でした。
Selective dependency resolutions | Yarn
ドキュメントに従ってpackage.json
に追記します。
"resolutions": { "graphql": "^15.5.0" },
依存をきちんとインストールしてあげます。
$ yarn
もう一度codegen
$ yarn codegen yarn run v1.22.10 $ yarn graphql-codegen $ /Users/xxxx/yyy/node_modules/.bin/graphql-codegen ✔ Parse configuration ✔ Generate outputs ✨ Done in 1.29s.
できました!