Skip to content

Commit

Permalink
initial approach
Browse files Browse the repository at this point in the history
  • Loading branch information
Thunkar committed Aug 1, 2024
1 parent 7fc9321 commit 1f3cd02
Show file tree
Hide file tree
Showing 28 changed files with 533 additions and 121 deletions.
3 changes: 2 additions & 1 deletion noir-projects/noir-contracts/Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ members = [
"contracts/docs_example_contract",
"contracts/easy_private_token_contract",
"contracts/easy_private_voting_contract",
"contracts/ecdsa_account_contract",
"contracts/ecdsa_k_account_contract",
"contracts/ecdsa_r_account_contract",
"contracts/escrow_contract",
"contracts/gas_token_contract",
"contracts/import_test_contract",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
[package]
name = "ecdsa_account_contract"
name = "ecdsa_k_account_contract"
authors = [""]
compiler_version = ">=0.25.0"
type = "contract"

[dependencies]
aztec = { path = "../../../aztec-nr/aztec" }
authwit = { path = "../../../aztec-nr/authwit" }
ecdsa_public_key_note = { path = "../ecdsa_public_key_note" }
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
mod ecdsa_public_key_note;

// Account contract that uses ECDSA signatures for authentication on the same curve as Ethereum.
// The signing key is stored in an immutable private note and should be different from the signing key.
contract EcdsaAccount {
contract EcdsaKAccount {
use dep::aztec::prelude::{AztecAddress, FunctionSelector, NoteHeader, NoteGetterOptions, PrivateContext, PrivateImmutable};
use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note;

Expand All @@ -14,7 +12,7 @@ contract EcdsaAccount {
auth_witness::get_auth_witness
};

use crate::ecdsa_public_key_note::EcdsaPublicKeyNote;
use dep::ecdsa_public_key_note::EcdsaPublicKeyNote;

#[aztec(storage)]
struct Storage {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "ecdsa_public_key_note"
authors = ["aztec-labs"]
compiler_version = ">=0.25.0"
type = "lib"

[dependencies]
aztec = { path = "../../../aztec-nr/aztec" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "ecdsa_r_account_contract"
authors = [""]
compiler_version = ">=0.25.0"
type = "contract"

[dependencies]
aztec = { path = "../../../aztec-nr/aztec" }
authwit = { path = "../../../aztec-nr/authwit" }
ecdsa_public_key_note = { path = "../ecdsa_public_key_note" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Account contract that uses ECDSA signatures for authentication on random version of the p256 curve (to use with touchID).
contract EcdsaRAccount {
use dep::aztec::prelude::{AztecAddress, FunctionSelector, NoteHeader, NoteGetterOptions, PrivateContext, PrivateImmutable};
use dep::aztec::encrypted_logs::encrypted_note_emission::encode_and_encrypt_note;

use dep::aztec::protocol_types::abis::call_context::CallContext;
use dep::std;

use dep::authwit::{
entrypoint::{app::AppPayload, fee::FeePayload}, account::AccountActions,
auth_witness::get_auth_witness
};

use dep::ecdsa_public_key_note::EcdsaPublicKeyNote;

#[aztec(storage)]
struct Storage {
public_key: PrivateImmutable<EcdsaPublicKeyNote>,
}

// Creates a new account out of an ECDSA public key to use for signature verification
#[aztec(private)]
#[aztec(initializer)]
fn constructor(signing_pub_key_x: [u8; 32], signing_pub_key_y: [u8; 32]) {
let this = context.this_address();
let header = context.get_header();
let this_npk_m_hash = header.get_npk_m_hash(&mut context, this);
// Not emitting outgoing for msg_sender here to not have to register keys for the contract through which we
// deploy this (typically MultiCallEntrypoint). I think it's ok here as I feel the outgoing here is not that
// important.

let mut pub_key_note = EcdsaPublicKeyNote::new(signing_pub_key_x, signing_pub_key_y, this_npk_m_hash);
storage.public_key.initialize(&mut pub_key_note).emit(encode_and_encrypt_note(&mut context, this, this));
}

// Note: If you globally change the entrypoint signature don't forget to update default_entrypoint.ts
#[aztec(private)]
fn entrypoint(app_payload: AppPayload, fee_payload: FeePayload) {
let actions = AccountActions::init(&mut context, is_valid_impl);
actions.entrypoint(app_payload, fee_payload);
}

#[aztec(private)]
#[aztec(noinitcheck)]
#[aztec(view)]
fn verify_private_authwit(inner_hash: Field) -> Field {
let actions = AccountActions::init(&mut context, is_valid_impl);
actions.verify_private_authwit(inner_hash)
}

#[contract_library_method]
fn is_valid_impl(context: &mut PrivateContext, outer_hash: Field) -> bool {
// Load public key from storage
let storage = Storage::init(context);
let public_key = storage.public_key.get_note();

// Load auth witness
let witness: [Field; 64] = get_auth_witness(outer_hash);
let mut signature: [u8; 64] = [0; 64];
for i in 0..64 {
signature[i] = witness[i] as u8;
}

// Verify payload signature using Ethereum's signing scheme
// Note that noir expects the hash of the message/challenge as input to the ECDSA verification.
let outer_hash_bytes: [u8; 32] = outer_hash.to_be_bytes(32).as_array();
let hashed_message: [u8; 32] = std::hash::sha256(outer_hash_bytes);
let verification = std::ecdsa_secp256r1::verify_signature(public_key.x, public_key.y, signature, hashed_message);
assert(verification == true);

true
}
}
3 changes: 2 additions & 1 deletion yarn-project/accounts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"./ecdsa": "./dest/ecdsa/index.js",
"./schnorr": "./dest/schnorr/index.js",
"./single_key": "./dest/single_key/index.js",
"./testing": "./dest/testing/index.js"
"./testing": "./dest/testing/index.js",
"./utils": "./dest/utils/index.js"
},
"typedocOptions": {
"entryPoints": [
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/accounts/scripts/copy-contracts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
set -euo pipefail
mkdir -p ./artifacts

contracts=(schnorr_account_contract-SchnorrAccount ecdsa_account_contract-EcdsaAccount schnorr_single_key_account_contract-SchnorrSingleKeyAccount)
contracts=(schnorr_account_contract-SchnorrAccount ecdsa_k_account_contract-EcdsaKAccount ecdsa_r_account_contract-EcdsaRAccount schnorr_single_key_account_contract-SchnorrSingleKeyAccount)

decl=$(cat <<EOF
import { type NoirCompiledContract } from '@aztec/types/noir';
Expand Down
5 changes: 0 additions & 5 deletions yarn-project/accounts/src/ecdsa/artifact.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import { Ecdsa } from '@aztec/circuits.js/barretenberg';
import { type ContractArtifact } from '@aztec/foundation/abi';
import { type Fr } from '@aztec/foundation/fields';

import { DefaultAccountContract } from '../defaults/account_contract.js';
import { EcdsaAccountContractArtifact } from './artifact.js';
import { DefaultAccountContract } from '../../defaults/account_contract.js';
import { EcdsaKAccountContractArtifact } from './artifact.js';

/**
* Account contract that authenticates transactions using ECDSA signatures
* verified against a secp256k1 public key stored in an immutable encrypted note.
*/
export class EcdsaAccountContract extends DefaultAccountContract {
export class EcdsaKAccountContract extends DefaultAccountContract {
constructor(private signingPrivateKey: Buffer) {
super(EcdsaAccountContractArtifact as ContractArtifact);
super(EcdsaKAccountContractArtifact as ContractArtifact);
}

getDeploymentArgs() {
Expand All @@ -22,12 +22,12 @@ export class EcdsaAccountContract extends DefaultAccountContract {
}

getAuthWitnessProvider(_address: CompleteAddress): AuthWitnessProvider {
return new EcdsaAuthWitnessProvider(this.signingPrivateKey);
return new EcdsaKAuthWitnessProvider(this.signingPrivateKey);
}
}

/** Creates auth witnesses using ECDSA signatures. */
class EcdsaAuthWitnessProvider implements AuthWitnessProvider {
class EcdsaKAuthWitnessProvider implements AuthWitnessProvider {
constructor(private signingPrivateKey: Buffer) {}

createAuthWit(messageHash: Fr): Promise<AuthWitness> {
Expand Down
5 changes: 5 additions & 0 deletions yarn-project/accounts/src/ecdsa/ecdsa_k/artifact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { type NoirCompiledContract, loadContractArtifact } from '@aztec/aztec.js';

import EcdsaKAccountContractJson from '../../../artifacts/EcdsaKAccount.json' assert { type: 'json' };

export const EcdsaKAccountContractArtifact = loadContractArtifact(EcdsaKAccountContractJson as NoirCompiledContract);
37 changes: 37 additions & 0 deletions yarn-project/accounts/src/ecdsa/ecdsa_k/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* The `@aztec/accounts/ecdsa` export provides an ECDSA account contract implementation, that uses an ECDSA private key for authentication, and a Grumpkin key for encryption.
* Consider using this account type when working with integrations with Ethereum wallets.
*
* @packageDocumentation
*/
import { AccountManager, type Salt } from '@aztec/aztec.js/account';
import { type AccountWallet, getWallet } from '@aztec/aztec.js/wallet';
import { type PXE } from '@aztec/circuit-types';
import { type AztecAddress, type Fr } from '@aztec/circuits.js';

import { EcdsaKAccountContract } from './account_contract.js';

export { EcdsaKAccountContractArtifact } from './artifact.js';
export { EcdsaKAccountContract };

/**
* Creates an Account that relies on an ECDSA signing key for authentication.
* @param pxe - An PXE server instance.
* @param secretKey - Secret key used to derive all the keystore keys.
* @param signingPrivateKey - Secp256k1 key used for signing transactions.
* @param salt - Deployment salt.
*/
export function getEcdsaKAccount(pxe: PXE, secretKey: Fr, signingPrivateKey: Buffer, salt?: Salt): AccountManager {
return new AccountManager(pxe, secretKey, new EcdsaKAccountContract(signingPrivateKey), salt);
}

/**
* Gets a wallet for an already registered account using ECDSA signatures.
* @param pxe - An PXE server instance.
* @param address - Address for the account.
* @param signingPrivateKey - ECDSA key used for signing transactions.
* @returns A wallet for this account that can be used to interact with a contract instance.
*/
export function getEcdsaKWallet(pxe: PXE, address: AztecAddress, signingPrivateKey: Buffer): Promise<AccountWallet> {
return getWallet(pxe, address, new EcdsaKAccountContract(signingPrivateKey));
}
39 changes: 2 additions & 37 deletions yarn-project/accounts/src/ecdsa/index.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,2 @@
/**
* The `@aztec/accounts/ecdsa` export provides an ECDSA account contract implementation, that uses an ECDSA private key for authentication, and a Grumpkin key for encryption.
* Consider using this account type when working with integrations with Ethereum wallets.
*
* @packageDocumentation
*/
import { AccountManager, type Salt } from '@aztec/aztec.js/account';
import { type AccountWallet, getWallet } from '@aztec/aztec.js/wallet';
import { type PXE } from '@aztec/circuit-types';
import { type AztecAddress, type Fr } from '@aztec/circuits.js';

import { EcdsaAccountContract } from './account_contract.js';

export { EcdsaAccountContractArtifact } from './artifact.js';
export { EcdsaAccountContract };

/**
* Creates an Account that relies on an ECDSA signing key for authentication.
* @param pxe - An PXE server instance.
* @param secretKey - Secret key used to derive all the keystore keys.
* @param signingPrivateKey - Secp256k1 key used for signing transactions.
* @param salt - Deployment salt.
*/
export function getEcdsaAccount(pxe: PXE, secretKey: Fr, signingPrivateKey: Buffer, salt?: Salt): AccountManager {
return new AccountManager(pxe, secretKey, new EcdsaAccountContract(signingPrivateKey), salt);
}

/**
* Gets a wallet for an already registered account using ECDSA signatures.
* @param pxe - An PXE server instance.
* @param address - Address for the account.
* @param signingPrivateKey - ECDSA key used for signing transactions.
* @returns A wallet for this account that can be used to interact with a contract instance.
*/
export function getEcdsaWallet(pxe: PXE, address: AztecAddress, signingPrivateKey: Buffer): Promise<AccountWallet> {
return getWallet(pxe, address, new EcdsaAccountContract(signingPrivateKey));
}
export * from './ecdsa_k/index.js';
export * from './ssh_ecdsa_r/index.js';
39 changes: 39 additions & 0 deletions yarn-project/accounts/src/ecdsa/ssh_ecdsa_r/account_contract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { type AuthWitnessProvider } from '@aztec/aztec.js/account';
import { AuthWitness, type CompleteAddress } from '@aztec/circuit-types';
import { type ContractArtifact } from '@aztec/foundation/abi';
import { type Fr } from '@aztec/foundation/fields';

import { DefaultAccountContract } from '../../defaults/account_contract.js';
import { signWithAgent } from '../../utils/ssh_agent.js';
import { EcdsaRAccountContractArtifact } from './artifact.js';

/**
* Account contract that authenticates transactions using ECDSA signatures
* verified against a secp256r1 public key stored in an immutable encrypted note.
*/
export class EcdsaRSSHAccountContract extends DefaultAccountContract {
constructor(private signingPublicKey: Buffer) {
super(EcdsaRAccountContractArtifact as ContractArtifact);
}

getDeploymentArgs() {
return [this.signingPublicKey.subarray(0, 32), this.signingPublicKey.subarray(32, 64)];
}

getAuthWitnessProvider(_address: CompleteAddress): AuthWitnessProvider {
return new SSHEcdsaAuthWitnessProvider(this.signingPublicKey);
}
}

/** Creates auth witnesses using ECDSA signatures. */
class SSHEcdsaAuthWitnessProvider implements AuthWitnessProvider {
constructor(private signingPublicKey: Buffer) {}

async createAuthWit(messageHash: Fr): Promise<AuthWitness> {
// Key type and curve name
const keyType = Buffer.from('ecdsa-sha2-nistp256');
const curveName = Buffer.from('nistp256');
const signature = await signWithAgent(keyType, curveName, this.signingPublicKey, messageHash.toBuffer());
return Promise.resolve(new AuthWitness(messageHash, [...signature.r, ...signature.s]));
}
}
5 changes: 5 additions & 0 deletions yarn-project/accounts/src/ecdsa/ssh_ecdsa_r/artifact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { type NoirCompiledContract, loadContractArtifact } from '@aztec/aztec.js';

import EcdsaRAccountContractJson from '../../../artifacts/EcdsaRAccount.json' assert { type: 'json' };

export const EcdsaRAccountContractArtifact = loadContractArtifact(EcdsaRAccountContractJson as NoirCompiledContract);
37 changes: 37 additions & 0 deletions yarn-project/accounts/src/ecdsa/ssh_ecdsa_r/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* The `@aztec/accounts/ecdsa` export provides an ECDSA account contract implementation, that uses an ECDSA private key for authentication, and a Grumpkin key for encryption.
* Consider using this account type when working with integrations with Ethereum wallets.
*
* @packageDocumentation
*/
import { AccountManager, type Salt } from '@aztec/aztec.js/account';
import { type AccountWallet, getWallet } from '@aztec/aztec.js/wallet';
import { type PXE } from '@aztec/circuit-types';
import { type AztecAddress, type Fr } from '@aztec/circuits.js';

import { EcdsaRSSHAccountContract } from './account_contract.js';

export { EcdsaRAccountContractArtifact } from './artifact.js';
export { EcdsaRSSHAccountContract };

/**
* Creates an Account that relies on an ECDSA signing key for authentication.
* @param pxe - An PXE server instance.
* @param secretKey - Secret key used to derive all the keystore keys.
* @param signingPublicKey - Secp2561 key used to identify its corresponding private key in the SSH Agent.
* @param salt - Deployment salt.
*/
export function getEcdsaRSSHAccount(pxe: PXE, secretKey: Fr, signingPublicKey: Buffer, salt?: Salt): AccountManager {
return new AccountManager(pxe, secretKey, new EcdsaRSSHAccountContract(signingPublicKey), salt);
}

/**
* Gets a wallet for an already registered account using ECDSA signatures.
* @param pxe - An PXE server instance.
* @param address - Address for the account.
* @param signingPrivateKey - ECDSA key used for signing transactions.
* @returns A wallet for this account that can be used to interact with a contract instance.
*/
export function getEcdsaRSSHWallet(pxe: PXE, address: AztecAddress, signingPublicKey: Buffer): Promise<AccountWallet> {
return getWallet(pxe, address, new EcdsaRSSHAccountContract(signingPublicKey));
}
1 change: 1 addition & 0 deletions yarn-project/accounts/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ssh_agent.js';
Loading

0 comments on commit 1f3cd02

Please sign in to comment.