Skip to content

Instantly share code, notes, and snippets.

@marekyggdrasil
Last active December 10, 2020 08:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save marekyggdrasil/82716db323b34019bf02237012e0d16f to your computer and use it in GitHub Desktop.
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
/* 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()
{
"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