Skip to content

Encryption::createLocalKeyObjectUsingPurePhpMethod() creates invalid key #301

@rfool

Description

@rfool

Please confirm the following:

  • I have read the README entirely
  • I have verified in the issues that my problem hasn't already been resolved

Setup

Please provide the following details, the more info you can provide the
better.

  • Operating System: Windows
  • PHP Version: 7.4.8
  • web-push-php Version: 6.0.2

Please check that you have installed and enabled these PHP extensions :

  • gmp
  • mbstring
  • curl
  • openssl -

Problem

When Encryption::createLocalKeyObjectUsingPurePhpMethod() and Utils::serializePublicKeyFromJWK() is used to create $localPublicKey then this will result in a key (with binary size) slightly larger than the expected 65 bytes.

Thus, Encryption::createContext() will throw with "Invalid server public key length".

Expected

$localPublicKey should have binary size of 65 bytes exactly.

Features Used

  • VAPID Support
  • Sending with Payload

Example / Reproduce Case

Should be reproducable with official example, when openssl is not correctly configured.

Other

Of course, the best fix is to configure openssl correctly. However, Encryption::createLocalKeyObjectUsingPurePhpMethod() exists and thus should provide correct results.

Unfortunately, I am not an expert on elliptical curves, GMP, JWT and so on. But I have a guess: the error could be in the calls to BigInteger::toBytes(). By default it prepends a signed bit and represents the number in two's-complement. I think this sign-bit is responsible for enlarging key length.

Current:

new JWK([
	'kty' => 'EC',
	'crv' => 'P-256',
	'x' => Base64Url::encode(self::addNullPadding($publicKey->getPoint()->getX()->toBytes())),
	'y' => Base64Url::encode(self::addNullPadding($publicKey->getPoint()->getY()->toBytes())),
	'd' => Base64Url::encode(self::addNullPadding($privateKey->getSecret()->toBytes())),
])

Suggested fix:

new JWK([
	'kty' => 'EC',
	'crv' => 'P-256',
	'x' => Base64Url::encode(self::addNullPadding($publicKey->getPoint()->getX()->toBytes(false))),
	'y' => Base64Url::encode(self::addNullPadding($publicKey->getPoint()->getY()->toBytes(false))),
	'd' => Base64Url::encode(self::addNullPadding($privateKey->getSecret()->toBytes(false))),
])

With this change naiively applied, the key lengths will be exactly 65 bytes, as expected.

However, I don't know if this "fix" is technically correct. As x and y are coordinates, maybe just d should be handled as unsigned?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions