Subversion Repositories oidplus

Rev

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
}