diaspora_federation 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|