refactor encrypted slap to reuse it for every recipient
This commit is contained in:
parent
c7f33d7cf4
commit
a0398430ed
12 changed files with 204 additions and 130 deletions
|
|
@ -67,7 +67,7 @@ module DiasporaFederation
|
||||||
# Generates an XML document from the current instance and returns it as string
|
# Generates an XML document from the current instance and returns it as string
|
||||||
# @return [String] XML document
|
# @return [String] XML document
|
||||||
def to_xml
|
def to_xml
|
||||||
builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
|
Nokogiri::XML::Builder.new(encoding: "UTF-8") {|xml|
|
||||||
xml.XRD("xmlns" => XMLNS) {
|
xml.XRD("xmlns" => XMLNS) {
|
||||||
xml.Expires(@expires.strftime(DATETIME_FORMAT)) if @expires.instance_of?(DateTime)
|
xml.Expires(@expires.strftime(DATETIME_FORMAT)) if @expires.instance_of?(DateTime)
|
||||||
|
|
||||||
|
|
@ -77,8 +77,7 @@ module DiasporaFederation
|
||||||
add_properties_to(xml)
|
add_properties_to(xml)
|
||||||
add_links_to(xml)
|
add_links_to(xml)
|
||||||
}
|
}
|
||||||
end
|
}.to_xml
|
||||||
builder.to_xml
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Parse the XRD document from the given string and create a hash containing
|
# Parse the XRD document from the given string and create a hash containing
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ module DiasporaFederation
|
||||||
# resolved on each call
|
# resolved on each call
|
||||||
# @return [Hash] default values
|
# @return [Hash] default values
|
||||||
def default_values
|
def default_values
|
||||||
default_props.each_with_object({}) { |(name, prop), hash|
|
default_props.each_with_object({}) {|(name, prop), hash|
|
||||||
hash[name] = prop.respond_to?(:call) ? prop.call : prop
|
hash[name] = prop.respond_to?(:call) ? prop.call : prop
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
@ -105,7 +105,7 @@ module DiasporaFederation
|
||||||
# @param [Class] type the type to check
|
# @param [Class] type the type to check
|
||||||
# @return [Boolean]
|
# @return [Boolean]
|
||||||
def type_valid?(type)
|
def type_valid?(type)
|
||||||
[type].flatten.all? { |type|
|
[type].flatten.all? {|type|
|
||||||
type.respond_to?(:ancestors) && type.ancestors.include?(Entity)
|
type.respond_to?(:ancestors) && type.ancestors.include?(Entity)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -63,14 +63,22 @@ module DiasporaFederation
|
||||||
#
|
#
|
||||||
# entity = slap.entity(author_pubkey)
|
# entity = slap.entity(author_pubkey)
|
||||||
#
|
#
|
||||||
class EncryptedSlap
|
class EncryptedSlap < Slap
|
||||||
|
# the key and iv if it is an encrypted slap
|
||||||
|
# @param [Hash] value hash containing the key and iv
|
||||||
|
attr_writer :cipher_params
|
||||||
|
|
||||||
|
# the prepared encrypted magic envelope xml
|
||||||
|
# @param [Nokogiri::XML::Element] value magic envelope xml
|
||||||
|
attr_writer :magic_envelope_xml
|
||||||
|
|
||||||
# Creates a Slap instance from the data within the given XML string
|
# Creates a Slap instance from the data within the given XML string
|
||||||
# containing an encrypted payload.
|
# containing an encrypted payload.
|
||||||
#
|
#
|
||||||
# @param [String] slap_xml encrypted Salmon xml
|
# @param [String] slap_xml encrypted Salmon xml
|
||||||
# @param [OpenSSL::PKey::RSA] privkey recipient private_key for decryption
|
# @param [OpenSSL::PKey::RSA] privkey recipient private_key for decryption
|
||||||
#
|
#
|
||||||
# @return [Slap] new Slap instance
|
# @return [EncryptedSlap] new Slap instance
|
||||||
#
|
#
|
||||||
# @raise [ArgumentError] if any of the arguments is of the wrong type
|
# @raise [ArgumentError] if any of the arguments is of the wrong type
|
||||||
# @raise [MissingHeader] if the +encrypted_header+ element is missing in the XML
|
# @raise [MissingHeader] if the +encrypted_header+ element is missing in the XML
|
||||||
|
|
@ -79,7 +87,7 @@ module DiasporaFederation
|
||||||
raise ArgumentError unless slap_xml.instance_of?(String) && privkey.instance_of?(OpenSSL::PKey::RSA)
|
raise ArgumentError unless slap_xml.instance_of?(String) && privkey.instance_of?(OpenSSL::PKey::RSA)
|
||||||
doc = Nokogiri::XML::Document.parse(slap_xml)
|
doc = Nokogiri::XML::Document.parse(slap_xml)
|
||||||
|
|
||||||
Slap.new.tap do |slap|
|
EncryptedSlap.new.tap do |slap|
|
||||||
header_elem = doc.at_xpath("d:diaspora/d:encrypted_header", Slap::NS)
|
header_elem = doc.at_xpath("d:diaspora/d:encrypted_header", Slap::NS)
|
||||||
raise MissingHeader if header_elem.nil?
|
raise MissingHeader if header_elem.nil?
|
||||||
header = header_data(header_elem.content, privkey)
|
header = header_data(header_elem.content, privkey)
|
||||||
|
|
@ -90,29 +98,44 @@ module DiasporaFederation
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Creates an encrypted Salmon Slap and returns the XML string.
|
# Creates an encrypted Salmon Slap.
|
||||||
#
|
#
|
||||||
# @param [String] author_id Diaspora* handle of the author
|
# @param [String] author_id Diaspora* handle of the author
|
||||||
# @param [OpenSSL::PKey::RSA] privkey sender private key for signing the magic envelope
|
# @param [OpenSSL::PKey::RSA] privkey sender private key for signing the magic envelope
|
||||||
# @param [Entity] entity payload
|
# @param [Entity] entity payload
|
||||||
|
# @return [EncryptedSlap] encrypted Slap instance
|
||||||
|
# @raise [ArgumentError] if any of the arguments is of the wrong type
|
||||||
|
def self.prepare(author_id, privkey, entity)
|
||||||
|
raise ArgumentError unless author_id.instance_of?(String) &&
|
||||||
|
privkey.instance_of?(OpenSSL::PKey::RSA) &&
|
||||||
|
entity.is_a?(Entity)
|
||||||
|
|
||||||
|
EncryptedSlap.new.tap do |slap|
|
||||||
|
slap.author_id = author_id
|
||||||
|
|
||||||
|
magic_envelope = MagicEnvelope.new(privkey, entity)
|
||||||
|
slap.cipher_params = magic_envelope.encrypt!
|
||||||
|
slap.magic_envelope_xml = magic_envelope.envelop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates an encrypted Salmon Slap XML string.
|
||||||
|
#
|
||||||
# @param [OpenSSL::PKey::RSA] pubkey recipient public key for encrypting the AES key
|
# @param [OpenSSL::PKey::RSA] pubkey recipient public key for encrypting the AES key
|
||||||
# @return [String] Salmon XML string
|
# @return [String] Salmon XML string
|
||||||
# @raise [ArgumentError] if any of the arguments is of the wrong type
|
# @raise [ArgumentError] if any of the arguments is of the wrong type
|
||||||
def self.generate_xml(author_id, privkey, entity, pubkey)
|
def generate_xml(pubkey)
|
||||||
raise ArgumentError unless author_id.instance_of?(String) &&
|
raise ArgumentError unless pubkey.instance_of?(OpenSSL::PKey::RSA)
|
||||||
privkey.instance_of?(OpenSSL::PKey::RSA) &&
|
|
||||||
entity.is_a?(Entity) &&
|
|
||||||
pubkey.instance_of?(OpenSSL::PKey::RSA)
|
|
||||||
|
|
||||||
Slap.build_xml do |xml|
|
Slap.build_xml do |xml|
|
||||||
magic_envelope = MagicEnvelope.new(privkey, entity)
|
xml.encrypted_header(encrypted_header(author_id, @cipher_params, pubkey))
|
||||||
envelope_key = magic_envelope.encrypt!
|
|
||||||
|
|
||||||
encrypted_header(author_id, envelope_key, pubkey, xml)
|
xml.parent << @magic_envelope_xml
|
||||||
magic_envelope.envelop(xml)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
# decrypts and reads the data from the encrypted XML header
|
# decrypts and reads the data from the encrypted XML header
|
||||||
# @param [String] data base64 encoded, encrypted header data
|
# @param [String] data base64 encoded, encrypted header data
|
||||||
# @param [OpenSSL::PKey::RSA] privkey private key for decryption
|
# @param [OpenSSL::PKey::RSA] privkey private key for decryption
|
||||||
|
|
@ -147,43 +170,33 @@ module DiasporaFederation
|
||||||
# @param [String] author_id diaspora_handle
|
# @param [String] author_id diaspora_handle
|
||||||
# @param [Hash] envelope_key envelope cipher params
|
# @param [Hash] envelope_key envelope cipher params
|
||||||
# @param [OpenSSL::PKey::RSA] pubkey recipient public_key
|
# @param [OpenSSL::PKey::RSA] pubkey recipient public_key
|
||||||
# @param [Nokogiri::XML::Element] xml parent element for inserting in XML document
|
# @return [String] encrypted base64 encoded header
|
||||||
def self.encrypted_header(author_id, envelope_key, pubkey, xml)
|
def encrypted_header(author_id, envelope_key, pubkey)
|
||||||
data = header_xml(author_id, strict_base64_encode(envelope_key))
|
encoded_key = Hash[envelope_key.map {|k, v| [k, Base64.strict_encode64(v)] }]
|
||||||
key = AES.generate_key_and_iv
|
data = header_xml(author_id, encoded_key)
|
||||||
ciphertext = AES.encrypt(data, key[:key], key[:iv])
|
ciphertext = AES.encrypt(data, envelope_key[:key], envelope_key[:iv])
|
||||||
|
|
||||||
json_key = JSON.generate(strict_base64_encode(key))
|
json_key = JSON.generate(encoded_key)
|
||||||
encrypted_key = Base64.strict_encode64(pubkey.public_encrypt(json_key))
|
encrypted_key = Base64.strict_encode64(pubkey.public_encrypt(json_key))
|
||||||
|
|
||||||
json_header = JSON.generate(aes_key: encrypted_key, ciphertext: ciphertext)
|
json_header = JSON.generate(aes_key: encrypted_key, ciphertext: ciphertext)
|
||||||
|
|
||||||
xml.encrypted_header(Base64.strict_encode64(json_header))
|
Base64.strict_encode64(json_header)
|
||||||
end
|
end
|
||||||
private_class_method :encrypted_header
|
|
||||||
|
|
||||||
# generate the header xml string, including the author, aes_key and iv
|
# generate the header xml string, including the author, aes_key and iv
|
||||||
# @param [String] author_id diaspora_handle of the author
|
# @param [String] author_id diaspora_handle of the author
|
||||||
# @param [Hash] envelope_key { key: "...", iv: "..." } (values in base64)
|
# @param [Hash] envelope_key { key: "...", iv: "..." } (values in base64)
|
||||||
# @return [String] header XML string
|
# @return [String] header XML string
|
||||||
def self.header_xml(author_id, envelope_key)
|
def header_xml(author_id, envelope_key)
|
||||||
builder = Nokogiri::XML::Builder.new do |xml|
|
@header_xml ||= Nokogiri::XML::Builder.new(encoding: "UTF-8") {|xml|
|
||||||
xml.decrypted_header {
|
xml.decrypted_header {
|
||||||
xml.iv(envelope_key[:iv])
|
xml.iv(envelope_key[:iv])
|
||||||
xml.aes_key(envelope_key[:key])
|
xml.aes_key(envelope_key[:key])
|
||||||
xml.author_id(author_id)
|
xml.author_id(author_id)
|
||||||
}
|
}
|
||||||
end
|
}.to_xml.strip
|
||||||
builder.to_xml.strip
|
|
||||||
end
|
end
|
||||||
private_class_method :header_xml
|
|
||||||
|
|
||||||
# @param [Hash] hash { key: "...", iv: "..." }
|
|
||||||
# @return [Hash] encoded hash: { key: "...", iv: "..." }
|
|
||||||
def self.strict_base64_encode(hash)
|
|
||||||
Hash[hash.map {|k, v| [k, Base64.strict_encode64(v)] }]
|
|
||||||
end
|
|
||||||
private_class_method :strict_base64_encode
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -55,14 +55,20 @@ module DiasporaFederation
|
||||||
# Builds the XML structure for the magic envelope, inserts the {ENCODING}
|
# Builds the XML structure for the magic envelope, inserts the {ENCODING}
|
||||||
# encoded data and signs the envelope using {DIGEST}.
|
# encoded data and signs the envelope using {DIGEST}.
|
||||||
#
|
#
|
||||||
# @param [Nokogiri::XML::Builder] xml Salmon XML builder
|
# @return [Nokogiri::XML::Element] XML root node
|
||||||
def envelop(xml)
|
def envelop
|
||||||
xml["me"].env {
|
env_doc = Nokogiri::XML::DocumentFragment.new(Nokogiri::XML::Document.new)
|
||||||
xml["me"].data(Base64.urlsafe_encode64(@payload), type: DATA_TYPE)
|
Nokogiri::XML::Element.new("me:env", env_doc).tap do |env|
|
||||||
xml["me"].encoding(ENCODING)
|
env << Nokogiri::XML::Element.new("me:data", env_doc).tap {|node|
|
||||||
xml["me"].alg(ALGORITHM)
|
node.content = Base64.urlsafe_encode64(@payload)
|
||||||
xml["me"].sig(Base64.urlsafe_encode64(signature))
|
node["type"] = DATA_TYPE
|
||||||
}
|
}
|
||||||
|
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
|
end
|
||||||
|
|
||||||
# Encrypts the payload with a new, random AES cipher and returns the cipher
|
# Encrypts the payload with a new, random AES cipher and returns the cipher
|
||||||
|
|
@ -102,8 +108,8 @@ module DiasporaFederation
|
||||||
# @raise [InvalidEncoding] if the data is wrongly encoded
|
# @raise [InvalidEncoding] if the data is wrongly encoded
|
||||||
# @raise [InvalidAlgorithm] if the algorithm used doesn't match
|
# @raise [InvalidAlgorithm] if the algorithm used doesn't match
|
||||||
def self.unenvelop(magic_env, rsa_pubkey, cipher_params=nil)
|
def self.unenvelop(magic_env, rsa_pubkey, cipher_params=nil)
|
||||||
raise ArgumentError unless rsa_pubkey.instance_of?(OpenSSL::PKey::RSA) &&
|
raise ArgumentError unless magic_env.instance_of?(Nokogiri::XML::Element) &&
|
||||||
magic_env.instance_of?(Nokogiri::XML::Element)
|
rsa_pubkey.instance_of?(OpenSSL::PKey::RSA)
|
||||||
|
|
||||||
raise InvalidEnvelope unless envelope_valid?(magic_env)
|
raise InvalidEnvelope unless envelope_valid?(magic_env)
|
||||||
raise InvalidSignature unless signature_valid?(magic_env, rsa_pubkey)
|
raise InvalidSignature unless signature_valid?(magic_env, rsa_pubkey)
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,6 @@ module DiasporaFederation
|
||||||
# @param [String] the author diaspora id
|
# @param [String] the author diaspora id
|
||||||
attr_accessor :author_id
|
attr_accessor :author_id
|
||||||
|
|
||||||
# the key and iv if it is an encrypted slap
|
|
||||||
# @param [Hash] value hash containing the key and iv
|
|
||||||
attr_writer :cipher_params
|
|
||||||
|
|
||||||
# Namespaces
|
# Namespaces
|
||||||
NS = {d: Salmon::XMLNS, me: MagicEnvelope::XMLNS}
|
NS = {d: Salmon::XMLNS, me: MagicEnvelope::XMLNS}
|
||||||
|
|
||||||
|
|
@ -94,7 +90,7 @@ module DiasporaFederation
|
||||||
xml.author_id(author_id)
|
xml.author_id(author_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
MagicEnvelope.new(privkey, entity).envelop(xml)
|
xml.parent << MagicEnvelope.new(privkey, entity).envelop
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -104,12 +100,11 @@ module DiasporaFederation
|
||||||
# {http://www.rubydoc.info/gems/nokogiri/Nokogiri/XML/Builder Nokogiri::XML::Builder}
|
# {http://www.rubydoc.info/gems/nokogiri/Nokogiri/XML/Builder Nokogiri::XML::Builder}
|
||||||
# @return [String] Slap XML
|
# @return [String] Slap XML
|
||||||
def self.build_xml
|
def self.build_xml
|
||||||
builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
|
Nokogiri::XML::Builder.new(encoding: "UTF-8") {|xml|
|
||||||
xml.diaspora("xmlns" => Salmon::XMLNS, "xmlns:me" => MagicEnvelope::XMLNS) {
|
xml.diaspora("xmlns" => Salmon::XMLNS, "xmlns:me" => MagicEnvelope::XMLNS) {
|
||||||
yield xml
|
yield xml
|
||||||
}
|
}
|
||||||
end
|
}.to_xml
|
||||||
builder.to_xml
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Parses the magic envelop from the document.
|
# Parses the magic envelop from the document.
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ module DiasporaFederation
|
||||||
# and attached to resulted hash as string. It is intended to build a hash
|
# 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
|
# invariable of an Entity definition, in order to support receiving objects
|
||||||
# from the future versions of Diaspora, where new elements may have been added.
|
# from the future versions of Diaspora, where new elements may have been added.
|
||||||
data = Hash[root_node.element_children.map { |child|
|
data = Hash[root_node.element_children.map {|child|
|
||||||
xml_name = child.name
|
xml_name = child.name
|
||||||
property = klass.class_props.find {|prop| prop[:xml_name].to_s == xml_name }
|
property = klass.class_props.find {|prop| prop[:xml_name].to_s == xml_name }
|
||||||
if property
|
if property
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ module DiasporaFederation
|
||||||
# @param [Hash] hash data to sign
|
# @param [Hash] hash data to sign
|
||||||
# @return [String] signature data string
|
# @return [String] signature data string
|
||||||
def self.signable_string(hash)
|
def self.signable_string(hash)
|
||||||
hash.map { |name, value|
|
hash.map {|name, value|
|
||||||
value.to_s unless name.match(/signature/)
|
value.to_s unless name.match(/signature/)
|
||||||
}.compact.join(";")
|
}.compact.join(";")
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ module DiasporaFederation
|
||||||
# @param [Entity.Class] klass entity type to sort according to
|
# @param [Entity.Class] klass entity type to sort according to
|
||||||
# @return [Hash] sorted hash
|
# @return [Hash] sorted hash
|
||||||
def self.sort_hash(data, klass)
|
def self.sort_hash(data, klass)
|
||||||
Hash[klass.class_props.map { |prop|
|
Hash[klass.class_props.map {|prop|
|
||||||
[prop[:name], data[prop[:name]]] unless data[prop[:name]].nil?
|
[prop[:name], data[prop[:name]]] unless data[prop[:name]].nil?
|
||||||
}.compact]
|
}.compact]
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,8 @@ module DiasporaFederation
|
||||||
let(:sender_key) { OpenSSL::PKey::RSA.generate(1024) }
|
let(:sender_key) { OpenSSL::PKey::RSA.generate(1024) }
|
||||||
let(:recipient_key) { OpenSSL::PKey::RSA.generate(1024) }
|
let(:recipient_key) { OpenSSL::PKey::RSA.generate(1024) }
|
||||||
let(:xml) {
|
let(:xml) {
|
||||||
DiasporaFederation::Salmon::EncryptedSlap.generate_xml(
|
DiasporaFederation::Salmon::EncryptedSlap.prepare(sender_id, sender_key, FactoryGirl.build(:request_entity))
|
||||||
sender_id,
|
.generate_xml(recipient_key)
|
||||||
sender_key,
|
|
||||||
FactoryGirl.build(:request_entity),
|
|
||||||
recipient_key
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it "calls save_entity_after_receive if everything is fine" do
|
it "calls save_entity_after_receive if everything is fine" do
|
||||||
|
|
|
||||||
|
|
@ -4,72 +4,122 @@ module DiasporaFederation
|
||||||
let(:privkey) { OpenSSL::PKey::RSA.generate(512) } # use small key for speedy specs
|
let(:privkey) { OpenSSL::PKey::RSA.generate(512) } # use small key for speedy specs
|
||||||
let(:recipient_key) { OpenSSL::PKey::RSA.generate(1024) } # use small key for speedy specs
|
let(:recipient_key) { OpenSSL::PKey::RSA.generate(1024) } # use small key for speedy specs
|
||||||
let(:entity) { Entities::TestEntity.new(test: "qwertzuiop") }
|
let(:entity) { Entities::TestEntity.new(test: "qwertzuiop") }
|
||||||
let(:slap_xml) { Salmon::EncryptedSlap.generate_xml(author_id, privkey, entity, recipient_key.public_key) }
|
let(:slap_xml) { Salmon::EncryptedSlap.prepare(author_id, privkey, entity).generate_xml(recipient_key.public_key) }
|
||||||
let(:ns) { {d: Salmon::XMLNS, me: Salmon::MagicEnvelope::XMLNS} }
|
|
||||||
|
|
||||||
describe ".generate_xml" do
|
context "generate" do
|
||||||
context "sanity" do
|
describe ".prepare" do
|
||||||
it "accepts correct params" do
|
context "sanity" do
|
||||||
expect {
|
it "raises an error when the author_id is the wrong type" do
|
||||||
Salmon::EncryptedSlap.generate_xml(author_id, privkey, entity, recipient_key.public_key)
|
[1234, true, :symbol, entity, privkey].each do |val|
|
||||||
}.not_to raise_error
|
expect {
|
||||||
end
|
Salmon::EncryptedSlap.prepare(val, privkey, entity)
|
||||||
|
}.to raise_error ArgumentError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it "raises an error when the params are the wrong type" do
|
it "raises an error when the privkey is the wrong type" do
|
||||||
["asdf", 1234, true, :symbol, entity, privkey].each do |val|
|
["asdf", 1234, true, :symbol, entity].each do |val|
|
||||||
expect {
|
expect {
|
||||||
Salmon::EncryptedSlap.generate_xml(val, val, val, val)
|
Salmon::EncryptedSlap.prepare(author_id, val, entity)
|
||||||
}.to raise_error ArgumentError
|
}.to raise_error ArgumentError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error when the entity is the wrong type" do
|
||||||
|
["asdf", 1234, true, :symbol, privkey].each do |val|
|
||||||
|
expect {
|
||||||
|
Salmon::EncryptedSlap.prepare(author_id, privkey, val)
|
||||||
|
}.to raise_error ArgumentError
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "generates valid xml" do
|
describe ".generate_xml" do
|
||||||
doc = Nokogiri::XML::Document.parse(slap_xml)
|
let(:ns) { {d: Salmon::XMLNS, me: Salmon::MagicEnvelope::XMLNS} }
|
||||||
expect(doc.root.name).to eq("diaspora")
|
|
||||||
expect(doc.at_xpath("d:diaspora/d:encrypted_header", ns).content).to_not be_empty
|
|
||||||
expect(doc.xpath("d:diaspora/me:env", ns)).to have(1).item
|
|
||||||
end
|
|
||||||
|
|
||||||
context "header" do
|
context "sanity" do
|
||||||
subject {
|
it "accepts correct params" do
|
||||||
|
expect {
|
||||||
|
Salmon::EncryptedSlap.prepare(author_id, privkey, entity).generate_xml(recipient_key.public_key)
|
||||||
|
}.not_to raise_error
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error when the params are the wrong type" do
|
||||||
|
["asdf", 1234, true, :symbol, entity].each do |val|
|
||||||
|
expect {
|
||||||
|
Salmon::EncryptedSlap.prepare(author_id, privkey, entity).generate_xml(val)
|
||||||
|
}.to raise_error ArgumentError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "generates valid xml" do
|
||||||
doc = Nokogiri::XML::Document.parse(slap_xml)
|
doc = Nokogiri::XML::Document.parse(slap_xml)
|
||||||
doc.at_xpath("d:diaspora/d:encrypted_header", ns).content
|
expect(doc.root.name).to eq("diaspora")
|
||||||
}
|
expect(doc.at_xpath("d:diaspora/d:encrypted_header", ns).content).to_not be_empty
|
||||||
let(:cipher_header) { JSON.parse(Base64.decode64(subject)) }
|
expect(doc.xpath("d:diaspora/me:env", ns)).to have(1).item
|
||||||
let(:header_key) {
|
|
||||||
JSON.parse(recipient_key.private_decrypt(Base64.decode64(cipher_header["aes_key"])))
|
|
||||||
}
|
|
||||||
|
|
||||||
it "encodes the header correctly" do
|
|
||||||
json_header = {}
|
|
||||||
expect {
|
|
||||||
json_header = JSON.parse(Base64.decode64(subject))
|
|
||||||
}.not_to raise_error
|
|
||||||
expect(json_header).to include("aes_key", "ciphertext")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "encrypts the public_key encrypted header correctly" do
|
it "can generate xml for two people" do
|
||||||
key = {}
|
slap = Salmon::EncryptedSlap.prepare(author_id, privkey, entity)
|
||||||
expect {
|
|
||||||
key = JSON.parse(recipient_key.private_decrypt(Base64.decode64(cipher_header["aes_key"])))
|
doc1 = Nokogiri::XML::Document.parse(slap.generate_xml(recipient_key.public_key))
|
||||||
}.not_to raise_error
|
enc_header1 = doc1.at_xpath("d:diaspora/d:encrypted_header", ns).content
|
||||||
expect(key).to include("key", "iv")
|
cipher_header1 = JSON.parse(Base64.decode64(enc_header1))
|
||||||
|
key_json1 = recipient_key.private_decrypt(Base64.decode64(cipher_header1["aes_key"]))
|
||||||
|
|
||||||
|
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"]))
|
||||||
|
|
||||||
|
expect(enc_header1).not_to eq(enc_header2)
|
||||||
|
expect(key_json1).to eq(key_json2)
|
||||||
|
expect(doc1.xpath("d:diaspora/me:env", ns).to_xml).to eq(doc2.xpath("d:diaspora/me:env", ns).to_xml)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "encrypts the aes encrypted header correctly" do
|
context "header" do
|
||||||
header = ""
|
subject {
|
||||||
expect {
|
doc = Nokogiri::XML::Document.parse(slap_xml)
|
||||||
header = Salmon::AES.decrypt(cipher_header["ciphertext"],
|
doc.at_xpath("d:diaspora/d:encrypted_header", ns).content
|
||||||
Base64.decode64(header_key["key"]),
|
}
|
||||||
Base64.decode64(header_key["iv"]))
|
let(:cipher_header) { JSON.parse(Base64.decode64(subject)) }
|
||||||
}.not_to raise_error
|
let(:header_key) {
|
||||||
header_doc = Nokogiri::XML::Document.parse(header)
|
JSON.parse(recipient_key.private_decrypt(Base64.decode64(cipher_header["aes_key"])))
|
||||||
expect(header_doc.root.name).to eq("decrypted_header")
|
}
|
||||||
expect(header_doc.xpath("//iv")).to have(1).item
|
|
||||||
expect(header_doc.xpath("//aes_key")).to have(1).item
|
it "encodes the header correctly" do
|
||||||
expect(header_doc.xpath("//author_id")).to have(1).item
|
json_header = {}
|
||||||
expect(header_doc.at_xpath("//author_id").content).to eq(author_id)
|
expect {
|
||||||
|
json_header = JSON.parse(Base64.decode64(subject))
|
||||||
|
}.not_to raise_error
|
||||||
|
expect(json_header).to include("aes_key", "ciphertext")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "encrypts the public_key encrypted header correctly" do
|
||||||
|
key = {}
|
||||||
|
expect {
|
||||||
|
key = JSON.parse(recipient_key.private_decrypt(Base64.decode64(cipher_header["aes_key"])))
|
||||||
|
}.not_to raise_error
|
||||||
|
expect(key).to include("key", "iv")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "encrypts the aes encrypted header correctly" do
|
||||||
|
header = ""
|
||||||
|
expect {
|
||||||
|
header = Salmon::AES.decrypt(cipher_header["ciphertext"],
|
||||||
|
Base64.decode64(header_key["key"]),
|
||||||
|
Base64.decode64(header_key["iv"]))
|
||||||
|
}.not_to raise_error
|
||||||
|
header_doc = Nokogiri::XML::Document.parse(header)
|
||||||
|
expect(header_doc.root.name).to eq("decrypted_header")
|
||||||
|
expect(header_doc.xpath("//iv")).to have(1).item
|
||||||
|
expect(header_doc.xpath("//aes_key")).to have(1).item
|
||||||
|
expect(header_doc.xpath("//author_id")).to have(1).item
|
||||||
|
expect(header_doc.at_xpath("//author_id").content).to eq(author_id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,11 @@ module DiasporaFederation
|
||||||
let(:envelope) { envelop_xml(Salmon::MagicEnvelope.new(privkey, payload)) }
|
let(:envelope) { envelop_xml(Salmon::MagicEnvelope.new(privkey, payload)) }
|
||||||
|
|
||||||
def envelop_xml(magic_env)
|
def envelop_xml(magic_env)
|
||||||
builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
|
Nokogiri::XML::Builder.new(encoding: "UTF-8") {|xml|
|
||||||
xml.root("xmlns:me" => Salmon::MagicEnvelope::XMLNS) {
|
xml.root("xmlns:me" => Salmon::MagicEnvelope::XMLNS) {
|
||||||
magic_env.envelop(xml)
|
xml.parent << magic_env.envelop
|
||||||
}
|
}
|
||||||
end
|
}.doc.at_xpath("//me:env")
|
||||||
builder.doc.at_xpath("//me:env")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def sig_subj(env)
|
def sig_subj(env)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ module DiasporaFederation
|
||||||
let(:author_id) { "test_user@pod.somedomain.tld" }
|
let(:author_id) { "test_user@pod.somedomain.tld" }
|
||||||
let(:privkey) { OpenSSL::PKey::RSA.generate(512) } # use small key for speedy specs
|
let(:privkey) { OpenSSL::PKey::RSA.generate(512) } # use small key for speedy specs
|
||||||
let(:entity) { Entities::TestEntity.new(test: "qwertzuiop") }
|
let(:entity) { Entities::TestEntity.new(test: "qwertzuiop") }
|
||||||
let(:slap) { Salmon::Slap.generate_xml(author_id, privkey, entity) }
|
let(:slap_xml) { Salmon::Slap.generate_xml(author_id, privkey, entity) }
|
||||||
|
|
||||||
describe ".generate_xml" do
|
describe ".generate_xml" do
|
||||||
context "sanity" do
|
context "sanity" do
|
||||||
|
|
@ -13,10 +13,26 @@ module DiasporaFederation
|
||||||
}.not_to raise_error
|
}.not_to raise_error
|
||||||
end
|
end
|
||||||
|
|
||||||
it "raises an error when the params are the wrong type" do
|
it "raises an error when the author_id is the wrong type" do
|
||||||
["asdf", 1234, true, :symbol, entity, privkey].each do |val|
|
[1234, true, :symbol, entity, privkey].each do |val|
|
||||||
expect {
|
expect {
|
||||||
Salmon::Slap.generate_xml(val, val, val)
|
Salmon::Slap.generate_xml(val, privkey, entity)
|
||||||
|
}.to raise_error ArgumentError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error when the privkey is the wrong type" do
|
||||||
|
["asdf", 1234, true, :symbol, entity].each do |val|
|
||||||
|
expect {
|
||||||
|
Salmon::Slap.generate_xml(author_id, val, entity)
|
||||||
|
}.to raise_error ArgumentError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error when the entity is the wrong type" do
|
||||||
|
["asdf", 1234, true, :symbol, privkey].each do |val|
|
||||||
|
expect {
|
||||||
|
Salmon::Slap.generate_xml(author_id, privkey, val)
|
||||||
}.to raise_error ArgumentError
|
}.to raise_error ArgumentError
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -24,7 +40,7 @@ module DiasporaFederation
|
||||||
|
|
||||||
it "generates valid xml" do
|
it "generates valid xml" do
|
||||||
ns = {d: Salmon::XMLNS, me: Salmon::MagicEnvelope::XMLNS}
|
ns = {d: Salmon::XMLNS, me: Salmon::MagicEnvelope::XMLNS}
|
||||||
doc = Nokogiri::XML::Document.parse(slap)
|
doc = Nokogiri::XML::Document.parse(slap_xml)
|
||||||
expect(doc.root.name).to eq("diaspora")
|
expect(doc.root.name).to eq("diaspora")
|
||||||
expect(doc.at_xpath("d:diaspora/d:header/d:author_id", ns).content).to eq(author_id)
|
expect(doc.at_xpath("d:diaspora/d:header/d:author_id", ns).content).to eq(author_id)
|
||||||
expect(doc.xpath("d:diaspora/me:env", ns)).to have(1).item
|
expect(doc.xpath("d:diaspora/me:env", ns)).to have(1).item
|
||||||
|
|
@ -35,7 +51,7 @@ module DiasporaFederation
|
||||||
context "sanity" do
|
context "sanity" do
|
||||||
it "accepts salmon xml as param" do
|
it "accepts salmon xml as param" do
|
||||||
expect {
|
expect {
|
||||||
Salmon::Slap.from_xml(slap)
|
Salmon::Slap.from_xml(slap_xml)
|
||||||
}.not_to raise_error
|
}.not_to raise_error
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -75,7 +91,7 @@ XML
|
||||||
|
|
||||||
context "generated instance" do
|
context "generated instance" do
|
||||||
it_behaves_like "a Slap instance" do
|
it_behaves_like "a Slap instance" do
|
||||||
subject { Salmon::Slap.from_xml(slap) }
|
subject { Salmon::Slap.from_xml(slap_xml) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue