Skip to content

Commit 02b379d

Browse files
committed
Fix encryption, following RFC8291
1 parent 52725de commit 02b379d

File tree

1 file changed

+13
-4
lines changed

1 file changed

+13
-4
lines changed

lib/webpush/encryption.rb

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ module Encryption
77
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
88
def encrypt(message, p256dh, auth)
99
assert_arguments(message, p256dh, auth)
10+
# Following RFC8291, messages can't be longer than 3993 bytes long
11+
# so encrypted message do not exceed 4096 bytes
12+
raise ArgumentError, "message is too big" if message.bytesize > 3993
1013

1114
group_name = 'prime256v1'
1215
salt = Random.new.bytes(16)
@@ -33,10 +36,15 @@ def encrypt(message, p256dh, auth)
3336
nonce = HKDF.new(prk, salt: salt, info: nonce_info).next_bytes(12)
3437

3538
ciphertext = encrypt_payload(message, content_encryption_key, nonce)
39+
# This should never happen if message length <= 3993
40+
raise ArgumentError, "encrypted payload is too big" if ciphertext.bytesize > 4096
3641

3742
serverkey16bn = convert16bit(server_public_key_bn)
38-
rs = ciphertext.bytesize
39-
raise ArgumentError, "encrypted payload is too big" if rs > 4096
43+
# According to RFC8188, the final record can be smaller than the record size
44+
# RFC8291 requires encrypted messages to be at most 4096. And the example set the
45+
# RS to 4096. RS can be smaller to 4096 but hardcoding is also following the specs.
46+
# We set RS=4096 to allow testing with the RFC example.
47+
rs = 4096
4048

4149
aes128gcmheader = "#{salt}" + [rs].pack('N*') + [serverkey16bn.bytesize].pack('C*') + serverkey16bn
4250

@@ -47,13 +55,14 @@ def encrypt(message, p256dh, auth)
4755
private
4856

4957
def encrypt_payload(plaintext, content_encryption_key, nonce)
58+
# RFC8291 requires the padding delimiter to be 0x02
59+
plaintext = plaintext + "\x02"
5060
cipher = OpenSSL::Cipher.new('aes-128-gcm')
5161
cipher.encrypt
5262
cipher.key = content_encryption_key
5363
cipher.iv = nonce
5464
text = cipher.update(plaintext)
55-
padding = cipher.update("\2\0")
56-
e_text = text + padding + cipher.final
65+
e_text = text + cipher.final
5766
e_tag = cipher.auth_tag
5867

5968
e_text + e_tag

0 commit comments

Comments
 (0)