-
Notifications
You must be signed in to change notification settings - Fork 1k
/
Copy pathKeyPair.cs
160 lines (147 loc) · 5.83 KB
/
KeyPair.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// Copyright (C) 2015-2025 The Neo Project.
//
// KeyPair.cs file belongs to the neo project and is free
// software distributed under the MIT software license, see the
// accompanying file LICENSE in the main directory of the
// repository or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.
using Neo.Cryptography;
using Neo.SmartContract;
using Neo.Wallets.NEP6;
using Org.BouncyCastle.Crypto.Generators;
using System;
using System.Security.Cryptography;
using System.Text;
using static Neo.Wallets.Helper;
using ECCurve = Neo.Cryptography.ECC.ECCurve;
using ECPoint = Neo.Cryptography.ECC.ECPoint;
namespace Neo.Wallets
{
/// <summary>
/// Represents a private/public key pair in wallets.
/// </summary>
public class KeyPair : IEquatable<KeyPair>
{
/// <summary>
/// The private key.
/// </summary>
public readonly byte[] PrivateKey;
/// <summary>
/// The public key.
/// </summary>
public readonly ECPoint PublicKey;
/// <summary>
/// The hash of the public key.
/// </summary>
public UInt160 PublicKeyHash => PublicKey.EncodePoint(true).ToScriptHash();
/// <summary>
/// Initializes a new instance of the <see cref="KeyPair"/> class.
/// </summary>
/// <param name="privateKey">The private key in the <see cref="KeyPair"/>.</param>
public KeyPair(byte[] privateKey)
{
if (privateKey.Length != 32 && privateKey.Length != 96 && privateKey.Length != 104)
throw new ArgumentException(null, nameof(privateKey));
PrivateKey = privateKey[^32..];
if (privateKey.Length == 32)
{
PublicKey = ECCurve.Secp256r1.G * privateKey;
}
else
{
PublicKey = ECPoint.FromBytes(privateKey, ECCurve.Secp256r1);
}
}
public bool Equals(KeyPair other)
{
if (ReferenceEquals(this, other)) return true;
if (other is null) return false;
return PublicKey.Equals(other.PublicKey);
}
public override bool Equals(object obj)
{
return Equals(obj as KeyPair);
}
/// <summary>
/// Exports the private key in WIF format.
/// </summary>
/// <returns>The private key in WIF format.</returns>
public string Export()
{
Span<byte> data = stackalloc byte[34];
data[0] = 0x80;
PrivateKey.CopyTo(data[1..]);
data[33] = 0x01;
string wif = Base58.Base58CheckEncode(data);
data.Clear();
return wif;
}
/// <summary>
/// Exports the private key in NEP-2 format.
/// </summary>
/// <param name="passphrase">The passphrase of the private key.</param>
/// <param name="version">The address version.</param>
/// <param name="N">The N field of the <see cref="ScryptParameters"/> to be used.</param>
/// <param name="r">The R field of the <see cref="ScryptParameters"/> to be used.</param>
/// <param name="p">The P field of the <see cref="ScryptParameters"/> to be used.</param>
/// <returns>The private key in NEP-2 format.</returns>
public string Export(string passphrase, byte version, int N = 16384, int r = 8, int p = 8)
{
byte[] passphrasedata = Encoding.UTF8.GetBytes(passphrase);
try
{
return Export(passphrasedata, version, N, r, p);
}
finally
{
passphrasedata.AsSpan().Clear();
}
}
/// <summary>
/// Exports the private key in NEP-2 format.
/// </summary>
/// <param name="passphrase">The passphrase of the private key.</param>
/// <param name="version">The address version.</param>
/// <param name="N">The N field of the <see cref="ScryptParameters"/> to be used.</param>
/// <param name="r">The R field of the <see cref="ScryptParameters"/> to be used.</param>
/// <param name="p">The P field of the <see cref="ScryptParameters"/> to be used.</param>
/// <returns>The private key in NEP-2 format.</returns>
public string Export(byte[] passphrase, byte version, int N = 16384, int r = 8, int p = 8)
{
UInt160 script_hash = Contract.CreateSignatureRedeemScript(PublicKey).ToScriptHash();
string address = script_hash.ToAddress(version);
byte[] addresshash = Encoding.ASCII.GetBytes(address).Sha256().Sha256()[..4];
byte[] derivedkey = SCrypt.Generate(passphrase, addresshash, N, r, p, 64);
byte[] derivedhalf1 = derivedkey[..32];
byte[] derivedhalf2 = derivedkey[32..];
byte[] encryptedkey = Encrypt(XOR(PrivateKey, derivedhalf1), derivedhalf2);
Span<byte> buffer = stackalloc byte[39];
buffer[0] = 0x01;
buffer[1] = 0x42;
buffer[2] = 0xe0;
addresshash.CopyTo(buffer[3..]);
encryptedkey.CopyTo(buffer[7..]);
return Base58.Base58CheckEncode(buffer);
}
private static byte[] Encrypt(byte[] data, byte[] key)
{
using Aes aes = Aes.Create();
aes.Key = key;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
using ICryptoTransform encryptor = aes.CreateEncryptor();
return encryptor.TransformFinalBlock(data, 0, data.Length);
}
public override int GetHashCode()
{
return PublicKey.GetHashCode();
}
public override string ToString()
{
return PublicKey.ToString();
}
}
}