├── .gitignore ├── web ├── discocrypto.com │ ├── static │ │ ├── .gitkeep │ │ ├── favicon.ico │ │ ├── discoblue.png │ │ ├── handshakes │ │ │ ├── Noise_XX.jpg │ │ │ └── Noise_XX.png │ │ └── github.min.css │ ├── .dockerignore │ ├── .eslintignore │ ├── build │ │ ├── logo.png │ │ ├── vue-loader.conf.js │ │ ├── build.js │ │ ├── check-versions.js │ │ ├── webpack.base.conf.js │ │ ├── webpack.dev.conf.js │ │ ├── utils.js │ │ └── webpack.prod.conf.js │ ├── config │ │ ├── prod.env.js │ │ ├── dev.env.js │ │ └── index.js │ ├── src │ │ ├── assets │ │ │ ├── logo.png │ │ │ ├── unsupported_patterns.json │ │ │ └── patterns.json │ │ ├── components │ │ │ ├── library │ │ │ │ ├── assets │ │ │ │ │ └── integrity.png │ │ │ │ ├── PasswordHashing.vue │ │ │ │ ├── RandomNumbers.vue │ │ │ │ ├── DerivingKeys.vue │ │ │ │ ├── Signing.vue │ │ │ │ ├── Encryption.vue │ │ │ │ ├── Overview.vue │ │ │ │ ├── IntegrityProtection.vue │ │ │ │ └── Hashing.vue │ │ │ ├── handshakes │ │ │ │ ├── assets │ │ │ │ │ ├── Noise_K.png │ │ │ │ │ ├── Noise_KK.png │ │ │ │ │ ├── Noise_N.png │ │ │ │ │ ├── Noise_NK.png │ │ │ │ │ ├── Noise_NX.png │ │ │ │ │ ├── Noise_X.png │ │ │ │ │ ├── Noise_XX.png │ │ │ │ │ └── Noise_NNpsk2.png │ │ │ │ ├── Noise_KX.vue │ │ │ │ ├── Noise_NNoob.vue │ │ │ │ ├── Noise_XK.vue │ │ │ │ ├── Noise_NK.vue │ │ │ │ ├── Noise_NNpsk2.vue │ │ │ │ ├── Noise_KK.vue │ │ │ │ ├── Noise_N.vue │ │ │ │ ├── Noise_K.vue │ │ │ │ ├── Noise_X.vue │ │ │ │ ├── Noise_XX.vue │ │ │ │ └── Noise_NX.vue │ │ │ ├── GetStarted.vue │ │ │ ├── Quizz.vue │ │ │ ├── Keys.vue │ │ │ └── LandingPage.vue │ │ ├── main.js │ │ ├── router │ │ │ └── index.js │ │ └── App.vue │ ├── .babelrc │ ├── .editorconfig │ ├── .gitignore │ ├── .postcssrc.js │ ├── Dockerfile │ ├── README.md │ ├── index.html │ ├── .eslintrc.js │ └── package.json └── resources │ ├── disco.ai │ └── diagrams.key ├── .travis.yml ├── libdisco ├── examples │ ├── RootSigningKeys │ │ ├── publicRoot │ │ ├── privateRoot │ │ └── root.go │ ├── Noise_NX │ │ ├── server │ │ │ ├── serverKeyPair │ │ │ └── server.go │ │ └── client │ │ │ └── client.go │ ├── Noise_X │ │ ├── client │ │ │ ├── clientKeyPair │ │ │ └── client.go │ │ └── server │ │ │ └── server.go │ ├── Noise_XX │ │ ├── client │ │ │ ├── clientKeyPair │ │ │ └── client.go │ │ └── server │ │ │ ├── serverKeyPair │ │ │ └── server.go │ ├── Noise_NK │ │ ├── client │ │ │ └── client.go │ │ └── server │ │ │ └── server.go │ ├── Noise_N │ │ ├── client │ │ │ └── client.go │ │ └── server │ │ │ └── server.go │ ├── Noise_NNpsk2 │ │ ├── client │ │ │ └── client.go │ │ └── server │ │ │ └── server.go │ ├── Noise_K │ │ ├── client │ │ │ └── client.go │ │ └── server │ │ │ └── server.go │ └── Noise_KK │ │ ├── client │ │ └── client.go │ │ └── server │ │ └── server.go ├── README.md ├── asymmetric_test.go ├── apis_test.go ├── common.go ├── disco_test.go ├── asymmetric.go ├── conn_test.go ├── patterns.go ├── patterns_test.go ├── symmetric_test.go └── symmetric.go ├── specification ├── output │ ├── disco.pdf │ └── spec_markdown.css ├── README.md ├── my.bib └── Makefile ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *#* -------------------------------------------------------------------------------- /web/discocrypto.com/static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/discocrypto.com/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - master 4 | before_script: cd libdisco 5 | -------------------------------------------------------------------------------- /web/discocrypto.com/.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /dist/ 4 | /*.js 5 | * -------------------------------------------------------------------------------- /web/resources/disco.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/resources/disco.ai -------------------------------------------------------------------------------- /web/resources/diagrams.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/resources/diagrams.key -------------------------------------------------------------------------------- /libdisco/examples/RootSigningKeys/publicRoot: -------------------------------------------------------------------------------- 1 | 60e326c4ff52b1cbdb80f9fcf383f7a71c8e13dfa5ea301d6e63a36bddc00100 -------------------------------------------------------------------------------- /specification/output/disco.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/specification/output/disco.pdf -------------------------------------------------------------------------------- /web/discocrypto.com/build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/discocrypto.com/build/logo.png -------------------------------------------------------------------------------- /web/discocrypto.com/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /web/discocrypto.com/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/discocrypto.com/static/favicon.ico -------------------------------------------------------------------------------- /web/discocrypto.com/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/discocrypto.com/src/assets/logo.png -------------------------------------------------------------------------------- /web/discocrypto.com/static/discoblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/discocrypto.com/static/discoblue.png -------------------------------------------------------------------------------- /web/discocrypto.com/static/handshakes/Noise_XX.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/discocrypto.com/static/handshakes/Noise_XX.jpg -------------------------------------------------------------------------------- /web/discocrypto.com/static/handshakes/Noise_XX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/discocrypto.com/static/handshakes/Noise_XX.png -------------------------------------------------------------------------------- /libdisco/examples/RootSigningKeys/privateRoot: -------------------------------------------------------------------------------- 1 | 0783a6a78689f571816fb32830e8ba487375701730cae325a1f5ab9b844aa54860e326c4ff52b1cbdb80f9fcf383f7a71c8e13dfa5ea301d6e63a36bddc00100 -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/library/assets/integrity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/discocrypto.com/src/components/library/assets/integrity.png -------------------------------------------------------------------------------- /libdisco/examples/Noise_NX/server/serverKeyPair: -------------------------------------------------------------------------------- 1 | 8936b324eb77d1af2ada04c5ba8379489fffdf3e0c87670c47a76d10e977183a84b6e61e4a77fe97ecc40ec8b126295c92f03afb2b85d170640c7f30eb71e505 -------------------------------------------------------------------------------- /libdisco/examples/Noise_X/client/clientKeyPair: -------------------------------------------------------------------------------- 1 | d8d940af8bd8591b0d5d0a93367eec458b22d2e132ccc08d2b5339555ecc47fdcdf82af0ac7568631a7446e1fca2a8714bc78d9205cde0f19b24b52fc0db1e45 -------------------------------------------------------------------------------- /libdisco/examples/Noise_XX/client/clientKeyPair: -------------------------------------------------------------------------------- 1 | 38c0d2285ad0f55c7132e411e6d057dbd918051c0085ef4d9fa0abd0207f2d703e2850d80d08ce188b0510fbc1155e5820a13b113129113396c2dafabf52cc29 -------------------------------------------------------------------------------- /libdisco/examples/Noise_XX/server/serverKeyPair: -------------------------------------------------------------------------------- 1 | 346cedfcc41b186af49be44f5551d31fd2aa7fc6e4c7c4ebd6ed472f82f37e4d9b93f1483cdd73ed2de44bbfd1ac4c97e33a972b03bda5133485c5dad29cb23a -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/assets/Noise_K.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/discocrypto.com/src/components/handshakes/assets/Noise_K.png -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/assets/Noise_KK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/discocrypto.com/src/components/handshakes/assets/Noise_KK.png -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/assets/Noise_N.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/discocrypto.com/src/components/handshakes/assets/Noise_N.png -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/assets/Noise_NK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/discocrypto.com/src/components/handshakes/assets/Noise_NK.png -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/assets/Noise_NX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/discocrypto.com/src/components/handshakes/assets/Noise_NX.png -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/assets/Noise_X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/discocrypto.com/src/components/handshakes/assets/Noise_X.png -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/assets/Noise_XX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/discocrypto.com/src/components/handshakes/assets/Noise_XX.png -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/assets/Noise_NNpsk2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mimoo/disco/HEAD/web/discocrypto.com/src/components/handshakes/assets/Noise_NNpsk2.png -------------------------------------------------------------------------------- /web/discocrypto.com/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false 5 | }], 6 | "stage-2" 7 | ], 8 | "plugins": ["transform-runtime"] 9 | } 10 | -------------------------------------------------------------------------------- /specification/README.md: -------------------------------------------------------------------------------- 1 | To build this specification, the Noise Specification tooling is used: 2 | 3 | * https://github.com/noiseprotocol/spectemplate 4 | * https://github.com/noiseprotocol/spectools -------------------------------------------------------------------------------- /web/discocrypto.com/config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /web/discocrypto.com/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /web/discocrypto.com/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /web/discocrypto.com/.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | // to edit target browsers: use "browserslist" field in package.json 6 | "postcss-import": {}, 7 | "autoprefixer": {} 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /web/discocrypto.com/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:stretch 2 | 3 | WORKDIR /web 4 | 5 | COPY package*.json ./ 6 | RUN npm install 7 | 8 | COPY . /web 9 | 10 | RUN npm install -g nodemon 11 | 12 | ENV DEBUG=express:* 13 | EXPOSE 80 14 | #CMD [ "nodemon", "" ] 15 | CMD ["npm", "run", "start"] 16 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App' 5 | import router from './router' 6 | 7 | Vue.config.productionTip = false 8 | 9 | /* eslint-disable no-new */ 10 | new Vue({ 11 | el: '#app', 12 | router, 13 | template: '', 14 | components: { App } 15 | }) 16 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/library/PasswordHashing.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /web/discocrypto.com/README.md: -------------------------------------------------------------------------------- 1 | # disco 2 | 3 | > a plug-and-play secure protocol and a cryptographic library 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | npm install 10 | 11 | # serve with hot reload at localhost:8080 12 | npm run dev 13 | 14 | # build for production with minification 15 | npm run build 16 | 17 | # build for production and view the bundle analyzer report 18 | npm run build --report 19 | ``` 20 | 21 | For a detailed explanation on how things work, check out the [guide](https://vuejs-templates.github.io/webpack/) and [docs for vue-loader](https://vuejs.github.io/vue-loader). 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2020 david wong 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/library/RandomNumbers.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /web/discocrypto.com/build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | 10 | module.exports = { 11 | loaders: utils.cssLoaders({ 12 | sourceMap: sourceMapEnabled, 13 | extract: isProduction 14 | }), 15 | cssSourceMap: sourceMapEnabled, 16 | cacheBusting: config.dev.cacheBusting, 17 | transformToRequire: { 18 | video: 'src', 19 | source: 'src', 20 | img: 'src', 21 | image: 'xlink:href' 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![disco](https://i.imgur.com/4a9upuk.jpg) 2 | 3 | This repository contains the **Disco extension** of the [Noise protocol framework](https://noiseprotocol.org/) to make use of the [Strobe protocol framework](https://strobe.sourceforge.io/) as well as a **cryptographic library**. 4 | 5 | → The **specification** can be found online at [www.discocrypto.com](https://www.discocrypto.com/disco.html) 6 | 7 | → A **golang cryptographic library** called **LibDisco** and based on the Disco specification is available in [libdisco/](libdisco/). 8 | 9 | → A **C cryptographic library** called **EmbeddedDisco** and based on the Disco specification is available at [github.com/mimoo/disco-c/](https://www.github.com/mimoo/disco-c/). 10 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/library/DerivingKeys.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /libdisco/README.md: -------------------------------------------------------------------------------- 1 | # LibDisco 2 | 3 | The `libdisco` package contained in this folder is a **plug-and-play** secure protocol and library based on the [Noise protocol framework](https://noiseprotocol.org) and [Strobe protocol framework](https://strobe.sourceforge.io). It has been implemented following the same patterns used in [crypto/tls](https://golang.org/pkg/crypto/tls/). 4 | 5 | This has use cases close to TLS: it allows you to encrypt communications. 6 | 7 | **This software is experimental. You must not use this in production.** 8 | 9 | [![Build Status](https://travis-ci.org/mimoo/disco.svg?branch=master)](https://travis-ci.org/mimoo/disco) 10 | 11 | ## Documentation 12 | 13 | head over at [www.discocrypto.com](https://www.discocrypto.com) 14 | -------------------------------------------------------------------------------- /web/discocrypto.com/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Disco! 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /web/discocrypto.com/.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | sourceType: 'module' 8 | }, 9 | env: { 10 | browser: true, 11 | }, 12 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 13 | extends: 'standard', 14 | // required to lint *.vue files 15 | plugins: [ 16 | 'html' 17 | ], 18 | // add your custom rules here 19 | 'rules': { 20 | // allow paren-less arrow functions 21 | 'arrow-parens': 0, 22 | // allow async-await 23 | 'generator-star-spacing': 0, 24 | // allow debugger during development 25 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/library/Signing.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /web/discocrypto.com/static/github.min.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:0;color:#333;background:rgba(0,0,0,.02);padding:25px;}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:bold}.hljs-number,.hljs-literal,.hljs-variable,.hljs-template-variable,.hljs-tag .hljs-attr{color:#008080}.hljs-string,.hljs-doctag{color:#d14}.hljs-title,.hljs-section,.hljs-selector-id{color:#900;font-weight:bold}.hljs-subst{font-weight:normal}.hljs-type,.hljs-class .hljs-title{color:#458;font-weight:bold}.hljs-tag,.hljs-name,.hljs-attribute{color:#000080;font-weight:normal}.hljs-regexp,.hljs-link{color:#009926}.hljs-symbol,.hljs-bullet{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999;font-weight:bold}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/library/Encryption.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /specification/my.bib: -------------------------------------------------------------------------------- 1 | @misc{noise, 2 | author = "{Trevor Perrin}", 3 | title = "{Noise protocol framework}", 4 | year = {2017}, 5 | url = "https://noiseprotocol.org", 6 | } 7 | @misc{strobe, 8 | author = "{Mike Hamburg}", 9 | title = "{The STROBE protocol framework}", 10 | howpublished = {Cryptology ePrint Archive, Report 2017/003}, 11 | year = {2017}, 12 | url = "https://eprint.iacr.org/2017/003", 13 | } 14 | @misc{duplex, 15 | author = {Guido Bertoni and Joan Daemen and Michaël Peeters and Gilles Van Assche}, 16 | title = {Duplexing the sponge: single-pass authenticated encryption and other applications}, 17 | howpublished = {Cryptology ePrint Archive, Report 2011/499}, 18 | year = {2011}, 19 | url = "https://eprint.iacr.org/2011/499", 20 | } 21 | @misc{strobespec, 22 | author = "{Mike Hamburg}", 23 | title = "{The STROBE protocol framework specification}", 24 | year = {2017}, 25 | url = "https://strobe.sourceforge.io/", 26 | } 27 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/assets/unsupported_patterns.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Noise_KX", 4 | "tags": ["two_way", "K", "X"], 5 | "description": "Noise_KK is a handshake pattern where:
  1. the server already knows the client's public key
  2. the server sends its static public key as part of the handshake
" 6 | }, 7 | { 8 | "name": "Noise_XK", 9 | "tags": ["two_way", "X", "K"], 10 | "description": "Noise_XK is a handshake pattern where:
  1. the client already knows the server's public key
  2. the client sends its static public key as part of the handshake
" 11 | }, 12 | { 13 | "name": "Noise_NNoob", 14 | "tags": ["two_way", "out_of_band"], 15 | "description": "Noise_NNoob is a handshake pattern where the connection is authenticated \"after the fact\" via a string comparison done out-of-band. If the string doesn't match on both side of the connection, the session should be aborted. This handshake pattern is not yet supported." 16 | } 17 | ] -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/library/Overview.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | -------------------------------------------------------------------------------- /libdisco/examples/Noise_NK/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/hex" 6 | "fmt" 7 | "os" 8 | 9 | "github.com/mimoo/disco/libdisco" 10 | ) 11 | 12 | func main() { 13 | // retrieve the server's public key from an argument 14 | serverPublicKey, _ := hex.DecodeString(os.Args[1]) 15 | 16 | // configure the Disco connection with Noise_NK 17 | // meaning the client knows the key (retrieved from the CLI) 18 | clientConfig := libdisco.Config{ 19 | HandshakePattern: libdisco.NoiseNK, 20 | RemoteKey: serverPublicKey, 21 | } 22 | // Dial the port 6666 of localhost 23 | client, err := libdisco.Dial("tcp", "127.0.0.1:6666", &clientConfig) 24 | if err != nil { 25 | fmt.Println("client can't connect to server:", err) 26 | return 27 | } 28 | defer client.Close() 29 | fmt.Println("connected to", client.RemoteAddr()) 30 | 31 | // write whatever stdin has to say to the socket 32 | scanner := bufio.NewScanner(os.Stdin) 33 | for { 34 | scanner.Scan() 35 | _, err = client.Write([]byte(scanner.Text())) 36 | if err != nil { 37 | fmt.Println("client can't write on socket:", err) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /libdisco/examples/Noise_N/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/hex" 6 | "fmt" 7 | "os" 8 | 9 | "github.com/mimoo/disco/libdisco" 10 | ) 11 | 12 | func main() { 13 | // retrieve the server's public key from an argument 14 | serverPubKey := os.Args[1] 15 | serverKey, _ := hex.DecodeString(serverPubKey) 16 | 17 | // configure the Disco connection with Noise_N 18 | // meaning the client knows the server's static public key (retrieved from the CLI) 19 | clientConfig := libdisco.Config{ 20 | HandshakePattern: libdisco.NoiseN, 21 | RemoteKey: serverKey, 22 | } 23 | // Dial the port 6666 of localhost 24 | client, err := libdisco.Dial("tcp", "127.0.0.1:6666", &clientConfig) 25 | if err != nil { 26 | fmt.Println("client can't connect to server:", err) 27 | return 28 | } 29 | defer client.Close() 30 | fmt.Println("connected to", client.RemoteAddr()) 31 | 32 | // write whatever stdin has to say to the socket 33 | scanner := bufio.NewScanner(os.Stdin) 34 | for { 35 | scanner.Scan() 36 | _, err = client.Write([]byte(scanner.Text())) 37 | if err != nil { 38 | fmt.Println("client can't write on socket:", err) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /libdisco/examples/Noise_NNpsk2/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/hex" 6 | "fmt" 7 | "os" 8 | 9 | "github.com/mimoo/disco/libdisco" 10 | ) 11 | 12 | func main() { 13 | // usage 14 | if len(os.Args) != 2 { 15 | fmt.Println("usage: go run client.go hex_shared_secret") 16 | return 17 | } 18 | 19 | // retrieve the server's public key from an argument 20 | sharedSecret, _ := hex.DecodeString(os.Args[1]) 21 | 22 | // configure the Disco connection 23 | clientConfig := libdisco.Config{ 24 | HandshakePattern: libdisco.NoiseNNpsk2, 25 | PreSharedKey: sharedSecret, 26 | } 27 | 28 | // Dial the port 6666 of localhost 29 | client, err := libdisco.Dial("tcp", "127.0.0.1:6666", &clientConfig) 30 | if err != nil { 31 | fmt.Println("client can't connect to server:", err) 32 | return 33 | } 34 | defer client.Close() 35 | fmt.Println("connected to", client.RemoteAddr()) 36 | 37 | // write whatever stdin has to say to the socket 38 | scanner := bufio.NewScanner(os.Stdin) 39 | for { 40 | scanner.Scan() 41 | _, err = client.Write([]byte(scanner.Text())) 42 | if err != nil { 43 | fmt.Println("client can't write on socket:", err) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /web/discocrypto.com/build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, function (err, stats) { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/library/IntegrityProtection.vue: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /libdisco/asymmetric_test.go: -------------------------------------------------------------------------------- 1 | package libdisco 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestSignVerify(t *testing.T) { 9 | input := []byte("hi, how are you?") 10 | kp, err := GenerateSigningKeypair() 11 | 12 | if err != nil { 13 | t.Fatal("failed to generate a signing keypair") 14 | } 15 | sig := kp.Sign(input) 16 | 17 | err = kp.Verify(input, sig) 18 | if err != nil { 19 | t.Fatal("failed to verify signature with error : ", err) 20 | } 21 | } 22 | 23 | func TestDeterministicSignatures(t *testing.T) { 24 | kp, err := GenerateSigningKeypair() 25 | if err != nil { 26 | t.Fatal("failed to generate a signing keypair") 27 | } 28 | input := []byte("hi, how are you?") 29 | sig1 := kp.Sign(input) 30 | sig2 := kp.Sign(input) 31 | 32 | sig1Bytes := sig1.Encode() 33 | sig2Bytes := sig2.Encode() 34 | 35 | if !bytes.Equal(sig1Bytes[:], sig2Bytes[:]) { 36 | t.Fatal("signatures are not deterministic doesn't work") 37 | } 38 | } 39 | 40 | func BenchmarkSignVerify(b *testing.B) { 41 | kp, err := GenerateSigningKeypair() 42 | if err != nil { 43 | b.Fatal("failed to generate a signing keypair") 44 | } 45 | input := []byte("benchmark how fast do I get signed and it stays consistent") 46 | 47 | for n := 0; n < b.N; n++ { 48 | sig := kp.Sign(input) 49 | kp.Verify(input, sig) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /libdisco/examples/Noise_K/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/hex" 6 | "fmt" 7 | "os" 8 | 9 | "github.com/mimoo/disco/libdisco" 10 | ) 11 | 12 | func main() { 13 | // generating the client key pair 14 | clientKeyPair := libdisco.GenerateKeypair(nil) 15 | fmt.Println("client's public key:", clientKeyPair.ExportPublicKey()) 16 | 17 | // configure the Disco connection 18 | clientConfig := libdisco.Config{ 19 | HandshakePattern: libdisco.NoiseK, 20 | KeyPair: clientKeyPair, 21 | } 22 | 23 | // retrieve the server's public key from an argument 24 | fmt.Println("please enter the server's public key in hexadecimal") 25 | scanner := bufio.NewScanner(os.Stdin) 26 | scanner.Scan() 27 | serverKey, _ := hex.DecodeString(scanner.Text()) 28 | clientConfig.RemoteKey = serverKey 29 | 30 | // Dial the port 6666 of localhost 31 | client, err := libdisco.Dial("tcp", "127.0.0.1:6666", &clientConfig) 32 | if err != nil { 33 | fmt.Println("client can't connect to server:", err) 34 | return 35 | } 36 | defer client.Close() 37 | fmt.Println("connected to", client.RemoteAddr()) 38 | 39 | // write whatever stdin has to say to the socket 40 | for { 41 | scanner.Scan() 42 | _, err = client.Write([]byte(scanner.Text())) 43 | if err != nil { 44 | fmt.Println("client can't write on socket:", err) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/GetStarted.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /libdisco/examples/Noise_NX/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/hex" 6 | "fmt" 7 | "os" 8 | 9 | "github.com/mimoo/disco/libdisco" 10 | ) 11 | 12 | func main() { 13 | 14 | if len(os.Args) != 2 { 15 | fmt.Println("usage: go run client.go hex_root_public_key") 16 | } 17 | 18 | // retrieve root public key 19 | rootPublicKey, err := hex.DecodeString(os.Args[1]) 20 | if err != nil || len(rootPublicKey) != 32 { 21 | fmt.Println("public root key passed is not a 32-byte value in hexadecimal (", len(rootPublicKey), ")") 22 | return 23 | } 24 | 25 | // create a verifier for when we will receive the server's public key 26 | verifier := libdisco.CreatePublicKeyVerifier(rootPublicKey) 27 | 28 | // configure the Disco connection 29 | clientConfig := libdisco.Config{ 30 | HandshakePattern: libdisco.NoiseNX, 31 | PublicKeyVerifier: verifier, 32 | } 33 | 34 | // Dial the port 6666 of localhost 35 | client, err := libdisco.Dial("tcp", "127.0.0.1:6666", &clientConfig) 36 | if err != nil { 37 | fmt.Println("client can't connect to server:", err) 38 | return 39 | } 40 | defer client.Close() 41 | fmt.Println("connected to", client.RemoteAddr()) 42 | 43 | // write whatever stdin has to say to the socket 44 | scanner := bufio.NewScanner(os.Stdin) 45 | for { 46 | scanner.Scan() 47 | _, err = client.Write([]byte(scanner.Text())) 48 | if err != nil { 49 | fmt.Println("client can't write on socket:", err) 50 | } 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /libdisco/examples/Noise_KK/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/hex" 6 | "fmt" 7 | "os" 8 | 9 | "github.com/mimoo/disco/libdisco" 10 | ) 11 | 12 | func main() { 13 | // generating the client key pair 14 | clientKeyPair := libdisco.GenerateKeypair(nil) 15 | fmt.Println("client's public key:", clientKeyPair.ExportPublicKey()) 16 | 17 | // configure the Disco connection 18 | // in which the client knows the static public key of the server 19 | // and the server knows the static public key of the client 20 | clientConfig := libdisco.Config{ 21 | HandshakePattern: libdisco.NoiseKK, 22 | KeyPair: clientKeyPair, 23 | } 24 | 25 | // retrieve the server's public key from an argument 26 | fmt.Println("please enter the server's public key in hexadecimal") 27 | scanner := bufio.NewScanner(os.Stdin) 28 | scanner.Scan() 29 | serverKey, _ := hex.DecodeString(scanner.Text()) 30 | clientConfig.RemoteKey = serverKey 31 | 32 | // Dial the port 6666 of localhost 33 | client, err := libdisco.Dial("tcp", "127.0.0.1:6666", &clientConfig) 34 | if err != nil { 35 | fmt.Println("client can't connect to server:", err) 36 | return 37 | } 38 | defer client.Close() 39 | fmt.Println("connected to", client.RemoteAddr()) 40 | 41 | // write whatever stdin has to say to the socket 42 | for { 43 | scanner.Scan() 44 | _, err = client.Write([]byte(scanner.Text())) 45 | if err != nil { 46 | fmt.Println("client can't write on socket:", err) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /specification/Makefile: -------------------------------------------------------------------------------- 1 | 2 | # Edit SPECNAME for the name your spec 3 | SPECNAME := disco 4 | # Ensure SPECTOOLS points at your spectools 5 | PANDOC := $(SPECTOOLS)/pandoc 6 | CITEPROC := $(SPECTOOLS)/pandoc-citeproc 7 | 8 | # Use "make", "make html", "make pdf", or "make clean" 9 | all: html pdf 10 | 11 | html: output/$(SPECNAME).html 12 | 13 | pdf: output/$(SPECNAME).pdf 14 | 15 | output/$(SPECNAME).html: $(SPECNAME).md $(PANDOC)/template_pandoc.html $(PANDOC)/spec_markdown.css $(CITEPROC)/ieee-with-url.csl $(CITEPROC)/general.bib my.bib 16 | pandoc $(SPECNAME).md --standalone --toc \ 17 | --from markdown\ 18 | --template $(PANDOC)/template_pandoc.html \ 19 | --metadata=pdfn:$(SPECNAME).pdf \ 20 | --css=spec_markdown.css \ 21 | --filter pandoc-citeproc \ 22 | --bibliography=$(CITEPROC)/general.bib \ 23 | --bibliography=my.bib \ 24 | --csl=$(CITEPROC)/ieee-with-url.csl \ 25 | -o output/$(SPECNAME).html 26 | cp $(PANDOC)/spec_markdown.css output 27 | 28 | output/$(SPECNAME).pdf: $(SPECNAME).md $(PANDOC)/template_pandoc.latex $(CITEPROC)/ieee-with-url.csl $(CITEPROC)/general.bib my.bib 29 | pandoc $(SPECNAME).md --standalone --toc \ 30 | --from markdown\ 31 | --template $(PANDOC)/template_pandoc.latex \ 32 | --filter pandoc-citeproc \ 33 | --bibliography=$(CITEPROC)/general.bib \ 34 | --bibliography=my.bib \ 35 | --csl=$(CITEPROC)/ieee-with-url.csl \ 36 | -o output/$(SPECNAME).pdf 37 | 38 | clean: 39 | rm -f output/$(SPECNAME).html output/spec_markdown.css output/$(SPECNAME).pdf 40 | -------------------------------------------------------------------------------- /web/discocrypto.com/build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | function exec (cmd) { 7 | return require('child_process').execSync(cmd).toString().trim() 8 | } 9 | 10 | const versionRequirements = [ 11 | { 12 | name: 'node', 13 | currentVersion: semver.clean(process.version), 14 | versionRequirement: packageConfig.engines.node 15 | } 16 | ] 17 | 18 | if (shell.which('npm')) { 19 | versionRequirements.push({ 20 | name: 'npm', 21 | currentVersion: exec('npm --version'), 22 | versionRequirement: packageConfig.engines.npm 23 | }) 24 | } 25 | 26 | module.exports = function () { 27 | const warnings = [] 28 | for (let i = 0; i < versionRequirements.length; i++) { 29 | const mod = versionRequirements[i] 30 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 31 | warnings.push(mod.name + ': ' + 32 | chalk.red(mod.currentVersion) + ' should be ' + 33 | chalk.green(mod.versionRequirement) 34 | ) 35 | } 36 | } 37 | 38 | if (warnings.length) { 39 | console.log('') 40 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 41 | console.log() 42 | for (let i = 0; i < warnings.length; i++) { 43 | const warning = warnings[i] 44 | console.log(' ' + warning) 45 | } 46 | console.log() 47 | process.exit(1) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /libdisco/examples/Noise_NNpsk2/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "net" 7 | "os" 8 | 9 | "github.com/mimoo/disco/libdisco" 10 | ) 11 | 12 | func main() { 13 | // usage 14 | if len(os.Args) != 2 { 15 | fmt.Println("usage: go run client.go hex_shared_secret") 16 | return 17 | } 18 | 19 | // retrieve the server's public key from an argument 20 | sharedSecret, _ := hex.DecodeString(os.Args[1]) 21 | 22 | // configuring the Disco connection 23 | serverConfig := libdisco.Config{ 24 | HandshakePattern: libdisco.NoiseNNpsk2, 25 | PreSharedKey: sharedSecret, 26 | } 27 | 28 | // listen on port 6666 29 | listener, err := libdisco.Listen("tcp", "127.0.0.1:6666", &serverConfig) 30 | if err != nil { 31 | fmt.Println("cannot setup a listener on localhost:", err) 32 | return 33 | } 34 | addr := listener.Addr().String() 35 | fmt.Println("listening on:", addr) 36 | 37 | for { 38 | // accept a connection 39 | server, err := listener.Accept() 40 | if err != nil { 41 | fmt.Println("server cannot accept()") 42 | server.Close() 43 | continue 44 | } 45 | fmt.Println("server accepted connection from", server.RemoteAddr()) 46 | // read what the socket has to say until connection is closed 47 | go func(server net.Conn) { 48 | buf := make([]byte, 100) 49 | for { 50 | n, err := server.Read(buf) 51 | if err != nil { 52 | fmt.Println("server can't read on socket for", server.RemoteAddr(), ":", err) 53 | break 54 | } 55 | fmt.Println("received data from", server.RemoteAddr(), ":", string(buf[:n])) 56 | } 57 | fmt.Println("shutting down the connection with", server.RemoteAddr()) 58 | server.Close() 59 | }(server) 60 | 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /libdisco/examples/Noise_NK/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "github.com/mimoo/disco/libdisco" 8 | ) 9 | 10 | func main() { 11 | // generating the server key pair 12 | serverKeyPair := libdisco.GenerateKeypair(nil) 13 | 14 | // configuring the Disco connection with a Noise_NK handshake 15 | // in which the client already knows the server's public key 16 | serverConfig := libdisco.Config{ 17 | HandshakePattern: libdisco.NoiseNK, 18 | KeyPair: serverKeyPair, 19 | } 20 | // listen on port 6666 21 | listener, err := libdisco.Listen("tcp", "127.0.0.1:6666", &serverConfig) 22 | if err != nil { 23 | fmt.Println("cannot setup a listener on localhost:", err) 24 | return 25 | } 26 | addr := listener.Addr().String() 27 | fmt.Println("listening on:", addr) 28 | // export public key so that client can retrieve it out of band 29 | fmt.Println("server's public key:", serverKeyPair.ExportPublicKey()) 30 | 31 | for { 32 | // accept a connection 33 | server, err := listener.Accept() 34 | if err != nil { 35 | fmt.Println("server cannot accept()") 36 | server.Close() 37 | continue 38 | } 39 | fmt.Println("server accepted connection from", server.RemoteAddr()) 40 | // read what the socket has to say until connection is closed 41 | go func(server net.Conn) { 42 | buf := make([]byte, 100) 43 | for { 44 | n, err := server.Read(buf) 45 | if err != nil { 46 | fmt.Println("server can't read on socket for", server.RemoteAddr(), ":", err) 47 | break 48 | } 49 | fmt.Println("received data from", server.RemoteAddr(), ":", string(buf[:n])) 50 | } 51 | fmt.Println("shutting down the connection with", server.RemoteAddr()) 52 | server.Close() 53 | }(server) 54 | 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/Noise_KX.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/Noise_NNoob.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/Noise_XK.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | -------------------------------------------------------------------------------- /libdisco/examples/Noise_N/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "github.com/mimoo/disco/libdisco" 8 | ) 9 | 10 | func main() { 11 | // generating the server key pair 12 | serverKeyPair := libdisco.GenerateKeypair(nil) 13 | 14 | // configuring the Disco connection with a Noise_N handshake 15 | // in which the client already knows the server's static public key 16 | serverConfig := libdisco.Config{ 17 | HandshakePattern: libdisco.NoiseN, 18 | KeyPair: serverKeyPair, 19 | } 20 | // listen on port 6666 21 | listener, err := libdisco.Listen("tcp", "127.0.0.1:6666", &serverConfig) 22 | if err != nil { 23 | fmt.Println("cannot setup a listener on localhost:", err) 24 | return 25 | } 26 | addr := listener.Addr().String() 27 | fmt.Println("listening on:", addr) 28 | // export public key so that client can retrieve it out of band 29 | fmt.Println("server's public key:", serverKeyPair.ExportPublicKey()) 30 | 31 | for { 32 | // accept a connection 33 | server, err := listener.Accept() 34 | if err != nil { 35 | fmt.Println("server cannot accept()") 36 | server.Close() 37 | continue 38 | } 39 | fmt.Println("server accepted connection from", server.RemoteAddr()) 40 | // read what the socket has to say until connection is closed 41 | go func(server net.Conn) { 42 | buf := make([]byte, 100) 43 | for { 44 | n, err := server.Read(buf) 45 | if err != nil { 46 | fmt.Println("server can't read on socket for", server.RemoteAddr(), ":", err) 47 | break 48 | } 49 | fmt.Println("received data from", server.RemoteAddr(), ":", string(buf[:n])) 50 | } 51 | fmt.Println("shutting down the connection with", server.RemoteAddr()) 52 | server.Close() 53 | }(server) 54 | 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /libdisco/examples/RootSigningKeys/root.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/mimoo/disco/libdisco" 9 | ) 10 | 11 | func main() { 12 | 13 | // 14 | // run `go run root.go gen` to generate the root key 15 | // 16 | if len(os.Args) == 2 && os.Args[1] == "gen" { 17 | // generating the root signing key 18 | if err := libdisco.GenerateAndSaveDiscoRootKeyPair("./privateRoot", "./publicRoot"); err != nil { 19 | panic("cannot generate and save a root key") 20 | } 21 | 22 | // loading the public part 23 | pubkey, err := libdisco.LoadDiscoRootPublicKey("./publicRoot") 24 | if err != nil { 25 | fmt.Println("cannot load the disco root pubkey") 26 | return 27 | } 28 | 29 | // displaying the public part 30 | fmt.Println("generated the root signing key successfuly. Public root key:") 31 | fmt.Println(hex.EncodeToString(pubkey)) 32 | 33 | return 34 | } 35 | 36 | // 37 | // run `go run root.go sign hex_pubkey` to sign a public key 38 | // 39 | if len(os.Args) == 3 && os.Args[1] == "sign" { 40 | // what do we sign? 41 | toSign, err := hex.DecodeString(os.Args[2]) 42 | if err != nil || len(toSign) != 32 { 43 | fmt.Println("public key passed is not a 32-byte value in hexadecimal (", len(toSign), ")") 44 | return 45 | } 46 | 47 | // load the private root key 48 | privkey, err := libdisco.LoadDiscoRootPrivateKey("./privateRoot") 49 | if err != nil { 50 | fmt.Println("couldn't load the private root key") 51 | return 52 | } 53 | 54 | // create proof 55 | proof := libdisco.CreateStaticPublicKeyProof(privkey, toSign) 56 | 57 | // display the proof 58 | fmt.Println("proof successfuly created:") 59 | fmt.Println(hex.EncodeToString(proof)) 60 | 61 | return 62 | } 63 | 64 | // usage 65 | fmt.Println("read source code to find out usage") 66 | return 67 | 68 | } 69 | -------------------------------------------------------------------------------- /libdisco/examples/Noise_K/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/hex" 6 | "fmt" 7 | "net" 8 | "os" 9 | 10 | "github.com/mimoo/disco/libdisco" 11 | ) 12 | 13 | func main() { 14 | // generating the server key pair 15 | serverKeyPair := libdisco.GenerateKeypair(nil) 16 | fmt.Println("server's public key:", serverKeyPair.ExportPublicKey()) 17 | 18 | // configuring the Disco connection 19 | serverConfig := libdisco.Config{ 20 | HandshakePattern: libdisco.NoiseK, 21 | KeyPair: serverKeyPair, 22 | } 23 | 24 | // retrieve the client's public key from an argument 25 | fmt.Println("please enter the client's public key in hexadecimal") 26 | scanner := bufio.NewScanner(os.Stdin) 27 | scanner.Scan() 28 | clientKey, _ := hex.DecodeString(scanner.Text()) 29 | serverConfig.RemoteKey = clientKey 30 | 31 | // listen on port 6666 32 | listener, err := libdisco.Listen("tcp", "127.0.0.1:6666", &serverConfig) 33 | if err != nil { 34 | fmt.Println("cannot setup a listener on localhost:", err) 35 | return 36 | } 37 | addr := listener.Addr().String() 38 | fmt.Println("listening on:", addr) 39 | 40 | for { 41 | // accept a connection 42 | server, err := listener.Accept() 43 | if err != nil { 44 | fmt.Println("server cannot accept()") 45 | server.Close() 46 | continue 47 | } 48 | fmt.Println("server accepted connection from", server.RemoteAddr()) 49 | // read what the socket has to say until connection is closed 50 | go func(server net.Conn) { 51 | buf := make([]byte, 100) 52 | for { 53 | n, err := server.Read(buf) 54 | if err != nil { 55 | fmt.Println("server can't read on socket for", server.RemoteAddr(), ":", err) 56 | break 57 | } 58 | fmt.Println("received data from", server.RemoteAddr(), ":", string(buf[:n])) 59 | } 60 | fmt.Println("shutting down the connection with", server.RemoteAddr()) 61 | server.Close() 62 | }(server) 63 | 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /libdisco/examples/Noise_KK/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/hex" 6 | "fmt" 7 | "net" 8 | "os" 9 | 10 | "github.com/mimoo/disco/libdisco" 11 | ) 12 | 13 | func main() { 14 | // generating the server key pair 15 | serverKeyPair := libdisco.GenerateKeypair(nil) 16 | fmt.Println("server's public key:", serverKeyPair.ExportPublicKey()) 17 | 18 | // configuring the Disco connection 19 | serverConfig := libdisco.Config{ 20 | HandshakePattern: libdisco.NoiseKK, 21 | KeyPair: serverKeyPair, 22 | } 23 | 24 | // retrieve the client's public key from an argument 25 | fmt.Println("please enter the client's public key in hexadecimal") 26 | scanner := bufio.NewScanner(os.Stdin) 27 | scanner.Scan() 28 | clientKey, _ := hex.DecodeString(scanner.Text()) 29 | serverConfig.RemoteKey = clientKey 30 | 31 | // listen on port 6666 32 | listener, err := libdisco.Listen("tcp", "127.0.0.1:6666", &serverConfig) 33 | if err != nil { 34 | fmt.Println("cannot setup a listener on localhost:", err) 35 | return 36 | } 37 | addr := listener.Addr().String() 38 | fmt.Println("listening on:", addr) 39 | 40 | for { 41 | // accept a connection 42 | server, err := listener.Accept() 43 | if err != nil { 44 | fmt.Println("server cannot accept()") 45 | server.Close() 46 | continue 47 | } 48 | fmt.Println("server accepted connection from", server.RemoteAddr()) 49 | // read what the socket has to say until connection is closed 50 | go func(server net.Conn) { 51 | buf := make([]byte, 100) 52 | for { 53 | n, err := server.Read(buf) 54 | if err != nil { 55 | fmt.Println("server can't read on socket for", server.RemoteAddr(), ":", err) 56 | break 57 | } 58 | fmt.Println("received data from", server.RemoteAddr(), ":", string(buf[:n])) 59 | } 60 | fmt.Println("shutting down the connection with", server.RemoteAddr()) 61 | server.Close() 62 | }(server) 63 | 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /libdisco/apis_test.go: -------------------------------------------------------------------------------- 1 | package libdisco 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | func TestCreationKeys(t *testing.T) { 9 | 10 | // temporary files 11 | discoKeyPairFile := "./discoKeyPairFile" 12 | defer func() { 13 | if err := os.Remove(discoKeyPairFile); err != nil { 14 | panic(err) 15 | } 16 | }() 17 | rootPrivateKeyFile := "./rootPrivateKeyFile" 18 | defer os.Remove(rootPrivateKeyFile) 19 | rootPublicKeyFile := "./rootPublicKeyFile" 20 | defer os.Remove(rootPublicKeyFile) 21 | 22 | // Generate Disco Key pair 23 | keyPair, err := GenerateAndSaveDiscoKeyPair(discoKeyPairFile, "") 24 | if err != nil { 25 | t.Error("Disco key pair couldn't be written on disk") 26 | return 27 | } 28 | // Load Disco Key pair 29 | keyPairTemp, err := LoadDiscoKeyPair(discoKeyPairFile, "") 30 | if err != nil { 31 | t.Error("Disco key pair couldn't be loaded from disk") 32 | return 33 | } 34 | // compare 35 | for i := 0; i < 32; i++ { 36 | if keyPair.PublicKey[i] != keyPairTemp.PublicKey[i] || 37 | keyPair.PrivateKey[i] != keyPairTemp.PrivateKey[i] { 38 | t.Error("Disco key pair generated and loaded are different") 39 | return 40 | } 41 | } 42 | // generate root key 43 | err = GenerateAndSaveDiscoRootKeyPair(rootPrivateKeyFile, rootPublicKeyFile) 44 | if err != nil { 45 | t.Error("Disco key pair couldn't be written on disk") 46 | return 47 | } 48 | 49 | // load private root key 50 | rootPriv, err := LoadDiscoRootPrivateKey(rootPrivateKeyFile) 51 | if err != nil { 52 | t.Error("Disco root private key couldn't be loaded from disk") 53 | return 54 | } 55 | // load public root key 56 | rootPub, err := LoadDiscoRootPublicKey(rootPublicKeyFile) 57 | if err != nil { 58 | t.Error("Disco root public key couldn't be loaded from disk") 59 | return 60 | } 61 | 62 | // create a proof 63 | proof := CreateStaticPublicKeyProof(rootPriv, keyPair.PublicKey[:]) 64 | 65 | // verify the proof 66 | verifior := CreatePublicKeyVerifier(rootPub) 67 | if !verifior(keyPair.PublicKey[:], proof) { 68 | t.Error("cannot verify proof") 69 | return 70 | } 71 | 72 | // end 73 | } 74 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/library/Hashing.vue: -------------------------------------------------------------------------------- 1 | 45 | -------------------------------------------------------------------------------- /libdisco/common.go: -------------------------------------------------------------------------------- 1 | package libdisco 2 | 3 | // The following constants represent the details of this implementation of the Noise specification. 4 | const ( 5 | DiscoDraftVersion = "3" 6 | NoiseDH = "25519" 7 | ) 8 | 9 | // The following constants are taken directly from the Noise specification. 10 | const ( 11 | NoiseMessageLength = 65535 - 2 // 2-byte length 12 | NoiseTagLength = 16 13 | NoiseMaxPlaintextSize = NoiseMessageLength - NoiseTagLength 14 | ) 15 | 16 | // Config is mandatory to setup a Disco peer. It represents the configuration 17 | // of the Disco handshake that the peer will go through. 18 | type Config struct { 19 | // the type of Noise protocol that the client and the server will go through 20 | HandshakePattern noiseHandshakeType 21 | // the current peer's keyPair 22 | KeyPair *KeyPair 23 | // the other peer's public key 24 | RemoteKey []byte 25 | // any messages that the client and the server previously exchanged in clear 26 | Prologue []byte 27 | // if the chosen handshake pattern requires the current peer to send a static 28 | // public key as part of the handshake, this proof over the key is mandatory 29 | // in order for the other peer to verify the current peer's key 30 | StaticPublicKeyProof []byte 31 | // if the chosen handshake pattern requires the remote peer to send an unknown 32 | // static public key as part of the handshake, this callback is mandatory in 33 | // order to validate it 34 | PublicKeyVerifier func(publicKey, proof []byte) bool 35 | // a pre-shared key for handshake patterns including a `psk` token 36 | PreSharedKey []byte 37 | // by default a noise protocol is full-duplex, meaning that both the client 38 | // and the server can write on the channel at the same time. Setting this value 39 | // to true will require the peers to write and read in turns. If this requirement 40 | // is not respected by the application, the consequences could be catastrophic 41 | HalfDuplex bool 42 | // the Accept() and Dial() APIs both return a `net.Conn` interface. 43 | // Because of this, functions like `RemotePublicKey()` cannot be called 44 | // to circumvent this issue, set the following flag. After that, 45 | // `net.Conn`'s `RemoteAddress().String()` will return a tuple `ip:port:pubkey` 46 | RemoteAddrContainsRemotePubkey bool 47 | } 48 | -------------------------------------------------------------------------------- /libdisco/examples/Noise_X/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "net" 7 | "os" 8 | 9 | "github.com/mimoo/disco/libdisco" 10 | ) 11 | 12 | func main() { 13 | // usage 14 | if len(os.Args) != 2 { 15 | fmt.Println("usage:go run server.go hex_root_public_key") 16 | return 17 | } 18 | 19 | // generating the server key pair 20 | serverKeyPair := libdisco.GenerateKeypair(nil) 21 | fmt.Println("server's public key:", serverKeyPair.ExportPublicKey()) 22 | 23 | // retrieve root key 24 | rootPublicKey, err := hex.DecodeString(os.Args[1]) 25 | if err != nil || len(rootPublicKey) != 32 { 26 | fmt.Println("public root key passed is not a 32-byte value in hexadecimal (", len(rootPublicKey), ")") 27 | return 28 | } 29 | 30 | // create a verifier for when we will receive the server's public key 31 | verifier := libdisco.CreatePublicKeyVerifier(rootPublicKey) 32 | 33 | // configuring the Disco connection 34 | // in which the client already knows the server's public key 35 | serverConfig := libdisco.Config{ 36 | HandshakePattern: libdisco.NoiseX, 37 | KeyPair: serverKeyPair, 38 | PublicKeyVerifier: verifier, 39 | } 40 | 41 | // listen on port 6666 42 | listener, err := libdisco.Listen("tcp", "127.0.0.1:6666", &serverConfig) 43 | if err != nil { 44 | fmt.Println("cannot setup a listener on localhost:", err) 45 | return 46 | } 47 | addr := listener.Addr().String() 48 | fmt.Println("listening on:", addr) 49 | 50 | for { 51 | // accept a connection 52 | server, err := listener.Accept() 53 | if err != nil { 54 | fmt.Println("server cannot accept()") 55 | server.Close() 56 | continue 57 | } 58 | fmt.Println("server accepted connection from", server.RemoteAddr()) 59 | // read what the socket has to say until connection is closed 60 | go func(server net.Conn) { 61 | buf := make([]byte, 100) 62 | for { 63 | n, err := server.Read(buf) 64 | if err != nil { 65 | fmt.Println("server can't read on socket for", server.RemoteAddr(), ":", err) 66 | break 67 | } 68 | fmt.Println("received data from", server.RemoteAddr(), ":", string(buf[:n])) 69 | } 70 | fmt.Println("shutting down the connection with", server.RemoteAddr()) 71 | server.Close() 72 | }(server) 73 | 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /web/discocrypto.com/build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const vueLoaderConfig = require('./vue-loader.conf') 6 | 7 | function resolve (dir) { 8 | return path.join(__dirname, '..', dir) 9 | } 10 | 11 | module.exports = { 12 | context: path.resolve(__dirname, '../'), 13 | entry: { 14 | app: './src/main.js' 15 | }, 16 | output: { 17 | path: config.build.assetsRoot, 18 | filename: '[name].js', 19 | publicPath: process.env.NODE_ENV === 'production' 20 | ? config.build.assetsPublicPath 21 | : config.dev.assetsPublicPath 22 | }, 23 | resolve: { 24 | extensions: ['.js', '.vue', '.json'], 25 | alias: { 26 | 'vue$': 'vue/dist/vue.esm.js', 27 | '@': resolve('src'), 28 | } 29 | }, 30 | module: { 31 | rules: [ 32 | ...(config.dev.useEslint? [{ 33 | test: /\.(js|vue)$/, 34 | loader: 'eslint-loader', 35 | enforce: 'pre', 36 | include: [resolve('src'), resolve('test')], 37 | options: { 38 | formatter: require('eslint-friendly-formatter'), 39 | emitWarning: !config.dev.showEslintErrorsInOverlay 40 | } 41 | }] : []), 42 | { 43 | test: /\.vue$/, 44 | loader: 'vue-loader', 45 | options: vueLoaderConfig 46 | }, 47 | { 48 | test: /\.js$/, 49 | loader: 'babel-loader', 50 | include: [resolve('src'), resolve('test')] 51 | }, 52 | { 53 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 54 | loader: 'url-loader', 55 | options: { 56 | limit: 10000, 57 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 58 | } 59 | }, 60 | { 61 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 62 | loader: 'url-loader', 63 | options: { 64 | limit: 10000, 65 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 66 | } 67 | }, 68 | { 69 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 70 | loader: 'url-loader', 71 | options: { 72 | limit: 10000, 73 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 74 | } 75 | } 76 | ] 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /web/discocrypto.com/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "disco", 3 | "version": "1.0.0", 4 | "description": "a plug-and-play secure protocol and a cryptographic library", 5 | "author": "David Wong ", 6 | "private": true, 7 | "scripts": { 8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 9 | "start": "npm run dev", 10 | "lint": "eslint --ext .js,.vue src", 11 | "build": "node build/build.js" 12 | }, 13 | "dependencies": { 14 | "highlight.js": "^9.12.0", 15 | "vue": "^2.5.2", 16 | "vue-router": "^3.0.1" 17 | }, 18 | "devDependencies": { 19 | "autoprefixer": "^7.1.2", 20 | "babel-core": "^6.22.1", 21 | "babel-eslint": "^7.1.1", 22 | "babel-loader": "^7.1.1", 23 | "babel-plugin-transform-runtime": "^6.22.0", 24 | "babel-preset-env": "^1.3.2", 25 | "babel-preset-stage-2": "^6.22.0", 26 | "babel-register": "^6.22.0", 27 | "chalk": "^2.0.1", 28 | "copy-webpack-plugin": "^4.0.1", 29 | "css-loader": "^0.28.0", 30 | "eslint": "^4.19.1", 31 | "eslint-friendly-formatter": "^3.0.0", 32 | "eslint-loader": "^1.7.1", 33 | "eslint-plugin-html": "^3.0.0", 34 | "eslint-config-standard": "^10.2.1", 35 | "eslint-plugin-promise": "^3.4.0", 36 | "eslint-plugin-standard": "^3.0.1", 37 | "eslint-plugin-import": "^2.7.0", 38 | "eslint-plugin-node": "^5.2.0", 39 | "eventsource-polyfill": "^0.9.6", 40 | "extract-text-webpack-plugin": "^3.0.0", 41 | "file-loader": "^1.1.4", 42 | "friendly-errors-webpack-plugin": "^1.6.1", 43 | "html-webpack-plugin": "^2.30.1", 44 | "webpack-bundle-analyzer": "^3.6.0", 45 | "node-notifier": "^5.1.2", 46 | "postcss-import": "^11.0.0", 47 | "postcss-loader": "^2.0.8", 48 | "semver": "^5.3.0", 49 | "shelljs": "^0.7.6", 50 | "optimize-css-assets-webpack-plugin": "^3.2.0", 51 | "ora": "^1.2.0", 52 | "rimraf": "^2.6.0", 53 | "url-loader": "^0.5.8", 54 | "vue-loader": "^13.3.0", 55 | "vue-style-loader": "^3.0.1", 56 | "vue-template-compiler": "^2.5.2", 57 | "portfinder": "^1.0.13", 58 | "webpack": "^3.6.0", 59 | "webpack-dev-server": "^2.9.1", 60 | "webpack-merge": "^4.1.0" 61 | }, 62 | "engines": { 63 | "node": ">= 4.0.0", 64 | "npm": ">= 3.0.0" 65 | }, 66 | "browserslist": [ 67 | "> 1%", 68 | "last 2 versions", 69 | "not ie <= 8" 70 | ] 71 | } -------------------------------------------------------------------------------- /libdisco/examples/Noise_X/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/hex" 6 | "fmt" 7 | "os" 8 | 9 | "github.com/mimoo/disco/libdisco" 10 | ) 11 | 12 | func main() { 13 | 14 | // 15 | // run `go run client.go gen` to generate the static key of the client 16 | // 17 | if len(os.Args) == 2 && os.Args[1] == "setup" { 18 | 19 | // generating the client's keypair 20 | clientKeyPair, err := libdisco.GenerateAndSaveDiscoKeyPair("./clientKeyPair", "") 21 | if err != nil { 22 | panic("couldn't generate and save the client's key pair") 23 | } 24 | 25 | // displaying the public part 26 | fmt.Println("generated the client static public key successfuly. Client's public key:") 27 | fmt.Println(hex.EncodeToString(clientKeyPair.PublicKey[:])) 28 | 29 | return 30 | } 31 | 32 | // 33 | // run `go run client.go connect hex_proof hex_server_static_public_key` to connect to a server 34 | // 35 | if len(os.Args) == 4 && os.Args[1] == "connect" { 36 | 37 | // load the client's keypair 38 | clientKeyPair, err := libdisco.LoadDiscoKeyPair("./clientkeyPair", "") 39 | if err != nil { 40 | fmt.Println("couldn't load the client's key pair") 41 | return 42 | } 43 | 44 | // retrieve server's static public key 45 | serverPublicKey, err := hex.DecodeString(os.Args[3]) 46 | if err != nil || len(serverPublicKey) != 32 { 47 | fmt.Println("server's static public key passed is not a 32-byte value in hexadecimal (", len(serverPublicKey), ")") 48 | return 49 | } 50 | 51 | // retrieve signature/proof 52 | proof, err := hex.DecodeString(os.Args[2]) 53 | if err != nil || len(proof) != 64 { 54 | fmt.Println("proof passed is not a 64-byte value in hexadecimal (", len(proof), ")") 55 | return 56 | } 57 | 58 | // configure the Disco connection with Noise_XX 59 | clientConfig := libdisco.Config{ 60 | KeyPair: clientKeyPair, 61 | RemoteKey: serverPublicKey, 62 | HandshakePattern: libdisco.NoiseX, 63 | StaticPublicKeyProof: proof, 64 | } 65 | 66 | // Dial the port 6666 of localhost 67 | client, err := libdisco.Dial("tcp", "127.0.0.1:6666", &clientConfig) 68 | if err != nil { 69 | fmt.Println("client can't connect to server:", err) 70 | return 71 | } 72 | defer client.Close() 73 | fmt.Println("connected to", client.RemoteAddr()) 74 | 75 | // write whatever stdin has to say to the socket 76 | scanner := bufio.NewScanner(os.Stdin) 77 | for { 78 | scanner.Scan() 79 | _, err = client.Write([]byte(scanner.Text())) 80 | if err != nil { 81 | fmt.Println("client can't write on socket:", err) 82 | } 83 | } 84 | 85 | return 86 | } 87 | 88 | // usage 89 | fmt.Println("read source code to find out usage") 90 | return 91 | } 92 | -------------------------------------------------------------------------------- /web/discocrypto.com/build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const webpack = require('webpack') 4 | const config = require('../config') 5 | const merge = require('webpack-merge') 6 | const baseWebpackConfig = require('./webpack.base.conf') 7 | const HtmlWebpackPlugin = require('html-webpack-plugin') 8 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 9 | const portfinder = require('portfinder') 10 | 11 | const devWebpackConfig = merge(baseWebpackConfig, { 12 | module: { 13 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) 14 | }, 15 | // cheap-module-eval-source-map is faster for development 16 | devtool: config.dev.devtool, 17 | 18 | // these devServer options should be customized in /config/index.js 19 | devServer: { 20 | clientLogLevel: 'warning', 21 | historyApiFallback: true, 22 | hot: true, 23 | compress: true, 24 | host: process.env.HOST || config.dev.host, 25 | port: process.env.PORT || config.dev.port, 26 | open: config.dev.autoOpenBrowser, 27 | overlay: config.dev.errorOverlay ? { 28 | warnings: false, 29 | errors: true, 30 | } : false, 31 | publicPath: config.dev.assetsPublicPath, 32 | proxy: config.dev.proxyTable, 33 | quiet: true, // necessary for FriendlyErrorsPlugin 34 | watchOptions: { 35 | poll: config.dev.poll, 36 | } 37 | }, 38 | plugins: [ 39 | new webpack.DefinePlugin({ 40 | 'process.env': require('../config/dev.env') 41 | }), 42 | new webpack.HotModuleReplacementPlugin(), 43 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. 44 | new webpack.NoEmitOnErrorsPlugin(), 45 | // https://github.com/ampedandwired/html-webpack-plugin 46 | new HtmlWebpackPlugin({ 47 | filename: 'index.html', 48 | template: 'index.html', 49 | inject: true 50 | }), 51 | ] 52 | }) 53 | 54 | module.exports = new Promise((resolve, reject) => { 55 | portfinder.basePort = process.env.PORT || config.dev.port 56 | portfinder.getPort((err, port) => { 57 | if (err) { 58 | reject(err) 59 | } else { 60 | // publish the new Port, necessary for e2e tests 61 | process.env.PORT = port 62 | // add port to devServer config 63 | devWebpackConfig.devServer.port = port 64 | 65 | // Add FriendlyErrorsPlugin 66 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ 67 | compilationSuccessInfo: { 68 | messages: [`Your application is running here: http://${config.dev.host}:${port}`], 69 | }, 70 | onErrors: config.dev.notifyOnErrors 71 | ? utils.createNotifierCallback() 72 | : undefined 73 | })) 74 | 75 | resolve(devWebpackConfig) 76 | } 77 | }) 78 | }) 79 | -------------------------------------------------------------------------------- /libdisco/examples/Noise_XX/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/hex" 6 | "fmt" 7 | "os" 8 | 9 | "github.com/mimoo/disco/libdisco" 10 | ) 11 | 12 | func main() { 13 | 14 | // 15 | // run `go run client.go gen` to generate the static key of the client 16 | // 17 | if len(os.Args) == 2 && os.Args[1] == "setup" { 18 | 19 | // generating the client's keypair 20 | clientKeyPair, err := libdisco.GenerateAndSaveDiscoKeyPair("./clientKeyPair", "") 21 | if err != nil { 22 | panic("couldn't generate and save the client's key pair") 23 | } 24 | 25 | // displaying the public part 26 | fmt.Println("generated the client static public key successfuly. Client's public key:") 27 | fmt.Println(hex.EncodeToString(clientKeyPair.PublicKey[:])) 28 | 29 | return 30 | } 31 | 32 | // 33 | // run `go run client.go connect hex_proof hex_root_public_key` to connect to a server 34 | // 35 | if len(os.Args) == 4 && os.Args[1] == "connect" { 36 | 37 | // load the client's keypair 38 | clientKeyPair, err := libdisco.LoadDiscoKeyPair("./clientkeyPair", "") 39 | if err != nil { 40 | fmt.Println("couldn't load the client's key pair") 41 | return 42 | } 43 | 44 | // retrieve root key 45 | rootPublicKey, err := hex.DecodeString(os.Args[3]) 46 | if err != nil || len(rootPublicKey) != 32 { 47 | fmt.Println("public root key passed is not a 32-byte value in hexadecimal (", len(rootPublicKey), ")") 48 | return 49 | } 50 | 51 | // create a verifier for when we will receive the server's public key 52 | verifier := libdisco.CreatePublicKeyVerifier(rootPublicKey) 53 | 54 | // retrieve signature/proof 55 | proof, err := hex.DecodeString(os.Args[2]) 56 | if err != nil || len(proof) != 64 { 57 | fmt.Println("proof passed is not a 64-byte value in hexadecimal (", len(proof), ")") 58 | return 59 | } 60 | 61 | // configure the Disco connection with Noise_XX 62 | clientConfig := libdisco.Config{ 63 | KeyPair: clientKeyPair, 64 | HandshakePattern: libdisco.NoiseXX, 65 | PublicKeyVerifier: verifier, 66 | StaticPublicKeyProof: proof, 67 | } 68 | 69 | // Dial the port 6666 of localhost 70 | client, err := libdisco.Dial("tcp", "127.0.0.1:6666", &clientConfig) 71 | if err != nil { 72 | fmt.Println("client can't connect to server:", err) 73 | return 74 | } 75 | defer client.Close() 76 | fmt.Println("connected to", client.RemoteAddr()) 77 | 78 | // write whatever stdin has to say to the socket 79 | scanner := bufio.NewScanner(os.Stdin) 80 | for { 81 | scanner.Scan() 82 | _, err = client.Write([]byte(scanner.Text())) 83 | if err != nil { 84 | fmt.Println("client can't write on socket:", err) 85 | } 86 | } 87 | 88 | return 89 | } 90 | 91 | // usage 92 | fmt.Println("read source code to find out usage") 93 | return 94 | } 95 | -------------------------------------------------------------------------------- /web/discocrypto.com/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.2.4 3 | // see https://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: {}, 14 | 15 | // Various Dev Server settings 16 | host: 'localhost', // can be overwritten by process.env.HOST 17 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 18 | autoOpenBrowser: false, 19 | errorOverlay: true, 20 | notifyOnErrors: true, 21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 22 | 23 | // Use Eslint Loader? 24 | // If true, your code will be linted during bundling and 25 | // linting errors and warnings will be shown in the console. 26 | useEslint: true, 27 | // If true, eslint errors and warnings will also be shown in the error overlay 28 | // in the browser. 29 | showEslintErrorsInOverlay: false, 30 | 31 | /** 32 | * Source Maps 33 | */ 34 | 35 | // https://webpack.js.org/configuration/devtool/#development 36 | devtool: 'eval-source-map', 37 | 38 | // If you have problems debugging vue-files in devtools, 39 | // set this to false - it *may* help 40 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 41 | cacheBusting: true, 42 | 43 | // CSS Sourcemaps off by default because relative paths are "buggy" 44 | // with this option, according to the CSS-Loader README 45 | // (https://github.com/webpack/css-loader#sourcemaps) 46 | // In our experience, they generally work as expected, 47 | // just be aware of this issue when enabling this option. 48 | cssSourceMap: false, 49 | }, 50 | 51 | build: { 52 | // Template for index.html 53 | index: path.resolve(__dirname, '../dist/index.html'), 54 | 55 | // Paths 56 | assetsRoot: path.resolve(__dirname, '../dist'), 57 | assetsSubDirectory: 'static', 58 | assetsPublicPath: '/', 59 | 60 | /** 61 | * Source Maps 62 | */ 63 | 64 | productionSourceMap: true, 65 | // https://webpack.js.org/configuration/devtool/#production 66 | devtool: '#source-map', 67 | 68 | // Gzip off by default as many popular static hosts such as 69 | // Surge or Netlify already gzip all static assets for you. 70 | // Before setting to `true`, make sure to: 71 | // npm install --save-dev compression-webpack-plugin 72 | productionGzip: false, 73 | productionGzipExtensions: ['js', 'css'], 74 | 75 | // Run the build command with an extra argument to 76 | // View the bundle analyzer report after build finishes: 77 | // `npm run build --report` 78 | // Set to `true` or `false` to always turn it on or off 79 | bundleAnalyzerReport: process.env.npm_config_report 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /libdisco/examples/Noise_NX/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "net" 7 | "os" 8 | 9 | "github.com/mimoo/disco/libdisco" 10 | ) 11 | 12 | func main() { 13 | 14 | // 15 | // run `go run server.go gen` to generate the static keypair of the server 16 | // 17 | if len(os.Args) == 2 && os.Args[1] == "setup" { 18 | 19 | // generating the server's keypair 20 | serverKeyPair, err := libdisco.GenerateAndSaveDiscoKeyPair("./serverKeyPair", "") 21 | if err != nil { 22 | panic("couldn't generate and save the server's key pair") 23 | } 24 | 25 | // displaying the public part 26 | fmt.Println("generated the server static public key successfuly. server's public key:") 27 | fmt.Println(hex.EncodeToString(serverKeyPair.PublicKey[:])) 28 | 29 | return 30 | } 31 | 32 | // 33 | // run `go run server.go accept hex_proof` to accept connections 34 | // 35 | if len(os.Args) == 3 && os.Args[1] == "accept" { 36 | 37 | // load the server's keypair 38 | serverKeyPair, err := libdisco.LoadDiscoKeyPair("./serverkeyPair", "") 39 | if err != nil { 40 | fmt.Println("couldn't load the server's key pair") 41 | return 42 | } 43 | 44 | // retrieve signature/proof 45 | proof, err := hex.DecodeString(os.Args[2]) 46 | if err != nil || len(proof) != 64 { 47 | fmt.Println("proof passed is not a 64-byte value in hexadecimal (", len(proof), ")") 48 | return 49 | } 50 | 51 | // configure the Disco connection 52 | serverConfig := libdisco.Config{ 53 | KeyPair: serverKeyPair, 54 | HandshakePattern: libdisco.NoiseNX, 55 | StaticPublicKeyProof: proof, 56 | } 57 | 58 | // listen on port 6666 59 | listener, err := libdisco.Listen("tcp", "127.0.0.1:6666", &serverConfig) 60 | if err != nil { 61 | fmt.Println("cannot setup a listener on localhost:", err) 62 | return 63 | } 64 | addr := listener.Addr().String() 65 | fmt.Println("listening on:", addr) 66 | 67 | for { 68 | // accept a connection 69 | server, err := listener.Accept() 70 | if err != nil { 71 | fmt.Println("server cannot accept()") 72 | server.Close() 73 | continue 74 | } 75 | fmt.Println("server accepted connection from", server.RemoteAddr()) 76 | // read what the socket has to say until connection is closed 77 | go func(server net.Conn) { 78 | buf := make([]byte, 100) 79 | for { 80 | n, err := server.Read(buf) 81 | if err != nil { 82 | fmt.Println("server can't read on socket for", server.RemoteAddr(), ":", err) 83 | break 84 | } 85 | fmt.Println("received data from", server.RemoteAddr(), ":", string(buf[:n])) 86 | } 87 | fmt.Println("shutting down the connection with", server.RemoteAddr()) 88 | server.Close() 89 | }(server) 90 | 91 | } 92 | 93 | return 94 | } 95 | 96 | // usage 97 | fmt.Println("read source code to find out usage") 98 | return 99 | } 100 | -------------------------------------------------------------------------------- /web/discocrypto.com/build/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const config = require('../config') 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | const pkg = require('../package.json') 6 | 7 | exports.assetsPath = function (_path) { 8 | const assetsSubDirectory = process.env.NODE_ENV === 'production' 9 | ? config.build.assetsSubDirectory 10 | : config.dev.assetsSubDirectory 11 | return path.posix.join(assetsSubDirectory, _path) 12 | } 13 | 14 | exports.cssLoaders = function (options) { 15 | options = options || {} 16 | 17 | const cssLoader = { 18 | loader: 'css-loader', 19 | options: { 20 | sourceMap: options.sourceMap 21 | } 22 | } 23 | 24 | var postcssLoader = { 25 | loader: 'postcss-loader', 26 | options: { 27 | sourceMap: options.sourceMap 28 | } 29 | } 30 | 31 | // generate loader string to be used with extract text plugin 32 | function generateLoaders (loader, loaderOptions) { 33 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] 34 | if (loader) { 35 | loaders.push({ 36 | loader: loader + '-loader', 37 | options: Object.assign({}, loaderOptions, { 38 | sourceMap: options.sourceMap 39 | }) 40 | }) 41 | } 42 | 43 | // Extract CSS when that option is specified 44 | // (which is the case during production build) 45 | if (options.extract) { 46 | return ExtractTextPlugin.extract({ 47 | use: loaders, 48 | fallback: 'vue-style-loader' 49 | }) 50 | } else { 51 | return ['vue-style-loader'].concat(loaders) 52 | } 53 | } 54 | 55 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 56 | return { 57 | css: generateLoaders(), 58 | postcss: generateLoaders(), 59 | less: generateLoaders('less'), 60 | sass: generateLoaders('sass', { indentedSyntax: true }), 61 | scss: generateLoaders('sass'), 62 | stylus: generateLoaders('stylus'), 63 | styl: generateLoaders('stylus') 64 | } 65 | } 66 | 67 | // Generate loaders for standalone style files (outside of .vue) 68 | exports.styleLoaders = function (options) { 69 | const output = [] 70 | const loaders = exports.cssLoaders(options) 71 | for (const extension in loaders) { 72 | const loader = loaders[extension] 73 | output.push({ 74 | test: new RegExp('\\.' + extension + '$'), 75 | use: loader 76 | }) 77 | } 78 | return output 79 | } 80 | 81 | exports.createNotifierCallback = function () { 82 | const notifier = require('node-notifier') 83 | 84 | return (severity, errors) => { 85 | if (severity !== 'error') { 86 | return 87 | } 88 | const error = errors[0] 89 | 90 | const filename = error.file && error.file.split('!').pop() 91 | notifier.notify({ 92 | title: pkg.name, 93 | message: severity + ': ' + error.name, 94 | subtitle: filename || '', 95 | icon: path.join(__dirname, 'logo.png') 96 | }) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/assets/patterns.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Noise_K", 4 | "tags": ["one_way", "key_pinning"], 5 | "description": "Noise_K is a one-way handshake pattern (in which only the client sends application data to the server and the server never replies) where:
  1. the client already knows the server's public key
  2. the server already knows the client's public key
" 6 | }, 7 | { 8 | "name": "Noise_N", 9 | "tags": ["one_way", "key_pinning"], 10 | "description": "Noise_N is a one-way handshake pattern (in which only the client sends application data to the server and the server never replies) where:
  1. the client already knows the server's public key
  2. the client is not authenticated as part of the handshake
" 11 | }, 12 | { 13 | "name": "Noise_X", 14 | "tags": ["one_way", "key_pinning", "PKI"], 15 | "description": "Noise_X is a one-way handshake pattern (in which only the client sends application data to the server and the server never replies) where:
  1. the server receives the client's public key as part of the handshake
  2. the client already knows the server's public key
" 16 | }, 17 | { 18 | "name": "Noise_NNpsk2", 19 | "tags": ["two_way", "psk"], 20 | "description": "Noise_NNpsk2 is a handshake pattern where both the client and the server already share a 32-byte secret." 21 | }, 22 | { 23 | "name": "Noise_KK", 24 | "tags": ["two_way", "key_pinning"], 25 | "description": "Noise_KK is a handshake pattern where both peers already know each other's public key prior to the handshake." 26 | }, 27 | { 28 | "name": "Noise_IK", 29 | "tags": ["two_way", "key_pinning"], 30 | "description": "Noise_IK is a handshake pattern where the client already knows the server's public key prior to the handshake, and the client sends its static public key as part of the handshake." 31 | }, 32 | { 33 | "name": "Noise_NK", 34 | "tags": ["two_way", "key_pinning"], 35 | "description": "Noise_NK is a handshake pattern similar to mobile device applications connecting to webservers using public-key pinning. The static public key of the server is hardcoded on the client-side of the connection, thanks to this it is not \"sent\" by the server during the connection, but still used as part of the cryptographic computations. The client is not authenticated" 36 | }, 37 | { 38 | "name": "Noise_NX", 39 | "tags": ["two_way", "PKI"], 40 | "description": "Noise_NX is a handshake pattern similar to a typical browser ↔ HTTPS server scenario where the client does not authenticate itself and the server authenticates its public key via a signature from an authoritative signing key." 41 | }, 42 | { 43 | "name": "Noise_XX", 44 | "tags": ["two_way", "PKI"], 45 | "description": "Noise_XX is a handshake pattern similar to the typical browser ↔ HTTPS server scenario where both the client and the server authenticate themselves via a static public key along with a proof from an (or two different) authoritative signing key(s)." 46 | } 47 | ] 48 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/Noise_NK.vue: -------------------------------------------------------------------------------- 1 | 64 | 65 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/Noise_NNpsk2.vue: -------------------------------------------------------------------------------- 1 | 73 | 74 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/Noise_KK.vue: -------------------------------------------------------------------------------- 1 | 68 | 69 | -------------------------------------------------------------------------------- /libdisco/examples/Noise_XX/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "net" 7 | "os" 8 | 9 | "github.com/mimoo/disco/libdisco" 10 | ) 11 | 12 | func main() { 13 | 14 | // 15 | // run `go run server.go gen` to generate the static key of the server 16 | // 17 | if len(os.Args) == 2 && os.Args[1] == "setup" { 18 | 19 | // generating the server's keypair 20 | serverKeyPair, err := libdisco.GenerateAndSaveDiscoKeyPair("./serverKeyPair", "") 21 | if err != nil { 22 | panic("couldn't generate and save the server's key pair") 23 | } 24 | 25 | // displaying the public part 26 | fmt.Println("generated the server static public key successfuly. server's public key:") 27 | fmt.Println(hex.EncodeToString(serverKeyPair.PublicKey[:])) 28 | 29 | return 30 | } 31 | 32 | // 33 | // run `go run server.go accept hex_proof hex_root_public_key` to connect to a server 34 | // 35 | if len(os.Args) == 4 && os.Args[1] == "accept" { 36 | 37 | // load the server's keypair 38 | serverKeyPair, err := libdisco.LoadDiscoKeyPair("./serverkeyPair", "") 39 | if err != nil { 40 | fmt.Println("couldn't load the server's key pair") 41 | return 42 | } 43 | 44 | // retrieve root key 45 | rootPublicKey, err := hex.DecodeString(os.Args[3]) 46 | if err != nil || len(rootPublicKey) != 32 { 47 | fmt.Println("public root key passed is not a 32-byte value in hexadecimal (", len(rootPublicKey), ")") 48 | return 49 | } 50 | 51 | // create a verifier for when we will receive the server's public key 52 | verifier := libdisco.CreatePublicKeyVerifier(rootPublicKey) 53 | 54 | // retrieve signature/proof 55 | proof, err := hex.DecodeString(os.Args[2]) 56 | if err != nil || len(proof) != 64 { 57 | fmt.Println("proof passed is not a 64-byte value in hexadecimal (", len(proof), ")") 58 | return 59 | } 60 | 61 | // configure the Disco connection with Noise_XX 62 | serverConfig := libdisco.Config{ 63 | KeyPair: serverKeyPair, 64 | HandshakePattern: libdisco.NoiseXX, 65 | PublicKeyVerifier: verifier, 66 | StaticPublicKeyProof: proof, 67 | } 68 | 69 | // listen on port 6666 70 | listener, err := libdisco.Listen("tcp", "127.0.0.1:6666", &serverConfig) 71 | if err != nil { 72 | fmt.Println("cannot setup a listener on localhost:", err) 73 | return 74 | } 75 | addr := listener.Addr().String() 76 | fmt.Println("listening on:", addr) 77 | 78 | for { 79 | // accept a connection 80 | server, err := listener.Accept() 81 | if err != nil { 82 | fmt.Println("server cannot accept()") 83 | server.Close() 84 | continue 85 | } 86 | fmt.Println("server accepted connection from", server.RemoteAddr()) 87 | // read what the socket has to say until connection is closed 88 | go func(server net.Conn) { 89 | buf := make([]byte, 100) 90 | for { 91 | n, err := server.Read(buf) 92 | if err != nil { 93 | fmt.Println("server can't read on socket for", server.RemoteAddr(), ":", err) 94 | break 95 | } 96 | fmt.Println("received data from", server.RemoteAddr(), ":", string(buf[:n])) 97 | } 98 | fmt.Println("shutting down the connection with", server.RemoteAddr()) 99 | server.Close() 100 | }(server) 101 | 102 | } 103 | 104 | return 105 | } 106 | 107 | // usage 108 | fmt.Println("read source code to find out usage") 109 | return 110 | } 111 | -------------------------------------------------------------------------------- /specification/output/spec_markdown.css: -------------------------------------------------------------------------------- 1 | html { font-size: 100%; overflow-y: scroll; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } 2 | 3 | body{ 4 | color:#444; 5 | font-family:'Helvetica Neue', 'Helvetica', 'Arial'; 6 | font-size:12px; 7 | line-height:1.5em; 8 | padding:1em; 9 | margin:auto; 10 | max-width:42em; 11 | background:#ffffff; 12 | } 13 | 14 | a{ color: #0645ad; text-decoration:none;} 15 | a:hover{ color: #06e; } 16 | a:active{ color:#faa700; } 17 | a:focus{ outline: thin dotted; } 18 | a:hover, a:active{ outline: 0; } 19 | 20 | ::-moz-selection{background:rgba(255,255,0,0.3);color:#000} 21 | ::selection{background:rgba(255,255,0,0.3);color:#000} 22 | 23 | a::-moz-selection{background:rgba(255,255,0,0.3);color:#0645ad} 24 | a::selection{background:rgba(255,255,0,0.3);color:#0645ad} 25 | 26 | p{ 27 | margin:1em 0; 28 | } 29 | 30 | img{ 31 | max-width:100%; 32 | } 33 | 34 | h1,h2,h3,h4,h5,h6{ 35 | font-weight:normal; 36 | color:#111; 37 | line-height:1em; 38 | } 39 | h4,h5,h6{ font-weight: bold; } 40 | h1{ font-size:2.5em; } 41 | h2{ font-size:2em; } 42 | h3{ font-size:1.5em; } 43 | h4{ font-size:1.2em; } 44 | h5{ font-size:1em; } 45 | h6{ font-size:0.9em; } 46 | 47 | blockquote{ 48 | color:#666666; 49 | margin:0; 50 | padding-left: 3em; 51 | border-left: 0.5em #EEE solid; 52 | } 53 | hr { display: block; height: 2px; border: 0; border-top: 1px solid #aaa;border-bottom: 1px solid #eee; margin: 1em 0; padding: 0; } 54 | pre, code, kbd, samp { font-family: monospace, monospace; _font-family: 'courier new', monospace; font-size: 0.98em; line-height: 1.25em; } 55 | pre { background-color: #FFF; white-space: pre; white-space: pre-wrap; word-wrap: break-word; margin: 0 0; } 56 | 57 | b, strong { font-weight: bold; } 58 | 59 | dfn { font-style: italic; } 60 | 61 | ins { background: #ff9; color: #000; text-decoration: none; } 62 | 63 | mark { background: #ff0; color: #000; font-style: italic; font-weight: bold; } 64 | 65 | sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } 66 | sup { top: -0.5em; } 67 | sub { bottom: -0.25em; } 68 | 69 | ul, ol { margin: 1em 1; padding: 0 0 0 2em; } 70 | li p:last-child { margin:1 } 71 | dd { margin: 0 0 0 2em; } 72 | 73 | img { border: 0; -ms-interpolation-mode: bicubic; vertical-align: middle; } 74 | 75 | table { border-collapse: collapse; border-spacing: 0; } 76 | td { vertical-align: top; padding: 12px; } 77 | 78 | @media only screen and (min-width: 480px) { 79 | body{font-size:14px;} 80 | } 81 | 82 | @media only screen and (min-width: 768px) { 83 | body{font-size:16px;} 84 | } 85 | 86 | @media print { 87 | * { background: transparent !important; color: black !important; filter:none !important; -ms-filter: none !important; } 88 | body{font-size:12pt; max-width:100%;} 89 | a, a:visited { text-decoration: underline; } 90 | hr { height: 1px; border:0; border-bottom:1px solid black; } 91 | a[href]:after { content: " (" attr(href) ")"; } 92 | abbr[title]:after { content: " (" attr(title) ")"; } 93 | .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } 94 | pre, blockquote { border: 1px solid #999; padding-right: 1em; page-break-inside: avoid; } 95 | tr, img { page-break-inside: avoid; } 96 | img { max-width: 100% !important; } 97 | @page :left { margin: 15mm 20mm 15mm 10mm; } 98 | @page :right { margin: 15mm 10mm 15mm 20mm; } 99 | p, h2, h3 { orphans: 3; widows: 3; } 100 | h2, h3 { page-break-after: avoid; } 101 | } 102 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/Noise_N.vue: -------------------------------------------------------------------------------- 1 | 75 | 76 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/Noise_K.vue: -------------------------------------------------------------------------------- 1 | 88 | 89 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | // 4 | import LandingPage from '@/components/LandingPage' 5 | import GetStarted from '@/components/GetStarted' 6 | // protocol 7 | import protocolOverview from '@/components/handshakes/Overview' 8 | import Keys from '@/components/Keys' 9 | import Noise_K from '@/components/handshakes/Noise_K' 10 | import Noise_N from '@/components/handshakes/Noise_N' 11 | import Noise_X from '@/components/handshakes/Noise_X' 12 | import Noise_NNpsk2 from '@/components/handshakes/Noise_NNpsk2' 13 | import Noise_KK from '@/components/handshakes/Noise_KK' 14 | import Noise_NK from '@/components/handshakes/Noise_NK' 15 | import Noise_NX from '@/components/handshakes/Noise_NX' 16 | import Noise_KX from '@/components/handshakes/Noise_KX' 17 | import Noise_XK from '@/components/handshakes/Noise_XK' 18 | import Noise_XX from '@/components/handshakes/Noise_XX' 19 | import Noise_NNoob from '@/components/handshakes/Noise_NNoob' 20 | // library 21 | import libraryOverview from '@/components/library/Overview' 22 | import Hashing from '@/components/library/Hashing' 23 | import Encryption from '@/components/library/Encryption' 24 | import DerivingKeys from '@/components/library/DerivingKeys' 25 | import Signing from '@/components/library/Signing' 26 | import IntegrityProtection from '@/components/library/IntegrityProtection' 27 | import RandomNumbers from '@/components/library/RandomNumbers' 28 | import PasswordHashing from '@/components/library/PasswordHashing' 29 | 30 | Vue.use(Router) 31 | 32 | export default new Router({ 33 | scrollBehavior (to, from, savedPosition) { 34 | return { x: 0, y: 0 } 35 | }, 36 | routes: [ 37 | { 38 | path: '/', 39 | name: 'LandingPage', 40 | component: LandingPage 41 | }, 42 | { 43 | path: '/get_started', 44 | name: 'GetStarted', 45 | component: GetStarted 46 | }, 47 | // protocol 48 | { 49 | path: '/protocol/Overview', 50 | name: 'protocolOverview', 51 | component: protocolOverview 52 | }, 53 | { 54 | path: '/protocol/Keys', 55 | name: 'Keys', 56 | component: Keys 57 | }, 58 | { 59 | path: '/protocol/Noise_K', 60 | name: 'Noise_K', 61 | component: Noise_K 62 | }, 63 | { 64 | path: '/protocol/Noise_N', 65 | name: 'Noise_N', 66 | component: Noise_N 67 | }, 68 | { 69 | path: '/protocol/Noise_X', 70 | name: 'Noise_X', 71 | component: Noise_X 72 | }, 73 | { 74 | path: '/protocol/Noise_NNpsk2', 75 | name: 'Noise_NNpsk2', 76 | component: Noise_NNpsk2 77 | }, 78 | { 79 | path: '/protocol/Noise_KK', 80 | name: 'Noise_KK', 81 | component: Noise_KK 82 | }, 83 | { 84 | path: '/protocol/Noise_NK', 85 | name: 'Noise_NK', 86 | component: Noise_NK 87 | }, 88 | { 89 | path: '/protocol/Noise_NX', 90 | name: 'Noise_NX', 91 | component: Noise_NX 92 | }, 93 | { 94 | path: '/protocol/Noise_KX', 95 | name: 'Noise_KX', 96 | component: Noise_KX 97 | }, 98 | { 99 | path: '/protocol/Noise_XK', 100 | name: 'Noise_XK', 101 | component: Noise_XK 102 | }, 103 | { 104 | path: '/protocol/Noise_XX', 105 | name: 'Noise_XX', 106 | component: Noise_XX 107 | }, 108 | { 109 | path: '/protocol/Noise_NNoob', 110 | name: 'Noise_NNoob', 111 | component: Noise_NNoob 112 | }, 113 | // library 114 | { 115 | path: '/library/Overview', 116 | name: 'libraryOverview', 117 | component: libraryOverview 118 | }, 119 | { 120 | path: '/library/Hashing', 121 | name: 'Hashing', 122 | component: Hashing 123 | }, 124 | { 125 | path: '/library/Encryption', 126 | name: 'Encryption', 127 | component: Encryption 128 | }, 129 | { 130 | path: '/library/DerivingKeys', 131 | name: 'DerivingKeys', 132 | component: DerivingKeys 133 | }, 134 | { 135 | path: '/library/Signing', 136 | name: 'Signing', 137 | component: Signing 138 | }, 139 | { 140 | path: '/library/IntegrityProtection', 141 | name: 'IntegrityProtection', 142 | component: IntegrityProtection 143 | }, 144 | { 145 | path: '/library/PasswordHashing', 146 | name: 'PasswordHashing', 147 | component: PasswordHashing 148 | }, 149 | { 150 | path: '/library/RandomNumbers', 151 | name: 'RandomNumbers', 152 | component: RandomNumbers 153 | } 154 | ] 155 | }) 156 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/Quizz.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 62 | 63 | -------------------------------------------------------------------------------- /web/discocrypto.com/build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const webpack = require('webpack') 5 | const config = require('../config') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 12 | 13 | const env = require('../config/prod.env') 14 | 15 | const webpackConfig = merge(baseWebpackConfig, { 16 | module: { 17 | rules: utils.styleLoaders({ 18 | sourceMap: config.build.productionSourceMap, 19 | extract: true, 20 | usePostCSS: true 21 | }) 22 | }, 23 | devtool: config.build.productionSourceMap ? config.build.devtool : false, 24 | output: { 25 | path: config.build.assetsRoot, 26 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 27 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 28 | }, 29 | plugins: [ 30 | // https://vuejs.github.io/vue-loader/en/workflow/production.html 31 | new webpack.DefinePlugin({ 32 | 'process.env': env 33 | }), 34 | // UglifyJs do not support ES6+, you can also use babel-minify for better treeshaking: https://github.com/babel/minify 35 | new webpack.optimize.UglifyJsPlugin({ 36 | compress: { 37 | warnings: false 38 | }, 39 | sourceMap: config.build.productionSourceMap, 40 | parallel: true 41 | }), 42 | // extract css into its own file 43 | new ExtractTextPlugin({ 44 | filename: utils.assetsPath('css/[name].[contenthash].css'), 45 | // set the following option to `true` if you want to extract CSS from 46 | // codesplit chunks into this main css file as well. 47 | // This will result in *all* of your app's CSS being loaded upfront. 48 | allChunks: false, 49 | }), 50 | // Compress extracted CSS. We are using this plugin so that possible 51 | // duplicated CSS from different components can be deduped. 52 | new OptimizeCSSPlugin({ 53 | cssProcessorOptions: config.build.productionSourceMap 54 | ? { safe: true, map: { inline: false } } 55 | : { safe: true } 56 | }), 57 | // generate dist index.html with correct asset hash for caching. 58 | // you can customize output by editing /index.html 59 | // see https://github.com/ampedandwired/html-webpack-plugin 60 | new HtmlWebpackPlugin({ 61 | filename: config.build.index, 62 | template: 'index.html', 63 | inject: true, 64 | minify: { 65 | removeComments: true, 66 | collapseWhitespace: true, 67 | removeAttributeQuotes: true 68 | // more options: 69 | // https://github.com/kangax/html-minifier#options-quick-reference 70 | }, 71 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 72 | chunksSortMode: 'dependency' 73 | }), 74 | // keep module.id stable when vender modules does not change 75 | new webpack.HashedModuleIdsPlugin(), 76 | // enable scope hoisting 77 | new webpack.optimize.ModuleConcatenationPlugin(), 78 | // split vendor js into its own file 79 | new webpack.optimize.CommonsChunkPlugin({ 80 | name: 'vendor', 81 | minChunks: function (module) { 82 | // any required modules inside node_modules are extracted to vendor 83 | return ( 84 | module.resource && 85 | /\.js$/.test(module.resource) && 86 | module.resource.indexOf( 87 | path.join(__dirname, '../node_modules') 88 | ) === 0 89 | ) 90 | } 91 | }), 92 | // extract webpack runtime and module manifest to its own file in order to 93 | // prevent vendor hash from being updated whenever app bundle is updated 94 | new webpack.optimize.CommonsChunkPlugin({ 95 | name: 'manifest', 96 | minChunks: Infinity 97 | }), 98 | // This instance extracts shared chunks from code splitted chunks and bundles them 99 | // in a separate chunk, similar to the vendor chunk 100 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk 101 | new webpack.optimize.CommonsChunkPlugin({ 102 | name: 'app', 103 | async: 'vendor-async', 104 | children: true, 105 | minChunks: 3 106 | }), 107 | 108 | // copy custom static assets 109 | new CopyWebpackPlugin([ 110 | { 111 | from: path.resolve(__dirname, '../static'), 112 | to: config.build.assetsSubDirectory, 113 | ignore: ['.*'] 114 | } 115 | ]) 116 | ] 117 | }) 118 | 119 | if (config.build.productionGzip) { 120 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 121 | 122 | webpackConfig.plugins.push( 123 | new CompressionWebpackPlugin({ 124 | asset: '[path].gz[query]', 125 | algorithm: 'gzip', 126 | test: new RegExp( 127 | '\\.(' + 128 | config.build.productionGzipExtensions.join('|') + 129 | ')$' 130 | ), 131 | threshold: 10240, 132 | minRatio: 0.8 133 | }) 134 | ) 135 | } 136 | 137 | if (config.build.bundleAnalyzerReport) { 138 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 139 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 140 | } 141 | 142 | module.exports = webpackConfig 143 | -------------------------------------------------------------------------------- /libdisco/disco_test.go: -------------------------------------------------------------------------------- 1 | package libdisco 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "math" 8 | "net" 9 | "testing" 10 | "time" 11 | ) 12 | 13 | /* 14 | * These benchmarks were stolen from crypto/tls 15 | * 16 | */ 17 | 18 | func newLocalListener(t testing.TB) net.Listener { 19 | ln, err := net.Listen("tcp", "127.0.0.1:0") 20 | if err != nil { 21 | ln, err = net.Listen("tcp6", "[::1]:0") 22 | } 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | return ln 27 | } 28 | 29 | func throughput(b *testing.B, totalBytes int64) { 30 | 31 | serverKeyPair := GenerateKeypair(nil) 32 | 33 | ln := newLocalListener(b) 34 | defer ln.Close() 35 | 36 | N := b.N 37 | 38 | // Less than 64KB because Windows appears to use a TCP rwin < 64KB. 39 | // See Issue #15899. 40 | const bufsize = 32 << 10 41 | 42 | go func() { 43 | buf := make([]byte, bufsize) 44 | for i := 0; i < N; i++ { 45 | sconn, err := ln.Accept() 46 | if err != nil { 47 | // panic rather than synchronize to avoid benchmark overhead 48 | // (cannot call b.Fatal in goroutine) 49 | panic(fmt.Errorf("accept: %v", err)) 50 | } 51 | serverConfig := &Config{ 52 | HandshakePattern: NoiseNK, 53 | KeyPair: serverKeyPair, 54 | } 55 | srv := Server(sconn, serverConfig) 56 | if err := srv.Handshake(); err != nil { 57 | panic(fmt.Errorf("handshake: %v", err)) 58 | } 59 | if _, err := io.CopyBuffer(srv, srv, buf); err != nil { 60 | panic(fmt.Errorf("copy buffer: %v", err)) 61 | } 62 | } 63 | }() 64 | 65 | b.SetBytes(totalBytes) 66 | clientConfig := &Config{ 67 | HandshakePattern: NoiseNK, 68 | RemoteKey: serverKeyPair.PublicKey[:], 69 | } 70 | 71 | buf := make([]byte, bufsize) 72 | chunks := int(math.Ceil(float64(totalBytes) / float64(len(buf)))) 73 | for i := 0; i < N; i++ { 74 | conn, err := Dial("tcp", ln.Addr().String(), clientConfig) 75 | if err != nil { 76 | b.Fatal(err) 77 | } 78 | for j := 0; j < chunks; j++ { 79 | _, err := conn.Write(buf) 80 | if err != nil { 81 | b.Fatal(err) 82 | } 83 | _, err = io.ReadFull(conn, buf) 84 | if err != nil { 85 | b.Fatal(err) 86 | } 87 | } 88 | conn.Close() 89 | } 90 | } 91 | 92 | func BenchmarkThroughput(b *testing.B) { 93 | for size := 1; size <= 64; size <<= 1 { 94 | name := fmt.Sprintf("Packet/%dMB", size) 95 | b.Run(name, func(b *testing.B) { 96 | throughput(b, int64(size<<20)) 97 | }) 98 | } 99 | } 100 | 101 | type slowConn struct { 102 | net.Conn 103 | bps int 104 | } 105 | 106 | func (c *slowConn) Write(p []byte) (int, error) { 107 | if c.bps == 0 { 108 | panic("too slow") 109 | } 110 | t0 := time.Now() 111 | wrote := 0 112 | for wrote < len(p) { 113 | time.Sleep(100 * time.Microsecond) 114 | allowed := int(time.Since(t0).Seconds()*float64(c.bps)) / 8 115 | if allowed > len(p) { 116 | allowed = len(p) 117 | } 118 | if wrote < allowed { 119 | n, err := c.Conn.Write(p[wrote:allowed]) 120 | wrote += n 121 | if err != nil { 122 | return wrote, err 123 | } 124 | } 125 | } 126 | return len(p), nil 127 | } 128 | 129 | func latency(b *testing.B, bps int) { 130 | ln := newLocalListener(b) 131 | defer ln.Close() 132 | 133 | N := b.N 134 | 135 | serverKeyPair := GenerateKeypair(nil) 136 | 137 | go func() { 138 | for i := 0; i < N; i++ { 139 | sconn, err := ln.Accept() 140 | if err != nil { 141 | // panic rather than synchronize to avoid benchmark overhead 142 | // (cannot call b.Fatal in goroutine) 143 | panic(fmt.Errorf("accept: %v", err)) 144 | } 145 | serverConfig := &Config{ 146 | HandshakePattern: NoiseNK, 147 | KeyPair: serverKeyPair, 148 | } 149 | srv := Server(&slowConn{sconn, bps}, serverConfig) 150 | if err := srv.Handshake(); err != nil { 151 | panic(fmt.Errorf("handshake: %v", err)) 152 | } 153 | io.Copy(srv, srv) 154 | } 155 | }() 156 | 157 | clientConfig := &Config{ 158 | HandshakePattern: NoiseNK, 159 | RemoteKey: serverKeyPair.PublicKey[:], 160 | } 161 | 162 | buf := make([]byte, 16384) 163 | peek := make([]byte, 1) 164 | 165 | for i := 0; i < N; i++ { 166 | conn, err := Dial("tcp", ln.Addr().String(), clientConfig) 167 | if err != nil { 168 | b.Fatal(err) 169 | } 170 | // make sure we're connected and previous connection has stopped 171 | if _, err := conn.Write(buf[:1]); err != nil { 172 | b.Fatal(err) 173 | } 174 | if _, err := io.ReadFull(conn, peek); err != nil { 175 | b.Fatal(err) 176 | } 177 | if _, err := conn.Write(buf); err != nil { 178 | b.Fatal(err) 179 | } 180 | if _, err = io.ReadFull(conn, peek); err != nil { 181 | b.Fatal(err) 182 | } 183 | conn.Close() 184 | } 185 | } 186 | 187 | func BenchmarkLatency(b *testing.B) { 188 | for _, kbps := range []int{200, 500, 1000, 2000, 5000} { 189 | name := fmt.Sprintf("%dkbps", kbps) 190 | b.Run(name, func(b *testing.B) { 191 | latency(b, kbps*1000) 192 | }) 193 | } 194 | } 195 | 196 | func TestSerialize(t *testing.T) { 197 | // init 198 | s := GenerateKeypair(nil) 199 | rs := GenerateKeypair(nil) 200 | hs := Initialize(NoiseIK, true, nil, s, nil, rs, nil) 201 | // write first message 202 | var msg []byte 203 | hs.WriteMessage(nil, &msg) 204 | // serialize 205 | serialized := hs.Serialize() 206 | // unserialize 207 | hs2 := RecoverState(serialized, nil, s) 208 | 209 | // let's write a message to parse 210 | hsBob := Initialize(NoiseIK, false, nil, rs, nil, s, nil) 211 | var msg2 []byte 212 | hsBob.ReadMessage(msg, &msg2) 213 | msg2 = msg2[:0] 214 | hsBob.WriteMessage([]byte("hello"), &msg2) 215 | 216 | // let's parse it 217 | var msgRcv1, msgRcv2 []byte 218 | c1, c2, _ := hs.ReadMessage(msg2, &msgRcv1) 219 | t1, t2, _ := hs2.ReadMessage(msg2, &msgRcv2) 220 | 221 | if !bytes.Equal(msgRcv1, msgRcv2) { 222 | t.Fatal("received message not as expected") 223 | } 224 | 225 | if !bytes.Equal(c1.Serialize(), t1.Serialize()) { 226 | t.Fatal("obtained strobeState not matching") 227 | } 228 | 229 | if !bytes.Equal(c2.Serialize(), t2.Serialize()) { 230 | t.Fatal("obtained strobeState not matching") 231 | } 232 | 233 | } 234 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/Keys.vue: -------------------------------------------------------------------------------- 1 | 102 | 103 | 122 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/LandingPage.vue: -------------------------------------------------------------------------------- 1 | 94 | 95 | 100 | 101 | 111 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/Noise_X.vue: -------------------------------------------------------------------------------- 1 | 150 | 151 | 170 | -------------------------------------------------------------------------------- /libdisco/asymmetric.go: -------------------------------------------------------------------------------- 1 | package libdisco 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "encoding/hex" 7 | "errors" 8 | 9 | ristretto "github.com/gtank/ristretto255" 10 | "golang.org/x/crypto/curve25519" 11 | ) 12 | 13 | // The following code defines the X25519, chacha20poly1305, SHA-256 suite. 14 | const ( 15 | dhLen = 32 // A constant specifying the size in bytes of public keys and DH outputs. For security reasons, dhLen must be 32 or greater. 16 | skSize = 32 // a secret key is encoded as a 32 byte array. 17 | ) 18 | 19 | // 4.1. DH functions 20 | 21 | // TODO: store the KeyPair's parts in *[32]byte or []byte ? 22 | 23 | // KeyPair contains a private and a public part, both of 32-byte. 24 | // It can be generated via the GenerateKeyPair() function. 25 | // The public part can also be extracted via the ExportPublicKey function. 26 | type KeyPair struct { 27 | PrivateKey [32]byte // must stay a [32]byte because of Serialize() 28 | PublicKey [32]byte // must stay a [32]byte because of Serialize() 29 | } 30 | 31 | // GenerateKeypair creates a X25519 static keyPair out of a private key. If privateKey is nil the function generates a random key pair. 32 | func GenerateKeypair(privateKey *[32]byte) *KeyPair { 33 | 34 | var keyPair KeyPair 35 | if privateKey != nil { 36 | copy(keyPair.PrivateKey[:], privateKey[:]) 37 | } else { 38 | if _, err := rand.Read(keyPair.PrivateKey[:]); err != nil { 39 | panic(err) 40 | } 41 | } 42 | 43 | curve25519.ScalarBaseMult(&keyPair.PublicKey, &keyPair.PrivateKey) 44 | 45 | return &keyPair 46 | } 47 | 48 | // ExportPublicKey returns the public part in hex format of a static key pair. 49 | func (kp KeyPair) ExportPublicKey() string { 50 | return hex.EncodeToString(kp.PublicKey[:]) 51 | } 52 | 53 | func dh(keyPair KeyPair, publicKey [32]byte) (shared [32]byte) { 54 | 55 | curve25519.ScalarMult(&shared, &keyPair.PrivateKey, &publicKey) 56 | 57 | return 58 | } 59 | 60 | // The following code implements the Schnorrkel variant of Schnorr signatures 61 | // over ristretto255. 62 | // This implementation was picked from https://github.com/w3f/schnorrkel 63 | 64 | // SigningKeypair uses deterministic Schnorr with strobe 65 | type SigningKeypair struct { 66 | SecretKey ristretto.Scalar 67 | PublicKey ristretto.Element 68 | } 69 | 70 | // Signature represents a schnorrkel signature 71 | type Signature struct { 72 | R ristretto.Element 73 | S ristretto.Scalar 74 | } 75 | 76 | // Decode a schnorrkel signature from a bytearray. 77 | // ref: https://github.com/w3f/schnorrkel/blob/master/src/sign.rs#L100 78 | func (s *Signature) Decode(sigBytes [64]byte) error { 79 | err := s.R.Decode(sigBytes[:32]) 80 | if err != nil { 81 | return err 82 | } 83 | sigBytes[63] &= 127 84 | err = s.S.Decode(sigBytes[32:]) 85 | if err != nil { 86 | return err 87 | } 88 | return nil 89 | } 90 | 91 | // Encode a signature as a bytearray. 92 | // see: https://github.com/w3f/schnorrkel/blob/master/src/sign.rs#L77 93 | func (s *Signature) Encode() [64]byte { 94 | var sigBytes [64]byte 95 | 96 | rBytes := s.R.Encode(nil) 97 | copy(sigBytes[:32], rBytes) 98 | 99 | sBytes := s.S.Encode(nil) 100 | copy(sigBytes[32:], sBytes) 101 | 102 | sigBytes[63] |= 128 103 | 104 | return sigBytes 105 | } 106 | 107 | // GenerateSigningKeypair for schnorr signatures. 108 | func GenerateSigningKeypair() (SigningKeypair, error) { 109 | 110 | var sigpair SigningKeypair 111 | // Generate a schnorr keypair 112 | var publicKey ristretto.Element 113 | 114 | secretKey, err := newRandomScalar() 115 | if err != nil { 116 | return sigpair, err 117 | } 118 | sigpair.PublicKey = *publicKey.ScalarBaseMult(&secretKey) 119 | sigpair.SecretKey = secretKey 120 | 121 | return sigpair, nil 122 | } 123 | 124 | // ExportPublicKey returns a hexstring encoding of the signing keypair. 125 | func (kp SigningKeypair) ExportPublicKey() string { 126 | return hex.EncodeToString(kp.SecretKey.Encode(nil)) + hex.EncodeToString(kp.PublicKey.Encode(nil)) 127 | } 128 | 129 | // Sign a message using a deterministic nonce 130 | func (kp SigningKeypair) Sign(message []byte) Signature { 131 | 132 | // choose a deterministic nonce 133 | 134 | var kBytes [64]byte 135 | // preallocate and avoid append allocations 136 | var buf = make([]byte, 0, skSize+len(message)) 137 | buf = append(buf, kp.SecretKey.Encode(nil)...) 138 | buf = append(buf, message...) 139 | // buf ownership passed to reusable buffer 140 | var reusableBuffer = bytes.NewBuffer(buf) 141 | // the output should be 64 byte per ristretto255 rfc 142 | // ref : https://tools.ietf.org/html/draft-hdevalence-cfrg-ristretto-00 143 | copy(kBytes[:], Hash(reusableBuffer.Bytes(), 64)) 144 | // cleanup 145 | reusableBuffer.Reset() 146 | var k ristretto.Scalar 147 | var R ristretto.Element 148 | 149 | k.FromUniformBytes(kBytes[:]) 150 | R.ScalarBaseMult(&k) 151 | 152 | var e ristretto.Scalar 153 | var x ristretto.Scalar 154 | var s ristretto.Scalar 155 | 156 | // e = H(R||message) 157 | reusableBuffer.Write(R.Encode(nil)) 158 | reusableBuffer.Write(message) 159 | 160 | h := Hash(reusableBuffer.Bytes(), 64) 161 | e.FromUniformBytes(h) 162 | 163 | reusableBuffer.Reset() 164 | // x = sk*e 165 | x.Multiply(&kp.SecretKey, &e) 166 | // s = k + x 167 | s.Add(&k, &x) 168 | 169 | sig := Signature{R, s} 170 | 171 | return sig 172 | 173 | } 174 | 175 | // Verify a signature 176 | func (kp SigningKeypair) Verify(message []byte, signature Signature) error { 177 | 178 | // Verifying a signature of the form R,s 179 | // Decoding the signature 180 | 181 | var R ristretto.Element 182 | var s ristretto.Scalar 183 | 184 | R = signature.R 185 | s = signature.S 186 | 187 | ev := Hash(append(R.Encode(nil), message...), 64) 188 | 189 | var k ristretto.Scalar 190 | k.FromUniformBytes(ev) 191 | 192 | var Rp ristretto.Element 193 | Rp.ScalarBaseMult(&s) 194 | 195 | var ky ristretto.Element 196 | ky.ScalarMult(&k, &kp.PublicKey) 197 | 198 | Rp.Subtract(&Rp, &ky) 199 | 200 | if Rp.Equal(&R) != 1 { 201 | return errors.New("failed to verify signature") 202 | } 203 | return nil 204 | 205 | } 206 | 207 | // newRandomScalar generates a random ristretto scalar using crypto/rand. 208 | func newRandomScalar() (ristretto.Scalar, error) { 209 | 210 | var buf [64]byte 211 | var s ristretto.Scalar 212 | 213 | n, err := rand.Read(buf[:]) 214 | 215 | if n != 64 || err != nil { 216 | return s, err 217 | } 218 | 219 | s.FromUniformBytes(buf[:]) 220 | return s, nil 221 | } 222 | 223 | // newRandomElement generates a random ristretto point using crypto/rand. 224 | func newRandomElement() (ristretto.Element, error) { 225 | 226 | var buf [64]byte 227 | var P ristretto.Element 228 | 229 | n, err := rand.Read(buf[:]) 230 | 231 | if n != 64 || err != nil { 232 | return P, err 233 | } 234 | P.FromUniformBytes(buf[:]) 235 | return P, nil 236 | } 237 | -------------------------------------------------------------------------------- /libdisco/conn_test.go: -------------------------------------------------------------------------------- 1 | package libdisco 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "strings" 8 | "testing" 9 | ) 10 | 11 | // TODO: add more tests from tls/conn_test.go 12 | 13 | func verifier([]byte, []byte) bool { return true } 14 | 15 | func TestSeveralWriteRead(t *testing.T) { 16 | // init 17 | clientConfig := Config{ 18 | KeyPair: GenerateKeypair(nil), 19 | HandshakePattern: NoiseXX, 20 | StaticPublicKeyProof: []byte{}, 21 | PublicKeyVerifier: verifier, 22 | } 23 | serverConfig := Config{ 24 | KeyPair: GenerateKeypair(nil), 25 | HandshakePattern: NoiseXX, 26 | StaticPublicKeyProof: []byte{}, 27 | PublicKeyVerifier: verifier, 28 | } 29 | 30 | // get a libdisco.listener 31 | listener, err := Listen("tcp", "127.0.0.1:0", &serverConfig) // port 0 will find out a free port 32 | if err != nil { 33 | t.Fatal("cannot setup a listener on localhost:", err) 34 | } 35 | defer listener.Close() 36 | addr := listener.Addr().String() 37 | 38 | // run the server and Accept one connection 39 | go func(t *testing.T) { 40 | serverSocket, err2 := listener.Accept() 41 | if err2 != nil { 42 | t.Fatal("a server cannot accept()") 43 | } 44 | 45 | var buf [100]byte 46 | 47 | for { 48 | n, err2 := serverSocket.Read(buf[:]) 49 | if err2 != nil { 50 | if err2 == io.EOF { 51 | return 52 | } 53 | t.Fatal("server can't read on socket") 54 | } 55 | if !bytes.Equal(buf[:n-1], []byte("hello ")) { 56 | t.Fatal("received message not as expected") 57 | } 58 | 59 | //fmt.Println("server received:", string(buf[:n])) 60 | } 61 | 62 | }(t) 63 | 64 | // Run the client 65 | clientSocket, err := Dial("tcp", addr, &clientConfig) 66 | if err != nil { 67 | t.Fatal("client can't connect to server") 68 | } 69 | 70 | for i := 0; i < 100; i++ { 71 | message := "hello " + string(i) 72 | _, err = clientSocket.Write([]byte(message)) 73 | if err != nil { 74 | t.Fatal("client can't write on socket") 75 | } 76 | } 77 | 78 | clientSocket.Close() 79 | 80 | } 81 | 82 | func TestHalfDuplex(t *testing.T) { 83 | // init 84 | clientConfig := Config{ 85 | KeyPair: GenerateKeypair(nil), 86 | HandshakePattern: NoiseXX, 87 | StaticPublicKeyProof: []byte{}, 88 | PublicKeyVerifier: verifier, 89 | HalfDuplex: true, 90 | } 91 | serverConfig := Config{ 92 | KeyPair: GenerateKeypair(nil), 93 | HandshakePattern: NoiseXX, 94 | StaticPublicKeyProof: []byte{}, 95 | PublicKeyVerifier: verifier, 96 | HalfDuplex: true, 97 | } 98 | 99 | // get a libdisco.listener 100 | listener, err := Listen("tcp", "127.0.0.1:0", &serverConfig) 101 | 102 | if err != nil { 103 | t.Fatal("cannot setup a listener on localhost:", err) 104 | } 105 | addr := listener.Addr().String() 106 | 107 | // run the server and Accept one connection 108 | go func() { 109 | serverSocket, err2 := listener.Accept() 110 | if err2 != nil { 111 | t.Fatal("a server cannot accept()") 112 | } 113 | 114 | var buf [10]byte 115 | 116 | for { 117 | // read message first 118 | n, err2 := serverSocket.Read(buf[:]) 119 | if err2 != nil { 120 | if err2 == io.EOF { 121 | return 122 | } 123 | t.Fatal("server can't read on socket") 124 | } 125 | if n != 6 { 126 | t.Fatal("server is supposed to read 6 bytes") 127 | } 128 | if !bytes.Equal(buf[:n-1], []byte("hello")) { 129 | t.Fatal("server received message which is not hello+i") 130 | } 131 | // then write message 132 | _, err = serverSocket.Write(buf[:n]) 133 | if err != nil { 134 | fmt.Println("ERRROR", err) // debug 135 | t.Fatal("server can't write on socket") 136 | } 137 | if n != 6 { 138 | t.Fatal("server is supposed to write 6 bytes") 139 | } 140 | } 141 | 142 | }() 143 | 144 | // Run the client 145 | clientSocket, err := Dial("tcp", addr, &clientConfig) 146 | 147 | if err != nil { 148 | t.Fatal("client can't connect to server") 149 | } 150 | 151 | var buf [10]byte 152 | 153 | for i := 0; i < 100; i++ { 154 | // first write `hello + i` 155 | message := append([]byte("hello"), byte(i)) 156 | n, err := clientSocket.Write(message) 157 | if err != nil { 158 | fmt.Println("ERRROR", err) // debug 159 | t.Fatal("client can't write on socket") 160 | } 161 | if n != 6 { 162 | t.Fatal("client is supposed to write 6 bytes") 163 | } 164 | // then read `hello + (i+1)` 165 | n, err2 := clientSocket.Read(buf[:]) 166 | if err2 != nil { 167 | t.Fatal("server can't read on socket") 168 | } 169 | if n != 6 { 170 | t.Fatal("server is supposed to read 6 bytes") 171 | } 172 | if !bytes.Equal(buf[:n], message) { 173 | t.Fatal("received message not as expected") 174 | } 175 | } 176 | 177 | // 178 | clientSocket.Close() 179 | listener.Close() 180 | } 181 | 182 | func TestRemotePublicKey(t *testing.T) { 183 | // init 184 | clientConfig := Config{ 185 | KeyPair: GenerateKeypair(nil), 186 | HandshakePattern: NoiseXX, 187 | StaticPublicKeyProof: []byte{}, 188 | PublicKeyVerifier: verifier, 189 | } 190 | serverConfig := Config{ 191 | KeyPair: GenerateKeypair(nil), 192 | HandshakePattern: NoiseXX, 193 | StaticPublicKeyProof: []byte{}, 194 | PublicKeyVerifier: verifier, 195 | RemoteAddrContainsRemotePubkey: true, 196 | } 197 | 198 | // get a libdisco.listener 199 | listener, err := Listen("tcp", "127.0.0.1:0", &serverConfig) // port 0 will find out a free port 200 | if err != nil { 201 | t.Fatal("cannot setup a listener on localhost:", err) 202 | } 203 | defer listener.Close() 204 | addr := listener.Addr().String() 205 | 206 | // run the server and Accept one connection 207 | go func(t *testing.T) { 208 | serverSocket, err2 := listener.Accept() 209 | if err2 != nil { 210 | t.Fatal("a server cannot accept()") 211 | } 212 | 213 | var buf [100]byte 214 | 215 | n, err2 := serverSocket.Read(buf[:]) 216 | if err2 != nil { 217 | if err2 == io.EOF { 218 | return 219 | } 220 | t.Fatal("server can't read on socket") 221 | } 222 | // check if the RemoteAddr returns ip:port:pubkey (which is the msg being sent) 223 | a := strings.Split(serverSocket.RemoteAddr().String(), ":") 224 | if a[2] != string(buf[:n]) { 225 | t.Fatal("received message not as expected") 226 | } 227 | }(t) 228 | 229 | // Run the client 230 | clientSocket, err := Dial("tcp", addr, &clientConfig) 231 | if err != nil { 232 | t.Fatal("client can't connect to server") 233 | } 234 | 235 | // need to send a message for the handshake to start 236 | _, err = clientSocket.Write([]byte(clientConfig.KeyPair.ExportPublicKey())) 237 | if err != nil { 238 | t.Fatal("client can't write on socket") 239 | } 240 | clientSocket.Close() 241 | 242 | } 243 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/App.vue: -------------------------------------------------------------------------------- 1 | 94 | 95 | 100 | 101 | 163 | 186 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/Noise_XX.vue: -------------------------------------------------------------------------------- 1 | 143 | 144 | 162 | -------------------------------------------------------------------------------- /web/discocrypto.com/src/components/handshakes/Noise_NX.vue: -------------------------------------------------------------------------------- 1 | 145 | 146 | 165 | -------------------------------------------------------------------------------- /libdisco/patterns.go: -------------------------------------------------------------------------------- 1 | package libdisco 2 | 3 | // 4 | // Handshake Patterns 5 | // 6 | 7 | type noiseHandshakeType int8 8 | 9 | const ( 10 | // NoiseUnknown is for specifying an unknown pattern 11 | NoiseUnknown noiseHandshakeType = iota 12 | // NoiseN is a one-way pattern where a client can send 13 | // data to a server with a known static key. The server 14 | // can only receive data and cannot reply back. 15 | NoiseN 16 | 17 | // NoiseK is a one-way pattern where a client can send 18 | // data to a server with a known static key. The server 19 | // can only receive data and cannot reply back. The server 20 | // authenticates the client via a known key. 21 | NoiseK 22 | 23 | // NoiseX is a one-way pattern where a client can send 24 | // data to a server with a known static key. The server 25 | // can only receive data and cannot reply back. The server 26 | // authenticates the client via a key transmitted as part 27 | // of the handshake. 28 | NoiseX 29 | 30 | // NoiseKK is a pattern where both the client static key and the 31 | // server static key are known. 32 | NoiseKK 33 | 34 | // NoiseNX is a "HTTPS"-like pattern where the client is 35 | // not authenticated, and the static public key of the server 36 | // is transmitted during the handshake. It is the responsability of the client to validate the received key properly. 37 | NoiseNX 38 | 39 | // NoiseNK is a "Public Key Pinning"-like pattern where the client 40 | // is not authenticated, and the static public key of the server 41 | // is already known. 42 | NoiseNK 43 | 44 | // NoiseXX is a pattern where both static keys are transmitted. 45 | // It is the responsability of the server and of the client to 46 | // validate the received keys properly. 47 | NoiseXX 48 | 49 | // NoiseKX Not documented 50 | NoiseKX 51 | // NoiseXK Not documented 52 | NoiseXK 53 | // NoiseIK Not documented 54 | NoiseIK 55 | // NoiseIX Not documented 56 | NoiseIX 57 | // NoiseNNpsk2 Not documented 58 | NoiseNNpsk2 59 | 60 | // NoiseNN Not implemented 61 | NoiseNN 62 | // NoiseKN Not implemented 63 | NoiseKN 64 | // NoiseXN Not implemented 65 | NoiseXN 66 | // NoiseIN Not implemented 67 | NoiseIN 68 | ) 69 | 70 | type token uint8 71 | 72 | const ( 73 | tokenUnknown token = iota 74 | token_e 75 | token_s 76 | token_es 77 | token_se 78 | token_ss 79 | token_ee 80 | token_psk 81 | ) 82 | 83 | type messagePattern []token 84 | 85 | type handshakePattern struct { 86 | name string 87 | preMessagePatterns []messagePattern 88 | messagePatterns []messagePattern 89 | } 90 | 91 | // TODO: add more patterns 92 | var patterns = map[noiseHandshakeType]handshakePattern{ 93 | 94 | // 7.2. One-way patterns 95 | 96 | NoiseN: handshakePattern{ 97 | name: "N", 98 | preMessagePatterns: []messagePattern{ 99 | messagePattern{}, // → 100 | messagePattern{token_s}, // ← 101 | }, 102 | messagePatterns: []messagePattern{ 103 | messagePattern{token_e, token_es}, // → 104 | }, 105 | }, 106 | 107 | /* 108 | K(s, rs): 109 | -> s 110 | <- s 111 | ... 112 | -> e, es, ss 113 | */ 114 | NoiseK: handshakePattern{ 115 | name: "K", 116 | preMessagePatterns: []messagePattern{ 117 | messagePattern{token_s}, // → 118 | messagePattern{token_s}, // ← 119 | }, 120 | messagePatterns: []messagePattern{ 121 | messagePattern{token_e, token_es, token_ss}, // → 122 | }, 123 | }, 124 | /* 125 | X(s, rs): 126 | <- s 127 | ... 128 | -> e, es, s, ss 129 | */ 130 | NoiseX: handshakePattern{ 131 | name: "X", 132 | preMessagePatterns: []messagePattern{ 133 | messagePattern{}, // → 134 | messagePattern{token_s}, // ← 135 | }, 136 | messagePatterns: []messagePattern{ 137 | messagePattern{token_e, token_es, token_s, token_ss}, // → 138 | }, 139 | }, 140 | // 141 | // 7.3. Interactive patterns 142 | // 143 | NoiseKK: handshakePattern{ 144 | name: "KK", 145 | preMessagePatterns: []messagePattern{ 146 | messagePattern{token_s}, // → 147 | messagePattern{token_s}, // ← 148 | }, 149 | messagePatterns: []messagePattern{ 150 | messagePattern{token_e, token_es, token_ss}, // → 151 | messagePattern{token_e, token_ee, token_se}, // ← 152 | }, 153 | }, 154 | 155 | NoiseNX: handshakePattern{ 156 | name: "NX", 157 | preMessagePatterns: []messagePattern{ 158 | messagePattern{}, // → 159 | messagePattern{}, // ← 160 | }, 161 | messagePatterns: []messagePattern{ 162 | messagePattern{token_e}, // → 163 | messagePattern{token_e, token_ee, token_s, token_es}, // ← 164 | }, 165 | }, 166 | 167 | NoiseNK: handshakePattern{ 168 | name: "NK", 169 | preMessagePatterns: []messagePattern{ 170 | messagePattern{}, // → 171 | messagePattern{token_s}, // ← 172 | }, 173 | messagePatterns: []messagePattern{ 174 | messagePattern{token_e, token_es}, // → 175 | messagePattern{token_e, token_ee}, // ← 176 | }, 177 | }, 178 | 179 | NoiseXX: handshakePattern{ 180 | name: "XX", 181 | preMessagePatterns: []messagePattern{ 182 | messagePattern{}, // → 183 | messagePattern{}, // ← 184 | }, 185 | messagePatterns: []messagePattern{ 186 | messagePattern{token_e}, // → 187 | messagePattern{token_e, token_ee, token_s, token_es}, // ← 188 | messagePattern{token_s, token_se}, // → 189 | }, 190 | }, 191 | 192 | /* 193 | KX(s, rs): 194 | -> s 195 | ... 196 | -> e 197 | <- e, ee, se, s, es 198 | */ 199 | NoiseKX: handshakePattern{ 200 | name: "KX", 201 | preMessagePatterns: []messagePattern{ 202 | messagePattern{token_s}, // → 203 | messagePattern{}, // ← 204 | }, 205 | messagePatterns: []messagePattern{ 206 | messagePattern{token_e}, // → 207 | messagePattern{token_e, token_ee, token_se, token_s, token_es}, // ← 208 | }, 209 | }, 210 | /* 211 | XK(s, rs): 212 | <- s 213 | ... 214 | -> e, es 215 | <- e, ee 216 | -> s, se 217 | */ 218 | NoiseXK: handshakePattern{ 219 | name: "XK", 220 | preMessagePatterns: []messagePattern{ 221 | messagePattern{}, // → 222 | messagePattern{token_s}, // ← 223 | }, 224 | messagePatterns: []messagePattern{ 225 | messagePattern{token_e, token_es}, // → 226 | messagePattern{token_e, token_ee}, // ← 227 | messagePattern{token_s, token_se}, // → 228 | }, 229 | }, 230 | /* 231 | IK(s, rs): 232 | <- s 233 | ... 234 | -> e, es, s, ss 235 | <- e, ee, se 236 | */ 237 | NoiseIK: handshakePattern{ 238 | name: "IK", 239 | preMessagePatterns: []messagePattern{ 240 | messagePattern{}, // → 241 | messagePattern{token_s}, // ← 242 | }, 243 | messagePatterns: []messagePattern{ 244 | messagePattern{token_e, token_es, token_s, token_ss}, // → 245 | messagePattern{token_e, token_ee, token_se}, // ← 246 | }, 247 | }, 248 | /* 249 | IX(s, rs): 250 | -> e, s 251 | <- e, ee, se, s, es 252 | */ 253 | NoiseIX: handshakePattern{ 254 | name: "IX", 255 | preMessagePatterns: []messagePattern{ 256 | messagePattern{}, // → 257 | messagePattern{}, // ← 258 | }, 259 | messagePatterns: []messagePattern{ 260 | messagePattern{token_e, token_s}, // → 261 | messagePattern{token_e, token_ee, token_se, token_s, token_es}, // ← 262 | }, 263 | }, 264 | 265 | /* 266 | NNpsk2(): 267 | -> e 268 | <- e, ee, psk 269 | */ 270 | NoiseNNpsk2: handshakePattern{ 271 | name: "NNpsk2", 272 | preMessagePatterns: []messagePattern{ 273 | messagePattern{}, // → 274 | messagePattern{}, // ← 275 | }, 276 | messagePatterns: []messagePattern{ 277 | messagePattern{token_e}, // → 278 | messagePattern{token_e, token_ee, token_psk}, // ← 279 | }, 280 | }, 281 | } 282 | -------------------------------------------------------------------------------- /libdisco/patterns_test.go: -------------------------------------------------------------------------------- 1 | package libdisco 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "testing" 7 | 8 | "golang.org/x/crypto/ed25519" 9 | ) 10 | 11 | var rootKey struct { 12 | privateKey ed25519.PrivateKey 13 | publicKey ed25519.PublicKey 14 | } 15 | 16 | var publicKeyVerifier func([]byte, []byte) bool 17 | 18 | func init() { 19 | rootKey.publicKey, rootKey.privateKey, _ = ed25519.GenerateKey(rand.Reader) 20 | 21 | publicKeyVerifier = CreatePublicKeyVerifier(rootKey.publicKey) 22 | } 23 | 24 | func TestNoiseKK(t *testing.T) { 25 | 26 | // init 27 | clientConfig := Config{ 28 | KeyPair: GenerateKeypair(nil), 29 | HandshakePattern: NoiseKK, 30 | } 31 | serverConfig := Config{ 32 | KeyPair: GenerateKeypair(nil), 33 | HandshakePattern: NoiseKK, 34 | } 35 | 36 | // set up remote keys 37 | serverConfig.RemoteKey = clientConfig.KeyPair.PublicKey[:] 38 | clientConfig.RemoteKey = serverConfig.KeyPair.PublicKey[:] 39 | 40 | // get a Noise.listener 41 | listener, err := Listen("tcp", "127.0.0.1:0", &serverConfig) // port 0 will find out a free port 42 | if err != nil { 43 | t.Fatal("cannot setup a listener on localhost:", err) 44 | } 45 | addr := listener.Addr().String() 46 | 47 | // run the server and Accept one connection 48 | go func() { 49 | serverSocket, err := listener.Accept() 50 | if err != nil { 51 | t.Fatal("a server cannot accept()") 52 | } 53 | var buf [100]byte 54 | n, err := serverSocket.Read(buf[:]) 55 | if err != nil { 56 | t.Fatal("server can't read on socket") 57 | } 58 | if !bytes.Equal(buf[:n], []byte("hello")) { 59 | t.Fatal("client message failed") 60 | } 61 | 62 | if _, err = serverSocket.Write([]byte("ca va?")); err != nil { 63 | t.Fatal("server can't write on socket") 64 | } 65 | 66 | }() 67 | 68 | // Run the client 69 | clientSocket, err := Dial("tcp", addr, &clientConfig) 70 | if err != nil { 71 | t.Fatal("client can't connect to server", err) 72 | } 73 | _, err = clientSocket.Write([]byte("hello")) 74 | if err != nil { 75 | t.Fatal("client can't write on socket") 76 | } 77 | var buf [100]byte 78 | n, err := clientSocket.Read(buf[:]) 79 | if err != nil { 80 | t.Fatal("client can't read server's answer") 81 | } 82 | if !bytes.Equal(buf[:n], []byte("ca va?")) { 83 | t.Fatal("server message failed") 84 | } 85 | } 86 | 87 | func TestNoiseNK(t *testing.T) { 88 | 89 | testPattern := NoiseNK 90 | 91 | // init 92 | clientConfig := Config{ 93 | KeyPair: GenerateKeypair(nil), 94 | HandshakePattern: testPattern, 95 | } 96 | serverConfig := Config{ 97 | KeyPair: GenerateKeypair(nil), 98 | HandshakePattern: testPattern, 99 | } 100 | 101 | // setup remote key 102 | clientConfig.RemoteKey = serverConfig.KeyPair.PublicKey[:] 103 | 104 | // get a Noise.listener 105 | listener, err := Listen("tcp", "127.0.0.1:0", &serverConfig) // port 0 will find out a free port 106 | if err != nil { 107 | t.Fatal("cannot setup a listener on localhost:", err) 108 | } 109 | addr := listener.Addr().String() 110 | 111 | // run the server and Accept one connection 112 | go func() { 113 | serverSocket, err := listener.Accept() 114 | if err != nil { 115 | t.Fatal("a server cannot accept()") 116 | } 117 | var buf [100]byte 118 | n, err := serverSocket.Read(buf[:]) 119 | if err != nil { 120 | t.Fatal("server can't read on socket") 121 | } 122 | if !bytes.Equal(buf[:n], []byte("hello")) { 123 | t.Fatal("client message failed") 124 | } 125 | 126 | if _, err = serverSocket.Write([]byte("ca va?")); err != nil { 127 | t.Fatal("server can't write on socket") 128 | } 129 | 130 | }() 131 | 132 | // Run the client 133 | clientSocket, err := Dial("tcp", addr, &clientConfig) 134 | if err != nil { 135 | t.Fatal("client can't connect to server", err) 136 | } 137 | _, err = clientSocket.Write([]byte("hello")) 138 | if err != nil { 139 | t.Fatal("client can't write on socket") 140 | } 141 | var buf [100]byte 142 | n, err := clientSocket.Read(buf[:]) 143 | if err != nil { 144 | t.Fatal("client can't read server's answer") 145 | } 146 | if !bytes.Equal(buf[:n], []byte("ca va?")) { 147 | t.Fatal("server message failed") 148 | } 149 | } 150 | 151 | func TestNoiseXX(t *testing.T) { 152 | 153 | // init 154 | clientKeyPair := GenerateKeypair(nil) 155 | clientConfig := Config{ 156 | KeyPair: clientKeyPair, 157 | HandshakePattern: NoiseXX, 158 | PublicKeyVerifier: publicKeyVerifier, 159 | StaticPublicKeyProof: CreateStaticPublicKeyProof(rootKey.privateKey, clientKeyPair.PublicKey[:]), 160 | } 161 | serverKeyPair := GenerateKeypair(nil) 162 | serverConfig := Config{ 163 | KeyPair: serverKeyPair, 164 | HandshakePattern: NoiseXX, 165 | PublicKeyVerifier: publicKeyVerifier, 166 | StaticPublicKeyProof: CreateStaticPublicKeyProof(rootKey.privateKey, serverKeyPair.PublicKey[:]), 167 | } 168 | 169 | // get a Noise.listener 170 | listener, err := Listen("tcp", "127.0.0.1:0", &serverConfig) // port 0 will find out a free port 171 | if err != nil { 172 | t.Fatal("cannot setup a listener on localhost:", err) 173 | } 174 | addr := listener.Addr().String() 175 | 176 | // run the server and Accept one connection 177 | go func() { 178 | serverSocket, err := listener.Accept() 179 | if err != nil { 180 | t.Fatal("a server cannot accept()") 181 | } 182 | var buf [100]byte 183 | n, err := serverSocket.Read(buf[:]) 184 | if err != nil { 185 | t.Fatal("server can't read on socket") 186 | } 187 | if !bytes.Equal(buf[:n], []byte("hello")) { 188 | t.Fatal("client message failed") 189 | } 190 | 191 | if _, err = serverSocket.Write([]byte("ca va?")); err != nil { 192 | t.Fatal("server can't write on socket") 193 | } 194 | 195 | }() 196 | 197 | // Run the client 198 | clientSocket, err := Dial("tcp", addr, &clientConfig) 199 | if err != nil { 200 | t.Fatal("client can't connect to server", err) 201 | } 202 | _, err = clientSocket.Write([]byte("hello")) 203 | if err != nil { 204 | t.Fatal("client can't write on socket") 205 | } 206 | var buf [100]byte 207 | n, err := clientSocket.Read(buf[:]) 208 | if err != nil { 209 | t.Fatal("client can't read server's answer") 210 | } 211 | if !bytes.Equal(buf[:n], []byte("ca va?")) { 212 | t.Fatal("server message failed") 213 | } 214 | } 215 | 216 | func TestNoiseN(t *testing.T) { 217 | 218 | // init 219 | serverConfig := Config{ 220 | KeyPair: GenerateKeypair(nil), 221 | HandshakePattern: NoiseN, 222 | } 223 | 224 | clientConfig := Config{ 225 | KeyPair: GenerateKeypair(nil), 226 | HandshakePattern: NoiseN, 227 | RemoteKey: serverConfig.KeyPair.PublicKey[:], 228 | } 229 | 230 | // get a Noise.listener 231 | listener, err := Listen("tcp", "127.0.0.1:0", &serverConfig) // port 0 will find out a free port 232 | if err != nil { 233 | t.Fatal("cannot setup a listener on localhost:", err) 234 | } 235 | addr := listener.Addr().String() 236 | 237 | // run the server and Accept one connection 238 | go func() { 239 | serverSocket, err2 := listener.Accept() 240 | if err2 != nil { 241 | t.Fatal("a server cannot accept()") 242 | } 243 | var buf [100]byte 244 | n, err2 := serverSocket.Read(buf[:]) 245 | if err2 != nil { 246 | t.Fatal("server can't read on socket") 247 | } 248 | if !bytes.Equal(buf[:n], []byte("hello")) { 249 | t.Fatal("client message failed") 250 | } 251 | 252 | /* TODO: test that this fails 253 | if _, err = serverSocket.Write([]byte("ca va?")); err != nil { 254 | t.Fatal("server can't write on socket") 255 | } 256 | */ 257 | 258 | }() 259 | 260 | // Run the client 261 | clientSocket, err := Dial("tcp", addr, &clientConfig) 262 | if err != nil { 263 | t.Fatal("client can't connect to server") 264 | } 265 | _, err = clientSocket.Write([]byte("hello")) 266 | if err != nil { 267 | t.Fatal("client can't write on socket") 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /libdisco/symmetric_test.go: -------------------------------------------------------------------------------- 1 | package libdisco 2 | 3 | import ( 4 | "encoding/hex" 5 | "testing" 6 | ) 7 | 8 | func TestHash(t *testing.T) { 9 | 10 | input := []byte("hi, how are you?") 11 | 12 | if hex.EncodeToString(Hash(input, 32)) != "eda8506c1fb0bbcc3f62626fef074bbf2d09a8c7c608f3fa1482c9a625d00f75" { 13 | t.Fatal("Hash does not produce a correct output") 14 | } 15 | } 16 | 17 | func TestSum(t *testing.T) { 18 | message1 := "hello" 19 | message2 := "how are you good sir?" 20 | message3 := "sure thing" 21 | fullmessage := message1 + message2 22 | 23 | // trying with NewHash with streaming and without streaming 24 | h1 := NewHash(32) 25 | h1.Write([]byte(message1)) 26 | h1.Write([]byte(message2)) 27 | out1 := h1.Sum() 28 | 29 | h2 := NewHash(32) 30 | h2.Write([]byte(fullmessage)) 31 | out2 := h2.Sum() 32 | 33 | for idx := range out1 { 34 | if out1[idx] != out2[idx] { 35 | t.Fatal("Sum function does not work") 36 | } 37 | } 38 | 39 | // trying with Hash() 40 | out3 := Hash([]byte(fullmessage), 32) 41 | 42 | for idx := range out1 { 43 | if out1[idx] != out3[idx] { 44 | t.Fatal("Sum function does not work") 45 | } 46 | } 47 | 48 | // trying the streaming even more 49 | h1.Write([]byte(message3)) 50 | out1 = h1.Sum() 51 | h2.Write([]byte(message3)) 52 | out2 = h2.Sum() 53 | 54 | for idx := range out1 { 55 | if out1[idx] != out2[idx] { 56 | t.Fatal("Sum function does not work") 57 | } 58 | } 59 | 60 | // tring with Hash() 61 | out3 = Hash([]byte(fullmessage+message3), 32) 62 | 63 | for idx := range out1 { 64 | if out1[idx] != out3[idx] { 65 | t.Fatal("Sum function does not work") 66 | } 67 | } 68 | } 69 | 70 | func TestHashOutputHashOutput(t *testing.T) { 71 | message1 := "hello" 72 | message2 := "how are you good sir?" 73 | message3 := "sure thing" 74 | 75 | h1 := NewHash(32) 76 | h1.Write([]byte(message1)) 77 | h1.Write([]byte(message2)) 78 | h1.Sum() // this should not affect the state 79 | h1.Write([]byte(message3)) 80 | out1 := h1.Sum() 81 | 82 | h2 := NewHash(32) 83 | h2.Write([]byte(message1)) 84 | h2.Write([]byte(message2)) 85 | h2.Write([]byte(message3)) 86 | out2 := h2.Sum() 87 | 88 | for idx := range out1 { 89 | if out1[idx] != out2[idx] { 90 | t.Fatal("Sum function affects the hash state") 91 | } 92 | } 93 | } 94 | 95 | func TestTupleHash(t *testing.T) { 96 | message1 := "the plasma" 97 | message2 := "screen is broken, we need to do something about it!" 98 | message3 := "\x00\x01\x02\x03\x04\x05\x00\x01\x02\x03\x04\x05\x00\x01\x02\x03\x04\x05\x00\x01\x02\x03\x04\x05\x00\x01\x02\x03\x04\x05\x00\x01\x02\x03\x04\x05" 99 | message4 := "HAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHAHA" 100 | 101 | // trying with NewHash with streaming and without streaming 102 | h1 := NewHash(32) 103 | h1.Write([]byte(message1)) 104 | h1.Write([]byte(message2)) 105 | h1.Write([]byte(message3)) 106 | out1 := h1.Sum() 107 | 108 | h2 := NewHash(32) 109 | h2.WriteTuple([]byte(message1)) 110 | h1.WriteTuple([]byte(message2)) 111 | h1.WriteTuple([]byte(message3)) 112 | out2 := h2.Sum() 113 | 114 | same := true 115 | for idx := range out1 { 116 | if out1[idx] != out2[idx] { 117 | same = false 118 | break 119 | } 120 | } 121 | if same { 122 | t.Fatal("Tuple hashing should be different from stream hashing") 123 | } 124 | 125 | // trying a hybrid with streaming 126 | h3 := NewHash(32) 127 | h3.WriteTuple([]byte(message1)) 128 | h3.Write([]byte(message2)) 129 | h3.Write([]byte(message3)) 130 | h3.WriteTuple([]byte(message4)) 131 | out3 := h3.Sum() 132 | 133 | h4 := NewHash(32) 134 | h4.WriteTuple([]byte(message1)) 135 | h4.WriteTuple([]byte(message2 + message3)) 136 | h4.WriteTuple([]byte(message4)) 137 | out4 := h4.Sum() 138 | 139 | for idx := range out3 { 140 | if out3[idx] != out4[idx] { 141 | t.Fatal("Tuple hashing doesn't work properly with streaming") 142 | } 143 | } 144 | } 145 | 146 | func TestDeriveKeys(t *testing.T) { 147 | 148 | input := []byte("hi, how are you?") 149 | 150 | if hex.EncodeToString(DeriveKeys(input, 64)) != "d6350bb9b83884774fb9b0881680fc656be1071fff75d3fa94519d50a10b92644e3cc1cae166a60167d7bf00137018345bb8057be4b09f937b0e12066d5dc3df" { 151 | t.Fatal("DeriveKeys does not produce a correct output") 152 | } 153 | } 154 | 155 | func TestProtectVerifyIntegrity(t *testing.T) { 156 | key, _ := hex.DecodeString("eda8506c1fb0bbcc3f62626fef074bbf2d09a8c7c608f3fa1482c9a625d00f75") 157 | 158 | message := []byte("hoy, how are you?") 159 | 160 | plaintextAndTag := ProtectIntegrity(key, message) 161 | 162 | retrievedMessage, err := VerifyIntegrity(key, plaintextAndTag) 163 | 164 | if err != nil { 165 | t.Fatal("Protect/Verify did not work") 166 | } 167 | for idx := range message { 168 | if message[idx] != retrievedMessage[idx] { 169 | t.Fatal("Verify did not work") 170 | } 171 | } 172 | 173 | // tamper 174 | plaintextAndTag[len(plaintextAndTag)-1]++ 175 | 176 | _, err = VerifyIntegrity(key, plaintextAndTag) 177 | if err == nil { 178 | t.Fatal("Verify did not work") 179 | } 180 | 181 | } 182 | 183 | func TestNonceSize(t *testing.T) { 184 | key, _ := hex.DecodeString("eda8506c1fb0bbcc3f62626fef074bbf2d09a8c7c608f3fa1482c9a625d00f75") 185 | plaintext := []byte("hello, how are you?") 186 | ciphertext := Encrypt(key, plaintext) 187 | if len(ciphertext) != 19+16+24 { 188 | t.Fatal("Length of this ciphertext should be 19B (PT) + 16B (TAG) + 24B (NONCE)") 189 | } 190 | } 191 | 192 | func TestEncryptDecrypt(t *testing.T) { 193 | 194 | key, _ := hex.DecodeString("eda8506c1fb0bbcc3f62626fef074bbf2d09a8c7c608f3fa1482c9a625d00f75") 195 | plaintexts := []string{ 196 | "", 197 | "a", 198 | "ab", 199 | "abc", 200 | "abcd", 201 | "short", 202 | "hello, how are you?", 203 | "this is very short", 204 | "this is very long though, like, very very long, should we test very very long things here?", 205 | } 206 | for _, plaintext := range plaintexts { 207 | plaintextBytes := []byte(plaintext) 208 | ciphertext := Encrypt(key, plaintextBytes) 209 | decrypted, err := Decrypt(key, ciphertext) 210 | if err != nil { 211 | t.Fatal("Encrypt/Decrypt did not work") 212 | } 213 | if len(plaintext) != len(decrypted) { 214 | t.Fatal("Decrypt did not work") 215 | } 216 | for idx := range plaintext { 217 | if plaintext[idx] != decrypted[idx] { 218 | t.Fatal("Decrypt did not work") 219 | } 220 | } 221 | } 222 | } 223 | 224 | func TestEncryptDecryptAndAuthenticate(t *testing.T) { 225 | 226 | key, _ := hex.DecodeString("eda8506c1fb0bbcc3f62626fef074bbf2d09a8c7c608f3fa1482c9a625d00f75") 227 | plaintexts := []string{ 228 | "", 229 | "a", 230 | "ab", 231 | "abc", 232 | "abcd", 233 | "short", 234 | "hello, how are you?", 235 | "this is very short", 236 | "this is very long though, like, very very long, should we test very very long things here?", 237 | } 238 | ad := []string{ 239 | "blou blou", 240 | "a", 241 | "haahahAHAHAHhahaHAHAHahah so funny", 242 | "you must be fun at parties", 243 | "this is insanely long oh lala voulait dire le boulanger. C'est a dire que. Je ne sais pas. Merci.", 244 | "do I really need to do this? This is not fun anymore. Help me please. I am stuck in a keyboard and nobody knows I am here. This is getting quite uncomfortable", 245 | "bunch of \x00 and stuff \x00 you know", 246 | "89032", 247 | "9032ir9032kf9032fk093fewk90 fkwe09fk 903i2r 0932ir 0932ir 3029ir 230rk we0rkwe 09rkwer9 w0ekrw e09rkwe 09rew", 248 | } 249 | for idx, plaintext := range plaintexts { 250 | plaintextBytes := []byte(plaintext) 251 | adBytes := []byte(ad[idx]) 252 | ciphertext := EncryptAndAuthenticate(key, plaintextBytes, adBytes) 253 | decrypted, err := DecryptAndAuthenticate(key, ciphertext, adBytes) 254 | if err != nil { 255 | t.Fatal("Encrypt/Decrypt did not work") 256 | } 257 | if len(plaintext) != len(decrypted) { 258 | t.Fatal("Decrypt did not work") 259 | } 260 | for idx := range plaintext { 261 | if plaintext[idx] != decrypted[idx] { 262 | t.Fatal("Decrypt did not work") 263 | } 264 | } 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /libdisco/symmetric.go: -------------------------------------------------------------------------------- 1 | package libdisco 2 | 3 | import ( 4 | "crypto/rand" 5 | "errors" 6 | 7 | "github.com/mimoo/StrobeGo/strobe" 8 | ) 9 | 10 | const ( 11 | nonceSize = 192 / 8 12 | tagSize = 16 13 | minimumCiphertextSize = nonceSize + tagSize 14 | ) 15 | 16 | // Hash allows you to hash an input of any length and obtain an output of length greater or equal to 256 bits (32 bytes). 17 | func Hash(input []byte, outputLength int) []byte { 18 | if outputLength < 32 { 19 | panic("disco: an output length smaller than 256-bit (32 bytes) has security consequences") 20 | } 21 | hash := strobe.InitStrobe("DiscoHash", 128) 22 | hash.AD(false, input) 23 | return hash.PRF(outputLength) 24 | } 25 | 26 | // DiscoHash represents a strobe hash 27 | type DiscoHash struct { 28 | strobeState strobe.Strobe 29 | streaming bool 30 | outputLength int 31 | } 32 | 33 | // NewHash creates a new hashing state, allowing you to absorb data to hash via Write or WriteTuple, 34 | // and to create a digest via the Sum function. 35 | func NewHash(outputLength int) DiscoHash { 36 | if outputLength < 32 { 37 | panic("disco: an output length smaller than 256-bit (32 bytes) has security consequences") 38 | } 39 | return DiscoHash{strobeState: strobe.InitStrobe("DiscoHash", 128), outputLength: outputLength} 40 | } 41 | 42 | // Write absorbs more data into the hash's state. This function is usually called to hash contigous chunks 43 | // of data. For structured data please refer to WriteTuple 44 | func (d *DiscoHash) Write(inputData []byte) (written int, err error) { 45 | d.strobeState.Operate(false, "AD", inputData, 0, d.streaming) 46 | d.streaming = true 47 | written = len(inputData) 48 | return 49 | } 50 | 51 | // WriteTuple absorbs more data to hash in a non-ambigious way. This means that data absorbed 52 | // via this function is separated from the data surrounding it. Use this function instead of Write 53 | // to hash structured data. 54 | func (d *DiscoHash) WriteTuple(inputData []byte) (written int, err error) { 55 | d.strobeState.Operate(false, "AD", inputData, 0, false) 56 | written = len(inputData) 57 | return 58 | } 59 | 60 | // Sum reads more output from the hash; reading affects the hash's 61 | // state. (DiscoHash.Read is thus very different from Hash.Sum) 62 | // It never returns an error. 63 | func (d *DiscoHash) Sum() []byte { 64 | reader := d.strobeState.Clone() 65 | return reader.Operate(false, "PRF", nil, d.outputLength, false) 66 | } 67 | 68 | // Clone returns a copy of the DiscoHash in its current state. 69 | func (d *DiscoHash) Clone() DiscoHash { 70 | cloned := d.strobeState.Clone() 71 | return DiscoHash{strobeState: *cloned} 72 | } 73 | 74 | // DeriveKeys allows you to derive keys 75 | func DeriveKeys(inputKey []byte, outputLength int) []byte { 76 | if len(inputKey) < 16 { 77 | panic("disco: deriving keys from a value smaller than 128-bit (16 bytes) has security consequences") 78 | } 79 | hash := strobe.InitStrobe("DiscoKDF", 128) 80 | hash.AD(false, inputKey) 81 | return hash.PRF(outputLength) 82 | } 83 | 84 | // ProtectIntegrity allows you to send a message in cleartext (not encrypted) 85 | // You can later verify via the VerifyIntegrity function that the message has not been modified 86 | func ProtectIntegrity(key, plaintext []byte) []byte { 87 | if len(key) < 16 { 88 | panic("disco: using a key smaller than 128-bit (16 bytes) has security consequences") 89 | } 90 | hash := strobe.InitStrobe("DiscoMAC", 128) 91 | hash.AD(false, key) 92 | hash.AD(false, plaintext) 93 | return append(plaintext, hash.Send_MAC(false, tagSize)...) 94 | } 95 | 96 | // VerifyIntegrity allows you to retrieve a message created with the ProtectIntegrity function. 97 | // if it returns an error, it means that the message was altered. Otherwise it returns the original message. 98 | func VerifyIntegrity(key, plaintextAndTag []byte) ([]byte, error) { 99 | if len(key) < 16 { 100 | panic("disco: using a key smaller than 128-bit (16 bytes) has security consequences") 101 | } 102 | if len(plaintextAndTag) < tagSize { 103 | return nil, errors.New("disco: plaintext does not contain an integrity tag") 104 | } 105 | offset := len(plaintextAndTag) - tagSize 106 | plaintext := plaintextAndTag[:offset] 107 | tag := plaintextAndTag[offset:] 108 | 109 | hash := strobe.InitStrobe("DiscoMAC", 128) 110 | hash.AD(false, key) 111 | hash.AD(false, plaintext) 112 | 113 | // verify tag 114 | if !hash.Recv_MAC(false, tag) { 115 | return nil, errors.New("disco: the plaintext has been modified") 116 | } 117 | 118 | return plaintext, nil 119 | } 120 | 121 | // Encrypt allows you to encrypt a plaintext message with a key of any size greater than 128 bits (16 bytes). 122 | func Encrypt(key, plaintext []byte) []byte { 123 | if len(key) < 16 { 124 | panic("disco: using a key smaller than 128-bit (16 bytes) has security consequences") 125 | } 126 | ae := strobe.InitStrobe("DiscoAE", 128) 127 | // absorb the key 128 | ae.AD(false, key) 129 | // generate 192-bit nonce 130 | var nonce [nonceSize]byte 131 | _, err := rand.Read(nonce[:]) 132 | if err != nil { 133 | panic("disco: golang's random function is not working") 134 | } 135 | // absorb the nonce 136 | ae.AD(false, nonce[:]) 137 | // nonce + send_ENC(plaintext) + send_MAC(16) 138 | ciphertext := append(nonce[:], ae.Send_ENC_unauthenticated(false, plaintext)...) 139 | ciphertext = append(ciphertext, ae.Send_MAC(false, tagSize)...) 140 | // 141 | return ciphertext 142 | } 143 | 144 | // Decrypt allows you to decrypt a message that was encrypted with the Encrypt function. 145 | func Decrypt(key, ciphertext []byte) ([]byte, error) { 146 | if len(key) < 16 { 147 | return nil, errors.New("disco: using a key smaller than 128-bit (16 bytes) has security consequences") 148 | } 149 | if len(ciphertext) < minimumCiphertextSize { 150 | return nil, errors.New("disco: ciphertext is too small, it should contain at a minimum a 192-bit nonce and a 128-bit tag") 151 | } 152 | // instantiate 153 | ae := strobe.InitStrobe("DiscoAE", 128) 154 | // absorb the key 155 | ae.AD(false, key) 156 | // absorb the nonce 157 | ae.AD(false, ciphertext[:nonceSize]) 158 | // decrypt 159 | plaintext := ae.Recv_ENC_unauthenticated(false, ciphertext[nonceSize:len(ciphertext)-tagSize]) 160 | // verify tag 161 | ok := ae.Recv_MAC(false, ciphertext[len(ciphertext)-tagSize:]) 162 | if !ok { 163 | return nil, errors.New("disco: cannot decrypt the payload") 164 | } 165 | return plaintext, nil 166 | } 167 | 168 | // EncryptAndAuthenticate allows you to encrypt a plaintext message with a key of any size greater than 128 bits (16 bytes). 169 | func EncryptAndAuthenticate(key, plaintext, ad []byte) []byte { 170 | if len(key) < 16 { 171 | panic("disco: using a key smaller than 128-bit (16 bytes) has security consequences") 172 | } 173 | ae := strobe.InitStrobe("DiscoAEAD", 128) 174 | // absorb the key 175 | ae.AD(false, key) 176 | // absorb the AD 177 | ae.AD(false, ad) 178 | // generate 192-bit nonce 179 | var nonce [nonceSize]byte 180 | _, err := rand.Read(nonce[:]) 181 | if err != nil { 182 | panic("disco: golang's random function is not working") 183 | } 184 | // absorb the nonce 185 | ae.AD(false, nonce[:]) 186 | // nonce + send_ENC(plaintext) + send_MAC(16) 187 | ciphertext := append(nonce[:], ae.Send_ENC_unauthenticated(false, plaintext)...) 188 | ciphertext = append(ciphertext, ae.Send_MAC(false, tagSize)...) 189 | // 190 | return ciphertext 191 | } 192 | 193 | // DecryptAndAuthenticate allows you to decrypt a message that was encrypted with the Encrypt function. 194 | func DecryptAndAuthenticate(key, ciphertext, ad []byte) ([]byte, error) { 195 | if len(key) < 16 { 196 | return nil, errors.New("disco: using a key smaller than 128-bit (16 bytes) has security consequences") 197 | } 198 | if len(ciphertext) < minimumCiphertextSize { 199 | return nil, errors.New("disco: ciphertext is too small, it should contain at a minimum a 192-bit nonce and a 128-bit tag") 200 | } 201 | // instantiate 202 | ae := strobe.InitStrobe("DiscoAEAD", 128) 203 | // absorb the key 204 | ae.AD(false, key) 205 | // absorb the AD 206 | ae.AD(false, ad) 207 | // absorb the nonce 208 | ae.AD(false, ciphertext[:nonceSize]) 209 | // decrypt 210 | plaintext := ae.Recv_ENC_unauthenticated(false, ciphertext[nonceSize:len(ciphertext)-tagSize]) 211 | // verify tag 212 | ok := ae.Recv_MAC(false, ciphertext[len(ciphertext)-tagSize:]) 213 | if !ok { 214 | return nil, errors.New("disco: cannot decrypt the payload") 215 | } 216 | return plaintext, nil 217 | } 218 | --------------------------------------------------------------------------------