1<?php
2
3/**
4 * Pure-PHP FIPS 186-4 compliant implementation of DSA.
5 *
6 * PHP version 5
7 *
8 * Here's an example of how to create signatures and verify signatures with this library:
9 * <code>
10 * <?php
11 * include 'vendor/autoload.php';
12 *
13 * $private = \phpseclib3\Crypt\DSA::createKey();
14 * $public = $private->getPublicKey();
15 *
16 * $plaintext = 'terrafrost';
17 *
18 * $signature = $private->sign($plaintext);
19 *
20 * echo $public->verify($plaintext, $signature) ? 'verified' : 'unverified';
21 * ?>
22 * </code>
23 *
24 * @author    Jim Wigginton <terrafrost@php.net>
25 * @copyright 2016 Jim Wigginton
26 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
27 * @link      http://phpseclib.sourceforge.net
28 */
29
30namespace phpseclib3\Crypt;
31
32use phpseclib3\Crypt\Common\AsymmetricKey;
33use phpseclib3\Crypt\DSA\Parameters;
34use phpseclib3\Crypt\DSA\PrivateKey;
35use phpseclib3\Crypt\DSA\PublicKey;
36use phpseclib3\Exception\InsufficientSetupException;
37use phpseclib3\Math\BigInteger;
38
39/**
40 * Pure-PHP FIPS 186-4 compliant implementation of DSA.
41 *
42 * @author  Jim Wigginton <terrafrost@php.net>
43 */
44abstract class DSA extends AsymmetricKey
45{
46    /**
47     * Algorithm Name
48     *
49     * @var string
50     */
51    const ALGORITHM = 'DSA';
52
53    /**
54     * DSA Prime P
55     *
56     * @var \phpseclib3\Math\BigInteger
57     */
58    protected $p;
59
60    /**
61     * DSA Group Order q
62     *
63     * Prime divisor of p-1
64     *
65     * @var \phpseclib3\Math\BigInteger
66     */
67    protected $q;
68
69    /**
70     * DSA Group Generator G
71     *
72     * @var \phpseclib3\Math\BigInteger
73     */
74    protected $g;
75
76    /**
77     * DSA public key value y
78     *
79     * @var \phpseclib3\Math\BigInteger
80     */
81    protected $y;
82
83    /**
84     * Signature Format
85     *
86     * @var string
87     */
88    protected $sigFormat;
89
90    /**
91     * Signature Format (Short)
92     *
93     * @var string
94     */
95    protected $shortFormat;
96
97    /**
98     * Create DSA parameters
99     *
100     * @param int $L
101     * @param int $N
102     * @return \phpseclib3\Crypt\DSA|bool
103     */
104    public static function createParameters($L = 2048, $N = 224)
105    {
106        self::initialize_static_variables();
107
108        $class = new \ReflectionClass(static::class);
109        if ($class->isFinal()) {
110            throw new \RuntimeException('createParameters() should not be called from final classes (' . static::class . ')');
111        }
112
113        if (!isset(self::$engines['PHP'])) {
114            self::useBestEngine();
115        }
116
117        switch (true) {
118            case $N == 160:
119            /*
120              in FIPS 186-1 and 186-2 N was fixed at 160 whereas K had an upper bound of 1024.
121              RFC 4253 (SSH Transport Layer Protocol) references FIPS 186-2 and as such most
122              SSH DSA implementations only support keys with an N of 160.
123              puttygen let's you set the size of L (but not the size of N) and uses 2048 as the
124              default L value. that's not really compliant with any of the FIPS standards, however,
125              for the purposes of maintaining compatibility with puttygen, we'll support it
126            */
127            //case ($L >= 512 || $L <= 1024) && (($L & 0x3F) == 0) && $N == 160:
128            // FIPS 186-3 changed this as follows:
129            //case $L == 1024 && $N == 160:
130            case $L == 2048 && $N == 224:
131            case $L == 2048 && $N == 256:
132            case $L == 3072 && $N == 256:
133                break;
134            default:
135                throw new \InvalidArgumentException('Invalid values for N and L');
136        }
137
138        $two = new BigInteger(2);
139
140        $q = BigInteger::randomPrime($N);
141        $divisor = $q->multiply($two);
142
143        do {
144            $x = BigInteger::random($L);
145            list(, $c) = $x->divide($divisor);
146            $p = $x->subtract($c->subtract(self::$one));
147        } while ($p->getLength() != $L || !$p->isPrime());
148
149        $p_1 = $p->subtract(self::$one);
150        list($e) = $p_1->divide($q);
151
152        // quoting http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf#page=50 ,
153        // "h could be obtained from a random number generator or from a counter that
154        //  changes after each use". PuTTY (sshdssg.c) starts h off at 1 and increments
155        // it on each loop. wikipedia says "commonly h = 2 is used" so we'll just do that
156        $h = clone $two;
157        while (true) {
158            $g = $h->powMod($e, $p);
159            if (!$g->equals(self::$one)) {
160                break;
161            }
162            $h = $h->add(self::$one);
163        }
164
165        $dsa = new Parameters();
166        $dsa->p = $p;
167        $dsa->q = $q;
168        $dsa->g = $g;
169
170        return $dsa;
171    }
172
173    /**
174     * Create public / private key pair.
175     *
176     * This method is a bit polymorphic. It can take a DSA/Parameters object, L / N as two distinct parameters or
177     * no parameters (at which point L and N will be generated with this method)
178     *
179     * Returns the private key, from which the publickey can be extracted
180     *
181     * @param int[] ...$args
182     * @return DSA\PrivateKey
183     */
184    public static function createKey(...$args)
185    {
186        self::initialize_static_variables();
187
188        $class = new \ReflectionClass(static::class);
189        if ($class->isFinal()) {
190            throw new \RuntimeException('createKey() should not be called from final classes (' . static::class . ')');
191        }
192
193        if (!isset(self::$engines['PHP'])) {
194            self::useBestEngine();
195        }
196
197        if (count($args) == 2 && is_int($args[0]) && is_int($args[1])) {
198            $params = self::createParameters($args[0], $args[1]);
199        } elseif (count($args) == 1 && $args[0] instanceof Parameters) {
200            $params = $args[0];
201        } elseif (!count($args)) {
202            $params = self::createParameters();
203        } else {
204            throw new InsufficientSetupException('Valid parameters are either two integers (L and N), a single DSA object or no parameters at all.');
205        }
206
207        $private = new PrivateKey();
208        $private->p = $params->p;
209        $private->q = $params->q;
210        $private->g = $params->g;
211
212        $private->x = BigInteger::randomRange(self::$one, $private->q->subtract(self::$one));
213        $private->y = $private->g->powMod($private->x, $private->p);
214
215        //$public = clone $private;
216        //unset($public->x);
217
218        return $private
219            ->withHash($params->hash->getHash())
220            ->withSignatureFormat($params->shortFormat);
221    }
222
223    /**
224     * OnLoad Handler
225     *
226     * @return bool
227     */
228    protected static function onLoad(array $components)
229    {
230        if (!isset(self::$engines['PHP'])) {
231            self::useBestEngine();
232        }
233
234        if (!isset($components['x']) && !isset($components['y'])) {
235            $new = new Parameters();
236        } elseif (isset($components['x'])) {
237            $new = new PrivateKey();
238            $new->x = $components['x'];
239        } else {
240            $new = new PublicKey();
241        }
242
243        $new->p = $components['p'];
244        $new->q = $components['q'];
245        $new->g = $components['g'];
246
247        if (isset($components['y'])) {
248            $new->y = $components['y'];
249        }
250
251        return $new;
252    }
253
254    /**
255     * Constructor
256     *
257     * PublicKey and PrivateKey objects can only be created from abstract RSA class
258     */
259    protected function __construct()
260    {
261        $this->sigFormat = self::validatePlugin('Signature', 'ASN1');
262        $this->shortFormat = 'ASN1';
263
264        parent::__construct();
265    }
266
267    /**
268     * Returns the key size
269     *
270     * More specifically, this L (the length of DSA Prime P) and N (the length of DSA Group Order q)
271     *
272     * @return array
273     */
274    public function getLength()
275    {
276        return ['L' => $this->p->getLength(), 'N' => $this->q->getLength()];
277    }
278
279    /**
280     * Returns the current engine being used
281     *
282     * @see self::useInternalEngine()
283     * @see self::useBestEngine()
284     * @return string
285     */
286    public function getEngine()
287    {
288        if (!isset(self::$engines['PHP'])) {
289            self::useBestEngine();
290        }
291        return self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods()) ?
292            'OpenSSL' : 'PHP';
293    }
294
295    /**
296     * Returns the parameters
297     *
298     * A public / private key is only returned if the currently loaded "key" contains an x or y
299     * value.
300     *
301     * @see self::getPublicKey()
302     * @return mixed
303     */
304    public function getParameters()
305    {
306        $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');
307
308        $key = $type::saveParameters($this->p, $this->q, $this->g);
309        return DSA::load($key, 'PKCS1')
310            ->withHash($this->hash->getHash())
311            ->withSignatureFormat($this->shortFormat);
312    }
313
314    /**
315     * Determines the signature padding mode
316     *
317     * Valid values are: ASN1, SSH2, Raw
318     *
319     * @param string $format
320     */
321    public function withSignatureFormat($format)
322    {
323        $new = clone $this;
324        $new->shortFormat = $format;
325        $new->sigFormat = self::validatePlugin('Signature', $format);
326        return $new;
327    }
328
329    /**
330     * Returns the signature format currently being used
331     *
332     */
333    public function getSignatureFormat()
334    {
335        return $this->shortFormat;
336    }
337}
338