Skip to content

Commit e725f23

Browse files
Authentication for encrypted channels
Authenticated encrypted channels needs to include a step that generates and packages the shared secret that the client will use to decrypt messages. Only in the instance that the channel name has the 'private-encrypted` prefix will we generate and include a shared_secret key. If there is no encryption_master_key but the channel is prefixed with encrypted we will simply send back null.
1 parent 3c831a1 commit e725f23

File tree

4 files changed

+44
-1
lines changed

4 files changed

+44
-1
lines changed

lib/pusher/channel.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,15 @@ def authenticate(socket_id, custom_data = nil)
174174
r
175175
end
176176

177+
def shared_secret(encryption_master_key)
178+
return unless encryption_master_key
179+
180+
secret_string = @name + encryption_master_key
181+
digest = OpenSSL::Digest::SHA256.new
182+
digest << secret_string
183+
Base64.strict_encode64(digest.digest)
184+
end
185+
177186
private
178187

179188
def validate_socket_id(socket_id)

lib/pusher/client.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,11 @@ def notify(interests, data = {})
375375
#
376376
def authenticate(channel_name, socket_id, custom_data = nil)
377377
channel_instance = channel(channel_name)
378-
channel_instance.authenticate(socket_id, custom_data)
378+
r = channel_instance.authenticate(socket_id, custom_data)
379+
if channel_name.match(/^private-encrypted-/)
380+
r[:shared_secret] = channel_instance.shared_secret(encryption_master_key)
381+
end
382+
r
379383
end
380384

381385
# @private Construct a net/http http client

spec/channel_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,21 @@ def authentication_string(*data)
167167
}.to raise_error Pusher::Error
168168
end
169169
end
170+
171+
describe `#shared_secret` do
172+
before(:each) do
173+
@channel.instance_variable_set(:@name, 'private-encrypted-1')
174+
end
175+
176+
it 'should return a shared_secret based on the channel name and encryption master key' do
177+
key = '3W1pfB/Etr+ZIlfMWwZP3gz8jEeCt4s2pe6Vpr+2c3M='
178+
shared_secret = @channel.shared_secret(key)
179+
expect(shared_secret).to eq("6zeEp/chneRPS1cbK/hGeG860UhHomxSN6hTgzwT20I=")
180+
end
181+
182+
it 'should return nil if missing encryption master key' do
183+
shared_secret = @channel.shared_secret(nil)
184+
expect(shared_secret).to be_nil
185+
end
186+
end
170187
end

spec/client_spec.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,19 @@
276276
})
277277
end
278278

279+
it 'should include a shared_secret if the private-encrypted channel' do
280+
allow(MultiJson).to receive(:encode).with(@custom_data).and_return 'a json string'
281+
@client.instance_variable_set(:@encryption_master_key, '3W1pfB/Etr+ZIlfMWwZP3gz8jEeCt4s2pe6Vpr+2c3M=')
282+
283+
response = @client.authenticate('private-encrypted-test_channel', '1.1', @custom_data)
284+
285+
expect(response).to eq({
286+
:auth => "12345678900000001:#{hmac(@client.secret, "1.1:private-encrypted-test_channel:a json string")}",
287+
:shared_secret => "o0L3QnIovCeRC8KTD8KBRlmi31dGzHVS2M93uryqDdw=",
288+
:channel_data => 'a json string'
289+
})
290+
end
291+
279292
end
280293

281294
describe '#trigger' do

0 commit comments

Comments
 (0)