Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ dependencies {
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"

// Test dependencies
testImplementation "junit:junit:4.13.2"
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
}

if (isNewArchitectureEnabled()) {
Expand Down
156 changes: 130 additions & 26 deletions android/src/main/java/com/encryption/SignatureUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,33 +48,137 @@ object SignatureUtils {
* @return Base64-encoded public key derived from the private key.
*/
@Throws(Exception::class)
fun getPublicECDSAKey(privateKeyBase64: String): String {
return try {
// Decode the Base64 private key
val privateKeyBytes = Base64.decode(privateKeyBase64, Base64.DEFAULT)
val keyFactory = KeyFactory.getInstance("EC")

// Generate the private key
val privateKeySpec = PKCS8EncodedKeySpec(privateKeyBytes)
val privateKey = keyFactory.generatePrivate(privateKeySpec) as ECPrivateKey

// Generate the public key from the private key parameters
val ecParameterSpec = privateKey.params
val keyPairGenerator = KeyPairGenerator.getInstance("EC")
keyPairGenerator.initialize(ecParameterSpec)
val keyPair = keyPairGenerator.genKeyPair()

val publicKey = keyPair.public as ECPublicKey
val publicKeyBytes = publicKey.encoded
val publicKeyBase64 = Base64.encodeToString(publicKeyBytes, Base64.DEFAULT)

// Return both public and private keys
return publicKeyBase64
} catch (e: Exception) {
e.printStackTrace()
throw Exception("Failed to extract public key from private key: ${e.localizedMessage}")
fun getPublicECDSAKey(privateKeyBase64: String): String {
return try {
// Decode the Base64 private key
val privateKeyBytes = Base64.decode(privateKeyBase64, Base64.DEFAULT)
val keyFactory = KeyFactory.getInstance("EC")

// Reconstruct the private key
val privateKeySpec = PKCS8EncodedKeySpec(privateKeyBytes)
val privateKey = keyFactory.generatePrivate(privateKeySpec) as ECPrivateKey

// Derive the public key from the private key using EC point multiplication
// Public key Q = d * G, where d is the private scalar and G is the generator point
val ecParameterSpec = privateKey.params
val d = privateKey.s // The private key scalar
val g = ecParameterSpec.generator // The generator point G
val p = (ecParameterSpec.curve.field as java.security.spec.ECFieldFp).p
val a = ecParameterSpec.curve.a

// Calculate Q = d * G using double-and-add algorithm
val q = ecPointMultiply(g, d, p, a)

// Create the public key from the calculated point
val publicKeySpec = ECPublicKeySpec(q, ecParameterSpec)
val publicKey = keyFactory.generatePublic(publicKeySpec) as ECPublicKey

Base64.encodeToString(publicKey.encoded, Base64.DEFAULT)
} catch (e: Exception) {
e.printStackTrace()
throw Exception("Failed to extract public key from private key: ${e.localizedMessage}")
}
}

/**
* Performs elliptic curve point multiplication using the double-and-add algorithm.
* Calculates result = k * point on the elliptic curve.
*/
private fun ecPointMultiply(
point: java.security.spec.ECPoint,
k: BigInteger,
p: BigInteger,
a: BigInteger
): java.security.spec.ECPoint {
var result = java.security.spec.ECPoint.POINT_INFINITY
var addend = point
var scalar = k

while (scalar != BigInteger.ZERO) {
if (scalar.testBit(0)) {
result = ecPointAdd(result, addend, p, a)
}
addend = ecPointDouble(addend, p, a)
scalar = scalar.shiftRight(1)
}

return result
}

/**
* Adds two points on the elliptic curve.
*/
private fun ecPointAdd(
p1: java.security.spec.ECPoint,
p2: java.security.spec.ECPoint,
p: BigInteger,
a: BigInteger
): java.security.spec.ECPoint {
if (p1 == java.security.spec.ECPoint.POINT_INFINITY) return p2
if (p2 == java.security.spec.ECPoint.POINT_INFINITY) return p1

val x1 = p1.affineX
val y1 = p1.affineY
val x2 = p2.affineX
val y2 = p2.affineY

// Check if points are the same (use point doubling)
if (x1 == x2 && y1 == y2) {
return ecPointDouble(p1, p, a)
}

// Check if points are inverses (result is point at infinity)
if (x1 == x2) {
return java.security.spec.ECPoint.POINT_INFINITY
}

// Point addition: lambda = (y2 - y1) / (x2 - x1) mod p
val deltaY = y2.subtract(y1).mod(p)
val deltaX = x2.subtract(x1).mod(p)
val lambda = deltaY.multiply(deltaX.modInverse(p)).mod(p)

// x3 = lambda^2 - x1 - x2 mod p
val x3 = lambda.multiply(lambda).subtract(x1).subtract(x2).mod(p)

// y3 = lambda * (x1 - x3) - y1 mod p
val y3 = lambda.multiply(x1.subtract(x3)).subtract(y1).mod(p)

return java.security.spec.ECPoint(x3, y3)
}

/**
* Doubles a point on the elliptic curve.
*/
private fun ecPointDouble(
point: java.security.spec.ECPoint,
p: BigInteger,
a: BigInteger
): java.security.spec.ECPoint {
if (point == java.security.spec.ECPoint.POINT_INFINITY) {
return point
}

val x = point.affineX
val y = point.affineY

// Check for point at infinity (y = 0)
if (y == BigInteger.ZERO) {
return java.security.spec.ECPoint.POINT_INFINITY
}

// Point doubling: lambda = (3 * x^2 + a) / (2 * y) mod p
val numerator = x.multiply(x).multiply(BigInteger.valueOf(3)).add(a).mod(p)
val denominator = y.multiply(BigInteger.valueOf(2)).mod(p)
val lambda = numerator.multiply(denominator.modInverse(p)).mod(p)

// x3 = lambda^2 - 2*x mod p
val x3 = lambda.multiply(lambda).subtract(x.multiply(BigInteger.valueOf(2))).mod(p)

// y3 = lambda * (x - x3) - y mod p
val y3 = lambda.multiply(x.subtract(x3)).subtract(y).mod(p)

return java.security.spec.ECPoint(x3, y3)
}
}

/**
* Signs data using an ECDSA private key.
Expand Down
Loading