# rfc1751.py : Converts between 128-bit strings and a human-readable
# sequence of words, as defined in RFC1751: "A Convention for
# Human-Readable 128-bit Keys", by Daniel L. McDonald.
#
# Part of the Python Cryptography Toolkit
#
# Written by Andrew M. Kuchling and others
#
# ===================================================================
# The contents of this file are dedicated to the public domain. To
# the extent that dedication to the public domain is not available,
# everyone is granted a worldwide, perpetual, royalty-free,
# non-exclusive license to exercise all rights associated with the
# contents of this file for any purpose whatsoever.
# No rights are reserved.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# ===================================================================
from __future__ import print_function
import binascii
from Cryptodome.Util.py3compat import bord, bchr
binary = {0: '0000', 1: '0001', 2: '0010', 3: '0011', 4: '0100', 5: '0101',
6: '0110', 7: '0111', 8: '1000', 9: '1001', 10: '1010', 11: '1011',
12: '1100', 13: '1101', 14: '1110', 15: '1111'}
def _key2bin(s):
"Convert a key into a string of binary digits"
kl = map(lambda x: bord(x), s)
kl = map(lambda x: binary[x >> 4] + binary[x & 15], kl)
return ''.join(kl)
def _extract(key, start, length):
"""Extract a bitstring(2.x)/bytestring(2.x) from a string of binary digits, and return its
numeric value."""
result = 0
for y in key[start:start+length]:
result = result * 2 + ord(y) - 48
return result
def key_to_english(key):
"""Transform an arbitrary key into a string containing English words.
Example::
>>> from Cryptodome.Util.RFC1751 import key_to_english
>>> key_to_english(b'66666666')
'RAM LOIS GOAD CREW CARE HIT'
Args:
key (byte string):
The key to convert. Its length must be a multiple of 8.
Return:
A string of English words.
"""
if len(key) % 8 != 0:
raise ValueError('The length of the key must be a multiple of 8.')
english = ''
for index in range(0, len(key), 8): # Loop over 8-byte subkeys
subkey = key[index:index + 8]
# Compute the parity of the key
skbin = _key2bin(subkey)
p = 0
for i in range(0, 64, 2):
p = p + _extract(skbin, i, 2)
# Append parity bits to the subkey
skbin = _key2bin(subkey + bchr((p << 6) & 255))
for i in range(0, 64, 11):
english = english + wordlist[_extract(skbin, i, 11)] + ' '
return english.strip()
def english_to_key(s):
"""Transform a string into a corresponding key.
Example::
>>> from Cryptodome.Util.RFC1751 import english_to_key
>>> english_to_key('RAM LOIS GOAD CREW CARE HIT')
b'66666666'
Args:
s (string): the string with the words separated by whitespace;
the number of words must be a multiple of 6.
Return:
A byte string.
"""
L = s.upper().split()
key = b''
for index in range(0, len(L), 6):
sublist = L[index:index + 6]
char = 9 * [0]
bits = 0
for i in sublist:
index = wordlist.index(i)
shift = (8 - (bits + 11) % 8) % 8
y = index << shift
cl, cc, cr = (y >> 16), (y >> 8) & 0xff, y & 0xff
if (shift > 5):
char[bits >> 3] = char[bits >> 3] | cl
char[(bits >> 3) + 1] = char[(bits >> 3) + 1] | cc
char[(bits >> 3) + 2] = char[(bits >> 3) + 2] | cr
elif shift > -3:
char[bits >> 3] = char[bits >> 3] | cc
char[(bits >> 3) + 1] = char[(bits >> 3) + 1] | cr
else:
char[bits >> 3] = char[bits >> 3] | cr
bits = bits + 11
subkey = b''
for y in char:
subkey = subkey + bchr(y)
# Check the parity of the resulting key
skbin = _key2bin(subkey)
p = 0
for i in range(0, 64, 2):
p = p + _extract(skbin, i, 2)
if (p & 3) != _extract(skbin, 64, 2):
raise ValueError("Parity error in resulting key")
key = key + subkey[0:8]
return key
wordlist = [
"A", "ABE", "ACE", "ACT", "AD", "ADA", "ADD",
"AGO", "AID", "AIM", "AIR", "ALL", "ALP", "AM", "AMY", "AN", "ANA",
"AND", "ANN", "ANT", "ANY", "APE", "APS", "APT", "ARC", "ARE", "ARK",
"ARM", "ART", "AS", "ASH", "ASK", "AT", "ATE", "AUG", "AUK", "AVE",
"AWE", "AWK", "AWL", "AWN", "AX", "AYE", "BAD", "BAG", "BAH", "BAM",
"BAN", "BAR", "BAT", "BAY", "BE", "BED", "BEE", "BEG", "BEN", "BET",
"BEY", "BIB", "BID", "BIG", "BIN", "BIT", "BOB", "BOG", "BON", "BOO",
"BOP", "BOW", "BOY", "BUB", "BUD", "BUG", "BUM", "BUN", "BUS", "BUT",
"BUY", "BY", "BYE", "CAB", "CAL", "CAM", "CAN", "CAP", "CAR", "CAT",
"CAW", "COD", "COG", "COL", "CON", "COO", "COP", "COT", "COW", "COY",
"CRY", "CUB", "CUE", "CUP", "CUR", "CUT", "DAB", "DAD", "DAM", "DAN",