mode-ctr.ts 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. // Counter Mode
  2. import { ModeOfOperation } from "./mode.js";
  3. export class CTR extends ModeOfOperation {
  4. // Remaining bytes for the one-time pad
  5. #remaining: Uint8Array;
  6. #remainingIndex: number;
  7. // The current counter
  8. #counter: Uint8Array;
  9. constructor(key: Uint8Array, initialValue?: number | Uint8Array) {
  10. super("CTR", key, CTR);
  11. this.#counter = new Uint8Array(16)
  12. this.#counter.fill(0);
  13. this.#remaining = this.#counter; // This will be discarded immediately
  14. this.#remainingIndex = 16;
  15. if (initialValue == null) { initialValue = 1; }
  16. if (typeof(initialValue) === "number") {
  17. this.setCounterValue(initialValue);
  18. } else {
  19. this.setCounterBytes(initialValue);
  20. }
  21. }
  22. get counter(): Uint8Array { return new Uint8Array(this.#counter); }
  23. setCounterValue(value: number): void {
  24. if (!Number.isInteger(value) || value < 0 || value > Number.MAX_SAFE_INTEGER) {
  25. throw new TypeError("invalid counter initial integer value");
  26. }
  27. for (let index = 15; index >= 0; --index) {
  28. this.#counter[index] = value % 256;
  29. value = Math.floor(value / 256);
  30. }
  31. }
  32. setCounterBytes(value: Uint8Array): void {
  33. if (value.length !== 16) {
  34. throw new TypeError("invalid counter initial Uint8Array value length");
  35. }
  36. this.#counter.set(value);
  37. }
  38. increment() {
  39. for (let i = 15; i >= 0; i--) {
  40. if (this.#counter[i] === 255) {
  41. this.#counter[i] = 0;
  42. } else {
  43. this.#counter[i]++;
  44. break;
  45. }
  46. }
  47. }
  48. encrypt(plaintext: Uint8Array): Uint8Array {
  49. const crypttext = new Uint8Array(plaintext);
  50. for (let i = 0; i < crypttext.length; i++) {
  51. if (this.#remainingIndex === 16) {
  52. this.#remaining = this.aes.encrypt(this.#counter);
  53. this.#remainingIndex = 0;
  54. this.increment();
  55. }
  56. crypttext[i] ^= this.#remaining[this.#remainingIndex++];
  57. }
  58. return crypttext;
  59. }
  60. decrypt(ciphertext: Uint8Array): Uint8Array {
  61. return this.encrypt(ciphertext);
  62. }
  63. }