From 4294c61003721eb9e8e742086ff4ae04ad1c4c12 Mon Sep 17 00:00:00 2001 From: tcsenpai Date: Fri, 29 Mar 2024 22:41:06 +0100 Subject: [PATCH] first draft --- .gitignore | 177 ++++++++++++++++++++++++++++++++++++++++++ README.md | 35 +++++++++ index.ts | 24 ++++++ libs/Encryption.ts | 39 ++++++++++ libs/timeGet.ts | 17 ++++ libs/timeKeyHasher.ts | 11 +++ libs/types/timeKey.ts | 8 ++ package.json | 17 ++++ tsconfig.json | 27 +++++++ 9 files changed, 355 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 index.ts create mode 100644 libs/Encryption.ts create mode 100644 libs/timeGet.ts create mode 100644 libs/timeKeyHasher.ts create mode 100644 libs/types/timeKey.ts create mode 100644 package.json create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..562e051 --- /dev/null +++ b/.gitignore @@ -0,0 +1,177 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store +yarn.lock +package-lock.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..6bef889 --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# timecapsule +A for-fun digital time capsule that does not need a cloud service and can run everywhere. + +## What is this +Let's say you want to write a message to your future self, or you want to leave a funny riddle to your descendants. +Let's say you don't trust anybody other than yourself. +You can use timecapsule to achieve this! + +Basically, you can encrypt a message with a password and specify an "opening date". +As the date is hashed with your password and used to encrypt the message, that message can be opened only on that precise date (and with that specific password). + +## Disclaimer +This is a for-fun project and has no claims regarding security whatsoever. It is very simple to circumvent the mechanism by, for example, spoofing the "right date". Technically, though, if the receiver does not know the date, it should be hard to spoof it. It would make the time capsule not very useful too, though. + +See it as a funny riddle or funny game. + +## Usage + +See index.ts, the working code is there. + +## Installation + +To install dependencies: + +```bash +yarn install +``` + +## Run + +To run: + +```bash +tsx index.ts +``` diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..a6ec812 --- /dev/null +++ b/index.ts @@ -0,0 +1,24 @@ +import type timeKey from "./libs/types/timeKey" +import { timeEncrypt, timeDecrypt } from "./libs/Encryption"; + +async function main() { + // We set our secret and passwords here + let secret = "secret"; + let password = "password"; + // We want this to be opened only this day + let proposedTimeKey: timeKey = { + time: { + day: 29, + month: 3, + year: 2024, + }, + password: "" + } + // Testing + let enc = await timeEncrypt(secret, password, proposedTimeKey); + console.log(`Encrypted: ${enc}`); + let dec = await timeDecrypt(enc, password); + console.log(`Decrypted: ${dec}`); +} + +main() \ No newline at end of file diff --git a/libs/Encryption.ts b/libs/Encryption.ts new file mode 100644 index 0000000..8cb06e7 --- /dev/null +++ b/libs/Encryption.ts @@ -0,0 +1,39 @@ + +// Encryption.ts +import timeGet from "./timeGet" +import timeKeyHasher from "./timeKeyHasher" +import crypto from "crypto" +import type timeKey from "./types/timeKey"; +const algorithm = 'aes-256-ctr'; +const IV_LENGTH = 16; + +export async function timeEncrypt(text: string, plain_password: string, date: timeKey) { + console.log("To be opened on: ") + console.log(date) + // Getting the timed password and using it to encrypt the text + let password = await timeKeyHasher(date, plain_password) + // Adding a random iv + let iv = crypto.randomBytes(IV_LENGTH); + // Ciphering the plain text + let cipher = crypto.createCipheriv(algorithm, Buffer.from(password, 'hex'), iv); + let encrypted = cipher.update(text); + encrypted = Buffer.concat([encrypted, cipher.final()]); + return iv.toString('hex') + ':' + encrypted.toString('hex'); +} + +export async function timeDecrypt(text: string, plain_password: string) { + // Getting the timed password and using it to decrypt the text + let date = await timeGet() + console.log("Today is: ") + console.log(date) + let password = await timeKeyHasher(date, plain_password) + // Separating the iv + let textParts = text.split(':'); + let iv = Buffer.from(textParts.shift() as string, 'hex'); + let encryptedText = Buffer.from(textParts.join(':'), 'hex'); + // Deciphering the encrypted text + let decipher = crypto.createDecipheriv(algorithm, Buffer.from(password, 'hex'), iv); + let decrypted = decipher.update(encryptedText); + decrypted = Buffer.concat([decrypted, decipher.final()]); + return decrypted.toString(); +} diff --git a/libs/timeGet.ts b/libs/timeGet.ts new file mode 100644 index 0000000..fdbf760 --- /dev/null +++ b/libs/timeGet.ts @@ -0,0 +1,17 @@ +import type timeKey from "./types/timeKey"; +import { Client as NTP } from "ntp-time" +const ntp_client = new NTP('0.europe.pool.ntp.org', 123, { timeout: 1000 }); + +// Get time from NTP and create a timeKey without a password linked +export default async function timeGet(): Promise{ + const ntp = await ntp_client.syncTime(); + const date = new Date(ntp.time); + return { + time: { + day: date.getDate(), + month: date.getMonth() + 1, + year: date.getFullYear(), + }, + password: "", + } as timeKey; +} \ No newline at end of file diff --git a/libs/timeKeyHasher.ts b/libs/timeKeyHasher.ts new file mode 100644 index 0000000..03dec04 --- /dev/null +++ b/libs/timeKeyHasher.ts @@ -0,0 +1,11 @@ +import type timeKey from "./types/timeKey"; +import { sha256digest } from '@digitalcredentials/sha256-universal' + +// Join together a timeKey + a password in a sha256 hash that can be used to encrypt / decrypt data +export default async function timeKeyHasher(time: timeKey, password: string): Promise { + time.password = password; + let timeString = JSON.stringify(time) + let timeKeyHash = await sha256digest(timeString); + let timeKeyHashHex = Buffer.from(timeKeyHash).toString('hex') + return timeKeyHashHex; +} \ No newline at end of file diff --git a/libs/types/timeKey.ts b/libs/types/timeKey.ts new file mode 100644 index 0000000..6d5cac1 --- /dev/null +++ b/libs/types/timeKey.ts @@ -0,0 +1,8 @@ +export default interface timeKey { + time: { + day: number; + month: number; + year: number; + } + password: string; +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..aae1df3 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "timecapsule", + "module": "index.ts", + "type": "module", + "devDependencies": { + "@types/bun": "latest", + "@types/sha256-universal": "^1.2.4" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "@digitalcredentials/sha256-universal": "^1.1.1", + "ntp-time": "^2.0.4", + "tsx": "^4.7.1" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0fef23a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +}