diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78825897..caa08239 100755 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -383,16 +383,13 @@ jobs: curl https://storage.googleapis.com/git-repo-downloads/repo > /bin/repo && chmod a+x /bin/repo && \ mkdir -p optee && \ cd optee && \ - repo init -u https://github.com/edtubbs/manifest.git -m nanopc-t6.xml -b nanopc-t6 && \ + repo init -u https://github.com/OP-TEE/manifest.git -m nanopc-t6.xml -b master && \ export FORCE_UNSAFE_CONFIGURE=1 && \ repo sync -j\"$(getconf _NPROCESSORS_ONLN)\" && \ if [[ "${{ github.ref }}" == refs/tags/* ]]; then \ mv /src/rsa_private.pem /src/optee/optee_os/keys/default_ta.pem; \ fi && \ patch -F 4 /src/optee/build/common.mk < /src/src/optee/common.mk.patch && \ - patch /src/optee/build/kconfigs/qemu.conf < /src/src/optee/qemu.conf.patch && \ - patch /src/optee/linux/arch/arm64/boot/dts/rockchip/rk3588-nanopi6-common.dtsi < /src/src/optee/rk3588-nanopi6-common.dtsi.patch && \ - patch /src/optee/u-boot/include/configs/nanopi6.h < /src/src/optee/nanopi6.h.patch && \ cd build && \ make toolchains -j\"$(getconf _NPROCESSORS_ONLN)\" && \ export CFG_TEE_CORE_LOG_LEVEL=0 && \ @@ -451,15 +448,8 @@ jobs: export PATH=/src/optee/toolchains/aarch64/bin:$PATH && \ export CC=aarch64-linux-gnu-gcc && \ - # Run the libdogecoin TA - cd /src/src/optee/host && \ - make -j"$(getconf _NPROCESSORS_ONLN)" \ - CROSS_COMPILE=aarch64-linux-gnu- \ - LDFLAGS=\"-L/src/optee/toolchains/aarch64/lib -L/src/depends/aarch64-linux-gnu/lib -ldogecoin -lunistring\" \ - CFLAGS=\"-I/src/optee/toolchains/aarch64/include -I/src/src/optee/ta/include -I/src/depends/aarch64-linux-gnu/include -I/src/depends/aarch64-linux-gnu/include/ykpers-1 -I/src/depends/aarch64-linux-gnu/include/dogecoin\" && \ - # Build the Trusted Application - cd ../ta && \ + cd /src/src/optee/ta && \ make -j"$(getconf _NPROCESSORS_ONLN)" \ CROSS_COMPILE=aarch64-linux-gnu- \ LDFLAGS=\"-L/src/depends/aarch64-linux-gnu/lib -ldogecoin -lunistring\" \ @@ -467,6 +457,19 @@ jobs: PLATFORM=vexpress-qemu_armv8a \ TA_DEV_KIT_DIR=/src/optee/optee_os/out/arm/export-ta_arm64 && \ + # Build libdogecoin for Host + cd /src/ && \ + ./configure --prefix=/src/depends/aarch64-linux-gnu LIBS=-levent_pthreads --enable-static --disable-shared --enable-test-passwd HOST=aarch64-linux-gnu && \ + make -j 4 && \ + make install && \ + + # Run the libdogecoin TA + cd /src/src/optee/host && \ + make -j"$(getconf _NPROCESSORS_ONLN)" \ + CROSS_COMPILE=aarch64-linux-gnu- \ + LDFLAGS=\"-L/src/optee/toolchains/aarch64/lib -L/src/depends/aarch64-linux-gnu/lib -ldogecoin -lunistring\" \ + CFLAGS=\"-I/src/optee/toolchains/aarch64/include -I/src/src/optee/ta/include -I/src/depends/aarch64-linux-gnu/include -I/src/depends/aarch64-linux-gnu/include/ykpers-1 -I/src/depends/aarch64-linux-gnu/include/dogecoin\" && \ + # Create symbolic links and prepare image mkdir -p /src/optee/out/bin && \ cd /src/optee/out/bin && \ @@ -496,6 +499,10 @@ jobs: elif ([ "${{ matrix.name }}" == "x86_64-linux-openenclave" ]); then make install && \ mkdir -p src/openenclave/build && \ + make -j 4 -C depends HOST=x86_64-pc-linux-gnu/host && \ + ./configure --prefix=${{ github.workspace }}/depends/x86_64-pc-linux-gnu/host --enable-test-passwd && \ + make && \ + make install && \ if [[ "${{ github.ref }}" == refs/tags/* ]]; then printf "%s" "${{ secrets.OE_PRIVATE_PEM }}" > src/openenclave/build/private.pem && \ openssl rsa -pubout -in src/openenclave/build/private.pem -out src/openenclave/build/public.pem; \ diff --git a/doc/enclaves.md b/doc/enclaves.md index 848ea438..9ab2e18b 100644 --- a/doc/enclaves.md +++ b/doc/enclaves.md @@ -4,12 +4,28 @@ This document discusses our research on enhancing the security of **libdogecoin** key management operations using **Intel SGX** and **ARM TrustZone** as secure enclaves. By performing key management within secure enclaves, we significantly reduce the risk of key exposure, thereby increasing the overall security of Dogecoin transactions. This document includes an overview of the research, build instructions for the key management enclaves, and a step-by-step tutorial on using the host command line interface to interact with the enclaves in both **OP-TEE** and **OpenEnclave** environments. Additionally, we outline critical vulnerabilities in Trusted Execution Environments (TEEs) and provide recommendations for mitigating these risks. -The integration of secure enclaves in key management operations presents an innovative security solution, but it also introduces technical complexity and performance trade-offs that should be carefully considered. +This document covers mnemonic generation, public key derivation, address generation, message signing, and transaction signing as part of secure enclave operations. ## What are Secure Enclaves? Secure enclaves are isolated environments that provide a secure space for sensitive operations. These enclaves are isolated from the rest of the system, ensuring that sensitive data and operations are protected from unauthorized access. **Secure enclaves** within a processor can be implemented using different technologies such as **TEEs**, hardware security modules (**HSMs**), and virtualization-based solutions. To ensure a robust defense, secure enclaves must guarantee **confidentiality**, **integrity**, and **availability** for sensitive operations. However, enclaves are not a silver bullet and must be integrated within a broader security strategy. +Compared to hardware security modules (HSMs) and trusted platform modules (TPMs), secure enclaves provide in-CPU isolation, which makes them ideal for high-performance cryptographic operations without requiring external hardware. + +## Why Use Secure Enclaves? + +Secure enclaves offer several advantages for key management operations: + +- **Isolation**: Enclaves provide a secure environment that is isolated from the rest of the system, protecting sensitive data and operations. +- **Confidentiality**: Enclaves ensure that sensitive data is encrypted and protected from unauthorized access. +- **Integrity**: Enclaves guarantee the integrity of sensitive operations, ensuring that they have not been tampered with. +- **Remote Attestation**: Enclaves can be remotely attested to verify their integrity and authenticity to external parties. +- **Key Management**: Enclaves can securely generate, store, and manage cryptographic keys, protecting them from unauthorized access. + +By performing key operations within secure enclaves, developers can enhance the security of their applications and protect sensitive data from unauthorized access. + +## Secure Enclaves in libdogecoin + ### Key Concepts In this section, we define some key concepts central to secure enclaves and their implementation. @@ -25,7 +41,7 @@ By performing key operations such as **seedphrase generation**, **public key der ## What are Trusted Execution Environments (TEEs)? -TEEs are secure environments within a processor that provide a trusted execution domain for sensitive operations. TEEs ensure that sensitive data and operations are protected from unauthorized access and tampering. TEEs can be implemented using hardware-based security technologies such as **Intel SGX**, **ARM TrustZone**, or other vendor-specific technologies. While TEEs offer significant advantages in terms of isolating critical operations, they also come with their own set of challenges, particularly in terms of addressing vulnerabilities and maintaining performance. +TEEs are secure environments within a processor that provide a trusted execution domain for sensitive operations. They ensure that sensitive data and operations are protected from unauthorized access and tampering. TEEs can be implemented using hardware-based security technologies such as **Intel SGX**, **ARM TrustZone**, or other vendor-specific technologies. While they offer significant advantages in terms of isolating critical operations, TEEs also come with their own set of challenges, particularly in terms of addressing vulnerabilities and maintaining performance. ### Contrast Between Intel SGX and ARM TrustZone @@ -105,19 +121,25 @@ The secure enclave handles the following operations: While secure enclaves provide powerful protection, they are **not infallible**. The **Foreshadow** vulnerability is a good example of how attackers can bypass enclave protections. Furthermore, the security model assumes that the host environment is insecure, relying entirely on the enclave for protection. Developers should adopt a **defense-in-depth** strategy, using **secure coding practices** and **regular security audits** to complement enclave security. +### Performance trade-offs + +Secure enclaves introduce performance overhead due to the encryption and isolation mechanisms they employ. For example, **Intel SGX** enclaves have higher performance overhead compared to **ARM TrustZone** enclaves, especially for I/O and large memory use. **ARM TrustZone** enclaves are better suited for embedded and mobile devices due to their lower performance impact. However key management operations are typically not performance-critical, making secure enclave protection a worthwhile trade-off for the increased security it provides. + ### Secure Storage Mnemonic seedphrases are securely encrypted by the enclave to prevent unauthorized access. Encrypted data should still be backed up using the **rule of three**: store copies in three distinct locations to ensure recovery. ### Time-Based One-Time Password (TOTP) Authentication -TOTP authentication using a **YubiKey** further enhances security by ensuring that access to sensitive enclave operations is restricted to authorized users. This two-factor authentication approach increases security during enclave operations, preventing unauthorized key usage even if the host environment is compromised. +TOTP authentication using a **YubiKey** further enhances security by ensuring that access to sensitive enclave operations is restricted to authorized users. When combined with password-based authentication, TOTP provides an additional layer of security. This two-factor authentication approach increases security during enclave operations, preventing unauthorized key usage even if the host environment is compromised. ## Future Research -To further improve the security and functionality of Dogecoin’s ecosystem, we recommend exploring **Remote Attestation** to validate the integrity of enclaves in distributed systems. Integrating **proof-of-work (PoW)** operations within secure enclaves could also allow for **private mempools** or **payment channels**, enhancing the Dogecoin network's security and efficiency. +To further improve the security and functionality of Dogecoin’s ecosystem, we recommend exploring **Remote Attestation** to validate the integrity of enclaves in distributed systems. This would allow external parties to verify the authenticity of enclaves, ensuring that they have not been tampered with. Intel SGX supports remote attestation, while ARM TrustZone can be extended to include this feature. + +Performance optimizations for secure enclaves are another area of interest, as reducing overhead can make enclaves more practical for a wider range of applications. Additonal analysis is needed to evaluate the performance impact of secure enclaves on key management operations at scale. -Additionally, alternative secure technologies like **AMD SEV** and **RISC-V** should be explored to broaden the options available for TEE-based applications. +Additionally, alternative secure technologies like **AMD SEV** and **RISC-V** should be explored to broaden the options available for TEE-based applications. These technologies offer different security models and performance characteristics that may be better suited to specific use cases. ## References @@ -160,10 +182,7 @@ For vulnerabilities and security advisories related to OpenEnclave, refer to the **OP-TEE:** For vulnerabilities and security advisories related to OP-TEE, refer to their [GitHub security page](https://github.com/OP-TEE/optee_os/security). -### Best Practices for Mitigation -- Keep software up-to-date by regularly applying security patches. -- Implement defense-in-depth strategies such as isolating critical operations in separate enclaves. -- Consider using multuple secure technologies (e.g., TEEs, HSMs) to minimize reliance on any single point of failure. +To mitigate the risks associated with these vulnerabilities, it is essential to stay informed about the latest security advisories and best practices for secure enclave development. Regularly applying security patches and following secure coding practices can help protect sensitive data and operations from unauthorized access. ## Disclaimer @@ -190,9 +209,9 @@ cd libdogecoin The SDK has several components and requires over 10GB of disk space to build. The build process can take over 30 minutes on a modern machine. Docker is used to build the SDK and client in a clean environment. -### Building OP-TEE SDK and Client (NanoPC-T6) +### Step 1 (NanoPC): Building OP-TEE SDK and Client -This command builds the latest SDK and client for NanoPC-T6 (nanopc-t6.xml). When complete, the image will be located in `/doge/libdogecoin/optee/out/nanopc-t6.img`. Burn this image to an SD card to boot the NanoPC-T6. Connect an Ethernet cable to the NanoPC-T6 and power it on. The default IP address is `192.168.1.1`. Login as root via ssh (e.g. `ssh root@192.168.1.1`). +This command builds the latest SDK and client for NanoPC-T6 (nanopc-t6.xml). When complete, the image will be located in `/doge/libdogecoin/optee/out/nanopc-t6.img`. Burn this image to an SD card to boot the NanoPC-T6. Connect an Ethernet cable, USB keyboard and HDMI to the NanoPC-T6 and power it on. The default IP address is configured using DHCP. Login as root via ssh (e.g. `ssh root@192.168.137.19`) or using the HDMI console. ```sh docker pull jforissier/optee_os_ci:qemu_check @@ -206,13 +225,9 @@ docker run -v "$(pwd):/src" -w /src jforissier/optee_os_ci:qemu_check /bin/bash mkdir -p optee && \ cd optee && \ # repo init -u https://github.com/OP-TEE/manifest.git -m qemu_v8.xml -b 4.0.0 - repo init -u https://github.com/edtubbs/manifest.git -m nanopc-t6.xml -b nanopc-t6 && \ + repo init -u https://github.com/OP-TEE/manifest.git -m nanopc-t6.xml -b master && \ export FORCE_UNSAFE_CONFIGURE=1 && \ - repo sync -j 4 && \ - patch -F 4 /src/optee/build/common.mk < /src/src/optee/common.mk.patch && \ - patch /src/optee/build/kconfigs/qemu.conf < /src/src/optee/qemu.conf.patch && \ - patch /src/optee/linux/arch/arm64/boot/dts/rockchip/rk3588-nanopi6-common.dtsi < /src/src/optee/rk3588-nanopi6-common.dtsi.patch && \ - patch /src/optee/u-boot/include/configs/nanopi6.h < /src/src/optee/nanopi6.h.patch && \ + repo sync -j 4 --force-sync && \ cd build && \ make toolchains -j 4 && \ export CFG_TEE_CORE_LOG_LEVEL=0 && \ @@ -263,7 +278,7 @@ docker run -v "$(pwd):/src" -w /src jforissier/optee_os_ci:qemu_check /bin/bash # make -j 4 check make -j 4 && \ cd /src && \ - git clone https://github.com/OP-TEE/optee_client.git && \ + [ ! -d optee_client ] && git clone https://github.com/OP-TEE/optee_client.git && \ cd optee_client && \ mkdir -p build && \ cd build && \ @@ -274,9 +289,9 @@ docker run -v "$(pwd):/src" -w /src jforissier/optee_os_ci:qemu_check /bin/bash make install" ``` -### Building OP-TEE SDK and Client (QEMU ARMv8) +### Step 1 (QEMU): Building OP-TEE SDK and Client -This command builds the SDK (version 3.22.0) and client for ARMv8 QEMU emulation (qemu_v8.xml). For other platforms, change the manifest file in the `repo init` command accordingly. Replace `3.22.0` with the desired version and `qemu_v8.xml` with the desired platform. Refer to the [OP-TEE documentation](https://optee.readthedocs.io/en/latest/building/index.html) for more information. +This command builds the SDK (version 4.0.0) and client for ARMv8 QEMU emulation (qemu_v8.xml). For other platforms, change the manifest file in the `repo init` command accordingly. Replace `4.0.0` with the desired version and `qemu_v8.xml` with the desired platform. Refer to the [OP-TEE documentation](https://optee.readthedocs.io/en/latest/building/index.html) for more information. An RSA private key is generated and overwrites the default Trusted Application (TA) key. This key is used to sign the enclave binaries during development. In the Continuous Integration (CI) environment, an Actions secret is used. Subkeys are generated for testing purposes but are not used to sign the enclave binaries. @@ -291,11 +306,10 @@ docker run -v "$(pwd):/src" -w /src jforissier/optee_os_ci:qemu_check /bin/bash curl https://storage.googleapis.com/git-repo-downloads/repo > /bin/repo && chmod a+x /bin/repo && \ mkdir -p optee && \ cd optee && \ - repo init -u https://github.com/OP-TEE/manifest.git -m qemu_v8.xml -b 4.0.0 + repo init -u https://github.com/OP-TEE/manifest.git -m qemu_v8.xml -b 4.0.0 && \ export FORCE_UNSAFE_CONFIGURE=1 && \ - repo sync -j 4 && \ - patch -F 4 /src/optee/build/common.mk < /src/src/optee/common.mk.patch && \ - patch /src/optee/build/kconfigs/qemu.conf < /src/src/optee/qemu.conf.patch && \ + repo sync -j 4 --force-sync && \ + patch -N -F 4 /src/optee/build/common.mk < /src/src/optee/common.mk.patch && \ cd build && \ make toolchains -j 4 && \ export CFG_TEE_CORE_LOG_LEVEL=0 && \ @@ -345,7 +359,7 @@ docker run -v "$(pwd):/src" -w /src jforissier/optee_os_ci:qemu_check /bin/bash # Build and test the OP-TEE OS and client make -j 4 check cd /src && \ - git clone https://github.com/OP-TEE/optee_client.git && \ + [ ! -d optee_client ] && git clone https://github.com/OP-TEE/optee_client.git && \ cd optee_client && \ mkdir -p build && \ cd build && \ @@ -356,7 +370,9 @@ docker run -v "$(pwd):/src" -w /src jforissier/optee_os_ci:qemu_check /bin/bash make install" ``` -### Building OP-TEE Libdogecoin Key Manager Enclave (QEMU ARMv8 or NanoPC-T6) +### Step 2 (QEMU or NanoPC): Building OP-TEE Libdogecoin Key Manager Enclave + +This command builds the OP-TEE Libdogecoin Key Manager Enclave for QEMU ARMv8 or NanoPC-T6. The enclave is built using the OP-TEE SDK and client. The enclave binary is located in `/doge/libdogecoin/optee/out/bin/libdogecoin.img`. ```sh docker run --privileged -v "$(pwd):/src" -w /src jforissier/optee_os_ci:qemu_check /bin/bash -c "\ @@ -364,10 +380,10 @@ docker run --privileged -v "$(pwd):/src" -w /src jforissier/optee_os_ci:qemu_che apt-get update && \ apt-get install -y autoconf automake libtool-bin build-essential curl python3 valgrind g++-aarch64-linux-gnu qemu-user-static qemu-user && \ - make clean && \ + # Build libdogecoin for Host make -j 4 -C depends HOST=aarch64-linux-gnu && \ ./autogen.sh && \ - ./configure --prefix=/src/depends/aarch64-linux-gnu LIBS=-levent_pthreads --enable-static --disable-shared --enable-test-passwd --enable-optee CFLAGS=-U_FORTIFY_SOURCE HOST=aarch64-linux-gnu && \ + ./configure --prefix=/src/depends/aarch64-linux-gnu LIBS=-levent_pthreads --enable-static --disable-shared --enable-test-passwd HOST=aarch64-linux-gnu && \ make -j 4 && \ make install && \ @@ -381,8 +397,14 @@ docker run --privileged -v "$(pwd):/src" -w /src jforissier/optee_os_ci:qemu_che LDFLAGS=\"-L/src/optee/toolchains/aarch64/lib -L/src/depends/aarch64-linux-gnu/lib -ldogecoin -lunistring\" \ CFLAGS=\"-I/src/optee/toolchains/aarch64/include -I/src/src/optee/ta/include -I/src/depends/aarch64-linux-gnu/include -I/src/depends/aarch64-linux-gnu/include/ykpers-1 -I/src/depends/aarch64-linux-gnu/include/dogecoin\" && \ + # Build libdogecoin for OP-TEE + cd /src/ && \ + ./configure --prefix=/src/depends/aarch64-linux-gnu LIBS=-levent_pthreads --enable-static --disable-shared --enable-test-passwd --enable-optee CFLAGS=-U_FORTIFY_SOURCE HOST=aarch64-linux-gnu && \ + make -j 4 && \ + make install && \ + # Build the Enclave - cd ../ta && \ + cd /src/src/optee/ta && \ make -j 4 \ CROSS_COMPILE=aarch64-linux-gnu- \ LDFLAGS=\"-L/src/depends/aarch64-linux-gnu/lib -ldogecoin -lunistring\" \ @@ -418,9 +440,9 @@ docker run --privileged -v "$(pwd):/src" -w /src jforissier/optee_os_ci:qemu_che exit" ``` -### Running OP-TEE Libdogecoin Key Manager Enclave (on NanoPC-T6) +### Step 3 (NanoPC): Running OP-TEE Libdogecoin Key Manager Enclave -Use scp to copy the /doge/libdogecoin/optee/out/bin/libdogecoin.img to the NanoPC-T6 (e.g. `scp /doge/libdogecoin/optee/out/bin/libdogecoin.img root@192.168.1.1:/root/`). Then, SSH into the NanoPC-T6 and run the following commands: +Use scp to copy the /doge/libdogecoin/optee/out/bin/libdogecoin.img to the NanoPC-T6 (e.g. `scp /doge/libdogecoin/optee/out/bin/libdogecoin.img root@192.168.137.19:/root/`). Then, SSH into the NanoPC-T6 and run the following commands: ```sh mkdir /media/libdogecoin @@ -430,10 +452,10 @@ cp /media/libdogecoin/62d95dc0-7fc2-4cb3-a7f3-c13ae4e633c4.ta /lib/optee_armtz/ ./optee_libdogecoin -c generate_mnemonic ``` -### Running OP-TEE Libdogecoin Key Manager Enclave (in QEMU ARMv8) +### Step 3 (QEMU): Running OP-TEE Libdogecoin Key Manager Enclave ```sh -docker run --device=/dev/bus/usb/001/003 -it -v "$(pwd):/src" -w /src jforissier/optee_os_ci:qemu_check /bin/bash -c "\ +docker run --privileged -v /dev/bus/usb:/dev/bus/usb -it -v "$(pwd):/src" -w /src jforissier/optee_os_ci:qemu_check /bin/bash -c "\ chmod 777 /src/optee/qemu/build/aarch64-softmmu/qemu-system-aarch64 && \ cd /src/optee/out/bin && \ /src/optee/qemu/build/aarch64-softmmu/qemu-system-aarch64 \ @@ -459,7 +481,7 @@ docker run --device=/dev/bus/usb/001/003 -it -v "$(pwd):/src" -w /src jforissier -device usb-host,vendorid=0x1050,productid=0x0407" ``` -`--device=/dev/bus/usb/001/003` is used to pass the YubiKey device to the container. Replace the device path with the appropriate path for your system. +`-v /dev/bus/usb:/dev/bus/usb` is used to pass the USB bus and YubiKey device to the container. The `-device usb-host,vendorid=0x1050,productid=0x0407` flag is used to pass the YubiKey to the QEMU VM. Replace `0x1050` and `0x0407` with the YubiKey's vendor and product IDs. The QEMU ARMv8 emulator will boot the OP-TEE OS and Trusted Firmware, and the libdogecoin TA will be loaded into the enclave. The host application can then interact with the enclave to perform key management operations. @@ -510,22 +532,28 @@ cd libdogecoin This command uses package installs for the OpenEnclave SDK and Docker to build the OpenEnclave Libdogecoin Key Manager Enclave. Docker is used to build the enclave in a clean environment. Refer to the [OpenEnclave documentation](https://github.com/openenclave/openenclave/blob/master/docs/GettingStartedDocs/Contributors/BuildingInADockerContainer.md) for more information. ```sh -docker run -v $PWD:/src -w /src ubuntu:20.04 bash -c "\ +docker run --device /dev/sgx_enclave:/dev/sgx_enclave --device /dev/sgx_provision:/dev/sgx_provision -it -v $PWD:/src -w /src ubuntu:20.04 bash -c "\ # Set up the environment and build libdogecoin export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ apt-get install -y autoconf automake libtool-bin build-essential curl python3 valgrind python3-dev python3-dbg pkg-config && \ cd /src && \ + + # Build libdogecoin for Enclave make -j 4 -C depends HOST=x86_64-pc-linux-gnu && \ - make clean && \ ./autogen.sh && \ ./configure --prefix=/src/depends/x86_64-pc-linux-gnu --enable-openenclave --enable-test-passwd CFLAGS=-U_FORTIFY_SOURCE && \ make && \ make install && \ + # Build libdogecoin for Host + make -j 4 -C depends HOST=x86_64-pc-linux-gnu/host && \ + ./configure --prefix=/src/depends/x86_64-pc-linux-gnu/host --enable-test-passwd && \ + make && \ + make install && \ + # Set up the OpenEnclave environment and build the enclave apt-get install -y wget gnupg2 cmake && \ - cd /src/src/openenclave && \ echo 'deb [arch=amd64] https://download.01.org/intel-sgx/sgx_repo/ubuntu focal main' | tee /etc/apt/sources.list.d/intel-sgx.list && \ wget -qO - https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key | apt-key add - && \ echo 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-11 main' | tee /etc/apt/sources.list.d/llvm-toolchain-focal-11.list && \ @@ -536,9 +564,13 @@ docker run -v $PWD:/src -w /src ubuntu:20.04 bash -c "\ apt -y install clang-11 libssl-dev gdb libsgx-enclave-common libsgx-quote-ex libprotobuf17 libsgx-dcap-ql libsgx-dcap-ql-dev az-dcap-client open-enclave && \ apt -y install dkms && \ source /opt/openenclave/share/openenclave/openenclaverc && \ - mkdir -p build && cd build && cmake .. && make && make simulate" + mkdir -p /src/src/openenclave/build && cd /src/src/openenclave/build && \ + cmake .. && make && make run && \ + exec bash" ``` +Once the build is complete, see the OpenEnclave Host Command Line Tutorial below for instructions on running the OpenEnclave Libdogecoin Key Manager Enclave. + ## Host Command Line Tutorials In this section, we provide a step-by-step tutorial on using the host command line interface to interact with the key management enclaves in both environments. This tutorial will cover the basic operations including generating mnemonic seedphrases, generating public keys, generating addresses, signing messages, and signing transactions. @@ -553,12 +585,12 @@ Before proceeding, ensure you have successfully built and set up the OP-TEE and Generate a mnemonic seedphrase for backup and recovery purposes. This is the first step in creating a new wallet. It will only be displayed once, so make sure to back it up. -Enter the shared secret for TOTP when prompted. The shared secret must be 40 hex characters (*e.g., `f38243e0e3e97a5c8aa5cc481a815add6c119648`*). The shared secret will be set on the YubiKey (if present) by the host application, and the mnemonic will be generated and displayed. If OTP Slot 1 is already programmed, the host application will prompt you to overwrite it. +This command will generate a mnemonic seedphrase and display it on the screen. Either a shared secret for TOTP or a password must be provided. All other flags are optional, and the user will be prompted for input if not provided. -This command will generate a mnemonic seedphrase and display it on the screen. All flags are optional, and the user will be prompted for input if not provided. +Enter the shared secret for TOTP when prompted if enabled. The shared secret must be 40 hex characters (*e.g., `f38243e0e3e97a5c8aa5cc481a815add6c119648`*). If no shared secret is provided, a random one will be generated. The shared secret will be set on the YubiKey (if present) by the host application, and the mnemonic will be generated and displayed. If OTP Slot 1 is already programmed, the host application will prompt you to overwrite it. Ensure that your YubiKey slot configuration is not overwritten unless intended. ```sh -optee_libdogecoin -c generate_mnemonic -n -s -e -p or -u -f +optee_libdogecoin -c generate_mnemonic -n -s -e -p -f -z ``` The `-n` flag is used to provide the mnemonic input for recovery purposes if needed. Replace `` with the mnemonic seedphrase you want to use for recovery. If no mnemonic input is provided, the enclave will generate a random mnemonic seedphrase. @@ -569,20 +601,28 @@ The `-e` flag is used to provide the entropy size for the mnemonic seedphrase. R The `-p` flag is used to provide the password for the mnemonic seedphrase. Replace `` with the password you want to use for the mnemonic seedphrase. -The `-u` flag is used to prompt the user for input. If the `-u` flag is provided, the user will be prompted for a password. - The `-f` flag is used to provide additional flags for the mnemonic seedphrase. Replace `` with the desired flags for the mnemonic seedphrase. The `"delegate"` flag is used to delegate account keys to a third party. +The `-z` flag is used to enable YubiKey authentication. If the YubiKey is present, the shared secret will be set on the YubiKey. + #### Generating an Extended Public Key Generate an extended public key using the account and change level. The change level can be set to 0 for external addresses and 1 for internal addresses. The account number is the BIP-44 account. The public key will be derived from the seedphrase stored within the enclave. -The TOTP code will be retrieved from the YubiKey (if present) and used as the authentication token for this operation. +The `-o` flag is used to provide the account number for the extended public key. Replace `` with the desired account number. + +The `-l` flag is used to provide the change level for the extended public key. Replace `` with the desired change level. + +The `-h` flag is used to provide a custom path for the extended public key. Replace `` with the desired path for the extended public key. The `-a` flag is used to provide the auth token if a Yubikey is not present. Use a tool like `oathtool` to generate TOTP (*e.g., `oathtool --totp "f38243e0e3e97a5c8aa5cc481a815add6c119648"`). +The `-p` flag is used to provide the password for the mnemonic seedphrase. + +The `-z` flag is used to enable YubiKey authentication. If the YubiKey is present, the TOTP code will be retrieved from the YubiKey and used as the authentication token. + ```sh -optee_libdogecoin -c generate_extended_public_key -o -l -a -p or -u +optee_libdogecoin -c generate_extended_public_key -o -l -h -a or -p -z ``` #### Generating an Address @@ -590,27 +630,27 @@ optee_libdogecoin -c generate_extended_public_key -o -l -l -i -a -p or -u +optee_libdogecoin -c generate_address -o -l -i -h -a or -p -z ``` Replace ``, ``, and `` with appropriate values. The change level can be set to 0 for external addresses and 1 for internal addresses. The account number is the BIP-44 account. The address index is the index of the address within the account. The address will be derived from the seedphrase stored within the enclave. #### Signing a Message -Sign a message using the private key stored within the enclave. The message will be signed using the private key derived from the seedphrase stored within the enclave. +Sign a message using the private key stored within the enclave. The message will be signed using the private key derived from the seedphrase stored within the enclave. If no message is provided, an example message will be signed for demonstration purposes. ```sh -optee_libdogecoin -c sign_message -o -l -i -m -a -p or -u +optee_libdogecoin -c sign_message -o -l -i -m -h -a or -p -z ``` Replace `` with the message you want to sign. #### Signing a Transaction -Sign a raw transaction using the private key stored within the enclave. A raw transaction is a hexadecimal string representing the transaction data. Currently, if no transaction data is provided, an example transaction will be signed for demonstration purposes. The private key will be derived from the seedphrase stored within the enclave. +Sign a raw transaction using the private key stored within the enclave. The transaction will be signed using the private key derived from the seedphrase stored within the enclave. A raw transaction is a hexadecimal string representing the transaction data. Currently, if no transaction data is provided, an example transaction will be signed for demonstration purposes. ```sh -optee_libdogecoin -c sign_transaction -o -l -i -t -a -p or -u +optee_libdogecoin -c sign_transaction -o -l -i -t -h -a or -p -z ``` Replace `` with the raw transaction data. @@ -620,7 +660,7 @@ Replace `` with the raw transaction data. Delegate account keys to a third party. This operation allows a third party to manage the keys for a specific account. The third party will be able to export the account keys on behalf of the user using the delegate password. ```sh -optee_libdogecoin -c delegate_key -o -d or -u -a -p +optee_libdogecoin -c delegate_key -o -d -h -a or -p -z ``` Replace `` with the password for the delegate account. @@ -630,7 +670,7 @@ Replace `` with the password for the delegate account. Export the delegated account keys using the delegate password. This operation allows the third party to export the account keys on behalf of the user. ```sh -optee_libdogecoin -c export_delegate_key -o -d or -u +optee_libdogecoin -c export_delegate_key -o -d ``` Replace `` with the password for the delegate account. @@ -643,54 +683,74 @@ Note: In OpenEnclave, `--simulate` is used to run the enclave in simulation mode Generate a mnemonic seedphrase for backup and recovery purposes. This is the first step in creating a new wallet. It will only be displayed once, so make sure to back it up. -Enter the shared secret for TOTP when prompted. The shared secret must be 40 hex characters (*e.g., `f38243e0e3e97a5c8aa5cc481a815add6c119648`*). The shared secret will be set on the YubiKey (if present) by the host application, and the mnemonic will be generated and displayed. If OTP Slot 1 is already programmed, the host application will prompt you to overwrite it. +This command will generate a mnemonic seedphrase and display it on the screen. Either a shared secret for TOTP or a password must be provided. All other flags are optional, and the user will be prompted for input if not provided. -This command will generate a mnemonic seedphrase and display it on the screen. All flags are optional, and the user will be prompted for input if not provided. +Enter the shared secret for TOTP when prompted if enabled. The shared secret must be 40 hex characters (*e.g., `f38243e0e3e97a5c8aa5cc481a815add6c119648`*). If no shared secret is provided, a random one will be generated. The shared secret will be set on the YubiKey (if present) by the host application, and the mnemonic will be generated and displayed. If OTP Slot 1 is already programmed, the host application will prompt you to overwrite it. ```sh -/doge/libdogecoin/src/openenclave/build/host/host /doge/libdogecoin/src/openenclave/build/enclave/enclave.signed --simulate -c generate_mnemonic -n -s -e +/doge/libdogecoin/src/openenclave/build/host/host /doge/libdogecoin/src/openenclave/build/enclave/enclave.signed --simulate -c generate_mnemonic -n -s -e -p -z ``` +The `-n` flag is used to provide the mnemonic input for recovery purposes if needed. Replace `` with the mnemonic seedphrase you want to use for recovery. If no mnemonic input is provided, the enclave will generate a random mnemonic seedphrase. + +The `-s` flag is used to provide the shared secret for TOTP authentication from command line instead of prompting the user. Replace `` with the shared secret you want to use for TOTP authentication. + +The `-e` flag is used to provide the entropy size for the mnemonic seedphrase. Replace `` with the desired entropy size in bits. If no entropy size is provided, the default value of `"256"` bits (24 words) will be used. + +The `-p` flag is used to provide the password for the mnemonic seedphrase. Replace `` with the password you want to use for the mnemonic seedphrase. + +The `-z` flag is used to enable YubiKey authentication. If the YubiKey is present, the shared secret will be set on the YubiKey. + #### Generating an Extended Public Key Generate an extended public key using the account and change level. The change level can be set to 0 for external addresses and 1 for internal addresses. The account number is the BIP-44 account. The TOTP code will be retrieved from the YubiKey (if present) and used as the authentication token for this operation. -The `-a` flag is used to provide the auth token if a Yubikey is not present. Use a tool like `oathtool` to generate TOTP (*e.g., `oathtool --totp "f38243e0e3e97a5c8aa5cc481a815add6c119648"`). - ```sh -/doge/libdogecoin/src/openenclave/build/host/host /doge/libdogecoin/src/openenclave/build/enclave/enclave.signed --simulate -c generate_extended_public_key -o -l +/doge/libdogecoin/src/openenclave/build/host/host /doge/libdogecoin/src/openenclave/build/enclave/enclave.signed --simulate -c generate_extended_public_key -o -l -h -a or -p -z ``` +The `-o` flag is used to provide the account number for the extended public key. Replace `` with the desired account number. + +The `-l` flag is used to provide the change level for the extended public key. Replace `` with the desired change level. + +The `-h` flag is used to provide a custom path for the extended public key. Replace `` with the desired path for the extended public key. + +The `-a` flag is used to provide the auth token if a Yubikey is not present. Use a tool like `oathtool` to generate TOTP (*e.g., `oathtool --totp "f38243e0e3e97a5c8aa5cc481a815add6c119648"`). + +The `-p` flag is used to provide the password for the mnemonic seedphrase. + +The `-z` flag is used to enable YubiKey authentication. If the YubiKey is present, the TOTP code will be retrieved from the YubiKey and used as the authentication token. + #### Generating an Address Generate a Dogecoin address using the account, address index, and change level. ```sh -/doge/libdogecoin/src/openenclave/build/host/host /doge/libdogecoin/src/openenclave/build/enclave/enclave.signed --simulate -c generate_address -o -l -i +/doge/libdogecoin/src/openenclave/build/host/host /doge/libdogecoin/src/openenclave/build/enclave/enclave.signed --simulate -c generate_address -o -l -i -h -a or -p -z ``` Replace ``, ``, and `` with appropriate values. #### Signing a Message -Sign a message using the private key stored within the enclave. +Sign a message using the private key stored within the enclave. If no message is provided, an example message will be signed for demonstration purposes. ```sh -/doge/libdogecoin/src/openenclave/build/host/host /doge/libdogecoin/src/openenclave/build/enclave/enclave.signed --simulate -c sign_message -o -l -i -m "This is just a test message" +/doge/libdogecoin/src/openenclave/build/host/host /doge/libdogecoin/src/openenclave/build/enclave/enclave.signed --simulate -c sign_message -o -l -i -m "This is just a test message" -h -a or -p -z ``` +Replace `"This is just a test message"` with the message you want to sign. + #### Signing a Transaction -Sign a raw transaction using the private key stored within the enclave. A raw transaction is a hexadecimal string representing the transaction data. Currently, if no transaction data is provided, an example transaction will be signed for demonstration purposes. +Sign a raw transaction using the private key stored within the enclave. The transaction will be signed using the private key derived from the seedphrase stored within the enclave. A raw transaction is a hexadecimal string representing the transaction data. Currently, if no transaction data is provided, an example transaction will be signed for demonstration purposes. ```sh -/doge/libdogecoin/src/openenclave/build/host/host /doge/libdogecoin/src/openenclave/build/enclave/enclave.signed --simulate -c sign_transaction -o -l -i -t +/doge/libdogecoin/src/openenclave/build/host/host /doge/libdogecoin/src/openenclave/build/enclave/enclave.signed --simulate -c sign_transaction -o -l -i -t -h -a or -p -z ``` -Replace ``, ``, and `` with appropriate values. The change level can be set to 0 for external addresses and 1 for internal addresses. The account number is the BIP-44 account. The address index is the index of the address within the account. - Replace `` with the raw transaction data. ## Conclusion diff --git a/include/dogecoin/libdogecoin.h b/include/dogecoin/libdogecoin.h index 26686cbf..088acac3 100644 --- a/include/dogecoin/libdogecoin.h +++ b/include/dogecoin/libdogecoin.h @@ -291,6 +291,8 @@ dogecoin_bool deriveBIP44ExtendedPublicKey( /* utilities */ uint8_t* utils_hex_to_uint8(const char* str); char* utils_uint8_to_hex(const uint8_t* bin, size_t l); +void utils_hex_to_bin(const char* str, unsigned char* out, size_t inLen, size_t* outLen); +void utils_bin_to_hex(unsigned char* bin_in, size_t inlen, char* hex_out); char* getpass(const char *prompt); /* Advanced API functions for mnemonic seedphrase generation diff --git a/src/openenclave/enclave/CMakeLists.txt b/src/openenclave/enclave/CMakeLists.txt index 91a6448d..cd8a5475 100644 --- a/src/openenclave/enclave/CMakeLists.txt +++ b/src/openenclave/enclave/CMakeLists.txt @@ -30,12 +30,15 @@ if (LVI_MITIGATION MATCHES ControlFlow) # Link against LVI-mitigated libraries. target_link_libraries( enclave openenclave::oeenclave-lvi-cfg + $ openenclave::oecrypto${OE_CRYPTO_LIB}-lvi-cfg "libdogecoin.a" "libevent_core.a" "libunistring.a" openenclave::oelibc-lvi-cfg) else () target_link_libraries( - enclave openenclave::oeenclave openenclave::oecrypto${OE_CRYPTO_LIB} + enclave openenclave::oeenclave + $ + openenclave::oecrypto${OE_CRYPTO_LIB} "libdogecoin.a" "libevent_core.a" "libunistring.a" openenclave::oelibc) diff --git a/src/openenclave/enclave/enc.c b/src/openenclave/enclave/enc.c index a007479e..77569155 100644 --- a/src/openenclave/enclave/enc.c +++ b/src/openenclave/enclave/enc.c @@ -390,102 +390,8 @@ void enclave_libdogecoin_run_example() // TOTP Shared secret size in bytes #define TOTP_SECRET_SIZE 20 -// Wrap sealing function for simulation mode -oe_result_t oe_seal_wrap( - const oe_uuid_t* plugin_id, - const oe_seal_setting_t* settings, - size_t settings_count, - const uint8_t* plaintext, - size_t plaintext_size, - const uint8_t* additional_data, - size_t additional_data_size, - uint8_t** blob, - size_t* blob_size) -{ - // Try the real sealing operation first - oe_result_t result = oe_seal( - plugin_id, - settings, - settings_count, - plaintext, - plaintext_size, - additional_data, - additional_data_size, - blob, - blob_size); - - if (result == OE_OK) - { - return result; - } - else - { - fprintf(stdout, "Simulating sealing operation\n"); - // Check if only the size is requested - if (blob == NULL) - { - *blob_size = plaintext_size; - return OE_OK; - } - - // Normal sealing operation (mocked behavior) - if (blob_size == NULL) - return OE_INVALID_PARAMETER; - - *blob_size = plaintext_size; - *blob = (uint8_t*)malloc(*blob_size); - if (*blob == NULL) - return OE_OUT_OF_MEMORY; - - memcpy(*blob, plaintext, plaintext_size); - return OE_OK; - } -} - -oe_result_t oe_unseal_wrap( - const uint8_t* sealed_blob, - size_t sealed_blob_size, - const uint8_t* additional_data, - size_t additional_data_size, - uint8_t** plaintext, - size_t* plaintext_size) -{ - // Check for invalid parameters - if (sealed_blob == NULL || plaintext == NULL || plaintext_size == NULL) - { - return OE_INVALID_PARAMETER; - } - - // Try to the real unsealing operation first - oe_result_t result = oe_unseal( - sealed_blob, - sealed_blob_size, - additional_data, - additional_data_size, - plaintext, - plaintext_size); - - if (result == OE_OK) - { - return result; - } - else - { - fprintf(stdout, "Simulating unsealing operation\n"); - // Allocate memory for the plaintext - *plaintext = (uint8_t*)malloc(sealed_blob_size); - if (*plaintext == NULL) - { - return OE_OUT_OF_MEMORY; - } - - // Copy the sealed data to the plaintext buffer - memcpy(*plaintext, sealed_blob, sealed_blob_size); - *plaintext_size = sealed_blob_size; - - return OE_OK; - } -} +// Maximum size of managed credentials (shared secret, password) +#define MAX_MANAGED_CREDS_SIZE 1024 uint32_t get_totp(const char* shared_secret, uint64_t timestamp) { uint8_t hmac[SHA1_DIGEST_LENGTH]; @@ -545,45 +451,51 @@ void enclave_libdogecoin_attest(uint8_t* report, size_t len) dogecoin_free(report_buffer); } -void enclave_libdogecoin_generate_encrypted_seed(uint8_t** encrypted_blob, size_t* len) { +void enclave_libdogecoin_generate_encrypted_seed(data_t* encrypted_blob) { oe_result_t result; SEED seed; + uint8_t* blob; + size_t blob_size; // Generate a new seed if (!dogecoin_random_bytes(seed, sizeof(seed), 1)) { fprintf(stderr, "Failed to generate random bytes\n"); // Handle error - *encrypted_blob = NULL; - *len = 0; + encrypted_blob->size = 0; } // Initialize the seal key info - const oe_seal_setting_t settings[] = {OE_SEAL_SET_POLICY(OE_SEAL_POLICY_UNIQUE)}; - result = oe_seal_wrap( + const oe_seal_setting_t settings[1] = {OE_SEAL_SET_POLICY(OE_SEAL_POLICY_UNIQUE)}; + result = oe_seal( NULL, settings, - sizeof(settings) / sizeof(*settings), + 1, (const uint8_t*) seed, // Data to seal sizeof(seed), // Size of data NULL, // No additional data 0, // No additional data size - encrypted_blob, // Output: Pointer to the encrypted blob - len); // Output: Size of the encrypted blob + &blob, // Output: Pointer to the encrypted blob + &blob_size); // Output: Size of the encrypted blob - if (result != OE_OK) { + if (result == OE_OK) { + // Set the output parameters + encrypted_blob->data = blob; + encrypted_blob->size = blob_size; + } else { fprintf(stderr, "Sealing failed with %d\n", result); // Handle error - *encrypted_blob = NULL; - *len = 0; + encrypted_blob->size = 0; } } // ECALL to generate and encrypt a new master keypair -void enclave_libdogecoin_generate_master_key(uint8_t** encrypted_blob, size_t* len) { +void enclave_libdogecoin_generate_master_key(data_t* encrypted_blob) { oe_result_t result; char hd_master_privkey[HDKEYLEN]; char p2pkh_master_pubkey[P2PKHLEN]; + uint8_t* blob; + size_t blob_size; // Set the Open Enclave random number generator in libdogecoin set_rng (&oe_random); @@ -596,41 +508,46 @@ void enclave_libdogecoin_generate_master_key(uint8_t** encrypted_blob, size_t* l else { printf("Error occurred.\n"); // Handle error - *encrypted_blob = NULL; - *len = 0; + encrypted_blob->size = 0; } dogecoin_ecc_stop(); // Initialize the seal key info - const oe_seal_setting_t settings[] = {OE_SEAL_SET_POLICY(OE_SEAL_POLICY_UNIQUE)}; - result = oe_seal_wrap( + const oe_seal_setting_t settings[1] = {OE_SEAL_SET_POLICY(OE_SEAL_POLICY_UNIQUE)}; + result = oe_seal( NULL, settings, - sizeof(settings) / sizeof(*settings), + 1, (const uint8_t*) hd_master_privkey, // Data to seal sizeof(hd_master_privkey), // Size of data NULL, // No additional data 0, // No additional data size - encrypted_blob, // Output: Pointer to the encrypted blob - len); // Output: Size of the encrypted blob + &blob, // Output: Pointer to the encrypted blob + &blob_size); // Output: Size of the encrypted blob - if (result != OE_OK) { + if (result == OE_OK) { + // Set the output parameters + encrypted_blob->data = blob; + encrypted_blob->size = blob_size; + } else { fprintf(stderr, "Sealing failed with %d\n", result); // Handle error - *encrypted_blob = NULL; - *len = 0; + encrypted_blob->size = 0; } } // ECALL to generate and encrypt a new mnemonic -void enclave_libdogecoin_generate_mnemonic(uint8_t** encrypted_blob, size_t* len, char* mnemonic, const char* shared_secret, const MNEMONIC mnemonic_input, const ENTROPY_SIZE entropy_size) { +void enclave_libdogecoin_generate_mnemonic(data_t* encrypted_blob, char* mnemonic, char* shared_secret, MNEMONIC mnemonic_input, ENTROPY_SIZE entropy_size, char* password) { // Validate the input parameters - if (encrypted_blob == NULL || len == NULL || mnemonic == NULL || shared_secret == NULL) { + if (mnemonic == NULL || (shared_secret == NULL && password == NULL)) { fprintf(stderr, "Invalid input parameters\n"); + encrypted_blob->size = 0; return; } + uint8_t* blob; + size_t blob_size; oe_result_t result; // Set the Open Enclave random number generator in libdogecoin @@ -648,9 +565,8 @@ void enclave_libdogecoin_generate_mnemonic(uint8_t** encrypted_blob, size_t* len int mnemonicResult = generateRandomEnglishMnemonic("256", mnemonic); if (mnemonicResult != 0) { fprintf(stderr, "Failed to generate mnemonic\n"); - // Handle error - *encrypted_blob = NULL; - *len = 0; + encrypted_blob->size = 0; + dogecoin_ecc_stop(); return; } } else { @@ -658,89 +574,45 @@ void enclave_libdogecoin_generate_mnemonic(uint8_t** encrypted_blob, size_t* len int mnemonicResult = generateRandomEnglishMnemonic(entropy_size, mnemonic); if (mnemonicResult != 0) { fprintf(stderr, "Failed to generate mnemonic\n"); - // Handle error - *encrypted_blob = NULL; - *len = 0; + encrypted_blob->size = 0; + dogecoin_ecc_stop(); return; } } } - // Calculate the total length - size_t total_length = strlen(mnemonic) + strlen(shared_secret) + 2; // +1 for the comma separator, +1 for the null terminator - - // Allocate the array - uint8_t* mnemonic_and_shared_secret = (uint8_t*)malloc(total_length); - - // Check if the allocation was successful - if (mnemonic_and_shared_secret == NULL) { - fprintf(stderr, "Memory allocation failed\n"); - return; - } - - // Concatenate the mnemonic and shared_secret with a comma separator - snprintf((char*)mnemonic_and_shared_secret, total_length, "%s,%s", mnemonic, shared_secret); + // Concatenate the mnemonic and managed credentials + char mnemonic_and_creds[MAX_MNEMONIC_SIZE + MAX_MANAGED_CREDS_SIZE] = {0}; + snprintf(mnemonic_and_creds, sizeof(mnemonic_and_creds), "%s,%s,%s", mnemonic, shared_secret ? shared_secret : "", password ? password : ""); dogecoin_ecc_stop(); // Initialize the seal key info const oe_seal_setting_t settings[1] = {OE_SEAL_SET_POLICY(OE_SEAL_POLICY_UNIQUE)}; - // Determine the size of the encrypted blob - size_t encrypted_blob_size = 0; - result = oe_seal_wrap( - NULL, // No specific plugin_id - settings, // Seal settings - sizeof(settings) / sizeof(settings[0]), // Number of settings - mnemonic_and_shared_secret, // Data to seal - total_length, // Size of data - NULL, // No additional data - 0, // No additional data size - NULL, // Output: Pointer to the encrypted blob (NULL to get the size) - &encrypted_blob_size); // Output: Size of the encrypted blob - - if (result != OE_OK) { - fprintf(stderr, "Failed to get encrypted blob size: %d\n", result); - free(mnemonic_and_shared_secret); - return; - } - - // Allocate memory for the encrypted blob - *encrypted_blob = (uint8_t*)malloc(encrypted_blob_size); - if (*encrypted_blob == NULL) { - fprintf(stderr, "Memory allocation failed for encrypted blob\n"); - free(mnemonic_and_shared_secret); - return; - } - // Perform the actual sealing - result = oe_seal_wrap( + result = oe_seal( NULL, // No specific plugin_id settings, // Seal settings - sizeof(settings) / sizeof(settings[0]), // Number of settings - mnemonic_and_shared_secret, // Data to seal - total_length, // Size of data + 1, // Number of settings + (const uint8_t*) mnemonic_and_creds, // Data to seal + sizeof(mnemonic_and_creds), // Size of data NULL, // No additional data 0, // No additional data size - encrypted_blob, // Output: Pointer to the encrypted blob - &encrypted_blob_size); // Output: Size of the encrypted blob + &blob, // Output: Pointer to the encrypted blob + &blob_size); // Output: Size of the encrypted blob - // Print out the encrypted blob and length if (result == OE_OK) { - *len = encrypted_blob_size; + // Set the output parameters + encrypted_blob->data = blob; + encrypted_blob->size = blob_size; } else { fprintf(stderr, "Sealing failed with %d\n", result); - // Handle error - free(*encrypted_blob); - *encrypted_blob = NULL; - *len = 0; + encrypted_blob->size = 0; } - - // Free the allocated memory - free(mnemonic_and_shared_secret); } -void enclave_libdogecoin_generate_extended_public_key(uint8_t* encrypted_blob, size_t len, char* pubkey, uint32_t* account, const char* change_level, const uint32_t auth_token) +void enclave_libdogecoin_generate_extended_public_key(const data_t* encrypted_blob, char* custom_path, char* pubkey, uint32_t account, char* change_level, const uint32_t auth_token, char* password) { // Validate the input parameters if (encrypted_blob == NULL || pubkey == NULL) { @@ -752,11 +624,13 @@ void enclave_libdogecoin_generate_extended_public_key(uint8_t* encrypted_blob, s size_t persistent_data_size = 0; char* mnemonic = NULL; char* shared_secret = NULL; + char* stored_password = NULL; + size_t password_size = password ? strlen(password) : 0; // Unseal the data - oe_result_t result = oe_unseal_wrap( - encrypted_blob, - len, + oe_result_t result = oe_unseal( + encrypted_blob->data, + encrypted_blob->size, NULL, 0, &persistent_data, @@ -768,8 +642,32 @@ void enclave_libdogecoin_generate_extended_public_key(uint8_t* encrypted_blob, s } // Split the persistent data into mnemonic and shared secret - mnemonic = strtok(persistent_data, ","); - shared_secret = strtok(NULL, ","); + mnemonic = strsep((char**)&persistent_data, ","); + if (!mnemonic) { + fprintf(stderr, "Failed to parse mnemonic\n"); + return; + } + shared_secret = strsep((char**)&persistent_data, ","); + if (!shared_secret) { + shared_secret = ""; + } + stored_password = strsep((char**)&persistent_data, ","); + if (!stored_password) { + stored_password = ""; + } + + // If the auth token is 0 and no password is provided, then return + if (auth_token == 0 && !password) { + fprintf(stderr, "Password or auth token required"); + return; + } + + // Verify that the password is part of the managed credentials, if supplied + if ((password && strcmp(password, stored_password)) || + (!password && strcmp(stored_password, "") != 0)) { + fprintf(stderr, "Password verification failed\n"); + return; + } // Verify TOTP using the shared secret struct timeval tv; @@ -777,22 +675,24 @@ void enclave_libdogecoin_generate_extended_public_key(uint8_t* encrypted_blob, s fprintf(stderr, "Failed to get time\n"); return; } - uint32_t current_time = tv.tv_sec; - uint32_t totp = get_totp((const char*) shared_secret, current_time / TOTP_TIME_STEP); - char totp_str[AUTH_TOKEN_LEN + 1]; // +1 for the null terminator - snprintf(totp_str, sizeof(totp_str), "%06u", totp); + if (strcmp(shared_secret, "") != 0) { + uint32_t current_time = tv.tv_sec; + uint32_t totp = get_totp((const char*) shared_secret, current_time / TOTP_TIME_STEP); - char auth_token_str[AUTH_TOKEN_LEN + 1]; // +1 for the null terminator - snprintf(auth_token_str, sizeof(auth_token_str), "%06u", auth_token); - - if (strcmp(totp_str, auth_token_str) != 0) { - printf("TOTP verification failed\n"); - return; + if (totp != auth_token) { + printf("TOTP verification failed\n"); + return; + } + } else { + if (strcmp(password, (const char*) stored_password) != 0) { + printf("Password verification failed\n"); + return; + } } SEED seed; - dogecoin_seed_from_mnemonic((const char*)mnemonic, NULL, seed); + dogecoin_seed_from_mnemonic((const char*)mnemonic, password, seed); dogecoin_ecc_start(); @@ -801,7 +701,11 @@ void enclave_libdogecoin_generate_extended_public_key(uint8_t* encrypted_blob, s char derived_pubkey[HDKEYLEN]; char keypath[BIP44_KEY_PATH_MAX_LENGTH + 1]; - deriveBIP44ExtendedPublicKey(master_key, account, change_level, NULL, NULL, derived_pubkey, keypath); + if (!deriveBIP44ExtendedPublicKey(master_key, &account, change_level, NULL, custom_path, derived_pubkey, keypath)) { + fprintf(stderr, "Failed to derive extended public key\n"); + dogecoin_ecc_stop(); + return; + } strncpy(pubkey, derived_pubkey, strlen(derived_pubkey)); pubkey[strlen(derived_pubkey)] = '\0'; @@ -811,7 +715,7 @@ void enclave_libdogecoin_generate_extended_public_key(uint8_t* encrypted_blob, s dogecoin_free(persistent_data); } -void enclave_libdogecoin_generate_address(uint8_t* encrypted_blob, size_t len, char* addresses, uint32_t account, uint32_t address_index, const char* change_level, uint32_t num_addresses, const uint32_t auth_token) +void enclave_libdogecoin_generate_address(const data_t* encrypted_blob, char* custom_path, char* addresses, uint32_t account, uint32_t address_index, char* change_level, uint32_t num_addresses, const uint32_t auth_token, char* password) { // Validate the input parameters if (encrypted_blob == NULL || addresses == NULL) { @@ -823,11 +727,13 @@ void enclave_libdogecoin_generate_address(uint8_t* encrypted_blob, size_t len, c size_t persistent_data_size = 0; char* mnemonic = NULL; char* shared_secret = NULL; + char* stored_password = NULL; + size_t password_size = password ? strlen(password) : 0; // Unseal the data - oe_result_t result = oe_unseal_wrap( - encrypted_blob, - len, + oe_result_t result = oe_unseal( + encrypted_blob->data, + encrypted_blob->size, NULL, 0, &persistent_data, @@ -839,8 +745,32 @@ void enclave_libdogecoin_generate_address(uint8_t* encrypted_blob, size_t len, c } // Split the persistent data into mnemonic and shared secret - mnemonic = strtok(persistent_data, ","); - shared_secret = strtok(NULL, ","); + mnemonic = strsep((char**)&persistent_data, ","); + if (!mnemonic) { + fprintf(stderr, "Failed to parse mnemonic\n"); + return; + } + shared_secret = strsep((char**)&persistent_data, ","); + if (!shared_secret) { + shared_secret = ""; + } + stored_password = strsep((char**)&persistent_data, ","); + if (!stored_password) { + stored_password = ""; + } + + // If the auth token is 0 and no password is provided, then return + if (auth_token == 0 && !password) { + fprintf(stderr, "Password or auth token required"); + return; + } + + // Verify that the password is part of the managed credentials, if supplied + if ((password && strcmp(password, stored_password)) || + (!password && strcmp(stored_password, "") != 0)) { + fprintf(stderr, "Password verification failed\n"); + return; + } // Verify TOTP using the shared secret struct timeval tv; @@ -848,22 +778,24 @@ void enclave_libdogecoin_generate_address(uint8_t* encrypted_blob, size_t len, c fprintf(stderr, "Failed to get time\n"); return; } - uint32_t current_time = tv.tv_sec; - uint32_t totp = get_totp((const char*) shared_secret, current_time / TOTP_TIME_STEP); - - char totp_str[AUTH_TOKEN_LEN + 1]; // +1 for the null terminator - snprintf(totp_str, sizeof(totp_str), "%06u", totp); - char auth_token_str[AUTH_TOKEN_LEN + 1]; // +1 for the null terminator - snprintf(auth_token_str, sizeof(auth_token_str), "%06u", auth_token); + if (strcmp(shared_secret, "") != 0) { + uint32_t current_time = tv.tv_sec; + uint32_t totp = get_totp((const char*) shared_secret, current_time / TOTP_TIME_STEP); - if (strcmp(totp_str, auth_token_str) != 0) { - printf("TOTP verification failed\n"); - return; + if (totp != auth_token) { + printf("TOTP verification failed\n"); + return; + } + } else { + if (strcmp(password, (const char*) stored_password) != 0) { + printf("Password verification failed\n"); + return; + } } SEED seed; - dogecoin_seed_from_mnemonic((const char*)mnemonic, NULL, seed); + dogecoin_seed_from_mnemonic((const char*)mnemonic, password, seed); dogecoin_ecc_start(); @@ -874,18 +806,22 @@ void enclave_libdogecoin_generate_address(uint8_t* encrypted_blob, size_t len, c memset(address_p2pkh, 0, sizeof(address_p2pkh)); size_t offset = 0; - for (uint32_t i = 0; i < num_addresses; i++) - { - char derived_pubkey[HDKEYLEN]; - char keypath[BIP44_KEY_PATH_MAX_LENGTH + 1]; - deriveBIP44ExtendedPublicKey(master_key, &account, NULL, NULL, NULL, derived_pubkey, keypath); - getDerivedHDAddressFromAcctPubKey(derived_pubkey, address_index, change_level, address_p2pkh + offset, false); - offset += strlen(address_p2pkh + offset); - if (i < num_addresses - 1) + if (custom_path) { + getDerivedHDAddressByPath(master_key, custom_path, address_p2pkh, false); + } else { + for (uint32_t i = 0; i < num_addresses; i++) { - address_p2pkh[offset++] = '\n'; + char derived_pubkey[HDKEYLEN]; + char keypath[BIP44_KEY_PATH_MAX_LENGTH + 1]; + deriveBIP44ExtendedPublicKey(master_key, &account, NULL, NULL, NULL, derived_pubkey, keypath); + getDerivedHDAddressFromAcctPubKey(derived_pubkey, address_index, change_level, address_p2pkh + offset, false); + offset += strlen(address_p2pkh + offset); + if (i < num_addresses - 1) + { + address_p2pkh[offset++] = '\n'; + } + address_index++; } - address_index++; } strncpy(addresses, address_p2pkh, strlen(address_p2pkh)); @@ -896,7 +832,7 @@ void enclave_libdogecoin_generate_address(uint8_t* encrypted_blob, size_t len, c dogecoin_free(persistent_data); } -void enclave_libdogecoin_sign_message(uint8_t* encrypted_blob, size_t len, const char* message, char* signature, uint32_t account, uint32_t address_index, const char* change_level, const uint32_t auth_token) +void enclave_libdogecoin_sign_message(const data_t* encrypted_blob, char* custom_path, char* message, char* signature, uint32_t account, uint32_t address_index, char* change_level, const uint32_t auth_token, char* password) { // Validate the input parameters if (encrypted_blob == NULL || message == NULL || signature == NULL) { @@ -908,11 +844,13 @@ void enclave_libdogecoin_sign_message(uint8_t* encrypted_blob, size_t len, const size_t persistent_data_size = 0; char* mnemonic = NULL; char* shared_secret = NULL; + char* stored_password = NULL; + size_t password_size = password ? strlen(password) : 0; // Unseal the data - oe_result_t result = oe_unseal_wrap( - encrypted_blob, - len, + oe_result_t result = oe_unseal( + encrypted_blob->data, + encrypted_blob->size, NULL, 0, &persistent_data, @@ -924,8 +862,32 @@ void enclave_libdogecoin_sign_message(uint8_t* encrypted_blob, size_t len, const } // Split the persistent data into mnemonic and shared secret - mnemonic = strtok(persistent_data, ","); - shared_secret = strtok(NULL, ","); + mnemonic = strsep((char**)&persistent_data, ","); + if (!mnemonic) { + fprintf(stderr, "Failed to parse mnemonic\n"); + return; + } + shared_secret = strsep((char**)&persistent_data, ","); + if (!shared_secret) { + shared_secret = ""; + } + stored_password = strsep((char**)&persistent_data, ","); + if (!stored_password) { + stored_password = ""; + } + + // If the auth token is 0 and no password is provided, then return + if (auth_token == 0 && !password) { + fprintf(stderr, "Password or auth token required"); + return; + } + + // Verify that the password is part of the managed credentials, if supplied + if ((password && strcmp(password, stored_password)) || + (!password && strcmp(stored_password, "") != 0)) { + fprintf(stderr, "Password verification failed\n"); + return; + } // Verify TOTP using the shared secret struct timeval tv; @@ -933,22 +895,24 @@ void enclave_libdogecoin_sign_message(uint8_t* encrypted_blob, size_t len, const fprintf(stderr, "Failed to get time\n"); return; } - uint32_t current_time = tv.tv_sec; - uint32_t totp = get_totp((const char*) shared_secret, current_time / TOTP_TIME_STEP); - - char totp_str[AUTH_TOKEN_LEN + 1]; // +1 for the null terminator - snprintf(totp_str, sizeof(totp_str), "%06u", totp); - char auth_token_str[AUTH_TOKEN_LEN + 1]; // +1 for the null terminator - snprintf(auth_token_str, sizeof(auth_token_str), "%06u", auth_token); + if (strcmp(shared_secret, "") != 0) { + uint32_t current_time = tv.tv_sec; + uint32_t totp = get_totp((const char*) shared_secret, current_time / TOTP_TIME_STEP); - if (strcmp(totp_str, auth_token_str) != 0) { - printf("TOTP verification failed\n"); - return; + if (totp != auth_token) { + printf("TOTP verification failed\n"); + return; + } + } else { + if (strcmp(password, (const char*) stored_password) != 0) { + printf("Password verification failed\n"); + return; + } } SEED seed; - dogecoin_seed_from_mnemonic((const char*)mnemonic, NULL, seed); + dogecoin_seed_from_mnemonic((const char*)mnemonic, password, seed); // Set the Open Enclave random number generator in libdogecoin set_rng (&oe_random); @@ -965,7 +929,7 @@ void enclave_libdogecoin_sign_message(uint8_t* encrypted_blob, size_t len, const const dogecoin_chainparams* chain = chain_from_b58_prefix(master_key); sprintf(derived_path, SLIP44_KEY_PATH "%s'/%d'/%s/%d", BIP44_COIN_TYPE, account, change_level, address_index); - dogecoin_hdnode* hdnode = getHDNodeAndExtKeyByPath(master_key, derived_path, outaddress, true); + dogecoin_hdnode* hdnode = getHDNodeAndExtKeyByPath(master_key, custom_path ? custom_path : derived_path, outaddress, true); dogecoin_privkey_encode_wif((const dogecoin_key*)hdnode->private_key, chain, privkeywif, &wiflen); char* sig = sign_message(privkeywif, (char*) message); @@ -978,7 +942,7 @@ void enclave_libdogecoin_sign_message(uint8_t* encrypted_blob, size_t len, const dogecoin_free(persistent_data); } -void enclave_libdogecoin_sign_transaction(uint8_t* encrypted_blob, size_t len, const char* raw_tx, char* signed_tx, uint32_t account, uint32_t address_index, const char* change_level, const uint32_t auth_token) +void enclave_libdogecoin_sign_transaction(const data_t* encrypted_blob, char* custom_path, char* raw_tx, char* signed_tx, uint32_t account, uint32_t address_index, char* change_level, const uint32_t auth_token, char* password) { // Validate the input parameters if (encrypted_blob == NULL || raw_tx == NULL || signed_tx == NULL) { @@ -990,11 +954,13 @@ void enclave_libdogecoin_sign_transaction(uint8_t* encrypted_blob, size_t len, c size_t persistent_data_size = 0; char* mnemonic = NULL; char* shared_secret = NULL; + char* stored_password = NULL; + size_t password_size = password ? strlen(password) : 0; // Unseal the data - oe_result_t result = oe_unseal_wrap( - encrypted_blob, - len, + oe_result_t result = oe_unseal( + encrypted_blob->data, + encrypted_blob->size, NULL, 0, &persistent_data, @@ -1006,8 +972,32 @@ void enclave_libdogecoin_sign_transaction(uint8_t* encrypted_blob, size_t len, c } // Split the persistent data into mnemonic and shared secret - mnemonic = strtok(persistent_data, ","); - shared_secret = strtok(NULL, ","); + mnemonic = strsep((char**)&persistent_data, ","); + if (!mnemonic) { + fprintf(stderr, "Failed to parse mnemonic\n"); + return; + } + shared_secret = strsep((char**)&persistent_data, ","); + if (!shared_secret) { + shared_secret = ""; + } + stored_password = strsep((char**)&persistent_data, ","); + if (!stored_password) { + stored_password = ""; + } + + // If the auth token is 0 and no password is provided, then return + if (auth_token == 0 && !password) { + fprintf(stderr, "Password or auth token required"); + return; + } + + // Verify that the password is part of the managed credentials, if supplied + if ((password && strcmp(password, stored_password)) || + (!password && strcmp(stored_password, "") != 0)) { + fprintf(stderr, "Password verification failed\n"); + return; + } // Verify TOTP using the shared secret struct timeval tv; @@ -1015,22 +1005,24 @@ void enclave_libdogecoin_sign_transaction(uint8_t* encrypted_blob, size_t len, c fprintf(stderr, "Failed to get time\n"); return; } - uint32_t current_time = tv.tv_sec; - uint32_t totp = get_totp((const char*) shared_secret, current_time / TOTP_TIME_STEP); - - char totp_str[AUTH_TOKEN_LEN + 1]; // +1 for the null terminator - snprintf(totp_str, sizeof(totp_str), "%06u", totp); - char auth_token_str[AUTH_TOKEN_LEN + 1]; // +1 for the null terminator - snprintf(auth_token_str, sizeof(auth_token_str), "%06u", auth_token); + if (strcmp(shared_secret, "") != 0) { + uint32_t current_time = tv.tv_sec; + uint32_t totp = get_totp((const char*) shared_secret, current_time / TOTP_TIME_STEP); - if (strcmp(totp_str, auth_token_str) != 0) { - printf("TOTP verification failed\n"); - return; + if (totp != auth_token) { + printf("TOTP verification failed\n"); + return; + } + } else { + if (strcmp(password, (const char*) stored_password) != 0) { + printf("Password verification failed\n"); + return; + } } SEED seed; - dogecoin_seed_from_mnemonic((const char*)mnemonic, NULL, seed); + dogecoin_seed_from_mnemonic((const char*)mnemonic, password, seed); // Set the Open Enclave random number generator in libdogecoin set_rng (&oe_random); @@ -1046,7 +1038,8 @@ void enclave_libdogecoin_sign_transaction(uint8_t* encrypted_blob, size_t len, c size_t wiflen = PRIVKEYWIFLEN; const dogecoin_chainparams* chain = chain_from_b58_prefix(master_key); sprintf(derived_path, SLIP44_KEY_PATH "%s'/%d'/%s/%d", BIP44_COIN_TYPE, account, change_level, address_index); - dogecoin_hdnode* hdnode = getHDNodeAndExtKeyByPath(master_key, derived_path, outaddress, true); + + dogecoin_hdnode* hdnode = getHDNodeAndExtKeyByPath(master_key, custom_path ? custom_path : derived_path, outaddress, true); dogecoin_privkey_encode_wif((const dogecoin_key*)hdnode->private_key, chain, privkeywif, &wiflen); // Store the raw transaction diff --git a/src/openenclave/host/CMakeLists.txt b/src/openenclave/host/CMakeLists.txt index 8c18e22c..51b253a5 100644 --- a/src/openenclave/host/CMakeLists.txt +++ b/src/openenclave/host/CMakeLists.txt @@ -18,9 +18,9 @@ endif () target_include_directories( host PRIVATE # Needed for the generated file libdogecoin_u.h - ${CMAKE_CURRENT_BINARY_DIR} /usr/local/include /usr/local/include/dogecoin /usr/include/ykpers-1 ${CMAKE_SOURCE_DIR}/../../depends/x86_64-pc-linux-gnu/include/ ${CMAKE_SOURCE_DIR}/../../depends/x86_64-pc-linux-gnu/include/dogecoin/ ${CMAKE_SOURCE_DIR}/../../depends/x86_64-pc-linux-gnu/include/ykpers-1 ${CMAKE_SOURCE_DIR}/../../src/libevent/build/include) + ${CMAKE_CURRENT_BINARY_DIR} /usr/local/include /usr/local/include/dogecoin /usr/include/ykpers-1 ${CMAKE_SOURCE_DIR}/../../depends/x86_64-pc-linux-gnu/host/include/ ${CMAKE_SOURCE_DIR}/../../depends/x86_64-pc-linux-gnu/host/include/dogecoin/ ${CMAKE_SOURCE_DIR}/../../depends/x86_64-pc-linux-gnu/host/include/ykpers-1 ${CMAKE_SOURCE_DIR}/../../src/libevent/build/include) # Add search paths to find the enclave libraries. -target_link_directories(host PRIVATE ${CMAKE_SOURCE_DIR}../../ ${CMAKE_SOURCE_DIR}/../../src/libevent/build/lib ${CMAKE_SOURCE_DIR}/../../depends/x86_64-pc-linux-gnu/lib) +target_link_directories(host PRIVATE ${CMAKE_SOURCE_DIR}../../ ${CMAKE_SOURCE_DIR}/../../src/libevent/build/lib ${CMAKE_SOURCE_DIR}/../../depends/x86_64-pc-linux-gnu/host/lib) target_link_libraries(host openenclave::oehost "libdogecoin.a" "libevent.a" "libunistring.a" "libykpers-1.so" "libyubikey.so" "libusb-1.0.so") diff --git a/src/openenclave/host/host.c b/src/openenclave/host/host.c index 330bea72..b663f3e6 100644 --- a/src/openenclave/host/host.c +++ b/src/openenclave/host/host.c @@ -91,23 +91,27 @@ static struct option long_options[] = {"mnemonic_input", required_argument, NULL, 'n'}, {"shared_secret", required_argument, NULL, 's'}, {"entropy_size", required_argument, NULL, 'e'}, + {"auth_token", required_argument, NULL, 'a'}, + {"password", required_argument, NULL, 'p'}, + {"custom_path", required_argument, NULL, 'h'}, + {"yubikey", no_argument, NULL, 'z'}, {NULL, 0, NULL, 0} }; static void print_usage() { - printf("Usage: host -c (-o|-account_int ) (-i|-input_index ) (-l|-change_level ) \ + printf("Usage: host/host enclave/enclave.signed -c (-o|-account_int ) (-i|-input_index ) (-l|-change_level ) \ (-m|-message ) (-t|-transaction ) (-n|-mnemonic_input ) (-s|-shared_secret ) \ -(-e|-entropy_size )\n"); +(-e|-entropy_size ) (-a|-auth_token ) (-p|-password ) (-h|custom_path ) (-z|yubikey)\n"); printf("Available commands:\n"); - printf(" generate_mnemonic (optional -n -s -e )\n"); - printf(" generate_extended_public_key (requires -o -i -l \n"); - printf(" generate_address (requires -o -i -l )\n"); - printf(" sign_message (requires -o -i -l -m )\n"); - printf(" sign_transaction (requires -o -i -l -t )\n"); + printf(" generate_mnemonic (optional -n -s -e -p -z)\n"); + printf(" generate_extended_public_key (requires -o -l ) (optional -h -a or -p -z)\n"); + printf(" generate_address (requires -o -i -l ) (optional -h -a or -p -z)\n"); + printf(" sign_message (requires -o -i -l -m ) (optional -h -a or -p -z)\n"); + printf(" sign_transaction (requires -o -i -l -t ) (optional -h -a or -p -z)\n"); } -void write_encrypted_file(const char* filename, const uint8_t* data, size_t data_size) +void write_encrypted_file(const char* filename, const data_t* enc_blob) { FILE* file = fopen(filename, "wb"); if (file == NULL) @@ -115,32 +119,49 @@ void write_encrypted_file(const char* filename, const uint8_t* data, size_t data fprintf(stderr, "Failed to open file %s for writing\n", filename); return; } - size_t written = fwrite(data, 1, data_size, file); - if (written != data_size) + size_t written = fwrite(enc_blob->data, 1, enc_blob->size, file); + if (written != enc_blob->size) { fprintf(stderr, "Failed to write data to file %s\n", filename); } fclose(file); } -void read_encrypted_file(const char* filename, uint8_t* data, size_t* data_size) +void read_encrypted_file(const char* filename, data_t* enc_blob) { FILE* file = fopen(filename, "rb"); if (file == NULL) { fprintf(stderr, "Failed to open file %s for reading\n", filename); - *data_size = 0; + enc_blob->data = NULL; + enc_blob->size = 0; return; } - *data_size = fread(data, 1, *data_size, file); - if (*data_size == 0) + fseek(file, 0, SEEK_END); + enc_blob->size = ftell(file); + fseek(file, 0, SEEK_SET); + + enc_blob->data = (uint8_t*)malloc(enc_blob->size); + if (enc_blob->data == NULL) + { + fprintf(stderr, "Memory allocation failed\n"); + fclose(file); + enc_blob->size = 0; + return; + } + + size_t read_size = fread(enc_blob->data, 1, enc_blob->size, file); + if (read_size != enc_blob->size) { - fprintf(stderr, "Failed to read data from file %s\n", filename); + fprintf(stderr, "Failed to read complete data from file %s\n", filename); + free(enc_blob->data); + enc_blob->data = NULL; + enc_blob->size = 0; } fclose(file); } -void set_totp_secret(YK_KEY *yk, const char *secret) { +void set_totp_secret(YK_KEY *yk, const char *secret, const uint8_t slot) { YKP_CONFIG *cfg = ykp_alloc(); YK_STATUS *st = ykds_alloc(); @@ -168,7 +189,7 @@ void set_totp_secret(YK_KEY *yk, const char *secret) { core_config->cfgFlags &= ~CFGFLAG_CHAL_BTN_TRIG; // Disable button press core_config->extFlags |= EXTFLAG_SERIAL_API_VISIBLE; - if (!ykp_configure_command(cfg, SLOT_CONFIG)) { + if (!ykp_configure_command(cfg, slot)) { fprintf(stderr, "Internal error: couldn't configure command\n"); ykp_free_config(cfg); ykds_free(st); @@ -207,7 +228,7 @@ void set_totp_secret(YK_KEY *yk, const char *secret) { ykds_free(st); } -uint32_t get_totp_from_yubikey(YK_KEY *yk) { +uint32_t get_totp_from_yubikey(YK_KEY *yk, uint8_t yk_cmd) { unsigned int t_counter = (unsigned int)time(NULL) / 30; unsigned char challenge[8]; for (int i = 7; i >= 0; i--) { @@ -221,9 +242,9 @@ uint32_t get_totp_from_yubikey(YK_KEY *yk) { } unsigned char response[SHA1_MAX_BLOCK_SIZE]; - if (!yk_challenge_response(yk, SLOT_CHAL_HMAC1, true, sizeof(challenge), challenge, sizeof(response), response)) { + if (!yk_challenge_response(yk, yk_cmd, true, sizeof(challenge), challenge, sizeof(response), response)) { fprintf(stderr, "Failed to generate TOTP code\n"); - return 0; + return 0; } unsigned int offset = response[19] & 0xf; @@ -271,21 +292,24 @@ int main(int argc, char* argv[]) int opt = 0; int long_index = 0; char* cmd = 0; - uint32_t* account = NULL; - uint32_t* input_index = NULL; - const char* change_level = NULL; + uint32_t auth_token = 0; + uint32_t account = 0; + uint32_t input_index = 0; + char* change_level = NULL; + char* custom_path = NULL; char* message = "This is a test message"; char* transaction = NULL; char* shared_secret = NULL; char* mnemonic_in = NULL; char* entropy_size = NULL; + char* password = NULL; + bool yubikey = false; // Allocate memory for encrypted blobs - uint8_t* encrypted_blob = malloc(MAX_ENCRYPTED_BLOB_SIZE); - size_t blob_size = MAX_ENCRYPTED_BLOB_SIZE; + data_t encrypted_blob = {NULL, 0}; // Parse remaining CLI options - while ((opt = getopt_long_only(remaining_argc, remaining_argv, "c:o:i:l:m:t:n:s:e:", long_options, &long_index)) != -1) + while ((opt = getopt_long_only(remaining_argc, remaining_argv, "c:o:i:l:m:t:n:s:e:p:a:h:z", long_options, &long_index)) != -1) { switch (opt) { @@ -293,12 +317,10 @@ int main(int argc, char* argv[]) cmd = optarg; break; case 'o': - account = (uint32_t*)malloc(sizeof(uint32_t)); - *account = (uint32_t)strtol(optarg, NULL, 10); + account = (uint32_t)strtol(optarg, NULL, 10); break; case 'i': - input_index = (uint32_t*)malloc(sizeof(uint32_t)); - *input_index = (uint32_t)strtol(optarg, NULL, 10); + input_index = (uint32_t)strtol(optarg, NULL, 10); break; case 'l': change_level = optarg; @@ -318,6 +340,18 @@ int main(int argc, char* argv[]) case 'e': entropy_size = optarg; break; + case 'p': + password = optarg; + break; + case 'a': + auth_token = (uint32_t)strtol(optarg, NULL, 10); + break; + case 'h': + custom_path = optarg; + break; + case 'z': + yubikey = true; + break; default: print_usage(); exit(EXIT_SUCCESS); @@ -330,13 +364,14 @@ int main(int argc, char* argv[]) exit(EXIT_SUCCESS); } - if (!yk_init()) { + YK_KEY *yk = NULL; + if (!yk_init() && yubikey) { fprintf(stderr, "Failed to initialize YubiKey\n"); - } - - YK_KEY *yk = yk_open_first_key(); - if (!yk) { - fprintf(stderr, "Failed to open YubiKey\n"); + } else if (yubikey) { + yk = yk_open_first_key(); + if (!yk) { + fprintf(stderr, "Failed to open YubiKey\n"); + } } if (strcmp(cmd, "run_example") == 0) @@ -351,53 +386,88 @@ int main(int argc, char* argv[]) else if (strcmp(cmd, "generate_master_key") == 0) { printf("- Generate a master key\n"); - result = enclave_libdogecoin_generate_master_key(enclave, &encrypted_blob, &blob_size); + result = enclave_libdogecoin_generate_master_key(enclave, &encrypted_blob); if (result != OE_OK) { fprintf(stderr, "Failed to generate a master key: result=%u (%s)\n", result, oe_result_str(result)); } else { - write_encrypted_file(MASTER_TEE_FILE_NAME, encrypted_blob, blob_size); + write_encrypted_file(MASTER_TEE_FILE_NAME, &encrypted_blob); } } else if (strcmp(cmd, "generate_mnemonic") == 0) { printf("- Generate and encrypt a mnemonic\n"); - if (!shared_secret) { - shared_secret = getpass("Enter shared secret (hex, 40 characters): "); + if (yubikey) { if (!shared_secret) { - fprintf(stderr, "Failed to read shared secret\n"); - goto exit; + shared_secret = getpass("Enter shared secret for TOTP (hex, 40 characters) or press enter to generate one: "); + if (!shared_secret) { + fprintf(stderr, "Failed to read shared secret\n"); + goto exit; + } } - } - // Check if there is an existing configuration in slot 1 - YK_STATUS *status = ykds_alloc(); - if (yk && !yk_get_status(yk, status)) { - fprintf(stderr, "Failed to get YubiKey status\n"); - } + if (strlen(shared_secret) == 0) { + printf("Shared secret not provided, generating one...\n"); + // Generate random 20 bytes (40 hex characters) + unsigned char random_bytes[TOTP_SECRET_HEX_SIZE / 2]; + dogecoin_random_bytes(random_bytes, sizeof(random_bytes), 0); + + shared_secret = malloc(TOTP_SECRET_HEX_SIZE + 1); + if (!shared_secret) { + fprintf(stderr, "Failed to allocate memory for shared secret\n"); + goto exit; + } + + utils_bin_to_hex(random_bytes, sizeof(random_bytes), shared_secret); + shared_secret[TOTP_SECRET_HEX_SIZE] = '\0'; + printf("Generated shared secret: %s\n", shared_secret); + } - // Check if slot 1 has a configuration - if (yk && (ykds_touch_level(status) & CONFIG1_VALID) == CONFIG1_VALID) { - char response; - printf("Slot 1 already has a configuration. Do you want to overwrite it? (y/N): "); - scanf(" %c", &response); - if (response != 'y' && response != 'Y') { - printf("Aborted by user\n"); - ykds_free(status); - goto exit; + // Check if there is an existing configuration in slot 1 + YK_STATUS *status = ykds_alloc(); + if (yk) { + if (!yk_get_status(yk, status)) { + fprintf(stderr, "Failed to get YubiKey status\n"); + } + } + + // Check if slot 1 has a configuration + if (yk && (ykds_touch_level(status) & CONFIG1_VALID) == CONFIG1_VALID) { + char response; + printf("Slot 1 already has a configuration. Do you want to overwrite it? (y/N): "); + scanf(" %c", &response); + if (response != 'y' && response != 'Y') { + printf("Aborted by user\n"); + ykds_free(status); + goto exit; + } } - } - ykds_free(status); + ykds_free(status); - if (yk) { - set_totp_secret(yk, shared_secret); + if (yk) { + set_totp_secret(yk, shared_secret, SLOT_CONFIG); + } + } + + if (!password && !shared_secret) { + password = getpass("Enter management password: "); + if (strlen(password) == 0) { + fprintf(stderr, "Password cannot be empty\n"); + goto exit; + } + printf("\n"); + if (strcmp (password, getpass("Confirm password: ")) != 0) { + fprintf(stderr, "Password mismatch\n"); + goto exit; + } + printf("\n"); } MNEMONIC mnemonic = {0}; - result = enclave_libdogecoin_generate_mnemonic(enclave, &encrypted_blob, &blob_size, mnemonic, shared_secret, mnemonic_in, entropy_size); + result = enclave_libdogecoin_generate_mnemonic(enclave, &encrypted_blob, mnemonic, shared_secret, mnemonic_in, entropy_size, password); if (result != OE_OK) { fprintf(stderr, "Failed to generate and encrypt a mnemonic: result=%u (%s)\n", result, oe_result_str(result)); @@ -410,32 +480,36 @@ int main(int argc, char* argv[]) fprintf(stderr, "ERROR: Failed to create directory\n"); return false; } - write_encrypted_file(MNEMONIC_TEE_FILE_NAME, encrypted_blob, blob_size); + write_encrypted_file(MNEMONIC_TEE_FILE_NAME, &encrypted_blob); } dogecoin_mem_zero(mnemonic, strlen(mnemonic)); } else if (strcmp(cmd, "generate_extended_public_key") == 0) { printf("- Generate a public key\n"); - uint8_t pubkeyhex[128]; - uint32_t auth_token = get_totp_from_yubikey(yk); + uint8_t pubkeyhex[128] = {0}; + if (!password && !yubikey && auth_token == 0) { + password = getpass("Enter management password: "); + if (!password) { + fprintf(stderr, "Failed to read password\n"); + goto exit; + } + } + + if (auth_token == 0 && yubikey) { + auth_token = get_totp_from_yubikey(yk, SLOT_CHAL_HMAC1); + } printf("Auth token: %u\n", auth_token); - read_encrypted_file(MNEMONIC_TEE_FILE_NAME, encrypted_blob, &blob_size); - if (blob_size == 0) + read_encrypted_file(MNEMONIC_TEE_FILE_NAME, &encrypted_blob); + if (encrypted_blob.size == 0) { fprintf(stderr, "Failed to read encrypted mnemonic from file\n"); ret = 0; goto exit; } - // verify account and change_level are set - if (!account || !change_level) - { - fprintf(stderr, "Account and change level must be set\n"); - ret = 0; - goto exit; - } - result = enclave_libdogecoin_generate_extended_public_key(enclave, encrypted_blob, blob_size, (char*)pubkeyhex, account, change_level, auth_token); - if (strlen(pubkeyhex) == 0) + + result = enclave_libdogecoin_generate_extended_public_key(enclave, &encrypted_blob, custom_path, (char*)pubkeyhex, account, change_level, auth_token, password); + if (result != OE_OK) { fprintf(stderr, "Failed to generate public key: result=%u (%s)\n", result, oe_result_str(result)); } @@ -447,24 +521,28 @@ int main(int argc, char* argv[]) else if (strcmp(cmd, "generate_address") == 0) { printf("- Generate address\n"); - char addresses[P2PKHLEN * NUM_ADDRESSES]; - uint32_t auth_token = get_totp_from_yubikey(yk); + char addresses[P2PKHLEN * NUM_ADDRESSES] = {0}; + if (!password && !yubikey && auth_token == 0) { + password = getpass("Enter management password: "); + if (!password) { + fprintf(stderr, "Failed to read password\n"); + goto exit; + } + } + + if (auth_token == 0 && yubikey) { + auth_token = get_totp_from_yubikey(yk, SLOT_CHAL_HMAC1); + } printf("Auth token: %u\n", auth_token); - read_encrypted_file(MNEMONIC_TEE_FILE_NAME, encrypted_blob, &blob_size); - if (blob_size == 0) + read_encrypted_file(MNEMONIC_TEE_FILE_NAME, &encrypted_blob); + if (encrypted_blob.size == 0) { fprintf(stderr, "Failed to read encrypted mnemonic from file\n"); ret = 0; goto exit; } - // verify account, input_index and change_level are set - if (!account || !input_index || !change_level) - { - fprintf(stderr, "Account, input index and change level must be set\n"); - ret = 0; - goto exit; - } - result = enclave_libdogecoin_generate_address(enclave, encrypted_blob, blob_size, addresses, *account, *input_index, change_level, NUM_ADDRESSES, auth_token); + + result = enclave_libdogecoin_generate_address(enclave, &encrypted_blob, custom_path, addresses, account, input_index, change_level, NUM_ADDRESSES, auth_token, password); if (result != OE_OK) { fprintf(stderr, "Failed to generate addresses: result=%u (%s)\n", result, oe_result_str(result)); @@ -478,24 +556,28 @@ int main(int argc, char* argv[]) { printf("- Sign a message\n"); uint8_t signature[2048] = {0}; - uint32_t auth_token = get_totp_from_yubikey(yk); + if (!password && !yubikey && auth_token == 0) { + password = getpass("Enter management password: "); + if (!password) { + fprintf(stderr, "Failed to read password\n"); + goto exit; + } + } + + if (auth_token == 0 && yubikey) { + auth_token = get_totp_from_yubikey(yk, SLOT_CHAL_HMAC1); + } printf("Auth token: %u\n", auth_token); - read_encrypted_file(MNEMONIC_TEE_FILE_NAME, encrypted_blob, &blob_size); - if (blob_size == 0) + read_encrypted_file(MNEMONIC_TEE_FILE_NAME, &encrypted_blob); + if (encrypted_blob.size == 0) { fprintf(stderr, "Failed to read encrypted mnemonic from file\n"); ret = 0; goto exit; } - // verify account, input_index and change_level are set - if (!account || !input_index || !change_level) - { - fprintf(stderr, "Account, input index and change level must be set\n"); - ret = 0; - goto exit; - } + printf ("Signing message: %s\n", message); - result = enclave_libdogecoin_sign_message(enclave, encrypted_blob, blob_size, message, (char*)signature, *account, *input_index, change_level, auth_token); + result = enclave_libdogecoin_sign_message(enclave, &encrypted_blob, custom_path, message, (char*)signature, account, input_index, change_level, auth_token, password); if (result != OE_OK) { fprintf(stderr, "Failed to sign the message: result=%u (%s)\n", result, oe_result_str(result)); @@ -508,22 +590,24 @@ int main(int argc, char* argv[]) else if (strcmp(cmd, "sign_transaction") == 0) { printf("- Sign a transaction\n"); - char raw_tx[1024]; - char signed_tx[4096]; - uint32_t auth_token = get_totp_from_yubikey(yk); - printf("Auth token: %u\n", auth_token); - read_encrypted_file(MNEMONIC_TEE_FILE_NAME, encrypted_blob, &blob_size); - if (blob_size == 0) - { - fprintf(stderr, "Failed to read encrypted mnemonic from file\n"); - ret = 0; - goto exit; + char raw_tx[1024] = {0}; + char signed_tx[4096] = {0}; + if (!password && !yubikey && auth_token == 0) { + password = getpass("Enter management password: "); + if (!password) { + fprintf(stderr, "Failed to read password\n"); + goto exit; + } } - // verify account, input_index and change_level are set - if (!account || !input_index || !change_level) + if (auth_token == 0 && yubikey) { + auth_token = get_totp_from_yubikey(yk, SLOT_CHAL_HMAC1); + } + printf("Auth token: %u\n", auth_token); + read_encrypted_file(MNEMONIC_TEE_FILE_NAME, &encrypted_blob); + if (encrypted_blob.size == 0) { - fprintf(stderr, "Account, input index and change level must be set\n"); + fprintf(stderr, "Failed to read encrypted mnemonic from file\n"); ret = 0; goto exit; } @@ -587,7 +671,7 @@ int main(int argc, char* argv[]) printf("Raw transaction created: %s\n", raw_tx); printf("Raw transaction length: %zu\n", strlen(raw_tx)); - result = enclave_libdogecoin_sign_transaction(enclave, encrypted_blob, blob_size, transaction != NULL ? transaction : raw_tx, signed_tx, *account, *input_index, change_level, auth_token); + result = enclave_libdogecoin_sign_transaction(enclave, &encrypted_blob, custom_path, transaction != NULL ? transaction : raw_tx, signed_tx, account, input_index, change_level, auth_token, password); if (result != OE_OK) { fprintf(stderr, "Failed to sign the transaction: result=%u (%s)\n", result, oe_result_str(result)); @@ -613,14 +697,6 @@ int main(int argc, char* argv[]) if (shared_secret) { dogecoin_mem_zero(shared_secret, strlen(shared_secret)); } - if (account) - { - free(account); - } - if (input_index) - { - free(input_index); - } if (yk) { yk_close_key(yk); diff --git a/src/openenclave/libdogecoin.edl b/src/openenclave/libdogecoin.edl index eafb7cb6..b55a15c5 100644 --- a/src/openenclave/libdogecoin.edl +++ b/src/openenclave/libdogecoin.edl @@ -1,27 +1,26 @@ -// Copyright (c) Open Enclave SDK contributors. -// Licensed under the MIT License. - enclave { from "openenclave/edl/syscall.edl" import *; - from "platform.edl" import *; from "openenclave/edl/logging.edl" import oe_write_ocall; - from "openenclave/edl/fcntl.edl" import *; - from "openenclave/edl/socket.edl" import *; - from "openenclave/edl/utsname.edl" import *; - from "openenclave/edl/epoll.edl" import *; + from "openenclave/edl/sgx/cpu.edl" import oe_sgx_get_cpuid_table_ocall; + + struct data_t + { + [size=size] uint8_t* data; + size_t size; + }; trusted { public void enclave_libdogecoin(); public void enclave_libdogecoin_attest(uint8_t* report, size_t len); public void enclave_libdogecoin_run_example(); - public void enclave_libdogecoin_generate_encrypted_seed(uint8_t** encrypted_blob, size_t* len); - public void enclave_libdogecoin_generate_master_key(uint8_t** encrypted_blob, size_t* len); + public void enclave_libdogecoin_generate_encrypted_seed([out] data_t* encrypted_blob); + public void enclave_libdogecoin_generate_master_key([out] data_t* encrypted_blob); - public void enclave_libdogecoin_generate_mnemonic(uint8_t** encrypted_blob, size_t* len, char* mnemonic, const char* shared_secret, const char* mnemonic_in, const char* entropy_size); - public void enclave_libdogecoin_generate_extended_public_key(uint8_t* encrypted_blob, size_t len, char* pubkey, uint32_t* account, const char* change_level, const uint32_t auth_token); - public void enclave_libdogecoin_generate_address(uint8_t* encrypted_blob, size_t len, char* addresses, uint32_t account, uint32_t address_index, const char* change_level, uint32_t num_addresses, const uint32_t auth_token); - public void enclave_libdogecoin_sign_message(uint8_t* encrypted_blob, size_t len, const char* message, char* signature, uint32_t account, uint32_t address_index, const char* change_level, const uint32_t auth_token); - public void enclave_libdogecoin_sign_transaction(uint8_t* encrypted_blob, size_t len, const char* raw_tx, char* signed_tx, uint32_t account, uint32_t address_index, const char* change_level, const uint32_t auth_token); + public void enclave_libdogecoin_generate_mnemonic([out] data_t* encrypted_blob, char* mnemonic, char* shared_secret, char* mnemonic_in, char* entropy_size, char* password); + public void enclave_libdogecoin_generate_extended_public_key([in] const data_t* encrypted_blob, char* custom_path, char* pubkey, uint32_t account, char* change_level, const uint32_t auth_token, char* password); + public void enclave_libdogecoin_generate_address([in] const data_t* encrypted_blob, char* custom_path, char* addresses, uint32_t account, uint32_t address_index, char* change_level, uint32_t num_addresses, const uint32_t auth_token, char* password); + public void enclave_libdogecoin_sign_message([in] const data_t* encrypted_blob, char* custom_path, char* message, char* signature, uint32_t account, uint32_t address_index, char* change_level, const uint32_t auth_token, char* password); + public void enclave_libdogecoin_sign_transaction([in] const data_t* encrypted_blob, char* custom_path, char* raw_tx, char* signed_tx, uint32_t account, uint32_t address_index, char* change_level, const uint32_t auth_token, char* password); }; untrusted { diff --git a/src/optee/host/main.c b/src/optee/host/main.c index d96b7b37..d12a4603 100644 --- a/src/optee/host/main.c +++ b/src/optee/host/main.c @@ -182,9 +182,9 @@ TEEC_Result generate_mnemonic(struct test_ctx *ctx, const char *shared_secret, c // Prepare the managed credentials as ",," snprintf(managed_creds, sizeof(managed_creds), "%s,%s,%s", - shared_secret ? shared_secret : "none", - password ? password : "none", - flags ? flags : "none"); + shared_secret ? shared_secret : "|", + password ? password : "|", + flags ? flags : "|"); // Prepare the operation memset(&op, 0, sizeof(op)); @@ -242,7 +242,7 @@ TEEC_Result generate_mnemonic(struct test_ctx *ctx, const char *shared_secret, c return res; } -TEEC_Result generate_address_ta(struct test_ctx *ctx, const char* account, const char* address_index, const char* change_level, uint32_t auth_token, const char* password, char *address, size_t *address_len) +TEEC_Result generate_address_ta(struct test_ctx *ctx, const char* custom_path, const char* account, const char* address_index, const char* change_level, uint32_t auth_token, const char* password, char *address, size_t *address_len) { TEEC_Operation op; uint32_t origin; @@ -257,8 +257,8 @@ TEEC_Result generate_address_ta(struct test_ctx *ctx, const char* account, const op.params[0].tmpref.buffer = address; op.params[0].tmpref.size = *address_len; - op.params[1].tmpref.buffer = key_path; - op.params[1].tmpref.size = strlen(key_path) + 1; + op.params[1].tmpref.buffer = (void *)((custom_path != NULL) ? custom_path : key_path); + op.params[1].tmpref.size = strlen(op.params[1].tmpref.buffer) + 1; op.params[2].value.a = auth_token; op.params[3].tmpref.buffer = (void *)password; op.params[3].tmpref.size = password ? strlen(password) + 1 : 0; @@ -289,7 +289,7 @@ TEEC_Result generate_address_ta(struct test_ctx *ctx, const char* account, const return res; } -TEEC_Result generate_extended_public_key_ta(struct test_ctx *ctx, const char* account, const char* change_level, uint32_t auth_token, const char* password, char *pubkey, size_t *pubkey_len) +TEEC_Result generate_extended_public_key_ta(struct test_ctx *ctx, const char* custom_path, const char* account, const char* change_level, uint32_t auth_token, const char* password, char *pubkey, size_t *pubkey_len) { TEEC_Operation op; uint32_t origin; @@ -304,8 +304,8 @@ TEEC_Result generate_extended_public_key_ta(struct test_ctx *ctx, const char* ac op.params[0].tmpref.buffer = pubkey; op.params[0].tmpref.size = *pubkey_len; - op.params[1].tmpref.buffer = key_path; - op.params[1].tmpref.size = strlen(key_path) + 1; + op.params[1].tmpref.buffer = (void *)((custom_path != NULL) ? custom_path : key_path); + op.params[1].tmpref.size = strlen(op.params[1].tmpref.buffer) + 1; op.params[2].value.a = auth_token; op.params[3].tmpref.buffer = (void *)password; op.params[3].tmpref.size = password ? strlen(password) + 1 : 0; @@ -336,7 +336,7 @@ TEEC_Result generate_extended_public_key_ta(struct test_ctx *ctx, const char* ac return res; } -TEEC_Result sign_message_ta(struct test_ctx *ctx, const char* account, const char* address_index, const char* change_level, uint32_t auth_token, const char* password, char *message, char *signature, size_t *signature_len) +TEEC_Result sign_message_ta(struct test_ctx *ctx, const char* custom_path, const char* account, const char* address_index, const char* change_level, uint32_t auth_token, const char* password, char *message, char *signature, size_t *signature_len) { TEEC_Operation op; uint32_t origin; @@ -344,7 +344,11 @@ TEEC_Result sign_message_ta(struct test_ctx *ctx, const char* account, const cha // Construct key path from account, change_level, and address_index char key_path[KEYPATHMAXLEN]; - snprintf(key_path, sizeof(key_path), "m/44'/3'/%s'/%s/%s,%s", account, change_level, address_index, password); + if (custom_path && strlen(custom_path) > 0) { + snprintf(key_path, sizeof(key_path), "%s,%s", custom_path, password); + } else { + snprintf(key_path, sizeof(key_path), "m/44'/3'/%s'/%s/%s,%s", account, change_level, address_index, password); + } memset(&op, 0, sizeof(op)); op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT, @@ -387,7 +391,7 @@ TEEC_Result sign_message_ta(struct test_ctx *ctx, const char* account, const cha return res; } -TEEC_Result sign_transaction_ta(struct test_ctx *ctx, const char* account, const char* address_index, const char* change_level, uint32_t auth_token, const char* password, char *raw_tx, size_t raw_tx_len, char *signed_tx, size_t *signed_tx_len) +TEEC_Result sign_transaction_ta(struct test_ctx *ctx, const char* custom_path, const char* account, const char* address_index, const char* change_level, uint32_t auth_token, const char* password, char *raw_tx, size_t raw_tx_len, char *signed_tx, size_t *signed_tx_len) { TEEC_Operation op; uint32_t origin; @@ -395,7 +399,11 @@ TEEC_Result sign_transaction_ta(struct test_ctx *ctx, const char* account, const // Construct key path from account, change_level, and address_index char key_path[KEYPATHMAXLEN]; - snprintf(key_path, sizeof(key_path), "m/44'/3'/%s'/%s/%s,%s", account, change_level, address_index, password); + if (custom_path && strlen(custom_path) > 0) { + snprintf(key_path, sizeof(key_path), "%s,%s", custom_path, password); + } else { + snprintf(key_path, sizeof(key_path), "m/44'/3'/%s'/%s/%s,%s", account, change_level, address_index, password); + } memset(&op, 0, sizeof(op)); op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT, @@ -438,7 +446,7 @@ TEEC_Result sign_transaction_ta(struct test_ctx *ctx, const char* account, const return res; } -TEEC_Result delegate_key_ta(struct test_ctx *ctx, const char* account, uint32_t auth_token, const char* delegate_password, const char *password, char *delegate_key) +TEEC_Result delegate_key_ta(struct test_ctx *ctx, const char* custom_path, const char* account, uint32_t auth_token, const char* delegate_password, const char *password, char *delegate_key) { TEEC_Operation op; uint32_t origin; @@ -461,8 +469,8 @@ TEEC_Result delegate_key_ta(struct test_ctx *ctx, const char* account, uint32_t op.params[0].tmpref.buffer = delegate_key; op.params[0].tmpref.size = HDKEYLEN; - op.params[1].tmpref.buffer = (void *)key_path; - op.params[1].tmpref.size = strlen(key_path) + 1; + op.params[1].tmpref.buffer = (void *)((custom_path != NULL) ? custom_path : key_path); + op.params[1].tmpref.size = strlen(op.params[1].tmpref.buffer) + 1; op.params[2].value.a = auth_token; op.params[3].tmpref.buffer = (void *)delegate_creds; op.params[3].tmpref.size = strlen(delegate_creds) + 1; @@ -480,7 +488,7 @@ TEEC_Result delegate_key_ta(struct test_ctx *ctx, const char* account, uint32_t return res; } -TEEC_Result export_delegate_key_ta(struct test_ctx *ctx, const char* account, const char *password, char *exported_key) { +TEEC_Result export_delegate_key_ta(struct test_ctx *ctx, const char* custom_path, const char* account, const char *password, char *exported_key) { TEEC_Operation op; uint32_t origin; TEEC_Result res; @@ -495,8 +503,8 @@ TEEC_Result export_delegate_key_ta(struct test_ctx *ctx, const char* account, co op.params[0].tmpref.buffer = exported_key; op.params[0].tmpref.size = HDKEYLEN; - op.params[1].tmpref.buffer = (void *)key_path; - op.params[1].tmpref.size = strlen(key_path) + 1; + op.params[1].tmpref.buffer = (void *)((custom_path != NULL) ? custom_path : key_path); + op.params[1].tmpref.size = strlen(op.params[1].tmpref.buffer) + 1; op.params[3].tmpref.buffer = (void *)password; op.params[3].tmpref.size = password ? strlen(password) + 1 : 0; @@ -525,23 +533,26 @@ static struct option long_options[] = { {"password", required_argument, NULL, 'p'}, {"delegate_password", required_argument, NULL, 'd'}, {"auth_token", required_argument, NULL, 'a'}, - {"prompt", no_argument, NULL, 'u'}, {"flags", required_argument, NULL, 'f'}, + {"custom_path", required_argument, NULL, 'h'}, + {"yubikey", no_argument, NULL, 'z'}, {NULL, 0, NULL, 0} }; static void print_usage() { - printf("Usage: such -c (-o ) (-l ) (-i ) (-m ) (-t ) \ -(-n ) (-s ) (-e ) (-a ) (-p/-d or -u ) (-f )\n"); + printf("Usage: optee_libdogecoin -c (-o|-account_int ) (-i|-input_index ) (-l|-change_level ) \ +(-m|-message ) (-t|-transaction ) (-n|-mnemonic_input ) (-s|-shared_secret ) \ +(-e|-entropy_size ) (-a|-auth_token ) (-p|-password ) (-d|-delegate_password ) \ +(-h|custom_path ) (-f|flags ) (-z|yubikey)\n"); printf("Available commands:\n"); - printf(" generate_mnemonic (optional -n -s -e -p or -u -f )\n"); - printf(" generate_address (requires -o -l -i , optional -a -p or -u )\n"); - printf(" generate_extended_public_key (requires -o -l , optional -a -p or -u )\n"); - printf(" sign_message (requires -o -l -i -m , optional -a -p or -u )\n"); - printf(" sign_transaction (requires -o -l -i -t , optional -a -p or -u )\n"); - printf(" delegate_key (requires -o -d or -u , optional -a -p )\n"); - printf(" export_delegate_key (requires -o -d or -u )\n"); + printf(" generate_mnemonic (optional -n -s -e -p -f )\n"); + printf(" generate_extended_public_key (requires -o -l , optional -h -a -p -z)\n"); + printf(" generate_address (requires -o -l -i , optional -h -a -p -z)\n"); + printf(" sign_message (requires -o -l -i -m , optional -h -a -p -z)\n"); + printf(" sign_transaction (requires -o -l -i -t , optional -h -a -p -z)\n"); + printf(" delegate_key (requires -o -d , optional -h -a -p -z)\n"); + printf(" export_delegate_key (requires -o -d , optional -h )\n"); } #define TIME_STEP 30 @@ -648,6 +659,7 @@ int main(int argc, const char* argv[]) int long_index = 0; char* cmd = 0; uint32_t auth_token = 0; + const char* custom_path = NULL; const char* account = NULL; const char* address_index = NULL; const char* change_level = NULL; @@ -658,10 +670,10 @@ int main(int argc, const char* argv[]) char* mnemonic = NULL; char* entropy_size = NULL; char* delegate_password = NULL; - dogecoin_bool prompt = false; + dogecoin_bool yubikey = false; char* flags = ""; - while ((opt = getopt_long_only(argc, argv, "c:o:l:i:m:t:n:s:e:p:d:a:f:u", long_options, &long_index)) != -1) { + while ((opt = getopt_long_only(argc, argv, "c:o:l:i:m:t:n:s:e:p:d:a:f:h:z", long_options, &long_index)) != -1) { switch (opt) { case 'c': cmd = optarg; @@ -693,9 +705,6 @@ int main(int argc, const char* argv[]) case 'd': delegate_password = optarg; break; - case 'u': - prompt = true; - break; case 'e': entropy_size = optarg; break; @@ -705,6 +714,12 @@ int main(int argc, const char* argv[]) case 'f': flags = optarg; break; + case 'h': + custom_path = optarg; + break; + case 'z': + yubikey = true; + break; default: print_usage(); exit(EXIT_SUCCESS); @@ -720,13 +735,14 @@ int main(int argc, const char* argv[]) printf("Prepare session with the TA\n"); prepare_tee_session(&ctx); - if (!yk_init()) { + YK_KEY *yk = NULL; + if (!yk_init() && yubikey) { fprintf(stderr, "Failed to initialize YubiKey\n"); - } - - YK_KEY *yk = yk_open_first_key(); - if (!yk) { - fprintf(stderr, "Failed to open YubiKey\n"); + } else if (yubikey) { + yk = yk_open_first_key(); + if (!yk) { + fprintf(stderr, "Failed to open YubiKey\n"); + } } if (strcmp(cmd, "generate_seed") == 0) { @@ -746,46 +762,71 @@ int main(int argc, const char* argv[]) } else if (strcmp(cmd, "generate_mnemonic") == 0) { printf("- Generate and encrypt a mnemonic\n"); - if (!shared_secret) { - shared_secret = getpass("Enter shared secret (hex, 40 characters): "); + if (yubikey) { if (!shared_secret) { - fprintf(stderr, "Failed to read shared secret\n"); - goto exit; + shared_secret = getpass("Enter shared secret for TOTP (hex, 40 characters) or press enter to generate one: "); + if (!shared_secret) { + fprintf(stderr, "Failed to read shared secret\n"); + goto exit; + } } - } - if (!password && prompt) { - password = getpass("Enter management password (or press enter for none): "); - if (!password) { - fprintf(stderr, "Failed to read password\n"); - goto exit; + if (strlen(shared_secret) == 0) { + printf("Shared secret not provided, generating one...\n"); + // Generate random 20 bytes (40 hex characters) + unsigned char random_bytes[TOTP_SECRET_HEX_SIZE / 2]; + dogecoin_random_bytes(random_bytes, sizeof(random_bytes), 0); + + shared_secret = malloc(TOTP_SECRET_HEX_SIZE + 1); + if (!shared_secret) { + fprintf(stderr, "Failed to allocate memory for shared secret\n"); + goto exit; + } + + utils_bin_to_hex(random_bytes, sizeof(random_bytes), shared_secret); + shared_secret[TOTP_SECRET_HEX_SIZE] = '\0'; + printf("Generated shared secret: %s\n", shared_secret); } - } - // Check if there is an existing configuration in slot 1 - YK_STATUS *status = ykds_alloc(); - if (yk) { - if (!yk_get_status(yk, status)) { - fprintf(stderr, "Failed to get YubiKey status\n"); + // Check if there is an existing configuration in slot 1 + YK_STATUS *status = ykds_alloc(); + if (yk) { + if (!yk_get_status(yk, status)) { + fprintf(stderr, "Failed to get YubiKey status\n"); + } } - } - // Check if slot 1 has a configuration - if (yk && (ykds_touch_level(status) & CONFIG1_VALID) == CONFIG1_VALID) { - char response; - printf("Slot 1 already has a configuration. Do you want to overwrite it? (y/N): "); - scanf(" %c", &response); - if (response != 'y' && response != 'Y') { - printf("Aborted by user\n"); - ykds_free(status); - goto exit; + // Check if slot 1 has a configuration + if (yk && (ykds_touch_level(status) & CONFIG1_VALID) == CONFIG1_VALID) { + char response; + printf("Slot 1 already has a configuration. Do you want to overwrite it? (y/N): "); + scanf(" %c", &response); + if (response != 'y' && response != 'Y') { + printf("Aborted by user\n"); + ykds_free(status); + goto exit; + } } - } - ykds_free(status); + ykds_free(status); - if (yk) { - set_totp_secret(yk, shared_secret, SLOT_CONFIG); + if (yk) { + set_totp_secret(yk, shared_secret, SLOT_CONFIG); + } + } + + if (!password && !shared_secret) { + password = getpass("Enter management password: "); + if (strlen(password) == 0) { + fprintf(stderr, "Password cannot be empty\n"); + goto exit; + } + printf("\n"); + if (strcmp (password, getpass("Confirm password: ")) != 0) { + fprintf(stderr, "Password mismatch\n"); + goto exit; + } + printf("\n"); } TEEC_Result res = generate_mnemonic(&ctx, shared_secret, password, flags, mnemonic, entropy_size); @@ -796,20 +837,20 @@ int main(int argc, const char* argv[]) char address[35]; size_t address_len = sizeof(address); - if (!password && prompt) { - password = getpass("Enter management password (or press enter for none): "); + if (!password && !yubikey && auth_token == 0) { + password = getpass("Enter management password: "); if (!password) { fprintf(stderr, "Failed to read password\n"); goto exit; } } - if (auth_token == 0) { + if (auth_token == 0 && yubikey) { auth_token = get_totp_from_yubikey(yk, SLOT_CHAL_HMAC1); } printf("Auth token: %u\n", auth_token); - TEEC_Result res = generate_address_ta(&ctx, account, address_index, change_level, auth_token, password, address, &address_len); + TEEC_Result res = generate_address_ta(&ctx, custom_path, account, address_index, change_level, auth_token, password, address, &address_len); if (res != TEEC_SUCCESS) errx(1, "Failed to generate address"); else @@ -819,20 +860,20 @@ int main(int argc, const char* argv[]) char pubkey[HDKEYLEN]; size_t pubkey_len = sizeof(pubkey); - if (!password && prompt) { - password = getpass("Enter management password (or press enter for none): "); + if (!password && !yubikey && auth_token == 0) { + password = getpass("Enter management password: "); if (!password) { fprintf(stderr, "Failed to read password\n"); goto exit; } } - if (auth_token == 0) { + if (auth_token == 0 && yubikey) { auth_token = get_totp_from_yubikey(yk, SLOT_CHAL_HMAC1); } printf("Auth token: %u\n", auth_token); - TEEC_Result res = generate_extended_public_key_ta(&ctx, account, change_level, auth_token, password, pubkey, &pubkey_len); + TEEC_Result res = generate_extended_public_key_ta(&ctx, custom_path, account, change_level, auth_token, password, pubkey, &pubkey_len); if (res != TEEC_SUCCESS) errx(1, "Failed to generate public extended key"); else @@ -842,20 +883,20 @@ int main(int argc, const char* argv[]) char signature[2048] = {0}; size_t signature_len = sizeof(signature); - if (!password && prompt) { - password = getpass("Enter management password (or press enter for none): "); + if (!password && !yubikey && auth_token == 0) { + password = getpass("Enter management password: "); if (!password) { fprintf(stderr, "Failed to read password\n"); goto exit; } } - if (auth_token == 0) { + if (auth_token == 0 && yubikey) { auth_token = get_totp_from_yubikey(yk, SLOT_CHAL_HMAC1); } printf("Auth token: %u\n", auth_token); - TEEC_Result res = sign_message_ta(&ctx, account, address_index, change_level, auth_token, password, message ? message : "This is a test message.", signature, &signature_len); + TEEC_Result res = sign_message_ta(&ctx, custom_path, account, address_index, change_level, auth_token, password, message ? message : "This is a test message", signature, &signature_len); if (res != TEEC_SUCCESS) errx(1, "Failed to sign the message"); else @@ -909,20 +950,20 @@ int main(int argc, const char* argv[]) printf("Raw transaction created: %s\n", raw_tx); printf("Raw transaction length: %zu\n", strlen(raw_tx)); - if (!password && prompt) { - password = getpass("Enter management password (or press enter for none): "); + if (!password && !yubikey && auth_token == 0) { + password = getpass("Enter management password: "); if (!password) { fprintf(stderr, "Failed to read password\n"); goto exit; } } - if (auth_token == 0) { + if (auth_token == 0 && yubikey) { auth_token = get_totp_from_yubikey(yk, SLOT_CHAL_HMAC1); } printf("Auth token: %u\n", auth_token); - TEEC_Result res = sign_transaction_ta(&ctx, account, address_index, change_level, auth_token, password, (transaction != NULL) ? transaction : raw_tx, strlen((transaction != NULL) ? transaction : raw_tx) + 1, signed_tx, &signed_tx_len); + TEEC_Result res = sign_transaction_ta(&ctx, custom_path, account, address_index, change_level, auth_token, password, (transaction != NULL) ? transaction : raw_tx, strlen((transaction != NULL) ? transaction : raw_tx) + 1, signed_tx, &signed_tx_len); if (res != TEEC_SUCCESS) errx(1, "Failed to sign the transaction"); else @@ -931,7 +972,7 @@ int main(int argc, const char* argv[]) printf("- Delegate a key\n"); char delegate_key[HDKEYLEN]; - if (!delegate_password && prompt) { + if (!delegate_password) { delegate_password = getpass("Enter delegate password: "); if (!delegate_password) { fprintf(stderr, "Failed to read delegate password\n"); @@ -939,20 +980,20 @@ int main(int argc, const char* argv[]) } } - if (!password && prompt) { - password = getpass("Enter management password (or press enter for none): "); + if (!password && !yubikey && auth_token == 0) { + password = getpass("Enter management password: "); if (!password) { fprintf(stderr, "Failed to read password\n"); goto exit; } } - if (auth_token == 0) { + if (auth_token == 0 && yubikey) { auth_token = get_totp_from_yubikey(yk, SLOT_CHAL_HMAC1); } printf("Auth token: %u\n", auth_token); - TEEC_Result res = delegate_key_ta(&ctx, account, auth_token, delegate_password, password, delegate_key); + TEEC_Result res = delegate_key_ta(&ctx, custom_path, account, auth_token, delegate_password, password, delegate_key); if (res != TEEC_SUCCESS) errx(1, "Failed to delegate the key"); else @@ -961,7 +1002,7 @@ int main(int argc, const char* argv[]) printf("- Export a delegated key\n"); char exported_key[HDKEYLEN]; - if (!delegate_password && prompt) { + if (!delegate_password) { delegate_password = getpass("Enter delegate password: "); if (!delegate_password) { fprintf(stderr, "Failed to read delegate password\n"); @@ -969,7 +1010,7 @@ int main(int argc, const char* argv[]) } } - TEEC_Result res = export_delegate_key_ta(&ctx, account, delegate_password, exported_key); + TEEC_Result res = export_delegate_key_ta(&ctx, custom_path, account, delegate_password, exported_key); if (res != TEEC_SUCCESS) errx(1, "Failed to export the delegated key"); else diff --git a/src/optee/nanopi6.h.patch b/src/optee/nanopi6.h.patch deleted file mode 100644 index babf5565..00000000 --- a/src/optee/nanopi6.h.patch +++ /dev/null @@ -1,24 +0,0 @@ ---- a/nanopi6.h -+++ b/nanopi6.h -@@ -31,20 +31,10 @@ - "stderr=serial,vidconsole\0" - - #define RKIMG_DET_BOOTDEV \ -- "rkimg_bootdev=" \ -- "if mmc dev 1 && rkimgtest mmc 1; then " \ -- "setenv devtype mmc; setenv devnum 1; echo Boot from SDcard;" \ -- "elif mmc dev 0; then " \ -- "setenv devtype mmc; setenv devnum 0;" \ -- "elif rksfc dev 1; then " \ -- "setenv devtype spinor; setenv devnum 1;" \ -- "fi; \0" -+ "bootcmd=setenv bootdev unknown; setenv kernel_addr_gz 0xa2080000; setenv fdt_addr_r 0x08300000; setenv kernel /boot/Image.gz; setenv fdtfile rk3588-nanopi6-rev01.dtb; for d in 1 0; do test ${bootdev} = unknown && echo .. Looking for ${kernel} in mmc ${d}:5 && test -e mmc ${d}:5 ${kernel} && setenv bootdev ${d} && echo .. Found; done; if test ${bootdev} = unknown; then echo .. Kernel not found; else echo .. Loading kernel; ext2load mmc ${bootdev}:5 ${kernel_addr_gz} ${kernel}; unzip ${kernel_addr_gz} ${kernel_addr_r}; echo .. Loading DTB: mmc ${bootdev}:5 ${fdtfile}; ext2load mmc ${bootdev}:5 ${fdt_addr_r} /boot/${fdtfile}; echo .. Booting kernel; booti ${kernel_addr_r} - ${fdt_addr_r}; fi; \0" - - #define RKIMG_BOOTCOMMAND \ -- "boot_fit;" \ -- "boot_android ${devtype} ${devnum};" \ -- "bootrkp;" \ -- "run distro_bootcmd;" -+ "run bootcmd;" - - #define CONFIG_BOOTCOMMAND RKIMG_BOOTCOMMAND diff --git a/src/optee/qemu.conf.patch b/src/optee/qemu.conf.patch deleted file mode 100644 index 083ee3b8..00000000 --- a/src/optee/qemu.conf.patch +++ /dev/null @@ -1,7 +0,0 @@ ---- a qemu.conf -+++ b qemu.conf -@@ -13,3 +13,4 @@ - CONFIG_TRUSTED_KEYS=y - CONFIG_ENCRYPTED_KEYS=y - CONFIG_ARM_FFA_TRANSPORT=y -+CONFIG_USB_HID=n diff --git a/src/optee/rk3588-nanopi6-common.dtsi.patch b/src/optee/rk3588-nanopi6-common.dtsi.patch deleted file mode 100644 index 585597f7..00000000 --- a/src/optee/rk3588-nanopi6-common.dtsi.patch +++ /dev/null @@ -1,27 +0,0 @@ ---- a/rk3588-nanopi6-common.dtsi -+++ b/rk3588-nanopi6-common.dtsi -@@ -124,6 +124,24 @@ pwm_backlight: pwm-backlight { - test-power { - status = "okay"; - }; -+ -+ firmware { -+ optee { -+ compatible = "linaro,optee-tz"; -+ method = "smc"; -+ }; -+ }; -+ -+ reserved-memory { -+ #address-cells = <2>; -+ #size-cells = <2>; -+ ranges; -+ -+ optee@8400000 { -+ reg = <0x0 0x8400000 0x0 0x2000000>; -+ no-map; -+ }; -+ }; - }; - - &av1d_mmu { diff --git a/src/optee/ta/libdogecoin_ta.c b/src/optee/ta/libdogecoin_ta.c index 2a454276..0e89bbbf 100644 --- a/src/optee/ta/libdogecoin_ta.c +++ b/src/optee/ta/libdogecoin_ta.c @@ -214,6 +214,9 @@ int fclose(void *stream) // Maximum size of managed credentials (shared secret, password) #define MAX_MANAGED_CREDS_SIZE 1024 +// Maximum size of a delegate name +#define MAX_DELEGATE_NAME_SIZE 256 + uint32_t get_totp(const char* shared_secret, uint64_t timestamp) { uint8_t hmac[SHA1_DIGEST_LENGTH]; uint8_t time_bytes[8]; @@ -423,6 +426,18 @@ static TEE_Result generate_and_store_mnemonic(uint32_t param_types, TEE_Param pa char mnemonic_and_creds[MAX_MNEMONIC_SIZE + MAX_MANAGED_CREDS_SIZE] = {0}; snprintf(mnemonic_and_creds, sizeof(mnemonic_and_creds), "%s,%s", mnemonic, managed_creds); + // Split the managed credentials into shared secret and password + char* saveptr; + char* shared_secret = strtok_r(managed_creds, ",", &saveptr); + char* stored_password = strtok_r(NULL, ",", &saveptr); + + // Check if the shared secret or password is present + if (!shared_secret && !stored_password) { + EMSG("Shared secret or password required"); + dogecoin_ecc_stop(); + return TEE_ERROR_BAD_PARAMETERS; + } + dogecoin_ecc_stop(); // Create a persistent object to store the mnemonic and managed credentials @@ -509,28 +524,31 @@ static TEE_Result generate_extended_public_key(uint32_t param_types, TEE_Param p char* shared_secret = strtok_r(NULL, ",", &saveptr); char* stored_password = strtok_r(NULL, ",", &saveptr); + // If the auth token is 0 and no password is provided, then return + if (auth_token == 0 && !password) { + EMSG("Password or auth token required"); + return TEE_ERROR_SECURITY; + } + // Verify that the password is part of the managed credentials, if supplied if ((password_size > 0 && password && stored_password && strcmp(password, stored_password)) || - (password_size == 0 && stored_password && strcmp(stored_password, "none") != 0) || - (password_size > 0 && !password && stored_password && strcmp(stored_password, "none") != 0)) { + (password_size == 0 && stored_password && strcmp(stored_password, "|") != 0) || + (password_size > 0 && !password && stored_password && strcmp(stored_password, "|") != 0)) { EMSG("Password verification failed"); return TEE_ERROR_SECURITY; } - // Verify TOTP using the managed credentials - TEE_Time current_time; - TEE_GetREETime(¤t_time); - uint32_t totp = get_totp(shared_secret, current_time.seconds / TOTP_TIME_STEP); - - char totp_str[AUTH_TOKEN_LEN + 1]; - snprintf(totp_str, sizeof(totp_str), "%06u", totp); + // If a shared secret is present, then perform TOTP verification + if (strcmp(shared_secret, "|") != 0) { + // Verify TOTP using the managed credentials + TEE_Time current_time; + TEE_GetREETime(¤t_time); + uint32_t totp = get_totp(shared_secret, current_time.seconds / TOTP_TIME_STEP); - char auth_token_str[AUTH_TOKEN_LEN + 1]; - snprintf(auth_token_str, sizeof(auth_token_str), "%06u", auth_token); - - if (strcmp(totp_str, auth_token_str) != 0) { - EMSG("TOTP verification failed"); - return TEE_ERROR_SECURITY; + if (auth_token != totp) { + EMSG("TOTP verification failed"); + return TEE_ERROR_SECURITY; + } } SEED seed; @@ -544,7 +562,11 @@ static TEE_Result generate_extended_public_key(uint32_t param_types, TEE_Param p char key_path_out[KEYPATHMAXLEN]; getHDRootKeyFromSeed(seed, sizeof(seed), false, master_key); - deriveBIP44ExtendedPublicKey(master_key, NULL, NULL, NULL, key_path, pubkey, key_path_out); + if (!deriveBIP44ExtendedPublicKey(master_key, NULL, NULL, NULL, key_path, pubkey, key_path_out)) { + dogecoin_ecc_stop(); + EMSG("Failed to derive extended public key"); + return TEE_ERROR_GENERIC; + } dogecoin_ecc_stop(); @@ -607,28 +629,31 @@ static TEE_Result generate_address(uint32_t param_types, TEE_Param params[4]) { char* shared_secret = strtok_r(NULL, ",", &saveptr); char* stored_password = strtok_r(NULL, ",", &saveptr); + // If the auth token is 0 and no password is provided, then return + if (auth_token == 0 && !password) { + EMSG("Password or auth token required"); + return TEE_ERROR_SECURITY; + } + // Verify that the password is part of the managed credentials, if supplied if ((password_size > 0 && password && stored_password && strcmp(password, stored_password)) || - (password_size == 0 && stored_password && strcmp(stored_password, "none") != 0) || - (password_size > 0 && !password && stored_password && strcmp(stored_password, "none") != 0)) { + (password_size == 0 && stored_password && strcmp(stored_password, "|") != 0) || + (password_size > 0 && !password && stored_password && strcmp(stored_password, "|") != 0)) { EMSG("Password verification failed"); return TEE_ERROR_SECURITY; } - // Verify TOTP using the managed credentials - TEE_Time current_time; - TEE_GetREETime(¤t_time); - uint32_t totp = get_totp(shared_secret, current_time.seconds / TOTP_TIME_STEP); - - char totp_str[AUTH_TOKEN_LEN + 1]; - snprintf(totp_str, sizeof(totp_str), "%06u", totp); - - char auth_token_str[AUTH_TOKEN_LEN + 1]; - snprintf(auth_token_str, sizeof(auth_token_str), "%06u", auth_token); + // If a shared secret is present, then perform TOTP verification + if (strcmp(shared_secret, "|") != 0) { + // Verify TOTP using the managed credentials + TEE_Time current_time; + TEE_GetREETime(¤t_time); + uint32_t totp = get_totp(shared_secret, current_time.seconds / TOTP_TIME_STEP); - if (strcmp(totp_str, auth_token_str) != 0) { - EMSG("TOTP verification failed"); - return TEE_ERROR_SECURITY; + if (auth_token != totp) { + EMSG("TOTP verification failed"); + return TEE_ERROR_SECURITY; + } } SEED seed; @@ -707,28 +732,31 @@ static TEE_Result sign_message_with_private_key(uint32_t param_types, TEE_Param char* shared_secret = strtok_r(NULL, ",", &saveptr2); char* stored_password = strtok_r(NULL, ",", &saveptr2); + // If the auth token is 0 and no password is provided, then return + if (auth_token == 0 && !password) { + EMSG("Password or auth token required"); + return TEE_ERROR_SECURITY; + } + // Verify that the password is part of the managed credentials, if supplied if ((password_size > 0 && password && stored_password && strcmp(password, stored_password)) || - (password_size == 0 && stored_password && strcmp(stored_password, "none") != 0) || - (password_size > 0 && !password && stored_password && strcmp(stored_password, "none") != 0)) { + (password_size == 0 && stored_password && strcmp(stored_password, "|") != 0) || + (password_size > 0 && !password && stored_password && strcmp(stored_password, "|") != 0)) { EMSG("Password verification failed"); return TEE_ERROR_SECURITY; } - // Verify TOTP using the managed credentials - TEE_Time current_time; - TEE_GetREETime(¤t_time); - uint32_t totp = get_totp(shared_secret, current_time.seconds / TOTP_TIME_STEP); - - char totp_str[AUTH_TOKEN_LEN + 1]; - snprintf(totp_str, sizeof(totp_str), "%06u", totp); + // If a shared secret is present, then perform TOTP verification + if (strcmp(shared_secret, "|") != 0) { + // Verify TOTP using the managed credentials + TEE_Time current_time; + TEE_GetREETime(¤t_time); + uint32_t totp = get_totp(shared_secret, current_time.seconds / TOTP_TIME_STEP); - char auth_token_str[AUTH_TOKEN_LEN + 1]; - snprintf(auth_token_str, sizeof(auth_token_str), "%06u", auth_token); - - if (strcmp(totp_str, auth_token_str) != 0) { - EMSG("TOTP verification failed"); - return TEE_ERROR_SECURITY; + if (auth_token != totp) { + EMSG("TOTP verification failed"); + return TEE_ERROR_SECURITY; + } } SEED seed; @@ -745,7 +773,6 @@ static TEE_Result sign_message_with_private_key(uint32_t param_types, TEE_Param char privkeywif[PRIVKEYWIFLEN]; size_t wiflen = PRIVKEYWIFLEN; const dogecoin_chainparams* chain = chain_from_b58_prefix(master_key); - dogecoin_hdnode* hdnode = getHDNodeAndExtKeyByPath(master_key, key_path, outaddress, true); dogecoin_privkey_encode_wif((const dogecoin_key*)hdnode->private_key, chain, privkeywif, &wiflen); @@ -823,28 +850,31 @@ static TEE_Result sign_transaction_with_private_key(uint32_t param_types, TEE_Pa char* shared_secret = strtok_r(NULL, ",", &saveptr2); char* stored_password = strtok_r(NULL, ",", &saveptr2); + // If the auth token is 0 and no password is provided, then return + if (auth_token == 0 && !password) { + EMSG("Password or auth token required"); + return TEE_ERROR_SECURITY; + } + // Verify that the password is part of the managed credentials, if supplied if ((password_size > 0 && password && stored_password && strcmp(password, stored_password)) || - (password_size == 0 && stored_password && strcmp(stored_password, "none") != 0) || - (password_size > 0 && !password && stored_password && strcmp(stored_password, "none") != 0)) { + (password_size == 0 && stored_password && strcmp(stored_password, "|") != 0) || + (password_size > 0 && !password && stored_password && strcmp(stored_password, "|") != 0)) { EMSG("Password verification failed"); return TEE_ERROR_SECURITY; } - // Verify TOTP using the managed credentials - TEE_Time current_time; - TEE_GetREETime(¤t_time); - uint32_t totp = get_totp(shared_secret, current_time.seconds / TOTP_TIME_STEP); - - char totp_str[AUTH_TOKEN_LEN + 1]; - snprintf(totp_str, sizeof(totp_str), "%06u", totp); + // If a shared secret is present, then perform TOTP verification + if (strcmp(shared_secret, "|") != 0) { + // Verify TOTP using the managed credentials + TEE_Time current_time; + TEE_GetREETime(¤t_time); + uint32_t totp = get_totp(shared_secret, current_time.seconds / TOTP_TIME_STEP); - char auth_token_str[AUTH_TOKEN_LEN + 1]; - snprintf(auth_token_str, sizeof(auth_token_str), "%06u", auth_token); - - if (strcmp(totp_str, auth_token_str) != 0) { - EMSG("TOTP verification failed"); - return TEE_ERROR_SECURITY; + if (auth_token != totp) { + EMSG("TOTP verification failed"); + return TEE_ERROR_SECURITY; + } } SEED seed; @@ -922,7 +952,7 @@ static TEE_Result delegate_key(uint32_t param_types, TEE_Param params[4]) { TEE_DATA_FLAG_ACCESS_WRITE_META | TEE_DATA_FLAG_OVERWRITE; char *delegate_key = params[0].memref.buffer; - const char *account = (const char *)params[1].memref.buffer; + const char *key_path = (const char *)params[1].memref.buffer; uint32_t auth_token = params[2].value.a; char *delegate_creds = params[3].memref.buffer; @@ -960,6 +990,12 @@ static TEE_Result delegate_key(uint32_t param_types, TEE_Param params[4]) { char* delegate_password = strtok_r(delegate_creds, ",", &saveptr2); char* password = strtok_r(NULL, ",", &saveptr2); + // If the auth token is 0 and no password is provided, then return + if (auth_token == 0 && !password) { + EMSG("Password or auth token required"); + return TEE_ERROR_SECURITY; + } + // Verify flag for delegate key creation if (!flags || strcmp(flags, "delegate") != 0) { EMSG("Delegate key creation flag not set"); @@ -973,45 +1009,26 @@ static TEE_Result delegate_key(uint32_t param_types, TEE_Param params[4]) { } // Verify delegate password was provided - if (strcmp(delegate_password, "none") == 0) { + if (strcmp(delegate_password, "|") == 0) { EMSG("Delegate password not provided"); return TEE_ERROR_BAD_PARAMETERS; } - // Verify TOTP using the managed credentials - TEE_Time current_time; - TEE_GetREETime(¤t_time); - uint32_t totp = get_totp(shared_secret, current_time.seconds / TOTP_TIME_STEP); - - char totp_str[AUTH_TOKEN_LEN + 1]; - snprintf(totp_str, sizeof(totp_str), "%06u", totp); + // If a shared secret is present, then perform TOTP verification + if (strcmp(shared_secret, "|") != 0) { + // Verify TOTP using the managed credentials + TEE_Time current_time; + TEE_GetREETime(¤t_time); + uint32_t totp = get_totp(shared_secret, current_time.seconds / TOTP_TIME_STEP); - char auth_token_str[AUTH_TOKEN_LEN + 1]; - snprintf(auth_token_str, sizeof(auth_token_str), "%06u", auth_token); - - if (strcmp(totp_str, auth_token_str) != 0) { - EMSG("TOTP verification failed"); - return TEE_ERROR_SECURITY; - } - - // Store the delegate credentials and mnemonic with the account number as the ID - char delegate_object_data[MAX_MNEMONIC_SIZE + MAX_MANAGED_CREDS_SIZE]; - snprintf(delegate_object_data, sizeof(delegate_object_data), "%s,%s", mnemonic, delegate_creds); - - char delegate_object_id[64]; - snprintf(delegate_object_id, sizeof(delegate_object_id), "delegate_%s", account); - - res = TEE_CreatePersistentObject(TEE_STORAGE_PRIVATE, delegate_object_id, strlen(delegate_object_id), - obj_data_flag, TEE_HANDLE_NULL, delegate_object_data, strlen(delegate_object_data), &object); - if (res != TEE_SUCCESS) { - EMSG("Failed to create persistent delegate object, res=0x%08x", res); - return res; + if (auth_token != totp) { + EMSG("TOTP verification failed"); + return TEE_ERROR_SECURITY; + } } - TEE_CloseObject(object); - // Derive the private child key - char key_path[BIP44_KEY_PATH_MAX_LENGTH + 1] = ""; + char key_path_out[BIP44_KEY_PATH_MAX_LENGTH + 1] = ""; SEED seed; dogecoin_seed_from_mnemonic((const char*)mnemonic, delegate_password, seed); @@ -1023,11 +1040,27 @@ static TEE_Result delegate_key(uint32_t param_types, TEE_Param params[4]) { char privkey[HDKEYLEN]; getHDRootKeyFromSeed(seed, sizeof(seed), false, master_key); - deriveBIP44ExtendedKey(master_key, NULL, NULL, NULL, account, privkey, key_path); + deriveBIP44ExtendedKey(master_key, NULL, NULL, NULL, key_path, privkey, key_path_out); strncpy(delegate_key, privkey, HDKEYLEN); dogecoin_ecc_stop(); + // Store the delegate credentials and mnemonic with the account number as the ID + char delegate_object_data[HDKEYLEN + MAX_MANAGED_CREDS_SIZE]; + snprintf(delegate_object_data, sizeof(delegate_object_data), "%s,%s", privkey, delegate_creds); + + char delegate_object_id[MAX_DELEGATE_NAME_SIZE]; + snprintf(delegate_object_id, sizeof(delegate_object_id), "%s", key_path_out); + + res = TEE_CreatePersistentObject(TEE_STORAGE_PRIVATE, delegate_object_id, strlen(delegate_object_id), + obj_data_flag, TEE_HANDLE_NULL, delegate_object_data, strlen(delegate_object_data), &object); + if (res != TEE_SUCCESS) { + EMSG("Failed to create persistent delegate object, res=0x%08x", res); + return res; + } + + TEE_CloseObject(object); + EMSG("Delegate key generated and stored successfully"); return TEE_SUCCESS; @@ -1050,8 +1083,8 @@ static TEE_Result export_delegate_key(uint32_t param_types, TEE_Param params[4]) char *exported_key = params[0].memref.buffer; // Open the delegate persistent object - char delegate_object_id[64]; - snprintf(delegate_object_id, sizeof(delegate_object_id), "delegate_%s", key_path); + char delegate_object_id[MAX_DELEGATE_NAME_SIZE]; + snprintf(delegate_object_id, sizeof(delegate_object_id), "%s", key_path); EMSG("Exporting delegate key"); @@ -1074,7 +1107,7 @@ static TEE_Result export_delegate_key(uint32_t param_types, TEE_Param params[4]) // Split the delegate object data into mnemonic and stored delegate credentials char* saveptr; - char* mnemonic = strtok_r(delegate_object_data, ",", &saveptr); + char* privkey = strtok_r(delegate_object_data, ",", &saveptr); char* stored_delegate_password = strtok_r(NULL, ",", &saveptr); // Verify that the password is the same as the stored delegate password @@ -1085,23 +1118,8 @@ static TEE_Result export_delegate_key(uint32_t param_types, TEE_Param params[4]) return TEE_ERROR_SECURITY; } - SEED seed; - dogecoin_seed_from_mnemonic((const char*)mnemonic, password, seed); - - set_rng(&TEE_GenerateRandom); - - dogecoin_ecc_start(); - - char master_key[HDKEYLEN]; - char privkey[HDKEYLEN]; - char key_path_out[KEYPATHMAXLEN]; - getHDRootKeyFromSeed(seed, sizeof(seed), false, master_key); - - deriveBIP44ExtendedKey(master_key, NULL, NULL, NULL, key_path, privkey, key_path_out); strncpy(exported_key, privkey, HDKEYLEN); - dogecoin_ecc_stop(); - EMSG("Delegate key exported successfully"); return TEE_SUCCESS;