Mettre en œuvre un bloc unique

13

Contexte

Un tampon à usage unique est une forme de cryptage qui s'est avérée impossible à casser s'il est utilisé correctement.

Le chiffrement est effectué en prenant un texte en clair (composé uniquement de lettres AZ) et en générant une chaîne aléatoire sur la même longueur (également uniquement des lettres). Cette chaîne sert de clé. Chaque caractère du texte en clair est ensuite associé au caractère correspondant dans la clé. Le texte chiffré est calculé comme suit: Pour chaque paire, les deux caractères sont convertis en nombres (A = 0, B = 1, ... Z = 25). Les deux nombres sont ajoutés modulo 26. Ce nombre est reconverti en caractère.

Le déchiffrement est exactement le contraire. Les caractères du texte chiffré et de la clé sont appariés et convertis en nombres. La clé est ensuite soustraite du module de texte chiffré 26, et le résultat est reconverti en un caractère AZ.

Le défi

Votre défi est d'écrire le programme le plus court possible qui peut à la fois crypter et décrypter un pad à usage unique.

Sur la première ligne d'entrée (vers STDIN), il y aura soit le mot "ENCRYPT" soit le mot "DECRYPT".

Si le mot est crypté, la ligne suivante sera le texte en clair. Votre programme doit produire deux lignes (vers STDOUT), la première étant la clé et la seconde le texte chiffré.

Si le mot est déchiffré, votre programme recevra deux lignes d'entrée supplémentaires. La première ligne sera la clé et la deuxième ligne sera le texte chiffré. Votre programme doit sortir une ligne, qui sera le texte en clair qui a été déchiffré.

Le texte en clair, le texte chiffré et la clé doivent toujours être constitués de lettres majuscules AZ. Ils seront toujours une seule ligne et ne contiendront aucun espace.

La clé doit toujours être aléatoire. Aucune grande partie de celui-ci ne doit se répéter entre les exécutions et aucun motif ne doit être trouvé dans le texte.

Deux exemples simples:

ENCRYPT
HAPPYBIRTHDAY
>ABKJAQLRJESMG
>HBZYYRTICLVME

DECRYPT
ABKJAQLRJESMG
HBZYYRTICLVME
>HAPPYBIRTHDAY

Le >représente les lignes qui sont sorties, vous n'avez donc pas besoin d'imprimer ce symbole comme sortie.

PhiNotPi
la source
7
De ne pas critiquer le défi sur ses propres mérites (qui sont très bien), mais je vais aller à critiquer la cryptographie ici. Ce que vous décrivez est un "chiffrement de flux" car il dépend d'un PRNG (à moins que votre ordinateur n'ait accès à une source ou à un réel hasard (et si l'implémentation linux de / dev / urandom fait l'objet d'un débat)), et avoir la clé développée au moment du chiffrement défait la seule très bonne utilisation pour un OTP qui est le décalage temporel des communications sécurisées.
dmckee --- chaton ex-modérateur
1
De plus, tous les défis sont indépendants de la langue par défaut, j'ai donc supprimé cette balise.
dmckee --- chaton ex-modérateur
7
@dmckee Concernant votre premier commentaire, je suis d'accord, c'est pourquoi je n'ai pas l'intention d'utiliser ces réponses pour sécuriser mes communications.
PhiNotPi
1
cela aurait été plus amusant IMO de laisser le hasard hors du problème; étant donné une source de caractère aléatoire ( /dev/random, haveged), chiffrer en xorant les ords avec les octets et déchiffrer en les xorant avec la clé. gist.github.com/5078264 la clé ou l'aléatoire peut être lu depuis stdin, le message ou le cryptogramme peut être un argument de nom de fichier.
ixtmixilix
@PhiNotPi J'ai une suggestion. Pourquoi ne pas donner un bonus s'ils utilisent une source vraiment aléatoire (comme /dev/hwrng, au lieu d'utiliser un pseudo aléatoire (ce qui le rend techniquement cassé))
PyRulez

Réponses:

8

GolfScript, 53 caractères

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

C'est une tâche pour laquelle GolfScript semble à peu près parfaitement adapté.

Pour garder le code court, j'utilise le même code à la fois pour le chiffrement et le déchiffrement: pour déchiffrer, je soustrais la clé du texte chiffré, tandis que pour le chiffrement, je génère d'abord un texte chiffré aléatoire, puis j'en soustrais le texte en clair. Même ainsi, le code supplémentaire pour implémenter le mode de cryptage prend un peu plus de la moitié de la longueur du programme.

Version dé-golfée avec commentaires:

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
Ilmari Karonen
la source
4

Ruby ( 200 185)

échantillon exécute + 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,:-]])
jsvnm
la source
s[k=(p=f).map{rand 26}],r[k,p,:-]devrait être écrits[k=f.map{rand 26}],r[k,$_,:-]
Hauleth
@Hauleth non, cela ne fonctionne pas, tout comme $_la dernière ligne lue par gets. ffait aussi .scan(/./).map{|b|b.ord-65}après avoir lu une ligne.
jsvnm
3

Haskell, 203 caractères

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

Exemple:

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

Perl, 220 171 caractères

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}

Exemple d'exécution:

ENCRYPT
HELLO
CCTKK
JGEVY

DECRYPT
CCTKK
JGEVY
HELLO

Remarque: au moins lorsque je l'exécute, "Appuyez sur n'importe quelle touche pour continuer ..." est ajouté à la fin de la dernière sortie. J'espère que ça va, car cela ne fait pas partie du programme. Sinon, je peux le faire apparaître sur la ligne suivante.

Ceci est mon premier vrai programme en Perl et mon tout premier golf, donc j'apprécierais vraiment les conseils. De plus, j'ai trouvé /(.)/gsur Internet, mais je n'ai aucune idée de comment cela fonctionne (est-ce une expression régulière? Je ne les ai pas encore apprises). Quelqu'un peut-il me l'expliquer?

EDIT: Merci à Ilmari Karonen de m'avoir aidé avec les regexps, j'ai utilisé mes nouvelles connaissances pour sauver 7 personnages!

Version étendue, légèrement lisible:

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
}
commando
la source
Oui, /(.)/gc'est une expression rationnelle. Vous voudrez certainement les apprendre si vous voulez jouer au golf Perl. perldoc.perl.org/perlre.html n'est pas un mauvais point de départ.
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)

Je crois que cela correspond exactement aux spécifications (y compris '>'au début des invites d'entrée.) Cela ne valide pas l'entrée, donc je pense que cela produira simplement une sortie poubelle si vous lui donnez des caractères en dehors de [A-Z]. Il vérifie également uniquement la première lettre de la commande d'entrée. Tout ce qui commence par Dentraînera un déchiffrement et toute autre chose entraînera un chiffrement.

Gordon Bailey
la source
Je ne m'attendais pas à ce que vous imprimiez le >, je l'utilisais simplement pour montrer quelles lignes étaient sorties. Vous n'avez pas à les implémenter.
PhiNotPi
Ok, cool, 9 personnages de moins alors.
Gordon Bailey
1

C ++ - 220 241 caractères, 4 lignes

#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);}

Edit 1- La bibliothèque standard MSVS semble inclure beaucoup de fichiers inutiles, ce qui signifie qu'ios avait toutes les inclusions dont j'avais besoin, mais cela ne fonctionnait pas avec d'autres compilateurs. Ios modifié pour les fichiers réels que les fonctions nécessaires apparaissent dans cstdlib et cstdio. Merci à Ilmari Karonen de l'avoir signalé.

Scott Logan
la source
Ne compile pas pour moi: g++ otp.cppditotp.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
C'est étrange, j'utilise Visual Studio. Il doit être non standard pour <ios> d'avoir <conio.h> et <stdio.h> dans ses inclusions. J'ai supposé que les en-têtes comprenaient toujours les mêmes fichiers sur différentes implémentations. J'y reviendrai plus tard, merci.
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

Exemple de sortie:

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

Nombre de caractères:

$ wc -c onetimepad.py 
270 onetimepad.py
tomcant
la source
1

J: 94 octets

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

Tous les espaces blancs nécessaires ont été comptés.

Version commentée:

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)
)
jpjacobs
la source
1

C # ( 445 416)

J'ai oublié Aggregate. Coupez un peu.

Assez joué au golf:

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);
        }
    }
}

}

Golfé:

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);}}}}
Farami
la source
0

C (159 + 11 pour les drapeaux du compilateur)

Golfé:

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);}

Non golfé:

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);
}

Compilez avec -Dg=gets(s).

Exemple:

$./onetimepad
ENCRYPT
FOOBAR
>PHQGHU
>UVEHHL
$./onetimepad
DECRYPT
PHQGHU
UVEHHL
>FOOBAR
es1024
la source
J'obtiens la même clé chaque fois que je l'exécute - il n'y a pas de hasard.
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}

Usage:

var str = "HELLOWORLD";
var key = R(str.length);
var enc = X(str, key, false);
console.log(enc);
console.log(X(enc,key, true));
marteau-de-loup
la source
0

Ruby - 184 179 177 caractères

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}*''

Exécutez-le comme ceci: $ ruby pad-lock.rb

Voici la version non golfée si quelqu'un est intéressé (elle n'est pas tout à fait à jour avec celle du 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("")
addison
la source