├── README.md ├── chrome-extension ├── .babelrc ├── .gitignore ├── .vscode │ └── settings.json ├── README.md ├── final-extension │ ├── app.bundle.js │ ├── index.html │ ├── jquery.bundle.js │ ├── manifest.json │ ├── tweet_signature128.png │ ├── tweet_signature16.png │ ├── tweet_signature256.png │ ├── tweet_signature32.png │ ├── tweet_signature48.png │ └── tweet_signature64.png ├── images │ ├── extension.png │ ├── result.png │ └── settings.png ├── package.json ├── src │ ├── App.js │ ├── PrivateKeyForm.js │ ├── TwitterGPG.js │ ├── assets │ │ ├── css │ │ │ └── style.css │ │ └── img │ │ │ └── icons │ │ │ ├── tweet_signature128.png │ │ │ ├── tweet_signature16.png │ │ │ ├── tweet_signature256.png │ │ │ ├── tweet_signature32.png │ │ │ ├── tweet_signature48.png │ │ │ └── tweet_signature64.png │ ├── index.html │ └── manifest.json ├── webpack.config.js └── yarn.lock ├── cli ├── bin │ └── tweet ├── package-lock.json └── package.json ├── docs ├── README.md ├── _config.yml ├── index.html ├── javascripts │ └── scale.fix.js ├── params.json └── stylesheets │ ├── github-light.css │ └── styles.css └── website ├── README.md ├── index.html └── static └── bcrypto.bundle.js /README.md: -------------------------------------------------------------------------------- 1 | Tweet Signer: 2 | ============= 3 | 4 | What is it? 5 | ------------ 6 | 7 | A way for you to cryptographically sign tweets, and for others to verify it. 8 | 9 | Signed and verified Tweets ✌ 10 | 11 | Demo: 12 | ---- 13 | 14 | Open: https://nitter.tuxcanfly.me/ 15 | 16 | Paste url: https://twitter.com/shabda/status/1284468335090954242 17 | 18 | Click Fetch. Wait until Public Key, Tweet and Signature fields are auto-populated. 19 | 20 | Click Verify. Should show :✅ 21 | 22 | Video: https://youtu.be/ZbhptAmG__Q 23 | 24 | Spec: 25 | ----- 26 | 27 | * Add a link to your profile bio ending with '.keys' which should be a valid 28 | public key. 29 | 30 | * In case .keys contains multiple keys, the first key will be used. 31 | 32 | * A tweet which needs verification must contain exactly one image attachment 33 | which should be the signature of the tweet as a QR code. 34 | 35 | * The signature must be verifiable against the tweet using the public key from 36 | the profile. 37 | 38 | Why does this exist? 39 | ---------------------- 40 | 41 | "Not Your Keys, Not Your Coin" is a oft-repeated adage. 42 | Exchanges get hacked - but nobody thought Twitter would get hacked, until they did. 43 | 44 | You need to be able to sign your tweets, so even if Twitter gets hacked again, people can verify that the tweet did not come from you. 45 | 46 | How does it work? 47 | -------------------- 48 | 49 | - You install our chrome extension. 50 | - You store your private key in the chrome extension. The private key is only stored locally and never leaves your computer. 51 | - You type your tweet in the extension 52 | - The extension generates the signature and the QR code for the signature 53 | - You download the QR code 54 | - You attach the QR code to the tweet. Your tweet is now signed. Anyone can verify the sanctity of your tweet. Even if posting via twitter gets compromised, nobody can post a signed tweet on your behalf. 55 | 56 | 57 | 58 | Loading the chrome extension 59 | ------------------------------- 60 | 61 | In the `chrome-extension` directory look for the folder => `final-extension`. Follow the steps to below to add the extension to chrome. 62 | 63 | 64 | 1. Open chrome://extensions/ in the chrome browser 65 | 66 | 67 | 2. Enable developer mode by button on the top right 68 | 69 | ![](https://lh4.googleusercontent.com/bRQJjstXpYmFXy_mna363Id00Pz8LJ6dDQCebJvJ990v_3WWcEifkCfsQ2HUxKZHM9G5hpmN--ZkqZ3XNDZ12IRYzHt0ClVEHaY3xOxkpRZF5pLpRgE9_R4iSHrrQrOEwCPIKa6V "Developer Mode") 70 | 71 | 72 | 3. Click on load unpack button on top left and select the `final-extension` folder 73 | 74 | ![](https://lh6.googleusercontent.com/-fBaT9aWtboCKa70SRuejDkLF-QxAsNRmOklhRaeMGtuVchCBX33pZ5KbiZr09t0xU7oNuWMzwp-eTnBfwSqcWTJG8S30FgzR8_MGMZMve77jmwlYRYoO3wEpXzWv8amInT5QYpT "Load unpacked") 75 | 76 | 77 | 4. It will load our chrome extension like below 78 | 79 | ![](chrome-extension/images/extension.png) 80 | 81 | 5. Enter a private key in settings to get started 82 | 83 | ![](chrome-extension/images/settings.png) 84 | 85 | 6. Type in the tweet to generate QR Code 86 | 87 | ![](chrome-extension/images/result.png) 88 | 89 | 90 | 91 | Usage: 92 | ------------- 93 | 94 | - Install the chrome extension 95 | - Add a private key to the extension 96 | - Host the public key somewhere and add public key url to your profile 97 | - Generate the signature QR code for your tweet and attach it to the tweet 98 | 99 | To verify 100 | ------------- 101 | 102 | - Go to https://nitter.tuxcanfly.me/ 103 | - Paste the url of a tweet you want to verify 104 | - If the tweet is signed, aka has the QR code, it is checked against the tweet contents. If the signature matches, the tweet is marked as verified. 105 | 106 | 107 | LICENSE: 108 | ------------ 109 | 110 | MIT 111 | -------------------------------------------------------------------------------- /chrome-extension/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-react"] 3 | } 4 | -------------------------------------------------------------------------------- /chrome-extension/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/* 3 | node_modules/* 4 | dist/ -------------------------------------------------------------------------------- /chrome-extension/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "standard.autoFixOnSave": true 3 | } -------------------------------------------------------------------------------- /chrome-extension/README.md: -------------------------------------------------------------------------------- 1 | # Tweet Signer Extension for Google Chrome 2 | ![logo](src/assets/img/icons/tweet_signature32.png) 3 | 4 | # Running the extension (Method 1): 5 | In the `chrome-extension` directory look for the folder => `final-extension`. Follow the steps to [add the extension](#add-extension-to-chrome) to chrome. 6 | 7 | # Runing Extension (Method 2) 8 | 1. cd into your extension directory 9 | 1. Install dependencies using yarn, i.e. `yarn install` 10 | 2. run `yarn run watch` 11 | 3. webpack will create `dist` directory inside extension directory with the bundled code and keep watching for code changes 12 | 13 | # Add extension to chrome 14 | 15 | To test the extension 16 | 17 | 1. Open chrome://extensions/ in the chrome browser 18 | 19 | 20 | 2. Enable developer mode by button on the top right 21 | 22 | ![](https://lh4.googleusercontent.com/bRQJjstXpYmFXy_mna363Id00Pz8LJ6dDQCebJvJ990v_3WWcEifkCfsQ2HUxKZHM9G5hpmN--ZkqZ3XNDZ12IRYzHt0ClVEHaY3xOxkpRZF5pLpRgE9_R4iSHrrQrOEwCPIKa6V "Developer Mode") 23 | 24 | 25 | 3. Click on load unpack button on top left and select the `final-extension` folder 26 | 27 | ![](https://lh6.googleusercontent.com/-fBaT9aWtboCKa70SRuejDkLF-QxAsNRmOklhRaeMGtuVchCBX33pZ5KbiZr09t0xU7oNuWMzwp-eTnBfwSqcWTJG8S30FgzR8_MGMZMve77jmwlYRYoO3wEpXzWv8amInT5QYpT "Load unpacked") 28 | 29 | 30 | 4. It will load our chrome extension like below 31 | 32 | ![](images/extension.png) 33 | 34 | 5. Enter a private key in settings to get started 35 | 36 | ![](images/settings.png) 37 | 38 | 6. Type in the tweet to generate QR Code 39 | 40 | ![](images/result.png) 41 | 42 | # Project Structure 43 | 44 | * `src` folder contains everything other than boilerplate code 45 | * `assets` folder contains static assets like css, js files, images and icons 46 | * `index.html` is the default_popup of chrome extension 47 | * `App.js` contains the root component, It renders the TodoList component 48 | * `TwitterGPG.js` contains the TwitterGPG component with all the logic for generating a QRCode from the GPG signature of the tweet 49 | -------------------------------------------------------------------------------- /chrome-extension/final-extension/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /chrome-extension/final-extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Tweet Signer", 3 | "version": "1.0", 4 | "description": "Chrome extension to sign tweets", 5 | "permissions": ["storage"], 6 | "browser_action": { 7 | "default_title": "Tweet Signer", 8 | "default_popup": "index.html", 9 | "default_icon": { 10 | "16": "tweet_signature16.png", 11 | "32": "tweet_signature32.png", 12 | "48": "tweet_signature48.png", 13 | "64": "tweet_signature64.png", 14 | "128": "tweet_signature128.png", 15 | "256": "tweet_signature256.png" 16 | } 17 | }, 18 | "icons": { 19 | "16": "tweet_signature16.png", 20 | "32": "tweet_signature32.png", 21 | "48": "tweet_signature48.png", 22 | "64": "tweet_signature64.png", 23 | "128": "tweet_signature128.png", 24 | "256": "tweet_signature256.png" 25 | }, 26 | "manifest_version": 2 27 | } 28 | -------------------------------------------------------------------------------- /chrome-extension/final-extension/tweet_signature128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shabda/tweet-signer/235f62394f2ea3ab53eed45a526281f7bab13edb/chrome-extension/final-extension/tweet_signature128.png -------------------------------------------------------------------------------- /chrome-extension/final-extension/tweet_signature16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shabda/tweet-signer/235f62394f2ea3ab53eed45a526281f7bab13edb/chrome-extension/final-extension/tweet_signature16.png -------------------------------------------------------------------------------- /chrome-extension/final-extension/tweet_signature256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shabda/tweet-signer/235f62394f2ea3ab53eed45a526281f7bab13edb/chrome-extension/final-extension/tweet_signature256.png -------------------------------------------------------------------------------- /chrome-extension/final-extension/tweet_signature32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shabda/tweet-signer/235f62394f2ea3ab53eed45a526281f7bab13edb/chrome-extension/final-extension/tweet_signature32.png -------------------------------------------------------------------------------- /chrome-extension/final-extension/tweet_signature48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shabda/tweet-signer/235f62394f2ea3ab53eed45a526281f7bab13edb/chrome-extension/final-extension/tweet_signature48.png -------------------------------------------------------------------------------- /chrome-extension/final-extension/tweet_signature64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shabda/tweet-signer/235f62394f2ea3ab53eed45a526281f7bab13edb/chrome-extension/final-extension/tweet_signature64.png -------------------------------------------------------------------------------- /chrome-extension/images/extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shabda/tweet-signer/235f62394f2ea3ab53eed45a526281f7bab13edb/chrome-extension/images/extension.png -------------------------------------------------------------------------------- /chrome-extension/images/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shabda/tweet-signer/235f62394f2ea3ab53eed45a526281f7bab13edb/chrome-extension/images/result.png -------------------------------------------------------------------------------- /chrome-extension/images/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shabda/tweet-signer/235f62394f2ea3ab53eed45a526281f7bab13edb/chrome-extension/images/settings.png -------------------------------------------------------------------------------- /chrome-extension/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample_ext", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "standard": { 7 | "parser": "babel-eslint" 8 | }, 9 | "scripts": { 10 | "build": "webpack --mode production", 11 | "watch": "webpack --watch", 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "devDependencies": { 18 | "@babel/core": "^7.10.5", 19 | "@babel/preset-react": "^7.10.4", 20 | "babel-loader": "^8.1.0", 21 | "clean-webpack-plugin": "^3.0.0", 22 | "copy-webpack-plugin": "^5.1.1", 23 | "css-loader": "^3.6.0", 24 | "file-loader": "^4.3.0", 25 | "html-webpack-plugin": "^3.2.0", 26 | "style-loader": "^0.23.1", 27 | "webpack": "^4.43.0", 28 | "webpack-cli": "^3.3.12", 29 | "webpack-dev-server": "^3.11.0" 30 | }, 31 | "dependencies": { 32 | "@fortawesome/fontawesome-svg-core": "^1.2.30", 33 | "@fortawesome/free-solid-svg-icons": "^5.14.0", 34 | "@fortawesome/react-fontawesome": "^0.1.11", 35 | "babel": "^6.23.0", 36 | "babel-eslint": "^10.1.0", 37 | "bcrypto": "tuxcanfly/bcrypto#verify", 38 | "bootstrap": "^4.5.0", 39 | "eslint": "^7.4.0", 40 | "jquery": "^3.5.1", 41 | "node-rsa": "^1.0.8", 42 | "popper.js": "^1.16.1", 43 | "qrcode": "^1.4.4", 44 | "qrcode.react": "^1.0.0", 45 | "react": "^16.13.1", 46 | "react-bootstrap": "^1.2.2", 47 | "react-dom": "^16.13.1" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /chrome-extension/src/App.js: -------------------------------------------------------------------------------- 1 | // Default imports 2 | import ReactDOM from 'react-dom' 3 | import React from 'react' 4 | 5 | // Custom imports 6 | import TwitterGPG from './TwitterGPG' 7 | import 'jquery/dist/jquery.min' 8 | import 'bootstrap/dist/js/bootstrap.min' 9 | import './assets/css/style.css' 10 | 11 | function App () { 12 | return ( 13 | 14 | ) 15 | } 16 | 17 | ReactDOM.render( 18 | , 19 | document.getElementById('app') 20 | ) 21 | -------------------------------------------------------------------------------- /chrome-extension/src/PrivateKeyForm.js: -------------------------------------------------------------------------------- 1 | // Default imports 2 | import React, { useState, useEffect } from 'react' 3 | import { InputGroup, FormControl, Button } from 'react-bootstrap' 4 | 5 | // Custom imports 6 | // None 7 | 8 | const PrivateKeyForm = (props) => { 9 | const [state, setState] = useState({ privateKey: '' }) 10 | 11 | useEffect(() => { 12 | chrome.storage.local.get(['privateKey'], (result) => { 13 | const privateKey = result.privateKey || '' 14 | setState({ ...state, privateKey }) 15 | }) 16 | }, [chrome, setState]) 17 | 18 | function handleOnClick () { 19 | const privateKey = state.privateKey 20 | chrome.storage.local.set({ privateKey }) 21 | props.rerenderParentCallback() 22 | setState({ privateKey: '' }) 23 | } 24 | 25 | return ( 26 |
27 |

Settings

28 |
29 | Please enter private key 30 |
31 | 32 |
33 | 34 | setState({ privateKey: e.target.value })} 41 | /> 42 | 43 | 44 | 45 | 46 |
47 |
48 | ) 49 | } 50 | 51 | export default PrivateKeyForm 52 | -------------------------------------------------------------------------------- /chrome-extension/src/TwitterGPG.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { InputGroup, FormControl, Button } from 'react-bootstrap' 3 | import QRCode from 'qrcode.react' 4 | import bcrypto from 'bcrypto' 5 | import PrivateKeyForm from './PrivateKeyForm' 6 | import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' 7 | import { faCogs } from '@fortawesome/free-solid-svg-icons' 8 | 9 | function makeid(length) { 10 | var result = ''; 11 | var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 12 | var charactersLength = characters.length; 13 | for ( var i = 0; i < length; i++ ) { 14 | result += characters.charAt(Math.floor(Math.random() * charactersLength)); 15 | } 16 | return result; 17 | } 18 | 19 | class TwitterGPG extends Component { 20 | constructor (props) { 21 | super(props) 22 | this.state = { 23 | privateKey: '', 24 | tweetMessage: '', 25 | signature: '' 26 | } 27 | this.rerenderParentCallback = this.rerenderParentCallback.bind(this) 28 | } 29 | 30 | componentDidMount () { 31 | chrome.storage.local.get(['privateKey'], (result) => { 32 | const privateKey = result.privateKey || '' 33 | this.setState({ ...this.state, privateKey }) 34 | }) 35 | } 36 | 37 | handleOnClick () { 38 | if (!this.state.tweetMessage) { return } 39 | this.setState((prevState) => { 40 | const responseSignature = this.signTweet(prevState.tweetMessage) 41 | return { ...this.state, signature: responseSignature } 42 | }) 43 | } 44 | 45 | signTweet (data) { 46 | return bcrypto.browser.sign(data, this.state.privateKey); 47 | } 48 | 49 | downloadQR() { 50 | var filename = 'qr' + makeid(5) + '.png' 51 | var canvas = document.getElementsByTagName('canvas')[0]; 52 | 53 | /// create an "off-screen" anchor tag 54 | var lnk = document.createElement('a'), e; 55 | 56 | /// the key here is to set the download attribute of the a tag 57 | lnk.download = filename; 58 | 59 | /// convert canvas content to data-uri for link. When download 60 | /// attribute is set the content pointed to by link will be 61 | /// pushed as "download" in HTML5 capable browsers 62 | lnk.href = canvas.toDataURL("image/png;base64"); 63 | 64 | /// create a "fake" click-event to trigger the download 65 | if (document.createEvent) { 66 | e = document.createEvent("MouseEvents"); 67 | e.initMouseEvent("click", true, true, window, 68 | 0, 0, 0, 0, 0, false, false, false, 69 | false, 0, null); 70 | 71 | lnk.dispatchEvent(e); 72 | } else if (lnk.fireEvent) { 73 | lnk.fireEvent("onclick"); 74 | } 75 | } 76 | 77 | rerenderParentCallback () { 78 | chrome.storage.local.get(['privateKey'], (result) => { 79 | const privateKey = result.privateKey || '' 80 | this.setState({ ...this.state, signature: '', privateKey }) 81 | }) 82 | } 83 | 84 | renderPrivateKeyPage () { 85 | return ( 86 | 87 | ) 88 | } 89 | 90 | renderTweetPage () { 91 | return ( 92 |
93 |
94 |
95 |

Tweet Signer

96 | 99 |
100 |
101 |
102 | {this.state.signature === '' ?
Welcome, please type a tweet to get started!
: } 103 |
104 | 105 |
106 | 107 | this.setState({ tweetMessage: e.target.value })} 114 | /> 115 |    116 | 117 | 118 |    119 | 120 | 121 | 122 |
123 |
124 | ) 125 | } 126 | 127 | render () { 128 | return ( 129 | <> 130 | {this.state.privateKey === '' ? this.renderPrivateKeyPage() : this.renderTweetPage()} 131 | 132 | ) 133 | } 134 | } 135 | 136 | export default TwitterGPG 137 | -------------------------------------------------------------------------------- /chrome-extension/src/assets/css/style.css: -------------------------------------------------------------------------------- 1 | @import "~bootstrap/dist/css/bootstrap.min.css"; 2 | .todo-list-container{ 3 | min-width: 500px; 4 | } 5 | .todo-list{ 6 | max-height: 400px; 7 | overflow-y: scroll; 8 | } 9 | .card-header{ 10 | background-color: rgba(255, 255, 255, 0.21); 11 | border-bottom: 2px solid #3c8dbc; 12 | } 13 | 14 | .card-title{ 15 | display: inline-block; 16 | font-size: 18px; 17 | margin: 0; 18 | line-height: 1 19 | } 20 | -------------------------------------------------------------------------------- /chrome-extension/src/assets/img/icons/tweet_signature128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shabda/tweet-signer/235f62394f2ea3ab53eed45a526281f7bab13edb/chrome-extension/src/assets/img/icons/tweet_signature128.png -------------------------------------------------------------------------------- /chrome-extension/src/assets/img/icons/tweet_signature16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shabda/tweet-signer/235f62394f2ea3ab53eed45a526281f7bab13edb/chrome-extension/src/assets/img/icons/tweet_signature16.png -------------------------------------------------------------------------------- /chrome-extension/src/assets/img/icons/tweet_signature256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shabda/tweet-signer/235f62394f2ea3ab53eed45a526281f7bab13edb/chrome-extension/src/assets/img/icons/tweet_signature256.png -------------------------------------------------------------------------------- /chrome-extension/src/assets/img/icons/tweet_signature32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shabda/tweet-signer/235f62394f2ea3ab53eed45a526281f7bab13edb/chrome-extension/src/assets/img/icons/tweet_signature32.png -------------------------------------------------------------------------------- /chrome-extension/src/assets/img/icons/tweet_signature48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shabda/tweet-signer/235f62394f2ea3ab53eed45a526281f7bab13edb/chrome-extension/src/assets/img/icons/tweet_signature48.png -------------------------------------------------------------------------------- /chrome-extension/src/assets/img/icons/tweet_signature64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shabda/tweet-signer/235f62394f2ea3ab53eed45a526281f7bab13edb/chrome-extension/src/assets/img/icons/tweet_signature64.png -------------------------------------------------------------------------------- /chrome-extension/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /chrome-extension/src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Tweet Signer", 3 | "version": "1.0", 4 | "description": "Chrome extension to sign tweets", 5 | "permissions": ["storage"], 6 | "browser_action": { 7 | "default_title": "Tweet Signer", 8 | "default_popup": "index.html", 9 | "default_icon": { 10 | "16": "tweet_signature16.png", 11 | "32": "tweet_signature32.png", 12 | "48": "tweet_signature48.png", 13 | "64": "tweet_signature64.png", 14 | "128": "tweet_signature128.png", 15 | "256": "tweet_signature256.png" 16 | } 17 | }, 18 | "icons": { 19 | "16": "tweet_signature16.png", 20 | "32": "tweet_signature32.png", 21 | "48": "tweet_signature48.png", 22 | "64": "tweet_signature64.png", 23 | "128": "tweet_signature128.png", 24 | "256": "tweet_signature256.png" 25 | }, 26 | "manifest_version": 2 27 | } 28 | -------------------------------------------------------------------------------- /chrome-extension/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const HtmlWebpackPlugin = require('html-webpack-plugin') 3 | const { CleanWebpackPlugin } = require('clean-webpack-plugin') 4 | const CopyWebpackPlugin = require('copy-webpack-plugin') 5 | 6 | module.exports = { 7 | mode: 'development', 8 | entry: { 9 | jquery: 'jquery', 10 | app: './src/App.js' 11 | }, 12 | devtool: 'inline-source-map', 13 | plugins: [ 14 | new CleanWebpackPlugin(), 15 | new HtmlWebpackPlugin({ 16 | title: 'Output Management', 17 | template: './src/index.html' 18 | }), 19 | new CopyWebpackPlugin([ 20 | { from: './src/manifest.json', to: './', flatten: true }, 21 | { from: './src/assets/img/icons/*', to: './', flatten: true } 22 | ], { 23 | copyUnmodified: true 24 | }) 25 | ], 26 | output: { 27 | filename: '[name].bundle.js', 28 | path: path.resolve(__dirname, 'dist') 29 | }, 30 | module: { 31 | rules: [ 32 | { 33 | test: /\.js$/, 34 | include: path.resolve(__dirname, 'src'), 35 | loader: 'babel-loader' 36 | }, 37 | { 38 | test: /\.css$/, 39 | include: path.resolve(__dirname, 'src'), 40 | use: [ 41 | 'style-loader', 42 | 'css-loader' 43 | ] 44 | }, 45 | { 46 | test: /\.(png|svg|jpg|gif)$/, 47 | include: path.resolve(__dirname, 'src'), 48 | use: [ 49 | 'file-loader' 50 | ] 51 | }, 52 | { 53 | test: /\.(woff|woff2|eot|ttf|otf)$/, 54 | include: path.resolve(__dirname, 'src'), 55 | use: ['file-loader'] 56 | } 57 | ] 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /cli/bin/tweet: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | const fs = require('fs'); 6 | const open = require('open'); 7 | const qrcode = require('qrcode'); 8 | const prompt = require('prompt'); 9 | 10 | const rsa = require('bcrypto/lib/rsa'); 11 | const ssh = require('bcrypto/lib/ssh'); 12 | const SHA256 = require('bcrypto/lib/sha256'); 13 | 14 | function sign(msg, privkey) { 15 | const hash = SHA256.digest(Buffer.from(msg)) 16 | const key = ssh.SSHPrivateKey.fromString(privkey); 17 | const pk = rsa.privateKeyImport(key); 18 | return rsa.sign('SHA256', hash, pk).toString('hex'); 19 | } 20 | 21 | let tweet, privkey; 22 | prompt.start(); 23 | prompt.get(['tweet', 'privkey'], async function (err, result) { 24 | if (err) { return 1; } 25 | tweet = result.tweet; 26 | privkey = result.privkey; 27 | 28 | const signature = sign(tweet, fs.readFileSync(privkey).toString()); 29 | 30 | const qr = await qrcode.toDataURL(signature); 31 | open(qr); 32 | }); 33 | -------------------------------------------------------------------------------- /cli/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tweet-signer", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-regex": { 8 | "version": "4.1.0", 9 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 10 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" 11 | }, 12 | "ansi-styles": { 13 | "version": "3.2.1", 14 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 15 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 16 | "requires": { 17 | "color-convert": "^1.9.0" 18 | } 19 | }, 20 | "async": { 21 | "version": "0.9.2", 22 | "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", 23 | "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" 24 | }, 25 | "balanced-match": { 26 | "version": "1.0.0", 27 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 28 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 29 | }, 30 | "base64-js": { 31 | "version": "1.3.1", 32 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", 33 | "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" 34 | }, 35 | "bcrypto": { 36 | "version": "5.2.0", 37 | "resolved": "https://registry.npmjs.org/bcrypto/-/bcrypto-5.2.0.tgz", 38 | "integrity": "sha512-yy+kDrUG6aXP7NIYq7kKIwlrXtx/51488IGfuqhyM6FYF8zNI1mPRwvAPvQ1RfE5e7WW7fVdZt4yHlmN4HJ4Hg==", 39 | "requires": { 40 | "bufio": "~1.0.7", 41 | "loady": "~0.0.1" 42 | } 43 | }, 44 | "brace-expansion": { 45 | "version": "1.1.11", 46 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 47 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 48 | "requires": { 49 | "balanced-match": "^1.0.0", 50 | "concat-map": "0.0.1" 51 | } 52 | }, 53 | "buffer": { 54 | "version": "5.6.0", 55 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", 56 | "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", 57 | "requires": { 58 | "base64-js": "^1.0.2", 59 | "ieee754": "^1.1.4" 60 | } 61 | }, 62 | "buffer-alloc": { 63 | "version": "1.2.0", 64 | "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", 65 | "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", 66 | "requires": { 67 | "buffer-alloc-unsafe": "^1.1.0", 68 | "buffer-fill": "^1.0.0" 69 | } 70 | }, 71 | "buffer-alloc-unsafe": { 72 | "version": "1.1.0", 73 | "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", 74 | "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" 75 | }, 76 | "buffer-fill": { 77 | "version": "1.0.0", 78 | "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", 79 | "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" 80 | }, 81 | "buffer-from": { 82 | "version": "1.1.1", 83 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 84 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" 85 | }, 86 | "bufio": { 87 | "version": "1.0.7", 88 | "resolved": "https://registry.npmjs.org/bufio/-/bufio-1.0.7.tgz", 89 | "integrity": "sha512-bd1dDQhiC+bEbEfg56IdBv7faWa6OipMs/AFFFvtFnB3wAYjlwQpQRZ0pm6ZkgtfL0pILRXhKxOiQj6UzoMR7A==" 90 | }, 91 | "camelcase": { 92 | "version": "5.3.1", 93 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 94 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" 95 | }, 96 | "cliui": { 97 | "version": "5.0.0", 98 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", 99 | "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", 100 | "requires": { 101 | "string-width": "^3.1.0", 102 | "strip-ansi": "^5.2.0", 103 | "wrap-ansi": "^5.1.0" 104 | } 105 | }, 106 | "color-convert": { 107 | "version": "1.9.3", 108 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 109 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 110 | "requires": { 111 | "color-name": "1.1.3" 112 | } 113 | }, 114 | "color-name": { 115 | "version": "1.1.3", 116 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 117 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 118 | }, 119 | "colors": { 120 | "version": "1.4.0", 121 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", 122 | "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" 123 | }, 124 | "concat-map": { 125 | "version": "0.0.1", 126 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 127 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 128 | }, 129 | "cycle": { 130 | "version": "1.0.3", 131 | "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", 132 | "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" 133 | }, 134 | "decamelize": { 135 | "version": "1.2.0", 136 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 137 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 138 | }, 139 | "deep-equal": { 140 | "version": "0.2.2", 141 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.2.2.tgz", 142 | "integrity": "sha1-hLdFiW80xoTpjyzg5Cq69Du6AX0=" 143 | }, 144 | "dijkstrajs": { 145 | "version": "1.0.1", 146 | "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.1.tgz", 147 | "integrity": "sha1-082BIh4+pAdCz83lVtTpnpjdxxs=" 148 | }, 149 | "emoji-regex": { 150 | "version": "7.0.3", 151 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 152 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" 153 | }, 154 | "eyes": { 155 | "version": "0.1.8", 156 | "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", 157 | "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" 158 | }, 159 | "find-up": { 160 | "version": "3.0.0", 161 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 162 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 163 | "requires": { 164 | "locate-path": "^3.0.0" 165 | } 166 | }, 167 | "fs.realpath": { 168 | "version": "1.0.0", 169 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 170 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 171 | }, 172 | "get-caller-file": { 173 | "version": "2.0.5", 174 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 175 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" 176 | }, 177 | "glob": { 178 | "version": "7.1.6", 179 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 180 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 181 | "requires": { 182 | "fs.realpath": "^1.0.0", 183 | "inflight": "^1.0.4", 184 | "inherits": "2", 185 | "minimatch": "^3.0.4", 186 | "once": "^1.3.0", 187 | "path-is-absolute": "^1.0.0" 188 | } 189 | }, 190 | "i": { 191 | "version": "0.3.6", 192 | "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz", 193 | "integrity": "sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0=" 194 | }, 195 | "ieee754": { 196 | "version": "1.1.13", 197 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 198 | "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" 199 | }, 200 | "inflight": { 201 | "version": "1.0.6", 202 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 203 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 204 | "requires": { 205 | "once": "^1.3.0", 206 | "wrappy": "1" 207 | } 208 | }, 209 | "inherits": { 210 | "version": "2.0.4", 211 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 212 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 213 | }, 214 | "is-docker": { 215 | "version": "2.0.0", 216 | "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", 217 | "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==" 218 | }, 219 | "is-fullwidth-code-point": { 220 | "version": "2.0.0", 221 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 222 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 223 | }, 224 | "is-wsl": { 225 | "version": "2.2.0", 226 | "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", 227 | "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", 228 | "requires": { 229 | "is-docker": "^2.0.0" 230 | } 231 | }, 232 | "isarray": { 233 | "version": "2.0.5", 234 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", 235 | "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" 236 | }, 237 | "isstream": { 238 | "version": "0.1.2", 239 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 240 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 241 | }, 242 | "loady": { 243 | "version": "0.0.1", 244 | "resolved": "https://registry.npmjs.org/loady/-/loady-0.0.1.tgz", 245 | "integrity": "sha512-PW5Z13Jd0v6ZcA1P6ZVUc3EV8BJwQuAiwUvvT6VQGHoaZ1d/tu7r1QZctuKfQqwy9SFBWeAGfcIdLxhp7ZW3Rw==" 246 | }, 247 | "locate-path": { 248 | "version": "3.0.0", 249 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 250 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 251 | "requires": { 252 | "p-locate": "^3.0.0", 253 | "path-exists": "^3.0.0" 254 | } 255 | }, 256 | "minimatch": { 257 | "version": "3.0.4", 258 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 259 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 260 | "requires": { 261 | "brace-expansion": "^1.1.7" 262 | } 263 | }, 264 | "minimist": { 265 | "version": "1.2.5", 266 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 267 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" 268 | }, 269 | "mkdirp": { 270 | "version": "0.5.5", 271 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 272 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 273 | "requires": { 274 | "minimist": "^1.2.5" 275 | } 276 | }, 277 | "mute-stream": { 278 | "version": "0.0.8", 279 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", 280 | "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" 281 | }, 282 | "ncp": { 283 | "version": "1.0.1", 284 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", 285 | "integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=" 286 | }, 287 | "once": { 288 | "version": "1.4.0", 289 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 290 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 291 | "requires": { 292 | "wrappy": "1" 293 | } 294 | }, 295 | "open": { 296 | "version": "7.0.4", 297 | "resolved": "https://registry.npmjs.org/open/-/open-7.0.4.tgz", 298 | "integrity": "sha512-brSA+/yq+b08Hsr4c8fsEW2CRzk1BmfN3SAK/5VCHQ9bdoZJ4qa/+AfR0xHjlbbZUyPkUHs1b8x1RqdyZdkVqQ==", 299 | "requires": { 300 | "is-docker": "^2.0.0", 301 | "is-wsl": "^2.1.1" 302 | } 303 | }, 304 | "p-limit": { 305 | "version": "2.3.0", 306 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 307 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 308 | "requires": { 309 | "p-try": "^2.0.0" 310 | } 311 | }, 312 | "p-locate": { 313 | "version": "3.0.0", 314 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 315 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 316 | "requires": { 317 | "p-limit": "^2.0.0" 318 | } 319 | }, 320 | "p-try": { 321 | "version": "2.2.0", 322 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 323 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" 324 | }, 325 | "path-exists": { 326 | "version": "3.0.0", 327 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 328 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" 329 | }, 330 | "path-is-absolute": { 331 | "version": "1.0.1", 332 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 333 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 334 | }, 335 | "pkginfo": { 336 | "version": "0.4.1", 337 | "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz", 338 | "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=" 339 | }, 340 | "pngjs": { 341 | "version": "3.4.0", 342 | "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", 343 | "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==" 344 | }, 345 | "prompt": { 346 | "version": "1.0.0", 347 | "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.0.0.tgz", 348 | "integrity": "sha1-jlcSPDlquYiJf7Mn/Trtw+c15P4=", 349 | "requires": { 350 | "colors": "^1.1.2", 351 | "pkginfo": "0.x.x", 352 | "read": "1.0.x", 353 | "revalidator": "0.1.x", 354 | "utile": "0.3.x", 355 | "winston": "2.1.x" 356 | } 357 | }, 358 | "qrcode": { 359 | "version": "1.4.4", 360 | "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.4.4.tgz", 361 | "integrity": "sha512-oLzEC5+NKFou9P0bMj5+v6Z40evexeE29Z9cummZXZ9QXyMr3lphkURzxjXgPJC5azpxcshoDWV1xE46z+/c3Q==", 362 | "requires": { 363 | "buffer": "^5.4.3", 364 | "buffer-alloc": "^1.2.0", 365 | "buffer-from": "^1.1.1", 366 | "dijkstrajs": "^1.0.1", 367 | "isarray": "^2.0.1", 368 | "pngjs": "^3.3.0", 369 | "yargs": "^13.2.4" 370 | } 371 | }, 372 | "read": { 373 | "version": "1.0.7", 374 | "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", 375 | "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", 376 | "requires": { 377 | "mute-stream": "~0.0.4" 378 | } 379 | }, 380 | "require-directory": { 381 | "version": "2.1.1", 382 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 383 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" 384 | }, 385 | "require-main-filename": { 386 | "version": "2.0.0", 387 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 388 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" 389 | }, 390 | "revalidator": { 391 | "version": "0.1.8", 392 | "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", 393 | "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs=" 394 | }, 395 | "rimraf": { 396 | "version": "2.7.1", 397 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", 398 | "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 399 | "requires": { 400 | "glob": "^7.1.3" 401 | } 402 | }, 403 | "set-blocking": { 404 | "version": "2.0.0", 405 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 406 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 407 | }, 408 | "stack-trace": { 409 | "version": "0.0.10", 410 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 411 | "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" 412 | }, 413 | "string-width": { 414 | "version": "3.1.0", 415 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 416 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 417 | "requires": { 418 | "emoji-regex": "^7.0.1", 419 | "is-fullwidth-code-point": "^2.0.0", 420 | "strip-ansi": "^5.1.0" 421 | } 422 | }, 423 | "strip-ansi": { 424 | "version": "5.2.0", 425 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 426 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 427 | "requires": { 428 | "ansi-regex": "^4.1.0" 429 | } 430 | }, 431 | "utile": { 432 | "version": "0.3.0", 433 | "resolved": "https://registry.npmjs.org/utile/-/utile-0.3.0.tgz", 434 | "integrity": "sha1-E1LDQOuCDk2N26A5pPv6oy7U7zo=", 435 | "requires": { 436 | "async": "~0.9.0", 437 | "deep-equal": "~0.2.1", 438 | "i": "0.3.x", 439 | "mkdirp": "0.x.x", 440 | "ncp": "1.0.x", 441 | "rimraf": "2.x.x" 442 | } 443 | }, 444 | "which-module": { 445 | "version": "2.0.0", 446 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 447 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" 448 | }, 449 | "winston": { 450 | "version": "2.1.1", 451 | "resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz", 452 | "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", 453 | "requires": { 454 | "async": "~1.0.0", 455 | "colors": "1.0.x", 456 | "cycle": "1.0.x", 457 | "eyes": "0.1.x", 458 | "isstream": "0.1.x", 459 | "pkginfo": "0.3.x", 460 | "stack-trace": "0.0.x" 461 | }, 462 | "dependencies": { 463 | "async": { 464 | "version": "1.0.0", 465 | "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", 466 | "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" 467 | }, 468 | "colors": { 469 | "version": "1.0.3", 470 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", 471 | "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" 472 | }, 473 | "pkginfo": { 474 | "version": "0.3.1", 475 | "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", 476 | "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=" 477 | } 478 | } 479 | }, 480 | "wrap-ansi": { 481 | "version": "5.1.0", 482 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", 483 | "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", 484 | "requires": { 485 | "ansi-styles": "^3.2.0", 486 | "string-width": "^3.0.0", 487 | "strip-ansi": "^5.0.0" 488 | } 489 | }, 490 | "wrappy": { 491 | "version": "1.0.2", 492 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 493 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 494 | }, 495 | "y18n": { 496 | "version": "4.0.0", 497 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", 498 | "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" 499 | }, 500 | "yargs": { 501 | "version": "13.3.2", 502 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", 503 | "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", 504 | "requires": { 505 | "cliui": "^5.0.0", 506 | "find-up": "^3.0.0", 507 | "get-caller-file": "^2.0.1", 508 | "require-directory": "^2.1.1", 509 | "require-main-filename": "^2.0.0", 510 | "set-blocking": "^2.0.0", 511 | "string-width": "^3.0.0", 512 | "which-module": "^2.0.0", 513 | "y18n": "^4.0.0", 514 | "yargs-parser": "^13.1.2" 515 | } 516 | }, 517 | "yargs-parser": { 518 | "version": "13.1.2", 519 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", 520 | "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", 521 | "requires": { 522 | "camelcase": "^5.0.0", 523 | "decamelize": "^1.2.0" 524 | } 525 | } 526 | } 527 | } 528 | -------------------------------------------------------------------------------- /cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tweet-signer", 3 | "version": "1.0.0", 4 | "description": "Sign and Verify Tweets", 5 | "main": "bin/tweet", 6 | "dependencies": { 7 | "bcrypto": "^5.2.0", 8 | "open": "^7.0.4", 9 | "prompt": "^1.0.0", 10 | "qrcode": "^1.4.4" 11 | }, 12 | "devDependencies": {}, 13 | "scripts": { 14 | "test": "echo \"Error: no test specified\" && exit 1" 15 | }, 16 | "author": "Javed Khan", 17 | "license": "ISC" 18 | } 19 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | Tweet Signer: 2 | ============= 3 | 4 | What is it? 5 | ------------ 6 | 7 | A way for you to cryptographically sign tweets, and for others to verify it. 8 | 9 | Signed and verified Tweets ✌ 10 | 11 | Why does this exist? 12 | ---------------------- 13 | 14 | "Not Your Keys, Not Your Coin" is a oft-repeated adage. 15 | Exchanges get hacked - but nobody thought Twitter would get hacked, until they did. 16 | 17 | You need to be abale to sign your tweets, so even if Twitter gets hacked again, people can verify that the tweet did not come from you. 18 | 19 | How does it work? 20 | -------------------- 21 | 22 | - You install our chrome extension. 23 | - You store your private key in the chrome extension. The private key is only stored locally. 24 | - You write the tweet in the chrome extension 25 | - The extension generates the signature and the QR code for the signature 26 | - You view and downloads the QR code 27 | - You attach the QR code to the tweet. Your tweet is now signed. Anyone can verify the sanctitu of your tweet. Even if twitter get compromised, nobody can tweet on your behalf. 28 | 29 | 30 | 31 | Usage: 32 | ====== 33 | 34 | Add public key to your profile. 35 | 36 | Users will see :✅: if your tweet is signed! 37 | 38 | LICENSE: 39 | ======== 40 | 41 | MIT 42 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Signed Tweets 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

Signed Tweets

19 |

“Not your keys, not your tweets.”

20 | 21 |

View the Project on GitHub shabda/tweet-signer

22 | 23 |
24 |
25 |

26 | Tweet Signer:

27 |

“Not your keys, not your tweets.”

28 |

Verified Tweets

29 |

30 | Usage:

31 |

Add public key to your profile.

32 |

Users will see :: if your tweet is signed!

33 |

34 | LICENSE:

35 |

MIT

36 |
37 |
38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /docs/javascripts/scale.fix.js: -------------------------------------------------------------------------------- 1 | var metas = document.getElementsByTagName('meta'); 2 | var i; 3 | if (navigator.userAgent.match(/iPhone/i)) { 4 | for (i=0; i 2 | 3 | 4 | 5 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |
34 |
35 |
36 | 39 | 40 |
41 |
42 | 45 |
46 |
47 |
48 | 49 |
50 |
51 |
52 | 55 | 56 |
57 | 58 |
59 | 62 | 63 |
64 | 65 | 71 | 72 |
73 | 76 | 77 |
78 | 79 |
80 | 83 |
84 | 85 |
86 | 89 | 90 |
91 | 92 |
93 |
94 | 95 | 96 | 97 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | --------------------------------------------------------------------------------