Skip to content

Commit a41f5ee

Browse files
ATL-6924: Refactor CryptoUtils and remove SHA256Digest
This commit improves the organization of CryptoUtils methods It also delete the used of SDK methods related to Sha256 hashing
1 parent 73ff642 commit a41f5ee

34 files changed

+233
-245
lines changed

node/src/main/scala/io/iohk/atala/prism/node/NodeGrpcServiceImpl.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ class NodeGrpcServiceImpl(
127127
node_api
128128
.GetDidDocumentResponse(document = didData.maybeData)
129129
.withLastUpdateOperation(
130-
didData.maybeOperation.map(a => ByteString.copyFrom(a.getValue)).getOrElse(ByteString.EMPTY)
130+
didData.maybeOperation.map(a => ByteString.copyFrom(a.bytes.toArray)).getOrElse(ByteString.EMPTY)
131131
)
132132
.withLastSyncedBlockTimestamp(didData.lastSyncedTimeStamp.toProtoTimestamp)
133133
)

node/src/main/scala/io/iohk/atala/prism/node/crypto/CryptoUtils.scala

+73-70
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,62 @@ object CryptoUtils {
2222
def y: Array[Byte] = publicKey.asInstanceOf[ECPublicKey].getQ.getAffineYCoord.getEncoded
2323
}
2424

25+
private[crypto] class SecpPublicKeyImpl(pubKey: PublicKey) extends SecpPublicKey {
26+
override private[crypto] def publicKey: PublicKey = pubKey
27+
}
28+
2529
// We define the constructor to SecpKeys private so that the only way to generate
2630
// these keys is by using the methods unsafeToPublicKeyFromByteCoordinates and
2731
// unsafeToPublicKeyFromCompressed.
28-
private object SecpPublicKey {
29-
private class SecpPublicKeyImpl(pubKey: PublicKey) extends SecpPublicKey {
30-
override private[crypto] def publicKey: PublicKey = pubKey
32+
object SecpPublicKey {
33+
34+
private[crypto] def fromPublicKey(key: PublicKey): SecpPublicKey = new SecpPublicKeyImpl(key)
35+
36+
def checkECDSASignature(msg: Array[Byte], sig: Array[Byte], pubKey: SecpPublicKey): Boolean = {
37+
val ecdsaVerify = Signature.getInstance("SHA256withECDSA", provider)
38+
ecdsaVerify.initVerify(pubKey.publicKey)
39+
ecdsaVerify.update(msg)
40+
ecdsaVerify.verify(sig)
41+
}
42+
43+
def unsafeToSecpPublicKeyFromCompressed(com: Vector[Byte]): SecpPublicKey = {
44+
val params = ECNamedCurveTable.getParameterSpec("secp256k1")
45+
val fact = KeyFactory.getInstance("ECDSA", provider)
46+
val curve = params.getCurve
47+
val ellipticCurve = EC5Util.convertCurve(curve, params.getSeed)
48+
val point = ECPointUtil.decodePoint(ellipticCurve, com.toArray)
49+
val params2 = EC5Util.convertSpec(ellipticCurve, params)
50+
val keySpec = new ECPublicKeySpec(point, params2)
51+
SecpPublicKey.fromPublicKey(fact.generatePublic(keySpec))
52+
}
53+
54+
def unsafeToSecpPublicKeyFromByteCoordinates(x: Array[Byte], y: Array[Byte]): SecpPublicKey = {
55+
def trimLeadingZeroes(arr: Array[Byte], c: String): Array[Byte] = {
56+
val trimmed = arr.dropWhile(_ == 0.toByte)
57+
require(
58+
trimmed.length <= PUBLIC_KEY_COORDINATE_BYTE_SIZE,
59+
s"Expected $c coordinate byte length to be less than or equal ${PUBLIC_KEY_COORDINATE_BYTE_SIZE}, but got ${trimmed.length} bytes"
60+
)
61+
trimmed
62+
}
63+
64+
val xTrimmed = trimLeadingZeroes(x, "x")
65+
val yTrimmed = trimLeadingZeroes(y, "y")
66+
val xInteger = BigInt(1, xTrimmed)
67+
val yInteger = BigInt(1, yTrimmed)
68+
SecpPublicKey.unsafeToSecpPublicKeyFromBigIntegerCoordinates(xInteger, yInteger)
3169
}
3270

33-
def fromPublicKey(key: PublicKey): SecpPublicKey = new SecpPublicKeyImpl(key)
71+
def unsafeToSecpPublicKeyFromBigIntegerCoordinates(x: BigInt, y: BigInt): SecpPublicKey = {
72+
val params = ECNamedCurveTable.getParameterSpec("secp256k1")
73+
val fact = KeyFactory.getInstance("ECDSA", provider)
74+
val curve = params.getCurve
75+
val ellipticCurve = EC5Util.convertCurve(curve, params.getSeed)
76+
val point = new ECPoint(x.bigInteger, y.bigInteger)
77+
val params2 = EC5Util.convertSpec(ellipticCurve, params)
78+
val keySpec = new ECPublicKeySpec(point, params2)
79+
SecpPublicKey.fromPublicKey(fact.generatePublic(keySpec))
80+
}
3481
}
3582

3683
private val provider = new BouncyCastleProvider()
@@ -40,19 +87,34 @@ object CryptoUtils {
4087

4188
trait Sha256Hash {
4289
def bytes: Vector[Byte]
90+
def hexEncoded: String = bytesToHex(bytes)
4391
}
4492

4593
private[crypto] case class Sha256HashImpl(bytes: Vector[Byte]) extends Sha256Hash {
4694
require(bytes.size == 32)
4795
}
4896

49-
def sha256Hash(bArray: Array[Byte]): Sha256Hash = {
50-
Sha256HashImpl(
51-
MessageDigest
52-
.getInstance("SHA-256")
53-
.digest(bArray)
54-
.toVector
55-
)
97+
object Sha256Hash {
98+
99+
def fromBytes(arr: Array[Byte]): Sha256Hash = Sha256HashImpl(arr.toVector)
100+
101+
def compute(bArray: Array[Byte]): Sha256Hash = {
102+
Sha256HashImpl(
103+
MessageDigest
104+
.getInstance("SHA-256")
105+
.digest(bArray)
106+
.toVector
107+
)
108+
}
109+
110+
def fromHex(hexedBytes: String): Sha256Hash = {
111+
val HEX_STRING_RE = "^[0-9a-fA-F]{64}$".r
112+
if (HEX_STRING_RE.matches(hexedBytes)) Sha256HashImpl(hexToBytes(hexedBytes))
113+
else
114+
throw new IllegalArgumentException(
115+
"The given hex string doesn't correspond to a valid SHA-256 hash encoded as string"
116+
)
117+
}
56118
}
57119

58120
def bytesToHex(bytes: Vector[Byte]): String = {
@@ -68,63 +130,4 @@ object CryptoUtils {
68130
octet = firstIndex << 4 | secondIndex
69131
} yield octet.toByte
70132
}
71-
72-
def hexedHash(hash: Sha256Hash): String = {
73-
bytesToHex(hash.bytes)
74-
}
75-
76-
def fromHex(hexedBytes: String): Sha256Hash = {
77-
val HEX_STRING_RE = "^[0-9a-fA-F]{64}$".r
78-
if (HEX_STRING_RE.matches(hexedBytes)) Sha256HashImpl(hexToBytes(hexedBytes))
79-
else
80-
throw new IllegalArgumentException(
81-
"The given hex string doesn't correspond to a valid SHA-256 hash encoded as string"
82-
)
83-
}
84-
85-
def checkECDSASignature(msg: Array[Byte], sig: Array[Byte], pubKey: SecpPublicKey): Boolean = {
86-
val ecdsaVerify = Signature.getInstance("SHA256withECDSA", provider)
87-
ecdsaVerify.initVerify(pubKey.publicKey)
88-
ecdsaVerify.update(msg)
89-
ecdsaVerify.verify(sig)
90-
}
91-
92-
def unsafeToSecpPublicKeyFromByteCoordinates(x: Array[Byte], y: Array[Byte]): SecpPublicKey = {
93-
def trimLeadingZeroes(arr: Array[Byte], c: String): Array[Byte] = {
94-
val trimmed = arr.dropWhile(_ == 0.toByte)
95-
require(
96-
trimmed.length <= PUBLIC_KEY_COORDINATE_BYTE_SIZE,
97-
s"Expected $c coordinate byte length to be less than or equal ${PUBLIC_KEY_COORDINATE_BYTE_SIZE}, but got ${trimmed.length} bytes"
98-
)
99-
trimmed
100-
}
101-
102-
val xTrimmed = trimLeadingZeroes(x, "x")
103-
val yTrimmed = trimLeadingZeroes(y, "y")
104-
val xInteger = BigInt(1, xTrimmed)
105-
val yInteger = BigInt(1, yTrimmed)
106-
unsafeToSecpPublicKeyFromBigIntegerCoordinates(xInteger, yInteger)
107-
}
108-
109-
private def unsafeToSecpPublicKeyFromBigIntegerCoordinates(x: BigInt, y: BigInt): SecpPublicKey = {
110-
val params = ECNamedCurveTable.getParameterSpec("secp256k1")
111-
val fact = KeyFactory.getInstance("ECDSA", provider)
112-
val curve = params.getCurve
113-
val ellipticCurve = EC5Util.convertCurve(curve, params.getSeed)
114-
val point = new ECPoint(x.bigInteger, y.bigInteger)
115-
val params2 = EC5Util.convertSpec(ellipticCurve, params)
116-
val keySpec = new ECPublicKeySpec(point, params2)
117-
SecpPublicKey.fromPublicKey(fact.generatePublic(keySpec))
118-
}
119-
120-
def unsafeToSecpPublicKeyFromCompressed(com: Vector[Byte]): SecpPublicKey = {
121-
val params = ECNamedCurveTable.getParameterSpec("secp256k1")
122-
val fact = KeyFactory.getInstance("ECDSA", provider)
123-
val curve = params.getCurve
124-
val ellipticCurve = EC5Util.convertCurve(curve, params.getSeed)
125-
val point = ECPointUtil.decodePoint(ellipticCurve, com.toArray)
126-
val params2 = EC5Util.convertSpec(ellipticCurve, params)
127-
val keySpec = new ECPublicKeySpec(point, params2)
128-
SecpPublicKey.fromPublicKey(fact.generatePublic(keySpec))
129-
}
130133
}

node/src/main/scala/io/iohk/atala/prism/node/interop/implicits.scala

+7-13
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package io.iohk.atala.prism.node.interop
22

33
import cats.data.NonEmptyList
44
import doobie.{Get, Meta, Read, Write}
5-
import io.iohk.atala.prism.crypto.{MerkleRoot, Sha256Digest}
65
import doobie.implicits.legacy.instant._
6+
import io.iohk.atala.prism.node.crypto.CryptoUtils.Sha256Hash
77
import io.iohk.atala.prism.protos.models.TimestampInfo
88
import io.iohk.atala.prism.node.models.{DidSuffix, Ledger, TransactionId}
99
import io.iohk.atala.prism.node.utils.DoobieImplicits.byteArraySeqMeta
@@ -12,12 +12,6 @@ import java.time.Instant
1212
import scala.collection.compat.immutable.ArraySeq
1313

1414
object implicits {
15-
implicit val merkleRootMeta: Meta[MerkleRoot] =
16-
Meta[Array[Byte]].timap(arr => new MerkleRoot(Sha256Digest.fromBytes(arr)))(
17-
_.getHash.getValue
18-
)
19-
implicit val merkleRootRead: Read[MerkleRoot] =
20-
Read[Array[Byte]].map(arr => new MerkleRoot(Sha256Digest.fromBytes(arr)))
2115

2216
implicit val didSuffixMeta: Meta[DidSuffix] =
2317
Meta[String].timap { DidSuffix.apply }(_.value)
@@ -36,12 +30,12 @@ object implicits {
3630
implicit val ledgerRead: Read[Ledger] =
3731
Read[String].map { Ledger.withNameInsensitive }
3832

39-
implicit val Sha256DigestWrite: Write[Sha256Digest] =
40-
Write[Array[Byte]].contramap(_.getValue)
41-
implicit val Sha256DigestRead: Read[Sha256Digest] =
42-
Read[Array[Byte]].map(Sha256Digest.fromBytes)
43-
implicit val Sha256DigestGet: Get[Sha256Digest] =
44-
Get[Array[Byte]].map(Sha256Digest.fromBytes)
33+
implicit val Sha256DigestWrite: Write[Sha256Hash] =
34+
Write[Array[Byte]].contramap(_.bytes.toArray)
35+
implicit val Sha256HashRead: Read[Sha256Hash] =
36+
Read[Array[Byte]].map(Sha256Hash.fromBytes)
37+
implicit val Sha256HashGet: Get[Sha256Hash] =
38+
Get[Array[Byte]].map(Sha256Hash.fromBytes)
4539

4640
implicit val timestampInfoRead: Read[TimestampInfo] =
4741
Read[(Instant, Int, Int)].map { case (abt, absn, osn) =>

node/src/main/scala/io/iohk/atala/prism/node/models/AtalaObjectId.scala

+6-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.iohk.atala.prism.node.models
22

3-
import io.iohk.atala.prism.crypto.{Sha256, Sha256Digest}
3+
import io.iohk.atala.prism.node.crypto.CryptoUtils.Sha256Hash
44
import io.iohk.atala.prism.protos.node_models
55
import io.iohk.atala.prism.node.utils.BytesOps
66
import tofu.logging.{DictLoggable, LogRenderer}
@@ -25,19 +25,17 @@ object AtalaObjectId {
2525
}
2626

2727
def apply(value: Vector[Byte]): AtalaObjectId = {
28-
// temporary replace for require(value.length == SHA256Digest.getBYTE_LENGTH)
29-
// rewrite to safe version pls
30-
// will throw an error if something is wrong with the value
31-
val digestUnsafe = Sha256Digest.fromBytes(value.toArray).getValue
32-
new AtalaObjectId(digestUnsafe.toVector)
28+
// This will throw an error if something is wrong with the value
29+
val digestUnsafe = Sha256Hash.fromBytes(value.toArray).bytes
30+
new AtalaObjectId(digestUnsafe)
3331
}
3432

3533
def of(atalaObject: node_models.AtalaObject): AtalaObjectId = {
3634
of(atalaObject.toByteArray)
3735
}
3836

3937
def of(bytes: Array[Byte]): AtalaObjectId = {
40-
val hash = Sha256.compute(bytes)
41-
AtalaObjectId(hash.getValue.toVector)
38+
val hash = Sha256Hash.compute(bytes)
39+
AtalaObjectId(hash.bytes)
4240
}
4341
}

node/src/main/scala/io/iohk/atala/prism/node/models/AtalaOperationId.scala

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
package io.iohk.atala.prism.node.models
22

33
import com.google.protobuf.ByteString
4-
import io.iohk.atala.prism.crypto.{Sha256, Sha256Digest}
4+
import io.iohk.atala.prism.node.crypto.CryptoUtils.Sha256Hash
55
import io.iohk.atala.prism.protos.node_models
66
import tofu.logging.{DictLoggable, LogRenderer}
77
import io.iohk.atala.prism.node.utils.BytesOps
88

99
import java.util.UUID
1010

11-
class AtalaOperationId private (val digest: Sha256Digest) {
12-
def value: Vector[Byte] = digest.getValue.toVector
11+
class AtalaOperationId private (val digest: Sha256Hash) {
12+
def value: Vector[Byte] = digest.bytes
1313

1414
def hexValue: String = BytesOps.bytesToHex(value)
1515

@@ -40,22 +40,22 @@ object AtalaOperationId {
4040
}
4141

4242
def of(atalaOperation: node_models.SignedAtalaOperation): AtalaOperationId = {
43-
val hash = Sha256.compute(atalaOperation.toByteArray)
43+
val hash = Sha256Hash.compute(atalaOperation.toByteArray)
4444
new AtalaOperationId(hash)
4545
}
4646

4747
def random(): AtalaOperationId = {
48-
val hash = Sha256.compute(UUID.randomUUID().toString.getBytes())
48+
val hash = Sha256Hash.compute(UUID.randomUUID().toString.getBytes())
4949
new AtalaOperationId(hash)
5050
}
5151

5252
def fromVectorUnsafe(bytes: Vector[Byte]): AtalaOperationId = {
53-
val hash = Sha256Digest.fromBytes(bytes.toArray)
53+
val hash = Sha256Hash.fromBytes(bytes.toArray)
5454
new AtalaOperationId(hash)
5555
}
5656

5757
def fromHexUnsafe(hex: String): AtalaOperationId = {
58-
val hash = Sha256Digest.fromHex(hex)
58+
val hash = Sha256Hash.fromHex(hex)
5959
new AtalaOperationId(hash)
6060
}
6161
}

node/src/main/scala/io/iohk/atala/prism/node/models/DidSuffix.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.iohk.atala.prism.node.models
22

3-
import io.iohk.atala.prism.crypto.Sha256Digest
3+
import io.iohk.atala.prism.node.crypto.CryptoUtils.Sha256Hash
44

55
import scala.util.matching.Regex
66
import scala.util.{Failure, Success, Try}
@@ -15,7 +15,7 @@ object DidSuffix {
1515

1616
def didFromStringSuffix(in: String): String = "did:prism:" + in
1717

18-
def fromDigest(in: Sha256Digest): DidSuffix = DidSuffix(in.getHexValue)
18+
def fromDigest(in: Sha256Hash): DidSuffix = DidSuffix(in.hexEncoded)
1919

2020
def fromString(string: String): Try[DidSuffix] = {
2121
if (string.nonEmpty && suffixRegex.pattern.matcher(string).matches())

node/src/main/scala/io/iohk/atala/prism/node/models/IdType.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.iohk.atala.prism.node.models
22

3-
import io.iohk.atala.prism.crypto.Sha256
3+
import io.iohk.atala.prism.node.crypto.CryptoUtils.Sha256Hash
44

55
import scala.util.matching.Regex
66
import scala.util.{Failure, Success, Try}
@@ -26,5 +26,5 @@ object IdType {
2626
)
2727
}
2828

29-
def random: IdType = IdType(value = Sha256.compute(java.util.UUID.randomUUID.toString.getBytes).getHexValue)
29+
def random: IdType = IdType(value = Sha256Hash.compute(java.util.UUID.randomUUID.toString.getBytes).hexEncoded)
3030
}

node/src/main/scala/io/iohk/atala/prism/node/models/package.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package io.iohk.atala.prism.node
33
import derevo.derive
44
import enumeratum.EnumEntry.UpperSnakecase
55
import enumeratum._
6-
import io.iohk.atala.prism.crypto.Sha256Digest
6+
import io.iohk.atala.prism.node.crypto.CryptoUtils.Sha256Hash
77
import io.iohk.atala.prism.protos.models.TimestampInfo
88
import io.iohk.atala.prism.protos.node_models
99
import tofu.logging.derivation.loggable
@@ -60,7 +60,7 @@ package object models {
6060
keys: List[DIDPublicKey],
6161
services: List[DIDService],
6262
context: List[String],
63-
lastOperation: Sha256Digest
63+
lastOperation: Sha256Hash
6464
)
6565

6666
@derive(loggable)
@@ -147,7 +147,7 @@ package object models {
147147
keys: List[DIDPublicKeyState],
148148
services: List[DIDServiceState],
149149
context: List[String],
150-
lastOperation: Sha256Digest
150+
lastOperation: Sha256Hash
151151
)
152152

153153
case class LedgerData(

0 commit comments

Comments
 (0)