You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Context.spec.js 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /* eslint-disable no-bitwise */
  2. import { Context } from './Context';
  3. import { importKey, ratchet } from './crypto-utils';
  4. /*
  5. function hexdump(buffer) {
  6. const a = new Uint8Array(buffer);
  7. let s = '';
  8. for (let i = 0; i < a.byteLength; i++) {
  9. s += '0x';
  10. s += a[i].toString(16);
  11. s += ' ';
  12. }
  13. return s.trim();
  14. }
  15. */
  16. /* TODO: more tests
  17. * - delta frames
  18. * - frame header is not encrypted
  19. * - different sendCounts
  20. * - different key length
  21. * - ratcheting in decodeFunction
  22. * etc
  23. */
  24. const audioBytes = [ 0xde, 0xad, 0xbe, 0xef ];
  25. const videoBytes = [ 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef ];
  26. /**
  27. * generates a dummy audio frame
  28. */
  29. function makeAudioFrame() {
  30. return {
  31. data: new Uint8Array(audioBytes).buffer,
  32. type: undefined, // type is undefined for audio frames.
  33. getMetadata: () => {
  34. return { synchronizationSource: 123 };
  35. }
  36. };
  37. }
  38. /**
  39. * generates a dummy video frame
  40. */
  41. function makeVideoFrame() {
  42. return {
  43. data: new Uint8Array(videoBytes).buffer,
  44. type: 'key',
  45. getMetadata: () => {
  46. return { synchronizationSource: 321 };
  47. }
  48. };
  49. }
  50. describe('E2EE Context', () => {
  51. let sender;
  52. let sendController;
  53. let receiver;
  54. let receiveController;
  55. const key = new Uint8Array([
  56. 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  57. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  58. ]);
  59. beforeEach(() => {
  60. sender = new Context('sender');
  61. sender.setEnabled(true);
  62. receiver = new Context('receiver');
  63. receiver.setEnabled(true);
  64. });
  65. describe('encode function', () => {
  66. beforeEach(async () => {
  67. await sender.setKey(key, 0);
  68. await receiver.setKey(key, 0);
  69. });
  70. it('with an audio frame', done => {
  71. sendController = {
  72. enqueue: encodedFrame => {
  73. const data = new Uint8Array(encodedFrame.data);
  74. // An audio frame will have an overhead of 30 bytes and key size:
  75. // 16 bytes authentication tag, 12 bytes iv, iv length (1 byte) and 1 byte key index.
  76. expect(data.byteLength).toEqual(audioBytes.length + 30);
  77. // TODO: provide test vector.
  78. done();
  79. }
  80. };
  81. sender.encodeFunction(makeAudioFrame(), sendController);
  82. });
  83. it('with a video frame', done => {
  84. sendController = {
  85. enqueue: encodedFrame => {
  86. const data = new Uint8Array(encodedFrame.data);
  87. // A video frame will have an overhead of 30 bytes and key size:
  88. // 16 bytes authentication tag, 12 bytes iv, iv length (1 byte) and 1 byte key index.
  89. expect(data.byteLength).toEqual(videoBytes.length + 30);
  90. // TODO: provide test vector.
  91. done();
  92. }
  93. };
  94. sender.encodeFunction(makeVideoFrame(), sendController);
  95. });
  96. });
  97. describe('end-to-end test', () => {
  98. beforeEach(async () => {
  99. await sender.setKey(key, 0);
  100. await receiver.setKey(key, 0);
  101. sendController = {
  102. enqueue: async encodedFrame => {
  103. await receiver.decodeFunction(encodedFrame, receiveController);
  104. }
  105. };
  106. });
  107. it('with an audio frame', done => {
  108. receiveController = {
  109. enqueue: encodedFrame => {
  110. const data = new Uint8Array(encodedFrame.data);
  111. expect(data.byteLength).toEqual(audioBytes.length);
  112. expect(Array.from(data)).toEqual(audioBytes);
  113. done();
  114. }
  115. };
  116. sender.encodeFunction(makeAudioFrame(), sendController);
  117. });
  118. it('with a video frame', done => {
  119. receiveController = {
  120. enqueue: encodedFrame => {
  121. const data = new Uint8Array(encodedFrame.data);
  122. expect(data.byteLength).toEqual(videoBytes.length);
  123. expect(Array.from(data)).toEqual(videoBytes);
  124. done();
  125. }
  126. };
  127. sender.encodeFunction(makeVideoFrame(), sendController);
  128. });
  129. it('the receiver ratchets forward', done => {
  130. receiveController = {
  131. enqueue: encodedFrame => {
  132. const data = new Uint8Array(encodedFrame.data);
  133. expect(data.byteLength).toEqual(audioBytes.length);
  134. expect(Array.from(data)).toEqual(audioBytes);
  135. done();
  136. }
  137. };
  138. const encodeFunction = async () => {
  139. // Ratchet the key. We reimport from the raw bytes.
  140. const material = await importKey(key);
  141. await sender.setKey(await ratchet(material), 0);
  142. sender.encodeFunction(makeAudioFrame(), sendController);
  143. };
  144. encodeFunction();
  145. });
  146. });
  147. });