Skip to content

Commit 1ea7991

Browse files
JOHNJOHN
authored andcommitted
feat: add ed25519Sign/Verify FFI bindings
1 parent 140c446 commit 1ea7991

File tree

21 files changed

+1408
-2
lines changed

21 files changed

+1408
-2
lines changed

generated-kotlin/uniffi/pubky_noise/pubky_noise.kt

Lines changed: 327 additions & 0 deletions
Large diffs are not rendered by default.

generated-swift/pubky_noise.swift

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1685,6 +1685,102 @@ public func deriveDeviceKey(seed: Data, deviceId: Data, epoch: UInt32)throws ->
16851685
)
16861686
})
16871687
}
1688+
/**
1689+
* Derive a full X25519 keypair from seed, device ID, and epoch.
1690+
*
1691+
* This is a convenience function that combines `derive_device_key` and
1692+
* `public_key_from_secret` to return both the secret and public keys.
1693+
*
1694+
* # Errors
1695+
*
1696+
* Returns `FfiNoiseError::Ring` if seed is less than 32 bytes.
1697+
* Returns `FfiNoiseError::Other` if key derivation fails.
1698+
*/
1699+
public func deriveDeviceKeypair(seed: Data, deviceId: Data, epoch: UInt32)throws -> FfiX25519Keypair {
1700+
return try FfiConverterTypeFfiX25519Keypair_lift(try rustCallWithError(FfiConverterTypeFfiNoiseError_lift) {
1701+
uniffi_pubky_noise_fn_func_derive_device_keypair(
1702+
FfiConverterData.lower(seed),
1703+
FfiConverterData.lower(deviceId),
1704+
FfiConverterUInt32.lower(epoch),$0
1705+
)
1706+
})
1707+
}
1708+
/**
1709+
* Derive noise seed from Ed25519 secret key using HKDF-SHA256.
1710+
*
1711+
* This is used to derive future X25519 epoch keys locally without
1712+
* needing to call Ring again. The seed is domain-separated and
1713+
* cannot be used for signing.
1714+
*
1715+
* HKDF parameters:
1716+
* - salt: "paykit-noise-seed-v1"
1717+
* - ikm: Ed25519 secret key (32 bytes)
1718+
* - info: device ID
1719+
* - output: 32 bytes
1720+
*
1721+
* # Arguments
1722+
*
1723+
* * `ed25519_secret_hex` - Ed25519 secret key as 64-char hex string (32 bytes)
1724+
* * `device_id_hex` - Device ID as hex string
1725+
*
1726+
* # Returns
1727+
*
1728+
* 64-character hex string of the 32-byte noise seed.
1729+
*
1730+
* # Errors
1731+
*
1732+
* Returns `FfiNoiseError::Ring` if input is invalid.
1733+
*/
1734+
public func deriveNoiseSeed(ed25519SecretHex: String, deviceIdHex: String)throws -> String {
1735+
return try FfiConverterString.lift(try rustCallWithError(FfiConverterTypeFfiNoiseError_lift) {
1736+
uniffi_pubky_noise_fn_func_derive_noise_seed(
1737+
FfiConverterString.lower(ed25519SecretHex),
1738+
FfiConverterString.lower(deviceIdHex),$0
1739+
)
1740+
})
1741+
}
1742+
/**
1743+
* Sign an arbitrary message with an Ed25519 secret key.
1744+
*
1745+
* # Arguments
1746+
*
1747+
* * `ed25519_secret_hex` - 64-character hex string of the 32-byte Ed25519 secret key
1748+
* * `message_hex` - Hex-encoded message bytes to sign
1749+
*
1750+
* # Returns
1751+
*
1752+
* 128-character hex string of the 64-byte Ed25519 signature.
1753+
*/
1754+
public func ed25519Sign(ed25519SecretHex: String, messageHex: String)throws -> String {
1755+
return try FfiConverterString.lift(try rustCallWithError(FfiConverterTypeFfiNoiseError_lift) {
1756+
uniffi_pubky_noise_fn_func_ed25519_sign(
1757+
FfiConverterString.lower(ed25519SecretHex),
1758+
FfiConverterString.lower(messageHex),$0
1759+
)
1760+
})
1761+
}
1762+
/**
1763+
* Verify an Ed25519 signature.
1764+
*
1765+
* # Arguments
1766+
*
1767+
* * `ed25519_public_hex` - 64-character hex string of the 32-byte Ed25519 public key
1768+
* * `message_hex` - Hex-encoded message bytes that were signed
1769+
* * `signature_hex` - 128-character hex string of the 64-byte signature
1770+
*
1771+
* # Returns
1772+
*
1773+
* `true` if the signature is valid, `false` otherwise.
1774+
*/
1775+
public func ed25519Verify(ed25519PublicHex: String, messageHex: String, signatureHex: String)throws -> Bool {
1776+
return try FfiConverterBool.lift(try rustCallWithError(FfiConverterTypeFfiNoiseError_lift) {
1777+
uniffi_pubky_noise_fn_func_ed25519_verify(
1778+
FfiConverterString.lower(ed25519PublicHex),
1779+
FfiConverterString.lower(messageHex),
1780+
FfiConverterString.lower(signatureHex),$0
1781+
)
1782+
})
1783+
}
16881784
/**
16891785
* Check if a JSON string looks like a sealed blob envelope.
16901786
*
@@ -1824,6 +1920,18 @@ private let initializationResult: InitializationResult = {
18241920
if (uniffi_pubky_noise_checksum_func_derive_device_key() != 53176) {
18251921
return InitializationResult.apiChecksumMismatch
18261922
}
1923+
if (uniffi_pubky_noise_checksum_func_derive_device_keypair() != 18334) {
1924+
return InitializationResult.apiChecksumMismatch
1925+
}
1926+
if (uniffi_pubky_noise_checksum_func_derive_noise_seed() != 52084) {
1927+
return InitializationResult.apiChecksumMismatch
1928+
}
1929+
if (uniffi_pubky_noise_checksum_func_ed25519_sign() != 64498) {
1930+
return InitializationResult.apiChecksumMismatch
1931+
}
1932+
if (uniffi_pubky_noise_checksum_func_ed25519_verify() != 14993) {
1933+
return InitializationResult.apiChecksumMismatch
1934+
}
18271935
if (uniffi_pubky_noise_checksum_func_is_sealed_blob() != 59485) {
18281936
return InitializationResult.apiChecksumMismatch
18291937
}

generated-swift/pubky_noiseFFI.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,26 @@ RustBuffer uniffi_pubky_noise_fn_func_default_config(RustCallStatus *_Nonnull ou
348348
RustBuffer uniffi_pubky_noise_fn_func_derive_device_key(RustBuffer seed, RustBuffer device_id, uint32_t epoch, RustCallStatus *_Nonnull out_status
349349
);
350350
#endif
351+
#ifndef UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_FN_FUNC_DERIVE_DEVICE_KEYPAIR
352+
#define UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_FN_FUNC_DERIVE_DEVICE_KEYPAIR
353+
RustBuffer uniffi_pubky_noise_fn_func_derive_device_keypair(RustBuffer seed, RustBuffer device_id, uint32_t epoch, RustCallStatus *_Nonnull out_status
354+
);
355+
#endif
356+
#ifndef UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_FN_FUNC_DERIVE_NOISE_SEED
357+
#define UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_FN_FUNC_DERIVE_NOISE_SEED
358+
RustBuffer uniffi_pubky_noise_fn_func_derive_noise_seed(RustBuffer ed25519_secret_hex, RustBuffer device_id_hex, RustCallStatus *_Nonnull out_status
359+
);
360+
#endif
361+
#ifndef UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_FN_FUNC_ED25519_SIGN
362+
#define UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_FN_FUNC_ED25519_SIGN
363+
RustBuffer uniffi_pubky_noise_fn_func_ed25519_sign(RustBuffer ed25519_secret_hex, RustBuffer message_hex, RustCallStatus *_Nonnull out_status
364+
);
365+
#endif
366+
#ifndef UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_FN_FUNC_ED25519_VERIFY
367+
#define UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_FN_FUNC_ED25519_VERIFY
368+
int8_t uniffi_pubky_noise_fn_func_ed25519_verify(RustBuffer ed25519_public_hex, RustBuffer message_hex, RustBuffer signature_hex, RustCallStatus *_Nonnull out_status
369+
);
370+
#endif
351371
#ifndef UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_FN_FUNC_IS_SEALED_BLOB
352372
#define UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_FN_FUNC_IS_SEALED_BLOB
353373
int8_t uniffi_pubky_noise_fn_func_is_sealed_blob(RustBuffer json, RustCallStatus *_Nonnull out_status
@@ -681,6 +701,30 @@ uint16_t uniffi_pubky_noise_checksum_func_default_config(void
681701
#define UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_CHECKSUM_FUNC_DERIVE_DEVICE_KEY
682702
uint16_t uniffi_pubky_noise_checksum_func_derive_device_key(void
683703

704+
);
705+
#endif
706+
#ifndef UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_CHECKSUM_FUNC_DERIVE_DEVICE_KEYPAIR
707+
#define UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_CHECKSUM_FUNC_DERIVE_DEVICE_KEYPAIR
708+
uint16_t uniffi_pubky_noise_checksum_func_derive_device_keypair(void
709+
710+
);
711+
#endif
712+
#ifndef UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_CHECKSUM_FUNC_DERIVE_NOISE_SEED
713+
#define UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_CHECKSUM_FUNC_DERIVE_NOISE_SEED
714+
uint16_t uniffi_pubky_noise_checksum_func_derive_noise_seed(void
715+
716+
);
717+
#endif
718+
#ifndef UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_CHECKSUM_FUNC_ED25519_SIGN
719+
#define UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_CHECKSUM_FUNC_ED25519_SIGN
720+
uint16_t uniffi_pubky_noise_checksum_func_ed25519_sign(void
721+
722+
);
723+
#endif
724+
#ifndef UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_CHECKSUM_FUNC_ED25519_VERIFY
725+
#define UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_CHECKSUM_FUNC_ED25519_VERIFY
726+
uint16_t uniffi_pubky_noise_checksum_func_ed25519_verify(void
727+
684728
);
685729
#endif
686730
#ifndef UNIFFI_FFIDEF_UNIFFI_PUBKY_NOISE_CHECKSUM_FUNC_IS_SEALED_BLOB

platforms/android/src/main/java/uniffi/pubky_noise/pubky_noise.kt

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,10 @@ internal interface UniffiForeignFutureCompleteVoid : com.sun.jna.Callback {
759759

760760

761761

762+
763+
764+
765+
762766

763767

764768

@@ -787,6 +791,10 @@ fun uniffi_pubky_noise_checksum_func_derive_device_key(
787791
): Short
788792
fun uniffi_pubky_noise_checksum_func_derive_device_keypair(
789793
): Short
794+
fun uniffi_pubky_noise_checksum_func_ed25519_sign(
795+
): Short
796+
fun uniffi_pubky_noise_checksum_func_ed25519_verify(
797+
): Short
790798
fun uniffi_pubky_noise_checksum_func_is_sealed_blob(
791799
): Short
792800
fun uniffi_pubky_noise_checksum_func_performance_config(
@@ -918,6 +926,10 @@ fun uniffi_pubky_noise_fn_func_derive_device_key(`seed`: RustBuffer.ByValue,`dev
918926
): RustBuffer.ByValue
919927
fun uniffi_pubky_noise_fn_func_derive_device_keypair(`seed`: RustBuffer.ByValue,`deviceId`: RustBuffer.ByValue,`epoch`: Int,uniffi_out_err: UniffiRustCallStatus,
920928
): RustBuffer.ByValue
929+
fun uniffi_pubky_noise_fn_func_ed25519_sign(`ed25519SecretHex`: RustBuffer.ByValue,`messageHex`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
930+
): RustBuffer.ByValue
931+
fun uniffi_pubky_noise_fn_func_ed25519_verify(`ed25519PublicHex`: RustBuffer.ByValue,`messageHex`: RustBuffer.ByValue,`signatureHex`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
932+
): Byte
921933
fun uniffi_pubky_noise_fn_func_is_sealed_blob(`json`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
922934
): Byte
923935
fun uniffi_pubky_noise_fn_func_performance_config(uniffi_out_err: UniffiRustCallStatus,
@@ -1070,6 +1082,12 @@ private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) {
10701082
if (lib.uniffi_pubky_noise_checksum_func_derive_device_keypair() != 18334.toShort()) {
10711083
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
10721084
}
1085+
if (lib.uniffi_pubky_noise_checksum_func_ed25519_sign() != 64498.toShort()) {
1086+
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
1087+
}
1088+
if (lib.uniffi_pubky_noise_checksum_func_ed25519_verify() != 14993.toShort()) {
1089+
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
1090+
}
10731091
if (lib.uniffi_pubky_noise_checksum_func_is_sealed_blob() != 59485.toShort()) {
10741092
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
10751093
}
@@ -2692,6 +2710,51 @@ public object FfiConverterSequenceString: FfiConverterRustBuffer<List<kotlin.Str
26922710
}
26932711

26942712

2713+
/**
2714+
* Sign an arbitrary message with an Ed25519 secret key.
2715+
*
2716+
* # Arguments
2717+
*
2718+
* * `ed25519_secret_hex` - 64-character hex string of the 32-byte Ed25519 secret key
2719+
* * `message_hex` - Hex-encoded message bytes to sign
2720+
*
2721+
* # Returns
2722+
*
2723+
* 128-character hex string of the 64-byte Ed25519 signature.
2724+
*/
2725+
@Throws(FfiNoiseException::class) fun `ed25519Sign`(`ed25519SecretHex`: kotlin.String, `messageHex`: kotlin.String): kotlin.String {
2726+
return FfiConverterString.lift(
2727+
uniffiRustCallWithError(FfiNoiseException) { _status ->
2728+
UniffiLib.INSTANCE.uniffi_pubky_noise_fn_func_ed25519_sign(
2729+
FfiConverterString.lower(`ed25519SecretHex`),FfiConverterString.lower(`messageHex`),_status)
2730+
}
2731+
)
2732+
}
2733+
2734+
2735+
/**
2736+
* Verify an Ed25519 signature.
2737+
*
2738+
* # Arguments
2739+
*
2740+
* * `ed25519_public_hex` - 64-character hex string of the 32-byte Ed25519 public key
2741+
* * `message_hex` - Hex-encoded message bytes that were signed
2742+
* * `signature_hex` - 128-character hex string of the 64-byte signature
2743+
*
2744+
* # Returns
2745+
*
2746+
* `true` if the signature is valid, `false` otherwise.
2747+
*/
2748+
@Throws(FfiNoiseException::class) fun `ed25519Verify`(`ed25519PublicHex`: kotlin.String, `messageHex`: kotlin.String, `signatureHex`: kotlin.String): kotlin.Boolean {
2749+
return FfiConverterBoolean.lift(
2750+
uniffiRustCallWithError(FfiNoiseException) { _status ->
2751+
UniffiLib.INSTANCE.uniffi_pubky_noise_fn_func_ed25519_verify(
2752+
FfiConverterString.lower(`ed25519PublicHex`),FfiConverterString.lower(`messageHex`),FfiConverterString.lower(`signatureHex`),_status)
2753+
}
2754+
)
2755+
}
2756+
2757+
26952758
/**
26962759
* Check if a JSON string looks like a sealed blob envelope.
26972760
*
11.2 KB
Binary file not shown.
11.6 KB
Binary file not shown.
14.8 KB
Binary file not shown.

0 commit comments

Comments
 (0)