refactored the salmon models and the xml, added a pending spec to strip '='
This commit is contained in:
parent
b7288c8d8e
commit
c98189b057
10 changed files with 156 additions and 48 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -25,11 +35,20 @@ XML
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
11
spec/lib/salmon/base64_spec.rb
Normal file
11
spec/lib/salmon/base64_spec.rb
Normal file
|
|
@ -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
|
||||
|
|
@ -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}
|
||||
|
||||
it 'has a encrypted header field' do
|
||||
xml.include?("encrypted_header").should be true
|
||||
before do
|
||||
@xml = @created_salmon.xml_for eve.person
|
||||
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 'has a encrypted header field' do
|
||||
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 'contains the author id' do
|
||||
@dh_doc.search('author_id').map(&:text).should == [alice.diaspora_handle]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue