/* Curve, Salsa and Poly classes translated to C# from nacl library version 20081011. Adaptations: C# reserved words: out -> output; in -> input. C# does not allow pointer arithmetic; Use _32, _64 variants or extra offset parameters. Some extra casts required. */ using System; using System.Collections; using System.Collections.Generic; using System.IO; class KeyPair { public byte[] Secret; public byte[] Public; public BigMap Cache = new BigMap( ByteComparer.Instance ); } class SecurityArg { public byte [] Nonce; public byte [] PublicKey; public KeyPair ServerKey; } class Crypt { public static KeyPair ClientKey = MakeKeyPair(); public static List ServerKey; // Initialised by call to LoadServerKey static byte [] PublicKeyPrefix = new byte[3] { (byte)'Q', (byte)'K', (byte)'-' }; static KeyPair MakeKeyPair() { KeyPair x = new KeyPair(); x.Secret = Random.Bytes(32); x.Public = new byte[32]; Curve.crypto_scalarmult_curve25519( x.Public, x.Secret, Curve.Base ); return x; } public static void LoadServerKey( String path, String AltPath ) { ServerKey = new List(); String SecretPrefix = "Server Secret Key is "; String PublicPrefix = "Server Public Key is QK-"; KeyPair x; if ( File.Exists( path ) ) { StreamReader f = File.OpenText( path ); String Line1 = f.ReadLine(); String Line2 = f.ReadLine(); f.Close(); x = new KeyPair(); x.Secret = Base32StrDecode( SecretPrefix, Line1 ); x.Public = Base32StrDecode( PublicPrefix, Line2 ); } else { x = MakeKeyPair(); StreamWriter f = File.CreateText( path ); f.WriteLine( SecretPrefix + ToString( Base32Encode( x.Secret ) ) ); f.WriteLine( PublicPrefix + ToString( Base32Encode( x.Public, 51 ) ) ); f.Close(); } ServerKey.Add( x ); if( File.Exists( AltPath ) ) { StreamReader f = File.OpenText( AltPath ); while (true) { String Line1 = f.ReadLine(); if ( Line1 == null || Line1 == "" ) break; String Line2 = f.ReadLine(); x = new KeyPair(); x.Secret = Base32StrDecode( SecretPrefix, Line1 ); x.Public = Base32StrDecode( PublicPrefix, Line2 ); ServerKey.Add( x ); } f.Close(); } } public static byte [] GetPublicKey( Domain Name ) { byte [] result = null; for ( Domain x = Name; result == null && x != null; x = x.Parent ) { byte [] L = x.L; if ( L.Length == 54 && L[0] == 'Q' && L[1] == 'K' && L[2] == '-' ) { result = Base32Decode( L, 3, 32 ); } } return result; } public static byte [] GetPublicKey( String exthex ) { return Base32Decode( ToBytes(exthex,0), 0, 32 ); } static byte [] GetSharedSecret( byte [] PublicKey, KeyPair KP ) { byte [] k = KP.Cache[ PublicKey ]; if ( k == null ) { k = new byte[32]; Salsa.crypto_box_beforenm( k, PublicKey, KP.Secret ); KP.Cache[ PublicKey ] = k; } return k; } static byte[] Base32StrDecode( String prefix, String data ) { if ( !data.StartsWith(prefix) ) return null; byte [] b = ToBytes( data, prefix.Length ); return Base32Decode( b, 0 ); } static String ToString( byte [] x ) { int N = x.Length; char [] c = new char[ N ]; for ( int i=0; i= 5 && j < M ) { int x = 31 & Buf; Result[j++] = (byte)Base32[x]; NB -= 5; Buf = Buf >> 5; } } return Result; } static byte[] Base32Encode( byte[] b ) { return Base32Encode( b, ((b.Length*8)+4) / 5 ); } static byte[] Base32Decode( byte [] a, int off ) { int N = a.Length - off; int M = (N*5) / 8; return Base32Decode( a, off, M ); } static byte[] Base32Decode( byte [] a, int off, int M ) { int N = a.Length - off; byte [] result = new byte[ M ]; int ix = 0; int shift = 0; int buffer = 0; for ( int i=0; i= '0' && c <= '9' ) c = c -'0'; else if ( c >= 'B' && c <= 'D' ) c = c - 'B' + 10; else if ( c >= 'F' && c <= 'H' ) c = c - 'B' + 9; else if ( c >= 'J' && c <= 'N' ) c = c - 'B' + 8; else if ( c >= 'P' && c <= 'Z' ) c = c - 'B' + 7; else { return null; } buffer += c << shift; if ( ix < M ) result[ix] = (byte)( buffer & 0xff ); shift += 5; if ( shift >= 8 ) { buffer = buffer >> 8; shift -= 8; ix += 1; } } return result; } public static byte [] EncodeQuery( byte [] Pk, byte [] Packet, byte [] Nonce ) { byte [] PlainText = Data.Concat( new byte[32], Packet ); byte [] NonceExt = Data.Concat( Nonce, new byte[12] ); int N = PlainText.Length; byte [] Box = new byte[ N ]; byte [] k = GetSharedSecret( Pk, ClientKey ); Salsa.crypto_box_afternm( Box, PlainText, N, NonceExt, k ); Box = Data.Extract( Box, 16, N-16 ); DnsTx Tx = new DnsTx( 2 + 32 + 12 + 4 + Box.Length ); Tx.Put16( (int)QRM.Encrypt ); Tx.PutBytes( Nonce ); Tx.PutBytes( ClientKey.Public ); Tx.PutSlice( Pk, 0, 4 ); Tx.PutBytes( Box ); return Tx.Buffer; } private static bool CheckTag( byte [] Key, byte [] Tag ) { for (int i=0; i<4; i+=1 ) if ( Key[i] != Tag[i] ) return false; return true; } public static SecurityArg DecodeQuery( DnsRx Rx ) { SecurityArg Sa = new SecurityArg(); Sa.Nonce = Rx.ReadBytes( 12 ); Sa.PublicKey = Rx.ReadBytes( 32 ); byte [] Tag = Rx.ReadBytes( 4 ); byte [] data = Rx.ReadBytes( Rx.N - Rx.Ix ); // ToDo : throw exception if data is > some limit ( maybe 512? or based on loaded zones ) byte [] n = Data.Concat( Sa.Nonce, new byte[12] ); byte [] c = Data.Concat( new byte[16], data ); byte [] dm = new byte[ c.Length ]; bool Ok = false; foreach( KeyPair kp in ServerKey ) { if ( CheckTag( kp.Public, Tag ) ) { byte [] k = GetSharedSecret( Sa.PublicKey, kp ); if ( Salsa.crypto_secretbox_open( dm, c, c.Length, n, k ) == 0 ) { Ok = true; Sa.ServerKey = kp; break; } } } if ( !Ok ) throw new Exception( "Crypt Decode Query failed" ); Rx.Buffer = Data.Extract( dm, 32, dm.Length-32 ); Rx.N = Rx.Buffer.Length; Rx.Ix = 0; return Sa; } public static byte[] EncodeResponse( byte [] Response, SecurityArg Sa ) { if ( Sa != null ) { byte [] PlainText = Data.Concat( new byte[32], Response ); byte [] ServerNonce = Random.Bytes(12); byte [] NonceExt = Data.Concat( Sa.Nonce, ServerNonce ); int N = PlainText.Length; byte [] Box = new byte[ N ]; byte [] k = GetSharedSecret( Sa.PublicKey, Sa.ServerKey ); Salsa.crypto_box_afternm( Box, PlainText, N, NonceExt, k ); Box = Data.Extract( Box, 16, Box.Length-16 ); DnsTx Tx = new DnsTx( 2 + 24 + Box.Length ); Tx.Put16( (int)QRM.Encrypt ); Tx.PutBytes( NonceExt ); Tx.PutBytes( Box ); Response = Tx.Buffer; } return Response; } public static bool DecodeResponse( DnsRx Rx, byte [] Nonce, byte[] ServerNonce, byte[] ServerData, byte[] Pk ) { byte [] n = Data.Concat( Nonce, ServerNonce ); byte [] c = Data.Concat( new byte[16], ServerData ); byte [] dm = new byte[ c.Length ]; byte [] k = GetSharedSecret( Pk, ClientKey ); if ( Salsa.crypto_box_open_afternm( dm, c, c.Length, n, k ) != 0 ) return false; Rx.N = dm.Length-32; System.Buffer.BlockCopy( dm, 32, Rx.Buffer, 0, Rx.N ); Rx.Ix = 0; return true; } #if (Trace) public static void Test() { // Test data from http://cr.yp.to/highspeed/naclcrypto-20090310.pdf byte [] alicesk = new byte[32] { 0x77,0x07,0x6d,0x0a,0x73,0x18,0xa5,0x7d,0x3c,0x16,0xc1,0x72,0x51,0xb2,0x66,0x45,0xdf,0x4c,0x2f,0x87,0xeb,0xc0,0x99,0x2a,0xb1,0x77,0xfb,0xa5,0x1d,0xb9,0x2c,0x2a }; byte [] alicepk = new byte[32] { 0x85,0x20,0xf0,0x09,0x89,0x30,0xa7,0x54,0x74,0x8b,0x7d,0xdc,0xb4,0x3e,0xf7,0x5a,0x0d,0xbf,0x3a,0x0d,0x26,0x38,0x1a,0xf4,0xeb,0xa4,0xa9,0x8e,0xaa,0x9b,0x4e,0x6a }; byte [] bobsk = new byte[32] { 0x5d,0xab,0x08,0x7e,0x62,0x4a,0x8a,0x4b,0x79,0xe1,0x7f,0x8b,0x83,0x80,0x0e,0xe6,0x6f,0x3b,0xb1,0x29,0x26,0x18,0xb6,0xfd,0x1c,0x2f,0x8b,0x27,0xff,0x88,0xe0,0xeb }; byte [] bobpk = new byte[32] { 0xde,0x9e,0xdb,0x7d,0x7b,0x7d,0xc1,0xb4,0xd3,0x5b,0x61,0xc2,0xec,0xe4,0x35,0x37,0x3f,0x83,0x43,0xc8,0x5b,0x78,0x67,0x4d,0xad,0xfc,0x7e,0x14,0x6f,0x88,0x2b,0x4f }; byte [] sharedkey = new byte[32] { 0x4a,0x5d,0x9d,0x5b,0xa4,0xce,0x2d,0xe1,0x72,0x8e,0x3b,0xf4,0x80,0x35,0x0f,0x25,0xe0,0x7e,0x21,0xc9,0x47,0xd1,0x9e,0x33,0x76,0xf0,0x9b,0x3c,0x1e,0x16,0x17,0x42 }; byte [] firstkey = new byte[32] { 0x1b,0x27,0x55,0x64,0x73,0xe9,0x85,0xd4,0x62,0xcd,0x51,0x19,0x7a,0x9a,0x46,0xc7,0x60,0x09,0x54,0x9e,0xac,0x64,0x74,0xf2,0x06,0xc4,0xee,0x08,0x44,0xf6,0x83,0x89 }; byte [] secondkey = new byte[32] { 0xdc,0x90,0x8d,0xda,0x0b,0x93,0x44,0xa9,0x53,0x62,0x9b,0x73,0x38,0x20,0x77,0x88,0x80,0xf3,0xce,0xb4,0x21,0xbb,0x61,0xb9,0x1c,0xbd,0x4c,0x3e,0x66,0x25,0x6c,0xe4 }; byte [] q = new byte[32]; Curve.crypto_scalarmult_curve25519( q, alicesk, Curve.Base ); if ( !Data.BytesEqual( q, alicepk ) ) Cache.Log("Alice pk not correct"); Curve.crypto_scalarmult_curve25519( q, bobsk, Curve.Base ); if ( !Data.BytesEqual( q, bobpk ) ) Cache.Log("Bob pk not correct"); Curve.crypto_scalarmult_curve25519( q, alicesk, bobpk ); if ( !Data.BytesEqual( q, sharedkey ) ) Cache.Log("Shared secret not correct (1)" ); { byte [] zero = new byte[32]; byte [] c = new byte[16] { 0x65,0x78,0x70,0x61,0x6e,0x64,0x20,0x33,0x32,0x2d,0x62,0x79,0x74,0x65,0x20,0x6b }; Salsa.crypto_core_hsalsa20( q, 0, zero, sharedkey, c ); if ( !Data.BytesEqual( q, firstkey ) ) Cache.Log("First key not correct" ); byte [] nonceprefix = new byte[16] { 0x69,0x69,0x6e,0xe9,0x55,0xb6,0x2b,0x73,0xcd,0x62,0xbd,0xa8,0x75,0xfc,0x73,0xd6 } ; Salsa.crypto_core_hsalsa20( q, 0, nonceprefix, firstkey, c ); if ( !Data.BytesEqual( q, secondkey ) ) Cache.Log("Second key not correct" ); } { byte [] k = new byte[32] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216 } ; byte [] input = new byte[16] { 101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116 }; byte [] c = new byte[16] { 101,120,112, 97,110,100, 32, 51, 50, 45, 98,121,116,101, 32,107 }; byte [] output = new byte[64]; Salsa.crypto_core_salsa20( output, 0, input, k, c ); byte [] checkoutput = new byte[64]{ 69, 37, 68, 39, 41, 15,107,193,255,139,122, 6,170,233,217, 98, 89,144,182,106, 21, 51,200, 65,239, 49,222, 34,215,114, 40,126,104,197, 7,225,197,153, 31, 2,102, 78, 76,176, 84,245,246,184,177,160,133,130, 6, 72,149,119,192,195,132,236,234,103,246, 74 }; if ( !Data.BytesEqual( output, checkoutput ) ) Cache.Log("output not correct" ); } { byte [] k = new byte [32] { 0xee,0x30,0x4f,0xca,0x27,0x00,0x8d,0x8c,0x12,0x6f,0x90,0x02,0x79,0x01,0xd8,0x0f,0x7f,0x1d,0x8b,0x8d,0xc9,0x36,0xcf,0x3b,0x9f,0x81,0x96,0x92,0x82,0x7e,0x57,0x77 } ; byte [] input = new byte[16] { 0x81,0x91,0x8e,0xf2,0xa5,0xe0,0xda,0x9b,0x3e,0x90,0x60,0x52,0x1e,0x4b,0xb3,0x52 } ; byte [] c = new byte[16] { 101,120,112, 97,110,100, 32, 51, 50, 45, 98,121,116,101, 32,107 } ; byte [] output = new byte[32]; Salsa.crypto_core_hsalsa20(output,0,input,k,c); byte [] checkoutput = new byte[32]{ 0xbc,0x1b,0x30,0xfc,0x07,0x2c,0xc1,0x40,0x75,0xe4,0xba,0xa7,0x31,0xb5,0xa8,0x45,0xea,0x9b,0x11,0xe9,0xa5,0x19,0x1f,0x94,0xe1,0x8c,0xba,0x8f,0xd8,0x21,0xa7,0xcd }; if ( !Data.BytesEqual( output, checkoutput ) ) Cache.Log("output not correct" ); } { byte [] nonce = new byte[24]{ 0x69,0x69,0x6e,0xe9,0x55,0xb6,0x2b,0x73,0xcd,0x62,0xbd,0xa8,0x75,0xfc,0x73,0xd6,0x82,0x19,0xe0,0x03,0x6b,0x7a,0x0b,0x37 } ; // API requires first 32 bytes to be 0 byte [] m = new byte[163] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0xbe,0x07,0x5f,0xc5,0x3c,0x81,0xf2,0xd5,0xcf,0x14,0x13,0x16,0xeb,0xeb,0x0c,0x7b ,0x52,0x28,0xc5,0x2a,0x4c,0x62,0xcb,0xd4,0x4b,0x66,0x84,0x9b,0x64,0x24,0x4f,0xfc ,0xe5,0xec,0xba,0xaf,0x33,0xbd,0x75,0x1a,0x1a,0xc7,0x28,0xd4,0x5e,0x6c,0x61,0x29 ,0x6c,0xdc,0x3c,0x01,0x23,0x35,0x61,0xf4,0x1d,0xb6,0x6c,0xce,0x31,0x4a,0xdb,0x31 ,0x0e,0x3b,0xe8,0x25,0x0c,0x46,0xf0,0x6d,0xce,0xea,0x3a,0x7f,0xa1,0x34,0x80,0x57 ,0xe2,0xf6,0x55,0x6a,0xd6,0xb1,0x31,0x8a,0x02,0x4a,0x83,0x8f,0x21,0xaf,0x1f,0xde ,0x04,0x89,0x77,0xeb,0x48,0xf5,0x9f,0xfd,0x49,0x24,0xca,0x1c,0x60,0x90,0x2e,0x52 ,0xf0,0xa0,0x89,0xbc,0x76,0x89,0x70,0x40,0xe0,0x82,0xf9,0x37,0x76,0x38,0x48,0x64 ,0x5e,0x07,0x05 } ; byte [] c = new byte[163]; Salsa.crypto_box( c,m,163,nonce,bobpk,alicesk ); byte [] checkc = new byte[147]{ 0xf3,0xff,0xc7,0x70,0x3f,0x94,0x00,0xe5,0x2a,0x7d,0xfb,0x4b,0x3d,0x33,0x05,0xd9,0x8e,0x99,0x3b,0x9f,0x48,0x68,0x12,0x73,0xc2,0x96,0x50,0xba,0x32,0xfc,0x76,0xce,0x48,0x33,0x2e,0xa7,0x16,0x4d,0x96,0xa4,0x47,0x6f,0xb8,0xc5,0x31,0xa1,0x18,0x6a,0xc0,0xdf,0xc1,0x7c,0x98,0xdc,0xe8,0x7b,0x4d,0xa7,0xf0,0x11,0xec,0x48,0xc9,0x72,0x71,0xd2,0xc2,0x0f,0x9b,0x92,0x8f,0xe2,0x27,0x0d,0x6f,0xb8,0x63,0xd5,0x17,0x38,0xb4,0x8e,0xee,0xe3,0x14,0xa7,0xcc,0x8a,0xb9,0x32,0x16,0x45,0x48,0xe5,0x26,0xae,0x90,0x22,0x43,0x68,0x51,0x7a,0xcf,0xea,0xbd,0x6b,0xb3,0x73,0x2b,0xc0,0xe9,0xda,0x99,0x83,0x2b,0x61,0xca,0x01,0xb6,0xde,0x56,0x24,0x4a,0x9e,0x88,0xd5,0xf9,0xb3,0x79,0x73,0xf6,0x22,0xa4,0x3d,0x14,0xa6,0x59,0x9b,0x1f,0x65,0x4c,0xb4,0x5a,0x74,0xe3,0x55,0xa5 }; byte [] c1 = new byte[147]; for (int i=0; i<147; i+=1) c1[i] = c[16+i]; if ( !Data.BytesEqual( c1, checkc ) ) Cache.Log("c not correct" ); byte [] dm = new byte[163]; if ( Salsa.crypto_secretbox_open( dm,c,163,nonce,firstkey ) == 0 ) { bool ok = true; for (int i=32; i<163; i+=1) if ( dm[i] != m[i] ) ok = false; if ( !ok ) Cache.Log( "dm from box not correct" ); } else { Cache.Log( "Box failed to open" ); } } { byte [] Test = new byte[]{ 0x64, 0x88 }; byte [] ETest = Base32Encode( Test ); Cache.Log( Data.BytesToString(ETest) ); } { byte [] Epk = Data.Concat( PublicKeyPrefix, Base32Encode(alicepk,51) ); Domain d = Domain.New( Epk, Domain.Root ); byte [] Dpk = GetPublicKey( d ); if ( !Data.BytesEqual( Dpk, alicepk ) ) Cache.Log("Public key encode/decode failed" ); } } #endif } class Curve { static void add ( uint [] output, uint [] a, uint [] b ) { uint j; uint u; u = 0; for ( j = 0; j < 31; ++j ) { u += a[j] + b[j]; output[j] = u & 255; u >>= 8; } u += a[31] + b[31]; output[31] = u; } static void sub ( uint [] output, uint [] a, uint [] b ) { uint j; uint u; u = 218; for ( j = 0; j < 31; ++j ) { u += a[j] + 65280 - b[j]; output[j] = u & 255; u >>= 8; } u += a[31] - b[31]; output[31] = u; } static void squeeze ( uint [] a ) { uint j; uint u; u = 0; for ( j = 0; j < 31; ++j ) { u += a[j]; a[j] = u & 255; u >>= 8; } u += a[31]; a[31] = u & 127; u = 19 * ( u >> 7 ); for ( j = 0; j < 31; ++j ) { u += a[j]; a[j] = u & 255; u >>= 8; } u += a[31]; a[31] = u; } static uint [] minusp = new uint[32] { 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 }; static void freeze ( uint [] a ) { uint [] aorig = new uint[32]; uint j; uint negative; for ( j = 0; j < 32; ++j ) aorig[j] = a[j]; add ( a, a, minusp ); negative = (uint) ( - ( ( a[31] >> 7 ) & 1 ) ); for ( j = 0; j < 32; ++j ) a[j] ^= negative & ( aorig[j] ^ a[j] ); } static void mult ( uint [] output, uint [] a, uint [] b ) { uint i; uint j; uint u; for ( i = 0; i < 32; ++i ) { u = 0; for ( j = 0; j <= i; ++j ) u += a[j] * b[i - j]; for ( j = i + 1; j < 32; ++j ) u += 38 * a[j] * b[i + 32 - j]; output[i] = u; } squeeze ( output ); } static void mult121665 ( uint [] output, uint [] a ) { uint j; uint u; u = 0; for ( j = 0; j < 31; ++j ) { u += 121665 * a[j]; output[j] = u & 255; u >>= 8; } u += 121665 * a[31]; output[31] = u & 127; u = 19 * ( u >> 7 ); for ( j = 0; j < 31; ++j ) { u += output[j]; output[j] = u & 255; u >>= 8; } u += output[j]; output[j] = u; } static void square ( uint [] output, uint [] a ) { uint i; uint j; uint u; for ( i = 0; i < 32; ++i ) { u = 0; for ( j = 0; j < i - j; ++j ) u += a[j] * a[i - j]; for ( j = i + 1; j < i + 32 - j; ++j ) u += 38 * a[j] * a[i + 32 - j]; u *= 2; if ( ( i & 1 ) == 0 ) { u += a[i / 2] * a[i / 2]; u += 38 * a[i / 2 + 16] * a[i / 2 + 16]; } output[i] = u; } squeeze ( output ); } static void select ( uint [] p, uint [] q, uint [] r, uint [] s, uint b ) { uint j; uint t; uint bminus1; bminus1 = b - 1; for ( j = 0; j < 32; ++j ) { t = bminus1 & ( r[j] ^ s[j] ); p[j] = s[j] ^ t; q[j] = r[j] ^ t; } } static void mainloop ( uint [] work, uint [] work_32, byte [] e ) { uint [] xzm1 = new uint[32]; uint [] xzm = new uint[32]; uint [] xzmb = new uint[32]; uint [] xzm1b = new uint[32]; uint [] xznb = new uint[32]; uint [] xzn1b = new uint[32]; uint [] a0 = new uint[32]; uint [] a1 = new uint[32]; uint [] b0 = new uint[32]; uint [] b1 = new uint[32]; uint [] c1 = new uint[32]; uint [] xzm1_32 = new uint[32]; uint [] xzm_32 = new uint[32]; uint [] xzmb_32 = new uint[32]; uint [] xzm1b_32 = new uint[32]; uint [] xznb_32 = new uint[32]; uint [] xzn1b_32 = new uint[32]; uint [] a0_32 = new uint[32]; uint [] a1_32 = new uint[32]; uint [] b0_32 = new uint[32]; uint [] b1_32 = new uint[32]; uint [] c1_32 = new uint[32]; uint [] r = new uint[32]; uint [] s = new uint[32]; uint [] t = new uint[32]; uint [] u = new uint[32]; uint i; uint j; uint b; int pos; for ( j = 0; j < 32; ++j ) xzm1[j] = work[j]; xzm1_32[0] = 1; for ( j = 1; j < 32; ++j ) xzm1_32[j] = 0; xzm[0] = 1; for ( j = 1; j < 32; ++j ) xzm[j] = 0; for ( j = 0; j < 32; ++j ) xzm_32[j] = 0; for ( pos = 254; pos >= 0; --pos ) { b = (uint) e[pos / 8] >> ( pos & 7 ); b &= 1; select ( xzmb, xzm1b, xzm, xzm1, b ); select ( xzmb_32, xzm1b_32, xzm_32, xzm1_32, b ); add ( a0, xzmb, xzmb_32 ); sub ( a0_32, xzmb, xzmb_32 ); add ( a1, xzm1b, xzm1b_32 ); sub ( a1_32, xzm1b, xzm1b_32 ); square ( b0, a0 ); square ( b0_32, a0_32 ); mult ( b1, a1, a0_32 ); mult ( b1_32, a1_32, a0 ); add ( c1, b1, b1_32 ); sub ( c1_32, b1, b1_32 ); square ( r, c1_32 ); sub ( s, b0, b0_32 ); mult121665 ( t, s ); add ( u, t, b0 ); mult ( xznb, b0, b0_32 ); mult ( xznb_32, s, u ); square ( xzn1b, c1 ); mult ( xzn1b_32, r, work ); select ( xzm, xzm1, xznb, xzn1b, b ); select ( xzm_32, xzm1_32, xznb_32, xzn1b_32, b ); } for ( j = 0; j < 32; ++j ) { work[j] = xzm[j]; work_32[j] = xzm_32[j]; } } static void recip ( uint [] output, uint [] z ) { uint [] z2 = new uint[32]; uint [] z9 = new uint[32]; uint [] z11 = new uint[32]; uint [] z2_5_0 = new uint[32]; uint [] z2_10_0 = new uint[32]; uint [] z2_20_0 = new uint[32]; uint [] z2_50_0 = new uint[32]; uint [] z2_100_0 = new uint[32]; uint [] t0 = new uint[32]; uint [] t1 = new uint[32]; int i; /* 2 */ square ( z2, z ); /* 4 */ square ( t1, z2 ); /* 8 */ square ( t0, t1 ); /* 9 */ mult ( z9, t0, z ); /* 11 */ mult ( z11, z9, z2 ); /* 22 */ square ( t0, z11 ); /* 2^5 - 2^0 = 31 */ mult ( z2_5_0, t0, z9 ); /* 2^6 - 2^1 */ square ( t0, z2_5_0 ); /* 2^7 - 2^2 */ square ( t1, t0 ); /* 2^8 - 2^3 */ square ( t0, t1 ); /* 2^9 - 2^4 */ square ( t1, t0 ); /* 2^10 - 2^5 */ square ( t0, t1 ); /* 2^10 - 2^0 */ mult ( z2_10_0, t0, z2_5_0 ); /* 2^11 - 2^1 */ square ( t0, z2_10_0 ); /* 2^12 - 2^2 */ square ( t1, t0 ); /* 2^20 - 2^10 */ for ( i = 2; i < 10; i += 2 ) { square ( t0, t1 ); square ( t1, t0 ); } /* 2^20 - 2^0 */ mult ( z2_20_0, t1, z2_10_0 ); /* 2^21 - 2^1 */ square ( t0, z2_20_0 ); /* 2^22 - 2^2 */ square ( t1, t0 ); /* 2^40 - 2^20 */ for ( i = 2; i < 20; i += 2 ) { square ( t0, t1 ); square ( t1, t0 ); } /* 2^40 - 2^0 */ mult ( t0, t1, z2_20_0 ); /* 2^41 - 2^1 */ square ( t1, t0 ); /* 2^42 - 2^2 */ square ( t0, t1 ); /* 2^50 - 2^10 */ for ( i = 2; i < 10; i += 2 ) { square ( t1, t0 ); square ( t0, t1 ); } /* 2^50 - 2^0 */ mult ( z2_50_0, t0, z2_10_0 ); /* 2^51 - 2^1 */ square ( t0, z2_50_0 ); /* 2^52 - 2^2 */ square ( t1, t0 ); /* 2^100 - 2^50 */ for ( i = 2; i < 50; i += 2 ) { square ( t0, t1 ); square ( t1, t0 ); } /* 2^100 - 2^0 */ mult ( z2_100_0, t1, z2_50_0 ); /* 2^101 - 2^1 */ square ( t1, z2_100_0 ); /* 2^102 - 2^2 */ square ( t0, t1 ); /* 2^200 - 2^100 */ for ( i = 2; i < 100; i += 2 ) { square ( t1, t0 ); square ( t0, t1 ); } /* 2^200 - 2^0 */ mult ( t1, t0, z2_100_0 ); /* 2^201 - 2^1 */ square ( t0, t1 ); /* 2^202 - 2^2 */ square ( t1, t0 ); /* 2^250 - 2^50 */ for ( i = 2; i < 50; i += 2 ) { square ( t0, t1 ); square ( t1, t0 ); } /* 2^250 - 2^0 */ mult ( t0, t1, z2_50_0 ); /* 2^251 - 2^1 */ square ( t1, t0 ); /* 2^252 - 2^2 */ square ( t0, t1 ); /* 2^253 - 2^3 */ square ( t1, t0 ); /* 2^254 - 2^4 */ square ( t0, t1 ); /* 2^255 - 2^5 */ square ( t1, t0 ); /* 2^255 - 21 */ mult ( output, t1, z11 ); } public static int crypto_scalarmult_curve25519( byte [] q, byte [] n, byte [] p ) { uint [] work = new uint[32]; uint [] work_32 = new uint[32]; uint [] work_64 = new uint[32]; byte [] e = new byte[32]; uint i; for ( i = 0; i < 32; ++i ) e[i] = n[i]; e[0] &= 248; e[31] &= 127; e[31] |= 64; for ( i = 0; i < 32; ++i ) work[i] = p[i]; mainloop ( work, work_32, e ); recip ( work_32, work_32 ); mult ( work_64, work, work_32 ); freeze ( work_64 ); for ( i = 0; i < 32; ++i ) q[i] = (byte)work_64[i]; return 0; } public static readonly byte [] Base = GetStandard(); static byte [] GetStandard() { byte [] b = new byte[32]; b[0] = 9; return b; } } class Salsa { const int ROUNDS = 20; static uint rotate(uint u,int c) { return (u << c) | (u >> (32 - c)); } static uint load_littleendian( byte [] x, int off ) { return (uint) (x[off]) | (((uint) (x[off+1])) << 8) | (((uint) (x[off+2])) << 16) | (((uint) (x[off+3])) << 24); } static void store_littleendian( byte [] x, int off, uint u) { x[off++] = (byte)u; u >>= 8; x[off++] = (byte)u; u >>= 8; x[off++] = (byte)u; u >>= 8; x[off++] = (byte)u; } public static int crypto_core_hsalsa20( byte [] output, int off, byte[] input, byte[] k, byte[] c ) { uint x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; uint j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; int i; j0 = x0 = load_littleendian(c, 0); j1 = x1 = load_littleendian(k, 0); j2 = x2 = load_littleendian(k, 4); j3 = x3 = load_littleendian(k, 8); j4 = x4 = load_littleendian(k, 12); j5 = x5 = load_littleendian(c, 4); j6 = x6 = load_littleendian(input, 0); j7 = x7 = load_littleendian(input, 4); j8 = x8 = load_littleendian(input, 8); j9 = x9 = load_littleendian(input, 12); j10 = x10 = load_littleendian(c, 8); j11 = x11 = load_littleendian(k, 16); j12 = x12 = load_littleendian(k, 20); j13 = x13 = load_littleendian(k, 24); j14 = x14 = load_littleendian(k, 28); j15 = x15 = load_littleendian(c, 12); for (i = ROUNDS;i > 0;i -= 2) { x4 ^= rotate( x0+x12, 7); x8 ^= rotate( x4+ x0, 9); x12 ^= rotate( x8+ x4,13); x0 ^= rotate(x12+ x8,18); x9 ^= rotate( x5+ x1, 7); x13 ^= rotate( x9+ x5, 9); x1 ^= rotate(x13+ x9,13); x5 ^= rotate( x1+x13,18); x14 ^= rotate(x10+ x6, 7); x2 ^= rotate(x14+x10, 9); x6 ^= rotate( x2+x14,13); x10 ^= rotate( x6+ x2,18); x3 ^= rotate(x15+x11, 7); x7 ^= rotate( x3+x15, 9); x11 ^= rotate( x7+ x3,13); x15 ^= rotate(x11+ x7,18); x1 ^= rotate( x0+ x3, 7); x2 ^= rotate( x1+ x0, 9); x3 ^= rotate( x2+ x1,13); x0 ^= rotate( x3+ x2,18); x6 ^= rotate( x5+ x4, 7); x7 ^= rotate( x6+ x5, 9); x4 ^= rotate( x7+ x6,13); x5 ^= rotate( x4+ x7,18); x11 ^= rotate(x10+ x9, 7); x8 ^= rotate(x11+x10, 9); x9 ^= rotate( x8+x11,13); x10 ^= rotate( x9+ x8,18); x12 ^= rotate(x15+x14, 7); x13 ^= rotate(x12+x15, 9); x14 ^= rotate(x13+x12,13); x15 ^= rotate(x14+x13,18); } x0 += j0; x1 += j1; x2 += j2; x3 += j3; x4 += j4; x5 += j5; x6 += j6; x7 += j7; x8 += j8; x9 += j9; x10 += j10; x11 += j11; x12 += j12; x13 += j13; x14 += j14; x15 += j15; x0 -= load_littleendian(c, 0); x5 -= load_littleendian(c, 4); x10 -= load_littleendian(c, 8); x15 -= load_littleendian(c, 12); x6 -= load_littleendian(input, 0); x7 -= load_littleendian(input, 4); x8 -= load_littleendian(input, 8); x9 -= load_littleendian(input, 12); store_littleendian(output, off+0,x0); store_littleendian(output, off+4,x5); store_littleendian(output, off+8,x10); store_littleendian(output, off+12,x15); store_littleendian(output, off+16,x6); store_littleendian(output, off+20,x7); store_littleendian(output, off+24,x8); store_littleendian(output, off+28,x9); return 0; } public static int crypto_core_salsa20( byte [] output, int off, byte [] input, byte [] k, byte[] c ) { uint x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; uint j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; int i; j0 = x0 = load_littleendian(c,0); j1 = x1 = load_littleendian(k,0); j2 = x2 = load_littleendian(k,4); j3 = x3 = load_littleendian(k,8); j4 = x4 = load_littleendian(k,12); j5 = x5 = load_littleendian(c,4); j6 = x6 = load_littleendian(input,0); j7 = x7 = load_littleendian(input,4); j8 = x8 = load_littleendian(input,8); j9 = x9 = load_littleendian(input,12); j10 = x10 = load_littleendian(c,8); j11 = x11 = load_littleendian(k,16); j12 = x12 = load_littleendian(k,20); j13 = x13 = load_littleendian(k,24); j14 = x14 = load_littleendian(k,28); j15 = x15 = load_littleendian(c,12); for (i = ROUNDS;i > 0;i -= 2) { x4 ^= rotate( x0+x12, 7); x8 ^= rotate( x4+ x0, 9); x12 ^= rotate( x8+ x4,13); x0 ^= rotate(x12+ x8,18); x9 ^= rotate( x5+ x1, 7); x13 ^= rotate( x9+ x5, 9); x1 ^= rotate(x13+ x9,13); x5 ^= rotate( x1+x13,18); x14 ^= rotate(x10+ x6, 7); x2 ^= rotate(x14+x10, 9); x6 ^= rotate( x2+x14,13); x10 ^= rotate( x6+ x2,18); x3 ^= rotate(x15+x11, 7); x7 ^= rotate( x3+x15, 9); x11 ^= rotate( x7+ x3,13); x15 ^= rotate(x11+ x7,18); x1 ^= rotate( x0+ x3, 7); x2 ^= rotate( x1+ x0, 9); x3 ^= rotate( x2+ x1,13); x0 ^= rotate( x3+ x2,18); x6 ^= rotate( x5+ x4, 7); x7 ^= rotate( x6+ x5, 9); x4 ^= rotate( x7+ x6,13); x5 ^= rotate( x4+ x7,18); x11 ^= rotate(x10+ x9, 7); x8 ^= rotate(x11+x10, 9); x9 ^= rotate( x8+x11,13); x10 ^= rotate( x9+ x8,18); x12 ^= rotate(x15+x14, 7); x13 ^= rotate(x12+x15, 9); x14 ^= rotate(x13+x12,13); x15 ^= rotate(x14+x13,18); } x0 += j0; x1 += j1; x2 += j2; x3 += j3; x4 += j4; x5 += j5; x6 += j6; x7 += j7; x8 += j8; x9 += j9; x10 += j10; x11 += j11; x12 += j12; x13 += j13; x14 += j14; x15 += j15; store_littleendian(output,off+0,x0); store_littleendian(output,off+4,x1); store_littleendian(output,off+8,x2); store_littleendian(output,off+12,x3); store_littleendian(output,off+16,x4); store_littleendian(output,off+20,x5); store_littleendian(output,off+24,x6); store_littleendian(output,off+28,x7); store_littleendian(output,off+32,x8); store_littleendian(output,off+36,x9); store_littleendian(output,off+40,x10); store_littleendian(output,off+44,x11); store_littleendian(output,off+48,x12); store_littleendian(output,off+52,x13); store_littleendian(output,off+56,x14); store_littleendian(output,off+60,x15); return 0; } // "expand 32-byte k"; static byte [] sigma = new byte[16] { 0x65,0x78,0x70,0x61,0x6e,0x64,0x20,0x33,0x32,0x2d,0x62,0x79,0x74,0x65,0x20,0x6b}; static int crypto_stream_salsa20( byte [] c, int clen, byte [] n, int noff, byte [] k ) { byte [] input = new byte[16]; byte [] block = new byte[64]; int i; uint u; if (clen==0) return 0; for (i = 0;i < 8;++i) input[i] = n[noff+i]; for (i = 8;i < 16;++i) input[i] = 0; int coff = 0; while (clen >= 64) { crypto_core_salsa20(c,coff,input,k,sigma); u = 1; for (i = 8;i < 16;++i) { u += (uint) input[i]; input[i] = (byte)u; u >>= 8; } clen -= 64; coff += 64; } if (clen != 0) { crypto_core_salsa20(block,0,input,k,sigma); for (i = 0;i < clen;++i) c[coff+i] = block[i]; } return 0; } static int crypto_stream_xsalsa20( byte [] c, int clen, byte [] n, byte [] k ) { byte [] subkey = new byte[32]; crypto_core_hsalsa20(subkey,0,n,k,sigma); return crypto_stream_salsa20(c,clen,n,16,subkey); } public static int crypto_box( byte [] c, byte [] m, int mlen, byte [] n, byte [] pk, byte [] sk ) { byte [] k = new byte[32]; crypto_box_beforenm( k, pk, sk ); return crypto_box_afternm(c,m,mlen,n,k); } static byte [] n = new byte[16]; public static int crypto_box_beforenm( byte [] k, byte [] pk, byte [] sk ) { byte [] s = new byte[32]; Curve.crypto_scalarmult_curve25519( s, sk, pk ); return crypto_core_hsalsa20( k, 0, n, s, sigma ); } public static int crypto_box_afternm( byte [] c, byte [] m, int mlen, byte [] n, byte [] k ) { return crypto_secretbox(c,m,mlen,n,k); } public static int crypto_box_open_afternm( byte [] m, byte [] c, int clen, byte [] n, byte [] k ) { return crypto_secretbox_open(m,c,clen,n,k); } static int crypto_secretbox( byte [] c, byte [] m, int mlen, byte [] n, byte [] k ) { if ( mlen < 32 ) return -1; crypto_stream_xsalsa20_xor(c,m,mlen,n,k); Poly.crypto_onetimeauth( c, 16, c, 32, mlen - 32, c ); for ( int i = 0; i < 16; ++i ) c[i] = 0; return 0; } public static int crypto_secretbox_open( byte [] m, byte [] c, int clen, byte [] n, byte [] k ) { byte [] subkey = new byte[32]; if ( clen < 32 ) return -1; crypto_stream_xsalsa20(subkey,32,n,k); if ( Poly.crypto_onetimeauth_verify( c, 16, c, 32, clen - 32, subkey ) != 0 ) return -1; crypto_stream_xsalsa20_xor( m, c, clen, n, k ); for ( int i = 0; i < 32; ++i ) m[i] = 0; return 0; } static int crypto_stream_xsalsa20_xor( byte [] c, byte [] m, int mlen, byte [] n, byte [] k ) { byte [] subkey = new byte[32]; crypto_core_hsalsa20(subkey,0,n,k,sigma); return crypto_stream_salsa20_xor(c,m,mlen,n,16,subkey); } static int crypto_stream_salsa20_xor( byte [] c, byte [] m, int mlen, byte [] n, int noff, byte [] k ) { byte [] input = new byte[16]; byte [] block = new byte[64]; int i; uint u; int coff = 0; int moff = 0; if (mlen==0) return 0; for (i = 0;i < 8;++i) input[i] = n[noff+i]; for (i = 8;i < 16;++i) input[i] = 0; while (mlen >= 64) { crypto_core_salsa20(block,0,input,k,sigma); for (i = 0;i < 64;++i) c[coff+i] = (byte) ( m[moff+i] ^ block[i] ); u = 1; for (i = 8;i < 16;++i) { u += (uint) input[i]; input[i] = (byte)u; u >>= 8; } mlen -= 64; coff += 64; moff += 64; } if (mlen!=0) { crypto_core_salsa20(block,0,input,k,sigma); for (i = 0;i < mlen;++i) c[coff+i] = (byte)( m[moff+i] ^ block[i] ); } return 0; } } class Poly { static void add( uint [] h, uint [] c ) { uint j; uint u; u = 0; for (j = 0;j < 17;++j) { u += h[j] + c[j]; h[j] = u & 255; u >>= 8; } } static void squeeze( uint [] h ) { uint j; uint u; u = 0; for (j = 0;j < 16;++j) { u += h[j]; h[j] = u & 255; u >>= 8; } u += h[16]; h[16] = u & 3; u = 5 * (u >> 2); for (j = 0;j < 16;++j) { u += h[j]; h[j] = u & 255; u >>= 8; } u += h[16]; h[16] = u; } static uint [] minusp = new uint[17] { 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252 } ; static void freeze( uint [] h ) { uint [] horig = new uint[17]; uint j; uint negative; for (j = 0;j < 17;++j) horig[j] = h[j]; add(h,minusp); negative = (uint) -(h[16] >> 7); for (j = 0;j < 17;++j) h[j] ^= negative & (horig[j] ^ h[j]); } static void mulmod( uint [] h, uint [] r ) { uint [] hr = new uint[17]; uint i; uint j; uint u; for (i = 0;i < 17;++i) { u = 0; for (j = 0;j <= i;++j) u += h[j] * r[i - j]; for (j = i + 1;j < 17;++j) u += 320 * h[j] * r[i + 17 - j]; hr[i] = u; } for (i = 0;i < 17;++i) h[i] = hr[i]; squeeze(h); } public static int crypto_onetimeauth( byte [] output, uint outoff, byte [] input, uint inpoff, int inlen, byte [] k ) { uint j; uint [] r = new uint[17]; uint [] h = new uint[17]; uint [] c = new uint[17]; r[0] = k[0]; r[1] = k[1]; r[2] = k[2]; r[3] = k[3] & 15u; r[4] = k[4] & 252u; r[5] = k[5]; r[6] = k[6]; r[7] = k[7] & 15u; r[8] = k[8] & 252u; r[9] = k[9]; r[10] = k[10]; r[11] = k[11] & 15u; r[12] = k[12] & 252u; r[13] = k[13]; r[14] = k[14]; r[15] = k[15] & 15u; r[16] = 0; for (j = 0;j < 17;++j) h[j] = 0; while (inlen > 0) { for (j = 0;j < 17;++j) c[j] = 0; for (j = 0;(j < 16) && (j < inlen);++j) c[j] = input[inpoff+j]; c[j] = 1; inpoff += j; inlen -= (int)j; add(h,c); mulmod(h,r); } freeze(h); for (j = 0;j < 16;++j) c[j] = k[j + 16]; c[16] = 0; add(h,c); for (j = 0;j < 16;++j) output[outoff+j] = (byte)h[j]; return 0; } static int crypto_verify_16( byte [] x, uint xoff, byte [] y ) { uint differentbits = 0; for ( int i=0; i<16; i+=1 )differentbits |= ( (uint)x[xoff+i] ^ (uint)y[i] ); return (int)( (1 & ((differentbits - 1) >> 8)) - 1 ); } public static int crypto_onetimeauth_verify( byte [] h, uint hoff, byte [] input, uint inpoff, int inlen, byte [] k ) { byte [] correct = new byte[16]; crypto_onetimeauth( correct, 0, input, inpoff,inlen,k ); return crypto_verify_16(h,hoff,correct); } }