2023年振り返り

2023年の振り返りとしてやったことをまとめる。

目標管理

例年通り、四半期ごとの見直し、月次の進捗確認で運用をした。 1,4,7,10月に目標の見直しを行い次四半期の目標を立て、2,3,5,6,8,9,11,12月は進捗を確認する運用。ただし今年は目標管理のモチベーションが低く、あまり見直しはしなかった。新しい目標を達成するというよりは、淡々と、コツコツと積み上げた形になった。昨年秋に始めた朝読書を一日も欠かさなかったし、完全に習慣化した。

論文

1月

4月

ざっと読み、抜粋読み

1月

4月

5月

6月

読む本数が少なすぎるが、朝読書と違って習慣化していないのがよくない。週末は趣味の活動もあるのでうまくバランスを取りながら、数分でもいいのでコツコツと読む習慣を作るのが来年のアクション。

書籍

  • 漫画を含めた書籍: 42 冊(前年比+13冊 from 29冊)

特に読んで良かった本はデータ指向アプリケーションデザイン、考える技術・書く技術、イシューからはじめよ、ナチスは「良いこと」もしたのか? 特に『ナチスは「良いこと」もしたのか?』については、歴史研究における事実/解釈/意見の三層構造に関する考え方が述べられるまえがきでもいいので、必読であった。

技術書

ビジネス書・趣味

記事

昨年に引き続き、英語記事に目を通すようになった。readingスキルの伸びを感じるが、まだ読むのが遅いし、疲れていると英文が頭に入ってこない。 以下は今年読んでよかった記事。

Github

昨年の17 contributionsに対して、+57となった。

Scrapbox

  • 16111764 pages

2022年振り返り

2022年の振り返りとしてやったことをまとめる。

目標管理

例年通り、四半期ごとの見直し、月次の進捗確認で運用をした。 1,4,7,10月に目標の見直しを行い次四半期の目標を立て、2,3,5,6,8,9,11,12月は進捗を確認する運用。

大項目として以下4つを設け、それぞれ中項目の目標を立てた。中項目をさらに細分化して、四半期ごとに小項目での目標管理をした。括弧内は中項目のうち、達成できた数を記載。

  • 読書(2/3)
  • 健康(1/2)
  • 英語(1/1)
  • 趣味(2/5)

論文

1月

  • Amin, K., Dick, T., Kulesza, A., Medina, A. M., & Vassilvitskii, S. (2019). Differentially private covariance estimation. Advances in Neural Information Processing Systems, 32(NeurIPS).

  • Stadler, T., Oprisanu, B., & Troncoso, C. (2020). Synthetic Data -- Anonymisation Groundhog Day.

  • Rogers, R., Subramaniam, S., Peng, S., Durfee, D., Lee, S., Kancha, S. K., Sahay, S., & Ahammad, P. (2020). LinkedIn’s Audience Engagements API: A Privacy Preserving Data Analytics System at Scale. http://arxiv.org/abs/2002.05839

2月

  • Cao, Yang and Yoshikawa, Masatoshi and Xiao, Yonghui and Xiong, Li, Quantifying differential privacy under temporal correlations, Data Engineering (ICDE), 2017 IEEE 33rd International Conference on, 821--832, 2017.

3月

  • Kareem Amin, Jennifer Gillenwater, Matthew Joseph, Alex Kulesza, Sergei Vassilvitskii. "Plume: Differential Privacy at Scale" arXiv preprint arXiv:2201.11603 (2022).

4月

5月

6月

  • Adam, Nabil R., and John C. Worthmann. "Security-control methods for statistical databases: a comparative study." ACM Computing Surveys (CSUR) 21.4 (1989): 515-556. https://dl.acm.org/doi/pdf/10.1145/76894.76895

  • Rowe, Neil C. "Diophantine inferences from statistical aggregates on few-valued attributes." 1984 IEEE First International Conference on Data Engineering. IEEE, 1984.

7月

9月 - Kifer, Daniel, and Ashwin Machanavajjhala. "No free lunch in data privacy." Proceedings of the 2011 ACM SIGMOD International Conference on Management of data. 2011. https://www.cse.psu.edu/~duk17/papers/nflprivacy.pdf

12月 - El Emam, Khaled, et al. "A systematic review of re-identification attacks on health data." PloS one 6.12 (2011): e28071. https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0028071

ざっと読み、抜粋読み

1月

  • Desfontaines, D., Voss, J., Gipson, B., & Mandayam, C. (2022). Differentially private partition selection. Proceedings on Privacy Enhancing Technologies, 2022(1), 339–352. https://doi.org/10.2478/popets-2022-0017

3月

  • Asi, Hilal, John Duchi, and Omid Javidbakht. "Element level differential privacy: The right granularity of privacy." arXiv preprint arXiv:1912.04042 (2019).

4月

5月

6月

7月

書籍

  • 漫画を含めた書籍: 29 冊(前年比-34冊※)

※今年から雑誌は含めないことにした

技術書

  • なし

ビジネス書・趣味

記事

昨年に引き続き、あまり記事を読んでいない。ただし、英語記事に目を通すようになった。 以下は今年読んでよかった記事。

Github

昨年の1,249 contributionsに対して、-1,231となった。
Githubで管理していないコードやクエリを書く機会が多かったため。

Scrapbox

  • 13761611 pages

dbtを使い始めて数ヶ月が経った雑感

この記事は dbt Advent Calendar 2022 の25日目の記事です。dbtを使い始めて数ヶ月ほど経過したので、これまでの所感を書きます。

はじめに

dbt (data build tool) はデータ処理変換を担うフレームワークです[1]。データエンジニアリングにおいて重要なデータ分析の品質保証をデータモデルの構築、テスト、ドキュメンテーションをいった要素で実現します。とりわけDWHに統合して使われることが意図されており、ETLにおけるTransferの役割を実現してくれます。

Coalece2022の[3]のセッションでも語られていますが、この25年間、ストレージコストが低下したことや、よりデータへのアクセスが容易になったなったことで、足元ではすべての変換を一つのDWHで行うETLからELTへといった流れも出てきました。この流れによって、よりdbtが真価を発揮しやすい環境が整ってきたと言えると思います。

チームでデータ分析

個人的に感じているdbtの本質的な価値はデータ分析のプロセスの管理と効率化です。

特に現代データ分析においてはデータエンジニア一人で分析業務を担い切ることは少なく、チーム・組織として分析を高度化し、顧客により大きな価値を届けられるようなデータ分析プロセスの実現とその管理が求められます。

私が従事している業務でも、構造が同一のクエリを何度も何度も手書きし、それを都度レビューするといったプロセスに課題を感じてしました(何度同じクエリを書くのかといった開発者視点での課題であったり、ほぼ同じクエリだからという心理状態によるレビュー品質の低下といったレビュワー・チーム視点での課題など)。

SQLをチームで書いた経験のある方ならわかると思いますが、クエリのコードレビューには大きな集中力を要します。CTEに分解しながら手元で小さなダミーデータを用意したりしながら実行し、クエリ全体としても問題ないということを考えるレビューは大きな労力です。

弱い心を前に浅いレビューで通してしまったりすることもあり、それが積み重なると属人的なクエリのできあがりです。さらに大きなクエリは依存関係も複雑になりがちで、どの中間テーブルを使うのか、その中間テーブルの品質がどれくらい保証されているのかといった課題が発生しがち(「この中間テーブルって最新ですか?」とテーブル作成者にヒアリングして回ったり)で、結果として品質の低いデータ分析結果を顧客に届けることになりかねません。データやファクトに基づく意思決定を誤った方向に導き、ビジネス上の損失や信頼を毀損する結果にも繋がりえます。

ここまでの話を整理すると、以下3つに課題を感じていました。

  • 依存関係が複雑化し、認知的負荷が大きい
  • 中間テーブルの各カラムが満たすべき制約がわからず、設計が見えない
  • 同じようなクエリを都度レビューしなくてはいけない

dbtでの解決

依存関係が複雑化し、認知的負荷が大きい

この課題については、シンプルに dbt docs コマンドで可視化してくれることが、依存関係の把握を助けてくれます。Coalece2022の[4]のセッションではモデリングパターンを以下4つに分類し、これをリファクタリングするワークショップの様子が届けられていますが、可視化されるとどのパターンに分類されるのかがひと目で分かるので、改善へのステップがエスカレーターになっていることにもdbtというプロダクトの強さを感じます。

  • ソースから直接データをjoinしている
  • 同じデータセットから何度もjoinしている
  • 依存の依存をjoinしている
  • 成果物にソースを使っている

([4]の発表中より引用)

中間テーブルの各カラムが満たすべき制約がわからず、設計が見えない

テストが書けることで各カラムにどういった制約が見えるようになります。
uniquenot_nullaccepted_valuesrelationships (そのカラムが他テーブルに存在するreferential integrityの担保)といったテストが標準 [5]で用意されていますし、これら標準機能で手が届かないようなテストもdbt-utils [6]に解決策が用意されています(複数カラムを跨ったuniquenessを保証したいときに unique_combination_of_columns などをよく利用します)。これらも実態としてはJinjaマクロなので、さらに物足りないときは自作でき十分な柔軟性を備えていると思います。

そして[2] は、data profilingやdata contract、data pipelinesの文脈での話にはなりますが、データ品質問題に対するアプローチとして以下の流れを提案しています。

  1. 品質の低いデータを特定する(異常値など)
  2. 許容する値を規定する
  3. データパイプライン上で、顧客に届く前に、エラーを補足する

dbtのテストはこの2.と3.に対しての保証と見ることもできるでしょう。

個人的にはこれに加えてテストを見れば各カラムにどのような制約が掛かっているのか(どういう設計なのか)が理解しやすく、テスト自体がドキュメンテーションになる点も気に入っています。

同じようなクエリを都度レビューしなくてはいけない

dbtのデータ変換はSQLとJinjaを組み合わせて実現されています[7]。Jinja[8]はPython-likeなシンタックスを持つテンプレートエンジンです。dbtで利用できるマクロもJinjaを使って生成できるため、複雑なクエリを汎用化し、再利用性を得ることができます。これによって算術演算が多い等の低レベルのクエリや注意深いレビューが必要なクエリなどを一度十分なレビューをすることで品質を保ったまま再利用可能なクエリを生成することができます。

このJinjaマクロを使った方法で一定の解決をみているものの、マクロのメンテナンスコストも無視できないものだと感じています。モデリングによる解決とマクロでの解決の両軸をどのようにバランスさせていくかは個人的にも興味が向いている点です。「同じようなクエリを都度レビューしなくてはいけない」という課題で対象としているクエリを分解すると「ロジックが共通であるクエリ」と「低レベルのクエリ」があるように感じているので、前者はモデリング+マクロによる解決、後者はdbt Core v1.3で導入されたPython models [9] による解決が望ましいのではとも思え、このあたりは考えを深めていきたい論点になります(まだ視聴できていないのですが、Coalece2022でのなぜPython第二言語としたのかについてのセッション[10]で話されていたりしないかも気になっています)。

最後に

簡単ではありますが、dbtを使い始めて数ヶ月の雑感でした。まだまだ知らないことだらけではありますが、使い始める前に感じていた課題が解消されていてdbtの恩恵を感じています。何より複雑なSQLを書いていたときに比べて、構造化ができている点、しっかりと管理でき品質保証されている点に安心感を持って開発を進めることができています。dbt Advent Calendar 2022 を拝見していても、初めて知ることも多かったり、エコシステム自体が大きく成長しようとしている兆しも感じるところで、2023年もどのように変わっていくのか非常に楽しみな技術です。

References

2021年振り返り

2021年の振り返りとしてやったことをまとめる。

目標管理

昨年に引き続き、四半期ごとの見直し、月次の進捗確認で運用をした。 1,4,7,10月に目標の見直しを行い次四半期の目標を立て、2,3,5,6,8,9,11,12月は進捗を確認する運用。

大項目として以下5つを設け、四半期ごとに小項目での目標管理をした。括弧内は小項目のうち、達成できた数を記載(次四半期で取り返せたものは達成扱い)。

  • 技術(4/12)
  • 読書(10/17)
  • 健康(6/8)
  • 英語(5/7)
  • 趣味(5/11)
  • 資産運用(2/5)

全体として53%の達成率だった。昨年の 65% に対して大きく減ってしまった。
毎月の目標見直しで「またこの目標に着手できなかった」となることが多く、目標管理自体のモチベが大きく下がる要因となっていたし、精神的な重荷にもなっていた。

来年は以下を意識する。

  • 目標の取捨選択
  • 目標への取り組みを行う時間を何処で取るのかまで含めて計画

論文

日本語の論文や金融機関のレポート、決算資料も読んでいるが、ここでは含めない。

  • Christodorescu, Mihai, et al. "Towards a Two-Tier Hierarchical Infrastructure: An Offline Payment System for Central Bank Digital Currencies." arXiv preprint arXiv:2012.08003 (2020).
  • Han, Shujie, et al. "An In-Depth Study of Correlated Failures in Production SSD-Based Data Centers." 19th {USENIX} Conference on File and Storage Technologies ({FAST} 21). 2021.
  • Bahmani, Raad, et al. "{CURE}: A Security Architecture with CUstomizable and Resilient Enclaves." 30th {USENIX} Security Symposium ({USENIX} Security 21). 2021.
  • Vasily A. Sartakov, et al. "Spons & Shields: practical isolation for trusted execution" The 17th ACM SIGPLAN/SIGOPS International Conference on Virtual Execution Environments. 2021.
  • van Schaik, Stephan, et al. "CacheOut: Leaking data on Intel CPUs via cache evictions." 2021 IEEE Symposium on Security and Privacy (SP). IEEE, 2021.
  • Alexander Sprogø Banks, "Remote Attestation: A Literature Review", 2021.
  • Purnal, Antoon, et al. "Systematic analysis of randomization-based protected cache architectures." 42th IEEE Symposium on Security and Privacy. Vol. 5. 2021.
  • Lefeuvre, Hugo, et al. "FlexOS: making OS isolation flexible." Proceedings of the Workshop on Hot Topics in Operating Systems. 2021.
  • Nider, Joel, and Alexandra Fedorova. "The last CPU." Proceedings of the Workshop on Hot Topics in Operating Systems. 2021.
  • Lillian Tsai, et al. "Privacy Heroes Need Data Disguises" Proceedings of the Workshop on Hot Topics in Operating Systems. 2021.
  • Feng, Erhu, et al. "Scalable Memory Protection in the PENGLAI Enclave." 15th {USENIX} Symposium on Operating Systems Design and Implementation ({OSDI} 21). 2021.
  • Kumar, Sam, David E. Culler, and Raluca Ada Popa. "{MAGE}: Nearly Zero-Cost Virtual Memory for Secure Computation." 15th {USENIX} Symposium on Operating Systems Design and Implementation ({OSDI} 21). 2021.
  • Farke, Florian M., et al. "Are Privacy Dashboards Good for End Users? Evaluating User Perceptions and Reactions to Google's My Activity." 30th USENIX Security Symposium (USENIX Security 21). 2021.
  • Julie Haney, et al. "\"It's the Company, the Government, You and I\": User Perceptions of Responsibility for Smart Home Privacy and Security", 30th USENIX Security Symposium (USENIX Security 21). 2021.
  • Messing, Solomon, et al. "State, Bogdan; Wilkins, Arjun, 2020,” Facebook Privacy-Protected Full URLs Data Set”.", 2020.
  • Chanyaswad, Thee, et al. "Mvg mechanism: Differential privacy under matrix-valued query." Proceedings of the 2018 ACM SIGSAC Conference on Computer and Communications S, 2018.
  • Abowd, John, et al. "An Uncertainty Principle is a Price of Privacy-Preserving Microdata." Advances in Neural Information Processing Systems 34 (2021).
  • Costan, Victor, and Srinivas Devadas. "Intel sgx explained." IACR Cryptol. ePrint Arch. 2016.86 (2016): 1-118.
  • Yoon, Jinsung, Daniel Jarrett, and Mihaela Van der Schaar. "Time-series generative adversarial networks." (2019).
  • Karwa, Vishesh, and Salil Vadhan. "Finite sample differentially private confidence intervals." arXiv preprint arXiv:1711.03908 (2017).
  • Balle, Borja, et al. "Hypothesis testing interpretations and renyi differential privacy." International Conference on Artificial Intelligence and Statistics. PMLR, 2020.
  • Mironov, Ilya, Kunal Talwar, and Li Zhang. "R\'enyi Differential Privacy of the Sampled Gaussian Mechanism." arXiv preprint arXiv:1908.10530 (2019).

書籍

  • HashHub Researchレポート: 317本(前年比+6本)
  • 漫画や雑誌等を含めた書籍: 63冊(前年比-117冊)

技術書

昨年より1冊増えたが、腰を据えて読めていない感覚がある。

ビジネス書・趣味

今年の課題感として本を読む時間を生活に組み込めなかったことがある。朝読書復活させる。

記事

昨年に引き続き、あまり記事は読んでいない。

以下では、Pocketでお気に入りしたものからリスト化する。いずれも2021年12月31日時点で有効なリンクのみを対象とした。 概ね読んだ順であり、「2021年に読んでよかった記事」のため2020年以前に公開されたものを含む。体感、去年よりお気に入りにする頻度が下がった。

Github

f:id:cipepser:20211231171059p:plain

昨年の1,038 contributionsに対して、+211となった。

Scrapbox

  • 7651376 pages

2020年振り返り

2020年の振り返りとしてやったことをまとめる。

目標管理

四半期ごとの見直し、月次の進捗確認で運用をした。

1,4,7,10月に目標の見直しを行い、次四半期の目標を立てる。
2,3,5,6,8,9,11,12月は進捗を確認する運用だった。

大項目として以下5つを設け、四半期ごとに小項目での目標管理をした。括弧内は小項目のうち、達成できた数を記載(次四半期で取り返せたものは達成扱い)。

  • 技術(5/9)
  • 読書(9/15)
  • 健康(7/9)
  • 英語(4/7)
  • 趣味(9/12)

全体として65%の達成率だった。

目標管理を開始して3年目であったが、運用が確立され、目標管理シートのフォーマットも定常化した。一方でサボることは少ないながら上述の達成率だった点はネガティブ。特に技術と読書。原因は目標が過大なことなので、2021年は本年のVelocityをベースとした目標策定へ改める。

論文

日本語の論文や金融機関のレポート、決算資料も読んでいるが、ここでは含めない。
隔週で担当しているLayerX Newsletter1により継続的に論文を読む習慣ができた。一方で開催される会議ベースで読んでいるので、過去の論文を読む習慣を付けることが課題。

書籍

  • HashHub Researchレポート: 311
  • 漫画や雑誌等を含めた書籍: 180

技術書

技術書を読む数が減った。増やしていくべき。今年は意識的に一年一言語を新規に学ぶ目標を停止したが、来年は復活させる。

ビジネス書

特によかったのは、「失敗の本質」と「決済システムのすべて」の2冊。いずれも重たい本ではあるが、組織、決済システムの文脈では必読書だと感じた。また、百人一首孔子といった教養も意識した選書となった。2021年は美術と舞台照明・演出に関する本を読みたい。

記事

論文を読む時間が増えたので記事を読む時間は減った。なお、これはポジティブだと捉えている。

以下では、Pocketでお気に入りしたものからリスト化する。いずれも2020年12月31日時点で有効なリンクのみを対象とした。残念ながら「読んでよかった」と思ったもののリンクが切れてしまっている記事もいくつか存在した。
概ね読んだ順であり、「2020年に読んでよかった記事」のため2019年以前に公開されたものを含む。体感、去年よりお気に入りにする頻度が下がった。
ざっと眺めてみるとジャンルは、Rust、CBDC、STO、組織論か。コンテナランタイムなど低レイヤの記事をお気に入りにすることも増え、基礎から重厚に議論を進めている記事を「読んでよかった」と思うようになった。ジャンルについて、改めて自分が何をおもしろいと感じたのか可視化できるので来年も振り返ろう。
リスト上は多くないが、翻訳にかけてもいいので英語や中国語の記事を読むようになった一年であった。

Github

f:id:cipepser:20201231170835p:plain

昨年の873 contributionsに対して、+155となった。

Scrapbox

privateのScrapboxの運用を開始した。

  • 0→765pages

趣味のほうは更に別に存在し、84pagesだった。


  1. 頑張って書いてるので、ぜひ購読してください!(宣伝)

【Go】Sodiumで認証付き公開鍵暗号

この記事は Go 2 Advent Calendar 2020 の23日目の記事です。

Sodiumとは

Sodium[1]は使いやすさを目的に開発された暗号学ライブラリです。 暗号の誤用に関する研究分野では、誤りの検出や復元のため技術とそれに伴う影響が議論されています。 一方で誤用そのものを防ぐような方法についての議論は比較的少数です[13]。 Sodiumは使いやすさからこの課題を解決するライブラリの一つです。

また使いやすさだけでなく、portable、cross-compilable、installableであることも謳われています。 awesome-cryptography[2]で検索してみた所、以下のように多様な言語で利用することができます。

C

C#

JavaScript

Java

PHP

Rust

Swift

GoでSodiumを使う

GoでもSodiumを扱うことができます。packageはnacl/ · pkg.go.devです。[1][14]にあるようにSodiumはNaClのforkで、Goはnacl packageとして準標準ライブラリに用意されています。

今回はnacl/boxが提供している、公開鍵を使ったメッセージの認証と暗号化を試してみようと思います。内容は[16]をベースとしています。

実装に移る前にSodiumの特徴や注意点をいくつか述べておきます。
まず、メッセージは暗号化されますが、メッセージの長さは秘匿されません。暗号文の長さから元の平文が推測できてしまうようなアプリケーションには向きません。
また、Sodiumではメッセージにnonceを付与します。メッセージごとに異なるnonceを用いることは呼び出し側の責任です。
その他の注意点としては、[16]にも書かれていますが、メッセージ全体を処理するためにメッセージをメモリ上に保持する必要があるため、小さなメッセージが推奨されています。なお、暗号化のオーバーヘッドも存在しますが、8KB程度のメッセージでは十分に償却されるとのことです。

では本題の実装に入っていきましょう。 今回はclientからserverを送信するメッセージの暗号化を行います。

まず、GenerateKeyを用いて、鍵ペアを生成します。 認証のためclient、serverの両方で鍵ペアを生成します。

// client
clientPublicKey, clientPrivateKey, err := box.GenerateKey(crypto_rand.Reader)
if err != nil {
    panic(err)
}

// server
serverPublicKey, serverPrivateKey, err := box.GenerateKey(crypto_rand.Reader)
if err != nil {
    panic(err)
}

次にメッセージに付与するnonceを生成します。nonceの長さは24 bytesです。 nonceの使い回しは厳禁です。メッセージごとに生成しましょう。

var nonce [24]byte
if _, err := io.ReadFull(crypto_rand.Reader, nonce[:]); err != nil {
    panic(err)
}

いよいよ暗号化です。暗号化にはSealを用います。 暗号化対象のメッセージをmsg := []byte("sodium msg")とし、認証付きで公開鍵暗号を施します。

encrypted := box.Seal(nonce[:], msg, &nonce, serverPublicKey, clientPrivateKey)

clientの秘密鍵clientPrivateKeyだけでなく、serverの公開鍵serverPublicKeyを渡していることに注意してください。 復号にはサーバの秘密鍵が必要であるとともに、clientの公開鍵によってclientからのメッセージであることを認証します。

ちなみにnonceは暗号文の先頭に付与されます。 筆者の環境で実行した結果は以下のようになりました。 encryptedの先頭24 bytesがnonceとなっていることがわかります。

nonce:     [176 240 87 14 190 92 224 60 245 16 119 163 71 18 1 177 57 118 139 46 141 4 99 117]
encrypted: [176 240 87 14 190 92 224 60 245 16 119 163 71 18 1 177 57 118 139 46 141 4 99 117 65 153 138 25 206 247 18 42 0 162 85 88 223 68 203 63 241 28 35 232 242 176 184 56 97 177]

また上記のlen(encrypted)は50 bytesです。 msgsodium msg)が10 bytesなので、nonceの24 bytesを差し引きいても16 bytesのオーバーヘッドがあります。このオーバーヘッドは認証のために付与されたもので、暗号文とはオーバーラップしない設計となっています。

では復号してみましょう。 実際のアプリケーションではclientからserverへencryptedが送信されてからの復号となりますが、ここではserverで受信済みとして先に進みます。

Sealされたメッセージの復号にはOpenを用います。 Openシグネチャは以下のようになっています。

func Open(out, box []byte, nonce *[24]byte, peersPublicKey, privateKey *[32]byte) ([]byte, bool)

2つ目の引数に注目いただきたいのですが、nonceが必要です。 encryptedの先頭24 bytesがnonceになっていたことを思い出し、encryptedから取り出します。

var decryptNonce [24]byte
copy(decryptNonce[:], encrypted[:24])

必要な引数がすべて揃ったので、serverの秘密鍵serverPrivateKeyとclientの公開鍵clientPublicKeyを用いてOpenを実行します。

decrypted, _ := box.Open(nil, encrypted[24:], &decryptNonce, clientPublicKey, serverPrivateKey)

正しいserverの秘密鍵とclientの公開鍵があれば、認証と復号に成功し、元のmsgと同一のdecryptedを得ることができます。

まとめ

Sodiumの紹介と、Goで認証付き公開鍵暗号の暗号化/復号を試してみました。認証付きで公開鍵暗号を使えるので正しい相手から受信したことを保証したい場合などに有用でしょう。また利用できる言語が多いことも魅力的ですね。筆者はRustとjsのcompatibilityを手元でも動作確認しました。機会があれば、いつか記事にしたいと思います。Goとのcompatibilityも試してみたいですね。

また、同じようなプロジェクトは他にもMonocypher[17]、Themis[18]、Tink[19]もあるようです。特にTinkはGoogleのプロジェクトであることからも気になっています。APIのインターフェースが違うようなので、使い勝手といった視点も含めて触っておきたいところです。

References

Rustの可変長引数関数とHListの話

この記事は Rust Advent Calendar 2020 の5日目の記事です。

背景

RFCs#2137にあるようにRustでは、可変長引数関数を直接的に書くことはできません。とはいえ全くできないわけではありません。C言語から可変長引数関数呼び出しを実現するため、stub関数を記述することは可能です。例えば、以下のような関数をRustで実装します。

pub unsafe extern "C" fn func(arg: T, arg2: U, mut args: ...) {
    // do something
}

このような関数はuse extern "C"の中で使われ、unsafeを付与する必要があります。

別の実現方法としてはマクロを利用する案もあり、以下のようなマクロで実現できるでしょう。以下はargarg2を処理し、再帰的に可変長引数に相当するargsを処理する例です。

macro_rules! func {
    ( $arg:expr, $arg2:expr, $($args:expr), + ) => {
        do_something_for_arg($arg);
        do_something_for_arg1($arg2);
        func!(@inner $($args), +);
    };

    (@inner $tail:expr ) => { $tail };

    (@inner $head:expr, $($cons:expr), + ) => {
        do_something_args($head);
        func!(@inner $($cons), +)
     };
}

HListについて

今回は可変長引数関数を実現する方法の一つとして、HListを紹介します。HlistはHeterogenous Listの略で、値に異なる型を取れるリストです。例えば、[1, "a", true]のような[u32, &str, bool]型の値を持つことができます。
似たようなデータ構造としてはVec<T>やタプルが考えられますが、Vec<T>は同一の型Tを持つ点で異なります。一方、タプルは(u32, &str, bool)のように異なる型を持つこともできますが、HListを用いることでコンパイル時まで要素の数を決めないでいられます。

frunk

RustでHListを扱うために、frunk crateを利用します。frunkはgeneric functional programmingをRustで扱えるようにするためのcrateで、以下をサポートしています。

  • HLists (heterogeneously-typed lists)
  • LabelledGeneric, and Generic
  • Coproduct
  • Validated (accumulator for Result)
  • Semigroup
  • Monoid

今回は上記の中から、HListを利用します。frunk crateのHListはHNilHConsで表現されます。実装は以下のようになっています。

pub struct HNil;

pub struct HCons<H, T> {
    pub head: H,
    pub tail: T,
}

frunk crateにはhlistマクロが定義されており、hlist![1, "a", true]の返り値の型はHCons<i32, HCons<&str, HCons<bool, HNil>>>となります。このようにHNilHConsを組み合わせて、HListを表現します。

作るもの

可変長引数を取る関数の代表的な例として、print関数を実装します。 使い方をみたほうがイメージが湧きやすいと思うので、先にテストを書きます。

#[test]
fn my_print() {
    use frunk::hlist;
    use super::Printer;

    let args = hlist!["hello ", "world. ", 1, " == ", 2.0, " is ", false];
    let got = args.print();
    assert_eq!(got, "hello world. 1 == 2 is false");
}

let args = hlist!["hello ", "world. ", 1, " == ", 2.0, " is ", false];に注目してください。&stri32f64boolという4つの異なる型を含んだListとなっています。本当はargsを引数に取るような可変長引数関数を直接実装したかったのですが、今回は擬似的にargsに対するメソッドとして実装します。Implementing a Type-safe printf in Rustでは、プレースホルダFVarとして表現し、引数としてHListを受け取るformat関数を実装しています。ぜひこちらもご覧ください。

実装

まず、Printer traitを定義します。
Stringを返すようなprint関数を持ちます。

pub trait Printer<Args> {
    fn print(&self) -> String;
}

HNilHConsに対して、Printer traitを満たすように、print関数を実装していきます。

まずは、HNilから実装します。

impl Printer<HNil> for HNil {
    fn print(&self) -> String {
        "".to_string()
    }
}

HNilとなった時点でHListは末尾に到達しているので、空文字を返します。シンプルですね。

一方、HConsは少し複雑です。

impl<T, Args, PrintList> Printer<HCons<T, Args>> for HCons<T, PrintList>
    where
        PrintList: Printer<Args>,
        T: ToString,
{
    fn print(&self) -> String {
        self.head.to_string() + &self.tail.print()
    }
}

Printer<Args>ArgsHCons<T, Args>としています。TheadArgstailです。headはそのままStringに変換したいため、ジェネリック境界としてToStringを課しています。tail再帰的に処理を進めるため、PrintList: Printer<Args>とし、printを呼び出します。この処理はHNilに到達するまで繰り返されます。

このようにして、HNilHConsに対して、print関数を実装しました。

動作確認

改めて、今回実装したコード全体は以下のようになります。

use frunk::hlist::{HNil, HCons};

pub trait Printer<Args> {
    fn print(&self) -> String;
}

impl Printer<HNil> for HNil {
    fn print(&self) -> String {
        "".to_string()
    }
}

impl<T, Args, PrintList> Printer<HCons<T, Args>> for HCons<T, PrintList>
    where
        PrintList: Printer<Args>,
        T: ToString,
{
    fn print(&self) -> String {
        self.head.to_string() + &self.tail.print()
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn my_print() {
        use frunk::hlist;
        use super::Printer;
        
        let args = hlist!["hello ", "world. ", 1, " == ", 2.0, " is ", false];
        let got = args.print();
        assert_eq!(got, "hello world. 1 == 2 is false");
    }
}

テストの実行結果です。

❯ cargo test
   Compiling rust-variadic-artgument v0.1.0 (/Users/cipepser/work/github.com/cipepser/rust-variadic-artgument)
    Finished test [unoptimized + debuginfo] target(s) in 0.47s
     Running target/debug/deps/rust_variadic_artgument-3442cb5a09929580

running 1 test
test tests::my_print ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests rust-variadic-artgument

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

いい感じですね。

最後に

可変長引数関数の話から始まり、HListでprint関数を実装しました。

今回記事を執筆するにあたり、frunc crateの存在を初めて知りました。CoproductやSemigroup、Monoidも扱えるようなので、折を見て遊んでみようと思います。

References