use author_id from slap to fetch pubkey in magic-env

This commit is contained in:
Benjamin Neff 2016-03-20 17:07:21 +01:00
parent 4e0c7e205b
commit 3a83dc97ac
5 changed files with 48 additions and 40 deletions

View file

@ -14,10 +14,7 @@ module DiasporaFederation
# Parse the salmon xml # Parse the salmon xml
def parse def parse
sender_id = slap.author_id slap.entity
public_key = DiasporaFederation.callbacks.trigger(:fetch_public_key_by_diaspora_id, sender_id)
raise Salmon::SenderKeyNotFound if public_key.nil?
slap.entity(public_key)
end end
end end
end end

View file

@ -99,7 +99,7 @@ module DiasporaFederation
# @see AES#decrypt # @see AES#decrypt
# #
# @param [Nokogiri::XML::Element] magic_env XML root node of a magic envelope # @param [Nokogiri::XML::Element] magic_env XML root node of a magic envelope
# @param [OpenSSL::PKey::RSA] rsa_pubkey public key to verify the signature # @param [String] sender diaspora-ID of the sender or nil
# @param [Hash] cipher_params hash containing the key and iv for # @param [Hash] cipher_params hash containing the key and iv for
# AES-decrypting previously encrypted data. E.g.: { iv: "...", key: "..." } # AES-decrypting previously encrypted data. E.g.: { iv: "...", key: "..." }
# #
@ -110,11 +110,11 @@ module DiasporaFederation
# @raise [InvalidSignature] if the signature can't be verified # @raise [InvalidSignature] if the signature can't be verified
# @raise [InvalidEncoding] if the data is wrongly encoded # @raise [InvalidEncoding] if the data is wrongly encoded
# @raise [InvalidAlgorithm] if the algorithm used doesn't match # @raise [InvalidAlgorithm] if the algorithm used doesn't match
def self.unenvelop(magic_env, rsa_pubkey=nil, cipher_params=nil) def self.unenvelop(magic_env, sender=nil, cipher_params=nil)
raise ArgumentError unless magic_env.instance_of?(Nokogiri::XML::Element) raise ArgumentError unless magic_env.instance_of?(Nokogiri::XML::Element)
raise InvalidEnvelope unless envelope_valid?(magic_env) raise InvalidEnvelope unless envelope_valid?(magic_env)
raise InvalidSignature unless signature_valid?(magic_env, rsa_pubkey) raise InvalidSignature unless signature_valid?(magic_env, sender)
raise InvalidEncoding unless encoding_valid?(magic_env) raise InvalidEncoding unless encoding_valid?(magic_env)
raise InvalidAlgorithm unless algorithm_valid?(magic_env) raise InvalidAlgorithm unless algorithm_valid?(magic_env)
@ -162,15 +162,18 @@ module DiasporaFederation
private_class_method :envelope_valid? private_class_method :envelope_valid?
# @param [Nokogiri::XML::Element] env magic envelope XML # @param [Nokogiri::XML::Element] env magic envelope XML
# @param [OpenSSL::PKey::RSA] pubkey public key or nil # @param [String] sender diaspora-ID of the sender or nil
# @return [Boolean] # @return [Boolean]
def self.signature_valid?(env, pubkey) def self.signature_valid?(env, sender)
sender ||= sender(env)
subject = sig_subject([Base64.urlsafe_decode64(env.at_xpath("me:data").content), subject = sig_subject([Base64.urlsafe_decode64(env.at_xpath("me:data").content),
env.at_xpath("me:data")["type"], env.at_xpath("me:data")["type"],
env.at_xpath("me:encoding").content, env.at_xpath("me:encoding").content,
env.at_xpath("me:alg").content]) env.at_xpath("me:alg").content])
sender_key = pubkey || sender_key(env) sender_key = DiasporaFederation.callbacks.trigger(:fetch_public_key_by_diaspora_id, sender)
raise SenderKeyNotFound unless sender_key
sig = Base64.urlsafe_decode64(env.at_xpath("me:sig").content) sig = Base64.urlsafe_decode64(env.at_xpath("me:sig").content)
sender_key.verify(DIGEST, sig, subject) sender_key.verify(DIGEST, sig, subject)
@ -179,17 +182,13 @@ module DiasporaFederation
# reads the +key_id+ from the magic envelope # reads the +key_id+ from the magic envelope
# @param [Nokogiri::XML::Element] env magic envelope XML # @param [Nokogiri::XML::Element] env magic envelope XML
# @return [OpenSSL::PKey::RSA] sender public key # @return [String] diaspora-ID of the sender
def self.sender_key(env) def self.sender(env)
key_id = env.at_xpath("me:sig")["key_id"] key_id = env.at_xpath("me:sig")["key_id"]
raise InvalidEnvelope, "no key_id" unless key_id # TODO: move to `envelope_valid?` raise InvalidEnvelope, "no key_id" unless key_id # TODO: move to `envelope_valid?`
sender = Base64.urlsafe_decode64(key_id) 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 end
private_class_method :sender_key private_class_method :sender
# constructs the signature subject. # constructs the signature subject.
# the given array should consist of the data, data_type (mimetype), encoding # the given array should consist of the data, data_type (mimetype), encoding

View file

@ -46,12 +46,10 @@ module DiasporaFederation
# #
# @see MagicEnvelope.unenvelop # @see MagicEnvelope.unenvelop
# #
# @param [OpenSSL::PKey::RSA] pubkey public key for validating the signature
# @return [Entity] entity instance from the XML # @return [Entity] entity instance from the XML
# @raise [ArgumentError] if the public key is of the wrong type # @raise [ArgumentError] if the public key is of the wrong type
def entity(pubkey) def entity
raise ArgumentError unless pubkey.instance_of?(OpenSSL::PKey::RSA) MagicEnvelope.unenvelop(@magic_envelope, author_id, @cipher_params)
MagicEnvelope.unenvelop(@magic_envelope, pubkey, @cipher_params)
end end
# Parses an unencrypted Salmon XML string and returns a new instance of # Parses an unencrypted Salmon XML string and returns a new instance of

View file

@ -104,6 +104,12 @@ module DiasporaFederation
describe ".unenvelop" do describe ".unenvelop" do
context "sanity" do context "sanity" do
before do
allow(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_public_key_by_diaspora_id, sender
).and_return(privkey.public_key)
end
def re_sign(env, key) def re_sign(env, key)
new_sig = Base64.urlsafe_encode64(key.sign(OpenSSL::Digest::SHA256.new, sig_subj(env))) new_sig = Base64.urlsafe_encode64(key.sign(OpenSSL::Digest::SHA256.new, sig_subj(env)))
env.at_xpath("me:sig").content = new_sig env.at_xpath("me:sig").content = new_sig
@ -111,7 +117,7 @@ module DiasporaFederation
it "works with sane input" do it "works with sane input" do
expect { expect {
Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey), privkey.public_key) Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey), sender)
}.not_to raise_error }.not_to raise_error
end end
@ -125,14 +131,20 @@ module DiasporaFederation
it "verifies the envelope structure" do it "verifies the envelope structure" do
expect { expect {
Salmon::MagicEnvelope.unenvelop(Nokogiri::XML::Document.parse("<asdf/>").root, privkey.public_key) Salmon::MagicEnvelope.unenvelop(Nokogiri::XML::Document.parse("<asdf/>").root, sender)
}.to raise_error Salmon::InvalidEnvelope }.to raise_error Salmon::InvalidEnvelope
end end
it "verifies the signature" do it "verifies the signature" do
other_sender = FactoryGirl.generate(:diaspora_id)
other_key = OpenSSL::PKey::RSA.generate(512) other_key = OpenSSL::PKey::RSA.generate(512)
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_public_key_by_diaspora_id, other_sender
).and_return(other_key)
expect { expect {
Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey), other_key.public_key) Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey), other_sender)
}.to raise_error Salmon::InvalidSignature }.to raise_error Salmon::InvalidSignature
end end
@ -141,7 +153,7 @@ module DiasporaFederation
bad_env.at_xpath("me:encoding").content = "invalid_enc" bad_env.at_xpath("me:encoding").content = "invalid_enc"
re_sign(bad_env, privkey) re_sign(bad_env, privkey)
expect { expect {
Salmon::MagicEnvelope.unenvelop(bad_env, privkey.public_key) Salmon::MagicEnvelope.unenvelop(bad_env, sender)
}.to raise_error Salmon::InvalidEncoding }.to raise_error Salmon::InvalidEncoding
end end
@ -150,23 +162,31 @@ module DiasporaFederation
bad_env.at_xpath("me:alg").content = "invalid_alg" bad_env.at_xpath("me:alg").content = "invalid_alg"
re_sign(bad_env, privkey) re_sign(bad_env, privkey)
expect { expect {
Salmon::MagicEnvelope.unenvelop(bad_env, privkey.public_key) Salmon::MagicEnvelope.unenvelop(bad_env, sender)
}.to raise_error Salmon::InvalidAlgorithm }.to raise_error Salmon::InvalidAlgorithm
end end
end end
it "returns the original entity" do it "returns the original entity" do
entity = Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey), privkey.public_key) allow(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_public_key_by_diaspora_id, sender
).and_return(privkey.public_key)
entity = Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey), sender)
expect(entity).to be_an_instance_of Entities::TestEntity expect(entity).to be_an_instance_of Entities::TestEntity
expect(entity.test).to eq("asdf") expect(entity.test).to eq("asdf")
end end
it "decrypts on the fly, when cipher params are present" do it "decrypts on the fly, when cipher params are present" do
allow(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_public_key_by_diaspora_id, sender
).and_return(privkey.public_key)
params = envelope.encrypt! params = envelope.encrypt!
env_xml = envelope.envelop(privkey) env_xml = envelope.envelop(privkey)
entity = Salmon::MagicEnvelope.unenvelop(env_xml, privkey.public_key, params) entity = Salmon::MagicEnvelope.unenvelop(env_xml, sender, params)
expect(entity).to be_an_instance_of Entities::TestEntity expect(entity).to be_an_instance_of Entities::TestEntity
expect(entity.test).to eq("asdf") expect(entity.test).to eq("asdf")
end end

View file

@ -4,18 +4,12 @@ shared_examples "a Slap instance" do
end end
context "#entity" do context "#entity" do
it "requires the pubkey for the first time (to verify the signature)" do
expect { subject.entity }.to raise_error ArgumentError
end
it "works when the pubkey is given" do
expect {
subject.entity(privkey.public_key)
}.not_to raise_error
end
it "returns the entity" do it "returns the entity" do
entity = subject.entity(privkey.public_key) allow(DiasporaFederation.callbacks).to receive(:trigger).with(
:fetch_public_key_by_diaspora_id, author_id
).and_return(privkey.public_key)
entity = subject.entity
expect(entity).to be_an_instance_of DiasporaFederation::Entities::TestEntity expect(entity).to be_an_instance_of DiasporaFederation::Entities::TestEntity
expect(entity.test).to eq("qwertzuiop") expect(entity.test).to eq("qwertzuiop")
end end