この記事の続きです
上の記事ではTransferTransactionの署名を検証しました.ためしに他のトランザクション(シークレットロック)をいれてみたところこちらも成功したので基本的なTXの検証は上の記事でできると思います(全部は検証してないので保証はできません,署名まわりのコードを見る限りは大丈夫なはず...)
ただ,AggregateTransactionの場合のみ署名が異なっていたためこちらの検証を行なってみました
AggregateTransactionの署名について
前回のTransferTransactionでは署名を行うデータとしてgenerationHashとTXの内容を合わせたものを使用しました.
AggregateTransacionではTXの内容のうち,先頭の52バイトのみを署名に使用します(おそらく,AggregateTransacionが最大100件ものTXを内包できるためこのような仕様になっているのだと思います)
そこで前回の関数に手を加えてあげます
function validatePayload(payload){
try{
//payloadをuint8Arrayへ
const uint8ArrayPayload = bufferToUint8Array(Buffer.from(payload, "hex"));
//署名を切り出す
const signature = uint8ArrayPayload.slice(8, 8 + 64);
//署名者公開鍵を切り出す
const signerPublicKey = uint8ArrayPayload.slice(8 + 64, 8 + 64 + 32);
const tx = xym.TransactionMapping.createFromPayload(payload);
const transactionWithoutHeader =
(()=>
{
if(tx.type=== xym.TransactionType.AGGREGATE_BONDED || tx.type === xym.TransactionType.AGGREGATE_COMPLETE) {
return uint8ArrayPayload.slice(8 + 64 + 32 + 4, 8 + 64 + 32 + 4 + 52 );
} else {
return uint8ArrayPayload.slice(8 + 64 + 32 + 4, );
}
})();
console.log(transactionWithoutHeader);
//generationHashをuint8arrayに
const uint8arrayGenerationHash = bufferToUint8Array(Buffer.from(networkGenerationHash, "hex"));
//generationHashとTXをくっつける
const validateData = new Uint8Array([...uint8arrayGenerationHash, ...transactionWithoutHeader]);
if (nacl.sign.detached.verify(validateData, signature, signerPublicKey)) return true;
else return false;
}catch{
return false;
}
}
これでAggregateTransactionの検証もできるようになりました
全文です
const xym = require('symbol-sdk');
const nacl = require("tweetnacl");
var networkGenerationHash = '';
var epochAdjustment = '';
var repo;
var node;
var networkType = xym.NetworkType.TEST_NET;
const privateKey = ''.padStart(64,"0");
//送信元アカウント
const listenAccount = xym.Account.createFromPrivateKey(
privateKey,
networkType,
);
(async () => {
node = 'https://sym-test-09.opening-line.jp:3001';
repo = new xym.RepositoryFactoryHttp(node);
await getInfo()
simpleTx()
})();
async function getInfo() {
epochAdjustment = await repo.getEpochAdjustment().toPromise();
networkGenerationHash = await repo.getGenerationHash().toPromise();
}
function simpleTx(){
const tx = xym.TransferTransaction.create(
xym.Deadline.create(epochAdjustment),
listenAccount.address,
[],
xym.PlainMessage.create(""),
networkType
).toAggregate(listenAccount.publicAccount);
const agg = xym.AggregateTransaction.createComplete(
xym.Deadline.create(epochAdjustment),
[tx],
networkType,
[],
).setMaxFeeForAggregate(0);
const st = listenAccount.sign(agg, networkGenerationHash);
console.log(agg);
console.log("signedTx:", validatePayload(st.payload));
}
function bufferToUint8Array(buf) {
const view = new Uint8Array(buf.length);
for (let i = 0; i < buf.length; ++i) {
view[i] = buf[i];
}
return view;
}
function validatePayload(payload){
try{
//payloadをuint8Arrayへ
const uint8ArrayPayload = bufferToUint8Array(Buffer.from(payload, "hex"));
//署名を切り出す
const signature = uint8ArrayPayload.slice(8, 8 + 64);
//署名者公開鍵を切り出す
const signerPublicKey = uint8ArrayPayload.slice(8 + 64, 8 + 64 + 32);
const tx = xym.TransactionMapping.createFromPayload(payload);
const transactionWithoutHeader =
(()=>
{
if(tx.type=== xym.TransactionType.AGGREGATE_BONDED || tx.type === xym.TransactionType.AGGREGATE_COMPLETE) {
return uint8ArrayPayload.slice(8 + 64 + 32 + 4, 8 + 64 + 32 + 4 + 52 );
} else {
return uint8ArrayPayload.slice(8 + 64 + 32 + 4, );
}
})();
console.log(transactionWithoutHeader);
//generationHashをuint8arrayに
const uint8arrayGenerationHash = bufferToUint8Array(Buffer.from(networkGenerationHash, "hex"));
//generationHashとTXをくっつける
const validateData = new Uint8Array([...uint8arrayGenerationHash, ...transactionWithoutHeader]);
if (nacl.sign.detached.verify(validateData, signature, signerPublicKey)) return true;
else return false;
}catch{
return false;
}
}
前回の記事ではオフラインの取引でしかこの署名検証はあまり意味をなさないと書いたのですが,署名者の検証には使えることに気づきました.
例えばSymbol上でログインを必要とするアプリを作成する場合,ユーザーが本人か(秘密鍵保持者)であるかを検証する必要があります.この際,
const tx = xym.TransferTransaction.create(
xym.Deadline.create(1),
listenAccount.address,
[],
xym.PlainMessage.create("Login"),
networkType
).setMaxFee(0);
のような支払い手数料0,モザイク送金0の空TXを作成してユーザーに署名してもらい,これをSymbolネットワークを介さずにサーバに送信してもらうことで署名の検証を完全無料で行うことができます.秘密鍵保持の確認に応用するのも良いかもしれません.