Merge branch 'encrypted-salmon' into develop
This commit is contained in:
commit
03fe90ffaa
22 changed files with 488 additions and 209 deletions
|
|
@ -9,11 +9,12 @@ module DiasporaFederation
|
|||
#
|
||||
# POST /receive/public
|
||||
def public
|
||||
logger.info "received a public message"
|
||||
xml = CGI.unescape(params[:xml])
|
||||
logger.debug xml
|
||||
legacy = request.content_type != "application/magic-envelope+xml"
|
||||
|
||||
DiasporaFederation.callbacks.trigger(:queue_public_receive, xml)
|
||||
data = data_for_public_message(legacy)
|
||||
logger.debug data
|
||||
|
||||
DiasporaFederation.callbacks.trigger(:queue_public_receive, data, legacy)
|
||||
|
||||
render nothing: true, status: 202
|
||||
end
|
||||
|
|
@ -22,19 +23,43 @@ module DiasporaFederation
|
|||
#
|
||||
# POST /receive/users/:guid
|
||||
def private
|
||||
logger.info "received a private message for #{params[:guid]}"
|
||||
xml = CGI.unescape(params[:xml])
|
||||
logger.debug xml
|
||||
legacy = request.content_type != "application/json"
|
||||
|
||||
success = DiasporaFederation.callbacks.trigger(:queue_private_receive, params[:guid], xml)
|
||||
data = data_for_private_message(legacy)
|
||||
logger.debug data
|
||||
|
||||
success = DiasporaFederation.callbacks.trigger(:queue_private_receive, params[:guid], data, legacy)
|
||||
|
||||
render nothing: true, status: success ? 202 : 404
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# checks the xml parameter for legacy salmon slaps
|
||||
# @deprecated
|
||||
def check_for_xml
|
||||
render nothing: true, status: 422 if params[:xml].nil?
|
||||
legacy_request = request.content_type.nil? || request.content_type == "application/x-www-form-urlencoded"
|
||||
render nothing: true, status: 422 if params[:xml].nil? && legacy_request
|
||||
end
|
||||
|
||||
def data_for_public_message(legacy)
|
||||
if legacy
|
||||
logger.info "received a public salmon slap"
|
||||
CGI.unescape(params[:xml])
|
||||
else
|
||||
logger.info "received a public magic envelope"
|
||||
request.body.read
|
||||
end
|
||||
end
|
||||
|
||||
def data_for_private_message(legacy)
|
||||
if legacy
|
||||
logger.info "received a private salmon slap for #{params[:guid]}"
|
||||
CGI.unescape(params[:xml])
|
||||
else
|
||||
logger.info "received a private encrypted magic envelope for #{params[:guid]}"
|
||||
request.body.read
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -155,12 +155,14 @@ module DiasporaFederation
|
|||
#
|
||||
# queue_public_receive
|
||||
# Queue a public salmon xml to process in background
|
||||
# @param [String] xml salmon xml
|
||||
# @param [String] data salmon slap xml or magic envelope xml
|
||||
# @param [Boolean] legacy true if it is a legacy salmon slap, false if it is a magic envelope xml
|
||||
#
|
||||
# queue_private_receive
|
||||
# Queue a private salmon xml to process in background
|
||||
# @param [String] guid guid of the receiver person
|
||||
# @param [String] xml salmon xml
|
||||
# @param [String] data salmon slap xml or encrypted magic envelope json
|
||||
# @param [Boolean] legacy true if it is a legacy salmon slap, false if it is a encrypted magic envelope json
|
||||
# @return [Boolean] true if successful, false if the user was not found
|
||||
#
|
||||
# save_entity_after_receive
|
||||
|
|
|
|||
|
|
@ -4,6 +4,5 @@ module DiasporaFederation
|
|||
end
|
||||
end
|
||||
|
||||
require "diaspora_federation/federation/exceptions"
|
||||
require "diaspora_federation/federation/receiver"
|
||||
require "diaspora_federation/federation/sender"
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
module DiasporaFederation
|
||||
module Federation
|
||||
# Raised if failed to fetch a public key of the sender of the received message
|
||||
class SenderKeyNotFound < RuntimeError
|
||||
end
|
||||
|
||||
# Raised if recipient private key is missing for a private receive
|
||||
class RecipientKeyNotFound < RuntimeError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,25 +1,10 @@
|
|||
module DiasporaFederation
|
||||
module Federation
|
||||
# Common base for Private and Public receivers
|
||||
# @see Receiver::Public
|
||||
# @see Receiver::Private
|
||||
class Receiver
|
||||
# initializes a new Receiver for a salmon XML
|
||||
# @param [String] salmon_xml the message salmon xml
|
||||
def initialize(salmon_xml)
|
||||
@salmon_xml = salmon_xml
|
||||
end
|
||||
|
||||
# Parse the salmon xml and send it to the +:save_entity_after_receive+ callback
|
||||
def receive!
|
||||
sender_id = slap.author_id
|
||||
public_key = DiasporaFederation.callbacks.trigger(:fetch_public_key_by_diaspora_id, sender_id)
|
||||
raise SenderKeyNotFound if public_key.nil?
|
||||
DiasporaFederation.callbacks.trigger(:save_entity_after_receive, slap.entity(public_key))
|
||||
end
|
||||
module Receiver
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require "diaspora_federation/federation/receiver/private"
|
||||
require "diaspora_federation/federation/receiver/public"
|
||||
require "diaspora_federation/federation/receiver/slap_receiver"
|
||||
require "diaspora_federation/federation/receiver/private_slap_receiver"
|
||||
require "diaspora_federation/federation/receiver/public_slap_receiver"
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
module DiasporaFederation
|
||||
module Federation
|
||||
class Receiver
|
||||
# Receiver::Private is used to receive private messages, which are addressed
|
||||
# to a specific user, encrypted with his public key and packed using Salmon::EncryptedSlap
|
||||
class Private < Receiver
|
||||
# initializes a new Private Receiver for a salmon XML
|
||||
# @param [String] salmon_xml the message salmon xml
|
||||
# @param [OpenSSL::PKey::RSA] recipient_private_key recipient private key to decrypt the message
|
||||
def initialize(salmon_xml, recipient_private_key)
|
||||
super(salmon_xml)
|
||||
raise RecipientKeyNotFound if recipient_private_key.nil?
|
||||
@recipient_private_key = recipient_private_key
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# parses the encrypted slap xml
|
||||
# @return [Salmon::EncryptedSlap] slap instance
|
||||
def slap
|
||||
@salmon ||= Salmon::EncryptedSlap.from_xml(@salmon_xml, @recipient_private_key)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
module DiasporaFederation
|
||||
module Federation
|
||||
module Receiver
|
||||
# This is used to receive private messages, which are addressed to a specific user,
|
||||
# encrypted with his public key and packed using {Salmon::EncryptedSlap}.
|
||||
# @deprecated
|
||||
class PrivateSlapReceiver < SlapReceiver
|
||||
# initializes a new Private Receiver for a salmon slap XML
|
||||
# @param [String] slap_xml the message salmon slap xml
|
||||
# @param [OpenSSL::PKey::RSA] recipient_private_key recipient private key to decrypt the message
|
||||
def initialize(slap_xml, recipient_private_key)
|
||||
super(slap_xml)
|
||||
raise ArgumentError, "no recipient key provided" unless recipient_private_key.instance_of?(OpenSSL::PKey::RSA)
|
||||
@recipient_private_key = recipient_private_key
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# parses the encrypted slap xml
|
||||
# @return [Salmon::EncryptedSlap] slap instance
|
||||
def slap
|
||||
@slap ||= Salmon::EncryptedSlap.from_xml(@slap_xml, @recipient_private_key)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
module DiasporaFederation
|
||||
module Federation
|
||||
class Receiver
|
||||
# Receiver::Public is used to receive public messages, which are not addressed to a specific user, unencrypted
|
||||
# and packed using Salmon::Slap
|
||||
class Public < Receiver
|
||||
protected
|
||||
|
||||
# parses the public slap xml
|
||||
# @return [Salmon::Slap] slap instance
|
||||
def slap
|
||||
@salmon ||= Salmon::Slap.from_xml(@salmon_xml)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
module DiasporaFederation
|
||||
module Federation
|
||||
module Receiver
|
||||
# This is used to receive public messages, which are not addressed to
|
||||
# a specific user, unencrypted and packed using {Salmon::Slap}.
|
||||
# @deprecated
|
||||
class PublicSlapReceiver < SlapReceiver
|
||||
protected
|
||||
|
||||
# parses the public slap xml
|
||||
# @return [Salmon::Slap] slap instance
|
||||
def slap
|
||||
@slap ||= Salmon::Slap.from_xml(@slap_xml)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
25
lib/diaspora_federation/federation/receiver/slap_receiver.rb
Normal file
25
lib/diaspora_federation/federation/receiver/slap_receiver.rb
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
module DiasporaFederation
|
||||
module Federation
|
||||
module Receiver
|
||||
# Common base for Private and Public receivers
|
||||
# @see PublicSlapReceiver
|
||||
# @see PrivateSlapReceiver
|
||||
# @deprecated
|
||||
class SlapReceiver
|
||||
# initializes a new SlapReceiver for a salmon slap XML
|
||||
# @param [String] slap_xml the message salmon xml
|
||||
def initialize(slap_xml)
|
||||
@slap_xml = slap_xml
|
||||
end
|
||||
|
||||
# Parse the salmon xml and send it to the +:save_entity_after_receive+ callback
|
||||
def receive!
|
||||
sender_id = slap.author_id
|
||||
public_key = DiasporaFederation.callbacks.trigger(:fetch_public_key_by_diaspora_id, sender_id)
|
||||
raise Salmon::SenderKeyNotFound if public_key.nil?
|
||||
DiasporaFederation.callbacks.trigger(:save_entity_after_receive, slap.entity(public_key))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -13,5 +13,6 @@ require "diaspora_federation/salmon/aes"
|
|||
require "diaspora_federation/salmon/exceptions"
|
||||
require "diaspora_federation/salmon/xml_payload"
|
||||
require "diaspora_federation/salmon/magic_envelope"
|
||||
require "diaspora_federation/salmon/encrypted_magic_envelope"
|
||||
require "diaspora_federation/salmon/slap"
|
||||
require "diaspora_federation/salmon/encrypted_slap"
|
||||
|
|
|
|||
61
lib/diaspora_federation/salmon/encrypted_magic_envelope.rb
Normal file
61
lib/diaspora_federation/salmon/encrypted_magic_envelope.rb
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
module DiasporaFederation
|
||||
module Salmon
|
||||
# This is a simple crypt-wrapper for {MagicEnvelope}.
|
||||
#
|
||||
# The wrapper is JSON with the following structure:
|
||||
#
|
||||
# {
|
||||
# "aes_key": "...",
|
||||
# "encrypted_magic_envelope": "..."
|
||||
# }
|
||||
#
|
||||
# +aes_key+ is encrypted using the recipients public key, and contains the AES
|
||||
# +key+ and +iv+ as JSON:
|
||||
#
|
||||
# {
|
||||
# "key": "...",
|
||||
# "iv": "..."
|
||||
# }
|
||||
#
|
||||
# +encrypted_magic_envelope+ is encrypted using the +key+ and +iv+ from +aes_key+.
|
||||
# Once decrypted it contains the {MagicEnvelope} xml:
|
||||
#
|
||||
# <me:env>
|
||||
# ...
|
||||
# </me:env>
|
||||
#
|
||||
# All JSON-values (+aes_key+, +encrypted_magic_envelope+, +key+ and +iv+) are
|
||||
# base64 encoded.
|
||||
module EncryptedMagicEnvelope
|
||||
# Generates a new random AES key and encrypts the {MagicEnvelope} with it.
|
||||
# Then encrypts the AES key with the receivers public key.
|
||||
# @param [Nokogiri::XML::Element] magic_env XML root node of a magic envelope
|
||||
# @param [OpenSSL::PKey::RSA] pubkey recipient public_key
|
||||
# @return [String] json string
|
||||
def self.encrypt(magic_env, pubkey)
|
||||
key = AES.generate_key_and_iv
|
||||
encrypted_env = AES.encrypt(magic_env.to_xml, key[:key], key[:iv])
|
||||
|
||||
encoded_key = Hash[key.map {|k, v| [k, Base64.strict_encode64(v)] }]
|
||||
encrypted_key = Base64.strict_encode64(pubkey.public_encrypt(JSON.generate(encoded_key)))
|
||||
|
||||
JSON.generate(aes_key: encrypted_key, encrypted_magic_envelope: encrypted_env)
|
||||
end
|
||||
|
||||
# Decrypts the AES key with the private key of the receiver and decrypts the
|
||||
# encrypted {MagicEnvelope} with it.
|
||||
# @param [String] encrypted_env json string with aes_key and encrypted_magic_envelope
|
||||
# @param [OpenSSL::PKey::RSA] privkey private key for decryption
|
||||
# @return [Nokogiri::XML::Element] decrypted magic envelope xml
|
||||
def self.decrypt(encrypted_env, privkey)
|
||||
encrypted_json = JSON.parse(encrypted_env)
|
||||
|
||||
encoded_key = JSON.parse(privkey.private_decrypt(Base64.decode64(encrypted_json["aes_key"])))
|
||||
key = Hash[encoded_key.map {|k, v| [k, Base64.decode64(v)] }]
|
||||
|
||||
xml = AES.decrypt(encrypted_json["encrypted_magic_envelope"], key["key"], key["iv"])
|
||||
Nokogiri::XML::Document.parse(xml).root
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -63,6 +63,7 @@ module DiasporaFederation
|
|||
#
|
||||
# entity = slap.entity(author_pubkey)
|
||||
#
|
||||
# @deprecated
|
||||
class EncryptedSlap < Slap
|
||||
# the key and iv if it is an encrypted slap
|
||||
# @param [Hash] value hash containing the key and iv
|
||||
|
|
@ -113,9 +114,9 @@ module DiasporaFederation
|
|||
EncryptedSlap.new.tap do |slap|
|
||||
slap.author_id = author_id
|
||||
|
||||
magic_envelope = MagicEnvelope.new(privkey, entity)
|
||||
magic_envelope = MagicEnvelope.new(entity)
|
||||
slap.cipher_params = magic_envelope.encrypt!
|
||||
slap.magic_envelope_xml = magic_envelope.envelop
|
||||
slap.magic_envelope_xml = magic_envelope.envelop(privkey, author_id)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -172,11 +173,11 @@ module DiasporaFederation
|
|||
# @param [OpenSSL::PKey::RSA] pubkey recipient public_key
|
||||
# @return [String] encrypted base64 encoded header
|
||||
def encrypted_header(author_id, envelope_key, pubkey)
|
||||
encoded_key = Hash[envelope_key.map {|k, v| [k, Base64.strict_encode64(v)] }]
|
||||
data = header_xml(author_id, encoded_key)
|
||||
ciphertext = AES.encrypt(data, envelope_key[:key], envelope_key[:iv])
|
||||
data = header_xml(author_id, strict_base64_encode(envelope_key))
|
||||
header_key = AES.generate_key_and_iv
|
||||
ciphertext = AES.encrypt(data, header_key[:key], header_key[:iv])
|
||||
|
||||
json_key = JSON.generate(encoded_key)
|
||||
json_key = JSON.generate(strict_base64_encode(header_key))
|
||||
encrypted_key = Base64.strict_encode64(pubkey.public_encrypt(json_key))
|
||||
|
||||
json_header = JSON.generate(aes_key: encrypted_key, ciphertext: ciphertext)
|
||||
|
|
@ -197,6 +198,12 @@ module DiasporaFederation
|
|||
}
|
||||
}.to_xml.strip
|
||||
end
|
||||
|
||||
# @param [Hash] hash { key: "...", iv: "..." }
|
||||
# @return [Hash] encoded hash: { key: "...", iv: "..." }
|
||||
def strict_base64_encode(hash)
|
||||
Hash[hash.map {|k, v| [k, Base64.strict_encode64(v)] }]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,21 +1,29 @@
|
|||
module DiasporaFederation
|
||||
module Salmon
|
||||
# Raised, if the element containing the Magic Envelope is missing from the XML
|
||||
# @deprecated
|
||||
class MissingMagicEnvelope < RuntimeError
|
||||
end
|
||||
|
||||
# Raised, if the element containing the author is empty.
|
||||
# @deprecated
|
||||
class MissingAuthor < RuntimeError
|
||||
end
|
||||
|
||||
# Raised, if the element containing the header is missing from the XML
|
||||
# @deprecated
|
||||
class MissingHeader < RuntimeError
|
||||
end
|
||||
|
||||
# Raised if the decrypted header has an unexpected XML structure
|
||||
# @deprecated
|
||||
class InvalidHeader < RuntimeError
|
||||
end
|
||||
|
||||
# Raised, if failed to fetch the public key of the sender of the received message
|
||||
class SenderKeyNotFound < RuntimeError
|
||||
end
|
||||
|
||||
# Raised, if the Magic Envelope XML structure is malformed.
|
||||
class InvalidEnvelope < RuntimeError
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ module DiasporaFederation
|
|||
# <me:data type="application/xml">{data}</me:data>
|
||||
# <me:encoding>base64url</me:encoding>
|
||||
# <me:alg>RSA-SHA256</me:alg>
|
||||
# <me:sig>{signature}</me:sig>
|
||||
# <me:sig key_id="{sender}">{signature}</me:sig>
|
||||
# </me:env>
|
||||
#
|
||||
# When parsing the XML of an incoming Magic Envelope {MagicEnvelope.unenvelop}
|
||||
|
|
@ -21,9 +21,6 @@ module DiasporaFederation
|
|||
#
|
||||
# @see http://salmon-protocol.googlecode.com/svn/trunk/draft-panzer-magicsig-01.html
|
||||
class MagicEnvelope
|
||||
# returns the payload (only used for testing purposes)
|
||||
attr_reader :payload
|
||||
|
||||
# encoding used for the payload data
|
||||
ENCODING = "base64url".freeze
|
||||
|
||||
|
|
@ -41,35 +38,32 @@ module DiasporaFederation
|
|||
|
||||
# Creates a new instance of MagicEnvelope.
|
||||
#
|
||||
# @param [OpenSSL::PKey::RSA] rsa_privkey private key used for signing
|
||||
# @param [Entity] payload Entity instance
|
||||
# @raise [ArgumentError] if either argument is not of the right type
|
||||
def initialize(rsa_privkey, payload)
|
||||
raise ArgumentError unless rsa_privkey.instance_of?(OpenSSL::PKey::RSA) &&
|
||||
payload.is_a?(Entity)
|
||||
def initialize(payload)
|
||||
raise ArgumentError unless payload.is_a?(Entity)
|
||||
|
||||
@rsa_privkey = rsa_privkey
|
||||
@payload = XmlPayload.pack(payload).to_xml.strip
|
||||
end
|
||||
|
||||
# Builds the XML structure for the magic envelope, inserts the {ENCODING}
|
||||
# encoded data and signs the envelope using {DIGEST}.
|
||||
#
|
||||
# @param [OpenSSL::PKey::RSA] privkey private key used for signing
|
||||
# @param [String] sender_id diaspora-ID of the sender
|
||||
# @return [Nokogiri::XML::Element] XML root node
|
||||
def envelop
|
||||
env_doc = Nokogiri::XML::DocumentFragment.new(Nokogiri::XML::Document.new)
|
||||
Nokogiri::XML::Element.new("me:env", env_doc).tap do |env|
|
||||
env << Nokogiri::XML::Element.new("me:data", env_doc).tap {|node|
|
||||
node.content = Base64.urlsafe_encode64(@payload)
|
||||
node["type"] = DATA_TYPE
|
||||
def envelop(privkey, sender_id)
|
||||
raise ArgumentError unless privkey.instance_of?(OpenSSL::PKey::RSA) && sender_id.is_a?(String)
|
||||
|
||||
build_xml {|xml|
|
||||
xml["me"].env("xmlns:me" => XMLNS) {
|
||||
xml["me"].data(Base64.urlsafe_encode64(@payload), type: DATA_TYPE)
|
||||
xml["me"].encoding(ENCODING)
|
||||
xml["me"].alg(ALGORITHM)
|
||||
xml["me"].sig(Base64.urlsafe_encode64(sign(privkey)), key_id: Base64.urlsafe_encode64(sender_id))
|
||||
}
|
||||
env << Nokogiri::XML::Element.new("me:encoding", env_doc).tap {|node| node.content = ENCODING }
|
||||
env << Nokogiri::XML::Element.new("me:alg", env_doc).tap {|node| node.content = ALGORITHM }
|
||||
env << Nokogiri::XML::Element.new("me:sig", env_doc).tap {|node|
|
||||
node.content = Base64.urlsafe_encode64(signature)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# Encrypts the payload with a new, random AES cipher and returns the cipher
|
||||
# params that were used.
|
||||
|
|
@ -107,9 +101,8 @@ module DiasporaFederation
|
|||
# @raise [InvalidSignature] if the signature can't be verified
|
||||
# @raise [InvalidEncoding] if the data is wrongly encoded
|
||||
# @raise [InvalidAlgorithm] if the algorithm used doesn't match
|
||||
def self.unenvelop(magic_env, rsa_pubkey, cipher_params=nil)
|
||||
raise ArgumentError unless magic_env.instance_of?(Nokogiri::XML::Element) &&
|
||||
rsa_pubkey.instance_of?(OpenSSL::PKey::RSA)
|
||||
def self.unenvelop(magic_env, rsa_pubkey=nil, cipher_params=nil)
|
||||
raise ArgumentError unless magic_env.instance_of?(Nokogiri::XML::Element)
|
||||
|
||||
raise InvalidEnvelope unless envelope_valid?(magic_env)
|
||||
raise InvalidSignature unless signature_valid?(magic_env, rsa_pubkey)
|
||||
|
|
@ -124,12 +117,24 @@ module DiasporaFederation
|
|||
|
||||
private
|
||||
|
||||
# Builds the xml root node of the magic envelope.
|
||||
#
|
||||
# @yield [xml] Invokes the block with the
|
||||
# {http://www.rubydoc.info/gems/nokogiri/Nokogiri/XML/Builder Nokogiri::XML::Builder}
|
||||
# @return [Nokogiri::XML::Element] XML root node
|
||||
def build_xml
|
||||
Nokogiri::XML::Builder.new(encoding: "UTF-8") {|xml|
|
||||
yield xml
|
||||
}.doc.root
|
||||
end
|
||||
|
||||
# create the signature for all fields according to specification
|
||||
#
|
||||
# @param [OpenSSL::PKey::RSA] privkey private key used for signing
|
||||
# @return [String] the signature
|
||||
def signature
|
||||
def sign(privkey)
|
||||
subject = MagicEnvelope.send(:sig_subject, [@payload, DATA_TYPE, ENCODING, ALGORITHM])
|
||||
@rsa_privkey.sign(DIGEST, subject)
|
||||
privkey.sign(DIGEST, subject)
|
||||
end
|
||||
|
||||
# @param [Nokogiri::XML::Element] env magic envelope XML
|
||||
|
|
@ -137,14 +142,12 @@ module DiasporaFederation
|
|||
(env.instance_of?(Nokogiri::XML::Element) &&
|
||||
env.name == "env" &&
|
||||
!env.at_xpath("me:data").content.empty? &&
|
||||
!env.at_xpath("me:encoding").content.empty? &&
|
||||
!env.at_xpath("me:alg").content.empty? &&
|
||||
!env.at_xpath("me:sig").content.empty?)
|
||||
end
|
||||
private_class_method :envelope_valid?
|
||||
|
||||
# @param [Nokogiri::XML::Element] env magic envelope XML
|
||||
# @param [OpenSSL::PKey::RSA] pubkey public key
|
||||
# @param [OpenSSL::PKey::RSA] pubkey public key or nil
|
||||
# @return [Boolean]
|
||||
def self.signature_valid?(env, pubkey)
|
||||
subject = sig_subject([Base64.urlsafe_decode64(env.at_xpath("me:data").content),
|
||||
|
|
@ -152,11 +155,27 @@ module DiasporaFederation
|
|||
env.at_xpath("me:encoding").content,
|
||||
env.at_xpath("me:alg").content])
|
||||
|
||||
sender_key = pubkey || sender_key(env)
|
||||
|
||||
sig = Base64.urlsafe_decode64(env.at_xpath("me:sig").content)
|
||||
pubkey.verify(DIGEST, sig, subject)
|
||||
sender_key.verify(DIGEST, sig, subject)
|
||||
end
|
||||
private_class_method :signature_valid?
|
||||
|
||||
# reads the +key_id+ from the magic envelope
|
||||
# @param [Nokogiri::XML::Element] env magic envelope XML
|
||||
# @return [OpenSSL::PKey::RSA] sender public key
|
||||
def self.sender_key(env)
|
||||
key_id = env.at_xpath("me:sig")["key_id"]
|
||||
raise InvalidEnvelope, "no key_id" unless key_id # TODO: move to `envelope_valid?`
|
||||
sender = 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
|
||||
private_class_method :sender_key
|
||||
|
||||
# constructs the signature subject.
|
||||
# the given array should consist of the data, data_type (mimetype), encoding
|
||||
# and the algorithm
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ module DiasporaFederation
|
|||
# author_pubkey = however_you_retrieve_the_authors_public_key(slap.author_id)
|
||||
#
|
||||
# entity = slap.entity(author_pubkey)
|
||||
#
|
||||
# @deprecated
|
||||
class Slap
|
||||
# the author of the slap
|
||||
# @overload author_id
|
||||
|
|
@ -90,7 +92,7 @@ module DiasporaFederation
|
|||
xml.author_id(author_id)
|
||||
}
|
||||
|
||||
xml.parent << MagicEnvelope.new(privkey, entity).envelop
|
||||
xml.parent << MagicEnvelope.new(entity).envelop(privkey, author_id)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -3,29 +3,51 @@ module DiasporaFederation
|
|||
routes { DiasporaFederation::Engine.routes }
|
||||
|
||||
describe "POST #public" do
|
||||
context "legacy salmon slap" do
|
||||
it "returns a 422 if no xml is passed" do
|
||||
post :public
|
||||
expect(response.code).to eq("422")
|
||||
end
|
||||
|
||||
it "returns a 422 if no xml is passed with content-type application/x-www-form-urlencoded" do
|
||||
@request.env["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
|
||||
post :public
|
||||
expect(response.code).to eq("422")
|
||||
end
|
||||
|
||||
it "returns a 202 if queued correctly" do
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:queue_public_receive, "<diaspora/>")
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:queue_public_receive, "<diaspora/>", true)
|
||||
|
||||
post :public, xml: "<diaspora/>"
|
||||
expect(response.code).to eq("202")
|
||||
end
|
||||
|
||||
it "unescapes the xml before sending it to the callback" do
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:queue_public_receive, "<diaspora/>")
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:queue_public_receive, "<diaspora/>", true)
|
||||
|
||||
post :public, xml: CGI.escape("<diaspora/>")
|
||||
end
|
||||
end
|
||||
|
||||
context "magic envelope" do
|
||||
before do
|
||||
@request.env["CONTENT_TYPE"] = "application/magic-envelope+xml"
|
||||
end
|
||||
|
||||
it "returns a 202 if queued correctly" do
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(:queue_public_receive, "<me:env/>", false)
|
||||
|
||||
post :public, "<me:env/>"
|
||||
expect(response.code).to eq("202")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST #private" do
|
||||
context "legacy salmon slap" do
|
||||
it "return a 404 if not queued successfully (unknown user guid)" do
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:queue_private_receive, "any-guid", "<diaspora/>"
|
||||
:queue_private_receive, "any-guid", "<diaspora/>", true
|
||||
).and_return(false)
|
||||
|
||||
post :private, guid: "any-guid", xml: "<diaspora/>"
|
||||
|
|
@ -37,9 +59,15 @@ module DiasporaFederation
|
|||
expect(response.code).to eq("422")
|
||||
end
|
||||
|
||||
it "returns a 422 if no xml is passed with content-type application/x-www-form-urlencoded" do
|
||||
@request.env["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
|
||||
post :private, guid: "any-guid"
|
||||
expect(response.code).to eq("422")
|
||||
end
|
||||
|
||||
it "returns a 202 if the callback returned true" do
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:queue_private_receive, "any-guid", "<diaspora/>"
|
||||
:queue_private_receive, "any-guid", "<diaspora/>", true
|
||||
).and_return(true)
|
||||
|
||||
post :private, guid: "any-guid", xml: "<diaspora/>"
|
||||
|
|
@ -48,11 +76,36 @@ module DiasporaFederation
|
|||
|
||||
it "unescapes the xml before sending it to the callback" do
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:queue_private_receive, "any-guid", "<diaspora/>"
|
||||
:queue_private_receive, "any-guid", "<diaspora/>", true
|
||||
).and_return(true)
|
||||
|
||||
post :private, guid: "any-guid", xml: CGI.escape("<diaspora/>")
|
||||
end
|
||||
end
|
||||
|
||||
context "encrypted magic envelope" do
|
||||
before do
|
||||
@request.env["CONTENT_TYPE"] = "application/json"
|
||||
end
|
||||
|
||||
it "return a 404 if not queued successfully (unknown user guid)" do
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:queue_private_receive, "any-guid", "{\"aes_key\": \"key\", \"encrypted_magic_envelope\": \"env\"}", false
|
||||
).and_return(false)
|
||||
|
||||
post :private, "{\"aes_key\": \"key\", \"encrypted_magic_envelope\": \"env\"}", guid: "any-guid"
|
||||
expect(response.code).to eq("404")
|
||||
end
|
||||
|
||||
it "returns a 202 if the callback returned true" do
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:queue_private_receive, "any-guid", "{\"aes_key\": \"key\", \"encrypted_magic_envelope\": \"env\"}", false
|
||||
).and_return(true)
|
||||
|
||||
post :private, "{\"aes_key\": \"key\", \"encrypted_magic_envelope\": \"env\"}", guid: "any-guid"
|
||||
expect(response.code).to eq("202")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
module DiasporaFederation
|
||||
describe Federation::Receiver::Private do
|
||||
describe Federation::Receiver::PrivateSlapReceiver do
|
||||
let(:sender_id) { FactoryGirl.generate(:diaspora_id) }
|
||||
let(:sender_key) { OpenSSL::PKey::RSA.generate(1024) }
|
||||
let(:recipient_key) { OpenSSL::PKey::RSA.generate(1024) }
|
||||
|
|
@ -25,13 +25,13 @@ module DiasporaFederation
|
|||
|
||||
expect {
|
||||
described_class.new(xml, recipient_key).receive!
|
||||
}.to raise_error Federation::SenderKeyNotFound
|
||||
}.to raise_error Salmon::SenderKeyNotFound
|
||||
end
|
||||
|
||||
it "raises when recipient private key is not available" do
|
||||
expect {
|
||||
described_class.new(xml, nil).receive!
|
||||
}.to raise_error Federation::RecipientKeyNotFound
|
||||
}.to raise_error ArgumentError, "no recipient key provided"
|
||||
end
|
||||
|
||||
it "raises when bad xml was supplied" do
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
module DiasporaFederation
|
||||
describe Federation::Receiver::Public do
|
||||
describe Federation::Receiver::PublicSlapReceiver do
|
||||
let(:sender_id) { FactoryGirl.generate(:diaspora_id) }
|
||||
let(:sender_key) { OpenSSL::PKey::RSA.generate(1024) }
|
||||
let(:xml) {
|
||||
|
|
@ -27,7 +27,7 @@ module DiasporaFederation
|
|||
|
||||
expect {
|
||||
described_class.new(xml).receive!
|
||||
}.to raise_error Federation::SenderKeyNotFound
|
||||
}.to raise_error Salmon::SenderKeyNotFound
|
||||
end
|
||||
|
||||
it "raises when bad xml was supplied" do
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
module DiasporaFederation
|
||||
describe Salmon::EncryptedMagicEnvelope do
|
||||
let(:sender_id) { FactoryGirl.generate(:diaspora_id) }
|
||||
let(:sender_key) { OpenSSL::PKey::RSA.generate(512) } # use small key for speedy specs
|
||||
let(:entity) { Entities::TestEntity.new(test: "abcd") }
|
||||
let(:magic_env) { Salmon::MagicEnvelope.new(entity).envelop(sender_key, sender_id) }
|
||||
|
||||
let(:privkey) { OpenSSL::PKey::RSA.generate(1024) } # use small key for speedy specs
|
||||
|
||||
describe ".encrypt" do
|
||||
it "creates the json correctly" do
|
||||
encrypted = Salmon::EncryptedMagicEnvelope.encrypt(magic_env, privkey.public_key)
|
||||
|
||||
expect(JSON.parse(encrypted)).to include("aes_key", "encrypted_magic_envelope")
|
||||
end
|
||||
|
||||
it "encrypts the aes_key correctly" do
|
||||
encrypted = Salmon::EncryptedMagicEnvelope.encrypt(magic_env, privkey.public_key)
|
||||
|
||||
json = JSON.parse(encrypted)
|
||||
aes_key = JSON.parse(privkey.private_decrypt(Base64.decode64(json["aes_key"])))
|
||||
|
||||
expect(aes_key).to include("key", "iv")
|
||||
end
|
||||
|
||||
it "encrypts the magic_envelope correctly" do
|
||||
encrypted = Salmon::EncryptedMagicEnvelope.encrypt(magic_env, privkey.public_key)
|
||||
|
||||
json = JSON.parse(encrypted)
|
||||
aes_key = JSON.parse(privkey.private_decrypt(Base64.decode64(json["aes_key"])))
|
||||
key = Hash[aes_key.map {|k, v| [k, Base64.decode64(v)] }]
|
||||
|
||||
xml = Salmon::AES.decrypt(json["encrypted_magic_envelope"], key["key"], key["iv"])
|
||||
|
||||
expect(Nokogiri::XML::Document.parse(xml).root.to_xml).to eq(magic_env.to_xml)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".decrypt" do
|
||||
let(:encrypted_env) { Salmon::EncryptedMagicEnvelope.encrypt(magic_env, privkey.public_key) }
|
||||
|
||||
it "returns the magic envelope xml" do
|
||||
decrypted = Salmon::EncryptedMagicEnvelope.decrypt(encrypted_env, privkey)
|
||||
|
||||
expect(decrypted.name).to eq("env")
|
||||
|
||||
expect(decrypted.xpath("me:data")).to have(1).item
|
||||
expect(decrypted.xpath("me:encoding")).to have(1).item
|
||||
expect(decrypted.xpath("me:alg")).to have(1).item
|
||||
expect(decrypted.xpath("me:sig")).to have(1).item
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -67,16 +67,23 @@ module DiasporaFederation
|
|||
doc1 = Nokogiri::XML::Document.parse(slap.generate_xml(recipient_key.public_key))
|
||||
enc_header1 = doc1.at_xpath("d:diaspora/d:encrypted_header", ns).content
|
||||
cipher_header1 = JSON.parse(Base64.decode64(enc_header1))
|
||||
key_json1 = recipient_key.private_decrypt(Base64.decode64(cipher_header1["aes_key"]))
|
||||
header_key1 = JSON.parse(recipient_key.private_decrypt(Base64.decode64(cipher_header1["aes_key"])))
|
||||
decrypted_header1 = Salmon::AES.decrypt(cipher_header1["ciphertext"],
|
||||
Base64.decode64(header_key1["key"]),
|
||||
Base64.decode64(header_key1["iv"]))
|
||||
|
||||
recipient2_key = OpenSSL::PKey::RSA.generate(1024)
|
||||
doc2 = Nokogiri::XML::Document.parse(slap.generate_xml(recipient2_key.public_key))
|
||||
enc_header2 = doc2.at_xpath("d:diaspora/d:encrypted_header", ns).content
|
||||
cipher_header2 = JSON.parse(Base64.decode64(enc_header2))
|
||||
key_json2 = recipient2_key.private_decrypt(Base64.decode64(cipher_header2["aes_key"]))
|
||||
header_key2 = JSON.parse(recipient2_key.private_decrypt(Base64.decode64(cipher_header2["aes_key"])))
|
||||
decrypted_header2 = Salmon::AES.decrypt(cipher_header2["ciphertext"],
|
||||
Base64.decode64(header_key2["key"]),
|
||||
Base64.decode64(header_key2["iv"]))
|
||||
|
||||
expect(enc_header1).not_to eq(enc_header2)
|
||||
expect(key_json1).to eq(key_json2)
|
||||
expect(header_key1).not_to eq(header_key2)
|
||||
expect(decrypted_header1).to eq(decrypted_header2)
|
||||
expect(doc1.xpath("d:diaspora/me:env", ns).to_xml).to eq(doc2.xpath("d:diaspora/me:env", ns).to_xml)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,9 @@
|
|||
module DiasporaFederation
|
||||
describe Salmon::MagicEnvelope do
|
||||
let(:payload) { Entities::TestEntity.new(test: "asdf") }
|
||||
let(:sender_id) { FactoryGirl.generate(:diaspora_id) }
|
||||
let(:privkey) { OpenSSL::PKey::RSA.generate(512) } # use small key for speedy specs
|
||||
let(:envelope) { envelop_xml(Salmon::MagicEnvelope.new(privkey, payload)) }
|
||||
|
||||
def envelop_xml(magic_env)
|
||||
Nokogiri::XML::Builder.new(encoding: "UTF-8") {|xml|
|
||||
xml.root("xmlns:me" => Salmon::MagicEnvelope::XMLNS) {
|
||||
xml.parent << magic_env.envelop
|
||||
}
|
||||
}.doc.at_xpath("//me:env")
|
||||
end
|
||||
let(:payload) { Entities::TestEntity.new(test: "asdf") }
|
||||
let(:envelope) { Salmon::MagicEnvelope.new(payload) }
|
||||
|
||||
def sig_subj(env)
|
||||
data = Base64.urlsafe_decode64(env.at_xpath("me:data").content)
|
||||
|
|
@ -21,40 +14,43 @@ module DiasporaFederation
|
|||
[data, type, enc, alg].map {|i| Base64.urlsafe_encode64(i) }.join(".")
|
||||
end
|
||||
|
||||
def re_sign(env, key)
|
||||
new_sig = Base64.urlsafe_encode64(key.sign(OpenSSL::Digest::SHA256.new, sig_subj(env)))
|
||||
env.at_xpath("me:sig").content = new_sig
|
||||
end
|
||||
|
||||
context "sanity" do
|
||||
it "constructs an instance" do
|
||||
expect {
|
||||
Salmon::MagicEnvelope.new(privkey, payload)
|
||||
Salmon::MagicEnvelope.new(payload)
|
||||
}.not_to raise_error
|
||||
end
|
||||
|
||||
it "raises an error if the param types are wrong" do
|
||||
["asdf", 1234, :test, false].each do |val|
|
||||
expect {
|
||||
Salmon::MagicEnvelope.new(val, val)
|
||||
Salmon::MagicEnvelope.new(val)
|
||||
}.to raise_error ArgumentError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#envelop" do
|
||||
subject { Salmon::MagicEnvelope.new(privkey, payload) }
|
||||
context "sanity" do
|
||||
it "raises an error if the param types are wrong" do
|
||||
["asdf", 1234, :test, false].each do |val|
|
||||
expect {
|
||||
envelope.envelop(val, val)
|
||||
}.to raise_error ArgumentError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "should be an instance of Nokogiri::XML::Element" do
|
||||
expect(envelop_xml(subject)).to be_an_instance_of Nokogiri::XML::Element
|
||||
expect(envelope.envelop(privkey, sender_id)).to be_an_instance_of Nokogiri::XML::Element
|
||||
end
|
||||
|
||||
it "returns a magic envelope of correct structure" do
|
||||
env = envelop_xml(subject)
|
||||
expect(env.name).to eq("env")
|
||||
env_xml = envelope.envelop(privkey, sender_id)
|
||||
expect(env_xml.name).to eq("env")
|
||||
|
||||
control = %w(data encoding alg sig)
|
||||
env.children.each do |node|
|
||||
env_xml.children.each do |node|
|
||||
expect(control).to include(node.name)
|
||||
control.reject! {|i| i == node.name }
|
||||
end
|
||||
|
|
@ -62,28 +58,38 @@ module DiasporaFederation
|
|||
expect(control).to be_empty
|
||||
end
|
||||
|
||||
it "signs the payload correctly" do
|
||||
env = envelop_xml(subject)
|
||||
it "adds the sender_id to the signature" do
|
||||
key_id = envelope.envelop(privkey, sender_id).at_xpath("me:sig")["key_id"]
|
||||
|
||||
subj = sig_subj(env)
|
||||
sig = Base64.urlsafe_decode64(env.at_xpath("me:sig").content)
|
||||
expect(Base64.urlsafe_decode64(key_id)).to eq(sender_id)
|
||||
end
|
||||
|
||||
it "adds the data_type" do
|
||||
data_type = envelope.envelop(privkey, sender_id).at_xpath("me:data")["type"]
|
||||
|
||||
expect(data_type).to eq("application/xml")
|
||||
end
|
||||
|
||||
it "signs the payload correctly" do
|
||||
env_xml = envelope.envelop(privkey, sender_id)
|
||||
|
||||
subj = sig_subj(env_xml)
|
||||
sig = Base64.urlsafe_decode64(env_xml.at_xpath("me:sig").content)
|
||||
|
||||
expect(privkey.public_key.verify(OpenSSL::Digest::SHA256.new, sig, subj)).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
describe "#encrypt!" do
|
||||
subject { Salmon::MagicEnvelope.new(privkey, payload) }
|
||||
|
||||
it "encrypts the payload, returning cipher params" do
|
||||
params = subject.encrypt!
|
||||
params = envelope.encrypt!
|
||||
expect(params).to include(:key, :iv)
|
||||
end
|
||||
|
||||
it "actually encrypts the payload" do
|
||||
plain_payload = subject.payload
|
||||
params = subject.encrypt!
|
||||
encrypted_payload = subject.payload
|
||||
plain_payload = envelope.instance_variable_get(:@payload)
|
||||
params = envelope.encrypt!
|
||||
encrypted_payload = envelope.instance_variable_get(:@payload)
|
||||
|
||||
cipher = OpenSSL::Cipher.new(Salmon::AES::CIPHER)
|
||||
cipher.encrypt
|
||||
|
|
@ -98,9 +104,14 @@ module DiasporaFederation
|
|||
|
||||
describe ".unenvelop" do
|
||||
context "sanity" do
|
||||
def re_sign(env, key)
|
||||
new_sig = Base64.urlsafe_encode64(key.sign(OpenSSL::Digest::SHA256.new, sig_subj(env)))
|
||||
env.at_xpath("me:sig").content = new_sig
|
||||
end
|
||||
|
||||
it "works with sane input" do
|
||||
expect {
|
||||
Salmon::MagicEnvelope.unenvelop(envelope, privkey.public_key)
|
||||
Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey, sender_id), privkey.public_key)
|
||||
}.not_to raise_error
|
||||
end
|
||||
|
||||
|
|
@ -121,14 +132,13 @@ module DiasporaFederation
|
|||
it "verifies the signature" do
|
||||
other_key = OpenSSL::PKey::RSA.generate(512)
|
||||
expect {
|
||||
Salmon::MagicEnvelope.unenvelop(envelope, other_key.public_key)
|
||||
Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey, sender_id), other_key.public_key)
|
||||
}.to raise_error Salmon::InvalidSignature
|
||||
end
|
||||
|
||||
it "verifies the encoding" do
|
||||
bad_env = envelop_xml(Salmon::MagicEnvelope.new(privkey, payload))
|
||||
elem = bad_env.at_xpath("me:encoding")
|
||||
elem.content = "invalid_enc"
|
||||
bad_env = envelope.envelop(privkey, sender_id)
|
||||
bad_env.at_xpath("me:encoding").content = "invalid_enc"
|
||||
re_sign(bad_env, privkey)
|
||||
expect {
|
||||
Salmon::MagicEnvelope.unenvelop(bad_env, privkey.public_key)
|
||||
|
|
@ -136,9 +146,8 @@ module DiasporaFederation
|
|||
end
|
||||
|
||||
it "verifies the algorithm" do
|
||||
bad_env = envelop_xml(Salmon::MagicEnvelope.new(privkey, payload))
|
||||
elem = bad_env.at_xpath("me:alg")
|
||||
elem.content = "invalid_alg"
|
||||
bad_env = envelope.envelop(privkey, sender_id)
|
||||
bad_env.at_xpath("me:alg").content = "invalid_alg"
|
||||
re_sign(bad_env, privkey)
|
||||
expect {
|
||||
Salmon::MagicEnvelope.unenvelop(bad_env, privkey.public_key)
|
||||
|
|
@ -147,21 +156,52 @@ module DiasporaFederation
|
|||
end
|
||||
|
||||
it "returns the original entity" do
|
||||
entity = Salmon::MagicEnvelope.unenvelop(envelope, privkey.public_key)
|
||||
entity = Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey, sender_id), privkey.public_key)
|
||||
expect(entity).to be_an_instance_of Entities::TestEntity
|
||||
expect(entity.test).to eq("asdf")
|
||||
end
|
||||
|
||||
it "decrypts on the fly, when cipher params are present" do
|
||||
env = Salmon::MagicEnvelope.new(privkey, payload)
|
||||
params = env.encrypt!
|
||||
params = envelope.encrypt!
|
||||
|
||||
envelope = envelop_xml(env)
|
||||
env_xml = envelope.envelop(privkey, sender_id)
|
||||
|
||||
entity = Salmon::MagicEnvelope.unenvelop(envelope, privkey.public_key, params)
|
||||
entity = Salmon::MagicEnvelope.unenvelop(env_xml, privkey.public_key, params)
|
||||
expect(entity).to be_an_instance_of Entities::TestEntity
|
||||
expect(entity.test).to eq("asdf")
|
||||
end
|
||||
|
||||
context "use key_id from magic envelope" do
|
||||
it "returns the original entity" do
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:fetch_public_key_by_diaspora_id, sender_id
|
||||
).and_return(privkey.public_key)
|
||||
|
||||
entity = Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey, sender_id))
|
||||
expect(entity).to be_an_instance_of Entities::TestEntity
|
||||
expect(entity.test).to eq("asdf")
|
||||
end
|
||||
|
||||
it "raises if the magic envelope has no key_id" do
|
||||
bad_env = envelope.envelop(privkey, sender_id)
|
||||
|
||||
bad_env.at_xpath("me:sig").attributes["key_id"].remove
|
||||
|
||||
expect {
|
||||
Salmon::MagicEnvelope.unenvelop(bad_env)
|
||||
}.to raise_error Salmon::InvalidEnvelope
|
||||
end
|
||||
|
||||
it "raises if the sender key is not found" do
|
||||
expect(DiasporaFederation.callbacks).to receive(:trigger).with(
|
||||
:fetch_public_key_by_diaspora_id, sender_id
|
||||
).and_return(nil)
|
||||
|
||||
expect {
|
||||
Salmon::MagicEnvelope.unenvelop(envelope.envelop(privkey, sender_id))
|
||||
}.to raise_error Salmon::SenderKeyNotFound
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue