Skip to content

Commit 5204e97

Browse files
authored
Fix cleartext leak in signatures of UTF-8 encoded messages (#244)
When signing UTF-8 encoded messages containing multi-byte characters (like German umlauts ö, ü, ä), the signature could be incorrectly sized (e.g., 90 bytes instead of 64 bytes). This occurred because Ruby's string slicing [0, n] operates on characters (not bytes) when the string has UTF-8 encoding. The issue affected both signing and verification: 1. SigningKey#sign: The line `sign_attached(message)[0, signature_bytes]` would slice the first 64 characters instead of the first 64 bytes, resulting in signatures larger than 64 bytes when the combined signature+message buffer inherited UTF-8 encoding from the message. 2. VerifyKey#verify: The concatenation `signature + message` would fail with an encoding compatibility error when trying to combine an ASCII-8BIT signature with a UTF-8 message. Fix: Force messages to binary encoding (ASCII-8BIT) before passing them to the underlying libsodium functions. This ensures: - String slicing operates on bytes, not characters - Signatures are always exactly 64 bytes - No encoding compatibility errors during verification Added test case with German umlauts to prevent regression.
1 parent 4d50984 commit 5204e97

3 files changed

Lines changed: 16 additions & 2 deletions

File tree

lib/rbnacl/signatures/ed25519/signing_key.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ def initialize(seed)
7777
#
7878
# @return [String] Signature as bytes
7979
def sign(message)
80-
sign_attached(message)[0, signature_bytes]
80+
# Force message to binary encoding to ensure [0, signature_bytes] slices by bytes not characters
81+
# This prevents issues with UTF-8 encoded strings where multi-byte characters would cause
82+
# incorrect signature length (e.g., 90 bytes instead of 64)
83+
message_binary = message.b
84+
sign_attached(message_binary)[0, signature_bytes]
8185
end
8286

8387
# Sign a message using this key, attaching the signature to the message

lib/rbnacl/signatures/ed25519/verify_key.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ def initialize(key)
5151
def verify(signature, message)
5252
signature = signature.to_str
5353
Util.check_length(signature, signature_bytes, "signature")
54-
verify_attached(signature + message)
54+
# Force message to binary encoding to prevent encoding compatibility errors
55+
# when concatenating with signature (which is always ASCII-8BIT)
56+
message_binary = message.b
57+
verify_attached(signature + message_binary)
5558
end
5659

5760
# Verify a signature for a given signed message

spec/rbnacl/signatures/ed25519/signing_key_spec.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
expect(subject.sign(message)).to eq signature
1919
end
2020

21+
it "signs UTF-8 encoded messages" do
22+
utf8_message = "Björn Müller from München".dup.force_encoding("UTF-8")
23+
signature = subject.sign(utf8_message)
24+
expect(signature.bytesize).to eq(64)
25+
expect(subject.verify_key.verify(signature, utf8_message)).to be true
26+
end
27+
2128
it "signs messages, full version" do
2229
expect(subject.sign_attached(message)[0, RbNaCl::SigningKey.signature_bytes]).to eq signature
2330
expect(subject.sign_attached(message)[RbNaCl::SigningKey.signature_bytes, message.length]).to eq message

0 commit comments

Comments
 (0)