Vigenere Cipher

From PostgreSQL wiki
Jump to navigationJump to search

Snippets

Vigenere Cipher

Works with PostgreSQL

9.4+

Written in

plpgsql

Depends on

Nothing


This is a simple implementation of an extended multialphabetic substitution Cipher leaned on the Vigenere Cipher. Plain text and key are transformed to base64 before encrypting, making it suitable for all encodings. The Vigenere Cipher is not suitable for usage as alternative to modern cryptographic algorithms. It is purely for fun.

CREATE OR REPLACE FUNCTION public.vigenere_encrypt (
  p_plain_text TEXT,
  p_key TEXT
)
RETURNS TEXT
AS $$
DECLARE
  v_alphabet TEXT := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  v_tab TEXT[] := regexp_split_to_array(v_alphabet,'');
  v_mod INTEGER := length(v_alphabet);
  v_key TEXT[];
  v_plain_text TEXT[];
  v_enc TEXT := '';
  v_i INTEGER;
  t_i INTEGER;
  e_i INTEGER;
  k_i INTEGER;
  v_dec TEXT := '';
BEGIN
  IF length(p_plain_text) = 0 THEN RETURN '';
  END IF;
  -- Text to base 64 without padding
  v_plain_text := regexp_split_to_array(replace(replace(encode(convert_to(p_plain_text,'utf8')::BYTEA, 'base64'::TEXT),E'\n',''),'=',''), '');
  -- Key to base 64 without padding
  v_key := regexp_split_to_array(replace(replace(encode(convert_to(p_key,'utf8')::BYTEA, 'base64'::TEXT),E'\n',''),'=',''),'');

  FOR v_i IN 0..(array_length(v_plain_text,1) - 1)
  LOOP
    t_i := position(v_plain_text[1 + v_i] IN v_alphabet) - 1;
    k_i := position(v_key[1 + v_i % array_length(v_key, 1)] IN v_alphabet) - 1;
    e_i := (t_i + k_i) % v_mod;

    v_enc := v_enc || v_tab[1 + (t_i + k_i) % v_mod];
    v_i := v_i + 1;
  END LOOP;
  RETURN v_enc;
END;
$$ LANGUAGE plpgsql
IMMUTABLE;
CREATE OR REPLACE FUNCTION public.vigenere_decrypt(
  p_enc_text TEXT,
  p_key TEXT
)
RETURNS TEXT
AS $$
DECLARE
  v_alphabet TEXT := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  v_tab TEXT[] := regexp_split_to_array(v_alphabet,'');
  v_mod INTEGER := length(v_alphabet);
  v_key TEXT[];
  v_enc_text TEXT[];
  v_i INTEGER;
  t_i INTEGER;
  k_i INTEGER;
  v_dec TEXT := '';
BEGIN
  IF length(p_enc_text) = 0 THEN RETURN '';
  END IF;

  v_enc_text := regexp_split_to_array(p_enc_text, '');
  -- Key to base 64 without padding
  v_key := regexp_split_to_array(replace(replace(encode(convert_to(p_key,'utf8')::BYTEA, 'base64'::TEXT),E'\n',''),'=',''),'');

  FOR v_i IN 0..(array_length(v_enc_text,1) - 1)
  LOOP
    t_i := position(v_enc_text[1 + v_i] IN v_alphabet) - 1;
    k_i := position(v_key[1 + v_i % array_length(v_key, 1)] IN v_alphabet) - 1;

    v_dec := v_dec || v_tab[1 + ((t_i - k_i + CASE WHEN t_i < k_i THEN v_mod ELSE 0 END) % v_mod)];
    v_i := v_i + 1;
  END LOOP;
  RETURN convert_from(decode(rpad(v_dec, (4 * ceiling(length(v_dec)::NUMERIC / 4.0))::INTEGER, '=')::TEXT,'base64'::TEXT),'utf8');
END;
$$ LANGUAGE plpgsql
IMMUTABLE;


Usage

db=> select * from public.vigenere_encrypt('Non parlo italiano. Я не говорю по-русски. Γνῶθι σεαυτόν.','terra γαμμαβετα Россия');
                                                   vigenere_encrypt
----------------------------------------------------------------------------------------------------------------------
 wsSgktFBPRA90ifwLBk3Eyzh+NafM+9B3qEV8Y9EUoBRg4lPXIgX4Uf2zd1parycuUC8Trydu9e7FRZstaXbx+6Ko9aV5sYrWJy+KiPjPebvU3NafHuq
(1 row)

db=> select * from public.vigenere_decrypt('wsSgktFBPRA90ifwLBk3Eyzh+NafM+9B3qEV8Y9EUoBRg4lPXIgX4Uf2zd1parycuUC8Trydu9e7FRZstaXbx+6Ko9aV5sYrWJy+KiPjPebvU3NafHuq','terra γαμμαβετα Россия');
                     vigenere_decrypt
-----------------------------------------------------------
 Non parlo italiano. Я не говорю по-русски. Γνῶθι σεαυτόν.
(1 row)

Caution

This kind of cipher is not secure enough to be used in a critical situation. Use it only for fun.