| | | 1 | | using System; |
| | | 2 | | using System.Collections.Generic; |
| | | 3 | | using Renci.SshNet.Security.Chaos.NaCl.Internal; |
| | | 4 | | using Renci.SshNet.Security.Chaos.NaCl.Internal.Ed25519Ref10; |
| | | 5 | | using Renci.SshNet.Security.Chaos.NaCl.Internal.Salsa; |
| | | 6 | | |
| | | 7 | | namespace Renci.SshNet.Security.Chaos.NaCl |
| | | 8 | | { |
| | | 9 | | // This class is mainly for compatibility with NaCl's Curve25519 implementation |
| | | 10 | | // If you don't need that compatibility, use Ed25519.KeyExchange |
| | | 11 | | internal static class MontgomeryCurve25519 |
| | | 12 | | { |
| | 1 | 13 | | internal static readonly int PublicKeySizeInBytes = 32; |
| | 1 | 14 | | internal static readonly int PrivateKeySizeInBytes = 32; |
| | 1 | 15 | | internal static readonly int SharedKeySizeInBytes = 32; |
| | | 16 | | |
| | | 17 | | internal static byte[] GetPublicKey(byte[] privateKey) |
| | 0 | 18 | | { |
| | 0 | 19 | | if (privateKey == null) |
| | 0 | 20 | | throw new ArgumentNullException("privateKey"); |
| | 0 | 21 | | if (privateKey.Length != PrivateKeySizeInBytes) |
| | 0 | 22 | | throw new ArgumentException("privateKey.Length must be 32"); |
| | 0 | 23 | | var publicKey = new byte[32]; |
| | 0 | 24 | | GetPublicKey(new ArraySegment<byte>(publicKey), new ArraySegment<byte>(privateKey)); |
| | 0 | 25 | | return publicKey; |
| | 0 | 26 | | } |
| | | 27 | | |
| | 1 | 28 | | static readonly byte[] _basePoint = new byte[32] |
| | 1 | 29 | | { |
| | 1 | 30 | | 9, 0, 0, 0, 0, 0, 0, 0, |
| | 1 | 31 | | 0, 0, 0 ,0, 0, 0, 0, 0, |
| | 1 | 32 | | 0, 0, 0 ,0, 0, 0, 0, 0, |
| | 1 | 33 | | 0, 0, 0 ,0, 0, 0, 0, 0 |
| | 1 | 34 | | }; |
| | | 35 | | |
| | | 36 | | internal static void GetPublicKey(ArraySegment<byte> publicKey, ArraySegment<byte> privateKey) |
| | 0 | 37 | | { |
| | 0 | 38 | | if (publicKey.Array == null) |
| | 0 | 39 | | throw new ArgumentNullException("publicKey.Array"); |
| | 0 | 40 | | if (privateKey.Array == null) |
| | 0 | 41 | | throw new ArgumentNullException("privateKey.Array"); |
| | 0 | 42 | | if (publicKey.Count != PublicKeySizeInBytes) |
| | 0 | 43 | | throw new ArgumentException("privateKey.Count must be 32"); |
| | 0 | 44 | | if (privateKey.Count != PrivateKeySizeInBytes) |
| | 0 | 45 | | throw new ArgumentException("privateKey.Count must be 32"); |
| | | 46 | | |
| | | 47 | | // hack: abusing publicKey as temporary storage |
| | | 48 | | // todo: remove hack |
| | 0 | 49 | | for (int i = 0; i < 32; i++) |
| | 0 | 50 | | { |
| | 0 | 51 | | publicKey.Array[publicKey.Offset + i] = privateKey.Array[privateKey.Offset + i]; |
| | 0 | 52 | | } |
| | 0 | 53 | | ScalarOperations.sc_clamp(publicKey.Array, publicKey.Offset); |
| | | 54 | | |
| | | 55 | | GroupElementP3 A; |
| | 0 | 56 | | GroupOperations.ge_scalarmult_base(out A, publicKey.Array, publicKey.Offset); |
| | | 57 | | FieldElement publicKeyFE; |
| | 0 | 58 | | EdwardsToMontgomeryX(out publicKeyFE, ref A.Y, ref A.Z); |
| | 0 | 59 | | FieldOperations.fe_tobytes(publicKey.Array, publicKey.Offset, ref publicKeyFE); |
| | 0 | 60 | | } |
| | | 61 | | |
| | | 62 | | // hashes like the Curve25519 paper says |
| | | 63 | | internal static void KeyExchangeOutputHashCurve25519Paper(byte[] sharedKey, int offset) |
| | 0 | 64 | | { |
| | | 65 | | //c = Curve25519output |
| | | 66 | | const UInt32 c0 = 'C' | 'u' << 8 | 'r' << 16 | (UInt32)'v' << 24; |
| | | 67 | | const UInt32 c1 = 'e' | '2' << 8 | '5' << 16 | (UInt32)'5' << 24; |
| | | 68 | | const UInt32 c2 = '1' | '9' << 8 | 'o' << 16 | (UInt32)'u' << 24; |
| | | 69 | | const UInt32 c3 = 't' | 'p' << 8 | 'u' << 16 | (UInt32)'t' << 24; |
| | | 70 | | |
| | | 71 | | Array16<UInt32> salsaState; |
| | 0 | 72 | | salsaState.x0 = c0; |
| | 0 | 73 | | salsaState.x1 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 0); |
| | 0 | 74 | | salsaState.x2 = 0; |
| | 0 | 75 | | salsaState.x3 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 4); |
| | 0 | 76 | | salsaState.x4 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 8); |
| | 0 | 77 | | salsaState.x5 = c1; |
| | 0 | 78 | | salsaState.x6 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 12); |
| | 0 | 79 | | salsaState.x7 = 0; |
| | 0 | 80 | | salsaState.x8 = 0; |
| | 0 | 81 | | salsaState.x9 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 16); |
| | 0 | 82 | | salsaState.x10 = c2; |
| | 0 | 83 | | salsaState.x11 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 20); |
| | 0 | 84 | | salsaState.x12 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 24); |
| | 0 | 85 | | salsaState.x13 = 0; |
| | 0 | 86 | | salsaState.x14 = ByteIntegerConverter.LoadLittleEndian32(sharedKey, offset + 28); |
| | 0 | 87 | | salsaState.x15 = c3; |
| | 0 | 88 | | SalsaCore.Salsa(out salsaState, ref salsaState, 20); |
| | | 89 | | |
| | 0 | 90 | | ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 0, salsaState.x0); |
| | 0 | 91 | | ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 4, salsaState.x1); |
| | 0 | 92 | | ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 8, salsaState.x2); |
| | 0 | 93 | | ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 12, salsaState.x3); |
| | 0 | 94 | | ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 16, salsaState.x4); |
| | 0 | 95 | | ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 20, salsaState.x5); |
| | 0 | 96 | | ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 24, salsaState.x6); |
| | 0 | 97 | | ByteIntegerConverter.StoreLittleEndian32(sharedKey, offset + 28, salsaState.x7); |
| | 0 | 98 | | } |
| | | 99 | | |
| | 1 | 100 | | private static readonly byte[] _zero16 = new byte[16]; |
| | | 101 | | |
| | | 102 | | // hashes like the NaCl paper says instead i.e. HSalsa(x,0) |
| | | 103 | | internal static void KeyExchangeOutputHashNaCl(byte[] sharedKey, int offset) |
| | 0 | 104 | | { |
| | 0 | 105 | | Salsa20.HSalsa20(sharedKey, offset, sharedKey, offset, _zero16, 0); |
| | 0 | 106 | | } |
| | | 107 | | |
| | | 108 | | internal static byte[] KeyExchange(byte[] publicKey, byte[] privateKey) |
| | 0 | 109 | | { |
| | 0 | 110 | | var sharedKey = new byte[SharedKeySizeInBytes]; |
| | 0 | 111 | | KeyExchange(new ArraySegment<byte>(sharedKey), new ArraySegment<byte>(publicKey), new ArraySegment<byte>(pri |
| | 0 | 112 | | return sharedKey; |
| | 0 | 113 | | } |
| | | 114 | | |
| | | 115 | | internal static void KeyExchange(ArraySegment<byte> sharedKey, ArraySegment<byte> publicKey, ArraySegment<byte> |
| | 0 | 116 | | { |
| | 0 | 117 | | if (sharedKey.Array == null) |
| | 0 | 118 | | throw new ArgumentNullException("sharedKey.Array"); |
| | 0 | 119 | | if (publicKey.Array == null) |
| | 0 | 120 | | throw new ArgumentNullException("publicKey.Array"); |
| | 0 | 121 | | if (privateKey.Array == null) |
| | 0 | 122 | | throw new ArgumentNullException("privateKey"); |
| | 0 | 123 | | if (sharedKey.Count != 32) |
| | 0 | 124 | | throw new ArgumentException("sharedKey.Count != 32"); |
| | 0 | 125 | | if (publicKey.Count != 32) |
| | 0 | 126 | | throw new ArgumentException("publicKey.Count != 32"); |
| | 0 | 127 | | if (privateKey.Count != 32) |
| | 0 | 128 | | throw new ArgumentException("privateKey.Count != 32"); |
| | 0 | 129 | | MontgomeryOperations.scalarmult(sharedKey.Array, sharedKey.Offset, privateKey.Array, privateKey.Offset, publ |
| | 0 | 130 | | KeyExchangeOutputHashNaCl(sharedKey.Array, sharedKey.Offset); |
| | 0 | 131 | | } |
| | | 132 | | |
| | | 133 | | internal static void EdwardsToMontgomeryX(out FieldElement montgomeryX, ref FieldElement edwardsY, ref FieldElem |
| | 0 | 134 | | { |
| | | 135 | | FieldElement tempX, tempZ; |
| | 0 | 136 | | FieldOperations.fe_add(out tempX, ref edwardsZ, ref edwardsY); |
| | 0 | 137 | | FieldOperations.fe_sub(out tempZ, ref edwardsZ, ref edwardsY); |
| | 0 | 138 | | FieldOperations.fe_invert(out tempZ, ref tempZ); |
| | 0 | 139 | | FieldOperations.fe_mul(out montgomeryX, ref tempX, ref tempZ); |
| | 0 | 140 | | } |
| | | 141 | | } |
| | | 142 | | } |