Use implicit type casting operator for serialization.
[NovacoinLibrary.git] / Novacoin / CKeyPair.cs
1 \feff/**
2  *  Novacoin classes library
3  *  Copyright (C) 2015 Alex D. (balthazar.ad@gmail.com)
4
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU Affero General Public License as
7  *  published by the Free Software Foundation, either version 3 of the
8  *  License, or (at your option) any later version.
9
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU Affero General Public License for more details.
14
15  *  You should have received a copy of the GNU Affero General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 using Org.BouncyCastle.Crypto;
20 using Org.BouncyCastle.Crypto.Generators;
21
22 using System.Security.Cryptography;
23
24 using Org.BouncyCastle.Crypto.Parameters;
25 using Org.BouncyCastle.Math;
26 using Org.BouncyCastle.Security;
27 using System.Collections.Generic;
28 using System.Linq;
29 using System;
30
31 namespace Novacoin
32 {
33     public class CKeyPair : CKey
34     {
35         private ECPrivateKeyParameters _Private;
36         private RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
37
38         /// <summary>
39         /// Initialize new CKeyPair instance with random secret.
40         /// </summary>
41         public CKeyPair(bool Compressed = true)
42         {
43
44
45             var genParams = new ECKeyGenerationParameters(domain, new SecureRandom());
46             var generator = new ECKeyPairGenerator("ECDSA");
47             generator.Init(genParams);
48             var ecKeyPair = generator.GenerateKeyPair();
49
50             _Private = (ECPrivateKeyParameters)ecKeyPair.Private;
51             _Public = (ECPublicKeyParameters)ecKeyPair.Public;
52
53             /*
54               BigInteger D;
55               var buffer1 = new byte[32];
56               var buffer2 = new byte[32];
57
58               do
59               {
60                   rng.GetBytes(buffer1);
61                   rng.GetNonZeroBytes(buffer2);
62
63                   D = new BigInteger(Hash256.ComputeRaw256(ref buffer1, ref buffer2));
64
65                   if (D.BitLength < 249)
66                       System.Console.WriteLine(D.BitLength);
67               }
68               while (D.SignValue == -1);
69
70               var Q = curve.G.Multiply(D);
71
72               _Private = new ECPrivateKeyParameters(D, domain);
73               _Public = new ECPublicKeyParameters(Q, domain);
74             */
75
76             if (Compressed)
77             {
78                 _Public = Compress(_Public);
79             }
80         }
81
82         /// <summary>
83         /// Init key pair using secret sequence of bytes
84         /// </summary>
85         /// <param name="secretBytes">Byte sequence</param>
86         /// <param name="Compressed">Compression flag</param>
87         public CKeyPair(byte[] secretBytes, bool Compressed=true)
88         {
89             if (secretBytes.Length != 32)
90             {
91                 throw new ArgumentException("Serialized secret key must be 32 bytes long.");
92             }
93
94             // Deserialize secret value
95             var D = new BigInteger(secretBytes);
96
97             // Append with zero byte if necessary
98             if (D.SignValue == -1)
99             {
100                 var positiveKeyBytes = new byte[33];
101                 Array.Copy(secretBytes, 0, positiveKeyBytes, 1, 32);
102                 D = new BigInteger(positiveKeyBytes);
103             }
104
105             // Calculate public key
106             var Q = curve.G.Multiply(D);
107
108             _Private = new ECPrivateKeyParameters(D, domain);
109             _Public = new ECPublicKeyParameters(Q, domain);
110
111             if (Compressed)
112             {
113                 _Public = Compress(_Public);
114             }
115         }
116
117         /// <summary>
118         /// Init key pair using secret sequence of bytes
119         /// </summary>
120         /// <param name="secretBytes">Byte sequence</param>
121         public CKeyPair(byte[] secretBytes) : 
122             this (secretBytes.Take(32).ToArray(), (secretBytes.Count() == 33 && secretBytes.Last() == 0x01))
123         {
124         }
125
126         public CKeyPair(string strBase58)
127         {
128             var rawSecretBytes = AddressTools.Base58DecodeCheck(strBase58);
129
130             if (rawSecretBytes.Length > 34 || rawSecretBytes.Length < 33)
131             {
132                 throw new ArgumentException("Though you have provided a correct Base58 representation of some data, this data doesn't represent a valid private key.");
133             }
134
135             // Deserialize secret value
136             var D = new BigInteger(rawSecretBytes.Skip(1).Take(32).ToArray());
137
138             if (D.SignValue == -1)
139             {
140                 var secretBytes = new byte[33];
141                 Array.Copy(rawSecretBytes, 1, secretBytes, 1, 32); // Copying the privkey, 32 bytes starting from second byte of array
142
143                 D = new BigInteger(secretBytes); // Try decoding again
144             }
145
146             // Calculate public key
147             var Q = curve.G.Multiply(D);
148
149             _Private = new ECPrivateKeyParameters(D, domain);
150             _Public = new ECPublicKeyParameters(Q, domain);
151
152             if (rawSecretBytes.Length == 34 && rawSecretBytes.Last() == 0x01) // Check compression tag
153             {
154                 _Public = Compress(_Public);
155             }
156         }
157
158         /// <summary>
159         /// Initialize a copy of CKeyPair instance
160         /// </summary>
161         /// <param name="pair">CKyPair instance</param>
162         public CKeyPair(CKeyPair pair)
163         {
164             _Public = pair._Public;
165             _Private = pair._Private;
166         }
167
168         /// <summary>
169         /// Create signature for supplied data
170         /// </summary>
171         /// <param name="data">Hash to sigh</param>
172         /// <returns>Signature bytes sequence</returns>
173         public byte[] Sign(Hash sigHash)
174         {
175             var signer = SignerUtilities.GetSigner("NONEwithECDSA");
176             signer.Init(true, _Private);
177             signer.BlockUpdate(sigHash, 0, sigHash.hashSize);
178
179             return signer.GenerateSignature();
180         }
181
182         public CPubKey PubKey
183         {
184             get { return new CPubKey(_Public.Q.GetEncoded()); }
185         }
186
187         /// <summary>
188         /// SecretBytes part of key pair
189         /// </summary>
190         public static implicit operator byte[] (CKeyPair kp)
191         {
192             var secretBytes = new List<byte>(kp._Private.D.ToByteArray());
193
194             if (secretBytes.Count == 33 && secretBytes[0] == 0x00)
195             {
196                 // Remove sign
197                 secretBytes.RemoveAt(0);
198             }
199
200             if (kp.IsCompressed)
201             {
202                 // Set compression flag
203                 secretBytes.Add(0x01);
204             }
205
206             return secretBytes.ToArray();
207         }
208
209         public string ToHex()
210         {
211             return Interop.ToHex((byte[])this);
212         }
213
214         /// <summary>
215         /// Generate Base58 string in wallet import format
216         /// </summary>
217         /// <returns></returns>
218         public override string ToString()
219         {
220             var r = new List<byte>();
221
222             r.Add((byte)(128 + AddrType.PUBKEY_ADDRESS)); // Key version
223             r.AddRange((byte[])this); // Key data
224
225             return AddressTools.Base58EncodeCheck(r.ToArray());
226         }
227     }
228 }