Coverage Summary for Class: Secp256k1ServiceNative (org.ethereum.crypto.signature)
Class |
Class, %
|
Method, %
|
Line, %
|
Secp256k1ServiceNative |
0%
(0/1)
|
0%
(0/6)
|
0%
(0/32)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2020 RSK Labs Ltd.
4 * (derived from ethereumJ library, Copyright (c) 2016 <ether.camp>)
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 package org.ethereum.crypto.signature;
21
22 import org.bitcoin.NativeSecp256k1;
23 import org.bitcoin.NativeSecp256k1Exception;
24 import org.ethereum.crypto.ECKey;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 import javax.annotation.Nullable;
29
30 import static java.lang.System.arraycopy;
31
32 /**
33 * Implementation of SignatureService with Native library.
34 */
35 public class Secp256k1ServiceNative extends Secp256k1ServiceBC {
36
37 private static final Logger logger = LoggerFactory.getLogger(Secp256k1ServiceNative.class);
38 private static final byte[] ZERO_PUB = {0};
39
40 @Nullable
41 @Override
42 public ECKey recoverFromSignature(int recId, ECDSASignature sig, byte[] messageHash, boolean compressed) {
43 check(recId >= 0, "recId must be positive");
44 check(sig.getR().signum() >= 0, "r must be positive");
45 check(sig.getS().signum() >= 0, "s must be positive");
46 check(messageHash != null, "messageHash must not be null");
47
48 // to be compatible with BC implementation. and to prevent garbage in signature truncation when concatenate.
49 if (!sig.validateComponentsWithoutV()) {
50 return null;
51 }
52
53 byte[] pbKey;
54 try {
55 byte[] sigBytes = concatenate(sig);
56 logger.trace("Recovering key from signature: comporessed[{}] - recId[{}] - sig[{}] - msgHash[{}].", compressed, recId, sigBytes, messageHash);
57 try {
58 pbKey = NativeSecp256k1.ecdsaRecover(sigBytes, messageHash, recId, compressed);
59 } catch (NativeSecp256k1Exception e) {
60 if (NativeSecp256k1.isInfinity(sigBytes, messageHash, recId)) {
61 return ECKey.fromPublicOnly(ZERO_PUB);
62 }
63 throw e;
64 }
65 } catch (Exception e) {
66 logger.error("Couldnt recover key from signature.", e);
67 return null;
68 }
69 return ECKey.fromPublicOnly(pbKey);
70 }
71
72 /**
73 * Returns a (r.length + s.length) bytes array long
74 * <p>
75 * Note: we take 32 bytes from "r" and 32 bytes from "s".
76 *
77 * @param sig {r,s}
78 * @return r + s (64 length byte array)
79 */
80 byte[] concatenate(ECDSASignature sig) {
81 byte[] rBytes = sig.getR().toByteArray();
82 byte[] sBytes = sig.getS().toByteArray();
83 byte[] result = new byte[64];
84 int rLength = getLength(rBytes);
85 int sLength = getLength(sBytes);
86 arraycopy(rBytes, getStartIndex(rBytes), result, 32 - rLength, rLength);
87 arraycopy(sBytes, getStartIndex(sBytes), result, 64 - sLength, sLength);
88 return result;
89 }
90
91 /**
92 * Get the length of valid data to copy from the array, with a max of 32 bytes.
93 *
94 * @param rs
95 * @return
96 */
97 private static final int getLength(byte[] rs) {
98 return Math.min(rs.length, 32);
99 }
100
101 /**
102 * If bytes length is greater than 32, we keep the last 32 bytes at the right.
103 * - So starting byte index will be = length - 32.
104 * If not
105 * - Starting byte index = 0.
106 *
107 * @param rs
108 * @return
109 */
110 private static final int getStartIndex(byte[] rs) {
111 return Math.max(rs.length - 32, 0);
112 }
113 }