* bumped nacl and unpaddedbase32 verison

* added/improved support for unpaddedbase32 keys
* greatly improved home UI and mail
* deniable blocks shouldnt use forward secrecy anymore
* dont add yourself as a contact
This commit is contained in:
Kevin Froman 2019-06-19 01:57:13 -05:00
parent cb2e803ae8
commit 8082570b7f
18 changed files with 117 additions and 55 deletions

View file

@ -27,5 +27,5 @@ def insert_deniable_block(comm_inst):
# This assumes on the libsodium primitives to have key-privacy
fakePeer = onionrvalues.DENIABLE_PEER_ADDRESS
data = secrets.token_hex(secrets.randbelow(1024) + 1)
comm_inst._core.insertBlock(data, header='pm', encryptType='asym', asymPeer=fakePeer, meta={'subject': 'foo'})
comm_inst._core.insertBlock(data, header='pm', encryptType='asym', asymPeer=fakePeer, disableForward=True, meta={'subject': 'foo'})
comm_inst.decrementThreadCount('insert_deniable_block')

View file

@ -128,7 +128,8 @@ class Core:
'''
Adds a public key to the key database (misleading function name)
'''
assert peerID not in self.listPeers()
if peerID in self.listPeers() or peerID == self._crypto.pubKey:
raise ValueError("specified id is already known")
# This function simply adds a peer to the DB
if not self._utils.validatePubKey(peerID):
@ -776,7 +777,11 @@ class Core:
data = self._crypto.pubKeyEncrypt(data, asymPeer, encodedData=True).decode()
signature = self._crypto.pubKeyEncrypt(signature, asymPeer, encodedData=True).decode()
signer = self._crypto.pubKeyEncrypt(signer, asymPeer, encodedData=True).decode()
onionrusers.OnionrUser(self, asymPeer, saveUser=True)
try:
onionrusers.OnionrUser(self, asymPeer, saveUser=True)
except ValueError:
# if peer is already known
pass
else:
raise onionrexceptions.InvalidPubkey(asymPeer + ' is not a valid base32 encoded ed25519 key')

View file

@ -26,7 +26,8 @@ friends = Blueprint('friends', __name__)
@friends.route('/friends/list')
def list_friends():
pubkey_list = {}
friend_list = contactmanager.ContactManager.list_friends(core.Core())
c = core.Core()
friend_list = contactmanager.ContactManager.list_friends(c)
for friend in friend_list:
pubkey_list[friend.publicKey] = {'name': friend.get_info('name')}
return json.dumps(pubkey_list)

View file

@ -21,6 +21,7 @@
import sys, getpass
import logger, onionrexceptions
from onionrusers import onionrusers, contactmanager
import unpaddedbase32
def add_ID(o_inst):
try:
sys.argv[2]
@ -50,6 +51,7 @@ def add_ID(o_inst):
def change_ID(o_inst):
try:
key = sys.argv[2]
key = unpaddedbase32.repad(key.encode()).decode()
except IndexError:
logger.warn('Specify pubkey to use')
else:

View file

@ -19,6 +19,7 @@
'''
import os, binascii, base64, hashlib, time, sys, hmac, secrets
import nacl.signing, nacl.encoding, nacl.public, nacl.hash, nacl.pwhash, nacl.utils, nacl.secret
import unpaddedbase32
import logger, onionrproofs
import onionrexceptions, keymanager, core
import config
@ -93,6 +94,7 @@ class OnionrCrypto:
def pubKeyEncrypt(self, data, pubkey, encodedData=False):
'''Encrypt to a public key (Curve25519, taken from base32 Ed25519 pubkey)'''
pubkey = unpaddedbase32.repad(self._core._utils.strToBytes(pubkey))
retVal = ''
box = None
data = self._core._utils.strToBytes(data)
@ -129,7 +131,7 @@ class OnionrCrypto:
return decrypted
def symmetricEncrypt(self, data, key, encodedKey=False, returnEncoded=True):
'''Encrypt data to a 32-byte key (Salsa20-Poly1305 MAC)'''
'''Encrypt data with a 32-byte key (Salsa20-Poly1305 MAC)'''
if encodedKey:
encoding = nacl.encoding.Base64Encoder
else:
@ -199,7 +201,7 @@ class OnionrCrypto:
if pubkey == '':
pubkey = self.pubKey
prev = ''
pubkey = pubkey.encode()
pubkey = self._core._utils.strToBytes(pubkey)
for i in range(self.HASH_ID_ROUNDS):
try:
prev = prev.encode()

View file

@ -18,10 +18,12 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import os, json, onionrexceptions
import unpaddedbase32
from onionrusers import onionrusers
class ContactManager(onionrusers.OnionrUser):
def __init__(self, coreInst, publicKey, saveUser=False, recordExpireSeconds=5):
publicKey = unpaddedbase32.repad(coreInst._utils.strToBytes(publicKey)).decode()
super(ContactManager, self).__init__(coreInst, publicKey, saveUser=saveUser)
self.dataDir = coreInst.dataDir + '/contacts/'
self.dataFile = '%s/contacts/%s.json' % (coreInst.dataDir, publicKey)

View file

@ -18,6 +18,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
'''
import onionrblockapi, logger, onionrexceptions, json, sqlite3, time
import unpaddedbase32
import nacl.exceptions
def deleteExpiredKeys(coreInst):
@ -55,8 +56,7 @@ class OnionrUser:
Takes an instance of onionr core, a base32 encoded ed25519 public key, and a bool saveUser
saveUser determines if we should add a user to our peer database or not.
'''
if ' ' in coreInst._utils.bytesToStr(publicKey).strip():
publicKey = coreInst._utils.convertHumanReadableID(publicKey)
publicKey = unpaddedbase32.repad(coreInst._utils.strToBytes(publicKey)).decode()
self.trust = 0
self._core = coreInst
@ -190,6 +190,7 @@ class OnionrUser:
return list(keyList)
def addForwardKey(self, newKey, expire=DEFAULT_KEY_EXPIRE):
newKey = self._core._utils.bytesToStr(unpaddedbase32.repad(self._core._utils.strToBytes(newKey)))
if not self._core._utils.validatePubKey(newKey):
# Do not add if something went wrong with the key
raise onionrexceptions.InvalidPubkey(newKey)

View file

@ -21,6 +21,7 @@
import sys, os, sqlite3, binascii, time, base64, json, glob, shutil, math, re, urllib.parse, string
import requests
import nacl.signing, nacl.encoding
import unpaddedbase32
from onionrblockapi import Block
import onionrexceptions, config, logger
from onionr import API_VERSION
@ -319,9 +320,12 @@ class OnionrUtils:
'''
Validate if a string is a valid base32 encoded Ed25519 key
'''
retVal = False
if type(key) is type(None):
return False
# Accept keys that have no = padding
key = unpaddedbase32.repad(self.strToBytes(key))
retVal = False
try:
nacl.signing.SigningKey(seed=key, encoder=nacl.encoding.Base32Encoder)
except nacl.exceptions.ValueError:

View file

@ -113,4 +113,8 @@ input{
color: black;
font-size: 1.5em;
width: 10%;
}
.content{
min-height: 1000px;
}

View file

@ -58,9 +58,9 @@ function openReply(bHash, quote, subject){
// Add quoted reply
var splitQuotes = quote.split('\n')
for (var x = 0; x < splitQuotes.length; x++){
splitQuotes[x] = '>' + splitQuotes[x]
splitQuotes[x] = '> ' + splitQuotes[x]
}
quote = '\n' + splitQuotes.join('\n')
quote = '\n' + key.substring(0, 12) + ' wrote:' + '\n' + splitQuotes.join('\n')
document.getElementById('draftText').value = quote
setActiveTab('send message')
}
@ -77,7 +77,7 @@ function openThread(bHash, sender, date, sigBool, pubkey, subjectLine){
var sigMsg = 'signature'
// show add unknown contact button if peer is unknown but still has pubkey
if (sender == pubkey){
if (sender === pubkey && sender !== myPub){
addUnknownContact.style.display = 'inline'
}

View file

@ -54,6 +54,11 @@ sendForm.onsubmit = function(){
return false
}
}
sendMail(to.value, messageContent.value, subject.value)
if (to.value.length !== 56 && to.value.length !== 52){
alert('Public key is not valid')
}
else{
sendMail(to.value, messageContent.value, subject.value)
}
return false
}

View file

@ -21,26 +21,26 @@
<br><br>
<div>🕵️‍♂️ Current Used Identity: <input class='myPub' type='text' readonly></div>
<br>
<button id='shutdownNode' class='warnBtn'>Shutdown Node</button> <button id='refreshStats' class='primaryBtn'>Refresh Stats</button>
<button id='shutdownNode' class='btn warnBtn'>Shutdown Node</button> <button id='refreshStats' class='btn primaryBtn'>Refresh Stats</button>
<br><br>
<h1>Onionr Services</h1>
<label>Open Site: <input type='text' id='siteViewer' placeholder='Site Hash'> <button id='openSite' class='primaryBtn openSiteBtn'>Open Onionr Site</button></label>
<br>
<br><br><a class='idLink' href='/mail/'>Mail</a> - <a class='idLink' href='/friends/'>Friend Manager</a> - <a class='idLink' href='/board/'>Boards</a> -
<br><br><a class='idLink' href='/mail/'>Mail</a> - <a class='idLink' href='/friends/'>Friend Manager</a> - <a class='idLink' href='/board/'>Circle</a> -
<a class='idLink' href='/clandestine/'>Clandestine</a>
<br><br><hr>
<details class='configArea'>
<summary><b>Edit Configuration</b></summary>
<br>
<p><em>Warning: </em><b>Some values can be dangerous to change. Use caution.</b></p>
<p><em>Warning: </em><b>Some values can be dangerous to change.<br><br>Configuration contains sensitive information.</b></p>
<br>
<textarea class='configEditor'></textarea>
<button class='saveConfig successBtn'>Save Config</button>
</details>
<hr>
<h1>Statistics</h1>
<p>🔒 Security Level: <span id='securityLevel'></span></p>
<p>🕰️ Uptime: <span id='uptime'></span></p>
<h2>Connections</h2>
<p class='secRequestNotice hidden'>Note: on high security levels, you should have <em>no</em> received requests.</p>
<p>🖇️ Last Received Request: <span id='lastIncoming'>None since start</span></p>
<p>⬇️ Total Requests Received: <span id='totalRec'>None since start</span></p>
<p>🔗 Outgoing Connections:</p>

View file

@ -5,4 +5,14 @@
.saveConfig{
margin-top: 1em;
}
.idLink{
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome and Opera */
}

View file

@ -18,10 +18,34 @@
*/
uptimeDisplay = document.getElementById('uptime')
connectedDisplay = document.getElementById('connectedNodes')
connectedDisplay.style.maxHeight = '300px'
connectedDisplay.style.overflowY = 'scroll'
storedBlockDisplay = document.getElementById('storedBlocks')
queuedBlockDisplay = document.getElementById('blockQueue')
lastIncoming = document.getElementById('lastIncoming')
totalRec = document.getElementById('totalRec')
securityLevel = document.getElementById('securityLevel')
sec_description_str = 'unknown'
function showSecStatNotice(){
var secWarnEls = document.getElementsByClassName('secRequestNotice')
for (el = 0; el < secWarnEls.length; el++){
secWarnEls[el].style.display = 'block'
}
}
switch (httpGet('/config/get/general.security_level')){
case "0":
sec_description_str = 'normal'
break;
case "1":
sec_description_str = 'high'
break;
}
if (sec_description_str !== 'normal'){
showSecStatNotice()
}
function getStats(){
stats = JSON.parse(httpGet('getstats', webpass))
@ -29,6 +53,7 @@ function getStats(){
connectedDisplay.innerText = stats['connectedNodes']
storedBlockDisplay.innerText = stats['blockCount']
queuedBlockDisplay.innerText = stats['blockQueueCount']
securityLevel.innerText = sec_description_str
totalRec.innerText = httpGet('/hitcount')
var lastConnect = httpGet('/lastconnect')
if (lastConnect > 0){

View file

@ -178,8 +178,16 @@ body{
background-color:#396BAC;
}
.btn:hover{
opacity: 0.6;
}
.openSiteBtn{
padding: 5px;
border: 1px solid black;
border-radius: 5px;
}
.hidden{
display: none;
}

View file

@ -29,11 +29,13 @@ class OnionrValidations(unittest.TestCase):
def test_pubkey_validator(self):
# Test ed25519 public key validity
valid = 'JZ5VE72GUS3C7BOHDRIYZX4B5U5EJMCMLKHLYCVBQQF3UKHYIRRQ===='
valids = ['JZ5VE72GUS3C7BOHDRIYZX4B5U5EJMCMLKHLYCVBQQF3UKHYIRRQ====', 'JZ5VE72GUS3C7BOHDRIYZX4B5U5EJMCMLKHLYCVBQQF3UKHYIRRQ']
invalid = [None, '', ' ', 'dfsg', '\n', 'JZ5VE72GUS3C7BOHDRIYZX4B5U5EJMCMLKHLYCVBQQF3UKHYIR$Q====']
c = core.Core()
print('testing', valid)
self.assertTrue(c._utils.validatePubKey(valid))
for valid in valids:
print('testing', valid)
self.assertTrue(c._utils.validatePubKey(valid))
for x in invalid:
#print('testing', x)