diff --git a/lib/diaspora_federation/entities/relayable.rb b/lib/diaspora_federation/entities/relayable.rb index d284799..09f9960 100644 --- a/lib/diaspora_federation/entities/relayable.rb +++ b/lib/diaspora_federation/entities/relayable.rb @@ -32,6 +32,8 @@ module DiasporaFederation end # Generates XML and updates signatures + # @see Entity#to_xml + # @return [Nokogiri::XML::Element] root element containing properties as child elements def to_xml entity_xml.tap do |xml| hash = to_h diff --git a/lib/diaspora_federation/entity.rb b/lib/diaspora_federation/entity.rb index ba294df..7cf648a 100644 --- a/lib/diaspora_federation/entity.rb +++ b/lib/diaspora_federation/entity.rb @@ -76,7 +76,7 @@ module DiasporaFederation # {http://www.rubydoc.info/gems/nokogiri/Nokogiri/XML/Element Nokogiri::XML::Element}s # # @see Nokogiri::XML::Node.to_xml - # @see Salmon::XmlPayload.pack + # @see XmlPayload#pack # # @return [Nokogiri::XML::Element] root element containing properties as child elements def to_xml diff --git a/lib/diaspora_federation/salmon/aes.rb b/lib/diaspora_federation/salmon/aes.rb index 7507cfa..2e7fdc4 100644 --- a/lib/diaspora_federation/salmon/aes.rb +++ b/lib/diaspora_federation/salmon/aes.rb @@ -18,8 +18,11 @@ module DiasporaFederation # @param [String] key AES key # @param [String] iv AES initialization vector # @return [String] base64 encoded ciphertext + # @raise [ArgumentError] if any of the arguments is missing or not the correct type def self.encrypt(data, key, iv) - raise ArgumentError unless data.instance_of?(String) + raise ArgumentError unless data.instance_of?(String) && + key.instance_of?(String) && + iv.instance_of?(String) cipher = OpenSSL::Cipher.new(CIPHER) cipher.encrypt diff --git a/lib/diaspora_federation/salmon/magic_envelope.rb b/lib/diaspora_federation/salmon/magic_envelope.rb index c637c70..d191437 100644 --- a/lib/diaspora_federation/salmon/magic_envelope.rb +++ b/lib/diaspora_federation/salmon/magic_envelope.rb @@ -41,8 +41,8 @@ module DiasporaFederation # Creates a new instance of MagicEnvelope. # - # @param rsa_pkey [OpenSSL::PKey::RSA] private key used for signing - # @param payload [Entity] Entity instance + # @param [OpenSSL::PKey::RSA] rsa_pkey private key used for signing + # @param [Entity] payload Entity instance # @raise [ArgumentError] if either argument is not of the right type def initialize(rsa_pkey, payload) raise ArgumentError unless rsa_pkey.instance_of?(OpenSSL::PKey::RSA) && @@ -55,7 +55,7 @@ module DiasporaFederation # Builds the XML structure for the magic envelope, inserts the {ENCODING} # encoded data and signs the envelope using {DIGEST}. # - # @return [Nokogiri::XML::Element] XML root node + # @param [Nokogiri::XML::Builder] xml Salmon XML builder def envelop(xml) xml["me"].env { xml["me"].data(Base64.urlsafe_encode64(@payload), type: DATA_TYPE) @@ -71,7 +71,8 @@ module DiasporaFederation # This must happen after the MagicEnvelope instance was created and before # {MagicEnvelope#envelop} is called. # - # @see Salmon.aes_encrypt + # @see AES#generate_key_and_iv + # @see AES#encrypt # # @return [Hash] AES key and iv. E.g.: { key: "...", iv: "..." } def encrypt! @@ -85,8 +86,8 @@ module DiasporaFederation # # Does some sanity checking to avoid bad surprises... # - # @see XmlPayload.unpack - # @see Salmon.aes_decrypt + # @see XmlPayload#unpack + # @see AES#decrypt # # @param [Nokogiri::XML::Element] magic_env XML root node of a magic envelope # @param [OpenSSL::PKey::RSA] rsa_pubkey public key to verify the signature @@ -118,6 +119,8 @@ module DiasporaFederation private # create the signature for all fields according to specification + # + # @return [String] the signature def signature subject = self.class.sig_subject([@payload, DATA_TYPE, @@ -126,7 +129,7 @@ module DiasporaFederation @rsa_pkey.sign(DIGEST, subject) end - # @param [Nokogiri::XML::Element] env envelope + # @param [Nokogiri::XML::Element] env magic envelope XML def self.envelope_valid?(env) (env.instance_of?(Nokogiri::XML::Element) && env.name == "env" && @@ -137,8 +140,9 @@ module DiasporaFederation end private_class_method :envelope_valid? - # @param [Nokogiri::XML::Element] env + # @param [Nokogiri::XML::Element] env magic envelope XML # @param [OpenSSL::PKey::RSA] pkey public key + # @return [Boolean] def self.signature_valid?(env, pkey) subject = sig_subject([Base64.urlsafe_decode64(env.at_xpath("me:data").content), env.at_xpath("me:data")["type"], @@ -154,20 +158,28 @@ module DiasporaFederation # the given array should consist of the data, data_type (mimetype), encoding # and the algorithm # @param [Array] data_arr + # @return [String] signature subject def self.sig_subject(data_arr) data_arr.map {|i| Base64.urlsafe_encode64(i) }.join(".") end + # @param [Nokogiri::XML::Element] magic_env magic envelope XML + # @return [Boolean] def self.encoding_valid?(magic_env) magic_env.at_xpath("me:encoding").content == ENCODING end private_class_method :encoding_valid? + # @param [Nokogiri::XML::Element] magic_env magic envelope XML + # @return [Boolean] def self.algorithm_valid?(magic_env) magic_env.at_xpath("me:alg").content == ALGORITHM end private_class_method :algorithm_valid? + # @param [Nokogiri::XML::Element] magic_env magic envelope XML + # @param [Hash] cipher_params hash containing the key and iv + # @return [String] data def self.read_and_decrypt_data(magic_env, cipher_params) data = Base64.urlsafe_decode64(magic_env.at_xpath("me:data").content) data = AES.decrypt(data, cipher_params[:key], cipher_params[:iv]) unless cipher_params.nil? diff --git a/lib/diaspora_federation/salmon/slap.rb b/lib/diaspora_federation/salmon/slap.rb index ac1052d..7a4b1d2 100644 --- a/lib/diaspora_federation/salmon/slap.rb +++ b/lib/diaspora_federation/salmon/slap.rb @@ -93,6 +93,11 @@ module DiasporaFederation end end + # Builds the xml for the Salmon Slap. + # + # @yield [xml] Invokes the block with the + # {http://www.rubydoc.info/gems/nokogiri/Nokogiri/XML/Builder Nokogiri::XML::Builder} + # @return [String] Slap XML def self.build_xml builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml| xml.diaspora("xmlns" => Salmon::XMLNS, "xmlns:me" => MagicEnvelope::XMLNS) { @@ -102,6 +107,9 @@ module DiasporaFederation builder.to_xml end + # Parses the magic envelop from the document. + # + # @param [Nokogiri::XML::Document] doc Salmon XML Document def add_magic_env_from_doc(doc) @magic_envelope = doc.at_xpath("d:diaspora/me:env", Slap::NS).tap do |env| raise MissingMagicEnvelope if env.nil? diff --git a/lib/diaspora_federation/salmon/xml_payload.rb b/lib/diaspora_federation/salmon/xml_payload.rb index 203619c..d5a8d1c 100644 --- a/lib/diaspora_federation/salmon/xml_payload.rb +++ b/lib/diaspora_federation/salmon/xml_payload.rb @@ -82,18 +82,18 @@ module DiasporaFederation # Works recursively on nested Entities and Arrays thereof. # # @param [Class] klass entity class - # @param [Nokogiri::XML::Element] node xml nodes + # @param [Nokogiri::XML::Element] root_node xml nodes # @return [Entity] instance - def self.populate_entity(klass, node) + def self.populate_entity(klass, root_node) # Use all known properties to build the Entity. All other elements are respected # and attached to resulted hash as string. It is intended to build a hash # invariable of an Entity definition, in order to support receiving objects # from the future versions of Diaspora, where new elements may have been added. - data = node.element_children.map { |child| + data = root_node.element_children.map { |child| xml_name = child.name property = klass.class_props.find {|prop| prop[:xml_name].to_s == xml_name } if property - parse_element_from_node(property[:name], property[:type], xml_name, node) + parse_element_from_node(property[:name], property[:type], xml_name, root_node) else [xml_name, child.text] end @@ -105,6 +105,11 @@ module DiasporaFederation end private_class_method :populate_entity + # @param [Symbol] name property name + # @param [Class] type target type to parse + # @param [String] xml_name xml tag to parse + # @param [Nokogiri::XML::Element] node XML node to parse + # @return [Array] parsed data def self.parse_element_from_node(name, type, xml_name, node) if type == String [name, parse_string_from_node(xml_name, node)] @@ -117,26 +122,35 @@ module DiasporaFederation private_class_method :parse_element_from_node # create simple entry in data hash + # + # @param [String] name xml tag to parse + # @param [Nokogiri::XML::Element] root_node XML root_node to parse # @return [String] data - def self.parse_string_from_node(name, node) - n = node.xpath(name.to_s) - n.first.text if n.any? + def self.parse_string_from_node(name, root_node) + node = root_node.xpath(name.to_s) + node.first.text if node.any? end private_class_method :parse_string_from_node # create an entry in the data hash for the nested entity + # + # @param [Class] type target type to parse + # @param [Nokogiri::XML::Element] root_node XML node to parse # @return [Entity] parsed child entity - def self.parse_entity_from_node(type, node) - n = node.xpath(type.entity_name) - populate_entity(type, n.first) if n.any? + def self.parse_entity_from_node(type, root_node) + node = root_node.xpath(type.entity_name) + populate_entity(type, node.first) if node.any? end private_class_method :parse_entity_from_node # collect all nested children of that type and create an array in the data hash + # + # @param [Array] type target type to parse + # @param [Nokogiri::XML::Element] root_node XML node to parse # @return [Array] array with parsed child entities - def self.parse_array_from_node(type, node) - n = node.xpath(type.first.entity_name) - n.map {|child| populate_entity(type.first, child) } + def self.parse_array_from_node(type, root_node) + node = root_node.xpath(type.first.entity_name) + node.map {|child| populate_entity(type.first, child) } end private_class_method :parse_array_from_node end diff --git a/lib/diaspora_federation/signing.rb b/lib/diaspora_federation/signing.rb index 1518195..f7cdc23 100644 --- a/lib/diaspora_federation/signing.rb +++ b/lib/diaspora_federation/signing.rb @@ -2,6 +2,10 @@ 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] key An RSA key # @return [String] A Base64 encoded signature of #signable_string with key def self.sign_with_key(hash, key) @@ -17,6 +21,7 @@ module DiasporaFederation # 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] key An RSA key # @return [Boolean] @@ -40,6 +45,8 @@ module DiasporaFederation private + # @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.match(/signature/) diff --git a/lib/diaspora_federation/test.rb b/lib/diaspora_federation/test.rb index b9402b9..789f53a 100644 --- a/lib/diaspora_federation/test.rb +++ b/lib/diaspora_federation/test.rb @@ -1,7 +1,7 @@ require "diaspora_federation/test/factories" module DiasporaFederation - # This module incapsulates helper functions maybe wanted by a testsuite of a diaspora_federation gem user application + # This module encapsulates helper functions maybe wanted by a testsuite of a diaspora_federation gem user application module Test # Sort hash according to an entity class's property sequence. # This is used for rspec tests in order to generate correct input hash to