HDKeys Sign Verify - javascript

Trying to sign and verify a random string using private keys. I'm Using hdkeys and bip39 packages
Code:
const HDkey = require('hdkey')
const bip39 = require('bip39')
const mnemonic = bip39.generateMnemonic()
console.log('You recovery seed phrase: \n'+ mnemonic)
let genkey = HDkey.fromMasterSeed(Buffer.from(mnemonic, 'hex'))
let privKey = genkey.privateKey;
let pubKey = genkey.publicExtendedKey;
console.log()
console.log('Private Key: ' + privKey)
console.log()
console.log('Public Key: ' + pubKey)
const hash = "Adadadas"
genkey.sign(hash)
hdkey.verify(hash, sign)
But there comes an error:
if (!cond) throw new Error(msg)
^
Error: Expected message to be an Uint8Array
I'm new to this.

As the error message describes, the message to be signed should be a Uint8Array object with 256-bits (32-bytes) length. You need to hash your message.
const message = "Adadadas"
const hash = crypto.createHash('sha256');
hash.update(message);
const hashBuffer = hash.digest();
const hashUint8Array = new Uint8Array(hashBuffer);
const signature = genkey.sign(new Uint8Array(hashUint8Array))
const result = genkey.verify(hashUint8Array, signature)
console.log(result);

I had faced a similar issue while using hdkey. Function sign expects a Uint8Array(32) [or equivalent] as input. My intent was to validate sign/verify and so I was simply looking for an input that could be passed accordingly.
Here's what I got working eventually:
console.log('Attempting sign n verify...')
const txnObj = {
from: 'abc',
to: 'xyz'
}
console.log('txnObj:', txnObj);
const txnStr = JSON.stringify(txnObj);
console.log('txnStr:', txnStr);
const txnHash = sha256(txnStr);
console.log('txnHash:', txnHash);
const txnBuffer = Buffer.from(txnHash.toString(), 'hex');
console.log('txnBuffer:', txnBuffer);
const hashedUint8Array = new Uint8Array(txnBuffer);
console.log('hashUint8Array:', hashedUint8Array);
Here's the outcome:
Outcome of executing above code
Additionally, your signing prv and pub keys are not traceable in the provided code.
const hash = "Adadadas"
genkey.sign(hash)
hdkey.verify(hash, sign)
Suggest changing them as below ie. sign using your privKey and verify using your pubKey:
const hash = "Adadadas"
const signed = privKey.sign(hash)
pubKey.verify(hash, signed)

Related

how to sign message in solana-web3.js?

i want to sign message in solana-web3.js but i can't find signMessage function like the one in phantom extension
using phantom extension will give result like this:
{"signature":"5uLfqwr19GdFBUyMTPeeL3HJ9U5f9TjRzAnCkzAaCyxZgNeA3C1i6c37FjPTowZRg8WSYdwANdVVXMMkWx2Hzwoy","address":"2o6AxcaMZqcS3L8oyPhS66keuxA1FrRtrSRcRxRw4U9C"}
i found the code but this code doesn't give the same result as signMessage function of phantom wallet extension
const signMessage = (message, mnemonic) => {
// mnemonic to keypair
const seed = bip39.mnemonicToSeedSync(mnemonic);
const path = 'm/44\'/501\'/0\'/0\'';
const derivedSeed = derivePath(path, seed.toString('hex')).key;
const keyPair = Keypair.fromSeed(derivedSeed);
if (!keyPair) throw new Error('Invalid secret key')
// sign message
const address = keyPair.publicKey.toBase58()
const bufSecretKey = keyPair.secretKey
const serializedData = Buffer.from(message)
const bufSig = tweetnacl.sign(serializedData, bufSecretKey)
const signature = Buffer.from(bufSig).toString('hex')
return { signature, address}
};
the result of the above code is like this:
{"signature":"f53409d847b5eae5ef3bb892b998805d2ea04b699e39bb5c126e757d1f4023488945ba210609fb661f70c10a45fdd55db09741d5e6a8839446a91af39d4fe5044c6f67696e204368616c6c656e67653a2066613666363234342d663863352d343162642d383033372d356333396562653534396366","address":"2o6AxcaMZqcS3L8oyPhS66keuxA1FrRtrSRcRxRw4U9C"}
how do i sign like in phantom wallet extension with solana-web3.js???

Sign a message with EdDSA algorithm in Javascript to get JWT

I need to get JWT with EdDSA algorithm to be able to use an API. I have the private key to sign the message and I could do that with PHP with the next library: https://github.com/firebase/php-jwt (you can see the example with EdDSA at README). Now I need to do the same in JS but I didn't find the way to get JWT with a given secret key (encoded base 64) like that (only an example is not the real secretKey):
const secretKey = Dm2xriMD6riJagld4WCA6zWqtuWh40UzT/ZKO0pZgtHATOt0pGw90jG8BQHCE3EOjiCkFR2/gaW6JWi+3nZp8A==
I tried a lot of libraries like jose, js-nacl, crypto, libsodium, etc. And I am really close to get the JWT with libsodium library, now I attach the code:
const base64url = require("base64url");
const _sodium = require("libsodium-wrappers");
const moment = require("moment");
const getJWT = async () => {
await _sodium.ready;
const sodium = _sodium;
const privateKey =
"Dm2xriMD6riJagld4WCA6zWqtuWh40UzT/ZKO0pZgtHATOt0pGw90jG8BQHCE3EOjiCkFR2/gaW6JWi+3nZp8A==";
const payload = {
iss: "test",
aud: "test.com",
iat: 1650101178,
exp: 1650101278,
sub: "12345678-1234-1234-1234-123456789123"
};
const { msg, keyAscii} = encode(payload, privateKey, "EdDSA");
const signature = sodium.crypto_sign_detached(msg, keyDecoded); //returns Uint8Array(64)
//Here is the problem.
};
const encode = (payload, key, alg) => {
const header = {
typ: "JWT",
alg //'EdDSA'
};
const headerBase64URL = base64url(JSON.stringify(header));
const payloadBase64URL = base64url(JSON.stringify(payload));
const headerAndPayloadBase64URL = `${headerBase64URL}.${payloadBase64URL}`;
const keyAscii= Buffer.from(key, "base64").toString("ascii");
return {headerAndPayloadBase64URL , keyAscii}
};
The problem is in the sodium.crypto_sign_detached function because it returns an Uint8Array(64) signature and and I need the JWT like that:
eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSJ9.eyJpc3MiOiJ0ZXN0IiwiYXVkIjoidGVzdC5jb20iLCJpYXQiOjE2NTAxMDExNzgsImV4cCI6MTY1MDEwMTI3OCwic3ViIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MTIzIn0.f7WG_02UKljrMeVVOTNNBAGxtLXJUT_8QAnujNhomV18Pn5cU-0lHRgVlmRttOlqI7Iol_fHut3C4AOXxDGnAQ
How can I change the Uint8Array(64) to get the signature in a right format to get the JWT? I tried with base64, base64url, hex, text, ascii, etc and the final JWT is not valid (because the signature is wrong).
If you compare my code with the code that I mentioned with PHP is very similar but the function sodium.crypto_sign_detached returns Uint8Array(64) at JS library and the same function in PHP returns an string and I can get the token.
Or maybe there a way to adapt my given private key for use in other library (like crypto or jose where I received an error for the private key format)
Thank you!
In the posted NodeJS code there are the following issues:
crypto_sign_detached() returns the signature as a Uint8Array, which can be imported with Buffer.from() and converted to a Base64 string with base64url().
Concatenating headerAndPayloadBase64URL and the Base64url encoded signature with a . as separator gives the JWT you are looking for.
The raw private key must not be decoded with 'ascii', as this generally corrupts the data. Instead, it should simply be handled as buffer. Note: If for some reason a conversion to a string is required, use 'binary' as encoding, which produces a byte string (however, this is not an option with crypto_sign_detached() as this function expects a buffer).
With these changes, the following NodeJS code results:
const _sodium = require('libsodium-wrappers');
const base64url = require("base64url");
const getJWT = async () => {
await _sodium.ready;
const sodium = _sodium;
const privateKey = "Dm2xriMD6riJagld4WCA6zWqtuWh40UzT/ZKO0pZgtHATOt0pGw90jG8BQHCE3EOjiCkFR2/gaW6JWi+3nZp8A==";
const payload = {
iss: "test",
aud: "test.com",
iat: 1650101178,
exp: 1650101278,
sub: "12345678-1234-1234-1234-123456789123"
};
const {headerAndPayloadBase64URL, keyBuf} = encode(payload, privateKey, "EdDSA");
const signature = sodium.crypto_sign_detached(headerAndPayloadBase64URL, keyBuf);
const signatureBase64url = base64url(Buffer.from(signature));
console.log(`${headerAndPayloadBase64URL}.${signatureBase64url}`) // eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSJ9.eyJpc3MiOiJ0ZXN0IiwiYXVkIjoidGVzdC5jb20iLCJpYXQiOjE2NTAxMDExNzgsImV4cCI6MTY1MDEwMTI3OCwic3ViIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MTIzIn0.f7WG_02UKljrMeVVOTNNBAGxtLXJUT_8QAnujNhomV18Pn5cU-0lHRgVlmRttOlqI7Iol_fHut3C4AOXxDGnAQ
};
const encode = (payload, key, alg) => {
const header = {
typ: "JWT",
alg //'EdDSA'
};
const headerBase64URL = base64url(JSON.stringify(header));
const payloadBase64URL = base64url(JSON.stringify(payload));
const headerAndPayloadBase64URL = `${headerBase64URL}.${payloadBase64URL}`;
const keyBuf = Buffer.from(key, "base64");
return {headerAndPayloadBase64URL, keyBuf};
};
getJWT();
Test:
Since Ed25519 is deterministic, the NodeJS code can be checked by comparing both JWTs: If, as in the above NodeJS code, the same header and payload are used as in the PHP code, the same signature and thus the same JWT is generated as by the PHP code, namely:
eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSJ9.eyJpc3MiOiJ0ZXN0IiwiYXVkIjoidGVzdC5jb20iLCJpYXQiOjE2NTAxMDExNzgsImV4cCI6MTY1MDEwMTI3OCwic3ViIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MTIzIn0.f7WG_02UKljrMeVVOTNNBAGxtLXJUT_8QAnujNhomV18Pn5cU-0lHRgVlmRttOlqI7Iol_fHut3C4AOXxDGnAQ
which shows that the NodeJS code works.
Note that instead of the moment package, Date.now() could be used. This will return the time in milliseconds, so the value has to be divided by 1000, e.g. Math.round(Date.now()/1000), but saves a dependency.

I am trying to execute a swap on Pancakeswap using web3, but I get an error when calculating transaction cost

I am new at ethereum development and I am working on a simple script to execute swaps using Pancakeswap. Everything seems to be working well up to the point where I am building my transaction and calculate the tx-cost. I am using the UniswapRouterV02 abi create my Pancakeswap contract. The code:
const init = async () => {
const [WBNB, BUSD] = await Promise.all(
[addresses.WBNB, addresses.BUSD].map(tokenAddress => (
new Token(
ChainId.MAINNET,
tokenAddress,
18
)
)));
const pair = await Fetcher.fetchPairData(WBNB, BUSD, provider)
const route = await new Route([pair], WBNB)
const trade = await new Trade(route, new TokenAmount(WBNB, tradeAmount), TradeType.EXACT_INPUT)
const executionPrice = trade.executionPrice.toSignificant(12)
// Correct prices; everything seems correct up until here
const slippageTolerance = new Percent('50', '10000')
const amountOutMin = trade.minimumAmountOut(slippageTolerance).raw
const path = [WBNB.address, BUSD.address]
const to = MY_ADDRESS
const deadline = Math.floor(Date.now() / 1000) + 60 * 20
const value = trade.inputAmount.raw
// Correct prices everything seems correct up until here
const pancakeSwap = new web3.eth.Contract(
abis.uniswapRouter.abi,
addresses.PANCAKE_ROUTER //'0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F'
);
let tx = pancakeSwap.methods.swapExactTokensForTokens(
tradeAmount,
web3.utils.toBN(amountOutMin.toString()),
path,
to,
deadline
)
const [gasPrice, gasCost] = await Promise.all([
web3.eth.getGasPrice(),
tx.estimateGas({from: admin}),
]);
console.log(`gasPrice: ${gasPrice}`)
console.log(`gasCost: ${gasCost}`)
}
init()
The price calculation for the swap returns correct prices. However when I try to calculate the transaction costs the following error is thrown:
Error: Returned error: gas required exceeds allowance (44038122) or always failing transaction
Any help would be greatly appreciated, please let me know if more of my code should be clarified!
In turns out that with web3 it is not possible to interact with the Pancakeswap contract. I found a solution using ethers.js. Following code worked for me to execute a swap on Pancake on mainnet.
Before executing the first transaction Pancakeswap needs to be allowed, this code is commented out.
I had to play around with the gasprice and gasLimit a bit to make it work.
require("dotenv").config()
const ethers = require('ethers')
const {ChainId, Token, TokenAmount, Fetcher, Pair, Route, Trade, TradeType, Percent} =
require('#pancakeswap-libs/sdk');
const Web3 = require('web3');
const web3 = new Web3('wss://apis.ankr.com/wss/c40792ffe3514537be9fb4109b32d257/946dd909d324e5a6caa2b72ba75c5799/binance/full/main');
const {JsonRpcProvider} = require("#ethersproject/providers");
const provider = new JsonRpcProvider('https://bsc-dataseed1.binance.org/');
const { address: admin } = web3.eth.accounts.wallet.add(process.env.PRIVATE_KEY)
const addresses = {
WBNB: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c',
BUSD: '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56',
PANCAKE_ROUTER: '0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F'
}
const ONE_ETH_IN_WEI = web3.utils.toBN(web3.utils.toWei('1'))
const tradeAmount = ONE_ETH_IN_WEI.div(web3.utils.toBN('1000'))
const init = async () => {
const [WBNB, BUSD] = await Promise.all(
[addresses.WBNB, addresses.BUSD].map(tokenAddress => (
new Token(
ChainId.MAINNET,
tokenAddress,
18
)
)));
const pair = await Fetcher.fetchPairData(WBNB, BUSD, provider)
const route = await new Route([pair], WBNB)
const trade = await new Trade(route, new TokenAmount(WBNB, tradeAmount), TradeType.EXACT_INPUT)
const slippageTolerance = new Percent('50', '10000')
// create transaction parameters
const amountOutMin = trade.minimumAmountOut(slippageTolerance).raw
const path = [WBNB.address, BUSD.address]
const to = admin
const deadline = Math.floor(Date.now() / 1000) + 60 * 20
// Create signer
const wallet = new ethers.Wallet(
Buffer.from(
process.env.PRIVATE_KEY, // paste your private key from metamask here
"hex"
)
)
const signer = wallet.connect(provider)
// Create Pancakeswap ethers Contract
const pancakeswap = new ethers.Contract(
addresses.PANCAKE_ROUTER,
['function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)'],
signer
)
// Allow Pancakeswap
// let abi = ["function approve(address _spender, uint256 _value) public returns (bool success)"]
// let contract = new ethers.Contract(WBNB.address, abi, signer)
// await contract.approve(addresses.PANCAKE_ROUTER, ethers.utils.parseUnits('1000.0', 18), {gasLimit: 100000, gasPrice: 5e9})
// Execute transaction
const tx = await pancakeswap.swapExactTokensForTokens(
ethers.utils.parseUnits('0.001', 18),
ethers.utils.parseUnits(web3.utils.fromWei(amountOutMin.toString()), 18),
path,
to,
deadline,
{ gasLimit: ethers.utils.hexlify(200000), gasPrice: ethers.utils.parseUnits("10", "gwei") }
)
console.log(`Tx-hash: ${tx.hash}`)
const receipt = await tx.wait();
console.log(`Tx was mined in block: ${receipt.blockNumber}`)
}
init()
You can check this working example that buys a token from pancakeswap.finance:
https://github.com/religion-counter/onlyone/blob/main/helper-scripts/buy-onlyone-pancakeswap.js
// Helper script that buys ONLYONE token from a specified address specified on text file SPECIFY_ACCOUNTS_YOU_WANT_TO_BUY_FOR_HERE.json
// The amount is specified with 'originalAmountToBuyWith' variable in the source
// The JSON file should have an array with objects with 'address' field and 'privateKey' field.
// Buys ONLYONE for ${bnbAmount} BNB from pancakeswap for address ${targetAccounts[targetIndex].address}
// targetIndex is passed as an argument: process.argv.splice(2)[0]
var fs = require('fs')
var Tx = require('ethereumjs-tx').Transaction;
var Web3 = require('web3')
var Common = require('ethereumjs-common').default;
var web3 = new Web3(new Web3.providers.HttpProvider('https://bsc-dataseed.binance.org/'))
var BSC_FORK = Common.forCustomChain(
'mainnet',
{
name: 'Binance Smart Chain Mainnet',
networkId: 56,
chainId: 56,
url: 'https://bsc-dataseed.binance.org/'
},
'istanbul',
);
// SPECIFY_THE_AMOUNT_OF_BNB_YOU_WANT_TO_BUY_FOR_HERE
var originalAmountToBuyWith = '0.007' + Math.random().toString().slice(2,7);
var bnbAmount = web3.utils.toWei(originalAmountToBuyWith, 'ether');
var targetAccounts = JSON.parse(fs.readFileSync('SPECIFY_ACCOUNTS_YOU_WANT_TO_BUY_FOR_HERE.json', 'utf-8'));
var targetIndex = Number(process.argv.splice(2)[0]);
var targetAccount = targetAccounts[targetIndex];
console.log(`Buying ONLYONE for ${originalAmountToBuyWith} BNB from pancakeswap for address ${targetAccount.address}`);
var res = buyOnlyone(targetAccounts[targetIndex], bnbAmount);
console.log(res);
async function buyOnlyone(targetAccount, amount) {
var amountToBuyWith = web3.utils.toHex(amount);
var privateKey = Buffer.from(targetAccount.privateKey.slice(2), 'hex') ;
var abiArray = JSON.parse(JSON.parse(fs.readFileSync('onlyone-abi.json','utf-8')));
var tokenAddress = '0xb899db682e6d6164d885ff67c1e676141deaaa40'; // ONLYONE contract address
var WBNBAddress = '0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c'; // WBNB token address
// var onlyOneWbnbCakePairAddress = '0xd22fa770dad9520924217b51bf7433c4a26067c2';
// var pairAbi = JSON.parse(fs.readFileSync('cake-pair-onlyone-bnb-abi.json', 'utf-8'));
// var pairContract = new web3.eth.Contract(pairAbi, onlyOneWbnbCakePairAddress/*, {from: targetAccount.address}*/);
var amountOutMin = '100' + Math.random().toString().slice(2,6);
var pancakeSwapRouterAddress = '0x10ed43c718714eb63d5aa57b78b54704e256024e';
var routerAbi = JSON.parse(fs.readFileSync('pancake-router-abi.json', 'utf-8'));
var contract = new web3.eth.Contract(routerAbi, pancakeSwapRouterAddress, {from: targetAccount.address});
var data = contract.methods.swapExactETHForTokens(
web3.utils.toHex(amountOutMin),
[WBNBAddress,
tokenAddress],
targetAccount.address,
web3.utils.toHex(Math.round(Date.now()/1000)+60*20),
);
var count = await web3.eth.getTransactionCount(targetAccount.address);
var rawTransaction = {
"from":targetAccount.address,
"gasPrice":web3.utils.toHex(5000000000),
"gasLimit":web3.utils.toHex(290000),
"to":pancakeSwapRouterAddress,
"value":web3.utils.toHex(amountToBuyWith),
"data":data.encodeABI(),
"nonce":web3.utils.toHex(count)
};
var transaction = new Tx(rawTransaction, { 'common': BSC_FORK });
transaction.sign(privateKey);
var result = await web3.eth.sendSignedTransaction('0x' + transaction.serialize().toString('hex'));
console.log(result)
return result;
}
You can also contribute to the repository if you are interested.
I am a total beginner but have the same goal as you. I'm trying to get your code to work.
I used the following Code to create the signer. I used my 'mnemonic' instead of the private key:
const wallet = ethers.Wallet.fromMnemonic(mnemonic);
I noticed that pancake uses the swapExactETHForTokens() function instead of swapExactTokensForTokens() for the transactions. Does that make any difference?
I am not quite sure whether the Output and Input Amount is calculated correctly... I don't quite understand it yet, but will try to go through it as soon as possible.
jklepatch has written a code which contains trading via pancakeswap V2. However, the trade does not work if i execute the code because the AmountOut generats an error (codeline 78)...
In the meantime, have you been able to modify your code or make it work? If it is helpful I can also post my version of your code here.

eth.sign vs eth.accounts.sign create different sign

When I sign a string with the web3.eth.sign() method and web3.eth.accounts.sign() method. The result value of the two signatures is different. I don't know why these two results are different.
I'm using the latest web3.js. And the private key is from metamask.
This is my code
await ethereum.enable();
web3 = new Web3(web3.currentProvider);
let accounts = await web3.eth.getAccounts();
let msg = "sign this message"
let prefix = "\x19Ethereum Signed Message:\n" + msg.length
let msgHash1 = web3.utils.sha3(prefix+msg)
let sig1 = await web3.eth.sign(msgHash1, accounts[0]);
let privateKey = "0xcfb51f3737044cb4bfb49cbb10ae67d79ee81523d7065e95972cc23ed914e95e"
let sigObj = await web3.eth.accounts.sign(msgHash1, privateKey)
let sig2 = sigObj.signature;
console.log(sig1)
console.log(sig2)
And this is result.
The source code shows that the difference is that web3.eth.accounts.sign prefixes and hashes the message before signing it.
In your example pass in msg instead of msgHash to eth.accounts.sign:
So this signature:
const msg = 'hello world'
const { signature } = await web3.eth.accounts.sign(msg, privateKey)
will produce the same signature as eth.sign with hashed message:
const msg = 'hello world'
const msgHash = web3.eth.accounts.hashMessage(msg)
const signature = await web3.eth.sign(accounts[0], msgHash)
The short answer is this is by design (for the best of my understanding), to prevent interpretation of a signed Ethereum transaction as other data (for example a meta transaction, verified by a smart contract logic).
More relevant details:
https://ethereum.stackexchange.com/questions/35425/web3-js-eth-sign-vs-eth-accounts-sign-producing-different-signatures
web3.eth.sign and web3.eth.accounts.sign generate the same signature in web3.js v1.3.4 .
Correct me if I am wrong, It seems web3.eth.sign calls web3.eth.accounts.sign under the hood, here

InvalidKey at Kraken API with JS, not with Python

My code to get my Balance from the Kraken API does work in Python (based on the krakenex library), but not in the JS version (based loosely off the kraken-api library, but with the crypto library substituted for crypto-js). The error is always: Invalid Key.
Even when I copy the headers and the nonce sent by the Python client into Postman, I get Invalid Key.
I believe the signature and nonce to be valid, because when they are not, Kraken retorts that either the signature or nonce are invalid.
Is there anything else that Javascript's fetch does differently than Python3 requests? Because the body and headers are otherwise identical.
JS code that generates auth data:
const getMessageSignature = (path, request, secret, nonce) => {
// API-Sign = Message signature using HMAC-SHA512 of (URI path + SHA256(nonce + POST data)) and base64 decoded secret API key
const message = qs.stringify(request);
console.log(message);
const secret_buffer = btoa(secret);
const hash = CryptoJS.algo.SHA256.create();
const hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA512, secret_buffer);
const hash_digest = hash.update(nonce + message).finalize().toString(CryptoJS.enc.Hex);
const hmac_digest = hmac.update(path + hash_digest).finalize().toString(CryptoJS.enc.Base64);
// CANNOT USE ORIGINAL LIB CODE (Buffer, got and crypto not supported)
// const secret_buffer = new Buffer(secret, 'base64');
// const hash = new crypto.createHash('sha256');
// const hmac = new crypto.createHmac('sha512', secret_buffer);
// const hash_digest = hash.update(nonce + message).digest('binary');
// const hmac_digest = hmac.update(path + hash_digest, 'binary').digest('base64');
return hmac_digest;
};
Update:
In fact, the following observations are weird:
correct key + correct signature = "incorrect key"
incorrect key + correct signature = "incorrect key"
incorrect key + incorrect signature = "incorrect key"
correct key + incorrect signature = "invalid signature"
what gives?
Update2
Seems the requests are identical (other than the signature and nonce of course, which will and should change with every request).
Turns out it was the signature after all and Kraken simply doesn't give very accurate responses (which makes some sense, but is a pain if you're trying to figure something out). Finally I was able to rewrite the code using CryptoJS only:
const getMessageSignature = (path, request, secret, nonce) => {
// API-Sign = Message signature using HMAC-SHA512 of (URI path + SHA256(nonce + POST data)) and base64 decoded secret API key
const message = JSON.stringify(request);
const hash = CryptoJS.SHA256(nonce + message);
const secret_buffer = CryptoJS.enc.Base64.parse(secret);
const hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA512, secret_buffer);
hmac.update(path, secret_buffer);
hmac.update(hash, secret_buffer);
return hmac.finalize().toString(CryptoJS.enc.Base64);
};
This yields a correct signature and Kraken no longer complains. Zzah.

Categories