Concise Binary Object Representation (CBOR) [1] integrates security into small data objects and small message sizes. COSE (CBOR Object Signing and Encryption) [2] can then build on this to include signatures, message authentication codes (MACs) and encryption and creating serialised objects. In this case we 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} \pmod \phi\)), and where \(\phi=(p-1)(q-1)\). To encrypt a message (\(M\)), we create a cipher \(c=M^e \pmod N\), and then decrypt with \(M=c^d \pmod N\).
COSE and CBOR - RSA Keys |
Outline
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 and encode the key pair into the CBOR representation. With this we have two prime numbers (\(p\) and \(q\)), and compute the modulus (\(N\)):
\(N=pq\)
We then pick an encryption key value (\(e=0x010001\)) and then compute:
\(d=e^{-1} \pmod \phi\))
and where:
\(\phi=(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 \pmod N\)
and then decrypt with:
\(M=c^d \pmod N\).
The code is:
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: ",cose_key)
A sample run shows that we have values of \(e, n, d, p\) and \(q\). The value of \(dQ\) is:
\(dQ = d \pmod {p-1}\)
and \(dP\) is:
\(dP = d \pmod {q-1}\)
and we also have:
\(InverseQ = q^{-1} \pmod p\)
These values are used with a Chinese remainder algorithm in order to optimize the processing of the decryption. The following is a test run with 512-bit keys:
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\)), in this case, 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 following is a sample run for 738-bit keys:
e= b'010001' n= b'02f3416ec757666e3103606da0c4beceb29934ce597bcb4ce4b2ee57f1ca3fc153d80179506d00d050432f7fb51cb785f20e907312f7ffdff3b94b3b5805b7cc6e1ef6e5f49ddad51cedaab4ed5dcc8e85bd9811f0ee33f24431d22ed9' d= b'02a933a05e6e07766600dcada4af3046b0b21cfded9a09c17e19a17e73186faec917f02462951e81c59303b9953aa17558725e00b8f32685dab21ee3f0151fc9a094faeee535af7390c956bdd86b3d96101e547a97fa5c838458f438bd' p= b'01ddf75f65af43393bb08926665c640f60cb9a7b463d24af7571ee49a7855840623bb21c12ece75fa585119cc2c2c3' q= b'01948451c6649fd0fe2747188088ab7c47b8859622fc3fde2945200ab62a5e11d25834f4c2afa93b09d47436caf633' dp= b'7dc4e31061bee1a7883b7ea43eb981a669ccfb952eefa14fd92be7abb215d7d3417c0b911b4f82c68e2e653e867b' dq= b'88afc17619504b6d65d647383f763ab37d6d5e530a0b86c5493f4f7f4887aadbf9c4432479f8e423292fefc7db33' qinv= b'013945dfef31113628d03c349b8ae3a5aa766416c400475488fe33ed24832cc0a466f72f653b3ac866de0aaef1a8ab' Message: qwerty Cipher: 1019938997706910415224494934543678517054869858804230079512556177939750524921217626418172546053782095658256449219780170671277219197056375680450607933154281156145383982891259240324344146492403096419259728245188228544353253428 Decrypted: qwerty Cose key: <COSE_Key(RSAKey): {'RSAKpQInv': b'\x019E\xdf\xef1\x116(\xd0.4\x9b\x8a\xe3\xa5\xaavd\x16\xc4\x00GT\x88\xfe3\xed.#36;\x83,\xc0\xa4f\xf7/e;:\xc8f\xde\n\xae\xf1\xa8\xab', 'RSAKpDQ': b'\x88\xaf\xc1v\x19PKme\xd6G8?v:\xb3}m^S\n\x0b\x86\xc5I?O\x7fH\x87\xaa\xdb\xf9\xc4C.#36;y\xf8\xe4#)/\xef\xc7\xdb3', 'RSAKpDP': b'}\xc4\xe3\x10a\xbe\xe1\xa7\x88;~\xa4.\xb9\x81\xa6i\xcc\xfb\x95.\xef\xa1O\xd9+\xe7\xab\xb2\x15\xd7\xd3A|\x0b\x91\x1bO\x82\xc6\x8e.e.\x86{', 'RSAKpQ': b'\x01\x94\x84Q\xc6d\x9f\xd0\xfe\'G\x18\x80\x88\xab|G\xb8\x85\x96"\xfc?\xde)E \n\xb6*^\x11\xd2X4\xf4\xc2\xaf\xa9;\t\xd4t6\xca\xf63', 'RSAKpP': b'\x01\xdd\xf7_e\xafC9;\xb0\x89.f\\d\x0f`\xcb\x9a{F=.#36;\xafuq\xeeI\xa7\x85X.b;\xb2\x1c\x12\xec\xe7_\xa5\x85\x11\x9c\xc2\xc2\xc3', 'RSAKpD': b'\x02\xa93\xa0^n\x07vf\x00\xdc\xad\xa4\xaf0F\xb0\xb2\x1c\xfd\xed\x9a\t\xc1~\x19\xa1~s\x18o\xae\xc9\x17\xf0.#36;b\x95\x1e\x81\xc5\x93\x03\xb9\x95:\xa1uXr^\x00\xb8\xf3.\x85\xda\xb2\x1e\xe3\xf0\x15\x1f\xc9\xa0\x94\xfa\xee\xe55\xafs\x90\xc9V\xbd\xd8k=\x96\x10\x1eTz\x97\xfa\\\x83\x84X\xf48\xbd', 'RSAKpE': b'\x01\x00\x01', 'RSAKpN': b'\x02\xf3An\xc7Wfn1\x03`m\xa0\xc4\xbe\xce\xb2\x994\xceY{\xcbL\xe4\xb2\xeeW\xf1\xca?\xc1S\xd8\x01yPm\x00\xd0PC/\x7f\xb5\x1c\xb7\x85\xf2\x0e\x90s\x12\xf7\xff\xdf\xf3\xb9K;X\x05\xb7\xccn\x1e\xf6\xe5\xf4\x9d\xda\xd5\x1c\xed\xaa\xb4\xed]\xcc\x8e\x85\xbd\x98\x11\xf0\xee3\xf2D1\xd2.\xd9', 'KpKty': 'KtyRSA'}.
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:
Coding
To run the code, you need to pip install cose:
References
[1] Bormann, C., & Hoffman, P. (2020). RFC 8949 Concise Binary Object Representation (CBOR). [here].
[2] Schaad, J. (2017), RFC 8152 – CBOR Object Signing and Encryption (COSE), 2017. [here]