```
import libscrc
def LEStringToInteger(k):
bytes = [b for b in k]
return sum((bytes[i]<<(8 * i)) for i in range(len(bytes)))
def IntegerToLEString(k):
kInt = k;
result = [];
n = 0
while (kInt):
result.append(kInt & 0xff);
kInt = kInt >> 8
n = n + 1
return result
def crockfordBase32Encode(val,blockSize = 5, numBlocks = 2):
table = b'0123456789ABCDEFGHJKNMPQRSTVWXYZ'
result = b""
ctr = 0
while (1):
ctr += 1
index = val & 0x1f
val = val >> 5
result+= table[index:(index + 1)]
if (ctr == blockSize):
numBlocks -= 1
if (val == 0) and (numBlocks == 0):
return result
result += b" "
ctr = 0
return result
Haase Expires September 10, 2020 [Page 5]
Internet-Draft Key encoding for manual typing operations. March 2020
def Base10Encode(val,blockSize = 7, numBlocks = 2):
table = b'0123456789'
result = b""
ctr = 0
while (1):
ctr += 1
index = val % 10
val = round((val - index) / 10)
result+= table[index:(index + 1)]
if (ctr == blockSize):
numBlocks -= 1
if (val == 0) and (numBlocks == 0):
return result
result += b" "
ctr = 0
return result
def encodeKeyAsString(key, domainSeparator = b"PSK128"):
keyAsInt = LEStringToInteger(key)
result = b""
chunkNo = 0
while (keyAsInt):
# take chunks of 43 bits and calculate a CRC7
# encode each chunk as 2 x 5 = 10 characters
chunk = keyAsInt - ((keyAsInt >> 43) << 43)
keyAsInt = keyAsInt >> 43
crc = libscrc.mmc(domainSeparator + bytes([chunkNo])
+ bytes(IntegerToLEString(chunk)))
chunkWithCrc = chunk + (crc << 43)
chunkNo += 1
result += crockfordBase32Encode(chunkWithCrc)
if (keyAsInt):
result += b" ";
return result
def encodeKeyAsDigits(key,domainSeparator = b"PSK128"):
keyAsInt = LEStringToInteger(key)
result = b""
debugPrints = 0
chunkNo = 0
while (keyAsInt):
# take chunks of 32 bits and calculate a CRC7
# encode each chunk as 2 x 6 = 12 digits
chunk = keyAsInt - ((keyAsInt >> 32) << 32)
Haase Expires September 10, 2020 [Page 6]
Internet-Draft Key encoding for manual typing operations. March 2020
keyAsInt = keyAsInt >> 32
crc = libscrc.mmc(domainSeparator + bytes([chunkNo])
+ bytes(IntegerToLEString(chunk)))
chunkWithCrc = chunk + (crc << 32)
result += Base10Encode(chunkWithCrc,6)
chunkNo += 1
if (keyAsInt):
result += b" ";
return result
def crockfordBase32DecodeChar(x):
toDecode = x.upper();
table = b'0123456789ABCDEFGHJKNMPQRSTVWXYZ'
if (toDecode == b'O'):
toDecode = b'0'
if ((toDecode == b'I') or (toDecode == b'L')):
toDecode = b'1'
return table.index(toDecode);
def decodeString(x):
result = 0
characters = x;
if (len(characters) > 0):
result += crockfordBase32DecodeChar(characters[0:1]);
result += 32 * decodeString(characters[1:])
return result
def decodeDigits(digits):
result = 0
if (len(digits) > 0):
result += digits[0] - ord('0')
result += 10 * decodeDigits(digits[1:])
return result
def decodeKeyFromDigits(digits,domainSeparator = b"PSK128"):
remainingDigits = digits
Haase Expires September 10, 2020 [Page 7]
Internet-Draft Key encoding for manual typing operations. March 2020
result = 0
factor = 1
chunkNo = 0
while (1):
remainingDigits = remainingDigits.lstrip()
if (len(remainingDigits) == 0):
return IntegerToLEString(result)
subChunk1 = remainingDigits[0:6]
remainingDigits = (remainingDigits[6:]).lstrip()
subChunk2 = remainingDigits[0:6]
remainingDigits = (remainingDigits[6:]).lstrip()
ChunkWithCrc = (decodeDigits(subChunk1)
+ (10**6) * decodeDigits(subChunk2))
# take chunks of 32 bits and calculate a CRC7
chunk = ChunkWithCrc & 0xffffffff
decodedCrc = ChunkWithCrc >> 32
calculatedCrc = libscrc.mmc(domainSeparator + bytes([chunkNo])
+ bytes(IntegerToLEString(chunk)))
if (calculatedCrc != decodedCrc):
raise ValueError(b"detected typing error in chunk "
+ subChunk1 + b" " + subChunk2 + b".")
result += chunk * factor
factor = factor << 32
chunkNo += 1
return result
def decodeKeyFromString(digits,domainSeparator = b"PSK128"):
remainingDigits = digits
result = 0
factor = 1
chunkNo = 0
while (1):
remainingDigits = remainingDigits.lstrip()
if (len(remainingDigits) == 0):
return IntegerToLEString(result)
Haase Expires September 10, 2020 [Page 8]
Internet-Draft Key encoding for manual typing operations. March 2020
subChunk1 = remainingDigits[0:5]
remainingDigits = (remainingDigits[5:]).lstrip()
subChunk2 = remainingDigits[0:5]
remainingDigits = (remainingDigits[5:]).lstrip()
ChunkWithCrc = (decodeString(subChunk1)
+ (decodeString(subChunk2) << (5*5)))
# take chunks of 43 bits and calculate a CRC7
decodedCrc = ChunkWithCrc >> 43
chunk = ChunkWithCrc - (decodedCrc << 43)
calculatedCrc = libscrc.mmc(domainSeparator + bytes([chunkNo])
+ bytes(IntegerToLEString(chunk)))
if (calculatedCrc != decodedCrc):
raise ValueError(b"detected typing error in chunk "
+ subChunk1 + b" " + subChunk2 + b".")
result += chunk * factor
factor = factor << 43
chunkNo += 1
``````
6. Security Considerations
The encoding defined here does not provide any security guarantees
except for detection of accidential typing errors.
Accidential typing errors will be detected with a probability in the
range of 1% only (CRC7).
Distinct applications SHALL use unique DSI strings, such that
accidential re-use of the same key for different applications is
typically observed already on the typing error detection level.
7. Status of this draft
Presently this draft is meant just to be used as a sketch of the
general idea that came up in the process of the discussions for the
preparation of the external PSK guidance documents. Comments are
welcome, specifically regarding the question, whether a stronger
checksum such as a CRC32 should be included over the entire key.
Presently only a large fraction of typing errors will be detected,
but with the present formulation using CRC7, this is far from a safe
Haase Expires September 10, 2020 [Page 9]
Internet-Draft Key encoding for manual typing operations. March 2020
detection level. This draft was based on the assessment, that for
manual typing this overhead might not be acceptable.
8. IANA Considerations
No IANA action is required.
9. Normative References
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
```.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, .
Appendix A. Test vectors for numeric and alphanumeric encodings
Haase Expires September 10, 2020 [Page 10]
Internet-Draft Key encoding for manual typing operations. March 2020
######################## /128 bit key ############################
Encoding for digits with DSI = b'PSK128'
Key: 0xa58c15a63325963cf79ab3b4c97d609d
Encoded as digits:
966215 677815
822225 364180
636215 761870
490755 095742
Encoded as string:
X4RTQ 4KPKP
PTWXS 3BPW6
C66D5 RRJTJ
######################## /128 bit key ############################
######################## 256 bit key ############################
Encoding for digits with DSI = b'PSK256'
Key:
0xd35a29ef387e015227ea161f4d4af51cdd9d5cf099c8d414b1558faa8a9396cb
Encoded as digits:
158768 709272
685258 719703
697517 428940
658360 268631
009721 830874
742921 983960
229175 168951
301905 580550
Encoded as string:
BP579 5AM75
HMAC9 1A3HG
7KG7Q TSEBS
ENYAA KY1V6
1MZ4J A0WQP
GKQ75 TTNS0
######################## /256 bit key #############################
Author's Address
Bjoern Haase
Endress + Hauser Liquid Analysis
Email: bjoern.m.haase@web.de
Haase Expires September 10, 2020 [Page 11]