use author_id from slap to fetch pubkey in magic-env
This commit is contained in:
parent
4e0c7e205b
commit
3a83dc97ac
5 changed files with 48 additions and 40 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue