-
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathCOSEKey.php
More file actions
111 lines (95 loc) · 3.84 KB
/
Copy pathCOSEKey.php
File metadata and controls
111 lines (95 loc) · 3.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<?php
declare(strict_types=1);
namespace Firehed\WebAuthn;
use DomainException;
use Firehed\CBOR\Decoder;
/**
* @internal
*
* Data structure for COSE keys
* This represents the post-CBOR-decoded structure
*
* Note: this should be more of a parser than an actual data structure,
* favoring the actual implementations of PublicKeyInterface instead.
* Currently, the Credential Codec needs access to the raw CBOR since
* PublicKeys don't have a CBOR encoder (or a different format could be used,
* but re-inventing a wheel is very undesirable!).
*
* @see RFC 8152
* @link https://www.rfc-editor.org/rfc/rfc8152.html
*
* @see RFC 8230 (RSA key support - not yet implemented)
*/
class COSEKey
{
// Data structure indexes
// @see section 7.1
private const INDEX_KEY_TYPE = 1;
private const INDEX_ALGORITHM = 3;
// 13.1.1-13.2
private const INDEX_CURVE = -1; // ECC, OKP
private const INDEX_X_COORDINATE = -2; // ECC, OKP
private const INDEX_Y_COORDINATE = -3; // ECC
private const INDEX_PRIVATE_KEY = -4; // ECC, OKP @phpstan-ignore-line
// index_key_value = -1 (same as index_curve, for Symmetric)
private COSE\KeyType $keyType;
private COSE\Algorithm $algorithm;
private COSE\Curve $curve;
private BinaryString $x;
private BinaryString $y;
// d ~ private key
public function __construct(public readonly BinaryString $cbor)
{
$decoder = new Decoder();
$decodedCbor = $decoder->decode($cbor->unwrap());
// Note: these limitations may be lifted in the future
$keyType = COSE\KeyType::tryFrom($decodedCbor[self::INDEX_KEY_TYPE]);
if ($keyType !== COSE\KeyType::EllipticCurve) {
throw new DomainException('Only EC2 keys supported');
}
$algorithm = COSE\Algorithm::tryFrom($decodedCbor[self::INDEX_ALGORITHM]);
if ($algorithm !== COSE\Algorithm::EcdsaSha256) {
throw new DomainException('Only ES256 supported');
}
$curve = COSE\Curve::tryFrom($decodedCbor[self::INDEX_CURVE]);
// https://www.w3.org/TR/webauthn-3/#sctn-alg-identifier
// 5.8.5 - curve must match algorithm
$expectedCurve = match ($algorithm) {
COSE\Algorithm::EcdsaSha256 => COSE\Curve::P256,
// Permit more later.
// COSE\Algorithm::EcdsaSha384 => COSE\Curve::P384,
// COSE\Algorithm::EcdsaSha512 => COSE\Curve::P521,
// COSE\Algorithm::EdDSA => COSE\Curve::ED25519,
};
if ($curve !== $expectedCurve) {
throw new DomainException('Only curve P-256 (secp256r1) supported');
}
$this->keyType = $keyType;
$this->algorithm = $algorithm;
$this->curve = $curve;
if (strlen($decodedCbor[self::INDEX_X_COORDINATE]) !== 32) {
throw new DomainException('X coordinate not 32 bytes');
}
$this->x = new BinaryString($decodedCbor[self::INDEX_X_COORDINATE]);
if (strlen($decodedCbor[self::INDEX_Y_COORDINATE]) !== 32) {
throw new DomainException('X coordinate not 32 bytes');
}
$this->y = new BinaryString($decodedCbor[self::INDEX_Y_COORDINATE]);
// d = cbor[INDEX_PRIVATE_KEY]
// Future: rfc8152/13.2
// if keytype == .OctetKeyPair, set `x` and `d`
}
/**
* FIXME: this indirection is not desirable
*/
public function getPublicKey(): PublicKey\PublicKeyInterface
{
// These are valid; the internal formats are brittle right now.
assert($this->keyType === COSE\KeyType::EllipticCurve);
assert($this->curve === COSE\Curve::P256);
// This I don't think conveys anything useful. Mostly retained to
// silence a warning about unused variables.
assert($this->algorithm === COSE\Algorithm::EcdsaSha256);
return new PublicKey\EllipticCurve($this->x, $this->y);
}
}