diaspora_federation 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -1
- data/lib/diaspora_federation.rb +66 -1
- data/lib/diaspora_federation/discovery/h_card.rb +2 -3
- data/lib/diaspora_federation/discovery/web_finger.rb +3 -6
- data/lib/diaspora_federation/entities.rb +18 -0
- data/lib/diaspora_federation/entities/account_deletion.rb +14 -0
- data/lib/diaspora_federation/entities/comment.rb +26 -0
- data/lib/diaspora_federation/entities/conversation.rb +38 -0
- data/lib/diaspora_federation/entities/like.rb +35 -0
- data/lib/diaspora_federation/entities/location.rb +23 -0
- data/lib/diaspora_federation/entities/message.rb +38 -0
- data/lib/diaspora_federation/entities/participation.rb +28 -0
- data/lib/diaspora_federation/entities/person.rb +6 -3
- data/lib/diaspora_federation/entities/photo.rb +59 -0
- data/lib/diaspora_federation/entities/poll.rb +24 -0
- data/lib/diaspora_federation/entities/poll_answer.rb +19 -0
- data/lib/diaspora_federation/entities/poll_participation.rb +28 -0
- data/lib/diaspora_federation/entities/profile.rb +10 -8
- data/lib/diaspora_federation/entities/relayable.rb +101 -0
- data/lib/diaspora_federation/entities/relayable_retraction.rb +95 -0
- data/lib/diaspora_federation/entities/request.rb +21 -0
- data/lib/diaspora_federation/entities/reshare.rb +49 -0
- data/lib/diaspora_federation/entities/retraction.rb +24 -0
- data/lib/diaspora_federation/entities/signed_retraction.rb +66 -0
- data/lib/diaspora_federation/entities/status_message.rb +55 -0
- data/lib/diaspora_federation/entity.rb +5 -6
- data/lib/diaspora_federation/fetcher.rb +1 -2
- data/lib/diaspora_federation/properties_dsl.rb +18 -8
- data/lib/diaspora_federation/salmon.rb +17 -0
- data/lib/diaspora_federation/salmon/aes.rb +58 -0
- data/lib/diaspora_federation/salmon/encrypted_slap.rb +187 -0
- data/lib/diaspora_federation/salmon/exceptions.rb +50 -0
- data/lib/diaspora_federation/salmon/magic_envelope.rb +191 -0
- data/lib/diaspora_federation/salmon/slap.rb +128 -0
- data/lib/diaspora_federation/salmon/xml_payload.rb +158 -0
- data/lib/diaspora_federation/signing.rb +56 -0
- data/lib/diaspora_federation/validators.rb +20 -0
- data/lib/diaspora_federation/validators/account_deletion_validator.rb +10 -0
- data/lib/diaspora_federation/validators/comment_validator.rb +17 -0
- data/lib/diaspora_federation/validators/conversation_validator.rb +14 -0
- data/lib/diaspora_federation/validators/like_validator.rb +14 -0
- data/lib/diaspora_federation/validators/location_validator.rb +11 -0
- data/lib/diaspora_federation/validators/message_validator.rb +16 -0
- data/lib/diaspora_federation/validators/participation_validator.rb +16 -0
- data/lib/diaspora_federation/validators/photo_validator.rb +24 -0
- data/lib/diaspora_federation/validators/poll_answer_validator.rb +11 -0
- data/lib/diaspora_federation/validators/poll_participation_validator.rb +16 -0
- data/lib/diaspora_federation/validators/poll_validator.rb +11 -0
- data/lib/diaspora_federation/validators/relayable_retraction_validator.rb +15 -0
- data/lib/diaspora_federation/validators/relayable_validator.rb +14 -0
- data/lib/diaspora_federation/validators/request_validator.rb +11 -0
- data/lib/diaspora_federation/validators/reshare_validator.rb +18 -0
- data/lib/diaspora_federation/validators/retraction_validator.rb +14 -0
- data/lib/diaspora_federation/validators/rules/diaspora_id_count.rb +37 -0
- data/lib/diaspora_federation/validators/signed_retraction_validator.rb +15 -0
- data/lib/diaspora_federation/validators/status_message_validator.rb +14 -0
- data/lib/diaspora_federation/version.rb +1 -1
- metadata +49 -4
@@ -0,0 +1,55 @@
|
|
1
|
+
module DiasporaFederation
|
2
|
+
module Entities
|
3
|
+
# this entity represents a status message sent by a user
|
4
|
+
#
|
5
|
+
# @see Validators::StatusMessageValidator
|
6
|
+
class StatusMessage < Entity
|
7
|
+
# @!attribute [r] raw_message
|
8
|
+
# text of the status message composed by the user
|
9
|
+
# @return [String] text of the status message
|
10
|
+
property :raw_message
|
11
|
+
|
12
|
+
# @!attribute [r] photos
|
13
|
+
# optional photos attached to the status message
|
14
|
+
# @return [[Entities::Photo]] photos
|
15
|
+
entity :photos, [Entities::Photo], default: []
|
16
|
+
|
17
|
+
# @!attribute [r] location
|
18
|
+
# optional location attached to the status message
|
19
|
+
# @return [Entities::Location] location
|
20
|
+
entity :location, Entities::Location, default: nil
|
21
|
+
|
22
|
+
# @!attribute [r] poll
|
23
|
+
# optional poll attached to the status message
|
24
|
+
# @return [Entities::Poll] poll
|
25
|
+
entity :poll, Entities::Poll, default: nil
|
26
|
+
|
27
|
+
# @!attribute [r] guid
|
28
|
+
# a random string of at least 16 chars.
|
29
|
+
# @see Validation::Rule::Guid
|
30
|
+
# @return [String] status message guid
|
31
|
+
property :guid
|
32
|
+
|
33
|
+
# @!attribute [r] diaspora_id
|
34
|
+
# The diaspora ID of the person who posts the status message
|
35
|
+
# @see Person#diaspora_id
|
36
|
+
# @return [String] diaspora ID
|
37
|
+
property :diaspora_id, xml_name: :diaspora_handle
|
38
|
+
|
39
|
+
# @!attribute [r] public
|
40
|
+
# shows whether the status message is visible to everyone or only to some aspects
|
41
|
+
# @return [Boolean] is it public
|
42
|
+
property :public, default: false
|
43
|
+
|
44
|
+
# @!attribute [r] created_at
|
45
|
+
# status message entity creation time
|
46
|
+
# @return [Time] creation time
|
47
|
+
property :created_at, default: -> { Time.now.utc }
|
48
|
+
|
49
|
+
# @!attribute [r] provider_display_name
|
50
|
+
# a string that describes a means by which a user has posted the status message
|
51
|
+
# @return [String] provider display name
|
52
|
+
property :provider_display_name, default: nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -6,7 +6,7 @@ module DiasporaFederation
|
|
6
6
|
#
|
7
7
|
# Any entity also provides the means to serialize itself and all nested
|
8
8
|
# entities to XML (for deserialization from XML to +Entity+ instances, see
|
9
|
-
# {XmlPayload}).
|
9
|
+
# {Salmon::XmlPayload}).
|
10
10
|
#
|
11
11
|
# @abstract Subclass and specify properties to implement various entities.
|
12
12
|
#
|
@@ -76,19 +76,18 @@ module DiasporaFederation
|
|
76
76
|
# {http://www.rubydoc.info/gems/nokogiri/Nokogiri/XML/Element Nokogiri::XML::Element}s
|
77
77
|
#
|
78
78
|
# @see Nokogiri::XML::Node.to_xml
|
79
|
-
# @see XmlPayload
|
79
|
+
# @see XmlPayload#pack
|
80
80
|
#
|
81
81
|
# @return [Nokogiri::XML::Element] root element containing properties as child elements
|
82
82
|
def to_xml
|
83
83
|
entity_xml
|
84
84
|
end
|
85
85
|
|
86
|
-
#
|
86
|
+
# Makes an underscored, lowercase form of the class name
|
87
|
+
# @return [String] entity name
|
87
88
|
def self.entity_name
|
88
89
|
name.rpartition("::").last.tap do |word|
|
89
|
-
word.gsub!(/(
|
90
|
-
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
91
|
-
word.tr!("-", "_")
|
90
|
+
word.gsub!(/(.)([A-Z])/, '\1_\2')
|
92
91
|
word.downcase!
|
93
92
|
end
|
94
93
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require "faraday"
|
2
2
|
require "faraday_middleware/response/follow_redirects"
|
3
|
-
require "typhoeus/adapters/faraday"
|
4
3
|
|
5
4
|
module DiasporaFederation
|
6
5
|
# A wrapper for {https://github.com/lostisland/faraday Faraday} used for
|
@@ -32,7 +31,7 @@ module DiasporaFederation
|
|
32
31
|
|
33
32
|
@connection = Faraday::Connection.new(options) do |builder|
|
34
33
|
builder.use FaradayMiddleware::FollowRedirects, limit: 4
|
35
|
-
builder.adapter
|
34
|
+
builder.adapter Faraday.default_adapter
|
36
35
|
end
|
37
36
|
|
38
37
|
@connection.headers["User-Agent"] = "DiasporaFederation/#{DiasporaFederation::VERSION}"
|
@@ -68,17 +68,27 @@ module DiasporaFederation
|
|
68
68
|
|
69
69
|
private
|
70
70
|
|
71
|
+
def determine_xml_name(name, type, opts={})
|
72
|
+
raise ArgumentError, "xml_name is not supported for nested entities" if type != String && opts.has_key?(:xml_name)
|
73
|
+
|
74
|
+
if type == String
|
75
|
+
if opts.has_key? :xml_name
|
76
|
+
raise InvalidName, "invalid xml_name" unless name_valid?(opts[:xml_name])
|
77
|
+
opts[:xml_name]
|
78
|
+
else
|
79
|
+
name
|
80
|
+
end
|
81
|
+
elsif type.instance_of?(Array)
|
82
|
+
type.first.entity_name.to_sym
|
83
|
+
elsif type.ancestors.include?(Entity)
|
84
|
+
type.entity_name.to_sym
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
71
88
|
def define_property(name, type, opts={})
|
72
89
|
raise InvalidName unless name_valid?(name)
|
73
90
|
|
74
|
-
xml_name
|
75
|
-
if opts.has_key? :xml_name
|
76
|
-
raise ArgumentError, "xml_name is not supported for nested entities" unless type == String
|
77
|
-
xml_name = opts[:xml_name]
|
78
|
-
raise InvalidName, "invalid xml_name" unless name_valid?(xml_name)
|
79
|
-
end
|
80
|
-
|
81
|
-
class_props << {name: name, xml_name: xml_name, type: type}
|
91
|
+
class_props << {name: name, xml_name: determine_xml_name(name, type, opts), type: type}
|
82
92
|
default_props[name] = opts[:default] if opts.has_key? :default
|
83
93
|
|
84
94
|
instance_eval { attr_reader name }
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module DiasporaFederation
|
2
|
+
# This module contains a Diaspora*-specific implementation of parts of the
|
3
|
+
# {http://www.salmon-protocol.org/ Salmon Protocol}.
|
4
|
+
module Salmon
|
5
|
+
# XML namespace url
|
6
|
+
XMLNS = "https://joindiaspora.com/protocol"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
require "base64"
|
11
|
+
|
12
|
+
require "diaspora_federation/salmon/aes"
|
13
|
+
require "diaspora_federation/salmon/exceptions"
|
14
|
+
require "diaspora_federation/salmon/xml_payload"
|
15
|
+
require "diaspora_federation/salmon/magic_envelope"
|
16
|
+
require "diaspora_federation/salmon/slap"
|
17
|
+
require "diaspora_federation/salmon/encrypted_slap"
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module DiasporaFederation
|
2
|
+
module Salmon
|
3
|
+
# class for AES encryption and decryption
|
4
|
+
class AES
|
5
|
+
# OpenSSL aes cipher definition
|
6
|
+
CIPHER = "AES-256-CBC"
|
7
|
+
|
8
|
+
# generates a random AES key and initialization vector
|
9
|
+
# @return [Hash] { key: "...", iv: "..." }
|
10
|
+
def self.generate_key_and_iv
|
11
|
+
cipher = OpenSSL::Cipher.new(CIPHER)
|
12
|
+
{key: cipher.random_key, iv: cipher.random_iv}
|
13
|
+
end
|
14
|
+
|
15
|
+
# encrypts the given data with an AES cipher defined by the given key
|
16
|
+
# and iv and returns the resulting ciphertext base64 strict_encoded.
|
17
|
+
# @param [String] data plain input
|
18
|
+
# @param [String] key AES key
|
19
|
+
# @param [String] iv AES initialization vector
|
20
|
+
# @return [String] base64 encoded ciphertext
|
21
|
+
# @raise [ArgumentError] if any of the arguments is missing or not the correct type
|
22
|
+
def self.encrypt(data, key, iv)
|
23
|
+
raise ArgumentError unless data.instance_of?(String) &&
|
24
|
+
key.instance_of?(String) &&
|
25
|
+
iv.instance_of?(String)
|
26
|
+
|
27
|
+
cipher = OpenSSL::Cipher.new(CIPHER)
|
28
|
+
cipher.encrypt
|
29
|
+
cipher.key = key
|
30
|
+
cipher.iv = iv
|
31
|
+
|
32
|
+
ciphertext = cipher.update(data) + cipher.final
|
33
|
+
|
34
|
+
Base64.strict_encode64(ciphertext)
|
35
|
+
end
|
36
|
+
|
37
|
+
# decrypts the given ciphertext with an AES cipher defined by the given key
|
38
|
+
# and iv. +ciphertext+ is expected to be base64 encoded
|
39
|
+
# @param [String] ciphertext input data
|
40
|
+
# @param [String] key AES key
|
41
|
+
# @param [String] iv AES initialization vector
|
42
|
+
# @return [String] decrypted plain message
|
43
|
+
# @raise [ArgumentError] if any of the arguments is missing or not the correct type
|
44
|
+
def self.decrypt(ciphertext, key, iv)
|
45
|
+
raise ArgumentError unless ciphertext.instance_of?(String) &&
|
46
|
+
key.instance_of?(String) &&
|
47
|
+
iv.instance_of?(String)
|
48
|
+
|
49
|
+
decipher = OpenSSL::Cipher.new(CIPHER)
|
50
|
+
decipher.decrypt
|
51
|
+
decipher.key = key
|
52
|
+
decipher.iv = iv
|
53
|
+
|
54
|
+
decipher.update(Base64.decode64(ciphertext)) + decipher.final
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
module DiasporaFederation
|
2
|
+
module Salmon
|
3
|
+
# +EncryptedSlap+ provides class methods for generating and parsing encrypted
|
4
|
+
# Slaps. (In principle the same as {Slap}, but with encryption.)
|
5
|
+
#
|
6
|
+
# The basic encryption mechanism used here is based on the knowledge that
|
7
|
+
# asymmetrical encryption is slow and symmetrical encryption is fast. Keeping in
|
8
|
+
# mind that a message we want to de-/encrypt may greatly vary in length,
|
9
|
+
# performance considerations must play a part of this scheme.
|
10
|
+
#
|
11
|
+
# A Diaspora*-flavored encrypted magic-enveloped XML message looks like the following:
|
12
|
+
#
|
13
|
+
# <?xml version="1.0" encoding="UTF-8"?>
|
14
|
+
# <diaspora xmlns="https://joindiaspora.com/protocol" xmlns:me="http://salmon-protocol.org/ns/magic-env">
|
15
|
+
# <encrypted_header>{encrypted_header}</encrypted_header>
|
16
|
+
# {magic_envelope with encrypted data}
|
17
|
+
# </diaspora>
|
18
|
+
#
|
19
|
+
# The encrypted header is encoded in JSON like this (when in plain text):
|
20
|
+
#
|
21
|
+
# {
|
22
|
+
# "aes_key" => "...",
|
23
|
+
# "ciphertext" => "..."
|
24
|
+
# }
|
25
|
+
#
|
26
|
+
# +aes_key+ is encrypted using the recipients public key, and contains the AES
|
27
|
+
# +key+ and +iv+ used to encrypt the +ciphertext+ also encoded as JSON.
|
28
|
+
#
|
29
|
+
# {
|
30
|
+
# "key" => "...",
|
31
|
+
# "iv" => "..."
|
32
|
+
# }
|
33
|
+
#
|
34
|
+
# +ciphertext+, once decrypted, contains the +author_id+, +aes_key+ and +iv+
|
35
|
+
# relevant to the decryption of the data in the magic_envelope and the
|
36
|
+
# verification of its signature.
|
37
|
+
#
|
38
|
+
# The decrypted cyphertext has this XML structure:
|
39
|
+
#
|
40
|
+
# <decrypted_header>
|
41
|
+
# <iv>{iv}</iv>
|
42
|
+
# <aes_key>{aes_key}</aes_key>
|
43
|
+
# <author_id>{author_id}</author_id>
|
44
|
+
# </decrypted_header>
|
45
|
+
#
|
46
|
+
# Finally, before decrypting the magic envelope payload, the signature should
|
47
|
+
# first be verified.
|
48
|
+
#
|
49
|
+
# @example Generating an encrypted Salmon Slap
|
50
|
+
# author_id = "author@pod.example.tld"
|
51
|
+
# author_privkey = however_you_retrieve_the_authors_private_key(author_id)
|
52
|
+
# recipient_pubkey = however_you_retrieve_the_recipients_public_key()
|
53
|
+
# entity = YourEntity.new(attr: "val")
|
54
|
+
#
|
55
|
+
# slap_xml = EncryptedSlap.generate_xml(author_id, author_privkey, entity, recipient_pubkey)
|
56
|
+
#
|
57
|
+
# @example Parsing a Salmon Slap
|
58
|
+
# recipient_privkey = however_you_retrieve_the_recipients_private_key()
|
59
|
+
# slap = EncryptedSlap.from_xml(slap_xml, recipient_privkey)
|
60
|
+
# author_pubkey = however_you_retrieve_the_authors_public_key(slap.author_id)
|
61
|
+
#
|
62
|
+
# entity = slap.entity(author_pubkey)
|
63
|
+
#
|
64
|
+
class EncryptedSlap
|
65
|
+
# Creates a Slap instance from the data within the given XML string
|
66
|
+
# containing an encrypted payload.
|
67
|
+
#
|
68
|
+
# @param [String] slap_xml encrypted Salmon xml
|
69
|
+
# @param [OpenSSL::PKey::RSA] pkey recipient private_key for decryption
|
70
|
+
#
|
71
|
+
# @return [Slap] new Slap instance
|
72
|
+
#
|
73
|
+
# @raise [ArgumentError] if any of the arguments is of the wrong type
|
74
|
+
# @raise [MissingHeader] if the +encrypted_header+ element is missing in the XML
|
75
|
+
# @raise [MissingMagicEnvelope] if the +me:env+ element is missing in the XML
|
76
|
+
def self.from_xml(slap_xml, pkey)
|
77
|
+
raise ArgumentError unless slap_xml.instance_of?(String) && pkey.instance_of?(OpenSSL::PKey::RSA)
|
78
|
+
doc = Nokogiri::XML::Document.parse(slap_xml)
|
79
|
+
|
80
|
+
Slap.new.tap do |slap|
|
81
|
+
header_elem = doc.at_xpath("d:diaspora/d:encrypted_header", Slap::NS)
|
82
|
+
raise MissingHeader if header_elem.nil?
|
83
|
+
header = header_data(header_elem.content, pkey)
|
84
|
+
slap.author_id = header[:author_id]
|
85
|
+
slap.cipher_params = {key: Base64.decode64(header[:aes_key]), iv: Base64.decode64(header[:iv])}
|
86
|
+
|
87
|
+
slap.add_magic_env_from_doc(doc)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Creates an encrypted Salmon Slap and returns the XML string.
|
92
|
+
#
|
93
|
+
# @param [String] author_id Diaspora* handle of the author
|
94
|
+
# @param [OpenSSL::PKey::RSA] pkey sender private key for signing the magic envelope
|
95
|
+
# @param [Entity] entity payload
|
96
|
+
# @param [OpenSSL::PKey::RSA] pubkey recipient public key for encrypting the AES key
|
97
|
+
# @return [String] Salmon XML string
|
98
|
+
# @raise [ArgumentError] if any of the arguments is of the wrong type
|
99
|
+
def self.generate_xml(author_id, pkey, entity, pubkey)
|
100
|
+
raise ArgumentError unless author_id.instance_of?(String) &&
|
101
|
+
pkey.instance_of?(OpenSSL::PKey::RSA) &&
|
102
|
+
entity.is_a?(Entity) &&
|
103
|
+
pubkey.instance_of?(OpenSSL::PKey::RSA)
|
104
|
+
|
105
|
+
Slap.build_xml do |xml|
|
106
|
+
magic_envelope = MagicEnvelope.new(pkey, entity)
|
107
|
+
envelope_key = magic_envelope.encrypt!
|
108
|
+
|
109
|
+
encrypted_header(author_id, envelope_key, pubkey, xml)
|
110
|
+
magic_envelope.envelop(xml)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# decrypts and reads the data from the encrypted XML header
|
115
|
+
# @param [String] data base64 encoded, encrypted header data
|
116
|
+
# @param [OpenSSL::PKey::RSA] pkey private key for decryption
|
117
|
+
# @return [Hash] { iv: "...", aes_key: "...", author_id: "..." }
|
118
|
+
def self.header_data(data, pkey)
|
119
|
+
header_elem = decrypt_header(data, pkey)
|
120
|
+
raise InvalidHeader unless header_elem.name == "decrypted_header"
|
121
|
+
|
122
|
+
iv = header_elem.at_xpath("iv").content
|
123
|
+
key = header_elem.at_xpath("aes_key").content
|
124
|
+
author_id = header_elem.at_xpath("author_id").content
|
125
|
+
|
126
|
+
{iv: iv, aes_key: key, author_id: author_id}
|
127
|
+
end
|
128
|
+
private_class_method :header_data
|
129
|
+
|
130
|
+
# decrypts the xml header
|
131
|
+
# @param [String] data base64 encoded, encrypted header data
|
132
|
+
# @param [OpenSSL::PKey::RSA] pkey private key for decryption
|
133
|
+
# @return [Nokogiri::XML::Element] header xml document
|
134
|
+
def self.decrypt_header(data, pkey)
|
135
|
+
cipher_header = JSON.parse(Base64.decode64(data))
|
136
|
+
key = JSON.parse(pkey.private_decrypt(Base64.decode64(cipher_header["aes_key"])))
|
137
|
+
|
138
|
+
xml = AES.decrypt(cipher_header["ciphertext"], Base64.decode64(key["key"]), Base64.decode64(key["iv"]))
|
139
|
+
Nokogiri::XML::Document.parse(xml).root
|
140
|
+
end
|
141
|
+
private_class_method :decrypt_header
|
142
|
+
|
143
|
+
# encrypt the header xml with an AES cipher and encrypt the cipher params
|
144
|
+
# with the recipients public_key
|
145
|
+
# @param [String] author_id diaspora_handle
|
146
|
+
# @param [Hash] envelope_key envelope cipher params
|
147
|
+
# @param [OpenSSL::PKey::RSA] pubkey recipient public_key
|
148
|
+
# @param [Nokogiri::XML::Element] xml parent element for inserting in XML document
|
149
|
+
def self.encrypted_header(author_id, envelope_key, pubkey, xml)
|
150
|
+
data = header_xml(author_id, strict_base64_encode(envelope_key))
|
151
|
+
key = AES.generate_key_and_iv
|
152
|
+
ciphertext = AES.encrypt(data, key[:key], key[:iv])
|
153
|
+
|
154
|
+
json_key = JSON.generate(strict_base64_encode(key))
|
155
|
+
encrypted_key = Base64.strict_encode64(pubkey.public_encrypt(json_key))
|
156
|
+
|
157
|
+
json_header = JSON.generate(aes_key: encrypted_key, ciphertext: ciphertext)
|
158
|
+
|
159
|
+
xml.encrypted_header(Base64.strict_encode64(json_header))
|
160
|
+
end
|
161
|
+
private_class_method :encrypted_header
|
162
|
+
|
163
|
+
# generate the header xml string, including the author, aes_key and iv
|
164
|
+
# @param [String] author_id diaspora_handle of the author
|
165
|
+
# @param [Hash] envelope_key { key: "...", iv: "..." } (values in base64)
|
166
|
+
# @return [String] header XML string
|
167
|
+
def self.header_xml(author_id, envelope_key)
|
168
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
169
|
+
xml.decrypted_header {
|
170
|
+
xml.iv(envelope_key[:iv])
|
171
|
+
xml.aes_key(envelope_key[:key])
|
172
|
+
xml.author_id(author_id)
|
173
|
+
}
|
174
|
+
end
|
175
|
+
builder.to_xml.strip
|
176
|
+
end
|
177
|
+
private_class_method :header_xml
|
178
|
+
|
179
|
+
# @param [Hash] hash { key: "...", iv: "..." }
|
180
|
+
# @return [Hash] encoded hash: { key: "...", iv: "..." }
|
181
|
+
def self.strict_base64_encode(hash)
|
182
|
+
Hash[hash.map {|k, v| [k, Base64.strict_encode64(v)] }]
|
183
|
+
end
|
184
|
+
private_class_method :strict_base64_encode
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module DiasporaFederation
|
2
|
+
module Salmon
|
3
|
+
# Raised, if the element containing the Magic Envelope is missing from the XML
|
4
|
+
class MissingMagicEnvelope < RuntimeError
|
5
|
+
end
|
6
|
+
|
7
|
+
# Raised, if the element containing the author is empty.
|
8
|
+
class MissingAuthor < RuntimeError
|
9
|
+
end
|
10
|
+
|
11
|
+
# Raised, if the element containing the header is missing from the XML
|
12
|
+
class MissingHeader < RuntimeError
|
13
|
+
end
|
14
|
+
|
15
|
+
# Raised if the decrypted header has an unexpected XML structure
|
16
|
+
class InvalidHeader < RuntimeError
|
17
|
+
end
|
18
|
+
|
19
|
+
# Raised, if the Magic Envelope XML structure is malformed.
|
20
|
+
class InvalidEnvelope < RuntimeError
|
21
|
+
end
|
22
|
+
|
23
|
+
# Raised, if the calculated signature doesn't match the one contained in the
|
24
|
+
# Magic Envelope.
|
25
|
+
class InvalidSignature < RuntimeError
|
26
|
+
end
|
27
|
+
|
28
|
+
# Raised, if the parsed Magic Envelope specifies an unhandled algorithm.
|
29
|
+
class InvalidAlgorithm < RuntimeError
|
30
|
+
end
|
31
|
+
|
32
|
+
# Raised, if the parsed Magic Envelope specifies an unhandled encoding.
|
33
|
+
class InvalidEncoding < RuntimeError
|
34
|
+
end
|
35
|
+
|
36
|
+
# Raised, if the XML structure of the parsed document doesn't resemble the
|
37
|
+
# expected structure.
|
38
|
+
class InvalidStructure < RuntimeError
|
39
|
+
end
|
40
|
+
|
41
|
+
# Raised, if the entity name in the XML is invalid
|
42
|
+
class InvalidEntityName < RuntimeError
|
43
|
+
end
|
44
|
+
|
45
|
+
# Raised, if the entity contained within the XML cannot be mapped to a
|
46
|
+
# defined {Entity} subclass.
|
47
|
+
class UnknownEntity < RuntimeError
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|