class PushNotification::GcmSender

Public Class Methods

new(endpoint, client_public_key, auth, server_key_pair) click to toggle source
# File lib/push_notification/gcm_sender.rb, line 3
def initialize(endpoint, client_public_key, auth, server_key_pair)
  @endpoint = URI.parse(endpoint)
  @salt = OpenSSL::BN.rand(128).to_s(2)
  @auth = auth
  @client_public_key = client_public_key
  @server_key_pair = server_key_pair
  @server_public_key = @server_key_pair.public_key.to_bn.to_s(2)
  @shared_key = @server_key_pair.dh_compute_key(client_public_key)
end

Public Instance Methods

send(data = '') click to toggle source
# File lib/push_notification/gcm_sender.rb, line 13
def send(data = '')
  header = "#{@salt}\x00\x00\x10\x00\x41#{@server_public_key}"
  msg = encrypt_msg(data)
  headers = {
    'Content-Type': 'application/octet-stream',
    'Crypto-Key': "p256ecdsa=#{Base64.urlsafe_encode64(@server_public_key)}",
    'Content-Encoding': 'aes128gcm',
    'Content-Length': (header.length + msg.length).to_s,
    'TTL': '86400',
    'Authorization': "WebPush #{signature()}"
  }

  http = Net::HTTP.new(@endpoint.host, @endpoint.port)
  http.use_ssl = true
  return http.request_post(@endpoint.path, header + msg, headers)
end

Private Instance Methods

encrypt_msg(data) click to toggle source
# File lib/push_notification/gcm_sender.rb, line 35
def encrypt_msg(data)
  cipher = OpenSSL::Cipher.new('aes-128-gcm')
  prk_key = sha256(@auth, @shared_key)
  ikm = sha256(prk_key, "WebPush: info\x00#{@client_public_key.to_bn.to_s(2)}#{@server_public_key}\x01")
  prk = sha256(@salt, ikm)
  enc_key = sha256(prk, "Content-Encoding: aes128gcm\x00\x01")[0, 16]
  enc_nonce = sha256(prk, "Content-Encoding: nonce\x00\x01")[0, 12]

  cipher.encrypt
  cipher.padding = 0
  cipher.key = enc_key
  cipher.iv = enc_nonce

  result = ''
  result << cipher.update("#{data}\x02\x00")
  result << cipher.final
  return "#{result}#{cipher.auth_tag}"
end
sha256(key, data) click to toggle source
# File lib/push_notification/gcm_sender.rb, line 31
def sha256(key, data)
  return OpenSSL::HMAC.digest('SHA256', key, data)
end
signature() click to toggle source
# File lib/push_notification/gcm_sender.rb, line 54
def signature
  payload = {
    aud: "#{@endpoint.scheme}://#{@endpoint.host}",
    exp: Time.now.to_i + 43200
  }
  return JWT.encode(payload, @server_key_pair, 'ES256')
end