PBKDF2AlgorithmIdentifier::__construct()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 8
c 0
b 0
f 0
ccs 6
cts 6
cp 1
rs 10
cc 2
nc 2
nop 4
crap 2
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Sop\PKCS5\ASN1\AlgorithmIdentifier;
6
7
use Sop\ASN1\Element;
8
use Sop\ASN1\Type\Constructed\Sequence;
9
use Sop\ASN1\Type\Primitive\Integer;
10
use Sop\ASN1\Type\Primitive\OctetString;
11
use Sop\ASN1\Type\UnspecifiedType;
12
use Sop\CryptoTypes\AlgorithmIdentifier\AlgorithmIdentifier;
13
use Sop\CryptoTypes\AlgorithmIdentifier\AlgorithmIdentifierFactory;
14
use Sop\CryptoTypes\AlgorithmIdentifier\Feature\PRFAlgorithmIdentifier;
15
use Sop\CryptoTypes\AlgorithmIdentifier\Hash\HMACWithSHA1AlgorithmIdentifier;
16
use Sop\CryptoTypes\AlgorithmIdentifier\SpecificAlgorithmIdentifier;
17
18
/*
19
From RFC 2898 - A.2   PBKDF2:
20
21
PBKDF2-params ::= SEQUENCE {
22
    salt CHOICE {
23
        specified OCTET STRING,
24
        otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
25
    },
26
    iterationCount INTEGER (1..MAX),
27
    keyLength INTEGER (1..MAX) OPTIONAL,
28
    prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT
29
    algid-hmacWithSHA1 }
30
*/
31
32
/**
33
 * Algorithm identifier for PBKDF2 key derivation function.
34
 *
35
 * @see https://tools.ietf.org/html/rfc2898#section-5.2
36
 * @see https://tools.ietf.org/html/rfc2898#appendix-A.2
37
 */
38
class PBKDF2AlgorithmIdentifier extends SpecificAlgorithmIdentifier
39
{
40
    const OID_PBKDF2 = '1.2.840.113549.1.5.12';
41
42
    /**
43
     * Explicitly specified salt.
44
     *
45
     * @var string
46
     */
47
    protected $_specifiedSalt;
48
49
    /**
50
     * Iteration count.
51
     *
52
     * @var int
53
     */
54
    protected $_iterationCount;
55
56
    /**
57
     * Key length.
58
     *
59
     * @var null|int
60
     */
61
    protected $_keyLength;
62
63
    /**
64
     * Pseudorandom function algorithm identifier.
65
     *
66
     * @var PRFAlgorithmIdentifier
67
     */
68
    protected $_prfAlgo;
69
70
    /**
71
     * Constructor.
72
     *
73
     * @param string                      $salt            Salt
74
     * @param int                         $iteration_count Iteration count
75
     * @param null|int                    $key_length      Optional key length
76
     * @param null|PRFAlgorithmIdentifier $prf_algo        Default to HMAC-SHA1
77
     */
78 8
    public function __construct(string $salt, int $iteration_count,
79
        ?int $key_length = null, ?PRFAlgorithmIdentifier $prf_algo = null)
80
    {
81 8
        $this->_oid = self::OID_PBKDF2;
82 8
        $this->_specifiedSalt = $salt;
83 8
        $this->_iterationCount = $iteration_count;
84 8
        $this->_keyLength = $key_length;
85 8
        $this->_prfAlgo = isset($prf_algo) ? $prf_algo : new HMACWithSHA1AlgorithmIdentifier();
86 8
    }
87
88
    /**
89
     * @param Sequence $seq
90
     *
91
     * @return AlgorithmIdentifier
92
     */
93 9
    public static function fromASN1(Sequence $seq): AlgorithmIdentifier
94
    {
95 9
        return (new AlgorithmIdentifierFactory(
96 9
            new PKCS5AlgorithmIdentifierProvider()))->parse($seq);
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102 1
    public function name(): string
103
    {
104 1
        return 'pBKDF2';
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     *
110
     * @throws \RuntimeException
111
     *
112
     * @return self
113
     */
114 8
    public static function fromASN1Params(
115
        ?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
116
    {
117 8
        if (!isset($params)) {
118 1
            throw new \UnexpectedValueException('No parameters.');
119
        }
120 7
        $seq = $params->asSequence();
121 7
        $el = $seq->at(0);
122 7
        switch ($el->tag()) {
123
            // specified
124 7
            case Element::TYPE_OCTET_STRING:
125 5
                $salt = $el->asOctetString()->string();
126 5
                break;
127
            // otherSource
128 2
            case Element::TYPE_SEQUENCE:
129 1
                AlgorithmIdentifier::fromASN1($el->asSequence());
130 1
                throw new \RuntimeException('otherSource not implemented.');
131
            default:
132 1
                throw new \UnexpectedValueException('Invalid salt encoding.');
133
        }
134 5
        $iteration_count = $seq->at(1)->asInteger()->intNumber();
135 5
        $key_length = null;
136 5
        $prf_algo = null;
137 5
        $idx = 2;
138 5
        if ($seq->has($idx, Element::TYPE_INTEGER)) {
139 3
            $key_length = $seq->at($idx++)->asInteger()->intNumber();
140
        }
141 5
        if ($seq->has($idx, Element::TYPE_SEQUENCE)) {
142 2
            $prf_algo = AlgorithmIdentifier::fromASN1(
143 2
                $seq->at($idx++)->asSequence());
144 2
            if (!($prf_algo instanceof PRFAlgorithmIdentifier)) {
145 1
                throw new \UnexpectedValueException(
146 1
                    sprintf('%s is not a supported pseudorandom function.',
147 1
                        $prf_algo->name()));
148
            }
149
        }
150 4
        return new self($salt, $iteration_count, $key_length, $prf_algo);
151
    }
152
153
    /**
154
     * Get salt.
155
     *
156
     * @return string
157
     */
158 3
    public function salt(): string
159
    {
160 3
        return $this->_specifiedSalt;
161
    }
162
163
    /**
164
     * Get iteration count.
165
     *
166
     * @return int
167
     */
168 3
    public function iterationCount(): int
169
    {
170 3
        return $this->_iterationCount;
171
    }
172
173
    /**
174
     * Whether key length is present.
175
     *
176
     * @return bool
177
     */
178 2
    public function hasKeyLength(): bool
179
    {
180 2
        return isset($this->_keyLength);
181
    }
182
183
    /**
184
     * Get key length.
185
     *
186
     * @throws \LogicException
187
     *
188
     * @return int
189
     */
190 2
    public function keyLength(): int
191
    {
192 2
        if (!$this->hasKeyLength()) {
193 1
            throw new \LogicException('keyLength not set.');
194
        }
195 1
        return $this->_keyLength;
196
    }
197
198
    /**
199
     * Get pseudorandom function algorithm.
200
     *
201
     * @return PRFAlgorithmIdentifier
202
     */
203 4
    public function prfAlgorithmIdentifier(): PRFAlgorithmIdentifier
204
    {
205 4
        return $this->_prfAlgo;
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     *
211
     * @return Sequence
212
     */
213 3
    protected function _paramsASN1(): ?Element
214
    {
215 3
        $elements = [];
216 3
        $elements[] = new OctetString($this->_specifiedSalt);
217 3
        $elements[] = new Integer($this->_iterationCount);
218 3
        if (isset($this->_keyLength)) {
219 2
            $elements[] = new Integer($this->_keyLength);
220
        }
221 3
        if (AlgorithmIdentifier::OID_HMAC_WITH_SHA1 !== $this->_prfAlgo->oid()) {
222 1
            $elements[] = $this->_prfAlgo->toASN1();
223
        }
224 3
        return new Sequence(...$elements);
225
    }
226
}
227