From 6180c8f27a3fdfd8da8f7fa350a425c156ffd1ad Mon Sep 17 00:00:00 2001 From: ThetaSinner Date: Fri, 16 Feb 2024 22:14:18 +0000 Subject: [PATCH] Distribute public keys --- .gitignore | 2 + README.md | 12 +- .../src/{gpg_key.rs => gpg_key_dist.rs} | 18 ++- .../zomes/coordinator/trusted/src/lib.rs | 8 +- .../zomes/integrity/trusted/src/gpg_key.rs | 28 ----- .../integrity/trusted/src/gpg_key_dist.rs | 31 +++++ .../zomes/integrity/trusted/src/lib.rs | 49 ++++---- flake.nix | 13 ++ package-lock.json | 38 ++++++ package.json | 4 +- ui/package.json | 13 +- ui/src/App.vue | 31 +---- ui/src/trusted/trusted/CreateGpgKey.vue | 113 ++++++++++++------ ui/src/trusted/trusted/GpgKeyDetail.vue | 6 +- ui/src/trusted/trusted/types.ts | 12 +- 15 files changed, 244 insertions(+), 134 deletions(-) rename dnas/trusted/zomes/coordinator/trusted/src/{gpg_key.rs => gpg_key_dist.rs} (57%) delete mode 100644 dnas/trusted/zomes/integrity/trusted/src/gpg_key.rs create mode 100644 dnas/trusted/zomes/integrity/trusted/src/gpg_key_dist.rs diff --git a/.gitignore b/.gitignore index 6fdaad6..82f4032 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ .hc* .hc .running +.gnupg +*.asc diff --git a/README.md b/README.md index b8b1718..aa50931 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Enter the nix shell by running this in the root folder of the repository: ```bash -nix-shell +nix develop npm install ``` @@ -57,3 +57,13 @@ This repository is using these tools: - [@holochain/tryorama](https://www.npmjs.com/package/@holochain/tryorama): test framework. - [@holochain/client](https://www.npmjs.com/package/@holochain/client): client library to connect to Holochain from the UI. - [@holochain-playground/cli](https://www.npmjs.com/package/@holochain-playground/cli): introspection tooling to understand what's going on in the Holochain nodes. + +## Keys + +Create a GPG key for testing + +``` +gpg --quick-generate-key tester +gpg --export --armor tester > tester_key.asc +``` + diff --git a/dnas/trusted/zomes/coordinator/trusted/src/gpg_key.rs b/dnas/trusted/zomes/coordinator/trusted/src/gpg_key_dist.rs similarity index 57% rename from dnas/trusted/zomes/coordinator/trusted/src/gpg_key.rs rename to dnas/trusted/zomes/coordinator/trusted/src/gpg_key_dist.rs index ce3c32b..890dc1c 100644 --- a/dnas/trusted/zomes/coordinator/trusted/src/gpg_key.rs +++ b/dnas/trusted/zomes/coordinator/trusted/src/gpg_key_dist.rs @@ -1,8 +1,17 @@ use hdk::prelude::*; -use trusted_integrity::*; +use trusted_integrity::prelude::*; + +#[derive(Serialize, Deserialize, Debug, Clone, SerializedBytes)] +pub struct DistributeGpgKeyRequest { + pub public_key: String, +} + #[hdk_extern] -pub fn create_gpg_key(gpg_key: GpgKey) -> ExternResult { - let gpg_key_hash = create_entry(&EntryTypes::GpgKey(gpg_key.clone()))?; +pub fn distribute_gpg_key(gpg_key: DistributeGpgKeyRequest) -> ExternResult { + let gpg_key_hash = create_entry(&EntryTypes::GpgKeyDist(GpgKeyDist { + public_key: gpg_key.public_key, + fingerprint: "TODO".to_string(), + }))?; let record = get(gpg_key_hash.clone(), GetOptions::default())? .ok_or( wasm_error!( @@ -11,8 +20,9 @@ pub fn create_gpg_key(gpg_key: GpgKey) -> ExternResult { )?; Ok(record) } + #[hdk_extern] -pub fn get_gpg_key(gpg_key_hash: ActionHash) -> ExternResult> { +pub fn get_gpg_key_dist(gpg_key_hash: ActionHash) -> ExternResult> { let Some(details) = get_details(gpg_key_hash, GetOptions::default())? else { return Ok(None); }; diff --git a/dnas/trusted/zomes/coordinator/trusted/src/lib.rs b/dnas/trusted/zomes/coordinator/trusted/src/lib.rs index 50a2308..3f15084 100644 --- a/dnas/trusted/zomes/coordinator/trusted/src/lib.rs +++ b/dnas/trusted/zomes/coordinator/trusted/src/lib.rs @@ -1,11 +1,12 @@ -pub mod gpg_key; +pub mod gpg_key_dist; use hdk::prelude::*; use trusted_integrity::*; -// Called the first time a zome call is made to the cell containing this zome + #[hdk_extern] pub fn init(_: ()) -> ExternResult { Ok(InitCallbackResult::Pass) } + // Don't modify this enum if you want the scaffolding tool to generate appropriate signals for your entries and links #[derive(Serialize, Deserialize, Debug)] #[serde(tag = "type")] @@ -18,6 +19,7 @@ pub enum Signal { }, EntryDeleted { action: SignedActionHashed, original_app_entry: EntryTypes }, } + // Whenever an action is committed, we emit a signal to the UI elements to reactively update them #[hdk_extern(infallible)] pub fn post_commit(committed_actions: Vec) { @@ -28,6 +30,7 @@ pub fn post_commit(committed_actions: Vec) { } } } + // Don't modify this function if you want the scaffolding tool to generate appropriate signals for your entries and links fn signal_action(action: SignedActionHashed) -> ExternResult<()> { match action.hashed.content.clone() { @@ -68,6 +71,7 @@ fn signal_action(action: SignedActionHashed) -> ExternResult<()> { _ => Ok(()), } } + fn get_entry_for_action(action_hash: &ActionHash) -> ExternResult> { let record = match get_details(action_hash.clone(), GetOptions::default())? { Some(Details::Record(record_details)) => record_details.record, diff --git a/dnas/trusted/zomes/integrity/trusted/src/gpg_key.rs b/dnas/trusted/zomes/integrity/trusted/src/gpg_key.rs deleted file mode 100644 index a5d2491..0000000 --- a/dnas/trusted/zomes/integrity/trusted/src/gpg_key.rs +++ /dev/null @@ -1,28 +0,0 @@ -use hdi::prelude::*; -#[hdk_entry_helper] -#[derive(Clone, PartialEq)] -pub struct GpgKey { - pub public_key: Vec, - pub fingerprint: String, -} -pub fn validate_create_gpg_key( - _action: EntryCreationAction, - _gpg_key: GpgKey, -) -> ExternResult { - Ok(ValidateCallbackResult::Valid) -} -pub fn validate_update_gpg_key( - _action: Update, - _gpg_key: GpgKey, - _original_action: EntryCreationAction, - _original_gpg_key: GpgKey, -) -> ExternResult { - Ok(ValidateCallbackResult::Invalid(String::from("Gpg Keys cannot be updated"))) -} -pub fn validate_delete_gpg_key( - _action: Delete, - _original_action: EntryCreationAction, - _original_gpg_key: GpgKey, -) -> ExternResult { - Ok(ValidateCallbackResult::Invalid(String::from("Gpg Keys cannot be deleted"))) -} diff --git a/dnas/trusted/zomes/integrity/trusted/src/gpg_key_dist.rs b/dnas/trusted/zomes/integrity/trusted/src/gpg_key_dist.rs new file mode 100644 index 0000000..48cde46 --- /dev/null +++ b/dnas/trusted/zomes/integrity/trusted/src/gpg_key_dist.rs @@ -0,0 +1,31 @@ +use hdi::prelude::*; +#[hdk_entry_helper] +#[derive(Clone, PartialEq)] +pub struct GpgKeyDist { + pub public_key: String, + pub fingerprint: String, +} + +pub fn validate_create_gpg_key_dist( + _action: EntryCreationAction, + _gpg_key: GpgKeyDist, +) -> ExternResult { + Ok(ValidateCallbackResult::Valid) +} + +pub fn validate_update_gpg_key_dist( + _action: Update, + _gpg_key: GpgKeyDist, + _original_action: EntryCreationAction, + _original_gpg_key: GpgKeyDist, +) -> ExternResult { + Ok(ValidateCallbackResult::Invalid(String::from("Gpg key distributions cannot be updated"))) +} + +pub fn validate_delete_gpg_key_dist( + _action: Delete, + _original_action: EntryCreationAction, + _original_gpg_key: GpgKeyDist, +) -> ExternResult { + Ok(ValidateCallbackResult::Invalid(String::from("Gpg key distributions cannot be deleted"))) +} diff --git a/dnas/trusted/zomes/integrity/trusted/src/lib.rs b/dnas/trusted/zomes/integrity/trusted/src/lib.rs index dd573e0..ca126c7 100644 --- a/dnas/trusted/zomes/integrity/trusted/src/lib.rs +++ b/dnas/trusted/zomes/integrity/trusted/src/lib.rs @@ -1,13 +1,20 @@ -pub mod gpg_key; -pub use gpg_key::*; +mod gpg_key_dist; + use hdi::prelude::*; + +pub mod prelude { + pub use crate::gpg_key_dist::*; + pub use crate::EntryTypes; +} + #[derive(Serialize, Deserialize)] #[serde(tag = "type")] #[hdk_entry_types] #[unit_enum(UnitEntryTypes)] pub enum EntryTypes { - GpgKey(GpgKey), + GpgKeyDist(gpg_key_dist::GpgKeyDist), } + // Validation you perform during the genesis process. Nobody else on the network performs it, only you. // There *is no* access to network calls in this callback #[hdk_extern] @@ -16,6 +23,7 @@ pub fn genesis_self_check( ) -> ExternResult { Ok(ValidateCallbackResult::Valid) } + // Validation the network performs when you try to join, you can't perform this validation yourself as you are not a member yet. // There *is* access to network calls in this function pub fn validate_agent_joining( @@ -24,6 +32,7 @@ pub fn validate_agent_joining( ) -> ExternResult { Ok(ValidateCallbackResult::Valid) } + // This is the unified validation callback for all entries and link types in this integrity zome // Below is a match template for all of the variants of `DHT Ops` and entry and link types // @@ -51,8 +60,8 @@ pub fn validate(op: Op) -> ExternResult { match store_entry { OpEntry::CreateEntry { app_entry, action } => { match app_entry { - EntryTypes::GpgKey(gpg_key) => { - validate_create_gpg_key( + EntryTypes::GpgKeyDist(gpg_key) => { + gpg_key_dist::validate_create_gpg_key_dist( EntryCreationAction::Create(action), gpg_key, ) @@ -61,8 +70,8 @@ pub fn validate(op: Op) -> ExternResult { } OpEntry::UpdateEntry { app_entry, action, .. } => { match app_entry { - EntryTypes::GpgKey(gpg_key) => { - validate_create_gpg_key( + EntryTypes::GpgKeyDist(gpg_key) => { + gpg_key_dist::validate_create_gpg_key_dist( EntryCreationAction::Update(action), gpg_key, ) @@ -82,10 +91,10 @@ pub fn validate(op: Op) -> ExternResult { } => { match (app_entry, original_app_entry) { ( - EntryTypes::GpgKey(gpg_key), - EntryTypes::GpgKey(original_gpg_key), + EntryTypes::GpgKeyDist(gpg_key), + EntryTypes::GpgKeyDist(original_gpg_key), ) => { - validate_update_gpg_key( + gpg_key_dist::validate_update_gpg_key_dist( action, gpg_key, original_action, @@ -109,8 +118,8 @@ pub fn validate(op: Op) -> ExternResult { match delete_entry { OpDelete::Entry { original_action, original_app_entry, action } => { match original_app_entry { - EntryTypes::GpgKey(gpg_key) => { - validate_delete_gpg_key(action, original_action, gpg_key) + EntryTypes::GpgKeyDist(gpg_key) => { + gpg_key_dist::validate_delete_gpg_key_dist(action, original_action, gpg_key) } } } @@ -151,8 +160,8 @@ pub fn validate(op: Op) -> ExternResult { // Notice that doing so will cause `must_get_valid_record` for this record to return a valid record even if the `StoreEntry` validation failed OpRecord::CreateEntry { app_entry, action } => { match app_entry { - EntryTypes::GpgKey(gpg_key) => { - validate_create_gpg_key( + EntryTypes::GpgKeyDist(gpg_key) => { + gpg_key_dist::validate_create_gpg_key_dist( EntryCreationAction::Create(action), gpg_key, ) @@ -183,13 +192,13 @@ pub fn validate(op: Op) -> ExternResult { } }; match app_entry { - EntryTypes::GpgKey(gpg_key) => { - let result = validate_create_gpg_key( + EntryTypes::GpgKeyDist(gpg_key) => { + let result = gpg_key_dist::validate_create_gpg_key_dist( EntryCreationAction::Update(action.clone()), gpg_key.clone(), )?; if let ValidateCallbackResult::Valid = result { - let original_gpg_key: Option = original_record + let original_gpg_key: Option = original_record .entry() .to_app_option() .map_err(|e| wasm_error!(e))?; @@ -204,7 +213,7 @@ pub fn validate(op: Op) -> ExternResult { ); } }; - validate_update_gpg_key( + gpg_key_dist::validate_update_gpg_key_dist( action, gpg_key, original_action, @@ -271,8 +280,8 @@ pub fn validate(op: Op) -> ExternResult { } }; match original_app_entry { - EntryTypes::GpgKey(original_gpg_key) => { - validate_delete_gpg_key( + EntryTypes::GpgKeyDist(original_gpg_key) => { + gpg_key_dist::validate_delete_gpg_key_dist( action, original_action, original_gpg_key, diff --git a/flake.nix b/flake.nix index e56f4d9..94a3f90 100644 --- a/flake.nix +++ b/flake.nix @@ -30,7 +30,20 @@ packages = [ pkgs.nodejs_20 pkgs.gnupg + pkgs.pinentry ]; + + shellHook = '' + export GNUPGHOME=$(pwd)/.gnupg + + if [[ ! -d $GNUPGHOME ]]; then + gpg --list-keys --no-keyring 2>&1 > /dev/null + rm $GNUPGHOME/common.conf + echo "pinentry-program $(which pinentry)" > $GNUPGHOME/gpg-agent.conf + pkill gpg-agent + gpg-agent --daemon + fi + ''; }; }; }; diff --git a/package-lock.json b/package-lock.json index 9303468..2b2d50c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2230,6 +2230,17 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -2308,6 +2319,11 @@ "readable-stream": "^3.4.0" } }, + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3382,6 +3398,11 @@ "node": ">=8.6" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3496,6 +3517,17 @@ "fn.name": "1.x.x" } }, + "node_modules/openpgp": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/openpgp/-/openpgp-5.11.0.tgz", + "integrity": "sha512-hytHsxIPtRhuh6uAmoBUThHSwHSX3imLu7x4453T+xkVqIw49rl22MRD4KQIAQdCDoVdouejzYgcuLmMA/2OAA==", + "dependencies": { + "asn1.js": "^5.0.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/p-limit": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", @@ -3828,6 +3860,11 @@ "node": ">=10" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/semver": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", @@ -4693,6 +4730,7 @@ "@material/mwc-textfield": "^0.27.0", "@msgpack/msgpack": "^2.7.2", "@vaadin/date-time-picker": "^23.2.8", + "openpgp": "^5.11.0", "vue": "^3.2.25" }, "devDependencies": { diff --git a/package.json b/package.json index 76ac989..2940382 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,8 @@ "devDependencies": { "@holochain-playground/cli": "^0.1.1", "concurrently": "^6.2.1", - "rimraf": "^3.0.2", - "new-port-cli": "^1.0.0" + "new-port-cli": "^1.0.0", + "rimraf": "^3.0.2" }, "engines": { "npm": ">=7.0.0" diff --git a/ui/package.json b/ui/package.json index 4bfe07b..42cccc9 100644 --- a/ui/package.json +++ b/ui/package.json @@ -9,18 +9,19 @@ }, "dependencies": { "@holochain/client": "^0.17.0-dev.3", - "@material/mwc-circular-progress": "^0.27.0", "@material/mwc-button": "^0.27.0", - "@material/mwc-textfield": "^0.27.0", - "@material/mwc-textarea": "^0.27.0", "@material/mwc-checkbox": "^0.27.0", - "@material/mwc-slider": "^0.27.0", - "@material/mwc-snackbar": "^0.27.0", + "@material/mwc-circular-progress": "^0.27.0", "@material/mwc-formfield": "^0.27.0", - "@material/mwc-select": "^0.27.0", "@material/mwc-icon-button": "^0.27.0", + "@material/mwc-select": "^0.27.0", + "@material/mwc-slider": "^0.27.0", + "@material/mwc-snackbar": "^0.27.0", + "@material/mwc-textarea": "^0.27.0", + "@material/mwc-textfield": "^0.27.0", "@msgpack/msgpack": "^2.7.2", "@vaadin/date-time-picker": "^23.2.8", + "openpgp": "^5.11.0", "vue": "^3.2.25" }, "devDependencies": { diff --git a/ui/src/App.vue b/ui/src/App.vue index 3f75d05..1cd2a68 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -5,33 +5,7 @@
-

EDIT ME! Add the components of your app here.

- - Look in the ui/src/DNA/ZOME folders for UI elements that are generated with hc scaffold entry-type, hc scaffold collection and hc scaffold link-type and add them here as appropriate. - - For example, if you have scaffolded a "todos" dna, a "todos" zome, a "todo_item" entry type, and a collection called "all_todos", you might want to add an element here to create and list your todo items, with the generated ui/src/todos/todos/AllTodos.vue and ui/src/todos/todos/CreateTodo.vue elements. - - So, to use those elements here: -
    -
  1. Import the elements with: -
    -import AllTodos from './todos/todos/AllTodos.vue';
    -import CreateTodo from './todos/todos/CreateTodo.vue';
    -          
    -
  2. -
  3. Add it into the subcomponents for the `App` component: -
    -export default defineComponent({
    -  components: {
    -    // Add your subcomponents here
    -    AllTodos,
    -    CreateTodo
    -  },
    -  ...
    -            
    -
  4. -
  5. Replace this "EDIT ME!" section with <CreateTodo></CreateTodo><AllTodos></AllTodos>.
  6. -
+
@@ -41,10 +15,11 @@ import { defineComponent, computed } from 'vue'; import { AppAgentClient, AppAgentWebsocket } from '@holochain/client'; import '@material/mwc-circular-progress'; import '@material/mwc-button'; +import CreateGpgKey from './trusted/trusted/CreateGpgKey.vue'; export default defineComponent({ components: { - // Add your subcomponents here + CreateGpgKey }, data(): { client: AppAgentClient | undefined; diff --git a/ui/src/trusted/trusted/CreateGpgKey.vue b/ui/src/trusted/trusted/CreateGpgKey.vue index 3440af5..a935071 100644 --- a/ui/src/trusted/trusted/CreateGpgKey.vue +++ b/ui/src/trusted/trusted/CreateGpgKey.vue @@ -2,80 +2,125 @@
- Create Gpg Key - -
- -
- - - + Distribute GPG public key + + + + + +