Class: DiasporaFederation::Salmon::MagicEnvelope

Inherits:
Object
  • Object
show all
Includes:
Logging
Defined in:
lib/diaspora_federation/salmon/magic_envelope.rb

Overview

Represents a Magic Envelope for diaspora* federation messages

When generating a Magic Envelope, an instance of this class is created and the contents are specified on initialization. Optionally, the payload can be encrypted (MagicEnvelope#encrypt!), before the XML is returned (#envelop).

The generated XML appears like so:

<me:env>
  <me:data type="application/xml">{data}</me:data>
  <me:encoding>base64url</me:encoding>
  <me:alg>RSA-SHA256</me:alg>
  <me:sig key_id="{sender}">{signature}</me:sig>
</me:env>

When parsing the XML of an incoming Magic Envelope MagicEnvelope.unenvelop is used.

Constant Summary collapse

ENCODING =

Encoding used for the payload data

"base64url"
ALGORITHM =

Algorithm used for signing the payload data

"RSA-SHA256"
DATA_TYPE =

Mime type describing the payload data

"application/xml"
DIGEST =

Digest instance used for signing

OpenSSL::Digest.new("SHA256")
XMLNS =

XML namespace url

"http://salmon-protocol.org/ns/magic-env"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Logging

included

Constructor Details

#initialize(payload, sender = nil) ⇒ MagicEnvelope

Creates a new instance of MagicEnvelope.

Parameters:

  • payload (Entity)

    Entity instance

  • sender (String) (defaults to: nil)

    diaspora-ID of the sender

Raises:

  • (ArgumentError)

    if either argument is not of the right type



56
57
58
59
60
61
# File 'lib/diaspora_federation/salmon/magic_envelope.rb', line 56

def initialize(payload, sender=nil)
  raise ArgumentError unless payload.is_a?(Entity)

  @payload = payload
  @sender = sender
end

Instance Attribute Details

#payloadEntity (readonly)

The payload entity of the magic envelope

Returns:



45
46
47
# File 'lib/diaspora_federation/salmon/magic_envelope.rb', line 45

def payload
  @payload
end

#senderString (readonly)

The sender of the magic envelope

Returns:

  • (String)

    diaspora-ID of the sender



49
50
51
# File 'lib/diaspora_federation/salmon/magic_envelope.rb', line 49

def sender
  @sender
end

Class Method Details

.unenvelop(magic_env, sender = nil, cipher_params = nil) ⇒ Entity

Extracts the entity encoded in the magic envelope data, if the signature is valid. If cipher_params is given, also attempts to decrypt the payload first.

Does some sanity checking to avoid bad surprises…

Parameters:

  • magic_env (Nokogiri::XML::Element)

    XML root node of a magic envelope

  • sender (String) (defaults to: nil)

    diaspora* ID of the sender or nil

  • cipher_params (Hash) (defaults to: nil)

    hash containing the key and iv for AES-decrypting previously encrypted data. E.g.: { iv: “…”, key: “…” }

Returns:

  • (Entity)

    reconstructed entity instance

Raises:

  • (ArgumentError)

    if any of the arguments is of invalid type

  • (InvalidEnvelope)

    if the envelope XML structure is malformed

  • (InvalidSignature)

    if the signature can’t be verified

  • (InvalidDataType)

    if the data is missing or unsupported

  • (InvalidEncoding)

    if the data is wrongly encoded or encoding is missing

  • (InvalidAlgorithm)

    if the algorithm is missing or doesn’t match

See Also:



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/diaspora_federation/salmon/magic_envelope.rb', line 102

def self.unenvelop(magic_env, sender=nil, cipher_params=nil)
  raise ArgumentError unless magic_env.instance_of?(Nokogiri::XML::Element)

  validate_envelope(magic_env)
  validate_type(magic_env)
  validate_encoding(magic_env)
  validate_algorithm(magic_env)

  sender ||= sender(magic_env)
  raise InvalidSignature unless signature_valid?(magic_env, sender)

  data = read_and_decrypt_data(magic_env, cipher_params)

  logger.debug "unenvelop message from #{sender}:\n#{data}"

  xml = Nokogiri::XML(data).root
  new(Entity.entity_class(xml.name).from_xml(xml), sender)
end

Instance Method Details

#envelop(privkey) ⇒ Nokogiri::XML::Element

Builds the XML structure for the magic envelope, inserts the ENCODING encoded data and signs the envelope using DIGEST.

Parameters:

  • privkey (OpenSSL::PKey::RSA)

    private key used for signing

Returns:

  • (Nokogiri::XML::Element)

    XML root node

Raises:

  • (ArgumentError)


68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/diaspora_federation/salmon/magic_envelope.rb', line 68

def envelop(privkey)
  raise ArgumentError unless privkey.instance_of?(OpenSSL::PKey::RSA)

  build_xml {|xml|
    xml["me"].env("xmlns:me" => XMLNS) {
      xml["me"].data(Base64.urlsafe_encode64(payload_data), type: DATA_TYPE)
      xml["me"].encoding(ENCODING)
      xml["me"].alg(ALGORITHM)
      xml["me"].sig(Base64.urlsafe_encode64(sign(privkey)), key_id)
    }
  }
end