Slide 1

Slide 1 text

End-to-End Encryption Saves Lives. You Can Start Saving Lives With Ruby, Too Ryo Kajiwara/ 梶原 龍 (sylph01) 2025/4/17 @ RubyKaigi 2025 日本語字幕版スライド。脚注に日本語字幕があります 1

Slide 2

Slide 2 text

Slides are available at: https:/ /speakerdeck.co m/sylph01/end-to- end-encryption-saves- lives-you-can-start- saving-lives-with- ruby-too 2

Slide 3

Slide 3 text

Hi! 3

Slide 4

Slide 4 text

ようおいでたなもし! Welcome to Matsuyama! 現地の人ももうほとんど使わない方言ですね 4

Slide 5

Slide 5 text

I do stuff Play rhythm games (especially DanceDanceRevolution) Play the bassoon/contrabassoon Play DJ at clubs (also at RubyMusicMixin!) Ride a lot of trains (Rails!) (travelled on 99% of JR) Build keyboards if anything catches your interest let's talk! 今回はあっさり飛ばします 5

Slide 6

Slide 6 text

And I do stuff that is more relevant to this talk: Freelance web developer focused on Digital Identity and Security Worked/ing on writing/editing and implementing standards HTTPS in Local Network CG / Web of Things WG @ W3C, OAuth / Messaging Layer Security WG @ IETF Worked as an Officer of Internet Society Japan Chapter (2020-23) 特に今回のトークで重要な点としてはW3C とかIETF とかで標準化に関わる活動をしてきました 6

Slide 7

Slide 7 text

The Usual Disclaimer Cryptographic API can be very easy to misuse Operational Security is also very difficult I've done my research, but I don't consider myself a cryptography expert a.k.a. "I am not djb" If you're not sure, please have your system audited by a security expert before going to production 暗号API は間違った使い方を容易にしてしまいがちです。あと私はdjb じゃないのでどうしても気になる場合 はセキュリティ専門家にレビューを依頼してね 7

Slide 8

Slide 8 text

Notes on Production Readiness The MLS implementation is still in progress Passes most test vectors "Passive Clients - Random" is not passing yet Lacks validation on error cases I am planning to release the complete version before the next IETF (2025/7) 実装はテストベクターを通す程度には作ったけどまだ完全ではないよ、特にエラーケースのバリデーション とかが落ちてるよ、次の7 月のIETF 目安で完全版リリースしたい(希望的観測) 8

Slide 9

Slide 9 text

9

Slide 10

Slide 10 text

I happen to be known as the SMTP をやめろ-guy SMTP をやめろ = lit. "stop using SMTP"; more like "SMTP is dead" 10

Slide 11

Slide 11 text

SMTP mail lacks two things: 1) Strong identity 2) End-to-end Encryption SMTP で私が気にしている点は2 点、アイデンティティの保証がないこと、そしてエンドツーエンド暗号化が ないことです。前者もMLS は助けてくれうるけど今回は後者 11

Slide 12

Slide 12 text

End-to-End Encryption = The people who run the service cannot read your messages LINE, Facebook Messenger, Signal, WhatsApp, ... They each have their own version of E2EE エンドツーエンド暗号化とは、サービスを運営している人があなたのメッセージを読むことができない、と いうことです。みなさんの使ってるメッセージングアプリにもなんらかの形で実装されていることが多いで す 12

Slide 13

Slide 13 text

ここから政治的な話をするよ、ちょっとの間我慢してね 13

Slide 14

Slide 14 text

E2EE is more important than ever Increasing tension in international affairs Powerful people wanting to control the Internet even in democratic countries! Provides a safe method to communicate under oppression / in war zones エンドツーエンド暗号化の重要性はかつてなく高まっています。それは国際情勢の緊張の高まりだったり、 民主主義国家においても力を持つ人がインターネットをコントロールしたいと考えるようになっているから です。抑圧や戦争の下でも安全な通信を保証してくれる暗号化の重要性がわかると思います 14

Slide 15

Slide 15 text

Hey, but doesn't E2EE help criminals? E2EE is being targeted by authorities even in democratic countries! Even if you ban E2EE, criminals will use it anyways Banning E2EE disproportionately harms vulnerable people えっ?犯罪者を助けるからE2EE を禁止しなきゃって?そう、E2EE は民主主義国家においてすら法執行機関 から敵対視されています。犯罪者はE2EE 禁止しても犯罪するんだから、E2EE を禁止することは立場の弱い 人を一方的に不利にするだけです 15

Slide 16

Slide 16 text

https:/ /www.internetsociety.org/blog/2024/07/encryption-is-a-preventative-tool-that-protects-children/ 16

Slide 17

Slide 17 text

HTTPS(TLS) also helps criminals, but are you going to ban HTTPS(TLS)? TLS だって暗号化だし犯罪者の犯罪行為を助けるんですよ、だけどTLS のないインターネットなんて考えられ ないでしょ? 17

Slide 18

Slide 18 text

政治的な話ここまで。RubyKaigi なんだから技術の話に戻りましょう 18

Slide 19

Slide 19 text

Messaging Layer Security More interoperable version of doing E2EE MLS provides key exchange Protocol (RFC 9420), Architecture (to be an RFC soon) Today's topic is the implementation of the Protocol Now we (kinda) have RFC 9420 in Ruby! MLS はE2EE のための相互運用性のある鍵交換を提供します。MLS にはプロトコル定義の文書とアーキテクチ ャのドキュメントがあって、アーキテクチャはそろそろRFC になりそうです。今回はプロトコルの話をしま す。 19

Slide 20

Slide 20 text

Messaging Layer Security is essentially SMTP をやめろ MLS は相互運用性のあるE2EE を実現してくれるのだから、実質SMTP をやめろであると言えますね! 20

Slide 21

Slide 21 text

mls gem already exists (for a defunct website...) gem を作ろうとしたところ、 mls っていうgem は2012 年、MLS が-00 Internet-Draft として出るはるか前から 存在してたんですね。でこのライブラリが使えるサイトもう生きてないんですが… 21

Slide 22

Slide 22 text

Melos https:/ /github.com/sylph01/melos じゃあM とL とS の入ってるいい感じの言葉を探すか、ってことで、日本の中学校・高校の国語の教科書の大 部分に掲載されてるアレです。 22

Slide 23

Slide 23 text

23

Slide 24

Slide 24 text

Messaging Layer Security Any% 『走れメロス』は言ってしまえば「友人の命を救うためにspeedrun(RTA) する話」 、今回も人の命を救うコー ドをRTA した話なので、Messaging Layer Security の解説もAny% speedrun します 24

Slide 25

Slide 25 text

Building blocks: HPKE "Encrypt with Public Key, Decrypt with Private Key", formalized A combination of the following: Key Encapsulation Mechanism (KEM) ( ≒ asymmetric crypto) Key Derivation Function (KDF) ( ≒ hash) Authenticated Encryption with Associated Data (AEAD) (= symmetric crypto) Available in Ruby: hpke gem HPKE はTLS で説明されるいわゆる「公開鍵で暗号化、秘密鍵で復号」をよりフォーマルに定義したもので、 鍵のカプセル化、鍵の導出関数、認証つき暗号の3 つからなります。gem があります。 25

Slide 26

Slide 26 text

Security Characteristics Forward Secrecy: messages sent at a certain point in time are secure in the face of later compromise of a group member secure against "harvest now, decrypt later" attack 暗号化メッセージングに欲しいセキュリティ性質は2 つあり、そのうちの1 つはForward Secrecy 、長期鍵があ る時点で破られたとしても過去のメッセージの安全性には影響がない、という性質です。TLS 1.3 の説明でよ く聞くアレですね 26

Slide 27

Slide 27 text

Security Characteristics Post-Compromise Security: messages are secure even if a group member was compromised at some point in the past Each member updates their key so that group secrets are not always encrypted with private keys that have been compromised もう1 つはPost-Compromise Security 、これは逆に、ある時点での長期鍵が破られても、鍵の更新が適切に行 えることで、将来のメッセージの安全性には影響がない、という性質です。 27

Slide 28

Slide 28 text

Security Characteristics FS は鍵の突破から前のメッセージ、PCS は鍵の突破から未来のメッセージを保護する性質であることを説明 する図です。 28

Slide 29

Slide 29 text

2 person is easy # both party starts with chain key 0 chain_key[0] = "some common secret" # when sending a message... message_key[n] = hmac_sha256(chain_key[n], 0x02) # encrypt the message #(n) using message_key[n] chain_key[n+1] = hmac_sha256(chain_key[n], 0x01) これらを2 人で実現することは簡単で、共通の鍵を申し合わせた上で、各メッセージの鍵をその世代の秘密 から導出し、次の世代の秘密を今の世代の秘密から別の関数で導出します。各メッセージの鍵は異なるので FS が実現できることがわかると思います。なお実はPCS は別の仕組みの組み合わせでやってる 29

Slide 30

Slide 30 text

3+ person is difficult Extend 2-person method? Number of edges in an n-node complete graph is O(n^2) じゃあ3 人以上は?ってなると急に難しいと思います。n-node の完全 グラフのエッジの数がO(n^2) なのはご存知の通り 30

Slide 31

Slide 31 text

Introducing Your Favorite Data Structure That Turns O(n) Into O(log n): Trees そこでみんな大好きO(n) をO(log n) にしてくれるデータ構造、木構造の出番です。 31

Slide 32

Slide 32 text

3 Parts to MLS MLS のRFC にはこんな図があるけど正直わかりにくい。3 つの要素が隠れていることが見えないので 32

Slide 33

Slide 33 text

Key Schedule まずはKey Schedule 。世代を更新するためのcommit_secret がグループで安定的に共有できる前提で、次の世 代のsecret を安定的に作る方法です。ただのハッシュのチェーンなので簡単。 33

Slide 34

Slide 34 text

Secret Tree 各世代のsecret が同じ前提でなら共通のencryption_secret が導出できて、各ユーザーの鍵が導出できる、と いうのがSecret Tree 。 34

Slide 35

Slide 35 text

Secret Tree def self.populate_tree_impl(suite, tree, index, secret) tree.array[index] = { 'handshake_ratchet_secret' => Melos::Crypto.expand_with_label(suite, secret, "handshake", "", suite.kdf.n_h), 'application_ratchet_secret' => Melos::Crypto.expand_with_label(suite, secret, "application", "", suite.kdf.n_h), 'next_handshake_ratchet_secret_generation' => 0, 'next_application_ratchet_secret_generation' => 0 } unless Melos::Tree.leaf?(index) left_secret = Melos::Crypto.expand_with_label(suite, secret, "tree", "left", suite.kdf.n_h) right_secret = Melos::Crypto.expand_with_label(suite, secret, "tree", "right", suite.kdf.n_h) populate_tree_impl(suite, tree, Melos::Tree.left(index), left_secret) populate_tree_impl(suite, tree, Melos::Tree.right(index), right_secret) end end Each user is assigned a leaf in the tree From the base encryption secret, we recursively populate (snip) ユーザーは木のleaf node を割り当てられ、Secret Tree を上からハッシュのチェーンで埋めていくことで秘密 を導出します 35

Slide 36

Slide 36 text

Secret Tree The actual ratcheting part: def self.ratchet_application(suite, tree, leaf_index) node_index = leaf_index * 2 generation = tree.array[node_index]['next_application_ratchet_secret_generation'] application_ratchet_secret = tree.array[node_index]['application_ratchet_secret'] application_nonce = Melos::Crypto.derive_tree_secret(suite, application_ratchet_secret, "nonce", generation, suite.hpke.n_n) application_key = Melos::Crypto.derive_tree_secret(suite, application_ratchet_secret, "key", generation, suite.hpke.n_k) next_application_ratchet_secret = Melos::Crypto.derive_tree_secret(suite, application_ratchet_secret, "secret", generation, suite.kdf.n_h) tree.array[node_index]['next_application_ratchet_secret_generation'] = generation + 1 tree.array[node_index]['application_ratchet_secret'] = next_application_ratchet_secret tree.array[node_index]['application_nonce'] = application_nonce tree.array[node_index]['application_key'] = application_key end 実際のコードはこんな感じで文字数多いですが… 36

Slide 37

Slide 37 text

Secret Tree It essentially boils down to this: # for generation n # derive_tree_secret is essentially a hash function key = Crypto.derive_tree_secret(ratchet_secret[n], "key", n) nonce = Crypto.derive_tree_secret(ratchet_secret[n], "nonce", n) ratchet_secret[n + 1] = Crypto.derive_tree_secret(ratchet_secret[n], "secret", n) This gives you the key and nonce to encrypt the actual messages. 実質的にはさっき説明したやつ(Signal のDouble Ratchet のうちHash Ratchet の部分)と同じ。 37

Slide 38

Slide 38 text

the hardest part: TreeKEM じゃあこれまでの前提、commit_secret をグループで安定的に得るのはどうすんの?でここが一番難しい部 分であるTreeKEM の出番です。 38

Slide 39

Slide 39 text

TreeKEM Users are assigned a leaf node Users have their leaf key pair TreeKEM でもユーザーに葉が割り当てられるのは同じ。ユーザ ーはleaf key pair という公開鍵・秘密鍵の組を持ちます。 39

Slide 40

Slide 40 text

TreeKEM When a user wants to update the group secret, user creates an UpdatePath ユーザーがPCS を実現するために自分自身の鍵を更新するため には、ユーザーはUpdatePath というものを作りそれをグループ に伝えることを行います。 40

Slide 41

Slide 41 text

TreeKEM User generates a random path_secret at their leaf The parent's path_secret is calculated using the child's path_secret (snip) まずはランダムなpath_secret を生成し、親ノードのpath_secret をそれのハッシュで計算、root まで続けて、root からもう一度 ハッシュを取ったのがcommit_secret になります 41

Slide 42

Slide 42 text

TreeKEM When 0 creates its UpdatePath (yellow) We find the copath nodes along the UpdatePath (green) (snip) 0 がUpdatePath を作るとき、UpdatePath (黄色)に沿って copath node (緑)の一覧を得て、もし木全体が埋まっているな らば、各UpdatePath ノードのpath_secret を対応する緑のノード の公開鍵を使って暗号化します。 42

Slide 43

Slide 43 text

TreeKEM We can see that's possible in a 2-leaf tree 0 creates an UpdatePath, node 1 has a path_secret the path secret is encrypted to 2 's public key node 2 knows the (snip) 2 ユーザーの場合で見てみましょう。0 がUpdatePath を作り、1 が path_secret を持ちます。2 の公開鍵で暗号化すると、2 は2 の秘密鍵を知っ ているのでpath_secret を復号でき、commit_secret を得られます。 43

Slide 44

Slide 44 text

TreeKEM What happens if there are blanks in the tree? We calculate the resolution of the copath node to figure out which node's keys are available. Then, we encrypt the path secret of the UpdatePath node to each key でも木が常に全部埋まっているとは限りません、というか埋まっていないことのほうが多いです。その場合 はどうする?実際にやっているのはUpdatePathNode のcopath node に対してそのノードのresolution を計算 し、そのノードの下全ての葉が鍵を知っているノードの組み合わせを計算し、それぞれのノードの鍵に対し てpath_secret を暗号化します。 44

Slide 45

Slide 45 text

TreeKEM The resolution of the node indicated right is [3, 2] The indicated node has unmerged leaves so its resolution is itself + list of unmerged leaves 右の例ではresolution は[3, 2] (※順序付き)と計算されます。3 は unmerged leaves list を持っているので本体→unmerged list の順に計算 します。 45

Slide 46

Slide 46 text

TreeKEM The resolution of the node indicated right is [3, 2, 9, 14] The resolution is used (snip) 右の例では、まず左の木から[3, 2] 、続いて右の木から左優先で足し ていくので[3, 2, 9, 14] です。こうすることで、対象ノードから見て全 ノードに対して暗号化するために必要な鍵のノードの最小セットを 知ることができます。 46

Slide 47

Slide 47 text

TreeKEM So actually what you do is: Calculate path_secret s for each node on UpdatePath then encrypt (snip) なので実際は、各UpdatePathNode に対して、copath node の resolution を取得し、resolution の各ノードの鍵に対して path_secret を暗号化する、ということをします。 47

Slide 48

Slide 48 text

Evolution of a Group A group is updated through messages called proposals and commits Proposals Add and remove users Notifies an update of user's leaf key Injects Pre-Shared Keys Commits will fix those information and advances the group's epoch UpdatePath s are conveyed in the Commit 次はグループの変遷を見ていきましょう。Proposal とCommit という2 種類のメッセージを使ってグループの 状態を変更していきます。 48

Slide 49

Slide 49 text

Evolution of a Group Users who want to join the group publishes their identity (including its public key) to a Directory using a KeyPackage グループに入りたいユーザーは、それぞれDirectory に自身の アイデンティティ情報を示すKeyPackage というものを登録し ます。 49

Slide 50

Slide 50 text

Evolution of a Group User A creates an initial group To add User B , A sends an Add proposal adding B , then Commit s (snip) A は最初のグループを作り、B を追加するためには、B の KeyPackage を含むAdd メッセージとCommit を送信し、B には Welcome を送信します。 50

Slide 51

Slide 51 text

Evolution of a Group When Add ing: Add the leaf_node from the KeyPackage in the proposal to the leftmost empty node Then add the leaf index to intermediate nodes' unmerged list Add のTreeKEM での動作はこう。KeyPackage からleaf_node を 取り出し一番左の空きに追加、でroot まで順にunmerged list に対象のノードindex を加えていきます。 51

Slide 52

Slide 52 text

Evolution of a Group User B sends an Update proposal notifying the group of its leaf key update Then User A Commit s that update to include it in the epoch advancement ユーザーが鍵を更新するときはUpdate メッセージを送信しま す。 52

Slide 53

Slide 53 text

Evolution of a Group When Update ing: Replace the leaf node of the sender with the leaf_node inside the Update proposal Then blank all nodes on its direct path up to the root Update のときは更新先のleaf_node が含まれてくるので、それ をsender のleaf_index に足し、root までのノードを空白化しま す 53

Slide 54

Slide 54 text

Evolution of a Group When removing a user: Z sends a Remove proposal and a Commit B cannot decrypt () ユーザーのグループからの削除の際にはRemove proposal を使 います。Remove 後のcommit に含まれるUpdatePath をremove 対象のユーザーは復号できなくなっているので、remove され たことはわかるけど次の世代に進めません 54

Slide 55

Slide 55 text

Evolution of a Group When Remove ing: Blank the specified node in the proposal Then blank all nodes on its direct path (snip) Update 同様、Remove では葉の削除後、root まで空白化しま す。その後に木の右半分が完全に空白なら木を縮めることを 行います 55

Slide 56

Slide 56 text

Evolution of a Group When are intermediate parent nodes filled? During the processing of a Commit When processing a Commit , the UpdatePath inside the Commit is merged into the ratchet tree ParentNode s are created based on the UpdatePath content 空白化するといったけど中間ノードはどうやって埋まるの?ということについては、Commit の際に UpdatePath から得たUpdatePathNode を使って埋めていきます。UpdatePathNode を復号できたユーザーは path_secret を知っているので、そこからそのノードの鍵を導出します 56

Slide 57

Slide 57 text

Stuff we are omitting here Injection of Pre-Shared Keys Transcript Hashes Hashes that summarize the proposals/commits taken place in the last epoch Signing/Verification of messages There are public messages and private messages というわけでMessaging Layer Security Any% 、タイマーストップです。完走した感想… ではなくて、Any% では 説明していない項目がかなりあります。RFC とか実装を読んでね 57

Slide 58

Slide 58 text

58

Slide 59

Slide 59 text

Implementation tricks 実装の小ネタを紹介します 59

Slide 60

Slide 60 text

It's not just a pack and unpack MLS has variable length vectors and optional values in their structs struct { opaque group_id; uint64 epoch; ContentType content_type; opaque authenticated_data; opaque encrypted_sender_data; opaque ciphertext; } PrivateMessage; 「バイナリの解析でしょ?pack/unpack じゃん、なんも難しいことないでしょ」っておもーじゃん? 60

Slide 61

Slide 61 text

Variable length vectors struct { uint32 fixed<0..255>; opaque variable; } StructWithVectors; Based on variable-length integer encoding in RFC 9000, Section 16 2-bit prefix (snip) MLS にはQUIC にある可変長整数エンコーディングを使った可変長文字列っていうのがあります。2^30 バイト までのベクターを表現できます。 61

Slide 62

Slide 62 text

Optional values struct { uint8 present; select (present) { case 0: struct{}; case 1: T value; }; } optional; MLS にはoptional values といって存在するかどうかの変数と存在した場合に値が入っているstruct がありま す。ね?parse するのめんどそうでしょ? 62

Slide 63

Slide 63 text

Define structs like this class Melos::Struct::PrivateMessage < Melos::Struct::Base attr_reader :group_id, :epoch, :content_type, :authenticated_data, :encrypted_sender_data, :ciphertext STRUCT = [ [:group_id, :vec], [:epoch, :uint64], [:content_type, :uint8], [:authenticated_data, :vec], [:encrypted_sender_data, :vec], [:ciphertext, :vec] ] でこんなんのencode/decode いちいち書いてられんので、メタプログラミングの出番です。こうやってstruct の定義をコードに落として 63

Slide 64

Slide 64 text

Melos::Struct::Base #initialize , .new_and_rest : deserializer #raw : serializer These operate based on the STRUCT constant of the class 実態としては Melos::Struct::Base はこのSTRUCT 定数をベースにserialize/deserialize していきます 64

Slide 65

Slide 65 text

Deserialize each element like this def deserialize_elem(buf, type, type_param) case type when :uint8 value = buf.byteslice(0, 1).unpack1('C') buf = buf.byteslice(1..) (snip) when :vec value, buf = Melos::Vec.parse_vec(buf) (snip) 各type のdeserialize はこうやってやっていきます。これ実際のところString を使ったバッファじゃなくて StringIO 一本でやるのがよさそうってのはわかってる 65

Slide 66

Slide 66 text

Sometimes a class is nested class Melos::Struct::FramedContent < Melos::Struct::Base (snip) STRUCT = [ [:group_id, :vec], [:epoch, :uint64], [:sender, :class, Melos::Struct::Sender], [:authenticated_data, :vec], [:content_type, :uint8], (snip) ] でMLS のstruct ってstruct の中にstruct がネストされてることがありまして 66

Slide 67

Slide 67 text

So we recursively call new when :class value, buf = type_param.send(:new_and_rest, buf) when :classes # prefix, length = buf.get_prefix_and_length # puts "#{prefix}, #{length}" vec, buf = Melos::Vec.parse_vec(buf) value = [] while (vec.bytesize > 0) current_instance, vec = type_param.send(:new_and_rest, vec) value << current_instance end そいつは :class タイプの追加の引数に対して send で new_and_rest を呼んで再帰的にバラします 67

Slide 68

Slide 68 text

We sometimes have values that depend on other values class Melos::Struct::Sender < Melos::Struct::Base attr_reader :sender_type, :leaf_index, :sender_index STRUCT = [ [:sender_type, :uint8], [:leaf_index, :select, ->(ctx){ctx[:sender_type] == 0x01}, :uint32], [:sender_index, :select, ->(ctx){ctx[:sender_type] == 0x02}, :uint32], ] でこれみたいに sender_type によって値があるとかないとかみたいなやつがあって 68

Slide 69

Slide 69 text

We call the Proc with the current context to handle that def deserialize_select_elem_with_context( buf, context, predicate, type, type_param) if predicate.(context) deserialize_elem(buf, type, type_param) else [nil, buf] end end それはProc を :select 形の引数にとって、これまで解析した値(context) を使ってProc を呼んであげた結果が true なら解析する、という方法でバラします 69

Slide 70

Slide 70 text

Trees with arrays Complete balanced trees can be described with a flat array For example, the tree on the right will be written as: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, nil, 11, nil, nil, nil] あとRuby で木構造扱うのってポインタでやろうとすると 生のポインタないから厄介で、完全二分木だったらarray で表現できるのでそうします 70

Slide 71

Slide 71 text

Trees with arrays Leaf nodes are always even indexed Parent nodes are always odd indexed (snip) そうするとleaf は偶数、parent は奇数、とか、あるノード index に対して右はどれ、左はどれ、みたいなのを木の性 質によって定義していきます 71

Slide 72

Slide 72 text

Trees with arrays The algorithms are described in Appendix C of RFC 9420 Also this comes with a serialization/deserialization format! アルゴリズム自体はRFC の付録に書いてあります。あと serialization/deserialization format もついてきて便利 72

Slide 73

Slide 73 text

73

Slide 74

Slide 74 text

Ongoing/Future work of introducing modern cryptography into Ruby 最後に、モダンな暗号技術をRuby に持ってくる上でやってること・やりたいことの話をします 74

Slide 75

Slide 75 text

HPKE using OpenSSL's API HPKE is nice, and OpenSSL itself has APIs that do this Talked about it at RubyConf Taiwan 2023 Haven't worked on it much since OpenSSL gem そのものにHPKE を入れる話をRubyConf Taiwan 2023 でしたんですが、そっからあんまり触って ないです 75

Slide 76

Slide 76 text

HPKE using OpenSSL's API But is it a good idea? Protocols need the whole cipher suite Not only encap/decap and open/seal Also want access to constants such as hash/key length OpenSSL's HPKE context remembers which algorithm to use, but to use them separately you have to call them separately でも本当に嬉しいかと言われるとそうでもない気がしていて、プロトコルはハッシュの長さとかの定数や、 encap/decap 以外の機能へもアクセスしたくて、それはOpenSSL のHPKE は直接は提供してくれないからです ね 76

Slide 77

Slide 77 text

Some APIs of OpenSSL gem are intentionally undocumented ...but I'm not gonna fix it. You have to know what you're doing to use them 前も言った気がするけどOpenSSL gem にはundocumented API がけっこうあります。がそれを直す気はない。 わかってる人にのみ使って欲しいAPI なんですよ 77

Slide 78

Slide 78 text

Missing features in OpenSSL? def self.signature_key_pair_corresponds?(suite, private_key, public_key) private_pkey = suite.pkey.deserialize_private_signature_key(private_key) public_pkey = suite.pkey.deserialize_public_signature_key(public_key) if suite.pkey.equal?(Melos::Crypto::CipherSuite::X25519) || suite.pkey.equal?(Melos::Crypto::CipherSuite::X448) # is an Edwards curve; check equality of the raw public key private_pkey.raw_public_key == public_pkey.raw_public_key else # is an EC; check equality of the public key Point private_pkey.public_key == public_pkey.public_key end end もしかしたらないかもって機能として鍵ペアの対応を得る機能があった。こんな感じのしんどいコードにな る 78

Slide 79

Slide 79 text

Missing features in OpenSSL? def self.derive_key_pair(suite, secret) pkey = suite.hpke.kem.derive_key_pair(secret) if suite.pkey.equal?(Melos::Crypto::CipherSuite::X25519) || suite.pkey.equal?(Melos::Crypto::CipherSuite::X448) # is an Edwards curve [pkey.raw_private_key, pkey.raw_public_key] else # is an EC [pkey.private_key.to_s(2), pkey.public_key.to_bn.to_s(2)] end end あとTLS やMLS が欲しい形のEC 鍵ペアのフォーマットを得る方法は自明じゃない 79

Slide 80

Slide 80 text

Additional features in OpenSSL OpenSSL 3.5.0 ships with post-quantum cryptography ML-KEM, ML-DSA, SLH-DSA There is an Internet-Draft that adds ML-KEM in MLS OpenSSL 3.5.0 は耐量子暗号アルゴリズムのサポートがあります。ML-KEM のMLS への導入のドラフトがある ので、もしOpenSSL gem でサポートできたらRuby のMLS は真っ先にサポートできますね! 80

Slide 81

Slide 81 text

Q: MLS is E2EE so client-side, right? Ruby is server-side lang lol もしかしたらこう聞く向きがあるかもしれません:MLS ってクライアントサイドの仕事でしょ?Ruby はサー バーサイド言語じゃん、笑 81

Slide 82

Slide 82 text

A: NO Ruby is not just Rails We have ruby-wasm いいえ。Ruby はRails だけじゃないし、ruby-wasm って知ってる? 82

Slide 83

Slide 83 text

Grand Unifying Cryptography API Different platforms depend on different cryptographic libraries Desktop: OpenSSL Compatible (to an extent) with LibreSSL, BoringSSL, and the like Browser: Web Crypto API Embedded: Mbed TLS, wolfSSL, ... ruby-wasm の話をしたので、ブラウザで暗号機能を動かす場合、Web Crypto API の助けを得る必要がありま す。各プラットフォームで異なる依存ライブラリに対して、 「大統一暗号ライブラリ」を提供することはで きないか?ということを最近考えています 83

Slide 84

Slide 84 text

Grand Unifying Cryptography API Very quick example of calling Web Crypto API's random number generator require "js" array = JS::eval('return new Uint8Array(16)') JS.global[:window][:crypto].getRandomValues(array) p array ruby-wasm 経由でWeb Crypto API の乱数生成を呼ぶ簡単なサンプルがこれです。こんな感じで主要な暗号機 能に対してラッパーを用意していくことを考えています 84

Slide 85

Slide 85 text

85

Slide 86

Slide 86 text

Conclusion トークを締めるにあたって、トークのdescription に書いた質問への回答を行いましょう。 86

Slide 87

Slide 87 text

Why do you need End-to-End Encryption in Ruby? Because... ( これはDanceDanceRevolution のネタです。ニコニコ大百科の「MAX.(period) 」 、 「Over the "Period" 」をご参照 ください) 87

Slide 88

Slide 88 text

Ruby needs them to stay relevant or else people would just use Python, Go, Rust, whatever the cool kids use these days 第一に、Ruby がこれらを持たない場合、 「えーRuby にないの?じゃあいいや、イケてるモダンな言語である ところのPython/Go/Rust... で作っちゃえ」ってなるでしょう。ところでPython ってまだMLS 実装ないらしい ですね? 88

Slide 89

Slide 89 text

Ruby needs Freedom on the Internet and the Free Internet needs Ruby そして、Ruby の発展のためには自由なインターネットは欠かすことができず、自由なインターネットも発展 のためにはRuby とそのコミュニティを必要とするでしょう。私はそのように信じています。 89

Slide 90

Slide 90 text

Shoutouts The Messaging Layer Security Working Group @ IETF Protocol implementers in Ruby (the list is growing!) RubyKaigi 2025 organizer team, esp. the local organizers 謝辞。そういえばこの後プロトコル実装者のトークが本編セッションとLT に1 個ずつあるんですよ、当然聞 きに行きますよね? 90

Slide 91

Slide 91 text

More Shoutouts Sponsors of RubyKaigi, esp: (ones I have personal connections with) codeTakt Inc. Bloomo Securities Inc. スポンサーさんはいいぞ。個人的につながりのあるところ。codeTakt さんは今の 現場の一つ。 「4 月仕事しねえぞ」って言って許してくれてありがとう。Bloomo さんはCEO が大学のゼミの同期でめっちゃびっくりした。 91

Slide 92

Slide 92 text

Questions? / Comments? Twitter: @s01 or Fediverse: @[email protected] also find me in the venue / at drinkups! I am at: codeTakt Day2/Day4 Drinkup, RubyMusicMixin @ Day3 このへんにいるから気になることある人は話しかけてね。あと本番のスライドにはこの前に2 枚入ってるけ どそれは本編限定です 92