Compare commits
50 Commits
Author | SHA1 | Date |
---|---|---|
dsc | 330edacab9 | 3 months ago |
dsc | ec6474a840 | 6 months ago |
dsc | 69e1749856 | 10 months ago |
dsc | e93488af9b | 10 months ago |
dsc | a8861c62ea | 10 months ago |
dsc | e6651d55ff | 10 months ago |
dsc | 9c58913ee2 | 10 months ago |
dsc | 24ff2b7120 | 1 year ago |
dsc | 7c4f99c85d | 1 year ago |
wowario | 7cc5fd880e | 1 year ago |
wowario | ed9edb5ad6 | 1 year ago |
dsc | c0323ee329 | 1 year ago |
dsc | 40493475ba | 1 year ago |
dsc | 4098e8c0e5 | 2 years ago |
dsc | ca234008b9 | 2 years ago |
dsc | 2ebb41a371 | 2 years ago |
dsc | 6cf4299f78 | 2 years ago |
dsc | df0459da69 | 2 years ago |
dsc | 46accb1077 | 2 years ago |
dsc | 7a91ba5a84 | 2 years ago |
dsc | f83ceb2a96 | 2 years ago |
dsc | d332121d7c | 2 years ago |
dsc | d151b47895 | 2 years ago |
dsc | caa8731410 | 2 years ago |
dsc | dc3ee66e3b | 2 years ago |
dsc | 289f9ab1d2 | 2 years ago |
dsc | 73747e05a7 | 2 years ago |
dsc | 3051ce5118 | 2 years ago |
dsc | fb32fa2fd2 | 2 years ago |
dsc | b3eab6085f | 2 years ago |
dsc | 835aecb79d | 2 years ago |
dsc | 6cba5d0487 | 2 years ago |
dsc | 50b78cee51 | 2 years ago |
dsc | 1b6f648a0b | 2 years ago |
dsc | 6b2f8f847e | 2 years ago |
dsc | 917f8b5812 | 2 years ago |
dsc | a62fb95fbf | 2 years ago |
dsc | c3b0d00a72 | 2 years ago |
dsc | d6dfd678b8 | 2 years ago |
dsc | 8b215c1e73 | 2 years ago |
dsc | 96295a52de | 2 years ago |
dsc | ccd0e8e64b | 2 years ago |
dsc | 3b3ec89306 | 2 years ago |
dsc | 373fe8e02a | 2 years ago |
dsc | 14d9793193 | 2 years ago |
dsc | 65ceab6323 | 3 years ago |
bruh | 6b2118ecf6 | 3 years ago |
dsc | ca78025735 | 3 years ago |
leonardgit6 | c5eb13145f | 3 years ago |
leonardgit6 | 946443bf8c | 3 years ago |
@ -1,13 +0,0 @@
|
|||||||
# this image is used internally for the buildbot
|
|
||||||
FROM ubuntu:20.04
|
|
||||||
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
|
||||||
|
|
||||||
RUN apt clean && apt update
|
|
||||||
RUN apt install -y git build-essential wget curl ngrep unzip file ssh zip
|
|
||||||
|
|
||||||
RUN cat /dev/zero | ssh-keygen -q -N ""
|
|
||||||
|
|
||||||
RUN cat ~/.ssh/id_rsa.pub
|
|
||||||
|
|
||||||
RUN printf "Host *\n StrictHostKeyChecking no" > ~/.ssh/config
|
|
@ -1,12 +1,14 @@
|
|||||||
[![Build Status](https://ci.wownero.com/api/badges/wowlet/wowlet/status.svg)](https://ci.wownero.com/wowlet/wowlet)
|
|
||||||
|
|
||||||
# WOWlet- a free Wownero desktop wallet
|
# WOWlet- a free Wownero desktop wallet
|
||||||
|
|
||||||
|
WOWlet is a free, open-source Wownero client for Linux, Mac OS, and Windows.
|
||||||
|
|
||||||
WOWlet is a free, open-source Wownero client for Linux with ports for Mac OS and Windows.
|
![https://i.imgur.com/l7fUf0f.png](https://i.imgur.com/l7fUf0f.png)
|
||||||
|
|
||||||
## Development resources
|
## Development resources
|
||||||
|
|
||||||
* Git: [git.wownero.com/wowlet/wowlet](https://git.wownero.com/wowlet/wowlet)
|
* Git: [git.wownero.com/wowlet/wowlet](https://git.wownero.com/wowlet/wowlet)
|
||||||
* IRC: `#wownero` on Freenode
|
* IRC: `#wownero-dev` on [OFTC](https://oftc.net/)
|
||||||
|
* [Building WOWlet from source](https://git.wownero.com/wowlet/wowlet/src/branch/master/docs/BUILDING.md)
|
||||||
|
* [Working on WOWlet](https://git.wownero.com/wowlet/wowlet/src/branch/master/docs/HACKING.md)
|
||||||
|
|
||||||
Copyright (c) 2020-2021 The Monero Project.
|
Copyright (c) 2020-2021 The Monero Project.
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
# - Try to find Cairo
|
||||||
|
# Once done, this will define
|
||||||
|
#
|
||||||
|
# CAIRO_FOUND - system has Cairo
|
||||||
|
# CAIRO_INCLUDE_DIRS - the Cairo include directories
|
||||||
|
# CAIRO_LIBRARIES - link these to use Cairo
|
||||||
|
#
|
||||||
|
# Copyright (C) 2012 Raphael Kubo da Costa <rakuco@webkit.org>
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions
|
||||||
|
# are met:
|
||||||
|
# 1. Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS
|
||||||
|
# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS
|
||||||
|
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
FIND_PACKAGE(PkgConfig)
|
||||||
|
PKG_CHECK_MODULES(PC_CAIRO cairo) # FIXME: After we require CMake 2.8.2 we can pass QUIET to this call.
|
||||||
|
|
||||||
|
FIND_PATH(CAIRO_INCLUDE_DIRS
|
||||||
|
NAMES cairo.h
|
||||||
|
HINTS ${PC_CAIRO_INCLUDEDIR}
|
||||||
|
${PC_CAIRO_INCLUDE_DIRS}
|
||||||
|
PATH_SUFFIXES cairo
|
||||||
|
)
|
||||||
|
|
||||||
|
FIND_LIBRARY(CAIRO_LIBRARIES
|
||||||
|
NAMES cairo
|
||||||
|
HINTS ${PC_CAIRO_LIBDIR}
|
||||||
|
${PC_CAIRO_LIBRARY_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
IF (CAIRO_INCLUDE_DIRS)
|
||||||
|
IF (EXISTS "${CAIRO_INCLUDE_DIRS}/cairo-version.h")
|
||||||
|
FILE(READ "${CAIRO_INCLUDE_DIRS}/cairo-version.h" CAIRO_VERSION_CONTENT)
|
||||||
|
|
||||||
|
STRING(REGEX MATCH "#define +CAIRO_VERSION_MAJOR +([0-9]+)" _dummy "${CAIRO_VERSION_CONTENT}")
|
||||||
|
SET(CAIRO_VERSION_MAJOR "${CMAKE_MATCH_1}")
|
||||||
|
|
||||||
|
STRING(REGEX MATCH "#define +CAIRO_VERSION_MINOR +([0-9]+)" _dummy "${CAIRO_VERSION_CONTENT}")
|
||||||
|
SET(CAIRO_VERSION_MINOR "${CMAKE_MATCH_1}")
|
||||||
|
|
||||||
|
STRING(REGEX MATCH "#define +CAIRO_VERSION_MICRO +([0-9]+)" _dummy "${CAIRO_VERSION_CONTENT}")
|
||||||
|
SET(CAIRO_VERSION_MICRO "${CMAKE_MATCH_1}")
|
||||||
|
|
||||||
|
SET(CAIRO_VERSION "${CAIRO_VERSION_MAJOR}.${CAIRO_VERSION_MINOR}.${CAIRO_VERSION_MICRO}")
|
||||||
|
ENDIF ()
|
||||||
|
ENDIF ()
|
||||||
|
|
||||||
|
# FIXME: Should not be needed anymore once we start depending on CMake 2.8.3
|
||||||
|
SET(VERSION_OK TRUE)
|
||||||
|
IF (Cairo_FIND_VERSION)
|
||||||
|
IF (Cairo_FIND_VERSION_EXACT)
|
||||||
|
IF ("${Cairo_FIND_VERSION}" VERSION_EQUAL "${CAIRO_VERSION}")
|
||||||
|
# FIXME: Use IF (NOT ...) with CMake 2.8.2+ to get rid of the ELSE block
|
||||||
|
ELSE ()
|
||||||
|
SET(VERSION_OK FALSE)
|
||||||
|
ENDIF ()
|
||||||
|
ELSE ()
|
||||||
|
IF ("${Cairo_FIND_VERSION}" VERSION_GREATER "${CAIRO_VERSION}")
|
||||||
|
SET(VERSION_OK FALSE)
|
||||||
|
ENDIF ()
|
||||||
|
ENDIF ()
|
||||||
|
ENDIF ()
|
||||||
|
|
||||||
|
INCLUDE(FindPackageHandleStandardArgs)
|
||||||
|
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Cairo DEFAULT_MSG CAIRO_INCLUDE_DIRS CAIRO_LIBRARIES VERSION_OK)
|
@ -0,0 +1,26 @@
|
|||||||
|
# - Find XFixes
|
||||||
|
# Find the XFixes libraries
|
||||||
|
#
|
||||||
|
# This module defines the following variables:
|
||||||
|
# XFIXES_FOUND - 1 if XFIXES_INCLUDE_DIR & XFIXES_LIBRARY are found, 0 otherwise
|
||||||
|
# XFIXES_INCLUDE_DIR - where to find Xlib.h, etc.
|
||||||
|
# XFIXES_LIBRARY - the X11 library
|
||||||
|
#
|
||||||
|
|
||||||
|
find_path( XFIXES_INCLUDE_DIR
|
||||||
|
NAMES X11/extensions/Xfixes.h
|
||||||
|
PATH_SUFFIXES X11/extensions
|
||||||
|
DOC "The XFixes include directory" )
|
||||||
|
|
||||||
|
find_library( XFIXES_LIBRARY
|
||||||
|
NAMES Xfixes
|
||||||
|
PATHS /usr/lib /lib
|
||||||
|
DOC "The XFixes library" )
|
||||||
|
|
||||||
|
if( XFIXES_INCLUDE_DIR AND XFIXES_LIBRARY )
|
||||||
|
set( XFIXES_FOUND 1 )
|
||||||
|
else()
|
||||||
|
set( XFIXES_FOUND 0 )
|
||||||
|
endif()
|
||||||
|
|
||||||
|
mark_as_advanced( XFIXES_INCLUDE_DIR XFIXES_LIBRARY )
|
@ -1,33 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
unset SOURCE_DATE_EPOCH
|
|
||||||
|
|
||||||
APPDIR="$PWD/wowlet.AppDir"
|
|
||||||
|
|
||||||
mkdir -p "$APPDIR"
|
|
||||||
mkdir -p "$APPDIR/usr/share/applications/"
|
|
||||||
mkdir -p "$APPDIR/usr/bin"
|
|
||||||
|
|
||||||
cp "$PWD/src/assets/org.wowlet.wowlet.desktop" "$APPDIR/usr/share/applications/org.wowlet.wowlet.desktop"
|
|
||||||
cp "$PWD/src/assets/images/appicons/64x64.png" "$APPDIR/wowlet.png"
|
|
||||||
cp "$PWD/build/bin/wowlet" "$APPDIR/usr/bin/wowlet"
|
|
||||||
|
|
||||||
LD_LIBRARY_PATH=/usr/local/lib /linuxdeployqt/squashfs-root/AppRun wowlet.AppDir/usr/share/applications/org.wowlet.wowlet.desktop -bundle-non-qt-libs
|
|
||||||
|
|
||||||
find wowlet.AppDir/ -exec touch -h -a -m -t 202101010100.00 {} \;
|
|
||||||
|
|
||||||
# Manually create AppImage (reproducibly)
|
|
||||||
|
|
||||||
# download runtime
|
|
||||||
wget -nc https://github.com/AppImage/AppImageKit/releases/download/12/runtime-x86_64
|
|
||||||
echo "24da8e0e149b7211cbfb00a545189a1101cb18d1f27d4cfc1895837d2c30bc30 runtime-x86_64" | sha256sum -c
|
|
||||||
|
|
||||||
mksquashfs wowlet.AppDir wowlet.squashfs -info -root-owned -no-xattrs -noappend -fstime 0
|
|
||||||
# mksquashfs writes a timestamp to the header
|
|
||||||
printf '\x00\x00\x00\x00' | dd conv=notrunc of=wowlet.squashfs bs=1 seek=$((0x8))
|
|
||||||
|
|
||||||
rm -f wowlet.AppImage
|
|
||||||
cat runtime-x86_64 >> wowlet.AppImage
|
|
||||||
cat wowlet.squashfs >> wowlet.AppImage
|
|
||||||
chmod a+x wowlet.AppImage
|
|
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 152 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 440 KiB |
After Width: | Height: | Size: 4.2 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 8.6 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 148 KiB |
@ -0,0 +1,49 @@
|
|||||||
|
in Conversations::Conversations(), results into `/tmp/results.txt`
|
||||||
|
|
||||||
|
QString fn_wallet_temp = "/tmp/lol";
|
||||||
|
QString fn_wallet_temp_keys = "/tmp/lol.keys";
|
||||||
|
QString fn_results = "/tmp/results.txt";
|
||||||
|
|
||||||
|
QFile f_results(fn_results);
|
||||||
|
f_results.open(QIODevice::Append);
|
||||||
|
|
||||||
|
QTextStream f_results_stream(&f_results);
|
||||||
|
|
||||||
|
QFile f_newseeds("/tmp/new_seeds.txt"); // seeds to test for validness
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
int offset = 0;
|
||||||
|
if (f_newseeds.open(QIODevice::ReadOnly))
|
||||||
|
{
|
||||||
|
QTextStream f_newseeds_stream(&f_newseeds);
|
||||||
|
|
||||||
|
while (!f_newseeds_stream.atEnd()) {
|
||||||
|
if(i < offset) {
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile::remove(fn_wallet_temp);
|
||||||
|
QFile::remove(fn_wallet_temp_keys);
|
||||||
|
|
||||||
|
QString seed = f_newseeds_stream.readLine().trimmed();
|
||||||
|
qWarning() << "[" << QString::number(i) << "]" << seed;
|
||||||
|
|
||||||
|
auto wallet = this->walletManager->recoveryWallet(fn_wallet_temp, "", seed, "", this->networkType, 0, this->kdfRounds);
|
||||||
|
auto wallet_status = wallet->status();
|
||||||
|
|
||||||
|
if(wallet_status == Wallet::Status::Status_Ok) {
|
||||||
|
//QString addr = QString::fromStdString(wallet->address(0, 0));
|
||||||
|
auto addr = wallet->address(0, 0);
|
||||||
|
auto result_line = QString("%1 : %2").arg(addr, seed);
|
||||||
|
qWarning() << result_line;
|
||||||
|
f_results_stream << result_line << "\n";
|
||||||
|
f_results_stream.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete wallet;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f_results.close();
|
@ -1,21 +0,0 @@
|
|||||||
file(GLOB_RECURSE SOURCES *.cpp)
|
|
||||||
file(GLOB_RECURSE HEADERS *.h)
|
|
||||||
|
|
||||||
find_library(GCRYPT_LIBRARY gcrypt)
|
|
||||||
find_library(GPG_ERROR_LIBRARY gpg-error)
|
|
||||||
|
|
||||||
add_library(openpgp
|
|
||||||
${SOURCES}
|
|
||||||
${HEADERS})
|
|
||||||
|
|
||||||
find_package(GCrypt)
|
|
||||||
target_include_directories(openpgp PUBLIC
|
|
||||||
${CMAKE_SOURCE_DIR}/monero/contrib/epee/include
|
|
||||||
${GCRYPT_INCLUDE_DIRS}
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(openpgp
|
|
||||||
PUBLIC
|
|
||||||
epee
|
|
||||||
${GCRYPT_LIBRARY}
|
|
||||||
${GPG_ERROR_LIBRARY})
|
|
@ -1,107 +0,0 @@
|
|||||||
// Copyright (c) 2020-2021, The Monero Project
|
|
||||||
//
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without modification, are
|
|
||||||
// permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
||||||
// conditions and the following disclaimer.
|
|
||||||
//
|
|
||||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
||||||
// of conditions and the following disclaimer in the documentation and/or other
|
|
||||||
// materials provided with the distribution.
|
|
||||||
//
|
|
||||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
||||||
// used to endorse or promote products derived from this software without specific
|
|
||||||
// prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
||||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
||||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
||||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
||||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
||||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <gcrypt.h>
|
|
||||||
#include <span.h>
|
|
||||||
|
|
||||||
namespace openpgp
|
|
||||||
{
|
|
||||||
|
|
||||||
class hash
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum algorithm : uint8_t
|
|
||||||
{
|
|
||||||
sha256 = 8,
|
|
||||||
};
|
|
||||||
|
|
||||||
hash(const hash &) = delete;
|
|
||||||
hash &operator=(const hash &) = delete;
|
|
||||||
|
|
||||||
hash(uint8_t algorithm)
|
|
||||||
: algo(algorithm)
|
|
||||||
, consumed(0)
|
|
||||||
{
|
|
||||||
if (gcry_md_open(&md, algo, 0) != GPG_ERR_NO_ERROR)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("failed to create message digest object");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~hash()
|
|
||||||
{
|
|
||||||
gcry_md_close(md);
|
|
||||||
}
|
|
||||||
|
|
||||||
hash &operator<<(uint8_t byte)
|
|
||||||
{
|
|
||||||
gcry_md_putc(md, byte);
|
|
||||||
++consumed;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
hash &operator<<(const epee::span<const uint8_t> &bytes)
|
|
||||||
{
|
|
||||||
gcry_md_write(md, &bytes[0], bytes.size());
|
|
||||||
consumed += bytes.size();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
hash &operator<<(const std::vector<uint8_t> &bytes)
|
|
||||||
{
|
|
||||||
return *this << epee::to_span(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> finish() const
|
|
||||||
{
|
|
||||||
std::vector<uint8_t> result(gcry_md_get_algo_dlen(algo));
|
|
||||||
const void *digest = gcry_md_read(md, algo);
|
|
||||||
if (digest == nullptr)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("failed to read the digest");
|
|
||||||
}
|
|
||||||
memcpy(&result[0], digest, result.size());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t consumed_bytes() const
|
|
||||||
{
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
const uint8_t algo;
|
|
||||||
gcry_md_hd_t md;
|
|
||||||
size_t consumed;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
// Copyright (c) 2020-2021, The Monero Project
|
|
||||||
//
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without modification, are
|
|
||||||
// permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
||||||
// conditions and the following disclaimer.
|
|
||||||
//
|
|
||||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
||||||
// of conditions and the following disclaimer in the documentation and/or other
|
|
||||||
// materials provided with the distribution.
|
|
||||||
//
|
|
||||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
||||||
// used to endorse or promote products derived from this software without specific
|
|
||||||
// prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
||||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
||||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
||||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
||||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
||||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <gcrypt.h>
|
|
||||||
|
|
||||||
namespace openpgp
|
|
||||||
{
|
|
||||||
|
|
||||||
class mpi
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
mpi(const mpi &) = delete;
|
|
||||||
mpi &operator=(const mpi &) = delete;
|
|
||||||
|
|
||||||
mpi(mpi &&other)
|
|
||||||
: data(other.data)
|
|
||||||
{
|
|
||||||
other.data = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <
|
|
||||||
typename byte_container,
|
|
||||||
typename = typename std::enable_if<(sizeof(typename byte_container::value_type) == 1)>::type>
|
|
||||||
mpi(const byte_container &buffer, gcry_mpi_format format = GCRYMPI_FMT_USG)
|
|
||||||
: mpi(&buffer[0], buffer.size(), format)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
mpi(const void *buffer, size_t size, gcry_mpi_format format = GCRYMPI_FMT_USG)
|
|
||||||
{
|
|
||||||
if (gcry_mpi_scan(&data, format, buffer, size, nullptr) != GPG_ERR_NO_ERROR)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("failed to read mpi from buffer");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~mpi()
|
|
||||||
{
|
|
||||||
gcry_mpi_release(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
const gcry_mpi_t &get() const
|
|
||||||
{
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
gcry_mpi_t data;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace openpgp
|
|
@ -1,378 +0,0 @@
|
|||||||
// Copyright (c) 2020-2021, The Monero Project
|
|
||||||
//
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without modification, are
|
|
||||||
// permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
||||||
// conditions and the following disclaimer.
|
|
||||||
//
|
|
||||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
||||||
// of conditions and the following disclaimer in the documentation and/or other
|
|
||||||
// materials provided with the distribution.
|
|
||||||
//
|
|
||||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
||||||
// used to endorse or promote products derived from this software without specific
|
|
||||||
// prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
||||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
||||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
||||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
||||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
||||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
#include "openpgp.h"
|
|
||||||
|
|
||||||
#include <locale>
|
|
||||||
|
|
||||||
#include <string_coding.h>
|
|
||||||
|
|
||||||
#include "hash.h"
|
|
||||||
#include "mpi.h"
|
|
||||||
#include "packet_stream.h"
|
|
||||||
|
|
||||||
namespace openpgp
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
std::string::const_iterator find_next_line(std::string::const_iterator begin, const std::string::const_iterator &end)
|
|
||||||
{
|
|
||||||
begin = std::find(begin, end, '\n');
|
|
||||||
return begin != end ? ++begin : end;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string::const_iterator find_line_starting_with(
|
|
||||||
std::string::const_iterator it,
|
|
||||||
const std::string::const_iterator &end,
|
|
||||||
const std::string &starts_with)
|
|
||||||
{
|
|
||||||
for (std::string::const_iterator next_line; it != end; it = next_line)
|
|
||||||
{
|
|
||||||
next_line = find_next_line(it, end);
|
|
||||||
const size_t line_length = static_cast<size_t>(std::distance(it, next_line));
|
|
||||||
if (line_length >= starts_with.size() && std::equal(starts_with.begin(), starts_with.end(), it))
|
|
||||||
{
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return end;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string::const_iterator find_empty_line(std::string::const_iterator it, const std::string::const_iterator &end)
|
|
||||||
{
|
|
||||||
for (; it != end && *it != '\r' && *it != '\n'; it = find_next_line(it, end))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_armored_block_contents(const std::string &text, const std::string &block_name)
|
|
||||||
{
|
|
||||||
static constexpr const char dashes[] = "-----";
|
|
||||||
const std::string armor_header = dashes + block_name + dashes;
|
|
||||||
auto block_start = find_line_starting_with(text.begin(), text.end(), armor_header);
|
|
||||||
auto block_headers = find_next_line(block_start, text.end());
|
|
||||||
auto block_end = find_line_starting_with(block_headers, text.end(), dashes);
|
|
||||||
auto contents_begin = find_next_line(find_empty_line(block_headers, block_end), block_end);
|
|
||||||
if (contents_begin == block_end)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("armored block not found");
|
|
||||||
}
|
|
||||||
return std::string(contents_begin, block_end);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
public_key_rsa::public_key_rsa(s_expression expression, size_t bits)
|
|
||||||
: m_expression(std::move(expression))
|
|
||||||
, m_bits(bits)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const gcry_sexp_t &public_key_rsa::get() const
|
|
||||||
{
|
|
||||||
return m_expression.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t public_key_rsa::bits() const
|
|
||||||
{
|
|
||||||
return m_bits;
|
|
||||||
}
|
|
||||||
|
|
||||||
public_key_block::public_key_block(const std::string &armored)
|
|
||||||
: public_key_block(epee::to_byte_span(epee::to_span(epee::string_encoding::base64_decode(
|
|
||||||
strip_line_breaks(get_armored_block_contents(armored, "BEGIN PGP PUBLIC KEY BLOCK"))))))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Public-Key expiration, User ID and Public-Key certification, Subkey binding checks
|
|
||||||
public_key_block::public_key_block(const epee::span<const uint8_t> buffer)
|
|
||||||
{
|
|
||||||
packet_stream packets(buffer);
|
|
||||||
|
|
||||||
const std::vector<uint8_t> *data = packets.find_first(packet_tag::type::user_id);
|
|
||||||
if (data == nullptr)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("user id is missing");
|
|
||||||
}
|
|
||||||
m_user_id.assign(data->begin(), data->end());
|
|
||||||
|
|
||||||
const auto append_public_key = [this](const std::vector<uint8_t> &data) {
|
|
||||||
deserializer<std::vector<uint8_t>> serialized(data);
|
|
||||||
|
|
||||||
const auto version = serialized.read_big_endian<uint8_t>();
|
|
||||||
if (version != 4)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("unsupported public key version");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* const auto timestamp = */ serialized.read_big_endian<uint32_t>();
|
|
||||||
|
|
||||||
const auto algorithm = serialized.read_big_endian<uint8_t>();
|
|
||||||
if (algorithm != openpgp::algorithm::rsa)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("unsupported public key algorithm");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const mpi public_key_n = serialized.read_mpi();
|
|
||||||
const mpi public_key_e = serialized.read_mpi();
|
|
||||||
|
|
||||||
emplace_back(
|
|
||||||
s_expression("(public-key (rsa (n %m) (e %m)))", public_key_n.get(), public_key_e.get()),
|
|
||||||
gcry_mpi_get_nbits(public_key_n.get()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
data = packets.find_first(packet_tag::type::public_key);
|
|
||||||
if (data == nullptr)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("public key is missing");
|
|
||||||
}
|
|
||||||
append_public_key(*data);
|
|
||||||
|
|
||||||
packets.for_each(packet_tag::type::public_subkey, append_public_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string public_key_block::user_id() const
|
|
||||||
{
|
|
||||||
return m_user_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Signature expiration check
|
|
||||||
signature_rsa::signature_rsa(
|
|
||||||
uint8_t algorithm,
|
|
||||||
std::pair<uint8_t, uint8_t> hash_leftmost_bytes,
|
|
||||||
uint8_t hash_algorithm,
|
|
||||||
const std::vector<uint8_t> &hashed_data,
|
|
||||||
type type,
|
|
||||||
s_expression signature,
|
|
||||||
uint8_t version)
|
|
||||||
: m_hash_algorithm(hash_algorithm)
|
|
||||||
, m_hash_leftmost_bytes(hash_leftmost_bytes)
|
|
||||||
, m_hashed_appendix(format_hashed_appendix(algorithm, hash_algorithm, hashed_data, type, version))
|
|
||||||
, m_signature(std::move(signature))
|
|
||||||
, m_type(type)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
signature_rsa signature_rsa::from_armored(const std::string &armored_signed_message)
|
|
||||||
{
|
|
||||||
return from_base64(get_armored_block_contents(armored_signed_message, "BEGIN PGP SIGNATURE"));
|
|
||||||
}
|
|
||||||
|
|
||||||
signature_rsa signature_rsa::from_base64(const std::string &base64)
|
|
||||||
{
|
|
||||||
std::string decoded = epee::string_encoding::base64_decode(strip_line_breaks(base64));
|
|
||||||
epee::span<const uint8_t> buffer(reinterpret_cast<const uint8_t *>(&decoded[0]), decoded.size());
|
|
||||||
return from_buffer(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
signature_rsa signature_rsa::from_buffer(const epee::span<const uint8_t> input)
|
|
||||||
{
|
|
||||||
packet_stream packets(input);
|
|
||||||
|
|
||||||
const std::vector<uint8_t> *data = packets.find_first(packet_tag::type::signature);
|
|
||||||
if (data == nullptr)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("signature is missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializer<std::vector<uint8_t>> buffer(*data);
|
|
||||||
|
|
||||||
const auto version = buffer.read_big_endian<uint8_t>();
|
|
||||||
if (version != 4)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("unsupported signature version");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto signature_type = static_cast<type>(buffer.read_big_endian<uint8_t>());
|
|
||||||
|
|
||||||
const auto algorithm = buffer.read_big_endian<uint8_t>();
|
|
||||||
if (algorithm != openpgp::algorithm::rsa)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("unsupported signature algorithm");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto hash_algorithm = buffer.read_big_endian<uint8_t>();
|
|
||||||
|
|
||||||
const auto hashed_data_length = buffer.read_big_endian<uint16_t>();
|
|
||||||
std::vector<uint8_t> hashed_data = buffer.read(hashed_data_length);
|
|
||||||
|
|
||||||
const auto unhashed_data_length = buffer.read_big_endian<uint16_t>();
|
|
||||||
buffer.read_span(unhashed_data_length);
|
|
||||||
|
|
||||||
std::pair<uint8_t, uint8_t> hash_leftmost_bytes{buffer.read_big_endian<uint8_t>(), buffer.read_big_endian<uint8_t>()};
|
|
||||||
|
|
||||||
const mpi signature = buffer.read_mpi();
|
|
||||||
|
|
||||||
return signature_rsa(
|
|
||||||
algorithm,
|
|
||||||
std::move(hash_leftmost_bytes),
|
|
||||||
hash_algorithm,
|
|
||||||
hashed_data,
|
|
||||||
signature_type,
|
|
||||||
s_expression("(sig-val (rsa (s %m)))", signature.get()),
|
|
||||||
version);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool signature_rsa::verify(const epee::span<const uint8_t> message, const public_key_rsa &public_key) const
|
|
||||||
{
|
|
||||||
const s_expression signed_data = hash_message(message, public_key.bits());
|
|
||||||
return gcry_pk_verify(m_signature.get(), signed_data.get(), public_key.get()) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
s_expression signature_rsa::hash_message(const epee::span<const uint8_t> message, size_t public_key_bits) const
|
|
||||||
{
|
|
||||||
switch (m_type)
|
|
||||||
{
|
|
||||||
case type::binary_document:
|
|
||||||
return hash_bytes(message, public_key_bits);
|
|
||||||
case type::canonical_text_document:
|
|
||||||
{
|
|
||||||
std::vector<uint8_t> crlf_formatted;
|
|
||||||
crlf_formatted.reserve(message.size());
|
|
||||||
const size_t message_size = message.size();
|
|
||||||
for (size_t offset = 0; offset < message_size; ++offset)
|
|
||||||
{
|
|
||||||
const auto &character = message[offset];
|
|
||||||
if (character == '\r')
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (character == '\n')
|
|
||||||
{
|
|
||||||
const bool skip_last_crlf = offset + 1 == message_size;
|
|
||||||
if (skip_last_crlf)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
crlf_formatted.push_back('\r');
|
|
||||||
}
|
|
||||||
crlf_formatted.push_back(character);
|
|
||||||
}
|
|
||||||
return hash_bytes(epee::to_span(crlf_formatted), public_key_bits);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("unsupported signature type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> signature_rsa::hash_asn_object_id() const
|
|
||||||
{
|
|
||||||
size_t size;
|
|
||||||
if (gcry_md_algo_info(m_hash_algorithm, GCRYCTL_GET_ASNOID, nullptr, &size) != GPG_ERR_NO_ERROR)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("failed to get ASN.1 Object Identifier (OID) size");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> asn_object_id(size);
|
|
||||||
if (gcry_md_algo_info(m_hash_algorithm, GCRYCTL_GET_ASNOID, &asn_object_id[0], &size) != GPG_ERR_NO_ERROR)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("failed to get ASN.1 Object Identifier (OID)");
|
|
||||||
}
|
|
||||||
|
|
||||||
return asn_object_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
s_expression signature_rsa::hash_bytes(const epee::span<const uint8_t> message, size_t public_key_bits) const
|
|
||||||
{
|
|
||||||
const std::vector<uint8_t> plain_hash = (hash(m_hash_algorithm) << message << m_hashed_appendix).finish();
|
|
||||||
if (plain_hash.size() < 2)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("insufficient message hash size");
|
|
||||||
}
|
|
||||||
if (plain_hash[0] != m_hash_leftmost_bytes.first || plain_hash[1] != m_hash_leftmost_bytes.second)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("signature checksum doesn't match the expected value");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> asn_object_id = hash_asn_object_id();
|
|
||||||
|
|
||||||
const size_t public_key_bytes = bits_to_bytes(public_key_bits);
|
|
||||||
if (public_key_bytes < plain_hash.size() + asn_object_id.size() + 11)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("insufficient public key bit length");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> emsa_pkcs1_v1_5_encoded;
|
|
||||||
emsa_pkcs1_v1_5_encoded.reserve(public_key_bytes);
|
|
||||||
emsa_pkcs1_v1_5_encoded.push_back(0);
|
|
||||||
emsa_pkcs1_v1_5_encoded.push_back(1);
|
|
||||||
const size_t ps_size = public_key_bytes - plain_hash.size() - asn_object_id.size() - 3;
|
|
||||||
emsa_pkcs1_v1_5_encoded.insert(emsa_pkcs1_v1_5_encoded.end(), ps_size, 0xff);
|
|
||||||
emsa_pkcs1_v1_5_encoded.push_back(0);
|
|
||||||
emsa_pkcs1_v1_5_encoded.insert(emsa_pkcs1_v1_5_encoded.end(), asn_object_id.begin(), asn_object_id.end());
|
|
||||||
emsa_pkcs1_v1_5_encoded.insert(emsa_pkcs1_v1_5_encoded.end(), plain_hash.begin(), plain_hash.end());
|
|
||||||
|
|
||||||
mpi value(emsa_pkcs1_v1_5_encoded);
|
|
||||||
return s_expression("(data (flags raw) (value %m))", value.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> signature_rsa::format_hashed_appendix(
|
|
||||||
uint8_t algorithm,
|
|
||||||
uint8_t hash_algorithm,
|
|
||||||
const std::vector<uint8_t> &hashed_data,
|
|
||||||
uint8_t type,
|
|
||||||
uint8_t version)
|
|
||||||
{
|
|
||||||
const uint16_t hashed_data_size = static_cast<uint16_t>(hashed_data.size());
|
|
||||||
const uint32_t hashed_pefix_size = sizeof(version) + sizeof(type) + sizeof(algorithm) + sizeof(hash_algorithm) +
|
|
||||||
sizeof(hashed_data_size) + hashed_data.size();
|
|
||||||
|
|
||||||
std::vector<uint8_t> appendix;
|
|
||||||
appendix.reserve(hashed_pefix_size + sizeof(version) + sizeof(uint8_t) + sizeof(hashed_pefix_size));
|
|
||||||
appendix.push_back(version);
|
|
||||||
appendix.push_back(type);
|
|
||||||
appendix.push_back(algorithm);
|
|
||||||
appendix.push_back(hash_algorithm);
|
|
||||||
appendix.push_back(static_cast<uint8_t>(hashed_data_size >> 8));
|
|
||||||
appendix.push_back(static_cast<uint8_t>(hashed_data_size));
|
|
||||||
appendix.insert(appendix.end(), hashed_data.begin(), hashed_data.end());
|
|
||||||
appendix.push_back(version);
|
|
||||||
appendix.push_back(0xff);
|
|
||||||
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size >> 24));
|
|
||||||
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size >> 16));
|
|
||||||
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size >> 8));
|
|
||||||
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size));
|
|
||||||
|
|
||||||
return appendix;
|
|
||||||
}
|
|
||||||
|
|
||||||
message_armored::message_armored(const std::string &message_armored)
|
|
||||||
: m_message(get_armored_block_contents(message_armored, "BEGIN PGP SIGNED MESSAGE"))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
message_armored::operator epee::span<const uint8_t>() const
|
|
||||||
{
|
|
||||||
return epee::to_byte_span(epee::to_span(m_message));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace openpgp
|
|
@ -1,127 +0,0 @@
|
|||||||
// Copyright (c) 2020-2021, The Monero Project
|
|
||||||
//
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without modification, are
|
|
||||||
// permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
||||||
// conditions and the following disclaimer.
|
|
||||||
//
|
|
||||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
||||||
// of conditions and the following disclaimer in the documentation and/or other
|
|
||||||
// materials provided with the distribution.
|
|
||||||
//
|
|
||||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
||||||
// used to endorse or promote products derived from this software without specific
|
|
||||||
// prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
||||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
||||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
||||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
||||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
||||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <gcrypt.h>
|
|
||||||
|
|
||||||
#include <span.h>
|
|
||||||
|
|
||||||
#include "s_expression.h"
|
|
||||||
|
|
||||||
namespace openpgp
|
|
||||||
{
|
|
||||||
|
|
||||||
enum algorithm : uint8_t
|
|
||||||
{
|
|
||||||
rsa = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
class public_key_rsa
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
public_key_rsa(s_expression expression, size_t bits);
|
|
||||||
|
|
||||||
size_t bits() const;
|
|
||||||
const gcry_sexp_t &get() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
s_expression m_expression;
|
|
||||||
size_t m_bits;
|
|
||||||
};
|
|
||||||
|
|
||||||
class public_key_block : public std::vector<public_key_rsa>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
public_key_block(const std::string &armored);
|
|
||||||
public_key_block(const epee::span<const uint8_t> buffer);
|
|
||||||
|
|
||||||
std::string user_id() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string m_user_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
class signature_rsa
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum type : uint8_t
|
|
||||||
{
|
|
||||||
binary_document = 0,
|
|
||||||
canonical_text_document = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
signature_rsa(
|
|
||||||
uint8_t algorithm,
|
|
||||||
std::pair<uint8_t, uint8_t> hash_leftmost_bytes,
|
|
||||||
uint8_t hash_algorithm,
|
|
||||||
const std::vector<uint8_t> &hashed_data,
|
|
||||||
type type,
|
|
||||||
s_expression signature,
|
|
||||||
uint8_t version);
|
|
||||||
|
|
||||||
static signature_rsa from_armored(const std::string &armored_signed_message);
|
|
||||||
static signature_rsa from_base64(const std::string &base64);
|
|
||||||
static signature_rsa from_buffer(const epee::span<const uint8_t> input);
|
|
||||||
|
|
||||||
bool verify(const epee::span<const uint8_t> message, const public_key_rsa &public_key) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
s_expression hash_message(const epee::span<const uint8_t> message, size_t public_key_bits) const;
|
|
||||||
std::vector<uint8_t> hash_asn_object_id() const;
|
|
||||||
s_expression hash_bytes(const epee::span<const uint8_t> message, size_t public_key_bits) const;
|
|
||||||
|
|
||||||
static std::vector<uint8_t> format_hashed_appendix(
|
|
||||||
uint8_t algorithm,
|
|
||||||
uint8_t hash_algorithm,
|
|
||||||
const std::vector<uint8_t> &hashed_data,
|
|
||||||
uint8_t type,
|
|
||||||
uint8_t version);
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint8_t m_hash_algorithm;
|
|
||||||
std::pair<uint8_t, uint8_t> m_hash_leftmost_bytes;
|
|
||||||
std::vector<uint8_t> m_hashed_appendix;
|
|
||||||
s_expression m_signature;
|
|
||||||
type m_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
class message_armored
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
message_armored(const std::string &message_armored);
|
|
||||||
|
|
||||||
operator epee::span<const uint8_t>() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string m_message;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace openpgp
|
|
@ -1,88 +0,0 @@
|
|||||||
// Copyright (c) 2020-2021, The Monero Project
|
|
||||||
//
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without modification, are
|
|
||||||
// permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
||||||
// conditions and the following disclaimer.
|
|
||||||
//
|
|
||||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
||||||
// of conditions and the following disclaimer in the documentation and/or other
|
|
||||||
// materials provided with the distribution.
|
|
||||||
//
|
|
||||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
||||||
// used to endorse or promote products derived from this software without specific
|
|
||||||
// prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
||||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
||||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
||||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
||||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
||||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <span.h>
|
|
||||||
|
|
||||||
#include "serialization.h"
|
|
||||||
|
|
||||||
namespace openpgp
|
|
||||||
{
|
|
||||||
|
|
||||||
class packet_stream
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
packet_stream(const epee::span<const uint8_t> buffer)
|
|
||||||
: packet_stream(deserializer<epee::span<const uint8_t>>(buffer))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <
|
|
||||||
typename byte_container,
|
|
||||||
typename = typename std::enable_if<(sizeof(typename byte_container::value_type) == 1)>::type>
|
|
||||||
packet_stream(deserializer<byte_container> buffer)
|
|
||||||
{
|
|
||||||
while (!buffer.empty())
|
|
||||||
{
|
|
||||||
packet_tag tag = buffer.read_packet_tag();
|
|
||||||
packets.push_back({std::move(tag), buffer.read(tag.length)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<uint8_t> *find_first(packet_tag::type type) const
|
|
||||||
{
|
|
||||||
for (const auto &packet : packets)
|
|
||||||
{
|
|
||||||
if (packet.first.packet_type == type)
|
|
||||||
{
|
|
||||||
return &packet.second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Callback>
|
|
||||||
void for_each(packet_tag::type type, Callback &callback) const
|
|
||||||
{
|
|
||||||
for (const auto &packet : packets)
|
|
||||||
{
|
|
||||||
if (packet.first.packet_type == type)
|
|
||||||
{
|
|
||||||
callback(packet.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<std::pair<packet_tag, std::vector<uint8_t>>> packets;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace openpgp
|
|
@ -1,78 +0,0 @@
|
|||||||
// Copyright (c) 2020-2021, The Monero Project
|
|
||||||
//
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without modification, are
|
|
||||||
// permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
||||||
// conditions and the following disclaimer.
|
|
||||||
//
|
|
||||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
||||||
// of conditions and the following disclaimer in the documentation and/or other
|
|
||||||
// materials provided with the distribution.
|
|
||||||
//
|
|
||||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
||||||
// used to endorse or promote products derived from this software without specific
|
|
||||||
// prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
||||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
||||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
||||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
||||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
||||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#include <gcrypt.h>
|
|
||||||
|
|
||||||
namespace openpgp
|
|
||||||
{
|
|
||||||
|
|
||||||
class s_expression
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
s_expression(const s_expression &) = delete;
|
|
||||||
s_expression &operator=(const s_expression &) = delete;
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
s_expression(Args... args)
|
|
||||||
{
|
|
||||||
if (gcry_sexp_build(&data, nullptr, args...) != GPG_ERR_NO_ERROR)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("failed to build S-expression");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s_expression(s_expression &&other)
|
|
||||||
{
|
|
||||||
std::swap(data, other.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
s_expression(gcry_sexp_t data)
|
|
||||||
: data(data)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~s_expression()
|
|
||||||
{
|
|
||||||
gcry_sexp_release(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
const gcry_sexp_t &get() const
|
|
||||||
{
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
gcry_sexp_t data = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace openpgp
|
|
@ -1,172 +0,0 @@
|
|||||||
// Copyright (c) 2020-2021, The Monero Project
|
|
||||||
//
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without modification, are
|
|
||||||
// permitted provided that the following conditions are met:
|
|
||||||
//
|
|
||||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
||||||
// conditions and the following disclaimer.
|
|
||||||
//
|
|
||||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
||||||
// of conditions and the following disclaimer in the documentation and/or other
|
|
||||||
// materials provided with the distribution.
|
|
||||||
//
|
|
||||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
||||||
// used to endorse or promote products derived from this software without specific
|
|
||||||
// prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
||||||
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
||||||
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
||||||
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
||||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
||||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "mpi.h"
|
|
||||||
|
|
||||||
namespace openpgp
|
|
||||||
{
|
|
||||||
|
|
||||||
size_t bits_to_bytes(size_t bits)
|
|
||||||
{
|
|
||||||
constexpr const uint16_t bits_in_byte = 8;
|
|
||||||
return (bits + bits_in_byte - 1) / bits_in_byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string strip_line_breaks(const std::string &string)
|
|
||||||
{
|
|
||||||
std::string result;
|
|
||||||
result.reserve(string.size());
|
|
||||||
for (const auto &character : string)
|
|
||||||
{
|
|
||||||
if (character != '\r' && character != '\n')
|
|
||||||
{
|
|
||||||
result.push_back(character);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct packet_tag
|
|
||||||
{
|
|
||||||
enum type : uint8_t
|
|
||||||
{
|
|
||||||
signature = 2,
|
|
||||||
public_key = 6,
|
|
||||||
user_id = 13,
|
|
||||||
public_subkey = 14,
|
|
||||||
};
|
|
||||||
|
|
||||||
const type packet_type;
|
|
||||||
const size_t length;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <
|
|
||||||
typename byte_container,
|
|
||||||
typename = typename std::enable_if<(sizeof(typename byte_container::value_type) == 1)>::type>
|
|
||||||
class deserializer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
deserializer(byte_container buffer)
|
|
||||||
: buffer(std::move(buffer))
|
|
||||||
, cursor(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool empty() const
|
|
||||||
{
|
|
||||||
return buffer.size() - cursor == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
packet_tag read_packet_tag()
|
|
||||||
{
|
|
||||||
const auto tag = read_big_endian<uint8_t>();
|
|
||||||
|
|
||||||
constexpr const uint8_t format_mask = 0b11000000;
|
|
||||||
constexpr const uint8_t format_old_tag = 0b10000000;
|
|
||||||
if ((tag & format_mask) != format_old_tag)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("invalid packet tag");
|
|
||||||
}
|
|
||||||
|
|
||||||
const packet_tag::type packet_type = static_cast<packet_tag::type>((tag & 0b00111100) >> 2);
|
|
||||||
const uint8_t length_type = tag & 0b00000011;
|
|
||||||
|
|
||||||
size_t length;
|
|
||||||
switch (length_type)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
length = read_big_endian<uint8_t>();
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
length = read_big_endian<uint16_t>();
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
length = read_big_endian<uint32_t>();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("unsupported packet length type");
|
|
||||||
}
|
|
||||||
|
|
||||||
return {packet_type, length};
|
|
||||||
}
|
|
||||||
|
|
||||||
mpi read_mpi()
|
|
||||||
{
|
|
||||||
const size_t bit_length = read_big_endian<uint16_t>();
|
|
||||||
return mpi(read_span(bits_to_bytes(bit_length)));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> read(size_t size)
|
|
||||||
{
|
|
||||||
if (buffer.size() - cursor < size)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("insufficient buffer size");
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t offset = cursor;
|
|
||||||
cursor += size;
|
|
||||||
|
|
||||||
return {&buffer[offset], &buffer[cursor]};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
|
|
||||||
T read_big_endian()
|
|
||||||
{
|
|
||||||
if (buffer.size() - cursor < sizeof(T))
|
|
||||||
{
|
|
||||||
throw std::runtime_error("insufficient buffer size");
|
|
||||||
}
|
|
||||||
T result = 0;
|
|
||||||
for (size_t read = 0; read < sizeof(T); ++read)
|
|
||||||
{
|
|
||||||
result = (result << 8) | static_cast<uint8_t>(buffer[cursor++]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
epee::span<const uint8_t> read_span(size_t size)
|
|
||||||
{
|
|
||||||
if (buffer.size() - cursor < size)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("insufficient buffer size");
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t offset = cursor;
|
|
||||||
cursor += size;
|
|
||||||
|
|
||||||
return {reinterpret_cast<const uint8_t *>(&buffer[offset]), size};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
byte_container buffer;
|
|
||||||
size_t cursor;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace openpgp
|
|
@ -0,0 +1,730 @@
|
|||||||
|
import QtQuick 2.7
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import QtQuick.Controls 2.3
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
color: "#181725"
|
||||||
|
anchors.fill: parent
|
||||||
|
property variant buffer: []
|
||||||
|
property int bufferMaxLength: 12
|
||||||
|
// state: 0:idle 1:startup 2:syncing 3:mining
|
||||||
|
signal startMining();
|
||||||
|
signal stopMining();
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
calcAvailableHeightConsoleLines();
|
||||||
|
}
|
||||||
|
|
||||||
|
onHeightChanged: {
|
||||||
|
calcAvailableHeightConsoleLines();
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcAvailableHeightConsoleLines() {
|
||||||
|
var max_lines = parseInt(textContainer.height / 20);
|
||||||
|
if(root.bufferMaxLength != max_lines && max_lines >= 4)
|
||||||
|
root.bufferMaxLength = max_lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
// width: 980
|
||||||
|
// height: 480
|
||||||
|
|
||||||
|
Column {
|
||||||
|
FontLoader { id: comicMono; source: "qrc:/fonts/ComicMono.ttf" }
|
||||||
|
FontLoader { id: comicMonoBold; source: "qrc:/fonts/ComicMono-Bold.ttf" }
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedImage {
|
||||||
|
//visible: mining.daemonMiningState === 0
|
||||||
|
source: "qrc:/mining/bg1.gif"
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottomMargin: 92
|
||||||
|
anchors.topMargin: 112
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
source: "qrc:/mining/overlay.png"
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottomMargin: 92
|
||||||
|
anchors.topMargin: 112
|
||||||
|
smooth: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: miningGradient
|
||||||
|
opacity: 0.0
|
||||||
|
source: "qrc:/mining/mining_gradient.png"
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottomMargin: 92
|
||||||
|
anchors.topMargin: 112
|
||||||
|
smooth: false
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State { when: mining.daemonMiningState !== 0;
|
||||||
|
PropertyChanges { target: miningGradient; opacity: 1.0 }},
|
||||||
|
State { when: mining.daemonMiningState === 0;
|
||||||
|
PropertyChanges { target: miningGradient; opacity: 0.0 }}
|
||||||
|
]
|
||||||
|
transitions: [ Transition { NumberAnimation { property: "opacity"; duration: 750}} ]
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 0
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 128
|
||||||
|
|
||||||
|
Image {
|
||||||
|
source: "qrc:/mining/topleft.png"
|
||||||
|
Layout.preferredWidth: 435
|
||||||
|
Layout.preferredHeight: 128
|
||||||
|
smooth: false
|
||||||
|
|
||||||
|
// top-left monitors
|
||||||
|
ColumnLayout {
|
||||||
|
width: 102
|
||||||
|
height: 74
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 14
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 42
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: "transparent"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Hashrate"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 4
|
||||||
|
font.pointSize: 14
|
||||||
|
font.family: comicMonoBold.name;
|
||||||
|
antialiasing: false
|
||||||
|
color: "#41FF00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: "transparent"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: hashRateText
|
||||||
|
text: "-"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 4
|
||||||
|
font.pointSize: 16
|
||||||
|
font.family: comicMono.name;
|
||||||
|
antialiasing: false
|
||||||
|
color: "#41FF00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
width: 102
|
||||||
|
height: 74
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 142
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 42
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: "transparent"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "uptime"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 4
|
||||||
|
font.pointSize: 14
|
||||||
|
font.family: comicMonoBold.name;
|
||||||
|
antialiasing: false
|
||||||
|
color: "#41FF00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: "transparent"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: miningUptime
|
||||||
|
text: "-"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 4
|
||||||
|
font.pointSize: 12
|
||||||
|
font.family: comicMono.name;
|
||||||
|
antialiasing: false
|
||||||
|
color: "#41FF00"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedImage {
|
||||||
|
visible: mining.daemonMiningState !== 0
|
||||||
|
source: "qrc:/mining/mining.webp"
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
width: 115
|
||||||
|
height: 86
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 263
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 34
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 128
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Image {
|
||||||
|
source: "qrc:/mining/warning.png"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 15
|
||||||
|
fillMode: Image.TileHorizontally
|
||||||
|
smooth: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
source: "qrc:/mining/topright_bar.png"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 4
|
||||||
|
fillMode: Image.TileHorizontally
|
||||||
|
smooth: false
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 0
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.preferredHeight: 102
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.preferredWidth: 5
|
||||||
|
Layout.preferredHeight: parent.height
|
||||||
|
source: "qrc:/mining/topright_left.png"
|
||||||
|
smooth: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: parent.height
|
||||||
|
source: "qrc:/mining/topright_middle.png"
|
||||||
|
fillMode: Image.TileHorizontally
|
||||||
|
smooth: false
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 6
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 8
|
||||||
|
anchors.topMargin: 12
|
||||||
|
|
||||||
|
height: 78
|
||||||
|
spacing: 16
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.minimumWidth: 200
|
||||||
|
Layout.maximumWidth: 260
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Block Height"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 8
|
||||||
|
font.pointSize: 20
|
||||||
|
font.family: comicMonoBold.name;
|
||||||
|
color: "#41FF00"
|
||||||
|
antialiasing: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: heightText
|
||||||
|
text: "-"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 8
|
||||||
|
font.pointSize: 18
|
||||||
|
font.family: comicMonoBold.name;
|
||||||
|
color: "#41FF00"
|
||||||
|
antialiasing: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.minimumWidth: 200
|
||||||
|
Layout.maximumWidth: 260
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Sync Progress"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 8
|
||||||
|
font.pointSize: 20
|
||||||
|
font.family: comicMonoBold.name;
|
||||||
|
color: "#41FF00"
|
||||||
|
antialiasing: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: syncPctText
|
||||||
|
text: "-"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
font.pointSize: 18
|
||||||
|
font.family: comicMonoBold.name;
|
||||||
|
color: "#41FF00"
|
||||||
|
antialiasing: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.preferredWidth: 5
|
||||||
|
Layout.preferredHeight: parent.height
|
||||||
|
source: "qrc:/mining/topright_right.png"
|
||||||
|
smooth: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredHeight: 7 // 15 + 4 + 102 + 7 = 128
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 0
|
||||||
|
//Layout.preferredHeight: 128
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.preferredWidth: 6
|
||||||
|
Layout.fillHeight: true
|
||||||
|
source: "qrc:/mining/r_left.png"
|
||||||
|
fillMode: Image.TileVertically
|
||||||
|
smooth: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
// text container
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: textContainer
|
||||||
|
color: "transparent"
|
||||||
|
height: parent.height - 20
|
||||||
|
width: parent.width - 32
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: cons
|
||||||
|
anchors.margins: 4
|
||||||
|
anchors.fill: parent
|
||||||
|
text: {
|
||||||
|
if(mining.daemonMiningState === 0) {
|
||||||
|
return "Press the pick-axe to start mining!";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
font.pointSize: 14
|
||||||
|
font.family: comicMono.name;
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.preferredWidth: 6
|
||||||
|
Layout.fillHeight: true
|
||||||
|
source: "qrc:/mining/r_right.png"
|
||||||
|
fillMode: Image.TileVertically
|
||||||
|
smooth: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 0
|
||||||
|
Layout.preferredHeight: 140
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.preferredWidth: 306
|
||||||
|
Layout.preferredHeight: 140
|
||||||
|
source: "qrc:/mining/lowerleft.png"
|
||||||
|
smooth: false
|
||||||
|
|
||||||
|
AnimatedImage {
|
||||||
|
source: mining.daemonMiningState === 0 ? "qrc:/assets/images/dog_sitting.gif" : "qrc:/assets/images/dog_running.gif"
|
||||||
|
width: 80
|
||||||
|
height: 60
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 22
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 22
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onEntered: {
|
||||||
|
showBubble();
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
hideBubble();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 140
|
||||||
|
source: "qrc:/mining/lower_repeat.png"
|
||||||
|
fillMode: Image.TileHorizontally
|
||||||
|
smooth: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.preferredWidth: 236
|
||||||
|
Layout.preferredHeight: 140
|
||||||
|
source: "qrc:/mining/bottom_center_console.png"
|
||||||
|
smooth: false
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
// middle console clock
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 100
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 8
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
width: 54
|
||||||
|
height: 16
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: clock
|
||||||
|
text: ""
|
||||||
|
antialiasing: false
|
||||||
|
font.pointSize: 9
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
font.family: comicMonoBold.name;
|
||||||
|
color: "#41FF00";
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
root.setClock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
source: "qrc:/mining/intel.png"
|
||||||
|
width: 100
|
||||||
|
height: 100
|
||||||
|
fillMode: Image.Pad
|
||||||
|
smooth: false
|
||||||
|
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 140
|
||||||
|
source: "qrc:/mining/lower_repeat.png"
|
||||||
|
fillMode: Image.TileHorizontally
|
||||||
|
smooth: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.preferredWidth: 308
|
||||||
|
Layout.preferredHeight: 140
|
||||||
|
source: "qrc:/mining/lowerright.png"
|
||||||
|
smooth: false
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
// lower-right button container
|
||||||
|
color: "transparent"
|
||||||
|
width: 106
|
||||||
|
height: 100
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 11
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 34
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: imgAxe
|
||||||
|
visible: mining.daemonMiningState === 0
|
||||||
|
source: "qrc:/mining/axe.png"
|
||||||
|
width: 73
|
||||||
|
height: 75
|
||||||
|
smooth: false
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimatedImage {
|
||||||
|
visible: mining.daemonMiningState !== 0
|
||||||
|
source: "qrc:/mining/elmo.gif"
|
||||||
|
width: 106
|
||||||
|
height: 100
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: stopMiningBtn
|
||||||
|
visible: true
|
||||||
|
text: "Stop Mining"
|
||||||
|
font.pointSize: 10
|
||||||
|
z: parent.z + 1
|
||||||
|
color: "black"
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 28
|
||||||
|
font.family: comicMonoBold.name;
|
||||||
|
antialiasing: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
z: parent.z + 1
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if(mining.daemonMiningState === 0) {
|
||||||
|
root.startMining();
|
||||||
|
root.calcAvailableHeightConsoleLines();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
root.stopMining();
|
||||||
|
}
|
||||||
|
onEntered: {
|
||||||
|
imgAxe.height = 64
|
||||||
|
imgAxe.width = 64
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
imgAxe.height = 75
|
||||||
|
imgAxe.width = 73
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: bubble
|
||||||
|
visible: false
|
||||||
|
source: "qrc:/mining/bubble.png"
|
||||||
|
width: 200
|
||||||
|
height: 60
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: 64
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 48
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 6
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
height: 26
|
||||||
|
color: "transparent"
|
||||||
|
width: 183
|
||||||
|
z: parent.z + 1
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: bubbleText
|
||||||
|
text: ""
|
||||||
|
color: "black"
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
font.pointSize: 14
|
||||||
|
font.family: ComicMonoBold.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: setClockTimer
|
||||||
|
interval: 1000*60
|
||||||
|
running: true
|
||||||
|
repeat: true
|
||||||
|
onTriggered: setClock()
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: dogBubbleTimer
|
||||||
|
interval: 1000*30
|
||||||
|
running: true
|
||||||
|
repeat: true
|
||||||
|
onTriggered: {
|
||||||
|
if(Math.random() >= 0.5) return;
|
||||||
|
if(bubble.visible) return;
|
||||||
|
root.dogBubbleRemoval.stop();
|
||||||
|
root.dogBubbleRemoval.start();
|
||||||
|
|
||||||
|
var msg = root.bubbleMessage();
|
||||||
|
|
||||||
|
bubbleText.text = msg;
|
||||||
|
bubble.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: dogBubbleRemoval
|
||||||
|
interval: 2500
|
||||||
|
running: false
|
||||||
|
repeat: false
|
||||||
|
onTriggered: bubble.visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setClock() {
|
||||||
|
var now = new Date();
|
||||||
|
var hours = now.getHours();
|
||||||
|
var minutes = ('0'+now.getMinutes()).slice(-2);
|
||||||
|
clock.text = hours + ":" + minutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetComponents() {
|
||||||
|
hashRateText.text = "-";
|
||||||
|
miningUptime.text = "-";
|
||||||
|
syncPctText.text = "-";
|
||||||
|
heightText.text = "-";
|
||||||
|
}
|
||||||
|
|
||||||
|
function consoleAppend(line) {
|
||||||
|
if(root.buffer.length >= root.bufferMaxLength)
|
||||||
|
root.buffer.shift()
|
||||||
|
root.buffer.push(line);
|
||||||
|
|
||||||
|
cons.text = "";
|
||||||
|
for(var i = 0; i != root.bufferMaxLength; i++) {
|
||||||
|
if(root.buffer[i])
|
||||||
|
cons.text += root.buffer[i] + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: mining
|
||||||
|
function onDaemonOutput(line) {
|
||||||
|
root.consoleAppend(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSyncStatus(from, to, pct) {
|
||||||
|
syncPctText.text = pct + "%";
|
||||||
|
heightText.text = from + "/" + to;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUptimeChanged(uptime) {
|
||||||
|
miningUptime.text = uptime;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onHashrate(hashrate) {
|
||||||
|
hashRateText.text = hashrate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showBubble() {
|
||||||
|
if(bubble.visible) return;
|
||||||
|
var msg = root.bubbleMessage();
|
||||||
|
|
||||||
|
bubbleText.text = msg;
|
||||||
|
bubble.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideBubble() {
|
||||||
|
bubble.visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bubbleMessage() {
|
||||||
|
var active = ["such work!", "mining WOW!", "woof woof!", "I am tired!", "mining :@", "weeeee", "blocks everywhere!", "wooohoo", "working, twerkin'", "looking4blocks", "mining blocks"];
|
||||||
|
var inactive = ["doing nothing!", "ZzZZzzZ", "wen mining?!", "ETA mining?!", "zZZzzZZz", "omg so bored", "so bored!!", "much idle, many zZz", "lets go!", "i'm ready!"];
|
||||||
|
var syncing = ["wen 1gbit", "syncin'", "zZzz", "ETA sync ready?!", "downloading blocks", "fetching blocks"];
|
||||||
|
var msg = "";
|
||||||
|
|
||||||
|
if(mining.daemonMiningState === 0) {
|
||||||
|
return inactive[Math.floor(Math.random()*inactive.length)];
|
||||||
|
} else if (mining.daemonMiningState === 2) {
|
||||||
|
return syncing[Math.floor(Math.random()*syncing.length)];
|
||||||
|
} else {
|
||||||
|
return active[Math.floor(Math.random()*active.length)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1 @@
|
|||||||
Subproject commit ff5182f7f2825263e93e88064931597b3c6cf928
|
Subproject commit a21819cc22587e16af00e2c3d8f70156c11310a0
|