CBOR, COSE and RSA Key Pairs

In computer security, we often have to represent binary data, in single values or groups of characters, bytes, words, long words, signed…

CBOR, COSE and RSA Key Pairs

In computer security, we often have to represent binary data, in single values or groups of characters, bytes, words, long words, signed integers, floating-point values, double-precision floating-point values, and so on. This might be in the form of a data object, a signature or even encrypted content. For this, the ANS.1 DER format is often used, such as presenting digital certificates and signatures. An improvement on this for small messages with security is Concise Binary Object Representation (CBOR) — and which is defined in RFC8949 [1]. While JSON represents text-based data objects CBOR focuses on binary objects. It has been designed to create a lightweight encoder and decoder. This supports the use of CBOR within an IoT infrastructure. The data, also, does not require a data schema to be able to decode it, along with being extensible.

CBOR integrates security into small data objects and small message sizes. In this case, we will generate an RSA key pair. With this we have two prime numbers (p and q), and compute the modulus:

N=pq

We then pick an encryption key value (e=0x010001) and then compute:

d=e^{−1} (modϕ)

and where:

ϕ=(p−1)(q−1)

The public key is then (e,N) and the private key is (d,N). To encrypt a message (M), we create a cipher:

c=M^e (mod N)

and then decrypt with:

M=c^d (mod N)

The code is [here]:


from cose.keys.rsa import RSA
from cose.keys.cosekey import CoseKey
import binascii
from Crypto.Util.number import *
import sys
size=512
msg='hello'
if (len(sys.argv)>1):
size=int(sys.argv[1])
if (len(sys.argv)>2):
msg=str(sys.argv[2])
M=  bytes_to_long(msg.encode('utf-8'))
cose_key= RSA.generate_key(size)
print ("\ne=",binascii.hexlify(cose_key.e))
print ("n=",binascii.hexlify(cose_key.n))
print ("d=",binascii.hexlify(cose_key.d))
print ("p=",binascii.hexlify(cose_key.p))
print ("q=",binascii.hexlify(cose_key.q))
print ("dp=",binascii.hexlify(cose_key.dp))
print ("dq=",binascii.hexlify(cose_key.dq))
print ("qinv=",binascii.hexlify(cose_key.qinv))
e=int.from_bytes(cose_key.e,byteorder='big')
d=int.from_bytes(cose_key.d,byteorder='big')
N=int.from_bytes(cose_key.n,byteorder='big')
cipher=pow(M,e,N)
mess=pow(cipher,d,N)
print ("\nMessage: ",msg)
print ("Cipher: ",cipher)
print ("Decrypted: ",long_to_bytes(mess).decode())
print ("\nCose key: ",binascii.hexlify(CoseKey.encode(cose_key)))
print ("\nCose key: ",cose_key)

A sample run shows that we have values of e, n, d, p and q. The value of dp is the d mod p-1:

dp=d (mod p−1)

and dq is:

dq=d (mod q−1)

and we also have:

InverseQ = 𝑞^{−1} (mod p)

These values are used with a Chinese remainder algorithm in order to optimize the processing of the decryption. A sample run for 512-bit keys is [here]:

e= b'010001'
n= b'b7b5aebe5e9efee6fa3ad33dab3902e2defb073daa88fad4c044485bf4d6785c017da5a5c4ebdc0ad64de47c247d600c70d1892c1d76f8e1e8e306161bb478cf'
d= b'a4c1f46e173e512da5cd8c160ab19ebd096de9188d113ca401ab3b99fd033bfd02d3599406f8a0f395f6f7a2f15635b45763e9f7c9c42ecf71e781c341ca6fc1'
p= b'df2d5c7c74a544b35c42521edf1cf1eb37bcab7bdaff59061859718c747eecb5'
q= b'd2ba5df74f508e93d4df6f4498dbb6865a760a57b4521843a5a61a037cd945f3'
dp= b'd3df13834f95aa70007131d6ff07529f748d97db0060141ad638ebb464d02e55'
dq= b'115246650cbcf76b4965305538275f427def13fc8f92ebf1ab8d12b854abdb67'
qinv= b'3dbf4bce162c2d7e6f52aca8fc056b1eda675130b6b220e9aaed81f8dcb76614'
Message:  qwerty
Cipher: 4639905396126890548459954183362075374252614899037074478070082360215814530705722628857186531457100839681398680215597770937058626460758168551608438617219151
Decrypted: qwerty
Cose key:  b'a901032143010001205840b7b5aebe5e9efee6fa3ad33dab3902e2defb073daa88fad4c044485bf4d6785c017da5a5c4ebdc0ad64de47c247d600c70d1892c1d76f8e1e8e306161bb478cf225840a4c1f46e173e512da5cd8c160ab19ebd096de9188d113ca401ab3b99fd033bfd02d3599406f8a0f395f6f7a2f15635b45763e9f7c9c42ecf71e781c341ca6fc1235820df2d5c7c74a544b35c42521edf1cf1eb37bcab7bdaff59061859718c747eecb5245820d2ba5df74f508e93d4df6f4498dbb6865a760a57b4521843a5a61a037cd945f3255820d3df13834f95aa70007131d6ff07529f748d97db0060141ad638ebb464d02e55265820115246650cbcf76b4965305538275f427def13fc8f92ebf1ab8d12b854abdb672758203dbf4bce162c2d7e6f52aca8fc056b1eda675130b6b220e9aaed81f8dcb76614'
Cose key:  .COSE_Key(RSAKey): {'RSAKpQInv': b'=\xbfK\xce\x16,-~oR\xac\xa8\xfc\x05k\x1e\xdagQ0\xb6\xb2 \xe9\xaa\xed\x81\xf8\xdc\xb7f\x14', 'RSAKpDQ': b"\x11RFe\x0c\xbc\xf7kIe0U8'_B}\xef\x13\xfc\x8f\x92\xeb\xf1\xab\x8d\x12\xb8T\xab\xdbg", 'RSAKpDP': b'\xd3\xdf\x13\x83O\x95\xaap\x00q1\xd6\xff\x07R\x9ft\x8d\x97\xdb\x00`\x14\x1a\xd68\xeb\xb4d\xd0.U', 'RSAKpQ': b'\xd2\xba]\xf7OP\x8e\x93\xd4\xdfoD\x98\xdb\xb6\x86Zv\nW\xb4R\x18C\xa5\xa6\x1a\x03|\xd9E\xf3', 'RSAKpP': b'\xdf-\\|t\xa5D\xb3\\BR\x1e\xdf\x1c\xf1\xeb7\xbc\xab{\xda\xffY\x06\x18Yq\x8ct~\xec\xb5', 'RSAKpD': b'\xa4\xc1\xf4n\x17.Q-\xa5\xcd\x8c\x16\n\xb1\x9e\xbd\tm\xe9\x18\x8d\x11.\xa4\x01\xab;\x99\xfd\x03;\xfd\x02\xd3Y\x94\x06\xf8\xa0\xf3\x95\xf6\xf7\xa2\xf1V5\xb4Wc\xe9\xf7\xc9\xc4.\xcfq\xe7\x81\xc3A\xcao\xc1', 'RSAKpE': b'\x01\x00\x01', 'RSAKpN': b'\xb7\xb5\xae\xbe^\x9e\xfe\xe6\xfa:\xd3=\xab9\x02\xe2\xde\xfb\x07=\xaa\x88\xfa\xd4\xc0DH[\xf4\xd6x\\\x01}\xa5\xa5\xc4\xeb\xdc\n\xd6M\xe4|.#36;}`\x0cp\xd1\x89,\x1dv\xf8\xe1\xe8\xe3\x06\x16\x1b\xb4x\xcf', 'KpKty': 'KtyRSA'}.

Note that the prime numbers p and q have 64 hex characters, and where each hex character represents 4 bits. The length of these primes numbers is thus 256 bits. The length of the modulus N is then 512 bits long.

The great advantage of CBOR is that we can encode the key pair in a hex string. In this case it is, and which contains the RSA key:

b'a901032143010001205840b7b5aebe5e9efee6fa3ad33dab3902e2defb073daa88fad4c044485bf4d6785c017da5a5c4ebdc0ad64de47c247d600c70d1892c1d76f8e1e8e306161bb478cf225840a4c1f46e173e512da5cd8c160ab19ebd096de9188d113ca401ab3b99fd033bfd02d3599406f8a0f395f6f7a2f15635b45763e9f7c9c42ecf71e781c341ca6fc1235820df2d5c7c74a544b35c42521edf1cf1eb37bcab7bdaff59061859718c747eecb5245820d2ba5df74f508e93d4df6f4498dbb6865a760a57b4521843a5a61a037cd945f3255820d3df13834f95aa70007131d6ff07529f748d97db0060141ad638ebb464d02e55265820115246650cbcf76b4965305538275f427def13fc8f92ebf1ab8d12b854abdb672758203dbf4bce162c2d7e6f52aca8fc056b1eda675130b6b220e9aaed81f8dcb76614'

Using the site here, we can decode the CBOR encoding and reveal the values:

And there you go. We can generate RSA keys, and then represent the binary key in a CBOR format, and then easily interpret it.

Subscribe: https://billatnapier.medium.com/membership