diff --git a/app/models/jobs/http_multi.rb b/app/models/jobs/http_multi.rb
index 3baff5ebb..ae9f8d47f 100644
--- a/app/models/jobs/http_multi.rb
+++ b/app/models/jobs/http_multi.rb
@@ -13,8 +13,6 @@ module Jobs
MAX_RETRIES = 3
def self.perform(user_id, encoded_object_xml, person_ids, dispatcher_class_as_string, retry_count=0)
- return true if user_id == '91842' #NOTE 09/08/11 blocking diapsorahqposts
-
user = User.find(user_id)
people = Person.where(:id => person_ids)
diff --git a/app/views/publics/webfinger.erb b/app/views/publics/webfinger.erb
index cc6c64b36..c462a13fe 100644
--- a/app/views/publics/webfinger.erb
+++ b/app/views/publics/webfinger.erb
@@ -9,5 +9,5 @@
true)}"%>/>
-
+
diff --git a/config/routes.rb b/config/routes.rb
index 217b17ef6..a86351dce 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -157,6 +157,9 @@ Diaspora::Application.routes.draw do
get 'mobile/toggle', :to => 'home#toggle_mobile', :as => 'toggle_mobile'
+ #Protocol Url
+ get 'protocol' => redirect("https://github.com/diaspora/diaspora/wiki/Diaspora%27s-federation-protocol")
+
# Startpage
root :to => 'home#show'
diff --git a/lib/diaspora/encryptable.rb b/lib/diaspora/encryptable.rb
index a950e38f5..cb31e56d6 100644
--- a/lib/diaspora/encryptable.rb
+++ b/lib/diaspora/encryptable.rb
@@ -1,5 +1,7 @@
module Diaspora
module Encryptable
+
+ 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.
@@ -17,7 +19,10 @@ module Diaspora
return false
end
log_string = "event=verify_signature status=complete guid=#{self.guid}"
- validity = person.public_key.verify "SHA", Base64.decode64(signature), signable_string
+ validity = person.public_key.verify OpenSSL::Digest::SHA256.new, Base64.decode64(signature), signable_string
+ if !validity && Time.now < Time.parse(LAST_FALLBACK_TIME)
+ validity = person.public_key.verify "SHA", Base64.decode64(signature), signable_string
+ end
log_string += " validity=#{validity}"
Rails.logger.info(log_string)
validity
@@ -26,7 +31,7 @@ module Diaspora
# @param [OpenSSL::PKey::RSA] key An RSA key
# @return [String] A Base64 encoded signature of #signable_string with key
def sign_with_key(key)
- sig = Base64.encode64(key.sign "SHA", signable_string)
+ sig = Base64.encode64s(key.sign( OpenSSL::Digest::SHA256.new, signable_string ))
log_hash = {:event => :sign_with_key, :status => :complete}
log_hash.merge(:model_id => self.id) if self.respond_to?(:persisted?)
Rails.logger.info(log_hash)
diff --git a/lib/encryptor.rb b/lib/encryptor.rb
index a75dc7ba6..165ab91f2 100644
--- a/lib/encryptor.rb
+++ b/lib/encryptor.rb
@@ -9,14 +9,14 @@ module Encryptor
ciphertext = aes_encrypt(cleartext, aes_key)
encrypted_key = encrypt_aes_key aes_key
cipher_hash = {:aes_key => encrypted_key, :ciphertext => ciphertext}
- Base64.encode64( cipher_hash.to_json )
+ Base64.encode64s( cipher_hash.to_json )
end
def gen_aes_key
cipher = OpenSSL::Cipher.new('AES-256-CBC')
key = cipher.random_key
iv = cipher.random_iv
- {'key' => Base64.encode64(key), 'iv' => Base64.encode64(iv)}
+ {'key' => Base64.encode64s(key), 'iv' => Base64.encode64s(iv)}
end
def aes_encrypt(txt, key)
@@ -27,11 +27,11 @@ module Encryptor
ciphertext = ''
ciphertext << cipher.update(txt)
ciphertext << cipher.final
- Base64.encode64 ciphertext
+ Base64.encode64s(ciphertext)
end
def encrypt_aes_key key
- Base64.encode64 public_key.public_encrypt( key.to_json )
+ Base64.encode64s(public_key.public_encrypt( key.to_json ))
end
end
diff --git a/lib/postzord/dispatcher.rb b/lib/postzord/dispatcher.rb
index 7cd8ddc04..01649d87b 100644
--- a/lib/postzord/dispatcher.rb
+++ b/lib/postzord/dispatcher.rb
@@ -15,11 +15,11 @@ class Postzord::Dispatcher
raise 'this object does not respond_to? to_diaspora xml. try including Diaspora::Webhooks into your object'
end
- #if self.object_should_be_processed_as_public?(object)
- # Postzord::Dispatcher::Public.new(user, object, opts)
- #else
+ if self.object_should_be_processed_as_public?(object)
+ Postzord::Dispatcher::Public.new(user, object, opts)
+ else
Postzord::Dispatcher::Private.new(user, object, opts)
- #end
+ end
end
# @param object [Object]
@@ -59,7 +59,7 @@ class Postzord::Dispatcher
self.deliver_to_local(local_people)
end
- self.deliver_to_remote(remote_people) unless @sender.username == 'diasporahq' #NOTE: 09/08/11 this is temporary (~3days max) till we fix fanout in federation
+ self.deliver_to_remote(remote_people)
end
# @return [Array] Recipients of the object, minus any additional subscribers
@@ -152,7 +152,7 @@ class Postzord::Dispatcher
# @param remote_people [Array] Recipients of the post on other pods
# @return [void]
def queue_remote_delivery_job(remote_people)
- Resque.enqueue(Jobs::HttpMulti, @sender.id, Base64.encode64(@object.to_diaspora_xml), remote_people.map{|p| p.id}, self.class.to_s)
+ Resque.enqueue(Jobs::HttpMulti, @sender.id, Base64.encode64s(@object.to_diaspora_xml), remote_people.map{|p| p.id}, self.class.to_s)
end
end
diff --git a/lib/postzord/receiver/private.rb b/lib/postzord/receiver/private.rb
index 21d0f872e..969632610 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 5ba887afd..0fc4374d4 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 6cc6feda2..2770f1ad0 100644
--- a/lib/salmon/encrypted_slap.rb
+++ b/lib/salmon/encrypted_slap.rb
@@ -10,11 +10,21 @@ module Salmon
def header(person)
<
- #{person.encrypt("#{plaintext_header}")}
+ #{person.encrypt(plaintext_header)}
XML
end
+ def plaintext_header
+ header =<
+ #{iv}
+ #{aes_key}
+ #{@author.diaspora_handle}
+
+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 bc15dd78b..44527513c 100644
--- a/lib/salmon/magic_sig_envelope.rb
+++ b/lib/salmon/magic_sig_envelope.rb
@@ -54,7 +54,7 @@ module Salmon
# @return [String]
def to_xml
<
+
#{@data}
#{@encoding}
#{@alg}
@@ -70,7 +70,7 @@ ENTRY
# @return [String]
def get_data_type
- 'application/atom+xml'
+ 'application/xml'
end
# @return [String]
diff --git a/lib/salmon/salmon.rb b/lib/salmon/salmon.rb
index 20c7c750b..3d09404d2 100644
--- a/lib/salmon/salmon.rb
+++ b/lib/salmon/salmon.rb
@@ -5,27 +5,12 @@
# Add URL safe Base64 support
module Base64
module_function
- # Returns the Base64-encoded version of +bin+.
- # This method complies with RFC 4648.
- # No line feeds are added.
- def strict_encode64(bin)
- [bin].pack("m0")
- end
-
- # Returns the Base64-decoded version of +str+.
- # This method complies with RFC 4648.
- # ArgumentError is raised if +str+ is incorrectly padded or contains
- # non-alphabet characters. Note that CR or LF are also rejected.
- def strict_decode64(str)
- str.unpack("m0").first
- end
-
# Returns the Base64-encoded version of +bin+.
# This method complies with ``Base 64 Encoding with URL and Filename Safe
# Alphabet'' in RFC 4648.
# The alphabet uses '-' instead of '+' and '_' instead of '/'.
def urlsafe_encode64(bin)
- strict_encode64(bin).tr("+/", "-_")
+ self.encode64s(bin).tr("+/", "-_")
end
# Returns the Base64-decoded version of +str+.
@@ -33,7 +18,7 @@ module Base64
# Alphabet'' in RFC 4648.
# The alphabet uses '-' instead of '+' and '_' instead of '/'.
def urlsafe_decode64(str)
- strict_decode64(str.tr("-_", "+/"))
+ self.decode64(str.tr("-_", "+/"))
end
end
diff --git a/lib/salmon/slap.rb b/lib/salmon/slap.rb
index 3007188b5..61ea14996 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 =<
-
+
#{header(person)}
#{@magic_sig.to_xml}
-
+
ENTRY
end
@@ -86,19 +87,14 @@ ENTRY
# @return [String] Header XML (sans tags)
def plaintext_header
header =<#{iv}
- #{aes_key}
-
- #{@author.name}
- acct:#{@author.diaspora_handle}
-
+ #{@author.diaspora_handle}
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/diaspora/encryptable_spec.rb b/spec/lib/diaspora/encryptable_spec.rb
new file mode 100644
index 000000000..9906ad870
--- /dev/null
+++ b/spec/lib/diaspora/encryptable_spec.rb
@@ -0,0 +1,38 @@
+# Copyright (c) 2010, Diaspora Inc. This file is
+# licensed under the Affero General Public License version 3 or later. See
+# the COPYRIGHT file.
+
+require 'spec_helper'
+
+describe Diaspora::Encryptable do
+ before do
+ @comment = Factory(:comment, :author => bob.person)
+ end
+ describe '#sign_with_key' do
+ it 'signs the object with RSA256 signature' do
+ sig = @comment.sign_with_key bob.encryption_key
+ bob.public_key.verify(OpenSSL::Digest::SHA256.new, Base64.decode64(sig), @comment.signable_string).should be_true
+ end
+ end
+
+ describe '#verify_signature' do
+ it 'verifies SHA256 signatures' do
+ sig = @comment.sign_with_key bob.encryption_key
+ @comment.verify_signature(sig, bob.person).should be_true
+ end
+
+ context "fallback" do
+ it "checks the SHA if it's within the week of the rollout window" do
+ sig = Base64.encode64s(bob.encryption_key.sign( "SHA", @comment.signable_string ))
+ @comment.verify_signature(sig, bob.person).should be_true
+ end
+
+ it 'does not verify the fallback after rollout window' do
+ Kernel::silence_warnings { Diaspora::Encryptable.const_set(:LAST_FALLBACK_TIME,((Time.now - 1.week).to_s))}
+
+ sig = Base64.encode64s(bob.encryption_key.sign( "SHA", @comment.signable_string ))
+ @comment.verify_signature(sig, bob.person).should be_false
+ end
+ end
+ end
+end
diff --git a/spec/lib/postzord/receiver/private_spec.rb b/spec/lib/postzord/receiver/private_spec.rb
index 8399e328b..636b9f220 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 000000000..1fd6bcaf8
--- /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 f76bf6bd9..90ed786a7 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}
-
- 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
diff --git a/spec/lib/salmon/slap_spec.rb b/spec/lib/salmon/slap_spec.rb
index c1d6cfa85..91d854bda 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
+