일회용 패드 구현


13

배경

한 번 패드를 적절하게 사용하면 해킹하는 것은 불가능 입증 된 암호화의 한 형태이다.

암호화는 평문 (문자 AZ만으로 구성됨)을 취하고 같은 길이 (문자 만)로 임의의 문자열을 생성하여 수행됩니다. 이 문자열은 키 역할을합니다. 그런 다음 일반 텍스트의 각 문자가 키의 해당 문자와 ​​쌍을 이룹니다. 암호문은 다음과 같이 계산됩니다. 각 쌍에 대해 두 문자 모두 숫자로 변환됩니다 (A = 0, B = 1, ... Z = 25). 이 두 숫자는 모듈로 26에 추가됩니다.이 숫자는 다시 문자로 변환됩니다.

해독은 정반대입니다. 암호문과 키의 문자가 쌍을 이루고 숫자로 변환됩니다. 그 후, 키는 암호문 모듈러스 (26)로부터 차감되고, 결과는 문자 AZ로 다시 변환된다.

도전

당신의 도전은 일회용 패드를 암호화하고 해독 할 수있는 가장 짧은 프로그램을 작성하는 것입니다.

첫 번째 입력 행 (STDIN으로)에는 "ENCRYPT"또는 "DECRYPT"라는 단어가 있습니다.

단어가 암호화 된 경우 다음 줄은 일반 텍스트입니다. 프로그램은 두 줄 (STDOUT에)을 출력해야합니다. 첫 번째는 키이고 두 번째는 암호문입니다.

단어의 암호가 해독되면 프로그램에서 두 줄을 더 입력합니다. 첫 번째 줄은 키가되고 두 ​​번째 줄은 암호문이됩니다. 프로그램은 해독 된 평문이 될 한 줄을 출력해야합니다.

평문, 암호문 및 키는 항상 대문자 AZ로 구성되어야합니다. 그것들은 항상 한 줄이며 공백을 포함하지 않습니다.

키는 항상 임의적이어야합니다. 런 사이에 큰 부분을 반복해서는 안되며 텍스트에서 찾을 수있는 패턴이 없어야합니다.

두 가지 간단한 예 :

ENCRYPT
HAPPYBIRTHDAY
>ABKJAQLRJESMG
>HBZYYRTICLVME

DECRYPT
ABKJAQLRJESMG
HBZYYRTICLVME
>HAPPYBIRTHDAY

>당신이 출력으로 그 기호를 인쇄하지 않아도, 출력되는 라인이다.


7
하지 (괜찮습니다) 그것의 자신의 장점에 대한 도전을 비판하는,하지만 난 하고 여기에 암호화를 비판하는 것이다. PRNG에 따라 "스트림 암호"라고 설명합니다 (컴퓨터가 소스 또는 실제 임의성에 액세스 할 수없는 경우 (그리고 / dev / urandom counts의 Linux 구현이 일부 토론의 문제인 경우 제외)). 암호화 시간에 키를 개발하면 보안 통신의 시간 이동 인 OTP에 대한 유일한 사용을 물리 칠 수 있습니다.
dmckee --- 전 변조 고양이 새끼

1
또한 모든 과제는 기본적으로 언어에 구애받지 않으므로 해당 태그를 제거했습니다.
dmckee --- 전 변조 고양이 새끼

7
@dmckee 첫 번째 의견과 관련하여, 나는 동의를 얻지 않기 때문에 이러한 답변을 사용하지 않을 것입니다.
PhiNotPi

1
이것은 문제에서 임의성을 제거하는 것이 더 재미있는 IMO 일 것입니다. 임의의 소스 ( /dev/random, haveged)가 제공된 경우 , ord를 바이트로 xoring하여 암호화하고 키로 xoring하여 해독하십시오. gist.github.com/5078264 stdin 에서 키 또는 임의성을 읽을 수 있으며 메시지 또는 암호문은 파일 이름 인수 일 수 있습니다.
ixtmixilix

@PhiNotPi 제안이 있습니다. 그들이 /dev/hwrng의사 난수를 사용하는 대신 (기술적으로 고장난) 대신에 무작위로 무작위 소스를 사용한다면 보너스를주지 않겠습니까?
PyRulez

답변:


8

GolfScript, 53 자

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

이것은 GolfScript가 완벽하게 적합한 것으로 보이는 작업입니다.

코드를 짧게 유지하려면 암호화와 암호 해독에 동일한 코드를 사용하고 있습니다. 해독하려면 암호문에서 키를 빼고 암호화를 위해 먼저 임의 암호문을 생성 한 다음 평문을 빼십시오. 그럼에도 불구하고 암호화 모드를 구현하기위한 추가 코드는 프로그램 길이의 절반 이상을 차지합니다.

주석이 포함 된 골프 골드 버전 :

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

루비 ( 200 185)

샘플 실행 + 화장실 :

$ 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,:-]작성해야합니다s[k=f.map{rand 26}],r[k,$_,:-]
Hauleth

@Hauleth no는 님이 $_마지막으로 읽은 것처럼 작동하지 않습니다 gets. f또한 .scan(/./).map{|b|b.ord-65}줄을 읽은 후 수행합니다.
jsvnm

3

하스켈, 203 자

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

예:

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

3

펄, (220) 171 자

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}

샘플 실행 :

ENCRYPT
HELLO
CCTKK
JGEVY

DECRYPT
CCTKK
JGEVY
HELLO

참고 : 적어도 내가 그것을 실행할 때 "계속하려면 아무 키나 누르십시오 ..."가 마지막 출력의 끝에 추가됩니다. 프로그램의 일부가 아니기 때문에 이것이 좋기를 바랍니다. 그렇지 않으면 다음 줄에 표시되도록 할 수 있습니다.

이것은 Perl의 첫 번째 실제 프로그램이며 첫 번째 골프이므로 팁을 많이 주시면 감사하겠습니다. 또한 /(.)/g인터넷에서 찾았 지만 그것이 어떻게 작동하는지 전혀 알지 못합니다 (정규 표현입니까? 아직 배우지 못했습니다). 누군가 나에게 설명 할 수 있습니까?

편집 : 정규식으로 나를 도와 준 Ilmari Karonen에게 감사드립니다. 나는 새로운 지식을 사용하여 7자를 절약했습니다!

약간 읽을 수있는 확장 된 버전 :

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
}

예, /(.)/g정규 표현식입니다. 펄 골프를하려고한다면 꼭 배우고 싶을 것입니다. perldoc.perl.org/perlre.html 은 나쁜 출발점이 아닙니다.
Ilmari Karonen

2

파이썬 -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)

나는 이것이 입력 사양 의 시작 부분을 포함하여'>' 정확하게 사양을 충족한다고 생각합니다 . 입력의 유효성을 검사하지 않으므로 외부에 문자를 주면 가비지 출력 만 생성한다고 생각합니다 [A-Z]. 또한 입력 명령의 첫 글자 만 확인합니다. 시작 D하는 것은 해독을 초래하고 다른 것은 암호화를 초래합니다.


나는 당신이을 인쇄 할 것이라고 기대하지 않았고 >, 단지 그것을 사용하여 출력 된 줄을 보여주기 위해 사용했습니다. 그것들을 구현할 필요는 없습니다.
PhiNotPi

좋아, 시원하고 9 자 적은 문자.
Gordon Bailey

1

C ++ - 220 개 241 문자, 4 개 라인

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

편집 1-MSVS 표준 라이브러리에는 불필요한 파일이 많이 포함되어있는 것 같습니다. 즉, ios에 필요한 모든 포함 기능이 있지만 다른 컴파일러에서는 작동하지 않습니다. 함수가 필요로하는 실제 파일에 대한 변경된 iOS는 cstdlib 및 cstdio에 나타납니다. 이것을 지적 해 주신 Ilmari Karonen에게 감사드립니다.


나를 위해 컴파일하지 않습니다 : g++ otp.cpp말합니다otp.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

허, 이상합니다. 저는 비주얼 스튜디오를 사용합니다. <ios>가 <conio.h>와 <stdio.h>를 포함하도록하려면 비표준이어야합니다. 헤더는 항상 다른 구현에서 동일한 파일을 포함한다고 가정했습니다. 나중에 살펴 볼게요, 고마워
Scott Logan

1

파이썬-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

샘플 출력 :

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

문자 수 :

$ wc -c onetimepad.py 
270 onetimepad.py

1

J : 94 바이트

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

필요한 모든 공백이 계산되었습니다.

댓글 버전 :

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 # ( 445416 )

집계에 대해 잊어 버렸습니다. 좋은 비트를 차단하십시오.

다소 골프 :

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

}

골프 :

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)

골프 :

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

언 골프 드 :

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

로 컴파일하십시오 -Dg=gets(s).

예:

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

실행할 때마다 동일한 키를 얻습니다. 임의성이 없습니다.
feersum

0

자바 스크립트 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}

용법:

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

0

루비 - 184 개 179 177 문자

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

다음과 같이 실행하십시오. $ ruby pad-lock.rb

관심있는 사람이 있다면 ungolfed 버전입니다 (골프 버전은 최신 버전이 아닙니다)

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("")
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.