bun
bun copied to clipboard
TypeError with Crypto's `createPublicKey` fn
What version of Bun is running?
1.0.21+837cbd60d
What platform is your computer?
Darwin 23.2.0 arm64 arm
What steps can reproduce the bug?
Run the following JS code in bun run
or bun repl
import { createPublicKey } from 'crypto';
const b64str = 'MDswDQYJKoZIhvcNAQEBBQADKgAwJwIg3wUvyMOfq7G6dT5bIM6keoShd9YGwP7PIc2Tfa8Q99ECAwEAAQ==';
// convert the b64 sring to a buffer
const buffer = Buffer.from(b64str, 'base64');
// create and log the key
const key = createPublicKey({
key: buffer,
type: 'spki',
format: 'der'
});
console.log(key);
// Output:
// NodeJS (v20.10.0): PublicKeyObject [KeyObject] { [Symbol(kKeyType)]: 'public' }
// Bun Run (v1.0.21):
// TypeError: Invalid public key
// at node:crypto:77:61
What is the expected behavior?
The code should console log the public key object as such:
mattmilan.dev@matt server % node crypto-test.js
PublicKeyObject [KeyObject] { [Symbol(kKeyType)]: 'public' }
What do you see instead?
mattmilan.dev@matt server % bun run crypto-test.js
4 |
5 | // convert the b64 sring to a buffer
6 | const buffer = Buffer.from(b64str, 'base64');
7 |
8 | // create and log the key
9 | const key = createPublicKey({
^
TypeError: Invalid public key
at node:crypto:77:61
at /Users/mattmilan.dev/Projects/portal/server/crypto-test.js:9:13
Additional information
The version of node tested on was v20.10.0 as a comparison. The reason for discovering this was due to it being used in quite a critical AWS project (https://github.com/awslabs/aws-jwt-verify) [A common library for decoding JWT's via AWS's Cognito].
For additional reference see here (https://github.com/awslabs/aws-jwt-verify/blob/2a818d9a57cc3a6c0bdea8cc9cb2e04a92e2a95c/src/node-web-compat-node.ts#L24) for the breaking case of the aws-jwt-verify package.
@cirospaciari Mind taking a look at this?
Interesting, if we re-export in nodejs using the code bellow it gives a different base64 and this can be successfully imported in Bun:
console.log(
key
.export({
type: "spki",
format: "der",
})
.toString("base64"),
);
outputs:
MDwwDQYJKoZIhvcNAQEBBQADKwAwKAIhAN8FL8jDn6uxunU+WyDOpHqEoXfWBsD+zyHNk32vEPfRAgMBAAE=
using this base64 string works fine in both and gives the same base64 as export output:
Will take a deeper look
Looking at BoringSSL flow we got a negative big number when decoding the RSA value.
BN_R_NEGATIVE_NUMBER (is negative) -> RSA_R_BAD_ENCODING -> EVP_R_DECODE_ERROR
When trying to use the key or the re-exported key in bun or node publicEncrypt
we got ERR_OSSL_RSA_DATA_TOO_LARGE_FOR_KEY_SIZE
, and when using an RSA key generated by generateKeyPairSync
we can parse and encrypt using the public key.
console.log(publicEncrypt(key, Buffer.from("Hello")).toString("base64"));
the parse error is probably BoringSSL failing earlier than OpenSSL on a badly formatted/encoded key.
@mattmilan-dev using the provided key can you sign/verify in nodejs? can you provide a signature so we can test this public key? This would help a lot.
Thanks for looking into this, and apologies for not being any help further so far. Unfortunately the public key I provided is a conversion into B64 (via buffer) of one of our JWKs used in AWS's Cognito and therefore I don't have the private key available to sign anything with. I could generate a new key pair from scratch on my machine but i feel this would be ineffective if the error is created by something AWS are doing to create their keys which causes the TypeError
within bun. If there's any other thing i can do with the JWKs to give any assistance feel free to let me know and i'd be more than happy to take a look.
No problem I will think in something, and look further, will try to get some environment to simulate the same behavior.
I think I'm having the same problem with bun
and aws-jwt-verify
I have also been led to this thread exactly from trying to verify AWS Cognito JWTs with aws-jwt-verify
using Bun.
Looks like a bug in the DER encoder of aws-jwt-verify that does not add a leading 0-byte if the MSB (most significant bit) of the public key modulus is 1
. In NodeJS this doesn't throw, because OpenSSL is friendly enough (?) to still parse the modulus as a positive number, but in bun it leads to the modulus being parsed as a negative number as @cirospaciari saw. I think that's actually right, because the number is supposed to be encoded in 2's complement per DER spec (https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#%5B%7B%22num%22%3A41%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22FitH%22%7D%2C799%5D)
Will fix in aws-jwt-verify (update: should be fixed with https://github.com/awslabs/aws-jwt-verify/pull/155)
since it was a DER specification issue, and fixed in the latest version of aws-jwt-verify I will close the issue, if anyone encountered this issue after upgrading to the latest version feel free to comment/reopen