Я углубляю свое понимание криптографии. Хотя мне, вероятно, следовало начать это 20 лет назад (или даже в 2014 году, когда Bit Coin начала шуметь), сейчас самое время продолжать учиться. Один из текстов, который я выбрал для начала, - Понимание криптографии. План состоит в том, чтобы запачкать руки и написать код вместе с текстом (чтобы доказать себе минимальную компетентность). Мое любимое оружие (в течение последних трех лет) - JavaScript - и хотя он отвечал задаче первого шифра, не обошлось без проблем.

Алгоритм берет алфавит, смещение и текст и заменяет каждый символ в тексте значением из алфавита, сдвинутым на количество символов в смещении. Если смещение проходит мимо конца алфавита, оно возвращается к началу. Чтобы зашифровать, мы берем символ и находим его числовое значение (назовем это x), добавляем к нему смещение (назовем это k) и модуль деления на количество символов в алфавите (назовем это m). Следовательно

encrypt(x) = (x + k) mod m

Расшифровка аналогична

decrypt(x) = (x - k) mod m

Тривиальность решения (которую я сначала подтвердил моделированием в Excel) разлетелась на части через 10 минут после начала упражнения, и оказалось, что есть некоторая двусмысленность по модулю в отношении отрицательных чисел. Есть две реализации, и если вы не используете правильную, ничего не пойдет. Как оказалось, оператор по модулю javascript не является правильной реализацией для этого варианта использования. Поскольку это был первый раз, когда я столкнулся с этой странностью по модулю, потребовалось некоторое исследование, чтобы решить (спасибо Википедии). Я определил свою собственную функцию по модулю:

const quotient = (dividend,divisor) => 
      Math.floor(dividend/divisor)

const floor_modulo = (dividend,divisor) => 
      dividend - divisor * quotient(dividend,divisor)

с множеством других помощников (чтобы мой алгоритм легко читался) я получил следующее решение:

const {reduce, pipe, curry, find} = 
       require('../helpers/functional-bits')
const {maxOr, increment, appendToObj, appendToArray, join, length,
    floor_modulo, numberOfKeys, values} = require('../helpers/common-bits')

const maxOrNegOne = maxOr(-1)

const alphabet = 'abcdefghijklmnopqrstuvwxyz'

// arrayToAlphabetMap :: [k] -> {k: Number}
const arrayToAlphabetMap = reduce(
    (map, a) => appendToObj(map, a, getNextValue(map)),
    {}
)

// getNextValue :: {character: Number} -> Number
const getNextValue = pipe([
    values,
    maxOrNegOne,
    increment
])

// enryptCharacter  :: ({character: Number} -> Number -> Number -> 
//     character -> character) -> {character: Number} -> Number -> 
//     Number -> [character] -> [character]
const applyWith = curry((algo, alphabetMap, k, m, xs) =>
    reduce(
        (arr, x) => appendToArray(
        arr, algo(alphabetMap, k, m, x))
        ,[]
        , xs
    )
)

// enryptCharacter  :: {character: Number} -> Number -> Number -> 
//     character -> character
const encryptCharacter = curry((alphabetMap, k, m, x) => {
    const rawValue = alphabetMap[x]
    const shiftValue = floor_modulo((rawValue + k), m)
    const shiftCharacter = find(alphabetMap, shiftValue)
    return shiftCharacter
})

// decryptCharacter  :: {character: Number} -> Number -> Number -> 
//      character -> character
const decryptCharacter = curry((alphabetMap, k, m, x) => {
    const shiftValue = alphabetMap[x]
    const rawValue = floor_modulo((shiftValue - k), m)
    const rawCharacter = find(alphabetMap, rawValue)
    return rawCharacter
})

// shiftCipher :: string, Number -> 
//      { encrypt :: string -> string, decrypt :: string -> string }
const shiftCipher = (alphabet, k) => {
    const alphabetArray = alphabet.split('')
    const alphabetMap = arrayToAlphabetMap(alphabetArray)
    const m = numberOfKeys(alphabetMap)

    return {
        encrypt: pipe([
            applyWith(encryptCharacter, alphabetMap, k, m),
            join
        ]),
        decrypt: pipe([
            applyWith(decryptCharacter, alphabetMap, k, m),
            join
        ]),
    }
}

const raw = 'attack'
const cipher = shiftCipher(alphabet, 17)

const secret = cipher.encrypt(raw)
//=> rkkrtb

const raw2 = cipher.decrypt(secret)
//=> attack

Обратите внимание: сопротивляйтесь зуду - это небезопасный криптографический алгоритм. Но, пожалуйста, поиграйте с кодом (его можно найти здесь). Также есть код? Я хотел бы получить известие от вас - напишите мне!

Обо мне



Первоначально опубликовано на https://www.linkedin.com.