ปรับใช้ One-Time Pad


13

พื้นหลัง

แผ่นเพียงครั้งเดียวเป็นรูปแบบของการเข้ารหัสที่พิสูจน์แล้วว่าเป็นไปไม่ได้ที่จะถอดรหัสหากใช้อย่างเหมาะสม

การเข้ารหัสจะดำเนินการโดยใช้ข้อความธรรมดา (ประกอบด้วยตัวอักษร AZ เท่านั้น) และสร้างสตริงแบบสุ่มที่ความยาวเท่ากัน (รวมถึงตัวอักษรเท่านั้น) สายนี้ทำหน้าที่เป็นกุญแจสำคัญ อักขระแต่ละตัวในข้อความธรรมดาจะถูกจับคู่กับอักขระที่สอดคล้องกันในคีย์ ciphertext ถูกคำนวณดังนี้: สำหรับแต่ละคู่อักขระทั้งสองจะถูกแปลงเป็นตัวเลข (A = 0, B = 1, ... Z = 25) ตัวเลขทั้งสองจะถูกเพิ่มแบบโมดูโล 26 หมายเลขนี้คือการแปลงกลับไปเป็นตัวละคร

การถอดรหัสเป็นสิ่งที่ตรงกันข้าม อักขระใน ciphertext และคีย์ถูกจับคู่และแปลงเป็นตัวเลข คีย์จะถูกลบออกจาก ciphertext modulo 26 และผลลัพธ์จะถูกแปลงกลับไปเป็นอักขระ AZ

ความท้าทาย

ความท้าทายของคุณคือการเขียนโปรแกรมที่สั้นที่สุดที่สามารถเข้ารหัสและถอดรหัสแผ่นเพียงครั้งเดียว

ในบรรทัดแรกของอินพุต (ถึง STDIN) จะมีคำว่า "ENCRYPT" หรือคำว่า "DECRYPT"

หากคำนั้นถูกเข้ารหัสแล้วบรรทัดถัดไปจะเป็นข้อความธรรมดา โปรแกรมของคุณควรเอาต์พุตสองบรรทัด (ไปยัง STDOUT), อันแรกคือกุญแจ, และอันที่สองคือ ciphertext

หากคำนั้นถอดรหัสโปรแกรมของคุณจะได้รับสองบรรทัดเพิ่มเติม บรรทัดแรกจะเป็นคีย์และบรรทัดที่สองจะเป็นไซเฟอร์เท็กซ์ คุณควรโปรแกรมออกหนึ่งบรรทัดซึ่งจะเป็นข้อความธรรมดาที่ได้รับการถอดรหัส

ข้อความธรรมดา, ไซเฟอร์เท็กซ์และคีย์ควรประกอบด้วยตัวอักษรตัวพิมพ์ใหญ่ AZ เสมอ พวกเขาจะเป็นบรรทัดเดียวเสมอและไม่มีช่องว่าง

ที่สำคัญควรเป็นแบบสุ่ม ไม่มีส่วนใหญ่ที่ควรทำซ้ำระหว่างการวิ่งและไม่ควรมีรูปแบบที่สามารถพบได้ในข้อความ

ตัวอย่างง่ายๆสองตัวอย่าง:

ENCRYPT
HAPPYBIRTHDAY
>ABKJAQLRJESMG
>HBZYYRTICLVME

DECRYPT
ABKJAQLRJESMG
HBZYYRTICLVME
>HAPPYBIRTHDAY

>หมายถึงเส้นที่มีการส่งออกเพื่อให้คุณไม่ต้องพิมพ์สัญลักษณ์ที่เป็นผลผลิต


7
ไม่ได้ที่จะวิพากษ์วิจารณ์ความท้าทายที่มันเป็นบุญของตัวเอง (ซึ่งจะมีการปรับ) แต่ฉันกำลังจะไปวิพากษ์วิจารณ์การเข้ารหัสที่นี่ สิ่งที่คุณกำลังอธิบายคือ "กระแสรหัส" ตามมันขึ้นอยู่กับ PRNG (ยกเว้นกรณีที่คอมพิวเตอร์ของคุณมีการเข้าถึงแหล่งที่มาหรือการสุ่มจริง (และหากการใช้งานลินุกซ์ของนับ / dev / urandom เป็นเรื่องของการอภิปราย การมีคีย์ที่พัฒนาขึ้นเมื่อเวลาการเข้ารหัสเอาชนะการใช้งานที่ดีเพียงอย่างเดียวสำหรับ OTP ซึ่งเป็นการเปลี่ยนเวลาของการสื่อสารที่ปลอดภัย
dmckee --- ผู้ดูแลอดีตลูกแมว

1
นอกจากนี้ความท้าทายทั้งหมดเป็นภาษาที่ไม่เชื่อเรื่องพระเจ้าตามค่าเริ่มต้นดังนั้นฉันจึงลบแท็กนั้นออก
dmckee --- ผู้ดูแลอดีตลูกแมว

7
@dmckee เกี่ยวกับความคิดเห็นแรกของคุณฉันเห็นด้วยซึ่งเป็นสาเหตุที่ฉันไม่ต้องการใช้คำตอบเหล่านี้เพื่อความปลอดภัยในการสื่อสารของฉัน
PhiNotPi

1
นี่จะเป็นเรื่องสนุกมากขึ้นที่ IMO จะไม่ปล่อยให้ปัญหาเกิดขึ้น กำหนดแหล่งที่มาของการสุ่ม ( /dev/random, haveged), เข้ารหัสโดย xoring the ords ด้วยไบต์และถอดรหัสโดย 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 ดูเหมือนจะเหมาะสมที่สุด

เพื่อให้รหัสสั้นฉันใช้รหัสเดียวกันทั้งการเข้ารหัสและการถอดรหัส: เพื่อถอดรหัสฉันจะลบคีย์ออกจาก ciphertext ในขณะที่สำหรับการเข้ารหัสฉันจะสร้าง ciphertext แบบสุ่มก่อนจากนั้นจึงลบข้อความธรรมดาออก อย่างไรก็ตามรหัสพิเศษสำหรับการใช้งานโหมดการเข้ารหัสใช้ความยาวเพียงครึ่งหนึ่งของโปรแกรม

เวอร์ชันสำหรับเล่นกอล์ฟพร้อมความคิดเห็น:

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)

ตัวอย่าง run + 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,:-]ควรเขียนs[k=f.map{rand 26}],r[k,$_,:-]
Hauleth

@Hauleth ไม่มีที่ไม่ได้ทำงานในฐานะเป็นเพียงบรรทัดสุดท้ายอ่านโดย$_ ยังทำหลังจากอ่านบรรทัด getsf.scan(/./).map{|b|b.ord-65}
jsvnm

3

Haskell, 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

Perl, 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 ที่ช่วยเหลือฉันด้วย regexps ฉันใช้ความรู้ใหม่ของฉันในการบันทึกตัวละคร 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เป็น regexp คุณจะต้องเรียนรู้สิ่งเหล่านี้อย่างแน่นอนหากคุณกำลังจะเล่นกอล์ฟ Perl perldoc.perl.org/perlre.htmlไม่ใช่จุดเริ่มต้นที่ไม่ดี
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)

ผมเชื่อว่านี้เป็นไปตามรายละเอียดที่ว่า(รวมทั้ง'>'ที่จุดเริ่มต้นของการแจ้งการป้อนข้อมูล.)[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

หืมแปลกฉันใช้ visual studio จะต้องไม่เป็นมาตรฐานสำหรับ <ios> ที่จะมี <conio.h> และ <stdio.h> ในนั้นประกอบด้วย ฉันคิดว่าส่วนหัวมักจะรวมไฟล์เดียวกันในการใช้งานที่แตกต่างกัน ฉันจะตรวจสอบในภายหลังขอบคุณ
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

ตัวอย่างผลลัพธ์:

$ 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 # ( 445 416)

ลืมเกี่ยวกับการรวม ตัดออกเล็กน้อย

ค่อนข้างกอล์ฟ:

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

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

-Dg=gets(s)คอมไพล์ด้วย

ตัวอย่าง:

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

ฉันได้รับรหัสเดิมทุกครั้งที่เรียกใช้ - ไม่มีการสุ่ม
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}

การใช้งาน:

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

0

Ruby - 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

นี่คือเวอร์ชั่นที่ไม่ดีหากใคร ๆ ที่สนใจ (มันยังไม่ถึงกับเป็นนักกอล์ฟคนนั้น)

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.