| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- /* mnemonic.c
- Copyright (c) 2000 Oren Tirosh <oren@hishome.net>
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
- */
- #include "mnemonic.h"
- /*
- * mn_words_required
- *
- * Description:
- * Calculate the number of words required to encode data using mnemonic
- * encoding.
- *
- * Parameters:
- * size - Size in bytes of data to be encoded
- *
- * Return value:
- * number of words required for the encoding
- */
- int
- mn_words_required (int size)
- {
- return ((size + 1) * 3) / 4;
- }
- /*
- * mn_encode_word_index
- *
- * Description:
- * Perform one step of encoding binary data into words. Returns word index.
- *
- * Parameters:
- * src - Pointer to data buffer to encode
- * srcsize - Size in bytes of data to encode
- * n - Sequence number of word to encode
- * 0 <= n < mn_words_required(srcsize)
- *
- * Return value:
- * 0 - no more words to encode / n is out of range
- * 1..MN_WORDS - word index. May be used as index to the mn_words[] array
- */
- mn_index mn_encode_word_index (void *src, int srcsize, int n)
- {
- mn_word32 x = 0; /* Temporary for MN_BASE arithmetic */
- int offset; /* Offset into src */
- int remaining; /* Octets remaining to end of src */
- int extra = 0; /* Index 7 extra words for 24 bit data */
- int i;
- if (n < 0 || n >= mn_words_required (srcsize))
- return 0; /* word out of range */
- offset = (n / 3) * 4; /* byte offset into src */
- remaining = srcsize - offset;
- if (remaining <= 0)
- return 0;
- if (remaining >= 4)
- remaining = 4;
- for (i = 0; i < remaining; i++)
- x |= ((mn_byte *) src)[offset + i] << (i * 8); /* endianness-agnostic */
- switch (n % 3)
- {
- case 2: /* Third word of group */
- if (remaining == 3) /* special case for 24 bits */
- extra = MN_BASE; /* use one of the 7 3-letter words */
- x /= (MN_BASE * MN_BASE);
- break;
- case 1: /* Second word of group */
- x /= MN_BASE;
- }
- return x % MN_BASE + extra + 1;
- }
- /*
- * mn_encode_word
- *
- * Description:
- * Perform one step of encoding binary data into words. Returns pointer
- * to word.
- *
- * Parameters:
- * src - Pointer to data buffer to encode
- * srcsize - Size of data to encode in bytes
- * n - Sequence number of word to encode.
- * 0 <= n < mn_words_required(srcsize)
- *
- * Return value:
- * NULL - no more words to encode / n is out of range
- * valid pointer - pointer to null-terminated lowercase word. length<=7
- */
- const char *
- mn_encode_word (void *src, int srcsize, int n)
- {
- return mn_words[mn_encode_word_index (src, srcsize, n)];
- }
- /*
- * isletter
- * Utility function - returns nonzero if character c is an ASCII letter.
- */
- static int
- isletter (char c)
- {
- return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z';
- }
- /*
- * mn_next_word_index
- *
- * Description:
- * Perform one step of decoding a null-terminated buffer into word indices.
- * A word is defined as a sequence of letter character separated by one
- * or more non-letter separator characters.
- *
- * Parameters:
- * ptr - Pointer to a pointer to the next character in the buffer.
- * *ptr is modified by the function; see Return Value below.
- *
- * Return value:
- * 0 - If *ptr==0 (points to the null at the end of the buffer) no more
- * words were found in the buffer. Otherwise *ptr points to beginning
- * of an unrecognized word.
- * >0 - index of word found, suitable for decoding with mn_decode_word_index
- * or comparison to values returned from mn_encode_index. *ptr points
- * to first character of next word or to terminating null.
- */
- mn_index
- mn_next_word_index (char **ptr)
- {
- char *wordstart;
- char wordbuf[MN_WORD_BUFLEN];
- int i = 0;
- char c;
- mn_index idx;
- while (**ptr && !isletter (**ptr)) /* skip separator chars */
- (*ptr)++;
- wordstart = *ptr; /* save for error reporting */
- while (**ptr && isletter (**ptr) && i < MN_WORD_BUFLEN - 1)
- {
- c = *(*ptr)++;
- if (c >= 'A' && c <= 'Z')
- c += 'a' - 'A'; /* convert to lowercase */
- wordbuf[i++] = c;
- }
- wordbuf[i] = '\0';
- while (**ptr && isletter (**ptr)) /* skip tail of long words */
- (*ptr)++;
- while (**ptr && !isletter (**ptr)) /* skip separators */
- (*ptr)++;
- if (wordbuf[0] == '\0')
- return 0; /* EOF, no word found */
- for (idx = 1; idx <= MN_WORDS; idx++)
- {
- if (!strcmp (wordbuf, mn_words[idx]))
- return idx;
- /* FIXME: some fancy code should go here
- to accept misspellings and soundalikes.
- (replacing the linear search would also be nice) */
- }
- *ptr = wordstart;
- return 0; /* not found */
- }
- /*
- * mn_decode_word_index
- *
- * Description:
- * Perform one step of decoding a sequence of words into binary data.
- *
- * Parameters:
- * index - Index of word, e.g. return value of mn_next_word_index. Use
- * the value MN_EOF(=0) to signal the end of input.
- * dest - Points to buffer to receive decoded binary result.
- * destsize - Size of buffer
- * offset - Pointer to an integer offset into the destination buffer for
- * next data byte. Initialize *offset to 0 before first call to
- * function. Modified by function and may be used as an
- * indication for the amount of data actually decoded.
- *
- * Return value:
- * The return value indicates the status of the decoding function. It is
- * ok to ignore this value on all calls to the function except the last
- * one (with index=MN_EOF). Any errors encountered will be reported on.
- * the last call. The error code is also returned in *offset (negative
- * values indicate error).
- *
- * MN_OK (==0)
- * for index!=MN_EOF a return value of MN_OK means that
- * decoding has been successful so far.
- * for index==MN_EOF a return value of MN_OK means that decoding
- * of the entire buffer has been successful and the decoder is in
- * a valid state for the end of the message. A total of *offset
- * valid decoded bytes is in the buffer.
- * MN_EREM
- * returned on MN_EOF when an unaccounted arithmetic remainder is
- * in the decoder. Most likely indicates a truncated word sequence.
- * MN_EOVERRUN
- * Not enough room in buffer for decoded data.
- * MN_EOVERRUN24
- * Returned when decoding of data is attempted after decoding one
- * of the 7 words reserved for 24 bit remainders at the end of the
- * message. Probably indicates a garbled messages.
- * MN_EINDEX
- * Bad input index. Naturally this should not happen when using
- * the result of mn_next_word_index.
- * MN_EINDEX24
- * Returned when one of the 7 words reserved for 24 bit remainders
- * is received at an offset inappropriate for a 24 bit remainder.
- * MN_EENCODING
- * Indicates an overflow in MN_BASE arithmetic. Approximately 0.09%
- * of the 3 word combinations are unused and will generate this error.
- */
- int
- mn_decode_word_index (mn_index index, void *dest, int destsize, int *offset)
- {
- mn_word32 x; /* Temporary for MN_BASE arithmetic */
- int groupofs;
- int i;
- if (*offset < 0) /* Error from previous call? report it */
- return *offset;
- if (index < 0 || index > MN_WORDS) /* Word index out of range */
- {
- *offset = MN_EINDEX;
- return *offset;
- }
- if (*offset > destsize) /* out of range? */
- {
- *offset = MN_EOVERRUN;
- return *offset;
- }
- if (index > MN_BASE && *offset % 4 != 2)
- { /* Unexpected 24 bit remainder word */
- *offset = MN_EINDEX24;
- return *offset;
- }
- groupofs = *offset & ~3; /* Offset of 4 byte group containing offet */
- x = 0;
- for (i = 0; i < 4; i++)
- if (groupofs + i < destsize) /* Ignore any bytes outside buffer */
- x |= ((mn_byte *) dest)[groupofs + i] << (i * 8); /* assemble number */
- if (index == MN_EOF) /* Got EOF signal */
- {
- switch (*offset % 4)
- {
- case 3: /* group was three words and the last */
- return MN_OK; /* word was a 24 bit remainder */
- case 2: /* last group has two words */
- if (x <= 0xFFFF) /* should encode 16 bit data */
- return MN_OK;
- else
- {
- *offset = MN_EREM;
- return *offset;
- }
- case 1: /* last group has just one word */
- if (x <= 0xFF) /* should encode 8 bits */
- return MN_OK;
- else
- {
- *offset = MN_EREM;
- return *offset;
- }
- case 0: /* last group was full 3 words */
- return MN_OK;
- }
- }
- if (*offset == destsize) /* At EOF but didn't get MN_EOF */
- {
- *offset = MN_EOVERRUN;
- return *offset;
- }
- index--; /* 1 based to 0 based index */
- switch (*offset % 4)
- {
- case 3: /* Got data past 24 bit remainder */
- *offset = MN_EOVERRUN24;
- return *offset;
- case 2:
- if (index >= MN_BASE)
- { /* 24 bit remainder */
- x += (index - MN_BASE) * MN_BASE * MN_BASE;
- (*offset)++; /* *offset%4 == 3 for next time */
- }
- else
- { /* catch invalid encodings */
- if (index >= 1625 || index == 1624 && x > 1312671)
- {
- *offset = MN_EENCODING;
- return *offset;
- }
- x += index * MN_BASE * MN_BASE;
- (*offset) += 2; /* *offset%4 == 0 for next time */
- }
- break;
- case 1:
- x += index * MN_BASE;
- (*offset)++;
- break;
- case 0:
- x = index;
- (*offset)++;
- break;
- }
- for (i = 0; i < 4; i++)
- if (groupofs + i < destsize) /* Don't step outside the buffer */
- {
- ((mn_byte *) dest)[groupofs + i] = (mn_byte) x % 256;
- x /= 256;
- }
- return MN_OK;
- }
- /*
- * mn_encode
- *
- * Description:
- * Encode a binary data buffer into a null-terminated sequence of words.
- * The word separators are taken from the format string.
- *
- * Parameters:
- * src - Pointer to the beginning of the binary data buffer.
- * srcsize - Size in bytes of binary data buffer
- * dest - Pointer to the beginning of a character buffer
- * destsize - Size in characters of character buffer
- * format - Null-terminated string describing the output format.
- * In the format string any letter or sequence of letters
- * acts as a placeholder for the encoded words. The word
- * placeholders are separated by one or more non-letter
- * characters. When the encoder reaches the end of the
- * format string it starts reading it again.
- * For sample formats see MN_F* constants in mnemonic.h
- * If format is empty or NULL the format MN_FDEFAULT
- * is used.
- *
- * Return value:
- * MN_OK(=0)
- * Encoding was successful.
- * MN_EOVERRUN
- * Output size exceeds size of destination buffer
- * MN_EFORMAT
- * Invalid format string. This function enforces formats which
- * will result in a string which can be successfully decoded by
- * the mn_decode function.
- */
- int
- mn_encode (void *src, int srcsize, char *dest, int destsize, char *format)
- {
- int n;
- char *fmt;
- char *destend = dest + destsize;
- const char *word;
- if (format == 0 || format[0] == '\0')
- format = MN_FDEFAULT;
- fmt = format;
- for (n = 0; n < mn_words_required (srcsize); n++)
- {
- while (dest < destend && *fmt != '\0' && !isletter (*fmt))
- *dest++ = *fmt++;
- if (dest >= destend)
- return MN_EOVERRUN;
- if (*fmt == '\0')
- {
- if (isletter (fmt[-1]) && isletter (format[0]))
- return MN_EFORMAT;
- fmt = format;
- while (dest < destend && *fmt != '\0' && !isletter (*fmt))
- *dest++ = *fmt++;
- if (!isletter (*fmt))
- return MN_EFORMAT;
- }
- word = mn_encode_word (src, srcsize, n);
- if (word == 0)
- return MN_EOVERRUN; /* shouldn't happen, actually */
- while (isletter (*fmt))
- fmt++;
- while (dest < destend && *word != '\0')
- *dest++ = *word++;
- }
- if (dest < destend)
- *dest++ = '\0';
- else
- return MN_EOVERRUN;
- return MN_OK;
- }
- /*
- * mn_decode
- *
- * Description:
- * Decode a text representation in null-terminated character buffer src to
- * binary buffer dest.
- *
- * Parameters:
- * src - Pointer to null-terminated character buffer
- * dest - Pointer to beginning of destination buffer
- * destsize - Size in bytes of destination buffer
- *
- * Return value:
- * This function may return all the value returned by mn_decode_word_index
- * plus the following result code:
- *
- * MN_EWORD - Unrecognized word.
- */
- int
- mn_decode (char *src, void *dest, int destsize)
- {
- mn_index index;
- int offset = 0;
- while ((index = mn_next_word_index (&src)) != 0)
- {
- if (index == 0 && *src != 0)
- return MN_EWORD;
- (void) mn_decode_word_index (index, dest, destsize, &offset);
- }
- (void) mn_decode_word_index (MN_EOF, dest, destsize, &offset);
- return offset;
- }
|