-
Notifications
You must be signed in to change notification settings - Fork 19
/
cosmos-keys.ts
125 lines (103 loc) · 4.17 KB
/
cosmos-keys.ts
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
import * as bip39 from 'bip39'
import * as bip32 from 'bip32'
import * as bech32 from 'bech32'
import * as secp256k1 from 'secp256k1'
import * as CryptoJS from 'crypto-js'
import { Wallet, StdSignMsg, KeyPair } from './types';
const hdPathAtom = `m/44'/118'/0'/0/0` // key controlling ATOM allocation
/* tslint:disable-next-line:strict-type-predicates */
const windowObject: Window | null = typeof window === 'undefined' ? null : window
// returns a byte buffer of the size specified
export function randomBytes(size: number, window = windowObject): Buffer {
// in browsers
if (window && window.crypto) {
return windowRandomBytes(size, window)
}
try {
// native node crypto
const crypto = require('crypto')
return crypto.randomBytes(size)
} catch (err) {
// no native node crypto available
}
throw new Error(
'There is no native support for random bytes on this system. Key generation is not safe here.'
)
}
export function getNewWalletFromSeed(mnemonic: string): Wallet {
const masterKey = deriveMasterKey(mnemonic)
const { privateKey, publicKey } = deriveKeypair(masterKey)
const cosmosAddress = getCosmosAddress(publicKey)
return {
privateKey: privateKey.toString('hex'),
publicKey: publicKey.toString('hex'),
cosmosAddress
}
}
export function getSeed(randomBytesFunc: (size: number) => Buffer = randomBytes): string {
const entropy = randomBytesFunc(32)
if (entropy.length !== 32) throw Error(`Entropy has incorrect length`)
const mnemonic = bip39.entropyToMnemonic(entropy.toString('hex'))
return mnemonic
}
export function getNewWallet(randomBytesFunc: (size: number) => Buffer = randomBytes): Wallet {
const mnemonic = getSeed(randomBytesFunc)
return getNewWalletFromSeed(mnemonic)
}
// NOTE: this only works with a compressed public key (33 bytes)
export function getCosmosAddress(publicKey: Buffer): string {
const message = CryptoJS.enc.Hex.parse(publicKey.toString('hex'))
const address = CryptoJS.RIPEMD160(CryptoJS.SHA256(message) as any).toString()
const cosmosAddress = bech32ify(address, `cosmos`)
return cosmosAddress
}
function deriveMasterKey(mnemonic: string): bip32.BIP32 {
// throws if mnemonic is invalid
bip39.validateMnemonic(mnemonic)
const seed = bip39.mnemonicToSeedSync(mnemonic)
const masterKey = bip32.fromSeed(seed)
return masterKey
}
function deriveKeypair(masterKey: bip32.BIP32): KeyPair {
const cosmosHD = masterKey.derivePath(hdPathAtom)
const privateKey = cosmosHD.privateKey
const publicKey = secp256k1.publicKeyCreate(privateKey, true)
return {
privateKey,
publicKey
}
}
// converts a string to a bech32 version of that string which shows a type and has a checksum
function bech32ify(address: string, prefix: string) {
const words = bech32.toWords(Buffer.from(address, 'hex'))
return bech32.encode(prefix, words)
}
// produces the signature for a message (returns Buffer)
export function signWithPrivateKey(signMessage: StdSignMsg | string, privateKey: Buffer): Buffer {
const signMessageString: string =
typeof signMessage === 'string' ? signMessage : JSON.stringify(signMessage)
const signHash = Buffer.from(CryptoJS.SHA256(signMessageString).toString(), `hex`)
const { signature } = secp256k1.sign(signHash, privateKey)
return signature
}
export function verifySignature(signMessage: StdSignMsg | string, signature: Buffer, publicKey: Buffer): boolean {
const signMessageString: string =
typeof signMessage === 'string' ? signMessage : JSON.stringify(signMessage)
const signHash = Buffer.from(CryptoJS.SHA256(signMessageString).toString(), `hex`)
return secp256k1.verify(signHash, signature, publicKey)
}
function windowRandomBytes(size: number, window: Window) {
const chunkSize = size / 4
let hexString = ''
let keyContainer = new Uint32Array(chunkSize)
keyContainer = window.crypto.getRandomValues(keyContainer)
for (let keySegment = 0; keySegment < keyContainer.length; keySegment++) {
let chunk = keyContainer[keySegment].toString(16) // Convert int to hex
while (chunk.length < chunkSize) {
// fill up so we get equal sized chunks
chunk = '0' + chunk
}
hexString += chunk // join
}
return Buffer.from(hexString, 'hex')
}