Digital signatures for RP2350 boards

I got a little time to start looking at this.

Anyway, I replaced the cert and key:

This is the key and cert I made the other day:

/*
    const char *cert_pem = "-----BEGIN CERTIFICATE-----\
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\
wrK6PPSRSHUXxZmvLVLMf0M=\
-----END CERTIFICATE-----";
*/
    const char *cert_pem = "-----BEGIN CERTIFICATE-----\n"
        "MIIDtTCCAp2gAwIBAgITaKShdq9xXbBofxeT8mpjU4xFQjANBgkqhkiG9w0BAQsF\n"
        "ADBgMQswCQYDVQQGEwJVUzELMAkGA1UECAwCV0kxIzAhBgNVBAoMGk1hdHR5ZHlu\n"
        "ZSBIZWF2eSBJbmR1c3RyaWVzMR8wHQYJKoZIhvcNAQkBFhB0ZXN0QGV4YW1wbGUu\n"
        "Y29tMB4XDTI1MDEyOTE2MTQxMVoXDTM1MDEyNzE2MTQxMVowYDELMAkGA1UEBhMC\n"
        "VVMxCzAJBgNVBAgMAldJMSMwIQYDVQQKDBpNYXR0eWR5bmUgSGVhdnkgSW5kdXN0\n"
        "cmllczEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbTCCASIwDQYJKoZI\n"
        "hvcNAQEBBQADggEPADCCAQoCggEBAM46AUpYay/BiKKXDnwdS9wI6yBYz6NClxWV\n"
        "vXhbs/I2Y/AH+A8ZmHnMYb0B9rSXRXyI5Ar3dTvultmJJ4qr6aibxvl99p7jmp0U\n"
        "MieX6EasClC+vmpzUTHTWMoGWy2eviyqbK+tnO5sD754DiAp8PgHdVq3E0rdoF6k\n"
        "bidmZLhdQD3CEmlSnPFllB2nELT//B18PE3jIf7iBU9Klbz6xS9iU5bM/g0R21yx\n"
        "69udOY1crREaWgBBH+1x0dfYyGNa3yVfgIIGfLk9mT3MTD1IlaI0JXAET7r5pcnt\n"
        "rW2l54ECaZ08SA9O0heKWWz3zrnGtXMemzNxx8AkKoinSqI0tbUCAwEAAaNoMGYw\n"
        "HwYIKwYBBAGh9CMEEwwRQlAgc2VyaWFsIDEyMzQ1NjcwJAYIKwYBBAGh9CQEGAwW\n"
        "clBJIHNlcmlhbCBhMWIyYzNkNDk5OTAdBgNVHQ4EFgQU/nqKhpUnDq82nl4lGYoO\n"
        "fCRlr3EwDQYJKoZIhvcNAQELBQADggEBAE6FUOZ2Wg/L5AG3UDqnpoX3FM6aLu7i\n"
        "IGnr8G+Ru9rxEQaIISPGdH2m3jaebM8AoZGLRhJooAev03AoiWuIJXSQB4nEQvSr\n"
        "DqOxf2KvkTcwaghjisr+duRQ3kkAM5xqxV1WjiL0jdE42t6FwesU0y45/UnMe8iQ\n"
        "ON+sd9b4xChgRS5g7dPK2XdMxJYeUNAEICKBSQeUWY7BRa3A50dHR23n5Xio5Rcy\n"
        "hZAiCQ7pLUUjjehcvmUVBG9PR7RPMNSkB6N0fd8GUwSl2fy+VG/BaScO68rUf0qj\n"
        "U0r95j0j1GsySuTRk16UMC9WBPqfPvUROOHrMXoT3cpTAVvl041WOsE=\n"
        "-----END CERTIFICATE-----";

/*
    const char *public_key_pem = "-----BEGIN PUBLIC KEY-----\
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApqU0p1cr4SMhCRIVD5o8\
XTnsMk93JmRNV+y3JxKhmKBWtilLY/W+s/ods34LKmyLdeGb/3gyt/0lvZpqfNtJ\
EceXBNm1MzV1c9+N5wYmtenjv6p9QxJnxhSk5LePMoVr8xTOAknFjpSw7+2+oVwx\
Wt4l9Yv9Mb4uEjdaOrdz6EhBOAI3QypTfIOdJbBxHTEJlfifTry+vD/iMvwO351H\
rshd0noGdk//aWub4J5YjwlQMzE5eDllkYa+V7H8nRoWlHFNlvC6X0odXBEY5e9h\
IkjJBX0GtpYta0toh3An/QE9OQiZ1OVjTHff6uIMmeyu8qGqrJJ1WGFSIJWyMChy\
uJ/01GUJ5jUqPuPdYZMS5Iaywuy4y1bU+Swu0qfGW2FrOdU6eMeP6gZxMwl6/Tqy\
LiDmnLw4xsm5eRuR78tT2rshmKaqxG/3SLASpvFZVCQhLDfK28XBnxk7gbrFb+r+\
uRHc7MQlrjTbJSb1ZeX+mps1pEyzVtzHBb+yA/dMv4kD38m6HeQimFKN83ywOZBy\
VUfT+b1vYCriFlGRO+UPXF5EqvrfRMR3PIzoVmaUrxTXf6oZEYuQeGcSE1AN2+Wk\
ER2bhxRBazcfKEQbbxFyAAjeSDRYVEbsmyongtMyhWtCvr0eswB087/06uEJlKgx\
m3jsnIbZ901lFgAcIQKyq4cCAwEAAQ==\
-----END PUBLIC KEY-----";
*/
    const char* public_key_pem = "-----BEGIN PUBLIC KEY-----\n"
        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzjoBSlhrL8GIopcOfB1L\n"
        "3AjrIFjPo0KXFZW9eFuz8jZj8Af4DxmYecxhvQH2tJdFfIjkCvd1O+6W2Ykniqvp\n"
        "qJvG+X32nuOanRQyJ5foRqwKUL6+anNRMdNYygZbLZ6+LKpsr62c7mwPvngOICnw\n"
        "+Ad1WrcTSt2gXqRuJ2ZkuF1APcISaVKc8WWUHacQtP/8HXw8TeMh/uIFT0qVvPrF\n"
        "L2JTlsz+DRHbXLHr2505jVytERpaAEEf7XHR19jIY1rfJV+AggZ8uT2ZPcxMPUiV\n"
        "ojQlcARPuvmlye2tbaXngQJpnTxID07SF4pZbPfOuca1cx6bM3HHwCQqiKdKojS1\n"
        "tQIDAQAB\n"
        "-----END PUBLIC KEY-----";

And the results:

HiZ> cert
Verifying the SHA-256 signatureHash done
Subject: C=US, ST=WI, O=Mattydyne Heavy Industries, emailAddress=test@example.com
Issuer: C=US, ST=WI, O=Mattydyne Heavy Industries, emailAddress=test@example.com
Valid from: 2025-01-29 16:14:11
Valid to: 2035-01-27 16:14:11
Serial Number: 68:A4:A1:76:AF:71:5D:B0:68:7F:17:93:F2:6A:63:53:8C:45:42
Certificate verified successfully

HiZ> 

I was seeing both cores hang as you described until I changed the cert & key.
At least it’s some progress!

I’ll play with the python script and make some certs to see what happens, (but I may not look until tomorrow or Sunday, depending on the grandkids’ soccer and the like :slight_smile: )

Just thinking about this before I shut down … Maybe mbedTLS doesn’t like the 4096 bit key? Mine is a 2048.

1 Like

Thank you so much. I can run with that. The 4k cert was my first attempt with openssl. In the .h is my 2K cert from the Python script.

2 Likes

I copied in your cert and key, but it still locks up during during the mbedtls_pk_verify for some reason. Is it possible you had an older version of the source that was incorrectly verifying the cert against itself?

CERT SIG LEN: 256
Hash LEN: 32
Hash: 5E6A8F124F84C69DC02A147BA3EEC512132D6B1F67DF2F786813F9592EC9FBF0

Went back to the pk_verify demo. I’m expecting a 32 byte hash, and it appears to be populated.

    // Verify the certificate signature using the public key
    ret = mbedtls_pk_verify(
        &public_key,
        MBEDTLS_MD_SHA256,
        hash, 0,
        cert.sig.p, cert.sig.len
    );

I am fairly confident from the examples that everything is correct with the exception of cert.sig.p, because the example loads it from a file instead of parsing the cert first. cert.sig.p is “ASN1 data, e.g. in ASCII” which seems like what is required?

ret = mbedtls_pk_verify(&pk, MBEDTLS_MD_SHA256, hash, 0, buf, i)

From the example in the mbedtls docs.

I also tried increasing the hash variable to 64 bytes to make sure there is no buffer overflow happening. I also explicitly set the hash length to both 32 and mdinfo->size (instead of the example that uses 0).

Pushed a small update that includes your keys and a tiny bit of debug info.

EDIT:

    unsigned char buf[256];
    for(int i = 0; i < cert.sig.len; i++) {
        printf("%02X", cert.sig.p[i]);
        buf[i] = cert.sig.p[i];
    }

For good measure I display the signature, copy it into a buffer, and then use the buffered copy with verify. Still locks both cores. It’s either something obvious or something horrible :slight_smile:

1 Like

I checked out your branch to work with, and it locked up mine before I changed cert and key.

I probably won’t get a chance to look today, but will tomorrow.

1 Like

Got it going with your cert. I’ve been using Bus Pirate 5 for dev the last few day, I switched to 6 and it works.

I’m not exactly pleased with that answer, but it satisfies our requirements.

Now to dial in my own certs.

1 Like

Funny, I only have a 5… Something strange is going on.

1 Like

That’s even more unsatisfactory :laughing: My little certs are certing now.

Todo:

  • Get custom field info
  • Swap to DER
  • Create “the key” and store it
  • A signing system for manufacturing
1 Like

Just the “easy” stuff now :rofl::rofl:

1 Like

Converted to DER. Added dump cert and pk in PEM format, which will eventually be an option to dump to a file.

cert.zip (2.0 KB)

cert.py now dumps to DER on screen. It’s messy, but this will be done on a server and will have a totally different final interface.

RP2xxxx Unique Serial Number: 0000000000000000

Another little surprise on the engineering samples: blank unique ID.

RP2xxx Unique Serial Number: 4E:1A:62:90:F8:7E:B4:B8
Certificate Serial Number: 13:37:30:0D:06:09:2A:86
Certificate Serial Number does not match RP2xxx Unique Serial Number

Extracting the unique serial and comparing to the cert serial. I understand this should probably go in the extensions, but the infrastructure is now there.

One thing I was unable to get working are the extensions. I see discussions of people doing it, but no real documented solution. One of the project maintainers mentioned using cert->v3_ext, but it is internal with no promise it won’t suddenly change. I pushed my attempt to make it work.

If extensions are out, I can see abusing the subject fields to store most of the info we want.

1 Like

Had a casual look at generating our own serial number.

Blowfish seems to be nice for 8 byte non-sequential serial number.

  • a database auto incrementing id is assigned to each RPI chip unique id.
  • use blowfish to encrypt it to an 8byte value

These can be decrypted so we can get the actual production number if there’s some reason to.

Blowfish has a cypher and an “IV” (I don’t know what this is yet), both seem to also be 8 byte values.

My thought is to generate these values, and then encrypt them with the public key. Access would would only be available on a per activation basis with the private key.

Private key security will be a long random (binary?) Password (eg not text if possible). Need to research more.

Private key will be stored on a USB key in the company safe (ok, a Mason jar on my office desk).

I usually print out my protected keys and file them as well.

For a demo project with no actual security implications this feels OK to me, but please chime in if there are other best practices that aren’t too out there.

EDIT: I suppose best practice is to use different private keys with different passwords. If one is compromised and an attacker can generate certs, they may not be able to generate serials. Or vise versa.

I confirmed passwords can be byte arrays. I assume this is slightly better than using 7 bits of ASCII characters? Maybe it doesn’t work that way.

Edit 2: attempted to use a 512bit public key in the cert while signing with 2048 key, but that doesn’t seem to be how it works. It did reduce the cert size by 300 bytes.

Edit 3: for generating certs

  • can I create a private manufacturing key for the manufacturing station
  • the public key and serial are known to the issuing server, and have a very short validity stored in database.
  • manufacturing station sends a cert with the unique chip id signed by the temporary private key
  • if the cert is valid, create the encrypted serial number, add the unique I’d to the database, and sign a cert with the master private key.

The idea is the manufacturing key is only issued for the duration of a manufacturing run. Then the server would do something drastic like delete expired certs to prevent SQL injection attacks.

I haven’t taken the time to fully understand what this encompasses and where any issues may lie.

But my internal “rolling your own crypto” warning triggered immediately. Things like what to select for an IV, what it’s properties must be and so on is really important in crypto and often depends on details that aren’t obvious. It takes someone with enough knowledge in cryptography to chose and verify such things.

So the common advice is to not do this unless you absolutely must for your concept to work. Better stick to default mechanisms and libraries that proper cryptographers have designed and verified. And then read their documentation and use them as these cryptographers intended so that you not accidently get one of the details that matter wrong.

2 Likes

This was a result of lots of searching and reading stack overflow, Reddit, and an occasional blog. I don’t know enough to do this without examples. Super open to advise from those who do know.

Is there really any need to encrypt the serials in any way?

Wouldn’t it be enough to put all the data into fields of the X.509 certificate and then sign that?

Then you wouldn’t do your own crypto, but just use the regular signing function of X.509 certificates.

2 Likes

I agree with @electronic_eel in that I don’t know that encrypting serial numbers adds a lot of value.

Relying on both the DP serial and rPI serial numbers in the cert is sufficient. (Not sure what to say about chips that have no set rPI serial…).

Additionally, the symmetric key used to encrypt the serial is another asset that needs to be protected.

(BTW, the IV is the “initial vector”. Feedback mode ciphers use the current encrypted block as part of the next block’s encryption. You need it for the first block; without it, it becomes possible to guess the key given enough samples. It’s typically pure random for each message encrypted, and is appended or prepended to the ciphertext)

As far as keys… In practice we only rotated the keys on a “series change”, that is a design change where the product isn’t form/fit/function. In other words, not very often; could be years.

Remember, every time you change the keys, the firmware needs to be updated with the public key. Probably not practical to do it for every production run; the list of keys on the firmware starts to get excessive.

That’s why the hardest part of this is always key management and PKI infrastructure.

1 Like

In this case, a random IV is needed for each serial number encrypted.

1 Like

I guess my motivation is that there is a demand for unique non sequential id/serial numbers. Manufacturers don’t want competitors to know their sales volume.

1 Like

Ok I’ll buy that. And of course an algorithm would eventually be discovered.

Maybe a salted hash of the sequential number? The number hashed with date/time (hashed 100 times); then the first n number of bytes? You’d have to do the math/testing to see what the likelihood of a had collision.

1 Like

That’s kind of odd; v3 extensions have been a part of the x509 standard for a long time. They’re pretty integral and widely used. :man_shrugging:

1 Like

If you’re planning on having a database already, why not generate random serial numbers and just store in the database? Like in this pseudocode DB schema
id int auto_increment
serial bytes[8] unique
batch int

where batch is the production run. Add other metadata fields to taste.

1 Like

For testing - okay, but for real, generating random numbers is hard, as in generating initialization numbers. You can to ensure that despite power crashes, reboots, acts of God, purposeful intent, there is no way these numbers are ever EVER duplicated. This is often the failure mechanism of crypto systems.
Some combine a monotonically increasing number as an IV with a key to create a unique random serial number, but you have to make sure the number never repeats.

I know nothing about purchasing HSMs. I see some that are in the 4-digit price range.

1 Like