View Javadoc
1   /*
2    * GordianKnot: Security Suite
3    * Copyright 2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
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.spec.sign.GordianCoreSignatureSpec;
29  import org.bouncycastle.crypto.params.ParametersWithRandom;
30  import org.bouncycastle.crypto.signers.ECGOST3410Signer;
31  
32  import java.math.BigInteger;
33  
34  /**
35   * GOST signer.
36   */
37  public class BouncyGOSTSignature
38          extends BouncyDigestSignature {
39      /**
40       * The Signer.
41       */
42      private final ECGOST3410Signer theSigner;
43  
44      /**
45       * The Coder.
46       */
47      private final BouncyGOSTCoder theCoder;
48  
49      /**
50       * Constructor.
51       *
52       * @param pFactory the factory
53       * @param pSpec    the signatureSpec.
54       * @throws GordianException on error
55       */
56      BouncyGOSTSignature(final GordianBaseFactory pFactory,
57                          final GordianSignatureSpec pSpec) throws GordianException {
58          /* Initialise underlying class */
59          super(pFactory, pSpec);
60  
61          /* Create the signer and Coder */
62          theSigner = new ECGOST3410Signer();
63          theCoder = new BouncyGOSTCoder(((GordianCoreSignatureSpec) pSpec).getDigestSpec().getDigestLength().getByteLength() << 1);
64      }
65  
66      @Override
67      public void initForSigning(final GordianNewSignParams pParams) throws GordianException {
68          /* Initialise detail */
69          super.initForSigning(pParams);
70          final BouncyKeyPair myPair = getKeyPair();
71          BouncyKeyPair.checkKeyPair(myPair);
72  
73          /* Initialise and set the signer */
74          final BouncyECPrivateKey myPrivate = (BouncyECPrivateKey) myPair.getPrivateKey();
75          final ParametersWithRandom myParms = new ParametersWithRandom(myPrivate.getPrivateKey(), getRandom());
76          theSigner.init(true, myParms);
77      }
78  
79      @Override
80      public void initForVerify(final GordianNewSignParams pParams) throws GordianException {
81          /* Initialise detail */
82          super.initForVerify(pParams);
83          final BouncyKeyPair myPair = getKeyPair();
84          BouncyKeyPair.checkKeyPair(myPair);
85  
86          /* Initialise and set the signer */
87          final BouncyECPublicKey myPublic = (BouncyECPublicKey) myPair.getPublicKey();
88          theSigner.init(false, myPublic.getPublicKey());
89      }
90  
91      @Override
92      public byte[] sign() throws GordianException {
93          /* Check that we are in signing mode */
94          checkMode(GordianSignatureMode.SIGN);
95  
96          /* Sign the message */
97          final BigInteger[] myValues = theSigner.generateSignature(getDigest());
98          return theCoder.dsaEncode(myValues[0], myValues[1]);
99      }
100 
101     @Override
102     public boolean verify(final byte[] pSignature) throws GordianException {
103         /* Check that we are in verify mode */
104         checkMode(GordianSignatureMode.VERIFY);
105 
106         /* Verify the message */
107         final BigInteger[] myValues = theCoder.dsaDecode(pSignature);
108         return theSigner.verifySignature(getDigest(), myValues[0], myValues[1]);
109     }
110 
111     /**
112      * GOST encoder.
113      */
114     static final class BouncyGOSTCoder
115             implements BouncyDSACoder {
116         /**
117          * The fixed length (if any).
118          */
119         private final Integer theLen;
120 
121         /**
122          * Constructor.
123          *
124          * @param pLen the fixed length (or null)
125          */
126         BouncyGOSTCoder(final Integer pLen) {
127             theLen = pLen;
128         }
129 
130         @Override
131         public byte[] dsaEncode(final BigInteger r,
132                                 final BigInteger s) {
133             /* Access byteArrays */
134             final byte[] myFirst = makeUnsigned(s);
135             final byte[] mySecond = makeUnsigned(r);
136             final byte[] myResult = new byte[theLen];
137 
138             /* Build array and return */
139             System.arraycopy(myFirst, 0, myResult, theLen / 2 - myFirst.length, myFirst.length);
140             System.arraycopy(mySecond, 0, myResult, theLen - mySecond.length, mySecond.length);
141             return myResult;
142         }
143 
144         /**
145          * Make the value unsigned.
146          *
147          * @param pValue the value
148          * @return the unsigned value
149          */
150         private static byte[] makeUnsigned(final BigInteger pValue) {
151             /* Convert to byteArray and return if OK */
152             final byte[] myResult = pValue.toByteArray();
153             if (myResult[0] != 0) {
154                 return myResult;
155             }
156 
157             /* Shorten the array */
158             final byte[] myTmp = new byte[myResult.length - 1];
159             System.arraycopy(myResult, 1, myTmp, 0, myTmp.length);
160             return myTmp;
161         }
162 
163         @Override
164         public BigInteger[] dsaDecode(final byte[] pEncoded) {
165             /* Build the value arrays */
166             final byte[] myFirst = new byte[pEncoded.length / 2];
167             final byte[] mySecond = new byte[pEncoded.length / 2];
168             System.arraycopy(pEncoded, 0, myFirst, 0, myFirst.length);
169             System.arraycopy(pEncoded, myFirst.length, mySecond, 0, mySecond.length);
170 
171             /* Create the signature values and return */
172             final BigInteger[] sig = new BigInteger[2];
173             sig[1] = new BigInteger(1, myFirst);
174             sig[0] = new BigInteger(1, mySecond);
175             return sig;
176         }
177     }
178 }