Explaining functions of Vigenère cipher

2019-08-19 04:34发布

i found the following code at http://rosettacode.org for the Vigenère cipher and i would like to undestand it better.

Could someone explain me what the single lines of code in function ordA(a) and in function(a) do?

function ordA(a) {
  return a.charCodeAt(0) - 65;
}

// vigenere
function vigenere2(text, key, decode) {
  var i = 0, b;
  key = key.toUpperCase().replace(/[^A-Z]/g, '');
  return text.toUpperCase().replace(/[^A-Z]/g, '').replace(/[A-Z]/g, function(a) {
    b = key[i++ % key.length];
    return String.fromCharCode(((ordA(a) + (decode ? 26 - ordA(b) : ordA(b))) % 26 + 65));
  });
}

1条回答
闹够了就滚
2楼-- · 2019-08-19 05:02

I'm not sure if that is supposed to be example code, but it mainly shows how not to program. Smart decisions are being made, but obviously the problem decomposition, variable naming and documentation leave a lot to be desired. Repeated code, convoluted lines, unexplained code fragments, the list goes on. Decode is a boolean, but the opposite of encryption is decryption not decoding. This code was made to not understand what is going on; what it does on the Rosetta site is mind-boggling in that respect.


returns an index in the English alphabet or ABC, assuming uppercase characters, 0 to 25 instead of 1 to 26 (because you can do modular calculations with zero indexing, not with one based indexing)

return a.charCodeAt(0) - 65;

function definition that takes a plaintext or ciphertext, a key which may be smaller than the plaintext and a Boolean to indicate if we're encoding or decoding

function vigenere2(text, key, decode) 

index in plaintext and variable b, which will hold a character of the key for the index

var i = 0, b;

converts the key to uppercase and removed all characters not in the uppercase alphabet as well

key = key.toUpperCase().replace(/[^A-Z]/g, '');

this line is too long obviously; it converts the text to uppercase and removes the non-alphabet characters again

then it replaces the characters in the string using the function defined in the second argument of replace

return text.toUpperCase().replace(/[^A-Z]/g, '').replace(/[A-Z]/g, function(a) {

take the next character of the key in round robin fashion, using the modulus operator, update the index afterwards

b = key[i++ % key.length];

too much going on here, very bad program decomposition; in order of execution:

  • (decode ? 26 - ordA(b) : ordA(b)): calculate a number in the range to update the index of the plaintext character; use the opposite value for decryption (wrongly called "decoding" here)
  • (ordA(a) + (decode ? 26 - ordA(b) : ordA(b))) % 26 perform the addition with the calculated number, reduce to 0 to 25 (i.e. when reaching Z continue with A and vice versa)
  • ((ordA(a) + (decode ? 26 - ordA(b) : ordA(b))) % 26 + 65) add 65 so the index is converted back into the ASCII index of uppercase characters, using two completely spurious parentheses
  • finally, returns a string from one character code result, otherwise + will be addition instead of concatenation
return String.fromCharCode(((ordA(a) + (decode ? 26 - ordA(b) : ordA(b))) % 26 + 65));

well, it needed to end

  });
}

Let's show another way of programming this, using well named variables, functions for reused code and regular expressions that badly need a name to explain what they do.

var ALPHABET_SIZE = 'Z'.charCodeAt(0) - 'A'.charCodeAt(0) + 1;

var encrypted = vigenere(false, "B", "Zaphod Breeblebox");
document.body.append('<div>' + encrypted + '</div>');
var decrypted = vigenere(true, "B", encrypted);
document.body.append('<div>' + decrypted + '</div>');

function vigenere(decrypt, key, text) {
    key = toJustUppercase(key);
    text = toJustUppercase(text);
  
    var textOffset = 0;
    // iterate over all characters, performing the function on each of them
    return text.replace(/[A-Z]/g, function(textChar) {
        var keyChar = key[textOffset++ % key.length];
        var cryptedChar = substituteCharacter(decrypt, keyChar, textChar);
        return cryptedChar;
    });
}

function substituteCharacter(decrypt, keyChar, textChar) {
    var keyIndex = charToABCIndex(keyChar);
    if (decrypt) {
        // create the opposite of the encryption key index
        keyIndex = ALPHABET_SIZE - keyIndex;
    }
    
    var textIndex = charToABCIndex(textChar);

    // the actual Vigenere substitution, the rest is just indexing and conversion
    var substitutedIndex = (textIndex + keyIndex) % ALPHABET_SIZE;
      
    var substitutedChar = abcIndexToChar(substitutedIndex);
    return substitutedChar;
}

function toJustUppercase(text) {
    return text.toUpperCase().replace(/[^A-Z]/g, '')
}

function charToABCIndex(charValue) {
    return charValue.charCodeAt(0) - 'A'.charCodeAt(0);
}

function abcIndexToChar(index) {
    return String.fromCharCode(index + 'A'.charCodeAt(0));
}

Too many functions you say? Not really, I've not implemented ord and chr, or vigenereEncrypt and viginereDecrypt to make it even easier to read.

查看更多
登录 后发表回答