3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

nemAdvent Calendar 2021

Day 3

AggregateTransacionの署名を検証する

Posted at

この記事の続きです

上の記事では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ネットワークを介さずにサーバに送信してもらうことで署名の検証を完全無料で行うことができます.秘密鍵保持の確認に応用するのも良いかもしれません.

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?