골프 엔드 투 엔드 암호화


16

이 도전은 첫 번째 답변에 대해 200 포인트현상금을 전달하며 최소 3 일 동안 무적 상태를 유지합니다. user3080953에 의해 청구되었습니다 .

최근 엔드-투-엔드 암호화에 대한 이야기가 많으며 회사가 제품에서 암호화를 제거해야한다는 압력이 있습니다. 나는 그 권리와 잘못에 관심이 없지만, 코드를 얼마나 짧게 사용하여 회사가 그것을 사용하지 않도록 압력을 가할 수 있을지 궁금해했다.

여기서 두 가지 네트워크 시스템간에 Diffie Hellman 키 교환 을 구현 한 다음 생성 된 대칭 키를 사용하여 사용자가 서로 통신 할 수 있도록해야합니다. 이 작업을 위해 다른 보호 기능이 필요하지 않습니다 (예 : 키 순환, ID 확인, DoS 방지 등). 개방형 인터넷 (사용중인 모든 포트를 모든 사람이 사용할 수 있음)으로 가정 할 수 있습니다. 내장 사용이 허용 되고 권장됩니다!

두 가지 모델 중 하나를 선택할 수 있습니다.

  • 서버 및 클라이언트 : 클라이언트가 서버에 연결하면 서버 또는 클라이언트가 다른 서버로 메시지를 보낼 수 있습니다. 두 당사자 사이의 타사는 메시지를 읽을 수 없어야합니다. 흐름의 예는 다음과 같습니다.
    1. 사용자 A가 서버를 시작 함
    2. 사용자 B가 클라이언트를 시작하고이를 사용자 A의 서버로 보낸다 (예 : IP / 포트를 통해) 프로그램이 연결을 엽니 다
    3. 사용자 A의 프로그램은 연결을 승인합니다 (선택적으로 사용자에게 먼저 동의를 요청 함)
    4. 사용자 B의 프로그램은 DH 비밀 생성을 시작하고 필요한 데이터 (공개 키, 프라임, 생성기, 구현에 필요한 다른 것)를 사용자 A에게 보냅니다.
    5. 사용자 A의 프로그램은 전송 된 데이터를 사용하여 공유 비밀 생성을 완료하고 필요한 데이터 (공개 키)를 사용자 B에게 다시 보냅니다.이 시점부터 사용자 A는 암호화되어 사용자에게 전송되는 메시지 (예 : stdin을 통해)를 입력 할 수 있습니다. B (예 : 표준 출력)
    6. 사용자 B의 프로그램은 공유 비밀 생성을 완료합니다. 이 시점에서 사용자 B는 사용자 A에게 메시지를 보낼 수 있습니다.
  • 또는 : 두 개의 클라이언트가 연결된 서버 : 각 클라이언트는 서버와 통신하여 메시지를 다른 클라이언트에게 전달합니다. 서버 자체 (및 그 사이의 타사)가 메시지를 읽을 수 없어야합니다. 초기 연결 이외의 프로세스는 첫 번째 옵션에서 설명한 것과 동일합니다.

자세한 규칙 :

  • 단일 프로그램 또는 여러 프로그램 (예 : 서버 및 클라이언트)을 제공 할 수 있습니다. 점수는 모든 프로그램의 총 코드 크기입니다.
  • 프로그램은 이론적으로 네트워크를 통해 통신 할 수 있어야합니다 (하지만 테스트를 위해 localhost는 괜찮습니다). 선택한 언어가 네트워킹을 지원하지 않는 경우이를 언어와 결합 할 수 있습니다 (예 : 셸 스크립트). 이 경우 점수는 사용 된 모든 언어의 총 코드 크기입니다.
  • Diffie Hellman 키 생성에는 하드 코딩 된 "p"및 "g"값을 사용할 수 있습니다.
  • 생성 된 공유 키는 1024 비트 이상이어야합니다.
  • 키가 공유되면 대칭 키 암호화의 선택은 사용자의 몫이지만 현재 키에 대한 실질적인 공격이있는 것으로 알려진 방법을 선택해서는 안됩니다 (예 : 키에 대한 지식이 없어도 시저 이동이 간단하지 않음). ). 허용되는 알고리즘 예 :
    • AES (모든 키 크기)
    • RC4 (이론적으로 망가졌지만 언급 할 수있는 실제적인 공격은 없으므로 여기에서 허용됩니다)
  • 사용자 A와 B는 대화식으로 서로에게 메시지를 보낼 수 있어야합니다 (예 : stdin에서 줄 읽기, 지속적으로 프롬프트 또는 단추 누르기와 같은 이벤트). 그것이 더 쉬운 경우, 당신은 번갈아 대화를 가정 할 수있다 (즉, 사용자가 메시지를 보낸 후, 다음 메시지를 보내기 전에 응답을 기다려야한다)
  • 언어 내장 허용됩니다 (이미 지원되는 경우 자체 암호화 또는 네트워킹 방법을 작성할 필요가 없습니다).
  • 기본 통신 형식은 귀하에게 달려 있습니다.
  • 위의 통신 단계는 예이지만 필요한 정보를 공유하고 중개인이 공유 키 또는 메시지를 계산할 수없는 경우에는 따를 필요가 없습니다.
  • 서버에 연결하는 데 필요한 세부 정보를 미리 알 수없는 경우 (예 : 임의 포트에서 수신하는 경우) 이러한 세부 정보를 인쇄해야합니다. 머신의 IP 주소가 알려져 있다고 가정 할 수 있습니다.
  • 오류 처리 (예 : 유효하지 않은 주소, 끊어진 연결 등)는 필요하지 않습니다.
  • 문제는 코드 골프이므로 바이트 단위의 가장 짧은 코드가 이깁니다.

하드 코딩되어 pg허용?
ASCII 전용

@ASCII 전용은 내가 말할 수있는 것으로부터 양질의 p & g 값을 하드 코딩하는 것이 좋습니다 (개발자가 악의적으로 특정 공격에 취약한 것으로 알려진 값을 사용하지 않는 한). 따라서이 도전에 대해서는 괜찮습니다 (비밀이 1024 비트 이상인 한)
Dave

답변:


3

Node.js를 ( 372 423 + 94 = 517 513 바이트)

골프

"가독성"을 위해 줄 바꿈이 추가되었습니다.

chat.js ( 423 419 바이트)

줄 바꿈 없음

[n,c,p]=["net","crypto","process"].map(require);r="rc4",a="create",h="DiffieHellman",z="pipe",w="write",o=128,g=p.argv;s=e=d=0,y=c[a+h](8*o),k=y.generateKeys();v=n.connect(9,g[2],_=>{g[3]&&(v[w](y.getPrime()),v[w](k));v.on("data",b=>{s||(g[3]||(y=c[a+h](b.slice(0,o)),k=y.generateKeys(),v[w](k),b=b.slice(o)),s=y.computeSecret(b),e=c[a+"Cipher"](r,s),p.stdin[z](e)[z](v),d=c[a+"Decipher"](r,s),v[z](d)[z](p.stdout))})})

줄 바꿈

[n,c,p]=["net","crypto","process"].map(require);
r="rc4",a="create",h="DiffieHellman",z="pipe",w="write",o=128,g=p.argv;
s=e=d=0,y=c[a+h](8*o),k=y.generateKeys();
v=n.connect(9,g[2],_=>{g[3]&&(v[w](y.getPrime()),v[w](k));
v.on("data",b=>{s||(g[3]||(y=c[a+h](b.slice(0,o)),k=y.generateKeys(),
v[w](k),b=b.slice(o)),s=y.computeSecret(b),e=c[a+"Cipher"](r,s),p.stdin[z](e)[z](v)
,d=c[a+"Decipher"](r,s),v[z](d)[z](p.stdout))})})

echo_server.js (94 바이트)

c=[],require("net").createServer(a=>{c.forEach(b=>{a.pipe(b),b.pipe(a)});c.push(a)}).listen(9);

언 골프

노드에는 내장 네트워킹 및 암호화 기능이 있습니다. 이것은 네트워킹을 위해 TCP를 사용합니다 (HTTP에 대한 노드의 인터페이스보다 간단하고 스트림과 잘 어울립니다).

블록 크기를 처리하지 않기 위해 AES 대신 스트림 암호 (RC4)를 사용합니다. Wikipedia는 그것이 취약 할 수 있다고 생각하는 것 같습니다. 따라서 누군가 암호가 선호되는 통찰력이 있다면 좋을 것입니다.

node echo_server.js포트 9에서 청취 할 에코 서버 를 실행하십시오.이 프로그램의 두 인스턴스를 node chat.js <server IP>and로 실행하십시오 node chat.js <server IP> 1(마지막 인수는 어느 것이 소수를 전송하는지 설정합니다). 각 인스턴스는 에코 서버에 연결됩니다. 첫 번째 메시지는 키 생성을 처리하고 후속 메시지는 스트림 암호를 사용합니다.

에코 서버는 원본을 제외한 모든 연결된 클라이언트로 모든 것을 다시 보냅니다.

고객

var net = require('net');
var crypto = require('crypto');
var process = require('process');
let [serverIP, first] = process.argv.slice(2);

var keys = crypto.createDiffieHellman(1024); // DH key exchange
var prime = keys.getPrime();
var k = keys.generateKeys();
var secret;

var cipher; // symmetric cipher
var decipher;

// broadcast prime
server = net.connect(9, serverIP, () => {
    console.log('connect')
    if(first) {
        server.write(prime);
        console.log('prime length', prime.length)
        server.write(k);
    }

    server.on('data', x => {
        if(!secret) { // if we still need to get the ciphers
            if(!first) { // generate a key with the received prime
                keys = crypto.createDiffieHellman(x.slice(0,128)); // separate prime and key
                k = keys.generateKeys();
                server.write(k);
                x = x.slice(128)
            }

            // generate the secret
            console.log('length x', x.length);
            secret = keys.computeSecret(x);
            console.log('secret', secret, secret.length) // verify that secret key is the same
            cipher = crypto.createCipher('rc4', secret);
            process.stdin.pipe(cipher).pipe(server);
            decipher = crypto.createDecipher('rc4', secret);
            server.pipe(decipher).pipe(process.stdout);
        }
        else {
            console.log('sent text ', x.toString()) // verify that text is sent encrypted
        }
    });
})

에코 서버

var net = require('net');
clients = [];

net.createServer(socket => {
    clients.forEach(c=>{socket.pipe(c); c.pipe(socket)});
    clients.push(socket);
}).listen(9)

모든 팁과 피드백에 감사드립니다.


1
골프 버전에 가독성을 추가하지 마십시오. 이것이 골프 용 버전입니다. 또는 그렇게하면 줄 바꿈 전에 세미콜론을 제거하십시오. 따라서 길이는 같습니다.
mbomb007 17

@ mbomb007 "가독성"은 주로 스크롤하지 않아도됩니다. 불행히도 코드 본문에는 세미콜론이 없으므로 작동하지 않습니다. 나는 빠른 찾기와 바꾸기가 너무 번거롭지 않을 것이라고 생각했습니다. 그래도 앞으로 의견을 위해 팁을 명심하십시오!
user3080953

@ 모든 의견에 감사드립니다! 바닐라 DH를 사용하도록 변경했습니다. 프라임을 교환해야하기 때문에 실제로 약간의 길이가 추가되었습니다 .AES는 실제로 드롭 인 교체로 작동하지만 AES의 문제는 완료 할 때까지 아무것도 보내지 않는다는 것입니다 패딩은 고통이 될 것입니다. 또한 rc4는 aes128보다 짧습니다
user3080953

1
네트워크를 통해 작동하는지 확실하지 않지만 아마도 작동하지 않을 것이며 버스에 작성하여 확인할 방법이 없었습니다. 새 버전은 대신 에코 서버를 사용합니다. 이것은 또한 타임 아웃 문제를 해결합니다. 나는 서버 + 클라이언트를 피하려고 노력했지만 훨씬 나은 형태입니다. 마지막으로,이 도전에 대한 감사로, 나는 어디서나 라이브러리를 잡는 대신 실제로 노드를 사용하는 방법에 대해 많은 것을 배웠다 :)
user3080953

@ user3080953의 소리가 좋습니다. 그 업데이트로 당신은 현상금을 위해 달려야합니다!
Dave

0

Node.js, 638 607 바이트

이제 잘되고 진정으로 (그리고 같은 언어로) 맞았으므로 여기에 내 테스트 답변이 있습니다.

R=require,P=process,s=R('net'),y=R('crypto'),w=0,C='create',W='write',D='data',B='hex',G=_=>a.generateKeys(B),Y=(t,m,g,f)=>g((c=y[C+t+'ipher']('aes192',w,k='')).on('readable',_=>k+=(c.read()||'').toString(m)).on('end',_=>f(k)))+c.end(),F=C+'DiffieHellman',X=s=>s.on(D,x=>(x+'').split(B).map(p=>p&&(w?Y('Dec','utf8',c=>c[W](p,B),console.log):P.stdin.on(D,m=>Y('C',B,c=>c[W](m),r=>s[W](r+B)),([p,q,r]=p.split(D),r&&s[W](G(a=y[F](q,B,r,B))),w=a.computeSecret(p,B))))));(R=P.argv)[3]?X(s.Socket()).connect(R[3],R[2]):s[C+'Server'](s=>X(s,a=y[F](2<<9))[W](G()+D+a.getPrime(B)+D+a.getGenerator(B)+B)).listen(R[2])

또는 포장 :

R=require,P=process,s=R('net'),y=R('crypto'),w=0,C='create',W='write',D='data',B
='hex',G=_=>a.generateKeys(B),Y=(t,m,g,f)=>g((c=y[C+t+'ipher']('aes192',w,k=''))
.on('readable',_=>k+=(c.read()||'').toString(m)).on('end',_=>f(k)))+c.end(),F=C+
'DiffieHellman',X=s=>s.on(D,x=>(x+'').split(B).map(p=>p&&(w?Y('Dec','utf8',c=>c[
W](p,B),console.log):P.stdin.on(D,m=>Y('C',B,c=>c[W](m),r=>s[W](r+B)),([p,q,r]=p
.split(D),r&&s[W](G(a=y[F](q,B,r,B))),w=a.computeSecret(p,B))))));(R=P.argv)[3]?
X(s.Socket()).connect(R[3],R[2]):s[C+'Server'](s=>X(s,a=y[F](2<<9))[W](G()+D+a.
getPrime(B)+D+a.getGenerator(B)+B)).listen(R[2])

용법

이것은 서버 / 클라이언트 구현입니다. 한 인스턴스는 서버이고 다른 인스턴스는 클라이언트입니다. 서버는 특정 포트로 시작된 다음 클라이언트가 서버의 포트를 가리 킵니다. 기기의 엔트로피가 낮은 경우 DH를 설정하는 데 몇 초가 걸릴 수 있으므로 첫 번째 메시지가 약간 지연 될 수 있습니다.

MACHINE 1                       MACHINE 2
$ node e2e.js <port>            :
:                               $ node e2e.js <address> <port>
$ hello                         :
:                               : hello
:                               $ hi
: hi                            :

고장

s=require('net'),
y=require('crypto'),
w=0,                                      // Shared secret starts unknown
Y=(t,m,g,f)=>g(                           // Helper for encryption & decryption
  (c=y['create'+t+'ipher']('aes192',w,k=''))
  .on('readable',_=>k+=(c.read()||'').toString(m))
  .on('end',_=>f(k)))+c.end();
X=s=>s.on('data',x=>(x+'').split('TOKEN2').map(p=>
  p&&(w                                   // Have we completed handshake?
    ?Y('Dec','utf8',c=>c.write(p,'hex'),console.log) // Decrypt + print messages
    :                                     // Haven't completed handshake:
     process.stdin.on('data',m=>          //  Prepare to encrypt + send input
       Y('C','hex',c=>c.write(m),r=>s.write(r+'TOKEN2')),(
       [p,q,r]=p.split('TOKEN1'),         //  Split up DH data sent to us
       r&&                                //  Given DH details? (client)
          s.write(
            (a=y.createDiffieHellman(     //   Compute key pair...
              q,'hex',r,'hex')            //   ...using the received params
            ).generateKeys('hex')),       //   And send the public key
       w=a.computeSecret(p,'hex')         //  Compute shared secret
       //,console.log(w.toString('hex'))  //  Print if you want to verify no MITM
))))),
(R=process.argv)[3]                       // Are we running as a client?
  ?X(s.Socket()).connect(R[3],R[2])       // Connect & start chat
  :s.createServer(s=>                     // Start server. On connection:
    X(s,                                  //  Start chat,
      a=y.createDiffieHellman(1024))      //  Calc DiffieHellman,
    .write(                               //  Send public key & public DH details
      a.generateKeys('hex')+'TOKEN1'+
      a.getPrime('hex')+'TOKEN1'+
      a.getGenerator('hex')+'TOKEN2')
  ).listen(R[2])                          // Listen on requested port

토큰에 대한 유일한 요구 사항은 하나 이상의 16 진 문자가 아닌 것이므로 축소 된 코드에서 다른 문자열 상수가 사용됩니다 ( datahex).

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