binaryUpload: implement file encryption

This commit is contained in:
Peter Cai 2020-02-19 09:33:04 +08:00
parent ed625596df
commit ec540f9718
No known key found for this signature in database
GPG Key ID: 71F5FB4E4F3FD54F
2 changed files with 64 additions and 6 deletions

View File

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

View File

@ -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
<a href={xhr.responseText} target="_blank">
https://{window.location.hostname}{xhr.responseText}
url = if not @state.encrypt
xhr.responseText
else
xhr.responseText + "?crypt#" + key + "+" + iv
<a href={url} target="_blank">
https://{window.location.hostname}{url}
</a>
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 <Redirect to="/paste/text" />
@ -72,6 +96,13 @@ class BinaryUpload extends React.Component
}
</Dropzone>
<div className="content-buttons">
<button
className="button-blue"
disabled={@state.uploading}
onClick={@toggleEncrypt}
>
{ "Encrypt: " + if @state.encrypt then "ON" else "OFF" }
</button>
<button
className="button-blue"
disabled={@state.uploading}
@ -87,6 +118,8 @@ class BinaryUpload extends React.Component
{
if not @state.uploading
"Upload"
else if @state.encrypting
"Encrypting"
else
@progressText()
}