Skip to content

Commit

Permalink
Update notary private key implementation (#128)
Browse files Browse the repository at this point in the history
* Update notary private key implementation

* Address PR comments

* Add exception annotation to sign method

* Version bump
  • Loading branch information
sergiupuhalschi-rdx authored May 9, 2024
1 parent d584767 commit 0c46ed7
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 87 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "sargon"
version = "0.7.3"
version = "0.7.8"
edition = "2021"
build = "build.rs"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.radixdlt.sargon.extensions

import com.radixdlt.sargon.Ed25519Signature
import com.radixdlt.sargon.Exactly32Bytes
import com.radixdlt.sargon.Hash
import com.radixdlt.sargon.NotarySignature
import com.radixdlt.sargon.PublicKey
import com.radixdlt.sargon.SignedIntentHash
import com.radixdlt.sargon.androidNotarizeHashWithPrivateKeyBytes
import com.radixdlt.sargon.androidSecretKeyGetPublicKeyFromPrivateKeyBytes
import com.radixdlt.sargon.androidSignHashWithPrivateKeyBytes
import com.radixdlt.sargon.annotation.KoverIgnore

class Curve25519SecretKey(
private val exactly32Bytes: Exactly32Bytes
) {

companion object {
fun secureRandom(): Curve25519SecretKey =
Curve25519SecretKey(exactly32Bytes = Exactly32Bytes.init(randomBagOfBytes(32)))
}

@Throws(SargonException::class)
fun notarize(signedIntentHash: SignedIntentHash): NotarySignature =
androidNotarizeHashWithPrivateKeyBytes(
privateKeyBytes = exactly32Bytes,
signedIntentHash = signedIntentHash
)

@Throws(SargonException::class)
fun sign(hash: Hash): Ed25519Signature =
androidSignHashWithPrivateKeyBytes(
privateKeyBytes = exactly32Bytes,
hash = hash
)

@Throws(SargonException::class)
fun toPublicKey(): PublicKey.Ed25519 =
androidSecretKeyGetPublicKeyFromPrivateKeyBytes(privateKeyBytes = exactly32Bytes).asGeneral()

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as Curve25519SecretKey

return exactly32Bytes == other.exactly32Bytes
}

override fun hashCode(): Int {
return exactly32Bytes.hashCode()
}

@KoverIgnore
override fun toString(): String {
return "Curve25519SecretKey(exactly32Bytes=$exactly32Bytes)"
}


}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.radixdlt.sargon.samples

import com.radixdlt.sargon.Exactly32Bytes
import com.radixdlt.sargon.annotation.UsesSampleValues
import com.radixdlt.sargon.newExactly32BytesSample
import com.radixdlt.sargon.newExactly32BytesSampleOther

@UsesSampleValues
val Exactly32Bytes.Companion.sample: Sample<Exactly32Bytes>
get() = object : Sample<Exactly32Bytes> {
override fun invoke(): Exactly32Bytes = newExactly32BytesSample()

override fun other(): Exactly32Bytes = newExactly32BytesSampleOther()
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
package com.radixdlt.sargon

import com.radixdlt.sargon.extensions.NotaryPrivateKey
import com.radixdlt.sargon.extensions.Curve25519SecretKey
import com.radixdlt.sargon.extensions.hex
import com.radixdlt.sargon.extensions.signature
import com.radixdlt.sargon.extensions.string
import com.radixdlt.sargon.samples.sample
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Test

class NotaryPrivateKeyTest {
class Curve25519SecretKeyTest {

@Test
fun testRandomness() {
val privateKeysCount = 100

val privateKeys = List(privateKeysCount) {
NotaryPrivateKey.secureRandom()
Curve25519SecretKey.secureRandom()
}

assertEquals(
Expand All @@ -28,8 +27,8 @@ class NotaryPrivateKeyTest {

@Test
fun testEquality() {
val thisNotary = NotaryPrivateKey(Entropy32Bytes.sample())
val otherNotary = NotaryPrivateKey(Entropy32Bytes.sample())
val thisNotary = Curve25519SecretKey(Exactly32Bytes.sample())
val otherNotary = Curve25519SecretKey(Exactly32Bytes.sample())

assertEquals(thisNotary, otherNotary)
assertEquals(thisNotary, thisNotary)
Expand All @@ -38,21 +37,32 @@ class NotaryPrivateKeyTest {

@Test
fun testNotarize() {
val sut = NotaryPrivateKey(Entropy32Bytes.sample())
val sut = Curve25519SecretKey(Exactly32Bytes.sample())
val result = sut.notarize(SignedIntentHash.sample())

assertEquals(
"08c6129fa6938a31e38dfe94effdce8f1a4021e22cf62344830d83dc45f32de0e3d112794c369450e62d245a17b18835f40c639033fbb4b1f975ad0ad71dbf0a",
"1a30347a04bc5d746b35a568330ba69c9b6ac60ef72d0a28cb63e25680e64908557d85a0e864c423ce782b5f43da3002c301045c6385b40cb013374045392404",
result.signature.string
)
}

@Test
fun testSign() {
val sut = Curve25519SecretKey(Exactly32Bytes.sample())
val result = sut.sign(Hash.sample())

assertEquals(
"1a30347a04bc5d746b35a568330ba69c9b6ac60ef72d0a28cb63e25680e64908557d85a0e864c423ce782b5f43da3002c301045c6385b40cb013374045392404",
result.bytes.hex
)
}

@Test
fun testGetPublicKey() {
val sut = NotaryPrivateKey(Entropy32Bytes.sample())
val sut = Curve25519SecretKey(Exactly32Bytes.sample())

Assertions.assertEquals(
"248acbdbaf9e050196de704bea2d68770e519150d103b587dae2d9cad53dd930",
assertEquals(
"3b321b74bdcb169f7260c60592bbb63d9b4d629424a0c58aff4640a75f0a2b06",
sut.toPublicKey().hex
)
}
Expand Down
24 changes: 8 additions & 16 deletions src/core/types/keys/ed25519/public_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,11 @@ pub fn new_ed25519_public_key_sample_other() -> Ed25519PublicKey {
}

#[uniffi::export]
pub fn android_notary_key_get_public_key_from_private_key_bytes(
private_key_bytes: Entropy32Bytes,
pub fn android_secret_key_get_public_key_from_private_key_bytes(
private_key_bytes: Exactly32Bytes,
) -> Result<Ed25519PublicKey> {
let mut private_key_bytes = private_key_bytes;

let private_key =
Ed25519PrivateKey::from_bytes(private_key_bytes.to_bytes())?;
let public_key = private_key.public_key();

private_key_bytes.zeroize();
// private_key.zeroize() // FIXME: Zeroize once RET has added Zeroize to PrivateKeys

Ok(public_key)
Ed25519PrivateKey::try_from(private_key_bytes.as_ref())
.map(|k| k.public_key())
}

/// Encodes the `Ed25519PublicKey` to a hexadecimal string, lowercased, without any `0x` prefix, e.g.
Expand Down Expand Up @@ -410,15 +402,15 @@ mod uniffi_tests {
}

#[test]
fn test_android_notary_key_get_public_key_from_private_key_bytes() {
let entropy = Entropy32Bytes::sample();
fn test_android_secret_key_get_public_key_from_private_key_bytes() {
let entropy = Exactly32Bytes::sample();

let sut =
android_notary_key_get_public_key_from_private_key_bytes(entropy)
android_secret_key_get_public_key_from_private_key_bytes(entropy)
.unwrap();

assert_eq!(
"248acbdbaf9e050196de704bea2d68770e519150d103b587dae2d9cad53dd930",
"3b321b74bdcb169f7260c60592bbb63d9b4d629424a0c58aff4640a75f0a2b06",
sut.to_hex()
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,27 @@ pub fn notary_signature_get_signature(

#[uniffi::export]
pub fn android_notarize_hash_with_private_key_bytes(
private_key_bytes: Entropy32Bytes,
private_key_bytes: Exactly32Bytes,
signed_intent_hash: &SignedIntentHash,
) -> Result<NotarySignature> {
let mut private_key_bytes = private_key_bytes;

let ed25519_private_key =
Ed25519PrivateKey::from_bytes(private_key_bytes.to_bytes())?;
Ed25519PrivateKey::try_from(private_key_bytes.as_ref())?;

let private_key = PrivateKey::from(ed25519_private_key);
let signature = private_key.notarize_hash(signed_intent_hash);
private_key_bytes.zeroize();
// private_key.zeroize() // FIXME: Zeroize once RET has added Zeroize to PrivateKeys

Ok(signature)
}

#[uniffi::export]
pub fn android_sign_hash_with_private_key_bytes(
private_key_bytes: Exactly32Bytes,
hash: &Hash,
) -> Result<Ed25519Signature> {
Ed25519PrivateKey::try_from(private_key_bytes.as_ref())
.map(|pk| pk.sign(hash))
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -74,13 +79,27 @@ mod tests {
#[test]
fn test_android_notarize_hash_with_private_key_bytes() {
let sut = android_notarize_hash_with_private_key_bytes(
Entropy32Bytes::sample(),
Exactly32Bytes::sample(),
&SignedIntentHash::sample(),
)
.unwrap();

assert_eq!(
"08c6129fa6938a31e38dfe94effdce8f1a4021e22cf62344830d83dc45f32de0e3d112794c369450e62d245a17b18835f40c639033fbb4b1f975ad0ad71dbf0a",
"1a30347a04bc5d746b35a568330ba69c9b6ac60ef72d0a28cb63e25680e64908557d85a0e864c423ce782b5f43da3002c301045c6385b40cb013374045392404",
sut.to_string()
)
}

#[test]
fn test_android_sign_hash_with_private_key_bytes() {
let sut = android_sign_hash_with_private_key_bytes(
Exactly32Bytes::sample(),
&Hash::sample(),
)
.unwrap();

assert_eq!(
"1a30347a04bc5d746b35a568330ba69c9b6ac60ef72d0a28cb63e25680e64908557d85a0e864c423ce782b5f43da3002c301045c6385b40cb013374045392404",
sut.to_string()
)
}
Expand Down

0 comments on commit 0c46ed7

Please sign in to comment.