1
1
using System ;
2
+ using System . Collections . Generic ;
3
+ using System . Linq ;
2
4
using NaCl ;
3
5
4
6
namespace NetMQ
@@ -8,6 +10,115 @@ namespace NetMQ
8
10
/// </summary>
9
11
public class NetMQCertificate
10
12
{
13
+ // Z85 codec, taken from 0MQ RFC project, implements RFC32 Z85 encoding
14
+
15
+ // Maps base 256 to base 85
16
+ private static string Encoder = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#" ;
17
+
18
+ // Maps base 85 to base 256
19
+ // We chop off lower 32 and higher 128 ranges
20
+ // 0xFF denotes invalid characters within this range
21
+ private static byte [ ] Decoder = {
22
+ 0xFF , 0x44 , 0xFF , 0x54 , 0x53 , 0x52 , 0x48 , 0xFF , 0x4B , 0x4C , 0x46 , 0x41 ,
23
+ 0xFF , 0x3F , 0x3E , 0x45 , 0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 ,
24
+ 0x08 , 0x09 , 0x40 , 0xFF , 0x49 , 0x42 , 0x4A , 0x47 , 0x51 , 0x24 , 0x25 , 0x26 ,
25
+ 0x27 , 0x28 , 0x29 , 0x2A , 0x2B , 0x2C , 0x2D , 0x2E , 0x2F , 0x30 , 0x31 , 0x32 ,
26
+ 0x33 , 0x34 , 0x35 , 0x36 , 0x37 , 0x38 , 0x39 , 0x3A , 0x3B , 0x3C , 0x3D , 0x4D ,
27
+ 0xFF , 0x4E , 0x43 , 0xFF , 0xFF , 0x0A , 0x0B , 0x0C , 0x0D , 0x0E , 0x0F , 0x10 ,
28
+ 0x11 , 0x12 , 0x13 , 0x14 , 0x15 , 0x16 , 0x17 , 0x18 , 0x19 , 0x1A , 0x1B , 0x1C ,
29
+ 0x1D , 0x1E , 0x1F , 0x20 , 0x21 , 0x22 , 0x23 , 0x4F , 0xFF , 0x50 , 0xFF , 0xFF } ;
30
+
31
+ // --------------------------------------------------------------------------
32
+ // Encode a binary frame as a string; destination string MUST be at least
33
+ // size * 5 / 4 bytes long plus 1 byte for the null terminator. Returns
34
+ // dest. Size must be a multiple of 4.
35
+ // Returns NULL for invalid input.
36
+ private string Z85Encode ( byte [ ] data )
37
+ {
38
+ if ( data . Length % 4 != 0 )
39
+ {
40
+ return null ;
41
+ }
42
+
43
+ byte byte_nbr = 0 ;
44
+ UInt32 value = 0 ;
45
+ string dest = null ;
46
+ while ( byte_nbr < data . Length ) {
47
+ // Accumulate value in base 256 (binary)
48
+ value = value * 256 + data [ byte_nbr ++ ] ;
49
+ if ( byte_nbr % 4 == 0 )
50
+ {
51
+ // Output value in base 85
52
+ UInt32 divisor = 85 * 85 * 85 * 85 ;
53
+ while ( divisor != 0 )
54
+ {
55
+ dest += Encoder [ ( int ) ( value / divisor % 85 ) ] ;
56
+ divisor /= 85 ;
57
+ }
58
+ value = 0 ;
59
+ }
60
+ }
61
+
62
+ dest += char . MinValue ;
63
+ return dest ;
64
+ }
65
+
66
+
67
+ // --------------------------------------------------------------------------
68
+ // Decode an encoded string into a binary frame; dest must be at least
69
+ // strlen (string) * 4 / 5 bytes long. Returns dest. strlen (string)
70
+ // must be a multiple of 5.
71
+ // Returns NULL for invalid input.
72
+ byte [ ] Z85Decode ( string key )
73
+ {
74
+ UInt32 byte_nbr = 0 ;
75
+ UInt32 char_nbr = 0 ;
76
+ UInt32 value = 0 ;
77
+ var dest_ = new List < byte > ( ) ;
78
+ foreach ( var cha in key . TakeWhile ( c => c != char . MinValue ) )
79
+ {
80
+
81
+ // Accumulate value in base 85
82
+ if ( UInt32 . MaxValue / 85 < value )
83
+ {
84
+ // Invalid z85 encoding, represented value exceeds 0xffffffff
85
+ return null ;
86
+ }
87
+ value *= 85 ;
88
+ char_nbr ++ ;
89
+ var index = cha - 32 ;
90
+ if ( index >= Decoder . Length )
91
+ {
92
+ // Invalid z85 encoding, character outside range
93
+ return null ;
94
+ }
95
+ UInt32 summand = Decoder [ index ] ;
96
+ if ( summand == 0xFF || summand > ( UInt32 . MaxValue - value ) )
97
+ {
98
+ // Invalid z85 encoding, invalid character or represented value exceeds 0xffffffff
99
+ return null ;
100
+ }
101
+ value += summand ;
102
+ if ( char_nbr % 5 == 0 )
103
+ {
104
+ // Output value in base 256
105
+ UInt32 divisor = 256 * 256 * 256 ;
106
+ while ( divisor != 0 )
107
+ {
108
+ dest_ . Add ( ( byte ) ( value / divisor % 256 ) ) ;
109
+ divisor /= 256 ;
110
+ }
111
+ value = 0 ;
112
+ }
113
+ }
114
+ if ( char_nbr % 5 != 0 )
115
+ {
116
+ return null ;
117
+ }
118
+ return dest_ . ToArray ( ) ;
119
+
120
+ }
121
+
11
122
/// <summary>
12
123
/// Create a Certificate with a random secret key and a derived public key for the curve encryption
13
124
/// </summary>
@@ -36,6 +147,24 @@ public NetMQCertificate(byte[] secretKey, byte[] publicKey)
36
147
PublicKey = publicKey ;
37
148
}
38
149
150
+ /// <summary>
151
+ /// Create a certificate from secret key and public key
152
+ /// </summary>
153
+ /// <param name="secretKey">Secret key</param>
154
+ /// <param name="publicKey">Public key</param>
155
+ /// <exception cref="ArgumentException">If secretKey or publicKey are not 41-chars long</exception>
156
+ public NetMQCertificate ( string secretKey , string publicKey )
157
+ {
158
+ if ( secretKey . Length != 41 )
159
+ throw new ArgumentException ( "secretKey must be 41 char long" ) ;
160
+
161
+ if ( publicKey . Length != 41 )
162
+ throw new ArgumentException ( "publicKey must be 41 char long" ) ;
163
+
164
+ SecretKey = Z85Decode ( secretKey ) ;
165
+ PublicKey = Z85Decode ( publicKey ) ;
166
+ }
167
+
39
168
private NetMQCertificate ( byte [ ] key , bool isSecret )
40
169
{
41
170
if ( key . Length != 32 )
@@ -49,7 +178,22 @@ private NetMQCertificate(byte[] key, bool isSecret)
49
178
else
50
179
PublicKey = key ;
51
180
}
52
-
181
+
182
+ private NetMQCertificate ( string keystr , bool isSecret )
183
+ {
184
+ if ( keystr . Length != 41 )
185
+ throw new ArgumentException ( "key must be 41 bytes length" ) ;
186
+
187
+ var key = Z85Decode ( keystr ) ;
188
+ if ( isSecret )
189
+ {
190
+ SecretKey = key ;
191
+ PublicKey = Curve25519 . ScalarMultiplicationBase ( key ) ;
192
+ }
193
+ else
194
+ PublicKey = key ;
195
+ }
196
+
53
197
/// <summary>
54
198
/// Create a certificate from secret key, public key is derived from the secret key
55
199
/// </summary>
@@ -60,7 +204,18 @@ public NetMQCertificate FromSecretKey(byte[] secretKey)
60
204
{
61
205
return new NetMQCertificate ( secretKey , true ) ;
62
206
}
63
-
207
+
208
+ /// <summary>
209
+ /// Create a certificate from secret key, public key is derived from the secret key
210
+ /// </summary>
211
+ /// <param name="secretKey">Secret Key</param>
212
+ /// <exception cref="ArgumentException">If secret key is not 41-chars long</exception>
213
+ /// <returns>The newly created certificate</returns>
214
+ public NetMQCertificate FromSecretKey ( string secretKey )
215
+ {
216
+ return new NetMQCertificate ( secretKey , true ) ;
217
+ }
218
+
64
219
/// <summary>
65
220
/// Create a public key only certificate.
66
221
/// </summary>
@@ -72,11 +227,29 @@ public static NetMQCertificate FromPublicKey(byte[] publicKey)
72
227
return new NetMQCertificate ( publicKey , false ) ;
73
228
}
74
229
230
+ /// <summary>
231
+ /// Create a public key only certificate.
232
+ /// </summary>
233
+ /// <param name="publicKey">Public key</param>
234
+ /// <exception cref="ArgumentException">If public key is not 41-chars long</exception>
235
+ /// <returns>The newly created certificate</returns>
236
+ public static NetMQCertificate FromPublicKey ( string publicKey )
237
+ {
238
+ return new NetMQCertificate ( publicKey , false ) ;
239
+ }
240
+
75
241
/// <summary>
76
242
/// Curve Secret key
77
243
/// </summary>
78
244
public byte [ ] ? SecretKey { get ; private set ; }
79
245
246
+
247
+ /// <summary>
248
+ /// Curve Public key
249
+ /// </summary>
250
+ public string SecretKeyZ85 => SecretKey != null ? Z85Encode ( SecretKey ) : null ;
251
+
252
+
80
253
/// <summary>
81
254
/// Returns true if the certificate also includes a secret key
82
255
/// </summary>
@@ -86,5 +259,13 @@ public static NetMQCertificate FromPublicKey(byte[] publicKey)
86
259
/// Curve Public key
87
260
/// </summary>
88
261
public byte [ ] PublicKey { get ; private set ; }
262
+
263
+ /// <summary>
264
+ /// Curve Public key
265
+ /// </summary>
266
+ public string PublicKeyZ85
267
+ {
268
+ get => Z85Encode ( PublicKey ) ;
269
+ }
89
270
}
90
271
}
0 commit comments