Skip to content

Commit

Permalink
Fix issue: Potential Signature Replay Attack (Add expiry time)
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelKim20 committed May 8, 2024
1 parent e43a3df commit af6ade6
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ contract BIP20DelegatedTransfer is BIP20, IBIP20DelegatedTransfer {
address from,
address to,
uint256 amount,
uint256 expiry,
bytes calldata signature
) external override returns (bool) {
bytes32 dataHash = keccak256(abi.encode(block.chainid, address(this), from, to, amount, nonce[from]));
bytes32 dataHash = keccak256(abi.encode(block.chainid, address(this), from, to, amount, nonce[from], expiry));
require(ECDSA.recover(ECDSA.toEthSignedMessageHash(dataHash), signature) == from, "Invalid signature");
require(expiry > block.timestamp, "Expired signature");

super._transfer(from, to, amount);
nonce[from]++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface IBIP20DelegatedTransfer is IBIP20 {
address from,
address to,
uint256 amount,
uint256 expiry,
bytes calldata signature
) external returns (bool);
}
7 changes: 4 additions & 3 deletions packages/contracts/src/utils/ContractUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,12 @@ export class ContractUtils {
from: string,
to: string,
amount: BigNumberish,
nonce: BigNumberish
nonce: BigNumberish,
expiry: number
): Uint8Array {
const encodedResult = defaultAbiCoder.encode(
["uint256", "address", "address", "address", "uint256", "uint256"],
[chainId, tokenAddress, from, to, amount, nonce]
["uint256", "address", "address", "address", "uint256", "uint256", "uint256"],
[chainId, tokenAddress, from, to, amount, nonce, expiry]
);
return arrayify(keccak256(encodedResult));
}
Expand Down
33 changes: 28 additions & 5 deletions packages/contracts/test/DelegatedTransfer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,33 +158,56 @@ describe("Test for LYT token", () => {
it("Transfer from account4 to account5 - Invalid Signature", async () => {
const amount = BOACoin.make(500).value;
const nonce = await token.nonceOf(account4.address);
const expiry = ContractUtils.getTimeStamp() + 12 * 5;
const message = ContractUtils.getTransferMessage(
ethers.provider.network.chainId,
token.address,
account4.address,
account5.address,
amount,
nonce
nonce,
expiry
);
const signature = ContractUtils.signMessage(account3, message);
await expect(token.delegatedTransfer(account4.address, account5.address, amount, signature)).to.be.revertedWith(
"Invalid signature"
await expect(
token.delegatedTransfer(account4.address, account5.address, amount, expiry, signature)
).to.be.revertedWith("Invalid signature");
});

it("Transfer from account4 to account5 - Expired signature", async () => {
const amount = BOACoin.make(500).value;
const nonce = await token.nonceOf(account4.address);
const expiry = ContractUtils.getTimeStamp() - 12;
const message = ContractUtils.getTransferMessage(
ethers.provider.network.chainId,
token.address,
account4.address,
account5.address,
amount,
nonce,
expiry
);
const signature = ContractUtils.signMessage(account4, message);
await expect(
token.delegatedTransfer(account4.address, account5.address, amount, expiry, signature)
).to.be.revertedWith("Expired signature");
});

it("Transfer from account4 to account5", async () => {
const amount = BOACoin.make(500).value;
const nonce = await token.nonceOf(account4.address);
const expiry = ContractUtils.getTimeStamp() + 12 * 5;
const message = ContractUtils.getTransferMessage(
ethers.provider.network.chainId,
token.address,
account4.address,
account5.address,
amount,
nonce
nonce,
expiry
);
const signature = ContractUtils.signMessage(account4, message);
await token.delegatedTransfer(account4.address, account5.address, amount, signature);
await token.delegatedTransfer(account4.address, account5.address, amount, expiry, signature);

assert.deepStrictEqual(await token.balanceOf(account5.address), amount);
});
Expand Down

0 comments on commit af6ade6

Please sign in to comment.