diff options
| author | mo khan <mo@mokhan.ca> | 2025-05-27 12:30:45 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-05-27 12:30:45 -0600 |
| commit | 527ca756cbe44016596fbfa27a090b2f330316bb (patch) | |
| tree | 1a56a80c03741b71d793aa968a2dd0ba21b2ee17 /share | |
| parent | c522506bb06ae36492dee4be50b565b25c430c72 (diff) | |
docs: add an example of sending/receiving messages with signatures
Diffstat (limited to 'share')
| -rw-r--r-- | share/man/ENVOY.md | 87 |
1 files changed, 78 insertions, 9 deletions
diff --git a/share/man/ENVOY.md b/share/man/ENVOY.md index 7db50cd..9fb0701 100644 --- a/share/man/ENVOY.md +++ b/share/man/ENVOY.md @@ -189,7 +189,7 @@ own private key. #!/bin/env ruby require 'openssl' -class Player +class Person attr_reader :name, :public_key def initialize(name, private_key = OpenSSL::PKey::RSA.new(2048)) @@ -198,22 +198,22 @@ class Player @public_key = private_key.public_key end - def send_to(player, plaintext) - ciphertext = player.public_key.public_encrypt(plaintext) - player.receive_from(self, ciphertext) + def send_to(person, plaintext) + ciphertext = person.public_key.public_encrypt(plaintext) + person.receive_from(self, ciphertext) end - def receive_from(player, ciphertext) + def receive_from(person, ciphertext) plaintext = @private_key.private_decrypt(ciphertext) - puts "#{player.name}: #{plaintext}\n" + puts "#{person.name}: #{plaintext}\n" end end -clifford = Player.new("clifford") -reginald = Player.new("reginald") +clifford = Person.new("clifford") +reginald = Person.new("reginald") clifford.send_to(reginald, "What time is it?") -reginald.send_to(clifford, "Time to go live!") +reginald.send_to(clifford, "Time to go live! Who sent this?") ``` #### Authenticity @@ -227,6 +227,14 @@ then they can decrypt the signature using the public key of the sender. If signature can be decrypted without an error then we can trust that the message did in fact originate from the sender. This authenticates the message. +In the previous code example each party was able to ensure that the message that +they delivered to the intended recipient could only be read by that recipient. +However, the recipient could not guarantee that they message that they received +actually came from the party that claims to have sent it. If an attacker could +eavesdrop on the conversation, they could intercept the message and rewrite it +before delivering it. This might cause confusion between the two parties and an +attacker could then coerce one of the parties into a specific action. + #### Integrity When a recipient receives a message from a sender the recipient also needs to @@ -235,6 +243,67 @@ an encrypted hash then the recipient can compute a hash of the plaintext message and compare it with the hash in the encrypted signature. This ensures that the message hasn't been tampered with. +In the following code example the two actors perform a public key exchange with +each other before they start to communicate with each other. This allows them to +verify that the message that they receive did in fact originate from the person +that they think it originated from. It also allows them to ensure that the +message hasn't been altered in transit by appending a signature. + +```ruby +#!/bin/env ruby +require 'openssl' + +class Person + attr_reader :name, :public_key + + def initialize(name, private_key = OpenSSL::PKey::RSA.new(2048)) + @name = name + @private_key = private_key + @public_key = private_key.public_key + @friends = {} + end + + def add_friend(friend) + @friends[friend.name] = friend.public_key + end + + def send_to(person, plaintext) + signature = @private_key.private_encrypt(Digest::SHA1.hexdigest(plaintext)) + person.receive([self.name, plaintext, signature]) + end + + def receive(message) + raise "This message cannot be trusted" unless valid?(message) + + name, plaintext, _ = message + puts "#{name}: #{plaintext}\n" + end + + private + + def valid?(message) + header, body, signature = message + public_key = @friends[header] + + # verify that we know the sender + return false if public_key.nil? + + # verify that the message hasn't been altered in transit + Digest::SHA1.hexdigest(body) == public_key.public_decrypt(signature) + end +end + +clifford = Person.new("clifford") +reginald = Person.new("reginald") + +# public key exchange +clifford.add_friend(reginald) +reginald.add_friend(clifford) + +clifford.send_to(reginald, "What time is it?") +reginald.send_to(clifford, "It's still time to go live!") +``` + In order for us to be able to trust JSON Web Tokens we need public/private key pairs that we can use to validate the authenticity and integrity of the token. It is also possible to encrypt the JWT body but this isn't necessary and this is |
