omq-blake3zmq 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +70 -10
- data/lib/omq/blake3zmq/crypto.rb +1 -1
- data/lib/omq/blake3zmq/mechanism.rb +22 -15
- data/lib/omq/blake3zmq/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f303db5348da7fbd1c223fc9159414f530a10db03de06a6b51ce0708731262c0
|
|
4
|
+
data.tar.gz: 697ef532c927f8c4e1f0d380e5a023c50004ba1e995b91b4152ead7be46eabc3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 477e5dca0140c73bc6bb312a0bd3c053462a989a035539b54ebd54ac933e5113916755de2869499532d4d2b52c9890a67415ae2fc80dd06c254ff3f58a0d9edc
|
|
7
|
+
data.tar.gz: 3fcc887fb323cd0bd8cfd0cdc5bd59519e6627a3874a5fa63ca4a430021531d5149b02619c72f8c17b903c0b792310570d559c71e8c2dbacf000692bd7dcec3d
|
data/README.md
CHANGED
|
@@ -62,7 +62,7 @@ server.mechanism = Protocol::ZMTP::Mechanism::Blake3.server(
|
|
|
62
62
|
)
|
|
63
63
|
server.bind("tcp://127.0.0.1:9999")
|
|
64
64
|
|
|
65
|
-
# Client socket (keys optional
|
|
65
|
+
# Client socket (keys optional; omit for anonymous/ephemeral identity)
|
|
66
66
|
client = OMQ::REQ.new
|
|
67
67
|
client.mechanism = Protocol::ZMTP::Mechanism::Blake3.client(
|
|
68
68
|
server_key: server_pk,
|
|
@@ -72,6 +72,75 @@ client.mechanism = Protocol::ZMTP::Mechanism::Blake3.client(
|
|
|
72
72
|
client.connect("tcp://127.0.0.1:9999")
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
## Wire format
|
|
76
|
+
|
|
77
|
+
### Handshake
|
|
78
|
+
|
|
79
|
+
Four ZMTP command frames over plain TCP:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
Client Server
|
|
83
|
+
| |
|
|
84
|
+
|-- HELLO (C', hello_box) ---------> | 232 bytes
|
|
85
|
+
| |
|
|
86
|
+
| <-- WELCOME (welcome_box) -------- | 224 bytes
|
|
87
|
+
| |
|
|
88
|
+
|-- INITIATE (cookie, init_box) ---> | variable
|
|
89
|
+
| |
|
|
90
|
+
| <-- READY (ready_box) ------------ | variable
|
|
91
|
+
| |
|
|
92
|
+
|====== encrypted data phase ========|
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
HELLO carries the client's ephemeral public key `C'` and a box proving
|
|
96
|
+
knowledge of the server's permanent public key `S`. WELCOME returns the
|
|
97
|
+
server's ephemeral key `S'` inside a stateless cookie. INITIATE sends
|
|
98
|
+
back the cookie plus the client's permanent key `C` and a vouch binding
|
|
99
|
+
`C'` to `C`. READY confirms the session.
|
|
100
|
+
|
|
101
|
+
### Data phase
|
|
102
|
+
|
|
103
|
+
Every post-handshake frame (data and command) is AEAD-encrypted:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
+-----------+----------------+------------------+-----------+
|
|
107
|
+
| flags | length | ciphertext | tag |
|
|
108
|
+
| (1 byte) | (1 or 8 bytes) | (N bytes) | (32 bytes)|
|
|
109
|
+
+-----------+----------------+------------------+-----------+
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The `flags` byte and `length` bytes are authenticated as AAD. No counter
|
|
113
|
+
is sent on the wire; both peers derive per-message nonces from a
|
|
114
|
+
synchronized monotonic counter and a session-unique nonce prefix.
|
|
115
|
+
|
|
116
|
+
Unlike CurveZMQ, SUBSCRIBE/CANCEL/JOIN/LEAVE/PING/PONG/ERROR are all
|
|
117
|
+
AEAD-encrypted. CurveZMQ sends these in plaintext, leaking subscription
|
|
118
|
+
topics, group memberships, and heartbeat content.
|
|
119
|
+
|
|
120
|
+
## Constants
|
|
121
|
+
|
|
122
|
+
| Constant | Value |
|
|
123
|
+
|---|---|
|
|
124
|
+
| Key size | 32 bytes |
|
|
125
|
+
| Nonce size | 24 bytes |
|
|
126
|
+
| Tag size | 32 bytes |
|
|
127
|
+
| Cookie size | 152 bytes (24 nonce + 96 payload + 32 tag) |
|
|
128
|
+
| Mechanism name | `BLAKE3` (6 octets, null-padded to 20) |
|
|
129
|
+
| Protocol ID | `BLAKE3ZMQ-1.0` |
|
|
130
|
+
| HELLO body | 232 bytes |
|
|
131
|
+
| WELCOME body | 224 bytes |
|
|
132
|
+
|
|
133
|
+
## Per-message overhead
|
|
134
|
+
|
|
135
|
+
| | BLAKE3ZMQ | CurveZMQ |
|
|
136
|
+
|---|---:|---:|
|
|
137
|
+
| Tag size | 32 bytes | 16 bytes |
|
|
138
|
+
| Counter on wire | 0 bytes | 8 bytes |
|
|
139
|
+
| Command wrapper | none | 17 bytes |
|
|
140
|
+
| **Total** | **32 bytes** | **41 bytes** |
|
|
141
|
+
|
|
142
|
+
BLAKE3ZMQ has 9 bytes less overhead per message despite a larger tag.
|
|
143
|
+
|
|
75
144
|
## Benchmarks
|
|
76
145
|
|
|
77
146
|
CurveZMQ (RbNaCl/libsodium) vs BLAKE3ZMQ (Rust native ChaCha20-BLAKE3 + C native X25519).
|
|
@@ -113,15 +182,6 @@ Run benchmarks yourself:
|
|
|
113
182
|
OMQ_DEV=1 bundle exec ruby bench/throughput.rb
|
|
114
183
|
```
|
|
115
184
|
|
|
116
|
-
## Per-message overhead
|
|
117
|
-
|
|
118
|
-
| | BLAKE3ZMQ | CurveZMQ |
|
|
119
|
-
|---|---:|---:|
|
|
120
|
-
| Tag size | 32 bytes | 16 bytes |
|
|
121
|
-
| Counter on wire | 0 bytes | 8 bytes |
|
|
122
|
-
| Command wrapper | none | 17 bytes |
|
|
123
|
-
| **Total** | **32 bytes** | **41 bytes** |
|
|
124
|
-
|
|
125
185
|
## Development
|
|
126
186
|
|
|
127
187
|
```
|
data/lib/omq/blake3zmq/crypto.rb
CHANGED
|
@@ -19,7 +19,7 @@ module Protocol
|
|
|
19
19
|
# backend::Cipher.new(key)
|
|
20
20
|
# #encrypt(nonce, plaintext, aad:) -> ciphertext+tag
|
|
21
21
|
# #decrypt(nonce, ciphertext+tag, aad:) -> plaintext
|
|
22
|
-
# backend::
|
|
22
|
+
# backend::Session.new(enc_key, auth_key, nonce)
|
|
23
23
|
# #encrypt(plaintext, aad:) -> ciphertext+tag
|
|
24
24
|
# #decrypt(ciphertext+tag, aad:) -> plaintext
|
|
25
25
|
# backend::Hash.digest(input) -> 32 bytes
|
|
@@ -100,8 +100,8 @@ module Protocol
|
|
|
100
100
|
end
|
|
101
101
|
end
|
|
102
102
|
|
|
103
|
-
@
|
|
104
|
-
@
|
|
103
|
+
@send_session = nil
|
|
104
|
+
@recv_session = nil
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
|
|
@@ -110,8 +110,8 @@ module Protocol
|
|
|
110
110
|
# @param source [Blake3] the original mechanism being duplicated
|
|
111
111
|
def initialize_dup(source)
|
|
112
112
|
super
|
|
113
|
-
@
|
|
114
|
-
@
|
|
113
|
+
@send_session = nil
|
|
114
|
+
@recv_session = nil
|
|
115
115
|
end
|
|
116
116
|
|
|
117
117
|
|
|
@@ -183,7 +183,7 @@ module Protocol
|
|
|
183
183
|
end
|
|
184
184
|
aad = wire_flags + length_bytes
|
|
185
185
|
|
|
186
|
-
ct = @
|
|
186
|
+
ct = @send_session.encrypt(body, aad: aad)
|
|
187
187
|
|
|
188
188
|
wire = String.new(encoding: Encoding::BINARY, capacity: aad.bytesize + frame_size)
|
|
189
189
|
wire << aad << ct
|
|
@@ -215,7 +215,7 @@ module Protocol
|
|
|
215
215
|
aad = wire_flags + length_bytes
|
|
216
216
|
|
|
217
217
|
begin
|
|
218
|
-
pt = @
|
|
218
|
+
pt = @recv_session.decrypt(frame.body, aad: aad)
|
|
219
219
|
rescue @crypto::CryptoError
|
|
220
220
|
raise Error, "decryption failed"
|
|
221
221
|
end
|
|
@@ -616,17 +616,19 @@ module Protocol
|
|
|
616
616
|
def derive_session_keys!(h4, dh2, as_client:)
|
|
617
617
|
ikm = h4 + dh2
|
|
618
618
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
619
|
+
c2s_enc_key = kdf("#{PROTOCOL_ID} client->server enc key", ikm)
|
|
620
|
+
c2s_auth_key = kdf("#{PROTOCOL_ID} client->server auth key", ikm)
|
|
621
|
+
c2s_nonce = kdf8("#{PROTOCOL_ID} client->server nonce", ikm)
|
|
622
|
+
s2c_enc_key = kdf("#{PROTOCOL_ID} server->client enc key", ikm)
|
|
623
|
+
s2c_auth_key = kdf("#{PROTOCOL_ID} server->client auth key", ikm)
|
|
624
|
+
s2c_nonce = kdf8("#{PROTOCOL_ID} server->client nonce", ikm)
|
|
623
625
|
|
|
624
626
|
if as_client
|
|
625
|
-
@
|
|
626
|
-
@
|
|
627
|
+
@send_session = @crypto::Session.new(c2s_enc_key, c2s_auth_key, c2s_nonce)
|
|
628
|
+
@recv_session = @crypto::Session.new(s2c_enc_key, s2c_auth_key, s2c_nonce)
|
|
627
629
|
else
|
|
628
|
-
@
|
|
629
|
-
@
|
|
630
|
+
@send_session = @crypto::Session.new(s2c_enc_key, s2c_auth_key, s2c_nonce)
|
|
631
|
+
@recv_session = @crypto::Session.new(c2s_enc_key, c2s_auth_key, c2s_nonce)
|
|
630
632
|
end
|
|
631
633
|
end
|
|
632
634
|
|
|
@@ -645,6 +647,11 @@ module Protocol
|
|
|
645
647
|
end
|
|
646
648
|
|
|
647
649
|
|
|
650
|
+
def kdf8(context, material)
|
|
651
|
+
@crypto::Hash.derive_key(context, material, size: 8)
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
|
|
648
655
|
def kdf24(context, material)
|
|
649
656
|
@crypto::Hash.derive_key(context, material, size: NONCE_SIZE)
|
|
650
657
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omq-blake3zmq
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Patrik Wenger
|
|
@@ -43,14 +43,14 @@ dependencies:
|
|
|
43
43
|
requirements:
|
|
44
44
|
- - ">="
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '0.
|
|
46
|
+
version: '0.3'
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '0.
|
|
53
|
+
version: '0.3'
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: x25519
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|