Skip to content

Commit

Permalink
Add multilanguage support; happy New Year!
Browse files Browse the repository at this point in the history
easyseed(1) now generates BIP 39 mnemonic phrases in all languages and
writing systems for which wordlists exist in the BIP repository:

 - Chinese (Simplified)
 - Chinese (Traditional)
 - English
 - French
 - Italian
 - Japanese
 - Korean
 - Spanish

Appropriate metadata updates were also made to reflect the year 2018.
  • Loading branch information
nym-zone committed Jan 1, 2018
1 parent 8bfefc6 commit 1a6e48b
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 27 deletions.
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ By nullius <nullius@nym.zone>
PGP: 0xC2E91CD74A4C57A105F6C21B5A00591B2F307E0C
Bitcoin: 3NULL3ZCUXr7RDLxXeLPDMZDZYxuaYkCnG

Copyright (c) 2017. All rights reserved.
Copyright (c) 2017-18. All rights reserved.

The Antiviral License (AVL) v0.0.1, with added Bitcoin Consensus Clause:

Expand Down
15 changes: 13 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# easyseed
# easyseed(1)

## The easy mnemonic generator for ![](img/bitcoin_32px.png) Bitcoin [BIP 39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) seed phrases.
## The easy, secure *multilanguage* mnemonic generator for ![](img/bitcoin_32px.png) Bitcoin [BIP 39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) seed phrases.

- By nullius <[nullius@nym.zone](mailto:nullius@nym.zone)>
- PGP: [0xC2E91CD74A4C57A105F6C21B5A00591B2F307E0C](https://sks-keyservers.net/pks/lookup?op=get&search=0xC2E91CD74A4C57A105F6C21B5A00591B2F307E0C)
Expand All @@ -10,6 +10,17 @@ I wrote this because I needed a lightweight, reliable BIP 39 seed phrase generat

The source code is written in (mostly sort of) [KNF](https://www.freebsd.org/cgi/man.cgi?query=style&apropos=0&sektion=9&manpath=FreeBSD+11.1-RELEASE+and+Ports&arch=default&format=html). It’s easy to read, and lovingly commented. Anybody with basic knowledge of the C programming language should be able to understand what it does at a glance.

**easyseed(1) can generate mnemonics in all languages and writing systems for which a wordlist exists in the [BIP 39 repository](https://github.com/bitcoin/bips/tree/master/bip-0039).** Listed in alphabetical order:

- Chinese (Simplified) (汉语)
- Chinese (Traditional) (漢語)
- English [default]
- French (Français)
- Italian (Italiano)
- Japanese (日本語)
- Korean (한국어)
- Spanish (Español)

It has been tested on FreeBSD, my main platform, and on Linux. [Unfortunately, I may have slightly mussed the BSD building while preparing for publication; this should soon be fixed. The build system generally is still wonky. This is an early release, with most attention paid to the source code and manpage!]

For further details, [RTFM](https://raw.githubusercontent.com/nym-zone/easyseed/master/easyseed.1.txt). Yes, it has a manpage. Software is unworthy of release if it does not have a proper manpage.
Expand Down
83 changes: 72 additions & 11 deletions easyseed.1
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
.\" PGP: 0xC2E91CD74A4C57A105F6C21B5A00591B2F307E0C
.\" Bitcoin: 3NULL3ZCUXr7RDLxXeLPDMZDZYxuaYkCnG
.\"
.\" Copyright (c) 2017. All rights reserved.
.\" Copyright (c) 2017-18. All rights reserved.
.\"
.\" The Antiviral License (AVL) v0.0.1, with added Bitcoin Consensus Clause:
.\"
Expand Down Expand Up @@ -38,7 +38,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd December 29, 2017
.Dd January 1, 2018
.Dt EASYSEED 1
.Os Bitcoin
.Sh NAME
Expand All @@ -48,6 +48,23 @@
.Nm
.Fl b Ar bits
.Op Fl k Ar keyfile
.Op Fl l Ar lang
.Pp
List languages:
.Pp
.Nm
.Fl L
.Pp
Print wordlist:
.Pp
.Nm
.Fl P
.Op Fl l Ar lang
.Pp
Full, verbose self-tests:
.Pp
.Nm
.Fl T
.Sh DESCRIPTION
.Nm
is a BIP 39 Bitcoin seed phrase generator which is intended to be
Expand All @@ -60,7 +77,7 @@ run on every use.
A seed is read off the system random device; and corresponding
seed words are printed to standard output.
.Pp
The options are as follows:
In general usage, the options are as follows:
.Bl -tag -width ".Fl d Ar argument"
.It Fl b Ar bits
Generate a mnemonic phrase representing a seed with
Expand All @@ -87,6 +104,37 @@ Read key material in the form of binary octets from the specified
Use
.Pa "-"
to specify standard input.
.It Fl l Ar lang
Produce a mnemonic in the language or writing system
.Ar lang .
The language may be specified via various identifiers as listed by the
.Fl L
option.
In alphabetical order, currently supported languages and writing systems are
Chinese
.Pq Simplified ,
Chinese
.Pq Traditional ,
English, French, Italian, Japanese, Korean, and Spanish.
The default language is English.
.El
.Pp
The following modes do not produce a mnemonic phrase:
.Bl -tag -width ".Fl d Ar argument"
.It Fl L
List languages and writing systems which may be used with the
.Fl l Ar lang
option, then exit.
Any of the listed strings may be used as an identifier.
.It Fl P
Print the wordlist to stdout in the source format used at build time,
and print the SHA-256 hash of the wordlist to stderr; then exit.
.It Fl T
Run a full battery of self-tests with verbose results printed to stdout,
then exit.
An abbreviated self-test is run each and every time the
.Nm
utility is used.
.El
.Sh EXIT STATUS
.Ex -std
Expand All @@ -96,6 +144,11 @@ Generate a 24-word mnemonic representing a 256-bit random seed:
.Dl "umask 077"
.Dl "easyseed -b 256 > myseed.txt"
.Pp
Generate an 18-word mnemonic in the Japanese language,
representing a 192-bit random seed:
.Pp
.Dl "easyseed -b 192 -l japanese"
.Pp
Use bits from a keyfile:
.Dl "dd if=/dev/random of=secret.key bs=32 count=1"
.Dl "easyseed -b 256 -k secret.key > myseed.txt"
Expand Down Expand Up @@ -124,17 +177,25 @@ Users should use the
.Nm
utility instead.
.Sh BUGS
.\" Language support is necessarily finite.
.\" The
\." .Nm
\." utility supports all languages which have official BIP 39
\." wordlists in the Bitcoin BIP repository.
Thus far, only English-language seed phrases are supported.
A future version will add support for all languages which have
official wordlists in the Bitcoin BIP repository.
Language support is necessarily finite.
The
.Nm
utility supports all languages which have official BIP 39
wordlists in the Bitcoin BIP repository.
To add your language to the
.Nm
utility, add a wordlist for your language to the BIP 39 wordlist set.
.Pp
The author monkey-pasted language names from a popular wiki site.
If the
.Fl L
option shows an incorrect name for your language,
.Em in
your language, then please file a bug or contact the author.
.Pp
The short identifiers for the two distinct Chinese writing systems are
suboptimal, and inconsistent with reality.
Better suggestions would be appreciated.
.Sh SECURITY CONSIDERATIONS
For secure seed phrase generation, it is imperative to use an adequate
CSPRNG as a source of randomness.
Expand Down
83 changes: 70 additions & 13 deletions easyseed.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* PGP: 0xC2E91CD74A4C57A105F6C21B5A00591B2F307E0C
* Bitcoin: 3NULL3ZCUXr7RDLxXeLPDMZDZYxuaYkCnG
*
* Copyright (c) 2017. All rights reserved.
* Copyright (c) 2017-18. All rights reserved.
*
* The Antiviral License (AVL) v0.0.1, with added Bitcoin Consensus Clause:
*
Expand Down Expand Up @@ -52,6 +52,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#include <err.h>

Expand Down Expand Up @@ -357,32 +358,67 @@ selftest_wordlists(int T_flag)
abort();
}

static const struct wordlist *
selectlang(const char *userlang)
{
size_t len, maxlen = 0;

for (int i = 0; i < sizeof(wordlists)/sizeof(*wordlists); ++i) {
len = strlen(wordlists[i].name);
maxlen = len > maxlen? len : maxlen;
}

for (int i = 0; i < sizeof(wordlists)/sizeof(*wordlists); ++i)
if (strncasecmp(userlang, wordlists[i].name, maxlen) == 0)
return (&wordlists[i]);

for (int i = 0; i < sizeof(wordlists)/sizeof(*wordlists); ++i) {
len = strlen(wordlists[i].lname);
maxlen = len > maxlen? len : maxlen;
}

/* XXX: I do not trust strncasecmp() here. Or setlocale() first? */
for (int i = 0; i < sizeof(wordlists)/sizeof(*wordlists); ++i)
if (strncmp(userlang, wordlists[i].lname, maxlen) == 0)
return (&wordlists[i]);

for (int i = 0; i < sizeof(wordlists)/sizeof(*wordlists); ++i) {
len = strlen(wordlists[i].code2);
maxlen = len > maxlen? len : maxlen;
}

for (int i = 0; i < sizeof(wordlists)/sizeof(*wordlists); ++i)
if (strncasecmp(userlang, wordlists[i].code2, maxlen) == 0)
return (&wordlists[i]);

return (NULL); /* not found */
}

static void
printlang(void)
printlang(FILE *f)
{

printf("# Available wordlists and selectors:\n");
fprintf(f, "# Available wordlists and selectors:\n");
for (int i = 0; i < sizeof(wordlists)/sizeof(*wordlists); ++i)
printf("\t%s: \"%s\" (%s)\n", wordlists[i].name,
fprintf(f, "\t%s: \"%s\" (%s)\n", wordlists[i].name,
wordlists[i].lname, wordlists[i].code2);

exit(1);
}

int
main(int argc, char *argv[])
{
int ch, keyfd = -1, error = 0, O_flag = 0, T_flag = 0;
int ch, keyfd = -1, error = 0, O_flag = 0, P_flag = 0, T_flag = 0;
size_t nbits = 0, nbytes;
char *keymat = NULL;
ssize_t rbytes, wbytes;
const struct wordlist *wl = default_wordlist;

unsigned char seed[32];
char mnemonic[816];
size_t len;

opterr = 0;
while ((ch = getopt(argc, argv, ":LOTb:k:")) > -1) {
while ((ch = getopt(argc, argv, ":LOPTb:k:l:")) > -1) {
switch (ch) {
case 'b': /* bits */
/* XXX: atoi(), hahah */
Expand All @@ -391,8 +427,17 @@ main(int argc, char *argv[])
case 'k':
keymat = optarg;
break;
case 'l':
if ((wl = selectlang(optarg)) == NULL) {
fprintf(stderr, "Unknown language: %s\n",
optarg);
printlang(stderr);
return (1);
}
break;
case 'L':
printlang();
printlang(stdout);
return (0);
case 'O':
O_flag = 1;
break;
Expand All @@ -404,6 +449,11 @@ main(int argc, char *argv[])
}
}

if (P_flag) {
reproduce_wordlist(wl);
return (0);
}

switch (nbits) {
case 128: case 160: case 192: case 224: case 256:
break;
Expand Down Expand Up @@ -470,7 +520,7 @@ main(int argc, char *argv[])
keyfd = -1;
}

mkmnemonic(mnemonic, nbits, seed, english, ascii_space);
mkmnemonic(mnemonic, nbits, seed, wl->wordlist, wl->space);

len = strlen(mnemonic);
mnemonic[len] = '\n';
Expand All @@ -492,9 +542,16 @@ usage(void)
{

fprintf(stderr,
"easyseed -b bits [-k file]\n"
"Valid values for bits: { 128, 160, 192, 224, 256 }\n"
"If give, file length must match bits. \"-\" for stdin.\n"
"# General usage:\n"
"easyseed -b bits [-k file] [-l lang]\n"
"# Valid values for bits: { 128, 160, 192, 224, 256 }\n"
"# If given, file length must match bits. \"-\" for stdin.\n"
"# List languages:\n"
"easyseed -L\n"
"# Print wordlist:\n"
"easyseed -P [-l lang]\n"
"# Full, verbose self-tests:\n"
"easyseed -T\n"
);
exit(1);
}

0 comments on commit 1a6e48b

Please sign in to comment.