File spectre-cli-3.0+git20230114.26c01a5.obscpio of Package spectre-cli

07070100000000000081A400000000000000000000000163C2D555000000B6000000000000000000000000000000000000002F00000000spectre-cli-3.0+git20230114.26c01a5/.gitignore*.o
*.dSYM

spectre
spectre-bench
spectre-tests

TAG
VERSION
spectre-*.tar.gz
spectre-*.tar.gz.sig

CMakeCache.txt
CMakeFiles
Makefile
cmake_install.cmake
install_manifest.txt
.idea
07070100000001000081A400000000000000000000000163C2D5550000004C000000000000000000000000000000000000003000000000spectre-cli-3.0+git20230114.26c01a5/.gitmodules[submodule "api"]
	path = api
	url = https://gitlab.com/spectre.app/api.git
07070100000002000081A400000000000000000000000163C2D55500001DDF000000000000000000000000000000000000003300000000spectre-cli-3.0+git20230114.26c01a5/CMakeLists.txt### CMAKE
project( spectre-cli C )
cmake_minimum_required( VERSION 3.0.2 )
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")


### CONFIGURATION
# Features.
option( USE_SODIUM              "Implement crypto functions with sodium (depends on libsodium)." ON )
option( USE_JSON                "Support JSON-based user configuration format (depends on libjson-c)." ON )
option( USE_COLOR               "Colorized identicon (depends on libncurses)." ON )
option( USE_XML                 "XML parsing (depends on libxml2)." ON )

option( BUILD_SPECTRE           "C CLI version of Spectre (needs: spectre_sodium, optional: spectre_color, spectre_json)." ON )
option( BUILD_SPECTRE_BENCH     "C CLI Spectre benchmark utility (needs: spectre_sodium)." OFF )
option( BUILD_SPECTRE_TESTS     "C Spectre algorithm test suite (needs: spectre_sodium, spectre_xml)." OFF )

# Default build flags.
set( CMAKE_BUILD_TYPE           Release )
set( CMAKE_C_FLAGS              "-O3" )

# Version.
find_package( Git )
if( GIT_FOUND )
    execute_process( COMMAND    "${GIT_EXECUTABLE}" describe --match *-cli* --long --dirty
                     OUTPUT_VARIABLE spectre_version OUTPUT_STRIP_TRAILING_WHITESPACE )
endif()
if( NOT spectre_version MATCHES "." )
    file( READ                  "VERSION" spectre_version )
    string( STRIP               "${spectre_version}" spectre_version )
endif()
if( spectre_version MATCHES "." )
    add_definitions(            "-DMP_VERSION=${spectre_version}" )
    message( STATUS             "Current spectre source version ${spectre_version}..." )
else()
    message( STATUS             "Current spectre source version unknown..." )
endif()


### DEPENDENCIES
function( use_spectre_sodium t r )
    if( USE_SODIUM )
        set( sodium_USE_STATIC_LIBS ON )
        find_package( sodium )
        if ( sodium_FOUND )
            target_link_libraries( "${t}" PRIVATE sodium )
            target_compile_definitions( "${t}" PRIVATE -DSPECTRE_SODIUM=1 )
            message( STATUS     "${t}: USE_SODIUM is enabled." )

        elseif( r STREQUAL "required" )
            message( FATAL_ERROR "${t}: USE_SODIUM was enabled but is missing libsodium.  Please install this library before continuing." )

        else()
            message( WARNING    "${t}: USE_SODIUM was enabled but is missing libsodium.  Will continue with USE_SODIUM disabled!" )

        endif()

    elseif( r STREQUAL "required" )
        message( FATAL_ERROR    "${t}: USE_SODIUM was required but is not enabled.  Please enable the option or remove this target." )

    else()
        message( STATUS         "${t}: USE_SODIUM is supported but not enabled." )

    endif()
endfunction()

function( use_spectre_color t )
    if( USE_COLOR )
        find_package( Curses )
        if ( CURSES_FOUND )
            target_include_directories( "${t}" PRIVATE ${CURSES_INCLUDE_DIRS} )
            target_link_libraries( "${t}" PRIVATE ${CURSES_LIBRARIES} )
            target_compile_definitions( "${t}" PRIVATE -DSPECTRE_COLOR=1 ${CURSES_CFLAGS} )
            message( STATUS     "${t}: USE_COLOR is enabled." )

        elseif( r STREQUAL "required" )
            message( FATAL_ERROR "${t}: USE_COLOR was enabled but is missing libcurses.  Please install this library before continuing." )

        else()
            message( WARNING    "${t}: USE_COLOR was enabled but is missing libcurses.  Will continue with USE_COLOR disabled!" )

        endif()

    elseif( r STREQUAL "required" )
        message( FATAL_ERROR    "${t}: USE_COLOR was required but is not enabled.  Please enable the option or remove this target." )

    else()
        message( STATUS         "${t}: USE_COLOR is supported but not enabled." )

    endif()
endfunction()

function( use_spectre_json t )
    if( USE_JSON )
        find_package( json-c )
        if ( json-c_FOUND )
            target_link_libraries( "${t}" PRIVATE json-c::json-c-static )
            target_compile_definitions( "${t}" PRIVATE -DSPECTRE_JSON=1 )
            message( STATUS     "${t}: USE_JSON is enabled." )

        elseif( r STREQUAL "required" )
            message( FATAL_ERROR "${t}: USE_JSON was enabled but is missing libjson-c.  Please install this library before continuing." )

        else()
            message( WARNING    "${t}: USE_JSON was enabled but is missing libjson-c.  Will continue with USE_JSON disabled!" )

        endif()

    elseif( r STREQUAL "required" )
        message( FATAL_ERROR    "${t}: USE_JSON was required but is not enabled.  Please enable the option or remove this target." )

    else()
        message( STATUS         "${t}: USE_JSON is supported but not enabled." )

    endif()
endfunction()

function( use_spectre_xml t r )
    find_package( LibXml2 )
    if( USE_XML )
        if ( LIBXML2_FOUND )
            target_link_libraries( "${t}" PRIVATE LibXml2::LibXml2 )
            target_compile_definitions( "${t}" PRIVATE -DSPECTRE_XML=1 )
            message( STATUS     "${t}: USE_XML is enabled." )

        elseif( r STREQUAL "required" )
            message( FATAL_ERROR "${t}: USE_XML was enabled but is missing libxml2.  Please install this library before continuing." )

        else()
            message( WARNING    "${t}: USE_XML was enabled but is missing libxml2.  Will continue with USE_XML disabled!" )

        endif()

    elseif( r STREQUAL "required" )
        message( FATAL_ERROR    "${t}: USE_XML was required but is not enabled.  Please enable the option or remove this target." )

    else()
        message( STATUS         "${t}: USE_XML is supported but not enabled." )

    endif()
endfunction()


### TARGET: SPECTRE
if( BUILD_SPECTRE )
    # target
    add_executable( spectre "api/c/aes.c" "api/c/spectre-algorithm.c"
                        "api/c/spectre-algorithm_v0.c" "api/c/spectre-algorithm_v1.c" "api/c/spectre-algorithm_v2.c" "api/c/spectre-algorithm_v3.c"
                        "api/c/spectre-types.c" "api/c/spectre-util.c" "api/c/spectre-marshal-util.c" "api/c/spectre-marshal.c"
                        "src/spectre-cli-util.c" "src/spectre-cli.c" )
    target_include_directories( spectre PUBLIC api/c src )
    install( TARGETS spectre RUNTIME DESTINATION bin )

    # dependencies
    use_spectre_sodium( spectre required )
    use_spectre_color( spectre optional )
    use_spectre_json( spectre optional )
endif()


### TARGET: SPECTRE-BENCH
if( BUILD_SPECTRE_BENCH )
    # target
    add_executable( spectre-bench "api/c/aes.c" "api/c/spectre-algorithm.c"
                              "api/c/spectre-algorithm_v0.c" "api/c/spectre-algorithm_v1.c" "api/c/spectre-algorithm_v2.c" "api/c/spectre-algorithm_v3.c"
                              "api/c/spectre-types.c" "api/c/spectre-util.c" "src/spectre-bench.c" )
    target_include_directories( spectre-bench PUBLIC api/c src )
    install( TARGETS spectre-bench RUNTIME DESTINATION bin )

    # dependencies
    use_spectre_sodium( spectre-bench required )
endif()


### TARGET: SPECTRE-TESTS
if( BUILD_SPECTRE_TESTS )
    # target
    add_executable( spectre-tests "api/c/aes.c" "api/c/spectre-algorithm.c"
                              "api/c/spectre-algorithm_v0.c" "api/c/spectre-algorithm_v1.c" "api/c/spectre-algorithm_v2.c" "api/c/spectre-algorithm_v3.c"
                              "api/c/spectre-types.c" "api/c/spectre-util.c" "src/spectre-tests-util.c" "src/spectre-tests.c" )
    target_include_directories( spectre-tests PUBLIC api/c src )
    install( TARGETS spectre-tests RUNTIME DESTINATION bin )

    # dependencies
    use_spectre_sodium( spectre-tests required )
    use_spectre_xml( spectre-tests required )
endif()
07070100000003000081A400000000000000000000000163C2D555000000E8000000000000000000000000000000000000002F00000000spectre-cli-3.0+git20230114.26c01a5/DockerfileFROM alpine
WORKDIR /spectre/cli
ADD . /spectre

RUN apk update && apk add cmake make gcc musl-dev ncurses-dev libsodium-dev json-c-dev libxml2-dev
RUN cmake -DBUILD_SPECTRE_TESTS=ON . && make install
RUN spectre-tests

CMD spectre
07070100000004000081A400000000000000000000000163C2D5550000894B000000000000000000000000000000000000002C00000000spectre-cli-3.0+git20230114.26c01a5/LICENSE                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
07070100000005000081A400000000000000000000000163C2D555000019DA000000000000000000000000000000000000002E00000000spectre-cli-3.0+git20230114.26c01a5/README.md# [Spectre](https://spectre.app)

Spectre introduces a completely new way of thinking about passwords.

[[_TOC_]]


## Don't store; derive

Every attempt to solve the problem of passwords by means of storing countless unique site-specific tokens inevitably leads to complexity, loss of control, and security compromise.

Spectre flips the problem on its head by rejecting the notion of statefulness and giving the user a single secret to remember.  The Spectre algorithm then derives whatever secret tokens you need.

    site-password = SPECTRE( user-name, user-secret, site-name )


## How does it work?

In short (simplified):

    user-key = SCRYPT( user-name, user-secret )
    site-key = HMAC-SHA-256( site-name . site-counter, user-key )
    site-password = PW( site-template, site-key )

Consequently, Spectre can derive any `site-password` given the necessary base ingredients (ie. the `user-name`, `user-secret`, `site-name`, `site-counter` and `site-template`).

As an example:

    user-name = Robert Lee Mitchell
    user-secret = banana colored duckling
    site-name = twitter.com
    site-counter = 1
    site-template = Long Password
    site-password = PozoLalv0_Yelo

We standardize `user-name` as your full legal name, `site-name` as the domain name that hosts the site, `site-counter` to `1` (unless you explicitly increment it) and `site-template` to `Long Password`; as a result the only token the user really needs to remember is their `user-secret`.


# Source Code

Spectre's algorithm and implementation is fully documented and licensed Free Software under the (GPLv3)[LICENSE].


## Components

The source is broken down into several components, each hosted in their own repository. Submodules are used to correlate dependencies.

 - [api](https://gitlab.com/spectre.app/api): The algorithm's reference implementation and API library.  There is a C, Java and W3C interface.
 - [cli](https://gitlab.com/spectre.app/cli): The official command-line interface for POSIX systems.
 - [desktop](https://gitlab.com/spectre.app/desktop): The official cross-platform desktop application.
 - [macos](https://gitlab.com/spectre.app/macos): The official Apple macOS desktop application.
 - [ios](https://gitlab.com/spectre.app/ios): The official Apple iOS mobile application.
 - [android](https://gitlab.com/spectre.app/android): The official Google Android mobile application.
 - [web](https://gitlab.com/spectre.app/web): The official cross-platform web application.
 - [www](https://gitlab.com/spectre.app/www): The Spectre homepage.


## Building

This repository hosts the POSIX command-line interface.

To build the code to run on your specific system, run the `build` script:

    ./build

Note that the build depends on your system having certain dependencies already installed.
By default, you'll need to have at least `libsodium`, `libjson-c` and `libncurses` installed.
Missing dependencies will be indicated by the script.

### Details

The build script comes with a default configuration which can be adjusted.  Full details on the build script are available by opening the build script file.

    [targets='...'] [spectre_feature=0|1 ...] [CFLAGS='...'] [LDFLAGS='...'] ./build [cc arguments ...]

By default, the build script only builds the `spectre` target.  You can specify other targets or `all` to build all available targets.  These are the currently available targets:

 - `spectre`        : The main app.  Options: needed: `spectre_sodium`, optional: `spectre_color`, `spectre_json`.
 - `spectre-bench`  : A benchmark utility.  Options: needed: `spectre_sodium`.
 - `spectre-tests`  : An algorithm test suite.  Options: needed: `spectre_sodium`, `spectre_xml`.

It is smart to build the test suite along with the app, eg.:

    targets='spectre spectre-tests' ./build

The options determine the dependencies that the build will require.  The following exist:

 - `spectre_sodium` : Use Sodium for the crypto implementation.  It needs libsodium.
 - `spectre_json`   : Support JSON-based user configuration format.  It needs libjson-c.
 - `spectre_color`  : Show a colorized identicon.  It needs libncurses.
 - `spectre_xml`    : Support XML parsing.  It needs libxml2.

By default, all options are enabled.  Each option can be disabled or enabled explicitly by prefixing the build command with an assignment of it to `0` or `1`, eg.:

    spectre_color=0 ./build

As a result of this command, you'd build the `spectre` target (which supports `spectre_color`) without color support.  The build no longer requires `libncurses` but the resulting `spectre` binary will not have support for colorized identicons.

You can also pass CFLAGS or LDFLAGS to the build, or extra custom compiler arguments as arguments to the build script.
For instance, to add a custom library search path, you could use:

    LDFLAGS='-L/usr/local/lib' ./build


### Alternative: cmake

There is also a cmake configuration you can use to build instead of using the `./build` script.  While `./build` depends on Bash and is geared toward POSIX systems, cmake is platform-independent.  You should use your platform's cmake tools to continue.  On POSIX systems, you should be able to use:

    cmake . && make

To get a list of options supported by the cmake configuration, use:

    cmake -LH

Options can be toggled like so:

    cmake -DUSE_COLOR=OFF -DBUILD_SPECTRE_TESTS=ON . && make

## Testing

Once the client is built, you should run a test suite to make sure everything works as intended.

There are currently two test programs:

 - `spectre-tests`     : Tests the algorithm implementation.
 - `spectre-cli-tests` : Tests the CLI application.

The `spectre-tests` program is only available if you enabled its target during build (see "Details" above).

The `spectre-cli-tests` is a Bash shell script, hence depends on your system having Bash available.


## Installing

Once you're happy with the result, you can install the `spectre` application into your system's `PATH`.

Generally, all you need to do is copy the `spectre` file into a PATH directory, eg.:

    cp spectre /usr/local/bin/

The directory that you should copy the `spectre` file into will depend on your system.  Also note that `cp` is a POSIX command, if your system is not a POSIX system (eg. Windows) you'll need to adjust accordingly.

There is also an `install` script to help with this process, though it is a Bash script and therefore requires that you have Bash installed:

    ./install

After installing, you should be able to run `spectre` and use it from anywhere in the terminal:

    spectre -h
    spectre google.com

07070100000006000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000002800000000spectre-cli-3.0+git20230114.26c01a5/api07070100000007000081A400000000000000000000000163C2D5550000894B000000000000000000000000000000000000003000000000spectre-cli-3.0+git20230114.26c01a5/api/LICENSE                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
07070100000008000081A400000000000000000000000163C2D55500000A26000000000000000000000000000000000000003200000000spectre-cli-3.0+git20230114.26c01a5/api/README.md# [Spectre](https://spectre.app)

Spectre introduces a completely new way of thinking about passwords.

[[_TOC_]]


## Don't store; derive

Every attempt to solve the problem of passwords by means of storing countless unique site-specific tokens inevitably leads to complexity, loss of control, and security compromise.

Spectre flips the problem on its head by rejecting the notion of statefulness and giving the user a single secret to remember.  The Spectre algorithm then derives whatever secret tokens you need.

    site-password = SPECTRE( user-name, user-secret, site-name )


## How does it work?

In short (simplified):

    user-key = SCRYPT( user-name, user-secret )
    site-key = HMAC-SHA-256( site-name . site-counter, user-key )
    site-password = PW( site-template, site-key )

Consequently, Spectre can derive any `site-password` given the necessary base ingredients (ie. the `user-name`, `user-secret`, `site-name`, `site-counter` and `site-template`).

As an example:

    user-name = Robert Lee Mitchell
    user-secret = banana colored duckling
    site-name = twitter.com
    site-counter = 1
    site-template = Long Password
    site-password = PozoLalv0_Yelo

We standardize `user-name` as your full legal name, `site-name` as the domain name that hosts the site, `site-counter` to `1` (unless you explicitly increment it) and `site-template` to `Long Password`; as a result the only token the user really needs to remember is their `user-secret`.


# Source Code

Spectre's algorithm and implementation is fully documented and licensed Free Software under the (GPLv3)[LICENSE].


## Components

The source is broken down into several components:

 - [api](https://gitlab.com/spectre.app/api): The algorithm's reference implementation and API library.  There is a C, Java and W3C interface.
 - [cli](https://gitlab.com/spectre.app/cli): The official command-line interface for POSIX systems.
 - [desktop](https://gitlab.com/spectre.app/desktop): The official cross-platform desktop application.
 - [macos](https://gitlab.com/spectre.app/macos): The official Apple macOS desktop application.
 - [ios](https://gitlab.com/spectre.app/ios): The official Apple iOS mobile application.
 - [android](https://gitlab.com/spectre.app/android): The official Google Android mobile application.
 - [web](https://gitlab.com/spectre.app/web): The official cross-platform web application.
 - [www](https://gitlab.com/spectre.app/www): The Spectre homepage.


## Building and running

This repository isn't built directly.  Rather, it is consumed by an application target as a submodule.
07070100000009000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000002A00000000spectre-cli-3.0+git20230114.26c01a5/api/c0707010000000A000081A400000000000000000000000163C2D55500004B16000000000000000000000000000000000000003000000000spectre-cli-3.0+git20230114.26c01a5/api/c/aes.c/*
Source: https://github.com/kokke/tiny-AES-c

This is free and unencumbered software released into the public domain.
For details, see https://github.com/kokke/tiny-AES-c/blob/master/unlicense.txt

This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode.
Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.

The implementation is verified against the test vectors in:
  National Institute of Standards and Technology Special Publication 800-38A 2001 ED

ECB-AES128
----------

  plain-text:
    6bc1bee22e409f96e93d7e117393172a
    ae2d8a571e03ac9c9eb76fac45af8e51
    30c81c46a35ce411e5fbc1191a0a52ef
    f69f2445df4f9b17ad2b417be66c3710

  key:
    2b7e151628aed2a6abf7158809cf4f3c

  resulting cipher
    3ad77bb40d7a3660a89ecaf32466ef97 
    f5d3d58503b9699de785895a96fdbaaf 
    43b1cd7f598ece23881b00e3ed030688 
    7b0c785e27e8ad3f8223207104725dd4 


NOTE:   String length must be evenly divisible by 16byte (str_len % 16 == 0)
        You should pad the end of the string with zeros if this is not the case.
        For AES192/256 the key size is proportionally larger.

*/


/*****************************************************************************/
/* Includes:                                                                 */
/*****************************************************************************/
#include <string.h> // CBC mode, for memset
#include "aes.h"

/*****************************************************************************/
/* Defines:                                                                  */
/*****************************************************************************/
// The number of columns comprising a state in AES. This is a constant in AES. Value=4
#define Nb 4

#if defined(AES256) && (AES256 == 1)
    #define Nk 8
    #define Nr 14
#elif defined(AES192) && (AES192 == 1)
    #define Nk 6
    #define Nr 12
#else
    #define Nk 4        // The number of 32 bit words in a key.
    #define Nr 10       // The number of rounds in AES Cipher.
#endif

// jcallan@github points out that declaring Multiply as a function 
// reduces code size considerably with the Keil ARM compiler.
// See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3
#ifndef MULTIPLY_AS_A_FUNCTION
  #define MULTIPLY_AS_A_FUNCTION 0
#endif




/*****************************************************************************/
/* Private variables:                                                        */
/*****************************************************************************/
// state - array holding the intermediate results during decryption.
typedef uint8_t state_t[4][4];



// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
// The numbers below can be computed dynamically trading ROM for RAM - 
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
static const uint8_t sbox[256] = {
  //0     1    2      3     4    5     6     7      8    9     A      B    C     D     E     F
  0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
  0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
  0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
  0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
  0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
  0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
  0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
  0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
  0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
  0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
  0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
  0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
  0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
  0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
  0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
  0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };

#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
static const uint8_t rsbox[256] = {
  0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
  0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
  0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
  0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
  0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
  0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
  0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
  0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
  0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
  0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
  0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
  0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
  0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
  0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
  0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
  0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d };
#endif

// The round constant word array, Rcon[i], contains the values given by 
// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
static const uint8_t Rcon[11] = {
  0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };

/*
 * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12),
 * that you can remove most of the elements in the Rcon array, because they are unused.
 *
 * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
 * 
 * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed), 
 *  up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
 */


/*****************************************************************************/
/* Private functions:                                                        */
/*****************************************************************************/
/*
static uint8_t getSBoxValue(uint8_t num)
{
  return sbox[num];
}
*/
#define getSBoxValue(num) (sbox[(num)])

// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states. 
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
{
  unsigned i, j, k;
  uint8_t tempa[4]; // Used for the column/row operations
  
  // The first round key is the key itself.
  for (i = 0; i < Nk; ++i)
  {
    RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
    RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
    RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
    RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
  }

  // All other round keys are found from the previous round keys.
  for (i = Nk; i < Nb * (Nr + 1); ++i)
  {
    {
      k = (i - 1) * 4;
      tempa[0]=RoundKey[k + 0];
      tempa[1]=RoundKey[k + 1];
      tempa[2]=RoundKey[k + 2];
      tempa[3]=RoundKey[k + 3];

    }

    if (i % Nk == 0)
    {
      // This function shifts the 4 bytes in a word to the left once.
      // [a0,a1,a2,a3] becomes [a1,a2,a3,a0]

      // Function RotWord()
      {
        const uint8_t u8tmp = tempa[0];
        tempa[0] = tempa[1];
        tempa[1] = tempa[2];
        tempa[2] = tempa[3];
        tempa[3] = u8tmp;
      }

      // SubWord() is a function that takes a four-byte input word and 
      // applies the S-box to each of the four bytes to produce an output word.

      // Function Subword()
      {
        tempa[0] = getSBoxValue(tempa[0]);
        tempa[1] = getSBoxValue(tempa[1]);
        tempa[2] = getSBoxValue(tempa[2]);
        tempa[3] = getSBoxValue(tempa[3]);
      }

      tempa[0] = tempa[0] ^ Rcon[i/Nk];
    }
#if defined(AES256) && (AES256 == 1)
    if (i % Nk == 4)
    {
      // Function Subword()
      {
        tempa[0] = getSBoxValue(tempa[0]);
        tempa[1] = getSBoxValue(tempa[1]);
        tempa[2] = getSBoxValue(tempa[2]);
        tempa[3] = getSBoxValue(tempa[3]);
      }
    }
#endif
    j = i * 4; k=(i - Nk) * 4;
    RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
    RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
    RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
    RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
  }
}

void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
{
  KeyExpansion(ctx->RoundKey, key);
}
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
{
  KeyExpansion(ctx->RoundKey, key);
  memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv)
{
  memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
#endif

// This function adds the round key to state.
// The round key is added to the state by an XOR function.
static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey)
{
  uint8_t i,j;
  for (i = 0; i < 4; ++i)
  {
    for (j = 0; j < 4; ++j)
    {
      (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
    }
  }
}

// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void SubBytes(state_t* state)
{
  uint8_t i, j;
  for (i = 0; i < 4; ++i)
  {
    for (j = 0; j < 4; ++j)
    {
      (*state)[j][i] = getSBoxValue((*state)[j][i]);
    }
  }
}

// The ShiftRows() function shifts the rows in the state to the left.
// Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted.
static void ShiftRows(state_t* state)
{
  uint8_t temp;

  // Rotate first row 1 columns to left  
  temp           = (*state)[0][1];
  (*state)[0][1] = (*state)[1][1];
  (*state)[1][1] = (*state)[2][1];
  (*state)[2][1] = (*state)[3][1];
  (*state)[3][1] = temp;

  // Rotate second row 2 columns to left  
  temp           = (*state)[0][2];
  (*state)[0][2] = (*state)[2][2];
  (*state)[2][2] = temp;

  temp           = (*state)[1][2];
  (*state)[1][2] = (*state)[3][2];
  (*state)[3][2] = temp;

  // Rotate third row 3 columns to left
  temp           = (*state)[0][3];
  (*state)[0][3] = (*state)[3][3];
  (*state)[3][3] = (*state)[2][3];
  (*state)[2][3] = (*state)[1][3];
  (*state)[1][3] = temp;
}

static uint8_t xtime(uint8_t x)
{
  return (uint8_t)((x<<1) ^ (((x>>7) & 1) * 0x1b));
}

// MixColumns function mixes the columns of the state matrix
static void MixColumns(state_t* state)
{
  uint8_t i;
  uint8_t Tmp, Tm, t;
  for (i = 0; i < 4; ++i)
  {  
    t   = (*state)[i][0];
    Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
    Tm  = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm);  (*state)[i][0] ^= Tm ^ Tmp ;
    Tm  = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm);  (*state)[i][1] ^= Tm ^ Tmp ;
    Tm  = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm);  (*state)[i][2] ^= Tm ^ Tmp ;
    Tm  = (*state)[i][3] ^ t ;              Tm = xtime(Tm);  (*state)[i][3] ^= Tm ^ Tmp ;
  }
}

// Multiply is used to multiply numbers in the field GF(2^8)
// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
//       The compiler seems to be able to vectorize the operation better this way.
//       See https://github.com/kokke/tiny-AES-c/pull/34
#if MULTIPLY_AS_A_FUNCTION
static uint8_t Multiply(uint8_t x, uint8_t y)
{
  return (((y & 1) * x) ^
       ((y>>1 & 1) * xtime(x)) ^
       ((y>>2 & 1) * xtime(xtime(x))) ^
       ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^
       ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */
  }
#else
#define Multiply(x, y)                                \
      (  ((y & 1) * x) ^                              \
      ((y>>1 & 1) * xtime(x)) ^                       \
      ((y>>2 & 1) * xtime(xtime(x))) ^                \
      ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^         \
      ((y>>4 & 1) * xtime(xtime(xtime(xtime(x))))))   \

#endif

#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
/*
static uint8_t getSBoxInvert(uint8_t num)
{
  return rsbox[num];
}
*/
#define getSBoxInvert(num) (rsbox[(num)])

// MixColumns function mixes the columns of the state matrix.
// The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information.
static void InvMixColumns(state_t* state)
{
  int i;
  uint8_t a, b, c, d;
  for (i = 0; i < 4; ++i)
  { 
    a = (*state)[i][0];
    b = (*state)[i][1];
    c = (*state)[i][2];
    d = (*state)[i][3];

    (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
    (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
    (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
    (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
  }
}


// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void InvSubBytes(state_t* state)
{
  uint8_t i, j;
  for (i = 0; i < 4; ++i)
  {
    for (j = 0; j < 4; ++j)
    {
      (*state)[j][i] = getSBoxInvert((*state)[j][i]);
    }
  }
}

static void InvShiftRows(state_t* state)
{
  uint8_t temp;

  // Rotate first row 1 columns to right  
  temp = (*state)[3][1];
  (*state)[3][1] = (*state)[2][1];
  (*state)[2][1] = (*state)[1][1];
  (*state)[1][1] = (*state)[0][1];
  (*state)[0][1] = temp;

  // Rotate second row 2 columns to right 
  temp = (*state)[0][2];
  (*state)[0][2] = (*state)[2][2];
  (*state)[2][2] = temp;

  temp = (*state)[1][2];
  (*state)[1][2] = (*state)[3][2];
  (*state)[3][2] = temp;

  // Rotate third row 3 columns to right
  temp = (*state)[0][3];
  (*state)[0][3] = (*state)[1][3];
  (*state)[1][3] = (*state)[2][3];
  (*state)[2][3] = (*state)[3][3];
  (*state)[3][3] = temp;
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)

// Cipher is the main function that encrypts the PlainText.
static void Cipher(state_t* state, const uint8_t* RoundKey)
{
  uint8_t round = 0;

  // Add the First round key to the state before starting the rounds.
  AddRoundKey(0, state, RoundKey);

  // There will be Nr rounds.
  // The first Nr-1 rounds are identical.
  // These Nr rounds are executed in the loop below.
  // Last one without MixColumns()
  for (round = 1; ; ++round)
  {
    SubBytes(state);
    ShiftRows(state);
    if (round == Nr) {
      break;
    }
    MixColumns(state);
    AddRoundKey(round, state, RoundKey);
  }
  // Add round key to last round
  AddRoundKey(Nr, state, RoundKey);
}

#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
static void InvCipher(state_t* state, const uint8_t* RoundKey)
{
  uint8_t round = 0;

  // Add the First round key to the state before starting the rounds.
  AddRoundKey(Nr, state, RoundKey);

  // There will be Nr rounds.
  // The first Nr-1 rounds are identical.
  // These Nr rounds are executed in the loop below.
  // Last one without InvMixColumn()
  for (round = (Nr - 1); ; --round)
  {
    InvShiftRows(state);
    InvSubBytes(state);
    AddRoundKey(round, state, RoundKey);
    if (round == 0) {
      break;
    }
    InvMixColumns(state);
  }

}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)

/*****************************************************************************/
/* Public functions:                                                         */
/*****************************************************************************/
#if defined(ECB) && (ECB == 1)


void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
  // The next function call encrypts the PlainText with the Key using AES algorithm.
  Cipher((state_t*)buf, ctx->RoundKey);
}

void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
  // The next function call decrypts the PlainText with the Key using AES algorithm.
  InvCipher((state_t*)buf, ctx->RoundKey);
}


#endif // #if defined(ECB) && (ECB == 1)





#if defined(CBC) && (CBC == 1)


static void XorWithIv(uint8_t* buf, const uint8_t* Iv)
{
  uint8_t i;
  for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size
  {
    buf[i] ^= Iv[i];
  }
}

void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, size_t length)
{
  size_t i;
  uint8_t *Iv = ctx->Iv;
  for (i = 0; i < length; i += AES_BLOCKLEN)
  {
    XorWithIv(buf, Iv);
    Cipher((state_t*)buf, ctx->RoundKey);
    Iv = buf;
    buf += AES_BLOCKLEN;
  }
  /* store Iv in ctx for next call */
  memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
}

void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
  size_t i;
  uint8_t storeNextIv[AES_BLOCKLEN];
  for (i = 0; i < length; i += AES_BLOCKLEN)
  {
    memcpy(storeNextIv, buf, AES_BLOCKLEN);
    InvCipher((state_t*)buf, ctx->RoundKey);
    XorWithIv(buf, ctx->Iv);
    memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);
    buf += AES_BLOCKLEN;
  }

}

#endif // #if defined(CBC) && (CBC == 1)



#if defined(CTR) && (CTR == 1)

/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
  uint8_t buffer[AES_BLOCKLEN];
  
  size_t i;
  int bi;
  for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
  {
    if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */
    {
      
      memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
      Cipher((state_t*)buffer,ctx->RoundKey);

      /* Increment Iv and handle overflow */
      for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
      {
	/* inc will overflow */
        if (ctx->Iv[bi] == 255)
	{
          ctx->Iv[bi] = 0;
          continue;
        } 
        ctx->Iv[bi] += 1;
        break;   
      }
      bi = 0;
    }

    buf[i] = (buf[i] ^ buffer[bi]);
  }
}

#endif // #if defined(CTR) && (CTR == 1)

0707010000000B000081A400000000000000000000000163C2D55500000BB0000000000000000000000000000000000000003000000000spectre-cli-3.0+git20230114.26c01a5/api/c/aes.h/*
Source: https://github.com/kokke/tiny-AES-c

This is free and unencumbered software released into the public domain.
For details, see https://github.com/kokke/tiny-AES-c/blob/master/unlicense.txt
*/
#ifndef _AES_H_
#define _AES_H_

#include <stdint.h>
#include <stddef.h>

// #define the macros below to 1/0 to enable/disable the mode of operation.
//
// CBC enables AES encryption in CBC-mode of operation.
// CTR enables encryption in counter-mode.
// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously.

// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
#ifndef CBC
  #define CBC 1
#endif

#ifndef ECB
  #define ECB 1
#endif

#ifndef CTR
  #define CTR 1
#endif


#define AES128 1
//#define AES192 1
//#define AES256 1

#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only

#if defined(AES256) && (AES256 == 1)
    #define AES_KEYLEN 32
    #define AES_keyExpSize 240
#elif defined(AES192) && (AES192 == 1)
    #define AES_KEYLEN 24
    #define AES_keyExpSize 208
#else
    #define AES_KEYLEN 16   // Key length in bytes
    #define AES_keyExpSize 176
#endif

struct AES_ctx
{
  uint8_t RoundKey[AES_keyExpSize];
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
  uint8_t Iv[AES_BLOCKLEN];
#endif
};

void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv);
#endif

#if defined(ECB) && (ECB == 1)
// buffer size is exactly AES_BLOCKLEN bytes; 
// you need only AES_init_ctx as IV is not used in ECB 
// NB: ECB is considered insecure for most uses
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);

#endif // #if defined(ECB) && (ECB == !)


#if defined(CBC) && (CBC == 1)
// buffer size MUST be mutile of AES_BLOCKLEN;
// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv()
//        no IV should ever be reused with the same key 
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);

#endif // #if defined(CBC) && (CBC == 1)


#if defined(CTR) && (CTR == 1)

// Same function for encrypting as for decrypting. 
// IV is incremented for every block, and used after encryption as XOR-compliment for output
// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv()
//        no IV should ever be reused with the same key 
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);

#endif // #if defined(CTR) && (CTR == 1)


#endif // _AES_H_
0707010000000C000081A400000000000000000000000163C2D55500000CFE000000000000000000000000000000000000003700000000spectre-cli-3.0+git20230114.26c01a5/api/c/build.gradleimport org.gradle.internal.jvm.Jvm


plugins {
    id 'base'
    id 'cpp-library'
}

description = 'Master Password Algorithm Implementation'

artifacts {
    'default' task( type: Zip, 'archive' ) {
        // TODO: exclude lib files that are produced by the build.
        from 'lib'

        components.withType( ComponentWithRuntimeFile ) {
            if (optimized)
                from runtimeFile, {
                    into targetMachine.getOperatingSystemFamily().getName() + '/' + targetMachine.getArchitecture().getName().replace('-', '_')
                }
        }
    }
}

library {
    baseName.set( 'spectre' )
    linkage.set( [Linkage.SHARED] )
    source.from files( 'src' )

    // JNI support requires JDK.
    privateHeaders.from files( new File( Jvm.current().javaHome, 'include' ) ) { first().eachDir { from it } }

    // Cross-compile for these native host platforms.
    // TODO: blocked by: https://github.com/gradle/gradle-native/issues/1031
    targetMachines.set( [
            machines.linux.x86, machines.linux.x86_64,
            machines.windows.x86, machines.windows.x86_64,
            machines.macOS.x86_64
    ] )

    binaries.configureEach( CppBinary ) {
        def compile = compileTask.get(), link = linkTask.get()
        def platform = targetMachine.getOperatingSystemFamily().getName()
        def arch = targetMachine.getArchitecture().getName().replace('-', '_')

        compile.macros.put("SPECTRE_SODIUM", "1")

        dependencies {
            // libsodium
            compile.dependsOn tasks.maybeCreate( "build_libsodium-${platform}", Exec ).configure {
                commandLine 'bash', "$rootDir/lib/bin/build_libsodium-${platform}"
                privateHeaders.from "$rootDir/lib/libsodium/build-${platform}~/out/include"
                implementation fileTree( "$rootDir/lib/libsodium/build-${platform}~/out/lib/${arch}" )
            }
            clean.dependsOn tasks.maybeCreate( "clean_libsodium-${platform}", Exec ).configure {
                commandLine 'bash', "$rootDir/lib/bin/build_libsodium-${platform}", 'clean'
            }

            // libjson-c
            compile.dependsOn tasks.maybeCreate( "build_libjson-c-${platform}", Exec ).configure {
                commandLine 'bash', "$rootDir/lib/bin/build_libjson-c-${platform}"
                privateHeaders.from "$rootDir/lib/libjson-c/build-${platform}~/out/include"
                implementation fileTree( "$rootDir/lib/libjson-c/build-${platform}~/out/lib/${arch}" )
            }
            clean.dependsOn tasks.maybeCreate( "clean_libjson-c-${platform}", Exec ).configure {
                commandLine 'bash', "$rootDir/lib/bin/build_libjson-c-${platform}", 'clean'
            }
        }

        // Reconfigure the toolchain from C++ to C.
        compile.source.from fileTree( "src" )
        if (toolChain in GccCompatibleToolChain) {
            compile.compilerArgs = ['-x', 'c', '-std=c11', '-O3', '-Werror', '-Wall']
            link.linkerArgs = ['-lc', '-nodefaultlibs', '-flto']
        } else if (toolChain in VisualCpp) {
            // TODO: Should this be shared instead of static?
            // TODO: Do we need these SODIUM defines?
            compile.compilerArgs = ['/TC', '/MT', '/Ox', '/DSODIUM_STATIC', '/DSODIUM_EXPORT=', '/std:c11']
        }
    }
}
0707010000000D000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000002F00000000spectre-cli-3.0+git20230114.26c01a5/api/c/java0707010000000E000081A400000000000000000000000163C2D555000008D5000000000000000000000000000000000000005F00000000spectre-cli-3.0+git20230114.26c01a5/api/c/java/com_lyndir_masterpassword_MPAlgorithm_Version.h/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_lyndir_masterpassword_MPAlgorithm_Version */

#ifndef _Included_com_lyndir_masterpassword_MPAlgorithm_Version
#define _Included_com_lyndir_masterpassword_MPAlgorithm_Version
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_lyndir_masterpassword_MPAlgorithm_Version
 * Method:    _userKey
 * Signature: (Ljava/lang/String;[BI)[B
 */
JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_MPAlgorithm_00024Version__1userKey
  (JNIEnv *, jobject, jstring, jbyteArray, jint);

/*
 * Class:     com_lyndir_masterpassword_MPAlgorithm_Version
 * Method:    _siteKey
 * Signature: ([BLjava/lang/String;JILjava/lang/String;I)[B
 */
JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_MPAlgorithm_00024Version__1siteKey
  (JNIEnv *, jobject, jbyteArray, jstring, jlong, jint, jstring, jint);

/*
 * Class:     com_lyndir_masterpassword_MPAlgorithm_Version
 * Method:    _siteResult
 * Signature: ([B[BLjava/lang/String;JILjava/lang/String;ILjava/lang/String;I)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_MPAlgorithm_00024Version__1siteResult
  (JNIEnv *, jobject, jbyteArray, jbyteArray, jstring, jlong, jint, jstring, jint, jstring, jint);

/*
 * Class:     com_lyndir_masterpassword_MPAlgorithm_Version
 * Method:    _siteState
 * Signature: ([B[BLjava/lang/String;JILjava/lang/String;ILjava/lang/String;I)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_MPAlgorithm_00024Version__1siteState
  (JNIEnv *, jobject, jbyteArray, jbyteArray, jstring, jlong, jint, jstring, jint, jstring, jint);

/*
 * Class:     com_lyndir_masterpassword_MPAlgorithm_Version
 * Method:    _identicon
 * Signature: (Ljava/lang/String;[B)Lcom/lyndir/masterpassword/MPIdenticon;
 */
JNIEXPORT jobject JNICALL Java_com_lyndir_masterpassword_MPAlgorithm_00024Version__1identicon
  (JNIEnv *, jobject, jstring, jbyteArray);

/*
 * Class:     com_lyndir_masterpassword_MPAlgorithm_Version
 * Method:    _toID
 * Signature: ([B)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_MPAlgorithm_00024Version__1toID
  (JNIEnv *, jobject, jbyteArray);

#ifdef __cplusplus
}
#endif
#endif
0707010000000F000081A400000000000000000000000163C2D555000032C2000000000000000000000000000000000000003400000000spectre-cli-3.0+git20230114.26c01a5/api/c/mpw-jni.c#include <string.h>

#include "java/com_lyndir_masterpassword_MPAlgorithm_Version.h"

#include "spectre-algorithm.h"
#include "spectre-util.h"

// TODO: We may need to zero the jbytes safely.

static JavaVM *_vm;
static jobject logger;

SpectreLogSink spectre_log_sink_jni;

bool spectre_log_sink_jni(const SpectreLogEvent *record) {

    bool sunk = false;

    JNIEnv *env;
    if ((*_vm)->GetEnv( _vm, (void **)&env, JNI_VERSION_1_6 ) != JNI_OK)
        return sunk;

    if (logger && (*env)->PushLocalFrame( env, 16 ) == OK) {
        jmethodID method = NULL;
        jclass cLogger = (*env)->GetObjectClass( env, logger );
        switch (record->level) {
            case SpectreLogLevelTrace:
                method = (*env)->GetMethodID( env, cLogger, "trace", "(Ljava/lang/String;)V" );
                break;
            case SpectreLogLevelDebug:
                method = (*env)->GetMethodID( env, cLogger, "debug", "(Ljava/lang/String;)V" );
                break;
            case SpectreLogLevelInfo:
                method = (*env)->GetMethodID( env, cLogger, "info", "(Ljava/lang/String;)V" );
                break;
            case SpectreLogLevelWarning:
                method = (*env)->GetMethodID( env, cLogger, "warn", "(Ljava/lang/String;)V" );
                break;
            case SpectreLogLevelError:
            case SpectreLogLevelFatal:
                method = (*env)->GetMethodID( env, cLogger, "error", "(Ljava/lang/String;)V" );
                break;
        }

        if (method && record->message) {
            // TODO: log file, line & function as markers?
            (*env)->CallVoidMethod( env, logger, method, (*env)->NewStringUTF( env, record->message ) );
            sunk = true;
        }

        (*env)->PopLocalFrame( env, NULL );
    }

    return sunk;
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {

    JNIEnv *env;
    if ((*vm)->GetEnv( _vm = vm, (void **)&env, JNI_VERSION_1_6 ) != JNI_OK)
        return -1;

    do {
        jclass cLoggerFactory = (*env)->FindClass( env, "org/slf4j/LoggerFactory" );
        if (!cLoggerFactory)
            break;
        jmethodID method = (*env)->GetStaticMethodID( env, cLoggerFactory, "getLogger", "(Ljava/lang/String;)Lorg/slf4j/Logger;" );
        if (!method)
            break;
        jstring name = (*env)->NewStringUTF( env, "com.lyndir.masterpassword.algorithm" );
        if (!name)
            break;
        logger = (*env)->NewGlobalRef( env, (*env)->CallStaticObjectMethod( env, cLoggerFactory, method, name ) );
        if (!logger)
            break;

        jclass cLogger = (*env)->GetObjectClass( env, logger );
        if ((*env)->CallBooleanMethod( env, logger, (*env)->GetMethodID( env, cLogger, "isTraceEnabled", "()Z" ) ))
            spectre_verbosity = SpectreLogLevelTrace;
        else if ((*env)->CallBooleanMethod( env, logger, (*env)->GetMethodID( env, cLogger, "isDebugEnabled", "()Z" ) ))
            spectre_verbosity = SpectreLogLevelDebug;
        else if ((*env)->CallBooleanMethod( env, logger, (*env)->GetMethodID( env, cLogger, "isInfoEnabled", "()Z" ) ))
            spectre_verbosity = SpectreLogLevelInfo;
        else if ((*env)->CallBooleanMethod( env, logger, (*env)->GetMethodID( env, cLogger, "isWarnEnabled", "()Z" ) ))
            spectre_verbosity = SpectreLogLevelWarning;
        else if ((*env)->CallBooleanMethod( env, logger, (*env)->GetMethodID( env, cLogger, "isErrorEnabled", "()Z" ) ))
            spectre_verbosity = SpectreLogLevelError;
        else
            spectre_verbosity = SpectreLogLevelFatal;

        spectre_log_sink_register( &spectre_log_sink_jni );
    } while (false);

    if (!logger)
        wrn( "Couldn't initialize JNI logger." );

    return JNI_VERSION_1_6;
}

/* native byte[] _userKey(final String userName, final byte[] userSecret, final int algorithmVersion) */
JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_MPAlgorithm_00024Version__1userKey(JNIEnv *env, jobject obj,
        jstring userName, jbyteArray userSecret, jint algorithmVersion) {

#error TODO
    if (!userName || !userSecret)
        return NULL;

    const char *userNameString = (*env)->GetStringUTFChars( env, userName, NULL );
    jbyte *userSecretString = (*env)->GetByteArrayElements( env, userSecret, NULL );

    SpectreUserKey *userKeyBytes = spectre_user_key( userNameString, (char *)userSecretString, (SpectreAlgorithm)algorithmVersion );
    (*env)->ReleaseStringUTFChars( env, userName, userNameString );
    (*env)->ReleaseByteArrayElements( env, userSecret, userSecretString, JNI_ABORT );

    if (!userKeyBytes)
        return NULL;

    jbyteArray userKey = (*env)->NewByteArray( env, (jsize)sizeof( userKeyBytes->bytes ) );
    (*env)->SetByteArrayRegion( env, userKey, 0, (jsize)sizeof( userKeyBytes->bytes ), (jbyte *)userKeyBytes );
    spectre_free( &userKeyBytes, sizeof( userKeyBytes->bytes ) );

    return userKey;
}

/* native byte[] _siteKey(final byte[] userKey, final String siteName, final long keyCounter,
                          final int keyPurpose, @Nullable final String keyContext, final int version) */
JNIEXPORT jbyteArray JNICALL Java_com_lyndir_masterpassword_MPAlgorithm_00024Version__1siteKey(JNIEnv *env, jobject obj,
        jbyteArray userKey, jstring siteName, jlong keyCounter, jint keyPurpose, jstring keyContext, jint algorithmVersion) {

#error TODO
    if (!userKey || !siteName)
        return NULL;

    jbyte *userKeyBytes = (*env)->GetByteArrayElements( env, userKey, NULL );
    const char *siteNameString = (*env)->GetStringUTFChars( env, siteName, NULL );
    const char *keyContextString = keyContext? (*env)->GetStringUTFChars( env, keyContext, NULL ): NULL;
    SpectreSiteKey siteKeyBytes = spectre_site_key(
            (SpectreUserKey)userKeyBytes, siteNameString, (SpectreCounter)keyCounter,
            (SpectreKeyPurpose)keyPurpose, keyContextString );
    (*env)->ReleaseByteArrayElements( env, userKey, userKeyBytes, JNI_ABORT );
    (*env)->ReleaseStringUTFChars( env, siteName, siteNameString );
    if (keyContext)
        (*env)->ReleaseStringUTFChars( env, keyContext, keyContextString );

    if (!siteKeyBytes)
        return NULL;

    jbyteArray siteKey = (*env)->NewByteArray( env, (jsize)sizeof( *userKey ) );
    (*env)->SetByteArrayRegion( env, siteKey, 0, (jsize)sizeof( *userKey ), (jbyte *)siteKeyBytes );
    spectre_free( &siteKeyBytes, sizeof( *siteKey ) );

    return siteKey;
}

/* native String _siteResult(final byte[] userKey, final byte[] siteKey, final String siteName, final long keyCounter,
                             final int keyPurpose, @Nullable final String keyContext,
                             final int resultType, @Nullable final String resultParam, final int algorithmVersion) */
JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_MPAlgorithm_00024Version__1siteResult(JNIEnv *env, jobject obj,
        jbyteArray userKey, jbyteArray siteKey, jstring siteName, jlong keyCounter, jint keyPurpose, jstring keyContext,
        jint resultType, jstring resultParam, jint algorithmVersion) {

#error TODO
    if (!userKey || !siteKey || !siteName)
        return NULL;

    jbyte *userKeyBytes = (*env)->GetByteArrayElements( env, userKey, NULL );
    jbyte *siteKeyBytes = (*env)->GetByteArrayElements( env, siteKey, NULL );
    const char *siteNameString = (*env)->GetStringUTFChars( env, siteName, NULL );
    const char *keyContextString = keyContext? (*env)->GetStringUTFChars( env, keyContext, NULL ): NULL;
    const char *resultParamString = resultParam? (*env)->GetStringUTFChars( env, resultParam, NULL ): NULL;
    const char *siteResultString = spectre_site_result( (SpectreUserKey)userKeyBytes, siteNameString,
            (SpectreResultType)resultType, resultParamString, (SpectreCounter)keyCounter, (SpectreKeyPurpose)keyPurpose, keyContextString );
    (*env)->ReleaseByteArrayElements( env, userKey, userKeyBytes, JNI_ABORT );
    (*env)->ReleaseByteArrayElements( env, siteKey, siteKeyBytes, JNI_ABORT );
    (*env)->ReleaseStringUTFChars( env, siteName, siteNameString );
    if (keyContext)
        (*env)->ReleaseStringUTFChars( env, keyContext, keyContextString );
    if (resultParam)
        (*env)->ReleaseStringUTFChars( env, resultParam, resultParamString );

    if (!siteResultString)
        return NULL;

    jstring siteResult = (*env)->NewStringUTF( env, siteResultString );
    spectre_free_string( &siteResultString );

    return siteResult;
}

/* native String _siteState(final byte[] userKey, final byte[] siteKey, final String siteName, final long keyCounter,
                            final int keyPurpose, @Nullable final String keyContext,
                            final int resultType, final String resultParam, final int algorithmVersion) */
JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_MPAlgorithm_00024Version__1siteState(JNIEnv *env, jobject obj,
        jbyteArray userKey, jbyteArray siteKey, jstring siteName, jlong keyCounter, jint keyPurpose, jstring keyContext,
        jint resultType, jstring resultParam, jint algorithmVersion) {

#error TODO
    if (!userKey || !siteKey || !siteName || !resultParam)
        return NULL;

    jbyte *userKeyBytes = (*env)->GetByteArrayElements( env, userKey, NULL );
    jbyte *siteKeyBytes = (*env)->GetByteArrayElements( env, siteKey, NULL );
    const char *siteNameString = (*env)->GetStringUTFChars( env, siteName, NULL );
    const char *keyContextString = keyContext? (*env)->GetStringUTFChars( env, keyContext, NULL ): NULL;
    const char *resultParamString = (*env)->GetStringUTFChars( env, resultParam, NULL );
    const char *siteStateString = spectre_site_state(
            (SpectreUserKey)userKeyBytes, siteNameString, (SpectreResultType)resultType, resultParamString, (SpectreCounter)keyCounter,
            (SpectreKeyPurpose)keyPurpose, keyContextString );
    (*env)->ReleaseByteArrayElements( env, userKey, userKeyBytes, JNI_ABORT );
    (*env)->ReleaseByteArrayElements( env, siteKey, siteKeyBytes, JNI_ABORT );
    (*env)->ReleaseStringUTFChars( env, siteName, siteNameString );
    if (keyContextString)
        (*env)->ReleaseStringUTFChars( env, keyContext, keyContextString );
    if (resultParam)
        (*env)->ReleaseStringUTFChars( env, resultParam, resultParamString );

    if (!siteStateString)
        return NULL;

    jstring siteState = (*env)->NewStringUTF( env, siteStateString );
    spectre_free_string( &siteStateString );

    return siteState;
}

/* native SpectreIdenticon _identicon(final String userName, final byte[] userSecret) */
JNIEXPORT jobject JNICALL Java_com_lyndir_masterpassword_MPAlgorithm_00024Version__1identicon(JNIEnv *env, jobject obj,
        jstring userName, jbyteArray userSecret) {

#error TODO
    if (!userName || !userSecret)
        return NULL;

    const char *userNameString = (*env)->GetStringUTFChars( env, userName, NULL );
    jbyte *userSecretString = (*env)->GetByteArrayElements( env, userSecret, NULL );

    SpectreIdenticon identicon = spectre_identicon( userNameString, (char *)userSecretString );
    (*env)->ReleaseStringUTFChars( env, userName, userNameString );
    (*env)->ReleaseByteArrayElements( env, userSecret, userSecretString, JNI_ABORT );
    if (identicon.color == SpectreIdenticonColorUnset)
        return NULL;

    jclass cspectre_identicon_color = (*env)->FindClass( env, "com/lyndir/masterpassword/spectre_identicon$Color" );
    if (!cspectre_identicon_color)
        return NULL;
    jmethodID method = (*env)->GetStaticMethodID( env, cspectre_identicon_color, "values", "()[Lcom/lyndir/masterpassword/spectre_identicon$Color;" );
    if (!method)
        return NULL;
    jobject values = (*env)->CallStaticObjectMethod( env, cspectre_identicon_color, method );
    if (!values)
        return NULL;

    jclass cspectre_identicon = (*env)->FindClass( env, "com/lyndir/masterpassword/SpectreIdenticon" );
    if (!cspectre_identicon)
        return NULL;
    jmethodID init = (*env)->GetMethodID( env, cspectre_identicon, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/lyndir/masterpassword/spectre_identicon$Color;)V" );
    if (!init)
        return NULL;

    return (*env)->NewObject( env, cspectre_identicon, init, userName,
            (*env)->NewStringUTF( env, identicon.leftArm ),
            (*env)->NewStringUTF( env, identicon.body ),
            (*env)->NewStringUTF( env, identicon.rightArm ),
            (*env)->NewStringUTF( env, identicon.accessory ),
            (*env)->GetObjectArrayElement( env, values, identicon.color ) );
}

/* native String _toID(final byte[] buffer) */
JNIEXPORT jstring JNICALL Java_com_lyndir_masterpassword_MPAlgorithm_00024Version__1toID(JNIEnv *env, jobject obj,
        jbyteArray buffer) {

#error TODO
    return (*env)->NewStringUTF( env, spectre_id_buf( (*env)->GetByteArrayElements( env, buffer, NULL ), (*env)->GetArrayLength( env, buffer ) ) );
}
07070100000010000081A400000000000000000000000163C2D55500003ABC000000000000000000000000000000000000003E00000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-algorithm.c// =============================================================================
// Created by Maarten Billemont on 2014-05-05.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#include "spectre-algorithm.h"
#include "spectre-algorithm_v0.h"
#include "spectre-algorithm_v1.h"
#include "spectre-algorithm_v2.h"
#include "spectre-algorithm_v3.h"
#include "spectre-util.h"

SPECTRE_LIBS_BEGIN
#include <string.h>
SPECTRE_LIBS_END

const SpectreUserKey *spectre_user_key(
        const char *userName, const char *userSecret, const SpectreAlgorithm algorithmVersion) {

    if (userName && !strlen( userName ))
        userName = NULL;
    if (userSecret && !strlen( userSecret ))
        userSecret = NULL;

    trc( "-- spectre_user_key (algorithm: %u)", algorithmVersion );
    trc( "userName: %s", userName );
    trc( "userSecret.id: %s", userSecret? spectre_id_buf( (uint8_t *)userSecret, strlen( userSecret ) ).hex: NULL );
    if (!userName) {
        err( "Missing userName" );
        return NULL;
    }
    if (!userSecret) {
        err( "Missing userSecret" );
        return NULL;
    }

    SpectreUserKey *userKey = memcpy( malloc( sizeof( SpectreUserKey ) ),
            &(SpectreUserKey){ .algorithm = algorithmVersion }, sizeof( SpectreUserKey ) );

    bool success = false;
    switch (algorithmVersion) {
        case SpectreAlgorithmV0:
            success = spectre_user_key_v0( userKey, userName, userSecret );
            break;
        case SpectreAlgorithmV1:
            success = spectre_user_key_v1( userKey, userName, userSecret );
            break;
        case SpectreAlgorithmV2:
            success = spectre_user_key_v2( userKey, userName, userSecret );
            break;
        case SpectreAlgorithmV3:
            success = spectre_user_key_v3( userKey, userName, userSecret );
            break;
        default:
            err( "Unsupported version: %d", algorithmVersion );
    }

    if (success)
        return userKey;

    spectre_free( &userKey, sizeof( SpectreUserKey ) );
    return NULL;
}

const SpectreSiteKey *spectre_site_key(
        const SpectreUserKey *userKey, const char *siteName,
        const SpectreCounter keyCounter, const SpectreKeyPurpose keyPurpose, const char *keyContext) {

    if (keyContext && !strlen( keyContext ))
        keyContext = NULL;
    if (!userKey) {
        err( "Missing userKey" );
        return NULL;
    }
    if (!siteName) {
        err( "Missing siteName" );
        return NULL;
    }

    trc( "-- spectre_site_key (algorithm: %u)", userKey->algorithm );
    trc( "siteName: %s", siteName );
    trc( "keyCounter: %d", keyCounter );
    trc( "keyPurpose: %d (%s)", keyPurpose, spectre_purpose_name( keyPurpose ) );
    trc( "keyContext: %s", keyContext );

    SpectreSiteKey *siteKey = memcpy( malloc( sizeof( SpectreSiteKey ) ),
            &(SpectreSiteKey){ .algorithm = userKey->algorithm }, sizeof( SpectreSiteKey ) );

    bool success = false;
    switch (userKey->algorithm) {
        case SpectreAlgorithmV0:
            success = spectre_site_key_v0( siteKey, userKey, siteName, keyCounter, keyPurpose, keyContext );
            break;
        case SpectreAlgorithmV1:
            success = spectre_site_key_v1( siteKey, userKey, siteName, keyCounter, keyPurpose, keyContext );
            break;
        case SpectreAlgorithmV2:
            success = spectre_site_key_v2( siteKey, userKey, siteName, keyCounter, keyPurpose, keyContext );
            break;
        case SpectreAlgorithmV3:
            success = spectre_site_key_v3( siteKey, userKey, siteName, keyCounter, keyPurpose, keyContext );
            break;
        default:
            err( "Unsupported version: %d", userKey->algorithm );
    }

    if (success)
        return siteKey;

    spectre_free( &siteKey, sizeof( SpectreSiteKey ) );
    return NULL;
}

const char *spectre_site_result(
        const SpectreUserKey *userKey, const char *siteName,
        const SpectreResultType resultType, const char *resultParam,
        const SpectreCounter keyCounter, const SpectreKeyPurpose keyPurpose, const char *keyContext) {

    if (keyContext && !strlen( keyContext ))
        keyContext = NULL;
    if (resultParam && !strlen( resultParam ))
        resultParam = NULL;
    if (!userKey) {
        err( "Missing userKey" );
        return NULL;
    }

    const SpectreSiteKey *siteKey = spectre_site_key( userKey, siteName, keyCounter, keyPurpose, keyContext );
    if (!siteKey) {
        err( "Missing siteKey" );
        return NULL;
    }

    trc( "-- spectre_site_result (algorithm: %u)", userKey->algorithm );
    trc( "resultType: %d (%s)", resultType, spectre_type_short_name( resultType ) );
    trc( "resultParam: %s", resultParam );

    const char *result = NULL;
    if (resultType == SpectreResultNone) {
        result = NULL;
    }
    else if (resultType & SpectreResultClassTemplate) {
        switch (userKey->algorithm) {
            case SpectreAlgorithmV0:
                result = spectre_site_template_password_v0( userKey, siteKey, resultType, resultParam );
                break;
            case SpectreAlgorithmV1:
                result = spectre_site_template_password_v1( userKey, siteKey, resultType, resultParam );
                break;
            case SpectreAlgorithmV2:
                result = spectre_site_template_password_v2( userKey, siteKey, resultType, resultParam );
                break;
            case SpectreAlgorithmV3:
                result = spectre_site_template_password_v3( userKey, siteKey, resultType, resultParam );
                break;
            default:
                err( "Unsupported version: %d", userKey->algorithm );
                break;
        }
    }
    else if (resultType & SpectreResultClassStateful) {
        switch (userKey->algorithm) {
            case SpectreAlgorithmV0:
                result = spectre_site_crypted_password_v0( userKey, siteKey, resultType, resultParam );
                break;
            case SpectreAlgorithmV1:
                result = spectre_site_crypted_password_v1( userKey, siteKey, resultType, resultParam );
                break;
            case SpectreAlgorithmV2:
                result = spectre_site_crypted_password_v2( userKey, siteKey, resultType, resultParam );
                break;
            case SpectreAlgorithmV3:
                result = spectre_site_crypted_password_v3( userKey, siteKey, resultType, resultParam );
                break;
            default:
                err( "Unsupported version: %d", userKey->algorithm );
                break;
        }
    }
    else if (resultType & SpectreResultClassDerive) {
        switch (userKey->algorithm) {
            case SpectreAlgorithmV0:
                result = spectre_site_derived_password_v0( userKey, siteKey, resultType, resultParam );
                break;
            case SpectreAlgorithmV1:
                result = spectre_site_derived_password_v1( userKey, siteKey, resultType, resultParam );
                break;
            case SpectreAlgorithmV2:
                result = spectre_site_derived_password_v2( userKey, siteKey, resultType, resultParam );
                break;
            case SpectreAlgorithmV3:
                result = spectre_site_derived_password_v3( userKey, siteKey, resultType, resultParam );
                break;
            default:
                err( "Unsupported version: %d", userKey->algorithm );
                break;
        }
    }
    else {
        err( "Unsupported password type: %d", resultType );
    }

    spectre_free( &siteKey, sizeof( SpectreSiteKey ) );
    return result;
}

const char *spectre_site_state(
        const SpectreUserKey *userKey, const char *siteName,
        const SpectreResultType resultType, const char *resultParam,
        const SpectreCounter keyCounter, const SpectreKeyPurpose keyPurpose, const char *keyContext) {

    if (keyContext && !strlen( keyContext ))
        keyContext = NULL;
    if (resultParam && !strlen( resultParam ))
        resultParam = NULL;
    if (!userKey) {
        err( "Missing userKey" );
        return NULL;
    }
    if (!resultParam) {
        err( "Missing resultParam" );
        return NULL;
    }

    const SpectreSiteKey *siteKey = spectre_site_key( userKey, siteName, keyCounter, keyPurpose, keyContext );
    if (!siteKey) {
        err( "Missing siteKey" );
        return NULL;
    }

    trc( "-- spectre_site_state (algorithm: %u)", userKey->algorithm );
    trc( "resultType: %d (%s)", resultType, spectre_type_short_name( resultType ) );
    trc( "resultParam: %zu bytes = %s", resultParam? strlen( resultParam ): 0, resultParam );

    const char *result = NULL;
    if (resultType == SpectreResultNone) {
        result = NULL;
    }
    else {
        switch (userKey->algorithm) {
            case SpectreAlgorithmV0:
                result = spectre_site_state_v0( userKey, siteKey, resultType, resultParam );
                break;
            case SpectreAlgorithmV1:
                result = spectre_site_state_v1( userKey, siteKey, resultType, resultParam );
                break;
            case SpectreAlgorithmV2:
                result = spectre_site_state_v2( userKey, siteKey, resultType, resultParam );
                break;
            case SpectreAlgorithmV3:
                result = spectre_site_state_v3( userKey, siteKey, resultType, resultParam );
                break;
            default:
                err( "Unsupported version: %d", userKey->algorithm );
                break;
        }
    }

    spectre_free( &siteKey, sizeof( SpectreSiteKey ) );
    return result;
}

static const char *spectre_identicon_leftArms[] = { "╔", "╚", "╰", "═" };
static const char *spectre_identicon_bodies[] = { "█", "░", "▒", "▓", "☺", "☻" };
static const char *spectre_identicon_rightArms[] = { "╗", "╝", "╯", "═" };
static const char *spectre_identicon_accessories[] = {
        "◈", "◎", "◐", "◑", "◒", "◓", "☀", "☁", "☂", "☃", "☄", "★", "☆", "☎", "☏", "⎈", "⌂", "☘", "☢", "☣",
        "☕", "⌚", "⌛", "⏰", "⚡", "⛄", "⛅", "☔", "♔", "♕", "♖", "♗", "♘", "♙", "♚", "♛", "♜", "♝", "♞", "♟",
        "♨", "♩", "♪", "♫", "⚐", "⚑", "⚔", "⚖", "⚙", "⚠", "⌘", "⏎", "✄", "✆", "✈", "✉", "✌"
};

const SpectreIdenticon spectre_identicon(
        const char *userName, const char *userSecret) {

    uint8_t seed[32] = { 0 };
    if (userName && strlen( userName ) && userSecret && strlen( userSecret ))
        if (!spectre_hash_hmac_sha256( seed,
                (const uint8_t *)userSecret, strlen( userSecret ),
                (const uint8_t *)userName, strlen( userName ) )) {
            spectre_zero( &seed, sizeof( seed ) );
            return SpectreIdenticonUnset;
        }

    SpectreIdenticon identicon = {
            .leftArm = spectre_identicon_leftArms[seed[0] % (sizeof( spectre_identicon_leftArms ) / sizeof( *spectre_identicon_leftArms ))],
            .body = spectre_identicon_bodies[seed[1] % (sizeof( spectre_identicon_bodies ) / sizeof( *spectre_identicon_bodies ))],
            .rightArm = spectre_identicon_rightArms[seed[2] % (sizeof( spectre_identicon_rightArms ) / sizeof( *spectre_identicon_rightArms ))],
            .accessory = spectre_identicon_accessories[seed[3] % (sizeof( spectre_identicon_accessories ) / sizeof( *spectre_identicon_accessories ))],
            .color = (SpectreIdenticonColor)(seed[4] % (SpectreIdenticonColorLast - SpectreIdenticonColorFirst + 1) + SpectreIdenticonColorFirst),
    };
    spectre_zero( &seed, sizeof( seed ) );

    return identicon;
}

const char *spectre_identicon_encode(
        const SpectreIdenticon identicon) {

    if (identicon.color == SpectreIdenticonColorUnset)
        return NULL;

    return spectre_str( "%hhu:%s%s%s%s",
            identicon.color, identicon.leftArm, identicon.body, identicon.rightArm, identicon.accessory );
}

const SpectreIdenticon spectre_identicon_encoded(
        const char *encoding) {

    SpectreIdenticon identicon = SpectreIdenticonUnset;
    if (!encoding || !strlen( encoding ))
        return identicon;

    char *string = calloc( strlen( encoding ), sizeof( *string ) ), *parser = string;
    const char *leftArm = NULL, *body = NULL, *rightArm = NULL, *accessory = NULL;
    unsigned int color;

    if (string && sscanf( encoding, "%u:%s", &color, string ) == 2) {
        if (*parser && color)
            for (unsigned int s = 0; s < sizeof( spectre_identicon_leftArms ) / sizeof( *spectre_identicon_leftArms ); ++s) {
                const char *limb = spectre_identicon_leftArms[s];
                if (strncmp( parser, limb, strlen( limb ) ) == 0) {
                    leftArm = limb;
                    parser += strlen( limb );
                    break;
                }
            }
        if (*parser && leftArm)
            for (unsigned int s = 0; s < sizeof( spectre_identicon_bodies ) / sizeof( *spectre_identicon_bodies ); ++s) {
                const char *limb = spectre_identicon_bodies[s];
                if (strncmp( parser, limb, strlen( limb ) ) == 0) {
                    body = limb;
                    parser += strlen( limb );
                    break;
                }
            }
        if (*parser && body)
            for (unsigned int s = 0; s < sizeof( spectre_identicon_rightArms ) / sizeof( *spectre_identicon_rightArms ); ++s) {
                const char *limb = spectre_identicon_rightArms[s];
                if (strncmp( parser, limb, strlen( limb ) ) == 0) {
                    rightArm = limb;
                    parser += strlen( limb );
                    break;
                }
            }
        if (*parser && rightArm)
            for (unsigned int s = 0; s < sizeof( spectre_identicon_accessories ) / sizeof( *spectre_identicon_accessories ); ++s) {
                const char *limb = spectre_identicon_accessories[s];
                if (strncmp( parser, limb, strlen( limb ) ) == 0) {
                    accessory = limb;
                    break;
                }
            }
        if (leftArm && body && rightArm && color >= SpectreIdenticonColorFirst && color <= SpectreIdenticonColorLast)
            identicon = (SpectreIdenticon){
                    .leftArm = leftArm,
                    .body = body,
                    .rightArm = rightArm,
                    .accessory = accessory,
                    .color = (SpectreIdenticonColor)color,
            };
    }

    spectre_free_string( &string );
    return identicon;
}
07070100000011000081A400000000000000000000000163C2D55500000D15000000000000000000000000000000000000003E00000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-algorithm.h// =============================================================================
// Created by Maarten Billemont on 2014-12-19.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#ifndef _SPECTRE_ALGORITHM_H
#define _SPECTRE_ALGORITHM_H

#include "spectre-types.h"

/** Derive the user key for a user based on their name and user secret.
 * @return A SpectreUserKey value (allocated) or NULL if the userName or userSecret is missing, the algorithm is unknown, or an algorithm error occurred. */
const SpectreUserKey *spectre_user_key(
        const char *userName, const char *userSecret, const SpectreAlgorithm algorithmVersion);

/** Generate a result token for a user from the user's user key and result parameters.
 * @param resultParam A parameter for the resultType.  For stateful result types, the output of spectre_site_state.
 * @return A C-string (allocated) or NULL if the userKey or siteName is missing, the algorithm is unknown, or an algorithm error occurred. */
const char *spectre_site_result(
        const SpectreUserKey *userKey, const char *siteName,
        const SpectreResultType resultType, const char *resultParam,
        const SpectreCounter keyCounter, const SpectreKeyPurpose keyPurpose, const char *keyContext);

/** Encrypt a result token for stateful persistence.
 * @param resultParam A parameter for the resultType.  For stateful result types, the desired spectre_site_result.
 * @return A C-string (allocated) or NULL if the userKey, siteName or resultType's resultParam is missing, the algorithm is unknown, or an algorithm error occurred. */
const char *spectre_site_state(
        const SpectreUserKey *userKey, const char *siteName,
        const SpectreResultType resultType, const char *resultParam,
        const SpectreCounter keyCounter, const SpectreKeyPurpose keyPurpose, const char *keyContext);

/** Derive the result key for a user from the user's user key and result parameters.
 * @return An SpectreSiteKey value (allocated) or NULL if the userKey or siteName is missing, the algorithm is unknown, or an algorithm error occurred. */
const SpectreSiteKey *spectre_site_key(
        const SpectreUserKey *userKey, const char *siteName,
        const SpectreCounter keyCounter, const SpectreKeyPurpose keyPurpose, const char *keyContext);

/** @return An identicon (static) that represents the user's identity. */
const SpectreIdenticon spectre_identicon(
        const char *userName, const char *userSecret);
/** @return A C-string encoded representation (allocated) of the given identicon or NULL if the identicon is unset. */
const char *spectre_identicon_encode(
        const SpectreIdenticon identicon);
/** @return An identicon (static) decoded from the given encoded identicon representation or an identicon with empty fields if the identicon could not be parsed. */
const SpectreIdenticon spectre_identicon_encoded(
        const char *encoding);

#endif // _SPECTRE_ALGORITHM_H
07070100000012000081A400000000000000000000000163C2D55500002E11000000000000000000000000000000000000004100000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-algorithm_v0.c// =============================================================================
// Created by Maarten Billemont on 2014-05-05.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#include "spectre-algorithm_v0.h"
#include "spectre-util.h"

SPECTRE_LIBS_BEGIN
#include <string.h>
#include <errno.h>
#include <time.h>
SPECTRE_LIBS_END

#define Spectre_N                32768LU
#define Spectre_r                8U
#define Spectre_p                2U
#define Spectre_otp_window       5 * 60 /* s */

// Algorithm version helpers.
const char *spectre_type_template_v0(const SpectreResultType type, uint16_t templateIndex) {

    size_t count = 0;
    const char **templates = spectre_type_templates( type, &count );
    char const *template = templates && count? templates[templateIndex % count]: NULL;
    free( templates );

    return template;
}

const char spectre_class_character_v0(char characterClass, uint16_t classIndex) {

    const char *classCharacters = spectre_class_characters( characterClass );
    if (!classCharacters)
        return '\0';

    return classCharacters[classIndex % strlen( classCharacters )];
}

// Algorithm version overrides.
bool spectre_user_key_v0(
        const SpectreUserKey *userKey, const char *userName, const char *userSecret) {

    const char *keyScope = spectre_purpose_scope( SpectreKeyPurposeAuthentication );
    trc( "keyScope: %s", keyScope );

    // Calculate the user key salt.
    trc( "userKeySalt: keyScope=%s | #userName=%s | userName=%s",
            keyScope, spectre_hex_l( (uint32_t)spectre_utf8_char_count( userName ), (char[9]){ 0 } ), userName );
    size_t userKeySaltSize = 0;
    uint8_t *userKeySalt = NULL;
    if (!(spectre_buf_push( &userKeySalt, &userKeySaltSize, keyScope ) &&
          spectre_buf_push( &userKeySalt, &userKeySaltSize, (uint32_t)spectre_utf8_char_count( userName ) ) &&
          spectre_buf_push( &userKeySalt, &userKeySaltSize, userName )) || !userKeySalt) {
        spectre_free( &userKeySalt, userKeySaltSize );
        err( "Could not allocate user key salt: %s", strerror( errno ) );
        return false;
    }
    trc( "  => userKeySalt.id: %s", spectre_id_buf( userKeySalt, userKeySaltSize ).hex );

    // Calculate the user key.
    trc( "userKey: scrypt( userSecret, userKeySalt, N=%lu, r=%u, p=%u )", Spectre_N, Spectre_r, Spectre_p );
    bool success = spectre_kdf_scrypt( (uint8_t *)userKey->bytes, sizeof( userKey->bytes ),
            (uint8_t *)userSecret, strlen( userSecret ), userKeySalt, userKeySaltSize, Spectre_N, Spectre_r, Spectre_p );
    spectre_free( &userKeySalt, userKeySaltSize );

    if (!success)
        err( "Could not derive user key: %s", strerror( errno ) );
    else {
        SpectreKeyID keyID = spectre_id_buf( userKey->bytes, sizeof( userKey->bytes ) );
        memcpy( (SpectreKeyID *)&userKey->keyID, &keyID, sizeof( userKey->keyID ) );
        trc( "  => userKey.id: %s (algorithm: %d:0)", userKey->keyID.hex, userKey->algorithm );
    }
    return success;
}

bool spectre_site_key_v0(
        const SpectreSiteKey *siteKey, const SpectreUserKey *userKey, const char *siteName,
        SpectreCounter keyCounter, SpectreKeyPurpose keyPurpose, const char *keyContext) {

    const char *keyScope = spectre_purpose_scope( keyPurpose );
    trc( "keyScope: %s", keyScope );

    // OTP counter value.
    if (keyCounter == SpectreCounterTOTP)
        keyCounter = ((SpectreCounter)time( NULL ) / Spectre_otp_window) * Spectre_otp_window;

    // Calculate the site seed.
    trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | keyCounter=%s | #keyContext=%s | keyContext=%s",
            keyScope, spectre_hex_l( (uint32_t)spectre_utf8_char_count( siteName ), (char[9]){ 0 } ), siteName,
            spectre_hex_l( keyCounter, (char[9]){ 0 } ),
            keyContext? spectre_hex_l( (uint32_t)spectre_utf8_char_count( keyContext ), (char[9]){ 0 } ): NULL, keyContext );
    size_t siteSaltSize = 0;
    uint8_t *siteSalt = NULL;
    if (!(spectre_buf_push( &siteSalt, &siteSaltSize, keyScope ) &&
          spectre_buf_push( &siteSalt, &siteSaltSize, (uint32_t)spectre_utf8_char_count( siteName ) ) &&
          spectre_buf_push( &siteSalt, &siteSaltSize, siteName ) &&
          spectre_buf_push( &siteSalt, &siteSaltSize, (uint32_t)keyCounter ) &&
          (!keyContext? true:
           spectre_buf_push( &siteSalt, &siteSaltSize, (uint32_t)spectre_utf8_char_count( keyContext ) ) &&
           spectre_buf_push( &siteSalt, &siteSaltSize, keyContext ))) || !siteSalt) {
        err( "Could not allocate site salt: %s", strerror( errno ) );
        return false;
    }
    trc( "  => siteSalt.id: %s", spectre_id_buf( siteSalt, siteSaltSize ).hex );

    trc( "siteKey: hmac-sha256( userKey.id=%s, siteSalt )", userKey->keyID.hex );
    bool success = spectre_hash_hmac_sha256( (uint8_t *)siteKey->bytes,
            userKey->bytes, sizeof( userKey->bytes ), siteSalt, siteSaltSize );
    spectre_free( &siteSalt, siteSaltSize );

    if (!success)
        err( "Could not derive site key: %s", strerror( errno ) );
    else {
        SpectreKeyID keyID = spectre_id_buf( siteKey->bytes, sizeof( siteKey->bytes ) );
        memcpy( (SpectreKeyID *)&siteKey->keyID, &keyID, sizeof( siteKey->keyID ) );
        trc( "  => siteKey.id: %s (algorithm: %d:0)", siteKey->keyID.hex, siteKey->algorithm );
    }
    return success;
}

const char *spectre_site_template_password_v0(
        __unused const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, __unused const char *resultParam) {

    const char *_siteKey = (const char *)siteKey->bytes;

    // Determine the template.
    uint16_t seedByte;
    spectre_uint16( (uint16_t)_siteKey[0], (uint8_t *)&seedByte );
    const char *template = spectre_type_template_v0( resultType, seedByte );
    trc( "template: %u => %s", seedByte, template );
    if (!template)
        return NULL;
    if (strlen( template ) > sizeof( siteKey->bytes ) - 1) {
        err( "Template too long for password seed: %zu", strlen( template ) );
        return NULL;
    }

    // Encode the password from the seed using the template.
    char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
    for (size_t c = 0; c < strlen( template ); ++c) {
        spectre_uint16( (uint16_t)_siteKey[c + 1], (uint8_t *)&seedByte );
        sitePassword[c] = spectre_class_character_v0( template[c], seedByte );
        trc( "  - class: %c, index: %5u (0x%.2hX) => character: %c",
                template[c], seedByte, seedByte, sitePassword[c] );
    }
    trc( "  => password: %s", sitePassword );

    return sitePassword;
}

const char *spectre_site_crypted_password_v0(
        const SpectreUserKey *userKey, __unused const SpectreSiteKey *siteKey, __unused SpectreResultType resultType, const char *cipherText) {

    if (!cipherText) {
        err( "Missing encrypted state." );
        return NULL;
    }
    size_t cipherLength = strlen( cipherText );
    if (cipherLength % 4 != 0) {
        wrn( "Malformed encrypted state, not base64." );
        // This can happen if state was stored in a non-encrypted form, eg. login in old mpsites.
        return spectre_strdup( cipherText );
    }

    // Base64-decode
    char *hex = NULL;
    uint8_t *cipherBuf = calloc( 1, spectre_base64_decode_max( cipherLength ) );
    size_t bufSize = spectre_base64_decode( cipherText, cipherBuf ), cipherBufSize = bufSize, hexSize = 0;
    if ((int)bufSize < 0) {
        err( "Base64 decoding error." );
        spectre_free( &cipherBuf, spectre_base64_decode_max( cipherLength ) );
        return NULL;
    }
    trc( "b64 decoded: %zu bytes = %s", bufSize, hex = spectre_hex( cipherBuf, bufSize, hex, &hexSize ) );

    // Decrypt
    const uint8_t *plainBytes = spectre_aes_decrypt( userKey->bytes, sizeof( userKey->bytes ), cipherBuf, &bufSize );
    spectre_free( &cipherBuf, cipherBufSize );
    const char *plainText = spectre_strndup( (char *)plainBytes, bufSize );
    if (!plainText)
        err( "AES decryption error: %s", strerror( errno ) );
    else if (!spectre_utf8_char_count( plainText ))
        trc( "decrypted -> plainText: %zu chars = (illegal UTF-8) :: %zu bytes = %s",
                strlen( plainText ), bufSize, hex = spectre_hex( plainBytes, bufSize, hex, &hexSize ) );
    else
        trc( "decrypted -> plainText: %zu chars = %s :: %zu bytes = %s",
                strlen( plainText ), plainText, bufSize, hex = spectre_hex( plainBytes, bufSize, hex, &hexSize ) );
    spectre_free( &plainBytes, bufSize );
    spectre_free_string( &hex );

    return plainText;
}

const char *spectre_site_derived_password_v0(
        __unused const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *resultParam) {

    switch (resultType) {
        case SpectreResultDeriveKey: {
            if (!resultParam) {
                err( "Missing key size parameter." );
                return NULL;
            }
            long parameter = strtol( resultParam, NULL, 10 );
            if (!parameter)
                parameter = 512;
            if (parameter < 128 || parameter > 512 || parameter % 8 != 0) {
                err( "Parameter is not a valid key size (should be 128 - 512): %s", resultParam );
                return NULL;
            }

            // Derive key
            uint8_t resultKey[parameter / 8];
            trc( "keySize: %u", sizeof( resultKey ) );
            if (!spectre_kdf_blake2b( resultKey, sizeof( resultKey ), siteKey->bytes, sizeof( siteKey->bytes ), NULL, 0, 0, NULL )) {
                err( "Could not derive result key: %s", strerror( errno ) );
                return NULL;
            }

            // Base64-encode
            char *b64Key = calloc( 1, spectre_base64_encode_max( sizeof( resultKey ) ) );
            if (spectre_base64_encode( resultKey, sizeof( resultKey ), b64Key ) < 0) {
                err( "Base64 encoding error." );
                spectre_free_string( &b64Key );
            }
            else
                trc( "b64 encoded -> key: %s", b64Key );
            spectre_zero( &resultKey, sizeof( resultKey ) );

            return b64Key;
        }
        default:
            err( "Unsupported derived password type: %d", resultType );
            return NULL;
    }
}

const char *spectre_site_state_v0(
        const SpectreUserKey *userKey, __unused const SpectreSiteKey *siteKey, __unused SpectreResultType resultType, const char *plainText) {

    // Encrypt
    char *hex = NULL;
    size_t bufSize = strlen( plainText ), hexSize = 0;
    const uint8_t *cipherBuf = spectre_aes_encrypt( userKey->bytes, sizeof( userKey->bytes ), (const uint8_t *)plainText, &bufSize );
    if (!cipherBuf) {
        err( "AES encryption error: %s", strerror( errno ) );
        return NULL;
    }
    trc( "cipherBuf: %zu bytes = %s", bufSize, hex = spectre_hex( cipherBuf, bufSize, hex, &hexSize ) );

    // Base64-encode
    char *cipherText = calloc( 1, spectre_base64_encode_max( bufSize ) );
    if (spectre_base64_encode( cipherBuf, bufSize, cipherText ) < 0) {
        err( "Base64 encoding error." );
        spectre_free_string( &cipherText );
    }
    else
        trc( "b64 encoded -> cipherText: %s", cipherText );
    spectre_free( &cipherBuf, bufSize );
    spectre_free_string( &hex );

    return cipherText;
}
07070100000013000081A400000000000000000000000163C2D5550000076F000000000000000000000000000000000000004100000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-algorithm_v0.h// =============================================================================
// Created by Maarten Billemont on 2019-11-27.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#ifndef _SPECTRE_ALGORITHM_V0_H
#define _SPECTRE_ALGORITHM_V0_H

#include "spectre-algorithm.h"

const char *spectre_type_template_v0(
        SpectreResultType type, uint16_t templateIndex);
const char spectre_class_character_v0(
        char characterClass, uint16_t classIndex);
bool spectre_user_key_v0(
        const SpectreUserKey *userKey, const char *userName, const char *userSecret);
bool spectre_site_key_v0(
        const SpectreSiteKey *siteKey, const SpectreUserKey *userKey, const char *siteName,
        SpectreCounter keyCounter, SpectreKeyPurpose keyPurpose, const char *keyContext);
const char *spectre_site_template_password_v0(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *resultParam);
const char *spectre_site_crypted_password_v0(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *cipherText);
const char *spectre_site_derived_password_v0(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *resultParam);
const char *spectre_site_state_v0(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *plainText);

#endif // _SPECTRE_ALGORITHM_V0_H
07070100000014000081A400000000000000000000000163C2D55500000CDE000000000000000000000000000000000000004100000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-algorithm_v1.c// =============================================================================
// Created by Maarten Billemont on 2014-05-05.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#include "spectre-algorithm_v1.h"
#include "spectre-util.h"

SPECTRE_LIBS_BEGIN
#include <string.h>
SPECTRE_LIBS_END

#define Spectre_N                32768LU
#define Spectre_r                8U
#define Spectre_p                2U
#define Spectre_otp_window       5 * 60 /* s */

// Algorithm version overrides.
bool spectre_user_key_v1(
        const SpectreUserKey *userKey, const char *userName, const char *userSecret) {

    return spectre_user_key_v0( userKey, userName, userSecret );
}

bool spectre_site_key_v1(
        const SpectreSiteKey *siteKey, const SpectreUserKey *userKey, const char *siteName,
        SpectreCounter keyCounter, SpectreKeyPurpose keyPurpose, const char *keyContext) {

    return spectre_site_key_v0( siteKey, userKey, siteName, keyCounter, keyPurpose, keyContext );
}

const char *spectre_site_template_password_v1(
        __unused const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, __unused const char *resultParam) {

    // Determine the template.
    uint8_t seedByte = siteKey->bytes[0];
    const char *template = spectre_type_template( resultType, seedByte );
    trc( "template: %u => %s", seedByte, template );
    if (!template)
        return NULL;
    if (strlen( template ) > sizeof( siteKey->bytes ) - 1) {
        err( "Template too long for password seed: %zu", strlen( template ) );
        return NULL;
    }

    // Encode the password from the seed using the template.
    char *const sitePassword = calloc( strlen( template ) + 1, sizeof( char ) );
    for (size_t c = 0; c < strlen( template ); ++c) {
        seedByte = siteKey->bytes[c + 1];
        sitePassword[c] = spectre_class_character( template[c], seedByte );
        trc( "  - class: %c, index: %3u (0x%.2hhX) => character: %c",
                template[c], seedByte, seedByte, sitePassword[c] );
    }
    trc( "  => password: %s", sitePassword );

    return sitePassword;
}

const char *spectre_site_crypted_password_v1(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *cipherText) {

    return spectre_site_crypted_password_v0( userKey, siteKey, resultType, cipherText );
}

const char *spectre_site_derived_password_v1(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *resultParam) {

    return spectre_site_derived_password_v0( userKey, siteKey, resultType, resultParam );
}

const char *spectre_site_state_v1(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *state) {

    return spectre_site_state_v0( userKey, siteKey, resultType, state );
}
07070100000015000081A400000000000000000000000163C2D55500000772000000000000000000000000000000000000004100000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-algorithm_v1.h// =============================================================================
// Created by Maarten Billemont on 2019-11-27.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#ifndef _SPECTRE_ALGORITHM_V1_H
#define _SPECTRE_ALGORITHM_V1_H

#include "spectre-algorithm_v0.h"

const char *spectre_type_template_v1(
        SpectreResultType type, uint16_t templateIndex);
const char spectre_class_character_v1(
        char characterClass, uint16_t classIndex);
bool spectre_user_key_v1(
        const SpectreUserKey *userKey, const char *userName, const char *userSecret);
bool spectre_site_key_v1(
        const SpectreSiteKey *siteKey, const SpectreUserKey *userKey, const char *siteName, SpectreCounter keyCounter,
        SpectreKeyPurpose keyPurpose, const char *keyContext);
const char *spectre_site_template_password_v1(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *resultParam);
const char *spectre_site_crypted_password_v1(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *cipherText);
const char *spectre_site_derived_password_v1(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *resultParam);
const char *spectre_site_state_v1(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *plainText);

#endif // _SPECTRE_ALGORITHM_V1_H
07070100000016000081A400000000000000000000000163C2D55500001161000000000000000000000000000000000000004100000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-algorithm_v2.c// =============================================================================
// Created by Maarten Billemont on 2014-05-05.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#include "spectre-algorithm_v2.h"
#include "spectre-util.h"

SPECTRE_LIBS_BEGIN
#include <string.h>
#include <errno.h>
#include <time.h>
SPECTRE_LIBS_END

#define Spectre_N                32768LU
#define Spectre_r                8U
#define Spectre_p                2U
#define Spectre_otp_window       5 * 60 /* s */

// Algorithm version overrides.
bool spectre_user_key_v2(
        const SpectreUserKey *userKey, const char *userName, const char *userSecret) {

    return spectre_user_key_v1( userKey, userName, userSecret );
}

bool spectre_site_key_v2(
        const SpectreSiteKey *siteKey, const SpectreUserKey *userKey, const char *siteName,
        SpectreCounter keyCounter, SpectreKeyPurpose keyPurpose, const char *keyContext) {

    const char *keyScope = spectre_purpose_scope( keyPurpose );
    trc( "keyScope: %s", keyScope );

    // OTP counter value.
    if (keyCounter == SpectreCounterTOTP)
        keyCounter = ((SpectreCounter)time( NULL ) / Spectre_otp_window) * Spectre_otp_window;

    // Calculate the site seed.
    trc( "siteSalt: keyScope=%s | #siteName=%s | siteName=%s | keyCounter=%s | #keyContext=%s | keyContext=%s",
            keyScope, spectre_hex_l( (uint32_t)strlen( siteName ), (char[9]){ 0 } ), siteName, spectre_hex_l( keyCounter, (char[9]){ 0 } ),
            keyContext? spectre_hex_l( (uint32_t)strlen( keyContext ), (char[9]){ 0 } ): NULL, keyContext );
    size_t siteSaltSize = 0;
    uint8_t *siteSalt = NULL;
    if (!(spectre_buf_push( &siteSalt, &siteSaltSize, keyScope ) &&
          spectre_buf_push( &siteSalt, &siteSaltSize, (uint32_t)strlen( siteName ) ) &&
          spectre_buf_push( &siteSalt, &siteSaltSize, siteName ) &&
          spectre_buf_push( &siteSalt, &siteSaltSize, (uint32_t)keyCounter ) &&
          (!keyContext? true:
           spectre_buf_push( &siteSalt, &siteSaltSize, (uint32_t)strlen( keyContext ) ) &&
           spectre_buf_push( &siteSalt, &siteSaltSize, keyContext ))) || !siteSalt) {
        err( "Could not allocate site salt: %s", strerror( errno ) );
        return false;
    }
    trc( "  => siteSalt.id: %s", spectre_id_buf( siteSalt, siteSaltSize ).hex );

    trc( "siteKey: hmac-sha256( userKey.id=%s, siteSalt )", userKey->keyID.hex );
    bool success = spectre_hash_hmac_sha256( (uint8_t *)siteKey->bytes,
            userKey->bytes, sizeof( userKey->bytes ), siteSalt, siteSaltSize );
    spectre_free( &siteSalt, siteSaltSize );

    if (!success)
        err( "Could not derive site key: %s", strerror( errno ) );
    else {
        SpectreKeyID keyID = spectre_id_buf( siteKey->bytes, sizeof( siteKey->bytes ) );
        memcpy( (SpectreKeyID *)&siteKey->keyID, &keyID, sizeof( siteKey->keyID ) );
        trc( "  => siteKey.id: %s (algorithm: %d:2)", siteKey->keyID.hex, siteKey->algorithm );
    }
    return success;
}

const char *spectre_site_template_password_v2(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *resultParam) {

    return spectre_site_template_password_v1( userKey, siteKey, resultType, resultParam );
}

const char *spectre_site_crypted_password_v2(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *cipherText) {

    return spectre_site_crypted_password_v1( userKey, siteKey, resultType, cipherText );
}

const char *spectre_site_derived_password_v2(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *resultParam) {

    return spectre_site_derived_password_v1( userKey, siteKey, resultType, resultParam );
}

const char *spectre_site_state_v2(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *state) {

    return spectre_site_state_v1( userKey, siteKey, resultType, state );
}
07070100000017000081A400000000000000000000000163C2D55500000772000000000000000000000000000000000000004100000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-algorithm_v2.h// =============================================================================
// Created by Maarten Billemont on 2019-11-27.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#ifndef _SPECTRE_ALGORITHM_V2_H
#define _SPECTRE_ALGORITHM_V2_H

#include "spectre-algorithm_v1.h"

const char *spectre_type_template_v2(
        SpectreResultType type, uint16_t templateIndex);
const char spectre_class_character_v2(
        char characterClass, uint16_t classIndex);
bool spectre_user_key_v2(
        const SpectreUserKey *userKey, const char *userName, const char *userSecret);
bool spectre_site_key_v2(
        const SpectreSiteKey *siteKey, const SpectreUserKey *userKey, const char *siteName, SpectreCounter keyCounter,
        SpectreKeyPurpose keyPurpose, const char *keyContext);
const char *spectre_site_template_password_v2(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *resultParam);
const char *spectre_site_crypted_password_v2(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *cipherText);
const char *spectre_site_derived_password_v2(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *resultParam);
const char *spectre_site_state_v2(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *plainText);

#endif // _SPECTRE_ALGORITHM_V2_H
07070100000018000081A400000000000000000000000163C2D55500000FEB000000000000000000000000000000000000004100000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-algorithm_v3.c// =============================================================================
// Created by Maarten Billemont on 2014-05-05.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#include "spectre-algorithm_v3.h"
#include "spectre-util.h"

SPECTRE_LIBS_BEGIN
#include <string.h>
#include <errno.h>
SPECTRE_LIBS_END

#define Spectre_N                32768LU
#define Spectre_r                8U
#define Spectre_p                2U
#define Spectre_otp_window       5 * 60 /* s */

// Algorithm version overrides.
bool spectre_user_key_v3(
        const SpectreUserKey *userKey, const char *userName, const char *userSecret) {

    const char *keyScope = spectre_purpose_scope( SpectreKeyPurposeAuthentication );
    trc( "keyScope: %s", keyScope );

    // Calculate the user key salt.
    trc( "userKeySalt: keyScope=%s | #userName=%s | userName=%s",
            keyScope, spectre_hex_l( (uint32_t)strlen( userName ), (char[9]){ 0 } ), userName );
    size_t userKeySaltSize = 0;
    uint8_t *userKeySalt = NULL;
    if (!(spectre_buf_push( &userKeySalt, &userKeySaltSize, keyScope ) &&
          spectre_buf_push( &userKeySalt, &userKeySaltSize, (uint32_t)strlen( userName ) ) &&
          spectre_buf_push( &userKeySalt, &userKeySaltSize, userName )) || !userKeySalt) {
        spectre_free( &userKeySalt, userKeySaltSize );
        err( "Could not allocate user key salt: %s", strerror( errno ) );
        return false;
    }
    trc( "  => userKeySalt.id: %s", spectre_id_buf( userKeySalt, userKeySaltSize ).hex );

    // Calculate the user key.
    trc( "userKey: scrypt( userSecret, userKeySalt, N=%lu, r=%u, p=%u )", Spectre_N, Spectre_r, Spectre_p );
    bool success = spectre_kdf_scrypt( (uint8_t *)userKey->bytes, sizeof( userKey->bytes ),
            (uint8_t *)userSecret, strlen( userSecret ), userKeySalt, userKeySaltSize, Spectre_N, Spectre_r, Spectre_p );
    spectre_free( &userKeySalt, userKeySaltSize );

    if (!success)
        err( "Could not derive user key: %s", strerror( errno ) );
    else {
        SpectreKeyID keyID = spectre_id_buf( userKey->bytes, sizeof( userKey->bytes ) );
        memcpy( (SpectreKeyID *)&userKey->keyID, &keyID, sizeof( userKey->keyID ) );
        trc( "  => userKey.id: %s (algorithm: %d:3)", userKey->keyID.hex, userKey->algorithm );
    }
    return success;
}

bool spectre_site_key_v3(
        const SpectreSiteKey *siteKey, const SpectreUserKey *userKey, const char *siteName,
        SpectreCounter keyCounter, SpectreKeyPurpose keyPurpose, const char *keyContext) {

    return spectre_site_key_v2( siteKey, userKey, siteName, keyCounter, keyPurpose, keyContext );
}

const char *spectre_site_template_password_v3(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *resultParam) {

    return spectre_site_template_password_v2( userKey, siteKey, resultType, resultParam );
}

const char *spectre_site_crypted_password_v3(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *cipherText) {

    return spectre_site_crypted_password_v2( userKey, siteKey, resultType, cipherText );
}

const char *spectre_site_derived_password_v3(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *resultParam) {

    return spectre_site_derived_password_v2( userKey, siteKey, resultType, resultParam );
}

const char *spectre_site_state_v3(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *state) {

    return spectre_site_state_v2( userKey, siteKey, resultType, state );
}
07070100000019000081A400000000000000000000000163C2D55500000772000000000000000000000000000000000000004100000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-algorithm_v3.h// =============================================================================
// Created by Maarten Billemont on 2019-11-27.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#ifndef _SPECTRE_ALGORITHM_V3_H
#define _SPECTRE_ALGORITHM_V3_H

#include "spectre-algorithm_v2.h"

const char *spectre_type_template_v3(
        SpectreResultType type, uint16_t templateIndex);
const char spectre_class_character_v3(
        char characterClass, uint16_t classIndex);
bool spectre_user_key_v3(
        const SpectreUserKey *userKey, const char *userName, const char *userSecret);
bool spectre_site_key_v3(
        const SpectreSiteKey *siteKey, const SpectreUserKey *userKey, const char *siteName,
        SpectreCounter keyCounter, SpectreKeyPurpose keyPurpose, const char *keyContext);
const char *spectre_site_template_password_v3(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *resultParam);
const char *spectre_site_crypted_password_v3(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *cipherText);
const char *spectre_site_derived_password_v3(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *resultParam);
const char *spectre_site_state_v3(
        const SpectreUserKey *userKey, const SpectreSiteKey *siteKey, SpectreResultType resultType, const char *plainText);

#endif // _SPECTRE_ALGORITHM_V3_H
0707010000001A000081A400000000000000000000000163C2D55500001D4B000000000000000000000000000000000000004100000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-marshal-util.c// =============================================================================
// Created by Maarten Billemont on 2017-07-28.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#include "spectre-marshal-util.h"
#include "spectre-util.h"

SPECTRE_LIBS_BEGIN
#include <string.h>
#include <math.h>
SPECTRE_LIBS_END

const char *spectre_get_token(const char **in, const char *eol, const char *delim) {

    // Skip leading spaces.
    for (; **in == ' '; ++*in);

    // Find characters up to the first delim.
    size_t len = strcspn( *in, delim );
    const char *token = len <= (size_t)(eol - *in)? spectre_strndup( *in, len ): NULL;

    // Advance past the delimitor.
    *in = min( eol, *in + len + 1 );
    return token;
}

bool spectre_get_bool(const char *in) {

    return in && (in[0] == 'y' || in[0] == 't' || strtol( in, NULL, 10 ) > 0);
}

time_t spectre_get_timegm(const char *in) {

    // TODO: Support for parsing non-UTC time strings
    // Parse as a UTC timestamp, into a tm.
    struct tm tm = { .tm_isdst = -1 };
    if (in && sscanf( in, "%4d-%2d-%2dT%2d:%2d:%2dZ",
            &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
            &tm.tm_hour, &tm.tm_min, &tm.tm_sec ) == 6) {
        tm.tm_year -= 1900; // tm_year 0 = rfc3339 year  1900
        tm.tm_mon -= 1;     // tm_mon  0 = rfc3339 month 1

        // mktime interprets tm as being local, we need to offset back to UTC (timegm/tm_gmtoff are non-standard).
        time_t local_time = mktime( &tm ), local_dst = tm.tm_isdst > 0? 3600: 0;
        time_t gmtoff = local_time + local_dst - mktime( gmtime( &local_time ) );
        return local_time + gmtoff;
    }

    return ERR;
}

bool spectre_update_user_key(const SpectreUserKey **userKey, SpectreAlgorithm *userKeyAlgorithm, const SpectreAlgorithm targetKeyAlgorithm,
        const char *userName, const char *userSecret) {

    if (!userKey || !userKeyAlgorithm)
        return false;

    if (!*userKey || *userKeyAlgorithm != targetKeyAlgorithm) {
        spectre_free( userKey, sizeof( **userKey ) );
        *userKeyAlgorithm = targetKeyAlgorithm;
        *userKey = spectre_user_key( userName, userSecret, *userKeyAlgorithm );
    }

    return *userKey != NULL;
}

#if SPECTRE_JSON

json_object *spectre_get_json_object(
        json_object *obj, const char *key, const bool create) {

    if (!obj)
        return NULL;

    json_object *json_value = NULL;
    if (!json_object_object_get_ex( obj, key, &json_value ) || !json_value)
        if (!create || json_object_object_add( obj, key, json_value = json_object_new_object() ) != OK) {
            trc( "Missing value for: %s", key );
            json_value = NULL;
        }

    return json_value;
}

const char *spectre_get_json_string(
        json_object *obj, const char *key, const char *defaultValue) {

    json_object *json_value = spectre_get_json_object( obj, key, false );
    if (!json_value)
        return defaultValue;

    return json_object_get_string( json_value );
}

int64_t spectre_get_json_int(
        json_object *obj, const char *key, const int64_t defaultValue) {

    json_object *json_value = spectre_get_json_object( obj, key, false );
    if (!json_value)
        return defaultValue;

    return json_object_get_int64( json_value );
}

bool spectre_get_json_boolean(
        json_object *obj, const char *key, const bool defaultValue) {

    json_object *json_value = spectre_get_json_object( obj, key, false );
    if (!json_value)
        return defaultValue;

    return json_object_get_boolean( json_value ) == true;
}

static bool spectre_marshal_data_filter_keyed(SpectreMarshalledData *child, __unused void *args) {

    return child->obj_key != NULL;
}

static bool spectre_marshal_data_filter_unkeyed(SpectreMarshalledData *child, __unused void *args) {

    return child->obj_key == NULL;
}

void spectre_set_json_data(
        SpectreMarshalledData *data, json_object *obj) {

    if (!data)
        return;

    json_type type = json_object_get_type( obj );
    data->is_null = type == json_type_null;
    data->is_bool = type == json_type_boolean;

    if (type == json_type_boolean)
        data->num_value = json_object_get_boolean( obj );
    else if (type == json_type_double)
        data->num_value = json_object_get_double( obj );
    else if (type == json_type_int)
        data->num_value = json_object_get_int64( obj );
    else
        data->num_value = NAN;

    const char *str = NULL;
    if (type == json_type_string || !isnan( data->num_value ))
        str = json_object_get_string( obj );
    if (!str || !data->str_value || strcmp( str, data->str_value ) != OK) {
        spectre_free_string( &data->str_value );
        data->str_value = spectre_strdup( str );
    }

    // Clean up children
    if (type != json_type_object && type != json_type_array) {
        spectre_marshal_data_filter( data, spectre_marshal_data_filter_empty, NULL );
    }
    else if (type == json_type_array) {
        spectre_marshal_data_filter( data, spectre_marshal_data_filter_unkeyed, NULL );
    }
    else /* type == json_type_object */ {
        spectre_marshal_data_filter( data, spectre_marshal_data_filter_keyed, NULL );
    }

    // Object
    if (type == json_type_object) {
        json_object_iter entry;
        json_object_object_foreachC( obj, entry ) {
            SpectreMarshalledData *child = NULL;

            // Find existing child.
            for (size_t c = 0; c < data->children_count; ++c)
                if (data->children[c].obj_key == entry.key ||
                    (data->children[c].obj_key && entry.key && strcmp( data->children[c].obj_key, entry.key ) == OK)) {
                    child = &data->children[c];
                    break;
                }

            // Create new child.
            if (!child) {
                if (!spectre_realloc( &data->children, NULL, SpectreMarshalledData, ++data->children_count )) {
                    --data->children_count;
                    continue;
                }
                *(child = &data->children[data->children_count - 1]) = (SpectreMarshalledData){ .obj_key = spectre_strdup( entry.key ) };
                spectre_marshal_data_set_null( child, NULL );
            }

            spectre_set_json_data( child, entry.val );
        }
    }

    // Array
    if (type == json_type_array) {
        for (size_t index = 0; index < json_object_array_length( obj ); ++index) {
            SpectreMarshalledData *child = NULL;

            if (index < data->children_count)
                child = &data->children[index];

            else {
                if (!spectre_realloc( &data->children, NULL, SpectreMarshalledData, ++data->children_count )) {
                    --data->children_count;
                    continue;
                }
                *(child = &data->children[data->children_count - 1]) = (SpectreMarshalledData){ .arr_index = index };
                spectre_marshal_data_set_null( child, NULL );
            }

            spectre_set_json_data( child, json_object_array_get_idx( obj, index ) );
        }
    }
}

#endif
0707010000001B000081A400000000000000000000000163C2D55500000F36000000000000000000000000000000000000004100000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-marshal-util.h// =============================================================================
// Created by Maarten Billemont on 2017-07-28.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#ifndef _SPECTRE_MARSHAL_UTIL_H
#define _SPECTRE_MARSHAL_UTIL_H

#include "spectre-algorithm.h"
#include "spectre-marshal.h"

SPECTRE_LIBS_BEGIN
#include <time.h>
#if SPECTRE_JSON
#include "json-c/json.h"
#endif
SPECTRE_LIBS_END

/// Type parsing.

/** Get a token from a string by searching until the first character in delim, no farther than eol.
 * The input string reference is advanced beyond the token delimitor if one is found.
 * @return A C-string (allocated) containing the token or NULL if the delim wasn't found before eol. */
const char *spectre_get_token(
        const char **in, const char *eol, const char *delim);
/** Get a boolean value as expressed by the given string.
 * @return true if the string is not NULL and holds a number larger than 0, or starts with a t (for true) or y (for yes). */
bool spectre_get_bool(
        const char *in);
/** Convert an RFC 3339 time string into epoch time.
 * @return ERR if the string could not be parsed. */
time_t spectre_get_timegm(
        const char *in);


/// Spectre.

/** Calculate a user key if the target user key algorithm is different from the given user key algorithm.
 * @param userKey A buffer (allocated).
 * @return false if an error occurred during the derivation of the user key. */
bool spectre_update_user_key(
        const SpectreUserKey **userKey, SpectreAlgorithm *userKeyAlgorithm, const SpectreAlgorithm targetKeyAlgorithm,
        const char *userName, const char *userSecret);


/// JSON parsing.

#if SPECTRE_JSON
/** Search for an object in a JSON object tree.
 * @param key A JSON object key for the child in this object.
 * @param create If true, create and insert new objects for any missing path components.
 * @return An object (shared) or a new object (shared) installed in the tree if the path's object path was not found. */
json_object *spectre_get_json_object(
        json_object *obj, const char *key, const bool create);
/** Search for a string in a JSON object tree.
 * @param key A dot-delimited list of JSON object keys to walk toward the child object.
 * @return A C-string (shared) or defaultValue if one of the path's object keys was not found in the source object's tree. */
const char *spectre_get_json_string(
        json_object *obj, const char *key, const char *defaultValue);
/** Search for an integer in a JSON object tree.
 * @param key A dot-delimited list of JSON object keys to walk toward the child object.
 * @return The integer value or defaultValue if one of the path's object keys was not found in the source object's tree. */
int64_t spectre_get_json_int(
        json_object *obj, const char *key, const int64_t defaultValue);
/** Search for a boolean in a JSON object tree.
 * @param key A dot-delimited list of JSON object keys to walk toward the child object.
 * @return The boolean value or defaultValue if one of the path's object keys was not found in the source object's tree. */
bool spectre_get_json_boolean(
        json_object *obj, const char *key, const bool defaultValue);
/** Translate a JSON object tree into a source-agnostic data object.
 * @param data A Spectre data object or NULL.
 * @param obj A JSON object tree or NULL. */
void spectre_set_json_data(
        SpectreMarshalledData *data, json_object *obj);
#endif

#endif // _SPECTRE_MARSHAL_UTIL_H
0707010000001C000081A400000000000000000000000163C2D5550000F09A000000000000000000000000000000000000003C00000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-marshal.c// =============================================================================
// Created by Maarten Billemont on 2017-07-15.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================


#include "spectre-marshal.h"
#include "spectre-util.h"
#include "spectre-marshal-util.h"

SPECTRE_LIBS_BEGIN
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
SPECTRE_LIBS_END

static SpectreKeyProviderProxy __spectre_proxy_provider_current = NULL;
static const SpectreUserKey *__spectre_proxy_provider_current_key = NULL;
static SpectreAlgorithm __spectre_proxy_provider_current_algorithm = (SpectreAlgorithm)ERR;
static const char *__spectre_proxy_provider_current_secret = NULL;

static bool __spectre_proxy_provider_secret(const SpectreUserKey **currentKey, SpectreAlgorithm *currentAlgorithm,
        SpectreAlgorithm algorithm, const char *userName) {

    if (!currentKey)
        return spectre_free_string( &__spectre_proxy_provider_current_secret );

    return spectre_update_user_key( currentKey, currentAlgorithm, algorithm, userName, __spectre_proxy_provider_current_secret );
}

static const SpectreUserKey *__spectre_proxy_provider(SpectreAlgorithm algorithm, const char *userName) {

    if (!__spectre_proxy_provider_current)
        return NULL;
    if (!__spectre_proxy_provider_current(
            &__spectre_proxy_provider_current_key, &__spectre_proxy_provider_current_algorithm, algorithm, userName ))
        return NULL;

    return spectre_memdup( __spectre_proxy_provider_current_key, sizeof( *__spectre_proxy_provider_current_key ) );
}

SpectreKeyProvider spectre_proxy_provider_set_secret(const char *userSecret) {

    spectre_proxy_provider_unset();
    __spectre_proxy_provider_current_secret = spectre_strdup( userSecret );
    return spectre_proxy_provider_set( __spectre_proxy_provider_secret );
}

SpectreKeyProvider spectre_proxy_provider_set(const SpectreKeyProviderProxy proxy) {

    spectre_proxy_provider_unset();
    __spectre_proxy_provider_current = proxy;
    return __spectre_proxy_provider;
}

void spectre_proxy_provider_unset() {

    spectre_free( &__spectre_proxy_provider_current_key, sizeof( *__spectre_proxy_provider_current_key ) );
    __spectre_proxy_provider_current_algorithm = (SpectreAlgorithm)ERR;
    if (__spectre_proxy_provider_current) {
        __spectre_proxy_provider_current( NULL, NULL, (SpectreAlgorithm)ERR, NULL );
        __spectre_proxy_provider_current = NULL;
    }
}

void spectre_key_provider_free(SpectreKeyProvider keyProvider) {

    if (keyProvider)
        keyProvider( (SpectreAlgorithm)ERR, NULL );
}

SpectreMarshalledUser *spectre_marshal_user(
        const char *userName, SpectreKeyProvider userKeyProvider, const SpectreAlgorithm algorithmVersion) {

    SpectreMarshalledUser *user;
    if (!userName || !(user = malloc( sizeof( SpectreMarshalledUser ) )))
        return NULL;

    *user = (SpectreMarshalledUser){
            .userKeyProvider = userKeyProvider,
            .algorithm = algorithmVersion,
            .redacted = true,

            .avatar = 0,
            .userName = spectre_strdup( userName ),
            .identicon = SpectreIdenticonUnset,
            .keyID = SpectreKeyIDUnset,
            .defaultType = SpectreResultDefaultResult,
            .loginType = SpectreResultDefaultLogin,
            .loginState = NULL,
            .lastUsed = 0,

            .sites_count = 0,
            .sites = NULL,
    };
    return user;
}

SpectreMarshalledSite *spectre_marshal_site(
        SpectreMarshalledUser *user, const char *siteName, const SpectreResultType resultType,
        const SpectreCounter keyCounter, const SpectreAlgorithm algorithmVersion) {

    if (!siteName)
        return NULL;
    if (!spectre_realloc( &user->sites, NULL, SpectreMarshalledSite, ++user->sites_count )) {
        user->sites_count--;
        return NULL;
    }

    SpectreMarshalledSite *site = &user->sites[user->sites_count - 1];
    *site = (SpectreMarshalledSite){
            .siteName = spectre_strdup( siteName ),
            .algorithm = algorithmVersion,
            .counter = keyCounter,

            .resultType = resultType,
            .resultState = NULL,

            .loginType = SpectreResultNone,
            .loginState = NULL,

            .url = NULL,
            .uses = 0,
            .lastUsed = 0,

            .questions_count = 0,
            .questions = NULL,
    };
    return site;
}

SpectreMarshalledQuestion *spectre_marshal_question(
        SpectreMarshalledSite *site, const char *keyword) {

    if (!spectre_realloc( &site->questions, NULL, SpectreMarshalledQuestion, ++site->questions_count )) {
        site->questions_count--;
        return NULL;
    }
    if (!keyword)
        keyword = "";

    SpectreMarshalledQuestion *question = &site->questions[site->questions_count - 1];
    *question = (SpectreMarshalledQuestion){
            .keyword = spectre_strdup( keyword ),
            .type = SpectreResultTemplatePhrase,
            .state = NULL,
    };
    return question;
}

SpectreMarshalledFile *spectre_marshal_file(
        SpectreMarshalledFile *file, SpectreMarshalledInfo *info, SpectreMarshalledData *data) {

    if (!file) {
        if (!(file = malloc( sizeof( SpectreMarshalledFile ) )))
            return NULL;

        *file = (SpectreMarshalledFile){
                .info = NULL, .data = NULL, .error = (SpectreMarshalError){ .type = SpectreMarshalSuccess, .message = NULL }
        };
    }

    if (data && data != file->data) {
        spectre_marshal_free( &file->data );
        file->data = data;
    }
    if (info && info != file->info) {
        spectre_marshal_free( &file->info );
        file->info = info;
    }

    return file;
}

SpectreMarshalledFile *spectre_marshal_error(
        SpectreMarshalledFile *file, SpectreMarshalErrorType type, const char *format, ...) {

    file = spectre_marshal_file( file, NULL, NULL );
    if (!file)
        return NULL;

    va_list args;
    va_start( args, format );
    file->error = (SpectreMarshalError){ type, spectre_vstr( format, args ) };
    va_end( args );

    return file;
}

void spectre_marshal_info_free(
        SpectreMarshalledInfo **info) {

    if (!info || !*info)
        return;

    spectre_free_strings( &(*info)->userName, NULL );
    spectre_free( info, sizeof( SpectreMarshalledInfo ) );
}

void spectre_marshal_user_free(
        SpectreMarshalledUser **user) {

    if (!user || !*user)
        return;

    spectre_free_strings( &(*user)->userName, NULL );

    for (size_t s = 0; s < (*user)->sites_count; ++s) {
        SpectreMarshalledSite *site = &(*user)->sites[s];
        spectre_free_strings( &site->siteName, &site->resultState, &site->loginState, &site->url, NULL );

        for (size_t q = 0; q < site->questions_count; ++q) {
            SpectreMarshalledQuestion *question = &site->questions[q];
            spectre_free_strings( &question->keyword, &question->state, NULL );
        }
        spectre_free( &site->questions, sizeof( SpectreMarshalledQuestion ) * site->questions_count );
    }

    spectre_free( &(*user)->sites, sizeof( SpectreMarshalledSite ) * (*user)->sites_count );
    spectre_free( user, sizeof( SpectreMarshalledUser ) );
}

void spectre_marshal_data_free(
        SpectreMarshalledData **data) {

    if (!data || !*data)
        return;

    spectre_marshal_data_set_null( *data, NULL );
    spectre_free_string( &(*data)->obj_key );
    spectre_free( data, sizeof( SpectreMarshalledData ) );
}

void spectre_marshal_file_free(
        SpectreMarshalledFile **file) {

    if (!file || !*file)
        return;

    spectre_marshal_free( &(*file)->info );
    spectre_marshal_free( &(*file)->data );
    spectre_free_string( &(*file)->error.message );
    spectre_free( file, sizeof( SpectreMarshalledFile ) );
}

SpectreMarshalledData *spectre_marshal_data_new() {

    SpectreMarshalledData *data = malloc( sizeof( SpectreMarshalledData ) );
    *data = (SpectreMarshalledData){ 0 };
    spectre_marshal_data_set_null( data, NULL );
    data->is_null = false;
    return data;
}

SpectreMarshalledData *spectre_marshal_data_vget(
        SpectreMarshalledData *data, va_list nodes) {

    SpectreMarshalledData *parent = data, *child = parent;
    for (const char *node; parent && (node = va_arg( nodes, const char * )); parent = child) {
        child = NULL;

        for (size_t c = 0; c < parent->children_count; ++c) {
            const char *key = parent->children[c].obj_key;
            if (key && strcmp( node, key ) == OK) {
                child = &parent->children[c];
                break;
            }
        }

        if (!child) {
            if (!spectre_realloc( &parent->children, NULL, SpectreMarshalledData, ++parent->children_count )) {
                --parent->children_count;
                break;
            }
            *(child = &parent->children[parent->children_count - 1]) = (SpectreMarshalledData){ .obj_key = spectre_strdup( node ) };
            spectre_marshal_data_set_null( child, NULL );
            child->is_null = false;
        }
    }

    return child;
}

SpectreMarshalledData *spectre_marshal_data_get(
        SpectreMarshalledData *data, ...) {

    va_list nodes;
    va_start( nodes, data );
    SpectreMarshalledData *child = spectre_marshal_data_vget( data, nodes );
    va_end( nodes );

    return child;
}

const SpectreMarshalledData *spectre_marshal_data_vfind(
        const SpectreMarshalledData *data, va_list nodes) {

    const SpectreMarshalledData *parent = data, *child = parent;
    for (const char *node; parent && (node = va_arg( nodes, const char * )); parent = child) {
        child = NULL;

        for (size_t c = 0; c < parent->children_count; ++c) {
            const char *key = parent->children[c].obj_key;
            if (key && strcmp( node, key ) == OK) {
                child = &parent->children[c];
                break;
            }
        }

        if (!child)
            break;
    }

    return child;
}

const SpectreMarshalledData *spectre_marshal_data_find(
        const SpectreMarshalledData *data, ...) {

    va_list nodes;
    va_start( nodes, data );
    const SpectreMarshalledData *child = spectre_marshal_data_vfind( data, nodes );
    va_end( nodes );

    return child;
}

bool spectre_marshal_data_vis_null(
        const SpectreMarshalledData *data, va_list nodes) {

    const SpectreMarshalledData *child = spectre_marshal_data_vfind( data, nodes );
    return !child || child->is_null;
}

bool spectre_marshal_data_is_null(
        const SpectreMarshalledData *data, ...) {

    va_list nodes;
    va_start( nodes, data );
    bool value = spectre_marshal_data_vis_null( data, nodes );
    va_end( nodes );

    return value;
}

bool spectre_marshal_data_vset_null(
        SpectreMarshalledData *data, va_list nodes) {

    SpectreMarshalledData *child = spectre_marshal_data_vget( data, nodes );
    if (!child)
        return false;

    spectre_free_string( &child->str_value );
    for (unsigned int c = 0; c < child->children_count; ++c) {
        spectre_marshal_data_set_null( &child->children[c], NULL );
        spectre_free_string( &child->children[c].obj_key );
    }
    spectre_free( &child->children, sizeof( SpectreMarshalledData ) * child->children_count );
    child->children_count = 0;
    child->num_value = NAN;
    child->is_bool = false;
    child->is_null = true;
    return true;
}

bool spectre_marshal_data_set_null(
        SpectreMarshalledData *data, ...) {

    va_list nodes;
    va_start( nodes, data );
    bool success = spectre_marshal_data_vset_null( data, nodes );
    va_end( nodes );

    return success;
}

bool spectre_marshal_data_vget_bool(
        const SpectreMarshalledData *data, va_list nodes) {

    const SpectreMarshalledData *child = spectre_marshal_data_vfind( data, nodes );
    return child && child->is_bool && child->num_value != false;
}

bool spectre_marshal_data_get_bool(
        const SpectreMarshalledData *data, ...) {

    va_list nodes;
    va_start( nodes, data );
    bool value = spectre_marshal_data_vget_bool( data, nodes );
    va_end( nodes );

    return value;
}

bool spectre_marshal_data_vset_bool(
        const bool value, SpectreMarshalledData *data, va_list nodes) {

    SpectreMarshalledData *child = spectre_marshal_data_vget( data, nodes );
    if (!child || !spectre_marshal_data_set_null( child, NULL ))
        return false;

    child->is_null = false;
    child->is_bool = true;
    child->num_value = value != false;
    return true;
}

bool spectre_marshal_data_set_bool(
        const bool value, SpectreMarshalledData *data, ...) {

    va_list nodes;
    va_start( nodes, data );
    bool success = spectre_marshal_data_vset_bool( value, data, nodes );
    va_end( nodes );

    return success;
}

double spectre_marshal_data_vget_num(
        const SpectreMarshalledData *data, va_list nodes) {

    const SpectreMarshalledData *child = spectre_marshal_data_vfind( data, nodes );
    return child == NULL? NAN: child->num_value;
}

double spectre_marshal_data_get_num(
        const SpectreMarshalledData *data, ...) {

    va_list nodes;
    va_start( nodes, data );
    double value = spectre_marshal_data_vget_num( data, nodes );
    va_end( nodes );

    return value;
}

bool spectre_marshal_data_vset_num(
        const double value, SpectreMarshalledData *data, va_list nodes) {

    SpectreMarshalledData *child = spectre_marshal_data_vget( data, nodes );
    if (!child || !spectre_marshal_data_set_null( child, NULL ))
        return false;

    child->is_null = false;
    child->num_value = value;
    child->str_value = spectre_str( "%g", value );
    return true;
}

bool spectre_marshal_data_set_num(
        const double value, SpectreMarshalledData *data, ...) {

    va_list nodes;
    va_start( nodes, data );
    bool success = spectre_marshal_data_vset_num( value, data, nodes );
    va_end( nodes );

    return success;
}

const char *spectre_marshal_data_vget_str(
        const SpectreMarshalledData *data, va_list nodes) {

    const SpectreMarshalledData *child = spectre_marshal_data_vfind( data, nodes );
    return child == NULL? NULL: child->str_value;
}

const char *spectre_marshal_data_get_str(
        const SpectreMarshalledData *data, ...) {

    va_list nodes;
    va_start( nodes, data );
    const char *value = spectre_marshal_data_vget_str( data, nodes );
    va_end( nodes );

    return value;
}

bool spectre_marshal_data_vset_str(
        const char *value, SpectreMarshalledData *data, va_list nodes) {

    SpectreMarshalledData *child = spectre_marshal_data_vget( data, nodes );
    if (!child || !spectre_marshal_data_set_null( child, NULL ))
        return false;

    if (value) {
        child->is_null = false;
        child->str_value = spectre_strdup( value );
    }

    return true;
}

bool spectre_marshal_data_set_str(
        const char *value, SpectreMarshalledData *data, ...) {

    va_list nodes;
    va_start( nodes, data );
    bool success = spectre_marshal_data_vset_str( value, data, nodes );
    va_end( nodes );

    return success;
}

void spectre_marshal_data_filter(
        SpectreMarshalledData *data, bool (*filter)(SpectreMarshalledData *, void *), void *args) {

    size_t children_count = 0;
    SpectreMarshalledData *children = NULL;

    for (size_t c = 0; c < data->children_count; ++c) {
        SpectreMarshalledData *child = &data->children[c];
        if (filter( child, args )) {
            // Valid child in this object, keep it.
            ++children_count;

            if (children) {
                if (!spectre_realloc( &children, NULL, SpectreMarshalledData, children_count )) {
                    --children_count;
                    continue;
                }
                child->arr_index = children_count - 1;
                children[child->arr_index] = *child;
            }
        }
        else {
            // Not a valid child in this object, remove it.
            spectre_marshal_data_set_null( child, NULL );
            spectre_free_string( &child->obj_key );

            if (!children)
                children = spectre_memdup( data->children, sizeof( SpectreMarshalledData ) * children_count );
        }
    }

    if (children) {
        spectre_free( &data->children, sizeof( SpectreMarshalledData ) * data->children_count );
        data->children = children;
        data->children_count = children_count;
    }
}

bool spectre_marshal_data_filter_empty(
        __unused SpectreMarshalledData *child, __unused void *args) {

    return false;
}

static const char *spectre_marshal_write_flat(
        SpectreMarshalledFile *file) {

    const SpectreMarshalledData *data = file->data;
    if (!data) {
        spectre_marshal_error( file, SpectreMarshalErrorMissing,
                "Missing data." );
        return NULL;
    }

    char *out = NULL;
    spectre_string_pushf( &out, "# Spectre site export\n" );
    spectre_string_pushf( &out, spectre_marshal_data_get_bool( data, "export", "redacted", NULL )?
                                "#     Export of site names and stored passwords (unless device-private) encrypted with the user key.\n":
                                "#     Export of site names and passwords in clear-text.\n" );
    spectre_string_pushf( &out, "# \n" );
    spectre_string_pushf( &out, "##\n" );
    spectre_string_pushf( &out, "# Format: %d\n", 1 );

    const char *out_date = spectre_default( "", spectre_marshal_data_get_str( data, "export", "date", NULL ) );
    const char *out_fullName = spectre_default( "", spectre_marshal_data_get_str( data, "user", "full_name", NULL ) );
    unsigned int out_avatar = (unsigned int)spectre_marshal_data_get_num( data, "user", "avatar", NULL );
    const char *out_identicon = spectre_default( "", spectre_marshal_data_get_str( data, "user", "identicon", NULL ) );
    const char *out_keyID = spectre_default( "", spectre_marshal_data_get_str( data, "user", "key_id", NULL ) );
    SpectreAlgorithm out_algorithm = (SpectreAlgorithm)spectre_marshal_data_get_num( data, "user", "algorithm", NULL );
    SpectreResultType out_defaultType = (SpectreResultType)spectre_marshal_data_get_num( data, "user", "default_type", NULL );
    bool out_redacted = spectre_marshal_data_get_bool( data, "export", "redacted", NULL );

    spectre_string_pushf( &out, "# Date: %s\n", out_date );
    spectre_string_pushf( &out, "# User Name: %s\n", out_fullName );
    spectre_string_pushf( &out, "# Full Name: %s\n", out_fullName );
    spectre_string_pushf( &out, "# Avatar: %u\n", out_avatar );
    spectre_string_pushf( &out, "# Identicon: %s\n", out_identicon );
    spectre_string_pushf( &out, "# Key ID: %s\n", out_keyID );
    spectre_string_pushf( &out, "# Algorithm: %d\n", out_algorithm );
    spectre_string_pushf( &out, "# Default Type: %d\n", out_defaultType );
    spectre_string_pushf( &out, "# Passwords: %s\n", out_redacted? "PROTECTED": "VISIBLE" );
    spectre_string_pushf( &out, "##\n" );
    spectre_string_pushf( &out, "#\n" );
    spectre_string_pushf( &out, "#%19s  %8s  %8s  %25s\t%25s\t%s\n", "Last", "Times", "Password", "Login", "Site", "Site" );
    spectre_string_pushf( &out, "#%19s  %8s  %8s  %25s\t%25s\t%s\n", "used", "used", "type", "name", "name", "password" );

    // Sites.
    const char *typeString;
    const SpectreMarshalledData *sites = spectre_marshal_data_find( data, "sites", NULL );
    for (size_t s = 0; s < (sites? sites->children_count: 0); ++s) {
        const SpectreMarshalledData *site = &sites->children[s];
        spectre_string_pushf( &out, "%s  %8ld  %8s  %25s\t%25s\t%s\n",
                spectre_default( "", spectre_marshal_data_get_str( site, "last_used", NULL ) ),
                (long)spectre_marshal_data_get_num( site, "uses", NULL ),
                typeString = spectre_str( "%lu:%lu:%lu",
                        (long)spectre_marshal_data_get_num( site, "type", NULL ),
                        (long)spectre_marshal_data_get_num( site, "algorithm", NULL ),
                        (long)spectre_marshal_data_get_num( site, "counter", NULL ) ),
                spectre_default( "", spectre_marshal_data_get_str( site, "login_name", NULL ) ),
                site->obj_key,
                spectre_default( "", spectre_marshal_data_get_str( site, "password", NULL ) ) );
        spectre_free_string( &typeString );
    }

    if (!out)
        spectre_marshal_error( file, SpectreMarshalErrorFormat,
                "Couldn't encode JSON." );
    else
        spectre_marshal_error( file, SpectreMarshalSuccess, NULL );

    return out;
}

#if SPECTRE_JSON

static json_object *spectre_get_json_data(
        const SpectreMarshalledData *data) {

    if (!data || data->is_null)
        return NULL;
    if (data->is_bool)
        return json_object_new_boolean( data->num_value != false );
    if (!isnan( data->num_value )) {
        if (data->str_value)
            return json_object_new_double_s( data->num_value, data->str_value );
        else
            return json_object_new_double( data->num_value );
    }
    if (data->str_value)
        return json_object_new_string( data->str_value );

    json_object *obj = NULL;
    for (size_t c = 0; c < data->children_count; ++c) {
        SpectreMarshalledData *child = &data->children[c];
        if (!obj) {
            if (child->obj_key)
                obj = json_object_new_object();
            else
                obj = json_object_new_array();
        }

        json_object *child_obj = spectre_get_json_data( child );
        if (json_object_is_type( obj, json_type_array ))
            json_object_array_add( obj, child_obj );
        else if (child_obj && !(json_object_is_type( child_obj, json_type_object ) && json_object_object_length( child_obj ) == 0))
            // We omit keys that map to null or empty object values.
            json_object_object_add( obj, child->obj_key, child_obj );
        else
            json_object_put( child_obj );
    }

    return obj;
}

static const char *spectre_marshal_write_json(
        SpectreMarshalledFile *file) {

    json_object *json_file = spectre_get_json_data( file->data );
    if (!json_file) {
        spectre_marshal_error( file, SpectreMarshalErrorFormat,
                "Couldn't serialize export data." );
        return NULL;
    }

    json_object *json_export = spectre_get_json_object( json_file, "export", true );
    json_object_object_add( json_export, "format", json_object_new_int( 2 ) );

    const char *out = spectre_strdup( json_object_to_json_string_ext( json_file,
            JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_NOSLASHESCAPE ) );
    json_object_put( json_file );

    if (!out)
        spectre_marshal_error( file, SpectreMarshalErrorFormat,
                "Couldn't encode JSON." );
    else
        spectre_marshal_error( file, SpectreMarshalSuccess, NULL );

    return out;
}

#endif

static bool spectre_marshal_data_filter_site_exists(
        SpectreMarshalledData *child, void *args) {

    SpectreMarshalledUser *user = args;

    for (size_t s = 0; s < user->sites_count; ++s) {
        if (strcmp( (&user->sites[s])->siteName, child->obj_key ) == OK)
            return true;
    }

    return false;
}

static bool spectre_marshal_data_filter_question_exists(
        SpectreMarshalledData *child, void *args) {

    SpectreMarshalledSite *site = args;

    for (size_t s = 0; s < site->questions_count; ++s) {
        if (strcmp( (&site->questions[s])->keyword, child->obj_key ) == OK)
            return true;
    }

    return false;
}

const char *spectre_marshal_write(
        const SpectreFormat outFormat, SpectreMarshalledFile **file_, SpectreMarshalledUser *user) {

    SpectreMarshalledFile *file = file_? *file_: NULL;
    file = spectre_marshal_file( file, NULL, file && file->data? file->data: spectre_marshal_data_new() );
    if (file_)
        *file_ = file;
    if (!file)
        return NULL;
    if (!file->data) {
        if (!file_)
            spectre_marshal_free( &file );
        else
            spectre_marshal_error( file, SpectreMarshalErrorInternal,
                    "Couldn't allocate data." );
        return NULL;
    }
    spectre_marshal_error( file, SpectreMarshalSuccess, NULL );

    if (user) {
        if (!user->userName || !strlen( user->userName )) {
            if (!file_)
                spectre_marshal_free( &file );
            else
                spectre_marshal_error( file, SpectreMarshalErrorMissing,
                        "Missing user name." );
            return NULL;
        }

        const SpectreUserKey *userKey = NULL;
        if (user->userKeyProvider)
            userKey = user->userKeyProvider( user->algorithm, user->userName );

        // Section: "export"
        SpectreMarshalledData *data_export = spectre_marshal_data_get( file->data, "export", NULL );
        char dateString[21];
        time_t now = time( NULL );
        if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &now ) ))
            spectre_marshal_data_set_str( dateString, data_export, "date", NULL );
        spectre_marshal_data_set_bool( user->redacted, data_export, "redacted", NULL );

        // Section: "user"
        const char *loginState = NULL;
        if (!user->redacted) {
            // Clear Text
            spectre_free( &userKey, sizeof( *userKey ) );
            if (!user->userKeyProvider || !(userKey = user->userKeyProvider( user->algorithm, user->userName ))) {
                if (!file_)
                    spectre_marshal_free( &file );
                else
                    spectre_marshal_error( file, SpectreMarshalErrorInternal,
                            "Couldn't derive user key." );
                return NULL;
            }

            loginState = spectre_site_result( userKey, user->userName, user->loginType, user->loginState,
                    SpectreCounterInitial, SpectreKeyPurposeIdentification, NULL );
        }
        else {
            // Redacted
            if (user->loginType & SpectreResultFeatureExportContent && user->loginState && strlen( user->loginState ))
                loginState = spectre_strdup( user->loginState );
        }

        const char *identiconString = spectre_identicon_encode( user->identicon );
        SpectreMarshalledData *data_user = spectre_marshal_data_get( file->data, "user", NULL );
        spectre_marshal_data_set_num( user->avatar, data_user, "avatar", NULL );
        spectre_marshal_data_set_str( user->userName, data_user, "full_name", NULL );
        spectre_marshal_data_set_str( identiconString, data_user, "identicon", NULL );
        spectre_marshal_data_set_num( user->algorithm, data_user, "algorithm", NULL );
        spectre_marshal_data_set_str( user->keyID.hex, data_user, "key_id", NULL );
        spectre_marshal_data_set_num( user->defaultType, data_user, "default_type", NULL );
        spectre_marshal_data_set_num( user->loginType, data_user, "login_type", NULL );
        spectre_marshal_data_set_str( loginState, data_user, "login_name", NULL );
        if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &user->lastUsed ) ))
            spectre_marshal_data_set_str( dateString, data_user, "last_used", NULL );
        spectre_free_strings( &identiconString, &loginState, NULL );

        // Section "sites"
        SpectreMarshalledData *data_sites = spectre_marshal_data_get( file->data, "sites", NULL );
        spectre_marshal_data_filter( data_sites, spectre_marshal_data_filter_site_exists, user );
        for (size_t s = 0; s < user->sites_count; ++s) {
            SpectreMarshalledSite *site = &user->sites[s];
            if (!site->siteName || !strlen( site->siteName ))
                continue;

            const char *resultState = NULL;
            if (!user->redacted) {
                // Clear Text
                spectre_free( &userKey, sizeof( *userKey ) );
                if (!user->userKeyProvider || !(userKey = user->userKeyProvider( site->algorithm, user->userName ))) {
                    if (!file_)
                        spectre_marshal_free( &file );
                    else
                        spectre_marshal_error( file, SpectreMarshalErrorInternal,
                                "Couldn't derive user key." );
                    return NULL;
                }

                resultState = spectre_site_result( userKey, site->siteName,
                        site->resultType, site->resultState, site->counter, SpectreKeyPurposeAuthentication, NULL );
                loginState = spectre_site_result( userKey, site->siteName,
                        site->loginType, site->loginState, SpectreCounterInitial, SpectreKeyPurposeIdentification, NULL );
            }
            else {
                // Redacted
                if (site->resultType & SpectreResultFeatureExportContent && site->resultState && strlen( site->resultState ))
                    resultState = spectre_strdup( site->resultState );
                if (site->loginType & SpectreResultFeatureExportContent && site->loginState && strlen( site->loginState ))
                    loginState = spectre_strdup( site->loginState );
            }

            spectre_marshal_data_set_num( site->counter, data_sites, site->siteName, "counter", NULL );
            spectre_marshal_data_set_num( site->algorithm, data_sites, site->siteName, "algorithm", NULL );
            spectre_marshal_data_set_num( site->resultType, data_sites, site->siteName, "type", NULL );
            spectre_marshal_data_set_str( resultState, data_sites, site->siteName, "password", NULL );
            spectre_marshal_data_set_num( site->loginType, data_sites, site->siteName, "login_type", NULL );
            spectre_marshal_data_set_str( loginState, data_sites, site->siteName, "login_name", NULL );
            spectre_marshal_data_set_num( site->uses, data_sites, site->siteName, "uses", NULL );
            if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &site->lastUsed ) ))
                spectre_marshal_data_set_str( dateString, data_sites, site->siteName, "last_used", NULL );

            SpectreMarshalledData *data_questions = spectre_marshal_data_get( file->data, "sites", site->siteName, "questions", NULL );
            spectre_marshal_data_filter( data_questions, spectre_marshal_data_filter_question_exists, site );
            for (size_t q = 0; q < site->questions_count; ++q) {
                SpectreMarshalledQuestion *question = &site->questions[q];
                if (!question->keyword)
                    continue;

                const char *answer = NULL;
                if (!user->redacted) {
                    // Clear Text
                    answer = spectre_site_result( userKey, site->siteName,
                            question->type, question->state, SpectreCounterInitial, SpectreKeyPurposeRecovery, question->keyword );
                }
                else {
                    // Redacted
                    if (question->state && strlen( question->state ) && site->resultType & SpectreResultFeatureExportContent)
                        answer = spectre_strdup( question->state );
                }

                spectre_marshal_data_set_num( question->type, data_questions, question->keyword, "type", NULL );
                spectre_marshal_data_set_str( answer, data_questions, question->keyword, "answer", NULL );
                spectre_free_strings( &answer, NULL );
            }

            spectre_marshal_data_set_str( site->url, data_sites, site->siteName, "_ext_spectre", "url", NULL );
            spectre_free_strings( &resultState, &loginState, NULL );
        }
    }

    const char *out = NULL;
    switch (outFormat) {
        case SpectreFormatNone:
            spectre_marshal_error( file, SpectreMarshalSuccess, NULL );
            break;
        case SpectreFormatFlat:
            out = spectre_marshal_write_flat( file );
            break;
#if SPECTRE_JSON
        case SpectreFormatJSON:
            out = spectre_marshal_write_json( file );
            break;
#endif
        default:
            spectre_marshal_error( file, SpectreMarshalErrorFormat,
                    "Unsupported output format: %u", outFormat );
            break;
    }
    if (out && file->error.type == SpectreMarshalSuccess)
        file = spectre_marshal_read( file, out );
    if (file_)
        *file_ = file;
    else
        spectre_marshal_free( &file );

    return out;
}

static void spectre_marshal_read_flat(
        SpectreMarshalledFile *file, const char *in) {

    if (!file)
        return;

    spectre_marshal_file( file, NULL, spectre_marshal_data_new() );
    if (!file->data) {
        spectre_marshal_error( file, SpectreMarshalErrorInternal,
                "Couldn't allocate data." );
        return;
    }

    // Parse import data.
    unsigned int format = 0, avatar = 0;
    const char *userName = NULL, *keyID = NULL;
    SpectreAlgorithm algorithm = SpectreAlgorithmCurrent;
    SpectreIdenticon identicon = SpectreIdenticonUnset;
    SpectreResultType defaultType = SpectreResultDefaultResult;
    time_t exportDate = 0;
    bool headerStarted = false, headerEnded = false, importRedacted = false;
    for (const char *endOfLine, *positionInLine = in; (endOfLine = strstr( positionInLine, "\n" )); positionInLine = endOfLine + 1) {

        // Comment or header
        if (*positionInLine == '#') {
            ++positionInLine;

            if (!headerStarted) {
                if (*positionInLine == '#')
                    // ## starts header
                    headerStarted = true;
                // Comment before header
                continue;
            }
            if (headerEnded)
                // Comment after header
                continue;
            if (*positionInLine == '#') {
                // ## ends header
                headerEnded = true;

                char dateString[21];
                const char *identiconString = spectre_identicon_encode( identicon );

                if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &exportDate ) )) {
                    spectre_marshal_data_set_str( dateString, file->data, "export", "date", NULL );
                    spectre_marshal_data_set_str( dateString, file->data, "user", "last_used", NULL );
                }
                spectre_marshal_data_set_num( algorithm, file->data, "user", "algorithm", NULL );
                spectre_marshal_data_set_bool( importRedacted, file->data, "export", "redacted", NULL );
                spectre_marshal_data_set_num( avatar, file->data, "user", "avatar", NULL );
                spectre_marshal_data_set_str( userName, file->data, "user", "full_name", NULL );
                spectre_marshal_data_set_str( identiconString, file->data, "user", "identicon", NULL );
                spectre_marshal_data_set_str( keyID, file->data, "user", "key_id", NULL );
                spectre_marshal_data_set_num( defaultType, file->data, "user", "default_type", NULL );
                spectre_free_string( &identiconString );
                continue;
            }

            // Header
            const char *line = positionInLine;
            const char *headerName = spectre_get_token( &positionInLine, endOfLine, ":\n" );
            const char *headerValue = spectre_get_token( &positionInLine, endOfLine, "\n" );
            if (!headerName || !headerValue) {
                spectre_marshal_error( file, SpectreMarshalErrorStructure,
                        "Invalid header: %s", spectre_strndup( line, (size_t)(endOfLine - line) ) );
                spectre_free_strings( &headerName, &headerValue, NULL );
                continue;
            }

            if (spectre_strcasecmp( headerName, "Format" ) == OK)
                format = (unsigned int)strtoul( headerValue, NULL, 10 );
            if (spectre_strcasecmp( headerName, "Date" ) == OK)
                exportDate = spectre_get_timegm( headerValue );
            if (spectre_strcasecmp( headerName, "Passwords" ) == OK)
                importRedacted = spectre_strcasecmp( headerValue, "VISIBLE" ) != OK;
            if (spectre_strcasecmp( headerName, "Algorithm" ) == OK) {
                unsigned long value = strtoul( headerValue, NULL, 10 );
                if (value < SpectreAlgorithmFirst || value > SpectreAlgorithmLast)
                    spectre_marshal_error( file, SpectreMarshalErrorIllegal,
                            "Invalid user algorithm version: %s", headerValue );
                else
                    algorithm = (SpectreAlgorithm)value;
            }
            if (spectre_strcasecmp( headerName, "Avatar" ) == OK)
                avatar = (unsigned int)strtoul( headerValue, NULL, 10 );
            if (spectre_strcasecmp( headerName, "Full Name" ) == OK || spectre_strcasecmp( headerName, "User Name" ) == OK)
                userName = spectre_strdup( headerValue );
            if (spectre_strcasecmp( headerName, "Identicon" ) == OK)
                identicon = spectre_identicon_encoded( headerValue );
            if (spectre_strcasecmp( headerName, "Key ID" ) == OK)
                keyID = spectre_strdup( headerValue );
            if (spectre_strcasecmp( headerName, "Default Type" ) == OK) {
                unsigned long value = strtoul( headerValue, NULL, 10 );
                if (!spectre_type_short_name( (SpectreResultType)value ))
                    spectre_marshal_error( file, SpectreMarshalErrorIllegal,
                            "Invalid user default type: %s", headerValue );
                else
                    defaultType = (SpectreResultType)value;
            }

            spectre_free_strings( &headerName, &headerValue, NULL );
            continue;
        }
        if (!headerEnded)
            continue;
        if (!userName)
            spectre_marshal_error( file, SpectreMarshalErrorMissing,
                    "Missing header: Full Name" );
        if (positionInLine >= endOfLine)
            continue;

        // Site
        const char *siteName = NULL, *siteResultState = NULL, *siteLoginState = NULL;
        const char *str_lastUsed = NULL, *str_uses = NULL, *str_type = NULL, *str_algorithm = NULL, *str_counter = NULL;
        switch (format) {
            case 0: {
                str_lastUsed = spectre_get_token( &positionInLine, endOfLine, " \t\n" );
                str_uses = spectre_get_token( &positionInLine, endOfLine, " \t\n" );
                char *typeAndVersion = (char *)spectre_get_token( &positionInLine, endOfLine, " \t\n" );
                if (typeAndVersion) {
                    str_type = spectre_strdup( strtok( typeAndVersion, ":" ) );
                    str_algorithm = spectre_strdup( strtok( NULL, "" ) );
                    spectre_free_string( &typeAndVersion );
                }
                str_counter = spectre_str( "%u", SpectreCounterDefault );
                siteLoginState = NULL;
                siteName = spectre_get_token( &positionInLine, endOfLine, "\t\n" );
                siteResultState = spectre_get_token( &positionInLine, endOfLine, "\n" );
                break;
            }
            case 1: {
                str_lastUsed = spectre_get_token( &positionInLine, endOfLine, " \t\n" );
                str_uses = spectre_get_token( &positionInLine, endOfLine, " \t\n" );
                char *typeAndVersionAndCounter = (char *)spectre_get_token( &positionInLine, endOfLine, " \t\n" );
                if (typeAndVersionAndCounter) {
                    str_type = spectre_strdup( strtok( typeAndVersionAndCounter, ":" ) );
                    str_algorithm = spectre_strdup( strtok( NULL, ":" ) );
                    str_counter = spectre_strdup( strtok( NULL, "" ) );
                    spectre_free_string( &typeAndVersionAndCounter );
                }
                siteLoginState = spectre_get_token( &positionInLine, endOfLine, "\t\n" );
                siteName = spectre_get_token( &positionInLine, endOfLine, "\t\n" );
                siteResultState = spectre_get_token( &positionInLine, endOfLine, "\n" );
                break;
            }
            default: {
                spectre_marshal_error( file, SpectreMarshalErrorFormat,
                        "Unexpected import format: %u", format );
                continue;
            }
        }

        if (siteName && str_type && str_counter && str_algorithm && str_uses && str_lastUsed) {
            SpectreResultType siteResultType = (SpectreResultType)strtoul( str_type, NULL, 10 );
            if (!spectre_type_short_name( siteResultType )) {
                spectre_marshal_error( file, SpectreMarshalErrorIllegal,
                        "Invalid site type: %s: %s", siteName, str_type );
                continue;
            }
            long long int value = strtoll( str_counter, NULL, 10 );
            if (value < SpectreCounterFirst || value > SpectreCounterLast) {
                spectre_marshal_error( file, SpectreMarshalErrorIllegal,
                        "Invalid site counter: %s: %s", siteName, str_counter );
                continue;
            }
            SpectreCounter siteKeyCounter = (SpectreCounter)value;
            value = strtoll( str_algorithm, NULL, 0 );
            if (value < SpectreAlgorithmFirst || value > SpectreAlgorithmLast) {
                spectre_marshal_error( file, SpectreMarshalErrorIllegal,
                        "Invalid site algorithm: %s: %s", siteName, str_algorithm );
                continue;
            }
            SpectreAlgorithm siteAlgorithm = (SpectreAlgorithm)value;
            time_t siteLastUsed = spectre_get_timegm( str_lastUsed );
            if (!siteLastUsed) {
                spectre_marshal_error( file, SpectreMarshalErrorIllegal,
                        "Invalid site last used: %s: %s", siteName, str_lastUsed );
                continue;
            }
            SpectreResultType siteLoginType = siteLoginState && *siteLoginState? SpectreResultStatePersonal: SpectreResultNone;

            char dateString[21];
            spectre_marshal_data_set_num( siteAlgorithm, file->data, "sites", siteName, "algorithm", NULL );
            spectre_marshal_data_set_num( siteKeyCounter, file->data, "sites", siteName, "counter", NULL );
            spectre_marshal_data_set_num( siteResultType, file->data, "sites", siteName, "type", NULL );
            spectre_marshal_data_set_str( siteResultState, file->data, "sites", siteName, "password", NULL );
            spectre_marshal_data_set_num( siteLoginType, file->data, "sites", siteName, "login_type", NULL );
            spectre_marshal_data_set_str( siteLoginState, file->data, "sites", siteName, "login_name", NULL );
            spectre_marshal_data_set_num( strtol( str_uses, NULL, 10 ), file->data, "sites", siteName, "uses", NULL );
            if (strftime( dateString, sizeof( dateString ), "%FT%TZ", gmtime( &siteLastUsed ) ))
                spectre_marshal_data_set_str( dateString, file->data, "sites", siteName, "last_used", NULL );
        }
        else {
            spectre_marshal_error( file, SpectreMarshalErrorMissing,
                    "Missing one of: lastUsed=%s, uses=%s, type=%s, version=%s, counter=%s, loginName=%s, siteName=%s",
                    str_lastUsed, str_uses, str_type, str_algorithm, str_counter, siteLoginState, siteName );
            continue;
        }

        spectre_free_strings( &str_lastUsed, &str_uses, &str_type, &str_algorithm, &str_counter, NULL );
        spectre_free_strings( &siteLoginState, &siteName, &siteResultState, NULL );
    }
    spectre_free_strings( &userName, &keyID, NULL );
}

#if SPECTRE_JSON

static void spectre_marshal_read_json(
        SpectreMarshalledFile *file, const char *in) {

    if (!file)
        return;

    spectre_marshal_file( file, NULL, spectre_marshal_data_new() );
    if (!file->data) {
        spectre_marshal_error( file, SpectreMarshalErrorInternal,
                "Couldn't allocate data." );
        return;
    }

    // Parse import data.
    enum json_tokener_error json_error = json_tokener_success;
    json_object *json_file = json_tokener_parse_verbose( in, &json_error );
    if (!json_file || json_error != json_tokener_success) {
        spectre_marshal_error( file, SpectreMarshalErrorFormat,
                "Couldn't parse JSON: %s", json_tokener_error_desc( json_error ) );
        return;
    }

    spectre_set_json_data( file->data, json_file );
    json_object_put( json_file );

    // version 1 fixes:
    if (spectre_marshal_data_get_num( file->data, "export", "format", NULL ) == 1) {
        SpectreMarshalledData *sites = (SpectreMarshalledData *)spectre_marshal_data_find( file->data, "sites", NULL );

        // - default login_type "name" written to file, preventing adoption of user-level standard login_type.
        for (size_t s = 0; s < (sites? sites->children_count: 0); ++s) {
            SpectreMarshalledData *site = &sites->children[s];
            if (spectre_marshal_data_get_num( site, "login_type", NULL ) == SpectreResultTemplateName)
                spectre_marshal_data_set_null( site, "login_type", NULL );
        }
    }

    return;
}

#endif

SpectreMarshalledFile *spectre_marshal_read(
        SpectreMarshalledFile *file, const char *in) {

    SpectreMarshalledInfo *info = malloc( sizeof( SpectreMarshalledInfo ) );
    file = spectre_marshal_file( file, info, NULL );
    if (!file)
        return NULL;

    spectre_marshal_error( file, SpectreMarshalSuccess, NULL );
    if (!info) {
        spectre_marshal_error( file, SpectreMarshalErrorInternal,
                "Couldn't allocate info." );
        return file;
    }

    *info = (SpectreMarshalledInfo){ .format = SpectreFormatNone, .identicon = SpectreIdenticonUnset };
    if (in && strlen( in )) {
        if (in[0] == '#') {
            info->format = SpectreFormatFlat;
            spectre_marshal_read_flat( file, in );
        }
        else if (in[0] == '{') {
            info->format = SpectreFormatJSON;
#if SPECTRE_JSON
            spectre_marshal_read_json( file, in );
#else
            spectre_marshal_error( file, SpectreMarshalErrorFormat,
                    "JSON support is not enabled." );
#endif
        }
    }

    // Section: "export"
    info->exportDate = spectre_get_timegm( spectre_marshal_data_get_str( file->data, "export", "date", NULL ) );
    info->redacted = spectre_marshal_data_get_bool( file->data, "export", "redacted", NULL )
                     || spectre_marshal_data_is_null( file->data, "export", "redacted", NULL );

    // Section: "user"
    info->algorithm = spectre_default_num( SpectreAlgorithmCurrent, spectre_marshal_data_get_num( file->data, "user", "algorithm", NULL ) );
    info->avatar = spectre_default_num( 0U, spectre_marshal_data_get_num( file->data, "user", "avatar", NULL ) );
    info->userName = spectre_strdup( spectre_marshal_data_get_str( file->data, "user", "full_name", NULL ) );
    info->identicon = spectre_identicon_encoded( spectre_marshal_data_get_str( file->data, "user", "identicon", NULL ) );
    info->keyID = spectre_id_str( spectre_marshal_data_get_str( file->data, "user", "key_id", NULL ) );
    info->lastUsed = spectre_get_timegm( spectre_marshal_data_get_str( file->data, "user", "last_used", NULL ) );

    return file;
}

SpectreMarshalledUser *spectre_marshal_auth(
        SpectreMarshalledFile *file, const SpectreKeyProvider userKeyProvider) {

    if (!file)
        return NULL;

    spectre_marshal_error( file, SpectreMarshalSuccess, NULL );
    if (!file->info) {
        spectre_marshal_error( file, SpectreMarshalErrorMissing,
                "File wasn't parsed yet." );
        return NULL;
    }
    if (!file->data) {
        spectre_marshal_error( file, SpectreMarshalErrorMissing,
                "No input data." );
        return NULL;
    }
    const SpectreMarshalledData *userData = spectre_marshal_data_find( file->data, "user", NULL );
    if (!userData) {
        spectre_marshal_error( file, SpectreMarshalErrorMissing,
                "Missing user data." );
        return NULL;
    }

    // Section: "user"
    bool fileRedacted = spectre_marshal_data_get_bool( file->data, "export", "redacted", NULL )
                        || spectre_marshal_data_is_null( file->data, "export", "redacted", NULL );

    SpectreAlgorithm algorithm = spectre_default_num( SpectreAlgorithmCurrent,
            spectre_marshal_data_get_num( userData, "algorithm", NULL ) );
    if (algorithm < SpectreAlgorithmFirst || algorithm > SpectreAlgorithmLast) {
        spectre_marshal_error( file, SpectreMarshalErrorIllegal,
                "Invalid user algorithm: %u", algorithm );
        return NULL;
    }

    unsigned int avatar = spectre_default_num( 0U,
            spectre_marshal_data_get_num( userData, "avatar", NULL ) );

    const char *userName = spectre_marshal_data_get_str( userData, "full_name", NULL );
    if (!userName || !strlen( userName )) {
        spectre_marshal_error( file, SpectreMarshalErrorMissing,
                "Missing value for user name." );
        return NULL;
    }

    SpectreIdenticon identicon = spectre_identicon_encoded( spectre_marshal_data_get_str( userData, "identicon", NULL ) );

    SpectreKeyID keyID = spectre_id_str( spectre_marshal_data_get_str( userData, "key_id", NULL ) );

    SpectreResultType defaultType = spectre_default_num( SpectreResultDefaultResult,
            spectre_marshal_data_get_num( userData, "default_type", NULL ) );
    if (!spectre_type_short_name( defaultType )) {
        spectre_marshal_error( file, SpectreMarshalErrorIllegal,
                "Invalid user default type: %u", defaultType );
        return NULL;
    }

    SpectreResultType loginType = spectre_default_num( SpectreResultDefaultLogin,
            spectre_marshal_data_get_num( userData, "login_type", NULL ) );
    if (!spectre_type_short_name( loginType )) {
        spectre_marshal_error( file, SpectreMarshalErrorIllegal,
                "Invalid user login type: %u", loginType );
        return NULL;
    }

    const char *loginState = spectre_marshal_data_get_str( userData, "login_name", NULL );

    const char *str_lastUsed = spectre_marshal_data_get_str( userData, "last_used", NULL );

    time_t lastUsed = spectre_get_timegm( str_lastUsed );
    if (!lastUsed) {
        spectre_marshal_error( file, SpectreMarshalErrorIllegal,
                "Invalid user last used: %s", str_lastUsed );
        return NULL;
    }

    const SpectreUserKey *userKey = NULL;
    if (userKeyProvider && !(userKey = userKeyProvider( algorithm, userName ))) {
        spectre_marshal_error( file, SpectreMarshalErrorInternal,
                "Couldn't derive user key." );
        return NULL;
    }
    if (userKey && !spectre_id_equals( &keyID, &userKey->keyID )) {
        spectre_marshal_error( file, SpectreMarshalErrorUserSecret,
                "User key: %s, doesn't match keyID: %s.", userKey->keyID.hex, keyID.hex );
        spectre_free( &userKey, sizeof( *userKey ) );
        return NULL;
    }

    SpectreMarshalledUser *user = NULL;
    if (!(user = spectre_marshal_user( userName, userKeyProvider, algorithm ))) {
        spectre_marshal_error( file, SpectreMarshalErrorInternal,
                "Couldn't allocate a new user." );
        spectre_free( &userKey, sizeof( *userKey ) );
        spectre_marshal_free( &user );
        return NULL;
    }

    user->redacted = fileRedacted;
    user->avatar = avatar;
    user->identicon = identicon;
    user->keyID = keyID;
    user->defaultType = defaultType;
    user->loginType = loginType;
    user->lastUsed = lastUsed;

    if (!user->redacted) {
        // Clear Text
        spectre_free( &userKey, sizeof( *userKey ) );
        if (!userKeyProvider || !(userKey = userKeyProvider( user->algorithm, user->userName ))) {
            spectre_marshal_error( file, SpectreMarshalErrorInternal,
                    "Couldn't derive user key." );
            spectre_free( &userKey, sizeof( *userKey ) );
            spectre_marshal_free( &user );
            return NULL;
        }

        if (loginState && strlen( loginState ) && userKey)
            user->loginState = spectre_site_state( userKey, user->userName, user->loginType, loginState,
                    SpectreCounterInitial, SpectreKeyPurposeIdentification, NULL );
    }
    else {
        // Redacted
        if (loginState && strlen( loginState ))
            user->loginState = spectre_strdup( loginState );
    }

    // Section "sites"
    const SpectreMarshalledData *sitesData = spectre_marshal_data_find( file->data, "sites", NULL );
    for (size_t s = 0; s < (sitesData? sitesData->children_count: 0); ++s) {
        const SpectreMarshalledData *siteData = &sitesData->children[s];
        const char *siteName = siteData->obj_key;

        algorithm = spectre_default_num( user->algorithm,
                spectre_marshal_data_get_num( siteData, "algorithm", NULL ) );
        if (algorithm < SpectreAlgorithmFirst || algorithm > SpectreAlgorithmLast) {
            spectre_marshal_error( file, SpectreMarshalErrorIllegal,
                    "Invalid site algorithm: %s: %u", siteName, algorithm );
            spectre_free( &userKey, sizeof( *userKey ) );
            spectre_marshal_free( &user );
            return NULL;
        }
        SpectreCounter siteCounter = spectre_default_num( SpectreCounterDefault,
                spectre_marshal_data_get_num( siteData, "counter", NULL ) );
        if (siteCounter < SpectreCounterFirst || siteCounter > SpectreCounterLast) {
            spectre_marshal_error( file, SpectreMarshalErrorIllegal,
                    "Invalid site result counter: %s: %d", siteName, siteCounter );
            spectre_free( &userKey, sizeof( *userKey ) );
            spectre_marshal_free( &user );
            return NULL;
        }
        SpectreResultType siteResultType = spectre_default_num( user->defaultType,
                spectre_marshal_data_get_num( siteData, "type", NULL ) );
        if (!spectre_type_short_name( siteResultType )) {
            spectre_marshal_error( file, SpectreMarshalErrorIllegal,
                    "Invalid site result type: %s: %u", siteName, siteResultType );
            spectre_free( &userKey, sizeof( *userKey ) );
            spectre_marshal_free( &user );
            return NULL;
        }
        const char *siteResultState = spectre_marshal_data_get_str( siteData, "password", NULL );
        SpectreResultType siteLoginType = spectre_default_num( SpectreResultNone,
                spectre_marshal_data_get_num( siteData, "login_type", NULL ) );
        if (!spectre_type_short_name( siteLoginType )) {
            spectre_marshal_error( file, SpectreMarshalErrorIllegal,
                    "Invalid site login type: %s: %u", siteName, siteLoginType );
            spectre_free( &userKey, sizeof( *userKey ) );
            spectre_marshal_free( &user );
            return NULL;
        }
        const char *siteLoginState = spectre_marshal_data_get_str( siteData, "login_name", NULL );
        unsigned int siteUses = spectre_default_num( 0U,
                spectre_marshal_data_get_num( siteData, "uses", NULL ) );
        str_lastUsed = spectre_marshal_data_get_str( siteData, "last_used", NULL );
        time_t siteLastUsed = spectre_get_timegm( str_lastUsed );
        if (!siteLastUsed) {
            spectre_marshal_error( file, SpectreMarshalErrorIllegal,
                    "Invalid site last used: %s: %s", siteName, str_lastUsed );
            spectre_free( &userKey, sizeof( *userKey ) );
            spectre_marshal_free( &user );
            return NULL;
        }

        const char *siteURL = spectre_marshal_data_get_str( siteData, "_ext_spectre", "url", NULL );

        SpectreMarshalledSite *site = spectre_marshal_site( user, siteName, siteResultType, siteCounter, algorithm );
        if (!site) {
            spectre_marshal_error( file, SpectreMarshalErrorInternal,
                    "Couldn't allocate a new site." );
            spectre_free( &userKey, sizeof( *userKey ) );
            spectre_marshal_free( &user );
            return NULL;
        }

        site->loginType = siteLoginType;
        site->url = siteURL? spectre_strdup( siteURL ): NULL;
        site->uses = siteUses;
        site->lastUsed = siteLastUsed;
        if (!user->redacted) {
            // Clear Text
            spectre_free( &userKey, sizeof( *userKey ) );
            if (!userKeyProvider || !(userKey = userKeyProvider( site->algorithm, user->userName ))) {
                spectre_marshal_error( file, SpectreMarshalErrorInternal,
                        "Couldn't derive user key." );
                spectre_free( &userKey, sizeof( *userKey ) );
                spectre_marshal_free( &user );
                return NULL;
            }

            if (siteResultState && strlen( siteResultState ) && userKey)
                site->resultState = spectre_site_state( userKey, site->siteName,
                        site->resultType, siteResultState, site->counter, SpectreKeyPurposeAuthentication, NULL );
            if (siteLoginState && strlen( siteLoginState ) && userKey)
                site->loginState = spectre_site_state( userKey, site->siteName,
                        site->loginType, siteLoginState, SpectreCounterInitial, SpectreKeyPurposeIdentification, NULL );
        }
        else {
            // Redacted
            if (siteResultState && strlen( siteResultState ))
                site->resultState = spectre_strdup( siteResultState );
            if (siteLoginState && strlen( siteLoginState ))
                site->loginState = spectre_strdup( siteLoginState );
        }

        const SpectreMarshalledData *questions = spectre_marshal_data_find( siteData, "questions", NULL );
        for (size_t q = 0; q < (questions? questions->children_count: 0); ++q) {
            const SpectreMarshalledData *questionData = &questions->children[q];
            SpectreMarshalledQuestion *question = spectre_marshal_question( site, questionData->obj_key );
            const char *answerState = spectre_marshal_data_get_str( questionData, "answer", NULL );
            question->type = spectre_default_num( SpectreResultTemplatePhrase,
                    spectre_marshal_data_get_num( questionData, "type", NULL ) );

            if (!user->redacted) {
                // Clear Text
                if (answerState && strlen( answerState ) && userKey)
                    question->state = spectre_site_state( userKey, site->siteName,
                            question->type, answerState, SpectreCounterInitial, SpectreKeyPurposeRecovery, question->keyword );
            }
            else {
                // Redacted
                if (answerState && strlen( answerState ))
                    question->state = spectre_strdup( answerState );
            }
        }
    }
    spectre_free( &userKey, sizeof( *userKey ) );

    return user;
}

const SpectreFormat spectre_format_named(
        const char *formatName) {

    if (!formatName || !strlen( formatName ))
        return SpectreFormatNone;

    if (spectre_strncasecmp( spectre_format_name( SpectreFormatNone ), formatName, strlen( formatName ) ) == OK)
        return SpectreFormatNone;
    if (spectre_strncasecmp( spectre_format_name( SpectreFormatFlat ), formatName, strlen( formatName ) ) == OK)
        return SpectreFormatFlat;
    if (spectre_strncasecmp( spectre_format_name( SpectreFormatJSON ), formatName, strlen( formatName ) ) == OK)
        return SpectreFormatJSON;

    wrn( "Not a format name: %s", formatName );
    return (SpectreFormat)ERR;
}

const char *spectre_format_name(
        const SpectreFormat format) {

    switch (format) {
        case SpectreFormatNone:
            return "none";
        case SpectreFormatFlat:
            return "flat";
        case SpectreFormatJSON:
            return "json";
        default: {
            wrn( "Unknown format: %d", format );
            return NULL;
        }
    }
}

const char *spectre_format_extension(
        const SpectreFormat format) {

    switch (format) {
        case SpectreFormatNone:
            return NULL;
        case SpectreFormatFlat:
            return "mpsites";
        case SpectreFormatJSON:
            return "mpjson";
        default: {
            wrn( "Unknown format: %d", format );
            return NULL;
        }
    }
}

const char **spectre_format_extensions(
        const SpectreFormat format, size_t *count) {

    *count = 0;
    switch (format) {
        case SpectreFormatNone:
            return NULL;
        case SpectreFormatFlat:
            return spectre_strings( count,
                    spectre_format_extension( format ), "mpsites.txt", "txt", NULL );
        case SpectreFormatJSON:
            return spectre_strings( count,
                    spectre_format_extension( format ), "mpsites.json", "json", NULL );
        default: {
            wrn( "Unknown format: %d", format );
            return NULL;
        }
    }
}
0707010000001D000081A400000000000000000000000163C2D55500004A47000000000000000000000000000000000000003C00000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-marshal.h// =============================================================================
// Created by Maarten Billemont on 2017-07-15.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#ifndef _SPECTRE_MARSHAL_H
#define _SPECTRE_MARSHAL_H

#include "spectre-algorithm.h"

SPECTRE_LIBS_BEGIN
#include <time.h>
#include <stdarg.h>
SPECTRE_LIBS_END

//// Types.

typedef spectre_enum( unsigned int, SpectreFormat ) {
    /** Do not marshal. */
    SpectreFormatNone,
    /** Marshal using the line-based plain-text format. */
    SpectreFormatFlat,
    /** Marshal using the JSON structured format. */
    SpectreFormatJSON,

#if SPECTRE_JSON
    SpectreFormatDefault = SpectreFormatJSON,
#else
    SpectreFormatDefault = SpectreFormatFlat,
#endif
    SpectreFormatFirst = SpectreFormatFlat,
    SpectreFormatLast = SpectreFormatJSON,
};

typedef spectre_enum( unsigned int, SpectreMarshalErrorType ) {
    /** The marshalling operation completed successfully. */
    SpectreMarshalSuccess,
    /** An error in the structure of the marshall file interrupted marshalling. */
    SpectreMarshalErrorStructure,
    /** The marshall file uses an unsupported format version. */
    SpectreMarshalErrorFormat,
    /** A required value is missing or not specified. */
    SpectreMarshalErrorMissing,
    /** The given user secret is not valid. */
    SpectreMarshalErrorUserSecret,
    /** An illegal value was specified. */
    SpectreMarshalErrorIllegal,
    /** An internal system error interrupted marshalling. */
    SpectreMarshalErrorInternal,
};

typedef struct SpectreMarshalError {
    /** The status of the most recent processing operation. */
    SpectreMarshalErrorType type;
    /** An explanation of the situation that caused the current status type. */
    const char *message;
} SpectreMarshalError;

/** A function that can resolve a user key of the given algorithm for the user with the given name.
 * @return A user key (allocated), or NULL if the key could not be resolved. */
typedef const SpectreUserKey *(*SpectreKeyProvider)(
        SpectreAlgorithm algorithm, const char *userName);
/** A function that updates the currentKey with the userKey of the given algorithm for the user with the given name.
 * @param currentKey A pointer to where the current userKey (allocated) can be found and a new one can be placed.
 *                   Free the old value if you update it. If NULL, the proxy is invalidated and should free any state it holds.
 * @param currentAlgorithm A pointer to where the algorithm of the current userKey is found and can be updated.
 * @param algorithm The algorithm of the userKey that should be placed in currentKey.
 * @param userName The name of the user whose userKey should be placed in currentKey.
 * @return false if not able to resolve the requested userKey. */
typedef bool (*SpectreKeyProviderProxy)(
        const SpectreUserKey **currentKey, SpectreAlgorithm *currentAlgorithm, SpectreAlgorithm algorithm, const char *userName);

/** Create a key provider which handles key generation by proxying the given function.
 * The proxy function receives the currently cached key and its algorithm.  If those are NULL, the proxy function should clean up its state. */
SpectreKeyProvider spectre_proxy_provider_set(
        const SpectreKeyProviderProxy proxy);
/** Create a key provider that computes a user key for the given user secret. */
SpectreKeyProvider spectre_proxy_provider_set_secret(
        const char *userSecret);

/** Unset the active proxy and free the proxy provider. */
void spectre_proxy_provider_unset(void);

/** Free the key provider's internal state. */
void spectre_key_provider_free(
        SpectreKeyProvider keyProvider);

typedef struct SpectreMarshalledData {
    /** If the parent is an object, this holds the key by which this data value is referenced. */
    const char *obj_key;
    /** If the parent is an array, this holds the index at which this data value is referenced. */
    size_t arr_index;

    /** Whether this data value represents a null value (true). */
    bool is_null;
    /** Whether this data value represents a boolean value (true). */
    bool is_bool;
    /** The textual value of this data if it holds a C-string. */
    const char *str_value;
    /** The numerical value of this data if it holds a number or a boolean. */
    double num_value;

    /** Amount of data values references under this value if it represents an object or an array. */
    size_t children_count;
    /** Array of data values referenced under this value. */
    struct SpectreMarshalledData *children;
} SpectreMarshalledData;

typedef struct SpectreMarshalledInfo {
    /** The data format used for serializing the file and user data into a byte stream. */
    SpectreFormat format;
    /** Date of when the file was previously serialized. */
    time_t exportDate;
    /** Whether secrets and state should be visible in clear-text (false) when serialized. */
    bool redacted;

    /** Algorithm version to use for user operations (eg. key ID operations). */
    SpectreAlgorithm algorithm;
    /** A number identifying the avatar to display for the user in this file. */
    unsigned int avatar;
    /** Unique name for this file's user, preferably the user's full legal name. */
    const char *userName;
    /** User metadata: The identicon that was generated to represent this file's user identity. */
    SpectreIdenticon identicon;
    /** A unique identifier (hex) for the user key, primarily for authentication/verification. */
    SpectreKeyID keyID;
    /** User metadata: Date of the most recent action taken by this user. */
    time_t lastUsed;
} SpectreMarshalledInfo;

typedef struct SpectreMarshalledQuestion {
    /** Unique name for the security question, preferably a single key word from the question sentence. */
    const char *keyword;
    /** The result type to use for generating an answer. */
    SpectreResultType type;
    /** State data (base64), if any, necessary for generating the question's answer. */
    const char *state;
} SpectreMarshalledQuestion;

typedef struct SpectreMarshalledSite {
    /** Unique name for this site. */
    const char *siteName;
    /** Algorithm version to use for all site operations (eg. result, login, question operations). */
    SpectreAlgorithm algorithm;

    /** The counter value of the site result to generate. */
    SpectreCounter counter;
    /** The result type to use for generating a site result. */
    SpectreResultType resultType;
    /** State data (base64), if any, necessary for generating the site result. */
    const char *resultState;

    /** The result type to use for generating a site login. */
    SpectreResultType loginType;
    /** State data (base64), if any, necessary for generating the site login. */
    const char *loginState;

    /** Site metadata: URL location where the site can be accessed. */
    const char *url;
    /** Site metadata: Amount of times an action has been taken for this site. */
    unsigned int uses;
    /** Site metadata: Date of the most recent action taken on this site. */
    time_t lastUsed;

    /** Amount of security questions associated with this site. */
    size_t questions_count;
    /** Array of security questions associated with this site. */
    SpectreMarshalledQuestion *questions;
} SpectreMarshalledSite;

typedef struct SpectreMarshalledUser {
    SpectreKeyProvider userKeyProvider;
    bool redacted;

    /** A number identifying the avatar to display for this user. */
    unsigned int avatar;
    /** Unique name for this user, preferably the user's full legal name. */
    const char *userName;
    /** User metadata: The identicon that was generated to represent this user's identity. */
    SpectreIdenticon identicon;
    /** Algorithm version to use for user operations (eg. key ID operations). */
    SpectreAlgorithm algorithm;
    /** A unique identifier (hex) for the user key, primarily for authentication/verification. */
    SpectreKeyID keyID;
    /** The initial result type to use for new sites created by the user. */
    SpectreResultType defaultType;
    /** The result type to use for generating the user's standard login. */
    SpectreResultType loginType;
    /** State data (base64), if any, necessary for generating the user's standard login. */
    const char *loginState;
    /** User metadata: Date of the most recent action taken by this user. */
    time_t lastUsed;

    /** Amount of sites associated to this user. */
    size_t sites_count;
    /** Array of sites associated to this user. */
    SpectreMarshalledSite *sites;
} SpectreMarshalledUser;

typedef struct SpectreMarshalledFile {
    /** Metadata from the file that holds user data, available without the need for user authentication. */
    SpectreMarshalledInfo *info;
    /** All data in the file, including extensions and other data present, even if not used by this library. */
    SpectreMarshalledData *data;
    /** Status of parsing the file and any errors that might have occurred during the process. */
    SpectreMarshalError error;
} SpectreMarshalledFile;

//// Marshalling.

/** Write the user and all associated data out using the given marshalling format.
 * @param file A pointer to the original file object to update with the user's data or to NULL to make a new.
 *             File object will be updated with state or new (allocated).  May be NULL if not interested in a file object.
 * @return A C-string (allocated), or NULL if the file is missing, format is unrecognized, does not support marshalling or a format error occurred. */
const char *spectre_marshal_write(
        const SpectreFormat outFormat, SpectreMarshalledFile **file, SpectreMarshalledUser *user);
/** Parse the user configuration in the input buffer.  Fields that could not be parsed remain at their type's initial value.
 * @return The updated file object or a new one (allocated) if none was provided; NULL if a file object could not be allocated. */
SpectreMarshalledFile *spectre_marshal_read(
        SpectreMarshalledFile *file, const char *in);
/** Authenticate as the user identified by the given marshalled file.
 * @note This object stores a reference to the given key provider.
 * @return A user object (allocated), or NULL if the file format provides no marshalling or a format error occurred. */
SpectreMarshalledUser *spectre_marshal_auth(
        SpectreMarshalledFile *file, const SpectreKeyProvider userKeyProvider);

//// Creating.

/** Create a new user object ready for marshalling.
 * @note This object stores copies of the strings assigned to it and manages their deallocation internally.
 * @return A user object (allocated), or NULL if the userName is missing or the marshalled user couldn't be allocated. */
SpectreMarshalledUser *spectre_marshal_user(
        const char *userName, const SpectreKeyProvider userKeyProvider, const SpectreAlgorithm algorithmVersion);
/** Create a new site attached to the given user object, ready for marshalling.
 * @note This object stores copies of the strings assigned to it and manages their deallocation internally.
 * @return A site object (allocated), or NULL if the siteName is missing or the marshalled site couldn't be allocated. */
SpectreMarshalledSite *spectre_marshal_site(
        SpectreMarshalledUser *user,
        const char *siteName, const SpectreResultType resultType, const SpectreCounter keyCounter, const SpectreAlgorithm algorithmVersion);
/** Create a new question attached to the given site object, ready for marshalling.
 * @note This object stores copies of the strings assigned to it and manages their deallocation internally.
 * @return A question object (allocated), or NULL if the marshalled question couldn't be allocated. */
SpectreMarshalledQuestion *spectre_marshal_question(
        SpectreMarshalledSite *site, const char *keyword);
/** Create or update a marshal file descriptor.
 * @param file If NULL, a new file will be allocated.  Otherwise, the given file will be updated and the updated file returned.
 * @param info If NULL, the file's info will be left as-is, otherwise it will be replaced by the given one.  The file will manage the info's deallocation.
 * @param data If NULL, the file's data will be left as-is, otherwise it will be replaced by the given one.  The file will manage the data's deallocation.
 * @return The given file or new (allocated) if file is NULL; or NULL if the user is missing or the file couldn't be allocated. */
SpectreMarshalledFile *spectre_marshal_file(
        SpectreMarshalledFile *file, SpectreMarshalledInfo *info, SpectreMarshalledData *data);
/** Record a marshal error.
 * @return The given file or new (allocated) if file is NULL; or NULL if the file couldn't be allocated. */
SpectreMarshalledFile *spectre_marshal_error(
        SpectreMarshalledFile *file, SpectreMarshalErrorType type, const char *format, ...);

//// Disposing.

/** Free the given user object and all associated data. */
#define spectre_marshal_free(object) _Generic( (object), \
        SpectreMarshalledInfo**: spectre_marshal_info_free,   \
        SpectreMarshalledUser**: spectre_marshal_user_free,   \
        SpectreMarshalledData**: spectre_marshal_data_free,   \
        SpectreMarshalledFile**: spectre_marshal_file_free)   \
        (object)
void spectre_marshal_info_free(
        SpectreMarshalledInfo **info);
void spectre_marshal_user_free(
        SpectreMarshalledUser **user);
void spectre_marshal_data_free(
        SpectreMarshalledData **data);
void spectre_marshal_file_free(
        SpectreMarshalledFile **file);

//// Exploring.

/** Create a null value.
 * @return A new data value (allocated), initialized to a null value, or NULL if the value couldn't be allocated. */
SpectreMarshalledData *spectre_marshal_data_new(void);
/** Get or create a value for the given path in the data store.
 * @return The value at this path (shared), or NULL if the value didn't exist and couldn't be created. */
SpectreMarshalledData *spectre_marshal_data_get(
        SpectreMarshalledData *data, ...);
SpectreMarshalledData *spectre_marshal_data_vget(
        SpectreMarshalledData *data, va_list nodes);
/** Look up the value at the given path in the data store.
 * @return The value at this path (shared), or NULL if there is no value at this path. */
const SpectreMarshalledData *spectre_marshal_data_find(
        const SpectreMarshalledData *data, ...);
const SpectreMarshalledData *spectre_marshal_data_vfind(
        const SpectreMarshalledData *data, va_list nodes);
/** Check if the data represents a NULL value.
 * @return true if the value at this path is null or is missing, false if it is a non-null type. */
bool spectre_marshal_data_is_null(
        const SpectreMarshalledData *data, ...);
bool spectre_marshal_data_vis_null(
        const SpectreMarshalledData *data, va_list nodes);
/** Set a null value at the given path in the data store.
 * @return true if the object was successfully modified. */
bool spectre_marshal_data_set_null(
        SpectreMarshalledData *data, ...);
bool spectre_marshal_data_vset_null(
        SpectreMarshalledData *data, va_list nodes);
/** Look up the boolean value at the given path in the data store.
 * @return true if the value at this path is true, false if it is not or there is no boolean value at this path. */
bool spectre_marshal_data_get_bool(
        const SpectreMarshalledData *data, ...);
bool spectre_marshal_data_vget_bool(
        const SpectreMarshalledData *data, va_list nodes);
/** Set a boolean value at the given path in the data store.
 * @return true if the object was successfully modified. */
bool spectre_marshal_data_set_bool(
        const bool value, SpectreMarshalledData *data, ...);
bool spectre_marshal_data_vset_bool(
        const bool value, SpectreMarshalledData *data, va_list nodes);
/** Look up the numeric value at the given path in the data store.
 * @return A number or NAN if there is no numeric value at this path. */
double spectre_marshal_data_get_num(
        const SpectreMarshalledData *data, ...);
double spectre_marshal_data_vget_num(
        const SpectreMarshalledData *data, va_list nodes);
bool spectre_marshal_data_set_num(
        const double value, SpectreMarshalledData *data, ...);
bool spectre_marshal_data_vset_num(
        const double value, SpectreMarshalledData *data, va_list nodes);
/** Look up string value at the given path in the data store.
 * @return The string value (shared) or string representation of the number at this path; NULL if there is no such value at this path. */
const char *spectre_marshal_data_get_str(
        const SpectreMarshalledData *data, ...);
const char *spectre_marshal_data_vget_str(
        const SpectreMarshalledData *data, va_list nodes);
/** Save a C-string value at the given path into the data store.
 * @param value The string value to save into the data store.  The data store will hold a copy of this object.
 * @return true if the value has been saved into the data store.  false if a node at the path didn't exist and couldn't be created or initialized. */
bool spectre_marshal_data_set_str(
        const char *value, SpectreMarshalledData *data, ...);
bool spectre_marshal_data_vset_str(
        const char *value, SpectreMarshalledData *data, va_list nodes);
/** Keep only the data children that pass the filter test. */
void spectre_marshal_data_filter(
        SpectreMarshalledData *data, bool (*filter)(SpectreMarshalledData *child, void *args), void *args);
bool spectre_marshal_data_filter_empty(
        SpectreMarshalledData *child, void *args);

//// Format.

/**
 * @return The purpose represented by the given name or ERR if the format was not recognized.
 */
const SpectreFormat spectre_format_named(
        const char *formatName);
/**
 * @return The standard name (static) for the given purpose or NULL if the format was not recognized.
 */
const char *spectre_format_name(
        const SpectreFormat format);
/**
 * @return The file extension (static) that's recommended and currently used for output files,
 *         or NULL if the format was not recognized or does not support marshalling.
 */
const char *spectre_format_extension(
        const SpectreFormat format);
/**
 * @return An array (allocated, count) of filename extensions (static) that are used for files of this format,
 *         the first being the currently preferred/output extension.
 *         NULL if the format is unrecognized or does not support marshalling.
 */
const char **spectre_format_extensions(
        const SpectreFormat format, size_t *count);

#endif // _SPECTRE_MARSHAL_H
0707010000001E000081A400000000000000000000000163C2D55500003A65000000000000000000000000000000000000003A00000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-types.c// =============================================================================
// Created by Maarten Billemont on 2012-01-04.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#include "spectre-types.h"
#include "spectre-util.h"

SPECTRE_LIBS_BEGIN
#include <string.h>
#include <ctype.h>

#if SPECTRE_CPERCIVA
#include <scrypt/crypto_scrypt.h>
#include <scrypt/sha256.h>
#elif SPECTRE_SODIUM
#include "sodium.h"
#endif
SPECTRE_LIBS_END

const SpectreKeyID SpectreKeyIDUnset = { .hex = "" };

const SpectreIdenticon SpectreIdenticonUnset = {
        .leftArm = "",
        .body = "",
        .rightArm = "",
        .accessory = "",
        .color = SpectreIdenticonColorUnset,
};

bool spectre_id_valid(const SpectreKeyID *id) {

    return id && strlen( id->hex ) + 1 == sizeof( id->hex );
}

bool spectre_id_equals(const SpectreKeyID *id1, const SpectreKeyID *id2) {

    if (!id1 || !id2)
        return !id1 && !id2;

    return memcmp( id1->bytes, id2->bytes, sizeof( id1->bytes ) ) == OK;
}

const SpectreKeyID spectre_id_buf(const uint8_t *buf, const size_t size) {

    SpectreKeyID keyID = SpectreKeyIDUnset;

    if (!buf)
        return keyID;

#if SPECTRE_CPERCIVA
    SHA256_Buf( buf, size, keyID.bytes );
#elif SPECTRE_SODIUM
    crypto_hash_sha256( keyID.bytes, buf, size );
#else
#error No crypto support for spectre_id_buf.
#endif

    size_t hexSize = sizeof( keyID.hex );
    if (spectre_hex( keyID.bytes, sizeof( keyID.bytes ), keyID.hex, &hexSize ) != keyID.hex)
        err( "KeyID string pointer mismatch." );

    return keyID;
}

const SpectreKeyID spectre_id_str(const char hex[static 65]) {

    SpectreKeyID keyID = SpectreKeyIDUnset;

    size_t hexSize = 0;
    const uint8_t *hexBytes = spectre_unhex( hex, &hexSize );
    if (hexSize != sizeof( keyID.bytes ))
        wrn( "Not a valid key ID: %s", hex );

    else {
        memcpy( keyID.bytes, hexBytes, sizeof( keyID.bytes ) );
        spectre_hex( keyID.bytes, sizeof( keyID.bytes ), keyID.hex, &((size_t){ sizeof( keyID.hex ) }) );
    }

    spectre_free( &hexBytes, hexSize );
    return keyID;
}

const SpectreResultType spectre_type_named(const char *typeName) {

    // Find what password type is represented by the type letter.
    if (strlen( typeName ) == 1) {
        if ('0' == typeName[0])
            return SpectreResultNone;
        if ('x' == typeName[0])
            return SpectreResultTemplateMaximum;
        if ('l' == typeName[0])
            return SpectreResultTemplateLong;
        if ('m' == typeName[0])
            return SpectreResultTemplateMedium;
        if ('b' == typeName[0])
            return SpectreResultTemplateBasic;
        if ('s' == typeName[0])
            return SpectreResultTemplateShort;
        if ('i' == typeName[0])
            return SpectreResultTemplatePIN;
        if ('n' == typeName[0])
            return SpectreResultTemplateName;
        if ('p' == typeName[0])
            return SpectreResultTemplatePhrase;
        if ('P' == typeName[0])
            return SpectreResultStatePersonal;
        if ('D' == typeName[0])
            return SpectreResultStateDevice;
        if ('K' == typeName[0])
            return SpectreResultDeriveKey;
    }

    // Find what password type is represented by the type name.
    if (spectre_strncasecmp( spectre_type_short_name( SpectreResultNone ), typeName, strlen( typeName ) ) == OK)
        return SpectreResultNone;
    if (spectre_strncasecmp( spectre_type_short_name( SpectreResultTemplateMaximum ), typeName, strlen( typeName ) ) == OK)
        return SpectreResultTemplateMaximum;
    if (spectre_strncasecmp( spectre_type_short_name( SpectreResultTemplateLong ), typeName, strlen( typeName ) ) == OK)
        return SpectreResultTemplateLong;
    if (spectre_strncasecmp( spectre_type_short_name( SpectreResultTemplateMedium ), typeName, strlen( typeName ) ) == OK)
        return SpectreResultTemplateMedium;
    if (spectre_strncasecmp( spectre_type_short_name( SpectreResultTemplateBasic ), typeName, strlen( typeName ) ) == OK)
        return SpectreResultTemplateBasic;
    if (spectre_strncasecmp( spectre_type_short_name( SpectreResultTemplateShort ), typeName, strlen( typeName ) ) == OK)
        return SpectreResultTemplateShort;
    if (spectre_strncasecmp( spectre_type_short_name( SpectreResultTemplatePIN ), typeName, strlen( typeName ) ) == OK)
        return SpectreResultTemplatePIN;
    if (spectre_strncasecmp( spectre_type_short_name( SpectreResultTemplateName ), typeName, strlen( typeName ) ) == OK)
        return SpectreResultTemplateName;
    if (spectre_strncasecmp( spectre_type_short_name( SpectreResultTemplatePhrase ), typeName, strlen( typeName ) ) == OK)
        return SpectreResultTemplatePhrase;
    if (spectre_strncasecmp( spectre_type_short_name( SpectreResultStatePersonal ), typeName, strlen( typeName ) ) == OK)
        return SpectreResultStatePersonal;
    if (spectre_strncasecmp( spectre_type_short_name( SpectreResultStateDevice ), typeName, strlen( typeName ) ) == OK)
        return SpectreResultStateDevice;
    if (spectre_strncasecmp( spectre_type_short_name( SpectreResultDeriveKey ), typeName, strlen( typeName ) ) == OK)
        return SpectreResultDeriveKey;

    wrn( "Not a generated type name: %s", typeName );
    return (SpectreResultType)ERR;
}

const char *spectre_type_abbreviation(const SpectreResultType resultType) {

    switch (resultType) {
        case SpectreResultNone:
            return "no";
        case SpectreResultTemplateMaximum:
            return "max";
        case SpectreResultTemplateLong:
            return "long";
        case SpectreResultTemplateMedium:
            return "med";
        case SpectreResultTemplateBasic:
            return "basic";
        case SpectreResultTemplateShort:
            return "short";
        case SpectreResultTemplatePIN:
            return "pin";
        case SpectreResultTemplateName:
            return "name";
        case SpectreResultTemplatePhrase:
            return "phrase";
        case SpectreResultStatePersonal:
            return "own";
        case SpectreResultStateDevice:
            return "device";
        case SpectreResultDeriveKey:
            return "key";
        default: {
            wrn( "Unknown password type: %d", resultType );
            return NULL;
        }
    }
}

const char *spectre_type_short_name(const SpectreResultType resultType) {

    switch (resultType) {
        case SpectreResultNone:
            return "none";
        case SpectreResultTemplateMaximum:
            return "maximum";
        case SpectreResultTemplateLong:
            return "long";
        case SpectreResultTemplateMedium:
            return "medium";
        case SpectreResultTemplateBasic:
            return "basic";
        case SpectreResultTemplateShort:
            return "short";
        case SpectreResultTemplatePIN:
            return "pin";
        case SpectreResultTemplateName:
            return "name";
        case SpectreResultTemplatePhrase:
            return "phrase";
        case SpectreResultStatePersonal:
            return "personal";
        case SpectreResultStateDevice:
            return "device";
        case SpectreResultDeriveKey:
            return "key";
        default: {
            wrn( "Unknown password type: %d", resultType );
            return NULL;
        }
    }
}

const char *spectre_type_long_name(const SpectreResultType resultType) {

    switch (resultType) {
        case SpectreResultNone:
            return "None";
        case SpectreResultTemplateMaximum:
            return "Maximum Security Password";
        case SpectreResultTemplateLong:
            return "Long Password";
        case SpectreResultTemplateMedium:
            return "Medium Password";
        case SpectreResultTemplateBasic:
            return "Basic Password";
        case SpectreResultTemplateShort:
            return "Short Password";
        case SpectreResultTemplatePIN:
            return "PIN";
        case SpectreResultTemplateName:
            return "Name";
        case SpectreResultTemplatePhrase:
            return "Phrase";
        case SpectreResultStatePersonal:
            return "Personal Password";
        case SpectreResultStateDevice:
            return "Device Private Password";
        case SpectreResultDeriveKey:
            return "Crypto Key";
        default: {
            wrn( "Unknown password type: %d", resultType );
            return NULL;
        }
    }
}

const char **spectre_type_templates(const SpectreResultType type, size_t *count) {

    *count = 0;
    if (!(type & SpectreResultClassTemplate)) {
        wrn( "Not a generated type: %d", type );
        return NULL;
    }

    switch (type) {
        case SpectreResultTemplateMaximum:
            return spectre_strings( count,
                    "anoxxxxxxxxxxxxxxxxx", "axxxxxxxxxxxxxxxxxno", NULL );
        case SpectreResultTemplateLong:
            return spectre_strings( count,
                    "CvcvnoCvcvCvcv", "CvcvCvcvnoCvcv", "CvcvCvcvCvcvno",
                    "CvccnoCvcvCvcv", "CvccCvcvnoCvcv", "CvccCvcvCvcvno",
                    "CvcvnoCvccCvcv", "CvcvCvccnoCvcv", "CvcvCvccCvcvno",
                    "CvcvnoCvcvCvcc", "CvcvCvcvnoCvcc", "CvcvCvcvCvccno",
                    "CvccnoCvccCvcv", "CvccCvccnoCvcv", "CvccCvccCvcvno",
                    "CvcvnoCvccCvcc", "CvcvCvccnoCvcc", "CvcvCvccCvccno",
                    "CvccnoCvcvCvcc", "CvccCvcvnoCvcc", "CvccCvcvCvccno", NULL );
        case SpectreResultTemplateMedium:
            return spectre_strings( count,
                    "CvcnoCvc", "CvcCvcno", NULL );
        case SpectreResultTemplateShort:
            return spectre_strings( count,
                    "Cvcn", NULL );
        case SpectreResultTemplateBasic:
            return spectre_strings( count,
                    "aaanaaan", "aannaaan", "aaannaaa", NULL );
        case SpectreResultTemplatePIN:
            return spectre_strings( count,
                    "nnnn", NULL );
        case SpectreResultTemplateName:
            return spectre_strings( count,
                    "cvccvcvcv", NULL );
        case SpectreResultTemplatePhrase:
            return spectre_strings( count,
                    "cvcc cvc cvccvcv cvc", "cvc cvccvcvcv cvcv", "cv cvccv cvc cvcvccv", NULL );
        default: {
            wrn( "Unknown generated type: %d", type );
            return NULL;
        }
    }
}

const char *spectre_type_template(const SpectreResultType type, const uint8_t templateIndex) {

    size_t count = 0;
    const char **templates = spectre_type_templates( type, &count );
    char const *template = templates && count? templates[templateIndex % count]: NULL;
    free( templates );

    return template;
}

const char *spectre_algorithm_short_name(const SpectreAlgorithm algorithm) {

    switch (algorithm) {
        case SpectreAlgorithmV0:
            return "v0";
        case SpectreAlgorithmV1:
            return "v1";
        case SpectreAlgorithmV2:
            return "v2";
        case SpectreAlgorithmV3:
            return "v3";
        default: {
            wrn( "Unknown algorithm: %d", algorithm );
            return NULL;
        }
    }
}

const char *spectre_algorithm_long_name(const SpectreAlgorithm algorithm) {

    switch (algorithm) {
        case SpectreAlgorithmV0:
            return "v0 (2012-03)";
        case SpectreAlgorithmV1:
            return "v1 (2012-07)";
        case SpectreAlgorithmV2:
            return "v2 (2014-09)";
        case SpectreAlgorithmV3:
            return "v3 (2015-01)";
        default: {
            wrn( "Unknown algorithm: %d", algorithm );
            return NULL;
        }
    }
}

const SpectreKeyPurpose spectre_purpose_named(const char *purposeName) {

    if (spectre_strncasecmp( spectre_purpose_name( SpectreKeyPurposeAuthentication ), purposeName, strlen( purposeName ) ) == OK)
        return SpectreKeyPurposeAuthentication;
    if (spectre_strncasecmp( spectre_purpose_name( SpectreKeyPurposeIdentification ), purposeName, strlen( purposeName ) ) == OK)
        return SpectreKeyPurposeIdentification;
    if (spectre_strncasecmp( spectre_purpose_name( SpectreKeyPurposeRecovery ), purposeName, strlen( purposeName ) ) == OK)
        return SpectreKeyPurposeRecovery;

    wrn( "Not a purpose name: %s", purposeName );
    return (SpectreKeyPurpose)ERR;
}

const char *spectre_purpose_name(const SpectreKeyPurpose purpose) {

    switch (purpose) {
        case SpectreKeyPurposeAuthentication:
            return "authentication";
        case SpectreKeyPurposeIdentification:
            return "identification";
        case SpectreKeyPurposeRecovery:
            return "recovery";
        default: {
            wrn( "Unknown purpose: %d", purpose );
            return NULL;
        }
    }
}

const char *spectre_purpose_scope(const SpectreKeyPurpose purpose) {

    switch (purpose) {
        case SpectreKeyPurposeAuthentication:
            return "com.lyndir.masterpassword";
        case SpectreKeyPurposeIdentification:
            return "com.lyndir.masterpassword.login";
        case SpectreKeyPurposeRecovery:
            return "com.lyndir.masterpassword.answer";
        default: {
            wrn( "Unknown purpose: %d", purpose );
            return NULL;
        }
    }
}

const char *spectre_class_characters(const char characterClass) {

    switch (characterClass) {
        case 'V':
            return "AEIOU";
        case 'C':
            return "BCDFGHJKLMNPQRSTVWXYZ";
        case 'v':
            return "aeiou";
        case 'c':
            return "bcdfghjklmnpqrstvwxyz";
        case 'A':
            return "AEIOUBCDFGHJKLMNPQRSTVWXYZ";
        case 'a':
            return "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz";
        case 'n':
            return "0123456789";
        case 'o':
            return "@&%?,=[]_:-+*$#!'^~;()/.";
        case 'x':
            return "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()";
        case ' ':
            return " ";
        default: {
            wrn( "Unknown character class: %c", characterClass );
            return NULL;
        }
    }
}

const char spectre_class_character(const char characterClass, const uint8_t seedByte) {

    const char *classCharacters = spectre_class_characters( characterClass );
    if (!classCharacters || !strlen( classCharacters ))
        return '\0';

    return classCharacters[seedByte % strlen( classCharacters )];
}
0707010000001F000081A400000000000000000000000163C2D555000028CA000000000000000000000000000000000000003A00000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-types.h// =============================================================================
// Created by Maarten Billemont on 2012-01-04.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#ifndef _SPECTRE_TYPES_H
#define _SPECTRE_TYPES_H

#ifndef SPECTRE_LIBS_BEGIN
#define SPECTRE_LIBS_BEGIN
#define SPECTRE_LIBS_END
#endif

SPECTRE_LIBS_BEGIN
#define __STDC_WANT_LIB_EXT1__ 1
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
SPECTRE_LIBS_END

#ifndef __unused
#define __unused
#endif

#ifndef __has_feature
#define __has_feature(x) 0
#endif
#ifndef __has_extension
#define __has_extension __has_feature
#endif

#ifdef NS_ENUM
#define spectre_enum(_type, _name) NS_ENUM(_type, _name)
#elif __has_extension( c_fixed_enum ) || __has_extension( objc_fixed_enum ) || __has_extension( cxx_fixed_enum )
#define spectre_enum(_type, _name) _type _name; enum _name : _type
#else
#define spectre_enum(_type, _name) _type _name; enum _name
#endif

#ifdef NS_OPTIONS
#define spectre_opts(_type, _name) NS_OPTIONS(_type, _name)
#elif __has_extension( c_fixed_enum ) || __has_extension( objc_fixed_enum ) || __has_extension( cxx_fixed_enum )
#define spectre_opts(_type, _name) _type _name; enum _name : _type
#else
#define spectre_opts(_type, _name) _type _name; enum _name
#endif

//// Types.

typedef spectre_enum( unsigned int, SpectreAlgorithm ) {
    /** (2012-03-05) V0 incorrectly performed host-endian math with bytes translated into 16-bit network-endian. */
    SpectreAlgorithmV0,
    /** (2012-07-17) V1 incorrectly sized site name fields by character count rather than byte count. */
    SpectreAlgorithmV1,
    /** (2014-09-24) V2 incorrectly sized user name fields by character count rather than byte count. */
    SpectreAlgorithmV2,
    /** (2015-01-15) V3 is the current version. */
    SpectreAlgorithmV3,

    SpectreAlgorithmCurrent = SpectreAlgorithmV3,
    SpectreAlgorithmFirst = SpectreAlgorithmV0,
    SpectreAlgorithmLast = SpectreAlgorithmV3,
};

typedef struct {
    /** SHA-256-sized hash */
    uint8_t bytes[256 / 8]; // SHA-256
    /** Hex c-string of the hash */
    char hex[2 * (256 / 8) + 1];
} SpectreKeyID;
extern const SpectreKeyID SpectreKeyIDUnset;

typedef struct {
    /** The cryptographic key */
    const uint8_t bytes[512 / 8];
    /** The key's identity */
    const SpectreKeyID keyID;
    /** The algorithm the key was made by & for */
    const SpectreAlgorithm algorithm;
} SpectreUserKey;

typedef struct {
    /** The cryptographic key */
    const uint8_t bytes[256 / 8]; // HMAC-SHA-256
    /** The key's identity */
    const SpectreKeyID keyID;
    /** The algorithm the key was made by & for */
    const SpectreAlgorithm algorithm;
} SpectreSiteKey;

typedef spectre_enum( uint8_t, SpectreKeyPurpose ) {
    /** Generate a key for authentication. */
    SpectreKeyPurposeAuthentication,
    /** Generate a name for identification. */
    SpectreKeyPurposeIdentification,
    /** Generate a recovery token. */
    SpectreKeyPurposeRecovery,
};

// bit 4 - 9
typedef spectre_opts( uint16_t, SpectreResultClass ) {
    /** Use the site key to generate a result from a template. */
    SpectreResultClassTemplate = 1 << 4,
    /** Use the site key to encrypt and decrypt a stateful entity. */
    SpectreResultClassStateful = 1 << 5,
    /** Use the site key to derive a site-specific object. */
    SpectreResultClassDerive = 1 << 6,
};

// bit 10 - 15
typedef spectre_opts( uint16_t, SpectreResultFeature ) {
    SpectreResultFeatureNone = 0,
    /** Export the key-protected content data. */
    SpectreResultFeatureExportContent = 1 << 10,
    /** Never export content. */
    SpectreResultFeatureDevicePrivate = 1 << 11,
    /** Don't use this as the primary authentication result type. */
    SpectreResultFeatureAlternate = 1 << 12,
};

// bit 0-3 | SpectreResultClass | SpectreResultFeature
typedef spectre_enum( uint32_t, SpectreResultType ) {
    /** 0: Don't produce a result */
    SpectreResultNone = 0,

    /** 16: pg^VMAUBk5x3p%HP%i4= */
    SpectreResultTemplateMaximum = 0x0 | SpectreResultClassTemplate | SpectreResultFeatureNone,
    /** 17: BiroYena8:Kixa */
    SpectreResultTemplateLong = 0x1 | SpectreResultClassTemplate | SpectreResultFeatureNone,
    /** 18: BirSuj0- */
    SpectreResultTemplateMedium = 0x2 | SpectreResultClassTemplate | SpectreResultFeatureNone,
    /** 19: Bir8 */
    SpectreResultTemplateShort = 0x3 | SpectreResultClassTemplate | SpectreResultFeatureNone,
    /** 20: pO98MoD0 */
    SpectreResultTemplateBasic = 0x4 | SpectreResultClassTemplate | SpectreResultFeatureNone,
    /** 21: 2798 */
    SpectreResultTemplatePIN = 0x5 | SpectreResultClassTemplate | SpectreResultFeatureNone,
    /** 30: birsujano */
    SpectreResultTemplateName = 0xE | SpectreResultClassTemplate | SpectreResultFeatureNone,
    /** 31: bir yennoquce fefi */
    SpectreResultTemplatePhrase = 0xF | SpectreResultClassTemplate | SpectreResultFeatureNone,

    /** 1056: Custom saved result. */
    SpectreResultStatePersonal = 0x0 | SpectreResultClassStateful | SpectreResultFeatureExportContent,
    /** 2081: Custom saved result that should not be exported from the device. */
    SpectreResultStateDevice = 0x1 | SpectreResultClassStateful | SpectreResultFeatureDevicePrivate,

    /** 4160: Derive a unique binary key. */
    SpectreResultDeriveKey = 0x0 | SpectreResultClassDerive | SpectreResultFeatureAlternate,

    SpectreResultDefaultResult = SpectreResultTemplateLong,
    SpectreResultDefaultLogin = SpectreResultTemplateName,
};

typedef spectre_enum( uint32_t, SpectreCounter ) {
    /** Use a time-based counter value, resulting in a TOTP generator. */
    SpectreCounterTOTP = 0,
    /** The initial value for a site's counter. */
    SpectreCounterInitial = 1,

    SpectreCounterDefault = SpectreCounterInitial,
    SpectreCounterFirst = SpectreCounterTOTP,
    SpectreCounterLast = UINT32_MAX,
};

/** These colours are compatible with the original ANSI SGR. */
typedef spectre_enum( uint8_t, SpectreIdenticonColor ) {
    SpectreIdenticonColorUnset,
    SpectreIdenticonColorRed,
    SpectreIdenticonColorGreen,
    SpectreIdenticonColorYellow,
    SpectreIdenticonColorBlue,
    SpectreIdenticonColorMagenta,
    SpectreIdenticonColorCyan,
    SpectreIdenticonColorMono,

    SpectreIdenticonColorFirst = SpectreIdenticonColorRed,
    SpectreIdenticonColorLast = SpectreIdenticonColorMono,
};

typedef struct {
    const char *leftArm;
    const char *body;
    const char *rightArm;
    const char *accessory;
    SpectreIdenticonColor color;
} SpectreIdenticon;
extern const SpectreIdenticon SpectreIdenticonUnset;

//// Type utilities.

/** Check whether the fingerprint is valid.
 * @return true if the fingerprints represents a fully complete print for a buffer. */
bool spectre_id_valid(const SpectreKeyID *id1);
/** Compare two fingerprints for equality.
 * @return true if the buffers represent identical fingerprints or are both NULL. */
bool spectre_id_equals(const SpectreKeyID *id1, const SpectreKeyID *id2);
/** Encode a fingerprint for a buffer. */
const SpectreKeyID spectre_id_buf(const uint8_t *buf, const size_t size);
/** Reconstruct a fingerprint from its hexadecimal string representation. */
const SpectreKeyID spectre_id_str(const char hex[static 65]);

/**
 * @return The standard identifying name (static) for the given algorithm or NULL if the algorithm is not known.
 */
const char *spectre_algorithm_short_name(const SpectreAlgorithm algorithm);
/**
 * @return The descriptive name (static) for the given algorithm or NULL if the algorithm is not known.
 */
const char *spectre_algorithm_long_name(const SpectreAlgorithm algorithm);

/**
 * @return The purpose represented by the given name or ERR if the name does not represent a known purpose.
 */
const SpectreKeyPurpose spectre_purpose_named(const char *purposeName);
/**
 * @return The standard name (static) for the given purpose or NULL if the purpose is not known.
 */
const char *spectre_purpose_name(const SpectreKeyPurpose purpose);
/**
 * @return The scope identifier (static) to apply when encoding for the given purpose or NULL if the purpose is not known.
 */
const char *spectre_purpose_scope(const SpectreKeyPurpose purpose);

/**
 * @return The result type represented by the given name or ERR if the name does not represent a known type.
 */
const SpectreResultType spectre_type_named(const char *typeName);
/**
 * @return The standard identifying name (static) for the given result type or NULL if the type is not known.
 */
const char *spectre_type_abbreviation(const SpectreResultType resultType);
/**
 * @return The standard identifying name (static) for the given result type or NULL if the type is not known.
 */
const char *spectre_type_short_name(const SpectreResultType resultType);
/**
 * @return The descriptive name (static) for the given result type or NULL if the type is not known.
 */
const char *spectre_type_long_name(const SpectreResultType resultType);

/**
 * @return An array (allocated, count) of strings (static) that express the templates to use for the given type.
 *         NULL if the type is not known or is not a SpectreResultClassTemplate.
 */
const char **spectre_type_templates(const SpectreResultType type, size_t *count);
/**
 * @return A C-string (static) that contains the result encoding template of the given type for a seed that starts with the given byte.
 *         NULL if the type is not known or is not a SpectreResultClassTemplate.
 */
const char *spectre_type_template(const SpectreResultType type, const uint8_t templateIndex);

/**
 * @return A C-string (static) with all the characters in the given character class or NULL if the character class is not known.
 */
const char *spectre_class_characters(const char characterClass);
/**
 * @return A character from given character class that encodes the given byte or NUL if the character class is not known or is empty.
 */
const char spectre_class_character(const char characterClass, const uint8_t seedByte);

#endif // _SPECTRE_TYPES_H
07070100000020000081A400000000000000000000000163C2D55500004CF7000000000000000000000000000000000000003900000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-util.c// =============================================================================
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#include "spectre-util.h"

SPECTRE_LIBS_BEGIN
#include <string.h>
#include <ctype.h>
#include <errno.h>

#if SPECTRE_CPERCIVA
#include <scrypt/crypto_scrypt.h>
#include <scrypt/sha256.h>
#elif SPECTRE_SODIUM
#include "sodium.h"
#endif
#define ECB 0
#define CBC 1
#define CTR 0
#include "aes.h"
SPECTRE_LIBS_END

SpectreLogLevel spectre_verbosity = SpectreLogLevelInfo;
FILE *spectre_log_sink_file_target = NULL;

static SpectreLogSink **sinks;
static size_t sinks_count;

bool spectre_log_sink_register(SpectreLogSink *sink) {

    if (!spectre_realloc( &sinks, NULL, SpectreLogSink *, ++sinks_count )) {
        --sinks_count;
        return false;
    }

    sinks[sinks_count - 1] = sink;
    return true;
}

bool spectre_log_sink_unregister(SpectreLogSink *sink) {

    for (unsigned int r = 0; r < sinks_count; ++r) {
        if (sinks[r] == sink) {
            sinks[r] = NULL;
            return true;
        }
    }

    return false;
}

bool spectre_log(SpectreLogLevel level, const char *file, int line, const char *function, const char *format, ...) {

    if (spectre_verbosity < level)
        return false;

    va_list args;
    va_start( args, format );
    bool sunk = spectre_vlog( level, file, line, function, format, &args );
    va_end( args );

    return sunk;
}

static const char *spectre_log_formatter(SpectreLogEvent *event) {

    if (!event->formatted) {
        va_list args;
        va_copy( args, *(va_list *)event->args );
        event->formatted = spectre_vstr( event->format, args );
        va_end( args );
    }

    return event->formatted;
}

bool spectre_vlog(SpectreLogLevel level, const char *file, int line, const char *function, const char *format, va_list *args) {

    if (spectre_verbosity < level)
        return false;

    SpectreLogEvent event = {
            .occurrence = time( NULL ),
            .level = level,
            .file = file,
            .line = line,
            .function = function,
            .format = format,
            .args = args,
            .formatter = &spectre_log_formatter,
    };
    bool sunk = spectre_elog( &event );

    return sunk;
}

bool spectre_elog(SpectreLogEvent *event) {

    if (spectre_verbosity < event->level)
        return false;

    bool sunk = false;
    if (!sinks_count)
        sunk = spectre_log_sink_file( event );

    else
        for (unsigned int s = 0; s < sinks_count; ++s) {
            SpectreLogSink *sink = sinks[s];

            if (sink)
                sunk |= sink( event );
        }

    if (event->level <= SpectreLogLevelWarning) {
        (void)event->level/* error breakpoint opportunity */;
    }
    spectre_free_string( &event->formatted );
    if (event->level <= SpectreLogLevelFatal)
        abort();

    return sunk;
}

bool spectre_log_sink_file(SpectreLogEvent *event) {

    if (!spectre_log_sink_file_target)
        spectre_log_sink_file_target = stderr;

    if (spectre_verbosity >= SpectreLogLevelDebug) {
        switch (event->level) {
            case SpectreLogLevelTrace:
                fprintf( spectre_log_sink_file_target, "[TRC] " );
                break;
            case SpectreLogLevelDebug:
                fprintf( spectre_log_sink_file_target, "[DBG] " );
                break;
            case SpectreLogLevelInfo:
                fprintf( spectre_log_sink_file_target, "[INF] " );
                break;
            case SpectreLogLevelWarning:
                fprintf( spectre_log_sink_file_target, "[WRN] " );
                break;
            case SpectreLogLevelError:
                fprintf( spectre_log_sink_file_target, "[ERR] " );
                break;
            case SpectreLogLevelFatal:
                fprintf( spectre_log_sink_file_target, "[FTL] " );
                break;
            default:
                fprintf( spectre_log_sink_file_target, "[???] " );
                break;
        }
    }

    fprintf( spectre_log_sink_file_target, "%s\n", event->formatter( event ) );
    return true;
}

void spectre_uint16(const uint16_t number, uint8_t buf[static 2]) {

    buf[0] = (uint8_t)((number >> 8L) & UINT8_MAX);
    buf[1] = (uint8_t)((number >> 0L) & UINT8_MAX);
}

void spectre_uint32(const uint32_t number, uint8_t buf[static 4]) {

    buf[0] = (uint8_t)((number >> 24) & UINT8_MAX);
    buf[1] = (uint8_t)((number >> 16) & UINT8_MAX);
    buf[2] = (uint8_t)((number >> 8L) & UINT8_MAX);
    buf[3] = (uint8_t)((number >> 0L) & UINT8_MAX);
}

void spectre_uint64(const uint64_t number, uint8_t buf[static 8]) {

    buf[0] = (uint8_t)((number >> 56) & UINT8_MAX);
    buf[1] = (uint8_t)((number >> 48) & UINT8_MAX);
    buf[2] = (uint8_t)((number >> 40) & UINT8_MAX);
    buf[3] = (uint8_t)((number >> 32) & UINT8_MAX);
    buf[4] = (uint8_t)((number >> 24) & UINT8_MAX);
    buf[5] = (uint8_t)((number >> 16) & UINT8_MAX);
    buf[6] = (uint8_t)((number >> 8L) & UINT8_MAX);
    buf[7] = (uint8_t)((number >> 0L) & UINT8_MAX);
}

const char **spectre_strings(size_t *count, const char *strings, ...) {

    *count = 0;
    size_t size = 0;
    const char **array = NULL;

    va_list args;
    va_start( args, strings );
    for (const char *string = strings; string; (string = va_arg( args, const char * ))) {
        size_t cursor = *count;

        if (!spectre_realloc( &array, &size, const char *, cursor + 1 )) {
            spectre_free( &array, size );
            break;
        }

        *count = size / sizeof( *array );
        array[cursor] = string;
    }
    va_end( args );

    return array;
}

bool spectre_buf_push_buf(uint8_t **buffer, size_t *bufferSize, const uint8_t *pushBuffer, const size_t pushSize) {

    if (!buffer || !bufferSize || !pushBuffer || !pushSize)
        return false;
    if (*bufferSize == (size_t)ERR)
        // The buffer was marked as broken, it is missing a previous push.  Abort to avoid corrupt content.
        return false;

    if (!spectre_realloc( buffer, bufferSize, uint8_t, (*bufferSize + pushSize) / sizeof( uint8_t ) )) {
        // realloc failed, we can't push.  Mark the buffer as broken.
        spectre_free( buffer, *bufferSize );
        *bufferSize = (size_t)ERR;
        return false;
    }

    uint8_t *bufferOffset = *buffer + *bufferSize - pushSize;
    memcpy( bufferOffset, pushBuffer, pushSize );
    return true;
}

bool spectre_buf_push_uint32(uint8_t **buffer, size_t *bufferSize, const uint32_t pushInt) {

    uint8_t pushBuf[4 /* 32 / 8 */];
    spectre_uint32( pushInt, pushBuf );
    return spectre_buf_push( buffer, bufferSize, pushBuf, sizeof( pushBuf ) );
}

bool spectre_buf_push_str(uint8_t **buffer, size_t *bufferSize, const char *pushString) {

    return pushString && spectre_buf_push( buffer, bufferSize, (const uint8_t *)pushString, strlen( pushString ) );
}

bool spectre_string_push(char **string, const char *pushString) {

    if (!string || !pushString)
        return false;

    // We overwrite an existing trailing NUL byte.
    return pushString && spectre_buf_push( (uint8_t **const)string, &((size_t){ *string? strlen( *string ): 0 }),
            (const uint8_t *)pushString, strlen( pushString ) + 1 );
}

bool spectre_string_pushf(char **string, const char *pushFormat, ...) {

    va_list args;
    va_start( args, pushFormat );
    const char *pushString = spectre_vstr( pushFormat, args );
    bool success = spectre_string_push( string, pushString );
    spectre_free_string( &pushString );
    va_end( args );

    return success;
}

bool __spectre_realloc(void **buffer, size_t *bufferSize, const size_t targetSize) {

    if (!buffer)
        return false;
    if (*buffer && bufferSize && *bufferSize == targetSize)
        return true;

    void *newBuffer = realloc( *buffer, targetSize );
    if (!newBuffer)
        return false;

    *buffer = newBuffer;
    if (bufferSize)
        *bufferSize = targetSize;

    return true;
}

void spectre_zero(void *buffer, size_t bufferSize) {

    uint8_t *b = buffer;
    for (; bufferSize > 0; --bufferSize)
        *b++ = 0;
}

bool __spectre_free(void **buffer, const size_t bufferSize) {

    if (!buffer || !*buffer)
        return false;

    spectre_zero( *buffer, bufferSize );
    free( *buffer );
    *buffer = NULL;

    return true;
}

bool __spectre_free_string(char **string) {

    return string && *string && __spectre_free( (void **)string, strlen( *string ) );
}

bool __spectre_free_strings(char **strings, ...) {

    bool success = true;

    va_list args;
    va_start( args, strings );
    success &= spectre_free_string( strings );
    for (char **string; (string = va_arg( args, char ** ));)
        success &= spectre_free_string( string );
    va_end( args );

    return success;
}

bool spectre_kdf_scrypt(uint8_t *key, const size_t keySize, const uint8_t *secret, const size_t secretSize, const uint8_t *salt, const size_t saltSize,
        const uint64_t N, const uint32_t r, const uint32_t p) {

    if (!key || !keySize || !secret || !secretSize || !salt || !saltSize)
        return false;

#if SPECTRE_CPERCIVA
    if (crypto_scrypt( (const void *)secret, strlen( secret ), salt, saltSize, N, r, p, key, keySize ) < 0) {
        return false;
    }
#elif SPECTRE_SODIUM
    if (crypto_pwhash_scryptsalsa208sha256_ll( secret, secretSize, salt, saltSize, N, r, p, key, keySize ) != OK) {
        return false;
    }
#else
#error No crypto support for spectre_kdf_scrypt.
#endif

    return true;
}

bool spectre_kdf_blake2b(uint8_t *subkey, const size_t subkeySize, const uint8_t *key, const size_t keySize,
        const uint8_t *context, const size_t contextSize, const uint64_t id, const char *personal) {

    if (!subkey || !subkeySize || !key || !keySize) {
        errno = EINVAL;
        return false;
    }

#if SPECTRE_SODIUM
    if (keySize < crypto_generichash_blake2b_KEYBYTES_MIN || keySize > crypto_generichash_blake2b_KEYBYTES_MAX ||
        subkeySize < crypto_generichash_blake2b_KEYBYTES_MIN || subkeySize > crypto_generichash_blake2b_KEYBYTES_MAX ||
        (personal && strlen( personal ) > crypto_generichash_blake2b_PERSONALBYTES)) {
        errno = EINVAL;
        return false;
    }

    uint8_t saltBuf[crypto_generichash_blake2b_SALTBYTES] = { 0 };
    if (id)
        spectre_uint64( id, saltBuf );

    uint8_t personalBuf[crypto_generichash_blake2b_PERSONALBYTES] = { 0 };
    if (personal && strlen( personal ))
        memcpy( personalBuf, personal, min( sizeof( personalBuf ), strlen( personal ) ) );

    if (crypto_generichash_blake2b_salt_personal( subkey, subkeySize, context, contextSize, key, keySize, saltBuf, personalBuf ) != OK)
        return false;
#else
#error No crypto support for spectre_kdf_blake2b.
#endif

    return true;
}

bool spectre_hash_hmac_sha256(uint8_t mac[static 32], const uint8_t *key, const size_t keySize, const uint8_t *message, const size_t messageSize) {

    if (!mac || !key || !keySize || !message || !messageSize)
        return false;

#if SPECTRE_CPERCIVA
    HMAC_SHA256_Buf( key, keySize, message, messageSize, mac );
    return true;
#elif SPECTRE_SODIUM
    crypto_auth_hmacsha256_state state;
    return crypto_auth_hmacsha256_init( &state, key, keySize ) == OK &&
           crypto_auth_hmacsha256_update( &state, message, messageSize ) == OK &&
           crypto_auth_hmacsha256_final( &state, mac ) == OK;
#else
#error No crypto support for spectre_hash_hmac_sha256.
#endif
}

const static uint8_t *spectre_aes(bool encrypt, const uint8_t *key, const size_t keySize, const uint8_t *buf, size_t *bufSize) {

    if (!key || keySize < AES_BLOCKLEN || !bufSize || !*bufSize)
        return NULL;

    // IV = zero
    static const uint8_t iv[AES_BLOCKLEN] = { 0 };

    // Add PKCS#7 padding
    uint32_t aesSize = (uint32_t)*bufSize, blockRemainder = aesSize % AES_BLOCKLEN;
    if (blockRemainder) // round up to block size.
        aesSize += AES_BLOCKLEN - blockRemainder;
    else if (encrypt) // add pad block if plain text fits block size.
        aesSize += AES_BLOCKLEN;
//    uint8_t *resultBuf = calloc( aesSize, sizeof( uint8_t ) );
//    if (!resultBuf)
//        return NULL;
    uint8_t *aesBuf = malloc( aesSize );
    if (!aesBuf) {
//        spectre_free( &resultBuf, aesSize );
        return NULL;
    }

    memcpy( aesBuf, buf, *bufSize );
    memset( aesBuf + *bufSize, (int)(aesSize - *bufSize), aesSize - *bufSize );

    struct AES_ctx aes;
    AES_init_ctx_iv( &aes, key, iv );

    if (encrypt)
        AES_CBC_encrypt_buffer( &aes, aesBuf, aesSize );
    else
        AES_CBC_decrypt_buffer( &aes, aesBuf, aesSize );
//    spectre_free( &aesBuf, aesSize );

    // Truncate PKCS#7 padding
    if (encrypt)
        *bufSize = aesSize;
    else if (aesBuf[aesSize - 1] <= AES_BLOCKLEN)
        *bufSize -= aesBuf[aesSize - 1];
    memset( aesBuf + *bufSize, 0, aesSize - *bufSize );

    return aesBuf;
}

const uint8_t *spectre_aes_encrypt(const uint8_t *key, const size_t keySize, const uint8_t *plainBuffer, size_t *bufferSize) {

    return spectre_aes( true, key, keySize, plainBuffer, bufferSize );
}

const uint8_t *spectre_aes_decrypt(const uint8_t *key, const size_t keySize, const uint8_t *cipherBuffer, size_t *bufferSize) {

    return spectre_aes( false, key, keySize, cipherBuffer, bufferSize );
}

#if UNUSED
const char *spectre_hotp(const uint8_t *key, size_t keySize, uint64_t movingFactor, uint8_t digits, uint8_t truncationOffset) {

    // Hash the moving factor with the key.
    uint8_t counter[8];
    spectre_uint64( movingFactor, counter );
    uint8_t hash[20];
    hmac_sha1( key, keySize, counter, sizeof( counter ), hash );

    // Determine the offset to select OTP bytes from.
    int offset;
    if ((truncationOffset >= 0) && (truncationOffset < (sizeof( hash ) - 4)))
        offset = truncationOffset;
    else
        offset = hash[sizeof( hash ) - 1] & 0xf;

    // Select four bytes from the truncation offset.
    uint32_t otp = 0U
            | ((hash[offset + 0] & 0x7f) << 24)
            | ((hash[offset + 1] & 0xff) << 16)
            | ((hash[offset + 2] & 0xff) << 8)
            | ((hash[offset + 3] & 0xff) << 0);

    // Render the OTP as `digits` decimal digits.
    otp %= (int)pow(10, digits);
    return spectre_strdup( spectre_str( "%0*d", digits, otp ) );
}
#endif

const char *spectre_str(const char *format, ...) {

    va_list args;
    va_start( args, format );
    const char *str = spectre_vstr( format, args );
    va_end( args );

    return str;
}

const char *spectre_vstr(const char *format, va_list args) {

    if (!format || !*format)
        return NULL;

    char *str = NULL;
    size_t size = 0;

    while (true) {
        va_list _args;
        va_copy( _args, args );
        size_t chars = (size_t)vsnprintf( str, size, format, _args );
        va_end( _args );

        if (chars < 0)
            break;
        if (chars < size)
            return str;
        if (!spectre_realloc( &str, &size, char, chars + 1 ))
            break;
    }

    spectre_free( &str, size );
    return NULL;
}

char *spectre_hex(const uint8_t *buf, const size_t size, char *hex, size_t *hexSize) {

    if (!buf || !size)
        return NULL;
    if (!spectre_realloc( &hex, hexSize, char, size * 2 + 1 ))
        return NULL;

    for (size_t kH = 0; kH < size; kH++)
        sprintf( &(hex[kH * 2]), "%.2hhX", buf[kH] );

    return hex;
}

const char *spectre_hex_l(const uint32_t number, char hex[static 9]) {

    uint8_t buf[4 /* 32 / 8 */];
    spectre_uint32( number, buf );

    size_t hexSize = 9;
    return spectre_hex( buf, sizeof( buf ), hex, &hexSize );
}

const uint8_t *spectre_unhex(const char *hex, size_t *size) {

    if (!hex)
        return NULL;

    size_t hexLength = strlen( hex );
    if (hexLength == 0 || hexLength % 2 != 0)
        return NULL;

    size_t bufSize = hexLength / 2;
    if (size)
        *size = bufSize;

    uint8_t *buf = malloc( bufSize );
    for (size_t b = 0; b < bufSize; ++b)
        if (sscanf( hex + b * 2, "%02hhX", &buf[b] ) != 1) {
            spectre_free( &buf, bufSize );
            return NULL;
        }

    return buf;
}

size_t spectre_base64_decode_max(size_t b64Length) {

    // Every 4 b64 chars yield 3 plain bytes => len = 3 * ceil(b64Size / 4)
    return 3 /*bytes*/ * ((b64Length + 4 /*chars*/ - 1) / 4 /*chars*/);
}

size_t spectre_base64_decode(const char *b64Text, uint8_t *byteBuf) {

    size_t b64Length = strlen( b64Text ), plainSize = 0;
    if (sodium_base642bin( byteBuf, spectre_base64_decode_max( b64Length ), b64Text, b64Length,
            " \n\r\t\v", &plainSize, NULL, sodium_base64_VARIANT_ORIGINAL ) == ERR)
        return 0;

    return plainSize;
}

size_t spectre_base64_encode_max(size_t byteSize) {

    return sodium_base64_ENCODED_LEN( byteSize, sodium_base64_VARIANT_ORIGINAL );
}

size_t spectre_base64_encode(const uint8_t *byteBuf, size_t byteSize, char *b64Text) {

    return strlen(
            sodium_bin2base64(
                    b64Text, spectre_base64_encode_max( byteSize ),
                    byteBuf, byteSize, sodium_base64_VARIANT_ORIGINAL ) );
}

size_t spectre_utf8_char_size(const char *utf8String) {

    if (!utf8String)
        return 0U;

    // Legal UTF-8 byte sequences: <http://www.unicode.org/unicode/uni2errata/UTF-8_Corrigendum.html>
    unsigned char utf8Char = (unsigned char)*utf8String;
    if (utf8Char >= 0x00 && utf8Char <= 0x7F)
        return min( 1U, strlen( utf8String ) );
    if (utf8Char >= 0xC2 && utf8Char <= 0xDF)
        return min( 2U, strlen( utf8String ) );
    if (utf8Char >= 0xE0 && utf8Char <= 0xEF)
        return min( 3U, strlen( utf8String ) );
    if (utf8Char >= 0xF0 && utf8Char <= 0xF4)
        return min( 4U, strlen( utf8String ) );

    return 0U;
}

size_t spectre_utf8_char_count(const char *utf8String) {

    size_t strchars = 0, charlen;
    for (char *remaining = (char *)utf8String; remaining && *remaining; remaining += charlen, ++strchars)
        if (!(charlen = spectre_utf8_char_size( remaining )))
            return 0;

    return strchars;
}

void *spectre_memdup(const void *src, const size_t len) {

    if (!src)
        return NULL;

    char *dst = malloc( len );
    if (dst)
        memcpy( dst, src, len );

    return dst;
}

const char *spectre_strdup(const char *src) {

    if (!src)
        return NULL;

    size_t len = strlen( src );
    return spectre_memdup( src, len + 1 );
}

const char *spectre_strndup(const char *src, const size_t max) {

    if (!src)
        return NULL;

    size_t len = 0;
    for (; len < max && src[len] != '\0'; ++len);

    char *dst = calloc( len + 1, sizeof( char ) );
    if (dst)
        memcpy( dst, src, len );

    return dst;
}

int spectre_strcasecmp(const char *s1, const char *s2) {

    return spectre_strncasecmp( s1, s2, s1 && s2? min( strlen( s1 ), strlen( s2 ) ): 0 );
}

int spectre_strncasecmp(const char *s1, const char *s2, size_t max) {

    if (s1 == s2)
        return 0;

    int cmp = s1 && s2 && max > 0? 0: s1? 1: -1;
    for (; !cmp && max && max-- > 0 && s1 && s2; ++s1, ++s2)
        cmp = tolower( (unsigned char)*s1 ) - tolower( (unsigned char)*s2 );

    return cmp;
}
07070100000021000081A400000000000000000000000163C2D55500004BDA000000000000000000000000000000000000003900000000spectre-cli-3.0+git20230114.26c01a5/api/c/spectre-util.h// =============================================================================
// Created by Maarten Billemont on 2014-12-20.
// Copyright (c) 2011, Maarten Billemont.
//
// This file is part of Spectre.
// Spectre is free software. You can modify it under the terms of
// the GNU General Public License, either version 3 or any later version.
// See the LICENSE file for details or consult <http://www.gnu.org/licenses/>.
//
// Note: this grant does not include any rights for use of Spectre's trademarks.
// =============================================================================

#ifndef _SPECTRE_UTIL_H
#define _SPECTRE_UTIL_H

#include "spectre-types.h"

SPECTRE_LIBS_BEGIN
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
SPECTRE_LIBS_END

//// Logging.
///
/// spectre's log mechanism uses a layered approach:
/// 1. trc/dbg/inf/wrn/err/ftl macros initiate a log event.
///    They record metadata such as severity, source code and time. Events are recorded as a static message and a set of data arguments.
///    The log message should be a static printf(3)-style format string with compatible arguments.
/// 2. The macros are handled by the SPECTRE_LOG define, which defaults to spectre_log.
///    It should reference a symbol with the signature:
///    (bool) (SpectreLogLevel level, const char *file, int line, const char *function, const char *format, ... args)
/// 3. spectre_verbosity determines the severity threshold for log processing; any messages above its threshold are discarded.
///    This avoids triggering the log mechanism for events which are not considered interesting at the time.
/// 4. The spectre_log implementation consumes the log event through spectre's log sink mechanism.
///    The sink mechanism aims to make log messages available to any interested party.
///    Only if there are no interested parties registered, log events will be sunk into spectre_log_sink_file.
///    The return value can be used to check if the event was consumed by any sink.
/// 5. SpectreLogEvent values are created by spectre_log to host the log event data for relaying into log sinks.
///    All values come directly from the log event macros, with two exceptions:
///    .formatter is a function capable of merging the message format and data arguments into a complete log message.
///    .formatted is a heap allocated string representing the completely merged log message.
///    spectre_elog can be used to introduce events into the system which do not originate from the macros.
///    These events need to at a minimum provide a .formatter to ensure argument data can be merged into the format message.
/// 6. spectre_log_sink_register records an SpectreLogSink as interested in receiving subsequent log events.
///    Any events dispatched into the spectre_elog mechanism hence forth will be relayed into the newly registered log sink.
///    spectre_log_sink_unregister should be used when a sink is no longer interested in consuming log events.
/// 7. An SpectreLogSink consumes SpectreLogEvent values in whatever way it sees fit.
///    If a sink is interested in a complete formatted message, it should use .formatted, if available, or .formatter
///    to obtain the message (and then store it in .formatted for future sinks).
///    If for whatever reason the sink did not act on the message, it should return false.
/// 8. The default sink, spectre_log_sink_file, consumes log events by writing them to the spectre_log_sink_file_target FILE.
///    A log event's complete message is resolved through its .formatter, prefixed with its severity and terminated by a newline.
///    The default spectre_log_sink_file_target is stderr, yielding a default behaviour that writes log events to the system's standard error.

typedef spectre_enum( int, SpectreLogLevel ) {
    /** Logging internal state. */
    SpectreLogLevelTrace = 3,
    /** Logging state and events interesting when investigating issues. */
    SpectreLogLevelDebug = 2,
    /** User messages. */
    SpectreLogLevelInfo = 1,
    /** Recoverable issues and user suggestions. */
    SpectreLogLevelWarning = 0,
    /** Unrecoverable issues. */
    SpectreLogLevelError = -1,
    /** Issues that lead to abortion. */
    SpectreLogLevelFatal = -2,
};
extern SpectreLogLevel spectre_verbosity;

/** A log event describes a message emitted through the log subsystem. */
typedef struct SpectreLogEvent {
    time_t occurrence;
    SpectreLogLevel level;
    const char *file;
    int line;
    const char *function;
    /** @return A C-string (allocated), cached in .formatted, of the .args interpolated into the .format message. */
    const char *(*formatter)(struct SpectreLogEvent *);
    const char *formatted;
    const char *format;
    const va_list *args;
} SpectreLogEvent;

/** A log sink describes a function that can receive log events. */
typedef bool (SpectreLogSink)(SpectreLogEvent *event);

/** spectre_log_sink_file is a sink that writes log messages to the spectre_log_sink_file_target, which defaults to stderr. */
extern SpectreLogSink spectre_log_sink_file;
extern FILE *spectre_log_sink_file_target;

/** To receive events, sinks need to be registered.  If no sinks are registered, log events are sent to the spectre_log_sink_file sink. */
bool spectre_log_sink_register(SpectreLogSink *sink);
bool spectre_log_sink_unregister(SpectreLogSink *sink);

/** These functions dispatch log events to the registered sinks.
 * @return false if no sink processed the log event (sinks may reject messages or fail). */
bool spectre_log(SpectreLogLevel level, const char *file, int line, const char *function, const char *format, ...);
bool spectre_vlog(SpectreLogLevel level, const char *file, int line, const char *function, const char *format, va_list *args);
bool spectre_elog(SpectreLogEvent *event);

/** The log dispatcher you want to channel log messages into; defaults to spectre_log, enabling the log sink mechanism. */
#ifndef SPECTRE_LOG
#define SPECTRE_LOG spectre_log
#endif

/** Application interface for logging events into the subsystem. */
#ifndef trc
#define trc(format, ...) SPECTRE_LOG( SpectreLogLevelTrace, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
#define dbg(format, ...) SPECTRE_LOG( SpectreLogLevelDebug, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
#define inf(format, ...) SPECTRE_LOG( SpectreLogLevelInfo, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
#define wrn(format, ...) SPECTRE_LOG( SpectreLogLevelWarning, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
#define err(format, ...) SPECTRE_LOG( SpectreLogLevelError, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
#define ftl(format, ...) SPECTRE_LOG( SpectreLogLevelFatal, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__ )
#endif


//// Utilities.

#ifndef OK
#define OK 0
#endif
#ifndef ERR
#define ERR -1
#endif

#ifndef stringify
#define stringify(s) #s
#endif
#ifndef stringify_def
#define stringify_def(s) stringify(s)
#endif

#if __GNUC__ >= 3
#ifndef min
#define min(a, b) ({ \
    __typeof__ (a) _a = (a); \
    __typeof__ (b) _b = (b); \
    _a < _b ? _a : _b; })
#endif
#ifndef max
#define max(a, b) ({ \
    __typeof__ (a) _a = (a); \
    __typeof__ (b) _b = (b); \
    _a > _b ? _a : _b; })
#endif
#define spectre_default(__default, __value) ({ __typeof__ (__value) _v = (__value); _v? _v: (__default); })
#define spectre_default_num(__default, __num) ({ __typeof__ (__num) _n = (__num); !isnan( _n )? (__typeof__ (__default))_n: (__default); })
#else
#ifndef min
#define min(a, b) ( (a) < (b) ? (a) : (b) )
#endif
#ifndef max
#define max(a, b) ( (a) > (b) ? (a) : (b) )
#endif
#define spectre_default(__default, __value) ( (__value)? (__value): (__default) )
#define spectre_default_num(__default, __num) ( !isnan( (__num) )? (__num): (__default) )
#endif


//// Buffers and memory.

/** Write a number to a byte buffer using spectre's endianness (big/network). */
void spectre_uint16(const uint16_t number, uint8_t buf[2]);
void spectre_uint32(const uint32_t number, uint8_t buf[4]);
void spectre_uint64(const uint64_t number, uint8_t buf[8]);

/** @return An array of strings (allocated, count) or NULL if no strings were given or we could not allocate space for the new array. */
const char **spectre_strings(
        size_t *count, const char *strings, ...);

/** Push a value onto a buffer.  The given buffer is realloc'ed and the value appended to the end of it.
 * @param buffer A pointer to the buffer (allocated, bufferSize) to append to. No-op if NULL. New buffer with value if NULL pointer.
 * @param value The object to append to the buffer.
 *              If char*, copies a C-string from the value.
 *              If uint8_t*, takes a size_t argument indicating the amount of uint8_t's to copy from the value. */
#define spectre_buf_push(buffer, bufferSize, value, ...) _Generic( (value), \
        uint32_t: spectre_buf_push_uint32,                                  \
        uint8_t *: spectre_buf_push_buf, const uint8_t *: spectre_buf_push_buf, \
        char *: spectre_buf_push_str, const char *: spectre_buf_push_str )      \
        ( buffer, bufferSize, value, ##__VA_ARGS__)
bool spectre_buf_push_buf(
        uint8_t **buffer, size_t *bufferSize, const uint8_t *pushBuffer, const size_t pushSize);
/** Push an integer onto a buffer.  reallocs the given buffer and appends the given integer using spectre's endianness (big/network).
 * @param buffer A pointer to the buffer (allocated, bufferSize) to append to, may be NULL. */
bool spectre_buf_push_uint32(
        uint8_t **buffer, size_t *bufferSize, const uint32_t pushInt);
/** Push a C-string onto a buffer.  reallocs the given buffer and appends the given string.
 * @param buffer A pointer to the buffer (allocated, bufferSize) to append to, may be NULL. */
bool spectre_buf_push_str(
        uint8_t **buffer, size_t *bufferSize, const char *pushString);

/** Push a C-string onto another string.  reallocs the target string and appends the source string.
 * @param string A pointer to the string (allocated) to append to, may be NULL. */
bool spectre_string_push(
        char **string, const char *pushString);
bool spectre_string_pushf(
        char **string, const char *pushFormat, ...);

// These defines merely exist to do type-checking, force the void** cast & drop any const qualifier.
/** Reallocate the given buffer from the given size by making space for the given amount of objects of the given type.
 * On success, the bufferSize pointer will be updated to the buffer's new byte size and the buffer pointer may be updated to a new memory address.
 * On failure, the pointers will remain unaffected.
 * @param buffer A pointer to the buffer (allocated, bufferSize) to reallocate.
 * @param bufferSize A pointer to the buffer's current size, or NULL.
 * @param targetSize The amount to reallocate the buffer's size into.
 * @return true if successful, false if reallocation failed.
 */
#define spectre_realloc(\
        /* const void** */buffer, /* size_t* */bufferSize, type, /* const size_t */typeCount) \
        ({ type **_buffer = buffer; __spectre_realloc( (void **)_buffer, bufferSize, sizeof( type ) * (typeCount) ); })
/** Free a buffer after zero'ing its contents, then set the reference to NULL.
 * @param bufferSize The byte-size of the buffer, these bytes will be zeroed prior to deallocation. */
#define spectre_free(\
        /* void** */buffer, /* size_t */ bufferSize) \
        ({ __typeof__(buffer) _b = buffer; const void *__b = *_b; (void)__b; __spectre_free( (void **)_b, bufferSize ); })
/** Free a C-string after zero'ing its contents, then set the reference to NULL. */
#define spectre_free_string(\
        /* char** */string) \
        ({ __typeof__(string) _s = string; const char *__s = *_s; (void)__s; __spectre_free_string( (char **)_s ); })
/** Free strings after zero'ing their contents, then set the references to NULL.  Terminate the va_list with NULL. */
#define spectre_free_strings(\
        /* char** */strings, ...) \
        ({ __typeof__(strings) _s = strings; const char *__s = *_s; (void)__s; __spectre_free_strings( (char **)_s, __VA_ARGS__ ); })
/** Free a C-string after zero'ing its contents, then set the reference to the replacement string.
 * The replacement string is generated before the original is freed; so it can be a derivative of the original. */
#define spectre_replace_string(\
        /* char* */string, /* char* */replacement) \
        do { const char *replacement_ = replacement; spectre_free_string( &string ); string = replacement_; } while (0)
#ifdef _MSC_VER
#undef spectre_realloc
#define spectre_realloc(buffer, bufferSize, targetSize) \
        __spectre_realloc( (void **)buffer, bufferSize, targetSize )
#undef spectre_free
#define spectre_free(buffer, bufferSize) \
        __spectre_free( (void **)buffer, bufferSize )
#undef spectre_free_string
#define spectre_free_string(string) \
        __spectre_free_string( (char **)string )
#undef spectre_free_strings
#define spectre_free_strings(strings, ...) \
        __spectre_free_strings( (char **)strings, __VA_ARGS__ )
#endif
bool __spectre_realloc(
        void **buffer, size_t *bufferSize, const size_t targetSize);
bool __spectre_free(
        void **buffer, size_t bufferSize);
bool __spectre_free_string(
        char **string);
bool __spectre_free_strings(
        char **strings, ...);
void spectre_zero(
        void *buffer, const size_t bufferSize);


//// Cryptography.

/** Derive a key from the given secret and salt using the scrypt KDF.
 * @return A buffer (allocated, keySize) containing the key or NULL if secret or salt is missing, key could not be allocated or the KDF failed. */
bool spectre_kdf_scrypt(
        uint8_t *key, const size_t keySize, const uint8_t *secret, const size_t secretSize, const uint8_t *salt, const size_t saltSize,
        const uint64_t N, const uint32_t r, const uint32_t p);
/** Derive a subkey from the given key using the blake2b KDF.
 * @return A buffer (allocated, keySize) containing the key or NULL if the key or subkeySize is missing, the key sizes are out of bounds, the subkey could not be allocated or derived. */
bool spectre_kdf_blake2b(
        uint8_t *subkey, const size_t subkeySize, const uint8_t *key, const size_t keySize,
        const uint8_t *context, const size_t contextSize, const uint64_t id, const char *personal);
/** Calculate the MAC for the given message with the given key using SHA256-HMAC.
 * @return A buffer (allocated, 32-byte) containing the MAC or NULL if the key or message is missing, the MAC could not be allocated or generated. */
bool spectre_hash_hmac_sha256(
        uint8_t mac[static 32], const uint8_t *key, const size_t keySize, const uint8_t *message, const size_t messageSize);
/** Encrypt a plainBuffer with the given key using AES-128-CBC.
 * @param bufferSize A pointer to the size of the plain buffer on input, and the size of the returned cipher buffer on output.
 * @return A buffer (allocated, bufferSize) containing the cipherBuffer or NULL if the key or buffer is missing, the key size is out of bounds or the result could not be allocated. */
const uint8_t *spectre_aes_encrypt(
        const uint8_t *key, const size_t keySize, const uint8_t *plainBuffer, size_t *bufferSize);
/** Decrypt a cipherBuffer with the given key using AES-128-CBC.
 * @param bufferSize A pointer to the size of the cipher buffer on input, and the size of the returned plain buffer on output.
 * @return A buffer (allocated, bufferSize) containing the plainBuffer or NULL if the key or buffer is missing, the key size is out of bounds or the result could not be allocated. */
const uint8_t *spectre_aes_decrypt(
        const uint8_t *key, const size_t keySize, const uint8_t *cipherBuffer, size_t *bufferSize);
#if UNUSED
/** Calculate an OTP using RFC-4226.
 * @return A C-string (allocated) containing exactly `digits` decimal OTP digits. */
const char *spectre_hotp(
        const uint8_t *key, size_t keySize, uint64_t movingFactor, uint8_t digits, uint8_t truncationOffset);
#endif


//// Encoding.

/** Compose a formatted string.
 * @return A C-string (allocated); or NULL if the format is missing or the result could not be allocated or formatted. */
const char *spectre_str(const char *format, ...);
const char *spectre_vstr(const char *format, va_list args);
/** Encode size-bytes from a buffer as a C-string of hexadecimal characters.
 * @param hex If not NULL, use it to store the hexadecimal characters.  Will be realloc'ed if it isn't large enough.  Result is returned.
 * @return A C-string (allocated, size * 2 + 1 bytes); NULL if the buffer is missing or the result could not be allocated. */
char *spectre_hex(const uint8_t *buf, const size_t size, char *hex, size_t *hexSize);
const char *spectre_hex_l(const uint32_t number, char hex[static 9]);
/** Decode a C-string of hexadecimal characters into a buffer of size-bytes.
 * @return A buffer (allocated, *size); or NULL if hex is NULL, empty, or not an even-length hexadecimal string. */
const uint8_t *spectre_unhex(const char *hex, size_t *size);

/** @return The amount of bytes needed to decode b64Length amount of base-64 characters. */
size_t spectre_base64_decode_max(size_t b64Length);
/** Decodes a base-64 encoded string into a byte buffer.
  * @param byteBuf a byte buffer, size should be at least spectre_base64_decode_max(strlen(b64Text))
  * @return The amount of bytes that were written to byteBuf or 0 if the base-64 string couldn't be fully decoded. */
size_t spectre_base64_decode(const char *b64Text, uint8_t *byteBuf);

/** @return The amount of bytes needed to encode a byteBuf of the given size as base-64 (including a terminating NUL). */
size_t spectre_base64_encode_max(size_t byteSize);
/** Encodes a byte buffer into a base-64 encoded string.
  * @param b64Text a character buffer, size should be at least spectre_base64_encode_max(byteSize)
  * @return The amount of characters that were written to b64Text, excluding the terminating NUL. */
size_t spectre_base64_encode(const uint8_t *byteBuf, size_t byteSize, char *b64Text);

/** @return The byte size of the UTF-8 character at the start of the given string or 0 if it is NULL, empty or not a legal UTF-8 character. */
size_t spectre_utf8_char_size(const char *utf8String);
/** @return The amount of UTF-8 characters in the given string or 0 if it is NULL, empty, or contains bytes that are not legal in UTF-8. */
size_t spectre_utf8_char_count(const char *utf8String);


//// Compatibility.

/** Drop-in for memdup(3).
 * @return A buffer (allocated, len) with len bytes copied from src or NULL if src is missing or the buffer could not be allocated. */
void *spectre_memdup(const void *src, const size_t len);
/** Drop-in for POSIX strdup(3).
 * @return A C-string (allocated) copied from src or NULL if src is missing or the buffer could not be allocated. */
const char *spectre_strdup(const char *src);
/** Drop-in for POSIX strndup(3).
 * @return A C-string (allocated) with no more than max bytes copied from src or NULL if src is missing or the buffer could not be allocated. */
const char *spectre_strndup(const char *src, const size_t max);
/** Drop-in for POSIX strcasecmp(3). */
int spectre_strcasecmp(const char *s1, const char *s2);
/** Drop-in for POSIX strncasecmp(3). */
int spectre_strncasecmp(const char *s1, const char *s2, const size_t max);

#endif // _SPECTRE_UTIL_H
07070100000022000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000002D00000000spectre-cli-3.0+git20230114.26c01a5/api/java07070100000023000081A400000000000000000000000163C2D5550000040F000000000000000000000000000000000000003A00000000spectre-cli-3.0+git20230114.26c01a5/api/java/build.gradleplugins {
    id 'java-library'
}

description = 'Master Password Algorithm Implementation'

tasks.withType( JavaCompile ) {
    // Native headers
    options.compilerArgs += ["-h", new File( new File( project.project( ':masterpassword-core' ).projectDir, 'src' ), 'java' ).absolutePath]
}

configurations {
    lib
}

dependencies {
    implementation group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.7-p2'
    implementation group: 'com.github.spotbugs', name: 'spotbugs-annotations', version: '4.2.1'

    api group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.9.8'
    api group: 'org.jetbrains', name: 'annotations', version: '16.0.2'

    lib project( path: ':masterpassword-core', configuration: 'default' )
}

processResources {
    dependsOn task( type: Sync, 'processResources-lib', {
        into new File( processResources.outputs.files.singleFile, "lib" )

        dependsOn configurations.lib {
            files.each { libFile -> from( zipTree( libFile ) ) }
        }
    } )
}
07070100000024000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000003100000000spectre-cli-3.0+git20230114.26c01a5/api/java/src07070100000025000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000003600000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main07070100000026000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000003B00000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java07070100000027000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000003F00000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com07070100000028000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000004600000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir07070100000029000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000005500000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword0707010000002A000081A400000000000000000000000163C2D55500003737000000000000000000000000000000000000006600000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/MPAlgorithm.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

package com.lyndir.masterpassword;

import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import com.google.common.base.Charsets;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.MessageAuthenticationDigests;
import com.lyndir.lhunath.opal.system.MessageDigests;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.masterpassword.impl.*;
import java.nio.*;
import java.nio.charset.*;
import java.util.Arrays;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;


/**
 * @see Version
 */
@SuppressWarnings({ "FieldMayBeStatic", "NewMethodNamingConvention", "MethodReturnAlwaysConstant" })
public interface MPAlgorithm {

    /**
     * Derive a master key that describes a user's identity.
     *
     * @param userName       The name of the user whose identity is described by the key.
     * @param userSecret The user's secret that authenticates his access to the identity.
     */
    @Nullable
    byte[] userKey(String userName, char[] userSecret);

    /**
     * Derive a site key that describes a user's access to a specific entity.
     *
     * @param userKey   The identity of the user trying to access the entity.
     * @param siteName    The name of the entity to access.
     * @param siteCounter The site key's generation.
     * @param keyPurpose  The action that the user aims to undertake with this key.
     * @param keyContext  An action-specific context within which to scope the key.
     */
    @Nullable
    byte[] siteKey(byte[] userKey, String siteName, UnsignedInteger siteCounter,
                   MPKeyPurpose keyPurpose, @Nullable String keyContext);

    /**
     * Encode a templated result for a site key.
     *
     * @param resultType  The template to base the site key's encoding on.
     * @param resultParam A parameter that provides contextual data specific to the type template.
     */
    @Nullable
    String siteResult(byte[] userKey, byte[] siteKey, String siteName, UnsignedInteger siteCounter,
                      MPKeyPurpose keyPurpose, @Nullable String keyContext,
                      MPResultType resultType, @Nullable String resultParam);

    /**
     * For {@link MPResultTypeClass#Stateful} {@code resultType}s, generate the {@code resultParam} to use with the
     * {@link #siteResult(byte[], byte[], String, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)} call
     * in order to reconstruct this call's original {@code resultParam}.
     *
     * @param resultType  The template to base the site key's encoding on.
     * @param resultParam A parameter that provides contextual data specific to the type template.
     */
    @Nullable
    String siteState(byte[] userKey, byte[] siteKey, String siteName, UnsignedInteger siteCounter,
                     MPKeyPurpose keyPurpose, @Nullable String keyContext,
                     MPResultType resultType, String resultParam);

    /**
     * Derive an identicon that represents the user's identity in a visually recognizable way.
     *
     * @param userName       The name of the user whose identity is described by the key.
     * @param userSecret The user's secret that authenticates his access to the identity.
     */
    MPIdenticon identicon(final String userName, final char[] userSecret);

    /**
     * Encode a fingerprint for a message.
     */
    String toID(final String string);

    /**
     * Encode a fingerprint for a char buffer.
     */
    String toID(final char[] message);

    /**
     * Encode a fingerprint for a byte buffer.
     */
    String toID(final byte[] buffer);

    // Configuration

    /**
     * The linear version identifier of this algorithm's implementation.
     */
    @Nonnull
    Version version();

    /**
     * mpw: defaults: initial counter value.
     */
    @Nonnull
    UnsignedInteger mpw_default_counter();

    /**
     * mpw: defaults: password result type.
     */
    @Nonnull
    MPResultType mpw_default_result_type();

    /**
     * mpw: defaults: login result type.
     */
    @Nonnull
    MPResultType mpw_default_login_type();

    /**
     * mpw: defaults: answer result type.
     */
    @Nonnull
    MPResultType mpw_default_answer_type();

    /**
     * mpw: Input character encoding.
     */
    @Nonnull
    Charset mpw_charset();

    /**
     * The algorithm iterations.
     */
    enum Version implements MPAlgorithm {

        /**
         * bugs:
         * - does math with chars whose signedness was platform-dependent.
         * - miscounted the byte-length for multi-byte site names.
         * - miscounted the byte-length for multi-byte user names.
         */
        V0,

        /**
         * bugs:
         * - miscounted the byte-length for multi-byte site names.
         * - miscounted the byte-length for multi-byte user names.
         */
        V1,

        /**
         * bugs:
         * - miscounted the byte-length for multi-byte user names.
         */
        V2,

        /**
         * bugs:
         * - no known issues.
         */
        V3;

        public static final Version CURRENT = V3;

        static {
            if (!Native.load( MPAlgorithm.class, "mpw" ))
                Logger.get( MPAlgorithm.class ).err( "Native mpw library unavailable." );
        }

        protected final Logger logger = Logger.get( getClass() );

        @JsonCreator
        public static Version fromInt(final int algorithmVersion) {

            return values()[algorithmVersion];
        }

        @JsonValue
        public int toInt() {

            return ordinal();
        }

        @Override
        public String toString() {

            return strf( "%d, %s", version().toInt(), getClass().getSimpleName() );
        }

        @Nullable
        @Override
        public byte[] userKey(final String userName, final char[] userSecret) {

            // Create a memory-safe NUL-terminated UTF-8 C-string byte array variant of userSecret.
            CharsetEncoder encoder             = mpw_charset().newEncoder();
            byte[]         userSecretBytes = new byte[(int) (userSecret.length * (double) encoder.maxBytesPerChar()) + 1];
            try {
                Arrays.fill( userSecretBytes, (byte) 0 );
                ByteBuffer userSecretBuffer = ByteBuffer.wrap( userSecretBytes );

                CoderResult result = encoder.encode( CharBuffer.wrap( userSecret ), userSecretBuffer, true );
                if (result.isError())
                    throw new IllegalStateException( result.toString() );
                result = encoder.flush( userSecretBuffer );
                if (result.isError())
                    throw new IllegalStateException( result.toString() );

                return _userKey( userName, userSecretBytes, version().toInt() );
            }
            finally {
                Arrays.fill( userSecretBytes, (byte) 0 );
            }
        }

        @Nullable
        protected native byte[] _userKey(final String userName, final byte[] userSecret, final int algorithmVersion);

        @Nullable
        @Override
        public byte[] siteKey(final byte[] userKey, final String siteName, final UnsignedInteger siteCounter,
                              final MPKeyPurpose keyPurpose, @Nullable final String keyContext) {

            return _siteKey( userKey, siteName, siteCounter.longValue(), keyPurpose.toInt(), keyContext, version().toInt() );
        }

        @Nullable
        protected native byte[] _siteKey(final byte[] userKey, final String siteName, final long siteCounter,
                                         final int keyPurpose, @Nullable final String keyContext, final int version);

        @Nullable
        @Override
        public String siteResult(final byte[] userKey, final byte[] siteKey, final String siteName, final UnsignedInteger siteCounter,
                                 final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
                                 final MPResultType resultType, @Nullable final String resultParam) {

            return _siteResult( userKey, siteKey, siteName, siteCounter.longValue(),
                                keyPurpose.toInt(), keyContext, resultType.getType(), resultParam, version().toInt() );
        }

        @Nullable
        protected native String _siteResult(final byte[] userKey, final byte[] siteKey, final String siteName, final long siteCounter,
                                            final int keyPurpose, @Nullable final String keyContext,
                                            final int resultType, @Nullable final String resultParam, final int algorithmVersion);

        @Nullable
        @Override
        public String siteState(final byte[] userKey, final byte[] siteKey, final String siteName, final UnsignedInteger siteCounter,
                                final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
                                final MPResultType resultType, final String resultParam) {

            return _siteState( userKey, siteKey, siteName, siteCounter.longValue(),
                               keyPurpose.toInt(), keyContext, resultType.getType(), resultParam, version().toInt() );
        }

        @Nullable
        protected native String _siteState(final byte[] userKey, final byte[] siteKey, final String siteName, final long siteCounter,
                                           final int keyPurpose, @Nullable final String keyContext,
                                           final int resultType, final String resultParam, final int algorithmVersion);

        @Nullable
        @Override
        public MPIdenticon identicon(final String userName, final char[] userSecret) {

            // Create a memory-safe NUL-terminated UTF-8 C-string byte array variant of userSecret.
            CharsetEncoder encoder             = mpw_charset().newEncoder();
            byte[]         userSecretBytes = new byte[(int) (userSecret.length * (double) encoder.maxBytesPerChar()) + 1];
            try {
                Arrays.fill( userSecretBytes, (byte) 0 );
                ByteBuffer userSecretBuffer = ByteBuffer.wrap( userSecretBytes );

                CoderResult result = encoder.encode( CharBuffer.wrap( userSecret ), userSecretBuffer, true );
                if (result.isError())
                    throw new IllegalStateException( result.toString() );
                result = encoder.flush( userSecretBuffer );
                if (result.isError())
                    throw new IllegalStateException( result.toString() );

                return _identicon( userName, userSecretBytes );
            }
            finally {
                Arrays.fill( userSecretBytes, (byte) 0 );
            }
        }

        @Nullable
        protected native MPIdenticon _identicon(final String userName, final byte[] userSecret);

        @Override
        public String toID(final String message) {
            return toID( message.toCharArray() );
        }

        @Override
        public String toID(final char[] message) {
            // Create a memory-safe NUL-terminated UTF-8 C-string byte array variant of userSecret.
            CharsetEncoder encoder             = mpw_charset().newEncoder();
            byte[]         messageBytes = new byte[(int) (message.length * (double) encoder.maxBytesPerChar()) + 1];
            try {
                Arrays.fill( messageBytes, (byte) 0 );
                ByteBuffer messageBuffer = ByteBuffer.wrap( messageBytes );

                CoderResult result = encoder.encode( CharBuffer.wrap( message ), messageBuffer, true );
                if (result.isError())
                    throw new IllegalStateException( result.toString() );
                result = encoder.flush( messageBuffer );
                if (result.isError())
                    throw new IllegalStateException( result.toString() );

                return toID( messageBytes );
            }
            finally {
                Arrays.fill( messageBytes, (byte) 0 );
            }
        }

        @Override
        public String toID(final byte[] buffer) {
            return _toID( buffer );
        }

        @Nullable
        protected native String _toID(final byte[] buffer);

        // Configuration

        @Nonnull
        @Override
        public Version version() {
            return this;
        }

        @Nonnull
        @Override
        public UnsignedInteger mpw_default_counter() {
            return UnsignedInteger.ONE;
        }

        @Nonnull
        @Override
        public MPResultType mpw_default_result_type() {
            return MPResultType.GeneratedLong;
        }

        @Nonnull
        @Override
        public MPResultType mpw_default_login_type() {
            return MPResultType.GeneratedName;
        }

        @Nonnull
        @Override
        public MPResultType mpw_default_answer_type() {
            return MPResultType.GeneratedPhrase;
        }

        @Nonnull
        @Override
        public Charset mpw_charset() {
            return Charsets.UTF_8;
        }
    }
}
0707010000002B000081A400000000000000000000000163C2D555000004D6000000000000000000000000000000000000006F00000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/MPAlgorithmException.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

package com.lyndir.masterpassword;

/**
 * @author lhunath, 2017-09-21
 */
public class MPAlgorithmException extends MPException {

    public MPAlgorithmException(final String message) {
        super( message );
    }

    public MPAlgorithmException(final String message, final Throwable cause) {
        super( message, cause );
    }
}
0707010000002C000081A400000000000000000000000163C2D555000004B9000000000000000000000000000000000000006600000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/MPException.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

package com.lyndir.masterpassword;

/**
 * @author lhunath, 2018-06-03
 */
public class MPException extends Exception {

    public MPException(final String message) {
        super( message );
    }

    public MPException(final String message, final Throwable cause) {
        super( message, cause );
    }
}
0707010000002D000081A400000000000000000000000163C2D55500000B37000000000000000000000000000000000000006600000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/MPIdenticon.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

package com.lyndir.masterpassword;

import static com.lyndir.lhunath.opal.system.util.StringUtils.*;

import com.lyndir.lhunath.opal.system.logging.Logger;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Locale;


/**
 * @author lhunath, 15-03-29
 */
public class MPIdenticon {

    @SuppressWarnings("UnusedDeclaration")
    private static final Logger logger = Logger.get( MPIdenticon.class );

    private final String userName;
    private final String leftArm;
    private final String body;
    private final String rightArm;
    private final String accessory;
    private final Color  color;

    @SuppressFBWarnings("CLI_CONSTANT_LIST_INDEX")
    @SuppressWarnings("MethodCanBeVariableArityMethod")
    public MPIdenticon(final String userName, final String leftArm, final String body, final String rightArm, final String accessory,
                       final Color color) {
        this.userName = userName;
        this.leftArm = leftArm;
        this.body = body;
        this.rightArm = rightArm;
        this.accessory = accessory;
        this.color = color;
    }

    public String getUserName() {
        return userName;
    }

    public String getText() {
        return strf( "%s%s%s%s", this.leftArm, this.body, this.rightArm, this.accessory );
    }

    public String getHTML() {
        return strf( "<span style='color: %s'>%s</span>", color.getCSS(), getText() );
    }

    public Color getColor() {
        return color;
    }

    public enum Color {
        UNSET {
            @Override
            public String getCSS() {
                return "inherit";
            }
        },
        RED,
        GREEN,
        YELLOW,
        BLUE,
        MAGENTA,
        CYAN,
        MONO {
            @Override
            public String getCSS() {
                return "inherit";
            }
        };

        public String getCSS() {
            return name().toLowerCase( Locale.ROOT );
        }
    }
}
0707010000002E000081A400000000000000000000000163C2D55500000CB0000000000000000000000000000000000000006700000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/MPKeyPurpose.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

package com.lyndir.masterpassword;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.Locale;
import javax.annotation.Nullable;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;


/**
 * @author lhunath, 14-12-02
 */
public enum MPKeyPurpose {
    /**
     * Generate a key for authentication.
     */
    Authentication( "authentication", "Generate a key for authentication.", "com.lyndir.masterpassword" ),

    /**
     * Generate a name for identification.
     */
    Identification( "identification", "Generate a name for identification.", "com.lyndir.masterpassword.login" ),

    /**
     * Generate a recovery token.
     */
    Recovery( "recovery", "Generate a recovery token.", "com.lyndir.masterpassword.answer" );

    static final Logger logger = Logger.get( MPResultType.class );

    private final String shortName;
    private final String description;
    private final String scope;

    MPKeyPurpose(final String shortName, final String description, @NonNls final String scope) {
        this.shortName = shortName;
        this.description = description;
        this.scope = scope;
    }

    public String getShortName() {
        return shortName;
    }

    public String getDescription() {
        return description;
    }

    public String getScope() {
        return scope;
    }

    /**
     * @param shortNamePrefix The name for the purpose to look up.  It is a case insensitive prefix of the purpose's short name.
     *
     * @return The purpose registered with the given name.
     */
    @Nullable
    @Contract("!null -> !null")
    public static MPKeyPurpose forName(@Nullable final String shortNamePrefix) {

        if (shortNamePrefix == null)
            return null;

        for (final MPKeyPurpose type : values())
            if (type.getShortName().toLowerCase( Locale.ROOT ).startsWith( shortNamePrefix.toLowerCase( Locale.ROOT ) ))
                return type;

        throw logger.bug( "No purpose for name: %s", shortNamePrefix );
    }

    @JsonCreator
    public static MPKeyPurpose forInt(final int keyPurpose) {

        return values()[keyPurpose];
    }

    @JsonValue
    public int toInt() {

        return ordinal();
    }
}
0707010000002F000081A400000000000000000000000163C2D555000004E5000000000000000000000000000000000000007400000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/MPKeyUnavailableException.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

package com.lyndir.masterpassword;

/**
 * @author lhunath, 2017-09-21
 */
public class MPKeyUnavailableException extends MPException {

    public MPKeyUnavailableException(final String message) {
        super( message );
    }

    public MPKeyUnavailableException(final String message, final Throwable cause) {
        super( message, cause );
    }
}
07070100000030000081A400000000000000000000000163C2D55500002985000000000000000000000000000000000000006700000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/MPResultType.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

package com.lyndir.masterpassword;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.List;
import java.util.Locale;
import javax.annotation.Nullable;
import org.jetbrains.annotations.Contract;


/**
 * <i>07 04, 2012</i>
 *
 * @author lhunath
 */
@SuppressWarnings({ "RedundantTypeArguments", "SpellCheckingInspection" })
public enum MPResultType {
    // bit 0-3 | MPResultTypeClass | MPSiteFeature

    /**
     * 16: pg^VMAUBk5x3p%HP%i4=
     */
    GeneratedMaximum( "maximum", "Maximum Security", "pg^VMAUBk5x3p%HP%i4=", "20 characters, contains symbols", //
                      ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ),
                                        new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), //
                      MPResultTypeClass.Template, 0x0 ),

    /**
     * 17: BiroYena8:Kixa
     */
    GeneratedLong( "long", "Long Password", "BiroYena8:Kixa", "Copy-friendly, 14 characters, contains symbols", //
                   ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ),
                                     new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ),
                                     new MPTemplate( "CvccCvcvnoCvcv" ), new MPTemplate( "CvccCvcvCvcvno" ),
                                     new MPTemplate( "CvcvnoCvccCvcv" ), new MPTemplate( "CvcvCvccnoCvcv" ),
                                     new MPTemplate( "CvcvCvccCvcvno" ), new MPTemplate( "CvcvnoCvcvCvcc" ),
                                     new MPTemplate( "CvcvCvcvnoCvcc" ), new MPTemplate( "CvcvCvcvCvccno" ),
                                     new MPTemplate( "CvccnoCvccCvcv" ), new MPTemplate( "CvccCvccnoCvcv" ),
                                     new MPTemplate( "CvccCvccCvcvno" ), new MPTemplate( "CvcvnoCvccCvcc" ),
                                     new MPTemplate( "CvcvCvccnoCvcc" ), new MPTemplate( "CvcvCvccCvccno" ),
                                     new MPTemplate( "CvccnoCvcvCvcc" ), new MPTemplate( "CvccCvcvnoCvcc" ),
                                     new MPTemplate( "CvccCvcvCvccno" ) ), //
                   MPResultTypeClass.Template, 0x1 ),

    /**
     * 18: BirSuj0-
     */
    GeneratedMedium( "medium", "Medium Password", "BirSuj0-", "Copy-friendly, 8 characters, contains symbols", //
                     ImmutableList.of( new MPTemplate( "CvcnoCvc" ),
                                       new MPTemplate( "CvcCvcno" ) ), //
                     MPResultTypeClass.Template, 0x2 ),

    /**
     * 19: Bir8
     */
    GeneratedShort( "short", "Short Password", "Bir8", "Copy-friendly, 4 characters, no symbols", //
                    ImmutableList.of( new MPTemplate( "Cvcn" ) ), //
                    MPResultTypeClass.Template, 0x3 ),

    /**
     * 20: pO98MoD0
     */
    GeneratedBasic( "basic", "Basic Password", "pO98MoD0", "8 characters, no symbols", //
                    ImmutableList.of( new MPTemplate( "aaanaaan" ),
                                      new MPTemplate( "aannaaan" ),
                                      new MPTemplate( "aaannaaa" ) ), //
                    MPResultTypeClass.Template, 0x4 ),

    /**
     * 21: 2798
     */
    GeneratedPIN( "pin", "PIN Code", "2798", "4 numbers", //
                  ImmutableList.of( new MPTemplate( "nnnn" ) ), //
                  MPResultTypeClass.Template, 0x5 ),

    /**
     * 30: birsujano
     */
    GeneratedName( "name", "Name", "birsujano", "9 letter name", //
                   ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), //
                   MPResultTypeClass.Template, 0xE ),

    /**
     * 31: bir yennoquce fefi
     */
    GeneratedPhrase( "phrase", "Phrase", "bir yennoquce fefi", "20 character sentence", //
                     ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ),
                                       new MPTemplate( "cvc cvccvcvcv cvcv" ),
                                       new MPTemplate( "cv cvccv cvc cvcvccv" ) ), //
                     MPResultTypeClass.Template, 0xF ),

    /**
     * 1056: Custom saved value.
     */
    StoredPersonal( "personal", "Saved", null, "AES-encrypted, exportable", //
                    ImmutableList.<MPTemplate>of(), //
                    MPResultTypeClass.Stateful, 0x0, MPSiteFeature.ExportContent ),

    /**
     * 2081: Custom saved value that should not be exported from the device.
     */
    StoredDevicePrivate( "device", "Private", null, "AES-encrypted, not exported", //
                         ImmutableList.<MPTemplate>of(), //
                         MPResultTypeClass.Stateful, 0x1, MPSiteFeature.DevicePrivate ),

    /**
     * 4160: Derive a unique binary key.
     */
    DeriveKey( "key", "Binary Key", null, "Encryption key", //
               ImmutableList.<MPTemplate>of(), //
               MPResultTypeClass.Derive, 0x0, MPSiteFeature.Alternative );

    static final Logger logger = Logger.get( MPResultType.class );

    private final String                      shortName;
    private final String                      longName;

    @Nullable
    private final String                      sample;
    private final String                      description;
    private final List<MPTemplate>            templates;
    private final MPResultTypeClass           typeClass;
    private final int                         typeIndex;
    private final ImmutableSet<MPSiteFeature> typeFeatures;

    MPResultType(final String shortName, final String longName, @Nullable final String sample, final String description,
                 final List<MPTemplate> templates,
                 final MPResultTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) {

        this.shortName = shortName;
        this.longName = longName;
        this.sample = sample;
        this.description = description;
        this.templates = templates;
        this.typeClass = typeClass;
        this.typeIndex = typeIndex;

        ImmutableSet.Builder<MPSiteFeature> typeFeaturesBuilder = ImmutableSet.builder();
        for (final MPSiteFeature typeFeature : typeFeatures) {
            typeFeaturesBuilder.add( typeFeature );
        }
        this.typeFeatures = typeFeaturesBuilder.build();
    }

    public String getShortName() {
        return shortName;
    }

    public String getLongName() {
        return longName;
    }

    @Nullable
    public String getSample() {
        return sample;
    }

    public String getDescription() {

        return description;
    }

    public MPResultTypeClass getTypeClass() {

        return typeClass;
    }

    @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType" /* IDEA-191042 */)
    public ImmutableSet<MPSiteFeature> getTypeFeatures() {

        return typeFeatures;
    }

    public boolean supportsTypeFeature(final MPSiteFeature feature) {

        return typeFeatures.contains( feature );
    }

    @JsonValue
    public int getType() {
        int mask = typeIndex | typeClass.getMask();
        for (final MPSiteFeature typeFeature : typeFeatures)
            mask |= typeFeature.getMask();

        return mask;
    }

    /**
     * @param shortNamePrefix The name for the type to look up.  It is a case insensitive prefix of the type's short name.
     *
     * @return The type registered with the given name.
     */
    @Nullable
    @Contract("!null -> !null")
    public static MPResultType forName(@Nullable final String shortNamePrefix) {

        if (shortNamePrefix == null)
            return null;

        for (final MPResultType type : values())
            if (type.getShortName().toLowerCase( Locale.ROOT ).startsWith( shortNamePrefix.toLowerCase( Locale.ROOT ) ))
                return type;

        throw logger.bug( "No type for name: %s", shortNamePrefix );
    }

    /**
     * @param typeClass The class for which we look up types.
     *
     * @return All types that support the given class.
     */
    public static ImmutableList<MPResultType> forClass(final MPResultTypeClass typeClass) {

        ImmutableList.Builder<MPResultType> types = ImmutableList.builder();
        for (final MPResultType type : values())
            if (type.getTypeClass() == typeClass)
                types.add( type );

        return types.build();
    }

    /**
     * @param type The type for which we look up types.
     *
     * @return The type registered with the given type.
     */
    @JsonCreator
    public static MPResultType forType(final int type) {

        for (final MPResultType resultType : values())
            if (resultType.getType() == type)
                return resultType;

        throw logger.bug( "No type: %s", type );
    }

    /**
     * @param mask The type mask for which we look up types.
     *
     * @return All types that support the given mask's class & features.
     */
    @SuppressWarnings({ "MagicNumber", "UnnecessaryParentheses" /* IDEA-191040 */ })
    public static ImmutableList<MPResultType> forMask(final int mask) {

        int typeMask = mask & ~0xF; // Ignore resultType bit 0-3

        ImmutableList.Builder<MPResultType> types = ImmutableList.builder();
        for (final MPResultType resultType : values())
            if (((resultType.getType() & ~0xF) & typeMask) != 0)
                types.add( resultType );

        return types.build();
    }

    public MPTemplate getTemplateAtRollingIndex(final int templateIndex) {
        return templates.get( templateIndex % templates.size() );
    }
}
07070100000031000081A400000000000000000000000163C2D555000005DC000000000000000000000000000000000000006C00000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/MPResultTypeClass.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

package com.lyndir.masterpassword;

/**
 * <i>07 04, 2012</i>
 *
 * @author lhunath
 */
public enum MPResultTypeClass {
    // bit 4 - 9

    /**
     * Use the site key to generate a password from a template.
     */
    Template( 1 << 4 ),
    /**
     * Use the site key to encrypt and decrypt a stateful entity.
     */
    Stateful( 1 << 5 ),
    /**
     * Use the site key to derive a site-specific object.
     */
    Derive( 1 << 6 );

    private final int mask;

    MPResultTypeClass(final int mask) {
        this.mask = mask;
    }

    public int getMask() {
        return mask;
    }
}
07070100000032000081A400000000000000000000000163C2D555000005BA000000000000000000000000000000000000006800000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/MPSiteFeature.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

package com.lyndir.masterpassword;

/**
 * <i>07 04, 2012</i>
 *
 * @author lhunath
 */
public enum MPSiteFeature {
    // bit 10 - 15

    /**
     * Export the key-protected content data.
     */
    ExportContent( 1 << 10 ),

    /**
     * Never export content.
     */
    DevicePrivate( 1 << 11 ),

    /**
     * Don't use this as the primary authentication result type.
     */
    Alternative( 1 << 12 );

    MPSiteFeature(final int mask) {
        this.mask = mask;
    }

    private final int mask;

    public int getMask() {
        return mask;
    }
}
07070100000033000081A400000000000000000000000163C2D555000008DF000000000000000000000000000000000000006500000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/MPTemplate.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

package com.lyndir.masterpassword;

import static com.lyndir.lhunath.opal.system.util.StringUtils.*;

import com.google.common.collect.ImmutableList;
import com.lyndir.lhunath.opal.system.util.MetaObject;
import java.io.Serializable;
import java.util.List;
import org.jetbrains.annotations.NonNls;


/**
 * <i>07 04, 2012</i>
 *
 * @author lhunath
 */
public class MPTemplate extends MetaObject implements Serializable {

    private static final long serialVersionUID = 1L;

    private final String                         templateString;
    private final List<MPTemplateCharacterClass> template;

    MPTemplate(@NonNls final String templateString) {

        ImmutableList.Builder<MPTemplateCharacterClass> builder = ImmutableList.builder();
        for (int i = 0; i < templateString.length(); ++i)
            builder.add( MPTemplateCharacterClass.forIdentifier( templateString.charAt( i ) ) );

        this.templateString = templateString;
        template = builder.build();
    }

    public String getTemplateString() {
        return templateString;
    }

    public MPTemplateCharacterClass getCharacterClassAtIndex(final int index) {

        return template.get( index );
    }

    public int length() {

        return template.size();
    }

    @Override
    public String toString() {
        return strf( "{MPTemplate: %s}", templateString );
    }
}
07070100000034000081A400000000000000000000000163C2D55500000A4E000000000000000000000000000000000000007300000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/MPTemplateCharacterClass.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

package com.lyndir.masterpassword;

import com.lyndir.lhunath.opal.system.logging.Logger;
import org.jetbrains.annotations.NonNls;


/**
 * <i>07 04, 2012</i>
 *
 * @author lhunath
 */
@SuppressWarnings({ "HardcodedFileSeparator", "SpellCheckingInspection" })
public enum MPTemplateCharacterClass {

    UpperVowel( 'V', "AEIOU" ),
    UpperConsonant( 'C', "BCDFGHJKLMNPQRSTVWXYZ" ),
    LowerVowel( 'v', "aeiou" ),
    LowerConsonant( 'c', "bcdfghjklmnpqrstvwxyz" ),
    UpperAlphanumeric( 'A', "AEIOUBCDFGHJKLMNPQRSTVWXYZ" ),
    Alphanumeric( 'a', "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz" ),
    Numeric( 'n', "0123456789" ),
    Other( 'o', "@&%?,=[]_:-+*$#!'^~;()/." ),
    Any( 'x', "AEIOUaeiouBCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz0123456789!@#$%^&*()" ),
    Space( ' ', " " );

    @SuppressWarnings("UnusedDeclaration")
    private static final Logger logger = Logger.get( MPTemplateCharacterClass.class );

    private final char   identifier;
    private final char[] characters;

    MPTemplateCharacterClass(final char identifier, @NonNls final String characters) {

        this.identifier = identifier;
        this.characters = characters.toCharArray();
    }

    public char getIdentifier() {

        return identifier;
    }

    public char getCharacterAtRollingIndex(final int index) {

        return characters[index % characters.length];
    }

    public static MPTemplateCharacterClass forIdentifier(final char identifier) {
        for (final MPTemplateCharacterClass characterClass : values())
            if (characterClass.getIdentifier() == identifier)
                return characterClass;

        throw logger.bug( "No character class defined for identifier: %s", identifier );
    }
}
07070100000035000081A400000000000000000000000163C2D5550000200D000000000000000000000000000000000000006400000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/MPUserKey.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

package com.lyndir.masterpassword;

import com.google.common.base.Preconditions;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.Arrays;
import java.util.EnumMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;


/**
 * @author lhunath, 2014-08-30
 */
public class MPUserKey {

    @SuppressWarnings("UnusedDeclaration")
    private static final Logger logger = Logger.get( MPUserKey.class );

    private final EnumMap<MPAlgorithm.Version, byte[]> keyByVersion = new EnumMap<>( MPAlgorithm.Version.class );
    private final String                               userName;
    private final char[]                               userSecret;

    private boolean invalidated;

    /**
     * @param userSecret The characters of the user's master password.
     *
     * @apiNote This method destroys the contents of the {@code userSecret} array.
     */
    @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
    public MPUserKey(final String userName, final char[] userSecret) {

        this.userName = userName;
        this.userSecret = userSecret.clone();
        Arrays.fill( userSecret, (char) 0 );
    }

    @Override
    @SuppressWarnings("deprecation")
    protected void finalize()
            throws Throwable {

        if (isValid()) {
            logger.wrn( "A master key for %s was abandoned without being invalidated.", getUserName() );
            invalidate();
        }

        super.finalize();
    }

    @Nonnull
    public String getUserName() {

        return userName;
    }

    /**
     * Calculate an identifier for the master key.
     *
     * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
     */
    @Nonnull
    public String getKeyID(final MPAlgorithm algorithm)
            throws MPKeyUnavailableException, MPAlgorithmException {

        return algorithm.toID( userKey( algorithm ) );
    }

    /**
     * Wipe this key's secrets from memory, making the object permanently unusable.
     */
    public void invalidate() {

        invalidated = true;
        for (final byte[] key : keyByVersion.values())
            Arrays.fill( key, (byte) 0 );
        Arrays.fill( userSecret, (char) 0 );
    }

    public boolean isValid() {
        return !invalidated;
    }

    @Nonnull
    private byte[] userKey(final MPAlgorithm algorithm)
            throws MPKeyUnavailableException, MPAlgorithmException {
        Preconditions.checkArgument( userSecret.length > 0 );

        if (!isValid())
            throw new MPKeyUnavailableException( "Master key was invalidated." );

        byte[] userKey = keyByVersion.get( algorithm.version() );
        if (userKey == null) {
            keyByVersion.put( algorithm.version(), userKey = algorithm.userKey( userName, userSecret ) );
        }
        if (userKey == null)
            throw new MPAlgorithmException( "Could not derive master key." );

        return userKey;
    }

    @Nonnull
    private byte[] siteKey(final String siteName, final MPAlgorithm algorithm, final UnsignedInteger siteCounter,
                           final MPKeyPurpose keyPurpose, @Nullable final String keyContext)
            throws MPKeyUnavailableException, MPAlgorithmException {
        Preconditions.checkArgument( !siteName.isEmpty() );

        byte[] userKey = userKey( algorithm );
        byte[] siteKey = algorithm.siteKey( userKey, siteName, siteCounter, keyPurpose, keyContext );
        if (siteKey == null)
            throw new MPAlgorithmException( "Could not derive site key." );

        return siteKey;
    }

    /**
     * Generate a token for use with site.
     *
     * @param siteName    The site's identifier.
     * @param siteCounter The result's generation.
     * @param keyPurpose  The intended purpose for the site token.
     * @param keyContext  The purpose-specific context for this token.
     * @param resultType  The type of token we're deriving.
     * @param resultParam Type-specific contextual data for the derivation.
     *                    In the case of {@link MPResultTypeClass#Stateful} types, the result of
     *                    {@link #siteState(String, MPAlgorithm, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)}.
     *
     * @return {@code null} if the result type is missing a required parameter.
     *
     * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
     * @throws MPAlgorithmException      An internal system or algorithm error has occurred.
     */
    @Nullable
    public String siteResult(final String siteName, final MPAlgorithm algorithm, final UnsignedInteger siteCounter,
                             final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
                             final MPResultType resultType, @Nullable final String resultParam)
            throws MPKeyUnavailableException, MPAlgorithmException {

        if ((resultType.getTypeClass() == MPResultTypeClass.Stateful) && (resultParam == null))
            return null;

        byte[] userKey = userKey( algorithm );
        byte[] siteKey   = siteKey( siteName, algorithm, siteCounter, keyPurpose, keyContext );

        String siteResult = algorithm.siteResult(
                userKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
        if (siteResult == null)
            throw new MPAlgorithmException( "Could not derive site result." );

        return siteResult;
    }

    /**
     * Encrypt a stateful site token for persistence.
     *
     * @param siteName    The site's identifier.
     * @param siteCounter The result's generation.
     * @param keyPurpose  The intended purpose for the site token.
     * @param keyContext  The purpose-specific context for this token.
     * @param resultType  The type of token we're deriving.
     * @param resultParam The original token that this method's state should reconstruct when passed into
     *                    {@link #siteResult(String, MPAlgorithm, UnsignedInteger, MPKeyPurpose, String, MPResultType, String)}.
     *
     * @throws MPKeyUnavailableException {@link #invalidate()} has been called on this object.
     * @throws MPAlgorithmException      An internal system or algorithm error has occurred.
     */
    @Nonnull
    public String siteState(final String siteName, final MPAlgorithm algorithm, final UnsignedInteger siteCounter,
                            final MPKeyPurpose keyPurpose, @Nullable final String keyContext,
                            final MPResultType resultType, final String resultParam)
            throws MPKeyUnavailableException, MPAlgorithmException {

        Preconditions.checkNotNull( resultParam );
        Preconditions.checkArgument( !resultParam.isEmpty() );

        byte[] userKey = userKey( algorithm );
        byte[] siteKey   = siteKey( siteName, algorithm, siteCounter, keyPurpose, keyContext );

        String siteState = algorithm.siteState(
                userKey, siteKey, siteName, siteCounter, keyPurpose, keyContext, resultType, resultParam );
        if (siteState == null)
            throw new MPAlgorithmException( "Could not derive site state." );

        return siteState;
    }
}
07070100000036000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000005A00000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/impl07070100000037000081A400000000000000000000000163C2D55500001DB5000000000000000000000000000000000000006600000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/impl/Native.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

package com.lyndir.masterpassword.impl;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteStreams;
import com.lyndir.lhunath.opal.system.logging.Logger;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.*;
import java.util.*;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;


/**
 * @author lhunath, 2018-05-22
 */
public final class Native {

    private static final Logger logger = Logger.get( Native.class );

    @SuppressWarnings("HardcodedFileSeparator")
    private static final char   RESOURCE_SEPARATOR  = '/';
    private static final char   EXTENSION_SEPARATOR = '.';
    private static final String NATIVES_PATH        = "lib";

    @SuppressFBWarnings({"PATH_TRAVERSAL_IN", "IOI_USE_OF_FILE_STREAM_CONSTRUCTORS", "EXS_EXCEPTION_SOFTENING_RETURN_FALSE"})
    @SuppressWarnings({ "HardcodedFileSeparator", "LoadLibraryWithNonConstantString" })
    public static boolean load(final Class<?> context, final String name) {

        // Try to load the library using the native system.
        try {
            System.loadLibrary( name );
            return true;
        }
        catch (@SuppressWarnings("ErrorNotRethrown") final UnsatisfiedLinkError ignored) {
        }

        // Try to find and open a stream to the packaged library resource.
        String             library          = System.mapLibraryName( name );
        int                libraryDot       = library.lastIndexOf( EXTENSION_SEPARATOR );
        String             libraryName      = (libraryDot > 0)? library.substring( 0, libraryDot ): library;
        String             libraryExtension = (libraryDot > 0)? library.substring( libraryDot ): ".lib";

        @Nullable
        File libraryFile = null;
        Set<String> libraryResources = getLibraryResources( library );
        for (final String libraryResource : libraryResources) {
            try {
                InputStream libraryStream = context.getResourceAsStream( libraryResource );
                if (libraryStream == null) {
                    logger.dbg( "No resource for library: %s", libraryResource );
                    continue;
                }

                // Write the library resource to a temporary file.
                libraryFile = File.createTempFile( libraryName, libraryExtension );
                libraryFile.deleteOnExit();
                FileOutputStream libraryFileStream = new FileOutputStream( libraryFile );
                try {
                    ByteStreams.copy( libraryStream, libraryFileStream );
                }
                finally {
                    libraryFileStream.close();
                    libraryStream.close();
                }

                // Load the library from the temporary file.
                System.load( libraryFile.getAbsolutePath() );
                return true;
            }
            catch (@SuppressWarnings("ErrorNotRethrown") final IOException | UnsatisfiedLinkError e) {
                logger.wrn( e, "Couldn't load library: %s", libraryResource );

                if (libraryFile != null && libraryFile.exists() && !libraryFile.delete())
                    logger.wrn( "Couldn't clean up library file: %s", libraryFile );
                libraryFile = null;
            }
        }

        return false;
    }

    @Nonnull
    private static Set<String> getLibraryResources(final String library) {
        // Standardize system naming in accordance with masterpassword-core.
        Sys system = Sys.findCurrent();

        // Standardize architecture naming in accordance with masterpassword-core.
        Collection<Arch> architectures = new LinkedHashSet<>();
        architectures.add( Arch.findCurrent() );
        architectures.addAll( Arrays.asList( Arch.values() ) );

        ImmutableSet.Builder<String> resources = ImmutableSet.builder();
        for (final Arch arch : architectures)
            resources.add( Joiner.on( RESOURCE_SEPARATOR ).join( "", NATIVES_PATH, system, arch, library ) );

        return resources.build();
    }

    private enum Sys implements Predicate<String> {
        windows {
            @Override
            public boolean test(final String system) {
                return system.contains( "windows" );
            }
        },
        macos {
            @Override
            public boolean test(final String system) {
                return system.contains( "mac os x" ) || system.contains( "darwin" ) || system.contains( "osx" );
            }
        },
        linux {
            @Override
            public boolean test(final String system) {
                return system.contains( "linux" );
            }
        };

        @Nonnull
        public static Sys findCurrent() {
            return find( System.getProperty( "os.name" ) );
        }

        @Nonnull
        public static Sys find(@Nullable String name) {
            if (name != null) {
                name = name.toLowerCase( Locale.ROOT );

                for (final Sys sys : values())
                    if (sys.test( name ))
                        return sys;
            }

            return linux;
        }
    }


    private enum Arch implements Predicate<String> {
        arm {
            @Override
            public boolean test(final String architecture) {
                return ImmutableList.of( "arm", "arm-v7", "armv7", "arm32" ).contains( architecture );
            }
        },
        arm64 {
            @Override
            public boolean test(final String architecture) {
                return architecture.startsWith( "arm" ) && !arm.test( architecture );
            }
        },
        x86_64 {
            @Override
            public boolean test(final String architecture) {
                return ImmutableList.of( "x86_64", "amd64", "x64", "x86-64" ).contains( architecture );
            }
        },
        x86 {
            @Override
            public boolean test(final String architecture) {
                return ImmutableList.of( "x86", "i386", "i686" ).contains( architecture );
            }
        };

        @Nonnull
        public static Arch findCurrent() {
            return find( System.getProperty( "os.arch" ) );
        }

        @Nonnull
        public static Arch find(@Nullable String name) {
            if (name != null) {
                name = name.toLowerCase( Locale.ROOT );

                for (final Arch arch : values())
                    if (arch.test( name ))
                        return arch;
            }

            return x86;
        }
    }
}
07070100000038000081A400000000000000000000000163C2D55500000427000000000000000000000000000000000000006C00000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/impl/package-info.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

/**
 * @author lhunath, 2018-05-15
 */
@ParametersAreNonnullByDefault
package com.lyndir.masterpassword.impl;

import javax.annotation.ParametersAreNonnullByDefault;
07070100000039000081A400000000000000000000000163C2D55500000421000000000000000000000000000000000000006700000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/package-info.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

/**
 * @author lhunath, 15-02-04
 */

@ParametersAreNonnullByDefault
package com.lyndir.masterpassword;

import javax.annotation.ParametersAreNonnullByDefault;
0707010000003A000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000005A00000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/util0707010000003B000081A400000000000000000000000163C2D55500000508000000000000000000000000000000000000006900000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/util/Utilities.javapackage com.lyndir.masterpassword.util;

import java.util.function.Consumer;
import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;


/**
 * @author lhunath, 2018-07-25
 */
public final class Utilities {

    @Nullable
    public static <T, R> R ifNotNull(@Nullable final T value, final Function<T, R> consumer) {
        if (value == null)
            return null;

        return consumer.apply( value );
    }

    @Nonnull
    public static <T> T ifNotNullElse(@Nullable final T value, @Nonnull final T nullValue) {
        if (value == null)
            return nullValue;

        return value;
    }

    public static String ifNotNullOrEmptyElse(@Nullable final String value, @Nonnull final String emptyValue) {
        if ((value == null) || value.isEmpty())
            return emptyValue;

        return value;
    }

    @Nonnull
    public static <T, R> R ifNotNullElse(@Nullable final T value, final Function<T, R> consumer, @Nonnull final R nullValue) {
        if (value == null)
            return nullValue;

        return consumer.apply( value );
    }

    public static <T> void ifNotNullDo(@Nullable final T value, final Consumer<T> consumer) {
        if (value != null)
            consumer.accept( value );
    }
}
0707010000003C000081A400000000000000000000000163C2D555000000A6000000000000000000000000000000000000006C00000000spectre-cli-3.0+git20230114.26c01a5/api/java/src/main/java/com/lyndir/masterpassword/util/package-info.java/**
 * @author lhunath, 2018-07-25
 */
@ParametersAreNonnullByDefault
package com.lyndir.masterpassword.util;

import javax.annotation.ParametersAreNonnullByDefault;
0707010000003D000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000003300000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests0707010000003E000081A400000000000000000000000163C2D555000002DF000000000000000000000000000000000000004000000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/build.gradleplugins {
    id 'java'
}

description = 'Master Password Test Suite'

dependencies {
    implementation group: 'com.lyndir.lhunath.opal', name: 'opal-system', version: '1.7-p2'
    implementation group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1'
    implementation group: 'com.github.spotbugs', name: 'spotbugs-annotations', version: '4.2.1'

    implementation project( ':masterpassword-algorithm' )
    implementation project( ':masterpassword-model' )

    testImplementation group: 'org.testng', name: 'testng', version: '6.8.5'
    testImplementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2'

    runtime group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '2.3.1'
}

test.useTestNG()
0707010000003F000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000003700000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src07070100000040000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000003C00000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/main07070100000041000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000004100000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/main/java07070100000042000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000004500000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/main/java/com07070100000043000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000004C00000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/main/java/com/lyndir07070100000044000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000005B00000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/main/java/com/lyndir/masterpassword07070100000045000081A400000000000000000000000163C2D555000022DD000000000000000000000000000000000000006C00000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/main/java/com/lyndir/masterpassword/MPTestSuite.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

package com.lyndir.masterpassword;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;
import java.util.concurrent.Callable;
import javax.xml.XMLConstants;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.ext.DefaultHandler2;


/**
 * @author lhunath, 2015-12-22
 */
@SuppressWarnings({ "HardCodedStringLiteral", "ProhibitedExceptionDeclared" })
public class MPTestSuite implements Callable<Boolean> {

    @SuppressWarnings("UnusedDeclaration")
    private static final Logger           logger                = Logger.get( MPTestSuite.class );
    private static final String           DEFAULT_RESOURCE_NAME = "mpw_tests.xml";
    private static final SAXParserFactory factory               = SAXParserFactory.newInstance();

    static {
        try {
            factory.setFeature( XMLConstants.FEATURE_SECURE_PROCESSING, true );
        }
        catch (ParserConfigurationException | SAXNotRecognizedException | SAXNotSupportedException e) {
            throw new UnsupportedOperationException( e );
        }
    }

    private final MPTests  tests;
    private       Listener listener;

    public MPTestSuite()
            throws UnavailableException {
        this( DEFAULT_RESOURCE_NAME );
    }

    @SuppressFBWarnings("XXE_SAXPARSER")
    public MPTestSuite(final String resourceName)
            throws UnavailableException {
        try {
            tests = new MPTests();
            tests.cases = Lists.newLinkedList();
            SAXParser   parser         = factory.newSAXParser();
            InputStream resourceStream = Thread.currentThread().getContextClassLoader().getResourceAsStream( resourceName );
            if (resourceStream == null)
                throw new UnavailableException( new NullPointerException( "Missing resource: " + resourceName ) );

            parser.parse( resourceStream, new DefaultHandler2() {
                private final Deque<String> currentTags = Lists.newLinkedList();
                private final Deque<StringBuilder> currentTexts = Lists.newLinkedList();
                private MPTests.Case currentCase;

                @Override
                public void startElement(final String uri, final String localName, final String qName, final Attributes attributes)
                        throws SAXException {
                    super.startElement( uri, localName, qName, attributes );
                    currentTags.push( qName );
                    currentTexts.push( new StringBuilder() );

                    if ("case".equals( qName )) {
                        currentCase = new MPTests.Case();
                        currentCase.identifier = attributes.getValue( "id" );
                        currentCase.parent = attributes.getValue( "parent" );
                    }
                }

                @Override
                public void endElement(final String uri, final String localName, final String qName)
                        throws SAXException {
                    super.endElement( uri, localName, qName );
                    Preconditions.checkState( qName.equals( currentTags.pop() ) );
                    String text = Preconditions.checkNotNull( currentTexts.pop() ).toString();

                    if ("case".equals( qName ))
                        tests.cases.add( currentCase );
                    if ("algorithm".equals( qName ))
                        currentCase.algorithm = ConversionUtils.toInteger( text ).orElse( null );
                    if ("userName".equals( qName ))
                        currentCase.userName = text;
                    if ("userSecret".equals( qName ))
                        currentCase.userSecret = text;
                    if ("keyID".equals( qName ))
                        currentCase.keyID = text;
                    if ("siteName".equals( qName ))
                        currentCase.siteName = text;
                    if ("siteCounter".equals( qName ))
                        currentCase.siteCounter = text.isEmpty()? null: UnsignedInteger.valueOf( text );
                    if ("resultType".equals( qName ))
                        currentCase.resultType = text;
                    if ("keyPurpose".equals( qName ))
                        currentCase.keyPurpose = text;
                    if ("keyContext".equals( qName ))
                        currentCase.keyContext = text;
                    if ("result".equals( qName ))
                        currentCase.result = text;
                }

                @Override
                public void characters(final char[] ch, final int start, final int length)
                        throws SAXException {
                    super.characters( ch, start, length );

                    Preconditions.checkNotNull( currentTexts.peek() ).append( ch, start, length );
                }
            } );
        }
        catch (final IllegalArgumentException | ParserConfigurationException | SAXException | IOException e) {
            throw new UnavailableException( e );
        }

        for (final MPTests.Case testCase : tests.getCases())
            testCase.initializeParentHierarchy( tests );
    }

    public void setListener(final Listener listener) {
        this.listener = listener;
    }

    public MPTests getTests() {
        return tests;
    }

    public boolean forEach(final String testName, final TestCase testFunction)
            throws Exception {
        List<MPTests.Case> cases = tests.getCases();
        for (int c = 0; c < cases.size(); c++) {
            MPTests.Case testCase = cases.get( c );
            if (testCase.getResult().isEmpty())
                continue;

            progress( Logger.Target.INFO, c, cases.size(), //
                      "[%s] on %s...", testName, testCase.getIdentifier() );

            if (!testFunction.run( testCase )) {
                progress( Logger.Target.ERROR, cases.size(), cases.size(), //
                          "[%s] on %s: FAILED!", testName, testCase.getIdentifier() );

                return false;
            }

            progress( Logger.Target.INFO, c + 1, cases.size(), //
                      "[%s] on %s: passed!", testName, testCase.getIdentifier() );
        }

        return true;
    }

    private void progress(final Logger.Target target, final int current, final int max, final String format, final Object... args) {
        logger.log( target, format, args );

        if (listener != null)
            listener.progress( current, max, format, args );
    }

    @Override
    public Boolean call()
            throws Exception {
        return forEach( "mpw", testCase -> {
            MPUserKey userKey = new MPUserKey( testCase.getUserName(), testCase.getUserSecret().toCharArray() );
            String sitePassword = userKey.siteResult( testCase.getSiteName(), testCase.getAlgorithm(), testCase.getSiteCounter(),
                                                        testCase.getKeyPurpose(), testCase.getKeyContext(),
                                                        testCase.getResultType(), null );

            return testCase.getResult().equals( sitePassword );
        } );
    }

    public static class UnavailableException extends Exception {

        private static final long serialVersionUID = 1L;

        public UnavailableException(final Throwable cause) {
            super( cause );
        }
    }


    @FunctionalInterface
    public interface Listener {

        void progress(int current, int max, String messageFormat, Object... args);
    }


    @FunctionalInterface
    public interface TestCase {

        boolean run(MPTests.Case testCase)
                throws Exception;
    }
}
07070100000046000081A400000000000000000000000163C2D55500001EB1000000000000000000000000000000000000006800000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/main/java/com/lyndir/masterpassword/MPTests.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

package com.lyndir.masterpassword;

import static com.google.common.base.Preconditions.*;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import static com.lyndir.lhunath.opal.system.util.StringUtils.*;

import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.NNSupplier;
import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.xml.bind.annotation.XmlTransient;


/**
 * @author lhunath, 14-12-05
 */
public class MPTests {

    private static final String ID_DEFAULT = "default";

    @SuppressWarnings("UnusedDeclaration")
    private static final Logger logger = Logger.get( MPTests.class );

    private final Set<String> filters = new HashSet<>();

    List<Case> cases;

    @Nonnull
    public List<Case> getCases() {
        if (filters.isEmpty())
            return checkNotNull( cases );

        return checkNotNull( cases ).stream().filter( testCase -> {
            for (final String filter : filters)
                if (testCase.getIdentifier().startsWith( filter ))
                    return true;
            return false;
        } ).collect( Collectors.toList() );
    }

    public Case getCase(final String identifier) {
        for (final Case testCase : cases)
            if (identifier.equals( testCase.getIdentifier() ))
                return testCase;

        throw new IllegalArgumentException( strf( "No case for identifier: %s", identifier ) );
    }

    public Case getDefaultCase() {
        try {
            return getCase( ID_DEFAULT );
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalStateException( strf( "Missing default case in test suite.  Add a case with id: %s", ID_DEFAULT ), e );
        }
    }

    public boolean addFilters(final String... filters) {
        return this.filters.addAll( Arrays.asList( filters ) );
    }

    public static class Case {

        String identifier;
        String parent;
        @Nullable
        Integer algorithm;
        String userName;
        String userSecret;
        String keyID;
        String siteName;
        @Nullable
        UnsignedInteger siteCounter;
        String resultType;
        String keyPurpose;
        String keyContext;
        String result;

        @XmlTransient
        private Case parentCase;

        public void initializeParentHierarchy(final MPTests tests) {

            if (parent != null) {
                parentCase = tests.getCase( parent );
                parentCase.initializeParentHierarchy( tests );
            }

            algorithm = ifNotNullElse( algorithm, new NNSupplier<Integer>() {
                @Nonnull
                @Override
                public Integer get() {
                    return checkNotNull( parentCase.algorithm );
                }
            } );
            userName = ifNotNullElse( userName, new NNSupplier<String>() {
                @Nonnull
                @Override
                public String get() {
                    return checkNotNull( parentCase.userName );
                }
            } );
            userSecret = ifNotNullElse( userSecret, new NNSupplier<String>() {
                @Nonnull
                @Override
                public String get() {
                    return checkNotNull( parentCase.userSecret );
                }
            } );
            keyID = ifNotNullElse( keyID, new NNSupplier<String>() {
                @Nonnull
                @Override
                public String get() {
                    return checkNotNull( parentCase.keyID );
                }
            } );
            siteName = ifNotNullElse( siteName, new NNSupplier<String>() {
                @Nonnull
                @Override
                public String get() {
                    return checkNotNull( parentCase.siteName );
                }
            } );
            siteCounter = ifNotNullElse( siteCounter, new NNSupplier<UnsignedInteger>() {
                @Nonnull
                @Override
                public UnsignedInteger get() {
                    return checkNotNull( parentCase.siteCounter );
                }
            } );
            resultType = ifNotNullElse( resultType, new NNSupplier<String>() {
                @Nonnull
                @Override
                public String get() {
                    return checkNotNull( parentCase.resultType );
                }
            } );
            keyPurpose = ifNotNullElse( keyPurpose, new NNSupplier<String>() {
                @Nonnull
                @Override
                public String get() {
                    return checkNotNull( parentCase.keyPurpose );
                }
            } );
            keyContext = ifNotNullElse( keyContext, new NNSupplier<String>() {
                @Nonnull
                @Override
                public String get() {
                    return (parentCase == null)? "": checkNotNull( parentCase.keyContext );
                }
            } );
            result = ifNotNullElse( result, new NNSupplier<String>() {
                @Nonnull
                @Override
                public String get() {
                    return (parentCase == null)? "": checkNotNull( parentCase.result );
                }
            } );
        }

        @Nonnull
        public String getIdentifier() {
            return identifier;
        }

        @Nullable
        public Case getParentCase() {
            return parentCase;
        }

        @Nonnull
        public MPAlgorithm getAlgorithm() {
            return MPAlgorithm.Version.fromInt( checkNotNull( algorithm ) );
        }

        @Nonnull
        public String getUserName() {
            return checkNotNull( userName );
        }

        @Nonnull
        public String getUserSecret() {
            return checkNotNull( userSecret );
        }

        @Nonnull
        public String getKeyID() {
            return checkNotNull( keyID );
        }

        @Nonnull
        public String getSiteName() {
            return checkNotNull( siteName );
        }

        public UnsignedInteger getSiteCounter() {
            return ifNotNullElse( siteCounter, UnsignedInteger.valueOf( 1 ) );
        }

        @Nonnull
        public MPResultType getResultType() {
            return MPResultType.forName( checkNotNull( resultType ) );
        }

        @Nonnull
        public MPKeyPurpose getKeyPurpose() {
            return MPKeyPurpose.forName( checkNotNull( keyPurpose ) );
        }

        @Nonnull
        public String getKeyContext() {
            return checkNotNull( keyContext );
        }

        @Nonnull
        public String getResult() {
            return checkNotNull( result );
        }

        @Override
        public String toString() {
            return identifier;
        }
    }
}
07070100000047000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000004600000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/main/resources070701000000480000A1FF000000000000000000000001656E6A2400000022000000000000000000000000000000000000005400000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/main/resources/mpw_tests.xml../../../../../c/cli/mpw_tests.xml07070100000049000081A400000000000000000000000163C2D555000006E8000000000000000000000000000000000000005800000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/main/resources/test.mpsites.json{
    "export": {
        "format": 1,
        "redacted": true,
        "date": "2018-05-10T03:41:18Z",
        "_ext_mpw": {
            "save": "me"
        },
        "_ext_other": {
            "save": "me"
        }
    },
    "user": {
        "avatar": 3,
        "full_name": "Robert Lee Mitchell",
        "last_used": "2018-05-10T03:41:18Z",
        "key_id": "98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302",
        "algorithm": 3,
        "default_type": 17,
        "_ext_mpw": {
            "save": "me"
        },
        "_ext_other": {
            "save": "me"
        }
    },
    "sites": {
        "masterpasswordapp.com": {
            "type": 17,
            "counter": 1,
            "algorithm": 3,
            "login_type": 30,
            "uses": 2,
            "last_used": "2018-05-10T03:41:18Z",
            "questions": {
                "": {
                    "type": 31
                },
                "mother": {
                    "type": 31
                }
            },
            "_ext_mpw": {
                "url": "https://masterpasswordapp.com",
                "save": "me"
            },
            "_ext_other": {
                "save": "me"
            }
        },
        "personal.site": {
            "type": 1056,
            "counter": 1,
            "algorithm": 3,
            "password": "ZTgr4cY6L28wG7DsO+iz\/hrTQxM3UHz0x8ZU99LjgxjHG+bLIJygkbg\/7HdjEIFH6A3z+Dt2H1gpt9yPyQGZcewTiPXJX0pNpVsIKAAdzVNcUfYoqkWjoFRoZD7sM\/ctxWDH4JUuJ+rjoBkWtRLK9kYBvu7UD1QdlEZI\/wPKv1A=",
            "login_type": 30,
            "uses": 1,
            "last_used": "2018-05-10T03:48:35Z"
        }
    },
    "_ext_mpw": {
        "save": "me"
    },
    "_ext_other": {
        "save": "me"
    }
}
0707010000004A000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000003C00000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/test0707010000004B000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000004100000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/test/java0707010000004C000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000004500000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/test/java/com0707010000004D000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000004C00000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/test/java/com/lyndir0707010000004E000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000005B00000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/test/java/com/lyndir/masterpassword0707010000004F000081A400000000000000000000000163C2D5550000151D000000000000000000000000000000000000006E00000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/test/java/com/lyndir/masterpassword/MPUserKeyTest.java//==============================================================================
// This file is part of Master Password.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Master Password is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Master Password is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

package com.lyndir.masterpassword;

import static org.testng.Assert.*;

import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.security.SecureRandom;
import java.util.Random;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;


public class MPUserKeyTest {

    @SuppressWarnings("UnusedDeclaration")
    private static final Logger logger = Logger.get( MPUserKeyTest.class );
    private static final Random random = new SecureRandom();

    private MPTestSuite testSuite;

    @BeforeMethod
    public void setUp()
            throws Exception {

        testSuite = new MPTestSuite();
        //testSuite.getTests().addFilters( "v3_type_maximum" );
    }

    @Test
    public void testUserKey()
            throws Exception {

        testSuite.forEach( "testUserKey", testCase -> {
            char[]      userSecret = testCase.getUserSecret().toCharArray();
            MPUserKey userKey      = new MPUserKey( testCase.getUserName(), userSecret );

            // Test key
            assertTrue(
                    testCase.getKeyID().equalsIgnoreCase( userKey.getKeyID( testCase.getAlgorithm() ) ),
                    "[testUserKey] keyID mismatch for test case: " + testCase );

            // Test invalidation
            userKey.invalidate();
            try {
                userKey.getKeyID( testCase.getAlgorithm() );
                fail( "[testUserKey] invalidate ineffective for test case: " + testCase );
            }
            catch (final MPKeyUnavailableException ignored) {
            }
            assertNotEquals(
                    userSecret,
                    testCase.getUserSecret().toCharArray(),
                    "[testUserKey] userSecret not wiped for test case: " + testCase );

            return true;
        } );
    }

    @Test
    public void testSiteResult()
            throws Exception {

        testSuite.forEach( "testSiteResult", testCase -> {
            char[]      userSecret = testCase.getUserSecret().toCharArray();
            MPUserKey userKey      = new MPUserKey( testCase.getUserName(), userSecret );

            // Test site result
            assertEquals(
                    userKey.siteResult( testCase.getSiteName(), testCase.getAlgorithm(), testCase.getSiteCounter(),
                                          testCase.getKeyPurpose(),
                                          testCase.getKeyContext(), testCase.getResultType(),
                                          null ),
                    testCase.getResult(),
                    "[testSiteResult] result mismatch for test case: " + testCase );

            return true;
        } );
    }

    @Test
    public void testSiteState()
            throws Exception {

        MPTests.Case testCase       = testSuite.getTests().getDefaultCase();
        char[]       userSecret = testCase.getUserSecret().toCharArray();
        MPUserKey    userKey      = new MPUserKey( testCase.getUserName(), userSecret );

        String       password   = randomString( 8 );
        MPResultType resultType = MPResultType.StoredPersonal;
        for (final MPAlgorithm.Version algorithm : MPAlgorithm.Version.values()) {
            // Test site state
            String state = userKey.siteState( testCase.getSiteName(), algorithm, testCase.getSiteCounter(), testCase.getKeyPurpose(),
                                                testCase.getKeyContext(), resultType, password );
            String result = userKey.siteResult( testCase.getSiteName(), algorithm, testCase.getSiteCounter(), testCase.getKeyPurpose(),
                                                  testCase.getKeyContext(), resultType, state );

            assertEquals(
                    result,
                    password,
                    "[testSiteState] state mismatch for test case: " + testCase );
        }
    }

    private static String randomString(int length) {
        StringBuilder builder = new StringBuilder();

        while (length > 0) {
            int codePoint = random.nextInt( Character.MAX_CODE_POINT - Character.MIN_CODE_POINT ) + Character.MIN_CODE_POINT;
            if (!Character.isDefined( codePoint ) || (Character.getType( codePoint ) == Character.PRIVATE_USE) || Character.isSurrogate(
                    (char) codePoint ))
                continue;

            builder.appendCodePoint( codePoint );
            length--;
        }

        return builder.toString();
    }
}
07070100000050000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000004600000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/test/resources07070100000051000081A400000000000000000000000163C2D5550000017F000000000000000000000000000000000000005200000000spectre-cli-3.0+git20230114.26c01a5/api/java/tests/src/test/resources/logback.xml<configuration scan="false">

    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%-8relative %22c{0} [%-5level] %msg%n</pattern>
        </encoder>
    </appender>

    <logger name="com.lyndir" level="${mp.log.level:-TRACE}" />

    <root level="INFO">
        <appender-ref ref="stdout" />
    </root>

</configuration>
07070100000052000081A400000000000000000000000163C2D55500000874000000000000000000000000000000000000003000000000spectre-cli-3.0+git20230114.26c01a5/bashcomplib#!/usr/bin/env bash

# FIXME
#   partials are currently readline words, but these can't be reliably compared against literal data.  We need to make them literal first.. in a safe way.  Currently using xargs' quote parser as a hack.

# Process literal completion options in COMPREPLY
#
# 1. Filter COMPREPLY by excluding the options that do not match the word that is being completed.
# 2. Shell-escape the COMPREPLY words so they remain syntactical words when injected into the completed command.
# 3. Add a space after the words so successful completions advance to the next word
#    (we disabled this default behavior with -o nospace so we can do completions that don't want this, eg. directory names)
_comp_finish_completions() {
    local partial=$(xargs <<< "${COMP_WORDS[COMP_CWORD]}") # FIXME
    local word words=( "${COMPREPLY[@]}" )

    COMPREPLY=()
    for word in "${words[@]}"; do
        ( shopt -s nocasematch; [[ $word = $partial* ]] ) && COMPREPLY+=( "$(printf '%q ' "$word")" )
    done

    if (( ${#COMPREPLY[@]} > 1 )) && [[ $_comp_title ]]; then
        printf '\n%s:' "$_comp_title"
        unset _comp_title
    fi
}

# Perform pathname completion.
#
# 1. Populate COMPREPLY with pathnames.
# 2. Shell-escape the COMPREPLY words so they remain syntactical words when injected into the completed command.
# 3. Add a space after file names so successful completions advance to the next word.
#    Directory names are suffixed with a / instead so we can keep completing the files inside.
_comp_complete_path() {
    local partial=$(xargs <<< "${COMP_WORDS[COMP_CWORD]}")
    local path

    COMPREPLY=()
    for path in "$partial"*; do
        if [[ -d $path ]]; then
            COMPREPLY+=( "$(printf '%q/' "$path")" )

        elif [[ -e $path ]]; then
            COMPREPLY+=( "$(printf '%q ' "$path")" )

        fi
    done
}

_show_args() {
    echo
    local i=0
    for arg; do
        printf "arg %d: %s\n" "$((i++))" "$arg"
    done

    i=0
    for word in "${COMP_WORDS[@]}"; do
        printf "word %d: %s -> %s %s\n" "$i" "$word" "$(xargs <<< "$word")" "$( ((i == $COMP_CWORD)) && echo '<CWORD>' )"
        let i++
    done
}
07070100000053000081ED00000000000000000000000163C2D5550000D2D7000000000000000000000000000000000000002C00000000spectre-cli-3.0+git20230114.26c01a5/bashlib#! /usr/bin/env bash
# ___________________________________________________________________________ #
#                                                                             #
#       BashLIB -- A library for Bash scripting convenience.                  #
#                                                                             #
#                                                                             #
#    Licensed under the Apache License, Version 2.0 (the "License");          #
#    you may not use this file except in compliance with the License.         #
#    You may obtain a copy of the License at                                  #
#                                                                             #
#        http://www.apache.org/licenses/LICENSE-2.0                           #
#                                                                             #
#    Unless required by applicable law or agreed to in writing, software      #
#    distributed under the License is distributed on an "AS IS" BASIS,        #
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
#    See the License for the specific language governing permissions and      #
#    limitations under the License.                                           #
# ___________________________________________________________________________ #
#                                                                             #
#                                                                             #
# Copyright 2007-2013, lhunath                                                #
#   * http://www.lhunath.com                                                  #
#   * Maarten Billemont                                                       #
#                                                                             #



#  ______________________________________________________________________ 
# |                                                                      |
# |                                            .:  TABLE OF CONTENTS  :. |
# |______________________________________________________________________|
# 
#     chr decimal
# Outputs the character that has the given decimal ASCII value.
#
#     ord character
# Outputs the decimal ASCII value of the given character.
#
#     hex character
# Outputs the hexadecimal ASCII value of the given character.
#
#     unhex character
# Outputs the character that has the given decimal ASCII value.
#
#     max numbers...
# Outputs the highest of the given numbers.
#
#     min numbers...
# Outputs the lowest of the given numbers.
#
#     totime "YYYY-MM-DD HH:MM:SS.mmm"...
# Outputs the number of milliseconds in the given date string(s).
#
#     exists application
# Succeeds if the application is in PATH and is executable.
#
#     eol message
# Return termination punctuation for a message, if necessary.
#
#     hr pattern [length]
# Outputs a horizontal ruler of the given length in characters or the terminal column length otherwise.
#
#     cloc
# Outputs the current cursor location as two space-separated numbers: row column.
#
#     readwhile command [args]
# Outputs the characters typed by the user into the terminal's input buffer while running the given command.
#
#     log [format] [arguments...]
# Log an event at a certain importance level.
# The event is expressed as a printf(1) format argument.
#
#     ask [-c optionchars|-d default] [-s|-S maskchar] message...
# Ask a question and read the user's reply to it.  Then output the result on stdout.
#
#     trim lines ...
# Trim the whitespace off of the beginning and end of the given lines.
#
#     reverse [-0|-d delimitor] [elements ...] [<<< elements]
# Reverse the order of the given elements.
#
#     order [-0|-d char] [-[cC] isAscending|-n] [-t number] [elements ...] [<<< elements]
# Orders the elements in ascending order.
#
#     mutex file
# Open a mutual exclusion lock on the file, unless another process already owns one.
#
#     pushjob [poolsize] command
# Start an asynchronous command within a pool, waiting for space in the pool if it is full.
#
#     fsleep time
# Wait for the given (fractional) amount of seconds.
#
#     showHelp name description author [option description]...
# Generate a prettily formatted usage description of the application.
#
#     shquote [-e] [argument...]
# Shell-quote the arguments to make them safe for injection into bash code.
#
#     requote [string]
# Escape the argument string to make it safe for injection into a regex.
#
#     shorten [-p pwd] path [suffix]...
# Shorten an absolute path for pretty printing.
#
#     up .../path|num
# Walk the current working directory up towards root num times or until path is found.
#
#     buildarray name terms... -- elements...
# Create an array by adding all the terms to it for each element, replacing {} terms by the element.
#
#     inArray element array
# Checks whether a certain element is in the given array.
#
#     xpathNodes query [files...]
# Outputs every xpath node that matches the query on a separate line.
#
#     hideDebug [on|off]
# Toggle Bash's debugging mode off temporarily.
#
#     stackTrace
# Output the current script's function execution stack.
#
_tocHash=71e13f42e1ea82c1c7019b27a3bc71f3


#  ______________________________________________________________________ 
# |                                                                      |
# |                                         .:  GLOBAL CONFIGURATION  :. |
# |______________________________________________________________________|

# Unset all exported functions.  Exported functions are evil.
while read _ _ func; do
    command unset -f "$func"
done < <(command declare -Fx)

{
shopt -s extglob
shopt -s globstar
} 2>/dev/null ||:

# Generate Table Of Contents
genToc() {
    local line= comments=() usage= whatis= lineno=0 out= outhash= outline=
    while read -r line; do
        (( ++lineno ))

        [[ $line = '#'* ]] && comments+=("$line") && continue
        [[ $line = +([[:alnum:]])'() {' ]] && IFS='()' read func _ <<< "$line" && [[ $func != $FUNCNAME ]] && {
            usage=${comments[3]##'#'+( )}
            whatis=${comments[5]##'#'+( )}
            [[ $usage = $func* && $whatis = *. ]] || err "Malformed docs for %s (line %d)." "$func" "$lineno"

            printf -v outline '#     %s\n# %s\n#\n' "$usage" "$whatis"
            out+=$outline
        }
        comments=()
    done < ~/.bin/bashlib

    outhash=$(openssl md5 <<< "$out")
    if [[ $_tocHash = "$outhash" ]]; then
        inf 'Table of contents up-to-date.'
    else
        printf '%s' "$out"
        printf '_tocHash=%q' "$outhash"
        wrn 'Table of contents outdated.'
    fi
}



#  ______________________________________________________________________ 
# |                                                                      |
# |                                          .:  GLOBAL DECLARATIONS  :. |
# |______________________________________________________________________|

# Variables for convenience sequences.
bobber=(     '.' 'o' 'O' 'o' )
spinner=(    '-' \\  '|' '/' )
crosser=(    '+' 'x' '+' 'x' )
runner=(     '> >'           \
             '>> '           \
             ' >>'           )

# Variables for terminal requests.
[[ -t 2 && $TERM != dumb ]] && {
    COLUMNS=$({ tput cols   || tput co;} 2>&3) # Columns in a line
    LINES=$({   tput lines  || tput li;} 2>&3) # Lines on screen
    alt=$(      tput smcup  || tput ti      ) # Start alt display
    ealt=$(     tput rmcup  || tput te      ) # End   alt display
    hide=$(     tput civis  || tput vi      ) # Hide cursor
    show=$(     tput cnorm  || tput ve      ) # Show cursor
    save=$(     tput sc                     ) # Save cursor
    load=$(     tput rc                     ) # Load cursor
    dim=$(      tput dim    || tput mh      ) # Start dim
    bold=$(     tput bold   || tput md      ) # Start bold
    stout=$(    tput smso   || tput so      ) # Start stand-out
    estout=$(   tput rmso   || tput se      ) # End stand-out
    under=$(    tput smul   || tput us      ) # Start underline
    eunder=$(   tput rmul   || tput ue      ) # End   underline
    reset=$(    tput sgr0   || tput me      ) # Reset cursor
    blink=$(    tput blink  || tput mb      ) # Start blinking
    italic=$(   tput sitm   || tput ZH      ) # Start italic
    eitalic=$(  tput ritm   || tput ZR      ) # End   italic
[[ $TERM != *-m ]] && {
    red=$(      tput setaf 1|| tput AF 1    )
    green=$(    tput setaf 2|| tput AF 2    )
    yellow=$(   tput setaf 3|| tput AF 3    )
    blue=$(     tput setaf 4|| tput AF 4    )
    magenta=$(  tput setaf 5|| tput AF 5    )
    cyan=$(     tput setaf 6|| tput AF 6    )
}
    black=$(    tput setaf 0|| tput AF 0    )
    white=$(    tput setaf 7|| tput AF 7    )
    default=$(  tput op                     )
    eed=$(      tput ed     || tput cd      )   # Erase to end of display
    eel=$(      tput el     || tput ce      )   # Erase to end of line
    ebl=$(      tput el1    || tput cb      )   # Erase to beginning of line
    ewl=$eel$ebl                                # Erase whole line
    draw=$(     tput -S <<< '   enacs
                                smacs
                                acsc
                                rmacs' || { \
                tput eA; tput as;
                tput ac; tput ae;         } )   # Drawing characters
    back=$'\b'
} 3>&2 2>/dev/null ||:





#  ______________________________________________________________________ 
# |                                                                      |
# |                                        .:  FUNCTION DECLARATIONS  :. |
# |______________________________________________________________________|



#  ______________________________________________________________________
# |__ Chr _______________________________________________________________|
#
#       chr decimal
#
# Outputs the character that has the given decimal ASCII value.
#
chr() {
    printf "\\$(printf '%03o' "$1")"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Ord _______________________________________________________________|
#
#       ord character
#
# Outputs the decimal ASCII value of the given character.
#
ord() {
    printf '%d' "'$1"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Hex _______________________________________________________________|
#
#       hex character
#
# Outputs the hexadecimal ASCII value of the given character.
#
hex() { 
    printf '%x' "'$1"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Unhex _______________________________________________________________|
#
#       unhex character
#
# Outputs the character that has the given hexadecimal ASCII value.
#
unhex() {
    printf "\\x$1"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ max _______________________________________________________________|
#
#       max numbers...
#
# Outputs the highest of the given numbers.
#
max() {
    local max=$1 n
    for n; do
        (( n > max )) && max=$n
    done
    printf %d "$max"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ min _______________________________________________________________|
#
#       min numbers...
#
# Outputs the lowest of the given numbers.
#
min() {
    local min=$1 n
    for n; do
        (( n < min )) && min=$n
    done
    printf '%d' "$min"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ totime ____________________________________________________________|
#
#       totime "YYYY-MM-DD HH:MM:SS.mmm"...
#
# Outputs the number of milliseconds in the given date string(s).
#
# When multiple date string arguments are given, multiple time strings are output, one per line.
#
# The fields should be in the above defined order.  The delimitor between the fields may be any one of [ -:.].
# If a date string does not follow the defined format, the result is undefined.
#
# Note that this function uses a very simplistic conversion formula which does not take any special calendar
# convenions into account.  It assumes there are 12 months in evert year, 31 days in every month, 24 hours
# in every day, 60 minutes in every hour, 60 seconds in every minute and 1000 milliseconds in every second.
#
totime() {
    local arg time year month day hour minute second milli
    for arg; do
        IFS=' -:.' read year month day hour minute second milli <<< "$arg" &&
            (( time = (((((((((((10#$year * 12) + 10#$month) * 31) + 10#$day) * 24) + 10#$hour) * 60) + 10#$minute) * 60) + 10#$second) * 1000) + 10#$milli )) &&
            printf '%d\n' "$time"
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Exists ____________________________________________________________|
#
#       exists application
#
# Succeeds if the application is in PATH and is executable.
#
exists() {
    [[ -x $(type -P "$1" 2>/dev/null) ]]
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ FirstExists ____________________________________________________________|
#
#       firstExists file...
#
# Outputs the first of the arguments that is a file which exists.
#
firstExists() {
    local file;
    for file; do
        [[ -e "$file" ]] && printf %s "$file" && exit
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Eol _______________________________________________________________|
#
#       eol message
#
# Return termination punctuation for a message, if necessary.
#
eol() {
    : #[[ $1 && $1 != *[\!\?.,:\;\|] ]] && printf .. ||:
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Hr _______________________________________________________________|
#
#       hr pattern [length]
#
# Outputs a horizontal ruler of the given length in characters or the terminal column length otherwise.
# The ruler is a repetition of the given pattern string.
#
hr() {
    local pattern=${1:--} length=${2:-$COLUMNS} ruler=

    while (( ${#ruler} < length )); do
        ruler+=${pattern:0:length-${#ruler}}
    done

    printf %s "$ruler"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ CLoc ______________________________________________________________|
#
#       cloc
#
# Outputs the current cursor location as two space-separated numbers: row column.
#
cloc() {
    local old=$(stty -g)
    trap 'stty "$old"' RETURN
    stty raw

    # If the tty has input waiting then we can't read back its response.  We'd only break and pollute the tty input buffer.
    read -t 0 < /dev/tty 2>/dev/null && return 1

    printf '\e[6n' > /dev/tty
    IFS='[;' read -dR _ row col < /dev/tty
    printf '%d %d' "$row" "$col"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ readwhile ______________________________________________________________|
#
#       readwhile command [args]
#
# Outputs the characters typed by the user into the terminal's input buffer while running the given command.
#
readwhile() {
    local old=$(stty -g) in result REPLY
    trap 'stty "$old"' RETURN
    stty raw

    "$@"
    result=$?

    while read -t 0; do
        IFS= read -rd '' -n1 && in+=$REPLY
    done
    printf %s "$in"

    return $result
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Latest ____________________________________________________________|
#
#       latest [file...]
#
# Output the argument that represents the file with the latest modification time.
#
latest() (
    shopt -s nullglob
    local file latest=$1
    for file; do
        [[ $file -nt $latest ]] && latest=$file
    done
    printf '%s\n' "$latest"
) # _____________________________________________________________________



#  _______________________________________________________________________
# |__ Iterate ____________________________________________________________|
#
#       iterate [command]
#
# All arguments to iterate make up a single command that will be executed.
#
# Any of the arguments may be of the format {x..y[..z]} which causes the command
# to be executed in a loop, each iteration substituting the argument for the
# current step the loop has reached from x to y.  We step from x to y by
# walking from x's position in the ASCII character table to y's with a step of z
# or 1 if z is not specified.
#
iterate() (
    local command=( "$@" ) iterationCommand=() loop= a= arg= current=() step=() target=()
    for a in "${!command[@]}"; do
        arg=${command[a]}
        if [[ $arg = '{'*'}' ]]; then
            loop=${arg#'{'} loop=${loop%'}'}
            step[a]=${loop#*..*..} current[a]=${loop%%..*} target[a]=${loop#*..} target[a]=${target[a]%.."${step[a]}"}
            [[ ! ${step[a]} || ${step[a]} = $loop ]] && step[a]=1
        fi
    done
    if (( ${#current[@]} )); then
        for loop in "${!current[@]}"; do
            while true; do
                iterationCommand=()

                for a in "${!command[@]}"; do
                    (( a == loop )) \
                        && iterationCommand+=( "${current[a]}" ) \
                        || iterationCommand+=( "${command[a]}" )
                done

                iterate "${iterationCommand[@]}"

                [[ ${current[loop]} = ${target[loop]} ]] && break
                current[loop]="$(chr "$(( $(ord "${current[loop]}") + ${step[loop]} ))")"
            done
        done
    else
        "${command[@]}"
    fi
) # _____________________________________________________________________

#  ______________________________________________________________________
# |__ Logging ___________________________________________________________|
#
#       log format [arguments...]
#
# Log an event at a certain importance level.  The event is expressed as a printf(1) format argument.
# The current exit code remains unaffected by the execution of this function.
#
# Instead of 'log', you can use a level as command name, to log at that level.  Using log, messages are
# logged at level inf.  The supported levels are: trc, dbg, inf, wrn, err, ftl.
#
# If you prefix the command name with a p, the log message is shown as a spinner and waits for the next
# closing statement.  Eg.
#   
#   pinf 'Converting image'
#       convert image.png image.jpg
#   fnip
#
# The closing statement (here fnip) is the reverse of the opening statement and exits with the exit code
# of the last command.  If the last command failed, it shows the exit code in the spinner before it is stopped.
# The closing statement also takes a format and arguments, which are displayed in the spinner.
#
log() {
    local exitcode=$? level=${level:-inf} supported=0 end=$'\n' type=msg conMsg= logMsg= format= colorFormat= date= info= arg= args=() colorArgs=() ruler=

    # Handle options.
    local OPTIND=1
    while getopts :tpuPrR:d:n arg; do
        case $arg in
            p)
                end='.. '
                type=startProgress ;;
            u)
                end='.. '
                type=updateProgress ;;
            P)
                type=stopProgress ;;
            r)
                ruler='____' ;;
            R)
                ruler=$OPTARG ;;
            d)
                end=$OPTARG ;;
            n)
                end= ;;
            t)
                date=$(date +"${_logDate:-%H:%M}") ;;
        esac
    done
    shift "$((OPTIND-1))"
    format=$1 args=( "${@:2}" )
    (( ! ${#args[@]} )) && [[ $format ]] && { args=("$format") format=%s; local bold=; }

    # Level-specific settings.
    local logLevelColor
    case $level in
        TRC)    (( supported = _logVerbosity >= 4 ))
                logLevelColor=$_logTrcColor ;;
        DBG)    (( supported = _logVerbosity >= 3 ))
                logLevelColor=$_logDbgColor ;;
        INF)    (( supported = _logVerbosity >= 2 ))
                logLevelColor=$_logInfColor ;;
        WRN)    (( supported = _logVerbosity >= 1 ))
                logLevelColor=$_logWrnColor ;;
        ERR)    (( supported = _logVerbosity >= 0 ))
                logLevelColor=$_logErrColor ;;
        FTL)    (( supported = 1 ))
                logLevelColor=$_logFtlColor ;;
        *)
                log FTL 'Log level %s does not exist' "$level"
                exit 1 ;;
    esac
    (( ! supported )) && return "$exitcode"
    local logColor=${_logColor:+$logLevelColor}

    # Generate the log message.
    case $type in
        msg|startProgress)
            printf -v logMsg "[${date:+%s }%-3s] $format$end" ${date:+"$date"} "$level" "${args[@]}"
            if (( _logColor )); then
                colorFormat=$(sed ${reset:+-e "s/$(requote "$reset")/$reset$logColor/g"} -e "s/%[^a-z]*[a-z]/$reset$bold$logColor&$reset$logColor/g" <<< "$format")
                colorArgs=("${args[@]//$reset/$reset$bold$logColor}")
                printf -v conMsg "$reset[${date:+%s }$logColor$bold%-3s$reset] $logColor$colorFormat$reset$black\$$reset$end$save" ${date:+"$date"} "$level" "${colorArgs[@]}"
            else
                conMsg=$logMsg
            fi
        ;;

        updateProgress)
            printf -v logMsg printf " [$format]" "${args[@]}"
            if (( _logColor )); then
                colorFormat=$(sed ${reset:+-e "s/$(requote "$reset")/$reset$logColor/g"} -e "s/%[^a-z]*[a-z]/$reset$bold$logColor&$reset$logColor/g" <<< "$format")
                colorArgs=("${args[@]//$reset/$reset$bold$logColor}")
                printf -v conMsg "$load$eel$blue$bold[$reset$logColor$colorFormat$reset$blue$bold]$reset$end" "${colorArgs[@]}"
            else
                conMsg=$logMsg
            fi
        ;;

        stopProgress)
            case $exitcode in
                0)  printf -v logMsg "done${format:+ ($format)}.\n" "${args[@]}"
                    if (( _logColor )); then
                        colorFormat=$(sed ${reset:+-e "s/$(requote "$reset")/$reset$logColor/g"} -e "s/%[^a-z]*[a-z]/$reset$bold$logColor&$reset$logColor/g" <<< "$format")
                        colorArgs=("${args[@]//$reset/$reset$bold$logColor}")
                        printf -v conMsg "$load$eel$green${bold}done${colorFormat:+ ($reset$logColor$colorFormat$reset$green$bold)}$reset.\n" "${colorArgs[@]}"
                    else
                        conMsg=$logMsg
                    fi
                ;;

                *)  info=${format:+$(printf ": $format" "${args[@]}")}
                    printf -v logMsg "error(%d%s).\n" "$exitcode" "$info"
                    if (( _logColor )); then
                        printf -v conMsg "${eel}${red}error${reset}(${bold}${red}%d${reset}%s).\n" "$exitcode" "$info"
                    else
                        conMsg=$logMsg
                    fi
                ;;
            esac
        ;;
    esac

    # Create the log file.
    if [[ $_logFile && ! -e $_logFile ]]; then
        [[ $_logFile = */* ]] || $_logFile=./$logFile
        mkdir -p "${_logFile%/*}" && touch "$_logFile"
    fi

    # Stop the spinner.
    if [[ $type = stopProgress && $_logSpinner ]]; then
        kill "$_logSpinner"
        wait "$_logSpinner" 2>/dev/null
        unset _logSpinner
    fi

    # Output the ruler.
    if [[ $ruler ]]; then
        printf >&2 '%s\n' "$(hr "$ruler")"
        [[ -w $_logFile ]] \
            && printf >> "$_logFile" '%s' "$ruler"
    fi

    # Output the log message.
    printf >&2 '%s' "$conMsg"
    [[ -w $_logFile ]] \
        && printf >> "$_logFile" '%s' "$logMsg"

    # Start the spinner.
    if [[ $type = startProgress && ! $_logSpinner && $TERM != dumb ]]; then
        {
            set +m
            trap 'printf >&2 %s "$show"' EXIT
            printf >&2 %s "$hide"
            while printf >&2 "$eel$blue$bold[$reset%s$reset$blue$bold]$reset\b\b\b" "${spinner[s++ % ${#spinner[@]}]}" && sleep .1
            do :; done
        } & _logSpinner=$!
    fi

    return $exitcode
}
trc() { level=TRC log "$@"; }
dbg() { level=DBG log "$@"; }
inf() { level=INF log "$@"; }
wrn() { level=WRN log "$@"; }
err() { level=ERR log "$@"; }
ftl() { level=FTL log "$@"; }
plog() { log -p "$@"; }
ulog() { log -u "$@"; }
golp() { log -P "$@"; }
ptrc() { level=TRC plog "$@"; }
pdbg() { level=DBG plog "$@"; }
pinf() { level=INF plog "$@"; }
pwrn() { level=WRN plog "$@"; }
perr() { level=ERR plog "$@"; }
pftl() { level=FTL plog "$@"; }
utrc() { level=TRC ulog "$@"; }
udbg() { level=DBG ulog "$@"; }
uinf() { level=INF ulog "$@"; }
uwrn() { level=WRN ulog "$@"; }
uerr() { level=ERR ulog "$@"; }
uftl() { level=FTL ulog "$@"; }
gtrc() { level=trc golp "$@"; }
gbdp() { level=DBG golp "$@"; }
fnip() { level=INF golp "$@"; }
nrwp() { level=WRN golp "$@"; }
rrep() { level=ERR golp "$@"; }
ltfp() { level=FTL golp "$@"; }
_logColor=${_logColor:-$([[ -t 2 ]] && echo 1)} _logVerbosity=2
_logTrcColor=$grey _logDbgColor=$blue _logInfColor=$white _logWrnColor=$yellow _logErrColor=$red _logFtlColor=$bold$red
# _______________________________________________________________________



#  ______________________________________________________________________
# |__ Ask _______________________________________________________________|
#
#       ask [-c optionchars|-d default] [-s|-S maskchar] format [arguments...]
#
# Ask a question and read the user's reply to it.  Then output the result on stdout.
#
# When in normal mode, a single line is read.  If the line is empty and
# -d was specified, the default argument is output instead of an empty line.
# The exit code is always 0.
#
# When in option mode (-c), the user is shown the option characters with
# which he can reply and a single character is read.
# If the reply is empty (user hits enter) and any of the optionchars are
# upper-case, the upper-case option (= the default option) character will
# be output instead of an empty line.
# If the reply character is not amoungst the provided options the default
# option is again output instead if present.  If no default was given, an
# exit code of 2 is returned.
# You may mark an optionchar as 'valid' by appending a '!' to it.  As a
# result, an exit code of 0 will only be returned if this valid option
# is replied.  If not, an exit code of 1 will be returned.
#
ask() {
   
    # Initialize the vars.
    local opt arg
    local option=
    local options=
    local default=
    local silent=
    local valid=
    local muteChar=
    local format=

    # Parse the options.
    local OPTIND=1
    while getopts :sS:c:d: opt; do
        case $opt in
            s)  silent=1 ;;
            S)  silent=1 muteChar=$OPTARG ;;
            c)  while read -n1 arg; do
                    case $arg in
                        [[:upper:]]) default=$arg ;;
                        !) valid=${options: -1}; continue ;;
                    esac

                    options+=$arg
                done <<< "$OPTARG" ;;
            d)  default=$OPTARG option=$default ;;
        esac
    done

    # Trim off the options.
    shift $((OPTIND-1))

    # Figure out what FD to use for our messages.
    [[ -t 1 ]] && local fd=1 || local fd=2

    # Ask the question.
    format=$1; shift
    level=${level:-WRN} log -n "$format${option:+ [%s]}${options:+ [%s]}" "$@" ${option:+"$option"} ${options:+"$options"}

    # Read the reply.
    exec 8<&0; [[ -t 8 ]] || exec 8</dev/tty
    if [[ $muteChar ]]; then
        local reply
        while read -u8 -s -n1 && [[ $REPLY ]]; do
            reply+=$REPLY
            printf '%s' "$muteChar"                                 >&$fd
        done
        REPLY=$reply
    else
        read -u8 -e ${options:+-n1} ${silent:+-s}
    fi
    [[ $options && $REPLY ]] || (( silent )) && printf '\n'         >&$fd

    # Evaluate the reply.
    while true; do
        if [[ $REPLY && ( ! $options || $options = *$REPLY* ) ]]; then
            if [[ $valid ]]
            then [[ $REPLY = $valid ]]
            else printf "%s" "$REPLY"
            fi

            return
        fi

        [[ -z $default || $REPLY = $default ]] \
            && return 2
        
        REPLY=$default
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Trim ______________________________________________________________|
#
#       trim lines ...
#
# Trim the whitespace off of the beginning and end of the given lines.
# Each argument is considdered one line; is treated and printed out.
#
# When no arguments are given, lines will be read from standard input.
#
trim() {
    { (( $# )) && printf '%s\n' "$@" || cat; } | \
        sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Reverse ___________________________________________________________|
#
#       reverse [-0|-d delimitor] [elements ...] [<<< elements]
#
# Reverse the order of the given elements.
# Elements are read from command arguments or standard input if no element
# arguments are given.
# They are reversed and output on standard output.
#
# If the -0 option is given, input and output are delimited by NUL bytes.
# If the -d option is given, input and output are delimited by the
# character argument.
# Otherwise, they are delimited by newlines.
#
reverse() {
   
    # Initialize the vars.
    local elements=() delimitor=$'\n' i

    # Parse the options.
    local OPTIND=1
    while getopts :0d: opt; do
        case $opt in
            0) delimitor=$'\0' ;;
            d) delimitor=$OPTARG ;;
        esac
    done
    shift "$((OPTIND-1))"

    # Get the elements.
    if (( $# )); then
        elements=( "$@" )
    else
        while IFS= read -r -d "$delimitor"; do
            elements+=("$REPLY")
        done
    fi

    # Iterate in reverse order.
    for (( i=${#elements[@]} - 1; i >=0; --i )); do
        printf "%s${delimitor:-'\0'}" "${elements[i]}"
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Order _____________________________________________________________|
#
#       order [-0|-d char] [-[fF] isDesired] [-[cC] isAscending|-n|-r|-t] [-T number] [-a array|elements ...] [<<< elements]
#
# Orders the elements in ascending order.
# Elements are read from command arguments or standard input if no element
# arguments are given.
# The result is output on standard output.
#
# By default, the elements will be ordered using lexicographic comparison.
# If the -n option is given, the elements will be ordered numerically.
# If the -r option is given, the elements will be ordered randomly.
# If the -f option is given, the command name following it will be used
# as a filter.
# If the -c option is given, the command name following it will be used
# as a comparator.
# If the -C option is given, the bash code following it will be used
# as a comparator.
# If the -t option is given, only the first number results are returned.
# If the -a option is given, the elements in array are ordered instead and
# array is mutated to contain the result.
# If number is 0, all results are returned.
#
# isDesired is a command name which will get one parameter.  The parameter
# is an element which will only be included if the command exits successfully.
# isAscending is a command name which will be executed for each element
# comparison and will be passed two element arguments.  The command should
# succeed if the first argument is less than the second argument for the
# purpose of this sort.
#
# If the -0 option is given, input and output are delimited by NUL bytes.
# If the -d option is given, input and output are delimited by the
# character argument.
# Otherwise, they are delimited by newlines.
#
# The ordering is implemented by an insertion sort algorithm.
#
order() {
   
    # Initialize the vars.
    local delimitor=$'\n' i isDesired=true isAscending=string_ascends top=0 arrayName= array=

    # Parse the options.
    local OPTIND=1
    while getopts :0nrd:f:F:c:C:tT:a: opt; do
        case $opt in
            0) delimitor=$'\0' ;;
            d) delimitor=$OPTARG ;;
            n) isAscending=number_ascends ;;
            r) isAscending=random_ascends ;;
            t) isAscending=mtime_ascends ;;
            f) isDesired=$OPTARG ;;
            F) isDesired=bash_desired bash_desired_code=$OPTARG ;;
            c) isAscending=$OPTARG ;;
            C) isAscending=bash_ascends bash_ascends_code=$OPTARG ;;
            T) top=$OPTARG ;;
            a) arrayName=$OPTARG array=$arrayName[@] ;;
        esac
    done
    shift "$((OPTIND-1))"

    # Get the elements.
    local elements=() element
    if [[ $arrayName ]]; then
        for element in "${!array}"; do
            "$isDesired" "$element" && elements+=("$element")
        done
    elif (( $# )); then
        for element; do
            "$isDesired" "$element" && elements+=("$element")
        done
    else
        while IFS= read -r -d "$delimitor" element; do
            "$isDesired" "$element" && elements+=("$element")
        done
    fi

    # Iterate in reverse order.
    for (( i = 1; i < ${#elements[@]}; ++i )); do
        for (( j = i; j > 0; --j )); do
            element=${elements[j]}
            if "$isAscending" "$element" "${elements[j-1]}"; then
                elements[j]=${elements[j-1]}
                elements[j-1]=$element
            fi
        done
    done

    (( top )) || top=${#elements[@]}
    if [[ $array ]]; then
        declare -ga "$array=($(printf '%q ' "${elements[@]:0:top}"))"
    else
        printf "%s${delimitor:-\0}" "${elements[@]:0:top}"
    fi
} # _____________________________________________________________________
string_ascends()    { [[ $1 < $2 ]]; }
number_ascends()    { (( $1 < $2 )); }
random_ascends()    { (( RANDOM % 2 )); }
mtime_ascends()     { [[ $1 -ot $2 ]]; }
exists_desired()    { [[ -e $1 ]]; }
line_desired()      { [[ $1 ]]; }
code_desired()      { line_desired "$1" && ! comment_desired "$1"; }
comment_desired()   { line_desired "$1" && [[ $1 = @(#|//|/\*)* ]]; }
bash_desired()      { bash -c "$bash_desired_code" -- "$@"; }
bash_ascends()      { bash -c "$bash_ascends_code" -- "$@"; }


#  ______________________________________________________________________
# |__ Mutex _____________________________________________________________|
#
#       mutex file
#
# Open a mutual exclusion lock on the file, unless another process already owns one.
#
# If the file is already locked by another process, the operation fails.
# This function defines a lock on a file as having a file descriptor open to the file.
# This function uses FD 9 to open a lock on the file.  To release the lock, close FD 9:
# exec 9>&-
#
mutex() {
    local lockfile=${1:-${BASH_SOURCE[-1]}} pid pids
    [[ -e $lockfile ]] || err "No such file: $lockfile" || return

    exec 9>> "$lockfile" && [[ $({ fuser -f "$lockfile"; } 2>&- 9>&-) == $$ ]]
}


#  ______________________________________________________________________
# |__ PushJob ___________________________________________________________|
#
#       pushjob [poolsize] command
#
# Start an asynchronous command within a pool, waiting for space in the pool if it is full.
#
# The pool is pruned automatically as running jobs complete.  This function
# allows you to easily run asynchronous commands within a pool of N,
# automatically starting the next command as soon as there's space.
#
pushjob() {
    local size=$1; shift 1

    # Wait for space in the pool.
    until (( ${#jobpool[@]} < size )); do
        sleep 1 & pushjobsleep=$!
        wait "$pushjobsleep"
    done 2>/dev/null

    # Register prunejobs and start the pushed job.
    trap _prunejobs SIGCHLD
    set -m
    "$@" & jobpool[$!]=
}
_prunejobs() {
    # Prune all pool jobs that are no longer running.
    for pid in "${!jobpool[@]}"; do
        kill -0 "$pid" 2>/dev/null || unset "jobpool[$pid]"
    done

    # Unregister SIGCHLD if our pool is empty.
    (( ${#jobpool[@]} )) || trap - SIGCHLD

    # Wake up pushjob.
    kill "$pushjobsleep" 2>/dev/null
}




#  ______________________________________________________________________
# |__ FSleep _____________________________________________________________|
#
#       fsleep time
#
# Wait for the given (fractional) amount of seconds.
#
# This implementation solves the problem portably, assuming that either
# bash 4.x or a fractional sleep(1) is available.
#
fsleep() {

    local fifo=${TMPDIR:-/tmp}/.fsleep.$$
    trap 'rm -f "$fifo"' RETURN
    mkfifo "$fifo" && { read -t "$1" <> "$fifo" 2>/dev/null || sleep "$1"; }
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ options ___________________________________________________________|
#
#       options [option description]...
#
# Specify and handle options in arguments.
#
# The 'setopt' function will be called for each option expected option
# passed to the script, with $1 set to the option character and $2
# its description.  Check OPTARG if the option takes an argument.
# 'setopt' will be called with '?' if an invalid option is passed.
#
# Unless specified, the -h option will show a usage description,
# explaining the options.
#
# Proposed usage:
# setopt() {
#     case "$1" in
#         a) echo "got option a" ;;
#         b) echo "got option b with argument $OPTARG" ;;
#     esac
# }
# options \
#     a   'option a' \
#     b:  'option b with argument'
#
options() {

    # Parse the expected options and their description.
    declare -A options=()
    while (( $# )); do
        local optchar=$1 optdesc=$2
        shift 2 || ftl 'Missing arguments, expected option (%s), description (%s).' "$optchar" "$optdesc" || exit
        options[$optchar]=$optdesc
    done

    # Find the script's options.
    local argc=${BASH_ARGC[@]: -1} argv=("${BASH_ARGV[@]: -argc}") arg
    local optstring=$(printf %s "${!options[@]}")h
    set -- # Sigh.  BASH_ARGV is all backwards.
    for arg in "${argv[@]}"; do
        set -- "$arg" "$@"
    done

    # Handle the script's options.
    while getopts "$optstring" arg; do
        if [[ $arg = h && ! ${options[h]} ]]; then
            # Show usage message.
            [[ -t 1 ]]; local fd=$(( $? + 1 )) optarg

            # Print out the app usage.
            printf "  Usage: $reset$bold%s$reset" "${BASH_SOURCE[1]##*/}"   >&$fd
            for optchar in "${!options[@]}"; do
                [[ $optchar = *: ]] && optarg=" arg" || optarg=
                printf " [$bold$green-%s$reset%s]" "${optchar%:}" "$optarg" >&$fd
            done
            printf "\n\n"                                                   >&$fd

            # Print out the option descriptions.
            for optchar in "${!options[@]}"; do
                local optdesc=${options[$optchar]}
                [[ $optchar = *: ]] && optarg=" arg" || optarg=
                printf "    $bold$green-%s$reset%s\t" "${optchar%:}" "$optarg"
                fmt -w "$COLUMNS" <<< "${optdesc//+( )/ }" | sed $'1!s/^/ \t/'
                printf "\n"
            done | column -t -s $'\t'                                       >&$fd
        else
            optchar=$arg; [[ ! ${options[$arg]} && ${options[$arg:]} ]] && optchar=$arg:
            setopt "$arg" "${options[$arg]}"
        fi
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ ShowHelp __________________________________________________________|
#
#       showHelp name description author [option description]...
#
# Generate a prettily formatted usage description of the application.
#
#   name        Provide the name of the application.
#
#   description Provide a detailed description of the application's
#               purpose and usage.
#
#   option      An option the application can take as argument.
#
#   description A description of the effect of the preceding option.
#
showHelp() {

    # Parse the options.
    local appName=$1; shift
    local appDesc=${1//+([[:space:]])/ }; shift
    local appAuthor=$1; shift
    local cols=$(tput cols)
    (( cols = ${cols:-80} - 10 ))

    # Figure out what FD to use for our messages.
    [[ -t 1 ]]; local fd=$(( $? + 1 ))

    # Print out the help header.
    printf "$reset$bold\n"                                          >&$fd
    printf "\t\t%s\n" "$appName"                                    >&$fd
    printf "$reset\n"                                               >&$fd
    printf "%s\n" "$appDesc" | fmt -w "$cols" | sed $'s/^/\t/'      >&$fd
    printf "\t   $reset$bold~ $reset$bold%s\n" "$appAuthor"         >&$fd
    printf "$reset\n"                                               >&$fd

    # Print out the application options and columnize them.
    while (( $# )); do
        local optName=$1; shift
        local optDesc=$1; shift
        printf "    %s\t" "$optName"
        printf "%s\n" "${optDesc//+( )/ }" | fmt -w "$cols" | sed $'1!s/^/ \t/'
        printf "\n"
    done | column -t -s $'\t' \
         | sed "s/^\(    [^ ]*\)/$bold$green\1$reset/"              >&$fd
    printf "\n"                                                     >&$fd
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Quote _____________________________________________________________|
#
#       shquote [-e] [argument...]
#
# Shell-quote the arguments to make them safe for injection into bash code.
# 
# The result is bash code that represents a series of words, where each
# word is a literal string argument.  By default, quoting happens using
# single-quotes.
#
#   -e      Use backslashes rather than single quotes.
#   -d      Use double-quotes rather than single quotes (does NOT disable expansions!).
#   -a      Normally, shquote doesn't quote arguments that don't need it.  This forces all arguments to be quoted.
#
shquote() {

    # Initialize the defaults.
    local arg escape=0 sq="'\\''" dq='\"' quotedArgs=() type=single always=0

    # Parse the options.
    while [[ $1 = -* ]]; do
        case $1 in
            -e) type=escape  ;;
            -d) type=double  ;;
            -a) always=1     ;;
            --) shift; break ;;
        esac
        shift
    done

    # Print out each argument, quoting it properly.
    for arg; do
        (( ! always )) && [[ $arg = "$(printf %q "$arg")" ]] && quotedArgs+=("$arg") && continue

        case "$type" in
            escape)
                quotedArgs+=("$(printf "%q"     "$arg")") ;;
            single)
                arg=${arg//"'"/$sq}
                quotedArgs+=("$(printf "'%s'"   "$arg")") ;;
            double)
                arg=${arg//'"'/$dq}
                quotedArgs+=("$(printf '"%s"'   "$arg")") ;;
        esac
    done

    printf '%s\n' "$(IFS=' '; echo "${quotedArgs[*]}")"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ ReQuote __________________________________________________________|
#
#       requote [string]
#
# Escape the argument string to make it safe for injection into a regex.
#
# The result is a regular expression that matches the literal argument
# string.
#
requote() {

    # Initialize the defaults.
    local char

    printf '%s' "$1" | while IFS= read -r -d '' -n1 char; do
        printf '[%s]' "$char"
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Shorten ___________________________________________________________|
#
#       shorten [-p pwd] path [suffix]...
#
# Shorten an absolute path for pretty printing.
# Paths are shortened by replacing the homedir by ~, making it relative and
# cutting off given suffixes from the end.
#
#   -p      Use the given pathname as the base for relative filenames instead of PWD.
#   path    The path string to shorten.
#   suffix  Suffix strings that must be cut off from the end.
#           Only the first suffix string matched will be cut off.
#
shorten() {

    # Parse the options.
    local suffix path pwd=$PWD
    [[ $1 = -p ]] && { pwd=$2; shift 2; }
    path=$1; shift

    # Make path absolute.
    [[ $path = /* ]] || path=$PWD/$path

    # If the path denotes something that exists; it's easy.
    if [[ -d $path ]]
    then path=$(cd "$path"; printf "%s" "$PWD")
    elif [[ -d ${path%/*} ]]
    then path=$(cd "${path%/*}"; printf "%s" "$PWD/${path##*/}")

    # If not, we'll try readlink -m.
    elif readlink -m / >/dev/null 2>&1; then
        path=$(readlink -m "$path")

    # If we don't have that - unleash the sed(1) madness.
    else
        local oldpath=/
        while [[ $oldpath != $path ]]; do
            oldpath=$path
            path=$(sed -e 's,///*,/,g' -e 's,\(^\|/\)\./,\1,g' -e 's,\(^\|/\)[^/]*/\.\.\($\|/\),\1,g' <<< "$path")
        done
    fi

    # Replace special paths.
    path=${path/#$HOME/'~'}
    path=${path#$pwd/}

    # Cut off suffix.
    for suffix; do
        [[ $path = *$suffix ]] && {
            path=${path%$suffix}
            break
        }
    done

    printf "%s" "$path"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Up ________________________________________________________________|
#
#       up .../path|num
#
# Walk the current working directory up towards root num times or until path is found.
#
# Returns 0 if the destination was reached or 1 if we hit root.
#
# Prints PWD on stdout on success.
#
up() {
    local up=0
    until [[ $PWD = / ]]; do
        cd ../

        if [[ $1 = .../* ]]; then
            [[ -e ${1#.../} ]] && pwd && return
        elif (( ++up == $1 )); then
            pwd && return
        fi
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ BuildArray ________________________________________________________|
#
#       buildarray name terms... -- elements...
#
# Create an array by adding all the terms to it for each element, replacing {} terms by the element.
#
#   name        The name of the array to put the result into.
#   terms       The values to add to the array for each of the elements.  A {} term is replaced by the current element.
#   elements    The elements to iterate the terms for.
#
buildarray() {
    local target=$1 term terms=() element value
    shift

    while [[ $1 != -- ]]; do
        terms+=("$1")
        shift
    done
    shift

    for element; do
        for term in "${terms[@]}"; do
            [[ $term = {} ]] && value="$element" || value="$term"
            declare -ag "$target+=($(printf '%q' "$value"))"
        done
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ InArray ___________________________________________________________|
#
#       inArray element array
#
# Checks whether a certain element is in the given array.
#
#   element The element to search the array for.
#   array   This is a list of elements to search through.
#
inArray() {

    # Parse the options.
    local element
    local search=$1; shift

    # Perform the search.
    for element
    do [[ $element = $search ]] && return 0; done
    return 1
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ HideDebug _________________________________________________________|
#
#       hideDebug [on|off]
#
# Toggle Bash's debugging mode off temporarily.
# To hide Bash's debugging output for a function, you should have
#     hideDebug on
# as its first line, and
#     hideDebug off
# as its last.
#
hideDebug() {

    if [[ $1 = on ]]; then
        : -- HIDING DEBUG OUTPUT ..
        [[ $- != *x* ]]; bashlib_debugWasOn=$?
        set +x
    elif [[ $1 = off ]]; then
        : -- SHOWING DEBUG OUTPUT ..
        (( bashlib_debugWasOn )) && \
        set -x
    fi
}



#  ______________________________________________________________________
# |__ anfunc ____________________________________________________________|
#
#       anfunc [on|off]
#
# Turn on or off support for annonymous functions.
#
# WARNING: This is a hack.  It turns on extdebug and causes any argument
# that matches (){code} to be replaced by a function name that if invoked
# runs code.
#
# eg.
#   confirm '(){ rm "$1" }' *.txt
#   # In this example, confirm() could be a function that asks confirmation
#   # for each argument past the first and runs the anfunc in the first
#   # argument on each confirmed argument.
#
# Don't use this.  It is an academic experiment and has bugs.
#
# Bugs:
#   - commands lose their exit code.
#       To inhibit the real command from running, we use extdebug and
#       a DEBUG trap that returns non-0.  As a result, the actual return
#       code is lost.
# 
anfunc() {
    case "$1" in
        on)
            shopt -s extdebug
            trap _anfunc_trap DEBUG
        ;;
        off)
            trap - DEBUG
            shopt -u extdebug
        ;;
    esac
}
_anfunc_trap() {
    local f w

    # Perform the command parsing and handling up to its word splitting.
    # This includes command substitution, quote handling, pathname expansion, etc.
    declare -a words="($BASH_COMMAND)"

    # Iterate the words to run in the final stage, and handle anfunc matches.
    for ((w=0; w<${#words[@]}; ++w)); do
        [[ ${words[w]} = '(){'*'}' ]] &&
            # Declare a new function for this anfunc.
            eval "_f$((++f))${words[w]}" &&
                # Replace the word by the new function's name.
                words[w]="_f$f"
    done
    
    # Run the command.
    eval "$(printf '%q ' "${words[@]}")"
    
    # Clean up the anfuncs.
    for ((; f>0; --f)); do
        unset -f "_f$f"
    done
    
    # Inhibit the real command's execution.
    return 1
}



#  ______________________________________________________________________
# |__ StackTrace ________________________________________________________|
#
#       stackTrace
#
# Output the current script's function execution stack.
#
stackTrace() {
    # Some general debug information.
    wrn "    [PID       : %15s]    [PPID       : %8s]    [Main PID   : %8s]" "$BASHPID" "$PPID" "$$"
    wrn "    [Level     : %15s]    [Subshells  : %8s]    [Runtime    : %7ss]" "$SHLVL" "$BASH_SUBSHELL" "$SECONDS"
    wrn "    [Locale    : %15s]    [IFS        : %8s]" "${LC_ALL:-${LC_COLLATE:-${LANG:-C}}}" "$(printf %q "$IFS")"
    wrn "    Dir Stack  : %s" "${DIRSTACK[*]}"
    wrn "    Shell      : %s v%s" "$BASH" "$BASH_VERSION"
    wrn "    Shell Opts : %s" "${SHELLOPTS//:/, }"
    wrn "    Bash Opts  : %s" "${BASHOPTS//:/, }"
    wrn "    Functions  :"


    # Search through the map.
    local arg=0 
    for stack in "${!FUNCNAME[@]}"; do
        (( stack+1 >= ${#BASH_SOURCE[@]} )) && break

        func=${FUNCNAME[stack]}
        line=${BASH_LINENO[stack]}
        file=${BASH_SOURCE[stack+1]}
        args=()
        for (( arg=0, s=0; s <= stack; ++s )); do
            for (( sarg=0; sarg < ${BASH_ARGC[s]:-0}; ++sarg, ++arg )); do
                (( s == stack )) && args[${BASH_ARGC[s]} - sarg]=${BASH_ARGV[arg]}
            done
        done
        wrn '%40s:%-3d | %s %s' "$file" "$line" "$func" "$(printf '%s ' "$(shquote "${args[@]}")")"
    done

} # _____________________________________________________________________





#  ______________________________________________________________________ 
# |                                                                      |
# |                                                  .:  ENTRY POINT  :. |
# |______________________________________________________________________|

# Make sure this file is sourced and not executed.
( return 2>/dev/null ) || {
    help=$(sed -n '1,/_tocHash=/{ /^#/p; }' "$BASH_SOURCE")
    if [[ $1 ]]; then
        while [[ $1 ]]; do
            awk "p && !/^# *[^ ]/ {exit}
                 p || /^#     $1/ {print; p=1}" <<< "$help"
            shift
        done
    else
        echo "$help"
        echo
        echo "To use bashlib, copy it into your PATH and put ''source bashlib'' at the top of your script."
    fi
}

:
:                                                   .:  END SOURCING  :.
:  ______________________________________________________________________ 
:
07070100000054000081ED00000000000000000000000163C2D55500001EB5000000000000000000000000000000000000002A00000000spectre-cli-3.0+git20230114.26c01a5/build#!/usr/bin/env bash
#
# USAGE
#   [targets='...'] [spectre_feature=0|1 ...] [CFLAGS='...'] [LDFLAGS='...'] ./build [-v|-d|-h|--] [cc arguments ...]
#
#   By default, you should only need to run ./build
#
#   -v: verbose mode, outputs state information and compiler commands.
#   -d: debug build, modifies default build flags to produce binaries best suited for debugging.
#   -h: show this usage information.
#
#   You can customize the targets that are built using targets='...'. Use targets='all' to build all targets.
#   By default, we only build the 'spectre' target.  
#   See targets_all for all possible targets as well as the features they support and require.
#
#   Several features can be enabled or disabled using feature flags.
#   See the Features section for an overview of the features, their default setting, their meaning and their dependencies.
#   You will need to have each of the feature's dependencies installed for the build to succeed with that feature enabled.
#
#   Finally, the C compiler can be tuned using CFLAGS, LDFLAGS and compiler arguments passed to the script.
#
# BUGS
#   support@spectre.app
#
# AUTHOR
#   Maarten Billemont
#
cd "${BASH_SOURCE%/*}"
shopt -s extglob
set -e


### CONFIGURATION
verbose=0

# Options
while getopts :vdh opt; do
    case $opt in
        v)      verbose=1 ;;
        d)      debug=1 ;;
        h|?)    sed -n '/^[^#]/q;p' "${BASH_SOURCE##*/}"; exit ;;
    esac
done
shift "$(( OPTIND - 1 ))"

# Targets to build
targets_all=(
    spectre                     # C CLI version of Spectre (needs: spectre_sodium, optional: spectre_color, spectre_json).
    spectre-bench               # C CLI Spectre benchmark utility (needs: spectre_sodium).
    spectre-tests               # C Spectre algorithm test suite (needs: spectre_sodium, spectre_xml).
)
targets_default='spectre'       # Override with: targets='...' ./build
targets=${targets[*]:-$targets_default} 

# Features
spectre_sodium=${spectre_sodium:-1} # Implement crypto functions with sodium (depends on libsodium).
spectre_json=${spectre_json:-1}     # Support JSON-based user configuration format (depends on libjson-c).
spectre_color=${spectre_color:-1}   # Colorized identicon (depends on libncurses).
spectre_xml=${spectre_xml:-1}       # XML parsing (depends on libxml2).

# Default build flags
cflags=( -O3 $CFLAGS ); unset CFLAGS
ldflags=( $LDFLAGS ); unset LDFLAGS
if (( debug )); then
    cflags+=( -O0 -g )
fi

# Version
if { spectre_version=$(git describe --match '*-cli*' --long --dirty) || spectre_version=$(<VERSION); } 2>/dev/null; then
    cflags+=( -D"MP_VERSION=$spectre_version" )
fi
echo "Current spectre source version ${spectre_version:-<unknown>}..."

# Meta
if (( verbose )); then
    echo "spectre_sodium=${spectre_sodium}, spectre_json=${spectre_json}, spectre_color=${spectre_color}, spectre_xml=${spectre_xml}"
    echo "CFLAGS: ${cflags[*]}"
    echo "LDFLAGS: ${ldflags[*]}"
    echo "targets: ${targets[*]}"
fi


### TARGET: SPECTRE
spectre() {
    # dependencies
    use_spectre_sodium required
    use_spectre_color optional
    use_spectre_json optional

    # target
    cflags=(
        "${cflags[@]}"

        # spectre paths
        -I"api/c" -I"src"
    )
    ldflags=(
        "${ldflags[@]}"
    )

    # build
    cc "${cflags[@]}" "$@" \
       "api/c/aes.c" "api/c/spectre-algorithm.c" \
       "api/c/spectre-algorithm_v0.c" "api/c/spectre-algorithm_v1.c" "api/c/spectre-algorithm_v2.c" "api/c/spectre-algorithm_v3.c" \
       "api/c/spectre-types.c" "api/c/spectre-util.c" "api/c/spectre-marshal-util.c" "api/c/spectre-marshal.c" "src/spectre-cli-util.c" \
       "${ldflags[@]}" "src/spectre-cli.c" -o "spectre"
    echo "done!  You can now run ./spectre-cli-tests, ./install or use ./$_"
}


### TARGET: SPECTRE-BENCH
spectre-bench() {
    # dependencies
    use_spectre_sodium required

    # target
    cflags=(
        "${cflags[@]}"

        # spectre paths
        -I"api/c" -I"src"
    )
    ldflags=(
        "${ldflags[@]}"
    )

    # build
    cc "${cflags[@]}" "$@" \
       "api/c/aes.c" "api/c/spectre-algorithm.c" \
       "api/c/spectre-algorithm_v0.c" "api/c/spectre-algorithm_v1.c" "api/c/spectre-algorithm_v2.c" "api/c/spectre-algorithm_v3.c" \
       "api/c/spectre-types.c" "api/c/spectre-util.c" \
       "${ldflags[@]}" "src/spectre-bench.c" -o "spectre-bench"
    echo "done!  You can now use ./$_"
}


### TARGET: SPECTRE-TESTS
spectre-tests() {
    # dependencies
    use_spectre_sodium required
    use_spectre_xml required

    # target
    cflags=(
        "${cflags[@]}"

        # spectre paths
        -I"api/c" -I"src"
    )
    ldflags=(
        "${ldflags[@]}"
    )

    # build
    cc "${cflags[@]}" "$@" \
       "api/c/aes.c" "api/c/spectre-algorithm.c" \
       "api/c/spectre-algorithm_v0.c" "api/c/spectre-algorithm_v1.c" "api/c/spectre-algorithm_v2.c" "api/c/spectre-algorithm_v3.c" \
       "api/c/spectre-types.c" "api/c/spectre-util.c" "src/spectre-tests-util.c" \
       "${ldflags[@]}" "src/spectre-tests.c" -o "spectre-tests"
    echo "done!  You can now use ./$_"
}


### TOOLS
haslib() {
    cc -x c "${ldflags[@]}" -l"$1" -o /dev/null - <<< 'int main() { return 0; }' &>/dev/null
}
cc() (
    (( verbose )) && set -x

    if { hash "$CC"; } 2>/dev/null; then
        "$CC" -std=c11 "$@"
    elif { hash clang; } 2>/dev/null; then
        clang -std=c11 "$@"
    elif { hash llvm-gcc; } 2>/dev/null; then
        llvm-gcc -std=c11 "$@"
    elif { hash gcc; } 2>/dev/null; then
        gcc -std=c11 "$@"
    else
        echo >&2 "Need a compiler.  Please install GCC or LLVM."
        exit 1
    fi
)


### DEPENDENCIES
use() {
    local option=$1 requisite=$2 lib=$3; shift 3
    local enabled=${!option} found=0 _cflags _ldflags

    if (( enabled )); then
        for lib in "$lib" "$@"; do
            if _cflags=$(pkg-config --cflags "$lib" 2>/dev/null) && _ldflags=$(pkg-config --libs "$lib" 2>/dev/null); then
                cflags+=( $_cflags ) ldflags+=( $_ldflags ); found=1
            elif _cflags=$(pkg-config --cflags "lib$lib" 2>/dev/null) && _ldflags=$(pkg-config --libs "lib$lib" 2>/dev/null); then
                cflags+=( $_cflags ) ldflags+=( $_ldflags ); found=1
            elif haslib "$lib"; then
                ldflags+=( -l"$lib" ); found=1
            fi
        done

        if (( found )); then
            echo "INFO:     Enabled $option (lib$lib)."
            return 0

        elif [[ $requisite == required ]]; then
            echo >&2 "ERROR:    $option was enabled but is missing $lib library.  Please install this library before continuing."
            exit 1

        else
            echo >&2 "WARNING:  $option was enabled but is missing $lib library.  Will continue with $option disabled!"
            return 1

        fi

    elif [[ $requisite == required ]]; then
        echo >&2 "ERROR:    $option was required but is not enabled.  Please enable the option or remove this target before continuing."
        exit 1

    else
        echo "INFO:     $option is supported but not enabled."
        return 1
    fi
}
use_spectre_sodium() {
    local requisite=$1
    use spectre_sodium "$requisite" sodium && cflags+=( -D"SPECTRE_SODIUM=1" ) ||:
}
use_spectre_color() {
    local requisite=$1
    use spectre_color "$requisite" curses tinfo && cflags+=( -D"SPECTRE_COLOR=1" ) ||:
}
use_spectre_json() {
    local requisite=$1
    use spectre_json "$requisite" json-c && cflags+=( -D"SPECTRE_JSON=1" ) ||:
}
use_spectre_xml() {
    local requisite=$1
    use spectre_xml "$requisite" xml2 && cflags+=( $(xml2-config --cflags) ) ldflags+=( $(xml2-config --libs) ) ||:
}


### BUILD TARGETS
for target in "${targets_all[@]}"; do
    if [[ $targets == 'all' || " $targets " = *" $target "*  ]]; then
        echo
        echo "Building target: $target..."
        ( "$target" "$@" )
    fi
done
07070100000055000081ED00000000000000000000000163C2D55500000084000000000000000000000000000000000000002A00000000spectre-cli-3.0+git20230114.26c01a5/clean#!/usr/bin/env bash
#
# Clean all generated build files.
set -e
cd "${BASH_SOURCE%/*}"

rm -vfr spectre spectre-bench spectre-tests
07070100000056000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000002A00000000spectre-cli-3.0+git20230114.26c01a5/cmake07070100000057000081A400000000000000000000000163C2D555000028E5000000000000000000000000000000000000003B00000000spectre-cli-3.0+git20230114.26c01a5/cmake/Findsodium.cmake# Written in 2016 by Henrik Steffen Gaßmann <henrik@gassmann.onl>
#
# To the extent possible under law, the author(s) have dedicated all copyright
# and related and neighboring rights to this software to the public domain
# worldwide. This software is distributed without any warranty.
#
# You should have received a copy of the CC0 Public Domain Dedication along with
# this software. If not, see
#
# http://creativecommons.org/publicdomain/zero/1.0/
#
# ##############################################################################
# Tries to find the local libsodium installation.
#
# On Windows the sodium_DIR environment variable is used as a default hint which
# can be overridden by setting the corresponding cmake variable.
#
# Once done the following variables will be defined:
#
# sodium_FOUND sodium_INCLUDE_DIR sodium_LIBRARY_DEBUG sodium_LIBRARY_RELEASE
# sodium_VERSION_STRING
#
# Furthermore an imported "sodium" target is created.
#

if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
  set(_GCC_COMPATIBLE 1)
endif()

# static library option
if(NOT DEFINED sodium_USE_STATIC_LIBS)
  option(sodium_USE_STATIC_LIBS "enable to statically link against sodium" OFF)
endif()
if(NOT (sodium_USE_STATIC_LIBS EQUAL sodium_USE_STATIC_LIBS_LAST))
  unset(sodium_LIBRARY CACHE)
  unset(sodium_LIBRARY_DEBUG CACHE)
  unset(sodium_LIBRARY_RELEASE CACHE)
  unset(sodium_DLL_DEBUG CACHE)
  unset(sodium_DLL_RELEASE CACHE)
  set(sodium_USE_STATIC_LIBS_LAST
      ${sodium_USE_STATIC_LIBS}
      CACHE INTERNAL "internal change tracking variable")
endif()

# ##############################################################################
# UNIX
if(UNIX)
  # import pkg-config
  find_package(PkgConfig QUIET)
  if(PKG_CONFIG_FOUND)
    pkg_check_modules(sodium_PKG QUIET libsodium)
  endif()

  if(sodium_USE_STATIC_LIBS)
    if(sodium_PKG_STATIC_LIBRARIES)
      foreach(_libname ${sodium_PKG_STATIC_LIBRARIES})
        if(NOT _libname MATCHES "^lib.*\\.a$") # ignore strings already ending
                                               # with .a
          list(INSERT sodium_PKG_STATIC_LIBRARIES 0 "lib${_libname}.a")
        endif()
      endforeach()
      list(REMOVE_DUPLICATES sodium_PKG_STATIC_LIBRARIES)
    else()
      # if pkgconfig for libsodium doesn't provide static lib info, then
      # override PKG_STATIC here..
      set(sodium_PKG_STATIC_LIBRARIES libsodium.a)
    endif()

    set(XPREFIX sodium_PKG_STATIC)
  else()
    if(sodium_PKG_LIBRARIES STREQUAL "")
      set(sodium_PKG_LIBRARIES sodium)
    endif()

    set(XPREFIX sodium_PKG)
  endif()

  find_path(sodium_INCLUDE_DIR sodium.h HINTS ${${XPREFIX}_INCLUDE_DIRS})
  find_library(sodium_LIBRARY_DEBUG
               NAMES ${${XPREFIX}_LIBRARIES}
               HINTS ${${XPREFIX}_LIBRARY_DIRS})
  find_library(sodium_LIBRARY_RELEASE
               NAMES ${${XPREFIX}_LIBRARIES}
               HINTS ${${XPREFIX}_LIBRARY_DIRS})

  # ############################################################################
  # Windows
elseif(WIN32)
  set(sodium_DIR "$ENV{sodium_DIR}" CACHE FILEPATH "sodium install directory")
  mark_as_advanced(sodium_DIR)

  find_path(sodium_INCLUDE_DIR sodium.h
            HINTS ${sodium_DIR}
            PATH_SUFFIXES include)

  if(MSVC)
    # detect target architecture
    file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/arch.c" [=[
            #if defined _M_IX86
            #error ARCH_VALUE x86_32
            #elif defined _M_X64
            #error ARCH_VALUE x86_64
            #endif
            #error ARCH_VALUE unknown
        ]=])
    try_compile(_UNUSED_VAR "${CMAKE_CURRENT_BINARY_DIR}"
                "${CMAKE_CURRENT_BINARY_DIR}/arch.c"
                OUTPUT_VARIABLE _COMPILATION_LOG)
    string(REGEX
           REPLACE ".*ARCH_VALUE ([a-zA-Z0-9_]+).*"
                   "\\1"
                   _TARGET_ARCH
                   "${_COMPILATION_LOG}")

    # construct library path
    if(_TARGET_ARCH STREQUAL "x86_32")
      string(APPEND _PLATFORM_PATH "Win32")
    elseif(_TARGET_ARCH STREQUAL "x86_64")
      string(APPEND _PLATFORM_PATH "x64")
    else()
      message(
        FATAL_ERROR
          "the ${_TARGET_ARCH} architecture is not supported by Findsodium.cmake."
        )
    endif()
    string(APPEND _PLATFORM_PATH "/$$CONFIG$$")

    if(MSVC_VERSION LESS 1900)
      math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 60")
    else()
      math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 50")
    endif()
    string(APPEND _PLATFORM_PATH "/v${_VS_VERSION}")

    if(sodium_USE_STATIC_LIBS)
      string(APPEND _PLATFORM_PATH "/static")
    else()
      string(APPEND _PLATFORM_PATH "/dynamic")
    endif()

    string(REPLACE "$$CONFIG$$"
                   "Debug"
                   _DEBUG_PATH_SUFFIX
                   "${_PLATFORM_PATH}")
    string(REPLACE "$$CONFIG$$"
                   "Release"
                   _RELEASE_PATH_SUFFIX
                   "${_PLATFORM_PATH}")

    find_library(sodium_LIBRARY_DEBUG libsodium.lib
                 HINTS ${sodium_DIR}
                 PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX})
    find_library(sodium_LIBRARY_RELEASE libsodium.lib
                 HINTS ${sodium_DIR}
                 PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX})
    if(NOT sodium_USE_STATIC_LIBS)
      set(CMAKE_FIND_LIBRARY_SUFFIXES_BCK ${CMAKE_FIND_LIBRARY_SUFFIXES})
      set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll")
      find_library(sodium_DLL_DEBUG libsodium
                   HINTS ${sodium_DIR}
                   PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX})
      find_library(sodium_DLL_RELEASE libsodium
                   HINTS ${sodium_DIR}
                   PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX})
      set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_BCK})
    endif()

  elseif(_GCC_COMPATIBLE)
    if(sodium_USE_STATIC_LIBS)
      find_library(sodium_LIBRARY_DEBUG libsodium.a
                   HINTS ${sodium_DIR}
                   PATH_SUFFIXES lib)
      find_library(sodium_LIBRARY_RELEASE libsodium.a
                   HINTS ${sodium_DIR}
                   PATH_SUFFIXES lib)
    else()
      find_library(sodium_LIBRARY_DEBUG libsodium.dll.a
                   HINTS ${sodium_DIR}
                   PATH_SUFFIXES lib)
      find_library(sodium_LIBRARY_RELEASE libsodium.dll.a
                   HINTS ${sodium_DIR}
                   PATH_SUFFIXES lib)

      file(GLOB _DLL
           LIST_DIRECTORIES false
           RELATIVE "${sodium_DIR}/bin"
           "${sodium_DIR}/bin/libsodium*.dll")
      find_library(sodium_DLL_DEBUG ${_DLL} libsodium
                   HINTS ${sodium_DIR}
                   PATH_SUFFIXES bin)
      find_library(sodium_DLL_RELEASE ${_DLL} libsodium
                   HINTS ${sodium_DIR}
                   PATH_SUFFIXES bin)
    endif()
  else()
    message(FATAL_ERROR "this platform is not supported by FindSodium.cmake")
  endif()

  # ############################################################################
  # unsupported
else()
  message(FATAL_ERROR "this platform is not supported by FindSodium.cmake")
endif()

# ##############################################################################
# common stuff

# extract sodium version
if(sodium_INCLUDE_DIR)
  set(_VERSION_HEADER "${sodium_INCLUDE_DIR}/sodium/version.h")
  if(EXISTS "${_VERSION_HEADER}")
    file(READ "${_VERSION_HEADER}" _VERSION_HEADER_CONTENT)
    string(REGEX
           REPLACE ".*define[ \t]+SODIUM_VERSION_STRING[^\"]+\"([^\"]+)\".*"
                   "\\1"
                   sodium_VERSION_STRING
                   "${_VERSION_HEADER_CONTENT}")
    set(sodium_VERSION_STRING "${sodium_VERSION_STRING}")
  endif()
endif()

# communicate results
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(sodium
                                  REQUIRED_VARS
                                  sodium_LIBRARY_RELEASE
                                  sodium_LIBRARY_DEBUG
                                  sodium_INCLUDE_DIR
                                  VERSION_VAR
                                  sodium_VERSION_STRING)

# mark file paths as advanced
mark_as_advanced(sodium_INCLUDE_DIR)
mark_as_advanced(sodium_LIBRARY_DEBUG)
mark_as_advanced(sodium_LIBRARY_RELEASE)
if(WIN32)
  mark_as_advanced(sodium_DLL_DEBUG)
  mark_as_advanced(sodium_DLL_RELEASE)
endif()

# create imported target
if(sodium_USE_STATIC_LIBS)
  set(_LIB_TYPE STATIC)
else()
  set(_LIB_TYPE SHARED)
endif()
add_library(sodium ${_LIB_TYPE} IMPORTED)

set_target_properties(sodium
                      PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
                                 "${sodium_INCLUDE_DIR}"
                                 IMPORTED_LINK_INTERFACE_LANGUAGES
                                 "C")

if(sodium_USE_STATIC_LIBS)
  set_target_properties(sodium
                        PROPERTIES INTERFACE_COMPILE_DEFINITIONS
                                   "SODIUM_STATIC"
                                   IMPORTED_LOCATION
                                   "${sodium_LIBRARY_RELEASE}"
                                   IMPORTED_LOCATION_DEBUG
                                   "${sodium_LIBRARY_DEBUG}")
else()
  if(UNIX)
    set_target_properties(sodium
                          PROPERTIES IMPORTED_LOCATION
                                     "${sodium_LIBRARY_RELEASE}"
                                     IMPORTED_LOCATION_DEBUG
                                     "${sodium_LIBRARY_DEBUG}")
  elseif(WIN32)
    set_target_properties(sodium
                          PROPERTIES IMPORTED_IMPLIB
                                     "${sodium_LIBRARY_RELEASE}"
                                     IMPORTED_IMPLIB_DEBUG
                                     "${sodium_LIBRARY_DEBUG}")
    if(NOT (sodium_DLL_DEBUG MATCHES ".*-NOTFOUND"))
      set_target_properties(sodium
                            PROPERTIES IMPORTED_LOCATION_DEBUG
                                       "${sodium_DLL_DEBUG}")
    endif()
    if(NOT (sodium_DLL_RELEASE MATCHES ".*-NOTFOUND"))
      set_target_properties(sodium
                            PROPERTIES IMPORTED_LOCATION_RELWITHDEBINFO
                                       "${sodium_DLL_RELEASE}"
                                       IMPORTED_LOCATION_MINSIZEREL
                                       "${sodium_DLL_RELEASE}"
                                       IMPORTED_LOCATION_RELEASE
                                       "${sodium_DLL_RELEASE}")
    endif()
  endif()
endif()
07070100000058000081ED00000000000000000000000163C2D555000005A0000000000000000000000000000000000000002F00000000spectre-cli-3.0+git20230114.26c01a5/distribute#!/usr/bin/env bash
set -e

cd "${BASH_SOURCE%/*}/.."
tag=$(git describe --exact-match --match '*-cli*') || { echo >&2 "Tree is not at a release tag."; exit 1; }
version=$(git describe --match '*-cli*' --long --dirty --broken)
[[ $version != *-dirty ]] || { echo >&2 "Tree is dirty, first commit any changes."; exit 1; }

spectreArchive=spectre-$version.tar.gz
[[ -e $spectreArchive ]] && echo >&2 "WARNING: $spectreArchive already exists.  Will overwrite."
read -n1 -p "Will prepare and release $spectreArchive.  Press a key to continue or ^C to abort."

echo "Cleaning .."
git clean -ffdx .

echo "Creating archive $spectreArchive .."
echo "$version" > VERSION
git show --show-signature --pretty=format:%H --quiet "$tag" > TAG
{ git ls-files -z .; printf '%s\0' VERSION TAG; } | xargs -0 tar -Lcvzf "$spectreArchive"

echo "Creating archive signature $spectreArchive.sig .."
gpg --detach-sign --local-user 5C2D1D61853F20F2FCDDCCB70EF21226F43EA6BC "$spectreArchive"

echo "Installing archive and signature in site .."
cd ../../public/site
mv "$OLDPWD/$spectreArchive" .; [[ -e $_ ]]
mv "$OLDPWD/$spectreArchive.sig" .; [[ -e $_ ]]
ln -sf "$spectreArchive" "spectre-cli.tar.gz"; [[ -e $_ ]]
ln -sf "$spectreArchive.sig" "spectre-cli.tar.gz.sig"; [[ -e $_ ]]

echo
echo "Done.  Ready to publish the site."
echo "     package: $spectreArchive"
echo "   signature: $spectreArchive.sig"
echo "         url: https://spectre.app/$spectreArchive"
07070100000059000081ED00000000000000000000000163C2D555000009B2000000000000000000000000000000000000002C00000000spectre-cli-3.0+git20230114.26c01a5/install#!/usr/bin/env bash
#
# Install the Spectre CLI tool.
set -e
cd "${BASH_SOURCE%/*}"
source bashlib

inf "This will install the spectre tool."

# Try to guess then ask for the bin dir to install to.
IFS=: read -a paths <<< "$PATH"
if inArray ~/bin "${paths[@]}"; then
    bindir=~/bin
elif inArray ~/.bin "${paths[@]}"; then
    bindir=~/.bin
elif inArray /usr/local/bin "${paths[@]}"; then
    bindir=/usr/local/bin
else
    bindir=~/bin
fi
bindir=$(ask -d "$bindir" "What bin directory should I install to?")
[[ -d "$bindir" ]] || mkdir "$bindir" || ftl 'Cannot create missing bin directory: %s' "$bindir" || exit
[[ -w "$bindir" ]] || ftl 'Cannot write to bin directory: %s' "$bindir" || exit

# Install Spectre.
install -m555 spectre "$bindir"
[[ ! -e "$bindir/bashlib" ]] && install bashlib "$bindir" ||:

# Convenience bash function.
inf "Installation successful!"
echo

inf "To improve usability, you can install an spectre function in your bash shell."
inf "This function adds the following features:"
inf "  - Automatically remember your user name in the shell if not set."
inf "  - Automatically put the password in the clipboard (some platforms)."
echo
inf "To do this you need the following function in ~/.bashrc:\n%s" "$(<spectre.bashrc)"
echo
inf "We can do this for you automatically now."
if ask -c Y!n "Append the spectre function to your .bashrc?"; then
    cat spectre.bashrc >> ~/.bashrc
    inf "Done!  Don't forget to run '%s' to apply the changes!" "source ~/.bashrc"
fi
echo

inf "You can also save your user name in ~/.bashrc.  Leave blank to skip this step."
if SPECTRE_USERNAME=$(ask "Your full name:") && [[ $SPECTRE_USERNAME ]] ; then
    printf 'export SPECTRE_USERNAME=%q\n' "$SPECTRE_USERNAME" >> ~/.bashrc
fi
inf "If you have an askpass program you'd like to use, you can specify it here."
inf "An askpass program provides a graphical interface for entering things like your personal secret."
inf "Leave blank to skip this step and enter passwords using the terminal."
if [[ ! $SPECTRE_ASKPASS ]] && hash ssh-askpass 2>/dev/null; then
    SPECTRE_ASKPASS=ssh-askpass
fi
if SPECTRE_ASKPASS=$(ask +"$SPECTRE_ASKPASS" "askpass program:") && [[ $SPECTRE_ASKPASS ]] ; then
    printf 'export SPECTRE_ASKPASS=%q\n' "$SPECTRE_ASKPASS" >> ~/.bashrc
fi
echo

inf "Shell features installed."
inf "To load these convenience features into your already running shell, type: source ~/.bashrc"
inf "To begin using Spectre, type: spectre -h or spectre my-site-name"
0707010000005A000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000002C00000000spectre-cli-3.0+git20230114.26c01a5/scripts0707010000005B000081ED00000000000000000000000163C2D5550000E566000000000000000000000000000000000000003400000000spectre-cli-3.0+git20230114.26c01a5/scripts/bashlib#! /usr/bin/env bash
# ___________________________________________________________________________ #
#                                                                             #
#       BashLIB -- A library for Bash scripting convenience.                  #
#                                                                             #
#                                                                             #
#    Licensed under the Apache License, Version 2.0 (the "License");          #
#    you may not use this file except in compliance with the License.         #
#    You may obtain a copy of the License at                                  #
#                                                                             #
#        http://www.apache.org/licenses/LICENSE-2.0                           #
#                                                                             #
#    Unless required by applicable law or agreed to in writing, software      #
#    distributed under the License is distributed on an "AS IS" BASIS,        #
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
#    See the License for the specific language governing permissions and      #
#    limitations under the License.                                           #
# ___________________________________________________________________________ #
#                                                                             #
#                                                                             #
# Copyright 2007-2013, lhunath                                                #
#   * http://www.lhunath.com                                                  #
#   * Maarten Billemont                                                       #
#                                                                             #



#  ______________________________________________________________________ 
# |                                                                      |
# |                                            .:  TABLE OF CONTENTS  :. |
# |______________________________________________________________________|
# 
#     chr decimal
# Outputs the character that has the given decimal ASCII value.
#
#     ord character
# Outputs the decimal ASCII value of the given character.
#
#     hex character
# Outputs the hexadecimal ASCII value of the given character.
#
#     unhex character
# Outputs the character that has the given decimal ASCII value.
#
#     max numbers...
# Outputs the highest of the given numbers.
#
#     min numbers...
# Outputs the lowest of the given numbers.
#
#     totime "YYYY-MM-DD HH:MM:SS.mmm"...
# Outputs the number of milliseconds in the given date string(s).
#
#     exists application
# Succeeds if the application is in PATH and is executable.
#
#     eol message
# Return termination punctuation for a message, if necessary.
#
#     hr pattern [length]
# Outputs a horizontal ruler of the given length in characters or the terminal column length otherwise.
#
#     cloc
# Outputs the current cursor location as two space-separated numbers: row column.
#
#     readwhile command [args]
# Outputs the characters typed by the user into the terminal's input buffer while running the given command.
#
#     log [format] [arguments...]
# Log an event at a certain importance level.
# The event is expressed as a printf(1) format argument.
#
#     ask [-c optionchars|-d default] [-s|-S maskchar] message...
# Ask a question and read the user's reply to it.  Then output the result on stdout.
#
#     trim lines ...
# Trim the whitespace off of the beginning and end of the given lines.
#
#     reverse [-0|-d delimitor] [elements ...] [<<< elements]
# Reverse the order of the given elements.
#
#     order [-0|-d char] [-[cC] comparator|-n] [-t number] [elements ...] [<<< elements]
# Orders the elements in ascending order.
#
#     mutex file
# Open a mutual exclusion lock on the file, unless another process already owns one.
#
#     pushjob [poolsize] command
# Start an asynchronous command within a pool, waiting for space in the pool if it is full.
#
#     fsleep time
# Wait for the given (fractional) amount of seconds.
#
#     showHelp name description author [option description]...
# Generate a prettily formatted usage description of the application.
#
#     shquote [-e] [argument...]
# Shell-quote the arguments to make them safe for injection into bash code.
#
#     requote [string]
# Escape the argument string to make it safe for injection into a regex.
#
#     shorten [-p pwd] path [suffix]...
# Shorten an absolute path for pretty printing.
#
#     up .../path|num
# Walk the current working directory up towards root num times or until path is found.
#
#     buildarray name terms... -- elements...
# Create an array by adding all the terms to it for each element, replacing {} terms by the element.
#
#     inArray element array
# Checks whether a certain element is in the given array.
#
#     xpathNodes query [files...]
# Outputs every xpath node that matches the query on a separate line.
#
#     hideDebug [on|off]
# Toggle Bash's debugging mode off temporarily.
#
#     stackTrace
# Output the current script's function execution stack.
#
_tocHash=71e13f42e1ea82c1c7019b27a3bc71f3


#  ______________________________________________________________________ 
# |                                                                      |
# |                                         .:  GLOBAL CONFIGURATION  :. |
# |______________________________________________________________________|

# Unset all exported functions.  Exported functions are evil.
while read _ _ func; do
    command unset -f "$func"
done < <(command declare -Fx)

{
shopt -s extglob
shopt -s globstar
} 2>/dev/null ||:

# Generate Table Of Contents
genToc() {
    local line= comments=() usage= whatis= lineno=0 out= outhash= outline=
    while read -r line; do
        (( ++lineno ))

        [[ $line = '#'* ]] && comments+=("$line") && continue
        [[ $line = +([[:alnum:]])'() {' ]] && IFS='()' read func _ <<< "$line" && [[ $func != $FUNCNAME ]] && {
            usage=${comments[3]##'#'+( )}
            whatis=${comments[5]##'#'+( )}
            [[ $usage = $func* && $whatis = *. ]] || err "Malformed docs for %s (line %d)." "$func" "$lineno"

            printf -v outline '#     %s\n# %s\n#\n' "$usage" "$whatis"
            out+=$outline
        }
        comments=()
    done < ~/.bin/bashlib

    outhash=$(openssl md5 <<< "$out")
    if [[ $_tocHash = "$outhash" ]]; then
        inf 'Table of contents up-to-date.'
    else
        printf '%s' "$out"
        printf '_tocHash=%q' "$outhash"
        wrn 'Table of contents outdated.'
    fi
}



#  ______________________________________________________________________ 
# |                                                                      |
# |                                          .:  GLOBAL DECLARATIONS  :. |
# |______________________________________________________________________|

# Environment
TMPDIR=${TMPDIR:-/tmp} TMPDIR=${TMPDIR%/}

# Variables for convenience sequences.
bobber=(     '.' 'o' 'O' 'o' )
spinner=(    '-' \\  '|' '/' )
crosser=(    '+' 'x' '+' 'x' )
runner=(     '> >'           \
             '>> '           \
             ' >>'           )

# Variables for terminal requests.
[[ -t 2 && $TERM != dumb ]] && {
    COLUMNS=$({ tput cols   || tput co;} 2>&3) # Columns in a line
    LINES=$({   tput lines  || tput li;} 2>&3) # Lines on screen
    alt=$(      tput smcup  || tput ti      ) # Start alt display
    ealt=$(     tput rmcup  || tput te      ) # End   alt display
    hide=$(     tput civis  || tput vi      ) # Hide cursor
    show=$(     tput cnorm  || tput ve      ) # Show cursor
    save=$(     tput sc                     ) # Save cursor
    load=$(     tput rc                     ) # Load cursor
    dim=$(      tput dim    || tput mh      ) # Start dim
    bold=$(     tput bold   || tput md      ) # Start bold
    stout=$(    tput smso   || tput so      ) # Start stand-out
    estout=$(   tput rmso   || tput se      ) # End stand-out
    under=$(    tput smul   || tput us      ) # Start underline
    eunder=$(   tput rmul   || tput ue      ) # End   underline
    reset=$(    tput sgr0   || tput me      ) # Reset cursor
    blink=$(    tput blink  || tput mb      ) # Start blinking
    italic=$(   tput sitm   || tput ZH      ) # Start italic
    eitalic=$(  tput ritm   || tput ZR      ) # End   italic
[[ $TERM != *-m ]] && {
    red=$(      tput setaf 1|| tput AF 1    )
    green=$(    tput setaf 2|| tput AF 2    )
    yellow=$(   tput setaf 3|| tput AF 3    )
    blue=$(     tput setaf 4|| tput AF 4    )
    magenta=$(  tput setaf 5|| tput AF 5    )
    cyan=$(     tput setaf 6|| tput AF 6    )
}
    black=$(    tput setaf 0|| tput AF 0    )
    white=$(    tput setaf 7|| tput AF 7    )
    default=$(  tput op                     )
    eed=$(      tput ed     || tput cd      )   # Erase to end of display
    eel=$(      tput el     || tput ce      )   # Erase to end of line
    ebl=$(      tput el1    || tput cb      )   # Erase to beginning of line
    ewl=$eel$ebl                                # Erase whole line
    draw=$(     tput -S <<< '   enacs
                                smacs
                                acsc
                                rmacs' || { \
                tput eA; tput as;
                tput ac; tput ae;         } )   # Drawing characters
    back=$'\b'
} 3>&2 2>/dev/null ||:





#  ______________________________________________________________________ 
# |                                                                      |
# |                                        .:  FUNCTION DECLARATIONS  :. |
# |______________________________________________________________________|



#  ______________________________________________________________________
# |__ Chr _______________________________________________________________|
#
#       chr decimal
#
# Outputs the character that has the given decimal ASCII value.
#
chr() {
    printf "\\$(printf '%03o' "$1")"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Ord _______________________________________________________________|
#
#       ord character
#
# Outputs the decimal ASCII value of the given character.
#
ord() {
    local str=$1 s
    for (( s=0; s < ${#str}; ++s )); do
        printf '%d' "'${str:s:1}"
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Hex _______________________________________________________________|
#
#       hex character
#
# Outputs the hexadecimal ASCII value of the given character.
#
hex() { 
    local str=$1 s
    for (( s=0; s < ${#str}; ++s )); do
        printf '%02X' "'${str:s:1}"
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Unhex _______________________________________________________________|
#
#       unhex character
#
# Outputs the character that has the given hexadecimal ASCII value.
#
unhex() {
    local hex=$1 h
    for (( h=0; h < ${#hex}; h+=2 )); do
        printf "\\x${hex:h:2}"
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ max _______________________________________________________________|
#
#       max numbers...
#
# Outputs the highest of the given numbers.
#
max() {
    local max=$1 n
    for n; do
        (( n > max )) && max=$n
    done
    printf %d "$max"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ min _______________________________________________________________|
#
#       min numbers...
#
# Outputs the lowest of the given numbers.
#
min() {
    local min=$1 n
    for n; do
        (( n < min )) && min=$n
    done
    printf '%d' "$min"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ si ________________________________________________________________|
#
#       si number
#
# Output a human-readable version of the number using SI units.
#
si() {
    local number=$1

    if (( number >= 1000000000000000 )); then printf '%dM' "$((number / 1000000000000000))"
    elif (( number >= 1000000000000 )); then printf '%dM' "$((number / 1000000000000))"
    elif (( number >= 1000000000 )); then printf '%dM' "$((number / 1000000000))"
    elif (( number >= 1000000 )); then printf '%dM' "$((number / 1000000))"
    elif (( number >= 1000 )); then printf '%dk' "$((number / 1000))"
    else printf '%d' "$number"; fi
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ totime ____________________________________________________________|
#
#       totime "YYYY-MM-DD HH:MM:SS.mmm"...
#
# Outputs the number of milliseconds in the given date string(s).
#
# When multiple date string arguments are given, multiple time strings are output, one per line.
#
# The fields should be in the above defined order.  The delimitor between the fields may be any one of [ -:.].
# If a date string does not follow the defined format, the result is undefined.
#
# Note that this function uses a very simplistic conversion formula which does not take any special calendar
# convenions into account.  It assumes there are 12 months in evert year, 31 days in every month, 24 hours
# in every day, 60 minutes in every hour, 60 seconds in every minute and 1000 milliseconds in every second.
#
totime() {
    local arg time year month day hour minute second milli
    for arg; do
        IFS=' -:.' read year month day hour minute second milli <<< "$arg" &&
            (( time = (((((((((((10#$year * 12) + 10#$month) * 31) + 10#$day) * 24) + 10#$hour) * 60) + 10#$minute) * 60) + 10#$second) * 1000) + 10#$milli )) &&
            printf '%d\n' "$time"
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Exists ____________________________________________________________|
#
#       exists application
#
# Succeeds if the application is in PATH and is executable.
#
exists() {
    [[ -x $(type -P "$1" 2>/dev/null) ]]
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ FirstExists ____________________________________________________________|
#
#       firstExists file...
#
# Outputs the first of the arguments that is a file which exists.
#
firstExists() {
    local file;
    for file; do
        [[ -e "$file" ]] && printf %s "$file" && exit
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Eol _______________________________________________________________|
#
#       eol message
#
# Return termination punctuation for a message, if necessary.
#
eol() {
    : #[[ $1 && $1 != *[\!\?.,:\;\|] ]] && printf .. ||:
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Hr _______________________________________________________________|
#
#       hr pattern [length]
#
# Outputs a horizontal ruler of the given length in characters or the terminal column length otherwise.
# The ruler is a repetition of the given pattern string.
#
hr() {
    local pattern=${1:--} length=${2:-$COLUMNS} ruler=

    while (( ${#ruler} < length )); do
        ruler+=${pattern:0:length-${#ruler}}
    done

    printf %s "$ruler"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ CLoc ______________________________________________________________|
#
#       cloc
#
# Outputs the current cursor location as two space-separated numbers: row column.
#
cloc() {
    local old=$(stty -g)
    trap 'stty "$old"' RETURN
    stty raw

    # If the tty has input waiting then we can't read back its response.  We'd only break and pollute the tty input buffer.
    read -t 0 < /dev/tty 2>/dev/null && return 1

    printf '\e[6n' > /dev/tty
    IFS='[;' read -dR _ row col < /dev/tty
    printf '%d %d' "$row" "$col"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ readwhile ______________________________________________________________|
#
#       readwhile command [args]
#
# Outputs the characters typed by the user into the terminal's input buffer while running the given command.
#
readwhile() {
    local old=$(stty -g) in result REPLY
    trap 'stty "$old"' RETURN
    stty raw

    "$@"
    result=$?

    while read -t 0; do
        IFS= read -rd '' -n1 && in+=$REPLY
    done
    printf %s "$in"

    return $result
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Latest ____________________________________________________________|
#
#       latest [file...]
#
# Output the argument that represents the file with the latest modification time.
#
latest() (
    shopt -s nullglob
    local file latest=$1
    for file; do
        [[ $file -nt $latest ]] && latest=$file
    done
    printf '%s\n' "$latest"
) # _____________________________________________________________________



#  _______________________________________________________________________
# |__ Iterate ____________________________________________________________|
#
#       iterate [command]
#
# All arguments to iterate make up a single command that will be executed.
#
# Any of the arguments may be of the format {x..y[..z]} which causes the command
# to be executed in a loop, each iteration substituting the argument for the
# current step the loop has reached from x to y.  We step from x to y by
# walking from x's position in the ASCII character table to y's with a step of z
# or 1 if z is not specified.
#
iterate() (
    local command=( "$@" ) iterationCommand=() loop= a= arg= current=() step=() target=()
    for a in "${!command[@]}"; do
        arg=${command[a]}
        if [[ $arg = '{'*'}' ]]; then
            loop=${arg#'{'} loop=${loop%'}'}
            step[a]=${loop#*..*..} current[a]=${loop%%..*} target[a]=${loop#*..} target[a]=${target[a]%.."${step[a]}"}
            [[ ! ${step[a]} || ${step[a]} = $loop ]] && step[a]=1
        fi
    done
    if (( ${#current[@]} )); then
        for loop in "${!current[@]}"; do
            while true; do
                iterationCommand=()

                for a in "${!command[@]}"; do
                    (( a == loop )) \
                        && iterationCommand+=( "${current[a]}" ) \
                        || iterationCommand+=( "${command[a]}" )
                done

                iterate "${iterationCommand[@]}"

                [[ ${current[loop]} = ${target[loop]} ]] && break
                current[loop]="$(chr "$(( $(ord "${current[loop]}") + ${step[loop]} ))")"
            done
        done
    else
        "${command[@]}"
    fi
) # _____________________________________________________________________



#  _______________________________________________________________________
# |__ csvline ____________________________________________________________|
#
#       csvline [-d delimiter] [-D line-delimiter]
#
# Parse a CSV record from standard input, storing the fields in the CSVLINE array.
#
# By default, a single line of input is read and parsed into comma-delimited fields.
# Fields can optionally contain double-quoted data, including field delimiters.
#
# A different field delimiter can be specified using -d.  You can use -D
# to change the definition of a "record" (eg. to support NULL-delimited records).
#
csvline() {
    CSVLINE=()
    local line field quoted=0 delimiter=, lineDelimiter=$'\n' c
    local OPTIND=1 arg
    while getopts :d: arg; do
        case $arg in
            d) delimiter=$OPTARG ;;
        esac
    done

    IFS= read -d "$lineDelimiter" -r line || return
    while IFS= read -rn1 c; do
        case $c in
            \")
                (( quoted = !quoted ))
                continue ;;
            $delimiter)
                if (( ! quoted )); then
                    CSVLINE+=( "$field" ) field=
                    continue
                fi ;;
        esac
        field+=$c
    done <<< "$line"
    [[ $field ]] && CSVLINE+=( "$field" ) ||:
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Logging ___________________________________________________________|
#
#       log format [arguments...]
#
# Log an event at a certain importance level.  The event is expressed as a printf(1) format argument.
# The current exit code remains unaffected by the execution of this function.
#
# Instead of 'log', you can use a level as command name, to log at that level.  Using log, messages are
# logged at level inf.  The supported levels are: trc, dbg, inf, wrn, err, ftl.
#
# If you prefix the command name with a p, the log message is shown as a spinner and waits for the next
# closing statement.  Eg.
#   
#   pinf 'Converting image'
#       convert image.png image.jpg
#   fnip
#
# The closing statement (here fnip) is the reverse of the opening statement and exits with the exit code
# of the last command.  If the last command failed, it shows the exit code in the spinner before it is stopped.
# The closing statement also takes a format and arguments, which are displayed in the spinner.
#
log() {
    local exitcode=$? result=0 level=${level:-inf} supported=0 end=$'\n' type=msg conMsg= logMsg= format= colorFormat= date= info= arg= args=() colorArgs=() ruler=

    # Handle options.
    local OPTIND=1
    while getopts :tpuPrR:d:nx arg; do
        case $arg in
            p)
                end='.. '
                type=startProgress ;;
            u)
                end='.. '
                type=updateProgress ;;
            P)
                type=stopProgress ;;
            r)
                ruler='____' ;;
            R)
                ruler=$OPTARG ;;
            d)
                end=$OPTARG ;;
            n)
                end= ;;
            x)
                result=$exitcode ;;
        esac
    done
    shift "$((OPTIND-1))"
    format=$1 args=( "${@:2}" )
    (( ! ${#args[@]} )) && [[ $format ]] && { args=("$format") format=%s; local bold=; }
    date=${_logDate+$(date +"${_logDate:-%H:%M}")}

    # Level-specific settings.
    local logLevelColor
    case $level in
        TRC)    (( supported = _logVerbosity >= 4 ))
                logLevelColor=$_logTrcColor ;;
        DBG)    (( supported = _logVerbosity >= 3 ))
                logLevelColor=$_logDbgColor ;;
        INF)    (( supported = _logVerbosity >= 2 ))
                logLevelColor=$_logInfColor ;;
        WRN)    (( supported = _logVerbosity >= 1 ))
                logLevelColor=$_logWrnColor ;;
        ERR)    (( supported = _logVerbosity >= 0 ))
                logLevelColor=$_logErrColor ;;
        FTL)    (( supported = 1 ))
                logLevelColor=$_logFtlColor ;;
        *)
                log FTL 'Log level %s does not exist' "$level"
                exit 1 ;;
    esac
    (( ! supported )) && return "$result"
    local logColor=${_logColor:+$logLevelColor}

    # Generate the log message.
    case $type in
        msg|startProgress)
            printf -v logMsg "${date:+%s }${_logLevel:+%-3s }$format$end" ${date:+"$date"} ${_logLevel:+"$level"} "${args[@]}"
            if (( _logColor )); then
                colorFormat=$(sed ${reset:+-e "s/$(requote "$reset")/$reset$_logAttributes$logColor/g"} -e "s/%[^a-z]*[a-z]/$reset$_logAttributes$bold$logColor&$reset$_logAttributes$logColor/g" <<< "$format")
                colorArgs=("${args[@]//$reset/$reset$_logAttributes$bold$logColor}")
                printf -v conMsg "$reset$_logAttributes${date:+%s }${_logLevel:+$logColor$bold%-3s$reset $_logAttributes}$logColor$colorFormat$reset$_logAttributes$black\$$reset$end$save" ${date:+"$date"} ${_logLevel:+"$level"} "${colorArgs[@]}"
            else
                conMsg=$logMsg
            fi
        ;;

        updateProgress)
            printf -v logMsg printf " [$format]" "${args[@]}"
            if (( _logColor )); then
                colorFormat=$(sed ${reset:+-e "s/$(requote "$reset")/$reset$_logAttributes$logColor/g"} -e "s/%[^a-z]*[a-z]/$reset$_logAttributes$bold$logColor&$reset$_logAttributes$logColor/g" <<< "$format")
                colorArgs=("${args[@]//$reset/$reset$_logAttributes$bold$logColor}")
                printf -v conMsg "$load$eel$blue$bold[$reset$_logAttributes$logColor$colorFormat$reset$_logAttributes$blue$bold]$reset$end" "${colorArgs[@]}"
            else
                conMsg=$logMsg
            fi
        ;;

        stopProgress)
            kill -0 "$_logSpinner" 2>/dev/null || return

            case $exitcode in
                0)  printf -v logMsg "done${format:+ ($format)}.\n" "${args[@]}"
                    if (( _logColor )); then
                        colorFormat=$(sed ${reset:+-e "s/$(requote "$reset")/$reset$logColor/g"} -e "s/%[^a-z]*[a-z]/$reset$bold$logColor&$reset$logColor/g" <<< "$format")
                        colorArgs=("${args[@]//$reset/$reset$bold$logColor}")
                        printf -v conMsg "$load$eel$green${bold}done${colorFormat:+ ($reset$logColor$colorFormat$reset$green$bold)}$reset.\n" "${colorArgs[@]}"
                    else
                        conMsg=$logMsg
                    fi
                ;;

                *)  info=${format:+$(printf ": $format" "${args[@]}")}
                    printf -v logMsg "error(%d%s).\n" "$exitcode" "$info"
                    if (( _logColor )); then
                        printf -v conMsg "${eel}${red}error${reset}(${bold}${red}%d${reset}%s).\n" "$exitcode" "$info"
                    else
                        conMsg=$logMsg
                    fi
                ;;
            esac
        ;;
    esac

    # Create the log file.
    if [[ $_logFile && ! -e $_logFile ]]; then
        [[ $_logFile = */* ]] || _logFile=./$_logFile
        mkdir -p "${_logFile%/*}" && touch "$_logFile"
    fi

    # Stop the spinner.
    if [[ $type = stopProgress && $_logSpinner ]]; then
        {
            kill "$_logSpinner" ||:
            wait "$_logSpinner" ||:
            unset _logSpinner
        } 2>/dev/null
    fi

    # Output the ruler.
    if [[ $ruler ]]; then
        printf >&2 '%s\n' "$(hr "$ruler")"
        [[ -w $_logFile ]] \
            && printf >> "$_logFile" '%s' "$ruler"
    fi

    # Output the log message.
    printf >&2 '%s' "$conMsg"
    [[ -w $_logFile ]] \
        && printf >> "$_logFile" '%s' "$logMsg"

    # Start the spinner.
    if [[ $type = startProgress && ! $_logSpinner && $TERM != dumb ]]; then
        {
            set +m
            trap 'printf >&2 %s "$show"' EXIT
            printf >&2 %s "$hide"
            while printf >&2 "$eel$blue$bold[$reset%s$reset$blue$bold]$reset\b\b\b" "${spinner[s++ % ${#spinner[@]}]}" && sleep .1
            do :; done
        } & _logSpinner=$!
        addtrap EXIT 'level=%q _logSpinner=%q golp' "$level" "$_logSpinner"
    fi

    return $result
}
trc() { level=TRC log "$@"; }
dbg() { level=DBG log "$@"; }
inf() { level=INF log "$@"; }
wrn() { level=WRN log "$@"; }
err() { level=ERR log "$@"; }
ftl() { level=FTL log "$@"; }
plog() { log -p "$@"; }
ulog() { log -u "$@"; }
golp() { log -P "$@"; }
ptrc() { level=TRC plog "$@"; }
pdbg() { level=DBG plog "$@"; }
pinf() { level=INF plog "$@"; }
pwrn() { level=WRN plog "$@"; }
perr() { level=ERR plog "$@"; }
pftl() { level=FTL plog "$@"; }
utrc() { level=TRC ulog "$@"; }
udbg() { level=DBG ulog "$@"; }
uinf() { level=INF ulog "$@"; }
uwrn() { level=WRN ulog "$@"; }
uerr() { level=ERR ulog "$@"; }
uftl() { level=FTL ulog "$@"; }
gtrc() { level=trc golp "$@"; }
gbdp() { level=DBG golp "$@"; }
fnip() { level=INF golp "$@"; }
nrwp() { level=WRN golp "$@"; }
rrep() { level=ERR golp "$@"; }
ltfp() { level=FTL golp "$@"; }
_logColor=${_logColor:-$([[ -t 2 ]] && echo 1)} _logVerbosity=2
_logTrcColor=$grey _logDbgColor=$blue _logInfColor=$white _logWrnColor=$yellow _logErrColor=$red _logFtlColor=$bold$red
#_logDate=%H:%M # Set this to enable date output in log messages.
#_logLevel=1 # Set this to enable level output in log messages.
# _______________________________________________________________________



#  ______________________________________________________________________
# |__ Ask _______________________________________________________________|
#
#       ask [-c optionchars|-d default] [-s|-S maskchar] format [arguments...]
#
# Ask a question and read the user's reply to it.  Then output the result on stdout.
#
# When in normal mode, a single line is read.  If the line is empty and
# -d was specified, the default argument is output instead of an empty line.
# The exit code is always 0.
#
# When in option mode (-c), the user is shown the option characters with
# which he can reply and a single character is read.
# If the reply is empty (user hits enter) and any of the optionchars are
# upper-case, the upper-case option (= the default option) character will
# be output instead of an empty line.
# If the reply character is not amoungst the provided options the default
# option is again output instead if present.  If no default was given, an
# exit code of 2 is returned.
# You may mark an optionchar as 'valid' by appending a '!' to it.  As a
# result, an exit code of 0 will only be returned if this valid option
# is replied.  If not, an exit code of 1 will be returned.
#
ask() {
   
    # Initialize the vars.
    local opt arg
    local option=
    local options=
    local default=
    local silent=
    local valid=
    local muteChar=
    local format=

    # Parse the options.
    local OPTIND=1
    while getopts :sS:c:d: opt; do
        case $opt in
            s)  silent=1 ;;
            S)  silent=1 muteChar=$OPTARG ;;
            c)  while read -n1 arg; do
                    case $arg in
                        [[:upper:]]) default=$arg ;;
                        !) valid=${options: -1}; continue ;;
                    esac

                    options+=$arg
                done <<< "$OPTARG" ;;
            d)  default=$OPTARG option=$default ;;
        esac
    done

    # Trim off the options.
    shift $((OPTIND-1))

    # Figure out what FD to use for our messages.
    [[ -t 1 ]] && local fd=1 || local fd=2

    # Ask the question.
    format=$1; shift
    level=${level:-WRN} log -n "$format${option:+ [%s]}${options:+ [%s]}" "$@" ${option:+"$option"} ${options:+"$options"}

    # Read the reply.
    exec 8<&0; [[ -t 8 ]] || exec 8</dev/tty
    if [[ $muteChar ]]; then
        local reply
        while read -u8 -s -n1 && [[ $REPLY ]]; do
            reply+=$REPLY
            printf '%s' "$muteChar"                                 >&$fd
        done
        REPLY=$reply
        [[ $options && $REPLY ]] || (( silent )) && printf '\n'     >&$fd
    else
        read -u8 -e ${options:+-n1} ${silent:+-s}
    fi

    # Evaluate the reply.
    while true; do
        if [[ $REPLY && ( ! $options || $options = *$REPLY* ) ]]; then
            if [[ $valid ]]
            then [[ $REPLY = $valid ]]
            else printf "%s" "$REPLY"
            fi

            return
        fi

        [[ -z $default || $REPLY = $default ]] \
            && return 2
        
        REPLY=$default
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Trim ______________________________________________________________|
#
#       trim lines ...
#
# Trim the whitespace off of the beginning and end of the given lines.
# Each argument is considdered one line; is treated and printed out.
#
# When no arguments are given, lines will be read from standard input.
#
trim() {
    { (( $# )) && printf '%s\n' "$@" || cat; } | \
        sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Reverse ___________________________________________________________|
#
#       reverse [-0|-d delimitor] [elements ...] [<<< elements]
#
# Reverse the order of the given elements.
# Elements are read from command arguments or standard input if no element
# arguments are given.
# They are reversed and output on standard output.
#
# If the -0 option is given, input and output are delimited by NUL bytes.
# If the -d option is given, input and output are delimited by the
# character argument.
# Otherwise, they are delimited by newlines.
#
reverse() {
   
    # Initialize the vars.
    local elements=() delimitor=$'\n' i

    # Parse the options.
    local OPTIND=1
    while getopts :0d: opt; do
        case $opt in
            0) delimitor=$'\0' ;;
            d) delimitor=$OPTARG ;;
        esac
    done
    shift "$((OPTIND-1))"

    # Get the elements.
    if (( $# )); then
        elements=( "$@" )
    else
        while IFS= read -r -d "$delimitor"; do
            elements+=("$REPLY")
        done
    fi

    # Iterate in reverse order.
    for (( i=${#elements[@]} - 1; i >=0; --i )); do
        printf "%s${delimitor:-'\0'}" "${elements[i]}"
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Order _____________________________________________________________|
#
#       order [-0|-d char] [-[fF] isDesired] [-[cC] comparator|-n|-R|-t] [-r] [-T number] [-a array|elements ...] [<<< elements]
#
# Orders the elements in ascending order.
# Elements are read from command arguments or standard input if no element
# arguments are given.
# The result is output on standard output.
#
# By default, the elements will be ordered using lexicographic comparison.
# If the -n option is given, the elements will be ordered numerically.
# If the -R option is given, the elements will be ordered randomly.
# If the -t option is given, the elements are ordered by file mtime.
# If the -f option is given, the command name following it will be used
# as a filter.
# If the -c option is given, the command name following it will be used
# as a comparator.
# If the -C option is given, the bash code following it will be used
# as a comparator.
# If the -r option is given, the ordering will be reversed.
# If the -T option is given, only the first number results are returned.
# If the -a option is given, the elements in array are ordered instead and
# array is mutated to contain the result.
# If number is 0, all results are returned.
#
# isDesired is a command name which will get one parameter.  The parameter
# is an element which will only be included if the command exits successfully.
# comparator is a command name which will be executed for each element
# comparison and will be passed two element arguments.  The command should
# succeed if the first argument is less than the second argument for the
# purpose of this sort.
#
# If the -0 option is given, input and output are delimited by NUL bytes.
# If the -d option is given, input and output are delimited by the
# character argument.
# Otherwise, they are delimited by newlines.
#
# The ordering is implemented by an insertion sort algorithm.
#
order() {
   
    # Initialize the vars.
    local _delimitor=$'\n' _i _j _isDesired=true _comparator=string_ascends _comparator_ascends=1 _top=0 _arrayName= _array=

    # Parse the options.
    local OPTIND=1
    while getopts :0nrRd:f:F:c:C:tT:a: opt; do
        case $opt in
            0) _delimitor=$'\0' ;;
            d) _delimitor=$OPTARG ;;
            n) _comparator=number_ascends ;;
            R) _comparator=random_ascends ;;
            t) _comparator=mtime_ascends ;;
            f) _isDesired=$OPTARG ;;
            F) _isDesired=bash_desired _bash_desired_code=$OPTARG ;;
            c) _comparator=$OPTARG ;;
            C) _comparator=bash_ascends _bash_ascends_code=$OPTARG ;;
            r) _comparator_ascends=0 ;;
            T) _top=$OPTARG ;;
            a) _arrayName=$OPTARG _array=$_arrayName[@] ;;
        esac
    done
    shift "$((OPTIND-1))"

    # Get the elements.
    local _elements=() _element
    if [[ $_arrayName ]]; then
        for _element in "${!_array}"; do
            "$_isDesired" "$_element" && _elements+=("$_element")
        done
    elif (( $# )); then
        for _element; do
            "$_isDesired" "$_element" && _elements+=("$_element")
        done
    else
        while IFS= read -r -d "$_delimitor" _element; do
            "$_isDesired" "$_element" && _elements+=("$_element")
        done
    fi

    # Iterate in reverse order.
    for (( _i = 1; _i < ${#_elements[@]}; ++_i )); do
        for (( _j = _i; _j > 0; --_j )); do
            _element=${_elements[_j]}
            if ( (( _comparator_ascends )) && "$_comparator" "$_element" "${_elements[_j-1]}" ) ||
               ( (( ! _comparator_ascends )) && ! "$_comparator" "$_element" "${_elements[_j-1]}" ); then
                _elements[_j]=${_elements[_j-1]}
                _elements[_j-1]=$_element
            fi
        done
    done

    (( _top )) || _top=${#_elements[@]}
    if [[ $_array ]]; then
        declare -ga "$_array=($(printf '%q ' "${_elements[@]:0:_top}"))"
    else
        printf "%s${_delimitor:-\0}" "${_elements[@]:0:_top}"
    fi
} # _____________________________________________________________________
string_ascends()    { [[ $1 < $2 ]]; }
number_ascends()    { (( $1 < $2 )); }
random_ascends()    { (( RANDOM % 2 )); }
mtime_ascends()     { [[ $1 -ot $2 ]]; }
exists_desired()    { [[ -e $1 ]]; }
line_desired()      { [[ $1 ]]; }
code_desired()      { line_desired "$1" && ! comment_desired "$1"; }
comment_desired()   { line_desired "$1" && [[ $1 = @(#|//|/\*)* ]]; }
bash_desired()      { bash -c "$_bash_desired_code" -- "$@"; }
bash_ascends()      { bash -c "$_bash_ascends_code" -- "$@"; }


#  ______________________________________________________________________
# |__ AddTrap _____________________________________________________________|
#
#       addtrap signal command-format [args...]
#
# Add a command to the current commands executed when a signal is received by the bash process.
#
# The command-format is a printf-style format for the command to execute.  The optional
# args are interpolated into the command-format by bash's built-in printf.
#
addtrap() {
    local signal=$1 cmd=$2; shift 2
    printf -v cmd "$cmd" "$@"

    read _ _ oldtrap <<< "$(trap -p "$signal")"
    eval "declare oldtrap=${oldtrap% *}"
    trap "$oldtrap${oldtrap:+; }$cmd" "$signal"
}


#  ______________________________________________________________________
# |__ Mutex _____________________________________________________________|
#
#       mutex file
#
# Open a mutual exclusion lock on the file, unless another process already owns one.
#
# If the file is already locked by another process, the operation fails.
# This function defines a lock on a file as having a file descriptor open to the file.
# This function uses FD 9 to open a lock on the file.  To release the lock, close FD 9:
# exec 9>&-
#
mutex() {
    local lockfile=${1:-${BASH_SOURCE[-1]}} pid pids
    [[ -e $lockfile ]] || err "No such file: $lockfile" || return

    exec 9>> "$lockfile" && [[ $({ fuser -f "$lockfile"; } 2>&- 9>&-) == $$ ]]
}


#  ______________________________________________________________________
# |__ PushJob ___________________________________________________________|
#
#       pushjob [poolsize] command
#
# Start an asynchronous command within a pool, waiting for space in the pool if it is full.
#
# The pool is pruned automatically as running jobs complete.  This function
# allows you to easily run asynchronous commands within a pool of N,
# automatically starting the next command as soon as there's space.
#
pushjob() {
    local size=$1; shift 1

    # Wait for space in the pool.
    until (( ${#jobpool[@]} < size )); do
        sleep 1 & pushjobsleep=$!
        wait "$pushjobsleep"
    done 2>/dev/null

    # Register prunejobs and start the pushed job.
    trap _prunejobs SIGCHLD
    set -m
    "$@" & jobpool[$!]=
}
_prunejobs() {
    # Prune all pool jobs that are no longer running.
    for pid in "${!jobpool[@]}"; do
        kill -0 "$pid" 2>/dev/null || unset "jobpool[$pid]"
    done

    # Unregister SIGCHLD if our pool is empty.
    (( ${#jobpool[@]} )) || trap - SIGCHLD

    # Wake up pushjob.
    kill "$pushjobsleep" 2>/dev/null
}




#  ______________________________________________________________________
# |__ FSleep _____________________________________________________________|
#
#       fsleep time
#
# Wait for the given (fractional) amount of seconds.
#
# This implementation solves the problem portably, assuming that either
# bash 4.x or a fractional sleep(1) is available.
#
fsleep() {

    local fifo=${TMPDIR:-/tmp}/.fsleep.$$
    trap 'rm -f "$fifo"' RETURN
    mkfifo "$fifo" && { read -t "$1" <> "$fifo" 2>/dev/null || sleep "$1"; }
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ options ___________________________________________________________|
#
#       options [option description]...
#
# Specify and handle options in arguments.
#
# The 'setopt' function will be called for each option expected option
# passed to the script, with $1 set to the option character and $2
# its description.  Check OPTARG if the option takes an argument.
# 'setopt' will be called with '?' if an invalid option is passed.
#
# Unless specified, the -h option will show a usage description,
# explaining the options.
#
# Proposed usage:
# setopt() {
#     case "$1" in
#         a) echo "got option a" ;;
#         b) echo "got option b with argument $OPTARG" ;;
#     esac
# }
# options \
#     a   'option a' \
#     b:  'option b with argument'
#
options() {

    # Parse the expected options and their description.
    declare -A options=()
    while (( $# )); do
        local optchar=$1 optdesc=$2
        shift 2 || ftl 'Missing arguments, expected option (%s), description (%s).' "$optchar" "$optdesc" || exit
        options[$optchar]=$optdesc
    done

    # Find the script's options.
    local argc=${BASH_ARGC[@]: -1} argv=("${BASH_ARGV[@]: -argc}") arg
    local optstring=$(printf %s "${!options[@]}")h
    set -- # Sigh.  BASH_ARGV is all backwards.
    for arg in "${argv[@]}"; do
        set -- "$arg" "$@"
    done

    # Handle the script's options.
    while getopts "$optstring" arg; do
        if [[ $arg = h && ! ${options[h]} ]]; then
            # Show usage message.
            [[ -t 1 ]]; local fd=$(( $? + 1 )) optarg

            # Print out the app usage.
            printf "  Usage: $reset$bold%s$reset" "${BASH_SOURCE[1]##*/}"   >&$fd
            for optchar in "${!options[@]}"; do
                [[ $optchar = *: ]] && optarg=" arg" || optarg=
                printf " [$bold$green-%s$reset%s]" "${optchar%:}" "$optarg" >&$fd
            done
            printf "\n\n"                                                   >&$fd

            # Print out the option descriptions.
            for optchar in "${!options[@]}"; do
                local optdesc=${options[$optchar]}
                [[ $optchar = *: ]] && optarg=" arg" || optarg=
                printf "    $bold$green-%s$reset%s\t" "${optchar%:}" "$optarg"
                fmt -w "$COLUMNS" <<< "${optdesc//+( )/ }" | sed $'1!s/^/ \t/'
                printf "\n"
            done | column -t -s $'\t'                                       >&$fd
        else
            optchar=$arg; [[ ! ${options[$arg]} && ${options[$arg:]} ]] && optchar=$arg:
            setopt "$arg" "${options[$arg]}"
        fi
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ ShowHelp __________________________________________________________|
#
#       showHelp name description author [option description]...
#
# Generate a prettily formatted usage description of the application.
#
#   name        Provide the name of the application.
#
#   description Provide a detailed description of the application's
#               purpose and usage.
#
#   option      An option the application can take as argument.
#
#   description A description of the effect of the preceding option.
#
showHelp() {

    # Parse the options.
    local appName=$1; shift
    local appDesc=${1//+([[:space:]])/ }; shift
    local appAuthor=$1; shift
    local cols=$(tput cols)
    (( cols = ${cols:-80} - 10 ))

    # Figure out what FD to use for our messages.
    [[ -t 1 ]]; local fd=$(( $? + 1 ))

    # Print out the help header.
    printf "$reset$bold\n"                                          >&$fd
    printf "\t\t%s\n" "$appName"                                    >&$fd
    printf "$reset\n"                                               >&$fd
    printf "%s\n" "$appDesc" | fmt -w "$cols" | sed $'s/^/\t/'      >&$fd
    printf "\t   $reset$bold~ $reset$bold%s\n" "$appAuthor"         >&$fd
    printf "$reset\n"                                               >&$fd

    # Print out the application options and columnize them.
    while (( $# )); do
        local optName=$1; shift
        local optDesc=$1; shift
        printf "    %s\t" "$optName"
        printf "%s\n" "${optDesc//+( )/ }" | fmt -w "$cols" | sed $'1!s/^/ \t/'
        printf "\n"
    done | column -t -s $'\t' \
         | sed "s/^\(    [^ ]*\)/$bold$green\1$reset/"              >&$fd
    printf "\n"                                                     >&$fd
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Quote _____________________________________________________________|
#
#       shquote [-e] [argument...]
#
# Shell-quote the arguments to make them safe for injection into bash code.
# 
# The result is bash code that represents a series of words, where each
# word is a literal string argument.  By default, quoting happens using
# single-quotes.
#
#   -e      Use backslashes rather than single quotes.
#   -d      Use double-quotes rather than single quotes (does NOT disable expansions!).
#   -a      Normally, shquote doesn't quote arguments that don't need it.  This forces all arguments to be quoted.
#
shquote() {

    # Initialize the defaults.
    local OPTIND=1 arg escape=0 sq="'\\''" dq='\"' quotedArgs=() type=single always=0

    # Parse the options.
    while getopts :eda arg; do
        case $arg in
            e) type=escape  ;;
            d) type=double  ;;
            a) always=1     ;;
        esac
    done
    shift "$((OPTIND-1))"

    # Print out each argument, quoting it properly.
    for arg; do
        (( ! always )) && [[ $arg = "$(printf %q "$arg")" ]] && quotedArgs+=("$arg") && continue

        case "$type" in
            escape)
                quotedArgs+=("$(printf "%q"     "$arg")") ;;
            single)
                arg=${arg//"'"/$sq}
                quotedArgs+=("$(printf "'%s'"   "$arg")") ;;
            double)
                arg=${arg//'"'/$dq}
                quotedArgs+=("$(printf '"%s"'   "$arg")") ;;
        esac
    done

    printf '%s\n' "$(IFS=' '; echo "${quotedArgs[*]}")"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ ReQuote __________________________________________________________|
#
#       requote [string]
#
# Escape the argument string to make it safe for injection into a regex.
#
# The result is a regular expression that matches the literal argument
# string.
#
requote() {

    # Initialize the defaults.
    local char

    printf '%s' "$1" | while IFS= read -r -d '' -n1 char; do
        printf '[%s]' "$char"
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Shorten ___________________________________________________________|
#
#       shorten [-p pwd] path [suffix]...
#
# Shorten an absolute path for pretty printing.
# Paths are shortened by replacing the homedir by ~, making it relative and
# cutting off given suffixes from the end.
#
#   -p      Use the given pathname as the base for relative filenames instead of PWD.
#   path    The path string to shorten.
#   suffix  Suffix strings that must be cut off from the end.
#           Only the first suffix string matched will be cut off.
#
shorten() {

    # Parse the options.
    local suffix path pwd=$PWD
    [[ $1 = -p ]] && { pwd=$2; shift 2; }
    path=$1; shift

    # Make path absolute.
    [[ $path = /* ]] || path=$PWD/$path

    # If the path denotes something that exists; it's easy.
    if [[ -d $path ]]
    then path=$(cd "$path"; printf "%s" "$PWD")
    elif [[ -d ${path%/*} ]]
    then path=$(cd "${path%/*}"; printf "%s" "$PWD/${path##*/}")

    # If not, we'll try readlink -m.
    elif readlink -m / >/dev/null 2>&1; then
        path=$(readlink -m "$path")

    # If we don't have that - unleash the sed(1) madness.
    else
        local oldpath=/
        while [[ $oldpath != $path ]]; do
            oldpath=$path
            path=$(sed -e 's,///*,/,g' -e 's,\(^\|/\)\./,\1,g' -e 's,\(^\|/\)[^/]*/\.\.\($\|/\),\1,g' <<< "$path")
        done
    fi

    # Replace special paths.
    path=${path/#$HOME/'~'}
    path=${path#$pwd/}

    # Cut off suffix.
    for suffix; do
        [[ $path = *$suffix ]] && {
            path=${path%$suffix}
            break
        }
    done

    printf "%s" "$path"
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ CdSource ________________________________________________________________|
#
#       cdsource [file]
#
# Change the current directory into the directory where the file is located, resolving symlinks.
#
cdsource() {
    local source=${1:-${BASH_SOURCE[1]}}

    while [[ $source ]]; do
        [[ $source = */* ]] && cd "${source%/*}"

        if [[ -L ${source##*/} ]]; then
            source=$(readlink "${source##*/}")
        else
            source=
        fi
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ Up ________________________________________________________________|
#
#       up .../path|num
#
# Walk the current working directory up towards root num times or until path is found.
#
# Returns 0 if the destination was reached or 1 if we hit root.
#
# Prints PWD on stdout on success.
#
up() {
    local up=0
    until [[ $PWD = / ]]; do
        cd ../

        if [[ $1 = .../* ]]; then
            [[ -e ${1#.../} ]] && pwd && return
        elif (( ++up == $1 )); then
            pwd && return
        fi
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ BuildArray ________________________________________________________|
#
#       buildarray name terms... -- elements...
#
# Create an array by adding all the terms to it for each element, replacing {} terms by the element.
#
#   name        The name of the array to put the result into.
#   terms       The values to add to the array for each of the elements.  A {} term is replaced by the current element.
#   elements    The elements to iterate the terms for.
#
buildarray() {
    local target=$1 term terms=() element value
    shift

    while [[ $1 != -- ]]; do
        terms+=("$1")
        shift
    done
    shift

    for element; do
        for term in "${terms[@]}"; do
            [[ $term = {} ]] && value="$element" || value="$term"
            declare -ag "$target+=($(printf '%q' "$value"))"
        done
    done
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ InArray ___________________________________________________________|
#
#       inArray element array
#
# Checks whether a certain element is in the given array.
#
#   element The element to search the array for.
#   array   This is a list of elements to search through.
#
inArray() {

    # Parse the options.
    local element
    local search=$1; shift

    # Perform the search.
    for element
    do [[ $element = $search ]] && return 0; done
    return 1
} # _____________________________________________________________________



#  ______________________________________________________________________
# |__ HideDebug _________________________________________________________|
#
#       hideDebug [on|off]
#
# Toggle Bash's debugging mode off temporarily.
# To hide Bash's debugging output for a function, you should have
#     hideDebug on
# as its first line, and
#     hideDebug off
# as its last.
#
hideDebug() {

    if [[ $1 = on ]]; then
        : -- HIDING DEBUG OUTPUT ..
        [[ $- != *x* ]]; bashlib_debugWasOn=$?
        set +x
    elif [[ $1 = off ]]; then
        : -- SHOWING DEBUG OUTPUT ..
        (( bashlib_debugWasOn )) && \
        set -x
    fi
}



#  ______________________________________________________________________
# |__ anfunc ____________________________________________________________|
#
#       anfunc [on|off]
#
# Turn on or off support for annonymous functions.
#
# WARNING: This is a hack.  It turns on extdebug and causes any argument
# that matches (){code} to be replaced by a function name that if invoked
# runs code.
#
# eg.
#   confirm '(){ rm "$1" }' *.txt
#   # In this example, confirm() could be a function that asks confirmation
#   # for each argument past the first and runs the anfunc in the first
#   # argument on each confirmed argument.
#
# Don't use this.  It is an academic experiment and has bugs.
#
# Bugs:
#   - commands lose their exit code.
#       To inhibit the real command from running, we use extdebug and
#       a DEBUG trap that returns non-0.  As a result, the actual return
#       code is lost.
# 
anfunc() {
    case "$1" in
        on)
            shopt -s extdebug
            trap _anfunc_trap DEBUG
        ;;
        off)
            trap - DEBUG
            shopt -u extdebug
        ;;
    esac
}
_anfunc_trap() {
    local f w

    # Perform the command parsing and handling up to its word splitting.
    # This includes command substitution, quote handling, pathname expansion, etc.
    declare -a words="($BASH_COMMAND)"

    # Iterate the words to run in the final stage, and handle anfunc matches.
    for ((w=0; w<${#words[@]}; ++w)); do
        [[ ${words[w]} = '(){'*'}' ]] &&
            # Declare a new function for this anfunc.
            eval "_f$((++f))${words[w]}" &&
                # Replace the word by the new function's name.
                words[w]="_f$f"
    done
    
    # Run the command.
    eval "$(printf '%q ' "${words[@]}")"
    
    # Clean up the anfuncs.
    for ((; f>0; --f)); do
        unset -f "_f$f"
    done
    
    # Inhibit the real command's execution.
    return 1
}



#  ______________________________________________________________________
# |__ StackTrace ________________________________________________________|
#
#       stackTrace
#
# Output the current script's function execution stack.
#
stackTrace() {
    # Some general debug information.
    wrn "    [PID       : %15s]    [PPID       : %8s]    [Main PID   : %8s]" "$BASHPID" "$PPID" "$$"
    wrn "    [Level     : %15s]    [Subshells  : %8s]    [Runtime    : %7ss]" "$SHLVL" "$BASH_SUBSHELL" "$SECONDS"
    wrn "    [Locale    : %15s]    [IFS        : %8s]" "${LC_ALL:-${LC_COLLATE:-${LANG:-C}}}" "$(printf %q "$IFS")"
    wrn "    Dir Stack  : %s" "${DIRSTACK[*]}"
    wrn "    Shell      : %s v%s" "$BASH" "$BASH_VERSION"
    wrn "    Shell Opts : %s" "${SHELLOPTS//:/, }"
    wrn "    Bash Opts  : %s" "${BASHOPTS//:/, }"
    wrn "    Functions  :"


    # Search through the map.
    local arg=0 
    for stack in "${!FUNCNAME[@]}"; do
        (( stack+1 >= ${#BASH_SOURCE[@]} )) && break

        func=${FUNCNAME[stack]}
        line=${BASH_LINENO[stack]}
        file=${BASH_SOURCE[stack+1]}
        args=()
        for (( arg=0, s=0; s <= stack; ++s )); do
            for (( sarg=0; sarg < ${BASH_ARGC[s]:-0}; ++sarg, ++arg )); do
                (( s == stack )) && args[${BASH_ARGC[s]} - sarg]=${BASH_ARGV[arg]}
            done
        done
        wrn '%40s:%-3d | %s %s' "$file" "$line" "$func" "$(printf '%s ' "$(shquote "${args[@]}")")"
    done

} # _____________________________________________________________________





#  ______________________________________________________________________ 
# |                                                                      |
# |                                                  .:  ENTRY POINT  :. |
# |______________________________________________________________________|

# Make sure this file is sourced and not executed.
( return 2>/dev/null ) || {
    help=$(sed -n '1,/_tocHash=/{ /^#/p; }' "$BASH_SOURCE")
    if [[ $1 ]]; then
        while [[ $1 ]]; do
            awk "p && !/^# *[^ ]/ {exit}
                 p || /^#     $1/ {print; p=1}" <<< "$help"
            shift
        done
    else
        echo "$help"
        echo
        echo "To use bashlib, copy it into your PATH and put ''source bashlib'' at the top of your script."
    fi
}

:
:                                                   .:  END SOURCING  :.
:  ______________________________________________________________________ 
:
0707010000005C000081ED00000000000000000000000163C2D555000003B5000000000000000000000000000000000000003D00000000spectre-cli-3.0+git20230114.26c01a5/scripts/categorize-words#!/usr/bin/env bash

categories=( adj adv noun verb )
declare -A categoriesByWord=()

echo "Parsing category lists.."
for (( c = 0; c < ${#categories[@]}; ++c )); do
    printf '\rCategory: %s..   ' "${categories[c]}"

    while read -r word _; do
        categoriesByWord["$word"]+="$c "
    done < ~/.dictionary.d/"index.${categories[c]}"
done
echo

echo "Processing words list.."
{
    fdByCategory=()
    for (( c = 0; c < ${#categories[@]}; ++c )); do
        exec {fdByCategory[c]}>"words.txt.${categories[c]}"
    done

    w=0
    while IFS= read -r word _; do
        let ++w

        if (( ${#word} < 3 )) || [[ $word != *[aeiou]* ]]; then
            continue
        fi

        wordCategories=${categoriesByWord["$word"]}

        for c in $wordCategories; do
            printf '%d %s\n' "$w" "$word" >&"${fdByCategory[c]}"
        done
    done < words.txt

    for fd in "${fdByCategory[@]}"; do
        exec {fd}>&-
    done
}
echo
0707010000005D000081ED00000000000000000000000163C2D555000004FF000000000000000000000000000000000000003100000000spectre-cli-3.0+git20230114.26c01a5/scripts/mkpw#!/usr/bin/env bash
source bashlib
cdsource

getword() {
    local cat=$1 pop_limit=$2 words=()

    while read pop word; do
        (( pop_limit && pop > pop_limit )) && break
        words+=( "$word" )
    done < "words.txt.$cat"

    (( ${#words[@]} )) || { echo '1 -'; return 1; }
    echo "${#words[@]} ${words[RANDOM % ${#words[@]}]}"
}

declare -A categoryByCharacter=(
    ['a']=adj
    ['r']=adv
    ['n']=noun
    ['v']=verb
)
templates=(
    ran # ~32.0 bit
)
permutations=1

while getopts :t:p: arg; do
    case $arg in
        # The sentence template to use.
        t) template=$OPTARG ;;
        # Use only the top N most popular words.
        p) pop_limit=$OPTARG ;;
    esac
done
template=${template:-${templates[$((RANDOM % ${#templates[@]}))]}}


while true; do (
    printf 'sentence: '
    while read -n1 character && [[ $character ]]; do
        category=${categoryByCharacter["$character"]}
        read p word < <(getword "$category" "$pop_limit")
        (( permutations *= p ))
        printf '%s ' "$word"
    done <<< "$template"
    echo
    printf 'permutations: %s, entropy: ~%.1f bit\n' "$permutations" "$(bc -l <<< "l($permutations) / l(2)")"

    read -s -n 1 -p 'Generate another? [Yn]' && echo || break
    [[ $REPLY = n ]] && break
); done

0707010000005E000081ED00000000000000000000000163C2D555000015D0000000000000000000000000000000000000003800000000spectre-cli-3.0+git20230114.26c01a5/scripts/timetocrack#!/usr/bin/env bash
source bashlib
calc() { python -c "import math; print $1"; }

inf 'Calculate the maximum amount of time required to brute-force search for a password.'

## CLASSES
V="AEIOU"
C="BCDFGHJKLMNPQRSTVWXYZ"
v="aeiou"
c="bcdfghjklmnpqrstvwxyz"
A="$V$C"
a="$V$v$C$c"
n="0123456789"
o="&@%?,=[]_:-+*\$#!'^~;()/."
x="$a$n!@#\$%^&*()"
w="@words.txt"

## METRICS
# Last update: 2016-09
# GTX Titan X can generate about 402.7M HMAC-SHA-256 hashes per second (5301.7M SHA1).  (ref. https://hashcat.net/forum/thread-4314.html)
# GTX Titan X can be bought for about 950$ used. (ref. amazon.com)
#hardwareName='GTX Titan X (SHA1)'                  hardwareSpeed='5302M'
#hardwareName='GTX Titan X (SHA1 @ 5k$)'            hardwareSpeed='5302M * 5k / 950'
#hardwareName='GTX Titan X (SHA1 @ 20k$)'           hardwareSpeed='5302M * 20k / 950'
#hardwareName='GTX Titan X (SHA1 @ 20M$)'           hardwareSpeed='5302M * 20M / 950'
#hardwareName='GTX Titan X (SHA1 @ 5B$)'            hardwareSpeed='5302M * 5B / 950'
 hardwareName='GTX Titan X (HMAC-SHA-256 @ 950$)'   hardwareSpeed='403M'
#hardwareName='GTX Titan X (HMAC-SHA-256 @ 5k$)'    hardwareSpeed='403M * 5k / 950'
#hardwareName='GTX Titan X (HMAC-SHA-256 @ 20k$)'   hardwareSpeed='403M * 20k / 950'
#hardwareName='GTX Titan X (HMAC-SHA-256 @ 20M$)'   hardwareSpeed='403M * 20M / 950'
#hardwareName='GTX Titan X (HMAC-SHA-256 @ 5B$)'    hardwareSpeed='403M * 5B / 950'

# ASICs
 hardwareName='AntMiner L3+ (scrypt)'               hardwareSpeed='1M'
#hardwareName='AntMiner L3+ (scrypt @ 5k$)'         hardwareSpeed='1M * 5k / 2500'
#hardwareName='AntMiner L3+ (scrypt @ 20k$)'        hardwareSpeed='1M * 20k / 2500'
#hardwareName='AntMiner L3+ (scrypt @ 20M$)'        hardwareSpeed='1M * 20M / 2500'
#hardwareName='AntMiner L3+ (scrypt @ 5B$)'         hardwareSpeed='1M * 5B / 2500'
 hardwareName='AntMiner S9 (SHA256)'                hardwareSpeed='14T'
#hardwareName='AntMiner S9 (SHA256 @ 5k$)'          hardwareSpeed='14T * 5k / 1288'
#hardwareName='AntMiner S9 (SHA256 @ 20k$)'         hardwareSpeed='14T * 20k / 1288'
#hardwareName='AntMiner S9 (SHA256 @ 20M$)'         hardwareSpeed='14T * 20M / 1288'
#hardwareName='AntMiner S9 (SHA256 @ 5B$)'          hardwareSpeed='14T * 5B / 1288'

# spectre-bench
#hardwareName='2.3 GHz i7, 8GB (SPECTRE)'               hardwareSpeed=7.46

second='1'
secondsInHour='3600'
secondsInDay='3600 * 24'
secondsInMonth='3600 * 24 * 30'
secondsInYear='3600 * 24 * 356'
hardwareSpeed=${hardwareSpeed//k/000}
hardwareSpeed=${hardwareSpeed//M/000000}
hardwareSpeed=${hardwareSpeed//G/000000000}
hardwareSpeed=${hardwareSpeed//T/000000000000}

## SEARCH SPACE
hr
inf 'SEARCH SPACE'
inf 'You can use the following variables:'
for _c in V C v c A a n o x w; do
    cc=${!_c}
    if [[ $cc = @* ]]; then
        file=${cc#@} file=${file/#~\//$HOME\/}
        if [[ ! -e $file && -e "${BASH_SOURCE%/*}/$file" ]]; then
            file="${BASH_SOURCE%/*}/$file"
        fi
        read cs < <(wc -l < "$file")
    else
        cs=${#cc}
    fi

    inf '%s: Class contains %d entities: %s' "$_c" "$cs" "$cc"
done
spaceString=${1:-$(ask -d "x ** 12" "Amount of space?")}
case "$spaceString" in
    -mp*) mpmode=${spaceString#-mp} mpmode=${mpmode:-long}
        case "$mpmode" in
            long|l) spaceString='(CvcvnoCvcvCvcv+CvcvCvcvnoCvcv+CvcvCvcvCvcvno+CvccnoCvcvCvcv+CvccCvcvnoCvcv+CvccCvcvCvcvno+CvcvnoCvccCvcv+CvcvCvccnoCvcv+CvcvCvccCvcvno+CvcvnoCvcvCvcc+CvcvCvcvnoCvcc+CvcvCvcvCvccno+CvccnoCvccCvcv+CvccCvccnoCvcv+CvccCvccCvcvno+CvcvnoCvccCvcc+CvcvCvccnoCvcc+CvcvCvccCvccno+CvccnoCvcvCvcc+CvccCvcvnoCvcc+CvccCvcvCvccno)' ;;
            max|secure|x) spaceString='aonxxxxxxxxxxxxxxxxx+axxxxxxxxxxxxxxxxxon' ;;
            med|m) spaceString='CvcnoCvc+CvcCvcno' ;;
            basic|b) spaceString='aaanaaan+aannaaan+aaannaaa' ;;
        esac ;;
esac
space=$spaceString
for _c in V C v c A a n o x w; do
    cc=${!_c}
    if [[ $cc = @* ]]; then
        file=${cc#@} file=${file/#~\//$HOME\/}
        read cs < <(wc -l < "$file")
    else
        cs=${#cc}
    fi

    space=${space//$_c/ 0$cs }
done
# Replace sequences of numbers by multiplication of those numbers.  Then, pretty-print.
space=$(sed -e 's/\([[:digit:]]\)  *\([[:digit:]]\)/\1 * \2/g' -e 's/ 00*\([1-9]\)/ \1/g' <<< "$space")
space=$(tr -s ' ' <<< "$space") space=${space# } space=${space% }
inf ''
inf "Search space: %s = %s = %'.f possibilities to try (~%.1f bit)." "$spaceString" "$space" "$(calc "$space")" "$(bc -l <<< "l($(calc "$space")) / l(2)")"

## CLUSTER SIZE
hr
inf 'CLUSTER SIZE'
inf "Simulating %s at a rate of about %'.1f attempts per second." "$hardwareName" "$(calc "$hardwareSpeed")"
cluster=$(ask -d 1 "Amount of GPUs?")


## CALCULATE
hr
inf 'TIMING'
inf "Time to search the entire space using %d GPUs of type %s (rate=%'.1f/s)" "$cluster" "$hardwareName" "$(calc "$hardwareSpeed")"
timing() {
    local title=$1 unit=$2 precision=$3 seconds=$4
    time=$(calc "1.0 * ($space) / ($hardwareSpeed * $cluster) / ($seconds)")
    percent=$(calc "100.0 * ($hardwareSpeed * $cluster) * ($seconds) / ($space)")
    amount=$(calc "$percent / 100.0")
    if [[ $amount = 0.* ]]; then
        inf "%10s to crack: %'0.${precision}f (search rate is %0.0f%% / %s)" \
            "$title" "$time" "$percent" "$unit"
    else
        inf "%10s to crack: %'0.${precision}f (completes %0.1fx / %s)" \
            "$title" "$time" "$amount" "$unit"
    fi
}
timing Seconds s 0 "$second"
timing Hours h 2 "$secondsInHour"
timing Days d 3 "$secondsInDay"
timing Months m 4 "$secondsInMonth"
timing Years y 4 "$secondsInYear"
0707010000005F000081A400000000000000000000000163C2D555000125DD000000000000000000000000000000000000003600000000spectre-cli-3.0+git20230114.26c01a5/scripts/words.txtthe
of
and
to
a
in
for
is
on
that
by
this
with
i
you
it
not
or
be
are
from
at
as
your
all
have
new
more
an
was
we
will
home
can
us
about
if
page
my
has
search
free
but
our
one
other
do
no
information
time
they
site
he
up
may
what
which
their
news
out
use
any
there
see
only
so
his
when
contact
here
business
who
web
also
now
help
get
pm
view
online
c
e
first
am
been
would
how
were
me
s
services
some
these
click
its
like
service
x
than
find
price
date
back
top
people
had
list
name
just
over
state
year
day
into
email
two
health
n
world
re
next
used
go
b
work
last
most
products
music
buy
data
make
them
should
product
system
post
her
city
t
add
policy
number
such
please
available
copyright
support
message
after
best
software
then
jan
good
video
well
d
where
info
rights
public
books
high
school
through
m
each
links
she
review
years
order
very
privacy
book
items
company
r
read
group
need
many
user
said
de
does
set
under
general
research
university
january
mail
full
map
reviews
program
life
know
games
way
days
management
p
part
could
great
united
hotel
real
f
item
international
center
ebay
must
store
travel
comments
made
development
report
off
member
details
line
terms
before
hotels
did
send
right
type
because
local
those
using
results
office
education
national
car
design
take
posted
internet
address
community
within
states
area
want
phone
dvd
shipping
reserved
subject
between
forum
family
l
long
based
w
code
show
o
even
black
check
special
prices
website
index
being
women
much
sign
file
link
open
today
technology
south
case
project
same
pages
uk
version
section
own
found
sports
house
related
security
both
g
county
american
photo
game
members
power
while
care
network
down
computer
systems
three
total
place
end
following
download
h
him
without
per
access
think
north
resources
current
posts
big
media
law
control
water
history
pictures
size
art
personal
since
including
guide
shop
directory
board
location
change
white
text
small
rating
rate
government
children
during
usa
return
students
v
shopping
account
times
sites
level
digital
profile
previous
form
events
love
old
john
main
call
hours
image
department
title
description
non
k
y
insurance
another
why
shall
property
class
cd
still
money
quality
every
listing
content
country
private
little
visit
save
tools
low
reply
customer
december
compare
movies
include
college
value
article
york
man
card
jobs
provide
j
food
source
author
different
press
u
learn
sale
around
print
course
job
canada
process
teen
room
stock
training
too
credit
point
join
science
men
categories
advanced
west
sales
look
english
left
team
estate
box
conditions
select
windows
photos
gay
thread
week
category
note
live
large
gallery
table
register
however
june
october
november
market
library
really
action
start
series
model
features
air
industry
plan
human
provided
tv
yes
required
second
hot
accessories
cost
movie
forums
march
la
september
better
say
questions
july
yahoo
going
medical
test
friend
come
dec
server
pc
study
application
cart
staff
articles
san
feedback
again
play
looking
issues
april
never
users
complete
street
topic
comment
financial
things
working
against
standard
tax
person
below
mobile
less
got
blog
party
payment
equipment
login
student
let
programs
offers
legal
above
recent
park
stores
side
act
problem
red
give
memory
performance
social
q
august
quote
language
story
sell
options
experience
rates
create
key
body
young
america
important
field
few
east
paper
single
ii
age
activities
club
example
girls
additional
password
z
latest
something
road
gift
question
changes
night
ca
hard
texas
oct
pay
four
poker
status
browse
issue
range
building
seller
court
february
always
result
audio
light
write
war
nov
offer
blue
groups
al
easy
given
files
event
release
analysis
request
fax
china
making
picture
needs
possible
might
professional
yet
month
major
star
areas
future
space
committee
hand
sun
cards
problems
london
washington
meeting
rss
become
interest
id
child
keep
enter
california
share
similar
garden
schools
million
added
reference
companies
listed
baby
learning
energy
run
delivery
net
popular
term
film
stories
put
computers
journal
reports
co
try
welcome
central
images
president
notice
original
head
radio
until
cell
color
self
council
away
includes
track
australia
discussion
archive
once
others
entertainment
agreement
format
least
society
months
log
safety
friends
sure
faq
trade
edition
cars
messages
marketing
tell
further
updated
association
able
having
provides
david
fun
already
green
studies
close
common
drive
specific
several
gold
feb
living
sep
collection
called
short
arts
lot
ask
display
limited
powered
solutions
means
director
daily
beach
past
natural
whether
due
et
electronics
five
upon
period
planning
database
says
official
weather
mar
land
average
done
technical
window
france
pro
region
island
record
direct
microsoft
conference
environment
records
st
district
calendar
costs
style
url
front
statement
update
parts
aug
ever
downloads
early
miles
sound
resource
present
applications
either
ago
document
word
works
material
bill
apr
written
talk
federal
hosting
rules
final
adult
tickets
thing
centre
requirements
via
cheap
kids
finance
true
minutes
else
mark
third
rock
gifts
europe
reading
topics
bad
individual
tips
plus
auto
cover
usually
edit
together
videos
percent
fast
function
fact
unit
getting
global
tech
meet
far
economic
en
player
projects
lyrics
often
subscribe
submit
germany
amount
watch
included
feel
though
bank
risk
thanks
everything
deals
various
words
linux
jul
production
commercial
james
weight
town
heart
advertising
received
choose
treatment
newsletter
archives
points
knowledge
magazine
error
camera
jun
girl
currently
construction
toys
registered
clear
golf
receive
domain
methods
chapter
makes
protection
policies
loan
wide
beauty
manager
india
position
taken
sort
listings
models
michael
known
half
cases
step
engineering
florida
simple
quick
none
wireless
license
paul
friday
lake
whole
annual
published
later
basic
sony
shows
corporate
google
church
method
purchase
customers
active
response
practice
hardware
figure
materials
fire
holiday
chat
enough
designed
along
among
death
writing
speed
html
countries
loss
face
brand
discount
higher
effects
created
remember
standards
oil
bit
yellow
political
increase
advertise
kingdom
base
near
environmental
thought
stuff
french
storage
oh
japan
doing
loans
shoes
entry
stay
nature
orders
availability
africa
summary
turn
mean
growth
notes
agency
king
monday
european
activity
copy
although
drug
pics
western
income
force
cash
employment
overall
bay
river
commission
ad
package
contents
seen
players
engine
port
album
regional
stop
supplies
started
administration
bar
institute
views
plans
double
dog
build
screen
exchange
types
soon
sponsored
lines
electronic
continue
across
benefits
needed
season
apply
someone
held
ny
anything
printer
condition
effective
believe
organization
effect
asked
eur
mind
sunday
selection
casino
pdf
lost
tour
menu
volume
cross
anyone
mortgage
hope
silver
corporation
wish
inside
solution
mature
role
rather
weeks
addition
came
supply
nothing
certain
usr
executive
running
lower
necessary
union
jewelry
according
dc
clothing
mon
com
particular
fine
names
robert
homepage
hour
gas
skills
six
bush
islands
advice
career
military
rental
decision
leave
british
teens
pre
huge
sat
woman
facilities
zip
bid
kind
sellers
middle
move
cable
opportunities
taking
values
division
coming
tuesday
object
lesbian
appropriate
machine
logo
length
actually
nice
score
statistics
client
ok
returns
capital
follow
sample
investment
sent
shown
saturday
christmas
england
culture
band
flash
ms
lead
george
choice
went
starting
registration
fri
thursday
courses
consumer
hi
airport
foreign
artist
outside
furniture
levels
channel
letter
mode
phones
ideas
wednesday
structure
fund
summer
allow
degree
contract
button
releases
wed
homes
super
male
matter
custom
virginia
almost
took
located
multiple
asian
distribution
editor
inn
industrial
cause
potential
song
cnet
ltd
los
hp
focus
late
fall
featured
idea
rooms
female
responsible
inc
communications
win
associated
thomas
primary
cancer
numbers
reason
tool
browser
spring
foundation
answer
voice
eg
friendly
schedule
documents
communication
purpose
feature
bed
comes
police
everyone
independent
ip
approach
cameras
brown
physical
operating
hill
maps
medicine
deal
hold
ratings
chicago
forms
glass
happy
tue
smith
wanted
developed
thank
safe
unique
survey
prior
telephone
sport
ready
feed
animal
sources
mexico
population
pa
regular
secure
navigation
operations
therefore
simply
evidence
station
christian
round
paypal
favorite
understand
option
master
valley
recently
probably
thu
rentals
sea
built
publications
blood
cut
worldwide
improve
connection
publisher
hall
larger
anti
networks
earth
parents
nokia
impact
transfer
introduction
kitchen
strong
tel
carolina
wedding
properties
hospital
ground
overview
ship
accommodation
owners
disease
tx
excellent
paid
italy
perfect
hair
opportunity
kit
classic
basis
command
cities
william
express
award
distance
tree
peter
assessment
ensure
thus
wall
ie
involved
el
extra
especially
interface
partners
budget
rated
guides
success
maximum
ma
operation
existing
quite
selected
boy
amazon
patients
restaurants
beautiful
warning
wine
locations
horse
vote
forward
flowers
stars
significant
lists
technologies
owner
retail
animals
useful
directly
manufacturer
ways
est
son
providing
rule
mac
housing
takes
iii
gmt
bring
catalog
searches
max
trying
mother
authority
considered
told
xml
traffic
programme
joined
input
strategy
feet
agent
valid
bin
modern
senior
ireland
teaching
door
grand
testing
trial
charge
units
instead
canadian
cool
normal
wrote
enterprise
ships
entire
educational
md
leading
metal
positive
fl
fitness
chinese
opinion
mb
asia
football
abstract
uses
output
funds
mr
greater
likely
develop
employees
artists
alternative
processing
responsibility
resolution
java
guest
seems
publication
pass
relations
trust
van
contains
session
multi
photography
republic
fees
components
vacation
century
academic
assistance
completed
skin
graphics
indian
prev
ads
mary
il
expected
ring
grade
dating
pacific
mountain
organizations
pop
filter
mailing
vehicle
longer
consider
int
northern
behind
panel
floor
german
buying
match
proposed
default
require
iraq
boys
outdoor
deep
morning
otherwise
allows
rest
protein
plant
reported
hit
transportation
mm
pool
mini
politics
partner
disclaimer
authors
boards
faculty
parties
fish
membership
mission
eye
string
sense
modified
pack
released
stage
internal
goods
recommended
born
unless
richard
detailed
japanese
race
approved
background
target
except
character
usb
maintenance
ability
maybe
functions
ed
moving
brands
places
php
pretty
trademarks
phentermine
spain
southern
yourself
etc
winter
battery
youth
pressure
submitted
boston
debt
keywords
medium
television
interested
core
break
purposes
throughout
sets
dance
wood
msn
itself
defined
papers
playing
awards
fee
studio
reader
virtual
device
established
answers
rent
las
remote
dark
programming
external
apple
le
regarding
instructions
min
offered
theory
enjoy
remove
aid
surface
minimum
visual
host
variety
teachers
isbn
martin
manual
block
subjects
agents
increased
repair
fair
civil
steel
understanding
songs
fixed
wrong
beginning
hands
associates
finally
az
updates
desktop
classes
paris
ohio
gets
sector
capacity
requires
jersey
un
fat
fully
father
electric
saw
instruments
quotes
officer
driver
businesses
dead
respect
unknown
specified
restaurant
mike
trip
pst
worth
mi
procedures
poor
teacher
eyes
relationship
workers
farm
georgia
peace
traditional
campus
tom
showing
creative
coast
benefit
progress
funding
devices
lord
grant
sub
agree
fiction
hear
sometimes
watches
careers
beyond
goes
families
led
museum
themselves
fan
transport
interesting
blogs
wife
evaluation
accepted
former
implementation
ten
hits
zone
complex
th
cat
galleries
references
die
presented
jack
flat
flow
agencies
literature
respective
parent
spanish
michigan
columbia
setting
dr
scale
stand
economy
highest
helpful
monthly
critical
frame
musical
definition
secretary
angeles
networking
path
australian
employee
chief
gives
kb
bottom
magazines
packages
detail
francisco
laws
changed
pet
heard
begin
individuals
colorado
royal
clean
switch
russian
largest
african
guy
titles
relevant
guidelines
justice
connect
bible
dev
cup
basket
applied
weekly
vol
installation
described
demand
pp
suite
vegas
na
square
chris
attention
advance
skip
diet
army
auction
gear
lee
os
difference
allowed
correct
charles
nation
selling
lots
piece
sheet
firm
seven
older
illinois
regulations
elements
species
jump
cells
module
resort
facility
random
pricing
dvds
certificate
minister
motion
looks
fashion
directions
visitors
documentation
monitor
trading
forest
calls
whose
coverage
couple
giving
chance
vision
ball
ending
clients
actions
listen
discuss
accept
automotive
naked
goal
successful
sold
wind
communities
clinical
situation
sciences
markets
lowest
highly
publishing
appear
emergency
developing
lives
currency
leather
determine
temperature
palm
announcements
patient
actual
historical
stone
bob
commerce
ringtones
perhaps
persons
difficult
scientific
satellite
fit
tests
village
accounts
amateur
ex
met
pain
xbox
particularly
factors
coffee
www
settings
buyer
cultural
steve
easily
oral
ford
poster
edge
functional
root
au
fi
closed
holidays
ice
pink
zealand
balance
monitoring
graduate
replies
shot
nc
architecture
initial
label
thinking
scott
llc
sec
recommend
canon
league
waste
minute
bus
provider
optional
dictionary
cold
accounting
manufacturing
sections
chair
fishing
effort
phase
fields
bag
fantasy
po
letters
motor
va
professor
context
install
shirt
apparel
generally
continued
foot
mass
crime
count
breast
techniques
ibm
rd
johnson
sc
quickly
dollars
websites
religion
claim
driving
permission
surgery
patch
heat
wild
measures
generation
kansas
miss
chemical
doctor
task
reduce
brought
himself
nor
component
enable
exercise
bug
santa
mid
guarantee
leader
diamond
israel
se
processes
soft
servers
alone
meetings
seconds
jones
arizona
keyword
interests
flight
congress
fuel
username
walk
produced
italian
paperback
classifieds
wait
supported
pocket
saint
rose
freedom
argument
competition
creating
jim
drugs
joint
premium
providers
fresh
characters
attorney
upgrade
di
factor
growing
thousands
km
stream
apartments
pick
hearing
eastern
auctions
therapy
entries
dates
generated
signed
upper
administrative
serious
prime
samsung
limit
began
louis
steps
errors
shops
del
efforts
informed
ga
ac
thoughts
creek
ft
worked
quantity
urban
practices
sorted
reporting
essential
myself
tours
platform
load
affiliate
labor
immediately
admin
nursing
defense
machines
designated
tags
heavy
covered
recovery
joe
guys
integrated
configuration
merchant
comprehensive
expert
universal
protect
drop
solid
cds
presentation
languages
became
orange
compliance
vehicles
prevent
theme
rich
im
campaign
marine
improvement
vs
guitar
finding
pennsylvania
examples
ipod
saying
spirit
ar
claims
challenge
motorola
acceptance
strategies
mo
seem
affairs
touch
intended
towards
sa
goals
hire
election
suggest
branch
charges
serve
affiliates
reasons
magic
mount
smart
talking
gave
ones
latin
multimedia
xp
avoid
certified
manage
corner
rank
computing
oregon
element
birth
virus
abuse
interactive
requests
separate
quarter
procedure
leadership
tables
define
racing
religious
facts
breakfast
kong
column
plants
faith
chain
developer
identify
avenue
missing
died
approximately
domestic
sitemap
recommendations
moved
houston
reach
comparison
mental
viewed
moment
extended
sequence
inch
attack
sorry
centers
opening
damage
lab
reserve
recipes
cvs
gamma
plastic
produce
snow
placed
truth
counter
failure
follows
eu
weekend
dollar
camp
ontario
automatically
des
minnesota
films
bridge
native
fill
williams
movement
printing
baseball
owned
approval
draft
chart
played
contacts
cc
jesus
readers
clubs
lcd
wa
jackson
equal
adventure
matching
offering
shirts
profit
leaders
posters
institutions
assistant
variable
ave
dj
advertisement
expect
parking
headlines
yesterday
compared
determined
wholesale
workshop
russia
gone
codes
kinds
extension
seattle
statements
golden
completely
teams
fort
cm
wi
lighting
senate
forces
funny
brother
gene
turned
portable
tried
electrical
applicable
disc
returned
pattern
ct
boat
named
theatre
laser
earlier
manufacturers
sponsor
classical
icon
warranty
dedicated
indiana
direction
harry
basketball
objects
ends
delete
evening
assembly
nuclear
taxes
mouse
signal
criminal
issued
brain
sexual
wisconsin
powerful
dream
obtained
false
da
cast
flower
felt
personnel
passed
supplied
identified
falls
pic
soul
aids
opinions
promote
stated
stats
hawaii
professionals
appears
carry
flag
decided
nj
covers
hr
em
advantage
hello
designs
maintain
tourism
priority
newsletters
adults
clips
savings
iv
graphic
atom
payments
rw
estimated
binding
brief
ended
winning
eight
anonymous
iron
straight
script
served
wants
miscellaneous
prepared
void
dining
alert
integration
atlanta
dakota
tag
interview
mix
framework
disk
installed
queen
vhs
credits
clearly
fix
handle
sweet
desk
criteria
pubmed
dave
massachusetts
diego
hong
vice
associate
ne
truck
behavior
enlarge
ray
frequently
revenue
measure
changing
votes
du
duty
looked
discussions
bear
gain
festival
laboratory
ocean
flights
experts
signs
lack
depth
iowa
whatever
logged
laptop
vintage
train
exactly
dry
explore
maryland
spa
concept
nearly
eligible
checkout
reality
forgot
handling
origin
knew
gaming
feeds
billion
destination
scotland
faster
intelligence
dallas
bought
con
ups
nations
route
followed
specifications
broken
tripadvisor
frank
alaska
zoom
blow
battle
residential
anime
speak
decisions
industries
protocol
query
clip
partnership
editorial
nt
expression
es
equity
provisions
speech
wire
principles
suggestions
rural
shared
sounds
replacement
tape
strategic
judge
spam
economics
acid
bytes
cent
forced
compatible
fight
apartment
height
null
zero
speaker
filed
gb
netherlands
obtain
bc
consulting
recreation
offices
designer
remain
managed
pr
failed
marriage
roll
korea
banks
fr
participants
secret
bath
aa
kelly
leads
negative
austin
favorites
toronto
theater
springs
missouri
andrew
var
perform
healthy
translation
estimates
font
assets
injury
mt
joseph
ministry
drivers
lawyer
figures
married
protected
proposal
sharing
philadelphia
portal
waiting
birthday
beta
fail
gratis
banking
officials
brian
toward
won
slightly
assist
conduct
contained
lingerie
legislation
calling
parameters
jazz
serving
bags
profiles
miami
comics
matters
houses
doc
postal
relationships
tennessee
wear
controls
breaking
combined
ultimate
wales
representative
frequency
introduced
minor
finish
departments
residents
noted
displayed
mom
reduced
physics
rare
spent
performed
extreme
samples
davis
daniel
bars
reviewed
row
oz
forecast
removed
helps
singles
administrator
cycle
amounts
contain
accuracy
dual
rise
usd
sleep
mg
bird
pharmacy
brazil
creation
static
scene
hunter
addresses
lady
crystal
famous
writer
chairman
violence
fans
oklahoma
speakers
drink
academy
dynamic
gender
eat
permanent
agriculture
dell
cleaning
constitutes
portfolio
practical
delivered
collectibles
infrastructure
exclusive
seat
concerns
vendor
originally
intel
utilities
philosophy
regulation
officers
reduction
aim
bids
referred
supports
nutrition
recording
regions
junior
toll
les
cape
ann
rings
meaning
tip
secondary
wonderful
mine
ladies
henry
ticket
announced
guess
agreed
prevention
whom
ski
soccer
math
import
posting
presence
instant
mentioned
automatic
healthcare
viewing
maintained
ch
increasing
majority
connected
christ
dan
dogs
sd
directors
aspects
austria
ahead
moon
participation
scheme
utility
preview
fly
manner
matrix
containing
combination
devel
amendment
despite
strength
guaranteed
turkey
libraries
proper
distributed
degrees
singapore
enterprises
delta
fear
seeking
inches
phoenix
rs
convention
shares
principal
daughter
standing
comfort
colors
wars
cisco
ordering
kept
alpha
appeal
cruise
bonus
certification
previously
hey
bookmark
buildings
specials
beat
disney
household
batteries
adobe
smoking
bbc
becomes
drives
arms
alabama
tea
improved
trees
avg
achieve
positions
dress
subscription
dealer
contemporary
sky
utah
nearby
rom
carried
happen
exposure
panasonic
hide
permalink
signature
gambling
refer
miller
provision
outdoors
clothes
caused
luxury
babes
frames
certainly
indeed
newspaper
toy
circuit
layer
printed
slow
removal
easier
src
liability
trademark
hip
printers
faqs
nine
adding
kentucky
mostly
eric
spot
taylor
trackback
prints
spend
factory
interior
revised
grow
americans
optical
promotion
relative
amazing
clock
dot
hiv
identity
suites
conversion
feeling
hidden
reasonable
victoria
serial
relief
revision
broadband
influence
ratio
pda
importance
rain
onto
dsl
planet
webmaster
copies
recipe
zum
permit
seeing
proof
dna
diff
tennis
bass
prescription
bedroom
empty
instance
hole
pets
ride
licensed
orlando
specifically
tim
bureau
maine
sql
represent
conservation
pair
ideal
specs
recorded
don
pieces
finished
parks
dinner
lawyers
sydney
stress
cream
ss
runs
trends
yeah
discover
ap
patterns
boxes
louisiana
hills
javascript
fourth
nm
advisor
mn
marketplace
nd
evil
aware
wilson
shape
evolution
irish
certificates
objectives
stations
suggested
gps
op
remains
acc
greatest
firms
concerned
euro
operator
structures
generic
encyclopedia
usage
cap
ink
charts
continuing
mixed
census
interracial
peak
tn
competitive
exist
wheel
transit
suppliers
salt
compact
poetry
lights
tracking
angel
bell
keeping
preparation
attempt
receiving
matches
accordance
width
noise
engines
forget
array
discussed
accurate
stephen
elizabeth
climate
reservations
pin
playstation
alcohol
greek
instruction
managing
annotation
sister
raw
differences
walking
explain
smaller
newest
establish
gnu
happened
expressed
jeff
extent
sharp
lesbians
ben
lane
paragraph
kill
mathematics
aol
compensation
ce
export
managers
aircraft
modules
sweden
conflict
conducted
versions
employer
occur
percentage
knows
mississippi
describe
concern
backup
requested
citizens
connecticut
heritage
personals
immediate
holding
trouble
spread
coach
kevin
agricultural
expand
supporting
audience
assigned
jordan
collections
ages
participate
plug
specialist
cook
affect
virgin
experienced
investigation
raised
hat
institution
directed
dealers
searching
sporting
helping
perl
affected
lib
bike
totally
plate
expenses
indicate
blonde
ab
proceedings
transmission
anderson
utc
characteristics
der
lose
organic
seek
experiences
albums
cheats
extremely
verzeichnis
contracts
guests
hosted
diseases
concerning
developers
equivalent
chemistry
tony
neighborhood
nevada
kits
thailand
variables
agenda
anyway
continues
tracks
advisory
cam
curriculum
logic
template
prince
circle
soil
grants
anywhere
psychology
responses
atlantic
wet
circumstances
edward
investor
identification
ram
leaving
wildlife
appliances
matt
elementary
cooking
speaking
sponsors
fox
unlimited
respond
sizes
plain
exit
entered
iran
arm
keys
launch
wave
checking
costa
belgium
printable
holy
acts
guidance
mesh
trail
enforcement
symbol
crafts
highway
buddy
hardcover
observed
dean
setup
poll
booking
glossary
fiscal
celebrity
styles
denver
unix
filled
bond
channels
ericsson
appendix
notify
blues
chocolate
pub
portion
scope
hampshire
supplier
cables
cotton
bluetooth
controlled
requirement
authorities
biology
dental
killed
border
ancient
debate
representatives
starts
pregnancy
causes
arkansas
biography
leisure
attractions
learned
transactions
notebook
explorer
historic
attached
opened
tm
husband
disabled
authorized
crazy
upcoming
britain
concert
retirement
scores
financing
efficiency
sp
comedy
adopted
efficient
weblog
linear
commitment
specialty
bears
jean
hop
carrier
edited
constant
visa
mouth
jewish
meter
linked
portland
interviews
concepts
nh
gun
reflect
pure
deliver
wonder
lessons
fruit
begins
qualified
reform
lens
alerts
treated
discovery
draw
mysql
classified
relating
assume
confidence
alliance
fm
confirm
warm
neither
lewis
howard
offline
leaves
engineer
lifestyle
consistent
replace
clearance
connections
inventory
converter
organisation
babe
checks
reached
becoming
safari
objective
indicated
sugar
crew
legs
sam
stick
securities
allen
pdt
relation
enabled
genre
slide
montana
volunteer
tested
rear
democratic
enhance
switzerland
exact
bound
parameter
adapter
processor
node
formal
dimensions
contribute
lock
hockey
storm
micro
colleges
laptops
mile
showed
challenges
editors
mens
threads
bowl
supreme
brothers
recognition
presents
ref
tank
submission
dolls
estimate
encourage
navy
kid
regulatory
inspection
consumers
cancel
limits
territory
transaction
manchester
weapons
paint
delay
pilot
outlet
contributions
continuous
db
czech
resulting
cambridge
initiative
novel
pan
execution
disability
increases
ultra
winner
idaho
contractor
ph
episode
examination
potter
dish
plays
bulletin
ia
pt
indicates
modify
oxford
adam
truly
epinions
painting
committed
extensive
affordable
universe
candidate
databases
patent
slot
psp
outstanding
ha
eating
perspective
planned
watching
lodge
messenger
mirror
tournament
consideration
ds
discounts
sterling
sessions
kernel
stocks
buyers
journals
gray
catalogue
ea
jennifer
antonio
charged
broad
taiwan
und
chosen
demo
greece
lg
swiss
sarah
clark
hate
terminal
publishers
nights
behalf
caribbean
liquid
rice
nebraska
loop
salary
reservation
foods
gourmet
guard
properly
orleans
saving
nfl
remaining
empire
resume
twenty
newly
raise
prepare
avatar
gary
depending
illegal
expansion
vary
hundreds
rome
arab
lincoln
helped
premier
tomorrow
purchased
milk
decide
consent
drama
visiting
performing
downtown
keyboard
contest
collected
nw
bands
boot
suitable
ff
absolutely
millions
lunch
audit
push
chamber
guinea
findings
muscle
featuring
iso
implement
clicking
scheduled
polls
typical
tower
yours
sum
misc
calculator
significantly
chicken
temporary
attend
shower
alan
sending
jason
tonight
dear
sufficient
holdem
shell
province
catholic
oak
vat
awareness
vancouver
governor
beer
seemed
contribution
measurement
swimming
spyware
formula
constitution
packaging
solar
jose
catch
jane
pakistan
ps
reliable
consultation
northwest
sir
doubt
earn
finder
unable
periods
classroom
tasks
democracy
attacks
kim
wallpaper
merchandise
const
resistance
doors
symptoms
resorts
biggest
memorial
visitor
twin
forth
insert
baltimore
gateway
ky
dont
alumni
drawing
candidates
charlotte
ordered
biological
fighting
transition
happens
preferences
spy
romance
instrument
bruce
split
themes
powers
heaven
br
bits
pregnant
twice
classification
focused
egypt
physician
hollywood
bargain
wikipedia
cellular
norway
vermont
asking
blocks
normally
lo
spiritual
hunting
diabetes
suit
ml
shift
chip
res
sit
bodies
photographs
cutting
wow
simon
writers
marks
flexible
loved
mapping
numerous
relatively
birds
satisfaction
represents
char
indexed
pittsburgh
superior
preferred
saved
paying
cartoon
shots
intellectual
moore
granted
choices
carbon
spending
comfortable
magnetic
interaction
listening
effectively
registry
crisis
outlook
massive
denmark
employed
bright
treat
header
cs
poverty
formed
piano
echo
que
grid
sheets
patrick
experimental
puerto
revolution
consolidation
displays
plasma
allowing
earnings
voip
mystery
landscape
dependent
mechanical
journey
delaware
bidding
consultants
risks
banner
applicant
charter
fig
barbara
cooperation
counties
acquisition
ports
implemented
sf
directories
recognized
dreams
blogger
notification
kg
licensing
stands
teach
occurred
textbooks
rapid
pull
hairy
diversity
cleveland
ut
reverse
deposit
seminar
investments
latina
nasa
wheels
sexcam
specify
accessibility
dutch
sensitive
templates
formats
tab
depends
boots
holds
router
concrete
si
editing
poland
folder
womens
css
completion
upload
pulse
universities
technique
contractors
milfhunter
voting
courts
notices
subscriptions
calculate
mc
detroit
alexander
broadcast
converted
metro
toshiba
anniversary
improvements
strip
specification
pearl
accident
nick
accessible
accessory
resident
plot
qty
possibly
airline
typically
representation
regard
pump
exists
arrangements
smooth
conferences
uniprotkb
strike
consumption
birmingham
flashing
lp
narrow
afternoon
threat
surveys
sitting
putting
consultant
controller
ownership
committees
legislative
researchers
vietnam
trailer
anne
castle
gardens
missed
malaysia
unsubscribe
antique
labels
willing
bio
molecular
acting
heads
stored
exam
logos
residence
attorneys
milfs
antiques
density
hundred
ryan
operators
strange
sustainable
philippines
statistical
beds
mention
innovation
pcs
employers
grey
parallel
honda
amended
operate
bills
bold
bathroom
stable
opera
definitions
von
doctors
lesson
cinema
asset
ag
scan
elections
drinking
reaction
blank
enhanced
entitled
severe
generate
stainless
newspapers
hospitals
vi
deluxe
humor
aged
monitors
exception
lived
duration
bulk
successfully
indonesia
pursuant
sci
fabric
edt
visits
primarily
tight
domains
capabilities
pmid
contrast
recommendation
flying
recruitment
sin
berlin
cute
organized
ba
para
siemens
adoption
improving
cr
expensive
meant
capture
pounds
buffalo
organisations
plane
pg
explained
seed
programmes
desire
expertise
mechanism
camping
ee
jewellery
meets
welfare
peer
caught
eventually
marked
driven
measured
medline
bottle
agreements
considering
innovative
marshall
massage
rubber
conclusion
closing
tampa
thousand
meat
legend
grace
susan
ing
ks
adams
python
monster
alex
bang
villa
bone
columns
disorders
bugs
collaboration
hamilton
detection
ftp
cookies
inner
formation
tutorial
med
engineers
entity
cruises
gate
holder
proposals
moderator
sw
tutorials
settlement
portugal
lawrence
roman
duties
valuable
tone
collectables
ethics
forever
dragon
busy
captain
fantastic
imagine
brings
heating
leg
neck
hd
wing
governments
purchasing
scripts
abc
stereo
appointed
taste
dealing
commit
tiny
operational
rail
airlines
liberal
livecam
jay
trips
gap
sides
tube
turns
corresponding
descriptions
cache
belt
jacket
determination
animation
oracle
er
matthew
lease
productions
aviation
hobbies
proud
excess
disaster
console
commands
jr
telecommunications
instructor
giant
achieved
injuries
shipped
seats
approaches
biz
alarm
voltage
anthony
nintendo
usual
loading
stamps
appeared
franklin
angle
rob
vinyl
highlights
mining
designers
melbourne
ongoing
worst
imaging
betting
scientists
liberty
wyoming
blackjack
argentina
era
convert
possibility
analyst
commissioner
dangerous
garage
exciting
reliability
thongs
gcc
unfortunately
respectively
volunteers
attachment
ringtone
finland
morgan
derived
pleasure
honor
asp
oriented
eagle
desktops
pants
columbus
nurse
prayer
appointment
workshops
hurricane
quiet
luck
postage
producer
represented
mortgages
dial
responsibilities
cheese
comic
carefully
jet
productivity
investors
crown
par
underground
diagnosis
maker
crack
principle
picks
vacations
gang
semester
calculated
fetish
applies
casinos
appearance
smoke
apache
filters
incorporated
nv
craft
cake
notebooks
apart
fellow
blind
lounge
mad
algorithm
semi
coins
andy
gross
strongly
cafe
valentine
hilton
ken
proteins
horror
su
exp
familiar
capable
douglas
debian
till
involving
pen
investing
christopher
admission
epson
shoe
elected
carrying
victory
sand
madison
terrorism
joy
editions
cpu
mainly
ethnic
ran
parliament
actor
finds
seal
situations
fifth
allocated
citizen
vertical
corrections
structural
municipal
describes
prize
sr
occurs
jon
absolute
disabilities
consists
anytime
substance
prohibited
addressed
lies
pipe
soldiers
nr
guardian
lecture
simulation
layout
initiatives
ill
concentration
classics
lbs
lay
interpretation
horses
lol
dirty
deck
wayne
donate
taught
bankruptcy
mp
worker
optimization
alive
temple
substances
prove
discovered
wings
breaks
genetic
restrictions
participating
waters
promise
thin
exhibition
prefer
ridge
cabinet
modem
harris
mph
bringing
sick
dose
evaluate
tiffany
tropical
collect
bet
composition
toyota
streets
nationwide
vector
definitely
shaved
turning
buffer
purple
existence
commentary
larry
limousines
developments
def
immigration
destinations
lets
mutual
pipeline
necessarily
syntax
li
attribute
prison
skill
chairs
nl
everyday
apparently
surrounding
mountains
moves
popularity
inquiry
ethernet
checked
exhibit
throw
trend
sierra
visible
cats
desert
postposted
ya
oldest
rhode
nba
coordinator
obviously
mercury
steven
handbook
greg
navigate
worse
summit
victims
epa
spaces
fundamental
burning
escape
coupons
somewhat
receiver
substantial
tr
progressive
cialis
bb
boats
glance
scottish
championship
arcade
richmond
sacramento
impossible
ron
russell
tells
obvious
fiber
depression
graph
covering
platinum
judgment
bedrooms
talks
filing
foster
modeling
passing
awarded
testimonials
trials
tissue
nz
memorabilia
clinton
masters
bonds
cartridge
alberta
explanation
folk
org
commons
cincinnati
subsection
fraud
electricity
permitted
spectrum
arrival
okay
pottery
emphasis
roger
aspect
workplace
awesome
mexican
confirmed
counts
priced
wallpapers
hist
crash
lift
desired
inter
closer
assumes
heights
shadow
riding
infection
firefox
lisa
expense
grove
eligibility
venture
clinic
korean
healing
princess
mall
entering
packet
spray
studios
involvement
dad
buttons
placement
observations
vbulletin
funded
thompson
winners
extend
roads
subsequent
pat
dublin
rolling
fell
motorcycle
yard
disclosure
establishment
memories
nelson
te
arrived
creates
faces
tourist
av
mayor
murder
sean
adequate
senator
yield
presentations
grades
cartoons
pour
digest
reg
lodging
tion
dust
hence
wiki
entirely
replaced
radar
rescue
undergraduate
losses
combat
reducing
stopped
occupation
lakes
donations
associations
citysearch
closely
radiation
diary
seriously
kings
shooting
kent
adds
nsw
ear
flags
pci
baker
launched
elsewhere
pollution
conservative
guestbook
shock
effectiveness
walls
abroad
ebony
tie
ward
drawn
arthur
ian
visited
roof
walker
demonstrate
atmosphere
suggests
kiss
beast
ra
operated
experiment
targets
overseas
purchases
dodge
counsel
federation
pizza
invited
yards
assignment
chemicals
gordon
mod
farmers
rc
queries
bmw
rush
ukraine
absence
nearest
cluster
vendors
mpeg
whereas
yoga
serves
woods
surprise
lamp
rico
partial
shoppers
phil
everybody
couples
nashville
ranking
jokes
cst
http
ceo
simpson
twiki
sublime
counseling
palace
acceptable
satisfied
glad
wins
measurements
verify
globe
trusted
copper
milwaukee
rack
medication
warehouse
shareware
ec
rep
dicke
kerry
receipt
supposed
ordinary
nobody
ghost
violation
configure
stability
mit
applying
southwest
boss
pride
institutional
expectations
independence
knowing
reporter
metabolism
keith
champion
cloudy
linda
ross
personally
chile
anna
plenty
solo
sentence
throat
ignore
maria
uniform
excellence
wealth
tall
rm
somewhere
vacuum
dancing
attributes
recognize
brass
writes
plaza
pdas
outcomes
survival
quest
publish
sri
screening
toe
thumbnail
trans
jonathan
whenever
nova
lifetime
api
pioneer
booty
forgotten
acrobat
plates
acres
venue
athletic
thermal
essays
vital
telling
fairly
coastal
config
cf
charity
intelligent
edinburgh
vt
excel
modes
obligation
campbell
wake
stupid
harbor
hungary
traveler
urw
segment
realize
regardless
lan
enemy
puzzle
rising
aluminum
wells
wishlist
opens
insight
sms
restricted
republican
secrets
lucky
latter
merchants
thick
trailers
repeat
syndrome
philips
attendance
penalty
drum
glasses
enables
nec
iraqi
builder
vista
jessica
chips
terry
flood
foto
ease
arguments
amsterdam
arena
adventures
pupils
stewart
announcement
tabs
outcome
appreciate
expanded
casual
grown
polish
lovely
extras
gm
centres
jerry
clause
smile
lands
ri
troops
indoor
bulgaria
armed
broker
charger
regularly
believed
pine
cooling
tend
gulf
rt
rick
trucks
cp
mechanisms
divorce
laura
shopper
tokyo
partly
nikon
customize
tradition
candy
pills
tiger
donald
folks
sensor
exposed
telecom
hunt
angels
deputy
indicators
sealed
thai
emissions
physicians
loaded
fred
complaint
scenes
experiments
afghanistan
dd
boost
spanking
scholarship
governance
mill
founded
supplements
chronic
icons
moral
den
catering
aud
finger
keeps
pound
locate
camcorder
pl
trained
burn
implementing
roses
labs
ourselves
bread
tobacco
wooden
motors
tough
roberts
incident
gonna
dynamics
lie
crm
rf
conversation
decrease
cumshots
chest
pension
billy
revenues
emerging
worship
capability
ak
fe
craig
herself
producing
churches
precision
damages
reserves
contributed
solve
shorts
reproduction
minority
td
diverse
amp
ingredients
sb
ah
johnny
sole
franchise
recorder
complaints
facing
sm
nancy
promotions
tones
passion
rehabilitation
maintaining
sight
laid
clay
defence
patches
weak
refund
usc
towns
environments
trembl
divided
blvd
reception
amd
wise
emails
cyprus
wv
odds
correctly
insider
seminars
consequences
makers
hearts
geography
appearing
integrity
worry
ns
discrimination
eve
carter
legacy
marc
pleased
danger
vitamin
widely
processed
phrase
genuine
raising
implications
functionality
paradise
hybrid
reads
roles
intermediate
emotional
sons
leaf
pad
glory
platforms
ja
bigger
billing
diesel
versus
combine
overnight
geographic
exceed
bs
rod
saudi
fault
cuba
hrs
preliminary
districts
introduce
silk
promotional
kate
chevrolet
babies
bi
karen
compiled
romantic
revealed
specialists
generator
albert
examine
jimmy
graham
suspension
bristol
margaret
compaq
sad
correction
wolf
slowly
authentication
communicate
rugby
supplement
showtimes
cal
portions
infant
promoting
sectors
samuel
fluid
grounds
fits
kick
regards
meal
ta
hurt
machinery
bandwidth
unlike
equation
baskets
probability
pot
dimension
wright
img
barry
proven
schedules
admissions
cached
warren
slip
studied
reviewer
involves
quarterly
rpm
profits
devil
grass
comply
marie
florist
illustrated
cherry
continental
alternate
deutsch
achievement
limitations
kenya
webcam
cuts
funeral
nutten
earrings
enjoyed
automated
chapters
pee
charlie
quebec
passenger
convenient
dennis
mars
francis
tvs
sized
manga
noticed
socket
silent
literary
egg
mhz
signals
caps
orientation
pill
theft
childhood
swing
symbols
lat
meta
humans
analog
facial
choosing
talent
dated
flexibility
seeker
wisdom
shoot
boundary
mint
packard
offset
payday
philip
elite
gi
spin
holders
believes
swedish
poems
deadline
jurisdiction
robot
displaying
witness
collins
equipped
stages
encouraged
sur
winds
powder
broadway
acquired
assess
wash
cartridges
stones
entrance
gnome
roots
declaration
losing
attempts
gadgets
noble
glasgow
automation
impacts
rev
gospel
advantages
shore
loves
induced
ll
knight
preparing
loose
aims
recipient
linking
extensions
appeals
cl
earned
illness
islamic
athletics
southeast
ieee
ho
alternatives
pending
parker
determining
lebanon
corp
personalized
kennedy
gt
sh
conditioning
teenage
soap
ae
triple
cooper
nyc
vincent
jam
secured
unusual
answered
partnerships
destruction
slots
increasingly
migration
disorder
routine
toolbar
basically
rocks
conventional
titans
applicants
wearing
axis
sought
genes
mounted
habitat
firewall
median
guns
scanner
herein
occupational
animated
judicial
rio
hs
adjustment
hero
integer
treatments
bachelor
attitude
camcorders
engaged
falling
basics
montreal
carpet
rv
struct
lenses
binary
genetics
attended
difficulty
punk
collective
coalition
pi
dropped
enrollment
duke
walter
ai
pace
besides
wage
producers
ot
collector
arc
hosts
interfaces
advertisers
moments
atlas
strings
dawn
representing
observation
feels
torture
carl
deleted
coat
mitchell
mrs
rica
restoration
convenience
returning
ralph
opposition
container
yr
defendant
warner
confirmation
app
embedded
inkjet
supervisor
wizard
corps
actors
liver
peripherals
liable
brochure
morris
bestsellers
petition
eminem
recall
antenna
picked
assumed
departure
minneapolis
belief
killing
bikini
memphis
shoulder
decor
lookup
texts
harvard
brokers
roy
ion
diameter
ottawa
doll
ic
podcast
seasons
peru
interactions
refine
bidder
singer
evans
herald
literacy
fails
aging
nike
intervention
fed
plugin
attraction
diving
invite
modification
alice
latinas
suppose
customized
reed
involve
moderate
terror
younger
thirty
mice
opposite
understood
rapidly
dealtime
ban
temp
intro
mercedes
zus
assurance
clerk
happening
vast
mills
outline
amendments
tramadol
holland
receives
jeans
metropolitan
compilation
verification
fonts
ent
odd
wrap
refers
mood
favor
veterans
quiz
mx
sigma
gr
attractive
xhtml
occasion
recordings
jefferson
victim
demands
sleeping
careful
ext
beam
gardening
obligations
arrive
orchestra
sunset
tracked
moreover
minimal
polyphonic
lottery
tops
framed
aside
outsourcing
licence
adjustable
allocation
michelle
essay
discipline
amy
ts
demonstrated
dialogue
identifying
alphabetical
camps
declared
dispatched
aaron
handheld
trace
disposal
shut
florists
packs
ge
installing
switches
romania
voluntary
ncaa
thou
consult
phd
greatly
blogging
mask
cycling
midnight
ng
commonly
pe
photographer
inform
turkish
coal
cry
messaging
pentium
quantum
murray
intent
tt
zoo
largely
pleasant
announce
constructed
additions
requiring
spoke
aka
arrow
engagement
sampling
rough
weird
tee
refinance
lion
inspired
holes
weddings
blade
suddenly
oxygen
cookie
meals
canyon
goto
meters
merely
calendars
arrangement
conclusions
passes
bibliography
pointer
compatibility
stretch
durham
furthermore
permits
cooperative
muslim
xl
neil
sleeve
netscape
cleaner
cricket
beef
feeding
stroke
township
rankings
measuring
cad
hats
robin
robinson
jacksonville
strap
headquarters
sharon
crowd
tcp
transfers
surf
olympic
transformation
remained
attachments
dv
dir
entities
customs
administrators
personality
rainbow
hook
roulette
decline
gloves
israeli
medicare
cord
skiing
cloud
facilitate
subscriber
valve
val
hewlett
explains
proceed
flickr
feelings
knife
jamaica
priorities
shelf
bookstore
timing
liked
parenting
adopt
denied
fotos
incredible
britney
freeware
donation
outer
crop
deaths
rivers
commonwealth
pharmaceutical
manhattan
tales
katrina
workforce
islam
nodes
tu
fy
thumbs
seeds
cited
lite
ghz
hub
targeted
organizational
skype
realized
twelve
founder
decade
gamecube
rr
dispute
portuguese
tired
titten
adverse
everywhere
excerpt
eng
steam
discharge
ef
drinks
ace
voices
acute
halloween
climbing
stood
sing
tons
perfume
carol
honest
albany
hazardous
restore
stack
methodology
somebody
sue
ep
housewares
reputation
resistant
democrats
recycling
hang
gbp
curve
creator
amber
qualifications
museums
coding
slideshow
tracker
variation
passage
transferred
trunk
hiking
lb
pierre
jelsoft
headset
photograph
oakland
colombia
waves
camel
distributor
lamps
underlying
hood
wrestling
suicide
archived
photoshop
jp
chi
bt
arabia
gathering
projection
juice
chase
mathematical
logical
sauce
fame
extract
specialized
diagnostic
panama
indianapolis
af
payable
corporations
courtesy
criticism
automobile
confidential
rfc
statutory
accommodations
athens
northeast
downloaded
judges
sl
seo
retired
isp
remarks
detected
decades
paintings
walked
arising
nissan
bracelet
ins
eggs
juvenile
injection
yorkshire
populations
protective
afraid
acoustic
railway
cassette
initially
indicator
pointed
hb
jpg
causing
mistake
norton
locked
eliminate
tc
fusion
mineral
sunglasses
ruby
steering
beads
fortune
preference
canvas
threshold
parish
claimed
screens
cemetery
planner
croatia
flows
stadium
venezuela
exploration
mins
fewer
sequences
coupon
nurses
ssl
stem
proxy
astronomy
lanka
opt
edwards
drew
contests
flu
translate
announces
mlb
costume
tagged
berkeley
voted
killer
bikes
gates
adjusted
rap
tune
bishop
pulled
corn
gp
shaped
compression
seasonal
establishing
farmer
counters
puts
constitutional
grew
perfectly
tin
slave
instantly
cultures
norfolk
coaching
examined
trek
encoding
litigation
submissions
oem
heroes
painted
lycos
ir
zdnet
broadcasting
horizontal
artwork
cosmetic
resulted
portrait
terrorist
informational
ethical
carriers
ecommerce
mobility
floral
builders
ties
struggle
schemes
suffering
neutral
fisher
rat
spears
prospective
bedding
ultimately
joining
heading
equally
artificial
bearing
spectacular
coordination
connector
brad
combo
seniors
worlds
guilty
affiliated
activation
naturally
haven
tablet
jury
dos
tail
subscribers
charm
lawn
violent
mitsubishi
underwear
basin
soup
potentially
ranch
constraints
crossing
inclusive
dimensional
cottage
drunk
considerable
crimes
resolved
mozilla
byte
toner
nose
latex
branches
anymore
oclc
delhi
holdings
alien
locator
selecting
processors
pantyhose
plc
broke
nepal
zimbabwe
difficulties
juan
complexity
msg
constantly
browsing
resolve
barcelona
presidential
documentary
cod
territories
melissa
moscow
thesis
thru
jews
nylon
palestinian
discs
rocky
bargains
frequent
trim
nigeria
ceiling
pixels
ensuring
hispanic
cv
cb
legislature
hospitality
gen
anybody
procurement
diamonds
espn
fleet
untitled
bunch
totals
marriott
singing
theoretical
afford
exercises
starring
referral
nhl
surveillance
optimal
quit
distinct
protocols
lung
highlight
substitute
inclusion
hopefully
brilliant
turner
sucking
cents
reuters
ti
fc
gel
todd
spoken
omega
evaluated
stayed
civic
assignments
fw
manuals
doug
sees
termination
watched
saver
thereof
grill
households
gs
redeem
rogers
grain
aaa
authentic
regime
wanna
wishes
bull
montgomery
architectural
louisville
depend
differ
macintosh
movements
ranging
monica
repairs
breath
amenities
virtually
cole
mart
candle
hanging
colored
authorization
tale
verified
lynn
formerly
projector
bp
situated
comparative
std
seeks
herbal
loving
strictly
routing
docs
stanley
psychological
surprised
retailer
vitamins
elegant
gains
renewal
vid
genealogy
opposed
deemed
scoring
expenditure
brooklyn
liverpool
sisters
critics
connectivity
spots
oo
algorithms
hacker
madrid
similarly
margin
coin
solely
fake
salon
collaborative
norman
fda
excluding
turbo
headed
voters
cure
madonna
commander
arch
ni
murphy
thinks
thats
suggestion
hdtv
soldier
phillips
asin
aimed
justin
bomb
harm
interval
mirrors
spotlight
tricks
reset
brush
investigate
thy
expansys
panels
repeated
assault
connecting
spare
logistics
deer
kodak
tongue
bowling
tri
danish
pal
monkey
proportion
filename
skirt
florence
invest
honey
um
analyzes
drawings
significance
scenario
ye
fs
lovers
atomic
approx
symposium
arabic
gauge
essentials
junction
protecting
nn
faced
mat
rachel
solving
transmitted
weekends
screenshots
produces
oven
ted
intensive
chains
kingston
sixth
engage
deviant
noon
switching
quoted
adapters
correspondence
farms
imports
supervision
cheat
bronze
expenditures
sandy
separation
testimony
suspect
celebrities
macro
sender
mandatory
boundaries
crucial
syndication
gym
celebration
kde
adjacent
filtering
tuition
spouse
exotic
viewer
signup
threats
luxembourg
puzzles
reaching
vb
damaged
cams
receptor
laugh
joel
surgical
destroy
citation
pitch
autos
yo
premises
perry
proved
offensive
imperial
dozen
benjamin
deployment
teeth
cloth
studying
colleagues
stamp
lotus
salmon
olympus
separated
proc
cargo
tan
directive
fx
salem
mate
dl
starter
upgrades
likes
butter
pepper
weapon
luggage
burden
chef
tapes
zones
races
isle
stylish
slim
maple
luke
grocery
offshore
governing
retailers
depot
kenneth
comp
alt
pie
blend
harrison
ls
julie
occasionally
cbs
attending
emission
pete
spec
finest
realty
janet
bow
penn
recruiting
apparent
instructional
phpbb
autumn
traveling
probe
midi
permissions
biotechnology
toilet
ranked
jackets
routes
packed
excited
outreach
helen
mounting
recover
tied
lopez
balanced
prescribed
catherine
timely
talked
upskirts
debug
delayed
chuck
reproduced
hon
dale
explicit
calculation
villas
ebook
consolidated
exclude
peeing
occasions
brooks
equations
newton
oils
sept
exceptional
anxiety
bingo
whilst
spatial
respondents
unto
lt
ceramic
prompt
precious
minds
annually
considerations
scanners
atm
xanax
eq
pays
fingers
sunny
ebooks
delivers
je
queensland
necklace
musicians
leeds
composite
unavailable
cedar
arranged
lang
theaters
advocacy
raleigh
stud
fold
essentially
designing
threaded
uv
qualify
blair
hopes
assessments
cms
mason
diagram
burns
pumps
footwear
sg
vic
beijing
peoples
victor
mario
pos
attach
licenses
utils
removing
advised
brunswick
spider
phys
ranges
pairs
sensitivity
trails
preservation
hudson
isolated
calgary
interim
assisted
divine
streaming
approve
chose
compound
intensity
technological
syndicate
abortion
dialog
venues
blast
wellness
calcium
newport
antivirus
addressing
pole
discounted
indians
shield
harvest
membrane
prague
previews
bangladesh
constitute
locally
concluded
pickup
desperate
mothers
nascar
iceland
demonstration
governmental
manufactured
candles
graduation
mega
bend
sailing
variations
moms
sacred
addiction
morocco
chrome
tommy
springfield
refused
brake
exterior
greeting
ecology
oliver
congo
glen
botswana
nav
delays
synthesis
olive
undefined
unemployment
cyber
verizon
scored
enhancement
newcastle
clone
dicks
velocity
lambda
relay
composed
tears
performances
oasis
baseline
cab
angry
fa
societies
silicon
brazilian
identical
petroleum
compete
ist
norwegian
lover
belong
honolulu
beatles
lips
retention
exchanges
pond
rolls
thomson
barnes
soundtrack
wondering
malta
daddy
lc
ferry
rabbit
profession
seating
dam
cnn
separately
physiology
lil
collecting
das
exports
omaha
tire
participant
scholarships
recreational
dominican
chad
electron
loads
friendship
heather
passport
motel
unions
treasury
warrant
sys
solaris
frozen
occupied
josh
royalty
scales
rally
observer
sunshine
strain
drag
ceremony
somehow
arrested
expanding
provincial
investigations
icq
ripe
yamaha
rely
medications
hebrew
gained
rochester
dying
laundry
stuck
solomon
placing
stops
homework
adjust
assessed
advertiser
enabling
encryption
filling
downloadable
sophisticated
imposed
silence
scsi
focuses
soviet
possession
cu
laboratories
treaty
vocal
trainer
organ
stronger
volumes
advances
vegetables
lemon
toxic
dns
thumbnails
darkness
pty
ws
nuts
nail
bizrate
vienna
implied
span
stanford
sox
stockings
joke
respondent
packing
statute
rejected
satisfy
destroyed
shelter
chapel
gamespot
manufacture
layers
wordpress
guided
vulnerability
accountability
celebrate
accredited
appliance
compressed
bahamas
powell
mixture
bench
univ
tub
rider
scheduling
radius
perspectives
mortality
logging
hampton
christians
borders
therapeutic
pads
butts
inns
bobby
impressive
sheep
accordingly
architect
railroad
lectures
challenging
wines
nursery
harder
cups
ash
microwave
cheapest
accidents
travesti
relocation
stuart
contributors
salvador
ali
salad
np
monroe
tender
violations
foam
temperatures
paste
clouds
competitions
discretion
tft
tanzania
preserve
jvc
poem
unsigned
staying
cosmetics
easter
theories
repository
praise
jeremy
venice
jo
concentrations
vibrators
estonia
christianity
veteran
streams
landing
signing
executed
katie
negotiations
realistic
dt
cgi
showcase
integral
asks
relax
namibia
generating
christina
congressional
synopsis
hardly
prairie
reunion
composer
bean
sword
absent
photographic
sells
ecuador
hoping
accessed
spirits
modifications
coral
pixel
float
colin
bias
imported
paths
bubble
por
acquire
contrary
millennium
tribune
vessel
acids
focusing
viruses
cheaper
admitted
dairy
admit
mem
fancy
equality
samoa
gc
achieving
tap
stickers
fisheries
exceptions
reactions
leasing
lauren
beliefs
ci
macromedia
companion
squad
analyze
ashley
scroll
relate
divisions
swim
wages
additionally
suffer
forests
fellowship
nano
invalid
concerts
martial
males
victorian
retain
execute
tunnel
genres
cambodia
patents
copyrights
yn
chaos
lithuania
mastercard
wheat
chronicles
obtaining
beaver
updating
distribute
readings
decorative
kijiji
confused
compiler
enlargement
eagles
bases
vii
accused
bee
campaigns
unity
loud
conjunction
bride
rats
defines
airports
instances
indigenous
begun
cfr
brunette
packets
anchor
socks
validation
parade
corruption
stat
trigger
incentives
cholesterol
gathered
essex
slovenia
notified
differential
beaches
folders
dramatic
surfaces
terrible
routers
cruz
pendant
dresses
baptist
scientist
starsmerchant
hiring
clocks
arthritis
bios
females
wallace
nevertheless
reflects
taxation
fever
pmc
cuisine
surely
practitioners
transcript
myspace
theorem
inflation
thee
nb
ruth
pray
stylus
compounds
pope
drums
contracting
arnold
structured
reasonably
jeep
chicks
bare
hung
cattle
mba
radical
graduates
rover
recommends
controlling
treasure
reload
distributors
flame
levitra
tanks
assuming
monetary
elderly
pit
arlington
mono
particles
floating
extraordinary
tile
indicating
bolivia
spell
hottest
stevens
coordinate
kuwait
exclusively
emily
alleged
limitation
widescreen
compile
squirting
webster
struck
rx
illustration
plymouth
warnings
construct
apps
inquiries
bridal
annex
mag
gsm
inspiration
tribal
curious
affecting
freight
rebate
meetup
eclipse
sudan
ddr
downloading
rec
shuttle
aggregate
stunning
cycles
affects
forecasts
detect
actively
ciao
ampland
knee
prep
pb
complicated
chem
fastest
butler
shopzilla
injured
decorating
payroll
cookbook
expressions
ton
courier
uploaded
shakespeare
hints
collapse
americas
connectors
twinks
unlikely
oe
gif
pros
conflicts
techno
beverage
tribute
wired
elvis
immune
latvia
travelers
forestry
barriers
cant
jd
rarely
gpl
infected
offerings
martha
genesis
barrier
argue
incorrect
trains
metals
bicycle
furnishings
letting
arise
guatemala
celtic
thereby
irc
jamie
particle
perception
minerals
advise
humidity
bottles
boxing
wy
dm
bangkok
renaissance
pathology
sara
bra
ordinance
hughes
photographers
infections
jeffrey
chess
operates
brisbane
configured
survive
oscar
festivals
menus
joan
possibilities
duck
reveal
canal
amino
phi
contributing
herbs
clinics
mls
cow
manitoba
analytical
missions
watson
lying
costumes
strict
dive
saddam
circulation
drill
offense
bryan
cet
protest
assumption
jerusalem
hobby
tries
transexuales
invention
nickname
fiji
technician
inline
executives
enquiries
washing
audi
staffing
cognitive
exploring
trick
enquiry
closure
raid
ppc
timber
volt
intense
div
playlist
registrar
showers
supporters
ruling
steady
dirt
statutes
withdrawal
myers
drops
predicted
wider
saskatchewan
jc
cancellation
plugins
enrolled
sensors
screw
ministers
publicly
hourly
blame
geneva
freebsd
veterinary
acer
prostores
reseller
dist
handed
suffered
intake
informal
relevance
incentive
butterfly
tucson
mechanics
heavily
swingers
fifty
headers
mistakes
numerical
ons
geek
uncle
defining
xnxx
counting
reflection
sink
accompanied
assure
invitation
devoted
princeton
jacob
sodium
randy
spirituality
hormone
meanwhile
proprietary
timothy
childrens
brick
grip
naval
thumbzilla
medieval
porcelain
avi
bridges
pichunter
captured
watt
thehun
decent
casting
dayton
translated
shortly
cameron
columnists
pins
carlos
reno
donna
andreas
warrior
diploma
cabin
innocent
scanning
ide
consensus
polo
valium
copying
rpg
delivering
cordless
patricia
horn
eddie
uganda
fired
journalism
pd
prot
trivia
adidas
perth
frog
grammar
intention
syria
disagree
klein
harvey
tires
logs
undertaken
tgp
hazard
retro
leo
livesex
statewide
semiconductor
gregory
episodes
boolean
circular
anger
diy
mainland
illustrations
suits
chances
interact
snap
happiness
arg
substantially
bizarre
glenn
ur
auckland
olympics
fruits
identifier
geo
worldsex
ribbon
calculations
doe
jpeg
conducting
startup
suzuki
trinidad
ati
kissing
wal
handy
swap
exempt
crops
reduces
accomplished
calculators
geometry
impression
abs
slovakia
flip
guild
correlation
gorgeous
capitol
sim
dishes
rna
barbados
chrysler
nervous
refuse
extends
fragrance
mcdonald
replica
plumbing
brussels
tribe
neighbors
trades
superb
buzz
transparent
nuke
rid
trinity
charleston
handled
legends
boom
calm
champions
floors
selections
projectors
inappropriate
exhaust
comparing
shanghai
speaks
burton
vocational
davidson
copied
scotia
farming
gibson
pharmacies
fork
troy
ln
roller
introducing
batch
organize
appreciated
alter
nicole
latino
ghana
edges
uc
mixing
handles
skilled
fitted
albuquerque
harmony
distinguished
asthma
projected
assumptions
shareholders
twins
developmental
rip
zope
regulated
triangle
amend
anticipated
oriental
reward
windsor
zambia
completing
gmbh
buf
ld
hydrogen
webshots
sprint
comparable
chick
advocate
sims
confusion
copyrighted
tray
inputs
warranties
genome
escorts
documented
thong
medal
paperbacks
coaches
vessels
walks
sol
keyboards
sage
knives
eco
vulnerable
arrange
artistic
bat
honors
booth
indie
reflected
unified
bones
breed
detector
ignored
polar
fallen
precise
sussex
respiratory
notifications
msgid
transexual
mainstream
invoice
evaluating
lip
subcommittee
sap
gather
suse
maternity
backed
alfred
colonial
mf
carey
motels
forming
embassy
cave
journalists
danny
rebecca
slight
proceeds
indirect
amongst
wool
foundations
msgstr
arrest
volleyball
mw
adipex
horizon
nu
deeply
toolbox
ict
marina
liabilities
prizes
bosnia
browsers
decreased
patio
dp
tolerance
surfing
creativity
lloyd
describing
optics
pursue
lightning
overcome
eyed
ou
quotations
grab
inspector
attract
brighton
beans
bookmarks
ellis
disable
snake
succeed
leonard
lending
oops
reminder
xi
searched
behavioral
riverside
bathrooms
plains
sku
ht
raymond
insights
abilities
initiated
sullivan
za
midwest
karaoke
trap
lonely
fool
ve
nonprofit
lancaster
suspended
hereby
observe
julia
containers
attitudes
karl
berry
collar
simultaneously
racial
integrate
bermuda
amanda
sociology
mobiles
screenshot
exhibitions
kelkoo
confident
retrieved
exhibits
officially
consortium
dies
terrace
bacteria
pts
replied
seafood
novels
rh
rrp
recipients
ought
delicious
traditions
fg
jail
safely
finite
kidney
periodically
fixes
sends
durable
mazda
allied
throws
moisture
hungarian
roster
referring
symantec
spencer
wichita
nasdaq
uruguay
ooo
hz
transform
timer
tablets
tuning
gotten
educators
tyler
futures
vegetable
verse
highs
humanities
independently
wanting
custody
scratch
launches
ipaq
alignment
masturbating
henderson
bk
britannica
comm
ellen
competitors
nhs
rocket
aye
bullet
towers
racks
lace
nasty
visibility
latitude
consciousness
ste
tumor
ugly
deposits
beverly
mistress
encounter
trustees
watts
duncan
reprints
hart
bernard
resolutions
ment
accessing
forty
tubes
attempted
col
midlands
priest
floyd
ronald
analysts
queue
dx
sk
trance
locale
nicholas
biol
yu
bundle
hammer
invasion
witnesses
runner
rows
administered
notion
sq
skins
mailed
oc
fujitsu
spelling
arctic
exams
rewards
beneath
strengthen
defend
aj
frederick
medicaid
treo
infrared
seventh
gods
une
welsh
belly
aggressive
tex
advertisements
quarters
stolen
cia
sublimedirectory
soonest
haiti
disturbed
determines
sculpture
poly
ears
dod
wp
fist
naturals
neo
motivation
lenders
pharmacology
fitting
fixtures
bloggers
mere
agrees
passengers
quantities
petersburg
consistently
powerpoint
cons
surplus
elder
sonic
obituaries
cheers
dig
taxi
punishment
appreciation
subsequently
om
belarus
nat
zoning
gravity
providence
thumb
restriction
incorporate
backgrounds
treasurer
guitars
essence
flooring
lightweight
ethiopia
tp
mighty
athletes
humanity
transcription
jm
holmes
complications
scholars
dpi
scripting
gis
remembered
galaxy
chester
snapshot
caring
loc
worn
synthetic
shaw
vp
segments
testament
expo
dominant
twist
specifics
itunes
stomach
partially
buried
cn
newbie
minimize
darwin
ranks
wilderness
debut
generations
tournaments
bradley
deny
anatomy
bali
judy
sponsorship
headphones
fraction
trio
proceeding
cube
defects
volkswagen
uncertainty
breakdown
milton
marker
reconstruction
subsidiary
strengths
clarity
rugs
sandra
adelaide
encouraging
furnished
monaco
settled
folding
emirates
terrorists
airfare
comparisons
beneficial
distributions
vaccine
belize
fate
viewpicture
promised
volvo
penny
robust
bookings
threatened
minolta
republicans
discusses
gui
porter
gras
jungle
ver
rn
responded
rim
abstracts
zen
ivory
alpine
dis
prediction
pharmaceuticals
andale
fabulous
remix
alias
thesaurus
individually
battlefield
literally
newer
kay
ecological
spice
oval
implies
cg
soma
ser
cooler
appraisal
consisting
maritime
periodic
submitting
overhead
ascii
prospect
shipment
breeding
citations
geographical
donor
mozambique
tension
href
benz
trash
shapes
wifi
tier
fwd
earl
manor
envelope
diane
homeland
disclaimers
championships
excluded
andrea
breeds
rapids
disco
sheffield
bailey
aus
endif
finishing
emotions
wellington
incoming
prospects
lexmark
cleaners
bulgarian
hwy
eternal
cashiers
guam
cite
aboriginal
remarkable
rotation
nam
preventing
productive
boulevard
eugene
ix
gdp
pig
metric
compliant
minus
penalties
bennett
imagination
hotmail
refurbished
joshua
armenia
varied
grande
closest
activated
actress
mess
conferencing
assign
armstrong
politicians
trackbacks
lit
accommodate
tigers
aurora
una
slides
milan
premiere
lender
villages
shade
chorus
christine
rhythm
digit
argued
dietary
symphony
clarke
sudden
accepting
precipitation
marilyn
lions
findlaw
ada
pools
tb
lyric
claire
isolation
speeds
sustained
matched
approximate
rope
carroll
rational
programmer
fighters
chambers
dump
greetings
inherited
warming
incomplete
vocals
chronicle
fountain
chubby
grave
legitimate
biographies
burner
yrs
foo
investigator
gba
plaintiff
finnish
gentle
bm
prisoners
deeper
muslims
hose
mediterranean
nightlife
footage
howto
worthy
reveals
architects
saints
entrepreneur
carries
sig
freelance
duo
excessive
devon
screensaver
helena
saves
regarded
valuation
unexpected
cigarette
fog
characteristic
marion
lobby
egyptian
tunisia
metallica
outlined
consequently
headline
treating
punch
appointments
str
gotta
cowboy
narrative
bahrain
enormous
karma
consist
betty
queens
academics
pubs
quantitative
shemales
lucas
screensavers
subdivision
tribes
vip
defeat
clicks
distinction
honduras
naughty
hazards
insured
harper
livestock
mardi
exemption
tenant
sustainability
cabinets
tattoo
shake
algebra
shadows
holly
formatting
silly
nutritional
yea
mercy
hartford
freely
marcus
sunrise
wrapping
mild
fur
nicaragua
weblogs
timeline
tar
belongs
rj
readily
affiliation
soc
fence
nudist
infinite
diana
ensures
relatives
lindsay
clan
legally
shame
satisfactory
revolutionary
bracelets
sync
civilian
telephony
mesa
fatal
remedy
realtors
breathing
briefly
thickness
adjustments
graphical
genius
discussing
aerospace
fighter
meaningful
flesh
retreat
adapted
barely
wherever
estates
rug
democrat
borough
maintains
failing
shortcuts
ka
retained
voyeurweb
pamela
andrews
marble
extending
jesse
specifies
hull
logitech
surrey
briefing
belkin
dem
accreditation
wav
blackberry
highland
meditation
modular
microphone
macedonia
combining
brandon
instrumental
giants
organizing
shed
balloon
moderators
winston
memo
ham
solved
tide
kazakhstan
hawaiian
standings
partition
invisible
gratuit
consoles
funk
fbi
qatar
magnet
translations
porsche
cayman
jaguar
reel
sheer
commodity
posing
kilometers
rp
bind
thanksgiving
rand
hopkins
urgent
guarantees
infants
gothic
cylinder
witch
buck
indication
eh
congratulations
tba
cohen
sie
usgs
puppy
kathy
acre
graphs
surround
cigarettes
revenge
expires
enemies
lows
controllers
aqua
chen
emma
consultancy
finances
accepts
enjoying
conventions
eva
patrol
smell
pest
hc
italiano
coordinates
rca
fp
carnival
roughly
sticker
promises
responding
reef
physically
divide
stakeholders
hydrocodone
gst
consecutive
cornell
satin
bon
deserve
attempting
mailto
promo
jj
representations
chan
worried
tunes
garbage
competing
combines
mas
beth
bradford
len
phrases
kai
peninsula
chelsea
boring
reynolds
dom
jill
accurately
speeches
reaches
schema
considers
sofa
catalogs
ministries
vacancies
quizzes
parliamentary
obj
prefix
lucia
savannah
barrel
typing
nerve
dans
planets
deficit
boulder
pointing
renew
coupled
viii
myanmar
metadata
harold
circuits
floppy
texture
handbags
jar
ev
somerset
incurred
acknowledge
thoroughly
antigua
nottingham
thunder
tent
caution
identifies
questionnaire
qualification
locks
modelling
namely
miniature
dept
hack
dare
euros
interstate
pirates
aerial
hawk
consequence
rebel
systematic
perceived
origins
hired
makeup
textile
lamb
madagascar
nathan
tobago
presenting
cos
troubleshooting
uzbekistan
indexes
pac
rl
erp
centuries
gl
magnitude
ui
richardson
hindu
dh
fragrances
vocabulary
licking
earthquake
vpn
fundraising
fcc
markers
weights
albania
geological
assessing
lasting
wicked
eds
introduces
kills
roommate
webcams
pushed
webmasters
ro
df
computational
acdbentity
participated
junk
handhelds
wax
lucy
answering
hans
impressed
slope
reggae
failures
poet
conspiracy
surname
theology
nails
evident
whats
rides
rehab
epic
saturn
organizer
nut
allergy
sake
twisted
combinations
preceding
merit
enzyme
cumulative
zshops
planes
edmonton
tackle
disks
condo
pokemon
amplifier
ambien
arbitrary
prominent
retrieve
lexington
vernon
sans
worldcat
titanium
irs
fairy
builds
contacted
shaft
lean
bye
cdt
recorders
occasional
leslie
casio
deutsche
ana
postings
innovations
kitty
postcards
dude
drain
monte
fires
algeria
blessed
luis
reviewing
cardiff
cornwall
favors
potato
panic
explicitly
sticks
leone
transsexual
ez
citizenship
excuse
reforms
basement
onion
strand
pf
sandwich
uw
lawsuit
alto
informative
girlfriend
bloomberg
cheque
hierarchy
influenced
banners
reject
eau
abandoned
bd
circles
italic
beats
merry
mil
scuba
gore
complement
cult
dash
passive
mauritius
valued
cage
checklist
bangbus
requesting
courage
verde
lauderdale
scenarios
gazette
hitachi
divx
extraction
batman
elevation
hearings
coleman
hugh
lap
utilization
beverages
calibration
jake
eval
efficiently
anaheim
ping
textbook
dried
entertaining
prerequisite
luther
frontier
settle
stopping
refugees
knights
hypothesis
palmer
medicines
flux
derby
sao
peaceful
altered
pontiac
regression
doctrine
scenic
trainers
muze
enhancements
renewable
intersection
passwords
sewing
consistency
collectors
conclude
munich
oman
celebs
gmc
propose
hh
azerbaijan
lighter
rage
adsl
uh
prix
astrology
advisors
pavilion
tactics
trusts
occurring
supplemental
travelling
talented
annie
pillow
induction
derek
precisely
shorter
harley
spreading
provinces
relying
finals
paraguay
steal
parcel
refined
fd
bo
fifteen
widespread
incidence
fears
predict
boutique
acrylic
rolled
tuner
avon
incidents
peterson
rays
asn
shannon
toddler
enhancing
flavor
alike
walt
homeless
horrible
hungry
metallic
acne
blocked
interference
warriors
palestine
listprice
libs
undo
cadillac
atmospheric
malawi
wm
pk
sagem
knowledgestorm
dana
halo
ppm
curtis
parental
referenced
strikes
lesser
publicity
marathon
ant
proposition
gays
pressing
gasoline
apt
dressed
scout
belfast
exec
dealt
niagara
inf
eos
warcraft
charms
catalyst
trader
bucks
allowance
vcr
denial
uri
designation
thrown
prepaid
raises
gem
duplicate
electro
criterion
badge
wrist
civilization
analyzed
vietnamese
heath
tremendous
ballot
lexus
varying
remedies
validity
trustee
maui
handjobs
weighted
angola
squirt
performs
plastics
realm
corrected
jenny
helmet
salaries
postcard
elephant
yemen
encountered
tsunami
scholar
nickel
internationally
surrounded
psi
buses
expedia
geology
pct
wb
creatures
coating
commented
wallet
cleared
smilies
vids
accomplish
boating
drainage
shakira
corners
broader
vegetarian
rouge
yeast
yale
newfoundland
sn
qld
pas
clearing
investigated
dk
ambassador
coated
intend
stephanie
contacting
vegetation
doom
findarticles
louise
kenny
specially
owen
routines
hitting
yukon
beings
bite
issn
aquatic
reliance
habits
striking
myth
infectious
podcasts
singh
gig
gilbert
sas
ferrari
continuity
brook
fu
outputs
phenomenon
ensemble
insulin
assured
biblical
weed
conscious
accent
mysimon
eleven
wives
ambient
utilize
mileage
oecd
prostate
adaptor
auburn
unlock
hyundai
pledge
vampire
angela
relates
nitrogen
xerox
dice
merger
softball
referrals
quad
dock
differently
firewire
mods
nextel
framing
musician
blocking
rwanda
sorts
integrating
vsnet
limiting
dispatch
revisions
papua
restored
hint
armor
riders
chargers
remark
dozens
varies
msie
reasoning
wn
liz
rendered
picking
charitable
guards
annotated
ccd
sv
convinced
openings
buys
burlington
replacing
researcher
watershed
councils
occupations
acknowledged
kruger
pockets
granny
pork
zu
equilibrium
viral
inquire
pipes
characterized
laden
aruba
cottages
realtor
merge
privilege
edgar
develops
qualifying
chassis
dubai
estimation
barn
pushing
llp
fleece
pediatric
boc
fare
dg
asus
pierce
allan
dressing
techrepublic
sperm
vg
bald
filme
craps
fuji
frost
leon
institutes
mold
dame
fo
sally
yacht
tracy
prefers
drilling
brochures
herb
tmp
alot
ate
breach
whale
traveller
appropriations
suspected
tomatoes
benchmark
beginners
instructors
highlighted
bedford
stationery
idle
mustang
unauthorized
clusters
antibody
competent
momentum
fin
wiring
io
pastor
mud
calvin
uni
shark
contributor
demonstrates
phases
grateful
emerald
gradually
laughing
grows
cliff
desirable
tract
ul
ballet
ol
journalist
abraham
js
bumper
afterwards
webpage
religions
garlic
hostels
shine
senegal
explosion
pn
banned
wendy
briefs
signatures
diffs
cove
mumbai
ozone
disciplines
casa
mu
daughters
conversations
radios
tariff
nvidia
opponent
pasta
simplified
muscles
serum
wrapped
swift
motherboard
runtime
inbox
focal
bibliographic
eden
distant
incl
champagne
ala
decimal
hq
deviation
superintendent
propecia
dip
nbc
samba
hostel
housewives
employ
mongolia
penguin
magical
influences
inspections
irrigation
miracle
manually
reprint
reid
wt
hydraulic
centered
robertson
flex
yearly
penetration
wound
belle
rosa
conviction
hash
omissions
writings
hamburg
lazy
mv
mpg
retrieval
qualities
cindy
fathers
carb
charging
cas
marvel
lined
cio
dow
prototype
importantly
rb
petite
apparatus
upc
terrain
dui
pens
explaining
yen
strips
gossip
rangers
nomination
empirical
mh
rotary
worm
dependence
discrete
beginner
boxed
lid
sexuality
polyester
cubic
deaf
commitments
suggesting
sapphire
kinase
skirts
mats
remainder
crawford
labeled
privileges
televisions
specializing
marking
commodities
pvc
serbia
sheriff
griffin
declined
guyana
spies
blah
mime
neighbor
motorcycles
elect
highways
thinkpad
concentrate
intimate
reproductive
preston
deadly
feof
bunny
chevy
molecules
rounds
longest
refrigerator
tions
intervals
sentences
dentists
usda
exclusion
workstation
holocaust
keen
flyer
peas
dosage
receivers
urls
disposition
variance
navigator
investigators
cameroon
baking
marijuana
adaptive
computed
needle
baths
enb
gg
cathedral
brakes
og
nirvana
ko
fairfield
owns
til
invision
sticky
destiny
generous
madness
emacs
climb
blowing
fascinating
landscapes
heated
lafayette
jackie
wto
computation
hay
cardiovascular
ww
sparc
cardiac
salvation
dover
adrian
predictions
accompanying
vatican
brutal
learners
gd
selective
arbitration
configuring
token
editorials
zinc
sacrifice
seekers
guru
isa
removable
convergence
yields
gibraltar
levy
suited
numeric
anthropology
skating
kinda
aberdeen
emperor
grad
malpractice
dylan
bras
belts
blacks
educated
rebates
reporters
burke
proudly
pix
necessity
rendering
mic
inserted
pulling
basename
kyle
obesity
curves
suburban
touring
clara
vertex
bw
hepatitis
nationally
tomato
andorra
waterproof
expired
mj
travels
flush
waiver
pale
specialties
hayes
humanitarian
invitations
functioning
delight
survivor
garcia
cingular
economies
alexandria
bacterial
moses
counted
undertake
declare
continuously
johns
valves
gaps
impaired
achievements
donors
tear
jewel
teddy
lf
convertible
ata
teaches
ventures
nil
bufing
stranger
tragedy
julian
nest
pam
dryer
painful
velvet
tribunal
ruled
nato
pensions
prayers
funky
secretariat
nowhere
cop
paragraphs
gale
joins
adolescent
nominations
wesley
dim
lately
cancelled
scary
mattress
mpegs
brunei
likewise
banana
introductory
slovak
cakes
stan
reservoir
occurrence
idol
mixer
remind
wc
worcester
sbjct
demographic
charming
mai
tooth
disciplinary
annoying
respected
stays
disclose
affair
drove
washer
upset
restrict
springer
beside
mines
portraits
rebound
logan
mentor
interpreted
evaluations
fought
baghdad
elimination
metres
hypothetical
immigrants
complimentary
helicopter
pencil
freeze
hk
performer
abu
titled
commissions
sphere
powerseller
moss
ratios
concord
graduated
endorsed
ty
surprising
walnut
lance
ladder
italia
unnecessary
dramatically
liberia
sherman
cork
maximize
cj
hansen
senators
workout
mali
yugoslavia
bleeding
characterization
colon
likelihood
lanes
purse
fundamentals
contamination
mtv
endangered
compromise
masturbation
optimize
stating
dome
caroline
leu
expiration
namespace
align
peripheral
bless
engaging
negotiation
crest
opponents
triumph
nominated
confidentiality
electoral
changelog
welding
deferred
alternatively
heel
alloy
condos
plots
polished
yang
gently
greensboro
tulsa
locking
casey
controversial
draws
fridge
blanket
bloom
qc
simpsons
lou
elliott
recovered
fraser
justify
upgrading
blades
pgp
loops
surge
frontpage
trauma
aw
tahoe
advert
possess
demanding
defensive
sip
flashers
subaru
forbidden
tf
vanilla
programmers
pj
monitored
installations
deutschland
picnic
souls
arrivals
spank
cw
practitioner
motivated
wr
dumb
smithsonian
hollow
vault
securely
examining
fioricet
groove
revelation
rg
pursuit
delegation
wires
bl
dictionaries
mails
backing
greenhouse
sleeps
vc
blake
transparency
dee
travis
wx
endless
figured
orbit
currencies
niger
bacon
survivors
positioning
heater
colony
cannon
circus
promoted
forbes
mae
moldova
mel
descending
paxil
spine
trout
enclosed
feat
temporarily
ntsc
cooked
thriller
transmit
apnic
fatty
gerald
pressed
frequencies
scanned
reflections
hunger
mariah
sic
municipality
usps
joyce
detective
surgeon
cement
experiencing
fireplace
endorsement
bg
planners
disputes
textiles
missile
intranet
closes
seq
psychiatry
persistent
deborah
conf
marco
assists
summaries
glow
gabriel
auditor
wma
aquarium
violin
prophet
cir
bracket
looksmart
isaac
oxide
oaks
magnificent
erik
colleague
naples
promptly
modems
adaptation
hu
harmful
paintball
prozac
sexually
enclosure
acm
dividend
newark
kw
paso
glucose
phantom
norm
playback
supervisors
westminster
turtle
ips
distances
absorption
treasures
dsc
warned
neural
ware
fossil
mia
hometown
badly
transcripts
apollo
wan
disappointed
persian
continually
communist
collectible
handmade
greene
entrepreneurs
robots
grenada
creations
jade
scoop
acquisitions
foul
keno
gtk
earning
mailman
sanyo
nested
biodiversity
excitement
somalia
movers
verbal
blink
presently
seas
carlo
workflow
mysterious
novelty
bryant
tiles
voyuer
librarian
subsidiaries
switched
stockholm
tamil
garmin
ru
pose
fuzzy
indonesian
grams
therapist
richards
mrna
budgets
toolkit
promising
relaxation
goat
render
carmen
ira
sen
thereafter
hardwood
erotica
temporal
sail
forge
commissioners
dense
dts
brave
forwarding
qt
awful
nightmare
airplane
reductions
southampton
istanbul
impose
organisms
sega
telescope
viewers
asbestos
portsmouth
cdna
meyer
enters
pod
savage
advancement
wu
harassment
willow
resumes
bolt
gage
throwing
existed
generators
lu
wagon
barbie
dat
soa
knock
urge
smtp
generates
potatoes
thorough
replication
inexpensive
kurt
receptors
peers
roland
optimum
neon
interventions
quilt
huntington
creature
ours
mounts
syracuse
internship
lone
refresh
aluminium
snowboard
beastality
webcast
michel
evanescence
subtle
coordinated
notre
shipments
maldives
stripes
firmware
antarctica
cope
shepherd
lm
canberra
cradle
chancellor
mambo
lime
kirk
flour
controversy
legendary
bool
sympathy
choir
avoiding
beautifully
blond
expects
cho
jumping
fabrics
antibodies
polymer
hygiene
wit
poultry
virtue
burst
examinations
surgeons
bouquet
immunology
promotes
mandate
wiley
departmental
bbs
spas
ind
corpus
johnston
terminology
gentleman
fibre
reproduce
convicted
shades
jets
indices
roommates
adware
qui
intl
threatening
spokesman
zoloft
activists
frankfurt
prisoner
daisy
halifax
encourages
ultram
cursor
assembled
earliest
donated
stuffed
restructuring
insects
terminals
crude
morrison
maiden
simulations
cz
sufficiently
examines
viking
myrtle
bored
cleanup
yarn
knit
conditional
mug
crossword
bother
budapest
conceptual
knitting
attacked
hl
bhutan
liechtenstein
mating
compute
redhead
arrives
translator
automobiles
tractor
allah
continent
ob
unwrap
fares
longitude
resist
challenged
telecharger
hoped
pike
safer
insertion
instrumentation
ids
hugo
wagner
constraint
groundwater
touched
strengthening
cologne
gzip
wishing
ranger
smallest
insulation
newman
marsh
ricky
ctrl
scared
theta
infringement
bent
laos
subjective
monsters
asylum
lightbox
robbie
stake
cocktail
outlets
swaziland
varieties
arbor
mediawiki
configurations
poison07070100000060000081A400000000000000000000000163C2D55500005E57000000000000000000000000000000000000003A00000000spectre-cli-3.0+git20230114.26c01a5/scripts/words.txt.adj25 all
27 new
28 more
33 home
36 about
42 free
45 one
46 other
60 out
62 any
65 only
70 here
80 online
83 first
92 some
96 like
103 back
104 top
109 just
110 over
116 two
119 world
121 next
122 used
126 last
127 most
144 such
146 available
150 after
151 best
153 then
155 good
157 well
162 public
164 high
166 through
168 each
174 very
183 many
185 said
188 set
189 under
190 general
195 full
208 great
209 united
211 real
214 international
215 center
217 must
221 made
224 off
233 right
236 local
242 national
246 posted
257 reserved
258 subject
263 long
264 based
269 even
270 black
272 special
278 much
279 sign
282 open
285 south
288 same
293 own
294 found
297 related
299 both
302 american
304 game
310 down
313 three
314 total
317 following
325 north
327 current
329 big
336 size
338 personal
347 white
349 small
363 level
364 digital
366 previous
370 old
372 main
383 another
389 still
391 quality
392 every
394 content
396 private
397 little
401 low
420 different
431 teen
433 stock
442 advanced
443 west
446 english
447 left
452 select
455 gay
460 live
461 large
475 model
480 human
484 required
485 second
486 hot
494 better
499 going
500 medical
516 looking
521 complete
525 financial
527 working
529 standard
533 mobile
534 less
545 legal
546 above
547 recent
550 side
553 red
557 social
559 august
568 key
570 young
572 important
574 few
575 east
577 single
584 additional
587 latest
595 hard
599 four
612 light
617 blue
620 easy
621 given
632 possible
634 professional
637 major
638 star
640 future
659 similar
660 garden
662 million
666 listed
672 net
673 popular
683 welcome
684 central
688 original
690 radio
693 color
694 self
696 away
707 least
713 sure
721 further
724 able
730 green
732 close
733 common
735 specific
736 several
737 gold
739 living
743 short
748 limited
749 powered
753 daily
755 past
756 natural
758 due
761 five
767 official
768 weather
771 average
772 done
773 technical
776 pro
780 direct
791 front
798 early
800 sound
802 present
805 ago
809 material
812 written
814 federal
817 final
818 adult
824 cheap
827 true
831 third
837 bad
838 individual
840 plus
845 together
848 fast
853 global
855 meet
856 far
857 economic
868 included
876 various
881 commercial
887 received
902 registered
903 clear
913 wide
918 taken
923 known
924 half
929 simple
930 quick
931 none
932 wireless
937 whole
938 annual
939 published
940 later
941 basic
944 corporate
950 active
959 enough
960 designed
972 higher
979 yellow
980 political
984 base
985 near
986 environmental
989 french
1002 summary
1004 mean
1010 european
1016 western
1021 overall
1022 bay
1031 port
1033 regional
1042 double
1051 electronic
1055 needed
1059 held
1064 effective
1075 lost
1079 cross
1083 silver
1086 inside
1088 mature
1096 certain
1098 executive
1099 running
1101 necessary
1102 union
1104 according
1109 particular
1110 fine
1117 six
1118 bush
1122 military
1123 rental
1126 british
1129 huge
1135 kind
1137 middle
1141 taking
1144 coming
1147 lesbian
1148 appropriate
1153 nice
1159 capital
1163 sent
1170 flash
1174 choice
1176 starting
1184 foreign
1186 outside
1203 wed
1205 super
1206 male
1208 custom
1212 located
1213 multiple
1214 asian
1218 industrial
1220 potential
1227 late
1229 featured
1232 female
1233 responsible
1239 primary
1250 friendly
1260 independent
1264 brown
1265 physical
1266 operating
1276 happy
1279 wanted
1280 developed
1282 safe
1283 unique
1285 prior
1288 ready
1290 animal
1295 regular
1296 secure
1303 christian
1304 round
1306 favorite
1309 master
1316 built
1319 cut
1320 worldwide
1325 larger
1326 anti
1335 strong
1348 excellent
1349 paid
1351 perfect
1355 classic
1360 express
1370 involved
1372 extra
1380 maximum
1383 existing
1385 selected
1390 beautiful
1391 warning
1396 forward
1399 significant
1405 useful
1416 iii
1422 trying
1425 considered
1430 joined
1435 valid
1437 modern
1438 senior
1442 grand
1448 canadian
1449 cool
1450 normal
1454 entire
1455 educational
1457 leading
1458 metal
1459 positive
1462 chinese
1467 abstract
1472 greater
1473 likely
1477 alternative
1485 pass
1498 academic
1500 completed
1503 indian
1508 expected
1512 pacific
1515 pop
1522 northern
1523 behind
1526 german
1534 outdoor
1535 deep
1537 otherwise
1542 reported
1547 mini
1561 modified
1565 internal
1568 born
1571 detailed
1572 japanese
1574 approved
1585 moving
1589 pretty
1593 southern
1604 medium
1606 interested
1616 defined
1623 virtual
1625 established
1629 remote
1630 dark
1632 external
1643 surface
1644 minimum
1645 visual
1651 manual
1655 increased
1657 fair
1658 civil
1660 understanding
1662 fixed
1663 wrong
1664 beginning
1680 fat
1683 electric
1690 dead
1692 unknown
1693 specified
1698 worth
1701 poor
1709 traditional
1713 creative
1736 interesting
1740 accepted
1741 former
1743 ten
1746 complex
1754 flat
1758 respective
1760 spanish
1769 helpful
1770 monthly
1771 critical
1773 musical
1779 australian
1781 chief
1784 bottom
1790 changed
1791 pet
1792 heard
1796 royal
1797 clean
1799 russian
1801 african
1804 relevant
1812 applied
1813 weekly
1816 described
1822 square
1825 advance
1831 lee
1835 correct
1842 firm
1843 seven
1844 older
1854 random
1872 giving
1873 chance
1882 automotive
1883 naked
1885 successful
1886 sold
1889 clinical
1893 lowest
1898 developing
1906 patient
1907 actual
1908 historical
1909 stone
1915 difficult
1916 scientific
1917 satellite
1918 fit
1922 amateur
1933 cultural
1936 oral
1940 functional
1944 closed
1947 pink
1951 graduate
1953 shot
1956 initial
1958 thinking
1961 sec
1965 waste
1966 minute
1969 optional
1971 cold
1984 motor
1992 continued
1994 mass
2008 driving
2013 wild
2018 chemical
2030 mid
2037 soft
2039 alone
2052 italian
2053 paperback
2056 supported
2059 rose
2066 joint
2067 premium
2069 fresh
2075 growing
2081 hearing
2082 eastern
2088 signed
2089 upper
2090 administrative
2091 serious
2092 prime
2102 informed
2110 urban
2112 sorted
2114 essential
2128 heavy
2129 covered
2133 integrated
2136 comprehensive
2137 expert
2138 universal
2141 solid
2146 orange
2151 rich
2154 marine
2174 intended
2186 magic
2188 smart
2192 latin
2196 certified
2199 rank
2206 interactive
2208 separate
2215 religious
2226 missing
2229 domestic
2232 moved
2236 mental
2239 extended
2243 sorry
2245 opening
2252 plastic
2255 placed
2257 counter
2263 camp
2270 native
2276 owned
2280 played
2289 equal
2291 matching
2298 assistant
2299 variable
2308 determined
2309 wholesale
2312 gone
2318 golden
2327 funny
2330 turned
2331 portable
2332 tried
2333 electrical
2334 applicable
2343 earlier
2346 classical
2349 dedicated
2359 nuclear
2362 signal
2363 criminal
2366 sexual
2368 powerful
2371 false
2379 identified
2386 stated
2393 decided
2409 graphic
2414 binding
2415 brief
2416 ended
2417 winning
2418 eight
2419 anonymous
2420 iron
2421 straight
2425 miscellaneous
2426 prepared
2427 void
2429 alert
2445 sweet
2454 associate
2463 changing
2480 whatever
2492 eligible
2501 billion
2514 broken
2516 frank
2521 residential
2530 editorial
2540 rural
2541 shared
2545 strategic
2549 acid
2552 forced
2553 compatible
2557 null
2558 zero
2579 secret
2584 negative
2594 healthy
2606 married
2607 protected
2609 sharing
2612 waiting
2614 beta
2616 gratis
2621 won
2625 contained
2639 postal
2645 combined
2646 ultimate
2648 representative
2651 minor
2655 noted
2658 reduced
2660 rare
2661 spent
2663 extreme
2672 removed
2680 dual
2689 static
2695 famous
2704 dynamic
2707 permanent
2713 practical
2717 exclusive
2725 regulation
2735 junior
2741 meaning
2743 secondary
2744 wonderful
2749 announced
2751 agreed
2760 instant
2762 automatic
2765 maintained
2767 increasing
2769 connected
2777 ahead
2781 utility
2795 proper
2796 distributed
2808 principal
2810 standing
2816 kept
2817 alpha
2827 beat
2832 smoking
2839 improved
2844 dress
2847 contemporary
2850 nearby
2876 slow
2882 hip
2885 nine
2896 interior
2897 revised
2900 optical
2902 relative
2903 amazing
2911 hidden
2912 reasonable
2914 serial
2917 broadband
2931 seeing
2932 proof
2936 bass
2937 prescription
2939 empty
2944 licensed
2954 ideal
2956 recorded
2959 finished
2977 fourth
2983 evil
2984 aware
2988 irish
2997 greatest
2999 concerned
3003 generic
3009 continuing
3010 mixed
3012 interracial
3015 competitive
3020 salt
3021 compact
3039 accurate
3047 greek
3052 raw
3054 walking
3056 smaller
3061 expressed
3064 sharp
3090 requested
3095 immediate
3098 spread
3101 agricultural
3103 supporting
3105 assigned
3114 virgin
3115 experienced
3117 raised
3120 directed
3122 searching
3123 sporting
3126 affected
3133 blonde
3142 organic
3155 equivalent
3167 advisory
3179 atlantic
3180 wet
3189 matt
3190 elementary
3192 speaking
3195 unlimited
3198 plain
3209 printable
3210 holy
3220 hardcover
3221 observed
3227 fiscal
3232 filled
3248 controlled
3252 dental
3255 ancient
3265 learned
3269 historic
3270 attached
3271 opened
3274 disabled
3275 authorized
3276 crazy
3277 upcoming
3286 adopted
3287 efficient
3289 linear
3296 edited
3297 constant
3300 jewish
3302 linked
3309 pure
3315 qualified
3319 treated
3323 classified
3330 warm
3331 neither
3338 consistent
3348 becoming
3350 objective
3365 volunteer
3366 tested
3367 rear
3368 democratic
3371 exact
3372 bound
3377 formal
3383 micro
3393 supreme
3405 regulatory
3419 continuous
3421 czech
3424 initiative
3425 novel
3430 ultra
3450 committed
3451 extensive
3452 affordable
3456 patent
3459 outstanding
3463 planned
3472 sterling
3478 gray
3483 charged
3484 broad
3491 swiss
3495 terminal
3500 liquid
3511 saving
3513 remaining
3516 twenty
3523 illegal
3531 premier
3540 downtown
3543 collected
3547 suitable
3562 scheduled
3564 typical
3571 chicken
3572 temporary
3579 dear
3580 sufficient
3584 catholic
3594 swimming
3599 solar
3605 reliable
3607 northwest
3612 unable
3629 twin
3640 ordered
3641 biological
3642 fighting
3647 romance
3650 split
3656 pregnant
3659 focused
3662 hollywood
3665 cellular
3672 spiritual
3683 cutting
3688 flexible
3689 loved
3691 numerous
3699 superior
3700 preferred
3701 saved
3702 paying
3705 intellectual
3707 granted
3711 comfortable
3712 magnetic
3719 massive
3721 employed
3722 bright
3727 formed
3728 piano
3734 experimental
3745 dependent
3746 mechanical
3752 banner
3761 implemented
3764 recognized
3774 rapid
3776 hairy
3780 reverse
3790 dutch
3791 sensitive
3799 concrete
3832 accessible
3833 accessory
3834 resident
3845 smooth
3853 narrow
3857 sitting
3863 legislative
3870 missed
3873 antique
3875 willing
3877 molecular
3878 acting
3888 hundred
3891 strange
3892 sustainable
3894 statistical
3900 grey
3901 parallel
3903 amended
3906 bold
3908 stable
3921 blank
3922 enhanced
3923 entitled
3924 severe
3926 stainless
3930 deluxe
3932 aged
3940 pursuant
3946 tight
3952 flying
3956 cute
3957 organized
3962 improving
3964 expensive
3970 plane
3986 marked
3987 driven
3988 measured
3993 innovative
3996 rubber
3998 closing
4000 thousand
4013 bone
4022 inner
4024 tutorial
4038 roman
4040 valuable
4046 busy
4048 fantastic
4060 stereo
4061 appointed
4065 tiny
4066 operational
4069 liberal
4077 corresponding
4091 proud
4092 excess
4099 giant
4110 usual
4122 ongoing
4123 worst
4125 betting
4136 dangerous
4138 exciting
4149 derived
4153 oriented
4163 quiet
4167 represented
4172 comic
4174 jet
4179 underground
4182 crack
4188 calculated
4196 incorporated
4201 apart
4203 blind
4205 mad
4210 gross
4220 familiar
4221 capable
4232 elected
4242 ethnic
4249 fifth
4252 vertical
4254 structural
4255 municipal
4257 prize
4261 absolute
4266 prohibited
4267 addressed
4277 ill
4281 lay
4285 dirty
4294 alive
4298 discovered
4301 genetic
4303 participating
4306 thin
4315 sick
4319 tropical
4320 collect
4325 nationwide
4328 shaved
4331 purple
4341 mutual
4351 everyday
4353 surrounding
4359 checked
4364 visible
4379 worse
4384 fundamental
4385 burning
4390 substantial
4392 progressive
4397 scottish
4402 impossible
4406 obvious
4416 foster
4418 passing
4441 okay
4447 awesome
4448 mexican
4449 confirmed
4456 desired
4471 korean
4472 healing
4485 funded
4490 subsequent
4491 pat
4493 rolling
4494 fell
4510 adequate
4532 stopped
4554 conservative
4559 abroad
4560 ebony
4563 drawn
4578 overseas
4589 mod
4594 rush
4608 partial
4614 ranking
4621 sublime
4624 acceptable
4625 satisfied
4626 glad
4631 trusted
4643 supposed
4644 ordinary
4652 southwest
4653 boss
4655 institutional
4658 knowing
4662 champion
4663 cloudy
4670 solo
4675 uniform
4678 tall
4705 forgotten
4710 athletic
4711 thermal
4713 vital
4714 telling
4716 coastal
4720 intelligent
4728 stupid
4735 regardless
4739 rising
4746 restricted
4747 republican
4749 lucky
4750 latter
4752 thick
4763 iraqi
4782 expanded
4783 casual
4784 grown
4785 polish
4786 lovely
4796 indoor
4798 armed
4826 exposed
4832 sealed
4833 thai
4836 loaded
4844 spanking
4850 chronic
4852 moral
4862 trained
4870 wooden
4872 tough
4874 incident
4887 emerging
4905 diverse
4911 sole
4924 laid
4928 weak
4934 divided
4938 wise
4959 pleased
4963 processed
4965 genuine
4966 raising
4970 hybrid
4973 intermediate
4974 emotional
4981 bigger
4986 overnight
4987 geographic
4991 saudi
4995 preliminary
4999 promotional
5006 romantic
5018 sad
5033 fluid
5040 hurt
5043 unlike
5052 proven
5058 studied
5061 quarterly
5070 cherry
5071 continental
5072 alternate
5083 automated
5089 convenient
5094 sized
5096 noticed
5098 silent
5099 literary
5113 analog
5114 facial
5117 dated
5123 mint
5128 elite
5133 swedish
5141 equipped
5143 encouraged
5148 acquired
5160 noble
5169 induced
5173 loose
5180 earned
5182 islamic
5184 southeast
5188 pending
5190 determining
5193 personalized
5198 teenage
5201 triple
5207 unusual
5215 routine
5219 conventional
5222 wearing
5224 sought
5226 mounted
5229 median
5233 occupational
5234 animated
5235 judicial
5245 engaged
5246 falling
5253 binary
5255 attended
5257 punk
5258 collective
5292 returning
5301 embedded
5304 wizard
5307 liver
5309 liable
5318 assumed
5322 killing
5348 aging
5363 moderate
5365 younger
5366 thirty
5368 opposite
5369 understood
5380 vast
5388 metropolitan
5393 odd
5403 attractive
5410 sleeping
5411 careful
5418 sunset
5419 tracked
5421 minimal
5422 polyphonic
5424 tops
5425 framed
5429 adjustable
5436 demonstrated
5439 alphabetical
5441 declared
5444 handheld
5447 shut
5454 voluntary
5469 turkish
5476 intent
5480 pleasant
5490 rough
5491 weird
5495 inspired
5514 stretch
5518 cooperative
5519 muslim
5544 olympic
5559 israeli
5579 liked
5584 incredible
5588 outer
5593 pharmaceutical
5605 lite
5609 organizational
5611 realized
5612 twelve
5618 portuguese
5619 tired
5621 adverse
5629 ace
5631 acute
5639 honest
5641 hazardous
5650 resistant
5657 amber
5679 underlying
5693 mathematical
5694 logical
5698 specialized
5699 diagnostic
5703 payable
5708 confidential
5710 statutory
5713 northeast
5718 retired
5721 detected
5730 juvenile
5734 protective
5735 afraid
5736 acoustic
5741 pointed
5751 mineral
5753 ruby
5771 fewer
5789 tagged
5795 adjusted
5802 shaped
5804 seasonal
5809 constitutional
5825 painted
5830 horizontal
5832 cosmetic
5836 informational
5837 ethical
5841 floral
5846 suffering
5847 neutral
5851 prospective
5857 artificial
5858 bearing
5859 spectacular
5866 guilty
5867 affiliated
5878 violent
5887 inclusive
5888 dimensional
5890 drunk
5891 considerable
5893 resolved
5904 alien
5910 broke
5921 presidential
5922 documentary
5923 cod
5931 palestinian
5933 rocky
5935 frequent
5936 trim
5941 hispanic
5951 fleet
5952 untitled
5956 singing
5957 theoretical
5960 starring
5964 optimal
5966 distinct
5970 substitute
5973 brilliant
5982 spoken
5986 civic
6003 authentic
6009 architectural
6015 ranging
6025 colored
6028 verified
6033 situated
6034 comparative
6037 herbal
6038 loving
6043 psychological
6044 surprised
6047 elegant
6052 opposed
6070 fake
6072 collaborative
6073 norman
6077 headed
6082 arch
6106 repeated
6109 spare
6116 danish
6124 honey
6133 atomic
6136 arabic
6140 protecting
6142 faced
6143 mat
6146 transmitted
6152 intensive
6155 sixth
6157 deviant
6167 bronze
6169 sandy
6172 suspect
6174 macro
6176 mandatory
6178 crucial
6183 adjacent
6187 exotic
6195 damaged
6200 surgical
6208 proved
6209 offensive
6210 imperial
6211 dozen
6220 salmon
6222 separated
6225 tan
6226 directive
6244 stylish
6245 slim
6249 offshore
6250 governing
6273 apparent
6274 instructional
6279 midi
6283 ranked
6286 packed
6287 excited
6292 tied
6294 balanced
6295 prescribed
6297 timely
6301 delayed
6306 explicit
6310 consolidated
6319 exceptional
6323 spatial
6327 ceramic
6328 prompt
6329 precious
6339 sunny
6347 composite
6348 unavailable
6350 arranged
6358 designing
6359 threaded
6382 advised
6392 isolated
6394 interim
6395 assisted
6396 divine
6397 streaming
6400 compound
6402 technological
6424 concluded
6426 desperate
6431 governmental
6432 manufactured
6440 sacred
6448 exterior
6458 olive
6459 undefined
6471 composed
6477 angry
6481 brazilian
6482 identical
6486 norwegian
6499 wondering
6519 recreational
6520 dominican
6533 frozen
6534 occupied
6547 provincial
6550 ripe
6554 hebrew
6557 dying
6559 stuck
6567 enabling
6571 sophisticated
6572 imposed
6576 soviet
6581 vocal
6589 toxic
6595 nuts
6605 respondent
6607 statute
6608 rejected
6610 destroyed
6617 guided
6621 accredited
6623 compressed
6639 therapeutic
6644 impressive
6650 challenging
6668 tender
6681 unsigned
6695 veteran
6699 executed
6702 realistic
6706 integral
6712 congressional
6720 absent
6721 photographic
6728 coral
6732 bias
6733 imported
6738 contrary
6750 fancy
6779 invalid
6781 martial
6783 victorian
6802 decorative
6804 confused
6809 vii
6814 loud
6821 indigenous
6824 brunette
6835 gathered
6839 differential
6842 dramatic
6844 terrible
6847 pendant
6880 structured
6884 bare
6888 radical
6892 controlling
6899 assuming
6900 monetary
6901 elderly
6904 mono
6906 floating
6907 extraordinary
6914 coordinate
6918 alleged
6922 squirting
6924 struck
6932 bridal
6937 tribal
6938 curious
6939 affecting
6949 aggregate
6950 stunning
6961 complicated
6966 injured
6980 unlikely
6988 wired
6990 immune
6999 infected
7005 incorrect
7013 celtic
7039 configured
7049 amino
7051 contributing
7057 analytical
7062 strict
7086 cognitive
7095 intense
7101 ruling
7102 steady
7103 dirt
7119 hourly
7120 blame
7123 veterinary
7128 handed
7131 informal
7139 fifty
7142 numerical
7151 accompanied
7154 devoted
7158 randy
7162 proprietary
7167 naval
7169 medieval
7177 decent
7192 innocent
7201 cordless
7206 fired
7225 retro
7228 statewide
7232 boolean
7233 circular
7245 bizarre
7265 handy
7267 exempt
7270 accomplished
7276 flip
7279 gorgeous
7286 nervous
7297 superb
7299 transparent
7304 handled
7307 calm
7312 inappropriate
7318 vocational
7322 farming
7332 appreciated
7335 latino
7341 skilled
7342 fitted
7345 distinguished
7347 projected
7351 developmental
7354 regulated
7357 anticipated
7358 oriental
7362 completing
7369 comparable
7374 copyrighted
7380 documented
7389 sage
7392 vulnerable
7394 artistic
7398 indie
7399 reflected
7400 unified
7404 ignored
7405 polar
7406 fallen
7407 precise
7409 respiratory
7422 backed
7424 colonial
7434 slight
7436 indirect
7455 decreased
7467 eyed
7486 behavioral
7501 lonely
7504 nonprofit
7506 suspended
7516 racial
7525 confident
7541 delicious
7546 finite
7551 durable
7553 allied
7556 hungarian
7579 wanting
7599 nasty
7605 ugly
7619 forty
7621 attempted
7646 mailed
7650 arctic
7660 infrared
7661 seventh
7664 welsh
7666 aggressive
7675 disturbed
7684 neo
7688 fitting
7691 mere
7699 surplus
7700 elder
7701 sonic
7717 incorporate
7723 lightweight
7726 mighty
7741 caring
7743 worn
7744 synthetic
7750 dominant
7756 buried
7784 subsidiary
7790 encouraging
7791 furnished
7793 settled
7794 folding
7799 beneficial
7808 robust
7810 threatened
7825 alpine
7830 fabulous
7839 ecological
7841 oval
7849 maritime
7850 periodic
7852 overhead
7856 breeding
7858 geographical
7888 incoming
7892 bulgarian
7894 eternal
7898 aboriginal
7899 remarkable
7903 productive
7909 metric
7910 compliant
7911 minus
7919 varied
7922 activated
7930 lit
7937 premiere
7946 dietary
7949 sudden
7950 accepting
7958 lyric
7962 sustained
7963 matched
7964 approximate
7967 rational
7973 inherited
7974 warming
7975 incomplete
7979 chubby
7980 grave
7981 legitimate
7989 finnish
7990 gentle
7996 mediterranean
8000 worthy
8007 freelance
8009 excessive
8016 unexpected
8019 characteristic
8022 egyptian
8025 outlined
8034 narrative
8036 enormous
8043 quantitative
8054 naughty
8056 insured
8070 silly
8071 nutritional
8077 sunrise
8079 mild
8092 infinite
8100 satisfactory
8101 revolutionary
8104 civilian
8107 fatal
8110 breathing
8114 graphical
8119 meaningful
8122 adapted
8130 failing
8133 retained
8150 highland
8152 modular
8157 instrumental
8160 shed
8166 solved
8169 hawaiian
8172 invisible
8184 sheer
8193 urgent
8196 gothic
8246 consecutive
8257 worried
8270 boring
8284 parliamentary
8298 coupled
8299 viii
8304 floppy
8324 miniature
8329 interstate
8331 aerial
8335 systematic
8336 perceived
8338 hired
8340 textile
8358 hindu
8370 geological
8372 lasting
8373 wicked
8383 computational
8390 answering
8392 impressed
8401 evident
8405 epic
8411 twisted
8413 preceding
8416 cumulative
8426 arbitrary
8427 prominent
8439 lean
8443 occasional
8457 blessed
8468 transsexual
8480 alto
8481 informative
8490 abandoned
8493 italic
8495 merry
8502 passive
8504 valued
8531 textbook
8532 dried
8533 entertaining
8534 prerequisite
8547 peaceful
8548 altered
8552 scenic
8556 renewable
8581 supplemental
8583 talented
8598 refined
8601 fifteen
8602 widespread
8608 rolled
8619 alike
8621 homeless
8622 horrible
8623 hungry
8624 metallic
8626 blocked
8634 atmospheric
8644 parental
8645 referenced
8647 lesser
8653 pressing
8655 apt
8656 dressed
8674 thrown
8675 prepaid
8678 duplicate
8684 analyzed
8685 vietnamese
8687 tremendous
8690 varying
8696 weighted
8702 corrected
8714 surrounded
8725 cleared
8746 coated
8763 aquatic
8766 striking
8768 infectious
8782 assured
8783 biblical
8785 conscious
8788 eleven
8790 ambient
8794 prostate
8796 auburn
8822 limiting
8835 reasoning
8840 charitable
8845 convinced
8854 acknowledged
8861 viral
8865 laden
8881 pediatric
8892 bald
8916 suspected
8924 idle
8926 unauthorized
8929 competent
8942 grateful
8945 laughing
8948 desirable
8966 banned
8982 opponent
8987 wrapped
8988 swift
8992 focal
8993 bibliographic
8995 distant
8999 decimal
9012 magical
9021 hydraulic
9022 centered
9025 yearly
9027 wound
9035 lazy
9046 lined
9052 petite
9064 empirical
9066 rotary
9069 discrete
9071 boxed
9075 cubic
9076 deaf
9079 sapphire
9085 labeled
9102 elect
9106 intimate
9107 reproductive
9109 deadly
9125 keen
9136 baking
9138 adaptive
9144 cathedral
9153 sticky
9155 generous
9160 fascinating
9162 heated
9168 cardiovascular
9171 cardiac
9176 accompanying
9178 brutal
9181 selective
9184 token
9191 removable
9196 suited
9197 numeric
9209 educated
9224 suburban
9233 waterproof
9234 expired
9237 flush
9239 pale
9242 humanitarian
9244 functioning
9251 bacterial
9260 impaired
9267 convertible
9275 julian
9279 painful
9280 velvet
9282 ruled
9286 funky
9293 adolescent
9296 dim
9298 cancelled
9299 scary
9305 introductory
9317 demographic
9318 charming
9321 disciplinary
9322 annoying
9323 respected
9329 upset
9338 interpreted
9344 hypothetical
9346 complimentary
9353 titled
9360 graduated
9363 surprising
9368 unnecessary
9389 endangered
9395 caroline
9400 peripheral
9402 engaging
9407 nominated
9409 electoral
9418 polished
9425 controversial
9428 blanket
9434 recovered
9448 demanding
9449 defensive
9453 forbidden
9455 vanilla
9467 motivated
9469 dumb
9471 hollow
9486 greenhouse
9494 endless
9495 figured
9501 positioning
9511 descending
9515 enclosed
9519 cooked
9523 fatty
9525 pressed
9550 persistent
9569 magnificent
9577 harmful
9588 phantom
9600 neural
9602 fossil
9608 wan
9609 disappointed
9610 persian
9612 communist
9613 collectible
9614 handmade
9620 jade
9623 foul
9634 verbal
9640 mysterious
9649 tamil
9653 fuzzy
9654 indonesian
9661 promising
9671 temporal
9675 dense
9677 brave
9680 awful
9697 savage
9718 thorough
9720 inexpensive
9725 optimum
9735 lone
9743 subtle
9744 coordinated
9762 legendary
9768 blond
9787 departmental
9805 threatening
9817 earliest
9819 stuffed
9823 crude
9825 maiden
9832 bored
9836 conditional
9841 conceptual
9855 continent
9873 touched
9885 scared
9888 bent
9890 subjective
07070100000061000081A400000000000000000000000163C2D555000014A5000000000000000000000000000000000000003A00000000spectre-cli-3.0+git20230114.26c01a5/scripts/words.txt.adv17 not
25 all
27 new
28 more
33 home
36 about
42 free
43 but
60 out
62 any
63 there
65 only
70 here
74 also
75 now
83 first
92 some
103 back
109 just
110 over
121 next
126 last
127 most
144 such
145 please
150 after
151 best
153 then
155 good
157 well
164 high
166 through
168 each
174 very
189 under
195 full
202 way
206 part
211 real
213 item
224 off
229 before
233 right
250 within
259 between
263 long
269 even
278 much
283 today
285 south
310 down
325 north
329 big
349 small
379 non
389 still
397 little
401 low
425 around
427 course
435 too
443 west
447 left
460 live
461 large
465 however
471 really
485 second
494 better
514 again
519 never
532 below
534 less
546 above
575 east
595 hard
609 always
612 light
620 easy
631 needs
635 yet
696 away
702 once
707 least
713 sure
721 further
729 already
732 close
743 short
753 daily
755 past
758 due
776 pro
780 direct
796 ever
798 early
804 either
805 ago
827 true
831 third
837 bad
843 usually
845 together
848 fast
856 far
862 often
870 though
899 currently
903 clear
913 wide
924 half
930 quick
931 none
937 whole
940 later
959 enough
961 along
985 near
1042 double
1048 soon
1053 across
1086 inside
1090 rather
1095 nothing
1110 fine
1152 actually
1186 outside
1205 super
1210 almost
1227 late
1299 therefore
1300 simply
1304 round
1311 recently
1312 probably
1360 express
1367 thus
1372 extra
1373 especially
1384 quite
1396 forward
1403 retail
1406 directly
1447 instead
1473 likely
1515 pop
1519 longer
1523 behind
1535 deep
1537 otherwise
1582 maybe
1589 pretty
1610 throughout
1657 fair
1663 wrong
1667 finally
1681 fully
1690 dead
1725 sometimes
1728 beyond
1754 flat
1770 monthly
1797 clean
1813 weekly
1822 square
1839 lots
1842 firm
1893 lowest
1894 highly
1913 perhaps
1927 particularly
1935 easily
1991 generally
2003 quickly
2013 wild
2037 soft
2039 alone
2069 fresh
2121 immediately
2128 heavy
2228 approximately
2257 counter
2265 automatically
2306 yesterday
2309 wholesale
2319 completely
2343 earlier
2368 powerful
2371 false
2421 straight
2442 clearly
2445 sweet
2460 frequently
2485 exactly
2491 nearly
2504 faster
2508 con
2616 gratis
2622 slightly
2721 originally
2777 ahead
2822 previously
2850 nearby
2863 outdoors
2869 certainly
2870 indeed
2876 slow
2888 mostly
2946 specifically
2969 yeah
2977 fourth
3064 sharp
3129 totally
3147 extremely
3164 anyway
3176 anywhere
3198 plain
3330 warm
3447 truly
3509 properly
3517 newly
3532 tomorrow
3540 downtown
3549 absolutely
3570 significantly
3578 tonight
3579 dear
3607 northwest
3630 forth
3657 twice
3670 normally
3692 relatively
3715 effectively
3722 bright
3728 piano
3837 possibly
3839 typically
3938 successfully
3945 primarily
3946 tight
3985 eventually
4011 bang
4044 forever
4123 worst
4142 unfortunately
4143 respectively
4163 quiet
4173 carefully
4179 underground
4201 apart
4211 strongly
4241 mainly
4277 ill
4306 thin
4320 collect
4325 nationwide
4327 definitely
4343 necessarily
4352 apparently
4373 obviously
4379 worse
4388 somewhat
4418 passing
4441 okay
4458 closer
4491 pat
4522 hence
4524 entirely
4538 closely
4541 seriously
4552 elsewhere
4559 abroad
4578 overseas
4597 nearest
4652 southwest
4666 personally
4669 plenty
4670 solo
4680 somewhere
4715 fairly
4735 regardless
4752 thick
4801 regularly
4816 partly
4943 correctly
4962 widely
4986 overnight
5021 slowly
5061 quarterly
5173 loose
5184 southeast
5212 increasingly
5217 basically
5232 herein
5267 besides
5368 opposite
5370 rapidly
5420 moreover
5426 aside
5459 greatly
5465 commonly
5479 largely
5490 rough
5499 suddenly
5506 merely
5516 furthermore
5622 everywhere
5713 northeast
5739 initially
5811 perfectly
5814 instantly
5853 ultimately
5856 equally
5869 naturally
5883 potentially
5900 anymore
5917 constantly
5923 cod
5972 hopefully
5995 thereof
6020 virtually
6030 formerly
6039 strictly
6066 similarly
6069 solely
6249 offshore
6261 occasionally
6297 timely
6329 precious
6331 annually
6357 essentially
6423 locally
6509 separately
6544 somehow
6646 accordingly
6714 hardly
6774 additionally
6814 loud
6858 nevertheless
6864 surely
6881 reasonably
6916 exclusively
6955 actively
6963 fastest
6997 rarely
7014 thereby
7102 steady
7118 publicly
7119 hourly
7137 heavily
7161 meanwhile
7177 decent
7181 shortly
7244 substantially
7447 deeply
7507 hereby
7515 simultaneously
7528 officially
7545 safely
7548 periodically
7578 independently
7653 beneath
7673 soonest
7696 consistently
7708 subsequently
7726 mighty
7755 partially
7832 alias
7834 individually
7836 literally
7852 overhead
7921 closest
8026 consequently
8072 yea
8075 freely
8087 readily
8098 legally
8111 briefly
8123 barely
8124 wherever
8184 sheer
8236 roughly
8241 physically
8246 consecutive
8274 accurately
8312 thoroughly
8323 namely
8465 explicitly
8528 efficiently
8588 precisely
8619 alike
8713 internationally
8755 specially
8811 differently
8944 gradually
8957 afterwards
9017 manually
9025 yearly
9050 importantly
9109 deadly
9115 longest
9200 kinda
9213 proudly
9230 nationally
9237 flush
9256 continuously
9288 nowhere
9297 lately
9303 likewise
9369 dramatically
9413 alternatively
9420 gently
9473 securely
9517 temporarily
9531 sic
9573 promptly
9580 sexually
9605 badly
9611 continually
9636 presently
9668 thereafter
9680 awful
9703 bolt
9767 beautifully
9817 earliest
9828 sufficiently
07070100000062000081A400000000000000000000000163C2D555000101CF000000000000000000000000000000000000003B00000000spectre-cli-3.0+git20230114.26c01a5/scripts/words.txt.noun20 are
26 have
28 more
32 will
33 home
34 can
38 page
41 search
42 free
45 one
49 information
50 time
52 site
55 may
59 news
60 out
61 use
63 there
64 see
69 contact
70 here
71 business
72 who
73 web
75 now
76 help
77 get
79 view
83 first
91 services
94 click
96 like
97 service
100 find
101 price
102 date
103 back
104 top
105 people
107 list
108 name
110 over
111 state
112 year
113 day
115 email
116 two
117 health
119 world
125 work
126 last
129 music
130 buy
131 data
132 make
135 product
136 system
137 post
139 city
141 add
142 policy
143 number
147 copyright
148 support
149 message
151 best
152 software
153 then
154 jan
155 good
156 video
157 well
160 info
162 public
164 high
165 school
169 links
171 review
172 years
173 order
175 privacy
176 book
178 company
180 read
181 group
182 need
184 user
188 set
190 general
191 research
192 university
193 january
194 mail
195 full
196 map
198 program
199 life
200 know
202 way
203 days
204 management
206 part
208 great
210 hotel
211 real
213 item
214 international
215 center
217 must
218 store
219 travel
222 development
223 report
225 member
226 details
227 line
228 terms
233 right
234 type
236 local
238 using
240 office
241 education
242 national
243 car
244 design
245 take
247 internet
248 address
249 community
252 area
253 want
254 phone
256 shipping
258 subject
260 forum
261 family
266 code
267 show
269 even
270 black
271 check
272 special
274 website
275 index
276 being
278 much
279 sign
280 file
281 link
282 open
283 today
284 technology
285 south
286 case
287 project
288 same
291 version
292 section
294 found
296 house
298 security
301 county
302 american
303 photo
304 game
306 power
307 while
308 care
309 network
310 down
311 computer
313 three
314 total
315 place
316 end
317 following
323 access
324 think
325 north
327 current
331 law
332 control
333 water
334 history
336 size
337 art
338 personal
341 guide
342 shop
343 directory
344 board
345 location
346 change
347 white
348 text
349 small
350 rating
351 rate
352 government
355 usa
356 return
359 shopping
360 account
361 times
363 level
365 profile
367 form
369 love
370 old
371 john
372 main
373 call
374 hours
375 image
376 department
377 title
378 description
382 insurance
386 property
387 class
389 still
390 money
391 quality
393 listing
394 content
395 country
396 private
397 little
398 visit
399 save
401 low
402 reply
403 customer
404 december
405 compare
408 college
409 value
410 article
411 york
412 man
413 card
417 food
418 source
419 author
421 press
424 sale
426 print
427 course
428 job
429 canada
430 process
431 teen
432 room
433 stock
434 training
436 credit
437 point
438 join
439 science
440 men
443 west
444 sales
445 look
446 english
447 left
448 team
449 estate
450 box
451 conditions
453 windows
455 gay
456 thread
457 week
458 category
459 note
461 large
462 gallery
463 table
464 register
466 june
467 october
468 november
469 market
470 library
472 action
473 start
474 series
475 model
477 air
478 industry
479 plan
480 human
483 yes
485 second
488 cost
489 movie
491 march
493 september
494 better
495 say
497 july
498 yahoo
499 going
500 medical
501 test
502 friend
503 come
504 dec
505 server
507 study
508 application
509 cart
510 staff
513 feedback
515 play
516 looking
518 april
522 street
523 topic
524 comment
526 things
527 working
529 standard
530 tax
531 person
533 mobile
536 blog
537 party
538 payment
539 equipment
541 student
542 let
546 above
547 recent
548 park
550 side
551 act
552 problem
553 red
554 give
555 memory
556 performance
557 social
559 august
560 quote
561 language
562 story
563 sell
565 experience
566 rates
568 key
569 body
570 young
571 america
573 field
574 few
575 east
576 paper
577 single
579 age
581 club
582 example
585 password
587 latest
589 road
590 gift
591 question
593 night
596 texas
597 oct
598 pay
599 four
600 poker
601 status
602 browse
603 issue
604 range
605 building
606 seller
607 court
608 february
610 result
611 audio
612 light
614 war
615 nov
616 offer
617 blue
621 given
623 event
624 release
625 analysis
626 request
627 fax
628 china
629 making
630 picture
632 possible
633 might
634 professional
636 month
637 major
638 star
640 future
641 space
642 committee
643 hand
644 sun
645 cards
647 london
648 washington
649 meeting
652 interest
654 child
655 keep
657 california
658 share
660 garden
662 million
664 reference
667 baby
668 learning
669 energy
670 run
671 delivery
672 net
674 term
675 film
677 put
679 journal
683 welcome
684 central
686 president
687 notice
688 original
689 head
690 radio
692 cell
693 color
694 self
695 council
698 track
699 australia
700 discussion
701 archive
704 entertainment
705 agreement
706 format
707 least
708 society
710 log
711 safety
714 faq
715 trade
716 edition
719 marketing
720 tell
723 association
727 david
728 fun
730 green
732 close
733 common
734 drive
735 specific
737 gold
738 feb
739 living
740 sep
741 collection
743 short
744 arts
745 lot
747 display
748 limited
751 means
752 director
753 daily
754 beach
755 past
756 natural
758 due
760 electronics
761 five
763 period
764 planning
765 database
767 official
768 weather
769 mar
770 land
771 average
773 technical
774 window
775 france
776 pro
777 region
778 island
779 record
782 conference
783 environment
786 district
787 calendar
788 costs
789 style
790 url
791 front
792 statement
793 update
794 parts
795 aug
800 sound
801 resource
802 present
806 document
807 word
808 works
809 material
810 bill
811 apr
813 talk
814 federal
817 final
818 adult
820 thing
821 centre
826 finance
827 true
828 minutes
830 mark
831 third
832 rock
834 europe
835 reading
837 bad
838 individual
840 plus
841 auto
842 cover
847 percent
848 fast
849 function
850 fact
851 unit
852 getting
854 tech
855 meet
856 far
859 player
865 germany
866 amount
867 watch
869 feel
871 bank
872 risk
873 thanks
877 words
878 linux
880 production
881 commercial
882 james
883 weight
884 town
885 heart
886 advertising
889 treatment
890 newsletter
891 archives
893 knowledge
894 magazine
895 error
896 camera
898 girl
900 construction
903 clear
904 golf
906 domain
908 chapter
910 protection
912 loan
914 beauty
915 manager
916 india
917 position
919 sort
922 michael
924 half
926 step
927 engineering
928 florida
929 simple
930 quick
931 none
932 wireless
933 license
934 paul
935 friday
936 lake
937 whole
938 annual
941 basic
945 google
946 church
947 method
948 purchase
950 active
951 response
952 practice
953 hardware
954 figure
956 fire
957 holiday
958 chat
959 enough
963 death
964 writing
965 speed
968 loss
969 face
970 brand
971 discount
973 effects
977 oil
978 bit
979 yellow
981 increase
983 kingdom
984 base
987 thought
988 stuff
989 french
990 storage
992 japan
995 shoes
996 entry
997 stay
998 nature
1000 availability
1001 africa
1002 summary
1003 turn
1004 mean
1005 growth
1007 agency
1008 king
1009 monday
1010 european
1011 activity
1012 copy
1014 drug
1016 western
1017 income
1018 force
1019 cash
1020 employment
1021 overall
1022 bay
1023 river
1024 commission
1026 package
1027 contents
1030 engine
1031 port
1032 album
1034 stop
1037 administration
1038 bar
1039 institute
1042 double
1043 dog
1044 build
1045 screen
1046 exchange
1056 season
1058 someone
1062 printer
1063 condition
1066 organization
1067 effect
1070 mind
1071 sunday
1072 selection
1073 casino
1075 lost
1076 tour
1077 menu
1078 volume
1079 cross
1081 mortgage
1082 hope
1083 silver
1084 corporation
1085 wish
1086 inside
1087 solution
1089 role
1092 addition
1094 supply
1095 nothing
1098 executive
1099 running
1100 lower
1101 necessary
1102 union
1103 jewelry
1106 clothing
1107 mon
1109 particular
1110 fine
1111 names
1112 robert
1113 homepage
1114 hour
1115 gas
1117 six
1118 bush
1120 advice
1121 career
1122 military
1123 rental
1124 decision
1125 leave
1126 british
1127 teens
1130 sat
1131 woman
1133 zip
1134 bid
1135 kind
1136 sellers
1137 middle
1138 move
1139 cable
1141 taking
1142 values
1143 division
1144 coming
1145 tuesday
1146 object
1147 lesbian
1149 machine
1150 logo
1151 length
1153 nice
1154 score
1155 statistics
1156 client
1159 capital
1161 sample
1162 investment
1163 sent
1165 saturday
1166 christmas
1167 england
1168 culture
1169 band
1170 flash
1172 lead
1173 george
1174 choice
1176 starting
1177 registration
1178 fri
1179 thursday
1181 consumer
1183 airport
1185 artist
1186 outside
1187 furniture
1189 channel
1190 letter
1191 mode
1194 wednesday
1195 structure
1196 fund
1197 summer
1199 degree
1200 contract
1201 button
1203 wed
1205 super
1206 male
1207 matter
1208 custom
1209 virginia
1213 multiple
1214 asian
1215 distribution
1216 editor
1217 inn
1219 cause
1220 potential
1221 song
1226 focus
1228 fall
1230 idea
1231 rooms
1232 female
1234 inc
1235 communications
1236 win
1238 thomas
1239 primary
1240 cancer
1241 numbers
1242 reason
1243 tool
1244 browser
1245 spring
1246 foundation
1247 answer
1248 voice
1250 friendly
1251 schedule
1253 communication
1254 purpose
1255 feature
1256 bed
1258 police
1260 independent
1262 approach
1264 brown
1267 hill
1269 medicine
1270 deal
1271 hold
1273 chicago
1275 glass
1278 smith
1282 safe
1284 survey
1285 prior
1286 telephone
1287 sport
1288 ready
1289 feed
1290 animal
1292 mexico
1293 population
1295 regular
1297 navigation
1298 operations
1301 evidence
1302 station
1303 christian
1304 round
1306 favorite
1308 option
1309 master
1310 valley
1315 sea
1318 blood
1319 cut
1322 connection
1323 publisher
1324 hall
1326 anti
1328 earth
1331 impact
1332 transfer
1333 introduction
1334 kitchen
1337 carolina
1338 wedding
1340 hospital
1341 ground
1342 overview
1343 ship
1344 accommodation
1346 disease
1350 italy
1351 perfect
1352 hair
1353 opportunity
1354 kit
1355 classic
1356 basis
1357 command
1360 express
1361 award
1362 distance
1363 tree
1364 peter
1365 assessment
1367 thus
1368 wall
1372 extra
1374 interface
1376 budget
1379 success
1380 maximum
1382 operation
1386 boy
1387 amazon
1391 warning
1392 wine
1394 horse
1395 vote
1396 forward
1402 owner
1403 retail
1407 manufacturer
1408 ways
1409 est
1410 son
1412 rule
1413 mac
1414 housing
1416 iii
1419 catalog
1421 max
1423 mother
1424 authority
1428 traffic
1429 programme
1431 input
1432 strategy
1434 agent
1436 bin
1437 modern
1438 senior
1439 ireland
1440 teaching
1441 door
1442 grand
1443 testing
1444 trial
1445 charge
1448 canadian
1449 cool
1450 normal
1452 enterprise
1454 entire
1457 leading
1458 metal
1459 positive
1461 fitness
1462 chinese
1463 opinion
1465 asia
1466 football
1467 abstract
1469 output
1470 funds
1477 alternative
1478 processing
1479 responsibility
1480 resolution
1481 java
1482 guest
1484 publication
1485 pass
1486 relations
1487 trust
1488 van
1490 session
1492 photography
1493 republic
1496 vacation
1497 century
1498 academic
1499 assistance
1501 skin
1502 graphics
1503 indian
1506 mary
1509 ring
1510 grade
1511 dating
1512 pacific
1513 mountain
1515 pop
1516 filter
1517 mailing
1518 vehicle
1519 longer
1522 northern
1523 behind
1524 panel
1525 floor
1526 german
1527 buying
1528 match
1530 default
1532 iraq
1535 deep
1536 morning
1539 rest
1540 protein
1541 plant
1543 hit
1544 transportation
1546 pool
1547 mini
1548 politics
1549 partner
1550 disclaimer
1552 boards
1553 faculty
1555 fish
1556 membership
1557 mission
1558 eye
1559 string
1560 sense
1562 pack
1564 stage
1568 born
1572 japanese
1573 race
1575 background
1576 target
1578 character
1580 maintenance
1581 ability
1592 spain
1596 winter
1597 battery
1598 youth
1599 pressure
1601 boston
1602 debt
1604 medium
1605 television
1607 core
1608 break
1612 dance
1613 wood
1617 papers
1618 playing
1620 fee
1621 studio
1622 reader
1624 device
1627 rent
1629 remote
1630 dark
1631 programming
1632 external
1633 apple
1636 instructions
1637 min
1639 theory
1641 remove
1642 aid
1643 surface
1644 minimum
1646 host
1647 variety
1650 martin
1651 manual
1652 block
1656 repair
1657 fair
1659 steel
1660 understanding
1663 wrong
1664 beginning
1665 hands
1670 desktop
1672 paris
1673 ohio
1675 sector
1676 capacity
1678 jersey
1680 fat
1682 father
1683 electric
1684 saw
1687 officer
1688 driver
1690 dead
1691 respect
1692 unknown
1694 restaurant
1695 mike
1696 trip
1698 worth
1701 poor
1702 teacher
1703 eyes
1704 relationship
1706 farm
1707 georgia
1708 peace
1710 campus
1711 tom
1712 showing
1714 coast
1715 benefit
1716 progress
1717 funding
1718 devices
1719 lord
1720 grant
1721 sub
1723 fiction
1731 led
1732 museum
1734 fan
1735 transport
1738 wife
1739 evaluation
1741 former
1742 implementation
1743 ten
1745 zone
1746 complex
1748 cat
1751 die
1753 jack
1754 flat
1755 flow
1757 literature
1759 parent
1760 spanish
1761 michigan
1762 columbia
1763 setting
1765 scale
1766 stand
1767 economy
1770 monthly
1772 frame
1773 musical
1774 definition
1775 secretary
1778 path
1779 australian
1780 employee
1781 chief
1784 bottom
1787 detail
1789 laws
1791 pet
1793 begin
1795 colorado
1796 royal
1797 clean
1798 switch
1799 russian
1801 african
1802 guy
1806 justice
1808 bible
1810 cup
1811 basket
1813 weekly
1815 installation
1817 demand
1819 suite
1822 square
1824 attention
1825 advance
1826 skip
1827 diet
1828 army
1829 auction
1830 gear
1831 lee
1833 difference
1836 charles
1837 nation
1838 selling
1839 lots
1840 piece
1841 sheet
1842 firm
1843 seven
1845 illinois
1847 elements
1848 species
1849 jump
1851 module
1852 resort
1853 facility
1855 pricing
1857 certificate
1858 minister
1859 motion
1861 fashion
1864 documentation
1865 monitor
1866 trading
1867 forest
1870 coverage
1871 couple
1872 giving
1873 chance
1874 vision
1875 ball
1876 ending
1884 goal
1887 wind
1890 situation
1895 publishing
1897 emergency
1898 developing
1900 currency
1901 leather
1903 temperature
1904 palm
1906 patient
1909 stone
1910 bob
1911 commerce
1917 satellite
1918 fit
1920 village
1922 amateur
1925 pain
1929 coffee
1932 buyer
1936 oral
1937 ford
1938 poster
1939 edge
1941 root
1946 ice
1947 pink
1948 zealand
1949 balance
1950 monitoring
1951 graduate
1953 shot
1955 architecture
1956 initial
1957 label
1958 thinking
1959 scott
1961 sec
1963 canon
1964 league
1965 waste
1966 minute
1967 bus
1968 provider
1970 dictionary
1971 cold
1972 accounting
1973 manufacturing
1975 chair
1976 fishing
1977 effort
1978 phase
1979 fields
1980 bag
1981 fantasy
1983 letters
1984 motor
1986 professor
1987 context
1989 shirt
1990 apparel
1993 foot
1994 mass
1995 crime
1996 count
1997 breast
2001 johnson
2006 religion
2007 claim
2008 driving
2009 permission
2010 surgery
2011 patch
2012 heat
2013 wild
2015 generation
2016 kansas
2017 miss
2018 chemical
2019 doctor
2020 task
2025 component
2027 exercise
2028 bug
2029 santa
2031 guarantee
2032 leader
2033 diamond
2034 israel
2042 jones
2043 arizona
2046 flight
2047 congress
2048 fuel
2050 walk
2052 italian
2053 paperback
2055 wait
2057 pocket
2058 saint
2059 rose
2060 freedom
2061 argument
2062 competition
2066 joint
2067 premium
2071 attorney
2072 upgrade
2074 factor
2075 growing
2078 stream
2080 pick
2081 hearing
2084 therapy
2089 upper
2092 prime
2094 limit
2096 louis
2097 steps
2106 creek
2109 quantity
2113 reporting
2114 essential
2116 tours
2117 platform
2118 load
2119 affiliate
2120 labor
2123 nursing
2124 defense
2128 heavy
2130 recovery
2134 configuration
2135 merchant
2136 comprehensive
2137 expert
2138 universal
2140 drop
2141 solid
2143 presentation
2146 orange
2147 compliance
2150 theme
2151 rich
2153 campaign
2154 marine
2155 improvement
2157 guitar
2158 finding
2159 pennsylvania
2161 ipod
2162 saying
2163 spirit
2166 challenge
2168 acceptance
2172 affairs
2173 touch
2178 hire
2179 election
2181 branch
2183 serve
2186 magic
2187 mount
2188 smart
2189 talking
2192 latin
2193 multimedia
2198 corner
2199 rank
2200 computing
2201 oregon
2202 element
2203 birth
2204 virus
2205 abuse
2208 separate
2209 quarter
2210 procedure
2211 leadership
2214 racing
2215 religious
2217 breakfast
2219 column
2221 faith
2222 chain
2223 developer
2225 avenue
2229 domestic
2233 houston
2234 reach
2235 comparison
2238 moment
2240 sequence
2241 inch
2242 attack
2245 opening
2246 damage
2247 lab
2248 reserve
2251 gamma
2252 plastic
2253 produce
2254 snow
2256 truth
2257 counter
2258 failure
2261 weekend
2262 dollar
2263 camp
2264 ontario
2266 des
2267 minnesota
2269 bridge
2270 native
2271 fill
2272 williams
2273 movement
2274 printing
2275 baseball
2277 approval
2278 draft
2279 chart
2283 jesus
2288 jackson
2289 equal
2290 adventure
2292 offering
2294 profit
2295 leaders
2298 assistant
2299 variable
2302 advertisement
2304 parking
2306 yesterday
2309 wholesale
2310 workshop
2311 russia
2315 extension
2316 seattle
2321 fort
2324 lighting
2325 senate
2327 funny
2328 brother
2329 gene
2331 portable
2335 disc
2337 pattern
2339 boat
2341 theatre
2342 laser
2345 sponsor
2346 classical
2347 icon
2348 warranty
2350 indiana
2351 direction
2353 basketball
2357 evening
2358 assembly
2361 mouse
2362 signal
2363 criminal
2365 brain
2367 wisconsin
2369 dream
2373 cast
2374 flower
2375 felt
2376 personnel
2380 falls
2381 pic
2382 soul
2383 aids
2388 hawaii
2391 carry
2392 flag
2398 advantage
2399 hello
2402 tourism
2403 priority
2407 savings
2409 graphic
2410 atom
2414 binding
2415 brief
2417 winning
2418 eight
2420 iron
2421 straight
2422 script
2427 void
2428 dining
2429 alert
2430 integration
2431 atlanta
2432 dakota
2433 tag
2434 interview
2435 mix
2436 framework
2437 disk
2439 queen
2441 credits
2443 fix
2444 handle
2445 sweet
2446 desk
2450 massachusetts
2453 vice
2454 associate
2456 truck
2457 behavior
2459 ray
2461 revenue
2462 measure
2466 duty
2469 bear
2470 gain
2471 festival
2472 laboratory
2473 ocean
2477 lack
2478 depth
2479 iowa
2482 laptop
2483 vintage
2484 train
2488 maryland
2489 spa
2490 concept
2493 checkout
2494 reality
2496 handling
2497 origin
2499 gaming
2501 billion
2502 destination
2503 scotland
2505 intelligence
2506 dallas
2508 con
2511 route
2516 frank
2517 alaska
2518 zoom
2519 blow
2520 battle
2522 anime
2526 protocol
2527 query
2528 clip
2529 partnership
2530 editorial
2532 expression
2534 equity
2535 provisions
2536 speech
2537 wire
2543 replacement
2544 tape
2546 judge
2547 spam
2548 economics
2549 acid
2551 cent
2554 fight
2555 apartment
2556 height
2557 null
2558 zero
2559 speaker
2562 netherlands
2566 recreation
2568 designer
2573 marriage
2574 roll
2575 korea
2576 banks
2579 secret
2580 bath
2582 kelly
2584 negative
2585 austin
2587 toronto
2588 theater
2590 missouri
2591 andrew
2592 var
2595 translation
2597 font
2598 assets
2599 injury
2601 joseph
2602 ministry
2604 lawyer
2606 married
2608 proposal
2609 sharing
2610 philadelphia
2611 portal
2612 waiting
2613 birthday
2614 beta
2617 banking
2621 won
2623 assist
2624 conduct
2626 lingerie
2627 legislation
2628 calling
2630 jazz
2631 serving
2634 miami
2638 doc
2641 tennessee
2642 wear
2644 breaking
2646 ultimate
2647 wales
2648 representative
2649 frequency
2651 minor
2652 finish
2657 mom
2659 physics
2663 extreme
2665 davis
2666 daniel
2667 bars
2669 row
2671 forecast
2674 singles
2675 administrator
2676 cycle
2679 accuracy
2681 rise
2683 sleep
2685 bird
2686 pharmacy
2687 brazil
2688 creation
2689 static
2690 scene
2691 hunter
2693 lady
2694 crystal
2696 writer
2697 chairman
2698 violence
2700 oklahoma
2702 drink
2703 academy
2704 dynamic
2705 gender
2707 permanent
2708 agriculture
2709 dell
2710 cleaning
2712 portfolio
2716 infrastructure
2717 exclusive
2718 seat
2720 vendor
2724 philosophy
2725 regulation
2727 reduction
2728 aim
2732 nutrition
2733 recording
2735 junior
2736 toll
2738 cape
2740 rings
2741 meaning
2742 tip
2743 secondary
2745 mine
2747 henry
2748 ticket
2750 guess
2752 prevention
2754 ski
2755 soccer
2756 math
2757 import
2758 posting
2759 presence
2760 instant
2762 automatic
2763 healthcare
2764 viewing
2768 majority
2770 christ
2776 austria
2778 moon
2779 participation
2780 scheme
2781 utility
2782 preview
2784 manner
2785 matrix
2787 combination
2789 amendment
2790 despite
2791 strength
2793 turkey
2798 singapore
2800 delta
2801 fear
2802 seeking
2804 phoenix
2806 convention
2808 principal
2809 daughter
2810 standing
2811 comfort
2812 colors
2814 cisco
2815 ordering
2817 alpha
2818 appeal
2819 cruise
2820 bonus
2821 certification
2824 bookmark
2827 beat
2828 disney
2829 household
2831 adobe
2832 smoking
2836 arms
2837 alabama
2838 tea
2844 dress
2845 subscription
2846 dealer
2847 contemporary
2849 utah
2851 rom
2854 exposure
2856 hide
2858 signature
2859 gambling
2861 miller
2862 provision
2863 outdoors
2864 clothes
2866 luxury
2871 newspaper
2872 toy
2873 circuit
2874 layer
2877 removal
2880 liability
2881 trademark
2882 hip
2885 nine
2887 kentucky
2890 spot
2891 taylor
2895 factory
2896 interior
2901 promotion
2902 relative
2904 clock
2905 dot
2906 hiv
2907 identity
2909 conversion
2910 feeling
2913 victoria
2914 serial
2915 relief
2916 revision
2918 influence
2919 ratio
2920 pda
2921 importance
2922 rain
2925 planet
2926 webmaster
2928 recipe
2930 permit
2931 seeing
2932 proof
2933 dna
2935 tennis
2936 bass
2937 prescription
2938 bedroom
2939 empty
2940 instance
2941 hole
2943 ride
2945 orlando
2948 bureau
2949 maine
2952 conservation
2953 pair
2954 ideal
2955 specs
2957 don
2960 parks
2961 dinner
2963 sydney
2964 stress
2965 cream
2974 louisiana
2977 fourth
2979 advisor
2981 marketplace
2983 evil
2985 wilson
2986 shape
2987 evolution
2988 irish
2991 stations
2995 remains
2996 acc
3000 euro
3001 operator
3003 generic
3004 encyclopedia
3005 usage
3006 cap
3007 ink
3011 census
3013 peak
3017 wheel
3018 transit
3020 salt
3021 compact
3022 poetry
3024 tracking
3025 angel
3026 bell
3027 keeping
3028 preparation
3029 attempt
3032 accordance
3033 width
3034 noise
3037 array
3040 stephen
3041 elizabeth
3042 climate
3044 pin
3046 alcohol
3047 greek
3048 instruction
3050 annotation
3051 sister
3052 raw
3054 walking
3059 gnu
3063 extent
3064 sharp
3066 ben
3067 lane
3068 paragraph
3069 kill
3070 mathematics
3072 compensation
3074 export
3076 aircraft
3078 sweden
3079 conflict
3082 employer
3084 percentage
3086 mississippi
3088 concern
3089 backup
3092 connecticut
3093 heritage
3096 holding
3097 trouble
3098 spread
3099 coach
3103 supporting
3104 audience
3106 jordan
3110 plug
3111 specialist
3112 cook
3113 affect
3114 virgin
3116 investigation
3118 hat
3119 institution
3124 helping
3128 bike
3130 plate
3133 blonde
3135 proceedings
3136 transmission
3137 anderson
3138 utc
3142 organic
3143 seek
3155 equivalent
3156 chemistry
3158 neighborhood
3159 nevada
3161 thailand
3163 agenda
3167 advisory
3168 cam
3169 curriculum
3170 logic
3171 template
3172 prince
3173 circle
3174 soil
3177 psychology
3179 atlantic
3180 wet
3181 circumstances
3182 edward
3183 investor
3184 identification
3185 ram
3186 leaving
3187 wildlife
3189 matt
3191 cooking
3192 speaking
3194 fox
3198 plain
3199 exit
3201 iran
3202 arm
3204 launch
3205 wave
3207 costa
3208 belgium
3210 holy
3211 acts
3212 guidance
3213 mesh
3214 trail
3215 enforcement
3216 symbol
3218 highway
3219 buddy
3220 hardcover
3222 dean
3223 setup
3224 poll
3225 booking
3226 glossary
3228 celebrity
3230 denver
3231 unix
3233 bond
3234 channels
3236 appendix
3238 blues
3239 chocolate
3240 pub
3241 portion
3242 scope
3243 hampshire
3244 supplier
3246 cotton
3249 requirement
3250 authorities
3251 biology
3252 dental
3254 border
3255 ancient
3256 debate
3259 pregnancy
3261 arkansas
3262 biography
3263 leisure
3266 transactions
3267 notebook
3268 explorer
3273 husband
3274 disabled
3276 crazy
3278 britain
3279 concert
3280 retirement
3281 scores
3282 financing
3283 efficiency
3285 comedy
3290 commitment
3291 specialty
3293 jean
3294 hop
3295 carrier
3297 constant
3298 visa
3299 mouth
3301 meter
3303 portland
3307 gun
3311 wonder
3313 fruit
3316 reform
3317 lens
3320 discovery
3321 draw
3323 classified
3326 confidence
3327 alliance
3332 lewis
3333 howard
3336 engineer
3337 lifestyle
3340 clearance
3342 inventory
3343 converter
3344 organisation
3345 babe
3349 safari
3350 objective
3352 sugar
3353 crew
3354 legs
3355 sam
3356 stick
3358 allen
3360 relation
3362 genre
3363 slide
3364 montana
3365 volunteer
3367 rear
3370 switzerland
3372 bound
3373 parameter
3374 adapter
3375 processor
3376 node
3377 formal
3380 lock
3381 hockey
3382 storm
3386 mile
3391 threads
3392 bowl
3395 recognition
3397 ref
3398 tank
3399 submission
3401 estimate
3403 navy
3404 kid
3406 inspection
3408 cancel
3410 territory
3411 transaction
3412 manchester
3414 paint
3415 delay
3416 pilot
3417 outlet
3421 czech
3423 cambridge
3424 initiative
3425 novel
3426 pan
3427 execution
3428 disability
3431 winner
3432 idaho
3433 contractor
3435 episode
3436 examination
3437 potter
3438 dish
3440 bulletin
3445 oxford
3446 adam
3449 painting
3453 universe
3454 candidate
3456 patent
3457 slot
3461 eating
3462 perspective
3464 watching
3465 lodge
3466 messenger
3467 mirror
3468 tournament
3469 consideration
3472 sterling
3473 sessions
3474 kernel
3475 stocks
3478 gray
3479 catalogue
3484 broad
3485 taiwan
3487 chosen
3488 demo
3489 greece
3491 swiss
3492 sarah
3493 clark
3494 hate
3495 terminal
3498 behalf
3499 caribbean
3500 liquid
3501 rice
3502 nebraska
3503 loop
3504 salary
3505 reservation
3507 gourmet
3508 guard
3510 orleans
3511 saving
3514 empire
3515 resume
3516 twenty
3518 raise
3520 avatar
3521 gary
3524 expansion
3527 rome
3528 arab
3529 lincoln
3531 premier
3532 tomorrow
3534 milk
3536 consent
3537 drama
3538 visiting
3539 performing
3540 downtown
3541 keyboard
3542 contest
3546 boot
3551 lunch
3552 audit
3553 push
3554 chamber
3555 guinea
3556 findings
3557 muscle
3560 implement
3563 polls
3565 tower
3567 sum
3569 calculator
3571 chicken
3572 temporary
3574 shower
3576 sending
3577 jason
3578 tonight
3579 dear
3582 shell
3583 province
3584 catholic
3585 oak
3586 vat
3587 awareness
3588 vancouver
3589 governor
3590 beer
3592 contribution
3593 measurement
3594 swimming
3595 spyware
3596 formula
3597 constitution
3598 packaging
3601 catch
3603 pakistan
3606 consultation
3607 northwest
3608 sir
3609 doubt
3611 finder
3614 classroom
3616 democracy
3619 wallpaper
3620 merchandise
3622 resistance
3627 memorial
3628 visitor
3629 twin
3630 forth
3631 insert
3632 baltimore
3633 gateway
3637 drawing
3639 charlotte
3642 fighting
3643 transition
3647 romance
3648 instrument
3649 bruce
3650 split
3653 heaven
3658 classification
3660 egypt
3661 physician
3662 hollywood
3663 bargain
3666 norway
3667 vermont
3668 asking
3672 spiritual
3673 hunting
3674 diabetes
3675 suit
3677 shift
3678 chip
3679 res
3683 cutting
3684 wow
3685 simon
3687 marks
3690 mapping
3694 satisfaction
3696 char
3698 pittsburgh
3699 superior
3703 cartoon
3705 intellectual
3706 moore
3709 carbon
3710 spending
3713 interaction
3714 listening
3716 registry
3717 crisis
3718 outlook
3720 denmark
3723 treat
3724 header
3726 poverty
3728 piano
3729 echo
3731 grid
3733 patrick
3736 revolution
3737 consolidation
3739 plasma
3741 earnings
3743 mystery
3744 landscape
3745 dependent
3747 journey
3748 delaware
3749 bidding
3752 banner
3753 applicant
3754 charter
3755 fig
3757 cooperation
3759 acquisition
3766 blogger
3767 notification
3771 teach
3774 rapid
3775 pull
3777 diversity
3778 cleveland
3780 reverse
3781 deposit
3782 seminar
3785 nasa
3789 accessibility
3790 dutch
3791 sensitive
3794 tab
3798 router
3799 concrete
3801 editing
3802 poland
3803 folder
3806 completion
3808 pulse
3810 technique
3813 voting
3819 detroit
3820 alexander
3821 broadcast
3823 metro
3825 anniversary
3827 strip
3828 specification
3829 pearl
3830 accident
3831 nick
3833 accessory
3834 resident
3835 plot
3838 airline
3840 representation
3841 regard
3842 pump
3845 smooth
3848 strike
3849 consumption
3850 birmingham
3851 flashing
3853 narrow
3854 afternoon
3855 threat
3857 sitting
3858 putting
3859 consultant
3860 controller
3861 ownership
3865 vietnam
3866 trailer
3867 anne
3868 castle
3871 malaysia
3873 antique
3875 willing
3878 acting
3881 exam
3882 logos
3883 residence
3887 density
3888 hundred
3893 philippines
3896 mention
3897 innovation
3900 grey
3901 parallel
3906 bold
3907 bathroom
3908 stable
3909 opera
3913 lesson
3914 cinema
3915 asset
3917 scan
3919 drinking
3920 reaction
3921 blank
3926 stainless
3931 humor
3932 aged
3934 exception
3936 duration
3937 bulk
3939 indonesia
3942 fabric
3950 contrast
3951 recommendation
3952 flying
3953 recruitment
3954 sin
3955 berlin
3959 para
3960 siemens
3961 adoption
3966 capture
3968 buffalo
3970 plane
3973 seed
3975 desire
3976 expertise
3977 mechanism
3978 camping
3980 jewellery
3982 welfare
3983 peer
3989 medline
3990 bottle
3994 marshall
3995 massage
3996 rubber
3997 conclusion
3998 closing
3999 tampa
4000 thousand
4001 meat
4002 legend
4003 grace
4007 adams
4008 python
4009 monster
4011 bang
4012 villa
4013 bone
4017 collaboration
4018 hamilton
4019 detection
4023 formation
4024 tutorial
4025 med
4027 entity
4029 gate
4030 holder
4032 moderator
4035 settlement
4036 portugal
4037 lawrence
4038 roman
4040 valuable
4041 tone
4043 ethics
4045 dragon
4047 captain
4051 heating
4052 leg
4053 neck
4055 wing
4057 purchasing
4059 abc
4060 stereo
4062 taste
4063 dealing
4067 rail
4069 liberal
4071 jay
4073 gap
4075 tube
4079 cache
4080 belt
4081 jacket
4082 determination
4083 animation
4084 oracle
4086 matthew
4087 lease
4089 aviation
4092 excess
4093 disaster
4094 console
4098 instructor
4099 giant
4103 seats
4105 biz
4106 alarm
4107 voltage
4108 anthony
4111 loading
4114 franklin
4115 angle
4117 vinyl
4119 mining
4121 melbourne
4123 worst
4124 imaging
4127 liberty
4128 wyoming
4129 blackjack
4130 argentina
4131 era
4132 convert
4133 possibility
4134 analyst
4135 commissioner
4137 garage
4139 reliability
4145 attachment
4147 finland
4148 morgan
4150 pleasure
4151 honor
4152 asp
4154 eagle
4156 pants
4157 columbus
4158 nurse
4159 prayer
4160 appointment
4162 hurricane
4163 quiet
4164 luck
4165 postage
4166 producer
4169 dial
4171 cheese
4172 comic
4174 jet
4175 productivity
4177 crown
4178 par
4179 underground
4180 diagnosis
4181 maker
4182 crack
4183 principle
4186 gang
4187 semester
4189 fetish
4192 appearance
4193 smoke
4194 apache
4198 craft
4199 cake
4202 fellow
4203 blind
4204 lounge
4206 algorithm
4207 semi
4210 gross
4212 cafe
4213 valentine
4215 ken
4217 horror
4220 familiar
4222 douglas
4224 till
4226 pen
4227 investing
4228 christopher
4229 admission
4231 shoe
4234 victory
4235 sand
4236 madison
4237 terrorism
4238 joy
4240 cpu
4242 ethnic
4244 parliament
4245 actor
4247 seal
4249 fifth
4251 citizen
4252 vertical
4253 corrections
4257 prize
4261 absolute
4265 substance
4269 pipe
4272 guardian
4273 lecture
4274 simulation
4275 layout
4277 ill
4278 concentration
4279 classics
4281 lay
4282 interpretation
4286 deck
4287 wayne
4290 bankruptcy
4292 worker
4293 optimization
4295 temple
4299 wings
4304 waters
4305 promise
4307 exhibition
4309 ridge
4310 cabinet
4311 modem
4312 harris
4314 bringing
4315 sick
4316 dose
4318 tiffany
4320 collect
4321 bet
4322 composition
4323 toyota
4326 vector
4329 turning
4330 buffer
4331 purple
4332 existence
4333 commentary
4338 immigration
4342 pipeline
4344 syntax
4346 attribute
4347 prison
4348 skill
4356 popularity
4357 inquiry
4358 ethernet
4360 exhibit
4361 throw
4362 trend
4363 sierra
4366 desert
4372 coordinator
4374 mercury
4376 handbook
4379 worse
4380 summit
4382 epa
4384 fundamental
4385 burning
4386 escape
4389 receiver
4392 progressive
4393 cialis
4396 glance
4397 scottish
4398 championship
4399 arcade
4400 richmond
4401 sacramento
4402 impossible
4403 ron
4404 russell
4407 fiber
4408 depression
4409 graph
4410 covering
4411 platinum
4412 judgment
4414 talks
4415 filing
4416 foster
4417 modeling
4418 passing
4422 tissue
4424 memorabilia
4425 clinton
4426 masters
4428 cartridge
4429 alberta
4430 explanation
4431 folk
4433 commons
4434 cincinnati
4435 subsection
4436 fraud
4437 electricity
4439 spectrum
4440 arrival
4441 okay
4442 pottery
4443 emphasis
4445 aspect
4446 workplace
4448 mexican
4454 crash
4455 lift
4458 closer
4460 heights
4461 shadow
4462 riding
4463 infection
4466 expense
4467 grove
4468 eligibility
4469 venture
4470 clinic
4471 korean
4472 healing
4473 princess
4474 mall
4475 entering
4476 packet
4477 spray
4479 involvement
4480 dad
4482 placement
4486 thompson
4489 roads
4491 pat
4492 dublin
4493 rolling
4494 fell
4495 motorcycle
4496 yard
4497 disclosure
4498 establishment
4500 nelson
4505 tourist
4507 mayor
4508 murder
4511 senator
4512 yield
4517 digest
4519 lodging
4521 dust
4526 radar
4527 rescue
4528 undergraduate
4529 losses
4530 combat
4531 reducing
4533 occupation
4539 radiation
4540 diary
4543 shooting
4544 kent
4547 ear
4550 baker
4553 pollution
4554 conservative
4556 shock
4557 effectiveness
4560 ebony
4561 tie
4562 ward
4564 arthur
4567 roof
4568 walker
4570 atmosphere
4572 kiss
4573 beast
4576 experiment
4580 dodge
4581 counsel
4582 federation
4583 pizza
4586 assignment
4589 mod
4594 rush
4595 ukraine
4596 absence
4598 cluster
4600 mpeg
4602 yoga
4604 woods
4605 surprise
4606 lamp
4607 rico
4608 partial
4613 nashville
4614 ranking
4618 ceo
4619 simpson
4622 counseling
4623 palace
4626 glad
4630 globe
4632 copper
4633 milwaukee
4634 rack
4635 medication
4636 warehouse
4637 shareware
4639 rep
4642 receipt
4644 ordinary
4645 nobody
4646 ghost
4647 violation
4649 stability
4650 mit
4652 southwest
4653 boss
4654 pride
4657 independence
4658 knowing
4659 reporter
4660 metabolism
4662 champion
4665 ross
4667 chile
4668 anna
4669 plenty
4670 solo
4671 sentence
4672 throat
4674 maria
4675 uniform
4676 excellence
4677 wealth
4678 tall
4680 somewhere
4681 vacuum
4682 dancing
4685 brass
4687 plaza
4690 survival
4691 quest
4694 screening
4695 toe
4696 thumbnail
4698 jonathan
4700 nova
4701 lifetime
4703 pioneer
4704 booty
4706 acrobat
4708 acres
4709 venue
4711 thermal
4714 telling
4719 charity
4721 edinburgh
4725 obligation
4726 campbell
4727 wake
4728 stupid
4729 harbor
4730 hungary
4731 traveler
4733 segment
4736 lan
4737 enemy
4738 puzzle
4739 rising
4740 aluminum
4741 wells
4744 insight
4747 republican
4750 latter
4752 thick
4754 repeat
4755 syndrome
4757 attendance
4758 penalty
4759 drum
4760 glasses
4762 nec
4763 iraqi
4764 builder
4765 vista
4767 chips
4768 terry
4769 flood
4771 ease
4773 amsterdam
4774 arena
4777 stewart
4778 announcement
4780 outcome
4785 polish
4786 lovely
4790 jerry
4791 clause
4792 smile
4795 troops
4797 bulgaria
4799 broker
4800 charger
4803 pine
4804 cooling
4806 gulf
4808 rick
4812 divorce
4814 shopper
4815 tokyo
4819 tradition
4820 candy
4822 tiger
4824 folks
4825 sensor
4827 telecom
4828 hunt
4830 deputy
4833 thai
4838 complaint
4841 afghanistan
4843 boost
4844 spanking
4845 scholarship
4846 governance
4847 mill
4852 moral
4853 den
4854 catering
4856 finger
4858 pound
4860 camcorder
4863 burn
4868 bread
4869 tobacco
4872 tough
4873 roberts
4874 incident
4876 dynamics
4877 lie
4880 conversation
4881 decrease
4883 chest
4884 pension
4885 billy
4888 worship
4889 capability
4896 precision
4897 damages
4898 reserves
4901 shorts
4902 reproduction
4903 minority
4906 amp
4910 johnny
4911 sole
4912 franchise
4913 recorder
4915 facing
4917 nancy
4920 passion
4921 rehabilitation
4923 sight
4925 clay
4926 defence
4929 refund
4936 reception
4937 amd
4938 wise
4940 cyprus
4942 odds
4944 insider
4948 hearts
4949 geography
4950 appearing
4951 integrity
4952 worry
4954 discrimination
4955 eve
4956 carter
4957 legacy
4958 marc
4960 danger
4961 vitamin
4964 phrase
4966 raising
4968 functionality
4969 paradise
4970 hybrid
4973 intermediate
4976 leaf
4977 pad
4978 glory
4982 billing
4983 diesel
4985 combine
4990 rod
4991 saudi
4992 fault
4993 cuba
4995 preliminary
4998 silk
5004 karen
5006 romantic
5009 generator
5010 albert
5012 jimmy
5013 graham
5014 suspension
5015 bristol
5019 correction
5020 wolf
5022 authentication
5024 rugby
5025 supplement
5029 infant
5032 samuel
5033 fluid
5034 grounds
5036 kick
5038 meal
5040 hurt
5041 machinery
5042 bandwidth
5044 equation
5046 probability
5047 pot
5048 dimension
5049 wright
5056 warren
5057 slip
5059 reviewer
5061 quarterly
5063 profits
5064 devil
5065 grass
5068 florist
5070 cherry
5072 alternate
5074 achievement
5076 kenya
5077 webcam
5079 funeral
5085 pee
5087 quebec
5088 passenger
5091 mars
5095 manga
5097 socket
5100 egg
5104 orientation
5105 pill
5106 theft
5107 childhood
5108 swing
5110 lat
5112 humans
5113 analog
5114 facial
5116 talent
5118 flexibility
5119 seeker
5120 wisdom
5121 shoot
5122 boundary
5123 mint
5125 offset
5126 payday
5127 philip
5128 elite
5130 spin
5133 swedish
5135 deadline
5136 jurisdiction
5137 robot
5139 witness
5140 collins
5144 sur
5146 powder
5147 broadway
5150 wash
5153 entrance
5154 gnome
5155 roots
5156 declaration
5160 noble
5161 glasgow
5162 automation
5164 rev
5165 gospel
5167 shore
5171 knight
5175 recipient
5181 illness
5183 athletics
5184 southeast
5189 parker
5191 lebanon
5192 corp
5194 kennedy
5197 conditioning
5199 soap
5201 triple
5202 cooper
5205 jam
5210 destruction
5213 migration
5214 disorder
5215 routine
5222 wearing
5223 axis
5227 habitat
5228 firewall
5229 median
5231 scanner
5236 rio
5238 adjustment
5239 hero
5240 integer
5242 bachelor
5243 attitude
5247 basics
5248 montreal
5249 carpet
5253 binary
5254 genetics
5256 difficulty
5257 punk
5258 collective
5259 coalition
5262 enrollment
5263 duke
5264 walter
5266 pace
5268 wage
5271 collector
5272 arc
5277 atlas
5278 strings
5279 dawn
5281 observation
5283 torture
5286 coat
5287 mitchell
5290 restoration
5291 convenience
5294 opposition
5295 container
5297 defendant
5298 warner
5299 confirmation
5303 supervisor
5304 wizard
5305 corps
5307 liver
5310 brochure
5311 morris
5313 petition
5315 recall
5316 antenna
5319 departure
5320 minneapolis
5321 belief
5322 killing
5323 bikini
5324 memphis
5325 shoulder
5326 decor
5327 lookup
5329 harvard
5332 ion
5333 diameter
5334 ottawa
5335 doll
5339 peru
5342 bidder
5343 singer
5344 evans
5345 herald
5346 literacy
5348 aging
5349 nike
5350 intervention
5351 fed
5353 attraction
5354 diving
5355 invite
5356 modification
5361 reed
5363 moderate
5364 terror
5366 thirty
5368 opposite
5372 ban
5373 temp
5374 intro
5377 assurance
5378 clerk
5379 happening
5381 mills
5382 outline
5385 holland
5387 jeans
5388 metropolitan
5389 compilation
5390 verification
5394 wrap
5396 mood
5397 favor
5399 quiz
5401 sigma
5405 occasion
5407 jefferson
5408 victim
5410 sleeping
5413 beam
5414 gardening
5417 orchestra
5418 sunset
5423 lottery
5426 aside
5428 licence
5430 allocation
5432 essay
5433 discipline
5437 dialogue
5443 aaron
5445 trace
5446 disposal
5451 installing
5453 romania
5454 voluntary
5456 thou
5461 mask
5462 cycling
5463 midnight
5467 photographer
5469 turkish
5470 coal
5472 messaging
5474 quantum
5475 murray
5476 intent
5478 zoo
5485 spoke
5487 arrow
5488 engagement
5489 sampling
5490 rough
5491 weird
5492 tee
5494 lion
5498 blade
5500 oxygen
5501 cookie
5503 canyon
5508 arrangement
5511 bibliography
5512 pointer
5513 compatibility
5514 stretch
5515 durham
5518 cooperative
5519 muslim
5522 sleeve
5523 netscape
5524 cleaner
5525 cricket
5526 beef
5527 feeding
5528 stroke
5529 township
5531 measuring
5532 cad
5534 robin
5535 robinson
5536 jacksonville
5537 strap
5538 headquarters
5540 crowd
5543 surf
5545 transformation
5551 customs
5553 personality
5554 rainbow
5555 hook
5556 roulette
5557 decline
5559 israeli
5560 medicare
5561 cord
5562 skiing
5563 cloud
5565 subscriber
5566 valve
5572 feelings
5573 knife
5574 jamaica
5576 shelf
5577 bookstore
5578 timing
5586 freeware
5587 donation
5589 crop
5592 commonwealth
5593 pharmaceutical
5594 manhattan
5597 workforce
5598 islam
5607 hub
5612 twelve
5613 founder
5614 decade
5617 dispute
5618 portuguese
5623 excerpt
5625 steam
5626 discharge
5629 ace
5631 acute
5632 halloween
5633 climbing
5636 tons
5637 perfume
5638 carol
5640 albany
5643 stack
5644 methodology
5645 somebody
5646 sue
5649 reputation
5652 recycling
5653 hang
5655 curve
5656 creator
5657 amber
5660 coding
5662 tracker
5663 variation
5664 passage
5666 trunk
5667 hiking
5669 pierre
5671 headset
5672 photograph
5673 oakland
5674 colombia
5676 camel
5677 distributor
5680 hood
5681 wrestling
5682 suicide
5686 chi
5688 arabia
5689 gathering
5690 projection
5691 juice
5692 chase
5695 sauce
5696 fame
5697 extract
5700 panama
5701 indianapolis
5703 payable
5705 courtesy
5706 criticism
5707 automobile
5712 athens
5713 northeast
5715 judges
5726 nissan
5727 bracelet
5728 ins
5729 eggs
5730 juvenile
5731 injection
5732 yorkshire
5736 acoustic
5737 railway
5738 cassette
5740 indicator
5744 causing
5745 mistake
5750 fusion
5751 mineral
5752 sunglasses
5753 ruby
5754 steering
5755 beads
5756 fortune
5757 preference
5758 canvas
5759 threshold
5760 parish
5763 cemetery
5764 planner
5765 croatia
5767 stadium
5768 venezuela
5769 exploration
5773 coupon
5776 stem
5777 proxy
5778 astronomy
5781 edwards
5782 drew
5784 flu
5788 costume
5790 berkeley
5792 killer
5794 gates
5796 rap
5797 tune
5798 bishop
5800 corn
5803 compression
5804 seasonal
5806 farmer
5809 constitutional
5812 tin
5813 slave
5816 norfolk
5817 coaching
5819 trek
5820 encoding
5821 litigation
5829 broadcasting
5830 horizontal
5831 artwork
5832 cosmetic
5834 portrait
5835 terrorist
5840 mobility
5844 struggle
5846 suffering
5847 neutral
5848 fisher
5849 rat
5852 bedding
5854 joining
5855 heading
5858 bearing
5859 spectacular
5860 coordination
5861 connector
5862 brad
5863 combo
5868 activation
5870 haven
5871 tablet
5872 jury
5873 dos
5874 tail
5876 charm
5877 lawn
5880 underwear
5881 basin
5882 soup
5884 ranch
5886 crossing
5889 cottage
5890 drunk
5895 byte
5896 toner
5897 nose
5898 latex
5902 delhi
5904 alien
5905 locator
5908 pantyhose
5911 nepal
5912 zimbabwe
5915 complexity
5918 browsing
5919 resolve
5920 barcelona
5922 documentary
5923 cod
5925 melissa
5926 moscow
5927 thesis
5930 nylon
5931 palestinian
5936 trim
5937 nigeria
5938 ceiling
5941 hispanic
5944 legislature
5945 hospitality
5946 gen
5948 procurement
5951 fleet
5953 bunch
5956 singing
5961 referral
5963 surveillance
5968 lung
5969 highlight
5970 substitute
5971 inclusion
5974 turner
5975 sucking
5980 gel
5981 todd
5983 omega
5992 termination
5994 saver
5996 grill
6000 rogers
6001 grain
6002 aaa
6004 regime
6007 bull
6008 montgomery
6010 louisville
6013 macintosh
6018 breath
6019 amenities
6021 cole
6022 mart
6023 candle
6024 hanging
6026 authorization
6027 tale
6031 projector
6034 comparative
6037 herbal
6042 stanley
6045 retailer
6049 renewal
6051 genealogy
6054 scoring
6055 expenditure
6056 brooklyn
6057 liverpool
6060 connectivity
6061 spots
6064 hacker
6065 madrid
6067 margin
6068 coin
6070 fake
6071 salon
6073 norman
6074 fda
6079 cure
6080 madonna
6081 commander
6082 arch
6084 murphy
6087 suggestion
6089 soldier
6091 asin
6094 bomb
6095 harm
6096 interval
6098 spotlight
6100 reset
6101 brush
6107 assault
6109 spare
6110 logistics
6111 deer
6113 tongue
6114 bowling
6116 danish
6117 pal
6118 monkey
6119 proportion
6120 filename
6121 skirt
6122 florence
6124 honey
6128 significance
6129 scenario
6135 symposium
6136 arabic
6137 gauge
6139 junction
6143 mat
6144 rachel
6145 solving
6150 oven
6151 ted
6152 intensive
6153 chains
6154 kingston
6155 sixth
6157 deviant
6158 noon
6159 switching
6162 correspondence
6165 supervision
6166 cheat
6167 bronze
6170 separation
6171 testimony
6172 suspect
6174 macro
6175 sender
6176 mandatory
6179 syndication
6181 celebration
6185 tuition
6186 spouse
6188 viewer
6191 luxembourg
6193 reaching
6197 receptor
6198 laugh
6199 joel
6202 citation
6203 pitch
6206 premises
6207 perry
6209 offensive
6210 imperial
6211 dozen
6212 benjamin
6213 deployment
6214 teeth
6215 cloth
6216 studying
6218 stamp
6219 lotus
6220 salmon
6221 olympus
6224 cargo
6225 tan
6226 directive
6228 salem
6229 mate
6231 starter
6234 butter
6235 pepper
6236 weapon
6237 luggage
6238 burden
6239 chef
6243 isle
6246 maple
6247 luke
6248 grocery
6250 governing
6252 depot
6254 comp
6255 alt
6256 pie
6257 blend
6258 harrison
6263 attending
6264 emission
6266 spec
6268 realty
6270 bow
6271 penn
6276 autumn
6277 traveling
6278 probe
6279 midi
6281 biotechnology
6282 toilet
6288 outreach
6289 helen
6290 mounting
6296 catherine
6302 chuck
6305 dale
6307 calculation
6312 peeing
6313 occasions
6314 brooks
6316 newton
6318 sept
6320 anxiety
6321 bingo
6327 ceramic
6328 prompt
6334 atm
6335 xanax
6343 queensland
6344 necklace
6346 leeds
6347 composite
6349 cedar
6353 advocacy
6354 raleigh
6355 stud
6356 fold
6358 designing
6362 blair
6366 mason
6367 diagram
6368 burns
6370 footwear
6373 beijing
6374 peoples
6375 victor
6383 brunswick
6384 spider
6388 sensitivity
6390 preservation
6391 hudson
6393 calgary
6394 interim
6396 divine
6397 streaming
6400 compound
6401 intensity
6403 syndicate
6404 abortion
6405 dialog
6407 blast
6408 wellness
6409 calcium
6410 newport
6413 pole
6416 shield
6417 harvest
6418 membrane
6419 prague
6421 bangladesh
6425 pickup
6426 desperate
6429 iceland
6430 demonstration
6434 graduation
6436 bend
6437 sailing
6441 addiction
6442 morocco
6443 chrome
6445 springfield
6447 brake
6448 exterior
6449 greeting
6450 ecology
6451 oliver
6452 congo
6453 glen
6454 botswana
6457 synthesis
6458 olive
6460 unemployment
6464 enhancement
6465 newcastle
6466 clone
6468 velocity
6469 lambda
6470 relay
6472 tears
6474 oasis
6475 baseline
6476 cab
6480 silicon
6481 brazilian
6483 petroleum
6486 norwegian
6487 lover
6489 honolulu
6490 beatles
6492 retention
6494 pond
6496 thomson
6498 soundtrack
6500 malta
6501 daddy
6503 ferry
6504 rabbit
6505 profession
6506 seating
6507 dam
6510 physiology
6512 collecting
6513 das
6515 omaha
6516 tire
6517 participant
6520 dominican
6521 chad
6522 electron
6523 loads
6524 friendship
6525 heather
6526 passport
6527 motel
6529 treasury
6530 warrant
6536 royalty
6538 rally
6539 observer
6540 sunshine
6541 strain
6542 drag
6543 ceremony
6547 provincial
6554 hebrew
6556 rochester
6557 dying
6558 laundry
6560 solomon
6562 stops
6563 homework
6566 advertiser
6568 encryption
6569 filling
6573 silence
6574 scsi
6576 soviet
6577 possession
6580 treaty
6581 vocal
6582 trainer
6583 organ
6588 lemon
6592 darkness
6596 nail
6598 vienna
6600 span
6601 stanford
6604 joke
6605 respondent
6606 packing
6607 statute
6611 shelter
6612 chapel
6614 manufacture
6618 vulnerability
6619 accountability
6622 appliance
6624 bahamas
6625 powell
6626 mixture
6627 bench
6629 tub
6630 rider
6631 scheduling
6632 radius
6634 mortality
6635 logging
6636 hampton
6639 therapeutic
6643 bobby
6645 sheep
6647 architect
6648 railroad
6652 nursery
6655 ash
6656 microwave
6660 relocation
6661 stuart
6663 salvador
6664 ali
6665 salad
6667 monroe
6668 tender
6670 foam
6672 paste
6675 discretion
6677 tanzania
6678 preserve
6680 poem
6684 easter
6686 repository
6687 praise
6689 venice
6693 estonia
6694 christianity
6695 veteran
6697 landing
6698 signing
6705 showcase
6706 integral
6709 namibia
6713 synopsis
6715 prairie
6716 reunion
6717 composer
6718 bean
6719 sword
6723 ecuador
6726 spirits
6728 coral
6729 pixel
6730 float
6732 bias
6735 bubble
6738 contrary
6739 millennium
6740 tribune
6741 vessel
6743 focusing
6747 dairy
6749 mem
6750 fancy
6751 equality
6752 samoa
6755 tap
6765 companion
6766 squad
6769 scroll
6772 swim
6773 wages
6777 fellowship
6779 invalid
6781 martial
6783 victorian
6786 tunnel
6788 cambodia
6792 chaos
6793 lithuania
6795 wheat
6798 beaver
6799 updating
6805 compiler
6806 enlargement
6809 vii
6810 accused
6811 bee
6813 unity
6815 conjunction
6816 bride
6824 brunette
6826 anchor
6828 validation
6829 parade
6830 corruption
6832 trigger
6834 cholesterol
6836 essex
6837 slovenia
6839 differential
6847 pendant
6849 baptist
6850 scientist
6853 clocks
6854 arthritis
6857 wallace
6860 taxation
6861 fever
6863 cuisine
6866 transcript
6868 theorem
6869 inflation
6872 ruth
6874 stylus
6876 pope
6878 contracting
6879 arnold
6882 jeep
6886 cattle
6887 mba
6888 radical
6890 rover
6893 treasure
6896 flame
6897 levitra
6901 elderly
6902 pit
6903 arlington
6904 mono
6906 floating
6908 tile
6910 bolivia
6911 spell
6913 stevens
6914 coordinate
6915 kuwait
6919 limitation
6923 webster
6926 illustration
6927 plymouth
6929 construct
6932 bridal
6933 annex
6934 mag
6936 inspiration
6940 freight
6941 rebate
6943 eclipse
6944 sudan
6948 shuttle
6949 aggregate
6956 ciao
6958 knee
6959 prep
6964 butler
6968 payroll
6969 cookbook
6971 ton
6972 courier
6974 shakespeare
6976 collapse
6985 techno
6986 beverage
6987 tribute
6989 elvis
6990 immune
6991 latvia
6993 forestry
6995 cant
7002 genesis
7003 barrier
7008 bicycle
7010 letting
7012 guatemala
7013 celtic
7017 particle
7018 perception
7021 humidity
7023 boxing
7026 bangkok
7027 renaissance
7028 pathology
7030 bra
7031 ordinance
7032 hughes
7036 chess
7038 brisbane
7041 oscar
7046 duck
7048 canal
7049 amino
7050 phi
7055 cow
7056 manitoba
7059 watson
7060 lying
7063 dive
7064 saddam
7065 circulation
7066 drill
7067 offense
7068 bryan
7070 protest
7071 assumption
7072 jerusalem
7073 hobby
7076 invention
7077 nickname
7078 fiji
7079 technician
7083 washing
7088 trick
7089 enquiry
7090 closure
7091 raid
7093 timber
7094 volt
7097 playlist
7098 registrar
7101 ruling
7102 steady
7103 dirt
7105 withdrawal
7110 saskatchewan
7112 cancellation
7116 screw
7120 blame
7121 geneva
7123 veterinary
7124 acer
7130 intake
7132 relevance
7133 incentive
7134 butterfly
7135 tucson
7136 mechanics
7139 fifty
7144 geek
7145 uncle
7146 defining
7148 counting
7149 reflection
7150 sink
7153 invitation
7155 princeton
7156 jacob
7157 sodium
7159 spirituality
7160 hormone
7161 meanwhile
7162 proprietary
7163 timothy
7165 brick
7166 grip
7170 porcelain
7172 bridges
7175 watt
7178 casting
7179 dayton
7185 carlos
7186 reno
7187 donna
7189 warrior
7190 diploma
7191 cabin
7192 innocent
7193 scanning
7195 consensus
7196 polo
7197 valium
7198 copying
7203 horn
7205 uganda
7207 journalism
7210 trivia
7212 perth
7213 frog
7214 grammar
7215 intention
7216 syria
7218 klein
7219 harvey
7224 hazard
7225 retro
7226 leo
7229 semiconductor
7230 gregory
7233 circular
7234 anger
7236 mainland
7241 snap
7242 happiness
7246 glenn
7248 auckland
7249 olympics
7251 identifier
7254 ribbon
7256 doe
7258 conducting
7259 startup
7261 trinidad
7263 kissing
7265 handy
7266 swap
7272 geometry
7273 impression
7274 abs
7275 slovakia
7276 flip
7277 guild
7278 correlation
7280 capitol
7283 rna
7284 barbados
7287 refuse
7289 fragrance
7291 replica
7292 plumbing
7293 brussels
7294 tribe
7298 buzz
7300 nuke
7302 trinity
7303 charleston
7306 boom
7307 calm
7313 exhaust
7314 comparing
7315 shanghai
7317 burton
7322 farming
7323 gibson
7325 fork
7326 troy
7328 roller
7330 batch
7335 latino
7336 ghana
7339 mixing
7343 albuquerque
7344 harmony
7346 asthma
7350 twins
7352 rip
7355 triangle
7359 reward
7360 windsor
7361 zambia
7366 hydrogen
7368 sprint
7370 chick
7371 advocate
7373 confusion
7375 tray
7378 genome
7381 thong
7382 medal
7387 sol
7389 sage
7395 bat
7397 booth
7398 indie
7401 bones
7402 breed
7403 detector
7408 sussex
7412 transexual
7413 mainstream
7414 invoice
7416 lip
7417 subcommittee
7418 sap
7419 gather
7421 maternity
7423 alfred
7424 colonial
7429 embassy
7430 cave
7433 rebecca
7434 slight
7435 proceeds
7438 wool
7441 arrest
7442 volleyball
7445 horizon
7448 toolbox
7450 marina
7451 liabilities
7453 bosnia
7456 patio
7458 tolerance
7459 surfing
7460 creativity
7461 lloyd
7463 optics
7465 lightning
7470 grab
7471 inspector
7473 brighton
7478 snake
7480 leonard
7481 lending
7483 reminder
7487 riverside
7496 sullivan
7498 midwest
7499 karaoke
7500 trap
7502 fool
7504 nonprofit
7505 lancaster
7513 berry
7514 collar
7518 bermuda
7520 sociology
7529 consortium
7531 terrace
7532 bacteria
7535 seafood
7541 delicious
7544 jail
7547 kidney
7555 moisture
7556 hungarian
7557 roster
7560 spencer
7561 wichita
7562 nasdaq
7563 uruguay
7567 timer
7569 tuning
7572 tyler
7574 vegetable
7575 verse
7577 humanities
7580 custody
7581 scratch
7584 alignment
7593 rocket
7595 bullet
7598 lace
7600 visibility
7601 latitude
7602 consciousness
7604 tumor
7608 mistress
7609 encounter
7611 watts
7612 duncan
7614 hart
7615 bernard
7619 forty
7622 col
7624 priest
7628 queue
7631 trance
7632 locale
7633 nicholas
7636 bundle
7637 hammer
7638 invasion
7640 runner
7643 notion
7649 spelling
7650 arctic
7657 frederick
7658 medicaid
7660 infrared
7661 seventh
7664 welsh
7665 belly
7669 quarters
7671 cia
7674 haiti
7677 sculpture
7680 dod
7682 fist
7685 motivation
7687 pharmacology
7688 fitting
7691 mere
7695 petersburg
7699 surplus
7700 elder
7704 dig
7705 taxi
7706 punishment
7707 appreciation
7710 belarus
7712 zoning
7713 gravity
7714 providence
7715 thumb
7716 restriction
7719 treasurer
7721 essence
7722 flooring
7723 lightweight
7724 ethiopia
7728 humanity
7729 transcription
7731 holmes
7738 galaxy
7739 chester
7740 snapshot
7741 caring
7744 synthetic
7745 shaw
7748 testament
7749 expo
7750 dominant
7751 twist
7754 stomach
7758 newbie
7760 darwin
7762 wilderness
7763 debut
7766 bradley
7768 anatomy
7769 bali
7771 sponsorship
7773 fraction
7774 trio
7775 proceeding
7776 cube
7779 uncertainty
7780 breakdown
7781 milton
7782 marker
7783 reconstruction
7784 subsidiary
7786 clarity
7789 adelaide
7792 monaco
7794 folding
7797 airfare
7801 vaccine
7802 belize
7803 fate
7807 penny
7814 gui
7815 porter
7817 jungle
7821 rim
7823 zen
7824 ivory
7826 dis
7827 prediction
7832 alias
7833 thesaurus
7835 battlefield
7840 spice
7841 oval
7844 soma
7846 cooler
7847 appraisal
7852 overhead
7853 ascii
7854 prospect
7855 shipment
7856 breeding
7859 donor
7860 mozambique
7861 tension
7864 trash
7866 wifi
7867 tier
7869 earl
7870 manor
7871 envelope
7873 homeland
7880 disco
7881 sheffield
7882 bailey
7885 finishing
7887 wellington
7888 incoming
7891 cleaners
7892 bulgarian
7896 guam
7897 cite
7898 aboriginal
7900 rotation
7904 boulevard
7905 eugene
7908 pig
7909 metric
7911 minus
7913 bennett
7914 imagination
7917 joshua
7918 armenia
7923 actress
7924 mess
7927 armstrong
7930 lit
7932 tigers
7933 aurora
7936 milan
7937 premiere
7938 lender
7940 shade
7941 chorus
7944 digit
7946 dietary
7947 symphony
7951 precipitation
7955 ada
7958 lyric
7960 isolation
7965 rope
7966 carroll
7967 rational
7968 programmer
7970 chambers
7971 dump
7974 warming
7977 chronicle
7978 fountain
7980 grave
7983 burner
7986 investigator
7988 plaintiff
7989 finnish
7995 hose
7996 mediterranean
7997 nightlife
7998 footage
8000 worthy
8004 entrepreneur
8007 freelance
8008 duo
8010 devon
8012 helena
8015 valuation
8017 cigarette
8018 fog
8019 characteristic
8021 lobby
8022 egyptian
8023 tunisia
8027 headline
8029 punch
8033 cowboy
8034 narrative
8035 bahrain
8037 karma
8040 queens
8045 lucas
8047 subdivision
8049 vip
8050 defeat
8052 distinction
8053 honduras
8056 insured
8057 harper
8058 livestock
8060 exemption
8061 tenant
8062 sustainability
8064 tattoo
8065 shake
8066 algebra
8068 holly
8069 formatting
8070 silly
8072 yea
8073 mercy
8074 hartford
8077 sunrise
8078 wrapping
8080 fur
8081 nicaragua
8083 timeline
8084 tar
8088 affiliation
8090 fence
8091 nudist
8092 infinite
8093 diana
8096 lindsay
8097 clan
8099 shame
8101 revolutionary
8104 civilian
8105 telephony
8106 mesa
8108 remedy
8110 breathing
8112 thickness
8115 genius
8117 aerospace
8118 fighter
8120 flesh
8121 retreat
8126 rug
8127 democrat
8128 borough
8130 failing
8136 andrews
8137 marble
8141 hull
8143 surrey
8144 briefing
8147 accreditation
8149 blackberry
8150 highland
8151 meditation
8153 microphone
8154 macedonia
8155 combining
8160 shed
8161 balloon
8164 memo
8165 ham
8167 tide
8168 kazakhstan
8169 hawaiian
8171 partition
8175 funk
8176 fbi
8177 qatar
8178 magnet
8181 cayman
8182 jaguar
8183 reel
8185 commodity
8186 posing
8189 bind
8190 thanksgiving
8191 rand
8192 hopkins
8196 gothic
8197 cylinder
8198 witch
8199 buck
8200 indication
8202 congratulations
8207 puppy
8209 acre
8211 surround
8213 revenge
8218 aqua
8219 chen
8221 consultancy
8222 finances
8227 patrol
8228 smell
8229 pest
8235 carnival
8237 sticker
8240 reef
8242 divide
8247 cornell
8248 satin
8253 promo
8259 garbage
8263 beth
8264 bradford
8268 peninsula
8270 boring
8271 reynolds
8277 schema
8279 sofa
8286 prefix
8288 savannah
8289 barrel
8290 typing
8291 nerve
8294 deficit
8295 boulder
8299 viii
8300 myanmar
8301 metadata
8304 floppy
8305 texture
8307 jar
8309 somerset
8313 antigua
8315 thunder
8316 tent
8317 caution
8319 questionnaire
8320 qualification
8322 modelling
8324 miniature
8326 hack
8327 dare
8329 interstate
8331 aerial
8332 hawk
8333 consequence
8334 rebel
8339 makeup
8340 textile
8341 lamb
8342 madagascar
8344 tobago
8346 cos
8348 uzbekistan
8350 pac
8355 magnitude
8357 richardson
8358 hindu
8361 vocabulary
8362 licking
8363 earthquake
8369 albania
8374 eds
8377 roommate
8386 junk
8388 wax
8389 lucy
8393 slope
8394 reggae
8396 poet
8397 conspiracy
8398 surname
8399 theology
8405 epic
8406 saturn
8407 organizer
8408 nut
8409 allergy
8410 sake
8414 merit
8415 enzyme
8419 edmonton
8420 tackle
8422 condo
8424 amplifier
8429 lexington
8433 titanium
8434 irs
8435 fairy
8438 shaft
8439 lean
8440 bye
8447 ana
8450 kitty
8452 dude
8453 drain
8454 monte
8456 algeria
8460 cardiff
8461 cornwall
8463 potato
8464 panic
8467 leone
8468 transsexual
8470 citizenship
8471 excuse
8473 basement
8474 onion
8475 strand
8477 sandwich
8479 lawsuit
8480 alto
8482 girlfriend
8484 cheque
8485 hierarchy
8488 reject
8493 italic
8494 beats
8496 mil
8497 scuba
8498 gore
8499 complement
8500 cult
8501 dash
8502 passive
8503 mauritius
8505 cage
8506 checklist
8509 courage
8513 gazette
8516 extraction
8517 batman
8518 elevation
8522 lap
8523 utilization
8525 calibration
8529 anaheim
8530 ping
8531 textbook
8534 prerequisite
8535 luther
8536 frontier
8537 settle
8538 stopping
8541 hypothesis
8542 palmer
8544 flux
8545 derby
8549 pontiac
8550 regression
8551 doctrine
8557 intersection
8559 sewing
8560 consistency
8563 munich
8564 oman
8569 azerbaijan
8570 lighter
8571 rage
8575 astrology
8577 pavilion
8578 tactics
8582 travelling
8585 pillow
8586 induction
8591 spreading
8595 paraguay
8596 steal
8597 parcel
8601 fifteen
8603 incidence
8606 boutique
8607 acrylic
8609 tuner
8610 avon
8615 shannon
8616 toddler
8618 flavor
8621 homeless
8624 metallic
8625 acne
8627 interference
8629 palestine
8635 malawi
8640 dana
8641 halo
8643 curtis
8648 publicity
8649 marathon
8650 ant
8651 proposition
8653 pressing
8654 gasoline
8657 scout
8658 belfast
8659 exec
8661 niagara
8663 eos
8666 catalyst
8667 trader
8669 allowance
8671 denial
8673 designation
8677 gem
8678 duplicate
8680 criterion
8681 badge
8682 wrist
8683 civilization
8685 vietnamese
8686 heath
8688 ballot
8692 validity
8693 trustee
8694 maui
8697 angola
8698 squirt
8701 realm
8703 jenny
8704 helmet
8706 postcard
8707 elephant
8708 yemen
8710 tsunami
8711 scholar
8712 nickel
8715 psi
8718 geology
8722 coating
8724 wallet
8729 boating
8730 drainage
8734 vegetarian
8735 rouge
8736 yeast
8737 yale
8738 newfoundland
8741 pas
8742 clearing
8745 ambassador
8750 vegetation
8751 doom
8756 owen
8758 hitting
8759 yukon
8761 bite
8763 aquatic
8764 reliance
8766 striking
8771 gig
8772 gilbert
8773 sas
8775 continuity
8776 brook
8779 phenomenon
8780 ensemble
8781 insulin
8784 weed
8786 accent
8788 eleven
8792 mileage
8794 prostate
8795 adaptor
8799 pledge
8800 vampire
8803 nitrogen
8804 xerox
8805 dice
8806 merger
8807 softball
8809 quad
8810 dock
8813 mods
8815 framing
8816 musician
8817 blocking
8818 rwanda
8820 integrating
8822 limiting
8823 dispatch
8825 papua
8827 hint
8828 armor
8831 remark
8832 dozens
8835 reasoning
8839 picking
8848 burlington
8849 replacing
8850 researcher
8851 watershed
8855 kruger
8857 granny
8858 pork
8860 equilibrium
8866 aruba
8868 realtor
8870 privilege
8871 edgar
8873 qualifying
8874 chassis
8875 dubai
8876 estimation
8877 barn
8878 pushing
8880 fleece
8883 fare
8886 pierce
8888 dressing
8890 sperm
8894 craps
8895 fuji
8896 frost
8897 leon
8899 mold
8900 dame
8902 sally
8903 yacht
8904 tracy
8906 drilling
8908 herb
8911 ate
8912 breach
8913 whale
8914 traveller
8918 benchmark
8923 stationery
8924 idle
8925 mustang
8928 antibody
8930 momentum
8931 fin
8932 wiring
8934 pastor
8935 mud
8936 calvin
8938 shark
8939 contributor
8943 emerald
8947 cliff
8949 tract
8951 ballet
8953 journalist
8954 abraham
8956 bumper
8958 webpage
8960 garlic
8962 shine
8963 senegal
8964 explosion
8968 briefs
8971 cove
8972 mumbai
8973 ozone
8980 tariff
8982 opponent
8983 pasta
8986 serum
8988 swift
8994 eden
8997 champagne
8998 ala
8999 decimal
9001 deviation
9002 superintendent
9004 dip
9006 samba
9007 hostel
9009 employ
9010 mongolia
9011 penguin
9015 irrigation
9016 miracle
9018 reprint
9019 reid
9023 robertson
9024 flex
9025 yearly
9026 penetration
9027 wound
9028 belle
9029 rosa
9030 conviction
9031 hash
9033 writings
9034 hamburg
9038 retrieval
9045 marvel
9047 cio
9049 prototype
9052 petite
9053 apparatus
9055 terrain
9059 yen
9061 gossip
9063 nomination
9066 rotary
9067 worm
9068 dependence
9070 beginner
9072 lid
9073 sexuality
9074 polyester
9076 deaf
9079 sapphire
9080 kinase
9083 remainder
9084 crawford
9089 marking
9092 serbia
9093 sheriff
9094 griffin
9096 guyana
9098 blah
9099 mime
9100 neighbor
9102 elect
9105 concentrate
9106 intimate
9111 bunny
9116 refrigerator
9121 usda
9122 exclusion
9123 workstation
9124 holocaust
9125 keen
9126 flyer
9128 dosage
9131 disposition
9132 variance
9133 navigator
9135 cameroon
9136 baking
9137 marijuana
9140 needle
9144 cathedral
9145 brakes
9147 nirvana
9154 destiny
9156 madness
9158 climb
9159 blowing
9163 lafayette
9165 wto
9166 computation
9167 hay
9172 salvation
9173 dover
9174 adrian
9177 vatican
9182 arbitration
9184 token
9186 zinc
9187 sacrifice
9189 guru
9192 convergence
9194 gibraltar
9195 levy
9198 anthropology
9199 skating
9201 aberdeen
9202 emperor
9203 grad
9204 malpractice
9205 dylan
9212 burke
9214 pix
9215 necessity
9216 rendering
9217 mic
9219 pulling
9222 obesity
9227 vertex
9229 hepatitis
9231 tomato
9232 andorra
9233 waterproof
9237 flush
9238 waiver
9239 pale
9241 hayes
9242 humanitarian
9244 functioning
9245 delight
9246 survivor
9250 alexandria
9252 moses
9257 johns
9263 tear
9264 jewel
9265 teddy
9267 convertible
9271 nil
9273 stranger
9274 tragedy
9275 julian
9276 nest
9278 dryer
9280 velvet
9281 tribunal
9283 nato
9287 secretariat
9288 nowhere
9289 cop
9291 gale
9293 adolescent
9295 wesley
9300 mattress
9302 brunei
9304 banana
9306 slovak
9309 reservoir
9310 occurrence
9311 idol
9312 mixer
9315 worcester
9317 demographic
9320 tooth
9322 annoying
9324 stays
9326 affair
9327 drove
9328 washer
9329 upset
9331 springer
9335 rebound
9336 logan
9337 mentor
9341 baghdad
9342 elimination
9344 hypothetical
9347 helicopter
9348 pencil
9349 freeze
9351 performer
9355 sphere
9357 moss
9359 concord
9364 walnut
9365 lance
9366 ladder
9367 italia
9370 liberia
9371 sherman
9372 cork
9377 workout
9378 mali
9379 yugoslavia
9380 bleeding
9381 characterization
9382 colon
9383 likelihood
9385 purse
9386 fundamentals
9387 contamination
9390 compromise
9391 masturbation
9394 dome
9396 leu
9397 expiration
9400 peripheral
9403 negotiation
9404 crest
9406 triumph
9408 confidentiality
9411 welding
9414 heel
9415 alloy
9419 yang
9421 greensboro
9422 tulsa
9423 locking
9427 fridge
9428 blanket
9429 bloom
9441 surge
9443 trauma
9446 advert
9449 defensive
9450 sip
9455 vanilla
9460 deutschland
9461 picnic
9464 spank
9466 practitioner
9471 hollow
9472 vault
9476 groove
9477 revelation
9479 pursuit
9480 delegation
9485 backing
9486 greenhouse
9489 blake
9490 transparency
9496 orbit
9498 niger
9499 bacon
9501 positioning
9502 heater
9503 colony
9504 cannon
9505 circus
9509 moldova
9512 paxil
9513 spine
9514 trout
9516 feat
9520 thriller
9523 fatty
9529 hunger
9532 municipality
9533 usps
9534 joyce
9535 detective
9536 surgeon
9537 cement
9539 fireplace
9540 endorsement
9545 missile
9546 intranet
9549 psychiatry
9556 glow
9557 gabriel
9558 auditor
9560 aquarium
9561 violin
9562 prophet
9564 bracket
9566 isaac
9567 oxide
9571 colleague
9572 naples
9575 adaptation
9578 paintball
9579 prozac
9581 enclosure
9583 dividend
9584 newark
9587 glucose
9588 phantom
9589 norm
9590 playback
9592 westminster
9593 turtle
9596 absorption
9601 ware
9602 fossil
9604 hometown
9607 apollo
9608 wan
9610 persian
9612 communist
9613 collectible
9615 greene
9618 grenada
9620 jade
9621 scoop
9623 foul
9624 keno
9627 mailman
9630 biodiversity
9631 excitement
9632 somalia
9635 blink
9639 workflow
9641 novelty
9645 librarian
9648 stockholm
9649 tamil
9652 pose
9654 indonesian
9656 therapist
9657 richards
9658 mrna
9662 relaxation
9663 goat
9664 render
9666 ira
9667 sen
9669 hardwood
9670 erotica
9671 temporal
9672 sail
9673 forge
9677 brave
9678 forwarding
9681 nightmare
9682 airplane
9685 istanbul
9689 telescope
9690 viewers
9691 asbestos
9692 portsmouth
9693 cdna
9696 pod
9697 savage
9698 advancement
9700 harassment
9701 willow
9703 bolt
9704 gage
9709 wagon
9711 dat
9713 knock
9714 urge
9719 replication
9725 optimum
9726 neon
9728 quilt
9729 huntington
9730 creature
9733 syracuse
9734 internship
9737 aluminium
9738 snowboard
9742 evanescence
9747 maldives
9748 stripes
9749 firmware
9750 antarctica
9751 cope
9752 shepherd
9754 canberra
9755 cradle
9756 chancellor
9757 mambo
9758 lime
9759 kirk
9760 flour
9761 controversy
9764 sympathy
9765 choir
9768 blond
9771 jumping
9774 polymer
9775 hygiene
9776 wit
9777 poultry
9778 virtue
9779 burst
9782 bouquet
9783 immunology
9785 mandate
9791 corpus
9792 johnston
9793 terminology
9794 gentleman
9795 fibre
9798 shades
9806 spokesman
9807 zoloft
9809 frankfurt
9810 prisoner
9811 daisy
9812 halifax
9815 cursor
9823 crude
9824 morrison
9825 maiden
9830 viking
9831 myrtle
9833 cleanup
9834 yarn
9835 knit
9837 mug
9838 crossword
9839 bother
9840 budapest
9842 knitting
9845 bhutan
9846 liechtenstein
9847 mating
9849 redhead
9851 translator
9853 tractor
9854 allah
9855 continent
9859 longitude
9864 pike
9866 insertion
9867 instrumentation
9869 hugo
9870 wagner
9871 constraint
9874 strengthening
9875 cologne
9877 wishing
9878 ranger
9880 insulation
9881 newman
9882 marsh
9886 theta
9887 infringement
9888 bent
9889 laos
9892 asylum
9895 stake
9896 cocktail
9898 swaziland
9900 arbor
07070100000063000081A400000000000000000000000163C2D5550000511F000000000000000000000000000000000000003B00000000spectre-cli-3.0+git20230114.26c01a5/scripts/words.txt.verb26 have
32 will
33 home
34 can
38 page
41 search
42 free
50 time
52 site
60 out
61 use
64 see
69 contact
73 web
76 help
77 get
79 view
94 click
96 like
97 service
100 find
101 price
102 date
103 back
104 top
105 people
107 list
108 name
111 state
115 email
125 work
126 last
130 buy
132 make
137 post
141 add
143 number
145 please
147 copyright
148 support
149 message
151 best
157 well
165 school
171 review
173 order
176 book
178 company
180 read
181 group
182 need
188 set
190 general
191 research
194 mail
195 full
196 map
198 program
200 know
206 part
215 center
218 store
219 travel
223 report
224 off
227 line
232 send
233 right
234 type
244 design
245 take
248 address
253 want
254 phone
258 subject
263 long
266 code
267 show
269 even
270 black
271 check
275 index
279 sign
280 file
281 link
282 open
286 case
287 project
292 section
293 own
294 found
296 house
304 game
306 power
308 care
309 network
310 down
314 total
315 place
316 end
318 download
323 access
324 think
332 control
333 water
336 size
341 guide
342 shop
344 board
346 change
347 white
351 rate
356 return
360 account
363 level
365 profile
367 form
369 love
373 call
375 image
377 title
387 class
389 still
394 content
398 visit
399 save
401 low
402 reply
405 compare
407 include
409 value
410 article
412 man
413 card
415 provide
418 source
419 author
421 press
423 learn
426 print
427 course
428 job
430 process
432 room
433 stock
436 credit
437 point
438 join
445 look
448 team
450 box
452 select
456 thread
459 note
460 live
463 table
464 register
469 market
472 action
473 start
475 model
477 air
479 plan
485 second
488 cost
491 march
494 better
495 say
501 test
503 come
507 study
509 cart
510 staff
515 play
521 complete
524 comment
530 tax
536 blog
537 party
542 let
548 park
550 side
551 act
554 give
560 quote
563 sell
565 experience
567 create
568 key
569 body
573 field
576 paper
577 single
579 age
581 club
590 gift
591 question
598 pay
602 browse
603 issue
604 range
607 court
610 result
612 light
613 write
614 war
616 offer
617 blue
624 release
626 request
627 fax
630 picture
637 major
638 star
641 space
643 hand
644 sun
651 become
652 interest
655 keep
656 enter
658 share
660 garden
664 reference
667 baby
670 run
672 net
674 term
675 film
677 put
683 welcome
687 notice
689 head
690 radio
693 color
698 track
701 archive
706 format
710 log
715 trade
720 tell
721 further
730 green
732 close
734 drive
743 short
745 lot
746 ask
747 display
754 beach
768 weather
769 mar
770 land
771 average
779 record
780 direct
786 district
787 calendar
789 style
791 front
793 update
800 sound
802 present
806 document
807 word
810 bill
813 talk
821 centre
826 finance
827 true
830 mark
832 rock
842 cover
844 edit
848 fast
849 function
855 meet
863 subscribe
864 submit
866 amount
867 watch
869 feel
871 bank
872 risk
883 weight
888 choose
903 clear
904 golf
905 receive
912 loan
917 position
919 sort
926 step
933 license
945 google
946 church
948 purchase
952 practice
954 figure
956 fire
957 holiday
958 chat
965 speed
969 face
970 brand
971 discount
975 remember
977 oil
979 yellow
981 increase
982 advertise
984 base
985 near
988 stuff
989 french
992 japan
997 stay
1003 turn
1004 mean
1012 copy
1014 drug
1018 force
1019 cash
1022 bay
1024 commission
1026 package
1031 port
1034 stop
1038 bar
1039 institute
1042 double
1043 dog
1044 build
1045 screen
1046 exchange
1052 continue
1056 season
1057 apply
1063 condition
1065 believe
1067 effect
1070 mind
1076 tour
1079 cross
1081 mortgage
1082 hope
1083 silver
1085 wish
1088 mature
1094 supply
1100 lower
1110 fine
1115 gas
1118 bush
1121 career
1125 leave
1133 zip
1134 bid
1137 middle
1138 move
1139 cable
1146 object
1148 appropriate
1149 machine
1154 score
1160 follow
1161 sample
1168 culture
1169 band
1170 flash
1172 lead
1189 channel
1190 letter
1195 structure
1196 fund
1197 summer
1198 allow
1200 contract
1201 button
1203 wed
1207 matter
1219 cause
1226 focus
1228 fall
1236 win
1242 reason
1243 tool
1245 spring
1247 answer
1248 voice
1251 schedule
1254 purpose
1255 feature
1256 bed
1258 police
1262 approach
1264 brown
1267 hill
1269 medicine
1270 deal
1271 hold
1275 glass
1281 thank
1284 survey
1286 telephone
1287 sport
1288 ready
1289 feed
1296 secure
1301 evidence
1302 station
1304 round
1307 understand
1309 master
1318 blood
1319 cut
1321 improve
1328 earth
1331 impact
1332 transfer
1341 ground
1343 ship
1351 perfect
1354 kit
1357 command
1360 express
1361 award
1362 distance
1363 tree
1366 ensure
1368 wall
1376 budget
1392 wine
1394 horse
1395 vote
1396 forward
1403 retail
1412 rule
1418 bring
1419 catalog
1423 mother
1428 traffic
1429 programme
1431 input
1436 bin
1445 charge
1449 cool
1458 metal
1467 abstract
1469 output
1474 develop
1485 pass
1487 trust
1496 vacation
1501 skin
1509 ring
1510 grade
1515 pop
1516 filter
1520 consider
1524 panel
1525 floor
1528 match
1530 default
1531 require
1539 rest
1541 plant
1543 hit
1546 pool
1549 partner
1555 fish
1558 eye
1559 string
1560 sense
1562 pack
1564 stage
1573 race
1575 background
1576 target
1577 except
1578 character
1596 winter
1599 pressure
1607 core
1608 break
1612 dance
1620 fee
1627 rent
1640 enjoy
1641 remove
1642 aid
1643 surface
1646 host
1652 block
1656 repair
1657 fair
1659 steel
1663 wrong
1680 fat
1682 father
1684 saw
1687 officer
1691 respect
1696 trip
1706 farm
1714 coast
1715 benefit
1716 progress
1719 lord
1720 grant
1721 sub
1722 agree
1724 hear
1734 fan
1735 transport
1745 zone
1748 cat
1751 die
1753 jack
1755 flow
1759 parent
1765 scale
1766 stand
1772 frame
1784 bottom
1787 detail
1791 pet
1793 begin
1797 clean
1798 switch
1802 guy
1807 connect
1810 cup
1817 demand
1822 square
1825 advance
1826 skip
1827 diet
1829 auction
1830 gear
1835 correct
1840 piece
1841 sheet
1842 firm
1849 jump
1852 resort
1857 certificate
1858 minister
1859 motion
1861 fashion
1865 monitor
1867 forest
1871 couple
1873 chance
1875 ball
1879 listen
1880 discuss
1881 accept
1887 wind
1896 appear
1901 leather
1902 determine
1904 palm
1909 stone
1910 bob
1917 satellite
1918 fit
1925 pain
1937 ford
1939 edge
1941 root
1946 ice
1947 pink
1949 balance
1951 graduate
1956 initial
1957 label
1962 recommend
1964 league
1965 waste
1967 bus
1975 chair
1978 phase
1980 bag
1981 fantasy
1984 motor
1988 install
1989 shirt
1990 apparel
1993 foot
1994 mass
1996 count
1997 breast
2007 claim
2011 patch
2012 heat
2017 miss
2019 doctor
2020 task
2021 reduce
2026 enable
2027 exercise
2028 bug
2031 guarantee
2046 flight
2048 fuel
2050 walk
2055 wait
2057 pocket
2058 saint
2066 joint
2072 upgrade
2074 factor
2078 stream
2080 pick
2092 prime
2094 limit
2118 load
2119 affiliate
2120 labor
2139 protect
2140 drop
2149 prevent
2150 theme
2153 campaign
2163 spirit
2166 challenge
2171 seem
2173 touch
2178 hire
2180 suggest
2181 branch
2183 serve
2187 mount
2188 smart
2195 avoid
2197 manage
2198 corner
2199 rank
2203 birth
2205 abuse
2208 separate
2209 quarter
2213 define
2217 breakfast
2222 chain
2224 identify
2234 reach
2240 sequence
2241 inch
2242 attack
2246 damage
2248 reserve
2253 produce
2254 snow
2257 counter
2261 weekend
2263 camp
2269 bridge
2271 fill
2278 draft
2279 chart
2289 equal
2290 adventure
2294 profit
2303 expect
2309 wholesale
2321 fort
2337 pattern
2339 boat
2345 sponsor
2352 harry
2356 delete
2361 mouse
2362 signal
2365 brain
2369 dream
2373 cast
2374 flower
2375 felt
2385 promote
2391 carry
2392 flag
2398 advantage
2401 maintain
2415 brief
2420 iron
2422 script
2427 void
2429 alert
2433 tag
2434 interview
2435 mix
2437 disk
2439 queen
2443 fix
2444 handle
2454 associate
2456 truck
2458 enlarge
2459 ray
2462 measure
2469 bear
2470 gain
2477 lack
2484 train
2487 explore
2508 con
2511 route
2516 frank
2518 zoom
2519 blow
2520 battle
2523 speak
2527 query
2528 clip
2537 wire
2544 tape
2546 judge
2547 spam
2554 fight
2558 zero
2563 obtain
2569 remain
2574 roll
2584 negative
2593 perform
2615 fail
2623 assist
2624 conduct
2630 jazz
2642 wear
2651 minor
2652 finish
2669 row
2671 forecast
2676 cycle
2678 contain
2681 rise
2683 sleep
2685 bird
2697 chairman
2702 drink
2706 eat
2718 seat
2728 aim
2736 toll
2742 tip
2745 mine
2748 ticket
2750 guess
2754 ski
2757 import
2778 moon
2780 scheme
2782 preview
2801 fear
2811 comfort
2818 appeal
2819 cruise
2827 beat
2842 achieve
2844 dress
2853 happen
2856 hide
2860 refer
2862 provision
2872 toy
2873 circuit
2874 layer
2876 slow
2881 trademark
2890 spot
2894 spend
2898 grow
2904 clock
2905 dot
2918 influence
2922 rain
2930 permit
2932 proof
2939 empty
2940 instance
2941 hole
2943 ride
2951 represent
2953 pair
2957 don
2964 stress
2965 cream
2970 discover
2986 shape
3006 cap
3007 ink
3011 census
3013 peak
3016 exist
3017 wheel
3018 transit
3020 salt
3021 compact
3026 bell
3029 attempt
3034 noise
3036 forget
3037 array
3044 pin
3055 explain
3058 establish
3068 paragraph
3069 kill
3074 export
3079 conflict
3083 occur
3087 describe
3088 concern
3097 trouble
3098 spread
3099 coach
3102 expand
3109 participate
3110 plug
3112 cook
3113 affect
3118 hat
3128 bike
3130 plate
3132 indicate
3141 lose
3143 seek
3173 circle
3174 soil
3180 wet
3185 ram
3194 fox
3196 respond
3198 plain
3199 exit
3202 arm
3204 launch
3205 wave
3213 mesh
3214 trail
3224 poll
3233 bond
3237 notify
3241 portion
3246 cotton
3254 border
3256 debate
3273 husband
3279 concert
3294 hop
3298 visa
3299 mouth
3301 meter
3307 gun
3308 reflect
3310 deliver
3311 wonder
3313 fruit
3316 reform
3321 draw
3325 assume
3329 confirm
3330 warm
3336 engineer
3339 replace
3342 inventory
3352 sugar
3353 crew
3356 stick
3363 slide
3365 volunteer
3367 rear
3369 enhance
3371 exact
3372 bound
3379 contribute
3380 lock
3382 storm
3392 bowl
3398 tank
3401 estimate
3402 encourage
3404 kid
3408 cancel
3414 paint
3415 delay
3416 pilot
3426 pan
3437 potter
3438 dish
3440 bulletin
3444 modify
3456 patent
3457 slot
3465 lodge
3467 mirror
3478 gray
3479 catalogue
3488 demo
3494 hate
3501 rice
3503 loop
3508 guard
3515 resume
3518 raise
3519 prepare
3525 vary
3531 premier
3534 milk
3535 decide
3536 consent
3542 contest
3546 boot
3551 lunch
3552 audit
3553 push
3554 chamber
3557 muscle
3560 implement
3565 tower
3567 sum
3573 attend
3574 shower
3582 shell
3601 catch
3609 doubt
3610 earn
3619 wallpaper
3620 merchandise
3629 twin
3631 insert
3643 transition
3647 romance
3648 instrument
3650 split
3663 bargain
3675 suit
3677 shift
3678 chip
3680 sit
3684 wow
3696 char
3703 cartoon
3723 treat
3729 echo
3744 landscape
3747 journey
3754 charter
3771 teach
3775 pull
3780 reverse
3781 deposit
3788 specify
3799 concrete
3807 upload
3808 pulse
3817 calculate
3821 broadcast
3827 strip
3829 pearl
3831 nick
3835 plot
3841 regard
3842 pump
3845 smooth
3848 strike
3853 narrow
3868 castle
3873 antique
3896 mention
3900 grey
3901 parallel
3904 operate
3908 stable
3917 scan
3921 blank
3925 generate
3931 humor
3937 bulk
3950 contrast
3954 sin
3966 capture
3968 buffalo
3970 plane
3973 seed
3975 desire
3983 peer
3990 bottle
3995 massage
3996 rubber
4003 grace
4011 bang
4013 bone
4029 gate
4041 tone
4046 busy
4047 captain
4049 imagine
4053 neck
4055 wing
4062 taste
4064 commit
4067 rail
4073 gap
4075 tube
4079 cache
4080 belt
4081 jacket
4087 lease
4094 console
4106 alarm
4115 angle
4116 rob
4123 worst
4129 blackjack
4132 convert
4137 garage
4151 honor
4154 eagle
4158 nurse
4163 quiet
4169 dial
4171 cheese
4174 jet
4177 crown
4178 par
4182 crack
4186 gang
4193 smoke
4198 craft
4199 cake
4203 blind
4204 lounge
4210 gross
4224 till
4226 pen
4231 shoe
4235 sand
4238 joy
4247 seal
4257 prize
4269 pipe
4273 lecture
4281 lay
4285 dirty
4286 deck
4288 donate
4297 prove
4305 promise
4306 thin
4308 prefer
4309 ridge
4315 sick
4316 dose
4317 evaluate
4320 collect
4321 bet
4330 buffer
4331 purple
4346 attribute
4360 exhibit
4361 throw
4362 trend
4366 desert
4378 navigate
4380 summit
4386 escape
4396 glance
4409 graph
4416 foster
4422 tissue
4441 okay
4454 crash
4455 lift
4457 inter
4461 shadow
4466 expense
4469 venture
4477 spray
4488 extend
4491 pat
4494 fell
4495 motorcycle
4508 murder
4512 yield
4516 pour
4517 digest
4521 dust
4527 rescue
4530 combat
4556 shock
4561 tie
4562 ward
4567 roof
4569 demonstrate
4572 kiss
4576 experiment
4580 dodge
4581 counsel
4594 rush
4598 cluster
4605 surprise
4621 sublime
4629 verify
4632 copper
4634 rack
4636 warehouse
4642 receipt
4646 ghost
4648 configure
4653 boss
4654 pride
4662 champion
4670 solo
4671 sentence
4673 ignore
4675 uniform
4681 vacuum
4684 recognize
4691 quest
4692 publish
4695 toe
4703 pioneer
4723 excel
4727 wake
4729 harbor
4733 segment
4734 realize
4738 puzzle
4754 repeat
4759 drum
4769 flood
4771 ease
4781 appreciate
4785 polish
4792 smile
4799 broker
4803 pine
4805 tend
4808 rick
4812 divorce
4818 customize
4820 candy
4828 hunt
4843 boost
4847 mill
4856 finger
4858 pound
4859 locate
4863 burn
4868 bread
4877 lie
4881 decrease
4884 pension
4888 worship
4900 solve
4911 sole
4912 franchise
4923 sight
4929 refund
4952 worry
4964 phrase
4973 intermediate
4976 leaf
4977 pad
4978 glory
4985 combine
4988 exceed
4992 fault
4997 introduce
5011 examine
5012 jimmy
5020 wolf
5023 communicate
5025 supplement
5036 kick
5040 hurt
5047 pot
5048 dimension
5057 slip
5064 devil
5065 grass
5066 comply
5072 alternate
5085 pee
5100 egg
5108 swing
5121 shoot
5123 mint
5125 offset
5130 spin
5139 witness
5146 powder
5149 assess
5150 wash
5153 entrance
5164 rev
5167 shore
5171 knight
5173 loose
5199 soap
5201 triple
5202 cooper
5205 jam
5214 disorder
5242 bachelor
5249 carpet
5266 pace
5268 wage
5272 arc
5279 dawn
5283 torture
5286 coat
5313 petition
5315 recall
5325 shoulder
5337 podcast
5341 refine
5345 herald
5355 invite
5359 suppose
5362 involve
5363 moderate
5372 ban
5378 clerk
5382 outline
5394 wrap
5397 favor
5399 quiz
5405 occasion
5413 beam
5416 arrive
5428 licence
5432 essay
5433 discipline
5445 trace
5447 shut
5457 consult
5461 mask
5468 inform
5470 coal
5481 announce
5490 rough
5492 tee
5493 refinance
5514 stretch
5525 cricket
5526 beef
5528 stroke
5537 strap
5540 crowd
5543 surf
5555 hook
5557 decline
5561 cord
5563 cloud
5564 facilitate
5570 proceed
5573 knife
5581 adopt
5589 crop
5613 founder
5617 dispute
5623 excerpt
5625 steam
5626 discharge
5629 ace
5635 sing
5637 perfume
5638 carol
5642 restore
5643 stack
5646 sue
5653 hang
5655 curve
5672 photograph
5680 hood
5692 chase
5695 sauce
5697 extract
5707 automobile
5745 mistake
5748 eliminate
5758 canvas
5776 stem
5780 opt
5785 translate
5788 costume
5796 rap
5797 tune
5800 corn
5812 tin
5813 slave
5819 trek
5844 struggle
5849 rat
5862 brad
5874 tail
5876 charm
5882 soup
5884 ranch
5897 nose
5904 alien
5919 resolve
5923 cod
5935 frequent
5936 trim
5951 fleet
5953 bunch
5958 afford
5965 quit
5969 highlight
5970 substitute
5980 gel
5996 grill
5999 redeem
6001 grain
6007 bull
6011 depend
6012 differ
6023 candle
6068 coin
6070 fake
6079 cure
6082 arch
6089 soldier
6094 bomb
6095 harm
6098 spotlight
6100 reset
6101 brush
6102 investigate
6107 assault
6109 spare
6113 tongue
6117 pal
6118 monkey
6119 proportion
6121 skirt
6123 invest
6124 honey
6137 gauge
6143 mat
6156 engage
6166 cheat
6167 bronze
6172 suspect
6198 laugh
6201 destroy
6203 pitch
6218 stamp
6225 tan
6229 mate
6234 butter
6235 pepper
6238 burden
6245 slim
6257 blend
6270 bow
6278 probe
6291 recover
6300 debug
6302 chuck
6311 exclude
6328 prompt
6355 stud
6356 fold
6361 qualify
6367 diagram
6378 attach
6396 divine
6398 approve
6400 compound
6403 syndicate
6407 blast
6413 pole
6416 shield
6417 harvest
6422 constitute
6436 bend
6443 chrome
6447 brake
6466 clone
6470 relay
6476 cab
6484 compete
6488 belong
6503 ferry
6504 rabbit
6507 dam
6516 tire
6530 warrant
6535 josh
6538 rally
6541 strain
6542 drag
6552 rely
6564 adjust
6573 silence
6596 nail
6600 span
6604 joke
6609 satisfy
6611 shelter
6614 manufacture
6620 celebrate
6627 bench
6648 railroad
6655 ash
6656 microwave
6668 tender
6670 foam
6672 paste
6678 preserve
6687 praise
6708 relax
6718 bean
6720 absent
6730 float
6732 bias
6735 bubble
6737 acquire
6748 admit
6750 fancy
6755 tap
6765 companion
6767 analyze
6769 scroll
6770 relate
6772 swim
6775 suffer
6779 invalid
6784 retain
6785 execute
6786 tunnel
6798 beaver
6800 distribute
6826 anchor
6829 parade
6832 trigger
6873 pray
6884 bare
6893 treasure
6894 reload
6896 flame
6902 pit
6908 tile
6911 spell
6914 coordinate
6921 compile
6929 construct
6933 annex
6940 freight
6941 rebate
6943 eclipse
6948 shuttle
6949 aggregate
6954 detect
6976 collapse
6995 cant
7004 argue
7008 bicycle
7011 arise
7020 advise
7040 survive
7046 duck
7047 reveal
7048 canal
7055 cow
7063 dive
7066 drill
7070 protest
7077 nickname
7088 trick
7090 closure
7091 raid
7102 steady
7116 screw
7120 blame
7134 butterfly
7150 sink
7152 assure
7166 grip
7191 cabin
7203 horn
7213 frog
7217 disagree
7224 hazard
7234 anger
7240 interact
7241 snap
7266 swap
7267 exempt
7276 flip
7287 refuse
7298 buzz
7300 nuke
7301 rid
7303 charleston
7306 boom
7307 calm
7313 exhaust
7315 shanghai
7325 fork
7330 batch
7331 organize
7333 alter
7352 rip
7356 amend
7359 reward
7368 sprint
7371 advocate
7393 arrange
7395 bat
7402 breed
7414 invoice
7418 sap
7419 gather
7430 cave
7434 slight
7441 arrest
7464 pursue
7466 overcome
7470 grab
7472 attract
7477 disable
7478 snake
7479 succeed
7500 trap
7502 fool
7508 observe
7513 berry
7514 collar
7517 integrate
7531 terrace
7544 jail
7566 transform
7575 verse
7581 scratch
7593 rocket
7598 lace
7609 encounter
7628 queue
7631 trance
7636 bundle
7637 hammer
7654 strengthen
7655 defend
7664 welsh
7665 belly
7677 sculpture
7704 dig
7705 taxi
7715 thumb
7717 incorporate
7751 twist
7754 stomach
7759 minimize
7763 debut
7767 deny
7773 fraction
7776 cube
7803 fate
7815 porter
7821 rim
7840 spice
7854 prospect
7864 trash
7880 disco
7897 cite
7908 pig
7924 mess
7926 assign
7931 accommodate
7937 premiere
7940 shade
7941 chorus
7958 lyric
7964 approximate
7965 rope
7971 dump
7977 chronicle
7980 grave
7981 legitimate
7990 gentle
7995 hose
8007 freelance
8018 fog
8021 lobby
8027 headline
8029 punch
8038 consist
8050 defeat
8061 tenant
8064 tattoo
8065 shake
8084 tar
8090 fence
8099 shame
8108 remedy
8120 flesh
8121 retreat
8137 marble
8141 hull
8149 blackberry
8160 shed
8161 balloon
8165 ham
8167 tide
8171 partition
8175 funk
8183 reel
8184 sheer
8189 bind
8198 witch
8199 buck
8211 surround
8213 revenge
8227 patrol
8228 smell
8240 reef
8242 divide
8250 deserve
8286 prefix
8289 barrel
8291 nerve
8297 renew
8307 jar
8311 acknowledge
8315 thunder
8316 tent
8317 caution
8326 hack
8327 dare
8332 hawk
8334 rebel
8341 lamb
8386 junk
8388 wax
8393 slope
8408 nut
8414 merit
8420 tackle
8428 retrieve
8438 shaft
8439 lean
8453 drain
8464 panic
8471 excuse
8475 strand
8477 sandwich
8484 cheque
8488 reject
8498 gore
8499 complement
8501 dash
8505 cage
8513 gazette
8522 lap
8530 ping
8537 settle
8544 flux
8562 conclude
8567 propose
8570 lighter
8571 rage
8585 pillow
8596 steal
8597 parcel
8605 predict
8618 flavor
8632 undo
8651 proposition
8657 scout
8669 allowance
8678 duplicate
8681 badge
8688 ballot
8698 squirt
8712 nickel
8728 accomplish
8735 rouge
8747 intend
8751 doom
8761 bite
8776 brook
8784 weed
8786 accent
8791 utilize
8797 unlock
8799 pledge
8804 xerox
8805 dice
8810 dock
8823 dispatch
8827 hint
8828 armor
8831 remark
8862 inquire
8865 laden
8869 merge
8870 privilege
8880 fleece
8883 fare
8886 pierce
8892 bald
8896 frost
8899 mold
8903 yacht
8912 breach
8913 whale
8924 idle
8931 fin
8935 mud
8938 shark
8962 shine
8980 tariff
9004 dip
9006 samba
9009 employ
9018 reprint
9024 flex
9027 wound
9031 hash
9045 marvel
9059 yen
9061 gossip
9067 worm
9076 deaf
9083 remainder
9099 mime
9100 neighbor
9102 elect
9105 concentrate
9106 intimate
9112 chevy
9125 keen
9140 needle
9158 climb
9167 hay
9186 zinc
9187 sacrifice
9195 levy
9212 burke
9233 waterproof
9237 flush
9239 pale
9245 delight
9254 undertake
9255 declare
9263 tear
9264 jewel
9276 nest
9289 cop
9296 dim
9313 remind
9325 disclose
9329 upset
9330 restrict
9335 rebound
9337 mentor
9348 pencil
9349 freeze
9359 concord
9365 lance
9366 ladder
9372 cork
9373 maximize
9385 purse
9390 compromise
9392 optimize
9399 align
9401 bless
9404 crest
9406 triumph
9414 heel
9415 alloy
9428 blanket
9429 bloom
9436 justify
9441 surge
9446 advert
9447 possess
9450 sip
9461 picnic
9464 spank
9471 hollow
9472 vault
9476 groove
9496 orbit
9504 cannon
9521 transmit
9529 hunger
9531 sic
9537 cement
9556 glow
9564 bracket
9593 turtle
9601 ware
9608 wan
9620 jade
9621 scoop
9623 foul
9635 blink
9652 pose
9664 render
9672 sail
9673 forge
9677 brave
9686 impose
9689 telescope
9696 pod
9697 savage
9703 bolt
9704 gage
9713 knock
9714 urge
9728 quilt
9736 refresh
9738 snowboard
9751 cope
9752 shepherd
9755 cradle
9757 mambo
9758 lime
9760 flour
9765 choir
9779 burst
9785 mandate
9796 reproduce
9834 yarn
9835 knit
9837 mug
9839 bother
9848 compute
9857 unwrap
9860 resist
9895 stake
07070100000064000081ED00000000000000000000000163C2D555000085B2000000000000000000000000000000000000003600000000spectre-cli-3.0+git20230114.26c01a5/spectre-cli-tests#!/usr/bin/env bash
cd "${BASH_SOURCE%/*}"


# Tooling
errors=0
keep=${keep:-0}
spectre_expect() {
    local expect=$1; shift
    local args=( "$@" ) OPTIND=1 user= format= redacted=1 purpose=authentication context=
    while getopts :u:U:s:S:t:P:c:a:p:C:f:F:R:vqh arg; do
        case $arg in
            u)  user=$OPTARG ;;
            F)  format=$OPTARG ;;
            R)  redacted=$OPTARG ;;
            t)  type=$OPTARG ;;
            p)  purpose=$OPTARG ;;
            C)  context=$OPTARG ;;
            *)  ;;
        esac
    done
    shift "$((OPTIND-1))"
    local site=$1

    local file=
    if (( ! redacted )); then
        case $format in
            flat)   file=~/.spectre.d/"$user.mpsites" ;;
            json)   file=~/.spectre.d/"$user.mpjson" ;;
        esac
    fi
    [[ -e $file ]] && (( ! keep )) && rm "$file"

    local result=$(./spectre -q "${args[@]}") err=$?

    if (( err )); then
        printf '*'
        printf >&2 "Error (exit %d) spectre%s\n" "$err" "$(printf ' %q' "${args[@]}")"
        return $(( ++errors ))
    fi
    if [[ $result != $expect ]]; then
        printf '!'
        printf >&2 "Bad result (got: %s != expected: %s) spectre%s\n" "$result" "$expect" "$(printf ' %q' "${args[@]}")"
        return $(( ++errors ))
    fi

    local one key password
    if (( ! redacted )); then
        case $format in
            flat)
                while IFS=$'\t' read -r one key password; do
                    read key <<< "$key"
                    [[ $key = $site ]] || continue

                    case $purpose in
                        a*) result=$password ;;
                        i*) [[ ! $type || $type = no* ]] && break
                            read _ _ _ result <<< "$one" ;;
                        r*) break ;;
                    esac

                    if [[ $result != $expect ]]; then
                        printf '#'
                        printf >&2 "Bad mpsites (found: %s != expected: %s) %s (after spectre%s)\n" "$result" "$expect" "$file" "$(printf ' %q' "${args[@]}")"
                        return $(( ++errors ))
                    fi

                    break
                done < "$file"
            ;;
            json)
                if ! hash jq 2>/dev/null; then
                    printf >&2 "Error: jq not installed. Please install through your package manager or from https://stedolan.github.io/jq/\n"
                    exit 1
                fi

                case $purpose in
                    a*) result=$(jq -r ".sites.\"$site\".password") ;;
                    i*) [[ $type && $type != none ]] \
                            && result=$(jq -r ".sites.\"$site\".login_name") \
                            || result=$(jq -r ".user.login_name") ;;
                    r*) result=$(jq -r ".sites.\"$site\".questions.\"$context\".answer") ;;
                esac < "$file"

                if [[ $result != $expect ]]; then
                    printf '#'
                    printf >&2 "Bad mpjson (found: %s != expected: %s) %s (after spectre%s)\n" "$result" "$expect" "$file" "$(printf ' %q' "${args[@]}")"
                    return $(( ++errors ))
                fi
            ;;
        esac
    fi

    printf '.'
    [[ -e $file ]] && (( ! keep )) && rm "$file"
}


#   spectre_tests.xml
##  V3
printf "\nV%d, none: " 3
spectre_expect 'CefoTiciJuba7@'         -Fnone \
    -u 'test' -S 'test'                                                        'test'
spectre_expect 'Tina0#NotaMahu'         -Fnone \
    -u 'tesẗ' -S 'ẗest'                                                        'ẗesẗ'
spectre_expect 'Tina0#NotaMahu'         -Fnone \
    -u 'tesẗ' -S 'ẗest'                                         -C ''          'ẗesẗ'
spectre_expect 'Tina0#NotaMahu'         -Fnone \
    -u 'tesẗ' -S 'ẗest'                     -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'Tina0#NotaMahu'         -Fnone \
    -u 'tesẗ' -S 'ẗest'               -a3   -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'Tina0#NotaMahu'         -Fnone \
    -u 'tesẗ' -S 'ẗest'             -c1 -a3 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'Tina0#NotaMahu'         -Fnone \
    -u 'tesẗ' -S 'ẗest' -tlong      -c1 -a3 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'KovxFipe5:Zatu'         -Fnone \
    -u '⛄'   -S 'ẗest' -tlong      -c1 -a3 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'ModoLalhRapo6#'         -Fnone \
    -u 'tesẗ' -S '⛄'   -tlong      -c1 -a3 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'CudmTecuPune7:'         -Fnone \
    -u 'tesẗ' -S 'ẗest' -tlong      -c1 -a3 -p 'authentication' -C ''          '⛄'
spectre_expect 'mebkovidu'              -Fnone \
    -u 'tesẗ' -S 'ẗest'                     -p 'identification' -C ''          'ẗesẗ'
spectre_expect 'mebkovidu'              -Fnone \
    -u 'tesẗ' -S 'ẗest' -tnone      -c1 -a3 -p 'identification' -C ''          'ẗesẗ'
spectre_expect 'yubfalago'              -Fnone \
    -u 'tesẗ' -S 'ẗest' -tname      -c1 -a3 -p 'identification' -C ''          'ẗesẗ'
spectre_expect 'jip nodwoqude dizo'     -Fnone \
    -u 'tesẗ' -S 'ẗest'                     -p 'recovery'       -C ''          'ẗesẗ'
spectre_expect 'jip nodwoqude dizo'     -Fnone \
    -u 'tesẗ' -S 'ẗest' -tphrase    -c1 -a3 -p 'recovery'       -C ''          'ẗesẗ'
spectre_expect 'dok sorkicoyu ruya'     -Fnone \
    -u 'tesẗ' -S 'ẗest' -tphrase    -c1 -a3 -p 'recovery'       -C 'quesẗion'  'ẗesẗ'
spectre_expect 'j5TJ%G0WWwSMvYb)hr4)'   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tmax       -c1 -a3 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'TinRaz2?'               -Fnone \
    -u 'tesẗ' -S 'ẗest' -tmed       -c1 -a3 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'jad0IQA3'               -Fnone \
    -u 'tesẗ' -S 'ẗest' -tbasic     -c1 -a3 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'Tin0'                   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tshort     -c1 -a3 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect '1710'                   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tpin       -c1 -a3 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'tinraziqu'              -Fnone \
    -u 'tesẗ' -S 'ẗest' -tname      -c1 -a3 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'tinr ziq taghuye zuj'   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tphrase    -c1 -a3 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'HidiLonoFopt9&'         -Fnone \
    -u 'tesẗ' -S 'ẗest' -tlong      -c4294967295 -a3 -p 'authentication' -C '' 'ẗesẗ'

##  V2
printf "\nV%d, none: " 2
spectre_expect 'CefoTiciJuba7@'         -Fnone \
    -u 'test' -S 'test' -tlong      -c1 -a2 -p 'authentication' -C ''          'test'
spectre_expect "HuczFina3'Qatf"         -Fnone \
    -u 'tesẗ' -S 'ẗest' -tlong      -c1 -a2 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'SicrJuwaWaql0#'         -Fnone \
    -u '⛄'   -S 'ẗest' -tlong      -c1 -a2 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'LokaJayp1@Faba'         -Fnone \
    -u 'tesẗ' -S '⛄'   -tlong      -c1 -a2 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'DoqaHulu8:Funh'         -Fnone \
    -u 'tesẗ' -S 'ẗest' -tlong      -c1 -a2 -p 'authentication' -C ''          '⛄'
spectre_expect 'yiyguxoxe'              -Fnone \
    -u 'tesẗ' -S 'ẗest' -tname      -c1 -a2 -p 'identification' -C ''          'ẗesẗ'
spectre_expect 'vu yelyo bat kujavmu'   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tphrase    -c1 -a2 -p 'recovery'       -C ''          'ẗesẗ'
spectre_expect 'ka deqce xad vomacgi'   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tphrase    -c1 -a2 -p 'recovery'       -C 'quesẗion'  'ẗesẗ'
spectre_expect 'wRF$LmB@umWGLWeVlB0-'   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tmax       -c1 -a2 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'HucZuk0!'               -Fnone \
    -u 'tesẗ' -S 'ẗest' -tmed       -c1 -a2 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'wb59VoB5'               -Fnone \
    -u 'tesẗ' -S 'ẗest' -tbasic     -c1 -a2 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'Huc9'                   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tshort     -c1 -a2 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect '2959'                   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tpin       -c1 -a2 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'huczukamo'              -Fnone \
    -u 'tesẗ' -S 'ẗest' -tname      -c1 -a2 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'huc finmokozi fota'     -Fnone \
    -u 'tesẗ' -S 'ẗest' -tphrase    -c1 -a2 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'Mixa1~BulgNijo'         -Fnone \
    -u 'tesẗ' -S 'ẗest' -tlong      -c4294967295 -a2 -p 'authentication' -C '' 'ẗesẗ'

##  V1
printf "\nV%d, none: " 1
spectre_expect 'CefoTiciJuba7@'         -Fnone \
    -u 'test' -S 'test' -tlong      -c1 -a1 -p 'authentication' -C ''          'test'
spectre_expect 'SuxiHoteCuwe3/'         -Fnone \
    -u 'tesẗ' -S 'ẗest' -tlong      -c1 -a1 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'CupaTixu8:Hetu'         -Fnone \
    -u '⛄'   -S 'ẗest' -tlong      -c1 -a1 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'NaqmBanu9+Decs'         -Fnone \
    -u 'tesẗ' -S '⛄'   -tlong      -c1 -a1 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'XowaDokoGeyu2)'         -Fnone \
    -u 'tesẗ' -S 'ẗest' -tlong      -c1 -a1 -p 'authentication' -C ''          '⛄'
spectre_expect 'makmabivo'              -Fnone \
    -u 'tesẗ' -S 'ẗest' -tname      -c1 -a1 -p 'identification' -C ''          'ẗesẗ'
spectre_expect 'je mutbo buf puhiywo'   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tphrase    -c1 -a1 -p 'recovery'       -C ''          'ẗesẗ'
spectre_expect 'ne hapfa dax qamayqo'   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tphrase    -c1 -a1 -p 'recovery'       -C 'quesẗion'  'ẗesẗ'
spectre_expect 'JlZo&eLhqgoxqtJ!NC5/'   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tmax       -c1 -a1 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'SuxHot2*'               -Fnone \
    -u 'tesẗ' -S 'ẗest' -tmed       -c1 -a1 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'Jly28Veh'               -Fnone \
    -u 'tesẗ' -S 'ẗest' -tbasic     -c1 -a1 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'Sux2'                   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tshort     -c1 -a1 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect '4922'                   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tpin       -c1 -a1 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'suxhotito'              -Fnone \
    -u 'tesẗ' -S 'ẗest' -tname      -c1 -a1 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'su hotte pav calewxo'   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tphrase    -c1 -a1 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'Luxn2#JapiXopa'         -Fnone \
    -u 'tesẗ' -S 'ẗest' -tlong      -c4294967295 -a1 -p 'authentication' -C '' 'ẗesẗ'

##  V0
printf "\nV%d, none: " 0
spectre_expect 'GeqoBigiFubh2!'         -Fnone \
    -u 'test' -S 'test' -tlong      -c1 -a0 -p 'authentication' -C ''          'test'
spectre_expect 'WumiZobxGuhe8]'         -Fnone \
    -u 'tesẗ' -S 'ẗest' -tlong      -c1 -a0 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'KuhaXimj8@Zebu'         -Fnone \
    -u '⛄'   -S 'ẗest' -tlong      -c1 -a0 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'CajtFayv9_Pego'         -Fnone \
    -u 'tesẗ' -S '⛄'   -tlong      -c1 -a0 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'QohaPokgYevu2!'         -Fnone \
    -u 'tesẗ' -S 'ẗest' -tlong      -c1 -a0 -p 'authentication' -C ''          '⛄'
spectre_expect 'takxabico'              -Fnone \
    -u 'tesẗ' -S 'ẗest' -tname      -c1 -a0 -p 'identification' -C ''          'ẗesẗ'
spectre_expect 'je tuxfo fut huzivlo'   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tphrase    -c1 -a0 -p 'recovery'       -C ''          'ẗesẗ'
spectre_expect 'ye zahqa lam jatavmo'   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tphrase    -c1 -a0 -p 'recovery'       -C 'quesẗion'  'ẗesẗ'
spectre_expect 'g4@)4SlA#)cJ#ib)vvH3'   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tmax       -c1 -a0 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'Wum7_Xix'               -Fnone \
    -u 'tesẗ' -S 'ẗest' -tmed       -c1 -a0 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'gAo78ARD'               -Fnone \
    -u 'tesẗ' -S 'ẗest' -tbasic     -c1 -a0 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'Wum7'                   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tshort     -c1 -a0 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect '9427'                   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tpin       -c1 -a0 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'wumdoxixo'              -Fnone \
    -u 'tesẗ' -S 'ẗest' -tname      -c1 -a0 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'wu doxbe hac kaselqo'   -Fnone \
    -u 'tesẗ' -S 'ẗest' -tphrase    -c1 -a0 -p 'authentication' -C ''          'ẗesẗ'
spectre_expect 'Pumy7.JadjQoda'         -Fnone \
    -u 'tesẗ' -S 'ẗest' -tlong      -c4294967295 -a0 -p 'authentication' -C '' 'ẗesẗ'

##  V3
printf "\nV%d, flat: " 3
spectre_expect 'IfHuAUUpqpKZDZlNvz8$'   -Fflat -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tmax    -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.max'
spectre_expect 'FamiJirk1)Zehc'         -Fflat -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tlong   -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.long'
spectre_expect 'NofhMusw8+Cebo'         -Fflat -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tlong   -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.⛄'
spectre_expect 'Necx1$LagaRizu'         -Fflat -R0 \
    -u 'tesẗ.v3' -S 'ẗest'          -c4294967295 -a3 -p 'authentication' -C '' 'ẗesẗ.c+a3pa'
spectre_expect 'Poq2)Tey'               -Fflat -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tmed    -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.med'
spectre_expect 'Wr07Okx0'               -Fflat -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tbasic  -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.basic'
spectre_expect 'Bug9'                   -Fflat -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tshort  -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.short'
spectre_expect '3560'                   -Fflat -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tpin    -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.pin'
spectre_expect 'jupxiqepi'              -Fflat -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tname   -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.name'
spectre_expect 'vuh buxtukewo puhe'     -Fflat -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.phrase'
spectre_expect 'Cq5$TfH#OHmPS9yREp7)'   -Fflat -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tmax    -c1 -a3 -p 'identification' -C ''          'ẗesẗ.c1a3pi.max'
spectre_expect 'secgudiho'              -Fflat -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tnone   -c1 -a3 -p 'identification' -C ''          'ẗesẗ.c1a3pi'
spectre_expect 'mophabiwe'              -Fflat -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tname   -c1 -a3 -p 'identification' -C ''          'ẗesẗ.c1a3pi'
spectre_expect 'lA^ul!%9&TD%fj6icT1['   -Fflat -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tmax    -c1 -a3 -p 'recovery'       -C ''          'ẗesẗ.c1a3pr.max'
spectre_expect 'mup wulbezaxa juca'     -Fflat -R0 \
    -u 'tesẗ.v3' -S 'ẗest'          -c1 -a3 -p 'recovery'       -C ''          'ẗesẗ.c1a3pr'
spectre_expect 'molg rux kaczuvi ror'   -Fflat -R0 \
    -u 'tesẗ.v3' -S 'ẗest'          -c1 -a3 -p 'recovery'       -C 'quesẗion'  'ẗesẗ.c1a3pr.quesẗion'

##  V2
printf "\nV%d, flat: " 2
spectre_expect 'i7@0M*DdP4DgD#jJIzyL'   -Fflat -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tmax    -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.max'
spectre_expect 'Lifw5]DablSuga'         -Fflat -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tlong   -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.long'
spectre_expect 'Leja5%RavoZapa'         -Fflat -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tlong   -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.⛄'
spectre_expect 'NejnGazo8?Seqo'         -Fflat -R0 \
    -u 'tesẗ.v2' -S 'ẗest'          -c4294967295 -a2 -p 'authentication' -C '' 'ẗesẗ.c+a2pa'
spectre_expect 'XicSux2&'               -Fflat -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tmed    -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.med'
spectre_expect 'uEY50hcZ'               -Fflat -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tbasic  -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.basic'
spectre_expect 'Jif6'                   -Fflat -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tshort  -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.short'
spectre_expect '4001'                   -Fflat -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tpin    -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.pin'
spectre_expect 'rexmibace'              -Fflat -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tname   -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.name'
spectre_expect 'cez fexlemozo yula'     -Fflat -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.phrase'
spectre_expect 'T8+xi4NMd3HUGdV#GW*%'   -Fflat -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tmax    -c1 -a2 -p 'identification' -C ''          'ẗesẗ.c1a2pi.max'
spectre_expect 'nidcepede'              -Fflat -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tnone   -c1 -a2 -p 'identification' -C ''          'ẗesẗ.c1a2pi'
spectre_expect 'camfibeye'              -Fflat -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tname   -c1 -a2 -p 'identification' -C ''          'ẗesẗ.c1a2pi'
spectre_expect 'YLcoWeBwyiBf2*irFq1.'   -Fflat -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tmax    -c1 -a2 -p 'recovery'       -C ''          'ẗesẗ.c1a2pr.max'
spectre_expect 'ye vemcu keq xepewmi'   -Fflat -R0 \
    -u 'tesẗ.v2' -S 'ẗest'          -c1 -a2 -p 'recovery'       -C ''          'ẗesẗ.c1a2pr'
spectre_expect 'yi qazne tid najuvme'   -Fflat -R0 \
    -u 'tesẗ.v2' -S 'ẗest'          -c1 -a2 -p 'recovery'       -C 'quesẗion'  'ẗesẗ.c1a2pr.quesẗion'

##  V1
printf "\nV%d, flat: " 1
spectre_expect 'a3~AiGkHk)Pgjbb)mk6H'   -Fflat -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tmax    -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.max'
spectre_expect 'Lojz6?VotaJall'         -Fflat -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tlong   -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.long'
spectre_expect 'Yoqu7)NiziFito'         -Fflat -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tlong   -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.⛄'
spectre_expect 'Foha4[TojmXanc'         -Fflat -R0 \
    -u 'tesẗ.v1' -S 'ẗest'          -c4294967295 -a1 -p 'authentication' -C '' 'ẗesẗ.c+a1pa'
spectre_expect 'Hiy3*Zag'               -Fflat -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tmed    -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.med'
spectre_expect 'UJR7HpG0'               -Fflat -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tbasic  -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.basic'
spectre_expect 'Cij7'                   -Fflat -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tshort  -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.short'
spectre_expect '0020'                   -Fflat -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tpin    -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.pin'
spectre_expect 'vadxovezu'              -Fflat -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tname   -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.name'
spectre_expect 'sij jihloyenu kizi'     -Fflat -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.phrase'
spectre_expect 'z2U9)(uQ78TXqtaus)8.'   -Fflat -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tmax    -c1 -a1 -p 'identification' -C ''          'ẗesẗ.c1a1pi.max'
spectre_expect 'wexducuvi'              -Fflat -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tnone   -c1 -a1 -p 'identification' -C ''          'ẗesẗ.c1a1pi'
spectre_expect 'qipberize'              -Fflat -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tname   -c1 -a1 -p 'identification' -C ''          'ẗesẗ.c1a1pi'
spectre_expect 'QMciaKyi1&I*g%tHz99,'   -Fflat -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tmax    -c1 -a1 -p 'recovery'       -C ''          'ẗesẗ.c1a1pr.max'
spectre_expect 'sok torxibute reza'     -Fflat -R0 \
    -u 'tesẗ.v1' -S 'ẗest'          -c1 -a1 -p 'recovery'       -C ''          'ẗesẗ.c1a1pr'
spectre_expect 'xacp qaw qutbece gan'   -Fflat -R0 \
    -u 'tesẗ.v1' -S 'ẗest'          -c1 -a1 -p 'recovery'       -C 'quesẗion'  'ẗesẗ.c1a1pr.quesẗion'

##  V0
printf "\nV%d, flat: " 0
spectre_expect 'b5@ww@Jmb4cAioRbivb)'   -Fflat -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tmax    -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.max'
spectre_expect 'ZuceHazwLojz8!'         -Fflat -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tlong   -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.long'
spectre_expect 'Boxj2!YabePodp'         -Fflat -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tlong   -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.⛄'
spectre_expect 'PeblLuqc6]Cala'         -Fflat -R0 \
    -u 'tesẗ.v0' -S 'ẗest'          -c4294967295 -a0 -p 'authentication' -C '' 'ẗesẗ.c+a0pa'
spectre_expect 'XelQac0@'               -Fflat -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tmed    -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.med'
spectre_expect 'qS07SRc8'               -Fflat -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tbasic  -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.basic'
spectre_expect 'Fih8'                   -Fflat -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tshort  -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.short'
spectre_expect '6121'                   -Fflat -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tpin    -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.pin'
spectre_expect 'rivfutipe'              -Fflat -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tname   -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.name'
spectre_expect 'xir qebdohogo buno'     -Fflat -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.phrase'
spectre_expect "RoAm3bJSvo@#loHSRA6\'"  -Fflat -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tmax    -c1 -a0 -p 'identification' -C ''          'ẗesẗ.c1a0pi.max'
spectre_expect 'biqwaxilu'              -Fflat -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tnone   -c1 -a0 -p 'identification' -C ''          'ẗesẗ.c1a0pi'
spectre_expect 'ragcoxudo'              -Fflat -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tname   -c1 -a0 -p 'identification' -C ''          'ẗesẗ.c1a0pi'
spectre_expect 'm8]SiJHiAS@H@Rbw))34'   -Fflat -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tmax    -c1 -a0 -p 'recovery'       -C ''          'ẗesẗ.c1a0pr.max'
spectre_expect 'kokl hov lowmaya xaf'   -Fflat -R0 \
    -u 'tesẗ.v0' -S 'ẗest'          -c1 -a0 -p 'recovery'       -C ''          'ẗesẗ.c1a0pr'
spectre_expect 'wi zanmu nug zuwidwe'   -Fflat -R0 \
    -u 'tesẗ.v0' -S 'ẗest'          -c1 -a0 -p 'recovery'       -C 'quesẗion'  'ẗesẗ.c1a0pr.quesẗion'


##  V3
printf "\nV%d, json: " 3
spectre_expect 'IfHuAUUpqpKZDZlNvz8$'   -Fjson -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tmax    -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.max'
spectre_expect 'FamiJirk1)Zehc'         -Fjson -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tlong   -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.long'
spectre_expect 'NofhMusw8+Cebo'         -Fjson -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tlong   -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.⛄'
spectre_expect 'Necx1$LagaRizu'         -Fjson -R0 \
    -u 'tesẗ.v3' -S 'ẗest'          -c4294967295 -a3 -p 'authentication' -C '' 'ẗesẗ.c+a3pa'
spectre_expect 'Poq2)Tey'               -Fjson -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tmed    -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.med'
spectre_expect 'Wr07Okx0'               -Fjson -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tbasic  -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.basic'
spectre_expect 'Bug9'                   -Fjson -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tshort  -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.short'
spectre_expect '3560'                   -Fjson -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tpin    -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.pin'
spectre_expect 'jupxiqepi'              -Fjson -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tname   -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.name'
spectre_expect 'vuh buxtukewo puhe'     -Fjson -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tphrase -c1 -a3 -p 'authentication' -C ''          'ẗesẗ.c1a3pa.phrase'
spectre_expect 'Cq5$TfH#OHmPS9yREp7)'   -Fjson -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tmax    -c1 -a3 -p 'identification' -C ''          'ẗesẗ.c1a3pi.max'
spectre_expect 'secgudiho'              -Fjson -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tnone   -c1 -a3 -p 'identification' -C ''          'ẗesẗ.c1a3pi'
spectre_expect 'mophabiwe'              -Fjson -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tname   -c1 -a3 -p 'identification' -C ''          'ẗesẗ.c1a3pi'
spectre_expect 'lA^ul!%9&TD%fj6icT1['   -Fjson -R0 \
    -u 'tesẗ.v3' -S 'ẗest' -tmax    -c1 -a3 -p 'recovery'       -C ''          'ẗesẗ.c1a3pr.max'
spectre_expect 'mup wulbezaxa juca'     -Fjson -R0 \
    -u 'tesẗ.v3' -S 'ẗest'          -c1 -a3 -p 'recovery'       -C ''          'ẗesẗ.c1a3pr'
spectre_expect 'molg rux kaczuvi ror'   -Fjson -R0 \
    -u 'tesẗ.v3' -S 'ẗest'          -c1 -a3 -p 'recovery'       -C 'quesẗion'  'ẗesẗ.c1a3pr.quesẗion'

##  V2
printf "\nV%d, json: " 2
spectre_expect 'i7@0M*DdP4DgD#jJIzyL'   -Fjson -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tmax    -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.max'
spectre_expect 'Lifw5]DablSuga'         -Fjson -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tlong   -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.long'
spectre_expect 'Leja5%RavoZapa'         -Fjson -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tlong   -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.⛄'
spectre_expect 'NejnGazo8?Seqo'         -Fjson -R0 \
    -u 'tesẗ.v2' -S 'ẗest'          -c4294967295 -a2 -p 'authentication' -C '' 'ẗesẗ.c+a2pa'
spectre_expect 'XicSux2&'               -Fjson -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tmed    -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.med'
spectre_expect 'uEY50hcZ'               -Fjson -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tbasic  -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.basic'
spectre_expect 'Jif6'                   -Fjson -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tshort  -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.short'
spectre_expect '4001'                   -Fjson -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tpin    -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.pin'
spectre_expect 'rexmibace'              -Fjson -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tname   -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.name'
spectre_expect 'cez fexlemozo yula'     -Fjson -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tphrase -c1 -a2 -p 'authentication' -C ''          'ẗesẗ.c1a2pa.phrase'
spectre_expect 'T8+xi4NMd3HUGdV#GW*%'   -Fjson -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tmax    -c1 -a2 -p 'identification' -C ''          'ẗesẗ.c1a2pi.max'
spectre_expect 'nidcepede'              -Fjson -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tnone   -c1 -a2 -p 'identification' -C ''          'ẗesẗ.c1a2pi'
spectre_expect 'camfibeye'              -Fjson -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tname   -c1 -a2 -p 'identification' -C ''          'ẗesẗ.c1a2pi'
spectre_expect 'YLcoWeBwyiBf2*irFq1.'   -Fjson -R0 \
    -u 'tesẗ.v2' -S 'ẗest' -tmax    -c1 -a2 -p 'recovery'       -C ''          'ẗesẗ.c1a2pr.max'
spectre_expect 'ye vemcu keq xepewmi'   -Fjson -R0 \
    -u 'tesẗ.v2' -S 'ẗest'          -c1 -a2 -p 'recovery'       -C ''          'ẗesẗ.c1a2pr'
spectre_expect 'yi qazne tid najuvme'   -Fjson -R0 \
    -u 'tesẗ.v2' -S 'ẗest'          -c1 -a2 -p 'recovery'       -C 'quesẗion'  'ẗesẗ.c1a2pr.quesẗion'

##  V1
printf "\nV%d, json: " 1
spectre_expect 'a3~AiGkHk)Pgjbb)mk6H'   -Fjson -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tmax    -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.max'
spectre_expect 'Lojz6?VotaJall'         -Fjson -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tlong   -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.long'
spectre_expect 'Yoqu7)NiziFito'         -Fjson -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tlong   -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.⛄'
spectre_expect 'Foha4[TojmXanc'         -Fjson -R0 \
    -u 'tesẗ.v1' -S 'ẗest'          -c4294967295 -a1 -p 'authentication' -C '' 'ẗesẗ.c+a1pa'
spectre_expect 'Hiy3*Zag'               -Fjson -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tmed    -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.med'
spectre_expect 'UJR7HpG0'               -Fjson -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tbasic  -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.basic'
spectre_expect 'Cij7'                   -Fjson -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tshort  -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.short'
spectre_expect '0020'                   -Fjson -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tpin    -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.pin'
spectre_expect 'vadxovezu'              -Fjson -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tname   -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.name'
spectre_expect 'sij jihloyenu kizi'     -Fjson -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tphrase -c1 -a1 -p 'authentication' -C ''          'ẗesẗ.c1a1pa.phrase'
spectre_expect 'z2U9)(uQ78TXqtaus)8.'   -Fjson -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tmax    -c1 -a1 -p 'identification' -C ''          'ẗesẗ.c1a1pi.max'
spectre_expect 'wexducuvi'              -Fjson -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tnone   -c1 -a1 -p 'identification' -C ''          'ẗesẗ.c1a1pi'
spectre_expect 'qipberize'              -Fjson -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tname   -c1 -a1 -p 'identification' -C ''          'ẗesẗ.c1a1pi'
spectre_expect 'QMciaKyi1&I*g%tHz99,'   -Fjson -R0 \
    -u 'tesẗ.v1' -S 'ẗest' -tmax    -c1 -a1 -p 'recovery'       -C ''          'ẗesẗ.c1a1pr.max'
spectre_expect 'sok torxibute reza'     -Fjson -R0 \
    -u 'tesẗ.v1' -S 'ẗest'          -c1 -a1 -p 'recovery'       -C ''          'ẗesẗ.c1a1pr'
spectre_expect 'xacp qaw qutbece gan'   -Fjson -R0 \
    -u 'tesẗ.v1' -S 'ẗest'          -c1 -a1 -p 'recovery'       -C 'quesẗion'  'ẗesẗ.c1a1pr.quesẗion'

##  V0
printf "\nV%d, json: " 0
spectre_expect 'b5@ww@Jmb4cAioRbivb)'   -Fjson -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tmax    -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.max'
spectre_expect 'ZuceHazwLojz8!'         -Fjson -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tlong   -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.long'
spectre_expect 'Boxj2!YabePodp'         -Fjson -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tlong   -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.⛄'
spectre_expect 'PeblLuqc6]Cala'         -Fjson -R0 \
    -u 'tesẗ.v0' -S 'ẗest'          -c4294967295 -a0 -p 'authentication' -C '' 'ẗesẗ.c+a0pa'
spectre_expect 'XelQac0@'               -Fjson -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tmed    -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.med'
spectre_expect 'qS07SRc8'               -Fjson -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tbasic  -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.basic'
spectre_expect 'Fih8'                   -Fjson -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tshort  -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.short'
spectre_expect '6121'                   -Fjson -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tpin    -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.pin'
spectre_expect 'rivfutipe'              -Fjson -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tname   -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.name'
spectre_expect 'xir qebdohogo buno'     -Fjson -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tphrase -c1 -a0 -p 'authentication' -C ''          'ẗesẗ.c1a0pa.phrase'
spectre_expect "RoAm3bJSvo@#loHSRA6\'"  -Fjson -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tmax    -c1 -a0 -p 'identification' -C ''          'ẗesẗ.c1a0pi.max'
spectre_expect 'biqwaxilu'              -Fjson -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tnone   -c1 -a0 -p 'identification' -C ''          'ẗesẗ.c1a0pi'
spectre_expect 'ragcoxudo'              -Fjson -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tname   -c1 -a0 -p 'identification' -C ''          'ẗesẗ.c1a0pi'
spectre_expect 'm8]SiJHiAS@H@Rbw))34'   -Fjson -R0 \
    -u 'tesẗ.v0' -S 'ẗest' -tmax    -c1 -a0 -p 'recovery'       -C ''          'ẗesẗ.c1a0pr.max'
spectre_expect 'kokl hov lowmaya xaf'   -Fjson -R0 \
    -u 'tesẗ.v0' -S 'ẗest'          -c1 -a0 -p 'recovery'       -C ''          'ẗesẗ.c1a0pr'
spectre_expect 'wi zanmu nug zuwidwe'   -Fjson -R0 \
    -u 'tesẗ.v0' -S 'ẗest'          -c1 -a0 -p 'recovery'       -C 'quesẗion'  'ẗesẗ.c1a0pr.quesẗion'


# Finish
printf '\nDone!\n'
exit "$errors"
07070100000065000081A400000000000000000000000163C2D55500000267000000000000000000000000000000000000003300000000spectre-cli-3.0+git20230114.26c01a5/spectre.bashrc## Added by Spectre
source bashlib
spectre() {
    _copy() {
        if hash pbcopy 2>/dev/null; then
            pbcopy
        elif hash xclip 2>/dev/null; then
            xclip -selection clip
        else
            cat; echo 2>/dev/null
            return
        fi
        echo >&2 "Copied!"
    }

    # Empty the clipboard
    :| _copy 2>/dev/null

    # Ask for the user's name and password if not yet known.
    SPECTRE_USERNAME=${SPECTRE_USERNAME:-$(ask 'Your Full Name:')}

    # Start Spectre and copy the output.
    printf %s "$(SPECTRE_USERNAME=$SPECTRE_USERNAME command spectre "$@")" | _copy
}
07070100000066000081A400000000000000000000000163C2D55500000DCF000000000000000000000000000000000000003C00000000spectre-cli-3.0+git20230114.26c01a5/spectre.completion.bash#!/usr/bin/env bash
source bashcomplib

# completing the 'spectre' command.
_comp_spectre() {
    local optarg= cword=${COMP_WORDS[COMP_CWORD]} pcword=
    (( COMP_CWORD )) && pcword=${COMP_WORDS[COMP_CWORD - 1]} 

    case $pcword in
        -u) # complete user names.
            COMPREPLY=( ~/.spectre.d/*.mpsites )
            [[ -e $COMPREPLY ]] || COMPREPLY=()
            COMPREPLY=( "${COMPREPLY[@]##*/}" ) COMPREPLY=( "${COMPREPLY[@]%.mpsites}" )
            ;;
        -t) # complete types.
            COMPREPLY=( maximum long medium basic short pin name phrase )
            ;;
        -c) # complete counter.
            COMPREPLY=( 1 )
            ;;
        -V) # complete versions.
            COMPREPLY=( 0 1 2 3 )
            ;;
        -v) # complete variants.
            COMPREPLY=( password login answer )
            ;;
        -C) # complete context.
            ;;
        *)
            # previous word is not an option we can complete, complete site name (or option if leading -)
            if [[ $cword = -* ]]; then
                COMPREPLY=( -u -t -c -V -v -C )
            else
                local w userName=$MP_USERNAME
                for (( w = 0; w < ${#COMP_WORDS[@]}; ++w )); do
                    [[ ${COMP_WORDS[w]} = -u ]] && userName=$(xargs <<< "${COMP_WORDS[w + 1]}") && break
                done
                if [[ -e ~/.spectre.d/"$userName.mpsites" ]]; then
                    IFS=$'\n' read -d '' -ra COMPREPLY < <(awk -F$'\t' '!/^ *#/{sub(/^ */, "", $2); print $2}' ~/.spectre.d/"$userName.mpsites")
                    printf -v _comp_title 'Sites for %s' "$userName"
                else
                    # Default list from the Alexa Top 500
                    COMPREPLY=(
                        163.com 360.cn 9gag.com adobe.com alibaba.com aliexpress.com amazon.com
                        apple.com archive.org ask.com baidu.com battle.net booking.com buzzfeed.com
                        chase.com cnn.com comcast.net craigslist.org dailymotion.com dell.com
                        deviantart.com diply.com disqus.com dropbox.com ebay.com engadget.com
                        espn.go.com evernote.com facebook.com fedex.com feedly.com flickr.com
                        flipkart.com github.com gizmodo.com go.com goodreads.com google.com
                        huffingtonpost.com hulu.com ign.com ikea.com imdb.com imgur.com
                        indiatimes.com instagram.com jd.com kickass.to kickstarter.com linkedin.com
                        live.com livedoor.com mail.ru mozilla.org naver.com netflix.com newegg.com
                        nicovideo.jp nytimes.com pandora.com paypal.com pinterest.com pornhub.com
                        qq.com rakuten.co.jp reddit.com redtube.com shutterstock.com skype.com
                        soso.com spiegel.de spotify.com stackexchange.com steampowered.com
                        stumbleupon.com taobao.com target.com thepiratebay.se tmall.com
                        torrentz.eu tripadvisor.com tube8.com tubecup.com tudou.com tumblr.com
                        twitter.com uol.com.br vimeo.com vk.com walmart.com weibo.com whatsapp.com
                        wikia.com wikipedia.org wired.com wordpress.com xhamster.com xinhuanet.com
                        xvideos.com yahoo.com yandex.ru yelp.com youku.com youporn.com ziddu.com
                    )
                fi
            fi ;;
    esac
    _comp_finish_completions
}

#complete -F _show_args spectre
complete -o nospace -F _comp_spectre spectre
07070100000067000081A400000000000000000000000163C2D55500002BE3000000000000000000000000000000000000003600000000spectre-cli-3.0+git20230114.26c01a5/spectre_tests.xml<tests>
    <!-- Default values for all parameters. -->
    <case id="default">
        <algorithm>-1</algorithm>
        <userName>Robert Lee Mitchell</userName>
        <userSecret>banana colored duckling</userSecret>
        <keyID>98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302</keyID>
        <siteName>spectre.app</siteName>
        <resultType>Long</resultType>
        <keyCounter>1</keyCounter>
        <keyPurpose>Authentication</keyPurpose>
        <result><!-- abstract --></result>
    </case>

    <!-- Algorithm 3 -->
    <case id="v3" parent="default">
        <algorithm>3</algorithm>
        <result>ReqoCenuXonu1?</result>
    </case>
    <case id="v3_mb_userName" parent="v3">
        <userName>⛄</userName>
        <keyID>1717AA1F9BF5BA56CD0965CDA3D78E6D2E6A1EA8C067A8EA621F3DDAD4A87EB8</keyID>
        <result>Wewa9$YaqdDaje</result>
    </case>
    <case id="v3_mb_userSecret" parent="v3">
        <userSecret>⛄</userSecret>
        <keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
        <result>TeweBacg0$Tobe</result>
    </case>
    <case id="v3_mb_siteName" parent="v3">
        <siteName>⛄</siteName>
        <result>LiheCuwhSerz6)</result>
    </case>
    <case id="v3_loginName" parent="v3">
        <keyPurpose>Identification</keyPurpose>
        <resultType>Name</resultType>
        <result>wesruqori</result>
    </case>
    <case id="v3_securityAnswer" parent="v3">
        <keyPurpose>Recovery</keyPurpose>
        <resultType>Phrase</resultType>
        <result>zowp quy roxzuyu qim</result>
    </case>
    <case id="v3_securityAnswer_context" parent="v3_securityAnswer">
        <keyContext>question</keyContext>
        <result>lek yubgiguko ruzo</result>
    </case>
    <case id="v3_type_maximum" parent="v3">
        <resultType>Maximum</resultType>
        <result>FB22U#U*LPFWlWxaxK2.</result>
    </case>
    <case id="v3_type_medium" parent="v3">
        <resultType>Medium</resultType>
        <result>ReqMon0)</result>
    </case>
    <case id="v3_type_basic" parent="v3">
        <resultType>Basic</resultType>
        <result>FLI88cDK</result>
    </case>
    <case id="v3_type_short" parent="v3">
        <resultType>Short</resultType>
        <result>Req8</result>
    </case>
    <case id="v3_type_pin" parent="v3">
        <resultType>PIN</resultType>
        <result>3648</result>
    </case>
    <case id="v3_type_name" parent="v3">
        <resultType>Name</resultType>
        <result>reqmonajo</result>
    </case>
    <case id="v3_type_phrase" parent="v3">
        <resultType>Phrase</resultType>
        <result>re monnu mit jededda</result>
    </case>
    <case id="v3_type_personal" parent="v3">
        <resultType>Personal</resultType>
        <resultParam>IfzlLN4J7XNazwSa17xB7A==</resultParam>
        <result>password</result>
    </case>
    <case id="v3_type_personal_mb" parent="v3">
        <resultType>Personal</resultType>
        <resultParam>det+TQ+yVcDxLZ30EE7hMw==</resultParam>
        <result>⛄</result>
    </case>
    <case id="v3_counter_ceiling" parent="v3">
        <keyCounter>4294967295</keyCounter>
        <result>ZurdYoda6:Jogs</result>
    </case>

    <!-- Algorithm 2 -->
    <case id="v2" parent="default">
        <algorithm>2</algorithm>
        <result>ReqoCenuXonu1?</result>
    </case>
    <case id="v2_mb_userName" parent="v2">
        <userName>⛄</userName>
        <keyID>4D5851D0B093D65DE0CF13D94877270468C0B65A6E42CA50D393AC9B99C457B5</keyID>
        <result>KoyvTocoVeyx8*</result>
    </case>
    <case id="v2_mb_userSecret" parent="v2">
        <userSecret>⛄</userSecret>
        <keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
        <result>TeweBacg0$Tobe</result>
    </case>
    <case id="v2_mb_siteName" parent="v2">
        <siteName>⛄</siteName>
        <result>LiheCuwhSerz6)</result>
    </case>
    <case id="v2_loginName" parent="v2">
        <keyPurpose>Identification</keyPurpose>
        <resultType>Name</resultType>
        <result>wesruqori</result>
    </case>
    <case id="v2_securityAnswer" parent="v2">
        <keyPurpose>Recovery</keyPurpose>
        <resultType>Phrase</resultType>
        <result>zowp quy roxzuyu qim</result>
    </case>
    <case id="v2_securityAnswer_context" parent="v2_securityAnswer">
        <keyContext>question</keyContext>
        <result>lek yubgiguko ruzo</result>
    </case>
    <case id="v2_type_maximum" parent="v2">
        <resultType>Maximum</resultType>
        <result>FB22U#U*LPFWlWxaxK2.</result>
    </case>
    <case id="v2_type_medium" parent="v2">
        <resultType>Medium</resultType>
        <result>ReqMon0)</result>
    </case>
    <case id="v2_type_basic" parent="v2">
        <resultType>Basic</resultType>
        <result>FLI88cDK</result>
    </case>
    <case id="v2_type_short" parent="v2">
        <resultType>Short</resultType>
        <result>Req8</result>
    </case>
    <case id="v2_type_pin" parent="v2">
        <resultType>PIN</resultType>
        <result>3648</result>
    </case>
    <case id="v2_type_name" parent="v2">
        <resultType>Name</resultType>
        <result>reqmonajo</result>
    </case>
    <case id="v2_type_phrase" parent="v2">
        <resultType>Phrase</resultType>
        <result>re monnu mit jededda</result>
    </case>
    <case id="v2_type_personal" parent="v2">
        <resultType>Personal</resultType>
        <resultParam>IfzlLN4J7XNazwSa17xB7A==</resultParam>
        <result>password</result>
    </case>
    <case id="v2_type_personal_mb" parent="v2">
        <resultType>Personal</resultType>
        <resultParam>det+TQ+yVcDxLZ30EE7hMw==</resultParam>
        <result>⛄</result>
    </case>
    <case id="v2_counter_ceiling" parent="v2">
        <keyCounter>4294967295</keyCounter>
        <result>ZurdYoda6:Jogs</result>
    </case>

    <!-- Algorithm 1 -->
    <case id="v1" parent="default">
        <algorithm>1</algorithm>
        <result>ReqoCenuXonu1?</result>
    </case>
    <case id="v1_mb_userName" parent="v1">
        <userName>⛄</userName>
        <keyID>4D5851D0B093D65DE0CF13D94877270468C0B65A6E42CA50D393AC9B99C457B5</keyID>
        <result>KoyvTocoVeyx8*</result>
    </case>
    <case id="v1_mb_userSecret" parent="v1">
        <userSecret>⛄</userSecret>
        <keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
        <result>TeweBacg0$Tobe</result>
    </case>
    <case id="v1_mb_siteName" parent="v1">
        <siteName>⛄</siteName>
        <result>WawiYarp2@Kodh</result>
    </case>
    <case id="v1_loginName" parent="v1">
        <keyPurpose>Identification</keyPurpose>
        <resultType>Name</resultType>
        <result>wesruqori</result>
    </case>
    <case id="v1_securityAnswer" parent="v1">
        <keyPurpose>Recovery</keyPurpose>
        <resultType>Phrase</resultType>
        <result>zowp quy roxzuyu qim</result>
    </case>
    <case id="v1_securityAnswer_context" parent="v1_securityAnswer">
        <keyContext>question</keyContext>
        <result>lek yubgiguko ruzo</result>
    </case>
    <case id="v1_type_maximum" parent="v1">
        <resultType>Maximum</resultType>
        <result>FB22U#U*LPFWlWxaxK2.</result>
    </case>
    <case id="v1_type_medium" parent="v1">
        <resultType>Medium</resultType>
        <result>ReqMon0)</result>
    </case>
    <case id="v1_type_basic" parent="v1">
        <resultType>Basic</resultType>
        <result>FLI88cDK</result>
    </case>
    <case id="v1_type_short" parent="v1">
        <resultType>Short</resultType>
        <result>Req8</result>
    </case>
    <case id="v1_type_pin" parent="v1">
        <resultType>PIN</resultType>
        <result>3648</result>
    </case>
    <case id="v1_type_name" parent="v1">
        <resultType>Name</resultType>
        <result>reqmonajo</result>
    </case>
    <case id="v1_type_phrase" parent="v1">
        <resultType>Phrase</resultType>
        <result>re monnu mit jededda</result>
    </case>
    <case id="v1_type_personal" parent="v1">
        <resultType>Personal</resultType>
        <resultParam>IfzlLN4J7XNazwSa17xB7A==</resultParam>
        <result>password</result>
    </case>
    <case id="v1_type_personal_mb" parent="v1">
        <resultType>Personal</resultType>
        <resultParam>det+TQ+yVcDxLZ30EE7hMw==</resultParam>
        <result>⛄</result>
    </case>
    <case id="v1_counter_ceiling" parent="v1">
        <keyCounter>4294967295</keyCounter>
        <result>ZurdYoda6:Jogs</result>
    </case>

    <!-- Algorithm 0 -->
    <case id="v0" parent="default">
        <algorithm>0</algorithm>
        <result>NejoKecuMocx6@</result>
    </case>
    <case id="v0_mb_userName" parent="v0">
        <userName>⛄</userName>
        <keyID>4D5851D0B093D65DE0CF13D94877270468C0B65A6E42CA50D393AC9B99C457B5</keyID>
        <result>KovgXogoCevq8!</result>
    </case>
    <case id="v0_mb_userSecret" parent="v0">
        <userSecret>⛄</userSecret>
        <keyID>351432B8528A5ABECAB768CA95015097DE76FE14C41E10AF36C67DCFB8917E08</keyID>
        <result>XeleBagv5]Xobe</result>
    </case>
    <case id="v0_mb_siteName" parent="v0">
        <siteName>⛄</siteName>
        <result>HahiVana2@Nole</result>
    </case>
    <case id="v0_loginName" parent="v0">
        <keyPurpose>Identification</keyPurpose>
        <resultType>Name</resultType>
        <result>lewrujori</result>
    </case>
    <case id="v0_securityAnswer" parent="v0">
        <keyPurpose>Recovery</keyPurpose>
        <resultType>Phrase</resultType>
        <result>zolh juv romzuvu jix</result>
    </case>
    <case id="v0_securityAnswer_context" parent="v0_securityAnswer">
        <keyContext>question</keyContext>
        <result>pek vubvivuno ruwo</result>
    </case>
    <case id="v0_type_maximum" parent="v0">
        <resultType>Maximum</resultType>
        <result>AiAl3i3SAw3AwAJRJ)2_</result>
    </case>
    <case id="v0_type_medium" parent="v0">
        <resultType>Medium</resultType>
        <result>NejXoc5@</result>
    </case>
    <case id="v0_type_basic" parent="v0">
        <resultType>Basic</resultType>
        <result>AWr33Rzh</result>
    </case>
    <case id="v0_type_short" parent="v0">
        <resultType>Short</resultType>
        <result>Nej3</result>
    </case>
    <case id="v0_type_pin" parent="v0">
        <resultType>PIN</resultType>
        <result>8143</result>
    </case>
    <case id="v0_type_name" parent="v0">
        <resultType>Name</resultType>
        <result>nejxocafo</result>
    </case>
    <case id="v0_type_phrase" parent="v0">
        <resultType>Phrase</resultType>
        <result>ne xoccu tix fepepla</result>
    </case>
    <case id="v0_type_personal" parent="v0">
        <resultType>Personal</resultType>
        <resultParam>IfzlLN4J7XNazwSa17xB7A==</resultParam>
        <result>password</result>
    </case>
    <case id="v0_type_personal_mb" parent="v0">
        <resultType>Personal</resultType>
        <resultParam>det+TQ+yVcDxLZ30EE7hMw==</resultParam>
        <result>⛄</result>
    </case>
    <case id="v0_counter_ceiling" parent="v0">
        <keyCounter>4294967295</keyCounter>
        <result>WunoRopz1!Fovw</result>
    </case>
</tests>

07070100000068000041ED00000000000000000000000263C2D55500000000000000000000000000000000000000000000002800000000spectre-cli-3.0+git20230114.26c01a5/src07070100000069000081A400000000000000000000000163C2D55500002594000000000000000000000000000000000000003100000000spectre-cli-3.0+git20230114.26c01a5/src/bcrypt.c/*	$OpenBSD: bcrypt.c,v 1.57 2016/08/26 08:25:02 guenther Exp $	*/

/*
 * Copyright (c) 2014 Ted Unangst <tedu@openbsd.org>
 * Copyright (c) 1997 Niels Provos <provos@umich.edu>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/* This password hashing algorithm was designed by David Mazieres
 * <dm@lcs.mit.edu> and works as follows:
 *
 * 1. state := InitState ()
 * 2. state := ExpandKey (state, salt, password)
 * 3. REPEAT rounds:
 *      state := ExpandKey (state, 0, password)
 *	state := ExpandKey (state, 0, salt)
 * 4. ctext := "OrpheanBeholderScryDoubt"
 * 5. REPEAT 64:
 * 	ctext := Encrypt_ECB (state, ctext);
 * 6. RETURN Concatenate (salt, ctext);
 *
 */

#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "blf.h"
#include "blowfish.c"
#include "spectre-util.h"

/* This implementation is adaptable to current computing power.
 * You can have up to 2^31 rounds which should be enough for some
 * time to come.
 */

#define BCRYPT_VERSION '2'
#define BCRYPT_MAXSALT 16    /* Precomputation is just so nice */
#define BCRYPT_WORDS 6        /* Ciphertext words */
#define BCRYPT_MINLOGROUNDS 4    /* we have log2(rounds) in salt */

#define    BCRYPT_SALTSPACE    (7 + (BCRYPT_MAXSALT * 4 + 2) / 3 + 1)
#define    BCRYPT_HASHSPACE    61

static int encode_base64(char *, const uint8_t *, size_t);
static int decode_base64(uint8_t *, size_t, const char *);

/*
 * Generates a salt for this version of crypt.
 */
static int
bcrypt_initsalt(int log_rounds, uint8_t *salt, size_t saltbuflen) {

    uint8_t csalt[BCRYPT_MAXSALT];

    if (saltbuflen < BCRYPT_SALTSPACE) {
        errno = EINVAL;
        return -1;
    }

    // Switching to PRNG rand() for portability.  Do not use in production code.
    //arc4random_buf( csalt, sizeof( csalt ) );
    srand( (unsigned)time( NULL ) );
    for (int s = 0; s < sizeof( csalt ); ++s)
        csalt[s] = (uint8_t)rand();

    if (log_rounds < 4)
        log_rounds = 4;
    else if (log_rounds > 31)
        log_rounds = 31;

    snprintf( (char *)salt, saltbuflen, "$2b$%2.2u$", log_rounds );
    encode_base64( (char *)salt + 7, csalt, sizeof( csalt ) );

    return 0;
}

/*
 * the core bcrypt function
 */
static int
bcrypt_hashpass(const char *key, const uint8_t *salt, char *encrypted,
        size_t encryptedlen) {

    blf_ctx state;
    uint32_t rounds, i, k;
    uint16_t j;
    size_t key_len;
    uint8_t salt_len, logr, minor;
    uint8_t ciphertext[4 * BCRYPT_WORDS] = "OrpheanBeholderScryDoubt";
    uint8_t csalt[BCRYPT_MAXSALT];
    uint32_t cdata[BCRYPT_WORDS];

    if (encryptedlen < BCRYPT_HASHSPACE)
        goto inval;

    /* Check and discard "$" identifier */
    if (salt[0] != '$')
        goto inval;
    salt += 1;

    if (salt[0] != BCRYPT_VERSION)
        goto inval;

    /* Check for minor versions */
    switch ((minor = salt[1])) {
        case 'a':
            key_len = (uint8_t)(strlen( key ) + 1);
            break;
        case 'b':
            /* strlen() returns a size_t, but the function calls
             * below result in implicit casts to a narrower integer
             * type, so cap key_len at the actual maximum supported
             * length here to avoid integer wraparound */
            key_len = strlen( key );
            if (key_len > 72)
                key_len = 72;
            key_len++; /* include the NUL */
            break;
        default:
            goto inval;
    }
    if (salt[2] != '$')
        goto inval;
    /* Discard version + "$" identifier */
    salt += 3;

    /* Check and parse num rounds */
    if (!isdigit( (unsigned char)salt[0] ) ||
        !isdigit( (unsigned char)salt[1] ) || salt[2] != '$')
        goto inval;
    logr = (uint8_t)((salt[1] - '0') + ((salt[0] - '0') * 10));
    if (logr < BCRYPT_MINLOGROUNDS || logr > 31)
        goto inval;
    /* Computer power doesn't increase linearly, 2^x should be fine */
    rounds = 1U << logr;

    /* Discard num rounds + "$" identifier */
    salt += 3;

    if (strlen( (char *)salt ) * 3 / 4 < BCRYPT_MAXSALT)
        goto inval;

    /* We dont want the base64 salt but the raw data */
    if (decode_base64( csalt, BCRYPT_MAXSALT, (char *)salt ))
        goto inval;
    salt_len = BCRYPT_MAXSALT;

    /* Setting up S-Boxes and Subkeys */
    Blowfish_initstate( &state );
    Blowfish_expandstate( &state, csalt, salt_len,
            (uint8_t *)key, (uint16_t)key_len );
    for (k = 0; k < rounds; k++) {
        Blowfish_expand0state( &state, (uint8_t *)key, (uint16_t)key_len );
        Blowfish_expand0state( &state, csalt, salt_len );
    }

    /* This can be precomputed later */
    j = 0;
    for (i = 0; i < BCRYPT_WORDS; i++)
        cdata[i] = Blowfish_stream2word( ciphertext, 4 * BCRYPT_WORDS, &j );

    /* Now do the encryption */
    for (k = 0; k < 64; k++)
        blf_enc( &state, cdata, BCRYPT_WORDS / 2 );

    for (i = 0; i < BCRYPT_WORDS; i++) {
        ciphertext[4 * i + 3] = (uint8_t)(cdata[i] & 0xff);
        cdata[i] = cdata[i] >> 8;
        ciphertext[4 * i + 2] = (uint8_t)(cdata[i] & 0xff);
        cdata[i] = cdata[i] >> 8;
        ciphertext[4 * i + 1] = (uint8_t)(cdata[i] & 0xff);
        cdata[i] = cdata[i] >> 8;
        ciphertext[4 * i + 0] = (uint8_t)(cdata[i] & 0xff);
    }

    snprintf( encrypted, 8, "$2%c$%2.2u$", minor, logr );
    encode_base64( encrypted + 7, csalt, BCRYPT_MAXSALT );
    encode_base64( encrypted + 7 + 22, ciphertext, 4 * BCRYPT_WORDS - 1 );
    spectre_zero( &state, sizeof( state ) );
    spectre_zero( ciphertext, sizeof( ciphertext ) );
    spectre_zero( csalt, sizeof( csalt ) );
    spectre_zero( cdata, sizeof( cdata ) );
    return 0;

    inval:
    errno = EINVAL;
    return -1;
}

/*
 * internal utilities
 */
static const uint8_t Base64Code[] =
        "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

static const uint8_t index_64[128] = {
        255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
        255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
        255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
        255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
        255, 255, 255, 255, 255, 255, 0, 1, 54, 55,
        56, 57, 58, 59, 60, 61, 62, 63, 255, 255,
        255, 255, 255, 255, 255, 2, 3, 4, 5, 6,
        7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
        17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
        255, 255, 255, 255, 255, 255, 28, 29, 30,
        31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
        41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
        51, 52, 53, 255, 255, 255, 255, 255
};
#define CHAR64(c)  ( (c) > 127 ? (uint8_t)255 : index_64[(c)])

/*
 * read buflen (after decoding) bytes of data from b64data
 */
static int
decode_base64(uint8_t *buffer, size_t len, const char *b64data) {

    uint8_t *bp = buffer;
    const uint8_t *p = (uint8_t *)b64data;
    uint8_t c1, c2, c3, c4;

    while (bp < buffer + len) {
        c1 = CHAR64( *p );
        /* Invalid data */
        if (c1 == 255)
            return -1;

        c2 = CHAR64( *(p + 1) );
        if (c2 == 255)
            return -1;

        *bp++ = (uint8_t)((c1 << 2) | ((c2 & 0x30) >> 4));
        if (bp >= buffer + len)
            break;

        c3 = CHAR64( *(p + 2) );
        if (c3 == 255)
            return -1;

        *bp++ = (uint8_t)(((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2));
        if (bp >= buffer + len)
            break;

        c4 = CHAR64( *(p + 3) );
        if (c4 == 255)
            return -1;
        *bp++ = (uint8_t)(((c3 & 0x03) << 6) | c4);

        p += 4;
    }
    return 0;
}

/*
 * Turn len bytes of data into base64 encoded data.
 * This works without = padding.
 */
static int
encode_base64(char *b64buffer, const uint8_t *data, size_t len) {

    uint8_t *bp = (uint8_t *)b64buffer;
    const uint8_t *p = data;
    uint8_t c1, c2;

    while (p < data + len) {
        c1 = *p++;
        *bp++ = Base64Code[(c1 >> 2)];
        c1 = (uint8_t)((c1 & 0x03) << 4);
        if (p >= data + len) {
            *bp++ = Base64Code[c1];
            break;
        }
        c2 = *p++;
        c1 |= (c2 >> 4) & 0x0f;
        *bp++ = Base64Code[c1];
        c1 = (uint8_t)((c2 & 0x0f) << 2);
        if (p >= data + len) {
            *bp++ = Base64Code[c1];
            break;
        }
        c2 = *p++;
        c1 |= (c2 >> 6) & 0x03;
        *bp++ = Base64Code[c1];
        *bp++ = Base64Code[c2 & 0x3f];
    }
    *bp = '\0';
    return 0;
}

/*
 * classic interface
 */
static uint8_t *
bcrypt_gensalt(uint8_t log_rounds) {

    static uint8_t gsalt[BCRYPT_SALTSPACE];

    bcrypt_initsalt( log_rounds, gsalt, sizeof( gsalt ) );

    return gsalt;
}

static char *
bcrypt(const char *pass, const uint8_t *salt) {

    static char gencrypted[BCRYPT_HASHSPACE];

    if (bcrypt_hashpass( pass, salt, gencrypted, sizeof( gencrypted ) ) != 0)
        return NULL;

    return gencrypted;
}
0707010000006A000081A400000000000000000000000163C2D55500000D84000000000000000000000000000000000000002E00000000spectre-cli-3.0+git20230114.26c01a5/src/blf.h/* $OpenBSD: blf.h,v 1.7 2007/03/14 17:59:41 grunk Exp $ */
/*
 * Blowfish - a fast block cipher designed by Bruce Schneier
 *
 * Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Niels Provos.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <stdint.h>

#ifndef _BLF_H_
#define _BLF_H_

/* Schneier specifies a maximum key length of 56 bytes.
 * This ensures that every key bit affects every cipher
 * bit.  However, the subkeys can hold up to 72 bytes.
 * Warning: For normal blowfish encryption only 56 bytes
 * of the key affect all cipherbits.
 */

#define BLF_N 16                        /* Number of Subkeys */
#define BLF_MAXKEYLEN ((BLF_N-2)*4)     /* 448 bits */
#define BLF_MAXUTILIZED ((BLF_N+2)*4)   /* 576 bits */

/* Blowfish context */
typedef struct BlowfishContext {
    uint32_t S[4][256];                /* S-Boxes */
    uint32_t P[BLF_N + 2];             /* Subkeys */
} blf_ctx;

/* Raw access to customized Blowfish
 *	blf_key is just:
 *	Blowfish_initstate( state )
 *	Blowfish_expand0state( state, key, keylen )
 */

void Blowfish_encipher(blf_ctx *, uint32_t *, uint32_t *);
void Blowfish_decipher(blf_ctx *, uint32_t *, uint32_t *);
void Blowfish_initstate(blf_ctx *);
void Blowfish_expand0state(blf_ctx *, const uint8_t *, uint16_t);
void Blowfish_expandstate(blf_ctx *, const uint8_t *, uint16_t, const uint8_t *, uint16_t);

/* Standard Blowfish */

void blf_key(blf_ctx *, const uint8_t *, uint16_t);
void blf_enc(blf_ctx *, uint32_t *, uint16_t);
void blf_dec(blf_ctx *, uint32_t *, uint16_t);

void blf_ecb_encrypt(blf_ctx *, uint8_t *, uint32_t);
void blf_ecb_decrypt(blf_ctx *, uint8_t *, uint32_t);

void blf_cbc_encrypt(blf_ctx *, uint8_t *, uint8_t *, uint32_t);
void blf_cbc_decrypt(blf_ctx *, uint8_t *, uint8_t *, uint32_t);

/* Converts uint8_t to uint32_t */
uint32_t Blowfish_stream2word(const uint8_t *, uint16_t, uint16_t *);

#endif
0707010000006B000081A400000000000000000000000163C2D55500005ADC000000000000000000000000000000000000003300000000spectre-cli-3.0+git20230114.26c01a5/src/blowfish.c/* $OpenBSD: blowfish.c,v 1.19 2015/09/11 09:18:27 guenther Exp $ */
/*
 * Blowfish block cipher for OpenBSD
 * Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de>
 * All rights reserved.
 *
 * Implementation advice by David Mazieres <dm@lcs.mit.edu>.
 *
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Niels Provos.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 */

/*
 * This code is derived from section 14.3 and the given source
 * in section V of Applied Cryptography, second edition.
 * Blowfish is an unpatented fast block cipher designed by
 * Bruce Schneier.
 */

#if 0
#include <stdio.h>		/* used for debugging */
#include <string.h>
#endif

#include "blf.h"

#undef inline
#ifdef __GNUC__
#define inline __inline
#else				/* !__GNUC__ */
#define inline
#endif				/* !__GNUC__ */

/* Function for Feistel Networks */

#define F(s, x) ((((s)[        (((x)>>24)&0xFF)]  \
		 + (s)[0x100 + (((x)>>16)&0xFF)]) \
		 ^ (s)[0x200 + (((x)>> 8)&0xFF)]) \
		 + (s)[0x300 + ( (x)     &0xFF)])

#define BLFRND(s,p,i,j,n) (i ^= F(s,j) ^ (p)[n])

void
Blowfish_encipher(blf_ctx *c, uint32_t *xl, uint32_t *xr)
{
	uint32_t Xl;
	uint32_t Xr;
	uint32_t *s = c->S[0];
	uint32_t *p = c->P;

	Xl = *xl;
	Xr = *xr;

	Xl ^= p[0];
	BLFRND(s, p, Xr, Xl, 1); BLFRND(s, p, Xl, Xr, 2);
	BLFRND(s, p, Xr, Xl, 3); BLFRND(s, p, Xl, Xr, 4);
	BLFRND(s, p, Xr, Xl, 5); BLFRND(s, p, Xl, Xr, 6);
	BLFRND(s, p, Xr, Xl, 7); BLFRND(s, p, Xl, Xr, 8);
	BLFRND(s, p, Xr, Xl, 9); BLFRND(s, p, Xl, Xr, 10);
	BLFRND(s, p, Xr, Xl, 11); BLFRND(s, p, Xl, Xr, 12);
	BLFRND(s, p, Xr, Xl, 13); BLFRND(s, p, Xl, Xr, 14);
	BLFRND(s, p, Xr, Xl, 15); BLFRND(s, p, Xl, Xr, 16);

	*xl = Xr ^ p[17];
	*xr = Xl;
}

void
Blowfish_decipher(blf_ctx *c, uint32_t *xl, uint32_t *xr)
{
	uint32_t Xl;
	uint32_t Xr;
	uint32_t *s = c->S[0];
	uint32_t *p = c->P;

	Xl = *xl;
	Xr = *xr;

	Xl ^= p[17];
	BLFRND(s, p, Xr, Xl, 16); BLFRND(s, p, Xl, Xr, 15);
	BLFRND(s, p, Xr, Xl, 14); BLFRND(s, p, Xl, Xr, 13);
	BLFRND(s, p, Xr, Xl, 12); BLFRND(s, p, Xl, Xr, 11);
	BLFRND(s, p, Xr, Xl, 10); BLFRND(s, p, Xl, Xr, 9);
	BLFRND(s, p, Xr, Xl, 8); BLFRND(s, p, Xl, Xr, 7);
	BLFRND(s, p, Xr, Xl, 6); BLFRND(s, p, Xl, Xr, 5);
	BLFRND(s, p, Xr, Xl, 4); BLFRND(s, p, Xl, Xr, 3);
	BLFRND(s, p, Xr, Xl, 2); BLFRND(s, p, Xl, Xr, 1);

	*xl = Xr ^ p[0];
	*xr = Xl;
}

void
Blowfish_initstate(blf_ctx *c)
{
	/* P-box and S-box tables initialized with digits of Pi */

	static const blf_ctx initstate =
	{ {
		{
			0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
			0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
			0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
			0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
			0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
			0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
			0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
			0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
			0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
			0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
			0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
			0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
			0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
			0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
			0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
			0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
			0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
			0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
			0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
			0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
			0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
			0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
			0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
			0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
			0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
			0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
			0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
			0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
			0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
			0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
			0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
			0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
			0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
			0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
			0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
			0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
			0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
			0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
			0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
			0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
			0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
			0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
			0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
			0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
			0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
			0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
			0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
			0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
			0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
			0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
			0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
			0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
			0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
			0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
			0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
			0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
			0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
			0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
			0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
			0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
			0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
			0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
			0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
		0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a},
		{
			0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
			0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
			0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
			0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
			0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
			0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
			0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
			0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
			0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
			0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
			0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
			0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
			0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
			0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
			0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
			0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
			0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
			0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
			0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
			0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
			0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
			0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
			0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
			0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
			0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
			0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
			0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
			0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
			0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
			0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
			0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
			0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
			0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
			0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
			0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
			0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
			0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
			0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
			0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
			0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
			0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
			0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
			0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
			0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
			0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
			0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
			0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
			0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
			0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
			0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
			0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
			0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
			0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
			0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
			0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
			0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
			0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
			0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
			0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
			0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
			0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
			0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
			0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
		0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7},
		{
			0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
			0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
			0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
			0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
			0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
			0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
			0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
			0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
			0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
			0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
			0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
			0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
			0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
			0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
			0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
			0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
			0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
			0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
			0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
			0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
			0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
			0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
			0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
			0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
			0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
			0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
			0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
			0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
			0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
			0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
			0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
			0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
			0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
			0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
			0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
			0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
			0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
			0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
			0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
			0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
			0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
			0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
			0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
			0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
			0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
			0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
			0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
			0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
			0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
			0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
			0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
			0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
			0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
			0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
			0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
			0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
			0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
			0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
			0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
			0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
			0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
			0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
			0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
		0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0},
		{
			0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
			0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
			0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
			0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
			0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
			0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
			0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
			0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
			0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
			0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
			0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
			0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
			0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
			0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
			0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
			0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
			0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
			0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
			0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
			0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
			0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
			0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
			0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
			0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
			0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
			0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
			0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
			0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
			0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
			0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
			0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
			0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
			0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
			0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
			0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
			0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
			0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
			0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
			0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
			0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
			0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
			0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
			0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
			0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
			0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
			0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
			0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
			0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
			0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
			0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
			0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
			0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
			0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
			0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
			0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
			0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
			0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
			0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
			0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
			0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
			0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
			0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
			0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
		0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6}
	},
	{
		0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
		0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
		0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
		0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
		0x9216d5d9, 0x8979fb1b
	} };

	*c = initstate;
}

uint32_t
Blowfish_stream2word(const uint8_t *data, uint16_t databytes,
    uint16_t *current)
{
	uint8_t i;
	uint16_t j;
	uint32_t temp;

	temp = 0x00000000;
	j = *current;

	for (i = 0; i < 4; i++, j++) {
		if (j >= databytes)
			j = 0;
		temp = (temp << 8) | data[j];
	}

	*current = j;
	return temp;
}

void
Blowfish_expand0state(blf_ctx *c, const uint8_t *key, uint16_t keybytes)
{
	uint16_t i;
	uint16_t j;
	uint16_t k;
	uint32_t temp;
	uint32_t datal;
	uint32_t datar;

	j = 0;
	for (i = 0; i < BLF_N + 2; i++) {
		/* Extract 4 int8 to 1 int32 from keystream */
		temp = Blowfish_stream2word(key, keybytes, &j);
		c->P[i] = c->P[i] ^ temp;
	}

	j = 0;
	datal = 0x00000000;
	datar = 0x00000000;
	for (i = 0; i < BLF_N + 2; i += 2) {
		Blowfish_encipher(c, &datal, &datar);

		c->P[i] = datal;
		c->P[i + 1] = datar;
	}

	for (i = 0; i < 4; i++) {
		for (k = 0; k < 256; k += 2) {
			Blowfish_encipher(c, &datal, &datar);

			c->S[i][k] = datal;
			c->S[i][k + 1] = datar;
		}
	}
}


void
Blowfish_expandstate(blf_ctx *c, const uint8_t *data, uint16_t databytes,
    const uint8_t *key, uint16_t keybytes)
{
	uint16_t i;
	uint16_t j;
	uint16_t k;
	uint32_t temp;
	uint32_t datal;
	uint32_t datar;

	j = 0;
	for (i = 0; i < BLF_N + 2; i++) {
		/* Extract 4 int8 to 1 int32 from keystream */
		temp = Blowfish_stream2word(key, keybytes, &j);
		c->P[i] = c->P[i] ^ temp;
	}

	j = 0;
	datal = 0x00000000;
	datar = 0x00000000;
	for (i = 0; i < BLF_N + 2; i += 2) {
		datal ^= Blowfish_stream2word(data, databytes, &j);
		datar ^= Blowfish_stream2word(data, databytes, &j);
		Blowfish_encipher(c, &datal, &datar);

		c->P[i] = datal;
		c->P[i + 1] = datar;
	}

	for (i = 0; i < 4; i++) {
		for (k = 0; k < 256; k += 2) {
			datal ^= Blowfish_stream2word(data, databytes, &j);
			datar ^= Blowfish_stream2word(data, databytes, &j);
			Blowfish_encipher(c, &datal, &datar);

			c->S[i][k] = datal;
			c->S[i][k + 1] = datar;
		}
	}

}

void
blf_key(blf_ctx *c, const uint8_t *k, uint16_t len)
{
	/* Initialize S-boxes and subkeys with Pi */
	Blowfish_initstate(c);

	/* Transform S-boxes and subkeys with key */
	Blowfish_expand0state(c, k, len);
}

void
blf_enc(blf_ctx *c, uint32_t *data, uint16_t blocks)
{
	uint32_t *d;
	uint16_t i;

	d = data;
	for (i = 0; i < blocks; i++) {
		Blowfish_encipher(c, d, d + 1);
		d += 2;
	}
}

void
blf_dec(blf_ctx *c, uint32_t *data, uint16_t blocks)
{
	uint32_t *d;
	uint16_t i;

	d = data;
	for (i = 0; i < blocks; i++) {
		Blowfish_decipher(c, d, d + 1);
		d += 2;
	}
}

void
blf_ecb_encrypt(blf_ctx *c, uint8_t *data, uint32_t len)
{
	uint32_t l, r;
	uint32_t i;

	for (i = 0; i < len; i += 8) {
		l = (uint32_t)data[0] << 24 | (uint32_t)data[1] << 16 | (uint32_t)data[2] << 8 | (uint32_t)data[3];
		r = (uint32_t)data[4] << 24 | (uint32_t)data[5] << 16 | (uint32_t)data[6] << 8 | (uint32_t)data[7];
		Blowfish_encipher(c, &l, &r);
		data[0] = (uint8_t)(l >> 24 & 0xff);
		data[1] = (uint8_t)(l >> 16 & 0xff);
		data[2] = (uint8_t)(l >> 8 & 0xff);
		data[3] = (uint8_t)(l & 0xff);
		data[4] = (uint8_t)(r >> 24 & 0xff);
		data[5] = (uint8_t)(r >> 16 & 0xff);
		data[6] = (uint8_t)(r >> 8 & 0xff);
		data[7] = (uint8_t)(r & 0xff);
		data += 8;
	}
}

void
blf_ecb_decrypt(blf_ctx *c, uint8_t *data, uint32_t len)
{
	uint32_t l, r;
	uint32_t i;

	for (i = 0; i < len; i += 8) {
		l = (uint32_t)data[0] << 24 | (uint32_t)data[1] << 16 | (uint32_t)data[2] << 8 | (uint32_t)data[3];
		r = (uint32_t)data[4] << 24 | (uint32_t)data[5] << 16 | (uint32_t)data[6] << 8 | (uint32_t)data[7];
		Blowfish_decipher(c, &l, &r);
		data[0] = (uint8_t)(l >> 24 & 0xff);
		data[1] = (uint8_t)(l >> 16 & 0xff);
		data[2] = (uint8_t)(l >> 8 & 0xff);
		data[3] = (uint8_t)(l & 0xff);
		data[4] = (uint8_t)(r >> 24 & 0xff);
		data[5] = (uint8_t)(r >> 16 & 0xff);
		data[6] = (uint8_t)(r >> 8 & 0xff);
		data[7] = (uint8_t)(r & 0xff);
		data += 8;
	}
}

void
blf_cbc_encrypt(blf_ctx *c, uint8_t *iv, uint8_t *data, uint32_t len)
{
	uint32_t l, r;
	uint32_t i, j;

	for (i = 0; i < len; i += 8) {
		for (j = 0; j < 8; j++)
			data[j] ^= iv[j];
		l = (uint32_t)data[0] << 24 | (uint32_t)data[1] << 16 | (uint32_t)data[2] << 8 | (uint32_t)data[3];
		r = (uint32_t)data[4] << 24 | (uint32_t)data[5] << 16 | (uint32_t)data[6] << 8 | (uint32_t)data[7];
		Blowfish_encipher(c, &l, &r);
		data[0] = (uint8_t)(l >> 24 & 0xff);
		data[1] = (uint8_t)(l >> 16 & 0xff);
		data[2] = (uint8_t)(l >> 8 & 0xff);
		data[3] = (uint8_t)(l & 0xff);
		data[4] = (uint8_t)(r >> 24 & 0xff);
		data[5] = (uint8_t)(r >> 16 & 0xff);
		data[6] = (uint8_t)(r >> 8 & 0xff);
		data[7] = (uint8_t)(r & 0xff);
		iv = data;
		data += 8;
	}
}

void
blf_cbc_decrypt(blf_ctx *c, uint8_t *iva, uint8_t *data, uint32_t len)
{
	uint32_t l, r;
	uint8_t *iv;
	uint32_t i, j;

	iv = data + len - 16;
	data = data + len - 8;
	for (i = len - 8; i >= 8; i -= 8) {
		l = (uint32_t)data[0] << 24 | (uint32_t)data[1] << 16 | (uint32_t)data[2] << 8 | (uint32_t)data[3];
		r = (uint32_t)data[4] << 24 | (uint32_t)data[5] << 16 | (uint32_t)data[6] << 8 | (uint32_t)data[7];
		Blowfish_decipher(c, &l, &r);
		data[0] = (uint8_t)(l >> 24 & 0xff);
		data[1] = (uint8_t)(l >> 16 & 0xff);
		data[2] = (uint8_t)(l >> 8 & 0xff);
		data[3] = (uint8_t)(l & 0xff);
		data[4] = (uint8_t)(r >> 24 & 0xff);
		data[5] = (uint8_t)(r >> 16 & 0xff);
		data[6] = (uint8_t)(r >> 8 & 0xff);
		data[7] = (uint8_t)(r & 0xff);
		for (j = 0; j < 8; j++)
			data[j] ^= iv[j];
		iv -= 8;
		data -= 8;
	}
	l = (uint32_t)data[0] << 24 | (uint32_t)data[1] << 16 | (uint32_t)data[2] << 8 | (uint32_t)data[3];
	r = (uint32_t)data[4] << 24 | (uint32_t)data[5] << 16 | (uint32_t)data[6] << 8 | (uint32_t)data[7];
	Blowfish_decipher(c, &l, &r);
	data[0] = (uint8_t)(l >> 24 & 0xff);
	data[1] = (uint8_t)(l >> 16 & 0xff);
	data[2] = (uint8_t)(l >> 8 & 0xff);
	data[3] = (uint8_t)(l & 0xff);
	data[4] = (uint8_t)(r >> 24 & 0xff);
	data[5] = (uint8_t)(r >> 16 & 0xff);
	data[6] = (uint8_t)(r >> 8 & 0xff);
	data[7] = (uint8_t)(r & 0xff);
	for (j = 0; j < 8; j++)
		data[j] ^= iva[j];
}
0707010000006C000081A400000000000000000000000163C2D555000018EC000000000000000000000000000000000000003800000000spectre-cli-3.0+git20230114.26c01a5/src/spectre-bench.c//==============================================================================
// This file is part of Spectre.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Spectre is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Spectre is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

//
//  spectre-bench.c
//  Spectre
//
//  Created by Maarten Billemont on 2014-12-20.
//  Copyright (c) 2014 Lyndir. All rights reserved.
//

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>

#include "bcrypt.c"

#include "spectre-algorithm.h"
#include "spectre-util.h"

#define Spectre_N                32768
#define Spectre_r                8
#define Spectre_p                2

static void spectre_time(struct timeval *time) {

    if (gettimeofday( time, NULL ) != OK)
        ftl( "Could not get time: %s", strerror( errno ) );
}

static const double spectre_show_speed(struct timeval startTime, const unsigned int iterations, const char *operation) {

    struct timeval endTime;
    spectre_time( &endTime );

    const time_t dsec = (endTime.tv_sec - startTime.tv_sec);
    const suseconds_t dusec = (endTime.tv_usec - startTime.tv_usec);
    const double elapsed = dsec + dusec / 1000000.;
    const double speed = iterations / elapsed;

    fprintf( stderr, " done.  " );
    fprintf( stdout, "%d %s iterations in %lus %luµs -> %.2f/s\n", iterations, operation, (unsigned long)dsec, (unsigned long)dusec, speed );

    return speed;
}

int main(int argc, char *const argv[]) {

    const char *userName = "Robert Lee Mitchel";
    const char *userSecret = "banana colored duckling";
    const char *siteName = "spectre.app";
    const SpectreResultType resultType = SpectreResultDefaultResult;
    const SpectreCounter keyCounter = SpectreCounterDefault;
    const SpectreKeyPurpose keyPurpose = SpectreKeyPurposeAuthentication;
    const char *keyContext = NULL;
    struct timeval startTime;
    unsigned int iterations;
    float percent;

    // Start HMAC-SHA-256
    // Similar to phase-two of spectre
    uint8_t *sitePasswordInfo = malloc( 128 );
    iterations = 4300000/* ~10s on dev machine */ * 2;
    const SpectreUserKey *userKey = spectre_user_key( userName, userSecret, SpectreAlgorithmCurrent );
    if (!userKey) {
        ftl( "Could not allocate user key: %s", strerror( errno ) );
        abort();
    }
    spectre_time( &startTime );
    for (int i = 1; i <= iterations; ++i) {
        uint8_t mac[32];
        spectre_hash_hmac_sha256( mac, userKey->bytes, sizeof( userKey->bytes ), sitePasswordInfo, 128 );

        if (modff( 100.f * i / iterations, &percent ) == 0)
            fprintf( stderr, "\rhmac-sha-256: iteration %d / %d (%.0f%%)..", i, iterations, percent );
    }
    const double hmacSha256Speed = spectre_show_speed( startTime, iterations, "hmac-sha-256" );
    free( (void *)userKey );

    // Start BCrypt
    // Similar to phase-one of spectre
    uint8_t bcrypt_rounds = 10;
    iterations = 170/* ~10s on dev machine */ * 2;
    spectre_time( &startTime );
    for (int i = 1; i <= iterations; ++i) {
        bcrypt( userSecret, bcrypt_gensalt( bcrypt_rounds ) );

        if (modff( 100.f * i / iterations, &percent ) == 0)
            fprintf( stderr, "\rbcrypt-%d: iteration %d / %d (%.0f%%)..", bcrypt_rounds, i, iterations, percent );
    }
    const double bcryptSpeed = spectre_show_speed( startTime, iterations, "bcrypt" );

    // Start SCrypt
    // Phase one of spectre
    uint8_t scrypt_rounds = 15;
    iterations = 2/* ~10s on dev machine */ * 2;
    spectre_time( &startTime );
    uint8_t *key = malloc(64);
    for (int i = 1; i <= iterations; ++i) {
        spectre_kdf_scrypt( key, 64, (uint8_t *)userName, strlen( userName ), (uint8_t *)userSecret, strlen( userSecret ), pow( 2, scrypt_rounds ), 8, 2 );

        if (modff( 100.f * i / iterations, &percent ) == 0)
            fprintf( stderr, "\rscrypt-%d: iteration %d / %d (%.0f%%)..", scrypt_rounds, i, iterations, percent );
    }
    free( key );
    const double scryptSpeed = spectre_show_speed( startTime, iterations, "scrypt" );

    // Start SPECTRE
    // Both phases of spectre
    iterations = 50; /* tuned to ~10s on dev machine */
    spectre_time( &startTime );
    for (int i = 1; i <= iterations; ++i) {
        userKey = spectre_user_key( userName, userSecret, SpectreAlgorithmCurrent );
        if (!userKey) {
            ftl( "Could not allocate user key: %s", strerror( errno ) );
            break;
        }

        free( (void *)spectre_site_result(
                userKey, siteName, resultType, NULL, keyCounter, keyPurpose, keyContext ) );
        free( (void *)userKey );

        if (modff( 100.f * i / iterations, &percent ) == 0)
            fprintf( stderr, "\rspectre: iteration %d / %d (%.0f%%)..", i, iterations, percent );
    }
    const double spectreSpeed = spectre_show_speed( startTime, iterations, "spectre" );

    // Summarize.
    fprintf( stdout, "\n== SUMMARY ==\nOn this machine,\n" );
    fprintf( stdout, " - 1 spectre      = %13.6f x hmac-sha-256.\n",                hmacSha256Speed / spectreSpeed  );
    fprintf( stdout, " - 1 spectre      = %13.6f x bcrypt-%d.\n",                   bcryptSpeed     / spectreSpeed, bcrypt_rounds );
    fprintf( stdout, " - 1 spectre      = %13.6f x scrypt-%d.\n",                   scryptSpeed     / spectreSpeed, scrypt_rounds );
    fprintf( stdout, " - 1 bcrypt-%-4d  = %13.6f x hmac-sha-256.\n", bcrypt_rounds, hmacSha256Speed / bcryptSpeed   );
    fprintf( stdout, " - 1 bcrypt-%-4d  = %13.6f x scrypt-%d.\n", bcrypt_rounds,    scryptSpeed     / bcryptSpeed,  scrypt_rounds );
    fprintf( stdout, " - 1 scrypt-%-4d  = %13.6f x hmac-sha-256.\n", scrypt_rounds, hmacSha256Speed / scryptSpeed   );

    return 0;
}
0707010000006D000081A400000000000000000000000163C2D55500002995000000000000000000000000000000000000003B00000000spectre-cli-3.0+git20230114.26c01a5/src/spectre-cli-util.c//==============================================================================
// This file is part of Spectre.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Spectre is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Spectre is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

#define _POSIX_C_SOURCE 200809L

#include "spectre-cli-util.h"

#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <pwd.h>
#include <string.h>
#include <errno.h>
#include <sysexits.h>

#define SPECTRE_MAX_INPUT 60

#if SPECTRE_COLOR
#include <curses.h>
#include <term.h>
#endif

#include "spectre-util.h"

const char *spectre_getenv(const char *variableName) {

    char *envBuf = getenv( variableName );
    return envBuf? spectre_strdup( envBuf ): NULL;
}

const char *spectre_askpass(const char *prompt) {

    const char *askpass = spectre_getenv( Spectre_ENV_askpass );
    if (!askpass)
        return NULL;

    int pipes[2];
    if (pipe( pipes ) == ERR) {
        wrn( "Couldn't create pipes for askpass: %s", strerror( errno ) );
        return NULL;
    }

    pid_t pid = fork();
    if (pid == ERR) {
        wrn( "Couldn't fork for askpass:\n  %s: %s", askpass, strerror( errno ) );
        return NULL;
    }

    if (!pid) {
        // askpass fork
        close( pipes[0] );
        if (dup2( pipes[1], STDOUT_FILENO ) == ERR)
            ftl( "Couldn't connect pipe to process: %s", strerror( errno ) );

        else if (execlp( askpass, askpass, prompt, NULL ) == ERR)
            ftl( "Couldn't execute askpass:\n  %s: %s", askpass, strerror( errno ) );

        exit( EX_SOFTWARE );
    }

    close( pipes[1] );
    const char *answer = spectre_read_fd( pipes[0] );
    close( pipes[0] );
    int status;
    if (waitpid( pid, &status, 0 ) == ERR) {
        wrn( "Couldn't wait for askpass: %s", strerror( errno ) );
        spectre_free_string( &answer );
        return NULL;
    }

    if (!WIFEXITED( status ) || WEXITSTATUS( status ) != EXIT_SUCCESS || !answer || !strlen( answer )) {
        // askpass failed.
        spectre_free_string( &answer );
        return NULL;
    }

    // Remove trailing newline.
    if (answer[strlen( answer ) - 1] == '\n')
        spectre_replace_string( answer, spectre_strndup( answer, strlen( answer ) - 1 ) );
    return answer;
}

static const char *_spectre_getline(const char *prompt, bool silent) {

    // Get answer from askpass.
    const char *answer = spectre_askpass( prompt );
    if (answer)
        return answer;

#if SPECTRE_COLOR
    // Initialize a curses screen.
    SCREEN *screen = newterm( NULL, stderr, stdin );
    if (screen) {
        start_color();
        init_pair( 1, COLOR_WHITE, COLOR_BLUE );
        init_pair( 2, COLOR_BLACK, COLOR_WHITE );
        int rows, cols;
        getmaxyx( stdscr, rows, cols );

        // Display a dialog box.
        int width = max( prompt? (int)strlen( prompt ): 0, SPECTRE_MAX_INPUT ) + 6;
        char *version = "spectre v" stringify_def( Spectre_VERSION );
        mvprintw( rows - 1, (cols - (int)strlen( version )) / 2, "%s", version );
        attron( A_BOLD );
        color_set( 2, NULL );
        mvprintw( rows / 2 - 1, (cols - width) / 2, "%s%*s%s", "*", width - 2, "", "*" );
        mvprintw( rows / 2 - 1, (cols - (int)strlen( prompt )) / 2, "%s", prompt );
        color_set( 1, NULL );
        mvprintw( rows / 2 + 0, (cols - width) / 2, "%s%*s%s", "|", width - 2, "", "|" );
        mvprintw( rows / 2 + 1, (cols - width) / 2, "%s%*s%s", "|", width - 2, "", "|" );
        mvprintw( rows / 2 + 2, (cols - width) / 2, "%s%*s%s", "|", width - 2, "", "|" );

        // Read response.
        color_set( 2, NULL );
        attron( A_STANDOUT );
        int result;
        char str[SPECTRE_MAX_INPUT + 1];
        if (silent) {
            mvprintw( rows / 2 + 1, (cols - 5) / 2, "[ * ]" );
            refresh();

            noecho();
            result = mvgetnstr( rows / 2 + 1, (cols - 1) / 2, str, SPECTRE_MAX_INPUT );
            echo();
        }
        else {
            mvprintw( rows / 2 + 1, (cols - (SPECTRE_MAX_INPUT + 2)) / 2, "%*s", SPECTRE_MAX_INPUT + 2, "" );
            refresh();

            echo();
            result = mvgetnstr( rows / 2 + 1, (cols - SPECTRE_MAX_INPUT) / 2, str, SPECTRE_MAX_INPUT );
        }
        attrset( 0 );
        endwin();
        delscreen( screen );

        return result == ERR? NULL: spectre_strndup( str, SPECTRE_MAX_INPUT );
    }
#endif

    // Get password from terminal.
    fprintf( stderr, "%s ", prompt );

    size_t bufSize = 0;
    ssize_t lineSize = getline( (char **)&answer, &bufSize, stdin );
    if (lineSize <= 1) {
        spectre_free_string( &answer );
        return NULL;
    }

    // Remove trailing newline.
    spectre_replace_string( answer, spectre_strndup( answer, (size_t)lineSize - 1 ) );
    return answer;
}

const char *spectre_getline(const char *prompt) {

    return _spectre_getline( prompt, false );
}

const char *spectre_getpass(const char *prompt) {

    return _spectre_getline( prompt, true );
}

const char *spectre_path(const char *prefix, const char *extension) {

    if (!prefix || !extension)
        return NULL;

    // Compose filename.
    const char *path = spectre_str( "%s.%s", prefix, extension );
    if (!path)
        return NULL;

    // This is a filename, remove all potential directory separators.
    for (char *slash; (slash = strstr( path, "/" )); *slash = '_');

    // Resolve user's home directory.
    const char *homeDir = NULL;
    if ((homeDir = getenv( "HOME" )))
        homeDir = spectre_strdup( homeDir );
    if (!homeDir)
        if ((homeDir = getenv( "USERPROFILE" )))
            homeDir = spectre_strdup( homeDir );
    if (!homeDir) {
        const char *homeDrive = getenv( "HOMEDRIVE" ), *homePath = getenv( "HOMEPATH" );
        if (homeDrive && homePath)
            homeDir = spectre_str( "%s%s", homeDrive, homePath );
    }
    if (!homeDir) {
        struct passwd *passwd = getpwuid( getuid() );
        if (passwd)
            homeDir = spectre_strdup( passwd->pw_dir );
    }
    if (!homeDir)
        homeDir = getcwd( NULL, 0 );

    // Compose pathname.
    if (homeDir) {
        const char *homePath = spectre_str( "%s/.spectre.d/%s", homeDir, path );
        spectre_free_string( &homeDir );

        if (homePath) {
            spectre_free_string( &path );
            path = homePath;
        }
    }

    return path;
}

bool spectre_mkdirs(const char *filePath) {

    if (!filePath)
        return false;

    // Save the cwd and for absolute paths, start at the root.
    char *cwd = getcwd( NULL, 0 );
    if (*filePath == '/')
        if (chdir( "/" ) == ERR)
            return false;

    // The path to mkdir is the filePath without the last path component.
    char *pathEnd = strrchr( filePath, '/' );
    if (!pathEnd)
        return true;

    // Walk the path.
    bool success = true;
    char *path = (char *)spectre_strndup( filePath, (size_t)(pathEnd - filePath) );
    for (char *dirName = strtok( path, "/" ); success && dirName; dirName = strtok( NULL, "/" )) {
        if (!strlen( dirName ))
            continue;

        success &= (mkdir( dirName, 0700 ) != ERR || errno == EEXIST) && chdir( dirName ) != ERR;
    }
    free( path );

    if (chdir( cwd ) == ERR)
        wrn( "Could not restore cwd:\n  %s: %s", cwd, strerror( errno ) );
    free( cwd );

    return success;
}

const char *spectre_read_fd(int fd) {

    char *buf = NULL;
    size_t blockSize = 4096, bufSize = 0, bufOffset = 0;
    ssize_t readSize = 0;
    while ((spectre_realloc( &buf, &bufSize, char, bufSize / sizeof( char ) + blockSize )) &&
           ((readSize = read( fd, buf + bufOffset, blockSize )) > 0));
    if (readSize == ERR)
        spectre_free( &buf, bufSize );

    return buf;
}

const char *spectre_read_file(FILE *file) {

    if (!file)
        return NULL;

    char *buf = NULL;
    size_t blockSize = 4096, bufSize = 0, bufOffset = 0, readSize = 0;
    while ((spectre_realloc( &buf, &bufSize, char, bufSize / sizeof( char ) + blockSize )) &&
           (bufOffset += (readSize = fread( buf + bufOffset, 1, blockSize, file ))) &&
           (readSize == blockSize));
    if (ferror( file ))
        spectre_free( &buf, bufSize );

    return buf;
}

#if SPECTRE_COLOR
static char *str_tputs;
static int str_tputs_cursor;
static const int str_tputs_max = 256;

static bool spectre_setupterm() {

    if (!isatty( STDERR_FILENO ))
        return false;

    static bool termsetup;
    if (!termsetup) {
        int errret;
        if (!(termsetup = (setupterm( NULL, STDERR_FILENO, &errret ) == OK))) {
            wrn( "Terminal doesn't support color (setupterm errret %d).", errret );
            return false;
        }
    }

    return true;
}

static int spectre_tputc(int c) {

    if (++str_tputs_cursor < str_tputs_max) {
        str_tputs[str_tputs_cursor] = (char)c;
        return OK;
    }

    return ERR;
}

static const char *spectre_tputs(const char *str, int affcnt) {

    if (str_tputs)
        spectre_free( &str_tputs, str_tputs_max );
    str_tputs = calloc( str_tputs_max, sizeof( char ) );
    str_tputs_cursor = -1;

    const char *result = tputs( str, affcnt, spectre_tputc ) == ERR? NULL: spectre_strndup( str_tputs, str_tputs_max );
    if (str_tputs)
        spectre_free( &str_tputs, str_tputs_max );

    return result;
}

#endif

const char *spectre_identicon_render(SpectreIdenticon identicon) {

    const char *colorString = NULL, *resetString = NULL;
#if SPECTRE_COLOR
    if (spectre_setupterm()) {
        colorString = spectre_tputs( tparm( tgetstr( "AF", NULL ), identicon.color ), 1 );
        resetString = spectre_tputs( tgetstr( "me", NULL ), 1 );
    }
#endif

    const char *str = spectre_str( "%s%s%s%s%s%s",
            colorString? colorString: "",
            identicon.leftArm, identicon.body, identicon.rightArm, identicon.accessory,
            resetString? resetString: "" );
    spectre_free_strings( &colorString, &resetString, NULL );

    return str;
}
0707010000006E000081A400000000000000000000000163C2D55500000C99000000000000000000000000000000000000003B00000000spectre-cli-3.0+git20230114.26c01a5/src/spectre-cli-util.h//==============================================================================
// This file is part of Spectre.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Spectre is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Spectre is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "spectre-types.h"

#ifndef Spectre_VERSION
#define Spectre_VERSION ?
#endif

#define Spectre_ENV_userName     "SPECTRE_USERNAME"
#define Spectre_ENV_algorithm    "SPECTRE_ALGORITHM"
#define Spectre_ENV_format       "SPECTRE_FORMAT"
#define Spectre_ENV_askpass      "SPECTRE_ASKPASS"

/** Read the value of an environment variable.
  * @return A newly allocated string or NULL if the variable doesn't exist. */
const char *spectre_getenv(const char *variableName);

/** Use the askpass program to prompt the user.
  * @return A newly allocated string or NULL if askpass is not enabled or could not be executed. */
const char *spectre_askpass(const char *prompt);

/** Ask the user a question.
  * @return A newly allocated string or NULL if an error occurred trying to read from the user. */
const char *spectre_getline(const char *prompt);

/** Ask the user for a password.
  * @return A newly allocated string or NULL if an error occurred trying to read from the user. */
const char *spectre_getpass(const char *prompt);

/** Get the absolute path to the spectre configuration file with the given prefix name and file extension.
  * Resolves the file <prefix.extension> as located in the <.spectre.d> directory inside the user's home directory
  * or current directory if it couldn't be resolved.
  * @return A newly allocated string or NULL if the prefix or extension is missing or the path could not be allocated. */
const char *spectre_path(const char *prefix, const char *extension);

/** mkdir all the directories up to the directory of the given file path.
  * @return true if the file's path exists. */
bool spectre_mkdirs(const char *filePath);

/** Read until EOF from the given file descriptor.
  * @return A newly allocated string or NULL if the an IO error occurred or the read buffer couldn't be allocated. */
const char *spectre_read_fd(int fd);

/** Read the file contents of a given file.
  * @return A newly allocated string or NULL if the file is missing, an IO error occurred or the read buffer couldn't be allocated. */
const char *spectre_read_file(FILE *file);

/** Encode a visual fingerprint for a user.
  * @return A newly allocated string or NULL if the identicon couldn't be allocated. */
const char *spectre_identicon_render(SpectreIdenticon identicon);
0707010000006F000081A400000000000000000000000163C2D5550000943A000000000000000000000000000000000000003600000000spectre-cli-3.0+git20230114.26c01a5/src/spectre-cli.c//==============================================================================
// This file is part of Spectre.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Spectre is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Spectre is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

#define _POSIX_C_SOURCE 200809L

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sysexits.h>

#include "spectre-cli-util.h"
#include "spectre-algorithm.h"
#include "spectre-util.h"
#include "spectre-marshal.h"
#include "spectre-marshal-util.h"

/** Output the program's usage documentation. */
static void usage() {

    inf( ""
         "  Spectre v%s - CLI\n"
         "--------------------------------------------------------------------------------\n"
         "      https://spectre.app\n", stringify_def( Spectre_VERSION ) );
    inf( ""
         "\nUSAGE\n\n"
         "  spectre [-u|-U user-name] [-s fd] [-t pw-type] [-P value] [-c counter]\n"
         "      [-a version] [-p purpose] [-C context] [-f|F format] [-R 0|1]\n"
         "      [-v|-q]* [-n] [-h] [site-name]\n" );
    inf( ""
         "  -u user-name Specify the user name of the user.\n"
         "               -u checks the personal secret against the config,\n"
         "               -U allows updating to a new personal secret.\n"
         "               Defaults to %s in env or prompts.\n", Spectre_ENV_userName );
    inf( ""
         "  -s fd        Read the personal secret of the user from a file descriptor.\n"
         "               Tip: don't send extra characters like newlines such as by using\n"
         "               echo in a pipe.  Consider printf instead.\n" );
    dbg( ""
         "  -S secret    Specify the personal secret of the user.\n"
         "               Passing secrets as arguments is unsafe, for use in testing only." );
    inf( ""
         "  -t pw-type   Specify the password's template.\n"
         "               Defaults to 'long' (-p a), 'name' (-p i) or 'phrase' (-p r).\n"
         "                   x, maximum  | 20 characters, contains symbols.\n"
         "                   l, long     | Copy-friendly, 14 characters, symbols.\n"
         "                   m, medium   | Copy-friendly, 8 characters, symbols.\n"
         "                   b, basic    | 8 characters, no symbols.\n"
         "                   s, short    | Copy-friendly, 4 characters, no symbols.\n"
         "                   i, pin      | 4 numbers.\n"
         "                   n, name     | 9 letter name.\n"
         "                   p, phrase   | 20 character sentence.\n"
         "                   K, key      | encryption key (512 bit or -P bits).\n"
         "                   P, personal | saved personal password (save with -P pw).\n" );
    inf( ""
         "  -P value     The parameter value.\n"
         "                   -p i        | The login name for the site.\n"
         "                   -t K        | The bit size of the key to generate (eg. 256).\n"
         "                   -t P        | The personal password to encrypt.\n" );
    inf( ""
         "  -c counter   The value of the counter.\n"
         "               Defaults to 1.\n" );
    inf( ""
         "  -a version   The algorithm version to use, %d - %d.\n"
         "               Defaults to env var %s or %d.\n",
            SpectreAlgorithmFirst, SpectreAlgorithmLast, Spectre_ENV_algorithm, SpectreAlgorithmCurrent );
    inf( ""
         "  -p purpose   The purpose of the generated token.\n"
         "               Defaults to 'auth'.\n"
         "                   a, auth     | An authentication token such as a password.\n"
         "                   i, ident    | An identification token such as a username.\n"
         "                   r, rec      | A recovery token such as a security answer.\n" );
    inf( ""
         "  -C context   A purpose-specific context.\n"
         "               Defaults to empty.\n"
         "                   -p a        | -\n"
         "                   -p i        | -\n"
         "                   -p r        | Most significant word in security question.\n" );
    inf( ""
         "  -f|F format  The file format to use for reading/writing user state.\n"
         "               -f reads the given format, allows fall-back & writes the default format (%s).\n"
         "               -F reads & writes only the given format,\n"
         "               Defaults to env var %s or the default format (%s).\n"
         "                   n, none     | No file\n"
         "                   f, flat     | ~/.spectre.d/user-name.%s\n"
         "                   j, json     | ~/.spectre.d/user-name.%s\n",
            spectre_format_name( SpectreFormatDefault ), Spectre_ENV_format, spectre_format_name( SpectreFormatDefault ),
            spectre_format_extension( SpectreFormatFlat ), spectre_format_extension( SpectreFormatJSON ) );
    inf( ""
         "  -R redacted  Whether to save the file in redacted format or not.\n"
         "               Redaction omits or encrypts any secrets, making the file safe\n"
         "               for saving on or transmitting via untrusted media.\n"
         "               Defaults to 1, redacted.\n" );
    inf( ""
         "  -v           Increase output verbosity (can be repeated).\n"
         "  -q           Decrease output verbosity (can be repeated).\n" );
    inf( ""
         "  -n           Omit trailing newline in output.\n" );
    inf( ""
         "  -h           Show this help output instead of performing any operation.\n" );
    inf( ""
         "  site-name    Name of the site for which to generate a token.\n" );
    inf( ""
         "\nENVIRONMENT\n\n"
         "  %-12s The user name of the user (see -u).\n"
         "  %-12s The default algorithm version (see -a).\n"
         "  %-12s The default file format (see -f).\n"
         "  %-12s The askpass program to use for prompting the user.\n",
            Spectre_ENV_userName, Spectre_ENV_algorithm, Spectre_ENV_format, Spectre_ENV_askpass );
    exit( EX_OK );
}

// Internal state.

typedef struct {
    const char *userName;
    const char *userSecretFD;
    const char *userSecret;
    const char *siteName;
    const char *resultType;
    const char *resultParam;
    const char *keyCounter;
    const char *keyPurpose;
    const char *keyContext;
    const char *algorithmVersion;
    const char *fileFormat;
    const char *fileRedacted;
} Arguments;

typedef struct {
    bool omitNewline;
    bool allowPasswordUpdate;
    bool fileFormatFixed;
    SpectreFormat fileFormat;
    const char *filePath;
    const char *userName;
    const char *userSecret;
    const char *identicon;
    const char *siteName;
    SpectreResultType resultType;
    const char *resultState;
    const char *resultParam;
    const char *resultPurpose;
    SpectreCounter keyCounter;
    SpectreKeyPurpose keyPurpose;
    const char *keyContext;
    SpectreAlgorithm algorithm;
    SpectreMarshalledFile *file;
    SpectreMarshalledUser *user;
    SpectreMarshalledSite *site;
    SpectreMarshalledQuestion *question;
} Operation;

// Processing steps.

void cli_free(Arguments *args, Operation *operation);
void cli_args(Arguments *args, Operation *operation, const int argc, char *const argv[]);
void cli_userName(Arguments *args, Operation *operation);
void cli_userSecret(Arguments *args, Operation *operation);
void cli_siteName(Arguments *args, Operation *operation);
void cli_fileFormat(Arguments *args, Operation *operation);
void cli_keyCounter(Arguments *args, Operation *operation);
void cli_keyPurpose(Arguments *args, Operation *operation);
void cli_keyContext(Arguments *args, Operation *operation);
void cli_user(Arguments *args, Operation *operation);
void cli_site(Arguments *args, Operation *operation);
void cli_question(Arguments *args, Operation *operation);
void cli_resultType(Arguments *args, Operation *operation);
void cli_resultState(Arguments *args, Operation *operation);
void cli_resultParam(Arguments *args, Operation *operation);
void cli_algorithmVersion(Arguments *args, Operation *operation);
void cli_fileRedacted(Arguments *args, Operation *operation);
void cli_spectre(Arguments *args, Operation *operation);
void cli_save(Arguments *args, Operation *operation);

SpectreKeyProvider spectre_proxy_provider_set_operation(Operation *operation);

/** ========================================================================
 *  MAIN                                                                     */
int main(const int argc, char *const argv[]) {

    // Application defaults.
    Arguments args = {
            .userName = spectre_getenv( Spectre_ENV_userName ),
            .algorithmVersion = spectre_getenv( Spectre_ENV_algorithm ),
            .fileFormat = spectre_getenv( Spectre_ENV_format ),
    };
    Operation operation = {
            .allowPasswordUpdate = false,
            .fileFormatFixed = false,
            .fileFormat = SpectreFormatDefault,
            .resultType = SpectreResultDefaultResult,
            .keyCounter = SpectreCounterDefault,
            .keyPurpose = SpectreKeyPurposeAuthentication,
    };

    // Read the command-line options.
    cli_args( &args, &operation, argc, argv );

    // Determine the operation parameters not sourced from the user's file.
    cli_userName( &args, &operation );
    cli_userSecret( &args, &operation );
    cli_siteName( &args, &operation );
    cli_fileFormat( &args, &operation );
    cli_keyPurpose( &args, &operation );
    cli_keyContext( &args, &operation );

    // Load the operation parameters present in the user's file.
    cli_user( &args, &operation );
    cli_site( &args, &operation );
    cli_question( &args, &operation );

    // Override the operation parameters from command-line arguments.
    cli_algorithmVersion( &args, &operation );
    cli_resultType( &args, &operation );
    cli_resultState( &args, &operation );
    cli_resultParam( &args, &operation );
    cli_keyCounter( &args, &operation );
    cli_fileRedacted( &args, &operation );
    cli_free( &args, NULL );

    // Operation summary.
    dbg( "-----------------" );
    if (operation.file && operation.user) {
        dbg( "userName         : %s", operation.user->userName );
        dbg( "identicon        : %s", operation.identicon );
        dbg( "fileFormat       : %s%s", spectre_format_name( operation.fileFormat ), operation.fileFormatFixed? " (fixed)": "" );
        dbg( "filePath         : %s", operation.filePath );
    }
    if (operation.site) {
        dbg( "siteName         : %s", operation.siteName );
        dbg( "resultType       : %s (%u)", spectre_type_short_name( operation.resultType ), operation.resultType );
        dbg( "resultParam      : %s", operation.resultParam );
        dbg( "keyCounter       : %u", operation.keyCounter );
        dbg( "keyPurpose       : %s (%u)", spectre_purpose_name( operation.keyPurpose ), operation.keyPurpose );
        dbg( "keyContext       : %s", operation.keyContext );
        dbg( "algorithmVersion : %u", operation.algorithm );
    }
    dbg( "-----------------" );

    // Finally ready to perform the actual operation.
    cli_spectre( NULL, &operation );

    // Save changes and clean up.
    cli_save( NULL, &operation );
    cli_free( NULL, &operation );

    return EX_OK;
}

void cli_free(Arguments *args, Operation *operation) {

    if (args) {
        spectre_free_strings( &args->userName, &args->userSecretFD, &args->userSecret, &args->siteName, NULL );
        spectre_free_strings( &args->resultType, &args->resultParam, &args->keyCounter, &args->algorithmVersion, NULL );
        spectre_free_strings( &args->keyPurpose, &args->keyContext, &args->fileFormat, &args->fileRedacted, NULL );
    }

    if (operation) {
        spectre_free_strings( &operation->userName, &operation->userSecret, &operation->siteName, NULL );
        spectre_free_strings( &operation->keyContext, &operation->resultState, &operation->resultParam, NULL );
        spectre_free_strings( &operation->identicon, &operation->filePath, NULL );
        spectre_marshal_file_free( &operation->file );
        spectre_marshal_user_free( &operation->user );
        operation->site = NULL;
        operation->question = NULL;
        spectre_proxy_provider_unset();
    }
}

void cli_args(Arguments *args, Operation *operation, const int argc, char *const argv[]) {

    for (int opt; (opt = getopt( argc, argv, "u:U:s:S:t:P:c:a:p:C:f:F:R:vqnh" )) != EOF;
         optarg? spectre_zero( optarg, strlen( optarg ) ): (void)0)
        switch (opt) {
            case 'u':
                args->userName = optarg && strlen( optarg )? spectre_strdup( optarg ): NULL;
                operation->allowPasswordUpdate = false;
                break;
            case 'U':
                args->userName = optarg && strlen( optarg )? spectre_strdup( optarg ): NULL;
                operation->allowPasswordUpdate = true;
                break;
            case 's':
                args->userSecretFD = optarg && strlen( optarg )? spectre_strdup( optarg ): NULL;
                break;
            case 'S':
                // Passing your personal secret via the command-line is insecure.  Testing purposes only.
                args->userSecret = optarg && strlen( optarg )? spectre_strdup( optarg ): NULL;
                break;
            case 't':
                args->resultType = optarg && strlen( optarg )? spectre_strdup( optarg ): NULL;
                break;
            case 'P':
                args->resultParam = optarg && strlen( optarg )? spectre_strdup( optarg ): NULL;
                break;
            case 'c':
                args->keyCounter = optarg && strlen( optarg )? spectre_strdup( optarg ): NULL;
                break;
            case 'a':
                args->algorithmVersion = optarg && strlen( optarg )? spectre_strdup( optarg ): NULL;
                break;
            case 'p':
                args->keyPurpose = optarg && strlen( optarg )? spectre_strdup( optarg ): NULL;
                break;
            case 'C':
                args->keyContext = optarg && strlen( optarg )? spectre_strdup( optarg ): NULL;
                break;
            case 'f':
                args->fileFormat = optarg && strlen( optarg )? spectre_strdup( optarg ): NULL;
                operation->fileFormatFixed = false;
                break;
            case 'F':
                args->fileFormat = optarg && strlen( optarg )? spectre_strdup( optarg ): NULL;
                operation->fileFormatFixed = true;
                break;
            case 'R':
                args->fileRedacted = optarg && strlen( optarg )? spectre_strdup( optarg ): NULL;
                break;
            case 'v':
                ++spectre_verbosity;
                break;
            case 'q':
                --spectre_verbosity;
                break;
            case 'n':
                operation->omitNewline = true;
                break;
            case 'h':
                usage();
                break;
            case '?':
                switch (optopt) {
                    case 'u':
                        ftl( "Missing user name to option: -%c", optopt );
                        exit( EX_USAGE );
                    case 't':
                        ftl( "Missing type name to option: -%c", optopt );
                        exit( EX_USAGE );
                    case 'c':
                        ftl( "Missing counter value to option: -%c", optopt );
                        exit( EX_USAGE );
                    default:
                        ftl( "Unknown option: -%c", optopt );
                        exit( EX_USAGE );
                }
            default:
                ftl( "Unexpected option: %c", opt );
                exit( EX_USAGE );
        }

    if (optind < argc && argv[optind])
        args->siteName = spectre_strdup( argv[optind] );
}

void cli_userName(Arguments *args, Operation *operation) {

    spectre_free_string( &operation->userName );

    if (args->userName)
        operation->userName = spectre_strdup( args->userName );

    if (!operation->userName || !strlen( operation->userName ))
        do {
            operation->userName = spectre_getline( "Your full name:" );
        } while (operation->userName && !strlen( operation->userName ));

    if (!operation->userName || !strlen( operation->userName )) {
        ftl( "Missing full name." );
        cli_free( args, operation );
        exit( EX_DATAERR );
    }
}

void cli_userSecret(Arguments *args, Operation *operation) {

    spectre_free_string( &operation->userSecret );

    if (args->userSecretFD) {
        operation->userSecret = spectre_read_fd( (int)strtol( args->userSecretFD, NULL, 10 ) );
        if (!operation->userSecret && errno)
            wrn( "Error reading personal secret from FD %s: %s", args->userSecretFD, strerror( errno ) );
    }

    if (args->userSecret && !operation->userSecret)
        operation->userSecret = spectre_strdup( args->userSecret );

    if (!operation->userSecret || !strlen( operation->userSecret ))
        do {
            operation->userSecret = spectre_getpass( "Your personal secret: " );
        } while (operation->userSecret && !strlen( operation->userSecret ));

    if (!operation->userSecret || !strlen( operation->userSecret )) {
        ftl( "Missing personal secret." );
        cli_free( args, operation );
        exit( EX_DATAERR );
    }
}

void cli_siteName(Arguments *args, Operation *operation) {

    spectre_free_string( &operation->siteName );

    if (args->siteName)
        operation->siteName = spectre_strdup( args->siteName );
    if (!operation->siteName)
        operation->siteName = spectre_getline( "Site Domain:" );

    if (!operation->siteName) {
        ftl( "Missing site name." );
        cli_free( args, operation );
        exit( EX_DATAERR );
    }
}

void cli_fileFormat(Arguments *args, Operation *operation) {

    if (!args->fileFormat)
        return;

    operation->fileFormat = spectre_format_named( args->fileFormat );
    if (ERR == (int)operation->fileFormat) {
        ftl( "Invalid file format: %s", args->fileFormat );
        cli_free( args, operation );
        exit( EX_DATAERR );
    }
}

void cli_keyPurpose(Arguments *args, Operation *operation) {

    if (!args->keyPurpose)
        return;

    operation->keyPurpose = spectre_purpose_named( args->keyPurpose );
    if (ERR == (int)operation->keyPurpose) {
        ftl( "Invalid purpose: %s", args->keyPurpose );
        cli_free( args, operation );
        exit( EX_DATAERR );
    }
}

void cli_keyContext(Arguments *args, Operation *operation) {

    if (!args->keyContext)
        return;

    operation->keyContext = spectre_strdup( args->keyContext );
}

static FILE *cli_user_open(const SpectreFormat format, Operation *operation) {

    FILE *userFile = NULL;
    size_t count = 0;
    const char **extensions = spectre_format_extensions( format, &count );
    for (int e = 0; !userFile && e < count; ++e) {
        spectre_free_string( &operation->filePath );
        operation->filePath = spectre_path( operation->userName, extensions[e] );

        if (!operation->filePath || !(userFile = fopen( operation->filePath, "r" )))
            dbg( "Couldn't open configuration file:\n  %s: %s", operation->filePath, strerror( errno ) );
    }
    spectre_free( &extensions, count * sizeof( *extensions ) );

    return userFile;
}

void cli_user(Arguments *args, Operation *operation) {

    // Find the user's file from parameters.
    FILE *userFile = cli_user_open( operation->fileFormat, operation );
    if (!userFile && !operation->fileFormatFixed)
        for (SpectreFormat format = SpectreFormatLast; !userFile && format >= SpectreFormatFirst; --format)
            userFile = cli_user_open( format, operation );

    if (!userFile) {
        // If no user from the user's file, create a new one.
        spectre_free_string( &operation->filePath );
        spectre_marshal_file_free( &operation->file );
        spectre_marshal_user_free( &operation->user );
        operation->file = spectre_marshal_file( NULL, NULL, NULL );
        operation->user = spectre_marshal_user( operation->userName, spectre_proxy_provider_set_operation( operation ), SpectreAlgorithmCurrent );
    }

    else {
        // Load the user object from the user's file.
        const char *fileInputData = spectre_read_file( userFile );
        if (!fileInputData || ferror( userFile ))
            wrn( "Error while reading configuration file:\n  %s: %d", operation->filePath, ferror( userFile ) );
        fclose( userFile );

        // Parse file.
        spectre_marshal_file_free( &operation->file );
        spectre_marshal_user_free( &operation->user );
        operation->file = spectre_marshal_read( NULL, fileInputData );
        if (operation->file && operation->file->error.type == SpectreMarshalSuccess) {
            operation->user = spectre_marshal_auth( operation->file, spectre_proxy_provider_set_operation( operation ) );

            if (operation->file->error.type == SpectreMarshalErrorUserSecret && operation->allowPasswordUpdate) {
                // Update personal secret in the user's file.
                while (operation->file->error.type == SpectreMarshalErrorUserSecret) {
                    inf( "Given personal secret does not match configuration." );
                    inf( "To update the configuration with this new personal secret, first confirm the old personal secret." );

                    const char *importUserSecret = NULL;
                    while (!importUserSecret || !strlen( importUserSecret )) {
                        spectre_free_string( &importUserSecret );
                        importUserSecret = spectre_getpass( "Old personal secret: " );
                    }

                    spectre_marshal_user_free( &operation->user );
                    operation->user = spectre_marshal_auth( operation->file, spectre_proxy_provider_set_secret( importUserSecret ) );
                    if (operation->file && operation->user)
                        operation->user->userKeyProvider = spectre_proxy_provider_set_operation( operation );
                    spectre_free_string( &importUserSecret );
                }
            }
        }
        spectre_free_string( &fileInputData );

        // Incorrect personal secret.
        if (operation->file->error.type == SpectreMarshalErrorUserSecret) {
            ftl( "Incorrect personal secret according to configuration:\n  %s: %s", operation->filePath, operation->file->error.message );
            cli_free( args, operation );
            exit( EX_DATAERR );
        }

        // Any other parse error.
        if (!operation->file || !operation->user || operation->file->error.type != SpectreMarshalSuccess) {
            err( "Couldn't parse configuration file:\n  %s: %s", operation->filePath, operation->file->error.message );
            cli_free( args, operation );
            exit( EX_DATAERR );
        }
    }

    if (operation->userSecret)
        operation->user->identicon = spectre_identicon( operation->user->userName, operation->userSecret );
    spectre_free_string( &operation->identicon );
    operation->identicon = spectre_identicon_render( operation->user->identicon );
}

void cli_site(Arguments *args, Operation *operation) {

    if (!operation->siteName)
        abort();

    // Load the site object from the user's file.
    SpectreMarshalledUser *user = operation->user;
    for (size_t s = 0; !operation->site && s < user->sites_count; ++s)
        if (strcmp( operation->siteName, (&user->sites[s])->siteName ) == OK)
            operation->site = &user->sites[s];

    // If no site from the user's file, create a new one.
    if (!operation->site)
        operation->site = spectre_marshal_site(
                user, operation->siteName, user->defaultType, SpectreCounterDefault, user->algorithm );
}

void cli_question(Arguments *args, Operation *operation) {

    if (!operation->site)
        abort();

    // Load the question object from the user's file.
    switch (operation->keyPurpose) {
        case SpectreKeyPurposeAuthentication:
        case SpectreKeyPurposeIdentification:
            break;
        case SpectreKeyPurposeRecovery:
            for (size_t q = 0; !operation->question && q < operation->site->questions_count; ++q)
                if (operation->keyContext == (&operation->site->questions[q])->keyword ||
                    (!operation->keyContext && !strlen( (&operation->site->questions[q])->keyword )) ||
                    (!(&operation->site->questions[q])->keyword && !strlen( operation->keyContext )) ||
                    ((operation->keyContext && (&operation->site->questions[q])->keyword) &&
                     strcmp( (&operation->site->questions[q])->keyword, operation->keyContext ) == OK))
                    operation->question = &operation->site->questions[q];

            // If no question from the user's file, create a new one.
            if (!operation->question)
                operation->question = spectre_marshal_question( operation->site, operation->keyContext );
            break;
    }
}

void cli_resultType(Arguments *args, Operation *operation) {

    if (!operation->site)
        abort();

    switch (operation->keyPurpose) {
        case SpectreKeyPurposeAuthentication: {
            operation->resultPurpose = "site password";
            operation->resultType = operation->site->resultType;
            operation->algorithm = operation->site->algorithm;
            break;
        }
        case SpectreKeyPurposeIdentification: {
            operation->resultPurpose = "site login";
            operation->resultType = operation->site->loginType;
            operation->algorithm = operation->site->algorithm;
            break;
        }
        case SpectreKeyPurposeRecovery: {
            operation->resultPurpose = "site answer";
            operation->resultType = operation->question->type;
            operation->algorithm = operation->site->algorithm;
            break;
        }
    }

    if (!args->resultType)
        return;

    operation->resultType = spectre_type_named( args->resultType );
    if (ERR == (int)operation->resultType) {
        ftl( "Invalid type: %s", args->resultType );
        cli_free( args, operation );
        exit( EX_USAGE );
    }

    if (!(operation->resultType & SpectreResultFeatureAlternate)) {
        switch (operation->keyPurpose) {
            case SpectreKeyPurposeAuthentication:
                operation->site->resultType = operation->resultType;
                break;
            case SpectreKeyPurposeIdentification:
                operation->site->loginType = operation->resultType;
                break;
            case SpectreKeyPurposeRecovery:
                operation->question->type = operation->resultType;
                break;
        }
    }
}

void cli_resultState(Arguments *args, Operation *operation) {

    if (!operation->site)
        abort();

    switch (operation->keyPurpose) {
        case SpectreKeyPurposeAuthentication: {
            operation->resultState = operation->site->resultState? spectre_strdup( operation->site->resultState ): NULL;
            operation->keyCounter = operation->site->counter;
            break;
        }
        case SpectreKeyPurposeIdentification: {
            if (operation->resultType != SpectreResultNone) {
                operation->resultState = operation->site->loginState? spectre_strdup( operation->site->loginState ): NULL;
                operation->keyCounter = SpectreCounterInitial;
            }
            else {
                // Identification at site-level is none, fall back to user-level.
                operation->resultPurpose = "global login";
                spectre_free_string( &operation->siteName );
                operation->siteName = spectre_strdup( operation->user->userName );
                operation->resultType = operation->user->loginType;
                operation->resultState = operation->user->loginState? spectre_strdup( operation->user->loginState ): NULL;
                operation->keyCounter = SpectreCounterInitial;
                operation->algorithm = operation->user->algorithm;
            }
            break;
        }
        case SpectreKeyPurposeRecovery: {
            operation->resultState = operation->question->state? spectre_strdup( operation->question->state ): NULL;
            operation->keyCounter = SpectreCounterInitial;
            spectre_free_string( &operation->keyContext );
            operation->keyContext = operation->question->keyword? spectre_strdup( operation->question->keyword ): NULL;
            break;
        }
    }
}

void cli_keyCounter(Arguments *args, Operation *operation) {

    if (!args->keyCounter)
        return;
    if (!operation->site)
        abort();

    long long int keyCounterInt = strtoll( args->keyCounter, NULL, 0 );
    if (keyCounterInt < SpectreCounterFirst || keyCounterInt > SpectreCounterLast) {
        ftl( "Invalid counter: %s", args->keyCounter );
        cli_free( args, operation );
        exit( EX_USAGE );
    }

    switch (operation->keyPurpose) {
        case SpectreKeyPurposeAuthentication:
            operation->keyCounter = operation->site->counter = (SpectreCounter)keyCounterInt;
            break;
        case SpectreKeyPurposeIdentification:
        case SpectreKeyPurposeRecovery:
            // NOTE: counter for login & question is not persisted.
            break;
    }
}

void cli_resultParam(Arguments *args, Operation *operation) {

    if (!args->resultParam)
        return;

    spectre_free_string( &operation->resultParam );
    operation->resultParam = spectre_strdup( args->resultParam );
}

void cli_algorithmVersion(Arguments *args, Operation *operation) {

    if (!args->algorithmVersion)
        return;
    if (!operation->site)
        abort();

    unsigned long algorithmVersion = strtoul( args->algorithmVersion, NULL, 10 );
    if (algorithmVersion < SpectreAlgorithmFirst || algorithmVersion > SpectreAlgorithmLast) {
        ftl( "Invalid algorithm version: %s", args->algorithmVersion );
        cli_free( args, operation );
        exit( EX_USAGE );
    }
    operation->site->algorithm = (SpectreAlgorithm)algorithmVersion;
}

void cli_fileRedacted(Arguments *args, Operation *operation) {

    if (args->fileRedacted)
        operation->user->redacted = spectre_get_bool( args->fileRedacted );

    else if (!operation->user->redacted)
        wrn( "User configuration file is not redacted.  Use -R 1 to change this." );
}

void cli_spectre(Arguments *args, Operation *operation) {

    if (!operation->site)
        abort();

    if (spectre_verbosity >= SpectreLogLevelInfo)
        fprintf( stderr, "%s's %s for %s:\n[ %s ]: ",
                operation->user->userName, operation->resultPurpose, operation->site->siteName, operation->identicon );

    // Check user keyID.
    const SpectreUserKey *userKey = NULL;
    if (operation->user->userKeyProvider)
        userKey = operation->user->userKeyProvider( operation->user->algorithm, operation->user->userName );
    if (!userKey) {
        ftl( "Couldn't derive user key." );
        cli_free( args, operation );
        exit( EX_SOFTWARE );
    }
    if (!spectre_id_valid( &operation->user->keyID ))
        operation->user->keyID = userKey->keyID;
    else if (!spectre_id_equals( &userKey->keyID, &operation->user->keyID )) {
        ftl( "user key mismatch." );
        spectre_free( &userKey, sizeof( *userKey ) );
        cli_free( args, operation );
        exit( EX_SOFTWARE );
    }

    // Resolve user key for site.
    spectre_free( &userKey, sizeof( *userKey ) );
    if (operation->user->userKeyProvider)
        userKey = operation->user->userKeyProvider( operation->algorithm, operation->user->userName );
    if (!userKey) {
        ftl( "Couldn't derive user key." );
        cli_free( args, operation );
        exit( EX_SOFTWARE );
    }

    // Update state from resultParam if stateful.
    if (operation->resultType & SpectreResultClassStateful && operation->resultParam) {
        spectre_free_string( &operation->resultState );
        if (!(operation->resultState =
                spectre_site_state( userKey, operation->siteName,
                        operation->resultType, operation->resultParam,
                        operation->keyCounter, operation->keyPurpose, operation->keyContext ))) {
            ftl( "Couldn't encrypt result." );
            spectre_free( &userKey, sizeof( *userKey ) );
            cli_free( args, operation );
            exit( EX_SOFTWARE );
        }
        inf( "(state) %s => ", operation->resultState );

        switch (operation->keyPurpose) {
            case SpectreKeyPurposeAuthentication: {
                spectre_free_string( &operation->site->resultState );
                operation->site->resultState = spectre_strdup( operation->resultState );
                break;
            }
            case SpectreKeyPurposeIdentification: {
                if (strcmp( operation->siteName, operation->userName ) == OK) {
                    spectre_free_string( &operation->user->loginState );
                    operation->user->loginState = spectre_strdup( operation->resultState );
                } else {
                    spectre_free_string( &operation->site->loginState );
                    operation->site->loginState = spectre_strdup( operation->resultState );
                }
                break;
            }

            case SpectreKeyPurposeRecovery: {
                spectre_free_string( &operation->question->state );
                operation->question->state = spectre_strdup( operation->resultState );
                break;
            }
        }

        // resultParam is consumed.
        spectre_free_string( &operation->resultParam );
    }

    // resultParam defaults to state.
    if (!operation->resultParam && operation->resultState)
        operation->resultParam = spectre_strdup( operation->resultState );

    // Generate result.
    const char *result = spectre_site_result( userKey, operation->siteName,
            operation->resultType, operation->resultParam, operation->keyCounter, operation->keyPurpose, operation->keyContext );
    spectre_free( &userKey, sizeof( *userKey ) );
    if (!result) {
        ftl( "Couldn't generate result." );
        cli_free( args, operation );
        exit( EX_SOFTWARE );
    }
    fflush( NULL );
    fprintf( stdout, "%s", result );
    if (!operation->omitNewline)
        fprintf( stdout, "\n" );
    if (operation->site->url)
        inf( "See: %s", operation->site->url );
    spectre_free_string( &result );

    // Update usage metadata.
    operation->site->lastUsed = operation->user->lastUsed = time( NULL );
    operation->site->uses++;
}

void cli_save(Arguments *args, Operation *operation) {

    if (!operation->file || !operation->user)
        return;

    if (!operation->fileFormatFixed)
        operation->fileFormat = SpectreFormatDefault;

    size_t count = 0;
    const char **extensions = spectre_format_extensions( operation->fileFormat, &count );
    if (!extensions || !count)
        return;

    spectre_free_string( &operation->filePath );
    operation->filePath = spectre_path( operation->user->userName, extensions[0] );
    dbg( "Updating: %s (%s)", operation->filePath, spectre_format_name( operation->fileFormat ) );
    spectre_free( &extensions, count * sizeof( *extensions ) );

    FILE *userFile = NULL;
    if (!operation->filePath || !spectre_mkdirs( operation->filePath ) || !(userFile = fopen( operation->filePath, "w" ))) {
        wrn( "Couldn't create updated configuration file:\n  %s: %s", operation->filePath, strerror( errno ) );
        return;
    }

    const char *buf = spectre_marshal_write( operation->fileFormat, &operation->file, operation->user );
    if (!buf || operation->file->error.type != SpectreMarshalSuccess)
        wrn( "Couldn't encode updated configuration file:\n  %s: %s", operation->filePath, operation->file->error.message );

    else if (fwrite( buf, sizeof( char ), strlen( buf ), userFile ) != strlen( buf ))
        wrn( "Error while writing updated configuration file:\n  %s: %d", operation->filePath, ferror( userFile ) );

    spectre_free_string( &buf );
    fclose( userFile );
}

static Operation *__spectre_proxy_provider_current_operation = NULL;

static bool __spectre_proxy_provider_operation(const SpectreUserKey **currentKey, SpectreAlgorithm *currentAlgorithm,
        SpectreAlgorithm algorithm, const char *userName) {

    if (!currentKey)
        __spectre_proxy_provider_current_operation = NULL;
    if (!__spectre_proxy_provider_current_operation)
        return false;

    return spectre_update_user_key( currentKey, currentAlgorithm, algorithm, userName,
                                    __spectre_proxy_provider_current_operation->userSecret );
}

SpectreKeyProvider spectre_proxy_provider_set_operation(Operation *operation) {

    spectre_proxy_provider_unset();
    __spectre_proxy_provider_current_operation = operation;
    return spectre_proxy_provider_set( __spectre_proxy_provider_operation );
}
07070100000070000081A400000000000000000000000163C2D55500000C71000000000000000000000000000000000000003D00000000spectre-cli-3.0+git20230114.26c01a5/src/spectre-tests-util.c//==============================================================================
// This file is part of Spectre.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Spectre is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Spectre is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

//
//  spectre-tests-util.c
//  Spectre
//
//  Created by Maarten Billemont on 2014-12-21.
//  Copyright (c) 2014 Lyndir. All rights reserved.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "spectre-util.h"

#include "spectre-tests-util.h"

xmlNodePtr spectre_xmlTestCaseNode(xmlNodePtr testCaseNode, const char *nodeName) {

    if (!nodeName)
        return NULL;

    // Try to find an attribute node.
    for (xmlAttrPtr child = testCaseNode->properties; child; child = child->next)
        if (xmlStrcmp( child->name, BAD_CAST nodeName ) == OK)
            return (xmlNodePtr)child;

    // Try to find an element node.
    for (xmlNodePtr child = testCaseNode->children; child; child = child->next)
        if (xmlStrcmp( child->name, BAD_CAST nodeName ) == OK)
            return child;

    // Missing content, try to find parent case.
    if (strcmp( nodeName, "parent" ) == OK)
        // Was just searching for testCaseNode's parent, none found.
        return NULL;
    xmlChar *parentId = spectre_xmlTestCaseString( testCaseNode, "parent" );
    if (!parentId)
        // testCaseNode has no parent, give up.
        return NULL;

    for (xmlNodePtr otherTestCaseNode = testCaseNode->parent->children; otherTestCaseNode; otherTestCaseNode = otherTestCaseNode->next) {
        xmlChar *id = spectre_xmlTestCaseString( otherTestCaseNode, "id" );
        int foundParent = id && xmlStrcmp( id, parentId ) == OK;
        xmlFree( id );

        if (foundParent) {
            xmlFree( parentId );
            return spectre_xmlTestCaseNode( otherTestCaseNode, nodeName );
        }
    }

    err( "Missing parent: %s, for case: %s", parentId, spectre_xmlTestCaseString( testCaseNode, "id" ) );
    return NULL;
}

xmlChar *spectre_xmlTestCaseString(xmlNodePtr context, const char *nodeName) {

    if (!nodeName)
        return NULL;

    xmlNodePtr child = spectre_xmlTestCaseNode( context, nodeName );
    return child? xmlNodeGetContent( child ): NULL;
}

uint32_t spectre_xmlTestCaseInteger(xmlNodePtr context, const char *nodeName) {

    if (!nodeName)
        return 0;

    xmlChar *string = spectre_xmlTestCaseString( context, nodeName );
    uint32_t integer = string? (uint32_t)strtoul( (char *)string, NULL, 10 ): 0;
    xmlFree( string );

    return integer;
}
07070100000071000081A400000000000000000000000163C2D55500000523000000000000000000000000000000000000003D00000000spectre-cli-3.0+git20230114.26c01a5/src/spectre-tests-util.h//==============================================================================
// This file is part of Spectre.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Spectre is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Spectre is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

//
//  spectre-tests-util.h
//  Spectre
//
//  Created by Maarten Billemont on 2014-12-21.
//  Copyright (c) 2014 Lyndir. All rights reserved.
//

#include <libxml/parser.h>

xmlNodePtr spectre_xmlTestCaseNode(
        xmlNodePtr testCaseNode, const char *nodeName);
xmlChar *spectre_xmlTestCaseString(
        xmlNodePtr context, const char *nodeName);
uint32_t spectre_xmlTestCaseInteger(
        xmlNodePtr context, const char *nodeName);
07070100000072000081A400000000000000000000000163C2D55500001A55000000000000000000000000000000000000003800000000spectre-cli-3.0+git20230114.26c01a5/src/spectre-tests.c//==============================================================================
// This file is part of Spectre.
// Copyright (c) 2011-2017, Maarten Billemont.
//
// Spectre is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Spectre is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You can find a copy of the GNU General Public License in the
// LICENSE file.  Alternatively, see <http://www.gnu.org/licenses/>.
//==============================================================================

#define _POSIX_C_SOURCE 200809L

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sysexits.h>

#ifndef spectre_log_do
#define spectre_log_do(level, format, ...) ({ \
    fprintf( stderr, format "\n", ##__VA_ARGS__ ); \
    if (level == ftl_level) \
        abort(); \
})
#endif

#include "spectre-algorithm.h"
#include "spectre-util.h"

#include "spectre-tests-util.h"

/** Output the program's usage documentation. */
static void usage() {

    inf( ""
            "  Spectre v%s - Tests\n"
            "--------------------------------------------------------------------------------\n"
            "      https://spectre.app\n", stringify_def( Spectre_VERSION ) );
    inf( ""
            "\nUSAGE\n\n"
            "  spectre-tests [-v|-q]* [-h] [test-name ...]\n" );
    inf( ""
            "  -v           Increase output verbosity (can be repeated).\n"
            "  -q           Decrease output verbosity (can be repeated).\n" );
    inf( ""
            "  -h           Show this help output instead of performing any operation.\n" );
    inf( ""
            "  test-name    Only run tests whose identifier starts with one of the these.\n" );
    exit( EX_OK );
}

int main(int argc, char *const argv[]) {

    for (int opt; (opt = getopt( argc, argv, "vqh" )) != EOF;
         optarg? spectre_zero( optarg, strlen( optarg ) ): (void)0)
        switch (opt) {
            case 'v':
                ++spectre_verbosity;
                break;
            case 'q':
                --spectre_verbosity;
                break;
            case 'h':
                usage();
                break;
            case '?':
                ftl( "Unknown option: -%c", optopt );
                exit( EX_USAGE );
            default:
                ftl( "Unexpected option: %c", opt );
                exit( EX_USAGE );
        }

    int failedTests = 0;

    xmlNodePtr tests = xmlDocGetRootElement( xmlParseFile( "spectre_tests.xml" ) );
    if (!tests) {
        ftl( "Couldn't find test case: spectre_tests.xml" );
        abort();
    }

    for (xmlNodePtr testCase = tests->children; testCase; testCase = testCase->next) {
        if (testCase->type != XML_ELEMENT_NODE || xmlStrcmp( testCase->name, BAD_CAST "case" ) != 0)
            continue;

        // Read in the test case.
        xmlChar *id = spectre_xmlTestCaseString( testCase, "id" );
        SpectreAlgorithm algorithm = (SpectreAlgorithm)spectre_xmlTestCaseInteger( testCase, "algorithm" );
        xmlChar *userName = spectre_xmlTestCaseString( testCase, "userName" );
        xmlChar *userSecret = spectre_xmlTestCaseString( testCase, "userSecret" );
        SpectreKeyID keyID = spectre_id_str( (char *)spectre_xmlTestCaseString( testCase, "keyID" ) );
        xmlChar *siteName = spectre_xmlTestCaseString( testCase, "siteName" );
        SpectreCounter keyCounter = (SpectreCounter)spectre_xmlTestCaseInteger( testCase, "keyCounter" );
        xmlChar *resultTypeString = spectre_xmlTestCaseString( testCase, "resultType" );
        xmlChar *resultParam = spectre_xmlTestCaseString( testCase, "resultParam" );
        xmlChar *keyPurposeString = spectre_xmlTestCaseString( testCase, "keyPurpose" );
        xmlChar *keyContext = spectre_xmlTestCaseString( testCase, "keyContext" );
        xmlChar *result = spectre_xmlTestCaseString( testCase, "result" );

        SpectreResultType resultType = spectre_type_named( (char *)resultTypeString );
        SpectreKeyPurpose keyPurpose = spectre_purpose_named( (char *)keyPurposeString );

        // Run the test case.
        do {
            if (optind < argc) {
                bool selected = false;
                for (int a = optind; !selected && a <= argc; ++a)
                    if (strstr((char *)id, argv[optind]) == (char *)id)
                        selected = true;
                if (!selected)
                    break;
            }

            fprintf( stdout, "test case %s... ", id );
            if (!xmlStrlen( result )) {
                fprintf( stdout, "abstract.\n" );
                break;
            }

            // 1. calculate the user key.
            const SpectreUserKey *userKey = spectre_user_key(
                    (char *)userName, (char *)userSecret, algorithm );
            if (!userKey) {
                ftl( "Couldn't derive user key." );
                break;
            }

            // Check the user key.
            if (!spectre_id_equals( &keyID, &userKey->keyID )) {
                ++failedTests;
                fprintf( stdout, "FAILED!  (keyID: got %s != expected %s)\n", userKey->keyID.hex, keyID.hex );
                break;
            }

            // 2. calculate the site password.
            const char *testResult = spectre_site_result(
                    userKey, (char *)siteName, resultType, (char *)resultParam, keyCounter, keyPurpose, (char *)keyContext );
            spectre_free( &userKey, sizeof( *userKey ) );
            if (!testResult) {
                ftl( "Couldn't derive site password." );
                break;
            }

            // Check the site result.
            if (xmlStrcmp( result, BAD_CAST testResult ) != 0) {
                ++failedTests;
                fprintf( stdout, "FAILED!  (result: got %s != expected %s)\n", testResult, result );
                spectre_free_string( &testResult );
                break;
            }
            spectre_free_string( &testResult );

            fprintf( stdout, "pass.\n" );
        } while(false);

        // Free test case.
        xmlFree( id );
        xmlFree( userName );
        xmlFree( userSecret );
        xmlFree( siteName );
        xmlFree( resultTypeString );
        xmlFree( resultParam );
        xmlFree( keyPurposeString );
        xmlFree( keyContext );
        xmlFree( result );
    }

    return failedTests;
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!1827 blocks
openSUSE Build Service is sponsored by