diff --git a/bindings/go/examples/address_from_mnemonic/main.go b/bindings/go/examples/address_from_mnemonic/main.go new file mode 100644 index 000000000..9fda779ea --- /dev/null +++ b/bindings/go/examples/address_from_mnemonic/main.go @@ -0,0 +1,70 @@ +// Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "fmt" + "log" + + sdk "bindings/iota_sdk_ffi" +) + +const MNEMONIC = "round attack kitchen wink winter music trip tiny nephew hire orange what" + +func main() { + privateKeyEd25519, err := sdk.Ed25519PrivateKeyFromMnemonic(MNEMONIC, 0, "") + if err != nil { + log.Fatalf("Failed to get key from mnemonic: %v", err) + } + privateKeyEd25519Bech32, err := privateKeyEd25519.ToBech32() + if err != nil { + log.Fatalf("Failed to convert to bech32: %v", err) + } + publicKeyEd25519 := privateKeyEd25519.PublicKey() + flaggedPublicKeyEd25519 := publicKeyEd25519.ToFlaggedBytes() + addressEd25519 := publicKeyEd25519.DeriveAddress() + + fmt.Println("Ed25519\n---:") + fmt.Println("Private Key:", privateKeyEd25519Bech32) + fmt.Println("Public Key:", sdk.Base64Encode(publicKeyEd25519.ToBytes())) + fmt.Println("Public Key With Flag:", sdk.Base64Encode(flaggedPublicKeyEd25519)) + fmt.Println("Address:", addressEd25519.ToHex()) + + privateKeySecp256k1, err := sdk.Secp256k1PrivateKeyFromMnemonic(MNEMONIC, 1, "my_password") + if err != nil { + log.Fatalf("Failed to get key from mnemonic: %v", err) + } + privateKeySecp256k1Bech32, err := privateKeySecp256k1.ToBech32() + if err != nil { + log.Fatalf("Failed to convert to bech32: %v", err) + } + publicKeySecp256k1 := privateKeySecp256k1.PublicKey() + flaggedPublicKeySecp256k1 := publicKeySecp256k1.ToFlaggedBytes() + addressSecp256k1 := publicKeySecp256k1.DeriveAddress() + + fmt.Println("\nSecp256k1\n---:") + fmt.Println("Private Key:", privateKeySecp256k1Bech32) + fmt.Println("Public Key:", sdk.Base64Encode(publicKeySecp256k1.ToBytes())) + fmt.Println("Public Key With Flag:", sdk.Base64Encode(flaggedPublicKeySecp256k1)) + fmt.Println("Address:", addressSecp256k1.ToHex()) + + privateKeySecp256r1, err := sdk.Secp256r1PrivateKeyFromMnemonicWithPath(MNEMONIC, "m/74'/4218'/0'/0/2", "") + if err != nil { + log.Fatalf("Failed to get key from mnemonic: %v", err) + } + privateKeySecp256r1Bech32, err := privateKeySecp256r1.ToBech32() + if err != nil { + log.Fatalf("Failed to convert to bech32: %v", err) + } + publicKeySecp256r1 := privateKeySecp256r1.PublicKey() + flaggedPublicKeySecp256r1 := publicKeySecp256r1.ToFlaggedBytes() + addressSecp256r1 := publicKeySecp256r1.DeriveAddress() + + fmt.Println("\nSecp256r1\n---:") + fmt.Println("Private Key:", privateKeySecp256r1Bech32) + fmt.Println("Public Key:", sdk.Base64Encode(publicKeySecp256r1.ToBytes())) + fmt.Println("Public Key With Flag:", sdk.Base64Encode(flaggedPublicKeySecp256r1)) + fmt.Println("Address:", addressSecp256r1.ToHex()) + +} diff --git a/bindings/go/iota_sdk_ffi/iota_sdk_ffi.go b/bindings/go/iota_sdk_ffi/iota_sdk_ffi.go index 3608c3799..bac382692 100644 --- a/bindings/go/iota_sdk_ffi/iota_sdk_ffi.go +++ b/bindings/go/iota_sdk_ffi/iota_sdk_ffi.go @@ -7163,6 +7163,24 @@ func uniffiCheckChecksums() { } } { + checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_mnemonic() + }) + if checksum != 55789 { + // If this happens try cleaning and rebuilding your project + panic("iota_sdk_ffi: uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_mnemonic: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_mnemonic_with_path() + }) + if checksum != 15255 { + // If this happens try cleaning and rebuilding your project + panic("iota_sdk_ffi: uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_mnemonic_with_path: UniFFI API checksum mismatch") + } + } + { checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t { return C.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_pem() }) @@ -8405,6 +8423,24 @@ func uniffiCheckChecksums() { } } { + checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_mnemonic() + }) + if checksum != 33082 { + // If this happens try cleaning and rebuilding your project + panic("iota_sdk_ffi: uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_mnemonic: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_mnemonic_with_path() + }) + if checksum != 7431 { + // If this happens try cleaning and rebuilding your project + panic("iota_sdk_ffi: uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_mnemonic_with_path: UniFFI API checksum mismatch") + } + } + { checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t { return C.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_pem() }) @@ -8540,6 +8576,24 @@ func uniffiCheckChecksums() { } } { + checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_mnemonic() + }) + if checksum != 57849 { + // If this happens try cleaning and rebuilding your project + panic("iota_sdk_ffi: uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_mnemonic: UniFFI API checksum mismatch") + } + } + { + checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_mnemonic_with_path() + }) + if checksum != 7709 { + // If this happens try cleaning and rebuilding your project + panic("iota_sdk_ffi: uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_mnemonic_with_path: UniFFI API checksum mismatch") + } + } + { checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t { return C.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_pem() }) @@ -13009,6 +13063,33 @@ func Ed25519PrivateKeyFromDer(bytes []byte) (*Ed25519PrivateKey, error) { } } +// Construct the private key from a mnemonic phrase +func Ed25519PrivateKeyFromMnemonic(phrase string, accountIndex uint64, password string) (*Ed25519PrivateKey, error) { + _uniffiRV, _uniffiErr := rustCallWithError[SdkFfiError](FfiConverterSdkFfiError{},func(_uniffiStatus *C.RustCallStatus) unsafe.Pointer { + return C.uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_mnemonic(FfiConverterStringINSTANCE.Lower(phrase), FfiConverterUint64INSTANCE.Lower(accountIndex), FfiConverterStringINSTANCE.Lower(password),_uniffiStatus) + }) + if _uniffiErr != nil { + var _uniffiDefaultValue *Ed25519PrivateKey + return _uniffiDefaultValue, _uniffiErr + } else { + return FfiConverterEd25519PrivateKeyINSTANCE.Lift(_uniffiRV), nil + } +} + +// Create an instance from a mnemonic phrase and a derivation path like +// `"m/44'/4218'/0'/0'/0'"` +func Ed25519PrivateKeyFromMnemonicWithPath(phrase string, path string, password string) (*Ed25519PrivateKey, error) { + _uniffiRV, _uniffiErr := rustCallWithError[SdkFfiError](FfiConverterSdkFfiError{},func(_uniffiStatus *C.RustCallStatus) unsafe.Pointer { + return C.uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_mnemonic_with_path(FfiConverterStringINSTANCE.Lower(phrase), FfiConverterStringINSTANCE.Lower(path), FfiConverterStringINSTANCE.Lower(password),_uniffiStatus) + }) + if _uniffiErr != nil { + var _uniffiDefaultValue *Ed25519PrivateKey + return _uniffiDefaultValue, _uniffiErr + } else { + return FfiConverterEd25519PrivateKeyINSTANCE.Lift(_uniffiRV), nil + } +} + // Deserialize PKCS#8-encoded private key from PEM. func Ed25519PrivateKeyFromPem(s string) (*Ed25519PrivateKey, error) { _uniffiRV, _uniffiErr := rustCallWithError[SdkFfiError](FfiConverterSdkFfiError{},func(_uniffiStatus *C.RustCallStatus) unsafe.Pointer { @@ -21631,6 +21712,33 @@ func Secp256k1PrivateKeyFromDer(bytes []byte) (*Secp256k1PrivateKey, error) { } } +// Construct the private key from a mnemonic phrase +func Secp256k1PrivateKeyFromMnemonic(phrase string, accountIndex uint64, password string) (*Secp256k1PrivateKey, error) { + _uniffiRV, _uniffiErr := rustCallWithError[SdkFfiError](FfiConverterSdkFfiError{},func(_uniffiStatus *C.RustCallStatus) unsafe.Pointer { + return C.uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_mnemonic(FfiConverterStringINSTANCE.Lower(phrase), FfiConverterUint64INSTANCE.Lower(accountIndex), FfiConverterStringINSTANCE.Lower(password),_uniffiStatus) + }) + if _uniffiErr != nil { + var _uniffiDefaultValue *Secp256k1PrivateKey + return _uniffiDefaultValue, _uniffiErr + } else { + return FfiConverterSecp256k1PrivateKeyINSTANCE.Lift(_uniffiRV), nil + } +} + +// Create an instance from a mnemonic phrase and a derivation path like +// `"m/54'/4218'/0'/0/0"` +func Secp256k1PrivateKeyFromMnemonicWithPath(phrase string, path string, password string) (*Secp256k1PrivateKey, error) { + _uniffiRV, _uniffiErr := rustCallWithError[SdkFfiError](FfiConverterSdkFfiError{},func(_uniffiStatus *C.RustCallStatus) unsafe.Pointer { + return C.uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_mnemonic_with_path(FfiConverterStringINSTANCE.Lower(phrase), FfiConverterStringINSTANCE.Lower(path), FfiConverterStringINSTANCE.Lower(password),_uniffiStatus) + }) + if _uniffiErr != nil { + var _uniffiDefaultValue *Secp256k1PrivateKey + return _uniffiDefaultValue, _uniffiErr + } else { + return FfiConverterSecp256k1PrivateKeyINSTANCE.Lift(_uniffiRV), nil + } +} + // Deserialize PKCS#8-encoded private key from PEM. func Secp256k1PrivateKeyFromPem(s string) (*Secp256k1PrivateKey, error) { _uniffiRV, _uniffiErr := rustCallWithError[SdkFfiError](FfiConverterSdkFfiError{},func(_uniffiStatus *C.RustCallStatus) unsafe.Pointer { @@ -22473,6 +22581,33 @@ func Secp256r1PrivateKeyFromDer(bytes []byte) (*Secp256r1PrivateKey, error) { } } +// Construct the private key from a mnemonic phrase +func Secp256r1PrivateKeyFromMnemonic(phrase string, accountIndex uint64, password string) (*Secp256r1PrivateKey, error) { + _uniffiRV, _uniffiErr := rustCallWithError[SdkFfiError](FfiConverterSdkFfiError{},func(_uniffiStatus *C.RustCallStatus) unsafe.Pointer { + return C.uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_mnemonic(FfiConverterStringINSTANCE.Lower(phrase), FfiConverterUint64INSTANCE.Lower(accountIndex), FfiConverterStringINSTANCE.Lower(password),_uniffiStatus) + }) + if _uniffiErr != nil { + var _uniffiDefaultValue *Secp256r1PrivateKey + return _uniffiDefaultValue, _uniffiErr + } else { + return FfiConverterSecp256r1PrivateKeyINSTANCE.Lift(_uniffiRV), nil + } +} + +// Create an instance from a mnemonic phrase and a derivation path like +// `"m/74'/4218'/0'/0/0"` +func Secp256r1PrivateKeyFromMnemonicWithPath(phrase string, path string, password string) (*Secp256r1PrivateKey, error) { + _uniffiRV, _uniffiErr := rustCallWithError[SdkFfiError](FfiConverterSdkFfiError{},func(_uniffiStatus *C.RustCallStatus) unsafe.Pointer { + return C.uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_mnemonic_with_path(FfiConverterStringINSTANCE.Lower(phrase), FfiConverterStringINSTANCE.Lower(path), FfiConverterStringINSTANCE.Lower(password),_uniffiStatus) + }) + if _uniffiErr != nil { + var _uniffiDefaultValue *Secp256r1PrivateKey + return _uniffiDefaultValue, _uniffiErr + } else { + return FfiConverterSecp256r1PrivateKeyINSTANCE.Lift(_uniffiRV), nil + } +} + // Deserialize PKCS#8-encoded private key from PEM. func Secp256r1PrivateKeyFromPem(s string) (*Secp256r1PrivateKey, error) { _uniffiRV, _uniffiErr := rustCallWithError[SdkFfiError](FfiConverterSdkFfiError{},func(_uniffiStatus *C.RustCallStatus) unsafe.Pointer { diff --git a/bindings/go/iota_sdk_ffi/iota_sdk_ffi.h b/bindings/go/iota_sdk_ffi/iota_sdk_ffi.h index 936c2eb9c..367936104 100644 --- a/bindings/go/iota_sdk_ffi/iota_sdk_ffi.h +++ b/bindings/go/iota_sdk_ffi/iota_sdk_ffi.h @@ -1168,6 +1168,16 @@ void* uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_bech32(RustBuffe void* uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_der(RustBuffer bytes, RustCallStatus *out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_ED25519PRIVATEKEY_FROM_MNEMONIC +#define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_ED25519PRIVATEKEY_FROM_MNEMONIC +void* uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_mnemonic(RustBuffer phrase, uint64_t account_index, RustBuffer password, RustCallStatus *out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_ED25519PRIVATEKEY_FROM_MNEMONIC_WITH_PATH +#define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_ED25519PRIVATEKEY_FROM_MNEMONIC_WITH_PATH +void* uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_mnemonic_with_path(RustBuffer phrase, RustBuffer path, RustBuffer password, RustCallStatus *out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_ED25519PRIVATEKEY_FROM_PEM #define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_ED25519PRIVATEKEY_FROM_PEM void* uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_pem(RustBuffer s, RustCallStatus *out_status @@ -3398,6 +3408,16 @@ void* uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_bech32(RustBuf void* uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_der(RustBuffer bytes, RustCallStatus *out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_SECP256K1PRIVATEKEY_FROM_MNEMONIC +#define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_SECP256K1PRIVATEKEY_FROM_MNEMONIC +void* uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_mnemonic(RustBuffer phrase, uint64_t account_index, RustBuffer password, RustCallStatus *out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_SECP256K1PRIVATEKEY_FROM_MNEMONIC_WITH_PATH +#define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_SECP256K1PRIVATEKEY_FROM_MNEMONIC_WITH_PATH +void* uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_mnemonic_with_path(RustBuffer phrase, RustBuffer path, RustBuffer password, RustCallStatus *out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_SECP256K1PRIVATEKEY_FROM_PEM #define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_SECP256K1PRIVATEKEY_FROM_PEM void* uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_pem(RustBuffer s, RustCallStatus *out_status @@ -3642,6 +3662,16 @@ void* uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_bech32(RustBuf void* uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_der(RustBuffer bytes, RustCallStatus *out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_SECP256R1PRIVATEKEY_FROM_MNEMONIC +#define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_SECP256R1PRIVATEKEY_FROM_MNEMONIC +void* uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_mnemonic(RustBuffer phrase, uint64_t account_index, RustBuffer password, RustCallStatus *out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_SECP256R1PRIVATEKEY_FROM_MNEMONIC_WITH_PATH +#define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_SECP256R1PRIVATEKEY_FROM_MNEMONIC_WITH_PATH +void* uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_mnemonic_with_path(RustBuffer phrase, RustBuffer path, RustBuffer password, RustCallStatus *out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_SECP256R1PRIVATEKEY_FROM_PEM #define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_FN_CONSTRUCTOR_SECP256R1PRIVATEKEY_FROM_PEM void* uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_pem(RustBuffer s, RustCallStatus *out_status @@ -11467,6 +11497,18 @@ uint16_t uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_bech32( #define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_ED25519PRIVATEKEY_FROM_DER uint16_t uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_der(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_ED25519PRIVATEKEY_FROM_MNEMONIC +#define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_ED25519PRIVATEKEY_FROM_MNEMONIC +uint16_t uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_mnemonic(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_ED25519PRIVATEKEY_FROM_MNEMONIC_WITH_PATH +#define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_ED25519PRIVATEKEY_FROM_MNEMONIC_WITH_PATH +uint16_t uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_mnemonic_with_path(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_ED25519PRIVATEKEY_FROM_PEM @@ -12295,6 +12337,18 @@ uint16_t uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_bech3 #define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_SECP256K1PRIVATEKEY_FROM_DER uint16_t uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_der(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_SECP256K1PRIVATEKEY_FROM_MNEMONIC +#define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_SECP256K1PRIVATEKEY_FROM_MNEMONIC +uint16_t uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_mnemonic(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_SECP256K1PRIVATEKEY_FROM_MNEMONIC_WITH_PATH +#define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_SECP256K1PRIVATEKEY_FROM_MNEMONIC_WITH_PATH +uint16_t uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_mnemonic_with_path(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_SECP256K1PRIVATEKEY_FROM_PEM @@ -12385,6 +12439,18 @@ uint16_t uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_bech3 #define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_SECP256R1PRIVATEKEY_FROM_DER uint16_t uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_der(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_SECP256R1PRIVATEKEY_FROM_MNEMONIC +#define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_SECP256R1PRIVATEKEY_FROM_MNEMONIC +uint16_t uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_mnemonic(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_SECP256R1PRIVATEKEY_FROM_MNEMONIC_WITH_PATH +#define UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_SECP256R1PRIVATEKEY_FROM_MNEMONIC_WITH_PATH +uint16_t uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_mnemonic_with_path(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IOTA_SDK_FFI_CHECKSUM_CONSTRUCTOR_SECP256R1PRIVATEKEY_FROM_PEM diff --git a/bindings/kotlin/examples/AddressFromMnemonic.kt b/bindings/kotlin/examples/AddressFromMnemonic.kt new file mode 100644 index 000000000..fcdd8d9e5 --- /dev/null +++ b/bindings/kotlin/examples/AddressFromMnemonic.kt @@ -0,0 +1,49 @@ +// Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import iota_sdk.Ed25519PrivateKey +import iota_sdk.Secp256k1PrivateKey +import iota_sdk.Secp256r1PrivateKey +import iota_sdk.base64Encode +import kotlin.io.println + +const val MNEMONIC = "round attack kitchen wink winter music trip tiny nephew hire orange what" + +fun main() { + val privateKeyEd25519 = Ed25519PrivateKey.fromMnemonic(MNEMONIC) + val privateKeyEd25519Bech32 = privateKeyEd25519.toBech32() + val publicKeyEd25519 = privateKeyEd25519.publicKey() + val flaggedPublicKeyEd25519 = publicKeyEd25519.toFlaggedBytes() + val addressEd25519 = publicKeyEd25519.deriveAddress() + + println("Ed25519\n---") + println("Private Key: ${privateKeyEd25519Bech32}") + println("Public Key: ${base64Encode(publicKeyEd25519.toBytes())}") + println("Public Key With Flag: ${base64Encode(flaggedPublicKeyEd25519)}") + println("Address: ${addressEd25519.toHex()}") + + val privateKeySecp256k1 = Secp256k1PrivateKey.fromMnemonic(MNEMONIC, 1uL) + val privateKeySecp256k1Bech32 = privateKeySecp256k1.toBech32() + val publicKeySecp256k1 = privateKeySecp256k1.publicKey() + val flaggedPublicKeySecp256k1 = publicKeySecp256k1.toFlaggedBytes() + val addressSecp256k1 = publicKeySecp256k1.deriveAddress() + + println("\nSecp256k1\n---") + println("Private Key: ${privateKeySecp256k1Bech32}") + println("Public Key: ${base64Encode(publicKeySecp256k1.toBytes())}") + println("Public Key With Flag: ${base64Encode(flaggedPublicKeySecp256k1)}") + println("Address: ${addressSecp256k1.toHex()}") + + val privateKeySecp256r1 = + Secp256r1PrivateKey.fromMnemonicWithPath(MNEMONIC, "m/74'/4218'/0'/0/2") + val privateKeySecp256r1Bech32 = privateKeySecp256r1.toBech32() + val publicKeySecp256r1 = privateKeySecp256r1.publicKey() + val flaggedPublicKeySecp256r1 = publicKeySecp256r1.toFlaggedBytes() + val addressSecp256r1 = publicKeySecp256r1.deriveAddress() + + println("\nSecp256r1\n---") + println("Private Key: ${privateKeySecp256r1Bech32}") + println("Public Key: ${base64Encode(publicKeySecp256r1.toBytes())}") + println("Public Key With Flag: ${base64Encode(flaggedPublicKeySecp256r1)}") + println("Address: ${addressSecp256r1.toHex()}") +} diff --git a/bindings/kotlin/lib/iota_sdk/iota_sdk_ffi.kt b/bindings/kotlin/lib/iota_sdk/iota_sdk_ffi.kt index 73e089bcd..2ab900211 100644 --- a/bindings/kotlin/lib/iota_sdk/iota_sdk_ffi.kt +++ b/bindings/kotlin/lib/iota_sdk/iota_sdk_ffi.kt @@ -2964,6 +2964,18 @@ internal interface UniffiForeignFutureCompleteVoid : com.sun.jna.Callback { + + + + + + + + + + + + @@ -4504,6 +4516,10 @@ fun uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_bech32( ): Short fun uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_der( ): Short +fun uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_mnemonic( +): Short +fun uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_mnemonic_with_path( +): Short fun uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_pem( ): Short fun uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_generate( @@ -4780,6 +4796,10 @@ fun uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_bech32( ): Short fun uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_der( ): Short +fun uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_mnemonic( +): Short +fun uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_mnemonic_with_path( +): Short fun uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_pem( ): Short fun uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_generate( @@ -4810,6 +4830,10 @@ fun uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_bech32( ): Short fun uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_der( ): Short +fun uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_mnemonic( +): Short +fun uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_mnemonic_with_path( +): Short fun uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_pem( ): Short fun uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_generate( @@ -5391,6 +5415,10 @@ fun uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_bech32(`value`: Ru ): Pointer fun uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_der(`bytes`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): Pointer +fun uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_mnemonic(`phrase`: RustBuffer.ByValue,`accountIndex`: Long,`password`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_mnemonic_with_path(`phrase`: RustBuffer.ByValue,`path`: RustBuffer.ByValue,`password`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Pointer fun uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_pem(`s`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): Pointer fun uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_generate(uniffi_out_err: UniffiRustCallStatus, @@ -6273,6 +6301,10 @@ fun uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_bech32(`value`: ): Pointer fun uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_der(`bytes`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): Pointer +fun uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_mnemonic(`phrase`: RustBuffer.ByValue,`accountIndex`: Long,`password`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_mnemonic_with_path(`phrase`: RustBuffer.ByValue,`path`: RustBuffer.ByValue,`password`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Pointer fun uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_pem(`s`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): Pointer fun uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_generate(uniffi_out_err: UniffiRustCallStatus, @@ -6369,6 +6401,10 @@ fun uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_bech32(`value`: ): Pointer fun uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_der(`bytes`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): Pointer +fun uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_mnemonic(`phrase`: RustBuffer.ByValue,`accountIndex`: Long,`password`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Pointer +fun uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_mnemonic_with_path(`phrase`: RustBuffer.ByValue,`path`: RustBuffer.ByValue,`password`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, +): Pointer fun uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_pem(`s`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, ): Pointer fun uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_generate(uniffi_out_err: UniffiRustCallStatus, @@ -9951,6 +9987,12 @@ private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) { if (lib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_der() != 42838.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } + if (lib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_mnemonic() != 55789.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_mnemonic_with_path() != 15255.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } if (lib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_pem() != 53776.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } @@ -10365,6 +10407,12 @@ private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) { if (lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_der() != 45448.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } + if (lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_mnemonic() != 33082.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_mnemonic_with_path() != 7431.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } if (lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_pem() != 20937.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } @@ -10410,6 +10458,12 @@ private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) { if (lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_der() != 63595.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } + if (lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_mnemonic() != 57849.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_mnemonic_with_path() != 7709.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } if (lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_pem() != 28166.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } @@ -18573,6 +18627,35 @@ open class Ed25519PrivateKey: Disposable, AutoCloseable, Ed25519PrivateKeyInterf + /** + * Construct the private key from a mnemonic phrase + */ + @Throws(SdkFfiException::class) fun `fromMnemonic`(`phrase`: kotlin.String, `accountIndex`: kotlin.ULong = 0uL, `password`: kotlin.String = ""): Ed25519PrivateKey { + return FfiConverterTypeEd25519PrivateKey.lift( + uniffiRustCallWithError(SdkFfiException) { _status -> + UniffiLib.INSTANCE.uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_mnemonic( + FfiConverterString.lower(`phrase`),FfiConverterULong.lower(`accountIndex`),FfiConverterString.lower(`password`),_status) +} + ) + } + + + + /** + * Create an instance from a mnemonic phrase and a derivation path like + * `"m/44'/4218'/0'/0'/0'"` + */ + @Throws(SdkFfiException::class) fun `fromMnemonicWithPath`(`phrase`: kotlin.String, `path`: kotlin.String, `password`: kotlin.String = ""): Ed25519PrivateKey { + return FfiConverterTypeEd25519PrivateKey.lift( + uniffiRustCallWithError(SdkFfiException) { _status -> + UniffiLib.INSTANCE.uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_mnemonic_with_path( + FfiConverterString.lower(`phrase`),FfiConverterString.lower(`path`),FfiConverterString.lower(`password`),_status) +} + ) + } + + + /** * Deserialize PKCS#8-encoded private key from PEM. */ @@ -35069,6 +35152,35 @@ open class Secp256k1PrivateKey: Disposable, AutoCloseable, Secp256k1PrivateKeyIn + /** + * Construct the private key from a mnemonic phrase + */ + @Throws(SdkFfiException::class) fun `fromMnemonic`(`phrase`: kotlin.String, `accountIndex`: kotlin.ULong = 0uL, `password`: kotlin.String = ""): Secp256k1PrivateKey { + return FfiConverterTypeSecp256k1PrivateKey.lift( + uniffiRustCallWithError(SdkFfiException) { _status -> + UniffiLib.INSTANCE.uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_mnemonic( + FfiConverterString.lower(`phrase`),FfiConverterULong.lower(`accountIndex`),FfiConverterString.lower(`password`),_status) +} + ) + } + + + + /** + * Create an instance from a mnemonic phrase and a derivation path like + * `"m/54'/4218'/0'/0/0"` + */ + @Throws(SdkFfiException::class) fun `fromMnemonicWithPath`(`phrase`: kotlin.String, `path`: kotlin.String, `password`: kotlin.String = ""): Secp256k1PrivateKey { + return FfiConverterTypeSecp256k1PrivateKey.lift( + uniffiRustCallWithError(SdkFfiException) { _status -> + UniffiLib.INSTANCE.uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_mnemonic_with_path( + FfiConverterString.lower(`phrase`),FfiConverterString.lower(`path`),FfiConverterString.lower(`password`),_status) +} + ) + } + + + /** * Deserialize PKCS#8-encoded private key from PEM. */ @@ -36816,6 +36928,35 @@ open class Secp256r1PrivateKey: Disposable, AutoCloseable, Secp256r1PrivateKeyIn + /** + * Construct the private key from a mnemonic phrase + */ + @Throws(SdkFfiException::class) fun `fromMnemonic`(`phrase`: kotlin.String, `accountIndex`: kotlin.ULong = 0uL, `password`: kotlin.String = ""): Secp256r1PrivateKey { + return FfiConverterTypeSecp256r1PrivateKey.lift( + uniffiRustCallWithError(SdkFfiException) { _status -> + UniffiLib.INSTANCE.uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_mnemonic( + FfiConverterString.lower(`phrase`),FfiConverterULong.lower(`accountIndex`),FfiConverterString.lower(`password`),_status) +} + ) + } + + + + /** + * Create an instance from a mnemonic phrase and a derivation path like + * `"m/74'/4218'/0'/0/0"` + */ + @Throws(SdkFfiException::class) fun `fromMnemonicWithPath`(`phrase`: kotlin.String, `path`: kotlin.String, `password`: kotlin.String = ""): Secp256r1PrivateKey { + return FfiConverterTypeSecp256r1PrivateKey.lift( + uniffiRustCallWithError(SdkFfiException) { _status -> + UniffiLib.INSTANCE.uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_mnemonic_with_path( + FfiConverterString.lower(`phrase`),FfiConverterString.lower(`path`),FfiConverterString.lower(`password`),_status) +} + ) + } + + + /** * Deserialize PKCS#8-encoded private key from PEM. */ diff --git a/bindings/python/examples/address_from_mnemonic.py b/bindings/python/examples/address_from_mnemonic.py new file mode 100644 index 000000000..a5fc982d6 --- /dev/null +++ b/bindings/python/examples/address_from_mnemonic.py @@ -0,0 +1,50 @@ +# Copyright (c) 2025 IOTA Stiftung +# SPDX-License-Identifier: Apache-2.0 + +from lib.iota_sdk_ffi import * + +MNEMONIC = "round attack kitchen wink winter music trip tiny nephew hire orange what" + + +def main(): + private_key = Ed25519PrivateKey.from_mnemonic(MNEMONIC) + private_key_bech32 = private_key.to_bech32() + public_key = private_key.public_key() + flagged_public_key = public_key.to_flagged_bytes() + address = public_key.derive_address() + + print(f"Ed25519\n---") + print(f"Private Key: {private_key_bech32}") + print(f"Public Key: {base64_encode(public_key.to_bytes())}") + print(f"Public Key With Flag: {base64_encode(flagged_public_key)}") + print(f"Address: {address.to_hex()}") + + private_key = Secp256k1PrivateKey.from_mnemonic(MNEMONIC, 1) + private_key_bech32 = private_key.to_bech32() + public_key = private_key.public_key() + flagged_public_key = public_key.to_flagged_bytes() + address = public_key.derive_address() + + print(f"\nSecp256k1\n---") + print(f"Private Key: {private_key_bech32}") + print(f"Public Key: {base64_encode(public_key.to_bytes())}") + print(f"Public Key With Flag: {base64_encode(flagged_public_key)}") + print(f"Address: {address.to_hex()}") + + private_key = Secp256r1PrivateKey.from_mnemonic_with_path( + MNEMONIC, "m/74'/4218'/0'/0/2" + ) + private_key_bech32 = private_key.to_bech32() + public_key = private_key.public_key() + flagged_public_key = public_key.to_flagged_bytes() + address = public_key.derive_address() + + print(f"\nSecp256r1\n---") + print(f"Private Key: {private_key_bech32}") + print(f"Public Key: {base64_encode(public_key.to_bytes())}") + print(f"Public Key With Flag: {base64_encode(flagged_public_key)}") + print(f"Address: {address.to_hex()}") + + +if __name__ == "__main__": + main() diff --git a/bindings/python/lib/iota_sdk_ffi.py b/bindings/python/lib/iota_sdk_ffi.py index 90579e954..f65c61837 100644 --- a/bindings/python/lib/iota_sdk_ffi.py +++ b/bindings/python/lib/iota_sdk_ffi.py @@ -1973,6 +1973,10 @@ def _uniffi_check_api_checksums(lib): raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") if lib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_der() != 42838: raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + if lib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_mnemonic() != 55789: + raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + if lib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_mnemonic_with_path() != 15255: + raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") if lib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_pem() != 53776: raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") if lib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_generate() != 53932: @@ -2249,6 +2253,10 @@ def _uniffi_check_api_checksums(lib): raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") if lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_der() != 45448: raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + if lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_mnemonic() != 33082: + raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + if lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_mnemonic_with_path() != 7431: + raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") if lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_pem() != 20937: raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") if lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_generate() != 49496: @@ -2279,6 +2287,10 @@ def _uniffi_check_api_checksums(lib): raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") if lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_der() != 63595: raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + if lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_mnemonic() != 57849: + raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + if lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_mnemonic_with_path() != 7709: + raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") if lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_pem() != 28166: raise InternalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project") if lib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_generate() != 47736: @@ -3420,6 +3432,20 @@ class _UniffiForeignFutureStructVoid(ctypes.Structure): ctypes.POINTER(_UniffiRustCallStatus), ) _UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_der.restype = ctypes.c_void_p +_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_mnemonic.argtypes = ( + _UniffiRustBuffer, + ctypes.c_uint64, + _UniffiRustBuffer, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_mnemonic.restype = ctypes.c_void_p +_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_mnemonic_with_path.argtypes = ( + _UniffiRustBuffer, + _UniffiRustBuffer, + _UniffiRustBuffer, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_mnemonic_with_path.restype = ctypes.c_void_p _UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_pem.argtypes = ( _UniffiRustBuffer, ctypes.POINTER(_UniffiRustCallStatus), @@ -5692,6 +5718,20 @@ class _UniffiForeignFutureStructVoid(ctypes.Structure): ctypes.POINTER(_UniffiRustCallStatus), ) _UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_der.restype = ctypes.c_void_p +_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_mnemonic.argtypes = ( + _UniffiRustBuffer, + ctypes.c_uint64, + _UniffiRustBuffer, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_mnemonic.restype = ctypes.c_void_p +_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_mnemonic_with_path.argtypes = ( + _UniffiRustBuffer, + _UniffiRustBuffer, + _UniffiRustBuffer, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_mnemonic_with_path.restype = ctypes.c_void_p _UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_pem.argtypes = ( _UniffiRustBuffer, ctypes.POINTER(_UniffiRustCallStatus), @@ -5941,6 +5981,20 @@ class _UniffiForeignFutureStructVoid(ctypes.Structure): ctypes.POINTER(_UniffiRustCallStatus), ) _UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_der.restype = ctypes.c_void_p +_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_mnemonic.argtypes = ( + _UniffiRustBuffer, + ctypes.c_uint64, + _UniffiRustBuffer, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_mnemonic.restype = ctypes.c_void_p +_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_mnemonic_with_path.argtypes = ( + _UniffiRustBuffer, + _UniffiRustBuffer, + _UniffiRustBuffer, + ctypes.POINTER(_UniffiRustCallStatus), +) +_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_mnemonic_with_path.restype = ctypes.c_void_p _UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_pem.argtypes = ( _UniffiRustBuffer, ctypes.POINTER(_UniffiRustCallStatus), @@ -11514,6 +11568,12 @@ class _UniffiForeignFutureStructVoid(ctypes.Structure): _UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_der.argtypes = ( ) _UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_der.restype = ctypes.c_uint16 +_UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_mnemonic.argtypes = ( +) +_UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_mnemonic.restype = ctypes.c_uint16 +_UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_mnemonic_with_path.argtypes = ( +) +_UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_mnemonic_with_path.restype = ctypes.c_uint16 _UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_pem.argtypes = ( ) _UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_ed25519privatekey_from_pem.restype = ctypes.c_uint16 @@ -11928,6 +11988,12 @@ class _UniffiForeignFutureStructVoid(ctypes.Structure): _UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_der.argtypes = ( ) _UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_der.restype = ctypes.c_uint16 +_UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_mnemonic.argtypes = ( +) +_UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_mnemonic.restype = ctypes.c_uint16 +_UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_mnemonic_with_path.argtypes = ( +) +_UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_mnemonic_with_path.restype = ctypes.c_uint16 _UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_pem.argtypes = ( ) _UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_secp256k1privatekey_from_pem.restype = ctypes.c_uint16 @@ -11973,6 +12039,12 @@ class _UniffiForeignFutureStructVoid(ctypes.Structure): _UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_der.argtypes = ( ) _UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_der.restype = ctypes.c_uint16 +_UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_mnemonic.argtypes = ( +) +_UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_mnemonic.restype = ctypes.c_uint16 +_UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_mnemonic_with_path.argtypes = ( +) +_UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_mnemonic_with_path.restype = ctypes.c_uint16 _UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_pem.argtypes = ( ) _UniffiLib.uniffi_iota_sdk_ffi_checksum_constructor_secp256r1privatekey_from_pem.restype = ctypes.c_uint16 @@ -29053,6 +29125,51 @@ def from_der(cls, bytes: "bytes"): _UniffiConverterBytes.lower(bytes)) return cls._make_instance_(pointer) + @classmethod + def from_mnemonic(cls, phrase: "str",account_index: "typing.Union[object, int]" = _DEFAULT,password: "typing.Union[object, str]" = _DEFAULT): + """ + Construct the private key from a mnemonic phrase + """ + + _UniffiConverterString.check_lower(phrase) + + if account_index is _DEFAULT: + account_index = 0 + _UniffiConverterUInt64.check_lower(account_index) + + if password is _DEFAULT: + password = "" + _UniffiConverterString.check_lower(password) + + # Call the (fallible) function before creating any half-baked object instances. + pointer = _uniffi_rust_call_with_error(_UniffiConverterTypeSdkFfiError,_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_mnemonic, + _UniffiConverterString.lower(phrase), + _UniffiConverterUInt64.lower(account_index), + _UniffiConverterString.lower(password)) + return cls._make_instance_(pointer) + + @classmethod + def from_mnemonic_with_path(cls, phrase: "str",path: "str",password: "typing.Union[object, str]" = _DEFAULT): + """ + Create an instance from a mnemonic phrase and a derivation path like + `"m/44'/4218'/0'/0'/0'"` + """ + + _UniffiConverterString.check_lower(phrase) + + _UniffiConverterString.check_lower(path) + + if password is _DEFAULT: + password = "" + _UniffiConverterString.check_lower(password) + + # Call the (fallible) function before creating any half-baked object instances. + pointer = _uniffi_rust_call_with_error(_UniffiConverterTypeSdkFfiError,_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_ed25519privatekey_from_mnemonic_with_path, + _UniffiConverterString.lower(phrase), + _UniffiConverterString.lower(path), + _UniffiConverterString.lower(password)) + return cls._make_instance_(pointer) + @classmethod def from_pem(cls, s: "str"): """ @@ -38208,6 +38325,51 @@ def from_der(cls, bytes: "bytes"): _UniffiConverterBytes.lower(bytes)) return cls._make_instance_(pointer) + @classmethod + def from_mnemonic(cls, phrase: "str",account_index: "typing.Union[object, int]" = _DEFAULT,password: "typing.Union[object, str]" = _DEFAULT): + """ + Construct the private key from a mnemonic phrase + """ + + _UniffiConverterString.check_lower(phrase) + + if account_index is _DEFAULT: + account_index = 0 + _UniffiConverterUInt64.check_lower(account_index) + + if password is _DEFAULT: + password = "" + _UniffiConverterString.check_lower(password) + + # Call the (fallible) function before creating any half-baked object instances. + pointer = _uniffi_rust_call_with_error(_UniffiConverterTypeSdkFfiError,_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_mnemonic, + _UniffiConverterString.lower(phrase), + _UniffiConverterUInt64.lower(account_index), + _UniffiConverterString.lower(password)) + return cls._make_instance_(pointer) + + @classmethod + def from_mnemonic_with_path(cls, phrase: "str",path: "str",password: "typing.Union[object, str]" = _DEFAULT): + """ + Create an instance from a mnemonic phrase and a derivation path like + `"m/54'/4218'/0'/0/0"` + """ + + _UniffiConverterString.check_lower(phrase) + + _UniffiConverterString.check_lower(path) + + if password is _DEFAULT: + password = "" + _UniffiConverterString.check_lower(password) + + # Call the (fallible) function before creating any half-baked object instances. + pointer = _uniffi_rust_call_with_error(_UniffiConverterTypeSdkFfiError,_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_secp256k1privatekey_from_mnemonic_with_path, + _UniffiConverterString.lower(phrase), + _UniffiConverterString.lower(path), + _UniffiConverterString.lower(password)) + return cls._make_instance_(pointer) + @classmethod def from_pem(cls, s: "str"): """ @@ -39044,6 +39206,51 @@ def from_der(cls, bytes: "bytes"): _UniffiConverterBytes.lower(bytes)) return cls._make_instance_(pointer) + @classmethod + def from_mnemonic(cls, phrase: "str",account_index: "typing.Union[object, int]" = _DEFAULT,password: "typing.Union[object, str]" = _DEFAULT): + """ + Construct the private key from a mnemonic phrase + """ + + _UniffiConverterString.check_lower(phrase) + + if account_index is _DEFAULT: + account_index = 0 + _UniffiConverterUInt64.check_lower(account_index) + + if password is _DEFAULT: + password = "" + _UniffiConverterString.check_lower(password) + + # Call the (fallible) function before creating any half-baked object instances. + pointer = _uniffi_rust_call_with_error(_UniffiConverterTypeSdkFfiError,_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_mnemonic, + _UniffiConverterString.lower(phrase), + _UniffiConverterUInt64.lower(account_index), + _UniffiConverterString.lower(password)) + return cls._make_instance_(pointer) + + @classmethod + def from_mnemonic_with_path(cls, phrase: "str",path: "str",password: "typing.Union[object, str]" = _DEFAULT): + """ + Create an instance from a mnemonic phrase and a derivation path like + `"m/74'/4218'/0'/0/0"` + """ + + _UniffiConverterString.check_lower(phrase) + + _UniffiConverterString.check_lower(path) + + if password is _DEFAULT: + password = "" + _UniffiConverterString.check_lower(password) + + # Call the (fallible) function before creating any half-baked object instances. + pointer = _uniffi_rust_call_with_error(_UniffiConverterTypeSdkFfiError,_UniffiLib.uniffi_iota_sdk_ffi_fn_constructor_secp256r1privatekey_from_mnemonic_with_path, + _UniffiConverterString.lower(phrase), + _UniffiConverterString.lower(path), + _UniffiConverterString.lower(password)) + return cls._make_instance_(pointer) + @classmethod def from_pem(cls, s: "str"): """ diff --git a/crates/iota-sdk-crypto/Cargo.toml b/crates/iota-sdk-crypto/Cargo.toml index f5ddb5b8e..dbba71a2f 100644 --- a/crates/iota-sdk-crypto/Cargo.toml +++ b/crates/iota-sdk-crypto/Cargo.toml @@ -54,6 +54,7 @@ pem = [ "k256?/pem", ] bls12381 = ["dep:blst", "dep:rand_core", "dep:roaring", "signature/std"] +mnemonic = ["dep:bip32", "dep:bip39", "dep:slip10_ed25519"] [dependencies] iota-types = { workspace = true, features = ["hash", "serde"] } @@ -99,6 +100,11 @@ roaring = { workspace = true, optional = true } # bech32 encoding support bech32 = { version = "0.11.0", optional = true } +# mnemonic support +bip32 = { version = "0.5.3", optional = true } +bip39 = { version = "2.0.0", optional = true } +slip10_ed25519 = { version = "0.1.3", optional = true } + [dev-dependencies] bcs.workspace = true hex.workspace = true diff --git a/crates/iota-sdk-crypto/src/ed25519.rs b/crates/iota-sdk-crypto/src/ed25519.rs index f3ddc4f2c..39f300918 100644 --- a/crates/iota-sdk-crypto/src/ed25519.rs +++ b/crates/iota-sdk-crypto/src/ed25519.rs @@ -111,15 +111,16 @@ impl Ed25519PrivateKey { } } -impl crate::PrivateKeyExt for Ed25519PrivateKey { - const SCHEME: SignatureScheme = SignatureScheme::Ed25519; +impl crate::ToFromBytes for Ed25519PrivateKey { + type Error = crate::PrivateKeyError; + type ByteArray = [u8; Self::LENGTH]; /// Return the raw 32-byte private key - fn to_bytes(&self) -> Vec { - self.0.to_bytes().to_vec() + fn to_bytes(&self) -> Self::ByteArray { + self.0.to_bytes() } - fn from_raw_bytes(bytes: &[u8]) -> Result { + fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != Self::LENGTH { return Err(crate::PrivateKeyError::InvalidScheme( "invalid ed25519 key length".to_string(), @@ -132,6 +133,53 @@ impl crate::PrivateKeyExt for Ed25519PrivateKey { } } +impl crate::PrivateKeyScheme for Ed25519PrivateKey { + const SCHEME: SignatureScheme = SignatureScheme::Ed25519; +} + +#[cfg(feature = "mnemonic")] +impl crate::FromMnemonic for Ed25519PrivateKey { + type Error = crate::PrivateKeyError; + + fn from_mnemonic( + phrase: &str, + account_index: impl Into>, + password: impl Into>, + ) -> Result + where + Self: Sized, + { + let path = format!( + "m/{}'/{}'/0'/0'/{}'", + crate::DERIVATION_PATH_PURPOSE_ED25519, + crate::DERIVATION_PATH_COIN_TYPE, + account_index.into().unwrap_or_default() + ); + Self::from_mnemonic_with_path(phrase, path, password) + } + + fn from_mnemonic_with_path( + phrase: &str, + path: String, + password: impl Into>, + ) -> Result + where + Self: Sized, + { + use std::str::FromStr; + + let mnemonic = bip39::Mnemonic::parse_in_normalized(bip39::Language::English, phrase)?; + let seed = mnemonic.to_seed(password.into().unwrap_or_default()); + let path = bip32::DerivationPath::from_str(&path)? + .into_iter() + .map(|c| c.0) + .collect::>(); + Ok(Self::new(slip10_ed25519::derive_ed25519_private_key( + &seed, &path, + ))) + } +} + impl Signer for Ed25519PrivateKey { fn try_sign(&self, msg: &[u8]) -> Result { self.0 diff --git a/crates/iota-sdk-crypto/src/lib.rs b/crates/iota-sdk-crypto/src/lib.rs index 8ddc0f23a..e86052efc 100644 --- a/crates/iota-sdk-crypto/src/lib.rs +++ b/crates/iota-sdk-crypto/src/lib.rs @@ -23,6 +23,12 @@ pub enum PrivateKeyError { /// HRP (Human Readable Part) error #[error("bech32 HRP error: {0}")] Bech32Hrp(String), + #[cfg(feature = "mnemonic")] + #[error("mnemonic error: {0}")] + Bip32(#[from] bip32::Error), + #[cfg(feature = "mnemonic")] + #[error("mnemonic error: {0}")] + Bip39(#[from] bip39::Error), } #[cfg(feature = "bls12381")] @@ -182,35 +188,69 @@ impl> IotaVerifier for T { #[cfg_attr(doc_cfg, doc(cfg(feature = "bech32")))] pub const IOTA_PRIV_KEY_PREFIX: &str = "iotaprivkey"; -/// Extension trait for private key types -pub trait PrivateKeyExt { - /// The signature scheme for this key type +#[cfg(feature = "mnemonic")] +pub const DERIVATION_PATH_COIN_TYPE: u32 = 4218; +#[cfg(feature = "mnemonic")] +pub const DERIVATION_PATH_PURPOSE_ED25519: u32 = 44; +#[cfg(feature = "mnemonic")] +pub const DERIVATION_PATH_PURPOSE_SECP256K1: u32 = 54; +#[cfg(feature = "mnemonic")] +pub const DERIVATION_PATH_PURPOSE_SECP256R1: u32 = 74; + +/// Defines the scheme of a private key +pub trait PrivateKeyScheme { const SCHEME: iota_types::SignatureScheme; /// Returns the signature scheme for this private key fn scheme(&self) -> iota_types::SignatureScheme { Self::SCHEME } +} + +/// Defines a type which can be constructed from bytes +pub trait ToFromBytes { + type Error; + type ByteArray; - /// Returns the raw bytes of this private key - fn to_bytes(&self) -> Vec; + /// Returns the raw bytes as a byte array. + fn to_bytes(&self) -> Self::ByteArray; - /// Creates an instance from raw key bytes (without scheme flag) - fn from_raw_bytes(bytes: &[u8]) -> Result + /// Create an instance from raw bytes + fn from_bytes(bytes: &[u8]) -> Result where Self: Sized; +} + +/// Defines a type that can be converted to and from flagged bytes, i.e. bytes +/// prepended by some variant indicator flag +pub trait ToFromFlaggedBytes { + type Error; + + /// Returns the bytes with the flag prepended + fn to_flagged_bytes(&self) -> Vec; + + /// Creates an instance from bytes that include the flag + fn from_flagged_bytes(bytes: &[u8]) -> Result + where + Self: Sized; +} + +impl + PrivateKeyScheme> ToFromFlaggedBytes for T +where + T::ByteArray: AsRef<[u8]>, +{ + type Error = PrivateKeyError; /// Returns the bytes with signature scheme flag prepended fn to_flagged_bytes(&self) -> Vec { let key_bytes = self.to_bytes(); - let mut bytes = Vec::with_capacity(1 + key_bytes.len()); + let mut bytes = Vec::with_capacity(1 + key_bytes.as_ref().len()); bytes.push(self.scheme().to_u8()); - bytes.extend_from_slice(&key_bytes); + bytes.extend_from_slice(key_bytes.as_ref()); bytes } - /// Creates an instance from bytes that include the signature scheme flag - fn from_flagged_bytes(bytes: &[u8]) -> Result + fn from_flagged_bytes(bytes: &[u8]) -> Result where Self: Sized, { @@ -229,12 +269,30 @@ pub trait PrivateKeyExt { } let key_bytes = &bytes[1..]; - Self::from_raw_bytes(key_bytes) + Self::from_bytes(key_bytes) } +} + +/// Defines a type which can be converted to and from bech32 strings +#[cfg(feature = "bech32")] +pub trait ToFromBech32 { + type Error; /// Encode this private key in Bech32 format with "iotaprivkey" prefix + fn to_bech32(&self) -> Result; + + /// Decode a private key from Bech32 format with "iotaprivkey" prefix + fn from_bech32(value: &str) -> Result + where + Self: Sized; +} + +#[cfg(feature = "bech32")] +impl> ToFromBech32 for T { + type Error = PrivateKeyError; + #[cfg(feature = "bech32")] - fn to_bech32(&self) -> Result { + fn to_bech32(&self) -> Result { use bech32::Hrp; let hrp = Hrp::parse(IOTA_PRIV_KEY_PREFIX) @@ -246,12 +304,8 @@ pub trait PrivateKeyExt { .map_err(|e| PrivateKeyError::Bech32(format!("encoding failed: {e}"))) } - /// Decode a private key from Bech32 format with "iotaprivkey" prefix #[cfg(feature = "bech32")] - fn from_bech32(value: &str) -> Result - where - Self: Sized, - { + fn from_bech32(value: &str) -> Result { use bech32::Hrp; let expected_hrp = Hrp::parse(IOTA_PRIV_KEY_PREFIX) @@ -273,3 +327,122 @@ pub trait PrivateKeyExt { Self::from_flagged_bytes(&data) } } + +/// Defines a type which can be constructed from a mnemonic phrase +#[cfg(feature = "mnemonic")] +pub trait FromMnemonic { + type Error; + + /// Create an instance from a mnemonic phrase + fn from_mnemonic( + phrase: &str, + account_index: impl Into>, + password: impl Into>, + ) -> Result + where + Self: Sized; + + /// Create an instance from a mnemonic phrase and a derivation path like: + /// - Ed25519: `"m/44'/4218'/0'/0'/0'"` + /// - Secp256k1: `"m/54'/4218'/0'/0/0"` + /// - Secp256r1: `"m/74'/4218'/0'/0/0"` + fn from_mnemonic_with_path( + phrase: &str, + path: String, + password: impl Into>, + ) -> Result + where + Self: Sized; +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + ed25519::Ed25519PrivateKey, secp256k1::Secp256k1PrivateKey, secp256r1::Secp256r1PrivateKey, + }; + + #[cfg(feature = "mnemonic")] + #[test] + fn test_mnemonics_ed25519() { + const TEST_CASES: [[&str; 3]; 3] = [ + [ + "film crazy soon outside stand loop subway crumble thrive popular green nuclear struggle pistol arm wife phrase warfare march wheat nephew ask sunny firm", + "iotaprivkey1qrqqxhsu3ndp96644fjk4z5ams5ulgmvprklngt2jhvg2ujn5w4q2d2vplv", + "0x9f8e5379678525edf768d7b507dc1ba9016fc4f0eac976ab7f74077d95fba312", + ], + [ + "require decline left thought grid priority false tiny gasp angle royal system attack beef setup reward aunt skill wasp tray vital bounce inflict level", + "iotaprivkey1qqcxaf57fnenvflpacacaumf6vl0rt0edddhytanvzhkqhwnjk0zspg902d", + "0x862738192e40540e0a5c9a5aca636f53b0cd76b0a9bef3386e05647feb4914ac", + ], + [ + "organ crash swim stick traffic remember army arctic mesh slice swear summer police vast chaos cradle squirrel hood useless evidence pet hub soap lake", + "iotaprivkey1qzq39vxzm0gq7l8dc5dj5allpuww4mavhwhg8mua4cl3lj2c3fvhcv5l2vn", + "0x2391788ca49c7f0f00699bc2bad45f80c343b4d1df024285c132259433d7ff31", + ], + ]; + + for [mnemonic, bech32, address] in TEST_CASES { + let key = Ed25519PrivateKey::from_mnemonic(mnemonic, None, None).unwrap(); + assert_eq!(key.to_bech32().unwrap(), bech32); + assert_eq!(key.public_key().derive_address().to_string(), address); + } + } + + #[cfg(feature = "mnemonic")] + #[test] + fn test_mnemonics_secp256k1() { + const TEST_CASES: [[&str; 3]; 3] = [ + [ + "film crazy soon outside stand loop subway crumble thrive popular green nuclear struggle pistol arm wife phrase warfare march wheat nephew ask sunny firm", + "iotaprivkey1q8cy2ll8a0dmzzzwn9zavrug0qf47cyuj6k2r4r6rnjtpjhrdh52vpegd4f", + "0x8520d58dde1ab268349b9a46e5124ae6fe7e4c61df4ca2bc9c97d3c4d07b0b55", + ], + [ + "require decline left thought grid priority false tiny gasp angle royal system attack beef setup reward aunt skill wasp tray vital bounce inflict level", + "iotaprivkey1q9hm330d05jcxfvmztv046p8kclyaj39hk6elqghgpq4sz4x23hk2wd6cfz", + "0x3740d570eefba29dfc0fdd5829848902064e31ecd059ca05c401907fa8646f61", + ], + [ + "organ crash swim stick traffic remember army arctic mesh slice swear summer police vast chaos cradle squirrel hood useless evidence pet hub soap lake", + "iotaprivkey1qx2dnch6363h7gdqqfkzmmlequzj4ul3x4fq6dzyajk7wc2c0jgcx32axh5", + "0x943b852c37fef403047e06ff5a2fa216557a4386212fb29554babdd3e1899da5", + ], + ]; + + for [mnemonic, bech32, address] in TEST_CASES { + let key = Secp256k1PrivateKey::from_mnemonic(mnemonic, None, None).unwrap(); + assert_eq!(key.to_bech32().unwrap(), bech32); + assert_eq!(key.public_key().derive_address().to_string(), address); + } + } + + #[cfg(feature = "mnemonic")] + #[test] + fn test_mnemonics_secp256r1() { + const TEST_CASES: [[&str; 3]; 3] = [ + [ + "act wing dilemma glory episode region allow mad tourist humble muffin oblige", + "iotaprivkey1qtt65ua2lhal76zg4cxd6umdqynv2rj2gzrntp5rwlnyj370jg3pwtqlwdn", + "0x779a63b28528210a5ec6c4af5a70382fa3f0c2d3f98dcbe4e3a4ae2f8c39cc9c", + ], + [ + "flag rebel cabbage captain minimum purpose long already valley horn enrich salt", + "iotaprivkey1qtcjgmue7q8u4gtutfvfpx3zj3aa2r9pqssuusrltxfv68eqhzsgjc3p4z7", + "0x8b45523042933aa55f57e2ccc661304baed292529b6e67a0c9857c1f3f871806", + ], + [ + "area renew bar language pudding trial small host remind supreme cabbage era", + "iotaprivkey1qtxafg26qxeqy7f56gd2rvsup0a5kl4cre7nt2rtcrf0p3v5pwd4cgrrff2", + "0x8528ef86150ec331928a8b3edb8adbe2fb523db8c84679aa57a931da6a4cdb25", + ], + ]; + + for [mnemonic, bech32, address] in TEST_CASES { + let key = Secp256r1PrivateKey::from_mnemonic(mnemonic, None, None).unwrap(); + assert_eq!(key.to_bech32().unwrap(), bech32); + assert_eq!(key.public_key().derive_address().to_string(), address); + } + } +} diff --git a/crates/iota-sdk-crypto/src/secp256k1.rs b/crates/iota-sdk-crypto/src/secp256k1.rs index ec94dd93e..560a18f79 100644 --- a/crates/iota-sdk-crypto/src/secp256k1.rs +++ b/crates/iota-sdk-crypto/src/secp256k1.rs @@ -116,14 +116,16 @@ impl Secp256k1PrivateKey { } } -impl crate::PrivateKeyExt for Secp256k1PrivateKey { - const SCHEME: SignatureScheme = SignatureScheme::Secp256k1; +impl crate::ToFromBytes for Secp256k1PrivateKey { + type Error = crate::PrivateKeyError; + type ByteArray = [u8; Self::LENGTH]; - fn to_bytes(&self) -> Vec { - self.0.to_bytes().to_vec() + /// Return the raw 32-byte private key + fn to_bytes(&self) -> Self::ByteArray { + self.0.to_bytes().into() } - fn from_raw_bytes(bytes: &[u8]) -> Result { + fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != Self::LENGTH { return Err(crate::PrivateKeyError::InvalidScheme( "invalid secp256k1 key length".to_string(), @@ -136,6 +138,51 @@ impl crate::PrivateKeyExt for Secp256k1PrivateKey { } } +impl crate::PrivateKeyScheme for Secp256k1PrivateKey { + const SCHEME: SignatureScheme = SignatureScheme::Secp256k1; +} + +#[cfg(feature = "mnemonic")] +impl crate::FromMnemonic for Secp256k1PrivateKey { + type Error = crate::PrivateKeyError; + + fn from_mnemonic( + phrase: &str, + account_index: impl Into>, + password: impl Into>, + ) -> Result + where + Self: Sized, + { + let path = format!( + "m/{}'/{}'/0'/0/{}", + crate::DERIVATION_PATH_PURPOSE_SECP256K1, + crate::DERIVATION_PATH_COIN_TYPE, + account_index.into().unwrap_or_default() + ); + Self::from_mnemonic_with_path(phrase, path, password) + } + + fn from_mnemonic_with_path( + phrase: &str, + path: String, + password: impl Into>, + ) -> Result + where + Self: Sized, + { + use std::str::FromStr; + + use crate::ToFromBytes; + + let mnemonic = bip39::Mnemonic::parse_in_normalized(bip39::Language::English, phrase)?; + let seed = mnemonic.to_seed(password.into().unwrap_or_default()); + let child_xprv = + bip32::XPrv::derive_from_path(seed, &bip32::DerivationPath::from_str(&path)?)?; + Self::from_bytes(&child_xprv.private_key().to_bytes()) + } +} + impl Signer for Secp256k1PrivateKey { fn try_sign(&self, message: &[u8]) -> Result { let signature: k256::ecdsa::Signature = self.0.try_sign(message)?; diff --git a/crates/iota-sdk-crypto/src/secp256r1.rs b/crates/iota-sdk-crypto/src/secp256r1.rs index 8195ad683..a6a0f666f 100644 --- a/crates/iota-sdk-crypto/src/secp256r1.rs +++ b/crates/iota-sdk-crypto/src/secp256r1.rs @@ -116,14 +116,16 @@ impl Secp256r1PrivateKey { } } -impl crate::PrivateKeyExt for Secp256r1PrivateKey { - const SCHEME: SignatureScheme = SignatureScheme::Secp256r1; +impl crate::ToFromBytes for Secp256r1PrivateKey { + type Error = crate::PrivateKeyError; + type ByteArray = [u8; Self::LENGTH]; - fn to_bytes(&self) -> Vec { - self.0.to_bytes().to_vec() + /// Return the raw 32-byte private key + fn to_bytes(&self) -> Self::ByteArray { + self.0.to_bytes().into() } - fn from_raw_bytes(bytes: &[u8]) -> Result { + fn from_bytes(bytes: &[u8]) -> Result { if bytes.len() != Self::LENGTH { return Err(crate::PrivateKeyError::InvalidScheme( "invalid secp256r1 key length".to_string(), @@ -136,6 +138,51 @@ impl crate::PrivateKeyExt for Secp256r1PrivateKey { } } +impl crate::PrivateKeyScheme for Secp256r1PrivateKey { + const SCHEME: SignatureScheme = SignatureScheme::Secp256r1; +} + +#[cfg(feature = "mnemonic")] +impl crate::FromMnemonic for Secp256r1PrivateKey { + type Error = crate::PrivateKeyError; + + fn from_mnemonic( + phrase: &str, + account_index: impl Into>, + password: impl Into>, + ) -> Result + where + Self: Sized, + { + let path = format!( + "m/{}'/{}'/0'/0/{}", + crate::DERIVATION_PATH_PURPOSE_SECP256R1, + crate::DERIVATION_PATH_COIN_TYPE, + account_index.into().unwrap_or_default() + ); + Self::from_mnemonic_with_path(phrase, path, password) + } + + fn from_mnemonic_with_path( + phrase: &str, + path: String, + password: impl Into>, + ) -> Result + where + Self: Sized, + { + use std::str::FromStr; + + use crate::ToFromBytes; + + let mnemonic = bip39::Mnemonic::parse_in_normalized(bip39::Language::English, phrase)?; + let seed = mnemonic.to_seed(password.into().unwrap_or_default()); + let child_xprv = + bip32::XPrv::derive_from_path(seed, &bip32::DerivationPath::from_str(&path)?)?; + Self::from_bytes(&child_xprv.private_key().to_bytes()) + } +} + impl Signer for Secp256r1PrivateKey { fn try_sign(&self, message: &[u8]) -> Result { let signature: p256::ecdsa::Signature = self.0.try_sign(message)?; diff --git a/crates/iota-sdk-crypto/src/simple.rs b/crates/iota-sdk-crypto/src/simple.rs index 9e5ab01dc..e862c42eb 100644 --- a/crates/iota-sdk-crypto/src/simple.rs +++ b/crates/iota-sdk-crypto/src/simple.rs @@ -82,7 +82,7 @@ mod keypair { }; use signature::{Signer, Verifier}; - use crate::{PrivateKeyExt, SignatureError}; + use crate::SignatureError; #[derive(Debug, Clone)] pub struct SimpleKeypair { @@ -138,6 +138,8 @@ mod keypair { /// Encode a SimpleKeypair as `flag || privkey` in bytes pub fn to_bytes(&self) -> Vec { + use crate::ToFromBytes; + let mut bytes = Vec::new(); bytes.push(self.scheme().to_u8()); @@ -538,37 +540,17 @@ mod keypair { } } - impl crate::PrivateKeyExt for SimpleKeypair { - // SimpleKeypair doesn't have a single scheme since it can contain any scheme - // We provide a dummy value since we override all methods - const SCHEME: SignatureScheme = SignatureScheme::Ed25519; - - fn scheme(&self) -> SignatureScheme { - self.scheme() - } - - fn to_bytes(&self) -> Vec { - // For SimpleKeypair, to_bytes() already returns flagged bytes - // We need the raw key bytes without the flag for the trait - match &self.inner { - #[cfg(feature = "ed25519")] - InnerKeypair::Ed25519(private_key) => private_key.to_bytes().to_vec(), - #[cfg(feature = "secp256k1")] - InnerKeypair::Secp256k1(private_key) => private_key.to_bytes().as_slice().to_vec(), - #[cfg(feature = "secp256r1")] - InnerKeypair::Secp256r1(private_key) => private_key.to_bytes().as_slice().to_vec(), - } - } + impl crate::ToFromFlaggedBytes for SimpleKeypair { + type Error = crate::PrivateKeyError; - fn from_raw_bytes(_bytes: &[u8]) -> Result { - // SimpleKeypair can't be created from raw bytes without knowing the scheme - // This should not be called since SimpleKeypair overrides from_flagged_bytes - Err(crate::PrivateKeyError::InvalidScheme( - "SimpleKeypair should use from_bytes instead".to_string(), - )) + fn to_flagged_bytes(&self) -> Vec { + self.to_bytes() } - fn from_flagged_bytes(bytes: &[u8]) -> Result { + fn from_flagged_bytes(bytes: &[u8]) -> Result + where + Self: Sized, + { Self::from_bytes(bytes) .map_err(|e| crate::PrivateKeyError::InvalidScheme(e.to_string())) } @@ -581,7 +563,7 @@ mod tests { use super::*; use crate::{ - PrivateKeyExt, + ToFromBech32, ed25519::{Ed25519PrivateKey, Ed25519VerifyingKey}, secp256k1::{Secp256k1PrivateKey, Secp256k1VerifyingKey}, secp256r1::{Secp256r1PrivateKey, Secp256r1VerifyingKey}, diff --git a/crates/iota-sdk-ffi/src/crypto/ed25519.rs b/crates/iota-sdk-ffi/src/crypto/ed25519.rs index bc3ec465f..8771e30da 100644 --- a/crates/iota-sdk-ffi/src/crypto/ed25519.rs +++ b/crates/iota-sdk-ffi/src/crypto/ed25519.rs @@ -1,7 +1,10 @@ // Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_sdk::{crypto::PrivateKeyExt, types::SignatureScheme}; +use iota_sdk::{ + crypto::{FromMnemonic, ToFromBech32, ToFromBytes}, + types::SignatureScheme, +}; use rand::rngs::OsRng; use crate::{ @@ -86,6 +89,29 @@ impl Ed25519PrivateKey { Ok(iota_sdk::crypto::ed25519::Ed25519PrivateKey::from_bech32(value)?.into()) } + /// Construct the private key from a mnemonic phrase + #[uniffi::constructor(default(password = "", account_index = 0))] + pub fn from_mnemonic(phrase: &str, account_index: u64, password: String) -> Result { + Ok(iota_sdk::crypto::ed25519::Ed25519PrivateKey::from_mnemonic( + phrase, + account_index, + password, + )? + .into()) + } + + /// Create an instance from a mnemonic phrase and a derivation path like + /// `"m/44'/4218'/0'/0'/0'"` + #[uniffi::constructor(default(password = ""))] + pub fn from_mnemonic_with_path(phrase: &str, path: String, password: String) -> Result { + Ok( + iota_sdk::crypto::ed25519::Ed25519PrivateKey::from_mnemonic_with_path( + phrase, path, password, + )? + .into(), + ) + } + pub fn try_sign(&self, message: &[u8]) -> Result { Ok( iota_sdk::crypto::Signer::::try_sign( diff --git a/crates/iota-sdk-ffi/src/crypto/secp256k1.rs b/crates/iota-sdk-ffi/src/crypto/secp256k1.rs index efa7ebb02..a7c406a51 100644 --- a/crates/iota-sdk-ffi/src/crypto/secp256k1.rs +++ b/crates/iota-sdk-ffi/src/crypto/secp256k1.rs @@ -1,7 +1,10 @@ // Copyright (c) 2025 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use iota_sdk::{crypto::PrivateKeyExt, types::SignatureScheme}; +use iota_sdk::{ + crypto::{FromMnemonic, ToFromBech32, ToFromBytes}, + types::SignatureScheme, +}; use rand::rngs::OsRng; use crate::{ @@ -86,6 +89,31 @@ impl Secp256k1PrivateKey { Ok(iota_sdk::crypto::secp256k1::Secp256k1PrivateKey::from_bech32(value)?.into()) } + /// Construct the private key from a mnemonic phrase + #[uniffi::constructor(default(password = "", account_index = 0))] + pub fn from_mnemonic(phrase: &str, account_index: u64, password: String) -> Result { + Ok( + iota_sdk::crypto::secp256k1::Secp256k1PrivateKey::from_mnemonic( + phrase, + account_index, + password, + )? + .into(), + ) + } + + /// Create an instance from a mnemonic phrase and a derivation path like + /// `"m/54'/4218'/0'/0/0"` + #[uniffi::constructor(default(password = ""))] + pub fn from_mnemonic_with_path(phrase: &str, path: String, password: String) -> Result { + Ok( + iota_sdk::crypto::secp256k1::Secp256k1PrivateKey::from_mnemonic_with_path( + phrase, path, password, + )? + .into(), + ) + } + pub fn try_sign(&self, message: &[u8]) -> Result { Ok( iota_sdk::crypto::Signer::::try_sign( diff --git a/crates/iota-sdk-ffi/src/crypto/secp256r1.rs b/crates/iota-sdk-ffi/src/crypto/secp256r1.rs index 0381a8870..7eceeeb00 100644 --- a/crates/iota-sdk-ffi/src/crypto/secp256r1.rs +++ b/crates/iota-sdk-ffi/src/crypto/secp256r1.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use iota_sdk::{ - crypto::{PrivateKeyExt, Signer, Verifier}, + crypto::{FromMnemonic, Signer, ToFromBech32, ToFromBytes, Verifier}, types::SignatureScheme, }; use rand::rngs::OsRng; @@ -120,6 +120,31 @@ impl Secp256r1PrivateKey { pub fn from_bech32(value: &str) -> Result { Ok(iota_sdk::crypto::secp256r1::Secp256r1PrivateKey::from_bech32(value)?.into()) } + + /// Construct the private key from a mnemonic phrase + #[uniffi::constructor(default(password = "", account_index = 0))] + pub fn from_mnemonic(phrase: &str, account_index: u64, password: String) -> Result { + Ok( + iota_sdk::crypto::secp256r1::Secp256r1PrivateKey::from_mnemonic( + phrase, + account_index, + password, + )? + .into(), + ) + } + + /// Create an instance from a mnemonic phrase and a derivation path like + /// `"m/74'/4218'/0'/0/0"` + #[uniffi::constructor(default(password = ""))] + pub fn from_mnemonic_with_path(phrase: &str, path: String, password: String) -> Result { + Ok( + iota_sdk::crypto::secp256r1::Secp256r1PrivateKey::from_mnemonic_with_path( + phrase, path, password, + )? + .into(), + ) + } } #[derive(derive_more::From, uniffi::Object)] diff --git a/crates/iota-sdk-ffi/src/crypto/simple.rs b/crates/iota-sdk-ffi/src/crypto/simple.rs index 41d4984a8..5179488cc 100644 --- a/crates/iota-sdk-ffi/src/crypto/simple.rs +++ b/crates/iota-sdk-ffi/src/crypto/simple.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use iota_sdk::{ - crypto::{PrivateKeyExt, Signer, Verifier}, + crypto::{Signer, ToFromBech32, Verifier}, types::SignatureScheme, }; diff --git a/crates/iota-sdk-types/src/crypto/signature.rs b/crates/iota-sdk-types/src/crypto/signature.rs index d1dc67fac..620f364c4 100644 --- a/crates/iota-sdk-types/src/crypto/signature.rs +++ b/crates/iota-sdk-types/src/crypto/signature.rs @@ -212,16 +212,23 @@ impl SimpleSignature { /// zklogin-flag = %x05 /// passkey-flag = %x06 /// ``` -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, derive_more::Display)] #[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))] #[repr(u8)] pub enum SignatureScheme { + #[display("ed25519")] Ed25519 = 0x00, + #[display("secp256k1")] Secp256k1 = 0x01, + #[display("secp256r1")] Secp256r1 = 0x02, + #[display("multisig")] Multisig = 0x03, + #[display("bls12381")] Bls12381 = 0x04, // This is currently not supported for user addresses + #[display("zklogin")] ZkLogin = 0x05, + #[display("passkey")] Passkey = 0x06, } @@ -230,19 +237,6 @@ impl SignatureScheme { Ed25519, Secp256k1, Secp256r1, Multisig, Bls12381, ZkLogin, Passkey, ); - /// Return the name of this signature scheme - pub fn name(self) -> &'static str { - match self { - SignatureScheme::Ed25519 => "ed25519", - SignatureScheme::Secp256k1 => "secp256k1", - SignatureScheme::Secp256r1 => "secp256r1", - SignatureScheme::Multisig => "multisig", - SignatureScheme::Bls12381 => "bls12381", - SignatureScheme::ZkLogin => "zklogin", - SignatureScheme::Passkey => "passkey", - } - } - /// Try constructing from a byte flag pub fn from_byte(flag: u8) -> Result { match flag { diff --git a/crates/iota-sdk/Cargo.toml b/crates/iota-sdk/Cargo.toml index 6f4d9cbd5..02b5e5544 100644 --- a/crates/iota-sdk/Cargo.toml +++ b/crates/iota-sdk/Cargo.toml @@ -19,6 +19,7 @@ default = [ "pem", "bls12381", "bech32", + "mnemonic", "graphql", "txn-builder", "types", @@ -39,6 +40,7 @@ zklogin = ["crypto", "iota-crypto/zklogin"] pem = ["crypto", "iota-crypto/pem"] bls12381 = ["crypto", "iota-crypto/bls12381"] bech32 = ["crypto", "iota-crypto/bech32"] +mnemonic = ["crypto", "iota-crypto/mnemonic"] # GraphQL graphql = ["dep:iota-graphql-client"] diff --git a/crates/iota-sdk/examples/address_from_mnemonic.rs b/crates/iota-sdk/examples/address_from_mnemonic.rs new file mode 100644 index 000000000..8dbfed6ef --- /dev/null +++ b/crates/iota-sdk/examples/address_from_mnemonic.rs @@ -0,0 +1,55 @@ +// Copyright (c) 2025 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use base64ct::{Base64, Encoding}; +use iota_crypto::{ + DERIVATION_PATH_COIN_TYPE, DERIVATION_PATH_PURPOSE_SECP256R1, FromMnemonic, ToFromBech32, + ed25519::Ed25519PrivateKey, secp256k1::Secp256k1PrivateKey, secp256r1::Secp256r1PrivateKey, +}; +use iota_types::PublicKeyExt; + +const MNEMONIC: &str = "round attack kitchen wink winter music trip tiny nephew hire orange what"; + +fn main() -> eyre::Result<()> { + let private_key = Ed25519PrivateKey::from_mnemonic(MNEMONIC, None, None)?; + let private_key_bech32 = private_key.to_bech32().unwrap(); + let public_key = private_key.public_key(); + let flagged_public_key = Base64::encode_string(&public_key.to_flagged_bytes()); + let address = public_key.derive_address(); + + println!("Ed25519\n---"); + println!("Private Key: {private_key_bech32}"); + println!("Public Key: {public_key}"); + println!("Public Key With Flag: {flagged_public_key}"); + println!("Address: {address}"); + + let private_key = Secp256k1PrivateKey::from_mnemonic(MNEMONIC, 1, "my_password".to_owned())?; + let private_key_bech32 = private_key.to_bech32().unwrap(); + let public_key = private_key.public_key(); + let flagged_public_key = Base64::encode_string(&public_key.to_flagged_bytes()); + let address = public_key.derive_address(); + + println!("\nSecp256k1\n---"); + println!("Private Key: {private_key_bech32}"); + println!("Public Key: {public_key}"); + println!("Public Key With Flag: {flagged_public_key}"); + println!("Address: {address}"); + + let private_key = Secp256r1PrivateKey::from_mnemonic_with_path( + MNEMONIC, + format!("m/{DERIVATION_PATH_PURPOSE_SECP256R1}'/{DERIVATION_PATH_COIN_TYPE}'/0'/0/2"), + None, + )?; + let private_key_bech32 = private_key.to_bech32().unwrap(); + let public_key = private_key.public_key(); + let flagged_public_key = Base64::encode_string(&public_key.to_flagged_bytes()); + let address = public_key.derive_address(); + + println!("\nSecp256r1\n---"); + println!("Private Key: {private_key_bech32}"); + println!("Public Key: {public_key}"); + println!("Public Key With Flag: {flagged_public_key}"); + println!("Address: {address}"); + + Ok(()) +} diff --git a/crates/iota-sdk/examples/generate_ed25519_address.rs b/crates/iota-sdk/examples/generate_ed25519_address.rs index aa9337d2b..0bc247fe9 100644 --- a/crates/iota-sdk/examples/generate_ed25519_address.rs +++ b/crates/iota-sdk/examples/generate_ed25519_address.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use base64ct::{Base64, Encoding}; -use iota_crypto::{PrivateKeyExt, ed25519::Ed25519PrivateKey}; +use iota_crypto::{ToFromBech32, ed25519::Ed25519PrivateKey}; use iota_types::PublicKeyExt; use rand::rngs::OsRng;