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