Rev 846 | Rev 1042 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
827 | daniel-mar | 1 | <?php |
2 | |||
3 | /** |
||
4 | * DSA Private Key |
||
5 | * |
||
874 | daniel-mar | 6 | * @category Crypt |
7 | * @package DSA |
||
827 | daniel-mar | 8 | * @author Jim Wigginton <terrafrost@php.net> |
9 | * @copyright 2015 Jim Wigginton |
||
10 | * @license http://www.opensource.org/licenses/mit-license.html MIT License |
||
11 | * @link http://phpseclib.sourceforge.net |
||
12 | */ |
||
13 | |||
14 | namespace phpseclib3\Crypt\DSA; |
||
15 | |||
16 | use phpseclib3\Crypt\Common; |
||
17 | use phpseclib3\Crypt\DSA; |
||
18 | use phpseclib3\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature; |
||
19 | use phpseclib3\Math\BigInteger; |
||
20 | |||
21 | /** |
||
22 | * DSA Private Key |
||
23 | * |
||
874 | daniel-mar | 24 | * @package DSA |
827 | daniel-mar | 25 | * @author Jim Wigginton <terrafrost@php.net> |
874 | daniel-mar | 26 | * @access public |
827 | daniel-mar | 27 | */ |
28 | class PrivateKey extends DSA implements Common\PrivateKey |
||
29 | { |
||
30 | use Common\Traits\PasswordProtected; |
||
31 | |||
32 | /** |
||
33 | * DSA secret exponent x |
||
34 | * |
||
35 | * @var \phpseclib3\Math\BigInteger |
||
874 | daniel-mar | 36 | * @access private |
827 | daniel-mar | 37 | */ |
38 | protected $x; |
||
39 | |||
40 | /** |
||
41 | * Returns the public key |
||
42 | * |
||
43 | * If you do "openssl rsa -in private.rsa -pubout -outform PEM" you get a PKCS8 formatted key |
||
44 | * that contains a publicKeyAlgorithm AlgorithmIdentifier and a publicKey BIT STRING. |
||
45 | * An AlgorithmIdentifier contains an OID and a parameters field. With RSA public keys this |
||
46 | * parameters field is NULL. With DSA PKCS8 public keys it is not - it contains the p, q and g |
||
47 | * variables. The publicKey BIT STRING contains, simply, the y variable. This can be verified |
||
48 | * by getting a DSA PKCS8 public key: |
||
49 | * |
||
50 | * "openssl dsa -in private.dsa -pubout -outform PEM" |
||
51 | * |
||
52 | * ie. just swap out rsa with dsa in the rsa command above. |
||
53 | * |
||
54 | * A PKCS1 public key corresponds to the publicKey portion of the PKCS8 key. In the case of RSA |
||
55 | * the publicKey portion /is/ the key. In the case of DSA it is not. You cannot verify a signature |
||
56 | * without the parameters and the PKCS1 DSA public key format does not include the parameters. |
||
57 | * |
||
58 | * @see self::getPrivateKey() |
||
874 | daniel-mar | 59 | * @access public |
827 | daniel-mar | 60 | * @return mixed |
61 | */ |
||
62 | public function getPublicKey() |
||
63 | { |
||
64 | $type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey'); |
||
65 | |||
66 | if (!isset($this->y)) { |
||
67 | $this->y = $this->g->powMod($this->x, $this->p); |
||
68 | } |
||
69 | |||
70 | $key = $type::savePublicKey($this->p, $this->q, $this->g, $this->y); |
||
71 | |||
72 | return DSA::loadFormat('PKCS8', $key) |
||
73 | ->withHash($this->hash->getHash()) |
||
74 | ->withSignatureFormat($this->shortFormat); |
||
75 | } |
||
76 | |||
77 | /** |
||
78 | * Create a signature |
||
79 | * |
||
80 | * @see self::verify() |
||
874 | daniel-mar | 81 | * @access public |
827 | daniel-mar | 82 | * @param string $message |
83 | * @return mixed |
||
84 | */ |
||
85 | public function sign($message) |
||
86 | { |
||
87 | $format = $this->sigFormat; |
||
88 | |||
89 | if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) { |
||
90 | $signature = ''; |
||
91 | $result = openssl_sign($message, $signature, $this->toString('PKCS8'), $this->hash->getHash()); |
||
92 | |||
93 | if ($result) { |
||
94 | if ($this->shortFormat == 'ASN1') { |
||
95 | return $signature; |
||
96 | } |
||
97 | |||
98 | extract(ASN1Signature::load($signature)); |
||
99 | |||
100 | return $format::save($r, $s); |
||
101 | } |
||
102 | } |
||
103 | |||
104 | $h = $this->hash->hash($message); |
||
105 | $h = $this->bits2int($h); |
||
106 | |||
107 | while (true) { |
||
108 | $k = BigInteger::randomRange(self::$one, $this->q->subtract(self::$one)); |
||
109 | $r = $this->g->powMod($k, $this->p); |
||
110 | list(, $r) = $r->divide($this->q); |
||
111 | if ($r->equals(self::$zero)) { |
||
112 | continue; |
||
113 | } |
||
114 | $kinv = $k->modInverse($this->q); |
||
115 | $temp = $h->add($this->x->multiply($r)); |
||
116 | $temp = $kinv->multiply($temp); |
||
117 | list(, $s) = $temp->divide($this->q); |
||
118 | if (!$s->equals(self::$zero)) { |
||
119 | break; |
||
120 | } |
||
121 | } |
||
122 | |||
123 | // the following is an RFC6979 compliant implementation of deterministic DSA |
||
124 | // it's unused because it's mainly intended for use when a good CSPRNG isn't |
||
125 | // available. if phpseclib's CSPRNG isn't good then even key generation is |
||
126 | // suspect |
||
127 | /* |
||
128 | $h1 = $this->hash->hash($message); |
||
129 | $k = $this->computek($h1); |
||
130 | $r = $this->g->powMod($k, $this->p); |
||
131 | list(, $r) = $r->divide($this->q); |
||
132 | $kinv = $k->modInverse($this->q); |
||
133 | $h1 = $this->bits2int($h1); |
||
134 | $temp = $h1->add($this->x->multiply($r)); |
||
135 | $temp = $kinv->multiply($temp); |
||
136 | list(, $s) = $temp->divide($this->q); |
||
137 | */ |
||
138 | |||
139 | return $format::save($r, $s); |
||
140 | } |
||
141 | |||
142 | /** |
||
143 | * Returns the private key |
||
144 | * |
||
145 | * @param string $type |
||
146 | * @param array $options optional |
||
147 | * @return string |
||
148 | */ |
||
149 | public function toString($type, array $options = []) |
||
150 | { |
||
151 | $type = self::validatePlugin('Keys', $type, 'savePrivateKey'); |
||
152 | |||
153 | if (!isset($this->y)) { |
||
154 | $this->y = $this->g->powMod($this->x, $this->p); |
||
155 | } |
||
156 | |||
157 | return $type::savePrivateKey($this->p, $this->q, $this->g, $this->y, $this->x, $this->password, $options); |
||
158 | } |
||
159 | } |