From f6554ffa53bb3247d184afc3c89b077c19c5cc5c Mon Sep 17 00:00:00 2001 From: Kaz Kylheku Date: Tue, 18 Oct 2022 07:27:49 -0700 Subject: New project: JP-Hash --- Makefile | 10 +++ README.md | 111 +++++++++++++++++++++++++++++++ jp-hash.c | 164 +++++++++++++++++++++++++++++++++++++++++++++ jp-hash.html | 213 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ jp-hash.js | 98 +++++++++++++++++++++++++++ jp-hash.tl | 68 +++++++++++++++++++ test.awk | 17 +++++ test.sh | 12 ++++ testvec | 71 ++++++++++++++++++++ 9 files changed, 764 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 jp-hash.c create mode 100644 jp-hash.html create mode 100755 jp-hash.js create mode 100755 jp-hash.tl create mode 100755 test.awk create mode 100755 test.sh create mode 100644 testvec diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fd99a3e --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +CFLAGS ?= -Wall -W -Wextra -g # -O2 +.PHONY: all +all: jp-hash jp-hash.tlo +jp-hash: LDLIBS += -lcrypto +jp-hash: jp-hash.o +jp-hash.o: CFLAGS += -DJP_HASH_IMPL=1 -DJP_HASH_MAIN=1 +jp-hash.o: jp-hash.c +jp-hash.tlo: jp-hash.tl; txr --compile=$^:$@ +.PHONY: clean +clean:; rm -f jp-hash jp-hash.o jp-hash.tlo diff --git a/README.md b/README.md new file mode 100644 index 0000000..655be3c --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +## What is JP-Hash? + +JP-Hash is an algorithm which converts any piece of text or other datum into a +textual digest, which has the following properties: + +* length between 8 and 21 characters. + +* consists mostly of lower-case letters. + +* includes one digit. + +* includes one non-alphanumeric character from the set + `!`, `#`, `@`, `$`, `%`, `^`, `&`, `*`, `?` and `/`. + +By amazing coincidence, these requirements are very similar to +common requirements imposed on people are creating or +changing a password. + +Additionally: + +* The digest string is based on combinations of vowels from the Japanese + language, written in romanized form. This means that many of the digests are + memorable and pronounceable, and have a vibe to them that is pleasing to + enthusiasts for things Japanese. + +## How do I get it? + +See the reference implementation source files. Code is given in +TXR Lisp, C and Javascript for the browser as well as Node.js. + +The self-contained jp-hash.html file should load in any browser, +providing a simple UI. + +## What are the details of the algorithm? + +1. First, the input is hashed via the standard SHA256 sum. + +2. Next, the first 18 bytes of the digest are interpreted as an array of 9 + (nine) 16-bit words, little endian. This array is referred to as `word[0]` + through `word[8]`. + +3. Six pseudo-Japanese syllables are derived from `word[0]` through `word[5]` + as follows: each of these word values is reduced to the remainder modulo 97. + Then, the remainder is used as an index into the following array of 97 + strings. The first letter of the first syllable is then capitalized. + +``` +["a", "i", "u", "e", "o", "ya", "yu", "yo", "wa", + "ka", "ki", "ku", "ke", "ko", "ga", "gi", "gu", "ge", "go", + "sa", "shi", "su", "se", "so", "za", "ji", "zu", "ze", "zo", + "ta", "chi", "tsu", "te", "to", "da", "de", "do", + "na", "ni", "nu", "ne", "no", "ha", "hi", "fu", "he", "ho", + "pa", "pi", "pu", "pe", "po", "ba", "bi", "bu", "be", "bo", + "ma", "mi", "mu", "me", "mo", "ra", "ri", "ru", "re", "ro", + "kya", "kyu", "kyo", "gya", "gyu", "gyo", "sha", "shu", "sho", + "ja", "ju", "jo", "cha", "chu", "cho", "nya", "nyu", "nyo", + "hya", "hyu", "hyo", "pya", "pyu", "pyo", "bya", "byu", "byo", + "mya", "myu", "myo", "rya", "ryu", "ryo"] +``` + +4. A digit is chosen using the modulo 10 remainder of `word[6]` as an index + into the digits `0` through `9`. + +5. Similarly, a symbol is chosen using the modulo 10 remainder of `word[7]` as + an index into the aforementioned list `!`, `#`, `@`, `$`, `%`, `^`, `&`, + `*`, `?` and `/`. + +6. The modulo 8 value of `word[8]` is used to select eight cases (0 to 7) for + combining the above values into an output string. The last four of these + cases insert the `n` (letter n) character into certain places of the string. + The details are in the reference implementation. + +## How many JP-Hash digests are there? + +Since there are six syllables chosen from a set of 92, plus two characters each +from a set of ten, the initial steps yield a space of 83,297,200,492,900 (83.3 +(American) trillion). The 8 cases in step (6) all yield distinct results, and +so multiply the space eight-fold to 666,377,603,943,200 possibilities (666.4 +trillion). + +## Are JP-Hash digests secure for password use? +e +JP-Hash is not advertised as being for a specific purpose. In a security +setting, each user must perform their own analysis to understand the security +risks of using any tool in certain ways and with certain inputs. + +## Examples + +These examples come from the `testvec` file. + +``` +a --> Mina4gai@gashan +y --> Shaba%megyu2shize +Mike --> !Tosuda2bukyochon +Romeo --> Potsun&gaso5machi +Sierra --> Nodon&yanu6zuchi +Tango --> Gyoda#hosa6segi +Whiskey --> Muji?pyuna6gyage +sashimi --> Izu0gyubya/gyumyu +ramen --> Byumi$betsu0nyohe +soba --> Arushin^hyapyuryu2 +futon --> Kyoriton#kyaseku1 +``` + +## License + +The JP-Hash reference code is offered under the a one-clause variant of the BSD +license. See the copyright headers in the source files. + +If you publish altered versions of this algorithm, please don't call it +JP-Hash, thanks! If it doesn't pass the `testvec`, it isn't JP-Hash. diff --git a/jp-hash.c b/jp-hash.c new file mode 100644 index 0000000..d4bb4d5 --- /dev/null +++ b/jp-hash.c @@ -0,0 +1,164 @@ +/* + * One-Clause BSD License ("1BSD") + * + * Copyright 2022 Kaz Kylheku + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following condition is + * met: + * + * 1. The source code distribution retains the above copyright notice, + * this condition, and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JP_HASH_H +#define JP_HASH_H + +#ifdef __cplusplus +extern "C" { +#endif + +void jp_hash(char *out, const char *in); + +#ifdef __cplusplus +} +#endif + +#if JP_HASH_IMPL + +#include +#include +#include +#include +#include + +#define elof(array) ((int)(sizeof(array)/sizeof(array[0]))) + +static const char *mora[] = { + "a", "i", "u", "e", "o", "ya", "yu", "yo", "wa", + "ka", "ki", "ku", "ke", "ko", "ga", "gi", "gu", "ge", "go", + "sa", "shi", "su", "se", "so", "za", "ji", "zu", "ze", "zo", + "ta", "chi", "tsu", "te", "to", "da", "de", "do", + "na", "ni", "nu", "ne", "no", "ha", "hi", "fu", "he", "ho", + "pa", "pi", "pu", "pe", "po", "ba", "bi", "bu", "be", "bo", + "ma", "mi", "mu", "me", "mo", "ra", "ri", "ru", "re", "ro", + "kya", "kyu", "kyo", "gya", "gyu", "gyo", "sha", "shu", "sho", + "ja", "ju", "jo", "cha", "chu", "cho", "nya", "nyu", "nyo", + "hya", "hyu", "hyo", "pya", "pyu", "pyo", "bya", "byu", "byo", + "mya", "myu", "myo", "rya", "ryu", "ryo" +}; + +static const char *digit[] = { + "0", "1", "2", "3", "4", + "5", "6", "7", "8", "9" +}; + +static const char *symbol[] = { + "!", "#", "@", "$", "%", + "^", "&", "*", "?", "/" +}; + +void jp_hash(char *out, const char *in) +{ + unsigned char hash[32]; + uint16_t word[9]; + const char *ms[6]; + const char *sym; + const char *dig; + char *ms0; + int i; + + SHA256((const unsigned char *) in, strlen(in), hash); + + for (i = 0; i < elof(word); i++) + word[i] = hash[2*i] | (((uint16_t) hash[2*i + 1]) << 8); + + for (i = 0; i < elof(ms); i++) + ms[i] = mora[word[i] % elof(mora)]; + + dig = digit[word[6] % elof(digit)]; + sym = symbol[word[7] % elof(symbol)]; + + ms0 = strdup(ms[0]); + ms0[0] = toupper((unsigned char) ms0[0]); + ms[0] = ms0; + + switch (word[8] & 7) { + case 0: + snprintf(out, 32, "%s%s%s%s%s%s%s%s", + ms[0], ms[1], ms[2], sym, + ms[3], ms[4], ms[5], dig); + break; + case 1: + snprintf(out, 32, "%s%s%s%s%s%s%s%s", + sym, ms[0], ms[1], ms[2], + dig, ms[3], ms[4], ms[5]); + break; + case 2: + snprintf(out, 32, "%s%s%s%s%s%s%s%s", + ms[0], ms[1], sym, ms[2], + ms[3], dig, ms[4], ms[5]); + break; + case 3: + snprintf(out, 32, "%s%s%s%s%s%s%s%s", + ms[0], ms[1], dig, ms[2], + ms[3], sym, ms[4], ms[5]); + break; + case 4: + snprintf(out, 32, "%s%s%sn%s%s%s%s%s", + ms[0], ms[1], ms[2], sym, + ms[3], ms[4], ms[5], dig); + break; + case 5: + snprintf(out, 32, "%s%s%s%s%s%s%s%sn", + sym, ms[0], ms[1], ms[2], + dig, ms[3], ms[4], ms[5]); + break; + case 6: + snprintf(out, 32, "%s%sn%s%s%s%s%s%s", + ms[0], ms[1], sym, ms[2], + ms[3], dig, ms[4], ms[5]); + break; + case 7: + snprintf(out, 32, "%s%s%s%s%s%s%s%sn", + ms[0], ms[1], dig, ms[2], + ms[3], sym, ms[4], ms[5]); + break; + } + + free(ms0); +} + +#if JP_HASH_MAIN + +int main(int argc, char **argv) +{ + char jph[32]; + + if (argc != 2) { + if (argc) + fprintf(stderr, "%s: one argument required\n", argv[0]); + return EXIT_FAILURE; + } + + jp_hash(jph, argv[1]); + puts(jph); + return 0; +} + +#endif // JP_HASH_MAIN +#endif // JP_HASH_IMPL +#endif // JP_HASH_H diff --git a/jp-hash.html b/jp-hash.html new file mode 100644 index 0000000..2a1514f --- /dev/null +++ b/jp-hash.html @@ -0,0 +1,213 @@ + + + + + JP-Hash + + + + + + + + diff --git a/jp-hash.js b/jp-hash.js new file mode 100755 index 0000000..12d7673 --- /dev/null +++ b/jp-hash.js @@ -0,0 +1,98 @@ +#!/usr/bin/env node + +// One-Clause BSD License ("1BSD") +// +// Copyright 2022 Kaz Kylheku +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following condition is +// met: +// +// 1. The source code distribution retains the above copyright notice, +// this condition, and the following disclaimer. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +const mora = [ + "a", "i", "u", "e", "o", "ya", "yu", "yo", "wa", + "ka", "ki", "ku", "ke", "ko", "ga", "gi", "gu", "ge", "go", + "sa", "shi", "su", "se", "so", "za", "ji", "zu", "ze", "zo", + "ta", "chi", "tsu", "te", "to", "da", "de", "do", + "na", "ni", "nu", "ne", "no", "ha", "hi", "fu", "he", "ho", + "pa", "pi", "pu", "pe", "po", "ba", "bi", "bu", "be", "bo", + "ma", "mi", "mu", "me", "mo", "ra", "ri", "ru", "re", "ro", + "kya", "kyu", "kyo", "gya", "gyu", "gyo", "sha", "shu", "sho", + "ja", "ju", "jo", "cha", "chu", "cho", "nya", "nyu", "nyo", + "hya", "hyu", "hyo", "pya", "pyu", "pyo", "bya", "byu", "byo", + "mya", "myu", "myo", "rya", "ryu", "ryo" +]; + +const digit = [ + "0", "1", "2", "3", "4", + "5", "6", "7", "8", "9" +]; + +const symbol = [ + "!", "#", "@", "$", "%", + "^", "&", "*", "?", "/" +]; + +const crypto = require('crypto'); + +function jp_hash(input) +{ + const hash = crypto.createHash('sha256').update(input).digest(); + let word = []; + let ms = []; + + for (let i = 0; i < 9; i++) + word[i] = hash.readUInt16LE(2*i); + + for (i = 0; i < 6; i++) + ms[i] = mora[word[i] % mora.length]; + + const dig = digit[word[6] % digit.length]; + const sym = symbol[word[7] % symbol.length]; + + ms[0] = ms[0][0].toUpperCase() + ms[0].slice(1); + + switch (word[8] & 7) { + case 0: + return [ms[0], ms[1], ms[2], sym, ms[3], ms[4], ms[5], dig].join(''); + case 1: + return [sym, ms[0], ms[1], ms[2], dig, ms[3], ms[4], ms[5]].join(''); + case 2: + return [ms[0], ms[1], sym, ms[2], ms[3], dig, ms[4], ms[5]].join(''); + case 3: + return [ms[0], ms[1], dig, ms[2], ms[3], sym, ms[4], ms[5]].join(''); + case 4: + return [ms[0], ms[1], ms[2], "n", sym, ms[3], ms[4], ms[5], dig].join(''); + case 5: + return [sym, ms[0], ms[1], ms[2], dig, ms[3], ms[4], ms[5], "n"].join(''); + case 6: + return [ms[0], ms[1], "n", sym, ms[2], ms[3], dig, ms[4], ms[5]].join(''); + case 7: + return [ms[0], ms[1], dig, ms[2], ms[3], sym, ms[4], ms[5], "n"].join(''); + } +} + +const myname = process.argv[1]; +const args = process.argv.slice(2); + +if (args.length != 1) { + console.log(myname, ": ", "one argument required"); + process.exit(1); +} + +console.log(jp_hash(args[0])); diff --git a/jp-hash.tl b/jp-hash.tl new file mode 100755 index 0000000..70f70c6 --- /dev/null +++ b/jp-hash.tl @@ -0,0 +1,68 @@ +#!/usr/bin/env txrlisp + +;; One-Clause BSD License ("1BSD") +;; +;; Copyright 2022 Kaz Kylheku +;; +;; Redistribution and use in source and binary forms, with or without +;; modification, are permitted provided that the following condition is +;; met: +;; +;; 1. The source code distribution retains the above copyright notice, +;; this condition, and the following disclaimer. +;; +;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +;; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +;; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +;; FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +;; COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +;; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +;; BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +;; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +;; CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +;; ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +;; POSSIBILITY OF SUCH DAMAGE. + +(defvarl vowels '#"a i u e o") +(defvarl diphthongs '#"ya yu yo") +(defvarl consonants '#"k g s z t d n h p b m r") +(defvarl symbols '#"! # @ $ % ^ & * ? /") + +(defvarl mora-fix (let ((trie (make-trie))) + (mapdo (op trie-add trie) + '#"si sy zi zy ti tu ty di du dy hu" + '#"shi sh ji j chi tsu ch ji zu j fu") + trie)) + +(defvar mora (flow (append vowels diphthongs '#"wa" + [maprod join consonants vowels] + [maprod join consonants diphthongs]) + (filter-string-tree mora-fix) + uniq)) + +(defun choose (list index) + [list (mod index (len list))]) + +(defun jp-hash (phrase) + (let* ((buf (sha256 phrase)) + (word (carray-buf buf (ffi le-uint16))) + (ms (mapcar (opip word (choose mora)) 0..6)) + (dig (list (pic "#" (mod [word 6] 10)))) + (sym (list (choose symbols [word 7])))) + (upd [ms 0] copy) + (upd [[ms 0] 0] chr-toupper) + (cat-str (caseq (logand [word 8] 7) + (0 (append [ms 0..3] sym [ms 3..6] dig)) + (1 (append sym [ms 0..3] dig [ms 3..6])) + (2 (append [ms 0..2] sym [ms 2..4] dig [ms 4..6])) + (3 (append [ms 0..2] dig [ms 2..4] sym [ms 4..6])) + (4 (append [ms 0..3] '#"n" sym [ms 3..6] dig)) + (5 (append sym [ms 0..3] dig [ms 3..6] '#"n")) + (6 (append [ms 0..2] '#"n" sym [ms 2..4] dig [ms 4..6])) + (7 (append [ms 0..2] dig [ms 2..4] sym [ms 4..6] '#"n")))))) + +(compile-only + (match-case *args* + ((@arg) (put-line (jp-hash arg))) + (@else (put-line `@{self-path}: one argument required`)))) diff --git a/test.awk b/test.awk new file mode 100755 index 0000000..a717ff7 --- /dev/null +++ b/test.awk @@ -0,0 +1,17 @@ +#!/usr/bin/awk -f + +BEGIN { + if (ARGC != 3) { + printf("usage: test.awk \n", ARGV[0]); + exit 1 + } + + program = ARGV[1] + file = ARGV[2] + + while ((getline < file) > 0) { + input = ($1 == "EMPTY" ? "" : $1) + program " '" input "'" | getline jhash + print $1, "-->", jhash + } +} diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..ae60bd8 --- /dev/null +++ b/test.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +success=0 + +printf "testing C implementation\n" +diff -u <(./test.awk ./jp-hash testvec) testvec || success=1 +printf "testing TXR Lisp implementation\n" +diff -u <(./test.awk ./jp-hash.tlo testvec) testvec || success=1 +printf "testing Javascript (Node.js) implementation\n" +diff -u <(./test.awk ./jp-hash.js testvec) testvec || success=1 + +exit $success diff --git a/testvec b/testvec new file mode 100644 index 0000000..bc8a276 --- /dev/null +++ b/testvec @@ -0,0 +1,71 @@ +EMPTY --> Nyubyu9rupi#kifun +a --> Mina4gai@gashan +b --> Seru0byuto/sode +c --> Hoke7pujo$gecho +d --> !Bouyo7myapiho +e --> $Nushori7yamiu +f --> Kyogu^pepa4yugyo +g --> Rege!shaga8mehi +h --> Dabyu%myoa9zachu +i --> Seku3yuyu&shojan +j --> Zoteku$tanogyo4 +k --> Dabya3kabo!yain +l --> Ohyu7daro@bezen +m --> Hyunin$tobo3ugyo +n --> Murekya@chonupyo6 +o --> Hebagya*kebubyo1 +p --> Gurepo#jujumo6 +q --> Kishu?deke3eke +r --> Sunoshu*niyana0 +s --> Basha*piryu3kiko +t --> Hyope1sago?yoran +u --> %Dekute4jupeo +v --> Rudo&mese5ibi +w --> &Guike7rirapun +x --> Shagoku!nyopyupya3 +y --> Shaba%megyu2shize +z --> Mayamyo*mabyomu0 +Alfa --> Sabapu/nyonyufu7 +Bravo --> Pyurya1honyu&namyan +Charlie --> !Dapugo1myushanyo +Delta --> Mepyu#sagyu0pyuro +Echo --> Ryonyon?zosu7gimu +Foxtrot --> !Boyoza2pojishi +Golf --> Tepa*chagi0zapo +Hotel --> @Mezocho0bahepi +India --> Shosu$mokyo3myasha +Juliett --> ^Momyugi3nebyuchi +Kilo --> Hyohi4kyoge%boyun +Lima --> Seke*hepi8ryuchu +Mike --> !Tosuda2bukyochon +November --> Najo%gyai8buji +Oscar --> Gamege$byukyuhyo3 +Papa --> Manyan/suya6bichi +Quebec --> Ryukase#toryopo2 +Romeo --> Potsun&gaso5machi +Sierra --> Nodon&yanu6zuchi +Tango --> Gyoda#hosa6segi +Uniform --> Kibyo!kai4pyukyo +Victor --> Wakiyun?jachuro0 +Whiskey --> Muji?pyuna6gyage +X-ray --> Adakon?kunoa9 +Yankee --> Bio&myubyo7pyuna +Zulu --> Pyarunyun&nyoryoshi9 +sushi --> Sachoken$toryojo0 +sashimi --> Izu0gyubya/gyumyu +wasabi --> Kuja0byuu%nobun +onigiri --> Henejun!bemyazo3 +miso --> Ichin^myuha4chojo +ramen --> Byumi$betsu0nyohe +udon --> Samu6fucho!byohon +soba --> Arushin^hyapyuryu2 +donburi --> &Kepyape7desudo +uni --> Shaton&hopa1kyopo +unagi --> Asha6zuhi$ryugyan +sake --> #Mikiyo5musepa +saba --> Masezan%shuguryu9 +futon --> Kyoriton#kyaseku1 +kaizen --> Zahi*soja0zucha +kamikaze --> $Gyamunyu8pusayun +karaoke --> Nopyade!yodakyu5 +pokemon --> !Yumoshu4myohinon -- cgit v1.2.3