Golf mã hóa đầu cuối


16

Thử thách này mang về tiền thưởng 200 điểm cho người đầu tiên trả lời và vẫn bất bại trong ít nhất 3 ngày.Được yêu cầu bởi người dùng3080953 .

Gần đây có rất nhiều cuộc nói chuyện về mã hóa đầu cuối và áp lực đối với các công ty loại bỏ nó khỏi sản phẩm của họ. Tôi không quan tâm đến các quyền và sai của điều đó, nhưng tôi tự hỏi: làm thế nào ngắn mã có thể khiến một công ty bị áp lực không sử dụng nó?

Thách thức ở đây là thực hiện trao đổi khóa Diffie Hellman giữa hai hệ thống được nối mạng, sau đó cho phép người dùng liên lạc qua lại bằng cách sử dụng khóa đối xứng được tạo. Với mục đích của nhiệm vụ này, không yêu cầu bảo vệ nào khác (ví dụ: không cần phải xoay vòng khóa, xác minh danh tính, bảo vệ chống lại DoS, v.v.) và bạn có thể giả sử một mạng internet mở (mọi cổng bạn nghe đều có sẵn cho mọi người). Sử dụng nội dung được cho phép và khuyến khích!

Bạn có thể chọn một trong hai mô hình:

  • Máy chủ và máy khách: máy khách kết nối với máy chủ, sau đó máy chủ hoặc máy khách có thể gửi tin nhắn đến máy chủ khác. Các bên thứ ba ở giữa hai bên phải không thể đọc được tin nhắn. Một luồng ví dụ có thể là:
    1. Người dùng A khởi chạy máy chủ
    2. Người dùng B khởi chạy ứng dụng khách và hướng nó đến máy chủ của người dùng A (ví dụ: qua IP / cổng), chương trình sẽ mở ra một kết nối
    3. Chương trình của Người dùng A thừa nhận kết nối (tùy chọn yêu cầu người dùng đồng ý trước)
    4. Chương trình của Người dùng B bắt đầu tạo bí mật DH và gửi dữ liệu cần thiết (khóa chung, số nguyên tố, trình tạo, bất kỳ thứ gì khác mà bạn cần thực hiện) cho Người dùng A
    5. Chương trình của Người dùng A sử dụng dữ liệu đã gửi để hoàn thành việc tạo bí mật được chia sẻ và gửi lại dữ liệu cần thiết (khóa chung) cho Người dùng B. Từ thời điểm này, Người dùng A có thể nhập tin nhắn (ví dụ qua stdin) sẽ được mã hóa và gửi cho Người dùng B (ví dụ như thiết bị xuất chuẩn).
    6. Chương trình của người dùng B hoàn thành việc tạo bí mật chung. Từ thời điểm này, Người dùng B có thể gửi tin nhắn đến Người dùng A.
  • Hoặc: Một máy chủ có hai máy khách được kết nối với nó: mỗi máy khách nói chuyện với máy chủ, chuyển tiếp tin nhắn của chúng đến máy khách khác. Bản thân máy chủ (và bất kỳ bên thứ ba nào ở giữa) phải không thể đọc được tin nhắn. Khác với kết nối ban đầu, quy trình này giống như kết nối được mô tả trong tùy chọn đầu tiên.

Quy tắc chi tiết:

  • Bạn có thể cung cấp một chương trình hoặc nhiều chương trình (ví dụ: máy chủ và máy khách). Điểm của bạn là tổng kích thước mã trên tất cả các chương trình.
  • Về mặt lý thuyết, chương trình của bạn phải có khả năng giao tiếp qua mạng (nhưng để kiểm tra, localhost vẫn ổn). Nếu ngôn ngữ bạn chọn không hỗ trợ kết nối mạng, bạn có thể kết hợp nó với ngôn ngữ nào đó (ví dụ: tập lệnh shell); trong trường hợp này, điểm của bạn là tổng kích thước mã trên tất cả các ngôn ngữ được sử dụng.
  • Thế hệ khóa Diffie Hellman có thể sử dụng các giá trị "p" và "g" được mã hóa cứng.
  • Khóa chia sẻ được tạo phải có ít nhất 1024 bit.
  • Khi khóa được chia sẻ, sự lựa chọn của mã hóa khóa đối xứng là tùy thuộc vào bạn, nhưng bạn không được chọn một phương pháp hiện được biết là có một cuộc tấn công thực tế chống lại nó (ví dụ: dịch chuyển caesar là không quan trọng để đảo ngược mà không biết về khóa ). Các thuật toán được phép ví dụ:
    • AES (kích thước bất kỳ)
    • RC4 (về mặt lý thuyết bị phá vỡ, nhưng không có cuộc tấn công thực tế nào mà tôi có thể tìm thấy đề cập đến, vì vậy nó được phép ở đây)
  • Cả người dùng A và B đều có thể gửi tin nhắn cho nhau (giao tiếp hai chiều) một cách tương tác (ví dụ: đọc các dòng từ stdin, liên tục nhắc hoặc các sự kiện như nhấn nút). Nếu việc này dễ dàng hơn, bạn có thể giả sử một cuộc trò chuyện xen kẽ (tức là sau khi người dùng gửi tin nhắn, họ phải chờ phản hồi trước khi gửi tin nhắn tiếp theo của họ)
  • Nội dung ngôn ngữ được cho phép (không cần phải viết phương thức mã hóa hoặc kết nối mạng của riêng bạn nếu chúng đã được hỗ trợ).
  • Các định dạng truyền thông cơ bản là tùy thuộc vào bạn.
  • Các bước giao tiếp được đưa ra ở trên là một ví dụ, nhưng bạn không bắt buộc phải tuân theo chúng (miễn là thông tin cần thiết được chia sẻ và không có người trung gian nào có thể tính toán khóa hoặc tin nhắn được chia sẻ)
  • Nếu các chi tiết cần thiết để kết nối với máy chủ của bạn không được biết trước (ví dụ: nếu nó nghe trên một cổng ngẫu nhiên), thì các chi tiết này phải được in. Bạn có thể giả sử rằng địa chỉ IP của máy đã được biết.
  • Xử lý lỗi (ví dụ: địa chỉ không hợp lệ, mất kết nối, v.v.) là không bắt buộc.
  • Thách thức là mã golf, vì vậy mã ngắn nhất tính bằng byte sẽ thắng.

Là mã hóa cứng pgđược phép?
ASCII - chỉ

@ ASCII - chỉ từ những gì tôi có thể nói, mã hóa giá trị p & g chất lượng tốt được coi là tốt (trừ khi nhà phát triển sử dụng độc hại các giá trị được biết là dễ bị tấn công cụ thể). Vì vậy, đối với thử thách này, mọi thứ đều ổn (miễn là bí mật thu được ít nhất là 1024 bit)
Dave

Câu trả lời:


3

Node.js ( 372 423 + 94 = 517 513 byte)

Chơi gôn

Linebreaks được thêm vào cho "khả năng đọc".

chat.js ( 423 419 byte)

Không ngắt dòng

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

Ngắt dòng

[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 byte)

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

Bị đánh cắp

Node có khả năng kết nối mạng và tiền điện tử tích hợp. Điều này sử dụng TCP để kết nối mạng (vì nó đơn giản hơn giao diện của Node cho HTTP và nó chơi độc đáo với các luồng).

Tôi sử dụng mật mã luồng (RC4) thay vì AES để tránh phải xử lý kích thước khối. Wikipedia dường như nghĩ rằng nó có thể dễ bị tổn thương, vì vậy nếu bất cứ ai có bất kỳ hiểu biết nào về mật mã được ưa thích, điều đó sẽ rất tuyệt.

Chạy máy chủ echo node echo_server.jssẽ lắng nghe trên cổng 9. Chạy hai phiên bản của chương trình này với node chat.js <server IP>node chat.js <server IP> 1(đối số cuối cùng chỉ đặt cái nào gửi một số nguyên tố). Mỗi trường hợp kết nối với máy chủ echo. Thông báo đầu tiên xử lý việc tạo khóa và các tin nhắn tiếp theo sử dụng mật mã luồng.

Máy chủ echo chỉ gửi mọi thứ trở lại cho tất cả các máy khách được kết nối ngoại trừ bản gốc.

Khách hàng

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

Máy chủ tiếng vang

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

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

Cảm ơn Dave cho tất cả các mẹo + thông tin phản hồi!


1
Đừng thêm khả năng đọc vào phiên bản chơi gôn, đó là những gì phiên bản không được dùng cho. Hoặc nếu bạn làm điều đó, hãy xóa dấu chấm phẩy trước khi ngắt dòng, để nó có cùng độ dài.
mbomb007

@ mbomb007, "khả năng đọc" chủ yếu là để tránh phải cuộn. Thật không may, phần thân của mã không có dấu chấm phẩy, do đó không hoạt động. Tôi đã tìm ra một phát hiện nhanh chóng và thay thế sẽ không quá nặng nề. Chắc chắn sẽ ghi nhớ mẹo của bạn cho ý kiến ​​trong tương lai!
dùng3080953

@Dave cảm ơn tất cả các thông tin phản hồi! Tôi đã thực hiện các thay đổi để sử dụng vanilla DH, thực sự đã thêm một chút độ dài vì bạn cần trao đổi các số nguyên tố cũng như AES thực sự hoạt động như một sự thay thế thả xuống, nhưng vấn đề với AES là không có gì gửi cho đến khi bạn hoàn thành một khối, và đệm sẽ là một nỗi đau. còn RC4 ngắn hơn
aes128

1
Tôi không chắc liệu nó có hoạt động qua mạng không, nhưng có lẽ nó sẽ không hoạt động và tôi đã viết nó trên xe buýt nên tôi không có cách nào kiểm tra. phiên bản mới sử dụng máy chủ echo thay thế. Điều này cũng giải quyết vấn đề thời gian chờ. Tôi đã cố gắng tránh một máy chủ + máy khách, nhưng nó là hình thức tốt hơn nhiều. cuối cùng, cảm ơn vì thử thách này, tôi đã học được rất nhiều về cách thực sự sử dụng nút thay vì chỉ lấy các thư viện từ khắp mọi nơi :)
user3080953

@ user3080953 nghe hay đấy. Với những cập nhật đó, bạn sẽ tham gia kiếm tiền thưởng!
Dave

0

Node.js, 638 607 byte

Bây giờ nó đã rất tốt và thực sự bị đánh bại (và trong cùng một ngôn ngữ), đây là câu trả lời kiểm tra của tôi:

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])

Hoặc với gói:

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])

Sử dụng

Đây là một máy chủ / máy khách thực hiện; một khởi tạo sẽ là máy chủ và máy khách khác. Máy chủ được khởi chạy với một cổng cụ thể, sau đó máy khách được trỏ đến cổng của máy chủ. DH có thể mất vài giây để thiết lập nếu máy của bạn thiếu entropy, vì vậy các tin nhắn đầu tiên có thể bị trì hoãn một chút.

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

Phá vỡ

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

Yêu cầu duy nhất cho các mã thông báo là chúng chứa ít nhất một ký tự không phải là hex, vì vậy trong mã rút gọn, các hằng chuỗi khác được sử dụng ( datahex).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.