Align client and server signature implementation

- Update server docs to use RFC 5869 hash_hkdf() for key derivation
- Add recursive key sorting to client ResponseSignature
- Ensures client and server produce matching signatures for nested objects

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-26 16:33:44 +01:00
parent 64d215cb26
commit 8062e1be77
2 changed files with 34 additions and 11 deletions

View File

@@ -188,22 +188,28 @@ Also include the `Retry-After` HTTP header.
### Key Derivation
Each license key gets a unique signing key derived from the server secret:
Each license key gets a unique signing key derived from the server secret using RFC 5869 HKDF:
```php
/**
* Derive a unique signing key for a license.
*
* Uses RFC 5869 HKDF for secure key derivation.
*
* @param string $licenseKey The license key
* @param string $serverSecret The server's master secret
* @return string The derived key (hex encoded)
* @return string The derived key (hex encoded, 64 chars)
*/
function derive_signing_key(string $licenseKey, string $serverSecret): string
{
// HKDF-like key derivation
$prk = hash_hmac('sha256', $licenseKey, $serverSecret, true);
// Use PHP's native HKDF implementation (RFC 5869)
// - IKM (input keying material): server secret
// - Length: 32 bytes (256 bits for SHA-256)
// - Info: license key (context-specific info)
// - Salt: empty (uses hash-length zero bytes as per RFC 5869)
$binaryKey = hash_hkdf('sha256', $serverSecret, 32, $licenseKey);
return hash_hmac('sha256', $prk . "\x01", $serverSecret);
return bin2hex($binaryKey);
}
```
@@ -278,7 +284,7 @@ signature = HMAC-SHA256(
Where:
- `derive_signing_key` uses HKDF-like derivation
- `derive_signing_key` uses RFC 5869 HKDF via `hash_hkdf()`
- `canonical_json` sorts keys recursively, uses `JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE`
- Result is hex-encoded (64 characters)
@@ -678,9 +684,10 @@ final class LicenseApi
private function deriveKey(string $licenseKey): string
{
$prk = hash_hmac('sha256', $licenseKey, $this->serverSecret, true);
// RFC 5869 HKDF key derivation
$binaryKey = hash_hkdf('sha256', $this->serverSecret, 32, $licenseKey);
return hash_hmac('sha256', $prk . "\x01", $this->serverSecret);
return bin2hex($binaryKey);
}
// =========================================================================