Skip to content

Commit

Permalink
Merge pull request #1 from oasislabs/rishi/initial_work
Browse files Browse the repository at this point in the history
Initial work on KMaaS
  • Loading branch information
rishibalakrishnan authored Sep 19, 2024
2 parents 5011c0e + c81a14e commit 6b866fe
Show file tree
Hide file tree
Showing 10 changed files with 703 additions and 0 deletions.
167 changes: 167 additions & 0 deletions contracts/Account.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {SignatureRSV, EthereumUtils} from "@oasisprotocol/sapphire-contracts/contracts/EthereumUtils.sol";
import {Sapphire} from "@oasisprotocol/sapphire-contracts/contracts/Sapphire.sol";
import {EIP155Signer} from "@oasisprotocol/sapphire-contracts/contracts/EIP155Signer.sol";
import {AccountBase, AccountFactoryBase} from "./AccountBase.sol";


// A contract to create per-identity account.
contract AccountFactory is AccountFactoryBase {
event AccountCreated(address contractAddress);

function clone(address target)
public virtual override {
bytes20 targetBytes = bytes20(target);
address contractAddr;
assembly {
let contractClone := mload(0x40)
mstore(contractClone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(contractClone, 0x14), targetBytes)
mstore(add(contractClone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
contractAddr := create(0, contractClone, 0x37)
}
require(contractAddr != address(0), "Create failed");
emit AccountCreated(contractAddr);
}
}

// A base class for a per-identity account.
// It can be extended to include additional data, for example a symmetric
// key for encrypting and decryption off-chain data.
contract Account is AccountBase {
uint64 nonce;
bool initialized;

function initialize(address starterOwner)
public virtual
{
require(!initialized, "Contract is already initialized");
controller = starterOwner;
// Generate the private / public keypair
bytes memory pubKey;
bytes memory privKey;
(pubKey, privKey) = Sapphire.generateSigningKeyPair(Sapphire.SigningAlg.Secp256k1PrehashedKeccak256, Sapphire.randomBytes(32, ""));
publicKey = EthereumUtils.k256PubkeyToEthereumAddress(pubKey);
privateKey = bytes32(privKey);
nonce = 0;
initialized = true;
}

modifier validateController {
require(msg.sender == controller, "Only the controller may access this function");
_;
}

// Update the controller of this account. Useful when a different
// type of credential is to be used and a validator contract is needed.
function updateController(address _controller) external override validateController {
// I'm not sure if this is the correct way to do access control
controller = _controller;
}

// Functions to check and update permissions.
function hasPermission(address grantee) public view override
returns (bool) {
// Check that there's an entry for the grantee and that the expiration is greater than the current timestamp
return (grantee == controller || (permission[grantee] != 0 && permission[grantee] >= block.timestamp));
}

modifier authorized {
require(hasPermission(msg.sender) || msg.sender == address(this),
"Message sender doesn't have permission to access this account");
_;
}

function grantPermission(address grantee, uint256 expiry) public virtual override validateController {
permission[grantee] = expiry;
}

function revokePermission(address grantee) public virtual override validateController {
permission[grantee] = 0;
}

// The remaining functions use the key pair for normal contract operations.
function signEIP155(EIP155Signer.EthTx calldata txToSign)
public view override authorized
returns (bytes memory) {
return EIP155Signer.sign(publicKey, privateKey, txToSign);
}

// Sign a digest.
function sign(bytes32 digest)
public view override authorized
returns (SignatureRSV memory) {
return EthereumUtils.sign(publicKey, privateKey, digest);
}

// Taken from https://github.com/oasisprotocol/sapphire-paratime/blob/main/examples/onchain-signer/contracts/Gasless.sol#L23
function makeProxyTx(
address in_contract,
bytes memory in_data
) 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
})
);
}

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;
}


// Call another contract.
function call(address in_contract, bytes memory in_data)
public payable override authorized
returns (bool success, bytes memory out_data) {

(success, out_data) = in_contract.call{value: msg.value, gas: gasleft()}(in_data);
if (!success) {
assembly {
revert(add(out_data, 32), mload(out_data))
}
}
}

// Call another contract.
function staticcall(address in_contract, bytes memory in_data)
public override view authorized
returns (bool success, bytes memory out_data) {

(success, out_data) = in_contract.staticcall{gas: gasleft()}(in_data);
if (!success) {
assembly {
revert(add(out_data, 32), mload(out_data))
}
}
}
}
63 changes: 63 additions & 0 deletions contracts/AccountBase.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import {SignatureRSV, EthereumUtils} from "@oasisprotocol/sapphire-contracts/contracts/EthereumUtils.sol";
import {EIP155Signer} from "@oasisprotocol/sapphire-contracts/contracts/EIP155Signer.sol";

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

// A base class for a per-identity account.
// It can be extended to include additional data, for example a symmetric
// key for encrypting and decryption off-chain data.
abstract contract AccountBase {
// Address of the controller of this account. It can either be
// an EOA (externally owned account), or a validator contract
// (defined below).
address internal controller;

// Update the controller of this account. Useful when a different
// type of credential is to be used and a validator contract is needed.
function updateController(address _controller) external virtual;

// A key pair for the account.
address public publicKey;
bytes32 internal privateKey;

// Grant permission for another address to act as owner for this account
// until expiry.
mapping (address => uint256) permission;

// Functions to check and update permissions.
function hasPermission(address grantee) public virtual view
returns (bool);

function grantPermission(address grantee, uint256 expiry)
public virtual;
function revokePermission(address grantee) public virtual;

// The remaining functions use the key pair for normal contract operations.

// Sign a transaction.
function signEIP155(EIP155Signer.EthTx calldata txToSign)
public view virtual
returns (bytes memory);

// Sign a digest.
function sign(bytes32 digest)
public virtual view
returns (SignatureRSV memory);

// Call another contract.
function call(address in_contract, bytes memory in_data)
public payable virtual
returns (bool success, bytes memory out_data);

// Call another contract.
function staticcall(address in_contract, bytes memory in_data)
public virtual view
returns (bool success, bytes memory out_data);
}
75 changes: 75 additions & 0 deletions contracts/AccountWithSymKey.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "./Account.sol";
import "./AccountBase.sol";
import {Sapphire} from "@oasisprotocol/sapphire-contracts/contracts/Sapphire.sol";

// A contract to create per-identity account.
contract AccountWithSymKeyFactory is AccountFactory {
function clone (address target)
public override {
bytes20 targetBytes = bytes20(target);
address contractAddr;
assembly {
let contractClone := mload(0x40)
mstore(contractClone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(contractClone, 0x14), targetBytes)
mstore(add(contractClone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
contractAddr := create(0, contractClone, 0x37)
}
emit AccountCreated(contractAddr);
}
}

contract AccountWithSymKey is Account {
type Key is bytes32;

function initialize(address starterOwner)
public override {
Account.initialize(starterOwner);
}

// Named symmetric keys. name -> key
mapping (string => Key) keys;

// Generate a named symmetric key.
function generateSymKey(string calldata name, bool overwrite)
public authorized {
require(overwrite || Key.unwrap(keys[name]) == bytes32(0), "Key already exists and overwrite is false");

keys[name] = Key.wrap(bytes32(Sapphire.randomBytes(32, bytes(name))));
}

// Retrieve a named symmetric key.
function getSymKey(string calldata name)
public view authorized
returns (Key key) {
key = keys[name];
}

// Delete a named symmetric key.
function deleteSymKey(string calldata name)
external virtual authorized {
keys[name] = Key.wrap(bytes32(0));
}

// Encrypt in_data with the named symmetric key.
function encryptSymKey(string calldata name, bytes memory in_data)
public virtual view authorized
returns (bytes memory out_data) {
require(Key.unwrap(keys[name]) != bytes32(0), "Requested key doesn't exist");
bytes32 nonce = bytes32(Sapphire.randomBytes(32, ""));
bytes memory ciphertext = Sapphire.encrypt(Key.unwrap(keys[name]), nonce, in_data, "");
out_data = abi.encode(nonce, ciphertext);
}

function decryptSymKey(string calldata name, bytes memory in_data)
public view authorized
returns (bytes memory out_data) {
(bytes32 nonce, bytes memory ciphertext) = abi.decode(in_data, (bytes32, bytes));
out_data = Sapphire.decrypt(Key.unwrap(keys[name]), nonce, ciphertext, "");
}

// Some key rotation stuff here?
}
14 changes: 14 additions & 0 deletions contracts/Test.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

contract Test {
uint256 public counter;

constructor() {
counter = 0;
}

function incrementCounter() public {
counter++;
}
}
Loading

0 comments on commit 6b866fe

Please sign in to comment.