first commit

This commit is contained in:
annnj-company
2026-04-17 18:29:53 +08:00
parent e49fa5a215
commit 130c1026c4
5615 changed files with 1639145 additions and 0 deletions

View File

@@ -0,0 +1,125 @@
<?php
/**
* 覆盖ecc里的椭圆类添加sm2
*/
declare(strict_types=1);
namespace Mdanter\Ecc\Curves;
// use Mdanter\Ecc\Exception\UnknownCurveException;
use Mdanter\Ecc\Exception\UnsupportedCurveException;
use Mdanter\Ecc\Math\GmpMathInterface;
use Mdanter\Ecc\Math\MathAdapterFactory;
use Mdanter\Ecc\Primitives\GeneratorPoint;
use Rtgm\ecc\Sm2Curve;
class CurveFactory
{
/**
* @param string $name
* @return NamedCurveFp
*/
public static function getCurveByName(string $name): NamedCurveFp
{
$adapter = MathAdapterFactory::getAdapter();
if($name == Sm2Curve::NAME_PSM2){
return self::getSm2Factory($adapter)->curveSm2();
}
$nistFactory = self::getNistFactory($adapter);
$secpFactory = self::getSecpFactory($adapter);
switch ($name) {
case NistCurve::NAME_P192:
return $nistFactory->curve192();
case NistCurve::NAME_P224:
return $nistFactory->curve224();
case NistCurve::NAME_P256:
return $nistFactory->curve256();
case NistCurve::NAME_P384:
return $nistFactory->curve384();
case NistCurve::NAME_P521:
return $nistFactory->curve521();
case SecgCurve::NAME_SECP_112R1:
return $secpFactory->curve112r1();
case SecgCurve::NAME_SECP_192K1:
return $secpFactory->curve192k1();
case SecgCurve::NAME_SECP_256K1:
return $secpFactory->curve256k1();
case SecgCurve::NAME_SECP_256R1:
return $secpFactory->curve256r1();
case SecgCurve::NAME_SECP_384R1:
return $secpFactory->curve384r1();
default:
$error = new UnsupportedCurveException('Unknown curve.');
$error->setCurveName($name);
throw $error;
}
}
/**
* @param string $name
* @return GeneratorPoint
*/
public static function getGeneratorByName(string $name): GeneratorPoint
{
$adapter = MathAdapterFactory::getAdapter();
if($name == Sm2Curve::NAME_PSM2){
return self::getSm2Factory($adapter)->generatorSm2();
}
$nistFactory = self::getNistFactory($adapter);
$secpFactory = self::getSecpFactory($adapter);
switch ($name) {
case NistCurve::NAME_P192:
return $nistFactory->generator192();
case NistCurve::NAME_P224:
return $nistFactory->generator224();
case NistCurve::NAME_P256:
return $nistFactory->generator256();
case NistCurve::NAME_P384:
return $nistFactory->generator384();
case NistCurve::NAME_P521:
return $nistFactory->generator521();
case SecgCurve::NAME_SECP_112R1:
return $secpFactory->generator112r1();
case SecgCurve::NAME_SECP_192K1:
return $secpFactory->generator192k1();
case SecgCurve::NAME_SECP_256K1:
return $secpFactory->generator256k1();
case SecgCurve::NAME_SECP_256R1:
return $secpFactory->generator256r1();
case SecgCurve::NAME_SECP_384R1:
return $secpFactory->generator384r1();
default:
$error = new UnsupportedCurveException('Unknown generator.');
$error->setCurveName($name);
throw $error;
}
}
/**
* @param GmpMathInterface $math
* @return NistCurve
*/
private static function getNistFactory(GmpMathInterface $math): NistCurve
{
return new NistCurve($math);
}
/**
* @param GmpMathInterface $math
* @return SecgCurve
*/
private static function getSecpFactory(GmpMathInterface $math): SecgCurve
{
return new SecgCurve($math);
}
/**
* @param GmpMathInterface $math
* @return Sm2
*/
private static function getSm2Factory(GmpMathInterface $math): Sm2Curve
{
return new Sm2Curve($math);
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Rtgm\ecc;
use Mdanter\Ecc\Math\GmpMathInterface;
use Mdanter\Ecc\Math\MathAdapterFactory;
use Mdanter\Ecc\EccFactory;
// use Rtgm\sm\ecc\NistCurve;
use Rtgm\ecc\Sm2Curve;
/**
* 添加sm2的工厂采用类的继承方式这里也可以采用如 \Curves\CurveFctory的覆盖的方式
*/
class RtEccFactory extends EccFactory{
/**
* Selects and creates the most appropriate adapter for the running environment.
*
* @param bool $debug [optional] Set to true to get a trace of all mathematical operations
*
* @throws \RuntimeException
* @return GmpMathInterface
*/
public static function getAdapter(bool $debug = false): GmpMathInterface
{
$adapter = MathAdapterFactory::getAdapter($debug);
return $adapter;
}
/**
* Returns a factory to create NIST Recommended curves and generators.
*
* @param GmpMathInterface $adapter [optional] Defaults to the return value of EccFactory::getAdapter().
* @return Sm2Curve
*/
public static function getSmCurves(GmpMathInterface $adapter = null): Sm2Curve
{
$adapter = $adapter ?: self::getAdapter();
// var_dump($adapter);
return new Sm2Curve($adapter);
}
}

View File

@@ -0,0 +1,149 @@
<?php
/**
* 覆盖ecc里的oid与椭圆的对照数组添加sm2
*/
declare(strict_types=1);
namespace Mdanter\Ecc\Serializer\Util;
use FG\ASN1\Universal\ObjectIdentifier;
use Mdanter\Ecc\Curves\NamedCurveFp;
use Mdanter\Ecc\Curves\CurveFactory;
use Mdanter\Ecc\Curves\NistCurve;
use Mdanter\Ecc\Curves\SecgCurve;
use Rtgm\ecc\Sm2Curve;
use Mdanter\Ecc\Exception\UnsupportedCurveException;
use Mdanter\Ecc\Primitives\CurveFpInterface;
use Mdanter\Ecc\Primitives\GeneratorPoint;
class CurveOidMapper
{
const NIST_P192_OID = '1.2.840.10045.3.1.1';
const NIST_P224_OID = '1.3.132.0.33';
const NIST_P256_OID = '1.2.840.10045.3.1.7';
const NIST_P384_OID = '1.3.132.0.34';
const NIST_P521_OID = '1.3.132.0.35';
const SECP_112R1_OID = '1.3.132.0.6';
const SECP_192K1_OID = '1.3.132.0.31';
const SECP_256K1_OID = '1.3.132.0.10';
const SECP_256R1_OID = '1.2.840.10045.3.1.7';
const SECP_384R1_OID = '1.3.132.0.34';
const SM2_OID = '1.2.156.10197.1.301';
// const SM2_OID = '1.2.840.10045.2.1';
/**
* @var array
*/
private static $oidMap = array(
NistCurve::NAME_P192 => self::NIST_P192_OID,
NistCurve::NAME_P224 => self::NIST_P224_OID,
NistCurve::NAME_P256 => self::NIST_P256_OID,
NistCurve::NAME_P384 => self::NIST_P384_OID,
NistCurve::NAME_P521 => self::NIST_P521_OID,
SecgCurve::NAME_SECP_112R1 => self::SECP_112R1_OID,
SecgCurve::NAME_SECP_192K1 => self::SECP_192K1_OID,
SecgCurve::NAME_SECP_256K1 => self::SECP_256K1_OID,
SecgCurve::NAME_SECP_256R1 => self::SECP_256R1_OID,
SecgCurve::NAME_SECP_384R1 => self::SECP_384R1_OID,
Sm2Curve::NAME_PSM2 => self::SM2_OID,
);
/**
* @var array
*/
private static $sizeMap = array(
NistCurve::NAME_P192 => 24,
NistCurve::NAME_P224 => 28,
NistCurve::NAME_P256 => 32,
NistCurve::NAME_P384 => 48,
NistCurve::NAME_P521 => 66,
SecgCurve::NAME_SECP_112R1 => 14,
SecgCurve::NAME_SECP_192K1 => 24,
SecgCurve::NAME_SECP_256K1 => 32,
SecgCurve::NAME_SECP_256R1 => 32,
SecgCurve::NAME_SECP_384R1 => 48,
Sm2Curve::NAME_PSM2 =>32, //sm2 也是256位的32个字节的密码
);
/**
* @return array
*/
public static function getNames(): array
{
return array_keys(self::$oidMap);
}
/**
* @param CurveFpInterface $curve
* @return int
*/
public static function getByteSize(CurveFpInterface $curve): int
{
if ($curve instanceof NamedCurveFp && array_key_exists($curve->getName(), self::$sizeMap)) {
return self::$sizeMap[$curve->getName()];
}
throw new UnsupportedCurveException('Unsupported curve type');
}
/**
* @param NamedCurveFp $curve
* @return ObjectIdentifier
*/
public static function getCurveOid(NamedCurveFp $curve): ObjectIdentifier
{
if (array_key_exists($curve->getName(), self::$oidMap)) {
$oidString = self::$oidMap[$curve->getName()];
return new ObjectIdentifier($oidString);
}
throw new UnsupportedCurveException('Unsupported curve type');
}
/**
* @param ObjectIdentifier $oid
* @return NamedCurveFp
*/
public static function getCurveFromOid(ObjectIdentifier $oid): NamedCurveFp
{
$oidString = $oid->getContent();
$invertedMap = array_flip(self::$oidMap);
if (array_key_exists($oidString, $invertedMap)) {
return CurveFactory::getCurveByName($invertedMap[$oidString]);
}
$error = new UnsupportedCurveException('Invalid data: unsupported curve.');
$error->setOid($oidString);
throw $error;
}
/**
* @param ObjectIdentifier $oid
* @return GeneratorPoint
*/
public static function getGeneratorFromOid(ObjectIdentifier $oid): GeneratorPoint
{
$oidString = $oid->getContent();
$invertedMap = array_flip(self::$oidMap);
if (array_key_exists($oidString, $invertedMap)) {
return CurveFactory::getGeneratorByName($invertedMap[$oidString]);
}
$error = new UnsupportedCurveException('Invalid data: unsupported generator.');
$error->setOid($oidString);
throw $error;
}
}

View File

@@ -0,0 +1,60 @@
<?php
namespace Rtgm\ecc;
use Mdanter\Ecc\Curves\NamedCurveFp;
use Mdanter\Ecc\Math\GmpMathInterface;
use Mdanter\Ecc\Primitives\CurveParameters;
use Mdanter\Ecc\Primitives\GeneratorPoint;
use Mdanter\Ecc\Random\RandomNumberGeneratorInterface;
/**
* 国密sm2椭圆
*/
class Sm2Curve
{
const NAME_PSM2 = 'SM2';
/**
* @var GmpMathInterface
*/
private $adapter;
/**
* @param GmpMathInterface $adapter
*/
public function __construct(GmpMathInterface $adapter)
{
$this->adapter = $adapter;
// echo "I am sm ecc\n";
}
/**
* Returns an sm2国密 curve.
*
* @return NamedCurveFp
*/
public function curveSm2(): NamedCurveFp
{
$p = gmp_init('0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF', 16);
$b = gmp_init('0x28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93', 16);
$a = gmp_init('0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC', 16);
$parameters = new CurveParameters(256, $p, $a, $b);
return new NamedCurveFp(self::NAME_PSM2, $parameters, $this->adapter);
}
/**
* Returns an sm2 generator.
*
* @param RandomNumberGeneratorInterface $randomGenerator
* @return GeneratorPoint
*/
public function generatorSm2(RandomNumberGeneratorInterface $randomGenerator = null): GeneratorPoint
{
$curve = $this->curveSm2();
$order = gmp_init('0xFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123', 16);
$x = gmp_init('0x32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7', 16);
$y = gmp_init('0xBC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0', 16);
return $curve->getGenerator($x, $y, $order, $randomGenerator);
}
}

View File

@@ -0,0 +1,126 @@
<?php
declare( strict_types = 1 );
// namespace Mdanter\Ecc\Crypto\Signature;
namespace Rtgm\ecc;
use Mdanter\Ecc\Math\GmpMathInterface;
use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface;
use Mdanter\Ecc\Crypto\Key\PublicKeyInterface;
use Mdanter\Ecc\Util\BinaryString;
use Mdanter\Ecc\Crypto\Signature\SignatureInterface;
use Mdanter\Ecc\Crypto\Signature\Signature;
/**
* sm2签名算法
*/
class Sm2Signer {
/**
*
* @var GmpMathInterface
*/
private $adapter;
/**
*
* @param GmpMathInterface $adapter
*/
public function __construct( GmpMathInterface $adapter ) {
$this->adapter = $adapter;
}
/**
* @param PrivateKeyInterface $key
* @param \GMP $truncatedHash - hash truncated for use in ECDSA hash算法然后truncated by 相关的椭圆字节
* @param \GMP $randomK
* @return SignatureInterface
*/
public function sign( PrivateKeyInterface $key, \GMP $truncatedHash, \GMP $randomK ): SignatureInterface {
$math = $this->adapter;
$generator = $key->getPoint();
// var_dump($generator);die();
$n = $generator->getOrder();
$modMath = $math->getModularArithmetic( $n );
$prikey = $key->getSecret();
//第一二步是userid, msg 生成 trucatedhash ,
$count = 0;
while (true) {
$count++;
// echo "count: $count\n";
if($count >5){
throw new \RuntimeException( 'Error: sign R or S = 0' );
}
// 第三步生成随机数
$k = $math->mod( $randomK, $n );
// 第四步 计算pt1(x1,y1) = [K]G这个点
// 生成一个新的点P = kG
$p1 = $generator->mul( $k );
// var_dump($p1);die();
// 第五步 计算 r = (truncatedHash + x1) mod n
$r = $modMath->add($truncatedHash,$p1->getX());
// var_dump(gmp_strval($r,16));die();
$zero = gmp_init( 0, 10 );
if ( $math->equals( $r, $zero ) ) {
// @todo 如报错,重来
// continue; //报错重来一次
// @todo
throw new \RuntimeException( 'Error: random number R = 0' );
}
// 第六步 计算 s = ((1 + d)^-1 * (k - rd)) mod n
$one = gmp_init(1,10);
$s1 = $math->inverseMod($math->add($one, $prikey),$n );
// print_r(gmp_strval($s1,16));die();
$s2 = $math->sub($k,$math->mul($r,$prikey));
// print_r(gmp_strval($s2,16));die();
$s = $modMath->mul($s1,$s2);
// var_dump($generator->mul($s));die();
if ( $math->equals( $s, $zero ) ) {
// continue;
throw new \RuntimeException( 'Error: random number S = 0' );
}
return new Signature( $r, $s );
}
}
/**
* @param PublicKeyInterface $key
* @param SignatureInterface $signature
* @param \GMP $hash
* @return bool
*/
public function verify( PublicKeyInterface $key, SignatureInterface $signature, \GMP $hash ): bool {
$generator = $key->getGenerator();
// var_dump($generator);die();
$n = $generator->getOrder();
$r = $signature->getR();
$s = $signature->getS();
$math = $this->adapter;
$one = gmp_init( 1, 10 );
if ( $math->cmp( $r, $one ) < 0 || $math->cmp( $r, $math->sub( $n, $one ) ) > 0 ) {
return false;
}
if ( $math->cmp( $s, $one ) < 0 || $math->cmp( $s, $math->sub( $n, $one ) ) > 0 ) {
return false;
}
// 1.2.3.4 sm3 取msg,userid的 hash值,这里直接就传过来了,
$modMath = $math->getModularArithmetic( $n );
// 第五步 计算t=(r'+s')mod n
$t = $modMath->add($r,$s);
// // 第六步 计算(x1,y1) = [s]G + [t]PA
$p1 = $generator->mul($s); // p1 = sG 是OK的与签名生成的sG一样
$p2 = $key->getPoint()->mul($t);
$xy = $p1->add($p2);
// // 第七步 R=(e' + x1') 验证R==r'?
$v = $modMath->add($hash, $xy->getX());
return BinaryString::constantTimeCompare( $math->toString( $v ), $math->toString( $r ) );
}
}

View File

@@ -0,0 +1,14 @@
<?php
spl_autoload_register(function ($cls) {
$map = array(
'Mdanter\Ecc\Serializer\Util\CurveOidMapper' => __DIR__ .'/ecc/Serializer/Util/CurveOidMapper.php',
'Mdanter\Ecc\Curves\CurveFactory'=>__DIR__ .'/ecc/Curves/CurveFactory.php',
);
if (isset($map[$cls])) {
// echo $cls . ' loaded abc '. $map[$cls]. PHP_EOL;
include_once $map[$cls];
return true;
}
}, true, true);

View File

@@ -0,0 +1,396 @@
<?php
namespace Rtgm\sm;
define("C1C3C2",1);
define("C1C2C3",0);
use Rtgm\ecc\RtEccFactory;
use Rtgm\ecc\Sm2Signer;
use Mdanter\Ecc\Crypto\Key\PrivateKey;
use Mdanter\Ecc\Crypto\Key\PublicKey;
use Mdanter\Ecc\Primitives\Point;
use Mdanter\Ecc\Serializer\PrivateKey\PemPrivateKeySerializer;
use Mdanter\Ecc\Serializer\PrivateKey\DerPrivateKeySerializer;
use Mdanter\Ecc\Serializer\PublicKey\PemPublicKeySerializer;
use Mdanter\Ecc\Serializer\PublicKey\DerPublicKeySerializer;
use Mdanter\Ecc\Serializer\Signature\DerSignatureSerializer;
use Rtgm\smecc\SM2\Sm2WithSm3;
use Rtgm\smecc\SM2\Hex2ByteBuf;
class RtSm2 {
protected $adapter = null;
protected $generator = null;
protected $userId = '1234567812345678';
// 是否固定签名不随机,好处是同一段参数的签名固定,增大别人的猜测的难度,
// 同样的key + document每次签名是一样的如果为false则每次不一样
protected $useDerandomizedSignatures = true;
// 是否固定加密不随机,算法中的是否每次都用不同的中间椭圆,如果固定的话,
// 同样的文本加密后的数据是一样的,但速度会更快一些,随机的话,每次加密出来的数据不一样
protected $useDerandomizedEncrypt = false;
// 输入输出的签名方式 16进制的还是base64
protected $formatSign = 'hex';
// 可扩展自定义多种返回签名方式
protected $arrFormat = ['hex','base64'];
// 加密时的中间椭圆,取任意的sm2中间椭圆都可以useDerandomizedEncrypt = true时使用为false时每次加密重新生成一个
protected $foreignKey= [
'21fbd478026e2d668e3570e514de0d312e443d1e294c1ca785dfbfb5f74de225',
'04e27c3780e7069bda7082a23a489d77587ce309583ed99253f66e1d9833ed1a1d0b5ce86dc6714e9974cf258589139d7b1855e8c9fa2f2c1175ee123a95a23e9b'
];
protected $cipher = null;
/**
* Undocumented function
*
* @param string $formatSign
* @param boolean $randFixed 是否使用中间椭圆,使用中间椭圆的话,速度会快一些,但同样的数据的签名或加密的值就固定了
*/
function __construct($formatSign='hex', $randFixed = true) {
$this->adapter = RtEccFactory::getAdapter();
$this->generator = RtEccFactory::getSmCurves()->generatorSm2();
if(in_array($formatSign,$this->arrFormat)){
$this->formatSign = $formatSign;
} else {
$this->formatSign = 'hex';
}
if(!$randFixed){
$this->useDerandomizedSignatures = false;
$this->useDerandomizedEncrypt = false;
}
}
/**
* 随机生成一对16进制明文公私钥
*/
public function generatekey() {
// $adapter = $this->adapter;
$generator = $this->generator;
//随机生成一个私钥类
$private = $generator->createPrivateKey();
//取出私钥16进制表示出来
$privateKey = $this->decHex( $private->getSecret() );
//取出公钥的椭圆点
$pubPoint = $private->getPublicKey()->getPoint();
//公钥上的点x, y
$pubX = $this->decHex( $pubPoint->getX() );
$pubY = $this->decHex( $pubPoint->getY() );
$publicKey = '04'.$pubX.$pubY;
return [$privateKey, $publicKey];
}
/**
* 随机生成一对PEM编码公私钥
*/
public function generatePemkey() {
$adapter = $this->adapter;
$generator = $this->generator;
$private = $generator->createPrivateKey();
$derSerializer = new DerPrivateKeySerializer( $adapter );
// der包 ans1编码 1 版本号 2私钥 3 oid 4 公钥 四组数据
// $der = $derSerializer->serialize( $private );
$pemSerializer = new PemPrivateKeySerializer( $derSerializer );
$privateKeyPem = $pemSerializer->serialize( $private );
$derPubSerializer = new DerPublicKeySerializer( $adapter );
$pemPubSerializer = new PemPublicKeySerializer( $derPubSerializer );
$publicKeyPem = $pemPubSerializer->serialize( $private->getPublicKey() );
return [$privateKeyPem, $publicKeyPem];
}
/**
* SM2 公钥加密算法
*
* @param string $document
* @param string $publicKey 如提供的base64的可使用 bin2hex(base64_decode($publicKey))
* @return string
*/
public function doEncrypt($document, $publicKey, $model = C1C3C2)
{
$adapter = $this->adapter;
$generator = $this->generator;
$this->cipher = new \Rtgm\smecc\SM2\Cipher();
$arrMsg = Hex2ByteBuf::HexStringToByteArray2(bin2hex($document));
list( $pubKeyX, $pubKeyY ) = $this->_getKeyXY( $publicKey );
// $key = $this->_getPubKeyObject( $pubKeyX, $pubKeyY );
$point = new Point( $adapter, $generator->getCurve(), gmp_init( $pubKeyX, 16 ), gmp_init( $pubKeyY, 16 ) );
// 是否使用固定的中间椭圆加密,
if($this->useDerandomizedEncrypt){
$c1 = $this->cipher->initEncipher($point,$this->foreignKey);
} else {
$c1 = $this->cipher->initEncipher($point,null);
}
// print_r($c1);
$arrMsg = $this->cipher->encryptBlock($arrMsg);
$c2 = strtolower(Hex2ByteBuf::ByteArrayToHexString($arrMsg)) ;
// print_R($c2);echo "\n";
$c3 = strtolower(Hex2ByteBuf::ByteArrayToHexString($this->cipher->Dofinal()));
// print_r($c1.$c3.$c2);
if($model == C1C3C2){
return $c1.$c3.$c2;
} else {
return $c1.$c2.$c3;
}
}
/**
* SM2 私钥解密算法,
*
* @param string $document
* @param string $privateKey 如提供的base64的可使用 bin2hex(base64_decode($privateKey))
* @param bool $trim 是否做04开头的去除看业务返回
* @return string
*/
public function doDecrypt($encryptData,$privateKey,$trim = true,$model = C1C3C2)
{
// $encryptData = $c1.$c3.$c2
if(substr($encryptData,0,2)=='04' && $trim){
$encryptData = substr($encryptData,2);
}
if(strlen($privateKey)==66 && substr($privateKey,0,2)=='00') {
$privateKey = substr($privateKey,2); // 个别的key 前面带着00
}
$adapter = $this->adapter;
$generator = $this->generator;
$this->cipher = new \Rtgm\smecc\SM2\Cipher();
$c1X = substr($encryptData,0,64);
$c1Y = substr($encryptData,strlen($c1X),64);
$c1Length = strlen($c1X) + strlen($c1Y);
if($model == C1C3C2){
$c3 = substr($encryptData,$c1Length,64);
$c2 = substr($encryptData,$c1Length+strlen($c3));
} else {
$c3 = substr($encryptData,-64);
$c2 = substr($encryptData,$c1Length,strlen($encryptData)-$c1Length-64);
}
$p1 = new Point( $adapter, $generator->getCurve(), gmp_init($c1X, 16 ), gmp_init( $c1Y, 16 ) );
$this->cipher->initDecipher($p1,$privateKey);
$arrMsg = Hex2ByteBuf::HexStringToByteArray2($c2);
$arrMsg = $this->cipher->decryptBlock($arrMsg);
$document = hex2bin(Hex2ByteBuf::ByteArrayToHexString($arrMsg));
$c3_ = strtolower(Hex2ByteBuf::ByteArrayToHexString($this->cipher->Dofinal()));
$c3 = strtolower($c3);
if($c3 == $c3_){ //hash签名相同
return $document;
} else {
return '';
}
}
/**
* SM2 签名明文16进制密码, 如提供的base64的可使用 bin2hex(base64_decode($privateKey))
*
*/
public function doSign( $document, $privateKey, $userId = null ) {
if ( empty( $userId ) ) {
$userId = $this->userId;
}
$adapter = $this->adapter;
$generator = $this->generator;
$algorithm = 'sha256';
$secret = gmp_init( $privateKey, 16 );
$key = new PrivateKey( $adapter, $generator, $secret );
return $this->_dosign( $document, $key, $adapter, $generator, $userId, $algorithm );
}
/**
* SM2 签名pem密码
*
*/
public function doSignOutKey( $document, $privateKeyFile, $userId = null ) {
if ( empty( $userId ) ) {
$userId = $this->userId;
}
if ( !file_exists( $privateKeyFile ) ) {
throw new \Exception( 'privatekey file not exists' );
}
$adapter = $this->adapter;
$generator = $this->generator;
//这个sha256 只是生成随机数时用到,和主体算法无关
$algorithm = 'sha256';
$pemSerializer = new PemPrivateKeySerializer( new DerPrivateKeySerializer( $adapter ) );
$keyData = file_get_contents( $privateKeyFile );
$key = $pemSerializer->parse( $keyData );
return $this->_dosign( $document, $key, $adapter, $generator, $userId, $algorithm );
}
protected function _dosign( $document, $key, $adapter,$generator, $userId, $algorithm = 'sha256' ) {
// $publickey = $key->getPublicKey();
$obPoint = $key->getPublicKey()->getPoint();
$pubKeyX = $adapter->decHex( $obPoint->getX() );
$pubKeyY = $adapter->decHex( $obPoint->getY() );
$hash = $this->_doS3Hash( $document, $pubKeyX, $pubKeyY, $generator, $userId );
# Derandomized signatures are not necessary, but is avoids
# the risk of a low entropy RNG, causing accidental reuse
# of a k value for a different message, which leaks the
# private key.
if ( $this->useDerandomizedSignatures ) {
$random = \Mdanter\Ecc\Random\RandomGeneratorFactory::getHmacRandomGenerator( $key, $hash, $algorithm );
} else {
$random = \Mdanter\Ecc\Random\RandomGeneratorFactory::getRandomGenerator();
}
$randomK = $random->generate( $generator->getOrder() );
$signer = new Sm2Signer( $adapter );
$signature = $signer->sign( $key, $hash, $randomK );
$serializer = new DerSignatureSerializer();
$serializedSig = $serializer->serialize( $signature );
if($this->formatSign == 'hex') {
return bin2hex($serializedSig);
} else if($this->formatSign == 'base64' ) {
return base64_encode( $serializedSig );
}
//缺省 hex
return bin2hex($serializedSig);
}
/**
* Undocumented function
*
* @param string $document
* @param string $sign
* @param string $publicKey 如提供的base64的可使用 bin2hex(base64_decode($publicKey))
* @param string|null $userId
* @return bool
*/
public function verifySign( $document, $sign, $publicKey, $userId = null ) {
$adapter = $this->adapter;
$generator = $this->generator;
if ( empty( $userId ) ) {
$userId = $this->userId;
}
if($this->formatSign == 'hex') {
$sigData = hex2bin($sign);
} else if($this->formatSign == 'base64' ) {
$sigData = base64_decode( $sign );
} else {
$sigData = hex2bin($sign);
}
// Parse signature
$sigSerializer = new DerSignatureSerializer();
$sig = $sigSerializer->parse( $sigData );
// get hash
list( $pubKeyX, $pubKeyY ) = $this->_getKeyXY( $publicKey );
$hash = $this->_doS3Hash( $document, $pubKeyX, $pubKeyY, $generator, $userId );
// get pubkey parse
$key = $this->_getPubKeyObject( $pubKeyX, $pubKeyY );
$signer = new Sm2Signer( $adapter );
return $signer->verify( $key, $sig, $hash );
}
public function verifySignOutKey( $document, $sign, $publickeyFile, $userId = null ) {
if ( empty( $userId ) ) {
$userId = $this->userId;
}
if ( !file_exists( $publickeyFile ) ) {
throw new \Exception( 'publickey file not exists' );
}
$adapter = $this->adapter;
$generator = $this->generator;
if($this->formatSign == 'hex') {
$sigData = hex2bin($sign);
} else if($this->formatSign == 'base64' ) {
$sigData = base64_decode( $sign );
} else {
$sigData = hex2bin($sign);
}
// Parse signature
$sigSerializer = new DerSignatureSerializer();
$sig = $sigSerializer->parse( $sigData );
// Parse public key
$keyData = file_get_contents( $publickeyFile );
$derSerializer = new DerPublicKeySerializer( $adapter );
$pemSerializer = new PemPublicKeySerializer( $derSerializer );
$key = $pemSerializer->parse( $keyData );
$pubKeyX = $this->decHex( $key->getPoint()->getX() );
$pubKeyY = $this->decHex( $key->getPoint()->getY() );
$hash = $this->_doS3Hash( $document, $pubKeyX, $pubKeyY, $generator, $userId );
$signer = new Sm2Signer( $adapter );
return $signer->verify( $key, $sig, $hash );
}
/**
*
*/
protected function _doS3Hash( $document, $pubKeyX, $pubKeyY, $generator, $userId ) {
$hasher = new Sm2WithSm3();
$hash = $hasher->getSm3Hash( $document, $pubKeyX, $pubKeyY, $generator, $userId );
return gmp_init( Hex2ByteBuf::ByteArrayToHexString( $hash ), 16 );
}
protected function _getKeyXY( $publicKey ) {
if ( strlen( $publicKey ) == 128 ) {
$pubKeyX = substr( $publicKey, 0, 64 );
$pubKeyY = substr( $publicKey, -64 );
} else if ( strlen( $publicKey ) == 130 && substr( $publicKey, 0, 2 ) == '04' ) {
$pubKeyX = substr( $publicKey, 2, 64 );
$pubKeyY = substr( $publicKey, -64 );
} else {
throw new \Exception( 'publickey format error' );
}
return [$pubKeyX, $pubKeyY];
}
protected function _getPubKeyObject( $pubKeyX, $pubKeyY ) {
$generator = $this->generator;
// __construct( GmpMathInterface $adapter, CurveFpInterface $curve, \GMP $x, \GMP $y, \GMP $order = null, bool $infinity = false )
$x = gmp_init( $pubKeyX, 16 );
$y = gmp_init( $pubKeyY, 16 );
$point = new Point( $this->adapter, $generator->getCurve(), $x, $y );
// __construct( GmpMathInterface $adapter, GeneratorPoint $generator, PointInterface $point )
return new PublicKey( $this->adapter, $this->generator, $point );
}
protected function _str2hex($str){
$res = array();
for($i=0; $i<strlen($str);$i++){
$res[$i] = sprintf("%02x",ord($str[$i]));
}
return implode("",$res);
}
public function decHex($dec,$len=64): string
{
if(gettype($dec)=='string'){
$dec = gmp_init($dec, 10);
}
if (gmp_cmp($dec, 0) < 0) {
throw new \InvalidArgumentException('Unable to convert negative integer to string');
}
$hex = gmp_strval($dec, 16);
/* if (strlen($hex) % 2 != 0) {
$hex = '0'.$hex;
} */
$left = $len - strlen($hex) ;
if($left >0){
$hex = str_repeat('0',$left) . $hex;
}
return $hex;
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Rtgm\sm;
use Rtgm\smecc\SM3\SM3Digest;
class RtSm3 {
public function digest($msg,$format=1){
$md = array();
$sm3 = new SM3Digest();
$msgArray = unpack("C*",$msg);
$sm3->BlockUpdate($msgArray, 1, sizeof($msgArray));
$sm3->DoFinal($md, 0);
if($format){
return $this->_dec2hex($md);
} else {
return $md;
}
}
protected function _dec2hex($md){
$res = array();
for($i=0; $i<count($md);$i++){
$res[$i] = sprintf("%02x",$md[$i]);
}
return implode("",$res);
}
}

View File

@@ -0,0 +1,113 @@
<?php
namespace Rtgm\sm;
/**
* Sm4 from https://github.com/lizhichao/sm
*/
use Exception;
use Rtgm\smecc\SM4\Sm4;
class RtSm4
{
protected $sm4;
protected $keyLen = 16;
protected $ivLen = 16;
function __construct($key)
{
$this->sm4 = new Sm4($key);
}
public function encrypt($data, $type = 'sm4', $iv = '', $formatOut = 'hex')
{
if ($type != 'sm4-ecb') {
$this->check_iv($iv);
}
$ret = '';
switch ($type) {
case 'sm4':
case 'sm4-cbc':
$data = $this->mystr_pad($data, $this->keyLen); //需要补齐
$ret = $this->sm4->enDataCbc($data, $iv);
break;
case 'sm4-ecb':
$data = $this->mystr_pad($data, $this->keyLen); //需要补齐
$ret = $this->sm4->enDataEcb($data);
break;
case 'sm4-ctr':
$ret = $this->sm4->enDataCtr($data, $iv);
break;
case 'sm4-ofb':
$ret = $this->sm4->enDataOfb($data, $iv);
break;
case 'sm4-cfb':
$ret = $this->sm4->enDataCfb($data, $iv);
break;
default:
throw new Exception('bad type');
}
if ($formatOut == 'hex') {
return bin2hex($ret);
} else if ($formatOut == 'base64') {
return base64_encode($ret);
}
return $ret;
}
public function decrypt($data, $type = 'sm4', $iv = '', $formatInput = 'hex')
{
if ($type != 'sm4-ecb') {
$this->check_iv($iv);
}
if ($formatInput == 'hex') {
$data = hex2bin($data);
} else if ($formatInput == 'base64') {
$data = base64_decode($data);
}
//else is raw
switch ($type) {
case 'sm4':
case 'sm4-cbc':
$ret = $this->sm4->deDataCbc($data, $iv);
$ret = $this->mystr_unpad($ret);
break;
case 'sm4-ecb':
$ret = $this->sm4->deDataEcb($data);
$ret = $this->mystr_unpad($ret);
break;
case 'sm4-ctr':
$ret = $this->sm4->deDataCtr($data, $iv);
break;
case 'sm4-ofb':
$ret = $this->sm4->deDataOfb($data, $iv);
break;
case 'sm4-cfb':
$ret = $this->sm4->deDataCfb($data, $iv);
break;
default:
throw new Exception('bad type');
}
return $ret;
}
//加密前补齐
protected function mystr_pad($data, $len = 16)
{
$n = $len - strlen($data) % $len;
return $data . str_repeat(chr($n), $n);
}
// 解密后去掉补齐
protected function mystr_unpad($data)
{
$n = ord(substr($data, -1));
return substr($data, 0, -$n);
}
protected function check_iv($iv)
{
if (strlen($iv) != $this->ivLen) {
throw new Exception('bad iv');
}
}
}

View File

@@ -0,0 +1,116 @@
<?php
//
namespace Rtgm\smecc\SM2;
use Rtgm\smecc\SM3\SM3Digest;
use Rtgm\sm\RtSm2;
class Cipher
{
private $ct = 1;
private $p2;
/**
* @var SM3Digest
*/
private $sm3keybase;
/**
* @var SM3Digest
*/
private $sm3c3;
private $key = array();
private $keyOff = 0;
private function Reset() //注意,加密使用无符号的数组转换,以便与硬件相一致
{
$this->sm3keybase = new SM3Digest();
$this->sm3c3 = new SM3Digest();
$p = array();
$gmp_x = $this->p2->GetX();
$x = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_x);
$this->sm3keybase->BlockUpdate($x, 0, sizeof($x));
$this->sm3c3->BlockUpdate($x, 0, sizeof($x));
$gmp_y = $this->p2->GetY();
$y = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_y);
$this->sm3keybase->BlockUpdate($y, 0, sizeof($y));
$this->ct = 1;
$this->NextKey();
}
public function initEncipher($userPoint, $foreignKey = null)
{
if (empty($foreignKey)) {
$sm2 = new RtSm2();
$foreignKey = $sm2->generatekey();
}
$foreignPriKey = $foreignKey[0];
$foreignPubKey = $foreignKey[1];
$this->p2 = $userPoint->mul(gmp_init($foreignPriKey, 16));
$this->reset();
return substr($foreignPubKey, -128);
}
public function initDecipher($userPoint, $privateKey)
{
$this->p2 = $userPoint->mul(gmp_init($privateKey, 16));
$this->reset();
}
private function NextKey()
{
$sm3keycur = new SM3Digest();
$sm3keycur->setSM3Digest($this->sm3keybase);
$sm3keycur->Update(($this->ct >> 24 & 0x00ff));
$sm3keycur->Update(($this->ct >> 16 & 0x00ff));
$sm3keycur->Update(($this->ct >> 8 & 0x00ff));
$sm3keycur->Update(($this->ct & 0x00ff));
$sm3keycur->DoFinal($this->key, 0);
$this->keyOff = 0;
$this->ct++;
}
public function encryptBlock($data)
{
$len = count($data);
$this->sm3c3->BlockUpdate($data, 0, $len);
// print_r($data);die();
for ($i = 0; $i < $len; $i++) {
if ($this->keyOff == sizeof($this->key)) {
$this->NextKey();
}
$data[$i] ^= $this->key[$this->keyOff++];
}
return $data;
}
public function decryptBlock($data)
{
$len = count($data);
for ($i = 0; $i < $len; $i++) {
if ($this->keyOff == sizeof($this->key))
$this->NextKey();
$data[$i] ^= $this->key[$this->keyOff++];
}
$this->sm3c3->BlockUpdate($data, 0, $len);
return $data;
}
public function Dofinal()
{
$c3 = array();
$gmp_p = $this->p2->GetY();
$p = Hex2ByteBuf::ConvertGmp2ByteArray($gmp_p);
$this->sm3c3->BlockUpdate($p, 0, sizeof($p));
$this->sm3c3->DoFinal($c3, 0);
$this->Reset();
return $c3;
}
}

View File

@@ -0,0 +1,107 @@
<?php
//
namespace Rtgm\smecc\SM2;
class Hex2ByteBuf
{
public static function ConvertGmp2ByteArray($GmpData)
{
$HexStr = gmp_strval($GmpData, 16);
$OutBuf = array();
$OutBuf = Hex2ByteBuf::HexStringToByteArray($HexStr);
return $OutBuf;
}
public static function ByteArrayToHexString($b, $nlen = null)
{
$outstring = array();
$nlen = $nlen == null ? count($b) : 0;
// var_dump($nlen);
for ($n = 0; $n < $nlen; $n++) {
$outstring[] = Hex2ByteBuf::myhex($b[$n]);
}
// echo (implode("x",$outstring));
return implode("", $outstring);
}
private static function myhex($indata)
{
$temp_1 = $indata / 16;
if ($temp_1 < 10)
$temp_1 = $temp_1 + 0x30;
else
$temp_1 = $temp_1 + 0x41 - 10;
$temp_2 = $indata % 16;
if ($temp_2 < 10)
$temp_2 = $temp_2 + 0x30;
else
$temp_2 = $temp_2 + 0x41 - 10;
return chr($temp_1) . chr($temp_2);
}
public static function HexStringToByteArray($InString)
{
for (
$m = strlen($InString);
$m < 64;
$m++
) {
$InString = '0' . $InString;
}
$g_len = 0;
$nlen = 0;
$n = 0;
$i = 0;
$temp = '';
$nlen = strlen($InString);
if ($nlen < 16) $g_len = 16;
$g_len = $nlen / 2;
for (
$n = 0;
$n < $nlen;
$n = $n + 2
) {
$temp = substr($InString, $n, 2);
$temp = '0x' . $temp;
$b[$i] = hexdec($temp);
$i = $i + 1;
}
return $b;
}
public static function HexStringToByteArray2($InString)
{
// for (
// $m = strlen($InString);
// $m < 64;
// $m++
// ) {
// $InString = '0' . $InString;
// }
$g_len = 0;
$nlen = 0;
$n = 0;
$i = 0;
$temp = '';
$nlen = strlen($InString);
if ($nlen < 16) $g_len = 16;
$g_len = $nlen / 2;
for (
$n = 0;
$n < $nlen;
$n = $n + 2
) {
$temp = substr($InString, $n, 2);
$temp = '0x' . $temp;
$b[$i] = hexdec($temp);
$i = $i + 1;
}
return $b;
}
}

View File

@@ -0,0 +1,100 @@
<?php
namespace Rtgm\smecc\SM2;
use Rtgm\smecc\SM3\SM3Digest;
class SM2Enc
{
private const SM2_ADDBYTE = 97;//加密后的数据会增加的长度
private const MAX_ENCLEN = 128; //最大的加密长度分组
private const MAX_DECLEN = (self::MAX_ENCLEN + self::SM2_ADDBYTE); //最大的解密长度分组
/* public function SM2_EncStringBySoft($InString, $PubKeyX, $PubKeyY, $generator)
{
$Kx = gmp_init($PubKeyX, 16);
$Ky = gmp_init($PubKeyY, 16);
$userKey = $generator->getPublicKeyFrom($Kx,$Ky,null);
$n = 0;
$incount = 0;
$outcount = 0;
$temp_InBuf = array();
$temp_OutBuf = array();
$inlen = strlen($InString) + 1;
$outlen = ($inlen / $this::MAX_ENCLEN + 1) * $this::SM2_ADDBYTE + $inlen;
$OutBuf = array();
$InBuf = array();
$InBuf=unpack("C*",$InString);
$InBuf[$inlen]=0;//这样是为了保挂与其它开发语言一致
$ret = 0;
$temp_inlen = 0;
while ( $inlen > 0)
{
if ( $inlen > $this::MAX_ENCLEN)
$temp_inlen = $this::MAX_ENCLEN;
else
$temp_inlen = $inlen;
for ( $n = 0; $n < $temp_inlen; $n++)
{
$temp_InBuf[$n] = $InBuf[$incount + $n + 1];//注意这里要加1因为UNPACK后是从1开始
}
$temp_OutBuf=$this->sub_EncBufBySoft($temp_InBuf, $temp_inlen, $userKey);
for ( $n = 0; $n < ($temp_inlen + $this::SM2_ADDBYTE); $n++)
{
$OutBuf[ $outcount + $n] = $temp_OutBuf[$n];
}
$inlen = $inlen - $this::MAX_ENCLEN;
$incount = $incount + $this::MAX_ENCLEN;
$outcount = $outcount + $this::MAX_DECLEN;
}
return Hex2ByteBuf::ByteArrayToHexString( $OutBuf, sizeof($OutBuf));
}
private function sub_EncBufBySoft($InBuf, $InBuflen, $userKey)
{
$n = 0 ;
$data = array();
$data = $InBuf;
$cipher = new Cipher();
$c1 = $cipher->Init_enc( $userKey);
$bc1[0]=4;
$gmp_x = $c1->getPoint()->GetX();
$x=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_x);
SM3Digest::arraycopy($x,0,$bc1,1,sizeof($x));
$gmp_y = $c1->getPoint()->GetY();
$y=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_y);
SM3Digest::arraycopy($y,0,$bc1,1+32,sizeof($y));
$c1_len = sizeof($bc1);
$data=$cipher->Encrypt( $data ,$InBuflen);
$c3 = array();
$c3=$cipher->Dofinal( );
$OutBuf=array();
for ( $n = 0; $n < $c1_len; $n++)
{
$OutBuf[$n] = $bc1[ $n];
}
for ( $n = 0; $n < $InBuflen; $n++)
{
$OutBuf[ $n + $c1_len] = $data[ $n];
}
for ( $n = 0; $n < 32; $n++)
{
$OutBuf[ $n + $c1_len + $InBuflen] = $c3[ $n];
}
return $OutBuf;
} */
}

View File

@@ -0,0 +1,172 @@
<?php
//
namespace Rtgm\smecc\SM2;
use Rtgm\smecc\SM3\SM3Digest;
class Sm2WithSm3
{
private function GetZ($userId, $HexPubKeyX,$HexPubKeyY,$generator)
{
//$PubKeyX_gmp = gmp_init($HexPubKeyX, 16);
// $PubKeyY_gmp = gmp_init($HexPubKeyY, 16);
$md = array();
$sm3 = new SM3Digest();
// userId length
$id=unpack("C*",$userId);
$len = sizeof($id)*8;
$sm3->Update( ($len >> 8 & 0x00ff));
$sm3->Update( ($len & 0x00ff));
// userId
$sm3->BlockUpdate($id, 1, sizeof($id));
// a,b
$gmp_a = $generator->getCurve()->GetA();
$a=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_a);
$sm3->BlockUpdate($a, 0, sizeof($a));
$gmp_b = $generator->getCurve()->GetB();
$b=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_b);
$sm3->BlockUpdate($b, 0, sizeof($b));
// gx,gy
$gmp_gx = $generator->GetX();
$gx=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_gx);
$sm3->BlockUpdate($gx, 0, sizeof($gx));
$gmp_gy = $generator->GetY();
$gy=Hex2ByteBuf::ConvertGmp2ByteArray($gmp_gy);
$sm3->BlockUpdate($gy, 0, sizeof($gy));
// x,y
$bPubKeyX=array();
$bPubKeyX=Hex2ByteBuf::HexStringToByteArray($HexPubKeyX);
$sm3->BlockUpdate($bPubKeyX, 0, sizeof($bPubKeyX));
$bPubKeyY=array();
$bPubKeyY=Hex2ByteBuf::HexStringToByteArray($HexPubKeyY);
$sm3->BlockUpdate($bPubKeyY, 0, sizeof($bPubKeyY));
$sm3->DoFinal($md, 0);
return $md;
}
private function GetE($z, $HashMsgValue)
{
$md = array();
$sm3 = new SM3Digest();
$sm3->BlockUpdate($z, 0, sizeof($z));
// byte[] p = Encoding.Default.GetBytes(msg);
// sm3.BlockUpdate(p, 0, p.Length);
$sm3->BlockUpdate($HashMsgValue, 0, 32);
$sm3->DoFinal($md, 0);
return $md;
}
public function GetMsgHash( $msg)
{
$md = array();
$sm3 = new SM3Digest();
$msgArray=unpack("C*",$msg);
$sm3->BlockUpdate($msgArray, 1, sizeof($msgArray));
$sm3->DoFinal($md, 0);
return $md;
}
private function Sm2Verify($md, $PubKeyX, $PubKeyY, $VerfiySign,$generator)
{
//SM2Result sm2Ret = new SM2Result();
$InSignBuf = array();
$InSignBuf=Hex2ByteBuf::HexStringToByteArray($VerfiySign);
$Kx = gmp_init($PubKeyX, 16);
$Ky = gmp_init($PubKeyY, 16);
$PubKey = $generator->getPublicKeyFrom($Kx,$Ky,null);
$r = gmp_init(substr($VerfiySign,0, 64), 16);
$s = gmp_init(substr($VerfiySign,64, 64), 16);
$ecc_point_g=$generator->getCurve()->getPoint($generator->GetX(), $generator->GetY());
$Sm2Ret=$this->sub_Sm2Verify($md, $PubKey , $r, $s,$ecc_point_g,$generator);
if (gmp_cmp($r,$Sm2Ret)==0)
{
return true;
}
else
return false;
}
private function sub_Sm2Verify($md, $userKey,$r, $s,$ecc_point_g,$generator)
{
$generator = $userKey->getGenerator();
$ecc_n = $generator->getOrder();
// e_
$md_gmp=Hex2ByteBuf::ByteArrayToHexString($md,sizeof($md));
$e =gmp_init($md_gmp,16);
// t
$t=gmp_add($r,$s);
$t=gmp_mod($t,$ecc_n);
$zero = gmp_init(0, 10);
if (gmp_cmp($t,$zero)==0)
return null;
// x1y1
$x1y1 = $ecc_point_g->mul($s);
$x1y1 = $x1y1->add($userKey->getPoint()->mul($t));
// R
return gmp_mod(gmp_add($e,$x1y1->GetX()),$ecc_n);
}
public function YtVerfiyBySoft($id, $msg, $PubKeyX, $PubKeyY, $VerfiySign,$generator)
{
$Z = array();
$E = array();
$MsgHashValue = array();
$Z = $this->GetZ($id, $PubKeyX, $PubKeyY,$generator);
$MsgHashValue = $this->GetMsgHash($msg);
$E = $this->GetE($Z, $MsgHashValue);
$IsVailSign = $this->Sm2Verify($E, $PubKeyX, $PubKeyY, $VerfiySign,$generator);
return $IsVailSign;
}
public function getSm3Hash($msg,$PubKeyX, $PubKeyY, $generator, $userId="1234567812345678") {
// sm3(z+msg)
$z = $this->GetZ($userId, $PubKeyX, $PubKeyY,$generator);
$md = array();
$sm3 = new SM3Digest();
$sm3->BlockUpdate($z, 0, sizeof($z));
$msgArray=unpack("C*",$msg);
$sm3->BlockUpdate($msgArray, 1, sizeof($msgArray));
$sm3->DoFinal($md, 0);
return $md;
}
}

View File

@@ -0,0 +1,112 @@
<?php
//
namespace Rtgm\smecc\SM3;
class GeneralDigest
{
private const BYTE_LENGTH = 64;
private $xBuf=array();
private $xBufOff;
private $byteCount;
public function setGeneralDigest($t)
{
$this->arraycopy($t->xBuf, 0, $this->xBuf, 0, sizeof($t->xBuf));
$this->xBufOff = $t->xBufOff;
$this->byteCount = $t->byteCount;
}
public function __construct( )
{
$this->xBuf[0]=0;
$this->xBuf[1]=0;
$this->xBuf[2]=0; $this->xBuf[3]=0;
}
public function Update($input)
{
$this->xBuf[$this->xBufOff++] = $input;
if ($this->xBufOff == sizeof($this->xBuf))
{
$this->ProcessWord($this->xBuf, 0);
$this->xBufOff = 0;
}
$this->byteCount++;
}
public function BlockUpdate(
$input,
$inOff,
$length)
{
//
// fill the current word
//
while (($this->xBufOff != 0) && ($length > 0))
{
$this->Update($input[$inOff]);
$inOff++;
$length--;
}
//
// process whole words.
//
while ($length > sizeof($this->xBuf))
{
$this->ProcessWord($input, $inOff);
$inOff += sizeof($this->xBuf);
$length -= sizeof($this->xBuf);
$this->byteCount += sizeof($this->xBuf);
}
//
// load in the remainder.
//
while ($length > 0)
{
$this->Update($input[$inOff]);
$inOff++;
$length--;
}
}
public function Finish()
{
// $bitLength = ($this->byteCount << 3);
$bitLength = $this->LeftRotateLong($this->byteCount , 3);
//
// add the pad bytes.
//
$this->Update(128);
while ($this->xBufOff != 0) $this->Update(0);
$this->ProcessLength($bitLength);
$this->ProcessBlock();
}
public function Reset()
{
$this->byteCount = 0;
$this->xBufOff = 0;
$this->xBuf[0]=0;
$this->xBuf[1]=0;
$this->xBuf[2]=0; $this->xBuf[3]=0;
}
public function GetByteLength():int
{
return $this::BYTE_LENGTH;
}
}

View File

@@ -0,0 +1,348 @@
<?php
//declare(strict_types=1);
namespace Rtgm\smecc\SM3;
use Rtgm\smecc\SM3\GeneralDigest;
class SM3Digest extends GeneralDigest
{
public static $AlgorithmName="SM3";
private const DIGEST_LENGTH = 32;
public function GetDigestSize():int
{
return $this::DIGEST_LENGTH;
}
private static $v0 = array(0x7380166f, 0x4914b2b9, 0x172442d7, -628488704, -1452330820, 0x163138aa,-477237683, -1325724082);
private $v = array(0, 0, 0, 0, 0, 0, 0, 0);
private $v_ = array(0, 0, 0, 0, 0, 0, 0, 0);
private static $X0 = array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
private $X = array();
private $XOff;
private $T_00_15 = 0x79cc4519;
private $T_16_63 = 0x7a879d8a;
public function __construct()
{
parent::__construct();
// parent::$gSM3Digest=$this;
$this->Reset();
}
public function setSM3Digest($t)
{
parent::setGeneralDigest($t);
$this->arraycopy($t->X, 0, $this->X, 0, sizeof($t->X));
$this->xOff = $t->xOff;
$this->arraycopy($t->v, 0, $this->v, 0, sizeof($t->v));
}
public function Reset()
{
parent::Reset();
$this->arraycopy(SM3Digest::$v0, 0, $this->v, 0, sizeof(SM3Digest::$v0));
$this->xOff = 0;
$this->arraycopy(SM3Digest::$X0, 0, $this->X, 0, sizeof(SM3Digest::$X0));
}
public static function arraycopy($InBuf,$InBufPos,&$OutBuf,$OutBufPos,$Len)
{
for( $n = 0 ;$n< $Len;$n++)
{
$OutBuf[$n + $OutBufPos] =$InBuf[$n+$InBufPos];
}
}
public function ProcessBlock()
{
$ww = $this->X;
$ww_ = array();
for ($i = 16; $i < 68; $i++)
{
$ww[$i] = $this->P1( $ww[ $i - 16] ^ $ww[$i - 9] ^ ($this->ROTATE($ww[$i - 3], 15))) ^ ($this->ROTATE($ww[$i - 13], 7)) ^ $ww[$i - 6];
}
for ($i = 0; $i < 64; $i++)
{
$ww_[$i] = $ww[$i] ^ $ww[$i + 4];
}
$vv = $this->v;
$vv_ = $this->v_;
$this->arraycopy($vv, 0, $vv_, 0, sizeof($vv));
// int SS1, SS2, TT1, TT2, aaa;
for ($i = 0; $i < 16; $i++)
{
$aaa = $this->ROTATE($vv_[0], 12);
$SS1 = $this->MyAdd($aaa , $vv_[4]);
$SS1 = $this->MyAdd($SS1, $this->ROTATE($this->T_00_15, $i));
$SS1 = $this->ROTATE($SS1, 7);
$SS2 = $SS1 ^ $aaa;
$TT1=$this->FF_00_15($vv_[0], $vv_[1], $vv_[2]);
$TT1=$this->MyAdd($TT1, $vv_[3]);
$TT1=$this->MyAdd($TT1, $SS2);
$TT1=$this->MyAdd($TT1, $ww_[$i]);
$TT2=$this->GG_00_15($vv_[4], $vv_[5], $vv_[6]);
$TT2=$this->MyAdd($TT2, $vv_[7]);
$TT2=$this->MyAdd($TT2, $SS1);
$TT2=$this->MyAdd($TT2, $ww[$i]);
$vv_[3] = $vv_[2];
$vv_[2] = $this->ROTATE($vv_[1], 9);
$vv_[1] = $vv_[0];
$vv_[0] = $TT1;
$vv_[7] = $vv_[6];
$vv_[6] = $this->ROTATE($vv_[5], 19);
$vv_[5] = $vv_[4];
$vv_[4] = $this->P0($TT2);
}
for ($i = 16; $i < 64; $i++)
{
$aaa = $this->ROTATE($vv_[0], 12);
$SS1 = $this->MyAdd($aaa , $vv_[4]);
$z= $this->ROTATE($this->T_16_63, $i);
$SS1 = $this->MyAdd( $SS1, $this->ROTATE($this->T_16_63, $i));
$SS1 = $this->ROTATE($SS1, 7);
$SS2 = $SS1 ^ $aaa;
$TT1 = $this->MyAdd($this->FF_16_63($vv_[0], $vv_[1], $vv_[2]) , $vv_[3]);
$TT1 = $this->MyAdd( $TT1, $SS2);
$TT1 = $this->MyAdd( $TT1, $ww_[$i]);
$TT2 = $this->MyAdd($this->GG_16_63($vv_[4], $vv_[5], $vv_[6]) , $vv_[7]);
$TT2 = $this->MyAdd( $TT2, $SS1);
$TT2 = $this->MyAdd($TT2 , $ww[$i]);
$vv_[3] = $vv_[2];
$vv_[2] = $this->ROTATE($vv_[1], 9);
$vv_[1] = $vv_[0];
$vv_[0] = $TT1;
$vv_[7] = $vv_[6];
$vv_[6] = $this->ROTATE($vv_[5], 19);
$vv_[5] = $vv_[4];
$vv_[4] = $this->P0($TT2);
}
for ($i = 0; $i < 8; $i++)
{
$vv[$i] ^= $vv_[$i];
}
$this->v=$vv;
$this->v_=$vv_;
// Reset
$this->xOff = 0;
$this->arraycopy(SM3Digest::$X0, 0, $this->X, 0, sizeof(SM3Digest::$X0));
}
public function ProcessWord($in_Renamed, $inOff)
{
$n = $this->LeftRotateLong($in_Renamed[$inOff] , 24);
$n |= $this->LeftRotateLong(($in_Renamed[++$inOff] & 0xff) , 16);
$n |= $this->LeftRotateLong(($in_Renamed[++$inOff] & 0xff) , 8);
$n |= ($in_Renamed[++$inOff] & 0xff);
$this->X[$this->xOff] = $n;
if (++$this->xOff == 16)
{
$this->ProcessBlock();
}
}
public function ProcessLength($bitLength)
{
if ($this->xOff > 14)
{
$this->ProcessBlock();
}
$this->X[14] = ($this->RightRotateLong($bitLength, 32));
$this->X[15] = ($bitLength & 0xffffffff);
}
public function IntToBigEndian($n, &$bs, $off)
{
$bs[$off] = ($this->RightRotateLong($n, 24)) & 0xff;
$bs[++$off] = ($this->RightRotateLong($n, 16)) & 0xff;
$bs[++$off] = ($this->RightRotateLong($n, 8)) & 0xff;
$bs[++$off] = ($n) & 0xff ;
}
public function DoFinal(&$out_Renamed, $outOff):int
{
$this->Finish();
for ($i = 0; $i < 8; $i++)
{
$this->IntToBigEndian($this->v[$i], $out_Renamed, $outOff + $i * 4);
}
$this->Reset();
return $this::DIGEST_LENGTH;
}
private function HandleSign($lValue)
{
$lValue = $lValue & 0xFFFFFFFF;
if($lValue>=0x80000000)
{
$lValue=$lValue-(0xffffffff+1);
}
return $lValue;
}
private function MyAdd($A,$B)
{
$lValue=$this->HandleSign($A) + $this->HandleSign($B);
return $this->HandleSign($lValue);
}
public function LeftRotateLong($lValue, $lBits )
{
$lBits = $lBits % 32;
$lValue=$lValue<< $lBits;
$lValue=$lValue & 0xffffffff;
if($lValue>=0x80000000)
{
$lValue=$lValue-(0xffffffff+1);
}
return $lValue;
/* $lngSign=0; $intI=0;
$mValue=0;
$lBits = $lBits % 32;
$mValue = $lValue;
if($lBits == 0) return $mValue;
For ($intI = 1 ;$intI<= $lBits;$intI++)
{
$lngSign = $mValue & 0x40000000;
$mValue = ($mValue & 0x3FFFFFFF) * 2;
if($lngSign & 0x40000000)
$mValue = $mValue | 0x80000000;
}
return $mValue;*/
}
private function RightRotateLong($lValue , $lBits)
{
$lngSign=0;$intI=0;
$mValue =0;
$mValue = $lValue;
// $lBits = $lBits % 32;
if( $lBits == 0 )
{
return $mValue ;
}
if ($lValue >= 0)
{
if($lBits<0) $lBits= 32+ $lBits;
$r = $lValue >> $lBits;
}
else
{
$t=~$lBits;
if($t<0) $t= 32+ $t;
// $t = $t % 32;
$r= ($lValue >> $lBits) + (2 << $t);
}
return $r;
/* For ($intI = 1 ;$intI<= $lBits;$intI++)
{
$lngSign = $mValue & 0x80000000;
$mValue = ($mValue & 0x7FFFFFFF) / 2;
if ($lngSign)
$mValue = $mValue | 0x40000000;
}
return $mValue;*/
}
private function FFj($X, $Y, $Z, $j) :int
{
if($j>=0 && $j<=15) {
return $this->FF_00_15($X, $Y, $Z);
} else {
return $this->FF_16_63($X, $Y, $Z);
}
}
private function GGj($X, $Y, $Z, $j) :int
{
if($j>=0 && $j<=15) {
return $this->GG_00_15($X, $Y, $Z);
} else {
return $this->GG_16_63($X, $Y, $Z);
}
}
private function ROTATE($X, $n):int
{
// $r=($this->RightRotateLong($X, (32 - $n)));
//$r1=$this->LeftRotateLong($X , $n);
// $r2=($X << $n);
return $this->LeftRotateLong($X , $n) | ($this->RightRotateLong($X, (32 - $n)));
}
private function P0($X):int
{
$a=$this->ROTATE(($X), 9);
$b= $this->ROTATE(($X), 17);
return (($X) ^ $this->ROTATE(($X), 9) ^ $this->ROTATE(($X), 17));
}
private function P1($X):int
{
return (($X) ^ $this->ROTATE(($X), 15) ^ $this->ROTATE(($X), 23));
}
private static function FF_00_15($X, $Y, $Z):int
{
return ($X ^ $Y ^ $Z);
}
private static function FF_16_63($X, $Y, $Z):int
{
return (($X & $Y) | ($X & $Z) | ($Y & $Z));
}
private static function GG_00_15($X, $Y, $Z):int
{
return ($X ^ $Y ^ $Z);
}
private static function GG_16_63($X, $Y, $Z):int
{
return (($X & $Y) | (~ $X & $Z));
}
}

View File

@@ -0,0 +1,349 @@
<?php
/**
* source: https://github.com/lizhichao/sm
*/
namespace Rtgm\smecc\SM4;
class Sm4
{
private $ck = [
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
];
private $Sbox = [
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48
];
private $fk = [0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC];
private $rk = [];
private $b = '';
private $len = 16;
/**
* Sm4 constructor.
* @param string $key 秘钥长度16位
* @param string $b 不是16的倍数 需要的补码
* @throws \Exception
*/
public function __construct($key, $b = ' ')
{
$this->ck16($key);
$this->crk($key);
}
private function dd(&$data)
{
$n = strlen($data) % $this->len;
$data = $data . str_repeat($this->b, $n);
}
private function ck16($str)
{
if (strlen($str) !== $this->len) {
throw new \Exception('秘钥长度为16位');
}
}
private function add($v)
{
$arr = unpack('N*', $v);
$max = 0xffffffff;
$j = 1;
for ($i = 4; $i > 0; $i--) {
if ($arr[$i] > $max - $j) {
$j = 1;
$arr[$i] = 0;
} else {
$arr[$i] += $j;
break;
}
}
return pack('N*', ...$arr);
}
/**
* @param string $str 加密字符串
* @param string $iv 初始化字符串16位
* @return string
* @throws \Exception
*/
public function deDataCtr($str, $iv)
{
return $this->enDataCtr($str, $iv);
}
/**
* @param string $str 加密字符串
* @param string $iv 初始化字符串16位
* @return string
* @throws \Exception
*/
public function enDataCtr($str, $iv)
{
$this->ck16($iv);
$r = '';
$this->dd($str);
$l = strlen($str) / $this->len;
for ($i = 0; $i < $l; $i++) {
$s = substr($str, $i * $this->len, $this->len);
$tr = [];
$this->encode(array_values(unpack('N*', $iv)), $tr);
$s1 = pack('N*', ...$tr);
$s1 = $s1 ^ $s;
$iv = $this->add($iv);
$r .= $s1;
}
return $r;
}
/**
* @param string $str 加密字符串
* @param string $iv 初始化字符串16位
* @return string
* @throws \Exception
*/
public function enDataOfb($str, $iv)
{
$this->ck16($iv);
$r = '';
$this->dd($str);
$l = strlen($str) / $this->len;
for ($i = 0; $i < $l; $i++) {
$s = substr($str, $i * $this->len, $this->len);
$tr = [];
$this->encode(array_values(unpack('N*', $iv)), $tr);
$iv = pack('N*', ...$tr);
$s1 = $s ^ $iv;
$r .= $s1;
}
return $r;
}
/**
* @param string $str 加密字符串
* @param string $iv 初始化字符串16位
* @return string
* @throws \Exception
*/
public function deDataOfb($str, $iv)
{
return $this->enDataOfb($str, $iv);
}
/**
* @param string $str 加密字符串
* @param string $iv 初始化字符串16位
* @return string
* @throws \Exception
*/
public function deDataCfb($str, $iv)
{
$this->ck16($iv);
$r = '';
$this->dd($str);
$l = strlen($str) / $this->len;
for ($i = 0; $i < $l; $i++) {
$s = substr($str, $i * $this->len, $this->len);
$tr = [];
$this->encode(array_values(unpack('N*', $iv)), $tr);
$s1 = pack('N*', ...$tr);
$s1 = $s ^ $s1;
$iv = $s;
$r .= $s1;
}
return $r;
}
/**
* @param string $str 加密字符串
* @param string $iv 初始化字符串16位
* @return string
* @throws \Exception
*/
public function enDataCfb($str, $iv)
{
$this->ck16($iv);
$r = '';
$this->dd($str);
$l = strlen($str) / $this->len;
for ($i = 0; $i < $l; $i++) {
$s = substr($str, $i * $this->len, $this->len);
$tr = [];
$this->encode(array_values(unpack('N*', $iv)), $tr);
$s1 = pack('N*', ...$tr);
$iv = $s ^ $s1;
$r .= $iv;
}
return $r;
}
/**
* @param string $str 加密字符串
* @param string $iv 初始化字符串16位
* @return string
* @throws \Exception
*/
public function enDataCbc($str, $iv)
{
$this->ck16($iv);
$r = '';
$this->dd($str);
$l = strlen($str) / $this->len;
for ($i = 0; $i < $l; $i++) {
$s = substr($str, $i * $this->len, $this->len);
$s = $iv ^ $s;
$tr = [];
$this->encode(array_values(unpack('N*', $s)), $tr);
$iv = pack('N*', ...$tr);
$r .= $iv;
}
return $r;
}
/**
* @param string $str 加密字符串
* @param string $iv 初始化字符串16位
* @return string
* @throws \Exception
*/
public function deDataCbc($str, $iv)
{
$this->ck16($iv);
$r = '';
$this->dd($str);
$l = strlen($str) / $this->len;
for ($i = 0; $i < $l; $i++) {
$s = substr($str, $i * $this->len, $this->len);
$tr = [];
$this->decode(array_values(unpack('N*', $s)), $tr);
$s1 = pack('N*', ...$tr);
$s1 = $iv ^ $s1;
$iv = $s;
$r .= $s1;
}
return $r;
}
/**
* @param string $str 加密字符串
* @return string
*/
public function enDataEcb($str)
{
$r = [];
$this->dd($str);
$ar = unpack('N*', $str);
do {
$this->encode([current($ar), next($ar), next($ar), next($ar)], $r);
} while (next($ar));
return pack('N*', ...$r);
}
/**
* @param string $str 解密字符串
* @return string
*/
public function deDataEcb($str)
{
$r = [];
$this->dd($str);
$ar = unpack('N*', $str);
do {
$this->decode([current($ar), next($ar), next($ar), next($ar)], $r);
} while (next($ar));
return pack('N*', ...$r);
}
private function encode($ar, &$r)
{
for ($i = 0; $i < 32; $i++) {
$ar[$i + 4] = $this->f($ar[$i], $ar[$i + 1], $ar[$i + 2], $ar[$i + 3], $this->rk[$i]);
}
$r[] = $ar[35];
$r[] = $ar[34];
$r[] = $ar[33];
$r[] = $ar[32];
}
private function decode($ar, &$r)
{
for ($i = 0; $i < 32; $i++) {
$ar[$i + 4] = $this->f($ar[$i], $ar[$i + 1], $ar[$i + 2], $ar[$i + 3], $this->rk[31 - $i]);
}
$r[] = $ar[35];
$r[] = $ar[34];
$r[] = $ar[33];
$r[] = $ar[32];
}
private function crk($key)
{
$keys = array_values(unpack('N*', $key));
$keys = [
$keys[0] ^ $this->fk[0],
$keys[1] ^ $this->fk[1],
$keys[2] ^ $this->fk[2],
$keys[3] ^ $this->fk[3]
];
for ($i = 0; $i < 32; $i++) {
$this->rk[] = $keys[] = $keys[$i] ^ $this->t1($keys[$i + 1] ^ $keys[$i + 2] ^ $keys[$i + 3] ^ $this->ck[$i]);
}
}
private function lm($a, $n)
{
return ($a >> (32 - $n) | (($a << $n) & 0xffffffff));
}
private function f($x0, $x1, $x2, $x3, $r)
{
return $x0 ^ $this->t($x1 ^ $x2 ^ $x3 ^ $r);
}
private function s($n)
{
return $this->Sbox[($n & 0xff)] | $this->Sbox[(($n >> 8) & 0xff)] << 8 | $this->Sbox[(($n >> 16) & 0xff)] << 16 | $this->Sbox[(($n >> 24) & 0xff)] << 24;
}
private function t($n)
{
$b = $this->s($n);
return $b ^ $this->lm($b, 2) ^ $this->lm($b, 10) ^ $this->lm($b, 18) ^ $this->lm($b, 24);
}
private function t1($n)
{
$b = $this->s($n);
return $b ^ $this->lm($b, 13) ^ $this->lm($b, 23);
}
}

View File

@@ -0,0 +1,2 @@
# sm
国密sm3,sm4 ECB加密源码

View File

@@ -0,0 +1,196 @@
<?php
namespace icequeen\sm\smecc\SM4;
use ErrorException;
use Rtgm\smecc\SM4\handler\ExtendedCompression;
use Rtgm\smecc\SM4\libs\WordConversion;
use Rtgm\smecc\SM4\types\BitString;
/**
* 入口类
* Class Sm3
*
* @package SM3
* @error_code 90xxx
*/
class SM3 implements \ArrayAccess
{
/** @var string 初始值常数 */
const IV = '7380166f4914b2b9172442d7da8a0600a96f30bc163138aae38dee4db0fb0e4e';
/** @var string 消息(加密前的结果) */
private $message = '';
/** @var string 杂凑值(加密后的结果) */
private $hash_value = '';
/**
* 实例化时直接调用将参数传给主方法
* Sm3 constructor.
*
* @param $message string|mixed 传入的消息
*
* @throws \ErrorException
*/
public function __construct($message)
{
// 输入验证
if (is_int($message)) {
$message = (string)$message;
}
if (empty($message)) {
$message = '';
}
if (!is_string($message)) {
throw new ErrorException('参数类型必须为string请检查后重新输入', 90001);
}
/** @var string message 消息 */
$this->message = $message;
/** @var string hash_value 杂凑值 */
$this->hash_value = $this->sm3();
}
/**
* 主方法
*
* @return string
* @throws \ErrorException
*/
private function sm3()
{
/** @var string $m 转化后的消息(二进制码) */
// $json = json_encode($this->message);
$m = new BitString($this->message, false);
//var_dump($m);die(); //11010010 10111011
// 一、填充
$l = strlen($m);
// 满足l + 1 + k ≡ 448mod512 的最小的非负整数
$k = $l % 512;
$k = $k + 64 >= 512
? 512 - ($k % 448) - 1
: 512 - 64 - $k - 1;
$bin_l = new BitString($l);
// 填充后的消息
$m_fill = new BitString(
$m # 原始消息m
. '1' # 拼个1
. str_pad('', $k, '0') # 拼上k个比特的0
. (
strlen($bin_l) >= 64
? substr($bin_l, 0, 64)
: str_pad($bin_l, 64, '0', STR_PAD_LEFT)
) # 64比特l的二进制表示
);
// 二、迭代压缩
// 迭代过程
$B = str_split($m_fill, 512);
/** @var int $n m'可分为的组数 */
$n = ($l + $k + 65) / 512;
if (count($B) !== $n) {
throw new ErrorException();
}
$V = array(
WordConversion::hex2bin(self::IV),
);
$extended = new ExtendedCompression();
foreach ($B as $key => $Bi) {
// print_r($Bi."\n"."====== $key ========\n".strlen($Bi)."\n");
$V[$key + 1] = $extended->CF($V[$key], $Bi)->getBitString();
}
krsort($V);
reset($V);
$binary = current($V);
$rt = WordConversion::bin2hex($binary);
return $rt;
}
/**
* 方便直接输出实例化的对象
*
* @return string
*/
public function __toString()
{
return $this->hash_value;
}
/**
* Whether a offset exists
*
* @link https://php.net/manual/en/arrayaccess.offsetexists.php
*
* @param mixed $offset <p>
* An offset to check for.
* </p>
*
* @return bool true on success or false on failure.
* </p>
* <p>
* The return value will be casted to boolean if non-boolean was returned.
* @since 5.0.0
*/
public function offsetExists($offset)
{
return isset($this->hash_value[$offset]);
}
/**
* Offset to retrieve
*
* @link https://php.net/manual/en/arrayaccess.offsetget.php
*
* @param mixed $offset <p>
* The offset to retrieve.
* </p>
*
* @return mixed Can return all value types.
* @since 5.0.0
*/
public function offsetGet($offset)
{
return $this->hash_value[$offset];
}
/**
* Offset to set
*
* @link https://php.net/manual/en/arrayaccess.offsetset.php
*
* @param mixed $offset <p>
* The offset to assign the value to.
* </p>
* @param mixed $value <p>
* The value to set.
* </p>
*
* @return \SM3\SM3
* @since 5.0.0
*/
public function offsetSet($offset, $value)
{
$this->hash_value[$offset] = $value;
return $this;
}
/**
* Offset to unset
*
* @link https://php.net/manual/en/arrayaccess.offsetunset.php
*
* @param mixed $offset <p>
* The offset to unset.
* </p>
*
* @return void
* @since 5.0.0
*/
public function offsetUnset($offset)
{
unset($this->hash_value[$offset]);
}
}

View File

@@ -0,0 +1,196 @@
<?php
namespace Rtgm\smecc\SM4;
class Sm4
{
static $SM4_CK = array(
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
);
static $SM4_SBOX = array(
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48
);
static $SM4_FK = array(0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC);
private $_rk = array();
private $_block_size = 16;
public function __construct()
{
if (PHP_INT_SIZE < 8) {
throw new \Exception('64bit OS supported only');
}
if (version_compare(PHP_VERSION, "7.0", "<")) {
throw new \Exception('php version 7.0 or greater');
}
}
public function encrypt($key, $data)
{
$this->_rk = array();
$this->sM4KeySchedule($key);
$bytes = $this->pad($data);
$chunks = array_chunk($bytes, $this->_block_size);
$ciphertext = "";
foreach ($chunks as $chunk) {
$ciphertext .= $this->sM4Encrypt($chunk);
}
return bin2hex($ciphertext);
return base64_encode($ciphertext);
}
public function decrypt($key, $data)
{
$this->_rk = array();
// $data = base64_decode($data);
$data = hex2bin($data);
if (strlen($data) < 0 || strlen($data) % $this->_block_size != 0) {
return false;
}
$this->sM4KeySchedule($key);
$bytes = unpack("C*", $data);
$chunks = array_chunk($bytes, $this->_block_size);
$plaintext = "";
foreach ($chunks as $chunk) {
$plaintext .= substr($this->sM4Decrypt($chunk), 0, 16);
}
$plaintext = $this->un_pad($plaintext);
return $plaintext;
}
private function sM4Decrypt($cipherText)
{
$x = array();
for ($j = 0; $j < 4; $j++) {
$x[$j] = ($cipherText[$j * 4] << 24) | ($cipherText[$j * 4 + 1] << 16) | ($cipherText[$j * 4 + 2] << 8)
| ($cipherText[$j * 4 + 3]);
}
for ($i = 0; $i < 32; $i++) {
$tmp = $x[$i + 1] ^ $x[$i + 2] ^ $x[$i + 3] ^ $this->_rk[31 - $i];
$buf = (self::$SM4_SBOX[($tmp >> 24) & 0xFF]) << 24 | (self::$SM4_SBOX[($tmp >> 16) & 0xFF]) << 16
| (self::$SM4_SBOX[($tmp >> 8) & 0xFF]) << 8 | (self::$SM4_SBOX[$tmp & 0xFF]);
$x[$i + 4] = $x[$i] ^ ($buf ^ $this->sm4Rotl32(($buf), 2) ^ $this->sm4Rotl32(($buf), 10)
^ $this->sm4Rotl32(($buf), 18) ^ $this->sm4Rotl32(($buf), 24));
}
$plainText = array();
for ($k = 0; $k < 4; $k++) {
$plainText[4 * $k] = ($x[35 - $k] >> 24) & 0xFF;
$plainText[4 * $k + 1] = ($x[35 - $k] >> 16) & 0xFF;
$plainText[4 * $k + 2] = ($x[35 - $k] >> 8) & 0xFF;
$plainText[4 * $k + 3] = ($x[35 - $k]) & 0xFF;
}
return $this->bytesToString($plainText);
}
private function sM4Encrypt($plainText)
{
$x = array();
for ($j = 0; $j < 4; $j++) {
$x[$j] = ($plainText[$j * 4] << 24) | ($plainText[$j * 4 + 1] << 16) | ($plainText[$j * 4 + 2] << 8)
| ($plainText[$j * 4 + 3]);
}
for ($i = 0; $i < 32; $i++) {
$tmp = $x[$i + 1] ^ $x[$i + 2] ^ $x[$i + 3] ^ $this->_rk[$i];
$buf = (self::$SM4_SBOX[($tmp >> 24) & 0xFF]) << 24 | (self::$SM4_SBOX[($tmp >> 16) & 0xFF]) << 16
| (self::$SM4_SBOX[($tmp >> 8) & 0xFF]) << 8 | (self::$SM4_SBOX[$tmp & 0xFF]);
$x[$i + 4] = $x[$i] ^ ($buf ^ $this->sm4Rotl32(($buf), 2) ^ $this->sm4Rotl32(($buf), 10)
^ $this->sm4Rotl32(($buf), 18) ^ $this->sm4Rotl32(($buf), 24));
}
$cipherText = array();
for ($k = 0; $k < 4; $k++) {
$cipherText[4 * $k] = ($x[35 - $k] >> 24) & 0xFF;
$cipherText[4 * $k + 1] = ($x[35 - $k] >> 16) & 0xFF;
$cipherText[4 * $k + 2] = ($x[35 - $k] >> 8) & 0xFF;
$cipherText[4 * $k + 3] = ($x[35 - $k]) & 0xFF;
}
return $this->bytesToString($cipherText);
}
private function stringToBytes($string)
{
return unpack('C*', $string);
}
private function bytesToString($bytes)
{
return vsprintf(str_repeat('%c', count($bytes)), $bytes);
}
private function pad($data)
{
$bytes = $this->stringToBytes($data);
$rem = $this->_block_size - count($bytes) % $this->_block_size;
for ($i = 0; $i < $rem; $i++) {
array_push($bytes, $rem);
}
return $bytes;
}
private function un_pad($data)
{
$bytes = $this->stringToBytes($data);
$rem = $bytes[count($bytes)];
$bytes = array_slice($bytes, 0, count($bytes) - $rem);
return $this->bytesToString($bytes);
}
private function sm4Rotl32($buf, $n)
{
return (($buf << $n) & 0xffffffff) | ($buf >> (32 - $n));
}
private function sM4KeySchedule($key)
{
$this->_rk = array();
$key = array_values(unpack("C*", $key));
$k = array();
// for ($i = 0; $i < 4; $i++) {
// $k[$i] = self::$SM4_FK[$i]
// ^ ((($key[4 * $i] ?? 0) << 24)
// | (($key[4 * $i + 1] ?? 0) << 16)
// | (($key[4 * $i + 2] ?? 0) << 8)
// | ($key[4 * $i + 3] ?? null));
// }
for ($i = 0; $i < 4; $i++) {
$k[$i] = self::$SM4_FK[$i]
^ (((isset($key[4 * $i]) ? $key[4 * $i] : 0) << 24)
| ((isset($key[4 * $i + 1]) ? $key[4 * $i + 1] : 0) << 16)
| ((isset($key[4 * $i + 2]) ? $key[4 * $i + 2] : 0) << 8)
| (isset($key[4 * $i + 3]) ? $key[4 * $i + 3] : null));
}
for ($j = 0; $j < 32; $j++) {
$tmp = $k[$j + 1] ^ $k[$j + 2] ^ $k[$j + 3] ^ self::$SM4_CK[$j];
$buf = (self::$SM4_SBOX[($tmp >> 24) & 0xFF]) << 24 | (self::$SM4_SBOX[($tmp >> 16) & 0xFF]) << 16
| (self::$SM4_SBOX[($tmp >> 8) & 0xFF]) << 8 | (self::$SM4_SBOX[$tmp & 0xFF]);
$k[$j + 4] = $k[$j] ^ (($buf) ^ ($this->sm4Rotl32(($buf), 13)) ^ ($this->sm4Rotl32(($buf), 23)));
$this->_rk[$j] = $k[$j + 4];
}
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace Rtgm\smecc\SM4\handler;
use Rtgm\smecc\SM4\libs\WordConversion;
/**
* BigJHandler @ SM3-PHP
*
* Code BY ch4o5
* 10月. 14日 2019年
* Powered by PhpStorm
*/
/**
* 小j处理类
* Class BigJHandler
*
* @package SM3\handler
*/
class BigJHandler extends JHandler
{
/** @var int j的最大可用值 */
const SMALLEST_J = 16;
/** @var int j的最小可用值 */
const BIGGEST_J = 63;
/** @var string T常量 */
const T = '7a879d8a';
/**
* 补充父类
* SmallJHandler constructor.
*/
public function __construct()
{
parent::__construct(self::T, self::SMALLEST_J, self::BIGGEST_J);
}
/**
* 布尔函数
*
* @param $X string 长度32的比特串
* @param $Y string
* @param $Z string
*
* @return mixed
*/
public function FF($X, $Y, $Z)
{
$X_and_Y = WordConversion::andConversion(array($X, $Y));
$X_and_Z = WordConversion::andConversion(array($X, $Z));
$Y_and_Z = WordConversion::andConversion(array($Y, $Z));
return WordConversion::orConversion(
array(
$X_and_Y,
$X_and_Z,
$Y_and_Z
)
);
}
/**
* 布尔函数
*
* @param $X
* @param $Y
* @param $Z
*
* @return mixed
*/
public function GG($X, $Y, $Z)
{
$X_and_Y = WordConversion::andConversion(array($X, $Y));
$not_X = WordConversion::notConversion($X);
$not_X_and_Z = WordConversion::andConversion(array($not_X, $Z));
return WordConversion::orConversion(array($X_and_Y, $not_X_and_Z));
}
}

View File

@@ -0,0 +1,207 @@
<?php
namespace Rtgm\smecc\SM4\handler;
/**
* ExtendedCompression @ SM3-PHP
*
* Code BY ch4o5
* 10月. 15日 2019年
* Powered by PhpStorm
*/
use Rtgm\smecc\SM4\libs\WordConversion;
use Rtgm\smecc\SM4\types\Word;
/**
* 扩展压缩算法
* Class ExtendedCompression
*
* @package SM3\handler
*/
class ExtendedCompression
{
/** @var array $W */
private $W;
/** @var array $W ' */
private $W_s;
/**
* 压缩函数
*
* @param $Vi
* @param $Bi
*
* @return Word
* @throws \ErrorException
*/
public function CF($Vi, $Bi)
{
// 消息扩展
$this->extended($Bi);
/** @var array $registers 八个寄存器的名字 */
$registers = array(
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H'
);
// 将 Vi 的值依次放入八个寄存器中
// 下列注释用于防止IDE报错
/** @var Word $A */
/** @var Word $B */
/** @var Word $C */
/** @var Word $D */
/** @var Word $E */
/** @var Word $F */
/** @var Word $G */
/** @var Word $H */
foreach ($registers as $i => $register) {
$$register = new Word(substr($Vi, $i * 32, 32));
}
$small_j_handler = new SmallJHandler();
$big_j_handler = new BigJHandler();
for ($j = 0; $j < 64; $j++) {
$j_handler = ($j >= SmallJHandler::SMALLEST_J && $j < BigJHandler::SMALLEST_J)
? $small_j_handler
: $big_j_handler;
$SS1 = WordConversion::shiftLeftConversion(
WordConversion::addConversion(
array(
WordConversion::shiftLeftConversion($A, 12),
$E,
WordConversion::shiftLeftConversion($j_handler->getT(), $j)
)
),
7
);
$SS2 = WordConversion::xorConversion(
array(
$SS1,
WordConversion::shiftLeftConversion($A, 12)
)
);
$TT1 = WordConversion::addConversion(
array(
$j_handler->FF($A, $B, $C),
$D,
$SS2,
$this->W_s[$j]
)
);
$TT2 = WordConversion::addConversion(
array(
$j_handler->GG($E, $F, $G),
$H,
$SS1,
$this->W[$j]
)
);
$D = $C;
$C = WordConversion::shiftLeftConversion($B, 9);
$B = $A;
$A = $TT1;
$H = $G;
$G = WordConversion::shiftLeftConversion($F, 19);
$F = $E;
$TT2_object = new Substitution($TT2);
$E = $TT2_object->P0();
}
return WordConversion::xorConversion(
array(
join(
'',
array(
(new Word($A)),
(new Word($B)),
(new Word($C)),
(new Word($D)),
(new Word($E)),
(new Word($F)),
(new Word($G)),
(new Word($H))
)
),
$Vi
)
);
}
/**
* 消息扩展
*
* 将消息分组B(i)按以下方法扩展生成132个字W0, W1, · · · , W67, W0, W1, · · · , W63
* 用于压缩函数CF
*
* @param $Bi 消息分组中的第i个最大512位 (\SM3\types\BitString)
*
* @return void
* @throws \ErrorException
*/
public function extended($Bi)
{
// 将消息分组B(i)划分为16个字W0, W1, · · · , W15。
$this->W = $this->W_s = array();
$word_per_times = (int)ceil(strlen($Bi) / 16);
for ($i = 0; $i < 16; $i++) {
$this->W[$i] = new Word(
substr($Bi, $i * $word_per_times, $word_per_times)
);
}
// 计算W
for ($j = 16; $j <= 67; $j++) {
$param_1 = (new Substitution(
WordConversion::xorConversion(
array(
$this->W[$j - 16],
$this->W[$j - 9],
WordConversion::shiftLeftConversion($this->W[$j - 3], 15)
)
)
));
$this->W[$j] = WordConversion::xorConversion(
array(
$param_1->P1(),
WordConversion::shiftLeftConversion($this->W[$j - 13], 7),
$this->W[$j - 6]
)
);
}
unset($j);
// 计算W'
for ($j = 0; $j <= 63; $j++) {
$this->W_s[$j] = WordConversion::xorConversion(
array(
$this->W[$j],
$this->W[$j + 4]
)
);
}
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace Rtgm\smecc\SM4\handler;
use Rtgm\smecc\SM4\libs\WordConversion;
use Rtgm\smecc\SM4\types\Word;
/**
* JHandler @ SM3-PHP
*
* Code BY ch4o5
* 10月. 14th 2019
* Powered by PhpStorm
*/
/**
* j处理抽象类
* Class JHandler
*
* @package SM3\instantiation
*/
abstract class JHandler
{
/** @var string 常量T */
protected $T = '';
/** @var array j的长度区间 */
protected $section_j = array();
/**
* JHandler constructor.
*
* @param $T string 常量T
* @param $smallest int j的最小可用值
* @param $biggest int j的最大可用值
*/
public function __construct($T, $smallest, $biggest)
{
$this->setT($T);
$this->setSectionJ($smallest, $biggest);
}
/**
* 配置 继承本抽象类的子类可以处理的j的大小
*
* @param $smallest int j的最小长度
* @param $biggest int j的最大长度
*/
public function setSectionJ($smallest, $biggest)
{
$this->section_j = array($smallest, $biggest);
}
/**
* 布尔函数
*
* @param $X string 长度32的比特串
* @param $Y string
* @param $Z string
*
* @return mixed
*/
abstract public function FF($X, $Y, $Z);
/**
* 布尔函数
*
* @param $X
* @param $Y
* @param $Z
*
* @return mixed
*/
abstract public function GG($X, $Y, $Z);
/**
* 读取常量T
* @return Word
* @throws \ErrorException
*/
public function getT()
{
return new Word($this->T);
}
/**
* 配置常量T
*
* @param string $T
*/
public function setT($T)
{
$this->T = WordConversion::hex2bin($T);
}
}

View File

@@ -0,0 +1,12 @@
# *handle/* 目录
本目录包含了加密过程中的核心算法
* `\SM3\handler\JHandler`
根据J的不同进行不同处理的抽象类
* `\SM3\handler\SmalJHandler`
变量j处于较小值时的处理类
* `\SM3\handler\BigJHandler`
变量j处于较大值时的处理类
* `\SM3\handler\Substitution`
置换函数类

View File

@@ -0,0 +1,79 @@
<?php
namespace Rtgm\smecc\SM4\handler;
use Rtgm\smecc\SM4\libs\WordConversion;
/**
* 小j处理类
* Class SmallJHandler
*
* @package SM3\handler
*/
class SmallJHandler extends JHandler
{
/** @var int j的最大可用值 */
const SMALLEST_J = 0;
/** @var int j的最小可用值 */
const BIGGEST_J = 15;
/** @var string T常量 */
const T = '79cc4519';
/**
* 补充父类
* SmallJHandler constructor.
*/
public function __construct()
{
parent::__construct(self::T, self::SMALLEST_J, self::BIGGEST_J);
}
/**
* 布尔函数
*
* @param $X \SM3\types\Word 长度32的比特串
* @param $Y \SM3\types\Word 长度32的比特串
* @param $Z \SM3\types\Word 长度32的比特串
*
* @return \SM3\types\Word
*/
public function FF($X, $Y, $Z)
{
return self::boolFunction($X, $Y, $Z);
}
/**
* 小j值的布尔函数公共方法
*
* @param $X \SM3\types\Word 长度32的比特串
* @param $Y \SM3\types\Word 长度32的比特串
* @param $Z \SM3\types\Word 长度32的比特串
*
* @return \SM3\types\Word
*/
private static function boolFunction($X, $Y, $Z)
{
return WordConversion::xorConversion(
array(
$X,
$Y,
$Z
)
);
}
/**
* 布尔函数
*
* @param $X \SM3\types\Word 长度32的比特串
* @param $Y \SM3\types\Word 长度32的比特串
* @param $Z \SM3\types\Word 长度32的比特串
*
* @return \SM3\types\Word
*/
public function GG($X, $Y, $Z)
{
return self::boolFunction($X, $Y, $Z);
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace Rtgm\smecc\SM4\handler;
use Rtgm\smecc\SM4\libs\WordConversion;
use Rtgm\smecc\SM4\types\Word;
/**
* Substitution @ SM3-PHP
*
* Code BY ch4o5
* 10月. 14日 2019年
* Powered by PhpStorm
*/
/**
* 置换函数
* Class Substitution
*
* @package SM3\handler
*/
class Substitution
{
/** @var \SM3\types\Word 待置换的字 */
private $X;
/** @var array P0函数中两次左移的位数 */
private $P0_shiftLeft_times = array(9, 17);
/** @var array P1函数中两次左移的位数 */
private $P1_shiftLeft_times = array(15, 23);
/**
* Substitution constructor.
*
* @param $X
*/
public function __construct($X)
{
$this->X = $X;
}
/**
* 压缩函数中的置换函数
*
* @return \SM3\types\Word 置换结果
*/
public function P0()
{
return $this->substitutionFunction(0);
}
/**
* 置换函数的公共函数
* @param $type
* @return Word
* @throws \ErrorException
*/
private function substitutionFunction($type)
{
if (!in_array($type, array(0, '0', 1, '1'))) {
return new Word('');
}
$times_name = $type == 1
? $this->P1_shiftLeft_times
: $this->P0_shiftLeft_times;
$X_shiftLeft_1 = WordConversion::shiftLeftConversion($this->X, $times_name[0]);
$X_shiftLeft_2 = WordConversion::shiftLeftConversion($this->X, $times_name[1]);
return WordConversion::xorConversion(
array(
$this->X,
$X_shiftLeft_1,
$X_shiftLeft_2
)
);
}
/**
* 消息扩展中的置换函数
* @return Word
* @throws \ErrorException
*/
public function P1()
{
return $this->substitutionFunction(1);
}
}

View File

@@ -0,0 +1,8 @@
# *libs/* 目录
本目录包含了 *动态读写配置**一些底层的基础操作(如:按位运算)*
* `\SM3\libs\Config`
配置类,获取和动态设定配置
* `\SM3\libs\WordConversion`
字的位运算类,包含了位的各种运算

View File

@@ -0,0 +1,271 @@
<?php
namespace Rtgm\smecc\SM4\libs;
/**
* Conversion @ SM3-PHP
*
* Code BY ch4o5
* 10月. 14th 2019
* Powered by PhpStorm
*/
use Rtgm\smecc\SM4\types\Word;
/**
* 字的位运算类
* Class Conversion
*
* @package SM3\libs
*/
class WordConversion
{
/**
* 字的异或运算
*
* @param $params array 需要进行异或运算的字列表
*
* @return Word
* @api $value \SM3\types\Word
*
*/
public static function xorConversion($params)
{
return self::conversion($params, 3);
}
public static function conversion_func($prevent, $current, $type)
{
if (is_null($prevent)) {
return $current;
}
$prevent = strval($prevent);
$current = strval($current);
if (strlen($current) >= strlen($prevent)) {
$longest = strlen($current);
$longest_value = $current;
$shortest = strlen($prevent);
} else {
$longest = strlen($prevent);
$longest_value = $prevent;
$shortest = strlen($current);
}
if ($prevent === '0' || $current === '0') {
switch ($type) {
// and
case 1:
return 0;
// or
case 2:
// xor
case 3:
// add
case 5:
return $prevent == '0' ? $current : $prevent;
default:
break;
}
}
$value = array();
/**
* 加运算时需要,用来储存需要进几
*
* @var int 向前一位进的数字
*/
$carry = 0;
/**
* 大端
*
* 这里从大端跑完之后,结果数组的序号是从大到小排列的
* 还需要根据键名排序一次
*
* 个人感觉区分不区分大端并没有什么意义
* 如果换成字符串拼接的话更好用
* 但是方便你理解,还是按照大端+数组的方式进行的排列
*/
for ($i = $longest - 1; $i >= 0; $i--) {
$prevent_number = $prevent[$i];
switch ($type) {
// 与
case 1:
$value[$i] = ($i >= $shortest)
? $longest_value[$i]
: ($prevent_number & $current[$i]);
break;
// 或
case 2:
$value[$i] = ($i >= $shortest)
? ~$longest_value[$i]
: ($prevent_number | $current[$i]);
break;
// 异或
case 3:
$value[$i] = $i > $shortest
? 1
: (intval($prevent_number) ^ intval($current[$i]));
break;
// 非(按位取反)
case 4:
$value[$i] = $prevent_number === '1'
? '0'
: '1';
break;
// 加
case 5:
$add = $prevent_number + $current[$i] + $carry;
$value[$i] = $add % 2;
$carry = ($add - $value[$i]) / 2;
break;
// 特殊情况
default:
break;
}
}
ksort($value);
return new Word(join('', $value));
}
/**
* @param $params array 需要进行异或运算的字列表
* @param $type int 位运算类型
* @return Word|null 运算结果
*/
private static function conversion($params, $type)
{
$prevent = null;
foreach ($params as $param){
$prevent = self::conversion_func($prevent,$param,$type);
}
return $prevent;
}
/**
* 字的与运算
*
* @param $params array 需要进行与运算的字列表
*
* @return Word
*/
public static function andConversion($params)
{
return self::conversion($params, 1);
}
/**
* 字的或运算
*
* @param $params
*
* @return Word
*/
public static function orConversion($params)
{
return self::conversion($params, 2);
}
/**
* 字的非运算
*
* @param $word
*
* @return Word
*/
public static function notConversion($word)
{
return self::conversion(array($word, null), 4);
}
/**
* @param $word Word 待运算的字
* @param $times int 左移的位数
* @return Word
* @throws \ErrorException
*/
public static function shiftLeftConversion($word, $times)
{
return new Word(
substr(
$word,
($times % strlen($word))
)
. substr(
$word,
0,
($times % strlen($word))
)
);
}
/**
* 将16进制数串转换为二进制数据
* 使用字符串形式实现解决了PHP本身进制转换的时候受限于浮点数大小的问题
*
* @param int|string $hex
*
* @return string
*/
public static function hex2bin($hex)
{
// 格式化为字符串
$hex = strval($hex);
/** 十六进制转二进制每1位一组 */
defined('HEX_TO_BIN_NUM') || define('HEX_TO_BIN_NUM', 1);
/** @var array $hex_array 把指定的十六进制数按位切片为数组 */
$hex_array = str_split($hex, HEX_TO_BIN_NUM);
// 最终的二进制数字(为确保长度不丢失,使用字符串类型)
$binary = '';
foreach ($hex_array as $number) {
$bin_number = strval(base_convert($number, 16, 2));
if (strlen($bin_number) < 4) {
$bin_number = str_pad($bin_number, 4, '0', STR_PAD_LEFT);
}
$binary .= $bin_number;
}
return $binary;
}
/**
* 二进制转十六进制
* @param $bin
*
* @return string
*/
public static function bin2hex($bin)
{
// 格式化为字符串
$bin = strval($bin);
/** 二进制转十六进制每4位一组 */
defined('BIN_TO_HEX_NUM') || define('BIN_TO_HEX_NUM', 4);
/** @var array $bin_array 把指定的二进制数按位切片为数组 */
$bin_array = str_split($bin, BIN_TO_HEX_NUM);
// 最终的二进制数字(为确保长度不丢失,使用字符串类型)
$hex = '';
foreach ($bin_array as $number) {
$hex .= strval(base_convert($number, 2, 16));
}
return $hex;
}
/**
* 二进制加运算
*
* @param array $params
*
* @return Word
*/
public static function addConversion($params)
{
return self::conversion($params, 5);
}
}

View File

@@ -0,0 +1,204 @@
<?php
namespace Rtgm\smecc\SM4\types;
use ErrorException;
/**
* BitString @ SM3-PHP
*
* Code BY ch4o5
* 10月. 14th 2019
* Powered by PhpStorm
*/
/**
* 比特串
* 由0和1组成的二进制数字序列。
* Class BitString
*
* @package SM3\types
*/
class BitString implements \ArrayAccess
{
/** @var string 一个比特串类型的变量 */
protected $bit_string = '';
/**
* BitString constructor.
*
* @param $string string|BitString|Word|mixed 传入的数据
* @param false|null $is_bit_string 是否比特串(只有入口明确知道不是)
*
* @throws \ErrorException
*/
public function __construct($string, $is_bit_string = null)
{
if (is_object($string)) {
$string = $string->getString();
}
if ($is_bit_string === false) {
// 指定不是比特串的,直接转换
$this->bit_string = "{$this->str2bin($string)}";
} else {
// 默认走个验证试试
$this->bit_string = $this->is_bit_string($string)
? $string
: "{$this->str2bin($string)}";
}
}
/**
* 字符串转比特串
*
* @param $str int|string 普通字符串
*
* @return string 转换为比特串
* @throws \ErrorException
*/
private function str2bin($str)
{
if (!is_string($str) && !is_int($str)) {
throw new ErrorException('输入的类型错误');
}
if (is_int($str)) {
return decbin($str);
}
$fileType = mb_detect_encoding($str , array('UTF-8','GBK','LATIN1','BIG5')) ;
if( $fileType != 'UTF-8'){
$str = mb_convert_encoding($str ,'utf-8' , $fileType);
}
$arr = preg_split('/(?<!^)(?!$)/u', $str);
foreach ($arr as &$v) {
$fileType = mb_detect_encoding($v , array('UTF-8','GBK','LATIN1','BIG5')) ;
/* if( $fileType != 'GBK'){
$v = mb_convert_encoding($v ,'GBK' , $fileType); //这里也要utf8啊
} */
if( $fileType != 'UTF-8'){
$str = mb_convert_encoding($str ,'utf-8' , $fileType);
}
$temp = unpack('H*', $v);
$v = base_convert($temp[1], 16, 2);
while (strlen($v) < 8) {
$v = '0' . $v;
}
unset($temp);
}
return join('', $arr);
}
/**
* 判断是否为比特串类型
*
* @param string|Word $string
*
* @return bool
*/
public function is_bit_string($string)
{
if (is_object($string)) {
$string = $string->getString();
}
// 检查是否为字符串
if (!is_string($string)) {
return false;
}
// 检查是否为只有0和1组成的字符串
$array = array_filter(str_split($string));
foreach ($array as $value) {
if (!in_array(
$value,
array(
0,
'0',
1,
'1'
),
true
)) {
return false;
}
}
return true;
}
public function __toString()
{
return $this->getString();
}
/**
* 获取比特串的值
*
* @return string
*/
public function getString()
{
return $this->bit_string;
}
public function offsetGet($offset)
{
return $this->bit_string[$offset];
}
/**
* Whether a offset exists
*
* @link https://php.net/manual/en/arrayaccess.offsetexists.php
*
* @param mixed $offset <p>
* An offset to check for.
* </p>
*
* @return bool true on success or false on failure.
* </p>
* <p>
* The return value will be casted to boolean if non-boolean was returned.
* @since 5.0.0
*/
public function offsetExists($offset)
{
return isset($this->bit_string[$offset]);
}
/**
* Offset to set
*
* @link https://php.net/manual/en/arrayaccess.offsetset.php
*
* @param mixed $offset <p>
* The offset to assign the value to.
* </p>
* @param mixed $value <p>
* The value to set.
* </p>
*
* @return BitString
* @since 5.0.0
*/
public function offsetSet($offset, $value)
{
$this->bit_string[$offset] = $value;
return $this;
}
/**
* Offset to unset
*
* @link https://php.net/manual/en/arrayaccess.offsetunset.php
*
* @param mixed $offset <p>
* The offset to unset.
* </p>
*
* @return void
* @since 5.0.0
*/
public function offsetUnset($offset)
{
unset($this->bit_string[$offset]);
}
}

View File

@@ -0,0 +1,8 @@
# *types/* 目录
本目录为自定义的扩展类型的目录
* `\SM3\types\BitString`
比特串 类型
* `\SM3\types\Word`
字 类型

View File

@@ -0,0 +1,73 @@
<?php
namespace Rtgm\smecc\SM4\types;
/**
* 字类型
* 长度32的比特串
* Class Word
*
* @package SM3\types\
*/
class Word extends BitString implements \ArrayAccess
{
/** @var int 设置长度为32 */
const length = 32;
/** @var string */
private $word = '';
/**
* Word constructor.
*
* @param $string
*
* @throws \ErrorException
*/
public function __construct($string)
{
parent::__construct($string);
if (strlen($this->bit_string) === self::length) {
$this->word = $this->bit_string;
} else {
$this->word = intval($this->bit_string) === 0
? 0
: $this->bit_string;
if (strlen($this->word) <= self::length) {
$this->word = str_pad(
$this->word,
self::length,
'0',
STR_PAD_LEFT
);
} else {
$this->word = substr($this->bit_string, -(self::length));
}
}
}
public function __toString()
{
return $this->word;
}
public function offsetGet($offset)
{
return $this->word[$offset];
}
public function getString()
{
return $this->word;
}
/**
* @return string
*/
public function getBitString()
{
return $this->bit_string;
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace Rtgm\util;
class FormatSign
{
public function run($sign)
{
list($binR, $binS) = $this->_decode_rs(base64_decode($sign));
$binR = $this->_trim_int_pad($binR);
$binS = $this->_trim_int_pad($binS);
$lenR = strlen($binR);
$lenS = strlen($binS);
$result = chr(48) . chr(2 + $lenR + 2 + $lenS) . chr(2) . chr($lenR) . $binR . chr(2) . chr($lenS) . $binS;
return base64_encode($result);
}
/**
*
*
* @return string
*/
/**
* 招行的解签没有用标准的asn1解析函数当出现r,s的位数不足的时候就报错了只支持rs, 31,32字节当字节数少时强制补0吧
*
* @param string $sign
* @return string
*/
public function format_cmbc($sign){
list($binR, $binS) = $this->_decode_rs(base64_decode($sign));
while(strlen($binR)<32){
$binR = chr(0).$binR;
}
while(strlen($binS)<32){
$binS = chr(0).$binS;
}
$lenR = strlen($binR);
$lenS = strlen($binS);
$result = chr(48) . chr(2 + $lenR + 2 + $lenS) . chr(2) . chr($lenR) . $binR . chr(2) . chr($lenS) . $binS;
return base64_encode($result);
}
private function _trim_int_pad($binStr)
{
// echo bin2hex($binStr)."\n";
//trim 0
while(ord($binStr[0])==0){
$binStr = substr($binStr,1);
}
// add 0 if necessary
if(ord($binStr[0])>127){
$binStr = chr(0).$binStr;
}
// echo bin2hex($binStr)."\n";
return $binStr;
}
private function _decode_rs($binSign)
{
$rLen = ord($binSign[3]);
$binR = substr($binSign, 4, $rLen);
$binS = substr($binSign, (4 + $rLen + 2));
// echo bin2hex($binR) . "\n----------\n" . bin2hex($binS) . "\n";
return [$binR, $binS];
}
}