Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issues for security audit #9

Merged
merged 2 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 11 additions & 56 deletions contracts/Account.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,24 @@ contract AccountFactory is AccountFactoryBase {
/// Logs the address of the created account on-chain
event AccountCreated(address contractAddress);

/// @notice Function to create a clone
/// @param target address of master contract to clone
function clone(address target)
/// @notice This function creates a proxy of the target contract as per ERC-1167.
/// @param target address of target master contract to clone
function createProxy(address target)
public virtual override {
// Note that this function creates a proxy contract to make contract deployment much cheaper. However, subsequent calls
// to the proxy contract are forwarded to the original contract instance making subsequent invocations slightly more expensive.
// As a result, consider the number of calls your application will make to the KMaaS account to determine whether creating
// a proxy contract or deploying the Account contract itself is gas-optimal.
bytes20 targetBytes = bytes20(target);
address contractAddr;
assembly {
let contractClone := mload(0x40)
// This contains 1. the init code to deploy the contract 2. bytes to copy calldata into memory and push the address
// of the target contract
mstore(contractClone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
// Store the address of the target contract
mstore(add(contractClone, 0x14), targetBytes)
// This contains the code to execute delegatecall to the target address, as well as return/revert based on function execution
mstore(add(contractClone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
contractAddr := create(0, contractClone, 0x37)
}
Expand Down Expand Up @@ -95,59 +103,6 @@ contract Account is AccountBase {
return EthereumUtils.sign(publicKey, privateKey, digest);
}

/// @notice Create a proxy transaction that the `proxy` function will then execute
/// This is helpful for using this account as a Gasless Relayer and code is taken from
/// https://github.com/oasisprotocol/sapphire-paratime/blob/main/examples/onchain-signer/contracts/Gasless.sol#L23
/// @param in_contract Address of contract to call
/// @param in_data Data to pass to the contract
/// @param nonce Nonce for the transaction
/// @returns Transaction encoded in EIP-155 format with signature
function makeProxyTx(
address in_contract,
bytes memory in_data,
uint64 nonce
) external view authorized
returns (bytes memory output) {
bytes memory data = abi.encode(in_contract, in_data);

return
EIP155Signer.sign(
publicKey,
privateKey,
EIP155Signer.EthTx({
nonce: nonce,
gasPrice: 100_000_000_000,
gasLimit: 250000,
to: address(this),
value: 0,
data: abi.encodeCall(this.proxy, data),
chainId: block.chainid
})
);
}


/// @notice Function that is called by the makeProxyTx function once the signed transaction
/// is submitted
/// @param data Data passed in by makeProxyTx that is unpacked into (address, bytes)
function proxy(bytes memory data) external authorized payable {
(address addr, bytes memory subcallData) = abi.decode(
data,
(address, bytes)
);
(bool success, bytes memory outData) = addr.call{value: msg.value}(
subcallData
);
if (!success) {
// Add inner-transaction meaningful data in case of error.
assembly {
revert(add(outData, 32), mload(outData))
}
}

nonce += 1;
}


/// @notice Call another contract. Useful if other contracts depend on authorization from this one
/// This function is potentially state-changing and is payable as well
Expand Down
4 changes: 2 additions & 2 deletions contracts/AccountBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {EIP155Signer} from "@oasisprotocol/sapphire-contracts/contracts/EIP155Si

// A contract to create per-identity account.
abstract contract AccountFactoryBase {
function clone (address starterOwner)
function createProxy(address target)
public virtual;
}

Expand All @@ -31,7 +31,7 @@ abstract contract AccountBase {
mapping (address => uint256) permission;

/// @notice Function to check permissions
/// @param Address to check
/// @param grantee Address to check
function hasPermission(address grantee) public virtual view
returns (bool);

Expand Down
4 changes: 2 additions & 2 deletions contracts/AccountWithSymKey.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ contract AccountWithSymKey is Account {
/// @notice Encrypt in_data with the named symmetric key.
/// @param name Key name
/// @param in_data Bytes to encrypt
/// @returns bytes array that contains both the nonce as well as encrypted output
/// @return out_data bytes array that contains both the nonce as well as encrypted output
function encryptSymKey(string calldata name, bytes memory in_data)
public virtual view authorized
returns (bytes memory out_data) {
Expand All @@ -59,7 +59,7 @@ contract AccountWithSymKey is Account {
/// @notice Decrypt in_data with the named symmetric key
/// @param name Key name
/// @param in_data Bytes to decrypt
/// @return Plaintext bytes
/// @return out_data Plaintext bytes
function decryptSymKey(string calldata name, bytes memory in_data)
public view authorized
returns (bytes memory out_data) {
Expand Down
5 changes: 4 additions & 1 deletion contracts/Validator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.9;

/// @title Contract to forward calls to an account once credentials are validated
contract Validator {
error InvalidCredentials();
type CredentialType is uint32;

// Some examples defined. Can be extended to other types.
Expand Down Expand Up @@ -39,7 +40,9 @@ contract Validator {
}

modifier authorized(address account, bytes calldata credData) {
require(validate(account, credData), "Specified credentials invalid");
if (!validate(account, credData)) {
revert InvalidCredentials();
}
_;
}

Expand Down
1 change: 0 additions & 1 deletion hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import '@oasisprotocol/sapphire-hardhat';
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import "./tasks/deploy";

require('dotenv').config();

Expand Down
4 changes: 2 additions & 2 deletions test/Basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ describe('Basic test', () => {
this.timeout(100000);
[account1, account2] = await ethers.getSigners();
const kmaasContractFactory = await ethers.getContractFactory('Account', account1);
const kmaasMasterContract = await kmaasContractFactory.deploy()
var kmaasMasterContract = await kmaasContractFactory.deploy()
await kmaasMasterContract.waitForDeployment();
const kmaasMasterContractAddr = await kmaasMasterContract.getAddress();
const factory = await ethers.getContractFactory('AccountFactory', account1);
const contract = await factory.deploy();
await contract.waitForDeployment();
const kmaasTx = await contract.clone(kmaasMasterContractAddr);
const kmaasTx = await contract.createProxy(kmaasMasterContractAddr);
const kmaasReceipt = await kmaasTx.wait();
// Grab the KMaaS Account address from the events of the transaction
const kmaasAddress = kmaasReceipt.logs[0].args[0];
Expand Down
2 changes: 1 addition & 1 deletion test/SymKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe("Symmetric Keys test", () => {
const factoryContractFactory = await ethers.getContractFactory('AccountFactory', account);
const factoryContract = await factoryContractFactory.deploy();
await factoryContract.waitForDeployment();
const kmaasTx = await factoryContract.clone(masterContractAddr);
const kmaasTx = await factoryContract.createProxy(masterContractAddr);
const kmaasReceipt = await kmaasTx.wait();

// Grab the KMaaS Account address from the events of the transaction
Expand Down
7 changes: 5 additions & 2 deletions test/Validator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ethers } from "hardhat";

const { expect } = require("chai");
import { ErrorDecoder } from 'ethers-decode-error';


describe("Validator test", function () {
Expand Down Expand Up @@ -28,8 +29,9 @@ describe("Validator test", function () {
};

const validatorFactory = await ethers.getContractFactory('Validator', account1);
validator = await validatorFactory.deploy(await kmaasInstance.getAddress(), cred);
await validator.waitForDeployment();
const validatorTx = await validatorFactory.deploy();
const val = await validatorTx.waitForDeployment();
validator = val.connect(account1);
var validatorAddress = await validator.getAddress();
var addAccountTx = await validator.addAccount(kmaasAddress, cred);
await addAccountTx.wait();
Expand Down Expand Up @@ -79,4 +81,5 @@ describe("Validator test", function () {
var recoveredAddress = ethers.verifyMessage(text, digestSignatureObj)
expect(recoveredAddress).to.equal(publicKey);
});

})