diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a0ec7f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Added by cargo + +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..02633a7 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,346 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f628eaec48bfd21b865dc2950cfa014450c01d2fa2b69a86c2fd5844ec523c0" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gongor" +version = "0.1.0" +dependencies = [ + "curve25519-dalek", + "ed25519-dalek", + "rand", +] + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "platforms" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..573dbe8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "gongor" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +curve25519-dalek = "4.1.1" +ed25519-dalek = { version = "2.1.0", features = ["rand_core"] } +rand = "0.8.5" + + +[[bin]] +name = "gongor" +path = "gongor.rs" diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..8279d42 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,15 @@ +Gongor - A system for managing personal cryptographic identity +Copyright (C) 2023 Eli Ribble + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . diff --git a/README.md b/README.md index ab5c55b..b2ae10c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,23 @@ -# gongor +# Gongor -Tool for producing identities \ No newline at end of file +Tool for producing identities + +Anagram of "Gorgon". + +## Actions + + * `cargo run -- create eli.key eli.publickey`: makes a new key. + * `cargo run -- sign eli.key message.txt [message.sig]`: signs a message, optionally writes the signature to a file. + * `cargo run -- validate eli.publickey message.txt message.signed`: validates a signature. + +## TODO + + * Fix up the aegis generation script to ensure that I either have, or don't need, the extensions from Step CA: + * X509v3 Key Usage: critical + * Certificate Sign, CRL Sign + * X509v3 Basic Constraints: critical + * CA:TRUE, pathlen:1 + +## aegis-generate + +This generates an initial ID, known as an 'aegis'. It uses [step-ca](https://smallstep.com/docs/step-cli/the-step-command/) defaults which puts the data files in `$HOME/.step`. diff --git a/gongor.rs b/gongor.rs new file mode 100644 index 0000000..b26625f --- /dev/null +++ b/gongor.rs @@ -0,0 +1,91 @@ +use std::io::BufReader; +use std::fs::File; +use std::io::prelude::*; + +use rand::rngs::OsRng; +use ed25519_dalek::SigningKey; +use ed25519_dalek::Signature; +use ed25519_dalek::Signer; +use ed25519_dalek::Verifier; +use ed25519_dalek::VerifyingKey; +use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, SIGNATURE_LENGTH}; + + +fn main() -> std::io::Result<()>{ + let action = std::env::args().nth(1).expect("no command given"); + if action == "sign" { + let keyfilepath = std::env::args().nth(2).expect("no keyfilepath given"); + let messagefilepath = std::env::args().nth(3).expect("no message given"); + let maybe_signature_filepath = std::env::args().nth(4); + + + let mut keyfile = File::open(&keyfilepath).expect("no file found"); + let mut keybuffer: [u8; 32] = [0; SECRET_KEY_LENGTH]; + keyfile.read(&mut keybuffer).expect("buffer overflow"); + let signing_key: SigningKey = SigningKey::from_bytes(&keybuffer); + + let messagefile= File::open(&messagefilepath).expect("no file found"); + let mut messagereader = BufReader::new(messagefile); + let mut messagebuf = Vec::new(); + messagereader.read_to_end(&mut messagebuf)?; + + + + println!("Using key {keyfilepath}"); + + let signature: Signature = signing_key.sign(&messagebuf); + if maybe_signature_filepath.is_some() { + let filename = maybe_signature_filepath.expect("Not possible"); + let mut file = File::create(filename.clone())?; + println!("Writing signature to {filename}"); + file.write_all(&signature.to_bytes())?; + } else { + println!("Signture: {signature}"); + } + } else if action == "validate" { + let public_key_filepath = std::env::args().nth(2).expect("no key filepath given"); + let message_filepath = std::env::args().nth(3).expect("no message filepath given"); + let signature_filepath = std::env::args().nth(4).expect("no signature filepath given"); + + let mut public_keyfile = File::open(&public_key_filepath).expect("no file found"); + let mut public_keybuffer: [u8; PUBLIC_KEY_LENGTH] = [0; PUBLIC_KEY_LENGTH]; + public_keyfile.read(&mut public_keybuffer).expect("buffer overflow"); + let public_key: VerifyingKey = VerifyingKey::from_bytes(&public_keybuffer).expect("Faled to make a key"); + + let messagefile= File::open(&message_filepath).expect("no file found"); + let mut messagereader = BufReader::new(messagefile); + let mut messagebuf = Vec::new(); + messagereader.read_to_end(&mut messagebuf)?; + + let signature_file = File::open(&signature_filepath).expect("no file found"); + let mut signature_reader = BufReader::new(signature_file); + let mut signature_buf: [u8; SIGNATURE_LENGTH] = [0; SIGNATURE_LENGTH]; + signature_reader.read(&mut signature_buf).expect("buffer overflow"); + let signature: Signature = Signature::try_from(&signature_buf[..]).expect("not a signature"); + + if public_key.verify(&messagebuf, &signature).is_ok() { + println!("Yep, that checks out."); + } else { + println!("Invalid signature."); + } + } else if action == "create" { + let signing_key_filepath = std::env::args().nth(2).expect("no signing key file path given"); + let verifying_key_filepath = std::env::args().nth(3).expect("no verifying key file path given"); + + let mut csprng = OsRng; + let signing_key: SigningKey = SigningKey::generate(&mut csprng); + + println!("Writing new private key to {signing_key_filepath}"); + let mut file = File::create(signing_key_filepath)?; + file.write_all(&signing_key.to_bytes())?; + + println!("Writing new public key to {verifying_key_filepath}"); + let verifying_key: VerifyingKey = signing_key.verifying_key(); + file = File::create(verifying_key_filepath)?; + file.write_all(&verifying_key.to_bytes())?; + + } else { + println!("Unrecognized command {action}"); + } + return Ok(()); +} diff --git a/temp/ecc-hybrid-encryption-example.py b/temp/ecc-hybrid-encryption-example.py new file mode 100644 index 0000000..b444a1b --- /dev/null +++ b/temp/ecc-hybrid-encryption-example.py @@ -0,0 +1,57 @@ +from tinyec import registry +from Crypto.Cipher import AES +import hashlib, secrets, binascii + +def encrypt_AES_GCM(msg, secretKey): + aesCipher = AES.new(secretKey, AES.MODE_GCM) + ciphertext, authTag = aesCipher.encrypt_and_digest(msg) + return (ciphertext, aesCipher.nonce, authTag) + +def decrypt_AES_GCM(ciphertext, nonce, authTag, secretKey): + aesCipher = AES.new(secretKey, AES.MODE_GCM, nonce) + plaintext = aesCipher.decrypt_and_verify(ciphertext, authTag) + return plaintext + +def ecc_point_to_256_bit_key(point): + sha = hashlib.sha256(int.to_bytes(point.x, 32, 'big')) + sha.update(int.to_bytes(point.y, 32, 'big')) + return sha.digest() + + +def encrypt_ECC(curve, msg, pubKey): + ciphertextPrivKey = secrets.randbelow(curve.field.n) + sharedECCKey = ciphertextPrivKey * pubKey + secretKey = ecc_point_to_256_bit_key(sharedECCKey) + ciphertext, nonce, authTag = encrypt_AES_GCM(msg, secretKey) + ciphertextPubKey = ciphertextPrivKey * curve.g + return (ciphertext, nonce, authTag, ciphertextPubKey) + +def decrypt_ECC(encryptedMsg, privKey): + (ciphertext, nonce, authTag, ciphertextPubKey) = encryptedMsg + sharedECCKey = privKey * ciphertextPubKey + secretKey = ecc_point_to_256_bit_key(sharedECCKey) + plaintext = decrypt_AES_GCM(ciphertext, nonce, authTag, secretKey) + return plaintext + +def main(): + curve = registry.get_curve('brainpoolP256r1') + msg = b'Text to be encrypted by ECC public key and ' \ + b'decrypted by its corresponding ECC private key' + print("original msg:", msg) + privKey = secrets.randbelow(curve.field.n) + pubKey = privKey * curve.g + + encryptedMsg = encrypt_ECC(curve, msg, pubKey) + encryptedMsgObj = { + 'ciphertext': binascii.hexlify(encryptedMsg[0]), + 'nonce': binascii.hexlify(encryptedMsg[1]), + 'authTag': binascii.hexlify(encryptedMsg[2]), + 'ciphertextPubKey': hex(encryptedMsg[3].x) + hex(encryptedMsg[3].y % 2)[2:] + } + print("encrypted msg:", encryptedMsgObj) + + decryptedMsg = decrypt_ECC(encryptedMsg, privKey) + print("decrypted msg:", decryptedMsg) + +if __name__ == "__main__": + main()