diff --git a/src/crypto.coffee b/src/crypto.coffee
index 605a3ae..c8b7922 100644
--- a/src/crypto.coffee
+++ b/src/crypto.coffee
@@ -17,9 +17,34 @@ HMAC_SHA256 = (key, str) ->
SHA256 = (str) ->
crypto.subtle.digest "SHA-256", utf8Bytes str
+# For client-side encryption of files,
+# always use AES-128-GCM
+# Encrypt a File object
+# Returns hexed key, iv, encrypted file name and mime type, and the encrypted ArrayBuffer
+encryptFile = (file) ->
+ # Generate a key to use
+ keyParams =
+ name: 'AES-GCM'
+ length: 128
+ keyUsage = ['encrypt', 'decrypt']
+ key = await crypto.subtle.generateKey keyParams, true, keyUsage
+ # Generate IV and configure the cipher
+ iv = crypto.getRandomValues new Uint8Array 16
+ algoParams =
+ name: 'AES-GCM'
+ iv: iv
+ tagLength: 128
+ # Encrypt
+ encrypted = await crypto.subtle.encrypt algoParams, key, await file.arrayBuffer()
+ name = hex await crypto.subtle.encrypt algoParams, key, utf8Bytes file.name
+ mime = 'binary/' + hex await crypto.subtle.encrypt algoParams, key, utf8Bytes file.type
+ exportedKey = hex await crypto.subtle.exportKey 'raw', key
+ [exportedKey, hex(iv), name, mime, encrypted]
+
export {
utf8Bytes,
hex,
HMAC_SHA256,
- SHA256
+ SHA256,
+ encryptFile
}
\ No newline at end of file
diff --git a/src/web/binaryUpload.coffee b/src/web/binaryUpload.coffee
index fa3bee8..063ee7c 100644
--- a/src/web/binaryUpload.coffee
+++ b/src/web/binaryUpload.coffee
@@ -1,6 +1,7 @@
import React from "react"
import { Redirect } from "react-router-dom"
import Dropzone from "react-dropzone"
+import * as crypto from "../crypto"
class BinaryUpload extends React.Component
constructor: (props) ->
@@ -10,14 +11,19 @@ class BinaryUpload extends React.Component
uploading: false
progress: 0
switchToText: false
+ encrypt: false
+ encrypting: false
onDrop: (files) =>
@setState
file: files[0]
doUpload: =>
+ key = null
+ iv = null
@setState
uploading: true
+ encrypting: @state.encrypt
progress: 0
# Due to the lack of progress feature in current Fetch API
# We have to use XHR for now. Dang.
@@ -30,16 +36,30 @@ class BinaryUpload extends React.Component
if xhr.readyState == XMLHttpRequest.DONE
@setState
uploading: false
+ encrypting: false
file: null
- @props.openDialog do ->
+ @props.openDialog do =>
if xhr.status == 200
-
- https://{window.location.hostname}{xhr.responseText}
+ url = if not @state.encrypt
+ xhr.responseText
+ else
+ xhr.responseText + "?crypt#" + key + "+" + iv
+
+ https://{window.location.hostname}{url}
else
xhr.responseText
- xhr.open 'PUT', '/paste/' + @state.file.name
- xhr.send @state.file
+
+ if not @state.encrypt
+ xhr.open 'PUT', '/paste/' + @state.file.name
+ xhr.send @state.file
+ else
+ [key, iv, name, mime, encrypted] = await crypto.encryptFile @state.file
+ xhr.open 'PUT', '/paste/' + name
+ xhr.setRequestHeader 'content-type', mime
+ xhr.send encrypted
+ @setState
+ encrypting: false
progressText: ->
txt = (@state.progress * 100).toFixed(2) + "%"
@@ -48,6 +68,10 @@ class BinaryUpload extends React.Component
else
txt
+ toggleEncrypt: =>
+ @setState (state, props) ->
+ { encrypt: not state.encrypt }
+
render: ->
if @state.switchToText
return