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