Viel neues

This commit is contained in:
Sven Steinert
2026-04-30 12:06:00 +02:00
parent 118809bfae
commit fce31ebcd7
1274 changed files with 181255 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
<?php
/**
* OpenSSH Formatted DSA Key Handler
*
* PHP version 5
*
* Place in $HOME/.ssh/authorized_keys
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\DSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\OpenSSH as Progenitor;
use phpseclib3\Exception\InvalidArgumentException;
use phpseclib3\Exception\RuntimeException;
use phpseclib3\Math\BigInteger;
/**
* OpenSSH Formatted DSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class OpenSSH extends Progenitor
{
/**
* Supported Key Types
*
* @var array
*/
protected static $types = ['ssh-dss'];
/**
* Break a public or private key down into its constituent components
*
* @param string|array $key
*/
public static function load($key, ?string $password = null): array
{
$parsed = parent::load($key, $password);
if (isset($parsed['paddedKey'])) {
[$type] = Strings::unpackSSH2('s', $parsed['paddedKey']);
if ($type != $parsed['type']) {
throw new RuntimeException("The public and private keys are not of the same type ($type vs $parsed[type])");
}
[$p, $q, $g, $y, $x, $comment] = Strings::unpackSSH2('i5s', $parsed['paddedKey']);
return compact('p', 'q', 'g', 'y', 'x', 'comment');
}
[$p, $q, $g, $y] = Strings::unpackSSH2('iiii', $parsed['publicKey']);
$comment = $parsed['comment'];
return compact('p', 'q', 'g', 'y', 'comment');
}
/**
* Convert a public key to the appropriate format
*
* @param array $options optional
*/
public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, array $options = []): string
{
if ($q->getLength() != 160) {
throw new InvalidArgumentException('SSH only supports keys with an N (length of Group Order q) of 160');
}
// from <http://tools.ietf.org/html/rfc4253#page-15>:
// string "ssh-dss"
// mpint p
// mpint q
// mpint g
// mpint y
$DSAPublicKey = Strings::packSSH2('siiii', 'ssh-dss', $p, $q, $g, $y);
if ($options['binary'] ?? self::$binary) {
return $DSAPublicKey;
}
$comment = $options['comment'] ?? self::$comment;
$DSAPublicKey = 'ssh-dss ' . base64_encode($DSAPublicKey) . ' ' . $comment;
return $DSAPublicKey;
}
/**
* Convert a private key to the appropriate format.
*/
public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, ?string $password = null, array $options = []): string
{
$publicKey = self::savePublicKey($p, $q, $g, $y, ['binary' => true]);
$privateKey = Strings::packSSH2('si5', 'ssh-dss', $p, $q, $g, $y, $x);
return self::wrapPrivateKey($publicKey, $privateKey, $password, $options);
}
}

View File

@@ -0,0 +1,127 @@
<?php
/**
* PKCS#1 Formatted DSA Key Handler
*
* PHP version 5
*
* Used by File/X509.php
*
* Processes keys with the following headers:
*
* -----BEGIN DSA PRIVATE KEY-----
* -----BEGIN DSA PUBLIC KEY-----
* -----BEGIN DSA PARAMETERS-----
*
* Analogous to ssh-keygen's pem format (as specified by -m)
*
* Also, technically, PKCS1 decribes RSA but I am not aware of a formal specification for DSA.
* The DSA private key format seems to have been adapted from the RSA private key format so
* we're just re-using that as the name.
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\DSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\PKCS1 as Progenitor;
use phpseclib3\Exception\RuntimeException;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps;
use phpseclib3\Math\BigInteger;
/**
* PKCS#1 Formatted DSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PKCS1 extends Progenitor
{
/**
* Break a public or private key down into its constituent components
*
* @param string|array $key
*/
public static function load($key, ?string $password = null): array
{
$key = parent::load($key, $password);
$decoded = ASN1::decodeBER($key);
if (!$decoded) {
throw new RuntimeException('Unable to decode BER');
}
$key = ASN1::asn1map($decoded[0], Maps\DSAParams::MAP);
if (is_array($key)) {
return $key;
}
$key = ASN1::asn1map($decoded[0], Maps\DSAPrivateKey::MAP);
if (is_array($key)) {
return $key;
}
$key = ASN1::asn1map($decoded[0], Maps\DSAPublicKey::MAP);
if (is_array($key)) {
return $key;
}
throw new RuntimeException('Unable to perform ASN1 mapping');
}
/**
* Convert DSA parameters to the appropriate format
*/
public static function saveParameters(BigInteger $p, BigInteger $q, BigInteger $g): string
{
$key = [
'p' => $p,
'q' => $q,
'g' => $g,
];
$key = ASN1::encodeDER($key, Maps\DSAParams::MAP);
return "-----BEGIN DSA PARAMETERS-----\r\n" .
chunk_split(Strings::base64_encode($key), 64) .
"-----END DSA PARAMETERS-----\r\n";
}
/**
* Convert a private key to the appropriate format.
*
* @param string $password optional
* @param array $options optional
*/
public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, string $password = '', array $options = []): string
{
$key = [
'version' => 0,
'p' => $p,
'q' => $q,
'g' => $g,
'y' => $y,
'x' => $x,
];
$key = ASN1::encodeDER($key, Maps\DSAPrivateKey::MAP);
return self::wrapPrivateKey($key, 'DSA', $password, $options);
}
/**
* Convert a public key to the appropriate format
*/
public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y): string
{
$key = ASN1::encodeDER($y, Maps\DSAPublicKey::MAP);
return self::wrapPublicKey($key, 'DSA');
}
}

View File

@@ -0,0 +1,133 @@
<?php
/**
* PKCS#8 Formatted DSA Key Handler
*
* PHP version 5
*
* Processes keys with the following headers:
*
* -----BEGIN ENCRYPTED PRIVATE KEY-----
* -----BEGIN PRIVATE KEY-----
* -----BEGIN PUBLIC KEY-----
*
* Analogous to ssh-keygen's pkcs8 format (as specified by -m). Although PKCS8
* is specific to private keys it's basically creating a DER-encoded wrapper
* for keys. This just extends that same concept to public keys (much like ssh-keygen)
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\DSA\Formats\Keys;
use phpseclib3\Crypt\Common\Formats\Keys\PKCS8 as Progenitor;
use phpseclib3\Exception\RuntimeException;
use phpseclib3\File\ASN1;
use phpseclib3\File\ASN1\Maps;
use phpseclib3\Math\BigInteger;
/**
* PKCS#8 Formatted DSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PKCS8 extends Progenitor
{
/**
* OID Name
*
* @var string
*/
public const OID_NAME = 'id-dsa';
/**
* OID Value
*
* @var string
*/
public const OID_VALUE = '1.2.840.10040.4.1';
/**
* Child OIDs loaded
*
* @var bool
*/
protected static $childOIDsLoaded = false;
/**
* Break a public or private key down into its constituent components
*
* @param string|array $key
*/
public static function load($key, ?string $password = null): array
{
$key = parent::load($key, $password);
$type = isset($key['privateKey']) ? 'privateKey' : 'publicKey';
$decoded = ASN1::decodeBER($key[$type . 'Algorithm']['parameters']->element);
if (!$decoded) {
throw new RuntimeException('Unable to decode BER of parameters');
}
$components = ASN1::asn1map($decoded[0], Maps\DSAParams::MAP);
if (!is_array($components)) {
throw new RuntimeException('Unable to perform ASN1 mapping on parameters');
}
$decoded = ASN1::decodeBER($key[$type]);
if (empty($decoded)) {
throw new RuntimeException('Unable to decode BER');
}
$var = $type == 'privateKey' ? 'x' : 'y';
$components[$var] = ASN1::asn1map($decoded[0], Maps\DSAPublicKey::MAP);
if (!$components[$var] instanceof BigInteger) {
throw new RuntimeException('Unable to perform ASN1 mapping');
}
if (isset($key['meta'])) {
$components['meta'] = $key['meta'];
}
return $components;
}
/**
* Convert a private key to the appropriate format.
*/
public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, ?string $password = null, array $options = []): string
{
$params = [
'p' => $p,
'q' => $q,
'g' => $g,
];
$params = ASN1::encodeDER($params, Maps\DSAParams::MAP);
$params = new ASN1\Element($params);
$key = ASN1::encodeDER($x, Maps\DSAPublicKey::MAP);
return self::wrapPrivateKey($key, [], $params, $password, null, '', $options);
}
/**
* Convert a public key to the appropriate format
*
* @param array $options optional
*/
public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, array $options = []): string
{
$params = [
'p' => $p,
'q' => $q,
'g' => $g,
];
$params = ASN1::encodeDER($params, Maps\DSAParams::MAP);
$params = new ASN1\Element($params);
$key = ASN1::encodeDER($y, Maps\DSAPublicKey::MAP);
return self::wrapPublicKey($key, $params);
}
}

View File

@@ -0,0 +1,97 @@
<?php
/**
* PuTTY Formatted DSA Key Handler
*
* puttygen does not generate DSA keys with an N of anything other than 160, however,
* it can still load them and convert them. PuTTY will load them, too, but SSH servers
* won't accept them. Since PuTTY formatted keys are primarily used with SSH this makes
* keys with N > 160 kinda useless, hence this handlers not supporting such keys.
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\DSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Crypt\Common\Formats\Keys\PuTTY as Progenitor;
use phpseclib3\Exception\InvalidArgumentException;
use phpseclib3\Math\BigInteger;
/**
* PuTTY Formatted DSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class PuTTY extends Progenitor
{
/**
* Public Handler
*
* @var string
*/
public const PUBLIC_HANDLER = 'phpseclib3\Crypt\DSA\Formats\Keys\OpenSSH';
/**
* Algorithm Identifier
*
* @var array
*/
protected static $types = ['ssh-dss'];
/**
* Break a public or private key down into its constituent components
*
* @param array|string $key
* @param string|false $password
* @return array|false
*/
public static function load($key, $password)
{
$components = parent::load($key, $password);
if (!isset($components['private'])) {
return $components;
}
extract($components);
unset($components['public'], $components['private']);
[$p, $q, $g, $y] = Strings::unpackSSH2('iiii', $public);
[$x] = Strings::unpackSSH2('i', $private);
return compact('p', 'q', 'g', 'y', 'x', 'comment');
}
/**
* Convert a private key to the appropriate format.
*/
public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, ?string $password = null, array $options = []): string
{
if ($q->getLength() != 160) {
throw new InvalidArgumentException('SSH only supports keys with an N (length of Group Order q) of 160');
}
$public = Strings::packSSH2('iiii', $p, $q, $g, $y);
$private = Strings::packSSH2('i', $x);
return self::wrapPrivateKey($public, $private, 'ssh-dss', $password, $options);
}
/**
* Convert a public key to the appropriate format
*/
public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y): string
{
if ($q->getLength() != 160) {
throw new InvalidArgumentException('SSH only supports keys with an N (length of Group Order q) of 160');
}
return self::wrapPublicKey(Strings::packSSH2('iiii', $p, $q, $g, $y), 'ssh-dss');
}
}

View File

@@ -0,0 +1,74 @@
<?php
/**
* Raw DSA Key Handler
*
* PHP version 5
*
* Reads and creates arrays as DSA keys
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\DSA\Formats\Keys;
use phpseclib3\Exception\UnexpectedValueException;
use phpseclib3\Math\BigInteger;
/**
* Raw DSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Raw
{
/**
* Break a public or private key down into its constituent components
*
* @param string|array $key
*/
public static function load($key, ?string $password = null): array
{
if (!is_array($key)) {
throw new UnexpectedValueException('Key should be a array - not a ' . gettype($key));
}
switch (true) {
case !isset($key['p']) || !isset($key['q']) || !isset($key['g']):
case !$key['p'] instanceof BigInteger:
case !$key['q'] instanceof BigInteger:
case !$key['g'] instanceof BigInteger:
case !isset($key['x']) && !isset($key['y']):
case isset($key['x']) && !$key['x'] instanceof BigInteger:
case isset($key['y']) && !$key['y'] instanceof BigInteger:
throw new UnexpectedValueException('Key appears to be malformed');
}
$options = ['p' => 1, 'q' => 1, 'g' => 1, 'x' => 1, 'y' => 1];
return array_intersect_key($key, $options);
}
/**
* Convert a private key to the appropriate format.
*
* @param string $password optional
*/
public static function savePrivateKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y, BigInteger $x, string $password = ''): string
{
return compact('p', 'q', 'g', 'y', 'x');
}
/**
* Convert a public key to the appropriate format
*/
public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y): string
{
return compact('p', 'q', 'g', 'y');
}
}

View File

@@ -0,0 +1,125 @@
<?php
/**
* XML Formatted DSA Key Handler
*
* While XKMS defines a private key format for RSA it does not do so for DSA. Quoting that standard:
*
* "[XKMS] does not specify private key parameters for the DSA signature algorithm since the algorithm only
* supports signature modes and so the application of server generated keys and key recovery is of limited
* value"
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\DSA\Formats\Keys;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Exception\BadConfigurationException;
use phpseclib3\Exception\UnexpectedValueException;
use phpseclib3\Math\BigInteger;
/**
* XML Formatted DSA Key Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class XML
{
/**
* Break a public or private key down into its constituent components
*/
public static function load(string $key, ?string $password = null): array
{
if (!Strings::is_stringable($key)) {
throw new UnexpectedValueException('Key should be a string - not a ' . gettype($key));
}
if (!class_exists('DOMDocument')) {
throw new BadConfigurationException('The dom extension is not setup correctly on this system');
}
$use_errors = libxml_use_internal_errors(true);
$dom = new \DOMDocument();
if (substr($key, 0, 5) != '<?xml') {
$key = '<xml>' . $key . '</xml>';
}
if (!$dom->loadXML($key)) {
libxml_use_internal_errors($use_errors);
throw new UnexpectedValueException('Key does not appear to contain XML');
}
$xpath = new \DOMXPath($dom);
$keys = ['p', 'q', 'g', 'y', 'j', 'seed', 'pgencounter'];
foreach ($keys as $key) {
// $dom->getElementsByTagName($key) is case-sensitive
$temp = $xpath->query("//*[translate(local-name(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='$key']");
if (!$temp->length) {
continue;
}
$value = new BigInteger(Strings::base64_decode($temp->item(0)->nodeValue), 256);
switch ($key) {
case 'p': // a prime modulus meeting the [DSS] requirements
// Parameters P, Q, and G can be public and common to a group of users. They might be known
// from application context. As such, they are optional but P and Q must either both appear
// or both be absent
$components['p'] = $value;
break;
case 'q': // an integer in the range 2**159 < Q < 2**160 which is a prime divisor of P-1
$components['q'] = $value;
break;
case 'g': // an integer with certain properties with respect to P and Q
$components['g'] = $value;
break;
case 'y': // G**X mod P (where X is part of the private key and not made public)
$components['y'] = $value;
// the remaining options do not do anything
case 'j': // (P - 1) / Q
// Parameter J is available for inclusion solely for efficiency as it is calculatable from
// P and Q
case 'seed': // a DSA prime generation seed
// Parameters seed and pgenCounter are used in the DSA prime number generation algorithm
// specified in [DSS]. As such, they are optional but must either both be present or both
// be absent
case 'pgencounter': // a DSA prime generation counter
}
}
libxml_use_internal_errors($use_errors);
if (!isset($components['y'])) {
throw new UnexpectedValueException('Key is missing y component');
}
switch (true) {
case !isset($components['p']):
case !isset($components['q']):
case !isset($components['g']):
return ['y' => $components['y']];
}
return $components;
}
/**
* Convert a public key to the appropriate format
*
* See https://www.w3.org/TR/xmldsig-core/#sec-DSAKeyValue
*/
public static function savePublicKey(BigInteger $p, BigInteger $q, BigInteger $g, BigInteger $y): string
{
return "<DSAKeyValue>\r\n" .
' <P>' . Strings::base64_encode($p->toBytes()) . "</P>\r\n" .
' <Q>' . Strings::base64_encode($q->toBytes()) . "</Q>\r\n" .
' <G>' . Strings::base64_encode($g->toBytes()) . "</G>\r\n" .
' <Y>' . Strings::base64_encode($y->toBytes()) . "</Y>\r\n" .
'</DSAKeyValue>';
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* ASN1 Signature Handler
*
* PHP version 5
*
* Handles signatures in the format described in
* https://tools.ietf.org/html/rfc3279#section-2.2.2
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\DSA\Formats\Signature;
use phpseclib3\File\ASN1 as Encoder;
use phpseclib3\File\ASN1\Maps;
use phpseclib3\Math\BigInteger;
/**
* ASN1 Signature Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class ASN1
{
/**
* Loads a signature
*
* @return array|bool
*/
public static function load(string $sig)
{
if (!is_string($sig)) {
return false;
}
$decoded = Encoder::decodeBER($sig);
if (empty($decoded)) {
return false;
}
$components = Encoder::asn1map($decoded[0], Maps\DssSigValue::MAP);
return $components;
}
/**
* Returns a signature in the appropriate format
*/
public static function save(BigInteger $r, BigInteger $s): string
{
return Encoder::encodeDER(compact('r', 's'), Maps\DssSigValue::MAP);
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* Raw DSA Signature Handler
*
* PHP version 5
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\DSA\Formats\Signature;
use phpseclib3\Crypt\Common\Formats\Signature\Raw as Progenitor;
/**
* Raw DSA Signature Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class Raw extends Progenitor
{
}

View File

@@ -0,0 +1,71 @@
<?php
/**
* SSH2 Signature Handler
*
* PHP version 5
*
* Handles signatures in the format used by SSH2
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2016 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\DSA\Formats\Signature;
use phpseclib3\Common\Functions\Strings;
use phpseclib3\Math\BigInteger;
/**
* SSH2 Signature Handler
*
* @author Jim Wigginton <terrafrost@php.net>
*/
abstract class SSH2
{
/**
* Loads a signature
*/
public static function load(string $sig)
{
if (!is_string($sig)) {
return false;
}
$result = Strings::unpackSSH2('ss', $sig);
if ($result === false) {
return false;
}
[$type, $blob] = $result;
if ($type != 'ssh-dss' || strlen($blob) != 40) {
return false;
}
return [
'r' => new BigInteger(substr($blob, 0, 20), 256),
's' => new BigInteger(substr($blob, 20), 256),
];
}
/**
* Returns a signature in the appropriate format
*
* @return string
*/
public static function save(BigInteger $r, BigInteger $s)
{
if ($r->getLength() > 160 || $s->getLength() > 160) {
return false;
}
return Strings::packSSH2(
'ss',
'ssh-dss',
str_pad($r->toBytes(), 20, "\0", STR_PAD_LEFT) .
str_pad($s->toBytes(), 20, "\0", STR_PAD_LEFT)
);
}
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* DSA Parameters
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\DSA;
use phpseclib3\Crypt\DSA;
/**
* DSA Parameters
*
* @author Jim Wigginton <terrafrost@php.net>
*/
final class Parameters extends DSA
{
/**
* Returns the parameters
*
* @param array $options optional
*/
public function toString(string $type = 'PKCS1', array $options = []): string
{
$type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');
return $type::saveParameters($this->p, $this->q, $this->g, $options);
}
}

View File

@@ -0,0 +1,150 @@
<?php
/**
* DSA Private Key
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\DSA;
use phpseclib3\Crypt\Common;
use phpseclib3\Crypt\DSA;
use phpseclib3\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature;
use phpseclib3\Math\BigInteger;
/**
* DSA Private Key
*
* @author Jim Wigginton <terrafrost@php.net>
*/
final class PrivateKey extends DSA implements Common\PrivateKey
{
use Common\Traits\PasswordProtected;
/**
* DSA secret exponent x
*
* @var BigInteger
*/
protected $x;
/**
* Returns the public key
*
* If you do "openssl rsa -in private.rsa -pubout -outform PEM" you get a PKCS8 formatted key
* that contains a publicKeyAlgorithm AlgorithmIdentifier and a publicKey BIT STRING.
* An AlgorithmIdentifier contains an OID and a parameters field. With RSA public keys this
* parameters field is NULL. With DSA PKCS8 public keys it is not - it contains the p, q and g
* variables. The publicKey BIT STRING contains, simply, the y variable. This can be verified
* by getting a DSA PKCS8 public key:
*
* "openssl dsa -in private.dsa -pubout -outform PEM"
*
* ie. just swap out rsa with dsa in the rsa command above.
*
* A PKCS1 public key corresponds to the publicKey portion of the PKCS8 key. In the case of RSA
* the publicKey portion /is/ the key. In the case of DSA it is not. You cannot verify a signature
* without the parameters and the PKCS1 DSA public key format does not include the parameters.
*
* @see self::getPrivateKey()
*/
public function getPublicKey()
{
$type = self::validatePlugin('Keys', 'PKCS8', 'savePublicKey');
if (!isset($this->y)) {
$this->y = $this->g->powMod($this->x, $this->p);
}
$key = $type::savePublicKey($this->p, $this->q, $this->g, $this->y);
return DSA::loadFormat('PKCS8', $key)
->withHash($this->hash->getHash())
->withSignatureFormat($this->shortFormat);
}
/**
* Create a signature
*
* @see self::verify()
* @param string $message
*/
public function sign($message): string
{
$format = $this->sigFormat;
if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
$signature = '';
$result = openssl_sign($message, $signature, $this->toString('PKCS8'), $this->hash->getHash());
if ($result) {
if ($this->shortFormat == 'ASN1') {
return $signature;
}
extract(ASN1Signature::load($signature));
return $format::save($r, $s);
}
}
$h = $this->hash->hash($message);
$h = $this->bits2int($h);
while (true) {
$k = BigInteger::randomRange(self::$one, $this->q->subtract(self::$one));
$r = $this->g->powMod($k, $this->p);
[, $r] = $r->divide($this->q);
if ($r->equals(self::$zero)) {
continue;
}
$kinv = $k->modInverse($this->q);
$temp = $h->add($this->x->multiply($r));
$temp = $kinv->multiply($temp);
[, $s] = $temp->divide($this->q);
if (!$s->equals(self::$zero)) {
break;
}
}
// the following is an RFC6979 compliant implementation of deterministic DSA
// it's unused because it's mainly intended for use when a good CSPRNG isn't
// available. if phpseclib's CSPRNG isn't good then even key generation is
// suspect
/*
$h1 = $this->hash->hash($message);
$k = $this->computek($h1);
$r = $this->g->powMod($k, $this->p);
list(, $r) = $r->divide($this->q);
$kinv = $k->modInverse($this->q);
$h1 = $this->bits2int($h1);
$temp = $h1->add($this->x->multiply($r));
$temp = $kinv->multiply($temp);
list(, $s) = $temp->divide($this->q);
*/
return $format::save($r, $s);
}
/**
* Returns the private key
*
* @param array $options optional
*/
public function toString(string $type, array $options = []): string
{
$type = self::validatePlugin('Keys', $type, 'savePrivateKey');
if (!isset($this->y)) {
$this->y = $this->g->powMod($this->x, $this->p);
}
return $type::savePrivateKey($this->p, $this->q, $this->g, $this->y, $this->x, $this->password, $options);
}
}

View File

@@ -0,0 +1,85 @@
<?php
/**
* DSA Public Key
*
* @author Jim Wigginton <terrafrost@php.net>
* @copyright 2015 Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @link http://phpseclib.sourceforge.net
*/
declare(strict_types=1);
namespace phpseclib3\Crypt\DSA;
use phpseclib3\Crypt\Common;
use phpseclib3\Crypt\DSA;
use phpseclib3\Crypt\DSA\Formats\Signature\ASN1 as ASN1Signature;
/**
* DSA Public Key
*
* @author Jim Wigginton <terrafrost@php.net>
*/
final class PublicKey extends DSA implements Common\PublicKey
{
use Common\Traits\Fingerprint;
/**
* Verify a signature
*
* @see self::verify()
* @param string $message
* @param string $signature
*/
public function verify($message, $signature): bool
{
$format = $this->sigFormat;
$params = $format::load($signature);
if ($params === false || count($params) != 2) {
return false;
}
extract($params);
if (self::$engines['OpenSSL'] && in_array($this->hash->getHash(), openssl_get_md_methods())) {
$sig = $format != 'ASN1' ? ASN1Signature::save($r, $s) : $signature;
$result = openssl_verify($message, $sig, $this->toString('PKCS8'), $this->hash->getHash());
if ($result != -1) {
return (bool) $result;
}
}
$q_1 = $this->q->subtract(self::$one);
if (!$r->between(self::$one, $q_1) || !$s->between(self::$one, $q_1)) {
return false;
}
$w = $s->modInverse($this->q);
$h = $this->hash->hash($message);
$h = $this->bits2int($h);
[, $u1] = $h->multiply($w)->divide($this->q);
[, $u2] = $r->multiply($w)->divide($this->q);
$v1 = $this->g->powMod($u1, $this->p);
$v2 = $this->y->powMod($u2, $this->p);
[, $v] = $v1->multiply($v2)->divide($this->p);
[, $v] = $v->divide($this->q);
return $v->equals($r);
}
/**
* Returns the public key
*
* @param array $options optional
*/
public function toString(string $type, array $options = []): string
{
$type = self::validatePlugin('Keys', $type, 'savePublicKey');
return $type::savePublicKey($this->p, $this->q, $this->g, $this->y, $options);
}
}