atsign-foundation / at_pico_w

Communicate with the atPlatform using a Raspberry Pi Pico W
BSD 3-Clause "New" or "Revised" License
5 stars 4 forks source link

feat: Add ability to extract RSA public key parameters #16

Closed cpswan closed 1 year ago

cpswan commented 1 year ago

Ongoing work for https://github.com/atsign-foundation/micropython/issues/2 requires the ability to extract integers from another atSign's public key.

- What I did

Tidied up the work done by @duanqi211 to add Bit String support to uasn1.py

Added a method to extract public key integers.

- How I did it

Getting the parameters from a public key is very similar to doing the same with a private key, except that they're encoded into a Bit String rather than an Octet String.

One trip hazard encountered is the spurious null byte at the beginning of the sequence extracted from the bit string, which throws off the ASN.1 parser if it's not trimmed out. @realvarx @TylerTrott @JeremyTubongbanua @XavierChanth any thoughts on how/why this is happening would be appreciated?

- How to verify it

Running micropython locally on WSL2:

import sys
sys.path.append('./lib')
from pem_service import get_pem_parameters, get_pub_parameters, get_pem_key
pubpem='MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArm6AMIrw+2lAPvXVNulIcjabLbhDIynXIMIwjoLGOHKgjvuALW9gySiIDBUG54wtsD15etVL9NSGVchjAoZi7H3NAKQCyn1UNaKzL7Bxnqdm+GyX1OtAQTTFEcqzQZmQDrhTNRs2W2GNaQ+NXEIETfeBxD/VNsumIrzUCybJfRGHcYj5XCcku7YvdluiO+VhQHH1mdGuKuunONo2czNa/0Ir27rWo1SB43jMfbzR/duUT3xD3tB2y+NdWSozbe4dnrCTip4yJ2gdqXwThfK8l2wr/QB/NcLhWcAEo9kQDboOKsS91f3ba7zcBao7Z0lfY/yF2i+dCXB0QA2mmzE6QQIDAQAB'
print(get_pub_parameters(pubpem))

Example output:

[22019955010203162012544509859681164488930880432261972458899838175754293611805217089916406940112100721734274388147856483780254109148070008367596231568313961662789496989123745899284270182818488572671086203864082243948488737628459929294695956520594608527748960101978987192974848362310384109216948438946485471834986032338428092083698086017140393675485856360534183839432285060596258898230833036180671422861642200309962484563154992547400750879332457266064473574420436143142612640028499344486953806118226974258478244812723412044668846945795043769351304074262759431478803683125647954039718875283087402759554272630345833724481, 65537]

- Description for the changelog

feat: Add ability to extract RSA public key parameters

realvarx commented 1 year ago

Got a possible answer for the "spurious null byte" @cpswan . Take a look at this, and also at this.

Looking at the DER scheme: image

We can see that the BIT STRING field has "4+271" bytes. But that "4" isn't taking in account one zero byte at the end of the encapsulation that we see before the 271 bytes which contain the integers that we need.

It isn't being taken in account because it doesn't belong to the encapsulation, but to the content of BIT STRING (that's why the asn1 decoder includes it):

image 271 + 1 = 272 (multiple of 8) 272/8 = 34 (which is a natural, non-decimal number)

That prepended zero byte means the number of zeroes padded at the end of the bit string in order to be a multiple of 8, including the prepended byte itself (so in this case the bit string has not been padded with zeroes (unused values) at the end).

An example of a public key with a BIT STRING with some zeroes padding would be useful. The thing is that I have tried with 3 different atSigns and I always get no padding. Maybe some keys created by some generation systems or with formats don't necessarily have to deal with paddings because they are intended to have a certain length (if that's true, it makes our task easier).

(Images are from ASN.1 JavaScript decoder, although I used a different key on them)

cpswan commented 1 year ago

Thanks @realvarx

I think in that case we should be handling stripping the zero in the uasn1 library.

Public keys are always going to be 2048 bits (==256 bytes) so I don't expect zero padding will ever come into play, which can leave us all scratching out heads why they chose to encode as a bit string rather than an octet string?

I'm going to make this a draft while I refactor.

realvarx commented 1 year ago

which can leave us all scratching out heads why they chose to encode as a bit string rather than an octet string?

I also found this comment in one of the links that I shared:

Public keys are always BIT STRING in X.509; this is an historical remnant from older times, kept for the sake of backward compatibility. – Thomas Pornin

JeremyTubongbanua commented 1 year ago

I was able to get the n and e parameters from a base64 encoded public rsa key here: https://github.com/atsign-foundation/at_pico_w/blob/e7fbbd159a4a68fb0e68bb41617da0ff39ddf738/lib/pem_service.py#L170

cpswan commented 1 year ago

Thanks @JeremyTubongbanua looks like we arrived at much the same approach.

This is ready for review again.