RsaKey::getModulus()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
namespace MadWizard\WebAuthn\Crypto;
4
5
use MadWizard\WebAuthn\Format\ByteBuffer;
6
use MadWizard\WebAuthn\Format\CborEncoder;
7
use MadWizard\WebAuthn\Format\CborMap;
8
use MadWizard\WebAuthn\Format\DataValidator;
9
10
class RsaKey extends CoseKey
11
{
12
    /**
13
     * RSA modulus n key type parameter (key type 3, RSA).
14
     *
15
     * @see https://www.iana.org/assignments/cose/cose.xhtml#key-type-parameters
16
     */
17
    private const KTP_N = -1;
18
19
    /**
20
     * RSA exponent e key type parameter (key type 3, RSA).
21
     *
22
     * @see https://www.iana.org/assignments/cose/cose.xhtml#key-type-parameters
23
     */
24
    private const KTP_E = -2;
25
26
    /**
27
     * RSA modulus. Unsigned integer value represented as binary buffer with leading zero bytes removed.
28
     *
29
     * @var ByteBuffer
30
     */
31
    private $modulus;
32
33
    /**
34
     * RSA exponent. Unsigned integer value represented as binary buffer with leading zero bytes removed.
35
     *
36
     * @var ByteBuffer
37
     */
38
    private $exponent;
39
40
    private const SUPPORTED_ALGORITHMS = [
41
        CoseAlgorithm::RS256,
42
        CoseAlgorithm::RS384,
43
        CoseAlgorithm::RS512,
44
        CoseAlgorithm::RS1,
45
    ];
46
47 9
    public function __construct(ByteBuffer $modulus, ByteBuffer $exponent, int $algorithm)
48
    {
49 9
        parent::__construct($algorithm);
50 9
        $this->modulus = $this->compactIntegerBuffer($modulus);
51 9
        $this->exponent = $this->compactIntegerBuffer($exponent);
52 9
    }
53
54 4
    public static function fromCborData(CborMap $data): RsaKey
55
    {
56 4
        DataValidator::checkMap(
57 4
            $data,
58
            [
59 4
                self::COSE_KEY_PARAM_KTY => 'integer',
60 4
                self::COSE_KEY_PARAM_ALG => 'integer',
61 4
                self::KTP_N => ByteBuffer::class,
62 4
                self::KTP_E => ByteBuffer::class,
63
            ]
64
        );
65
66 3
        $algorithm = $data->get(self::COSE_KEY_PARAM_ALG);
67 3
        $modulus = $data->get(self::KTP_N);
68 3
        $exponent = $data->get(self::KTP_E);
69
70 3
        return new RsaKey($modulus, $exponent, $algorithm);
71
    }
72
73 4
    public function verifySignature(ByteBuffer $data, ByteBuffer $signature): bool
74
    {
75 4
        $verifier = new OpenSslVerifier($this->getAlgorithm());
76 4
        return $verifier->verify($data->getBinaryString(), $signature->getBinaryString(), $this->asPem());
77
    }
78
79
    /**
80
     * Removes all leading zero bytes from a ByteBuffer, but keeps one zero byte if it is the only byte left and the
81
     * original buffer did not have zero length.
82
     */
83 9
    private function compactIntegerBuffer(ByteBuffer $buffer): ByteBuffer
84
    {
85 9
        $length = $buffer->getLength();
86 9
        $raw = $buffer->getBinaryString();
87 9
        for ($i = 0; $i < ($length - 1); $i++) {
88 9
            if (ord($raw[$i]) !== 0) {
89 9
                break;
90
            }
91
        }
92 9
        if ($i !== 0) {
93 1
            return new ByteBuffer(\substr($raw, $i));
94
        }
95 8
        return $buffer;
96
    }
97
98 4
    public function getModulus(): ByteBuffer
99
    {
100 4
        return $this->modulus;
101
    }
102
103 4
    public function getExponent(): ByteBuffer
104
    {
105 4
        return $this->exponent;
106
    }
107
108 5
    public function asDer(): string
109
    {
110
        // DER encoded RSA key
111
        return
112 5
            Der::sequence(
113 5
                Der::sequence(
114 5
                    Der::oid("\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01") . // OID 1.2.840.113549.1.1.1 rsaEncryption
115 5
                    Der::nullValue()
116
                ) .
117 5
                Der::bitString(
118 5
                    Der::sequence(
119 5
                        Der::unsignedInteger($this->modulus->getBinaryString()) .
120 5
                        Der::unsignedInteger($this->exponent->getBinaryString())
121
                    )
122
                )
123
            );
124
    }
125
126 1
    public function getCbor(): ByteBuffer
127
    {
128
        $map = [
129 1
            self::COSE_KEY_PARAM_KTY => self::COSE_KTY_RSA,
130 1
            self::COSE_KEY_PARAM_ALG => $this->getAlgorithm(),
131 1
            self::KTP_N => $this->modulus,
132 1
            self::KTP_E => $this->exponent,
133
        ];
134 1
        return new ByteBuffer(CborEncoder::encodeMap(CborMap::fromArray($map)));
135
    }
136
137 9
    protected function algorithmSupported(int $algorithm): bool
138
    {
139 9
        return in_array($algorithm, self::SUPPORTED_ALGORITHMS, true);
140
    }
141
}
142