diff --git a/jsr.json b/jsr.json index 58013e7..7c130eb 100644 --- a/jsr.json +++ b/jsr.json @@ -1,6 +1,6 @@ { "name": "@li0ard/crapto1-ts", - "version": "0.2.4", + "version": "0.2.5", "exports": "./src/index.ts", "publish": { "include": [ diff --git a/package.json b/package.json index 586f2d4..2ef5b00 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@li0ard/crapto1_ts", - "version": "0.2.4", + "version": "0.2.5", "main": "dist/index.js", "types": "dist/index.d.ts", "type": "module", diff --git a/src/crapto1.ts b/src/crapto1.ts index b3d407d..b525ba6 100644 --- a/src/crapto1.ts +++ b/src/crapto1.ts @@ -6,8 +6,8 @@ import { bebit, binsearch, bit, evenParity32, extend_table, extend_table_simple, /** * Rollback the shift register in order to get previous states (for bits) * @param s State - * @param in_ Bits - * @param isEncrypted Encrypted? + * @param in_ Input bit + * @param isEncrypted Is input bit encrypted? * @returns {number} */ export const lfsr_rollback_bit = (s: Crypto1State, in_: number, isEncrypted: boolean = false): number => { @@ -29,8 +29,8 @@ export const lfsr_rollback_bit = (s: Crypto1State, in_: number, isEncrypted: boo /** * Rollback the shift register in order to get previous states (for bytes) * @param s State - * @param in_ Word - * @param isEncrypted Encrypted? + * @param in_ Input byte + * @param isEncrypted Is input byte encrypted? * @returns {number} */ export const lfsr_rollback_byte = (s: Crypto1State, in_: number, isEncrypted: boolean = false): number => { @@ -44,8 +44,8 @@ export const lfsr_rollback_byte = (s: Crypto1State, in_: number, isEncrypted: bo /** * Rollback the shift register in order to get previous states (for words (uint32)) * @param s State - * @param in_ Word - * @param isEncrypted Encrypted? + * @param in_ Input word + * @param isEncrypted Is input word encrypted? * @returns {number} */ export const lfsr_rollback_word = (s: Crypto1State, in_: number, isEncrypted: boolean = false): number => { diff --git a/src/crypto1.ts b/src/crypto1.ts index 40a8e1c..e8b09bc 100644 --- a/src/crypto1.ts +++ b/src/crypto1.ts @@ -1,5 +1,5 @@ -import type { Crypto1State } from "./state"; -import { bebit, bit, filter, parity, swapendian } from "./utils"; +import { Crypto1State } from "./state"; +import { bebit, bit, filter, oddParity8, parity, swapendian } from "./utils"; export const LF_POLY_ODD: number = 0x29CE5C; export const LF_POLY_EVEN: number = 0x870804; @@ -19,10 +19,10 @@ export const prng_successor = (x: number, n: number): number => { } /** - * Proceed Crypto1 encryption/decryption process (for words (uint32)) + * Generate keystream for words (uint32) * @param s State - * @param in_ Word - * @param isEncrypted Encrypted? + * @param in_ Input word + * @param isEncrypted Is input word encrypted? * @returns {number} */ export const crypto1_word = (s: Crypto1State, in_: number, isEncrypted: boolean = false): number => { @@ -34,10 +34,10 @@ export const crypto1_word = (s: Crypto1State, in_: number, isEncrypted: boolean } /** - * Proceed Crypto1 encryption/decryption process (for bytes) + * Generate keystream for bytes * @param s State - * @param in_ Word - * @param isEncrypted Encrypted? + * @param in_ Input byte + * @param isEncrypted Is input byte encrypted? * @returns {number} */ export const crypto1_byte = (s: Crypto1State, in_: number, isEncrypted: boolean = false): number => { @@ -49,10 +49,10 @@ export const crypto1_byte = (s: Crypto1State, in_: number, isEncrypted: boolean } /** - * Proceed Crypto1 encryption/decryption process (for bits) + * Generate keystream for bits * @param s State - * @param in_ Bit - * @param isEncrypted Encrypted? + * @param in_ Input bit + * @param isEncrypted Is input bit encrypted? * @returns {number} */ export const crypto1_bit = (s: Crypto1State, in_: number, isEncrypted: boolean = false): number => { @@ -67,4 +67,20 @@ export const crypto1_bit = (s: Crypto1State, in_: number, isEncrypted: boolean = s.even ^= s.odd; s.odd ^= s.even; return ret; +} + +/** + * Proceed encryption/decryption process + * @param s State + * @param data Input data + * @param isIn Use input data as input word for keystream generation? + * @returns {number[]} + */ +export const encrypt = (s: Crypto1State, data: number[], isIn: boolean = false): number[] => { + let result: number[] = [] + for (let i = 0; i < data.length; i++) { + result[i] = data[i] ^ crypto1_byte(s, isIn ? data[i] : 0); + } + + return result } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 3ed7422..b3d62a2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ export { recovery32, recovery64, lfsr_recovery32, lfsr_recovery64, lfsr_rollback_bit, lfsr_rollback_byte, lfsr_rollback_word } from "./crapto1" export { Crypto1State } from "./state" -export { prng_successor, crypto1_bit, crypto1_byte, crypto1_word } from "./crypto1" \ No newline at end of file +export { prng_successor, crypto1_bit, crypto1_byte, crypto1_word, encrypt } from "./crypto1" \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 8ff1d64..62a8aa3 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -23,6 +23,9 @@ export const parity = (x: number): number => { return bit(0x6996, x & 0xf); } +/** + * Binary search for the first occurence of stop's MSB in sorted + */ export const binsearch = (data: number[], start: number, stop: number): number => { let mid: number, val: number = data[stop] & 0xff000000; while (start !== stop) { @@ -62,6 +65,9 @@ export const quicksort = (data: number[], start: number, stop: number): void => quicksort(data, rit + 1, stop); } +/** + * Helper, calculates the partial linear feedback contributions and puts in MSB + */ export const update_contribution = (data: number[], item: number, mask1: number, mask2: number): void => { let p: number = data[item] >>> 25; p = p << 1 | parity(data[item] & mask1); @@ -69,6 +75,9 @@ export const update_contribution = (data: number[], item: number, mask1: number, data[item] = p << 24 | (data[item] & 0xffffff); } +/** + * Using a bit of the keystream extend the table of possible lfsr states + */ export const extend_table = (data: number[], tbl: number, end: number, bit: number, m1: number, m2: number, in_: number): number => { in_ <<= 24; for (data[tbl] <<= 1; tbl <= end; data[++tbl] <<= 1) { @@ -90,6 +99,9 @@ export const extend_table = (data: number[], tbl: number, end: number, bit: numb return end; } +/** + * Using a bit of the keystream extend the table of possible lfsr states + */ export const extend_table_simple = (tbl: number[], end: number, bit: number): number => { let i = 0; for (tbl[i] <<= 1; i <= end; tbl[++i] <<= 1) { diff --git a/tests/index.test.ts b/tests/index.test.ts index 65e3002..87801d5 100644 --- a/tests/index.test.ts +++ b/tests/index.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test" -import { Crypto1State, recovery32, recovery64 } from "../src/" +import { Crypto1State, encrypt, lfsr_rollback_byte, recovery32, recovery64 } from "../src/" import { filter } from "../src/utils" test("Recovery by 2 auths", () => { @@ -64,3 +64,16 @@ test("State", () => { expect(s.peekCrypto1Bit).toBe(filter(s.odd)) } }) + +test("Encryption", () => { + let s = Crypto1State.fromKey(0x708076d3560en) + // Encrypt + expect(encrypt(s, [112, 147, 223, 153])).toEqual([48, 20, 167, 254]) + + // Rollback state + for(let i = 0; i < 4; i++) lfsr_rollback_byte(s, 0); + expect(s.lfsr).toBe(0x708076d3560en) + + // Decrypt + expect(encrypt(s, [48, 20, 167, 254])).toEqual([112, 147, 223, 153]) +}) \ No newline at end of file