diff --git a/lib/diaspora_federation/salmon/exceptions.rb b/lib/diaspora_federation/salmon/exceptions.rb index 853d12b..accc08b 100644 --- a/lib/diaspora_federation/salmon/exceptions.rb +++ b/lib/diaspora_federation/salmon/exceptions.rb @@ -20,6 +20,10 @@ module DiasporaFederation class InvalidHeader < RuntimeError end + # Raised, if failed to fetch the public key of the sender of the received message + class SenderKeyNotFound < RuntimeError + end + # Raised, if the Magic Envelope XML structure is malformed. class InvalidEnvelope < RuntimeError end diff --git a/lib/diaspora_federation/salmon/magic_envelope.rb b/lib/diaspora_federation/salmon/magic_envelope.rb index 7fa3d69..94509cc 100644 --- a/lib/diaspora_federation/salmon/magic_envelope.rb +++ b/lib/diaspora_federation/salmon/magic_envelope.rb @@ -101,9 +101,8 @@ module DiasporaFederation # @raise [InvalidSignature] if the signature can't be verified # @raise [InvalidEncoding] if the data is wrongly encoded # @raise [InvalidAlgorithm] if the algorithm used doesn't match - def self.unenvelop(magic_env, rsa_pubkey, cipher_params=nil) - raise ArgumentError unless magic_env.instance_of?(Nokogiri::XML::Element) && - rsa_pubkey.instance_of?(OpenSSL::PKey::RSA) + def self.unenvelop(magic_env, rsa_pubkey=nil, cipher_params=nil) + raise ArgumentError unless magic_env.instance_of?(Nokogiri::XML::Element) raise InvalidEnvelope unless envelope_valid?(magic_env) raise InvalidSignature unless signature_valid?(magic_env, rsa_pubkey) @@ -148,7 +147,7 @@ module DiasporaFederation private_class_method :envelope_valid? # @param [Nokogiri::XML::Element] env magic envelope XML - # @param [OpenSSL::PKey::RSA] pubkey public key + # @param [OpenSSL::PKey::RSA] pubkey public key or nil # @return [Boolean] def self.signature_valid?(env, pubkey) subject = sig_subject([Base64.urlsafe_decode64(env.at_xpath("me:data").content), @@ -156,11 +155,27 @@ module DiasporaFederation env.at_xpath("me:encoding").content, env.at_xpath("me:alg").content]) + sender_key = pubkey || sender_key(env) + sig = Base64.urlsafe_decode64(env.at_xpath("me:sig").content) - pubkey.verify(DIGEST, sig, subject) + sender_key.verify(DIGEST, sig, subject) end private_class_method :signature_valid? + # reads the +key_id+ from the magic envelope + # @param [Nokogiri::XML::Element] env magic envelope XML + # @return [OpenSSL::PKey::RSA] sender public key + def self.sender_key(env) + key_id = env.at_xpath("me:sig")["key_id"] + raise InvalidEnvelope, "no key_id" unless key_id # TODO: move to `envelope_valid?` + sender = Base64.urlsafe_decode64(key_id) + + sender_key = DiasporaFederation.callbacks.trigger(:fetch_public_key_by_diaspora_id, sender) + raise SenderKeyNotFound unless sender_key + sender_key + end + private_class_method :sender_key + # constructs the signature subject. # the given array should consist of the data, data_type (mimetype), encoding # and the algorithm diff --git a/spec/lib/diaspora_federation/salmon/magic_envelope_spec.rb b/spec/lib/diaspora_federation/salmon/magic_envelope_spec.rb index e1d2850..75484db 100644 --- a/spec/lib/diaspora_federation/salmon/magic_envelope_spec.rb +++ b/spec/lib/diaspora_federation/salmon/magic_envelope_spec.rb @@ -170,6 +170,38 @@ module DiasporaFederation expect(entity).to be_an_instance_of Entities::TestEntity expect(entity.test).to eq("asdf") end + + context "use key_id from magic envelope" do + it "returns the original entity" do + expect(DiasporaFederation.callbacks).to receive(:trigger).with( + :fetch_public_key_by_diaspora_id, sender_id + ).and_return(privkey.public_key) + + entity = Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey, sender_id)) + expect(entity).to be_an_instance_of Entities::TestEntity + expect(entity.test).to eq("asdf") + end + + it "raises if the magic envelope has no key_id" do + bad_env = envelope.envelop(privkey, sender_id) + + bad_env.at_xpath("me:sig").attributes["key_id"].remove + + expect { + Salmon::MagicEnvelope.unenvelop(bad_env) + }.to raise_error Salmon::InvalidEnvelope + end + + it "raises if the sender key is not found" do + expect(DiasporaFederation.callbacks).to receive(:trigger).with( + :fetch_public_key_by_diaspora_id, sender_id + ).and_return(nil) + + expect { + Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey, sender_id)) + }.to raise_error Salmon::SenderKeyNotFound + end + end end end end