Implementa un One-Time Pad


13

sfondo

Un pad una tantum è una forma di crittografia che è stata dimostrata impossibile da decifrare se usato correttamente.

La crittografia viene eseguita prendendo un testo in chiaro (composto solo da lettere AZ) e generando una stringa casuale sulla stessa lunghezza (anche solo lettere). Questa stringa funge da chiave. Ogni carattere nel testo in chiaro viene quindi associato al carattere corrispondente nella chiave. Il testo cifrato viene calcolato come segue: per ogni coppia, entrambi i caratteri vengono convertiti in numeri (A = 0, B = 1, ... Z = 25). I due numeri vengono aggiunti al modulo 26. Questo numero viene convertito nuovamente in un carattere.

La decrittazione è esattamente l'opposto. I caratteri nel testo cifrato e nella chiave vengono accoppiati e convertiti in numeri. La chiave viene quindi sottratta dal modulo 26 del testo cifrato e il risultato viene riconvertito in un carattere AZ.

La sfida

La tua sfida è quella di scrivere il programma più breve possibile in grado di crittografare e decrittografare un pad una tantum.

Sulla prima riga di input (verso STDIN), ci sarà la parola "ENCRYPT" o la parola "DECRYPT".

Se la parola è crittografata, la riga successiva sarà il testo in chiaro. Il programma dovrebbe generare due righe (su STDOUT), la prima è la chiave e la seconda il testo cifrato.

Se la parola è decrittografata, il tuo programma otterrà altre due righe di input. La prima riga sarà la chiave e la seconda riga sarà il testo cifrato. Il programma dovrebbe generare una riga, che sarà il testo in chiaro che è stato decifrato.

Il testo in chiaro, il testo cifrato e la chiave devono sempre essere composti da lettere maiuscole AZ. Saranno sempre un'unica riga e non contengono spazi bianchi.

La chiave dovrebbe essere sempre casuale. Nessuna grande parte di essa dovrebbe ripetersi tra le esecuzioni e non dovrebbero esserci motivi che possano essere trovati nel testo.

Due semplici esempi:

ENCRYPT
HAPPYBIRTHDAY
>ABKJAQLRJESMG
>HBZYYRTICLVME

DECRYPT
ABKJAQLRJESMG
HBZYYRTICLVME
>HAPPYBIRTHDAY

La >rappresenta che le linee sono in uscita, in modo da non dovete stampare quel simbolo come uscita.


7
Non per criticare la sfida su di essa la propria meriti (che vanno bene), ma io sto andando a criticare la crittografia qui. Quello che stai descrivendo è un "stream cipher" in quanto dipende da un PRNG (a meno che il tuo computer non abbia accesso a una sorgente o casualità reale (e se l'implementazione di Linux dei conteggi / dev / urandom è una questione di dibattito)), e avere la chiave sviluppata al momento della crittografia sconfigge l'unico uso davvero valido per un OTP che è il time shifting delle comunicazioni sicure.
dmckee --- ex-moderatore gattino

1
Inoltre, tutte le sfide sono indipendenti dalla lingua per impostazione predefinita, quindi ho rimosso quel tag.
dmckee --- ex-moderatore gattino

7
@dmckee Per quanto riguarda il tuo primo commento, sono d'accordo, motivo per cui non intendo utilizzare queste risposte per proteggere le mie comunicazioni.
PhiNotPi

1
sarebbe stato più divertente IMO lasciare fuori la casualità dal problema; data una fonte di casualità ( /dev/random, haveged), crittografare xoring degli ords con i byte e decrittografare xoring con la chiave. gist.github.com/5078264 la chiave o la casualità possono essere lette da stdin, il messaggio o il testo cifrato possono essere argomenti di nome file.
ixtmixilix

@PhiNotPi Ho un suggerimento. Perché non dare un bonus se usano una fonte veramente casuale (come /dev/hwrng, invece di usare lo pseudo casuale (che tecnicamente lo rende rotto)
PyRulez

Risposte:


8

GolfScript, 53 caratteri

n%(0=2%{~.,[{26rand 65+}*]:K]}*zip{{-}*~)26%65+}%K]n*

Questo è un compito per il quale GolfScript sembra perfettamente adatto.

Per farla breve, sto usando lo stesso codice sia per la crittografia che per la decrittografia: per decrittografare, sottraggo la chiave dal testo cifrato, mentre per la crittografia, prima di generare un testo cifrato casuale e quindi sottrarre il testo in chiaro da esso. Tuttavia, il codice aggiuntivo per l'implementazione della modalità di crittografia richiede poco più della metà della lunghezza del programma.

Versione de-golf con commenti:

n %             # split input into an array of lines

# KEY GENERATION FOR ENCRYPTION MODE:
(               # extract the first line from the array
0 = 2 %         # check if the first char of that line is odd (E = 69)...
{               # ...and execute this block if it is:
    ~           # dump the remaining lines (of which the should be only one) on the stack
    . ,         # calculate the length of the last line...
    [ { 26 rand 65 + } * ]  # ...make an array of that many random letters...
    :K          # ...and assign it to K
    ]           # collect all the lines, including K, back into an array
} *

# ENCRYPTION / DECRYPTION ROUTINE:
zip             # transpose the array of 2 n-char strings into n 2-char strings...
{               # ...and execute this block for each 2-char string:
    {-} *       # subtract the second char code from the first
    ~ )         # negate the result (using the two's complement trick -x = ~x+1)
    26 % 65 +   # reduce modulo 26 and add 65 = A
} %

# OUTPUT:
K ] n*         # join the result and K (if defined) with a newline, stringifying them

4

Rubino ( 200 185)

esecuzioni campione + wc:

$ ruby onetimepad.rb
ENCODE
ANOTHERTESTINPUTZZZ
ZYCLGHDWLDASFUTHWKC
BPMIBXOXTPTQIVBMDPX
$ ruby onetimepad.rb
DECODE
ZYCLGHDWLDASFUTHWKC
BPMIBXOXTPTQIVBMDPX
ANOTHERTESTINPUTZZZ
$ wc onetimepad.rb
       4       7     185 onetimepad.rb
def f;gets.scan(/./).map{|b|b.ord-65};end
s=->a{a.map{|b|(b+65).chr}*''}
r=->b,a,o{s[a.zip(b).map{|a,b|(a.send o,b)%26}]}
puts(gets=~/^D/?r[f,f,:+]:[s[k=(p=f).map{rand 26}],r[k,p,:-]])

s[k=(p=f).map{rand 26}],r[k,p,:-]dovrebbe essere scrittos[k=f.map{rand 26}],r[k,$_,:-]
Hauleth

@Hauleth no che non funziona, come $_è solo l'ultima riga letta da gets. flo fa anche .scan(/./).map{|b|b.ord-65}dopo aver letto una riga.
jsvnm,

3

Haskell, 203 caratteri

import Random
main=newStdGen>>=interact.(unlines.).(.lines).f.randomRs('A','Z')
f k['E':_,x]=[z const k x,z(e(+))k x]
f _[_,k,x]=[z(e(-))k x]
e(%)k x=toEnum$65+o x%o k`mod`26
o c=fromEnum c-65;z=zipWith

Esempio:

$ runghc OneTimePad.hs <<< $'ENCRYPT\nHELLOWORLD'
QMNQKGFZFD
XQYBYCTQQG
$ runghc OneTimePad.hs <<< $'DECRYPT\nQMNQKGFZFD\nXQYBYCTQQG'
HELLOWORLD

3

Perl, 220 171 caratteri

if(<>=~/D/){$_=<>;$w=<>;print chr((ord(substr$w,$i++,1)-ord$1)%26+65)while/(.)/g}else{$_=<>;$c.=chr((ord($1)-65+($i=rand(26)))%26+65),print chr$i+65while/(.)/g;print$/.$c}

Esempio di esecuzione:

ENCRYPT
HELLO
CCTKK
JGEVY

DECRYPT
CCTKK
JGEVY
HELLO

Nota: almeno quando lo eseguo, "Premere un tasto qualsiasi per continuare ..." viene aggiunto alla fine dell'ultimo output. Spero che vada bene, perché non fa parte del programma. In caso contrario, posso farcela in modo che appaia nella riga successiva.

Questo è il mio primo vero programma in Perl e il mio primo golf in assoluto, quindi apprezzerei molto i consigli. Inoltre, ho trovato /(.)/gsu Internet, ma non ho idea di come funzioni (è un'espressione regolare? Non ho ancora imparato quelli). Qualcuno può spiegarmelo?

EDIT: Grazie a Ilmari Karonen per avermi aiutato con le regexps, ho usato le mie nuove conoscenze per salvare 7 personaggi!

Versione ampliata, leggermente leggibile:

if(<>=~/D/){
    $_=<>;
    $w=<>;
    print chr((ord(substr$w,$i++,1)-ord$1)%26+65)while/(.)/g
}
else{
    $_=<>;
    $c.=chr((ord($1)-65+($i=rand(26)))%26+65),print chr$i+65while/(.)/g;
    print$/.$c
}

Sì, /(.)/gè una regexp. Avrai sicuramente voglia di imparare quelli se hai intenzione di giocare a Perl golf. perldoc.perl.org/perlre.html non è un brutto punto di partenza.
Ilmari Karonen,

2

Python - 304 295

import random
r=raw_input
R=lambda s:range(len(s))
o=lambda c:ord(c)-65
j=''.join
if r()[0]=='D':
 s=r()
 d=r()
 print j(chr((o(s[i])-o(d[i]))%26+65)for i in R(s))
else:
 s=r()
 d=[random.randint(0,26)for i in R(s)]
 print j(chr((o(s[i])+d[i])%26+65)for i in R(s))
 print j(chr(n+65)for n in d)

Credo che questo soddisfi esattamente le specifiche (incluso '>'all'inizio delle istruzioni di input.) Non convalida l'input, quindi penso che produrrà solo immondizia se gli dai caratteri al di fuori di [A-Z]. Controlla anche solo la prima lettera del comando di input. Qualunque cosa che inizia con D, comporterà una decrittografia e qualsiasi altra cosa comporterà una crittografia.


Non mi aspettavo che tu stampassi il >, lo stavo solo usando per dimostrare quali linee sono state stampate . Non devi implementarli.
PhiNotPi

Ok, bello, allora 9 personaggi in meno.
Gordon Bailey,

1

C ++ - 220 241 caratteri, 4 righe

#include<cstdlib>
#include<cstdio>
#define a scanf("%s"
char i,s[99],t[99];int main(){a,t);a,s);if(t[0]>68){for(;s[i];++i)s[i]=(s[i]+(t[i]=rand()%26+65))%26+65;puts(t);}else for(a,t);s[i];++i){s[i]=65+t[i]-s[i];if(s[i]<65)s[i]+=26;}puts(s);}

Modifica 1- La libreria standard MSVS sembra includere molti file non necessari, il che significa che iOS aveva tutte le inclusioni di cui avevo bisogno, ma questo non ha funzionato con altri compilatori. IOS modificato per i file effettivi in ​​cui le funzioni necessarie vengono visualizzate in cstdlib e cstdio. Grazie a Ilmari Karonen per averlo segnalato.


Non compilare per me: g++ otp.cppdiceotp.cpp: In function ‘int main()’: otp.cpp:3: error: ‘scanf’ was not declared in this scope otp.cpp:3: error: ‘rand’ was not declared in this scope otp.cpp:3: error: ‘puts’ was not declared in this scope otp.cpp:3: error: ‘puts’ was not declared in this scope
Ilmari Karonen,

Eh, è strano, io uso Visual Studio. Deve essere non standard per <ios> avere <conio.h> e <stdio.h> nelle sue inclusioni. Supponevo che le intestazioni includessero sempre gli stessi file su implementazioni diverse. Ci penserò più tardi, grazie.
Scott Logan,

1

Python - 270

import random
i=raw_input  
m=i()
a=i()
r=range(len(a))
o=ord
j=''.join
if m=='ENCRYPT':
  k=j(chr(65+random.randint(0,25)) for x in r)
  R=k+"\n"+j(chr((o(a[x])+o(k[x]))%26+65) for x in r)
elif m=='DECRYPT':
  k=i()
  R=j(chr((o(k[x])-o(a[x]))%26+65) for x in r)
print R

Uscita campione:

$ python onetimepad.py 
ENCRYPT
HELLOWORLD
UXCYNPXNNV
BBNJBLLEYY
$ python onetimepad.py 
DECRYPT
UXCYNPXNNV
BBNJBLLEYY
HELLOWORLD

Conteggio dei caratteri:

$ wc -c onetimepad.py 
270 onetimepad.py

1

J: 94 byte

3 :0]1
c=:(26&|@)(&.(65-~a.&i.))
r=:1!:1@1:
((],:+c)[:u:65+[:?26$~#)@r`(r-c r)@.('D'={.)r 1
)

Contava tutto lo spazio bianco necessario.

Versione commentata:

3 :0]1                                          NB. Make a function and call it
c=:(26&|@)(&.(65-~a.&i.))                       NB. Adverb for operating on the alphabet
                                                NB. (used for adding and subtracting the pad)
r=:1!:1@1:                                      NB. Read input line and decide (right to left)
((],:+c)[:u:65+[:?26$~#)@r   ` (r-c r)            @. ('D'={.)r 1
NB. Encryption (ger    0)    | Decryption (ger 1)| Agenda               
NB. pad,:(crypt=:plain + pad)| crypt - pad       | If D is first input, do (ger 1), else do (ger 0)
)

1

C # ( 445 416)

Dimenticato Aggregate. Tagliare un bel po '.

Un po 'golfato:

namespace G {
using System;
using System.Linq;
using x = System.Console;
class P {
    static void Main() {
        string p = "", c = "", k = "";
        Random r = new Random();
        int i = 0;
        if (x.ReadLine()[0] == 'E') {
            p = x.ReadLine();
            k=p.Aggregate(k,(l,_)=>l+(char)r.Next(65,90));
            c=p.Aggregate(c,(m,l)=>m+(char)((l+k[i++])%26+65));
            x.WriteLine(k + "\n" + c);
        } else {
            k = x.ReadLine();
            c = x.ReadLine();
            p=c.Aggregate(p,(l,a)=>l+(char)((a-k[i++]+26)%26+65));
            x.WriteLine(p);
        }
    }
}

}

golfed:

namespace G{using System;using System.Linq;using x=System.Console;class P{static void Main(){string p="",c="",k="";Random r=new Random();int i=0;if (x.ReadLine()[0]=='E'){p=x.ReadLine();k=p.Aggregate(k,(l,_)=>l+(char)r.Next(65,90));c=p.Aggregate(c,(m,l)=>m+(char)((l+k[i++])%26+65));x.WriteLine(k+"\n"+c);}else{k=x.ReadLine();c=x.ReadLine();p=c.Aggregate(p,(l,a)=>l+(char)((a-k[i++]+26)%26+65));x.WriteLine(p);}}}}

0

C (159 + 11 per flag del compilatore)

golfed:

d(a,b){return(a+b+26)%26+65;}a;char s[999],b,*c=s-1;main(){g;a=*s-69;g;while(*++c)a?b=-*c,*c=getchar():putchar(b=rand()%26+65),*c=d(*c,b);a||puts("");puts(s);}

Ungolfed:

d(a,b){
    //*a = (*a + b - 2*65 + 26) % 26 + 65; 
    return (a + b + 26) % 26 + 65;
}
a; char s[999], b, *c = s-1;
main(){
    gets(s);
    a = *s - 69; // -1 if decrypt 0 if encrypt
    gets(s);
    while(*++c){
        if(!a)
            putchar(b = rand() % 26 + 65); // 'A'
        else
            b = -*c, *c = getchar();
        *c = d(*c,b);
    }
    if(!a) puts("");
    puts(s);
}

Compila con -Dg=gets(s).

Esempio:

$./onetimepad
ENCRYPT
FOOBAR
>PHQGHU
>UVEHHL
$./onetimepad
DECRYPT
PHQGHU
UVEHHL
>FOOBAR

Ricevo la stessa chiave ogni volta che lo eseguo: non c'è casualità.
feersum

0

JavaScript 239

var F=String.fromCharCode
function R(l){var k='';while(l--)k+=F(~~(Math.random()*26)+65);return k}
function X(s,k,d){var o='',i=0,a,b,c
while(i<s.length)a=s.charCodeAt(i)-65,b=k.charCodeAt(i++)-65,c=d?26+(a-b):a+b,o+=F((c%26)+65)
return o}

Uso:

var str = "HELLOWORLD";
var key = R(str.length);
var enc = X(str, key, false);
console.log(enc);
console.log(X(enc,key, true));

0

Rubino - 184 179 177 caratteri

def g;gets.scan(/./).map{|c|c.ord-65}end
m,=g
k=(s=g).map{rand 26}
m==4?(puts k.map{|c|(c+65).chr}*'';y=:+):(k,s=s,g)
puts s.zip(k).map{|c,o|(c.send(y||:-,o).to_i%26+65).chr}*''

Eseguilo in questo modo: $ ruby pad-lock.rb

Ecco la versione non golfata se qualcuno è interessato (non è abbastanza aggiornato con quello del golf)

def prompt
    gets.scan(/./).map{ |c|c.ord - 65 }
end

mode = prompt[0]
operator = :-
secret = prompt
key = secret.map { |char| rand(26) }

if mode == 4 # the letter E, or ENCRYPT
    key.map { |char| print (char + 65).chr }
    puts
    operator = :+
else
    # make the old secret the new key,
    # and get a new secret (that has been encrypted)
    key, secret = secret, prompt
end

chars = secret.zip(key).map do |secret_char, key_char|

    # if mode == 4 (E) then add, otherwise subtract
    i = secret_char.send(operator, key_char).to_i

    ((i % 26) + 65).chr
end

puts chars.join("")
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.