MdanterEccSignatory::readPrivateKey()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
ccs 5
cts 5
cp 1
crap 1
1
<?php
2
3
/**
4
 * Copyright 2017 American Express Travel Related Services Company, Inc.
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
15
 * or implied. See the License for the specific language governing
16
 * permissions and limitations under the License.
17
 */
18
19
declare(strict_types=1);
20
21
namespace AmericanExpress\HyperledgerFabricClient\Signatory;
22
23
use AmericanExpress\HyperledgerFabricClient\Exception\RuntimeException;
24
use AmericanExpress\HyperledgerFabricClient\ProtoFactory\SignedProposalFactory;
25
use AmericanExpress\HyperledgerFabricClient\Serializer\AsciiCharStringSerializer;
26
use AmericanExpress\HyperledgerFabricClient\HashAlgorithm;
27
use AmericanExpress\HyperledgerFabricClient\Serializer\SignedCharStringSerializer;
28
use Hyperledger\Fabric\Protos\Peer\Proposal;
29
use Hyperledger\Fabric\Protos\Peer\SignedProposal;
30
use Mdanter\Ecc\Crypto\Key\PrivateKeyInterface;
31
use Mdanter\Ecc\Crypto\Signature\HasherInterface;
32
use Mdanter\Ecc\Crypto\Signature\Signature;
33
use Mdanter\Ecc\Crypto\Signature\SignatureInterface;
34
use Mdanter\Ecc\Crypto\Signature\SignHasher;
35
use Mdanter\Ecc\EccFactory;
36
use Mdanter\Ecc\Crypto\Signature\Signer;
37
use Mdanter\Ecc\Math\GmpMath;
38
use Mdanter\Ecc\Math\GmpMathInterface;
39
use Mdanter\Ecc\Primitives\GeneratorPoint;
40
use Mdanter\Ecc\Random\RandomGeneratorFactory;
41
use Mdanter\Ecc\Serializer\PrivateKey\PemPrivateKeySerializer;
42
use Mdanter\Ecc\Serializer\PrivateKey\DerPrivateKeySerializer;
43
use Mdanter\Ecc\Serializer\Signature\DerSignatureSerializer;
44
45
final class MdanterEccSignatory implements SignatoryInterface
46
{
47
    /**
48
     * @var DerSignatureSerializer
49
     */
50
    private $derSignatureSerializer;
51
52
    /**
53
     * @var GmpMath
54
     */
55
    private $gmpMath;
56
57
    /**
58
     * @var HashAlgorithm
59
     */
60
    private $hashAlgorithm;
61
62
    /**
63
     * @var AsciiCharStringSerializer
64
     */
65
    private $asciiCharStringSerializer;
66
67
    /**
68
     * @var SignedCharStringSerializer
69
     */
70
    private $signedCharStringSerializer;
71
72
    /**
73
     * @var GmpMathInterface
74
     */
75
    private $adapter;
76
77
    /**
78
     * @var GeneratorPoint
79
     */
80
    private $generator;
81
82
    /**
83
     * @var Signer
84
     */
85
    private $signer;
86
87
    /**
88
     * @var HasherInterface
89
     */
90
    private $hasher;
91
92
    /**
93
     * Utils constructor.
94
     * @param HashAlgorithm $hashAlgorithm
95
     * @throws RuntimeException
96
     * @throws \AmericanExpress\HyperledgerFabricClient\Exception\InvalidArgumentException
97
     */
98 6
    public function __construct(HashAlgorithm $hashAlgorithm = null)
99
    {
100 6
        $this->hashAlgorithm = $hashAlgorithm ?: new HashAlgorithm();
101 6
        $this->asciiCharStringSerializer = new AsciiCharStringSerializer();
102 6
        $this->signedCharStringSerializer = new SignedCharStringSerializer();
103 6
        $this->adapter = EccFactory::getAdapter();
104 6
        $this->generator = EccFactory::getNistCurves()->generator256();
105 6
        $this->signer = new Signer($this->adapter);
106 6
        $this->gmpMath = new GmpMath();
107 6
        $this->hasher = new SignHasher((string) $this->hashAlgorithm, $this->gmpMath);
108 6
        $this->derSignatureSerializer = new DerSignatureSerializer();
109 6
    }
110
111
    /**
112
     * @param Proposal $proposal
113
     * @param \SplFileObject $privateKeyFile
114
     * @return SignedProposal
115
     */
116 6
    public function signProposal(Proposal $proposal, \SplFileObject $privateKeyFile): SignedProposal
117
    {
118 6
        $proposalString = $proposal->serializeToString();
119 6
        $proposalArray = $this->signedCharStringSerializer->deserialize($proposalString);
120 6
        $privateKey = $this->readPrivateKey($privateKeyFile);
121 6
        $signature = $this->signData($privateKey, $proposalArray);
122
123 6
        return SignedProposalFactory::fromProposal($proposal, $signature);
124
    }
125
126
    /**
127
     * @param \SplFileObject $privateKeyPath
128
     * @return PrivateKeyInterface
129
     *
130
     */
131 6
    private function readPrivateKey(\SplFileObject $privateKeyPath): PrivateKeyInterface
132
    {
133
        ## You'll be restoring from a key, as opposed to generating one.
134 6
        $keyData = $privateKeyPath->fread($privateKeyPath->getSize());
135 6
        \openssl_pkey_export($keyData, $privateKey);
136 6
        $pemSerializer = new PemPrivateKeySerializer(new DerPrivateKeySerializer($this->adapter));
137
138 6
        return $pemSerializer->parse($privateKey);
139
    }
140
141
    /**
142
     * @param PrivateKeyInterface $privateKey
143
     * @param $dataArray
144
     * @return string
145
     * sign private key of node
146
     */
147 6
    private function signData(PrivateKeyInterface $privateKey, array $dataArray): string
148
    {
149 6
        $dataString = $this->asciiCharStringSerializer->serialize($dataArray);
150
151 6
        $hash = $this->hasher->makeHash($dataString, $this->generator);
152
153
        # Derandomized signatures are not necessary, but can reduce
154
        # the attack surface for a private key that is to be used often.
155 6
        $random = RandomGeneratorFactory::getHmacRandomGenerator($privateKey, $hash, (string) $this->hashAlgorithm);
156
157 6
        $randomK = $random->generate($this->generator->getOrder());
158
159 6
        $signature = $this->signer->sign($privateKey, $hash, $randomK);
160
161 6
        $eccSignature = new Signature($signature->getR(), $this->getS($signature));
162
163 6
        return $this->derSignatureSerializer->serialize($eccSignature);
164
    }
165
166
    /**
167
     * @param SignatureInterface $signature
168
     * @return \GMP
169
     */
170 6
    private function getS(SignatureInterface $signature): \GMP
171
    {
172 6
        $order = $this->generator->getOrder();
173 6
        $halfOrder = $this->adapter->rightShift($order, 1);
174
175 6
        $s = $signature->getS();
176 6
        if ($this->gmpMath->cmp($s, $halfOrder) > 0) {
177 3
            $s = $this->adapter->sub($order, $s);
178
        }
179
180 6
        return $s;
181
    }
182
}
183