move signing logic for relayables to Relayable
refactoring exceptions for relayables
This commit is contained in:
parent
714f6d8273
commit
b19e1b8e52
11 changed files with 152 additions and 194 deletions
|
|
@ -7,7 +7,6 @@ require "diaspora_federation/validators"
|
|||
|
||||
require "diaspora_federation/fetcher"
|
||||
|
||||
require "diaspora_federation/signing"
|
||||
require "diaspora_federation/entities"
|
||||
|
||||
require "diaspora_federation/discovery"
|
||||
|
|
@ -221,7 +220,7 @@ module DiasporaFederation
|
|||
end
|
||||
|
||||
def configuration_error(message)
|
||||
logger.fatal("diaspora federation configuration error: #{message}")
|
||||
logger.fatal "diaspora federation configuration error: #{message}"
|
||||
raise ConfigurationError, message
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,9 +2,14 @@ module DiasporaFederation
|
|||
module Entities
|
||||
# this is a module that defines common properties for relayable entities
|
||||
# which include Like, Comment, Participation, Message, etc. Each relayable
|
||||
# has a parent, identified by guid. Relayables also are signed and signing/verificating
|
||||
# has a parent, identified by guid. Relayables also are signed and signing/verification
|
||||
# logic is embedded into Salmon XML processing code.
|
||||
module Relayable
|
||||
include Logging
|
||||
|
||||
# digest instance used for signing
|
||||
DIGEST = OpenSSL::Digest::SHA256.new
|
||||
|
||||
# on inclusion of this module the required properties for a relayable are added to the object that includes it
|
||||
#
|
||||
# @!attribute [r] parent_guid
|
||||
|
|
@ -43,15 +48,12 @@ module DiasporaFederation
|
|||
super.tap do |hash|
|
||||
if author_signature.nil?
|
||||
privkey = DiasporaFederation.callbacks.trigger(:fetch_private_key_by_diaspora_id, diaspora_id)
|
||||
hash[:author_signature] = Signing.sign_with_key(hash, privkey) unless privkey.nil?
|
||||
raise AuthorPrivateKeyNotFound, "author=#{diaspora_id} guid=#{guid}" if privkey.nil?
|
||||
hash[:author_signature] = sign_with_key(privkey, hash)
|
||||
logger.info "event=sign_with_key signature=author_signature author=#{diaspora_id} guid=#{guid}"
|
||||
end
|
||||
|
||||
if parent_author_signature.nil?
|
||||
privkey = DiasporaFederation.callbacks.trigger(
|
||||
:fetch_author_private_key_by_entity_guid, parent_type, parent_guid
|
||||
)
|
||||
hash[:parent_author_signature] = Signing.sign_with_key(hash, privkey) unless privkey.nil?
|
||||
end
|
||||
try_sign_with_parent_author(hash) if parent_author_signature.nil?
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -66,33 +68,84 @@ module DiasporaFederation
|
|||
end
|
||||
end
|
||||
|
||||
# Exception raised when verify_signatures fails to verify signatures (signatures are wrong)
|
||||
class SignatureVerificationFailed < ArgumentError
|
||||
end
|
||||
|
||||
# verifies the signatures (+author_signature+ and +parent_author_signature+ if needed)
|
||||
# @raise [SignatureVerificationFailed] if the signature is not valid or no public key is found
|
||||
def verify_signatures
|
||||
pubkey = DiasporaFederation.callbacks.trigger(:fetch_public_key_by_diaspora_id, diaspora_id)
|
||||
raise SignatureVerificationFailed, "failed to fetch public key for #{diaspora_id}" if pubkey.nil?
|
||||
raise SignatureVerificationFailed, "wrong author_signature" unless Signing.verify_signature(
|
||||
data, author_signature, pubkey
|
||||
)
|
||||
raise PublicKeyNotFound, "author_signature author=#{diaspora_id} guid=#{guid}" if pubkey.nil?
|
||||
raise SignatureVerificationFailed, "wrong author_signature" unless verify_signature(pubkey, author_signature)
|
||||
|
||||
author_is_local = DiasporaFederation.callbacks.trigger(:entity_author_is_local?, parent_type, parent_guid)
|
||||
verify_parent_signature unless author_is_local
|
||||
parent_author_local = DiasporaFederation.callbacks.trigger(:entity_author_is_local?, parent_type, parent_guid)
|
||||
verify_parent_author_signature unless parent_author_local
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# sign with parent author, if the parent author is local (if the private key is found)
|
||||
# @param [Hash] hash the hash to sign
|
||||
def try_sign_with_parent_author(hash)
|
||||
privkey = DiasporaFederation.callbacks.trigger(
|
||||
:fetch_author_private_key_by_entity_guid, parent_type, parent_guid
|
||||
)
|
||||
unless privkey.nil?
|
||||
hash[:parent_author_signature] = sign_with_key(privkey, hash)
|
||||
logger.info "event=sign_with_key signature=parent_author_signature guid=#{guid}"
|
||||
end
|
||||
end
|
||||
|
||||
# this happens only on downstream federation
|
||||
def verify_parent_signature
|
||||
def verify_parent_author_signature
|
||||
pubkey = DiasporaFederation.callbacks.trigger(:fetch_author_public_key_by_entity_guid, parent_type, parent_guid)
|
||||
|
||||
raise SignatureVerificationFailed, "failed to fetch public key for author of #{parent_guid}" if pubkey.nil?
|
||||
raise SignatureVerificationFailed, "wrong parent_author_signature" unless Signing.verify_signature(
|
||||
data, parent_author_signature, pubkey
|
||||
)
|
||||
raise PublicKeyNotFound, "parent_author_signature parent_guid=#{parent_guid} guid=#{guid}" if pubkey.nil?
|
||||
unless verify_signature(pubkey, parent_author_signature)
|
||||
raise SignatureVerificationFailed, "wrong parent_author_signature parent_guid=#{parent_guid}"
|
||||
end
|
||||
end
|
||||
|
||||
# Sign the data with the key
|
||||
#
|
||||
# @param [OpenSSL::PKey::RSA] privkey An RSA key
|
||||
# @param [Hash] hash data to sign
|
||||
# @return [String] A Base64 encoded signature of #signable_string with key
|
||||
def sign_with_key(privkey, hash)
|
||||
Base64.strict_encode64(privkey.sign(DIGEST, signable_string(hash)))
|
||||
end
|
||||
|
||||
# Check that signature is a correct signature
|
||||
#
|
||||
# @param [OpenSSL::PKey::RSA] pubkey An RSA key
|
||||
# @param [String] signature The signature to be verified.
|
||||
# @return [Boolean]
|
||||
def verify_signature(pubkey, signature)
|
||||
if signature.nil?
|
||||
logger.warn "event=verify_signature status=abort reason=no_signature guid=#{guid}"
|
||||
return false
|
||||
end
|
||||
|
||||
validity = pubkey.verify(DIGEST, Base64.decode64(signature), signable_string(data))
|
||||
logger.info "event=verify_signature status=complete guid=#{guid} validity=#{validity}"
|
||||
validity
|
||||
end
|
||||
|
||||
# @param [Hash] hash data to sign
|
||||
# @return [String] signature data string
|
||||
def signable_string(hash)
|
||||
hash.map {|name, value|
|
||||
value.to_s unless name =~ /signature/
|
||||
}.compact.join(";")
|
||||
end
|
||||
|
||||
# Exception raised when creating the author_signature failes, because the private key was not found
|
||||
class AuthorPrivateKeyNotFound < RuntimeError
|
||||
end
|
||||
|
||||
# Exception raised when verify_signatures fails to verify signatures (no public key found)
|
||||
class PublicKeyNotFound < RuntimeError
|
||||
end
|
||||
|
||||
# Exception raised when verify_signatures fails to verify signatures (signatures are wrong)
|
||||
class SignatureVerificationFailed < RuntimeError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ module DiasporaFederation
|
|||
# @param [SignedRetraction, RelayableRetraction] ret the retraction to sign
|
||||
# @return [String] a Base64 encoded signature of the retraction with the key
|
||||
def self.sign_with_key(privkey, ret)
|
||||
Base64.strict_encode64(privkey.sign(OpenSSL::Digest::SHA256.new, [ret.target_guid, ret.target_type].join(";")))
|
||||
Base64.strict_encode64(privkey.sign(Relayable::DIGEST, [ret.target_guid, ret.target_type].join(";")))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
module DiasporaFederation
|
||||
module Federation
|
||||
# Raised if failed to fetch a public key of the sender of the received message
|
||||
class SenderKeyNotFound < Exception
|
||||
class SenderKeyNotFound < RuntimeError
|
||||
end
|
||||
|
||||
# Raised if recipient private key is missing for a private receive
|
||||
class RecipientKeyNotFound < Exception
|
||||
class RecipientKeyNotFound < RuntimeError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
module DiasporaFederation
|
||||
# this module defines operations of signing an arbitrary hash with an arbitrary key
|
||||
module Signing
|
||||
extend Logging
|
||||
|
||||
# Sign the data with the key
|
||||
#
|
||||
# @param [Hash] hash data to sign
|
||||
# @param [OpenSSL::PKey::RSA] privkey An RSA key
|
||||
# @return [String] A Base64 encoded signature of #signable_string with key
|
||||
def self.sign_with_key(hash, privkey)
|
||||
sig = Base64.strict_encode64(
|
||||
privkey.sign(
|
||||
OpenSSL::Digest::SHA256.new,
|
||||
signable_string(hash)
|
||||
)
|
||||
)
|
||||
logger.info "event=sign_with_key status=complete guid=#{hash[:guid]}"
|
||||
sig
|
||||
end
|
||||
|
||||
# Check that signature is a correct signature
|
||||
#
|
||||
# @param [Hash] hash data to verify
|
||||
# @param [String] signature The signature to be verified.
|
||||
# @param [OpenSSL::PKey::RSA] pubkey An RSA key
|
||||
# @return [Boolean]
|
||||
def self.verify_signature(hash, signature, pubkey)
|
||||
if pubkey.nil?
|
||||
logger.warn "event=verify_signature status=abort reason=no_key guid=#{hash[:guid]}"
|
||||
return false
|
||||
elsif signature.nil?
|
||||
logger.warn "event=verify_signature status=abort reason=no_signature guid=#{hash[:guid]}"
|
||||
return false
|
||||
end
|
||||
|
||||
validity = pubkey.verify(
|
||||
OpenSSL::Digest::SHA256.new,
|
||||
Base64.decode64(signature),
|
||||
signable_string(hash)
|
||||
)
|
||||
logger.info "event=verify_signature status=complete guid=#{hash[:guid]} validity=#{validity}"
|
||||
validity
|
||||
end
|
||||
|
||||
# @param [Hash] hash data to sign
|
||||
# @return [String] signature data string
|
||||
def self.signable_string(hash)
|
||||
hash.map {|name, value|
|
||||
value.to_s unless name =~ /signature/
|
||||
}.compact.join(";")
|
||||
end
|
||||
private_class_method :signable_string
|
||||
end
|
||||
end
|
||||
|
|
@ -31,12 +31,12 @@ XML
|
|||
:fetch_private_key_by_diaspora_id, hash[:diaspora_id]
|
||||
).and_return(author_pkey)
|
||||
|
||||
signed_string = "#{hash[:target_guid]};#{hash[:target_type]}"
|
||||
|
||||
signed_hash = Entities::RelayableRetraction.new(hash).to_h
|
||||
|
||||
signable_hash = hash.select do |key, _|
|
||||
%i(target_guid target_type).include?(key)
|
||||
end
|
||||
expect(Signing.verify_signature(signable_hash, signed_hash[:target_author_signature], author_pkey)).to be_truthy
|
||||
signature = Base64.decode64(signed_hash[:target_author_signature])
|
||||
expect(author_pkey.verify(OpenSSL::Digest::SHA256.new, signature, signed_string)).to be_truthy
|
||||
end
|
||||
|
||||
it "updates parent author signature when it was nil, key was supplied and sender is not author of the target" do
|
||||
|
|
@ -48,12 +48,12 @@ XML
|
|||
:fetch_private_key_by_diaspora_id, hash[:diaspora_id]
|
||||
).and_return(author_pkey)
|
||||
|
||||
signed_string = "#{hash[:target_guid]};#{hash[:target_type]}"
|
||||
|
||||
signed_hash = Entities::RelayableRetraction.new(hash).to_h
|
||||
|
||||
signable_hash = hash.select do |key, _|
|
||||
%i(target_guid target_type).include?(key)
|
||||
end
|
||||
expect(Signing.verify_signature(signable_hash, signed_hash[:parent_author_signature], author_pkey)).to be_truthy
|
||||
signature = Base64.decode64(signed_hash[:parent_author_signature])
|
||||
expect(author_pkey.verify(OpenSSL::Digest::SHA256.new, signature, signed_string)).to be_truthy
|
||||
end
|
||||
|
||||
it "doesn't change signatures if they are already set" do
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ module DiasporaFederation
|
|||
let(:parent_pkey) { OpenSSL::PKey::RSA.generate(1024) }
|
||||
let(:hash) {
|
||||
{
|
||||
guid: FactoryGirl.generate(:guid),
|
||||
diaspora_id: FactoryGirl.generate(:diaspora_id),
|
||||
parent_guid: FactoryGirl.generate(:guid),
|
||||
some_other_data: "a_random_string"
|
||||
|
|
@ -11,33 +12,39 @@ module DiasporaFederation
|
|||
}
|
||||
|
||||
class SomeRelayable < Entity
|
||||
include Entities::Relayable
|
||||
|
||||
property :guid
|
||||
property :diaspora_id, xml_name: :diaspora_handle
|
||||
|
||||
include Entities::Relayable
|
||||
|
||||
def parent_type
|
||||
"Target"
|
||||
"Parent"
|
||||
end
|
||||
end
|
||||
|
||||
def legacy_sign_with_key(privkey, hash)
|
||||
Base64.strict_encode64(privkey.sign(OpenSSL::Digest::SHA256.new, hash.values.join(";")))
|
||||
end
|
||||
|
||||
describe "#verify_signatures" do
|
||||
it "doesn't raise anything if correct data were passed" do
|
||||
hash[:author_signature] = Signing.sign_with_key(hash, author_pkey)
|
||||
hash[:parent_author_signature] = Signing.sign_with_key(hash, parent_pkey)
|
||||
signed_hash = hash.dup
|
||||
signed_hash[:author_signature] = legacy_sign_with_key(author_pkey, hash)
|
||||
signed_hash[:parent_author_signature] = legacy_sign_with_key(parent_pkey, hash)
|
||||
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:fetch_public_key_by_diaspora_id, hash[:diaspora_id]
|
||||
).and_return(author_pkey.public_key)
|
||||
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:fetch_author_public_key_by_entity_guid, "Target", hash[:parent_guid]
|
||||
:fetch_author_public_key_by_entity_guid, "Parent", hash[:parent_guid]
|
||||
).and_return(parent_pkey.public_key)
|
||||
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:entity_author_is_local?, "Target", hash[:parent_guid]
|
||||
:entity_author_is_local?, "Parent", hash[:parent_guid]
|
||||
).and_return(false)
|
||||
|
||||
expect { SomeRelayable.new(hash).verify_signatures }.not_to raise_error
|
||||
expect { SomeRelayable.new(signed_hash).verify_signatures }.not_to raise_error
|
||||
end
|
||||
|
||||
it "raises when no public key for author was fetched" do
|
||||
|
|
@ -47,7 +54,7 @@ module DiasporaFederation
|
|||
|
||||
expect {
|
||||
SomeRelayable.new(hash).verify_signatures
|
||||
}.to raise_error Entities::Relayable::SignatureVerificationFailed
|
||||
}.to raise_error Entities::Relayable::PublicKeyNotFound
|
||||
end
|
||||
|
||||
it "raises when bad author signature was passed" do
|
||||
|
|
@ -63,27 +70,27 @@ module DiasporaFederation
|
|||
end
|
||||
|
||||
it "raises when no public key for parent author was fetched" do
|
||||
hash[:author_signature] = Signing.sign_with_key(hash, author_pkey)
|
||||
hash[:author_signature] = legacy_sign_with_key(author_pkey, hash)
|
||||
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:fetch_public_key_by_diaspora_id, hash[:diaspora_id]
|
||||
).and_return(author_pkey.public_key)
|
||||
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:fetch_author_public_key_by_entity_guid, "Target", hash[:parent_guid]
|
||||
:fetch_author_public_key_by_entity_guid, "Parent", hash[:parent_guid]
|
||||
).and_return(nil)
|
||||
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:entity_author_is_local?, "Target", hash[:parent_guid]
|
||||
:entity_author_is_local?, "Parent", hash[:parent_guid]
|
||||
).and_return(false)
|
||||
|
||||
expect {
|
||||
SomeRelayable.new(hash).verify_signatures
|
||||
}.to raise_error Entities::Relayable::SignatureVerificationFailed
|
||||
}.to raise_error Entities::Relayable::PublicKeyNotFound
|
||||
end
|
||||
|
||||
it "raises when bad parent author signature was passed" do
|
||||
hash[:author_signature] = Signing.sign_with_key(hash, author_pkey)
|
||||
hash[:author_signature] = legacy_sign_with_key(author_pkey, hash)
|
||||
hash[:parent_author_signature] = nil
|
||||
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
|
|
@ -91,11 +98,11 @@ module DiasporaFederation
|
|||
).and_return(author_pkey.public_key)
|
||||
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:fetch_author_public_key_by_entity_guid, "Target", hash[:parent_guid]
|
||||
:fetch_author_public_key_by_entity_guid, "Parent", hash[:parent_guid]
|
||||
).and_return(parent_pkey.public_key)
|
||||
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:entity_author_is_local?, "Target", hash[:parent_guid]
|
||||
:entity_author_is_local?, "Parent", hash[:parent_guid]
|
||||
).and_return(false)
|
||||
|
||||
expect {
|
||||
|
|
@ -104,7 +111,7 @@ module DiasporaFederation
|
|||
end
|
||||
|
||||
it "doesn't raise if parent_author_signature isn't set but we're on upstream federation" do
|
||||
hash[:author_signature] = Signing.sign_with_key(hash, author_pkey)
|
||||
hash[:author_signature] = legacy_sign_with_key(author_pkey, hash)
|
||||
hash[:parent_author_signature] = nil
|
||||
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
|
|
@ -112,7 +119,7 @@ module DiasporaFederation
|
|||
).and_return(author_pkey.public_key)
|
||||
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:entity_author_is_local?, "Target", hash[:parent_guid]
|
||||
:entity_author_is_local?, "Parent", hash[:parent_guid]
|
||||
).and_return(true)
|
||||
|
||||
expect { SomeRelayable.new(hash).verify_signatures }.not_to raise_error
|
||||
|
|
@ -126,13 +133,19 @@ module DiasporaFederation
|
|||
).and_return(author_pkey)
|
||||
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:fetch_author_private_key_by_entity_guid, "Target", hash[:parent_guid]
|
||||
:fetch_author_private_key_by_entity_guid, "Parent", hash[:parent_guid]
|
||||
).and_return(parent_pkey)
|
||||
|
||||
signed_string = hash.reject {|key, _| key == :some_other_data }.values.join(";")
|
||||
|
||||
signed_hash = SomeRelayable.new(hash).to_h
|
||||
|
||||
expect(Signing.verify_signature(signed_hash, signed_hash[:author_signature], author_pkey)).to be_truthy
|
||||
expect(Signing.verify_signature(signed_hash, signed_hash[:parent_author_signature], parent_pkey)).to be_truthy
|
||||
def verify_signature(pubkey, signature, signed_string)
|
||||
pubkey.verify(OpenSSL::Digest::SHA256.new, Base64.decode64(signature), signed_string)
|
||||
end
|
||||
|
||||
expect(verify_signature(author_pkey, signed_hash[:author_signature], signed_string)).to be_truthy
|
||||
expect(verify_signature(parent_pkey, signed_hash[:parent_author_signature], signed_string)).to be_truthy
|
||||
end
|
||||
|
||||
it "doesn't change signatures if they are already set" do
|
||||
|
|
@ -141,18 +154,27 @@ module DiasporaFederation
|
|||
expect(SomeRelayable.new(hash).to_h).to eq(hash)
|
||||
end
|
||||
|
||||
it "doesn't change signatures if keys weren't supplied" do
|
||||
it "raises when author_signature not set and key isn't supplied" do
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:fetch_private_key_by_diaspora_id, hash[:diaspora_id]
|
||||
).and_return(nil)
|
||||
|
||||
expect {
|
||||
SomeRelayable.new(hash).to_h
|
||||
}.to raise_error Entities::Relayable::AuthorPrivateKeyNotFound
|
||||
end
|
||||
|
||||
it "doesn't set parent_author_signature if key isn't supplied" do
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:fetch_author_private_key_by_entity_guid, "Target", hash[:parent_guid]
|
||||
:fetch_private_key_by_diaspora_id, hash[:diaspora_id]
|
||||
).and_return(author_pkey)
|
||||
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:fetch_author_private_key_by_entity_guid, "Parent", hash[:parent_guid]
|
||||
).and_return(nil)
|
||||
|
||||
signed_hash = SomeRelayable.new(hash).to_h
|
||||
|
||||
expect(signed_hash[:author_signature]).to eq(nil)
|
||||
expect(signed_hash[:parent_author_signature]).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,13 +26,14 @@ XML
|
|||
:fetch_private_key_by_diaspora_id, hash[:diaspora_id]
|
||||
).and_return(author_pkey)
|
||||
|
||||
signable_hash = hash.select do |key, _|
|
||||
%i(target_guid target_type).include?(key)
|
||||
end
|
||||
signed_string = "#{hash[:target_guid]};#{hash[:target_type]}"
|
||||
|
||||
signed_hash = Entities::SignedRetraction.new(hash).to_h
|
||||
|
||||
expect(Signing.verify_signature(signable_hash, signed_hash[:target_author_signature], author_pkey)).to be_truthy
|
||||
valid = author_pkey.verify(
|
||||
OpenSSL::Digest::SHA256.new, Base64.decode64(signed_hash[:target_author_signature]), signed_string
|
||||
)
|
||||
expect(valid).to be_truthy
|
||||
end
|
||||
|
||||
it "doesn't change signature if it is already set" do
|
||||
|
|
|
|||
|
|
@ -147,8 +147,11 @@ XML
|
|||
it "calls signatures verification on relayable unpack" do
|
||||
entity = FactoryGirl.build(:comment_entity)
|
||||
payload = Salmon::XmlPayload.pack(entity)
|
||||
expect(Signing).to receive(:verify_signature).twice.and_call_original
|
||||
Salmon::XmlPayload.unpack(payload)
|
||||
payload.at_xpath("post/*[1]/author_signature").content = nil
|
||||
|
||||
expect {
|
||||
Salmon::XmlPayload.unpack(payload)
|
||||
}.to raise_error DiasporaFederation::Entities::Relayable::SignatureVerificationFailed
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
module DiasporaFederation
|
||||
describe Signing do
|
||||
let(:privkey) {
|
||||
OpenSSL::PKey::RSA.new <<-RSA
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXAIBAAKBgQDT7vBTAl0Z55bPcBjM9dvSOTuVtBxsgfrw2W0hTAYpd1H5032C
|
||||
cVW3mqd0l/9BHscgudVFAkvp+nf+wTQILn4qH4YAhOdWgrlSBA6Rbs3cmtmXzGNq
|
||||
oQr4NOMbqs6sP+bBjDuDdB+cAFms/NDUH3cHBKPXi3e3csxiErmN1zyfWwIDAQAB
|
||||
AoGAbpBC1CtxgqgtJz8l0ReafIvbJ/h0s68DyU7E/g/5TvyuyZSp77lMrKKEJfF9
|
||||
+u0hmVMZjgzqqcA/haopiPMoYcJAwwhJLeXAgAWA+8j60Y524WLDcMPwMxQvVFd9
|
||||
3FYXdOalojDoS34BWeBy6Gt+lLGyDvo/NnJBqIMPN0/KzYECQQDuslE4f1+RHhUq
|
||||
wf2rL/7gCgrnkDOcH1SPjN2FrKG5ALmjThCq7Wr1Umj81uvmglfpIRY/ORgYgujA
|
||||
kwNTB1ohAkEA40v0mHaYDegL//jucFmx/iK9Bs/722rJGIXI7bGIwLRC1hW101h3
|
||||
DLMEMT0QaamVEEnrXFdqhjz+bfYfqUkh+wJAU3a+t8ayIAgo1p6mmKlbsfNRBM+D
|
||||
fF/oLZnQC+HlWs9KGjQ918bU05tRYre0HRIOs1ICeXD5X/jGci/1xZ6YgQJAJony
|
||||
Zwd0sKbvoe8rPpF2xIhPVKBfK8znW+kTMHoxnbryuinkMnmFdfnEdDTOW5wNUj22
|
||||
Umnf/fLJkQtyQtnLkQJBANMoQPrP6aMRh45bhq+y6DbzHHHc2T5cuGBCtnhu+qrK
|
||||
hWHXqQT4rArfq8YBpvDUa7qD13WwFGK3TPRpQSVGzNg=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
RSA
|
||||
}
|
||||
|
||||
let(:hash) {
|
||||
{
|
||||
param1: "1",
|
||||
param2: "2",
|
||||
signature: "SIGNATURE_VALUE==",
|
||||
param3: "3",
|
||||
parent_signature: "SIGNATURE2_VALUE==",
|
||||
param4: "4"
|
||||
}
|
||||
}
|
||||
let(:signature) {
|
||||
"OesXlpesuLcA0t8gPyBjvznvkl0pz63p8z6+o2fxFNUaZkuR6YQv/sJOTSMPYBAFwcWr048Ol7yw4jSHq0gFCdBBeF7Mg287jktCie"\
|
||||
"xa6G6mA24hBlOWnyRJLV2OyqcTU1P5pXWlUc1Mbwbr6bSIs6VK9djFMLLQ6wjjpusJ0XU="
|
||||
}
|
||||
|
||||
describe ".signable_string" do
|
||||
it "forms correct string for a hash" do
|
||||
expect(Signing.send(:signable_string, hash)).to eq("1;2;3;4")
|
||||
end
|
||||
end
|
||||
|
||||
describe ".sign_with_key" do
|
||||
it "produces correct signature" do
|
||||
expect(Signing.sign_with_key(hash, privkey)).to eq(signature)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".verify_signature" do
|
||||
it "verifies correct signature" do
|
||||
expect(Signing.verify_signature(hash, signature, privkey.public_key)).to be_truthy
|
||||
end
|
||||
|
||||
it "doesn't verify wrong signature" do
|
||||
expect(Signing.verify_signature(hash, "false signature==", privkey.public_key)).to be_falsy
|
||||
end
|
||||
|
||||
it "doesn't verify when signature is missing" do
|
||||
expect(Signing.verify_signature(hash, nil, privkey.public_key)).to be_falsy
|
||||
end
|
||||
|
||||
it "doesn't verify when public key is missing" do
|
||||
expect(Signing.verify_signature(hash, signature, nil)).to be_falsy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -75,17 +75,20 @@ shared_examples "a relayable Entity" do
|
|||
let(:instance) { described_class.new(data.merge(author_signature: nil, parent_author_signature: nil)) }
|
||||
|
||||
context "signatures generation" do
|
||||
def legacy_verify_signature(pubkey, signature, signed_string)
|
||||
pubkey.verify(OpenSSL::Digest::SHA256.new, Base64.decode64(signature), signed_string)
|
||||
end
|
||||
|
||||
it "computes correct signatures for the entity" do
|
||||
hash = instance.to_h
|
||||
signed_string = instance.to_h.map {|name, value| value.to_s unless name =~ /signature/ }.compact.join(";")
|
||||
|
||||
xml = DiasporaFederation::Salmon::XmlPayload.pack(instance)
|
||||
|
||||
author_signature = xml.at_xpath("post/*[1]/author_signature").text
|
||||
parent_author_signature = xml.at_xpath("post/*[1]/parent_author_signature").text
|
||||
|
||||
expect(DiasporaFederation::Signing.verify_signature(hash, author_signature, test_pkey))
|
||||
.to be_truthy
|
||||
expect(DiasporaFederation::Signing.verify_signature(hash, parent_author_signature, test_pkey))
|
||||
.to be_truthy
|
||||
expect(legacy_verify_signature(test_pkey, author_signature, signed_string)).to be_truthy
|
||||
expect(legacy_verify_signature(test_pkey, parent_author_signature, signed_string)).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue