” describes their password scrambling algorithm in C++ and perl but it does not describe it in python. Nor does it describe how to unscramble the data.
This blog describes how I implemented both algorithms in python. I was surprised that NX didn’t use a standard symmetrical key algorithm like blowfish or DES for storing the keys but they must have some good reason.
Script
Description
nxpasswd.py
Scrambling algorithm.
nxpasswd.cc
Scrambling algorithm in vanilla C++. The NoMachine example assumes that you are using the Qt library.
nxdecode.py
Unscrambling algorithm.
test.sh
Test script.
SCRAMBLING ALGORITHM
The scrambling algorithm is more for visual obfuscation than security as is clearly stated on their web page.
#!/usr/bin/env python
# ================================================================
# Author : Joe Linoff
# Date : 2012-02-04
# Version: 1.0
#
# Implements the password scrambling algorithm used by NX in python as
# defined here:
#
# http://www.nomachine.com/ar/view.php?ar_id=AR01C00125
# https://gist.github.com/902387
#
# Usage:
# ./nxpasswd.py import os
# ================================================================
import os
import sys
import string
import time
validCharList = [
"!", "#", "$", "%", "&", "(", ")", "*", "+", "-",
".", "0", "1", "2", "3", "4", "5", "6", "7", "8",
"9", ":", ";", "<", ">", "?", "@", "A", "B", "C",
"D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
"X", "Y", "Z", "[", "]", "_", "a", "b", "c", "d",
"e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x",
"y", "z", "{", "|", "}"
]
def encodePassword(p):
sPass = ":"
sTmp = ""
if p in ['', None]:
return ''
for i in range(len(p)):
sPass += '%d:' % (ord(p[i])+i+1)
return sPass
def findCharInList(c):
global validCharList
for i in range(len(validCharList)):
if validCharList[i] == c:
return i
return -1
def getRandomValidCharFromList():
global validCharList
lt = time.localtime()
s = lt.tm_sec
if os.getenv('NXPASSWD_NONRANDOM'):
s = int(os.getenv('NXPASSWD_NONRANDOM'))
return validCharList[s]
def URLEncode(url):
url = url.replace('&','&')
url = url.replace('"','"')
url = url.replace("'",''')
url = url.replace('<','<')
url = url.replace('>','>')
return url
def scrambleString(s):
global validCharList
dummyString = "{{{{"
sRet = ''
if s in ['', None]:
return ''
s1 = encodePassword(s)
if len(s1) < 32:
sRet += dummyString
sRet += s1[::-1] # reverse string
if len(sRet) < 32:
sRet += dummyString
ch = getRandomValidCharFromList()
k = ord(ch) + len(sRet) - 2
sRet = ch + sRet
for i in range(1,len(sRet)):
j = findCharInList(sRet[i])
if j == -1:
return sRet
n = (j + k * (i+1)) % len(validCharList)
sRet = sRet[:i] + validCharList[n] + sRet[i+1:]
sRet += chr((ord(getRandomValidCharFromList())) + 2)
sRet = URLEncode(sRet)
return sRet
for i in range(1,len(sys.argv)):
p=sys.argv[i]
print scrambleString(p)
UNSCRAMBLING ALGORITHM
This algorithm shows how to unscramble the output from the previous script to regenerate the original password.
#!/usr/bin/env python
# ================================================================
# Author : Joe Linoff
# Date : 2012-02-04
# Version: 1.0
#
# Unscrambles passwords creating by the NX scrambling algorithm.
#
# http://www.nomachine.com/ar/view.php?ar_id=AR01C00125
#
# Usage:
# % ./nxpasswd.py
# <scramble>
# % ./nxdecode.py <scramble>
#
# ================================================================
import sys
import re
# We know that the first character is always a ':'
# We can use that to figure out the correct k value.
validCharList = [
"!", "#", "$", "%", "&", "(", ")", "*", "+", "-",
".", "0", "1", "2", "3", "4", "5", "6", "7", "8",
"9", ":", ";", "<", ">", "?", "@", "A", "B", "C",
"D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
"X", "Y", "Z", "[", "]", "_", "a", "b", "c", "d",
"e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x",
"y", "z", "{", "|", "}"
]
def findCharInList(c):
global validCharList
for i in range(len(validCharList)):
if validCharList[i] == c:
return i
return -1
def findK(ch0,i=5,chf=':'):
global validCharList
j = findCharInList(chf)
for k in range(0,200):
n = (j + k * (i+1)) % len(validCharList)
m = validCharList[n]
if m == ch0:
return k
return -1
def unScramble(scramble):
scramble=URLDecode(scramble)
# Were dummy strings appended/prepended?
# Need 3 tests to check.
tests = []
scramble=scramble[1:]
scramble=scramble[:-1]
tests.append([scramble,0])
scramble=scramble[4:]
tests.append([scramble,4])
scramble=scramble[:-4]
tests.append([scramble,4])
for data in tests:
scramble=data[0]
i=data[1]
k=findK(scramble[0],i+1)
charset=''
for ch in scramble:
i += 1
n = findCharInList(ch)
for j in range(len(validCharList)):
zn = (j + k * (i+1)) % len(validCharList)
if n == zn:
ich = validCharList[j]
charset += ich
break
charset = charset[::-1]
ordvals = charset.split(':')
passwd = ''
i=0
ok = True
for ordval in ordvals:
if len(ordval)>0:
m = re.search('^[0-9]+$',ordval)
if m:
j = int(ordval)-i-1
passwd += chr(j)
i += 1
else:
ok = False
break
if ok:
return passwd
return None
def URLDecode(url):
url = url.replace('&','&')
url = url.replace('"','"')
url = url.replace(''',"'")
url = url.replace('<','<')
url = url.replace('>','>')
return url
for i in range(1,len(sys.argv)):
p=sys.argv[i]
print unScramble(p)
EXAMPLE RUNS
Below I have run both programs to show they work using this test script.
#!/bin/bash
ps=(
'a'
'1'
'12'
'123'
'1234'
'12345'
'123456'
'1234567'
'12345678'
'123456789'
'1234567890'
'1234567890a'
'11111111111'
'1234567890ab' )
passed=0
failed=0
total=0
for p in ${ps[@]} ; do
(( total++ ))
echo
echo "# ================================================"
echo "# test : $total"
echo "# password : $p"
s=$(eval ./nxpasswd.py "'$p'" | tail -1)
echo "# scrambled : $s"
px=$(eval ./nxdecode.py "'$s'" | tail -1)
echo "# unscrambled: $px"
if [[ "$px" != "$p" ]] ; then
echo "# status : FAILED"
(( failed++ ))
else
echo "# status : OK"
(( passed++ ))
fi
done
echo
echo "# SUMMARY"
echo "# Passed : $passed"
echo "# Failed : $failed"
echo "# Total : $total"
# ================================================
# test : 1
# password : a
# scrambled : _Kbv1as.EAUl$a
# unscrambled: a
# status : OK
# ================================================
# test : 2
# password : 1
# scrambled : _Kbv1ak)EAUl$a
# unscrambled: 1
# status : OK
# ================================================
# test : 3
# password : 12
# scrambled : aUq1K#7Tu)Jkl*Fbc
# unscrambled: 12
# status : OK
# ================================================
# test : 4
# password : 123
# scrambled : b_}Bb?VwAVyCV{EJj-Ld
# unscrambled: 123
# status : OK
# ================================================
# test : 5
# password : 1234
# scrambled : bf+NqQp6]yBj&Mu2X$.Ps:d
# unscrambled: 1234
# status : OK
# ================================================
# test : 6
# password : 12345
# scrambled : cn9a*l5W(KqBc.YxGs4a1>e0Ue
# unscrambled: 12345
# status : OK
# ================================================
# test : 7
# password : 123456
# scrambled : ctCm;!CsFn9i3Z0R}QsEt8h;KuDne
# unscrambled: 123456
# status : OK
# ================================================
# test : 8
# password : 1234567
# scrambled : d|O}P>d:m5jAm<p?lDl@s>oGkCv0]1_f
# unscrambled: 1234567
# status : OK
# ================================================
# test : 9
# password : 12345678
# scrambled : d&X0bP{S.V2h3kEtG}O#Z*]9c;r>tOh>oEf
# unscrambled: 12345678
# status : OK
# ================================================
# test : 10
# password : 123456789
# scrambled : !L5xbdG0wXD1lWE}mX@#nQ9%eM;vcO-ve#
# unscrambled: 123456789
# status : OK
# ================================================
# test : 11
# password : 1234567890
# scrambled : !R?)qvaH9{hV<)uWH8tgU@&tZF7weT8%sSE6#
# unscrambled: 1234567890
# status : OK
# ================================================
# test : 12
# password : 1234567890a
# scrambled : #q[B1&oXL6!qWH:zm_B6&oXL6}qWG:zl_B5&%
# unscrambled: 1234567890a
# status : OK
# ================================================
# test : 13
# password : 11111111111
# scrambled : #oPD5{eUA)wbK>$m_F3#gSF*uhK;-l[N1}p%
# unscrambled: 11111111111
# status : OK
# ================================================
# test : 14
# password : 1234567890ab
# scrambled : ${eWKH9!spbPI8*}kaVB91tnfUF?-zsaSL7-$jcY&
# unscrambled: 1234567890ab
# status : OK
# SUMMARY
# Passed : 14
# Failed : 0
# Total : 14
Enjoy!
Post navigation