diff --git a/lib/diaspora/encryptable.rb b/lib/diaspora/encryptable.rb index 241c15dde7907d62640a846e5b5ff543bae0fdab..cb31e56d64be34fde61e83adacc0458d27b6dd18 100644 --- a/lib/diaspora/encryptable.rb +++ b/lib/diaspora/encryptable.rb @@ -1,7 +1,7 @@ module Diaspora module Encryptable - LAST_FALLBACK_TIME = "Sept 15 2011 17:00 UTC " + LAST_FALLBACK_TIME = "Sept 19 2011 17:00 UTC " # Check that signature is a correct signature of #signable_string by person # # @param [String] signature The signature to be verified. diff --git a/lib/postzord/receiver/private.rb b/lib/postzord/receiver/private.rb index 21d0f872eb701fbf54ee1978950625a3c1e63fbf..969632610106d36d56a094b227b45a45e4dd365c 100644 --- a/lib/postzord/receiver/private.rb +++ b/lib/postzord/receiver/private.rb @@ -13,7 +13,7 @@ module Postzord @user_person = @user.person @salmon_xml = opts[:salmon_xml] - @sender = opts[:person] || Webfinger.new(self.salmon.author_email).fetch + @sender = opts[:person] || Webfinger.new(self.salmon.author_id).fetch @author = @sender @object = opts[:object] @@ -23,7 +23,7 @@ module Postzord if @sender && self.salmon.verified_for_key?(@sender.public_key) parse_and_receive(salmon.parsed_data) else - Rails.logger.info("event=receive status=abort recipient=#{@user.diaspora_handle} sender=#{@salmon.author_email} reason='not_verified for key'") + Rails.logger.info("event=receive status=abort recipient=#{@user.diaspora_handle} sender=#{@salmon.author_id} reason='not_verified for key'") nil end end diff --git a/lib/postzord/receiver/public.rb b/lib/postzord/receiver/public.rb index 5ba887afd8f46f425395dbf4282a2c5b5ec34910..0fc4374d4e1aa24ea4d8483e6d3d1e8c0b9ac87d 100644 --- a/lib/postzord/receiver/public.rb +++ b/lib/postzord/receiver/public.rb @@ -9,7 +9,7 @@ module Postzord def initialize(xml) @salmon = Salmon::Slap.from_xml(xml) - @author = Webfinger.new(@salmon.author_email).fetch + @author = Webfinger.new(@salmon.author_id).fetch end # @return [Boolean] diff --git a/lib/salmon/encrypted_slap.rb b/lib/salmon/encrypted_slap.rb index 6cc6feda20ee22eba9a3e04f8b02a14b0c4318fb..2770f1ad0af42ae6078dd96802fb004568255fbf 100644 --- a/lib/salmon/encrypted_slap.rb +++ b/lib/salmon/encrypted_slap.rb @@ -10,11 +10,21 @@ module Salmon def header(person) <<XML <encrypted_header> - #{person.encrypt("<decrypted_header>#{plaintext_header}</decrypted_header>")} + #{person.encrypt(plaintext_header)} </encrypted_header> XML end + def plaintext_header + header =<<HEADER +<decrypted_header> + <iv>#{iv}</iv> + <aes_key>#{aes_key}</aes_key> + <author_id>#{@author.diaspora_handle}</author_id> +</decrypted_header> +HEADER + end + # @return [String, Boolean] False if RSAError; XML if no error def xml_for(person) begin @@ -24,12 +34,21 @@ XML false end end - + + # Takes in a doc of the header and sets the author id + # returns an empty hash + # @return [Hash] + def process_header(doc) + self.author_id = doc.search('author_id').text + self.aes_key = doc.search('aes_key').text + self.iv = doc.search('iv').text + end + # Decrypts an encrypted magic sig envelope # @param key_hash [Hash] Contains 'key' (aes) and 'iv' values # @param user [User] - def parse_data(key_hash, user) - user.aes_decrypt(super, key_hash) + def parse_data(user) + user.aes_decrypt(super, {'key' => self.aes_key, 'iv' => self.iv}) end # Decrypts and parses out the salmon header diff --git a/lib/salmon/magic_sig_envelope.rb b/lib/salmon/magic_sig_envelope.rb index bc15dd78b27a99d6f55f2e7e1d07184c7bf06989..d43644b9549e5a45530a424229d0528a2597b3c7 100644 --- a/lib/salmon/magic_sig_envelope.rb +++ b/lib/salmon/magic_sig_envelope.rb @@ -10,22 +10,21 @@ module Salmon # @return [MagicSigEnvelope] def self.parse(doc) env = self.new - ns = {'me'=>'http://salmon-protocol.org/ns/magic-env'} - env.encoding = doc.search('//me:env/me:encoding', ns).text.strip + env.encoding = doc.search('//me:env/me:encoding').text.strip if env.encoding != 'base64url' raise ArgumentError, "Magic Signature data must be encoded with base64url, was #{env.encoding}" end - env.data = doc.search('//me:env/me:data', ns).text - env.alg = doc.search('//me:env/me:alg', ns).text.strip + env.data = doc.search('//me:env/me:data').text + env.alg = doc.search('//me:env/me:alg').text.strip unless 'RSA-SHA256' == env.alg raise ArgumentError, "Magic Signature data must be signed with RSA-SHA256, was #{env.alg}" end - env.sig = doc.search('//me:env/me:sig', ns).text - env.data_type = doc.search('//me:env/me:data', ns).first['type'].strip + env.sig = doc.search('//me:env/me:sig').text + env.data_type = doc.search('//me:env/me:data').first['type'].strip env end @@ -54,7 +53,7 @@ module Salmon # @return [String] def to_xml <<ENTRY -<me:env xmlns:me="http://salmon-protocol.org/ns/magic-env"> +<me:env> <me:data type='#{@data_type}'>#{@data}</me:data> <me:encoding>#{@encoding}</me:encoding> <me:alg>#{@alg}</me:alg> diff --git a/lib/salmon/slap.rb b/lib/salmon/slap.rb index 3007188b5791d2037d6419f90ab373f2d27dbafa..1d67987a6b946a00cb041b47f78ab40a5a9fd68c 100644 --- a/lib/salmon/slap.rb +++ b/lib/salmon/slap.rb @@ -4,7 +4,7 @@ module Salmon class Slap - attr_accessor :magic_sig, :author, :author_email, :parsed_data + attr_accessor :magic_sig, :author, :author_id, :parsed_data attr_accessor :aes_key, :iv delegate :sig, :data_type, :to => :magic_sig @@ -29,22 +29,16 @@ module Salmon slap = self.new doc = Nokogiri::XML(xml) - entry_doc = doc.search('entry') + root_doc = doc.search('diaspora') ### Header ## header_doc = slap.salmon_header(doc, receiving_user) - slap.author_email= header_doc.search('uri').text.split("acct:").last + slap.process_header(header_doc) - slap.aes_key = header_doc.search('aes_key').text - slap.iv = header_doc.search('iv').text + ### Envelope ## + slap.magic_sig = MagicSigEnvelope.parse(root_doc) - slap.magic_sig = MagicSigEnvelope.parse(entry_doc) - - - #should be in encrypted salmon only - key_hash = {'key' => slap.aes_key, 'iv' => slap.iv} - - slap.parsed_data = slap.parse_data(key_hash, receiving_user) + slap.parsed_data = slap.parse_data(receiving_user) slap end @@ -54,8 +48,15 @@ module Salmon activity end + # Takes in a doc of the header and sets the author id + # returns an empty hash + # @return [String] Author id + def process_header(doc) + self.author_id = doc.search('author_id').text + end + # @return [String] - def parse_data(key_hash, user=nil) + def parse_data(user=nil) Slap.decode64url(self.magic_sig.data) end @@ -69,10 +70,10 @@ module Salmon def xml_for(person) @xml =<<ENTRY <?xml version='1.0' encoding='UTF-8'?> - <entry xmlns='http://www.w3.org/2005/Atom'> + <diaspora xmlns="https://github.com/diaspora/diaspora/wiki/Diaspora%27s-federation-protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env"> #{header(person)} #{@magic_sig.to_xml} - </entry> + </diaspora> ENTRY end @@ -86,19 +87,14 @@ ENTRY # @return [String] Header XML (sans <header></header> tags) def plaintext_header header =<<HEADER - <iv>#{iv}</iv> - <aes_key>#{aes_key}</aes_key> - <author> - <name>#{@author.name}</name> - <uri>acct:#{@author.diaspora_handle}</uri> - </author> + <author_id>#{@author.diaspora_handle}</author_id> HEADER end # @return [Person] Author of the salmon object def author if @author.nil? - @author ||= Person.by_account_identifier @author_email + @author ||= Person.by_account_identifier @author_id raise "did you remember to async webfinger?" if @author.nil? end @author diff --git a/spec/lib/postzord/receiver/private_spec.rb b/spec/lib/postzord/receiver/private_spec.rb index 8399e328b3fa27fe08047ae4acde68bd6b3e1b29..636b9f2201cc9594e002f6fe137411cbf3781f44 100644 --- a/spec/lib/postzord/receiver/private_spec.rb +++ b/spec/lib/postzord/receiver/private_spec.rb @@ -36,7 +36,7 @@ describe Postzord::Receiver::Private do salmon_mock = mock() web_mock = mock() web_mock.should_receive(:fetch).and_return true - salmon_mock.should_receive(:author_email).and_return(true) + salmon_mock.should_receive(:author_id).and_return(true) Salmon::EncryptedSlap.should_receive(:from_xml).with(@salmon_xml, @user).and_return(salmon_mock) Webfinger.should_receive(:new).and_return(web_mock) diff --git a/spec/lib/salmon/base64_spec.rb b/spec/lib/salmon/base64_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..1fd6bcaf8ca9996d37d38d9a763048316ddc5b8f --- /dev/null +++ b/spec/lib/salmon/base64_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' + +describe Base64 do + describe ".urlsafe_encode64_stripped" do + it "strips the trailing '=' from the url_safe characters" do + pending + Base64.should_receive(:urlsafe_encode64).and_return("MTIzMTIzMQ==") + Base64.urlsafe_encode64_stripped("random stuff").should == "MTIzMTIzMQ" + end + end +end diff --git a/spec/lib/salmon/encrypted_slap_spec.rb b/spec/lib/salmon/encrypted_slap_spec.rb index f76bf6bd90d9415e56e3d4b18b6f7f0444daaa9c..90ed786a752d655dcbb379567ec683dd73cb8aa9 100644 --- a/spec/lib/salmon/encrypted_slap_spec.rb +++ b/spec/lib/salmon/encrypted_slap_spec.rb @@ -23,6 +23,25 @@ describe Salmon::EncryptedSlap do end end + describe "#process_header" do + before do + @new_slap = Salmon::EncryptedSlap.new + @new_slap.process_header(Nokogiri::XML(@created_salmon.plaintext_header)) + end + + it 'sets the author id' do + @new_slap.author_id.should == alice.diaspora_handle + end + + it 'sets the aes_key' do + @new_slap.aes_key.should == @created_salmon.aes_key + end + + it 'sets the aes_key' do + @new_slap.iv.should == @created_salmon.iv + end + end + context 'marshalling' do let(:xml) {@created_salmon.xml_for(eve.person)} let(:parsed_salmon) { Salmon::EncryptedSlap.from_xml(xml, alice)} @@ -41,16 +60,33 @@ describe Salmon::EncryptedSlap do end describe '#xml_for' do - let(:xml) {@created_salmon.xml_for eve.person} + before do + @xml = @created_salmon.xml_for eve.person + end it 'has a encrypted header field' do - xml.include?("encrypted_header").should be true + doc = Nokogiri::XML(@xml) + doc.find("encrypted_header").should_not be_blank end + + context "encrypted header" do + before do + doc = Nokogiri::XML(@xml) + decrypted_header = eve.decrypt(doc.search('encrypted_header').text) + @dh_doc = Nokogiri::XML(decrypted_header) + end + + it 'contains the aes key' do + @dh_doc.search('aes_key').map(&:text).should == [@created_salmon.aes_key] + end + + it 'contains the initialization vector' do + @dh_doc.search('iv').map(&:text).should == [@created_salmon.iv] + end - it 'the encrypted_header field should contain the aes key' do - doc = Nokogiri::XML(xml) - decrypted_header = eve.decrypt(doc.search('encrypted_header').text) - decrypted_header.include?(@created_salmon.aes_key).should be true + it 'contains the author id' do + @dh_doc.search('author_id').map(&:text).should == [alice.diaspora_handle] + end end end end diff --git a/spec/lib/salmon/slap_spec.rb b/spec/lib/salmon/slap_spec.rb index c1d6cfa8520d1267ade1cd45af987f6d18355db2..91d854bda222ecb5315d36143be1c252b123a41f 100644 --- a/spec/lib/salmon/slap_spec.rb +++ b/spec/lib/salmon/slap_spec.rb @@ -24,6 +24,21 @@ describe Salmon::Slap do salmon.parsed_data.should == @post.to_diaspora_xml end + describe '#from_xml' do + it 'procsses the header' do + Salmon::Slap.any_instance.should_receive(:process_header) + Salmon::Slap.from_xml(@created_salmon.xml_for(eve.person)) + end + end + + describe "#process_header" do + it 'sets the author id' do + slap = Salmon::Slap.new + slap.process_header(Nokogiri::XML(@created_salmon.plaintext_header)) + slap.author_id.should == alice.diaspora_handle + end + end + describe '#author' do let(:xml) {@created_salmon.xml_for(eve.person)} let(:parsed_salmon) { Salmon::Slap.from_xml(xml, alice)} @@ -33,7 +48,7 @@ describe Salmon::Slap do end it 'should fail if no author is found' do - parsed_salmon.author_email = 'tom@tom.joindiaspora.com' + parsed_salmon.author_id = 'tom@tom.joindiaspora.com' expect { parsed_salmon.author.public_key }.should raise_error "did you remember to async webfinger?" @@ -45,7 +60,7 @@ describe Salmon::Slap do let(:parsed_salmon) { Salmon::Slap.from_xml(xml)} it 'should parse out the authors diaspora_handle' do - parsed_salmon.author_email.should == alice.person.diaspora_handle + parsed_salmon.author_id.should == alice.person.diaspora_handle end it 'verifies the signature for the sender' do @@ -60,4 +75,36 @@ describe Salmon::Slap do parsed_salmon.parsed_data.should == @post.to_diaspora_xml end end + + describe "#xml_for" do + before do + @xml = @created_salmon.xml_for(eve.person) + end + + it "has diaspora as the root" do + doc = Nokogiri::XML(@xml) + doc.root.name.should == "diaspora" + end + + it "it has the descrypted header" do + doc = Nokogiri::XML(@xml) + doc.search("header").should_not be_blank + end + + context "header" do + + it "it has author_id node " do + doc = Nokogiri::XML(@xml) + search = doc.search("header").search("author_id") + search.map(&:text).should == [alice.diaspora_handle] + end + + end + + it "it has the magic envelope " do + doc = Nokogiri::XML(@xml) + doc.find("/me:env").should_not be_blank + end + end end +