Work on cryptokeys
This commit is contained in:
parent
56a6e7145f
commit
87a802e210
12 changed files with 637 additions and 83 deletions
|
|
@ -1,18 +1,20 @@
|
|||
export default class Crypto {
|
||||
constructor(key) {
|
||||
constructor(key) {//{{{
|
||||
if(key === null)
|
||||
throw new Error("No key provided")
|
||||
|
||||
if(typeof key === 'string')
|
||||
this.key = sjcl.codec.base64.toBits(base64_key)
|
||||
else
|
||||
this.key = key
|
||||
|
||||
this.aes = new sjcl.cipher.aes(this.key)
|
||||
}
|
||||
}//}}}
|
||||
|
||||
static generate_key() {
|
||||
static generate_key() {//{{{
|
||||
return sjcl.random.randomWords(8)
|
||||
}
|
||||
|
||||
static pass_to_key(pass, salt = null) {
|
||||
}//}}}
|
||||
static pass_to_key(pass, salt = null) {//{{{
|
||||
if(salt === null)
|
||||
salt = sjcl.random.randomWords(4) // 128 bits (16 bytes)
|
||||
let key = sjcl.misc.pbkdf2(pass, salt, 10000)
|
||||
|
|
@ -21,9 +23,9 @@ export default class Crypto {
|
|||
salt,
|
||||
key,
|
||||
}
|
||||
}
|
||||
}//}}}
|
||||
|
||||
encrypt(plaintext_data_in_bits, counter, return_encoded = true) {
|
||||
encrypt(plaintext_data_in_bits, counter, return_encoded = true) {//{{{
|
||||
// 8 bytes of random data, (1 word = 4 bytes) * 2
|
||||
// with 8 bytes of byte encoded counter is used as
|
||||
// IV to guarantee a non-repeated IV (which is a catastrophe).
|
||||
|
|
@ -51,9 +53,8 @@ export default class Crypto {
|
|||
)
|
||||
else
|
||||
return iv.concat(encrypted)
|
||||
}
|
||||
|
||||
decrypt(encrypted_base64_data) {
|
||||
}//}}}
|
||||
decrypt(encrypted_base64_data) {//{{{
|
||||
try {
|
||||
let encoded = sjcl.codec.base64.toBits(encrypted_base64_data)
|
||||
let iv = encoded.slice(0, 4) // in words (4 bytes), not bytes
|
||||
|
|
@ -65,5 +66,7 @@ export default class Crypto {
|
|||
else
|
||||
throw(err)
|
||||
}
|
||||
}
|
||||
}//}}}
|
||||
}
|
||||
|
||||
// vim: foldmethod=marker
|
||||
|
|
|
|||
|
|
@ -7,9 +7,13 @@ const html = htm.bind(h)
|
|||
export class Keys extends Component {
|
||||
constructor(props) {//{{{
|
||||
super(props)
|
||||
this.retrieveKeys()
|
||||
this.state = {
|
||||
create: false,
|
||||
}
|
||||
|
||||
props.nodeui.retrieveKeys()
|
||||
}//}}}
|
||||
render({ nodeui }) {//{{{
|
||||
render({ nodeui }, { create }) {//{{{
|
||||
let keys = nodeui.keys.value
|
||||
.sort((a,b)=>{
|
||||
if(a.description < b.description) return -1
|
||||
|
|
@ -20,30 +24,127 @@ export class Keys extends Component {
|
|||
html`<${KeyComponent} key=${`key-${key.ID}`} model=${key} />`
|
||||
)
|
||||
|
||||
let createButton = ''
|
||||
let createComponents = ''
|
||||
if(create) {
|
||||
createComponents = html`
|
||||
<div id="key-create">
|
||||
<h2>New key</h2>
|
||||
|
||||
<div class="fields">
|
||||
<input type="text" id="key-description" placeholder="Name" />
|
||||
|
||||
<input type="password" id="key-pass1" placeholder="Password" />
|
||||
<input type="password" id="key-pass2" placeholder="Repeat password" />
|
||||
|
||||
<textarea id="key-key" placeholder="Key"></textarea>
|
||||
|
||||
<div>
|
||||
<button class="generate" onclick=${()=>this.generateKey()}>Generate</button>
|
||||
<button class="create" onclick=${()=>this.createKey()}>Create</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
} else {
|
||||
createButton = html`<div style="margin-top: 16px;"><button onclick=${()=>this.setState({ create: true })}>Create new key</button></div>`
|
||||
}
|
||||
|
||||
return html`
|
||||
<div id="keys">
|
||||
<h1>Keys</h1>
|
||||
<h1>Encryption keys</h1>
|
||||
<p>
|
||||
Unlock a key by clicking its name. Lock it by clicking it again.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Copy the key and store it in a very secure place to have a way to access notes
|
||||
in case the password is forgotten, or database is corrupted.
|
||||
</p>
|
||||
|
||||
<p>Click "View key" after unlocking it.</p>
|
||||
|
||||
${createButton}
|
||||
${createComponents}
|
||||
|
||||
<h2>Keys</h2>
|
||||
<div class="key-list">
|
||||
${keys}
|
||||
</div>
|
||||
</div>`
|
||||
}//}}}
|
||||
|
||||
retrieveKeys() {//{{{
|
||||
window._app.current.request('/key/retrieve', {})
|
||||
.then(res=>{
|
||||
this.props.nodeui.keys.value = res.Keys.map(keyData=>new Key(keyData))
|
||||
})
|
||||
.catch(window._app.current.responseError)
|
||||
generateKey() {//{{{
|
||||
let keyTextarea = document.getElementById('key-key')
|
||||
let key = sjcl.codec.hex.fromBits(Crypto.generate_key()).replace(/(....)/g, '$1 ').trim()
|
||||
keyTextarea.value = key
|
||||
}//}}}
|
||||
validateNewKey() {//{{{
|
||||
let keyDescription = document.getElementById('key-description').value
|
||||
let keyTextarea = document.getElementById('key-key').value
|
||||
let pass1 = document.getElementById('key-pass1').value
|
||||
let pass2 = document.getElementById('key-pass2').value
|
||||
|
||||
if(keyDescription.trim() == '')
|
||||
throw new Error('The key has to have a description')
|
||||
|
||||
if(pass1.trim() == '' || pass1.length < 4)
|
||||
throw new Error('The password has to be at least 4 characters long.')
|
||||
|
||||
if(pass1 != pass2)
|
||||
throw new Error(`Passwords doesn't match`)
|
||||
|
||||
let cleanKey = keyTextarea.replace(/\s+/g, '')
|
||||
if(!cleanKey.match(/^[0-9a-f]{64}$/i))
|
||||
throw new Error('Invalid key - has to be 64 characters of 0-9 and A-F')
|
||||
}//}}}
|
||||
createKey() {//{{{
|
||||
try {
|
||||
this.validateNewKey()
|
||||
|
||||
let description = document.getElementById('key-description').value
|
||||
let keyAscii = document.getElementById('key-key').value
|
||||
let pass1 = document.getElementById('key-pass1').value
|
||||
|
||||
// Key in hex taken from user.
|
||||
let actual_key = sjcl.codec.hex.toBits(keyAscii.replace(/\s+/g, ''))
|
||||
|
||||
// Key generated from password, used to encrypt the actual key.
|
||||
let pass_gen = Crypto.pass_to_key(pass1)
|
||||
|
||||
let crypto = new Crypto(pass_gen.key)
|
||||
let encrypted_actual_key = crypto.encrypt(actual_key, 0x1n, false)
|
||||
|
||||
// Database value is salt + actual key, needed to generate the same key from the password.
|
||||
let db_encoded = sjcl.codec.hex.fromBits(
|
||||
pass_gen.salt.concat(encrypted_actual_key)
|
||||
)
|
||||
|
||||
// Create on server.
|
||||
window._app.current.request('/key/create', {
|
||||
description,
|
||||
key: db_encoded,
|
||||
})
|
||||
.then(res=>{
|
||||
let key = new Key(res.Key, this.props.nodeui.keyCounter)
|
||||
this.props.nodeui.keys.value = this.props.nodeui.keys.value.concat(key)
|
||||
})
|
||||
.catch(window._app.current.responseError)
|
||||
} catch(err) {
|
||||
alert(err.message)
|
||||
return
|
||||
}
|
||||
}//}}}
|
||||
}
|
||||
|
||||
export class Key {
|
||||
constructor(data) {//{{{
|
||||
this.ID = data.ID
|
||||
this.description = data.Description
|
||||
this.unlockedKey = data.Key
|
||||
this.key = null
|
||||
constructor(data, counter_callback) {//{{{
|
||||
this.ID = data.ID
|
||||
this.description = data.Description
|
||||
this.encryptedKey = data.Key
|
||||
this.key = null
|
||||
|
||||
this._counter_cbk = counter_callback
|
||||
|
||||
let hex_key = window.sessionStorage.getItem(`key-${this.ID}`)
|
||||
if(hex_key)
|
||||
|
|
@ -59,17 +160,26 @@ export class Key {
|
|||
window.sessionStorage.removeItem(`key-${this.ID}`)
|
||||
}//}}}
|
||||
unlock(password) {//{{{
|
||||
let db = sjcl.codec.hex.toBits(this.unlockedKey)
|
||||
let db = sjcl.codec.hex.toBits(this.encryptedKey)
|
||||
let salt = db.slice(0, 4)
|
||||
let pass_key = Crypto.pass_to_key(password, salt)
|
||||
let crypto = new Crypto(pass_key.key)
|
||||
this.key = crypto.decrypt(sjcl.codec.base64.fromBits(db.slice(4)))
|
||||
window.sessionStorage.setItem(`key-${this.ID}`, sjcl.codec.hex.fromBits(this.key))
|
||||
}//}}}
|
||||
async counter() {//{{{
|
||||
return this._counter_cbk()
|
||||
}//}}}
|
||||
}
|
||||
|
||||
export class KeyComponent extends Component {
|
||||
render({ model }) {//{{{
|
||||
constructor({ model }) {//{{{
|
||||
super({ model })
|
||||
this.state = {
|
||||
show_key: false,
|
||||
}
|
||||
}//}}}
|
||||
render({ model }, { show_key }) {//{{{
|
||||
let status = ''
|
||||
switch(model.status()) {
|
||||
case 'locked':
|
||||
|
|
@ -81,9 +191,24 @@ export class KeyComponent extends Component {
|
|||
break
|
||||
}
|
||||
|
||||
let hex_key = ''
|
||||
if(show_key) {
|
||||
if(model.status() == 'locked')
|
||||
hex_key = html`<div class="hex-key">Unlock key first</div>`
|
||||
else {
|
||||
let key = sjcl.codec.hex.fromBits(model.key)
|
||||
key = key.replace(/(....)/g, "$1 ").trim()
|
||||
hex_key = html`<div class="hex-key">${key}</div>`
|
||||
}
|
||||
}
|
||||
|
||||
let unlocked = model.status()=='unlocked'
|
||||
|
||||
return html`
|
||||
<div class="status" onclick=${()=>this.toggle()}>${status}</div>
|
||||
<div class="description" onclick=${()=>this.toggle()}>${model.description}</div>
|
||||
<div class="view" onclick=${()=>this.toggleViewKey()}>${unlocked ? 'View key' : ''}</div>
|
||||
${hex_key}
|
||||
`
|
||||
}//}}}
|
||||
toggle() {//{{{
|
||||
|
|
@ -108,6 +233,9 @@ export class KeyComponent extends Component {
|
|||
alert(err)
|
||||
}
|
||||
}//}}}
|
||||
toggleViewKey() {//{{{
|
||||
this.setState({ show_key: !this.state.show_key })
|
||||
}//}}}
|
||||
}
|
||||
|
||||
// vim: foldmethod=marker
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ import { h, Component, createRef } from 'preact'
|
|||
import htm from 'htm'
|
||||
import { signal } from 'preact/signals'
|
||||
import { Keys, Key } from 'key'
|
||||
import Crypto from 'crypto'
|
||||
const html = htm.bind(h)
|
||||
|
||||
export class NodeUI extends Component {
|
||||
constructor() {//{{{
|
||||
super()
|
||||
constructor(props) {//{{{
|
||||
super(props)
|
||||
this.menu = signal(false)
|
||||
this.node = signal(null)
|
||||
this.nodeContent = createRef()
|
||||
|
|
@ -53,13 +54,19 @@ export class NodeUI extends Component {
|
|||
let page = ''
|
||||
switch(this.page.value) {
|
||||
case 'node':
|
||||
if(node.ID > 0)
|
||||
if(node.ID > 0) {
|
||||
let padlock = ''
|
||||
if(node.CryptoKeyID > 0)
|
||||
padlock = html`<img src="/images/${window._VERSION}/padlock-black.svg" style="height: 24px;" />`
|
||||
page = html`
|
||||
${children.length > 0 ? html`<div class="child-nodes">${children}</div>` : html``}
|
||||
<div class="node-name">${node.Name}</div>
|
||||
<${NodeContent} key=${node.ID} content=${node.Content} ref=${this.nodeContent} />
|
||||
<div class="node-name">
|
||||
${node.Name} ${padlock}
|
||||
</div>
|
||||
<${NodeContent} key=${node.ID} node=${node} ref=${this.nodeContent} />
|
||||
<${NodeFiles} node=${this.node.value} />
|
||||
`
|
||||
}
|
||||
break
|
||||
|
||||
case 'upload':
|
||||
|
|
@ -84,8 +91,8 @@ export class NodeUI extends Component {
|
|||
<header class="${modified}" onclick=${()=>this.saveNode()}>
|
||||
<div class="tree"><img src="/images/${window._VERSION}/tree.svg" onclick=${()=>document.getElementById('app').classList.toggle('toggle-tree')} /></div>
|
||||
<div class="name">Notes</div>
|
||||
<div class="add" onclick=${evt=>this.createNode(evt)}>+</div>
|
||||
<div class="keys" onclick=${()=>this.showPage('keys')}><img src="/images/${window._VERSION}/padlock.svg" /></div>
|
||||
<div class="add" onclick=${evt=>this.createNode(evt)}><img src="/images/${window._VERSION}/add.svg" /></div>
|
||||
<div class="keys" onclick=${evt=>{ evt.stopPropagation(); this.showPage('keys')}}><img src="/images/${window._VERSION}/padlock.svg" /></div>
|
||||
<div class="menu" onclick=${evt=>this.showMenu(evt)}>☰</div>
|
||||
</header>
|
||||
|
||||
|
|
@ -96,7 +103,11 @@ export class NodeUI extends Component {
|
|||
${page}
|
||||
`
|
||||
}//}}}
|
||||
componentDidMount() {//{{{
|
||||
async componentDidMount() {//{{{
|
||||
// When rendered and fetching the node, keys could be needed in order to
|
||||
// decrypt the content.
|
||||
await this.retrieveKeys()
|
||||
|
||||
this.props.app.startNode.retrieve(node=>{
|
||||
this.node.value = node
|
||||
|
||||
|
|
@ -177,6 +188,7 @@ export class NodeUI extends Component {
|
|||
node.retrieve(node=>{
|
||||
this.props.app.nodeModified.value = false
|
||||
this.node.value = node
|
||||
this.showPage('node')
|
||||
|
||||
// Tree needs to know another node is selected, in order to render any
|
||||
// previously selected node not selected.
|
||||
|
|
@ -218,6 +230,29 @@ export class NodeUI extends Component {
|
|||
})
|
||||
}//}}}
|
||||
|
||||
async retrieveKeys() {//{{{
|
||||
return new Promise((resolve, reject)=>{
|
||||
this.props.app.request('/key/retrieve', {})
|
||||
.then(res=>{
|
||||
this.keys.value = res.Keys.map(keyData=>new Key(keyData, this.keyCounter))
|
||||
resolve(this.keys.value)
|
||||
})
|
||||
.catch(reject)
|
||||
})
|
||||
}//}}}
|
||||
keyCounter() {//{{{
|
||||
return window._app.current.request('/key/counter', {})
|
||||
.then(res=>BigInt(res.Counter))
|
||||
.catch(window._app.current.responseError)
|
||||
}//}}}
|
||||
getKey(id) {//{{{
|
||||
let keys = this.keys.value
|
||||
for(let i = 0; i < keys.length; i++)
|
||||
if(keys[i].ID == id)
|
||||
return keys[i]
|
||||
return null
|
||||
}//}}}
|
||||
|
||||
showPage(pg) {//{{{
|
||||
this.page.value = pg
|
||||
}//}}}
|
||||
|
|
@ -229,10 +264,18 @@ class NodeContent extends Component {
|
|||
this.contentDiv = createRef()
|
||||
this.state = {
|
||||
modified: false,
|
||||
//content: props.content,
|
||||
}
|
||||
}//}}}
|
||||
render({ content }) {//{{{
|
||||
render({ node }) {//{{{
|
||||
let content = ''
|
||||
try {
|
||||
content = node.content()
|
||||
} catch(err) {
|
||||
return html`
|
||||
<div id="node-content" class="node-content encrypted">${err.message}</div>
|
||||
`
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="grow-wrap">
|
||||
<textarea id="node-content" class="node-content" ref=${this.contentDiv} oninput=${()=>this.contentChanged()} required rows=1>${content}</textarea>
|
||||
|
|
@ -255,6 +298,18 @@ class NodeContent extends Component {
|
|||
if(textarea)
|
||||
textarea.parentNode.dataset.replicatedValue = textarea.value
|
||||
}//}}}
|
||||
unlock() {//{{{
|
||||
let pass = prompt("Password")
|
||||
if(!pass)
|
||||
return
|
||||
|
||||
try {
|
||||
this.props.model.unlock(pass)
|
||||
this.forceUpdate()
|
||||
} catch(err) {
|
||||
alert(err)
|
||||
}
|
||||
}//}}}
|
||||
}
|
||||
|
||||
class NodeFiles extends Component {
|
||||
|
|
@ -299,11 +354,13 @@ export class Node {
|
|||
this.ID = nodeID
|
||||
this.ParentID = 0
|
||||
this.UserID = 0
|
||||
this.CryptoKeyID = 0
|
||||
this.Name = ''
|
||||
this.Content = ''
|
||||
this._content = ''
|
||||
this.Children = []
|
||||
this.Crumbs = []
|
||||
this.Files = []
|
||||
this._decrypted = false
|
||||
this._expanded = false // start value for the TreeNode component,
|
||||
// it doesn't control it afterwards.
|
||||
// Used to expand the crumbs upon site loading.
|
||||
|
|
@ -311,13 +368,14 @@ export class Node {
|
|||
retrieve(callback) {//{{{
|
||||
this.app.request('/node/retrieve', { ID: this.ID })
|
||||
.then(res=>{
|
||||
this.ParentID = res.Node.ParentID
|
||||
this.UserID = res.Node.UserID
|
||||
this.Name = res.Node.Name
|
||||
this.Content = res.Node.Content
|
||||
this.Children = res.Node.Children
|
||||
this.Crumbs = res.Node.Crumbs
|
||||
this.Files = res.Node.Files
|
||||
this.ParentID = res.Node.ParentID
|
||||
this.UserID = res.Node.UserID
|
||||
this.CryptoKeyID = res.Node.CryptoKeyID
|
||||
this.Name = res.Node.Name
|
||||
this._content = res.Node.Content
|
||||
this.Children = res.Node.Children
|
||||
this.Crumbs = res.Node.Crumbs
|
||||
this.Files = res.Node.Files
|
||||
callback(this)
|
||||
})
|
||||
.catch(this.app.responseError)
|
||||
|
|
@ -339,10 +397,18 @@ export class Node {
|
|||
})
|
||||
.catch(this.app.responseError)
|
||||
}//}}}
|
||||
save(content, callback) {//{{{
|
||||
async save(content, callback) {//{{{
|
||||
let update_content = content
|
||||
/*
|
||||
* XXX - fix encrypting when saving
|
||||
if(this.CryptoKeyID != 0)
|
||||
update_content = await this.#encrypt(content)
|
||||
*/
|
||||
|
||||
this.app.request('/node/update', {
|
||||
NodeID: this.ID,
|
||||
Content: content,
|
||||
Content: update_content,
|
||||
CryptoKeyID: this.CryptoKeyID,
|
||||
})
|
||||
.then(callback)
|
||||
.catch(this.app.responseError)
|
||||
|
|
@ -387,6 +453,74 @@ export class Node {
|
|||
a.remove() //afterwards we remove the element again
|
||||
})
|
||||
}//}}}
|
||||
content() {//{{{
|
||||
if(this.CryptoKeyID != 0 && !this._decrypted) {
|
||||
this.#decrypt()
|
||||
}
|
||||
return this._content
|
||||
}//}}}
|
||||
|
||||
async encrypt(obj_key) {//{{{
|
||||
if(obj_key.ID != this.CryptoKeyID)
|
||||
throw('Invalid key')
|
||||
|
||||
let crypto = new Crypto(obj_key.key)
|
||||
this._decrypted = false
|
||||
|
||||
let counter = await obj_key.counter()
|
||||
|
||||
this.content = sjcl.codec.base64.fromBits(
|
||||
crypto.encrypt(
|
||||
sjcl.codec.utf8String.toBits(this.content),
|
||||
counter,
|
||||
false,
|
||||
)
|
||||
)
|
||||
}//}}}
|
||||
#decrypt() {//{{{
|
||||
let obj_key = this.app.nodeUI.current.getKey(this.CryptoKeyID)
|
||||
if(obj_key === null || obj_key.ID != this.CryptoKeyID)
|
||||
throw('Invalid key')
|
||||
|
||||
// Ask user to unlock key first
|
||||
var pass = null
|
||||
while(pass || obj_key.status() == 'locked') {
|
||||
pass = prompt("Password")
|
||||
if(!pass)
|
||||
throw new Error(`Key "${obj_key.description}" is locked`)
|
||||
|
||||
try {
|
||||
obj_key.unlock(pass)
|
||||
} catch(err) {
|
||||
alert(err)
|
||||
}
|
||||
pass = null
|
||||
}
|
||||
|
||||
if(obj_key.status() == 'locked')
|
||||
throw new Error(`Key "${obj_key.description}" is locked`)
|
||||
|
||||
let crypto = new Crypto(obj_key.key)
|
||||
this._decrypted = true
|
||||
this._content = sjcl.codec.utf8String.fromBits(
|
||||
crypto.decrypt(this._content)
|
||||
)
|
||||
}//}}}
|
||||
/*
|
||||
async #encrypt(content) {//{{{
|
||||
let obj_key = this.app.nodeUI.current.getKey(this.CryptoKeyID)
|
||||
if(obj_key === null || obj_key.ID != this.CryptoKeyID)
|
||||
throw('Invalid key')
|
||||
|
||||
if(obj_key.status() == 'locked')
|
||||
throw new Error(`Key "${obj_key.description}" is locked`)
|
||||
|
||||
let crypto = new Crypto(obj_key.key)
|
||||
let content_bits = sjcl.codec.utf8String.toBits(content)
|
||||
let counter = await this.app.nodeUI.current.keyCounter()
|
||||
return crypto.encrypt(content_bits, counter, true)
|
||||
}//}}}
|
||||
*/
|
||||
}
|
||||
|
||||
class Menu extends Component {
|
||||
|
|
@ -504,16 +638,82 @@ class UploadUI extends Component {
|
|||
class NodeProperties extends Component {
|
||||
constructor(props) {//{{{
|
||||
super(props)
|
||||
this.props.nodeui.retrieveKeys()
|
||||
this.selected_key_id = 0
|
||||
}//}}}
|
||||
render({ nodeui }) {//{{{
|
||||
let save = true
|
||||
let keys = nodeui.keys.value
|
||||
.sort((a, b)=>{
|
||||
if(a.description < b.description) return -1
|
||||
if(a.description > b.description) return 1
|
||||
return 0
|
||||
})
|
||||
.map(key=>{
|
||||
this.props.nodeui.keys.value.some(uikey=>{
|
||||
if(uikey.ID == nodeui.node.value.ID) {
|
||||
save = (uikey.status() == 'unlocked')
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
return html`
|
||||
<div class="key ${key.status()}">
|
||||
<input type="radio" name="key" id="key-${key.ID}" checked=${nodeui.node.value.CryptoKeyID == key.ID} disabled=${key.status() == 'locked'} oninput=${()=>this.selected_key_id = key.ID} />
|
||||
<label for="key-${key.ID}">${key.description}</label>
|
||||
</div>`
|
||||
})
|
||||
|
||||
return html`
|
||||
<div id="blackout" onclick=${()=>nodeui.properties.value = false}></div>
|
||||
<div id="properties">
|
||||
<h1>Node properties</h1>
|
||||
<input type="checkbox" id="node-encrypted" /> <label for="node-encrypted">Encrypted</label>
|
||||
<h1>Note properties</h1>
|
||||
|
||||
These properties are only for this note.
|
||||
|
||||
<h2>Encryption</h2>
|
||||
<div class="key">
|
||||
<input type="radio" id="key-none" name="key" checked=${nodeui.node.value.CryptoKeyID == 0} oninput=${()=>this.selected_key_id = 0} />
|
||||
<label for="key-none">None</label>
|
||||
</div>
|
||||
${keys}
|
||||
|
||||
${save ? html`<button style="margin-top: 32px" onclick=${()=>this.save()}>Save</button>` : ''}
|
||||
</div>
|
||||
`
|
||||
}//}}}
|
||||
save() {//{{{
|
||||
let nodeui = this.props.nodeui
|
||||
let node = nodeui.node.value
|
||||
|
||||
// Find the actual key object used for encryption
|
||||
let encrypt_key = nodeui.getKey(this.selected_key_id)
|
||||
let decrypt_key = nodeui.getKey(node.CryptoKeyID)
|
||||
|
||||
if(decrypt_key && decrypt_key.status() == 'locked') {
|
||||
alert("Decryption key is locked and can not be used.")
|
||||
return
|
||||
}
|
||||
|
||||
if(encrypt_key && encrypt_key.status() == 'locked') {
|
||||
alert("Key is locked and can not be used.")
|
||||
return
|
||||
}
|
||||
|
||||
// Currently not encrypted - encrypt with new key.
|
||||
let crypto = new Crypto(selected_key.key)
|
||||
if(node.CryptoKeyID == 0) {
|
||||
let encrypted = crypto.encrypt(
|
||||
sjcl.codec.utf8String.toBits(node.Content()),
|
||||
1n,
|
||||
)
|
||||
console.log(encrypted)
|
||||
}
|
||||
|
||||
/*
|
||||
crypto.encrypt(
|
||||
)
|
||||
*/
|
||||
}//}}}
|
||||
}
|
||||
|
||||
// vim: foldmethod=marker
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue