From 744e194b90419b88eb960cade8df8eb186226d6d Mon Sep 17 00:00:00 2001 From: Benjamin Neff Date: Tue, 9 Feb 2016 20:37:28 +0100 Subject: [PATCH] add EncryptedMagicEnvelope This is a simpler replacement for the EncryptedSlap --- lib/diaspora_federation/salmon.rb | 1 + .../salmon/encrypted_magic_envelope.rb | 61 +++++++++++++++++++ .../salmon/encrypted_magic_envelope_spec.rb | 54 ++++++++++++++++ 3 files changed, 116 insertions(+) create mode 100644 lib/diaspora_federation/salmon/encrypted_magic_envelope.rb create mode 100644 spec/lib/diaspora_federation/salmon/encrypted_magic_envelope_spec.rb diff --git a/lib/diaspora_federation/salmon.rb b/lib/diaspora_federation/salmon.rb index dd8f288..945bc53 100644 --- a/lib/diaspora_federation/salmon.rb +++ b/lib/diaspora_federation/salmon.rb @@ -13,5 +13,6 @@ require "diaspora_federation/salmon/aes" require "diaspora_federation/salmon/exceptions" require "diaspora_federation/salmon/xml_payload" require "diaspora_federation/salmon/magic_envelope" +require "diaspora_federation/salmon/encrypted_magic_envelope" require "diaspora_federation/salmon/slap" require "diaspora_federation/salmon/encrypted_slap" diff --git a/lib/diaspora_federation/salmon/encrypted_magic_envelope.rb b/lib/diaspora_federation/salmon/encrypted_magic_envelope.rb new file mode 100644 index 0000000..a10c8dd --- /dev/null +++ b/lib/diaspora_federation/salmon/encrypted_magic_envelope.rb @@ -0,0 +1,61 @@ +module DiasporaFederation + module Salmon + # This is a simple crypt-wrapper for {MagicEnvelope}. + # + # The wrapper is JSON with the following structure: + # + # { + # "aes_key": "...", + # "encrypted_magic_envelope": "..." + # } + # + # +aes_key+ is encrypted using the recipients public key, and contains the AES + # +key+ and +iv+ as JSON: + # + # { + # "key": "...", + # "iv": "..." + # } + # + # +encrypted_magic_envelope+ is encrypted using the +key+ and +iv+ from +aes_key+. + # Once decrypted it contains the {MagicEnvelope} xml: + # + # + # ... + # + # + # All JSON-values (+aes_key+, +encrypted_magic_envelope+, +key+ and +iv+) are + # base64 encoded. + module EncryptedMagicEnvelope + # Generates a new random AES key and encrypts the {MagicEnvelope} with it. + # Then encrypts the AES key with the receivers public key. + # @param [Nokogiri::XML::Element] magic_env XML root node of a magic envelope + # @param [OpenSSL::PKey::RSA] pubkey recipient public_key + # @return [String] json string + def self.encrypt(magic_env, pubkey) + key = AES.generate_key_and_iv + encrypted_env = AES.encrypt(magic_env.to_xml, key[:key], key[:iv]) + + encoded_key = Hash[key.map {|k, v| [k, Base64.strict_encode64(v)] }] + encrypted_key = Base64.strict_encode64(pubkey.public_encrypt(JSON.generate(encoded_key))) + + JSON.generate(aes_key: encrypted_key, encrypted_magic_envelope: encrypted_env) + end + + # Decrypts the AES key with the private key of the receiver and decrypts the + # encrypted {MagicEnvelope} with it. + # @param [String] encrypted_env json string with aes_key and encrypted_magic_envelope + # @param [OpenSSL::PKey::RSA] privkey private key for decryption + # @return [Nokogiri::XML::Element] decrypted magic envelope xml + def self.decrypt(encrypted_env, privkey) + encrypted_json = JSON.parse(encrypted_env) + + encoded_key = JSON.parse(privkey.private_decrypt(Base64.decode64(encrypted_json["aes_key"]))) + key = Hash[encoded_key.map {|k, v| [k, Base64.decode64(v)] }] + + xml = AES.decrypt(encrypted_json["encrypted_magic_envelope"], key["key"], key["iv"]) + Nokogiri::XML::Document.parse(xml).root + end + end + end +end diff --git a/spec/lib/diaspora_federation/salmon/encrypted_magic_envelope_spec.rb b/spec/lib/diaspora_federation/salmon/encrypted_magic_envelope_spec.rb new file mode 100644 index 0000000..6c39196 --- /dev/null +++ b/spec/lib/diaspora_federation/salmon/encrypted_magic_envelope_spec.rb @@ -0,0 +1,54 @@ +module DiasporaFederation + describe Salmon::EncryptedMagicEnvelope do + let(:sender_id) { FactoryGirl.generate(:diaspora_id) } + let(:sender_key) { OpenSSL::PKey::RSA.generate(512) } # use small key for speedy specs + let(:entity) { Entities::TestEntity.new(test: "abcd") } + let(:magic_env) { Salmon::MagicEnvelope.new(entity).envelop(sender_key, sender_id) } + + let(:privkey) { OpenSSL::PKey::RSA.generate(1024) } # use small key for speedy specs + + describe ".encrypt" do + it "creates the json correctly" do + encrypted = Salmon::EncryptedMagicEnvelope.encrypt(magic_env, privkey.public_key) + + expect(JSON.parse(encrypted)).to include("aes_key", "encrypted_magic_envelope") + end + + it "encrypts the aes_key correctly" do + encrypted = Salmon::EncryptedMagicEnvelope.encrypt(magic_env, privkey.public_key) + + json = JSON.parse(encrypted) + aes_key = JSON.parse(privkey.private_decrypt(Base64.decode64(json["aes_key"]))) + + expect(aes_key).to include("key", "iv") + end + + it "encrypts the magic_envelope correctly" do + encrypted = Salmon::EncryptedMagicEnvelope.encrypt(magic_env, privkey.public_key) + + json = JSON.parse(encrypted) + aes_key = JSON.parse(privkey.private_decrypt(Base64.decode64(json["aes_key"]))) + key = Hash[aes_key.map {|k, v| [k, Base64.decode64(v)] }] + + xml = Salmon::AES.decrypt(json["encrypted_magic_envelope"], key["key"], key["iv"]) + + expect(Nokogiri::XML::Document.parse(xml).root.to_xml).to eq(magic_env.to_xml) + end + end + + describe ".decrypt" do + let(:encrypted_env) { Salmon::EncryptedMagicEnvelope.encrypt(magic_env, privkey.public_key) } + + it "returns the magic envelope xml" do + decrypted = Salmon::EncryptedMagicEnvelope.decrypt(encrypted_env, privkey) + + expect(decrypted.name).to eq("env") + + expect(decrypted.xpath("me:data")).to have(1).item + expect(decrypted.xpath("me:encoding")).to have(1).item + expect(decrypted.xpath("me:alg")).to have(1).item + expect(decrypted.xpath("me:sig")).to have(1).item + end + end + end +end