-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.html
187 lines (155 loc) · 5.83 KB
/
index.html
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
<html>
<meta charset="utf-8">
<title>Eos Private Key Compression Tweak</title>
<head>
<script src="./eos.js"></script>
<!-- ./index.js is crafted from ./src/index.js -->
<script src="./index.js"></script>
<script>
console.log(imports);
let {privateCompressionTweak, BigInteger, ecdsa, hash, ecsignature, curve} = imports
</script>
<script>
/**
This tweak signs with a 256 + 8 = 264 bit key (called the unintended key). It
mimics https://github.com/EOSIO/eosjs-ecc/issues/16 .. The goal is to
recover an account owned by the corresponding public key.
Due to a bug in eosjs-ecc v4.0.0 and prior, Bitcoin compressed WIF private keys
are accepted but interpreted incorrectly. Specifically the compression
flag (8 bits) was included as part of the Private Key changing the resulting
private key number. That private key number was used to generate a
matching public key.
Newly generated key-pairs are unaffected. This only affects users that
generated a compressed Bitcoin private key from other sources then
used that private key in a version of eosjs-ecc prior to v4.0.1
(before May 24, 2018).
Example Private key: L5TCkLizyYqjvKSy6jg1XM3Lc4uTDwwvHS2BYatyXSyoS8T5kC2z
Unintended Public key: EOS6ig7VSsHLtt8BDkVV39HYAQsdNUwwhY1SPJBUBEA4NK8Zpkz2y
Intended Public: EOS59DAgjLtnPY34ezGspqdezuNaES2HE99dcsay3uRjk557F2Hd9
Since the key-pair seems to be valid but different than expected, this
tweak is provided to allow affected keys to be re-parsed and provided
into either eosjs-ecc or eosjs for the purpose of account recovery.
@see https://github.com/EOSIO/eosjs-ecc/issues/16
<code>
Fixed in eosjs-ecc v4.0.1 May 24, 2018 9d4e859
Fixed in eosjs v12.0.2 May 24, 2018
Bug appeared in: eosjs-ecc v4.0.0
</code>
*/
network = {
// mainnet: {
// chainId: 'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906',
// libertyblock: 'https://mainnet.libertyblock.io:7777'
// },
testnet: {
chainId: '038f4b0fc8ff18a4f0842a8f0564611f6e96e8535901dd45e43ac8691a1c4dca',
junglenet: 'http://jungle.cryptolions.io:18888'
}
}
</script>
<script>
wif = 'L5TCkLizyYqjvKSy6jg1XM3Lc4uTDwwvHS2BYatyXSyoS8T5kC2z'
ecc = Eos.modules.ecc
Signature = ecc.Signature
unintendedPrivate = ecc.PrivateKey(privateCompressionTweak(wif))
unintendedPublic = unintendedPrivate.toPublic().toString()
intendedPrivate = ecc.PrivateKey(wif)
intendedPublic = intendedPrivate.toPublic().toString()
console.info('Unintended Public Key', unintendedPublic)
console.info('Intended Public Key', intendedPublic)
;(function () {
sig=ecc.sign('', unintendedPrivate)
sigPublic = ecc.recover(sig, '')
const match = sigPublic === unintendedPublic.toString()
console.info(`Signature recovered unintended public:`, match)
})()
chainId = network.testnet.chainId
httpEndpoint = network.testnet.junglenet
// global nonce allows for signatures
let nonce = (location.hash.match(/nonce=([0-9]+)/) || [,0])[1] // start with 0
console.info('Starting signature nonce:', nonce)
signProvider = ({ buf }) => {
var privateKey = unintendedPrivate
var dataSha256 = hash.sha256(buf)
var der, e, ecsignature, i, lenR, lenS;
i = null;
// nonce = 1;
e = BigInteger.fromBuffer(dataSha256);
while (true) {
ecsignature = ecdsa.sign(curve, dataSha256, privateKey.d, nonce);
der = ecsignature.toDER();
lenR = der[3];
lenS = der[5 + lenR];
if (lenR === 32 && lenS === 32) {
i = ecdsa.calcPubKeyRecoveryParam(curve, e, ecsignature, privateKey.toPublic().Q);
i += 4; // compressed
i += 27; // compact // 24 or 27 :( forcing odd-y 2nd key candidate)
break;
}
if (nonce % 10 === 0) {
console.log("WARN: " + nonce + " attempts to find canonical signature");
}
nonce++;
}
console.info('Generated signature using nonce:', nonce)
return Signature(ecsignature.r, ecsignature.s, i).toString()
}
config = {httpEndpoint, chainId, verbose: true, forceActionDataHex: false}
eos = Eos(Object.assign(config, {signProvider}))
// eos = Eos(Object.assign(config, {keyProvider: unintendedPrivate}))
</script>
<script>
/** Replace unintended with intended */
async function getNewPermissions(accountName) {
const account = await eos.getAccount(accountName)
const perms = JSON.parse(JSON.stringify(account.permissions))// clone
for(const perm of perms) {
for(const key of perm.required_auth.keys) {
if(key.key === unintendedPublic) {
key.key = intendedPublic
}
}
}
return perms
}
async function updateAuths() {
let affectedAccounts = (await eos.getKeyAccounts(unintendedPublic)).account_names
console.info('Affected account(s):', JSON.stringify(affectedAccounts))
if(affectedAccounts.length === 0) {
return
}
if(affectedAccounts.length > 1) {
// As a last step, this index.html file was tested by creating a faucet
// account called intendedacct with the correct public keys, then this account
// was updated using this program to have the unintendedpk keys. This
// served to test the updateauth and signing in this script.
if(JSON.stringify(affectedAccounts.sort()) === '["intendedacct","unintendedpk"]') {
affectedAccounts = ['unintendedpk']
} else {
throw new Error('Fix only one account at a time:', affectedAccounts)
}
}
const accountName = affectedAccounts[0]
const perms = await getNewPermissions(accountName)
console.log('New permissions =>', JSON.stringify(perms))
const updateAuthResult = await eos.transaction(tr => {
for(const perm of perms) {
tr.updateauth({
account: accountName,
permission: perm.perm_name,
parent: perm.parent,
auth: perm.required_auth
}, {authorization: `${accountName}@owner`})
}
})
console.log('Success =>', JSON.stringify(updateAuthResult));
}
(async function() {
await updateAuths()
})()
</script>
</head>
<body>
Test running.. Open the Web Inspector (Console) to view the output..
</body>
</html>