first draft

This commit is contained in:
tcsenpai 2024-03-29 22:41:06 +01:00
commit 4294c61003
9 changed files with 355 additions and 0 deletions

177
.gitignore vendored Normal file
View File

@ -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

35
README.md Normal file
View File

@ -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
```

24
index.ts Normal file
View File

@ -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()

39
libs/Encryption.ts Normal file
View File

@ -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();
}

17
libs/timeGet.ts Normal file
View File

@ -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<timeKey>{
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;
}

11
libs/timeKeyHasher.ts Normal file
View File

@ -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<string> {
time.password = password;
let timeString = JSON.stringify(time)
let timeKeyHash = await sha256digest(timeString);
let timeKeyHashHex = Buffer.from(timeKeyHash).toString('hex')
return timeKeyHashHex;
}

8
libs/types/timeKey.ts Normal file
View File

@ -0,0 +1,8 @@
export default interface timeKey {
time: {
day: number;
month: number;
year: number;
}
password: string;
}

17
package.json Normal file
View File

@ -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"
}
}

27
tsconfig.json Normal file
View File

@ -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
}
}