diff --git a/Makefile b/Makefile index d4bf5fb..a0e17bc 100644 --- a/Makefile +++ b/Makefile @@ -15,13 +15,11 @@ DEEPKEY_CSR_WASM = zomes/deepkey_csr.wasm TARGET = release TARGET_DIR = target/wasm32-unknown-unknown/release -COMMON_SOURCE_FILES = Makefile Cargo.toml \ - dnas/deepkey/types/Cargo.toml dnas/deepkey/types/src/*.rs +COMMON_SOURCE_FILES = Makefile Cargo.toml INT_SOURCE_FILES = $(COMMON_SOURCE_FILES) \ $(INT_DIR)/Cargo.toml $(INT_DIR)/src/*.rs $(INT_DIR)/src/validation/*.rs CSR_SOURCE_FILES = $(INT_SOURCE_FILES) \ - $(CSR_DIR)/Cargo.toml $(CSR_DIR)/src/*.rs \ - dnas/deepkey/sdk/Cargo.toml dnas/deepkey/sdk/src/*.rs + $(CSR_DIR)/Cargo.toml $(CSR_DIR)/src/*.rs # diff --git a/dnas/deepkey/sdk/Cargo.toml b/dnas/deepkey/sdk/Cargo.toml deleted file mode 100644 index 3484802..0000000 --- a/dnas/deepkey/sdk/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "hc_deepkey_sdk" -version = "0.7.0-dev.2" -authors = ["Matthew Brisebois "] -edition = "2021" -license = "CAL-1.0" -repository = "https://github.com/holochain/deepkey" -description = "SDK for the Deepkey DNA Zomes" -readme = "README.md" - -[lib] -name = "deepkey_sdk" -crate-type = ["cdylib", "rlib"] - -[dependencies] -hc_deepkey_types = { version = "0.8.0-dev.2", path = "../types" } -hdk = "=0.4.0-dev.12" -serde = "1" -serde_bytes = "0.11" - -arbitrary = { version = "1.0", features = ["derive"], optional = true } - -[features] -fuzzing = [ - "arbitrary", - "hc_deepkey_types/fuzzing", -] \ No newline at end of file diff --git a/dnas/deepkey/types/Cargo.toml b/dnas/deepkey/types/Cargo.toml deleted file mode 100644 index 3c7865d..0000000 --- a/dnas/deepkey/types/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "hc_deepkey_types" -version = "0.8.0-dev.2" -authors = ["Matthew Brisebois "] -edition = "2021" -license = "CAL-1.0" -repository = "https://github.com/holochain/deepkey" -description = "Definitions used by the Deepkey DNA Zomes" -readme = "README.md" - -[lib] -name = "deepkey_types" -crate-type = ["cdylib", "rlib"] - -[dependencies] -hdi = "=0.5.0-dev.10" -holo_hash = { version = "=0.4.0-dev.9", features = ["hashing", "encoding"] } -holochain_integrity_types = "=0.4.0-dev.10" -rmpv = { version = "1", features = ["with-serde"] } -serde = "1" - -arbitrary = { version = "1.0", features = ["derive"], optional = true } - - -[features] -fuzzing = [ - "arbitrary", - # "hdi/fuzzing", - "holochain_integrity_types/fuzzing", - "holo_hash/fuzzing", -] \ No newline at end of file diff --git a/dnas/deepkey/types/src/app_binding.rs b/dnas/deepkey/types/src/app_binding.rs deleted file mode 100644 index ca415f2..0000000 --- a/dnas/deepkey/types/src/app_binding.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::{ - MetaData, -}; -use hdi::prelude::*; -use holo_hash::DnaHash; - - -#[hdk_entry_helper] -#[derive(Clone, PartialEq)] -pub struct AppBinding { - pub app_index: u32, - pub app_name: String, - pub installed_app_id: String, - pub dna_hashes: Vec, - #[serde(default)] - pub metadata: MetaData, -} diff --git a/dnas/deepkey/types/src/change_rule.rs b/dnas/deepkey/types/src/change_rule.rs deleted file mode 100644 index 5139d03..0000000 --- a/dnas/deepkey/types/src/change_rule.rs +++ /dev/null @@ -1,26 +0,0 @@ -use hdi::prelude::*; - -use crate::{ - AuthorizedSpecChange, -}; - - -// The author needs to be linked from the KeysetRoot -#[hdk_entry_helper] -#[derive(Clone)] -pub struct ChangeRule { - pub keyset_root: ActionHash, - pub spec_change: AuthorizedSpecChange, -} - -impl ChangeRule { - pub fn new( - keyset_root: ActionHash, - spec_change: AuthorizedSpecChange, - ) -> Self { - Self { - keyset_root, - spec_change, - } - } -} diff --git a/dnas/deepkey/types/src/key_anchor.rs b/dnas/deepkey/types/src/key_anchor.rs deleted file mode 100644 index a0dc0a2..0000000 --- a/dnas/deepkey/types/src/key_anchor.rs +++ /dev/null @@ -1,46 +0,0 @@ -use hdi::prelude::*; - - -pub type KeyBytes = [u8; 32]; - - -/// A deterministic entry that contains only the cord 32 bytes of a key -/// -/// The core 32 bytes of a registered key is the `AgentPubKey` stripped of the 3 byte multihash -/// prefix and 4 byte DHT location suffix. The `EntryHash` can be derived so that the status of a -/// key can be looked up in a single `get_details` call. -#[hdk_entry_helper] -#[derive(Clone,PartialEq)] -pub struct KeyAnchor { pub bytes: KeyBytes, } - -impl KeyAnchor { - pub fn new(bytes: KeyBytes) -> Self { - KeyAnchor { - bytes, - } - } -} - - -impl TryFrom for KeyAnchor { - type Error = WasmError; - - fn try_from(input: AgentPubKey) -> Result { - Ok( - Self { - bytes: input.get_raw_32().try_into() - .map_err( |e| wasm_error!(WasmErrorInner::Guest(format!( - "Failed AgentPubKey to [u8;32] conversion: {:?}", e - ))) )?, - } - ) - } -} - -impl TryFrom<&AgentPubKey> for KeyAnchor { - type Error = WasmError; - - fn try_from(input: &AgentPubKey) -> Result { - input.to_owned().try_into() - } -} diff --git a/dnas/deepkey/types/src/key_meta.rs b/dnas/deepkey/types/src/key_meta.rs deleted file mode 100644 index 874f2fd..0000000 --- a/dnas/deepkey/types/src/key_meta.rs +++ /dev/null @@ -1,27 +0,0 @@ -use hdi::prelude::*; - - -// This is expected to have some compatibility with Lair Key API -// #[derive(Debug, Clone, Serialize, Deserialize)] -// pub enum KeyType { -// AppUI, -// AppSig, -// AppEncryption, -// TLS, -// } - - -#[hdk_entry_helper] -#[derive(Clone)] -#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] -pub struct KeyMeta { - // TODO: make sure we can ensure there is only 1 key anchor creation action - pub app_binding_addr: ActionHash, - pub key_index: u32, - pub key_registration_addr: ActionHash, - pub key_anchor_addr: ActionHash, - pub derivation_seed: Option>, - pub derivation_bytes: Option>, - - // pub key_type: KeyType, -} diff --git a/dnas/deepkey/types/src/key_registration.rs b/dnas/deepkey/types/src/key_registration.rs deleted file mode 100644 index c60df84..0000000 --- a/dnas/deepkey/types/src/key_registration.rs +++ /dev/null @@ -1,113 +0,0 @@ -use hdi::prelude::*; - -use crate::{ - KeyAnchor, - Authorization, -}; - - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] -pub struct KeyGeneration { - pub new_key: AgentPubKey, - - // The private key has signed the deepkey agent key to prove ownership - pub new_key_signing_of_author: Signature, - - // TODO - // generator: ActionHash, // This is the key authorized to generate new keys on this chain - // generator_signature: Signature, // The generator key signing the new key -} - -impl KeyGeneration { - pub fn new(key: AgentPubKey, signature: Signature) -> Self { - Self { - new_key: key, - new_key_signing_of_author: signature, - } - } -} - -impl From<(AgentPubKey, Signature)> for KeyGeneration { - fn from((key, signature): (AgentPubKey, Signature)) -> Self { - Self::new( key, signature ) - } -} - -impl From<(&AgentPubKey, &Signature)> for KeyGeneration { - fn from((key, signature): (&AgentPubKey, &Signature)) -> Self { - ( key.to_owned(), signature.to_owned() ).into() - } -} - - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] -pub struct KeyRevocation { - pub prior_key_registration: ActionHash, - pub revocation_authorization: Vec, -} - -impl KeyRevocation { - pub fn new(prior_key: ActionHash, authorizations: Vec) -> Self { - Self { - prior_key_registration: prior_key, - revocation_authorization: authorizations, - } - } -} - -impl From<(ActionHash, Vec)> for KeyRevocation { - fn from((prior_key, authorizations): (ActionHash, Vec)) -> Self { - Self::new( prior_key, authorizations ) - } -} - -impl From<(&ActionHash, &Vec)> for KeyRevocation { - fn from((prior_key, authorizations): (&ActionHash, &Vec)) -> Self { - ( prior_key.to_owned(), authorizations.to_owned() ).into() - } -} - - -/// Registration information used to validate operations on a `KeyAnchor` -/// -/// This enum supports 4 variants: -/// -/// - `Create` - *for a new key under the management of a KSR* -/// - `CreateOnly` - *for a new key that cannot be updated* -/// - `Update` - *for replacing a managed key* -/// - `Delete` - *for permanently ending the management of this registration* -#[hdk_entry_helper] -#[derive(Clone)] -#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))] -pub enum KeyRegistration { - // Creates a key under management of current KSR on this chain - Create(KeyGeneration), - - // Unmanaged key. Keys for hosted web users may be of this type, cannot replace/revoke - CreateOnly(KeyGeneration), - - // Revokes a key and replaces it with a newly generated one - Update(KeyRevocation, KeyGeneration), - - // Permanently revokes a key (Note: still uses an update action.) - Delete(KeyRevocation) -} - -impl KeyRegistration { - pub fn key_anchor(&self) -> ExternResult { - match self { - KeyRegistration::Create(key_gen) => key_gen.new_key.to_owned(), - KeyRegistration::CreateOnly(key_gen) => key_gen.new_key.to_owned(), - KeyRegistration::Update(_, key_gen) => key_gen.new_key.to_owned(), - KeyRegistration::Delete(_) => Err(wasm_error!(WasmErrorInner::Guest( - "Cannot derive KeyAnchor from a KeyRegistration::Delete".to_string() - )))?, - }.try_into() - } - - pub fn key_anchor_hash(&self) -> ExternResult { - hash_entry( self.key_anchor()? ) - } -} diff --git a/dnas/deepkey/zomelets/src/index.js b/dnas/deepkey/zomelets/src/index.js index fedd4f0..1644fdc 100644 --- a/dnas/deepkey/zomelets/src/index.js +++ b/dnas/deepkey/zomelets/src/index.js @@ -162,6 +162,16 @@ const functions = { return result; }, + async check_existing_derivation_details ( input ) { + const result = await this.call( input ); + + return result === null + ? null + : { + "app_binding": AppBinding( result[0] ), + "key_meta": KeyMeta( result[1] ), + }; + }, async create_key ( input ) { const result = await this.call( input ); diff --git a/package-lock.json b/package-lock.json index 9606c05..7bd211b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@noble/hashes": "^1.4.0", "@spartan-hc/app-interface-client": "^0.7.2", "@spartan-hc/holo-hash": "^0.7.0", - "@spartan-hc/holochain-backdrop": "^4.5.0", + "@spartan-hc/holochain-backdrop": "^4.5.1", "@whi/json": "^0.1.8", "@whi/weblogger": "^0.4.0", "chai": "^4.3.4", @@ -2118,9 +2118,9 @@ } }, "node_modules/@spartan-hc/holochain-backdrop": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@spartan-hc/holochain-backdrop/-/holochain-backdrop-4.5.0.tgz", - "integrity": "sha512-SEiE0vf5RaCqztkur8gDuRWygm3L13Iy7FPbJY2eSl4CU2rrDAmg3MwF2L9AW1QwERtNf5BZLjSY4e4hJCa01w==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@spartan-hc/holochain-backdrop/-/holochain-backdrop-4.5.1.tgz", + "integrity": "sha512-85FqnnfETKyFL9bXsZxzvoahZVpRZftwDAXtwVbtwsi+yS5SGf+w1c2fTgmiZZFA3hb5Cum+Txqj5TuvjEg43Q==", "dev": true, "license": "ISC", "dependencies": { @@ -2237,31 +2237,12 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, - "node_modules/@types/eslint": { - "version": "8.56.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", - "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -2491,7 +2472,8 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/@whi/bytes-class/-/bytes-class-0.1.0.tgz", "integrity": "sha512-+I1MnNi4MyEBw2fonAvfIUFrW9XNgRtlUnT01JLg1OS71pGjQhIFPjN4FPrjj8PGSKnFf6b/deGFJn6PUMJNxw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/@whi/into-struct": { "version": "0.2.1", @@ -3286,10 +3268,11 @@ "license": "MIT" }, "node_modules/enhanced-resolve": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", - "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -4886,6 +4869,7 @@ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -5338,12 +5322,12 @@ } }, "node_modules/webpack": { - "version": "5.92.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.0.tgz", - "integrity": "sha512-Bsw2X39MYIgxouNATyVpCNVWBCuUwDgWtN78g6lSdPJRLaQ/PUVm/oXcaRAyY/sMFoKFQrsPeqvTizWtq7QPCA==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, + "license": "MIT", "dependencies": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", @@ -5352,7 +5336,7 @@ "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", diff --git a/package.json b/package.json index 50f8b63..7788831 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "@noble/hashes": "^1.4.0", "@spartan-hc/app-interface-client": "^0.7.2", "@spartan-hc/holo-hash": "^0.7.0", - "@spartan-hc/holochain-backdrop": "^4.5.0", + "@spartan-hc/holochain-backdrop": "^4.5.1", "@whi/json": "^0.1.8", "@whi/weblogger": "^0.4.0", "chai": "^4.3.4", diff --git a/tests/integration/test_basic.js b/tests/integration/test_basic.js index 7c708d5..07cb325 100644 --- a/tests/integration/test_basic.js +++ b/tests/integration/test_basic.js @@ -45,6 +45,11 @@ const alice1_key_store = new KeyStore( ALICE1_DEVICE_SEED, "ali let app_port; let installations; +let app_count = 1; + +function next_app_index () { + return app_count++; +} describe("DeepKey", function () { @@ -123,11 +128,8 @@ function basic_tests () { it("should register new key", async function () { this.timeout( 5_000 ); - const derivation_details = await alice1_deepkey.next_derivation_details(); - const { - app_index, - key_index, - } = derivation_details; + const app_index = next_app_index(); + const key_index = 0; const path = `app/${app_index}/key/${key_index}`; const new_key = await alice1_key_store.createKey( path ); @@ -142,7 +144,8 @@ function basic_tests () { "new_key_signing_of_author": await new_key.sign( alice1_client.agent_id ), }, "derivation_details": { - ...derivation_details, + app_index, + key_index, "derivation_seed": alice1_key_store.seed, "derivation_bytes": new_key.derivation_bytes, }, diff --git a/tests/integration/test_key_management.js b/tests/integration/test_key_management.js index 9a0e9d9..a244c96 100644 --- a/tests/integration/test_key_management.js +++ b/tests/integration/test_key_management.js @@ -52,6 +52,11 @@ const bobby_app1_id = "bobby-app1"; let app_port; let installations; +let app_count = 1; + +function next_app_index () { + return app_count++; +} describe("DeepKey", function () { @@ -144,11 +149,8 @@ function basic_tests () { it("should register new key (alice)", async function () { this.timeout( 5_000 ); - const derivation_details = await alice_deepkey.next_derivation_details(); - const { - app_index, - key_index, - } = derivation_details; + const app_index = next_app_index(); + const key_index = 0; const path = `app/${app_index}/key/${key_index}`; const new_key = await alice_key_store.createKey( path ); @@ -163,7 +165,8 @@ function basic_tests () { "new_key_signing_of_author": await new_key.sign( alice_client.agent_id ), }, "derivation_details": { - ...derivation_details, + app_index, + key_index, "derivation_seed": alice_key_store.seed, "derivation_bytes": new_key.derivation_bytes, }, @@ -358,11 +361,8 @@ function basic_tests () { it("should register another key", async function () { this.timeout( 10_000 ); - const derivation_details = await alice_deepkey.next_derivation_details(); - const { - app_index, - key_index, - } = derivation_details; + const app_index = next_app_index(); + const key_index = 0; const path = `app/${app_index}/key/${key_index}`; const new_key = await alice_key_store.createKey( path ); @@ -377,7 +377,8 @@ function basic_tests () { "new_key_signing_of_author": await new_key.sign( alice_client.agent_id ), }, "derivation_details": { - ...derivation_details, + app_index, + key_index, "derivation_seed": alice_key_store.seed, "derivation_bytes": new_key.derivation_bytes, }, @@ -391,11 +392,10 @@ function basic_tests () { it("should register new key with the same app ID (alice)", async function () { this.timeout( 5_000 ); - const derivation_details = await alice_deepkey.next_derivation_details(); - const { - app_index, - key_index, - } = derivation_details; + next_app_index(); // Skip a number + + const app_index = next_app_index(); + const key_index = 0; const path = `app/${app_index}/key/${key_index}`; const new_key = await alice_key_store.createKey( path ); @@ -410,7 +410,8 @@ function basic_tests () { "new_key_signing_of_author": await new_key.sign( alice_client.agent_id ), }, "derivation_details": { - ...derivation_details, + app_index, + key_index, "derivation_seed": alice_key_store.seed, "derivation_bytes": new_key.derivation_bytes, }, @@ -487,6 +488,27 @@ function basic_tests () { } }); + it("should register new key without derivation details", async function () { + this.timeout( 5_000 ); + + const new_key = await random_key(); + + const [ addr, key_reg, key_meta ] = await alice_deepkey.create_key({ + "app_binding": { + "app_name": "Alice - App #4", + "installed_app_id": "random_agent_key", + "dna_hashes": [ dna1_hash ], + }, + "key_generation": { + "new_key": await new_key.getAgent(), + "new_key_signing_of_author": await new_key.sign( alice_client.agent_id ), + }, + }); + log.normal("Key Registration (%s): %s", addr, json.debug(key_reg) ); + log.normal("Key Meta: %s", json.debug(key_meta) ); + log.normal("Key registration (update) addr: %s", addr ); + }); + it("should query key lineage", async function () { { const lineage = await alice_deepkey.query_key_lineage( alice_key1a ); @@ -588,6 +610,50 @@ function basic_tests () { } }); + it("should check existing derivation details", async function () { + this.timeout( 10_000 ); + + { + const app_index = 1; + const key_index = 0; + const path = `app/${app_index}/key/${key_index}`; + const new_key = await alice_key_store.createKey( path ); + + const { key_meta } = await alice_deepkey.check_existing_derivation_details({ + app_index, + key_index, + "derivation_seed": alice_key_store.seed, + "derivation_bytes": new_key.derivation_bytes, + }); + + log.normal("Key meta: %s", json.debug(key_meta) ); + expect( key_meta.derivation_bytes ).to.deep.equal( new_key.derivation_bytes ); + } + + { + const app_index = 1; + const key_index = 0; + const path = `app/${app_index}/key/${key_index}`; + const new_key = await alice_key_store.createKey( path ); + + const { + app_binding, + key_meta, + } = await alice_deepkey.check_existing_derivation_details({ + app_index, + key_index, + "derivation_seed": alice_key_store.seed, + "derivation_bytes": [0,0,0], + }); + + log.normal("Key meta: %s", json.debug(key_meta) ); + + expect( app_binding.app_index ).to.equal( app_index ); + expect( key_meta.key_index ).to.equal( key_index ); + expect( key_meta.derivation_seed ).to.deep.equal( alice_key_store.seed ); + } + }); + linearSuite("Errors", function () { it("should fail to register invalid key", async function () { @@ -667,6 +733,36 @@ function basic_tests () { }, "already a KeyRegistration" ); }); + it("should fail to update key because app_index changed", async function () { + await expect_reject(async () => { + const app_index = 0; + const { + key_index, + } = await alice_deepkey.next_derivation_details( alice_key1c ); + const path = `app/${app_index}/key/${key_index}`; + const new_key = await alice_key_store.createKey( path ); + + await alice_deepkey.update_key({ + "key_revocation": { + "prior_key_registration": alice_key1c_reg_addr, + "revocation_authorization": [ + [ 0, await alice_deepkey.sign( alice_key1c_reg_addr ) ], + ], + }, + "key_generation": { + "new_key": await new_key.getAgent(), + "new_key_signing_of_author": await new_key.sign( alice_client.agent_id ), + }, + "derivation_details": { + app_index, + key_index, + "derivation_seed": alice_key_store.seed, + "derivation_bytes": new_key.derivation_bytes, + }, + }); + }, "derivation app index does not match the app binding" ); + }); + }); after(async function () { diff --git a/tests/key_store.js b/tests/key_store.js index de61f5b..6991ebf 100644 --- a/tests/key_store.js +++ b/tests/key_store.js @@ -5,6 +5,7 @@ import crypto from 'crypto'; import * as ed from '@noble/ed25519'; import { hmac } from '@noble/hashes/hmac'; import { sha256 } from '@noble/hashes/sha256'; +import { Bytes } from '@whi/bytes-class'; import { AgentPubKey, @@ -17,7 +18,7 @@ export class KeyStore { #keys = {}; constructor ( device_seed, name ) { - this.#device_seed = Buffer.from( device_seed ).toString("hex"); + this.#device_seed = new Bytes( device_seed ); if ( name ) this.#name = name; diff --git a/zomes/deepkey_csr/src/key_registration.rs b/zomes/deepkey_csr/src/key_registration.rs index 69391cc..6ab29ab 100644 --- a/zomes/deepkey_csr/src/key_registration.rs +++ b/zomes/deepkey_csr/src/key_registration.rs @@ -23,33 +23,20 @@ pub use deepkey_sdk::{ }; -// TODO: conductor will keep track of app_index instead of this; therefore, remove option #[hdk_extern] pub fn next_derivation_details( - input: Option> + key_bytes: ByteArray<32> ) -> ExternResult { - Ok( - match input { - Some(key_bytes) => { - let (_, app_binding) = crate::app_binding::query_app_binding_by_key( key_bytes )?; + let (_, app_binding) = crate::app_binding::query_app_binding_by_key( key_bytes )?; - let next_key_index = crate::key_meta::query_next_key_index_for_app_index( - app_binding.app_index - )?; + let next_key_index = crate::key_meta::query_next_key_index_for_app_index( + app_binding.app_index + )?; - DerivationDetails { - app_index: app_binding.app_index, - key_index: next_key_index, - } - }, - None => { - DerivationDetails { - app_index: crate::app_binding::query_next_app_index(())?, - key_index: 0, - } - }, - } - ) + Ok(DerivationDetails { + app_index: app_binding.app_index, + key_index: next_key_index, + }) } @@ -69,6 +56,35 @@ pub fn get_key_derivation_details( } +#[hdk_extern] +pub fn check_existing_derivation_details( + derivation_details: DerivationDetailsInput, +) -> ExternResult> { + for key_meta in crate::key_meta::query_key_metas(())? { + if let Some(ref derivation_bytes) = key_meta.derivation_bytes { + if *derivation_bytes == derivation_details.derivation_bytes { + let app_binding = crate::app_binding::query_app_binding_by_action( + key_meta.app_binding_addr.to_owned() + )?; + + return Ok(Some((app_binding, key_meta))); + } + } + + let app_binding = crate::app_binding::query_app_binding_by_action( + key_meta.app_binding_addr.to_owned() + )?; + + if derivation_details.app_index == app_binding.app_index && + Some(derivation_details.derivation_seed.to_owned()) == key_meta.derivation_seed { + return Ok(Some((app_binding, key_meta))); + } + } + + Ok(None) +} + + /// Register a new app/key pair and create associated private entries /// /// #### Example usage @@ -123,26 +139,6 @@ pub fn create_key(input: CreateKeyInput) -> ExternResult<(ActionHash, KeyRegistr let key_gen = input.key_generation; let next_app_index = crate::app_binding::query_next_app_index(())?; - // Check that derivation details match the chain state - if let Some(derivation_details) = &input.derivation_details { - let given_app_index = derivation_details.app_index; - let given_key_index = derivation_details.key_index; - - // TODO: change this check so that it only checks if the index was already use - if given_app_index != next_app_index { - Err(guest_error!(format!( - "The derivation app index does not match the chain state: [given] {} != {} [next]", - given_app_index, next_app_index, - )))? - } - if given_key_index != 0 { - Err(guest_error!(format!( - "The derivation key index must be 0 for a new key registration: [given] {} != 0", - given_key_index, - )))? - } - } - // Derive Key Anchor let key_anchor = KeyAnchor::try_from( &key_gen.new_key )?; @@ -264,7 +260,6 @@ pub fn update_key(input: UpdateKeyInput) -> ExternResult<(ActionHash, KeyRegistr // Check that derivation details match the chain state if let Some(derivation_details) = &input.derivation_details { let given_app_index = derivation_details.app_index; - let given_key_index = derivation_details.key_index; // Check that derivation details has the correct 'app_index' if given_app_index != app_binding.app_index { @@ -273,14 +268,6 @@ pub fn update_key(input: UpdateKeyInput) -> ExternResult<(ActionHash, KeyRegistr given_app_index, app_binding.app_index, )))? } - - // Check that derivation details has the correct 'key_index' - if given_key_index != next_key_index { - Err(guest_error!(format!( - "The derivation key index does not match the chain state: [given] {} != {} [next]", - given_key_index, next_key_index - )))? - } } // Derive Key Anchor @@ -299,7 +286,9 @@ pub fn update_key(input: UpdateKeyInput) -> ExternResult<(ActionHash, KeyRegistr // Create Meta let key_meta = KeyMeta { app_binding_addr: prior_key_meta.app_binding_addr.clone(), - key_index: next_key_index, + key_index: input.derivation_details.as_ref() + .map( |details| details.key_index.to_owned() ) + .unwrap_or( next_key_index ), key_registration_addr: key_reg_addr.clone(), key_anchor_addr: key_anchor_addr.clone(), derivation_seed: input.derivation_details.as_ref()