1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package io.github.tonywasher.joceanus.gordianknot.impl.bc.sign;
19
20 import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
21 import io.github.tonywasher.joceanus.gordianknot.api.sign.GordianNewSignParams;
22 import io.github.tonywasher.joceanus.gordianknot.api.sign.spec.GordianSignatureSpec;
23 import io.github.tonywasher.joceanus.gordianknot.impl.bc.keypair.BouncyEllipticKeyPair.BouncyECPrivateKey;
24 import io.github.tonywasher.joceanus.gordianknot.impl.bc.keypair.BouncyEllipticKeyPair.BouncyECPublicKey;
25 import io.github.tonywasher.joceanus.gordianknot.impl.bc.keypair.BouncyKeyPair;
26 import io.github.tonywasher.joceanus.gordianknot.impl.bc.sign.BouncySignature.BouncyDSACoder;
27 import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseFactory;
28 import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianDataConverter;
29 import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianCryptoException;
30 import org.bouncycastle.asn1.ASN1OctetString;
31 import org.bouncycastle.asn1.ASN1Primitive;
32 import org.bouncycastle.asn1.DEROctetString;
33 import org.bouncycastle.asn1.ua.DSTU4145Params;
34 import org.bouncycastle.crypto.Digest;
35 import org.bouncycastle.crypto.digests.GOST3411Digest;
36 import org.bouncycastle.crypto.params.ParametersWithRandom;
37 import org.bouncycastle.crypto.signers.DSTU4145Signer;
38
39 import java.math.BigInteger;
40
41
42
43
44 public class BouncyDSTUSignature
45 extends BouncyDigestSignature {
46
47
48
49 private static final int EXPANDED_LEN = 128;
50
51
52
53
54 private final DSTU4145Signer theSigner;
55
56
57
58
59 private final BouncyDSTUCoder theCoder;
60
61
62
63
64
65
66
67 BouncyDSTUSignature(final GordianBaseFactory pFactory,
68 final GordianSignatureSpec pSpec) {
69
70 super(pFactory, pSpec, newDigest());
71
72
73 theSigner = new DSTU4145Signer();
74 theCoder = new BouncyDSTUCoder();
75 }
76
77 @Override
78 public void initForSigning(final GordianNewSignParams pParams) throws GordianException {
79
80 super.initForSigning(pParams);
81 final BouncyKeyPair myPair = getKeyPair();
82 BouncyKeyPair.checkKeyPair(myPair);
83
84
85 final BouncyECPrivateKey myPrivate = (BouncyECPrivateKey) myPair.getPrivateKey();
86 final ParametersWithRandom myParms = new ParametersWithRandom(myPrivate.getPrivateKey(), getRandom());
87 theSigner.init(true, myParms);
88 }
89
90 @Override
91 public void initForVerify(final GordianNewSignParams pParams) throws GordianException {
92
93 super.initForVerify(pParams);
94 final BouncyKeyPair myPair = getKeyPair();
95 BouncyKeyPair.checkKeyPair(myPair);
96
97
98 final BouncyECPublicKey myPublic = (BouncyECPublicKey) myPair.getPublicKey();
99 theSigner.init(false, myPublic.getPublicKey());
100 }
101
102 @Override
103 public byte[] sign() throws GordianException {
104
105 checkMode(GordianSignatureMode.SIGN);
106
107
108 final BigInteger[] myValues = theSigner.generateSignature(getDigest());
109 return theCoder.dsaEncode(myValues[0], myValues[1]);
110 }
111
112 @Override
113 public boolean verify(final byte[] pSignature) throws GordianException {
114
115 checkMode(GordianSignatureMode.VERIFY);
116
117
118 final BigInteger[] myValues = theCoder.dsaDecode(pSignature);
119 return theSigner.verifySignature(getDigest(), myValues[0], myValues[1]);
120 }
121
122
123
124
125
126
127 private static Digest newDigest() {
128 final byte[] myCompressed = DSTU4145Params.getDefaultDKE();
129 final byte[] myExpanded = new byte[EXPANDED_LEN];
130
131 for (int i = 0; i < myCompressed.length; i++) {
132 myExpanded[i * 2] = (byte) ((myCompressed[i] >> GordianDataConverter.NYBBLE_SHIFT)
133 & GordianDataConverter.NYBBLE_MASK);
134 myExpanded[i * 2 + 1] = (byte) (myCompressed[i] & GordianDataConverter.NYBBLE_MASK);
135 }
136 return new GOST3411Digest(myExpanded);
137 }
138
139
140
141
142 static final class BouncyDSTUCoder implements BouncyDSACoder {
143 @Override
144 public byte[] dsaEncode(final BigInteger r,
145 final BigInteger s) throws GordianException {
146
147 try {
148
149 final byte[] myFirst = s.toByteArray();
150 final byte[] mySecond = r.toByteArray();
151 final byte[] myResult = myFirst.length > mySecond.length
152 ? new byte[myFirst.length * 2]
153 : new byte[mySecond.length * 2];
154
155
156 System.arraycopy(myFirst, 0, myResult, myResult.length / 2 - myFirst.length, myFirst.length);
157 System.arraycopy(mySecond, 0, myResult, myResult.length - mySecond.length, mySecond.length);
158 return new DEROctetString(myResult).getEncoded();
159 } catch (Exception e) {
160 throw new GordianCryptoException(BouncySignature.ERROR_SIGGEN, e);
161 }
162 }
163
164 @Override
165 public BigInteger[] dsaDecode(final byte[] pEncoded) throws GordianException {
166
167 try {
168
169 final byte[] bytes = ((ASN1OctetString) ASN1Primitive.fromByteArray(pEncoded)).getOctets();
170
171
172 final byte[] myFirst = new byte[bytes.length / 2];
173 final byte[] mySecond = new byte[bytes.length / 2];
174 System.arraycopy(bytes, 0, myFirst, 0, myFirst.length);
175 System.arraycopy(bytes, myFirst.length, mySecond, 0, mySecond.length);
176
177
178 final BigInteger[] sig = new BigInteger[2];
179 sig[1] = new BigInteger(1, myFirst);
180 sig[0] = new BigInteger(1, mySecond);
181 return sig;
182 } catch (Exception e) {
183 throw new GordianCryptoException(BouncySignature.ERROR_SIGPARSE, e);
184 }
185 }
186 }
187 }