/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; let db; let userAgentID = "f5b47f8d-771f-4ea3-b999-91c135f8766d"; function run_test() { do_get_profile(); setPrefs({ userAgentID, }); run_next_test(); } function putRecord(channelID, scope, publicKey, privateKey, authSecret) { return db.put({ channelID, pushEndpoint: "https://example.org/push/" + channelID, scope, pushCount: 0, lastPush: 0, originAttributes: "", quota: Infinity, systemRecord: true, p256dhPublicKey: ChromeUtils.base64URLDecode(publicKey, { padding: "reject", }), p256dhPrivateKey: privateKey, authenticationSecret: ChromeUtils.base64URLDecode(authSecret, { padding: "reject", }), }); } let ackDone; let server; add_task(async function test_notification_ack_data_setup() { db = PushServiceWebSocket.newPushDB(); registerCleanupFunction(() => { return db.drop().then(_ => db.close()); }); await putRecord( "subscription1", "https://example.com/page/1", "BPCd4gNQkjwRah61LpdALdzZKLLnU5UAwDztQ5_h0QsT26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA", { crv: "P-256", d: "1jUPhzVsRkzV0vIzwL4ZEsOlKdNOWm7TmaTfzitJkgM", ext: true, key_ops: ["deriveBits"], kty: "EC", x: "8J3iA1CSPBFqHrUul0At3NkosudTlQDAPO1Dn-HRCxM", y: "26jk0IFbqcK6-JxhHAm-rsHEwy0CyVJjtnfOcqc1tgA", }, "c_sGN6uCv9Hu7JOQT34jAQ" ); await putRecord( "subscription2", "https://example.com/page/2", "BPnWyUo7yMnuMlyKtERuLfWE8a09dtdjHSW2lpC9_BqR5TZ1rK8Ldih6ljyxVwnBA-nygQHGRpEmu1jV5K8437E", { crv: "P-256", d: "lFm4nPsUKYgNGBJb5nXXKxl8bspCSp0bAhCYxbveqT4", ext: true, key_ops: ["deriveBits"], kty: "EC", x: "-dbJSjvIye4yXIq0RG4t9YTxrT1212MdJbaWkL38GpE", y: "5TZ1rK8Ldih6ljyxVwnBA-nygQHGRpEmu1jV5K8437E", }, "t3P246Gj9vjKDHHRYaY6hw" ); await putRecord( "subscription3", "https://example.com/page/3", "BDhUHITSeVrWYybFnb7ylVTCDDLPdQWMpf8gXhcWwvaaJa6n3YH8TOcH8narDF6t8mKVvg2ioLW-8MH5O4dzGcI", { crv: "P-256", d: "Q1_SE1NySTYzjbqgWwPgrYh7XRg3adqZLkQPsy319G8", ext: true, key_ops: ["deriveBits"], kty: "EC", x: "OFQchNJ5WtZjJsWdvvKVVMIMMs91BYyl_yBeFxbC9po", y: "Ja6n3YH8TOcH8narDF6t8mKVvg2ioLW-8MH5O4dzGcI", }, "E0qiXGWvFSR0PS352ES1_Q" ); let setupDone; let setupDonePromise = new Promise(r => (setupDone = r)); PushService.init({ serverURI: "wss://push.example.org/", db, makeWebSocket(uri) { return new MockWebSocket(uri, { onHello(request) { equal( request.uaid, userAgentID, "Should send matching device IDs in handshake" ); this.serverSendMsg( JSON.stringify({ messageType: "hello", uaid: userAgentID, status: 200, use_webpush: true, }) ); server = this; setupDone(); }, onACK(request) { if (ackDone) { ackDone(request); } }, }); }, }); await setupDonePromise; }); add_task(async function test_notification_ack_data() { let allTestData = [ { channelID: "subscription1", version: "v1", send: { headers: { encryption_key: 'keyid="notification1"; dh="BO_tgGm-yvYAGLeRe16AvhzaUcpYRiqgsGOlXpt0DRWDRGGdzVLGlEVJMygqAUECarLnxCiAOHTP_znkedrlWoU"', encryption: 'keyid="notification1";salt="uAZaiXpOSfOLJxtOCZ09dA"', encoding: "aesgcm128", }, data: "NwrrOWPxLE8Sv5Rr0Kep7n0-r_j3rsYrUw_CXPo", version: "v1", }, ackCode: 100, receive: { scope: "https://example.com/page/1", data: "Some message", }, }, { channelID: "subscription2", version: "v2", send: { headers: { encryption_key: 'keyid="notification2"; dh="BKVdQcgfncpNyNWsGrbecX0zq3eHIlHu5XbCGmVcxPnRSbhjrA6GyBIeGdqsUL69j5Z2CvbZd-9z1UBH0akUnGQ"', encryption: 'keyid="notification2";salt="vFn3t3M_k42zHBdpch3VRw"', encoding: "aesgcm128", }, data: "Zt9dEdqgHlyAL_l83385aEtb98ZBilz5tgnGgmwEsl5AOCNgesUUJ4p9qUU", }, ackCode: 100, receive: { scope: "https://example.com/page/2", data: "Some message", }, }, { channelID: "subscription3", version: "v3", send: { headers: { encryption_key: 'keyid="notification3";dh="BD3xV_ACT8r6hdIYES3BJj1qhz9wyv7MBrG9vM2UCnjPzwE_YFVpkD-SGqE-BR2--0M-Yf31wctwNsO1qjBUeMg"', encryption: 'keyid="notification3"; salt="DFq188piWU7osPBgqn4Nlg"; rs=24', encoding: "aesgcm128", }, data: "LKru3ZzxBZuAxYtsaCfaj_fehkrIvqbVd1iSwnwAUgnL-cTeDD-83blxHXTq7r0z9ydTdMtC3UjAcWi8LMnfY-BFzi0qJAjGYIikDA", }, ackCode: 100, receive: { scope: "https://example.com/page/3", data: "Some message", }, }, // A message encoded with `aesgcm` (2 bytes of padding, authenticated). { channelID: "subscription1", version: "v5", send: { headers: { crypto_key: 'keyid=v4;dh="BMh_vsnqu79ZZkMTYkxl4gWDLdPSGE72Lr4w2hksSFW398xCMJszjzdblAWXyhSwakRNEU_GopAm4UGzyMVR83w"', encryption: 'keyid="v4";salt="C14Wb7rQTlXzrgcPHtaUzw"', encoding: "aesgcm", }, data: "pus4kUaBWzraH34M-d_oN8e0LPpF_X6acx695AMXovDe", }, ackCode: 100, receive: { scope: "https://example.com/page/1", data: "Another message", }, }, // A message with 17 bytes of padding and rs of 24 { channelID: "subscription2", version: "v5", send: { headers: { crypto_key: 'keyid="v5"; dh="BOp-DpyR9eLY5Ci11_loIFqeHzWfc_0evJmq7N8NKzgp60UAMMM06XIi2VZp2_TSdw1omk7E19SyeCCwRp76E-U"', encryption: 'keyid=v5;salt="TvjOou1TqJOQY_ZsOYV3Ww";rs=24', encoding: "aesgcm", }, data: "rG9WYQ2ZwUgfj_tMlZ0vcIaNpBN05FW-9RUBZAM-UUZf0_9eGpuENBpUDAw3mFmd2XJpmvPvAtLVs54l3rGwg1o", }, ackCode: 100, receive: { scope: "https://example.com/page/2", data: "Some message", }, }, // A message without key identifiers. { channelID: "subscription3", version: "v6", send: { headers: { crypto_key: 'dh="BEEjwWbF5jZKCgW0kmUWgG-wNcRvaa9_3zZElHAF8przHwd4cp5_kQsc-IMNZcVA0iUix31jxuMOytU-5DwWtyQ"', encryption: "salt=aAQcr2khAksgNspPiFEqiQ", encoding: "aesgcm", }, data: "pEYgefdI-7L46CYn5dR9TIy2AXGxe07zxclbhstY", }, ackCode: 100, receive: { scope: "https://example.com/page/3", data: "Some message", }, }, // A malformed encrypted message. { channelID: "subscription3", version: "v7", send: { headers: { crypto_key: "dh=AAAAAAAA", encryption: "salt=AAAAAAAA", }, data: "AAAAAAAA", }, ackCode: 101, receive: null, }, ]; let sendAndReceive = testData => { let messageReceived = testData.receive ? promiseObserverNotification( PushServiceComponent.pushTopic, (subject, data) => { let notification = subject.QueryInterface(Ci.nsIPushMessage).data; equal( notification.text(), testData.receive.data, "Check data for notification " + testData.version ); equal( data, testData.receive.scope, "Check scope for notification " + testData.version ); return true; } ) : Promise.resolve(); let ackReceived = new Promise(resolve => (ackDone = resolve)).then( ackData => { deepEqual( { messageType: "ack", updates: [ { channelID: testData.channelID, version: testData.version, code: testData.ackCode, }, ], }, ackData, "Check updates for acknowledgment " + testData.version ); } ); let msg = JSON.parse(JSON.stringify(testData.send)); msg.messageType = "notification"; msg.channelID = testData.channelID; msg.version = testData.version; server.serverSendMsg(JSON.stringify(msg)); return Promise.all([messageReceived, ackReceived]); }; await allTestData.reduce((p, testData) => { return p.then(_ => sendAndReceive(testData)); }, Promise.resolve()); });