1<?php
2
3/**
4 * OpenSSH Formatted DSA Key Handler
5 *
6 * PHP version 5
7 *
8 * Place in $HOME/.ssh/authorized_keys
9 *
10 * @category  Crypt
11 * @package   DSA
12 * @author    Jim Wigginton <terrafrost@php.net>
13 * @copyright 2015 Jim Wigginton
14 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
15 * @link      http://phpseclib.sourceforge.net
16 */
17
18namespace phpseclib3\Crypt\DSA\Formats\Keys;
19
20use phpseclib3\Common\Functions\Strings;
21use phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor;
22use phpseclib3\Math\BigInteger;
23
24/**
25 * OpenSSH Formatted DSA Key Handler
26 *
27 * @package DSA
28 * @author  Jim Wigginton <terrafrost@php.net>
29 * @access  public
30 */
31abstract class OpenSSH extends Progenitor
32{
33    /**
34     * Supported Key Types
35     *
36     * @var array
37     */
38    protected static $types = ['ssh-dss'];
39
40    /**
41     * Break a public or private key down into its constituent components
42     *
43     * @access public
44     * @param string $key
45     * @param string $password optional
46     * @return array
47     */
48    public static function load($key, $password = '')
49    {
50        $parsed = parent::load($key, $password);
51
52        if (isset($parsed['paddedKey'])) {
53            list($type) = Strings::unpackSSH2('s', $parsed['paddedKey']);
54            if ($type != $parsed['type']) {
55                throw new \RuntimeException("The public and private keys are not of the same type ($type vs $parsed[type])");
56            }
57
58            list($p, $q, $g, $y, $x, $comment) = Strings::unpackSSH2('i5s', $parsed['paddedKey']);
59
60            return compact('p', 'q', 'g', 'y', 'x', 'comment');
61        }
62
63        list($p, $q, $g, $y) = Strings::unpackSSH2('iiii', $parsed['publicKey']);
64
65        $comment = $parsed['comment'];
66
67        return compact('p', 'q', 'g', 'y', 'comment');
68    }
69
70    /**
71     * Convert a public key to the appropriate format
72     *
73     * @access public
74     * @param \phpseclib3\Math\BigInteger $p
75     * @param \phpseclib3\Math\BigInteger $q
76     * @param \phpseclib3\Math\BigInteger $g
77     * @param \phpseclib3\Math\BigInteger $y
78     * @param array $options optional
79     * @return string
80     */
81    public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, array $options = [])
82    {
83        if ($q->getLength() != 160) {
84            throw new \InvalidArgumentException('SSH only supports keys with an N (length of Group Order q) of 160');
85        }
86
87        // from <http://tools.ietf.org/html/rfc4253#page-15>:
88        // string    "ssh-dss"
89        // mpint     p
90        // mpint     q
91        // mpint     g
92        // mpint     y
93        $DSAPublicKey = Strings::packSSH2('siiii', 'ssh-dss', $p, $q, $g, $y);
94
95        if (isset($options['binary']) ? $options['binary'] : self::$binary) {
96            return $DSAPublicKey;
97        }
98
99        $comment = isset($options['comment']) ? $options['comment'] : self::$comment;
100        $DSAPublicKey = 'ssh-dss ' . base64_encode($DSAPublicKey) . ' ' . $comment;
101
102        return $DSAPublicKey;
103    }
104
105    /**
106     * Convert a private key to the appropriate format.
107     *
108     * @access public
109     * @param \phpseclib3\Math\BigInteger $p
110     * @param \phpseclib3\Math\BigInteger $q
111     * @param \phpseclib3\Math\BigInteger $g
112     * @param \phpseclib3\Math\BigInteger $y
113     * @param \phpseclib3\Math\BigInteger $x
114     * @param string $password optional
115     * @param array $options optional
116     * @return string
117     */
118    public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, $password = '', array $options = [])
119    {
120        $publicKey = self::savePublicKey($p, $q, $g, $y, ['binary' => true]);
121        $privateKey = Strings::packSSH2('si5', 'ssh-dss', $p, $q, $g, $y, $x);
122
123        return self::wrapPrivateKey($publicKey, $privateKey, $password, $options);
124    }
125}
126