Last active
December 10, 2020 08:29
-
-
Save marekyggdrasil/82716db323b34019bf02237012e0d16f to your computer and use it in GitHub Desktop.
node.js example on how to connect to the grin-wallet owner API based on: it is a modified official example from here: https://github.com/mimblewimble/grin-wallet/tree/1ced8990b9e21fa17c788d93a151ec1164ebbce5/doc/samples/v3_api_node
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Sample Code for connecting to the V3 Secure API via Node | |
* | |
* With thanks to xiaojay of Niffler Wallet: | |
* https://github.com/grinfans/Niffler/blob/gw3/src/shared/walletv3.js | |
* | |
*/ | |
const http = require('http'); | |
const crypto = require('crypto'); | |
// Demo implementation of using `aes-256-gcm` with node.js's `crypto` lib. | |
const aes256gcm = (shared_secret) => { | |
const ALGO = 'aes-256-gcm'; | |
// encrypt returns base64-encoded ciphertext | |
const encrypt = (str, nonce) => { | |
let key = Buffer.from(shared_secret, 'hex') | |
const cipher = crypto.createCipheriv(ALGO, key, nonce) | |
const enc = Buffer.concat([cipher.update(str, 'utf8'), cipher.final()]) | |
const tag = cipher.getAuthTag() | |
return Buffer.concat([enc, tag]).toString('base64') | |
}; | |
// decrypt decodes base64-encoded ciphertext into a utf8-encoded string | |
const decrypt = (enc, nonce) => { | |
//key,nonce is all buffer type; data is base64-encoded string | |
let key = Buffer.from(shared_secret, 'hex') | |
const data_ = Buffer.from(enc, 'base64') | |
const decipher = crypto.createDecipheriv(ALGO, key, nonce) | |
const len = data_.length | |
const tag = data_.slice(len-16, len) | |
const text = data_.slice(0, len-16) | |
decipher.setAuthTag(tag) | |
const dec = decipher.update(text, 'binary', 'utf8') + decipher.final('utf8'); | |
return dec | |
}; | |
return { | |
encrypt, | |
decrypt, | |
}; | |
}; | |
function requestAsync(payload_json, auth) { | |
return new Promise((resolve, reject) => { | |
const payload = JSON.stringify(payload_json) | |
const options = { | |
'host': 'localhost', | |
'port': 3420, | |
'path': '/v3/owner', | |
'auth': auth, | |
'headers': { | |
'Content-Type': 'application/json', | |
'Content-Length': payload.length | |
}, | |
'method': 'POST' | |
} | |
const req = http.request(options, (response) => { | |
let data = '' | |
response.on('data', (chunk) => { | |
data += chunk | |
}) | |
response.on('end', () => { | |
// console.log(response.statusCode) | |
resolve(JSON.parse(data)) | |
}) | |
response.on('error', (err) => { | |
reject(err) | |
}) | |
}) | |
req.write(payload) | |
req.end() | |
}) | |
} | |
async function encryptedPayload(rpc_data, aesCipher) { | |
const nonce = new Buffer.from(crypto.randomBytes(12)); | |
const enc = aesCipher.encrypt(JSON.stringify(rpc_data), nonce); | |
return { | |
'nonce': nonce.toString('hex'), | |
'body_enc': enc, | |
} | |
} | |
async function main() { | |
let ecdh = crypto.createECDH('secp256k1') | |
ecdh.generateKeys() | |
let publicKey = ecdh.getPublicKey('hex', 'compressed') | |
console.log('\nour public key') | |
console.log(publicKey) | |
const auth = 'grin:secret_api_key' | |
payload = { | |
'jsonrpc': '2.0', | |
'method': 'init_secure_api', | |
'params': { | |
'ecdh_pubkey': publicKey | |
}, | |
'id': 1 | |
} | |
const response = await requestAsync(payload, auth) | |
// console.log(response) | |
console.log('\nserver public key') | |
console.log(response.result.Ok) | |
const shared_secret = ecdh.computeSecret(response.result.Ok, 'hex', 'hex') | |
console.log('\nshared secret') | |
console.log(shared_secret) | |
const rpc_data = { | |
'jsonrpc': '2.0', | |
'method': 'accounts', | |
'params': { | |
'token': '12345', | |
}, | |
'id': 1 | |
} | |
const aesCipher = aes256gcm(shared_secret); | |
const params = await encryptedPayload(rpc_data, aesCipher) | |
const payload_json2 = { | |
'jsonrpc': '2.0', | |
'method': 'encrypted_request_v3', | |
'params': params, | |
'id': 1 | |
} | |
// console.log(payload_json2) | |
const response2 = await requestAsync(payload_json2, auth) | |
// console.log(response2) | |
const nonce2 = Buffer.from(response2.result.Ok.nonce, 'hex'); | |
const data = Buffer.from(response2.result.Ok.body_enc, 'base64'); | |
const dec = aesCipher.decrypt(data, nonce2) | |
const dec_parsed = JSON.parse(dec) | |
console.log('\ndecrypted response from the wallet') | |
console.log(dec_parsed) | |
} | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "node-sample", | |
"version": "0.0.1", | |
"description": "Sample of connecting to the secure OwnerAPI via node", | |
"main": "src/index.js", | |
"scripts": { | |
"test": "npm test" | |
}, | |
"author": "", | |
"license": "ISC", | |
"dependencies": { | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment