Golf uçtan uca şifreleme


16

Bu meydan okuma ilk cevaplayan için 200 puanlık bir ödül taşır ve en az 3 gün boyunca yenilmez. Toprak talebinde user3080953 .

Son zamanlarda uçtan uca şifreleme ve şirketlerin ürünlerinden çıkarmaları için baskı konusunda çok fazla konuşma var. Ben bunun hakları ve yanlışları ile ilgilenmiyorum, ama merak ettim: kod ne kadar kısa bir şirket onu kullanmamak içine baskı olsun olabilir?

Buradaki zorluk, iki ağa bağlı sistem arasında bir Diffie Hellman anahtar değişimi uygulamak ve daha sonra kullanıcıların oluşturulan simetrik anahtarı kullanarak ileri geri iletişim kurmasına izin vermektir. Bu görevin amacı için başka bir korumaya gerek yoktur (örneğin, anahtarın çevrilmesine, kimliklerin doğrulanmasına, DoS'a karşı korunmaya vb. Gerek yoktur) ve açık bir internet olduğunu varsayabilirsiniz (dinlediğiniz herhangi bir bağlantı noktası herkes tarafından kullanılabilir). Yerleşiklerin kullanımına izin verilir ve teşvik edilir!

İki modelden birini seçebilirsiniz:

  • Bir sunucu ve istemci: istemci sunucuya bağlanır, daha sonra sunucu veya istemci diğerine mesaj gönderebilir. İkisi arasındaki üçüncü tarafların mesajları okuyamaması gerekir. Örnek bir akış şöyle olabilir:
    1. A kullanıcısı sunucuyu başlatır
    2. B kullanıcısı istemciyi başlatır ve A kullanıcısının sunucusuna yönlendirir (örneğin IP / port üzerinden), program bir bağlantı açar
    3. A Kullanıcısının programı bağlantıyı kabul eder (isteğe bağlı olarak kullanıcıdan önce onayını ister)
    4. Kullanıcı B'nin programı bir DH sırrı oluşturmaya başlar ve gerekli verileri (ortak anahtar, asal, üreteç, uygulamanızın ihtiyaç duyduğu her şey) Kullanıcı A'ya gönderir
    5. A Kullanıcısının programı gönderilen verileri paylaşılan sırrın tamamlanmasını tamamlamak için kullanır ve gerekli verileri (ortak anahtar) B Kullanıcısına geri gönderir. Bu noktadan sonra A Kullanıcısı şifrelenecek ve Kullanıcıya gönderilecek mesajları (örneğin stdin aracılığıyla) gönderebilir B (örneğin stdout'a).
    6. Kullanıcı B'nin programı paylaşılan sırrın oluşturulmasını tamamlar. Bu noktadan sonra, Kullanıcı B Kullanıcı A'ya mesaj gönderebilir.
  • Veya: İki istemcinin bağlı olduğu bir sunucu: her istemci, sunucuyla konuşarak iletisini diğer istemciye iletir. Sunucunun kendisi (ve aradaki üçüncü taraflar) iletileri okuyamaz. İlk bağlantı dışında, işlem ilk seçenekte açıklananla aynıdır.

Ayrıntılı kurallar:

  • Tek bir program veya birden çok program (örn. Sunucu ve istemci) sağlayabilirsiniz. Puanınız, tüm programlardaki toplam kod boyutudur.
  • Programınız teorik olarak bir ağ üzerinden iletişim kurabilmelidir (ancak test için localhost iyidir). Seçtiğiniz dil ağ oluşturmayı desteklemiyorsa, bunu yapan bir şeyle birleştirebilirsiniz (örneğin bir kabuk komut dosyası); bu durumda puanınız kullanılan tüm dillerdeki toplam kod boyutudur.
  • Diffie Hellman anahtar üretimi sabit kodlanmış "p" ve "g" değerlerini kullanabilir.
  • Oluşturulan paylaşılan anahtar en az 1024 bit olmalıdır.
  • Anahtar paylaşıldıktan sonra, simetrik anahtar şifrelemesi seçimi size bağlıdır, ancak şu anda buna karşı pratik bir saldırı olduğu bilinen bir yöntem seçmemelisiniz (örneğin, sezaryen değişimi, anahtar bilgisi olmadan geri döndürmek için önemsizdir) ). İzin verilen algoritma örnekleri:
    • AES (herhangi bir anahtar boyutu)
    • RC4 (teorik olarak kırıldı, ancak bahsettiğim pratik saldırılar yok, bu yüzden burada izin verilebilir)
  • A ve B kullanıcıları birbirleriyle etkileşimli olarak mesaj gönderebilmelidir (iki yönlü iletişim) (örneğin stdin'den satır okuma, sürekli yönlendirme veya bir düğmeye basma gibi olaylar). Bunu kolaylaştırırsa, alternatif bir görüşme yapabilirsiniz (yani bir kullanıcı mesaj gönderdikten sonra, bir sonraki mesajını göndermeden önce bir yanıt beklemelidir)
  • Dil builtins edilir (zaten desteklenen eğer kendi kriptografik veya ağ yöntemleri yazmaya gerek yok) izin verilir.
  • Temel iletişim formatı size kalmış.
  • Yukarıda verilen iletişim adımları bir örnektir, ancak bunları takip etmeniz gerekmez (gerekli bilgi paylaşıldığı ve hiçbir arabulucu paylaşılan anahtarı veya mesajları hesaplayamadığı sürece)
  • Sunucunuza bağlanmak için gereken ayrıntılar önceden bilinmiyorsa (örneğin rasgele bir bağlantı noktasında dinliyorsa), bu ayrıntıların yazdırılması gerekir. Makinenin IP adresinin bilindiğini varsayabilirsiniz.
  • Hata işleme (örn. Geçersiz adresler, kayıp bağlantılar vb.) Gerekli değildir.
  • Zorluk kod golf, bu yüzden bayt içindeki en kısa kod kazanır.

Hardcoding mı pve gizin?
Sadece ASCII

@ ASCII-sadece söyleyebileceğim kadarıyla, sabit kodlama kaliteli p & g değerleri iyi kabul edilir (geliştirici kötü niyetli olarak belirli saldırılara karşı savunmasız olduğu bilinen değerleri kullanmıyorsa). Yani bu meydan okuma için sorun yok (ortaya çıkan sır en az 1024 bit olduğu sürece)
Dave

Yanıtlar:


3

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

golfed

"Okunabilirlik" için satır sınırları eklendi.

chat.js ( 423 419 bayt)

Satır sonu yok

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

Satır kesmeleri

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

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

Ungolfed

Düğüm, yerleşik ağ ve kripto yeteneklerine sahiptir. Bu, ağ iletişimi için TCP kullanır (çünkü Düğüm'ün HTTP arayüzünden daha basittir ve akışlarla güzel oynar).

Blok boyutları ile uğraşmaktan kaçınmak için AES yerine bir akış şifresi (RC4) kullanıyorum. Wikipedia, savunmasız olabileceğini düşünüyor, bu yüzden herhangi birinin şifrenin tercih edildiği herhangi bir görüşü varsa, bu harika olurdu.

Yankı sunucusu çalıştırın node echo_server.jsportu 9. Run iki ile bu programın örnekleri üzerinde dinleyecek node chat.js <server IP>ve node chat.js <server IP> 1(bir a ilk gönderir son argüman sadece setleri). Her örnek yankı sunucusuna bağlanır. İlk mesaj anahtar oluşturmayı yönetir ve sonraki mesajlar akış şifresini kullanır.

Echo sunucusu sadece orijinal dışındaki tüm bağlı istemcilere her şeyi geri gönderir.

müşteri

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

Yankı sunucusu

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

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

Dave tüm ipuçları + geribildirim için teşekkürler!


1
Golf edilmiş versiyona okunabilirlik eklemeyin, golf edilmemiş versiyon budur. Veya bunu yaparsanız, satır kesilmeden önce noktalı virgülleri kaldırın, böylece aynı uzunluktadır.
mbomb007

@ mbomb007 "okunabilirlik" çoğunlukla kaydırmaktan kaçınmak içindir. ne yazık ki, kodun gövdesinde noktalı virgül yoktur, bu yüzden çalışmaz. Çabuk bul ve değiştirmenin çok zahmetli olmayacağını düşündüm. kesinlikle gelecekteki yorumlar için ipucunu aklınızda tutacak!
user3080953

@ Tüm geri bildirimler için teşekkürler! Gerçekten de biraz uzunluk ekleyen vanilya DH kullanmak için değişiklikler yaptım çünkü primerler de değiştirmelisiniz çünkü AES aslında bir yedek değiştirme olarak çalışıyor, ancak AES ile ilgili sorun tamamlanana kadar hiçbir şeyin gönderilmemesi bir blok ve dolgu bir ağrı olurdu. Ayrıca rc4 aes128 daha kısadır
user3080953

1
Bir ağ üzerinde çalışıp çalışmayacağından emin değildim, ama muhtemelen işe yaramayacaktı ve onu bir otobüse yazdım, bu yüzden kontrol edemedim. yeni sürüm bunun yerine bir yankı sunucusu kullanır. Bu aynı zamanda zaman aşımı problemini de çözer. Bir sunucu + istemciden kaçınmaya çalışıyordum, ama çok daha iyi bir form. Sonunda, bu meydan okuma için teşekkürler, ben sadece her yerden kütüphaneleri kapmak yerine aslında düğüm kullanımı hakkında bir ton öğrendim :)
user3080953

@ user3080953 iyi görünüyor. Bu güncellemelerle ödülün peşinde olmalısın!
Dave

0

Node.js, 638 607 bayt

Şimdi iyi ve gerçekten dövüldüğüne (ve aynı dilde), işte test cevabım:

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

Veya sarma ile:

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

kullanım

Bu bir sunucu / istemci uygulamasıdır; örneklerden biri sunucu, diğeri istemci olacaktır. Sunucu belirli bir bağlantı noktasıyla başlatılır, daha sonra istemci sunucunun bağlantı noktasına yönlendirilir. Makinenizin entropisi azsa DH'nin kurulması birkaç saniye sürebilir, bu nedenle ilk mesajlar biraz gecikebilir.

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

Yıkmak

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

Jetonlar için tek gereksinim, en az bir onaltılı olmayan karakter içermesidir, bu nedenle küçültülmüş kodda diğer dize sabitleri kullanılır ( datave hex).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.