All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wildfly.security.sasl.otp.OTPUtil Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2015 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * 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.
 */

package org.wildfly.security.sasl.otp;

import static org.wildfly.security.mechanism._private.ElytronMessages.saslOTP;
import static org.wildfly.security.password.interfaces.OneTimePassword.*;
import static org.wildfly.security.sasl.otp.OTP.*;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Locale;
import java.util.Random;

import javax.security.sasl.SaslException;

import org.wildfly.common.iteration.ByteIterator;
import org.wildfly.common.iteration.CodePointIterator;

/**
 * @author Farah Juma
 */
class OTPUtil {

    public static final int[] DELIMS = new int[] {'\n', '\r', '\t', ' '};
    public static final String[] RESPONSE_TYPES = new String[] { WORD_RESPONSE, INIT_WORD_RESPONSE, HEX_RESPONSE, INIT_HEX_RESPONSE };
    public static final String[] PASSWORD_FORMAT_TYPES = new String[] { PASS_PHRASE, DIRECT_OTP };

    private static final int FOUR_LETTER_WORDS_OFFSET = 571;
    private static final byte[] randomCharDictionary;
    static {
        byte[] dict = new byte[36];
        int i = 0;
        for (byte c = 'a'; c <= 'z'; c ++) {
            dict[i ++] = c;
        }
        for (byte c = '0'; c <= '9'; c ++) {
            dict[i ++] = c;
        }
        assert i == dict.length;
        randomCharDictionary = dict;
    }

    /**
     * Pass the given input through a hash function and fold the result to 64 bits.
     *
     * @param algorithm the OTP algorithm, must be either "otp-md5" or "otp-sha1"
     * @param messageDigest the {@code MessageDigest} to use when generating the hash
     * @param input the data to hash
     * @return the folded hash
     */
    public static byte[] hashAndFold(String algorithm, MessageDigest messageDigest, byte[] input) {
        messageDigest.update(input);
        byte[] result = messageDigest.digest();
        byte[] hash = new byte[OTP_HASH_SIZE];

        // Fold the result (either 128 bits for MD5 or 160 bits for SHA-1) to 64 bits
        for (int i = OTP_HASH_SIZE; i < result.length; i++) {
            result[i % OTP_HASH_SIZE] ^= result[i];
        }
        System.arraycopy(result, 0, hash, 0, OTP_HASH_SIZE);

        if (algorithm.equals(ALGORITHM_OTP_SHA1)) {
            reverse(hash, 0, 4);
            reverse(hash, 4, 4);
        }
        return hash;
    }

    /**
     * Pass the given input through a hash function and fold the result to 64 bits.
     *
     * @param algorithm the OTP algorithm
     * @param input the data to hash
     * @return the folded hash
     * @throws SaslException if the given OTP algorithm is invalid
     */
    public static byte[] hashAndFold(String algorithm, byte[] input) throws SaslException {
        final MessageDigest messageDigest;
        try {
            messageDigest = getMessageDigest(algorithm);
        } catch (NoSuchAlgorithmException e) {
            throw saslOTP.mechInvalidOTPAlgorithm(algorithm).toSaslException();
        }
        return hashAndFold(algorithm, messageDigest, input);
    }

    /**
     * Convert the given OTP hash into hexadecimal format.
     *
     * @param otp the OTP hash
     * @return the hexadecimal value that corresponds to the given OTP hash
     */
    public static String convertToHex(byte[] otp) {
        return ByteIterator.ofBytes(otp).hexEncode().drainToString();
    }

    /**
     * Convert the given hexadecimal OTP value into a hash.
     *
     * @param otp the OTP in hexadecimal format
     * @return the OTP hash that corresponds to the given hexadecimal value
     * @throws SaslException if an error occurs while parsing the hexadecimal value
     */
    public static byte[] convertFromHex(String otp) throws SaslException {
        final CodePointIterator cpi = CodePointIterator.ofString(otp);
        final CodePointIterator di = cpi.delimitedBy(DELIMS);
        // Remove any white space
        final StringBuilder sb = new StringBuilder();
        while (di.hasNext()) {
            di.drainTo(sb);
            skipDelims(di, cpi);
        }
        return CodePointIterator.ofString(sb.toString()).hexDecode().drain();
    }

    /**
     * Convert the given OTP hash into a sequence of six words.
     *
     * @param otp the OTP hash
     * @param alternateDictionary the alternate dictionary to use (if {@code null}, the standard OTP dictionary will be used)
     * @return the sequence of six words that corresponds to the given OTP hash
     */
    public static String convertToWords(byte[] otp, String[] alternateDictionary) {
        final StringBuilder words = new StringBuilder();
        long otpValue = eightBytesToLong(otp);
        for (int i = 0; i < 6; i++) {
           words.append(getWord(otpValue, i, alternateDictionary));
           if (i != 5) {
               words.append(' ');
           }
        }
        return words.toString();
    }

    /**
     * Convert the given OTP hash into the specified format.
     *
     * @param otp the OTP hash
     * @param responseType the response type
     * @param alternateDictionary the alternate dictionary to use (if {@code null}, the standard OTP dictionary will be used)
     * @return the formatted OTP
     * @throws SaslException if the response type is invalid
     */
    public static String formatOTP(byte[] otp, String responseType, String[] alternateDictionary) throws SaslException {
        switch (responseType) {
            case HEX_RESPONSE:
            case INIT_HEX_RESPONSE: {
                return convertToHex(otp);
            }
            case WORD_RESPONSE:
            case INIT_WORD_RESPONSE: {
                return convertToWords(otp, alternateDictionary);
            }
            default:
                throw saslOTP.mechInvalidOTPResponseType().toSaslException();
        }
    }

    /**
     * Convert the given six words into an OTP hash.
     *
     * @param words the OTP formatted as a sequence of six words
     * @param algorithm the OTP algorithm
     * @return the OTP hash that corresponds to the given sequence of six words
     * @throws SaslException if the given algorithm is invalid or if the parity encoded in the
     * last two bits of the final word is incorrect or if an error occurs while parsing the words
     */
    public static byte[] convertFromWords(String words, String algorithm) throws SaslException {
        final CodePointIterator cpi = CodePointIterator.ofString(words);
        final CodePointIterator di = cpi.delimitedBy(DELIMS);
        final MessageDigest messageDigest;
        int dictionaryIndex = -1;
        long otpValue = 0L;
        int parity;
        String word;
        byte[] result;
        boolean useStandardDictionary = true;
        try {
            messageDigest = getMessageDigest(algorithm);
        } catch (NoSuchAlgorithmException e) {
            throw saslOTP.mechInvalidOTPAlgorithm(algorithm).toSaslException();
        }
        for (int i = 0; i < 6; i++) {
            word = di.drainToString();
            if (useStandardDictionary) {
                dictionaryIndex = searchStandardDictionary(word);
                if (dictionaryIndex < 0) {
                    if (i == 0) {
                        // The first word could not be found, switch to using an alternate dictionary instead
                        useStandardDictionary = false;
                    } else {
                        throw saslOTP.mechInvalidOTP().toSaslException();
                    }
                }
            }
            if (! useStandardDictionary) {
                // Alternate dictionaries must satisfy the following property:
                // alg(word) % 2048 = N (where N is the position of the word in the dictionary)
                result = hashAndFold(algorithm, messageDigest, word.getBytes(StandardCharsets.UTF_8));
                dictionaryIndex = ((result[result.length - 2] & 0x7) << 8) | (result[result.length - 1] & 0xff);
                messageDigest.reset();
            }
            skipDelims(di, cpi);
            if (i < 5) {
                otpValue |= (((long) dictionaryIndex) << (53 - (11 * i)));
            } else {
                otpValue |= ((((long) dictionaryIndex) & 0x7fc) >> 2);
                parity = dictionaryIndex & 0x3;
                if (parity != calculateParity(otpValue)) {
                    throw saslOTP.mechIncorrectParity().toSaslException();
                }
            }
        }
        return longToEightBytes(otpValue);
    }

    /**
     * Search the standard OTP dictionary for the given word.
     *
     * @param word the word to search for
     * @return the index of the word if it is found and a value less than 0 otherwise
     */
    private static int searchStandardDictionary(String word) {
        if (word.length() < 4) {
            return Arrays.binarySearch(STANDARD_DICTIONARY, 0, FOUR_LETTER_WORDS_OFFSET, word.toUpperCase(Locale.ENGLISH));
        } else {
            return Arrays.binarySearch(STANDARD_DICTIONARY, FOUR_LETTER_WORDS_OFFSET, STANDARD_DICTIONARY.length, word.toUpperCase(Locale.ENGLISH));
        }
    }

    /**
     * Get the word from the dictionary that corresponds to the given index for
     * the given OTP hash.
     *
     * @param otp the OTP hash
     * @param index the index of the word to obtain, must be between 0 and 5 (inclusive)
     * @param alternateDictionary the alternate dictionary to use (if {@code null}, the standard OTP dictionary will be used)
     * @return the word that corresponds to the given index for the given OTP hash
     */
    private static String getWord(long otp, int index, String[] alternateDictionary) {
        int dictionaryIndex;
        if ((index >= 0) && (index < 5)) {
            dictionaryIndex = (int) ((otp >> (53 - (11 * index))) & 0x7ff);
        } else if (index == 5) {
            dictionaryIndex = (int) ((((otp << 2) & 0x7fc) | calculateParity(otp)) & 0x7ff);
        } else {
            throw new IllegalArgumentException();
        }
        if (alternateDictionary != null) {
            return alternateDictionary[dictionaryIndex];
        } else {
            return STANDARD_DICTIONARY[dictionaryIndex];
        }
    }

    /**
     * Break down the given hash into pairs of bits and then calculate the sum of the pairs.
     *
     * @param hash the hash
     * @return the two least significant bits of the sum of the pairs of bits from the given hash
     */
    private static int calculateParity(long hash) {
        int parity = 0;
        for (int i = 0; i < 64; i += 2) {
            parity += (hash & 0x3);
            hash >>= 2;
        }
        return (parity & 0x3);
    }

    public static int getResponseTypeChoiceIndex(String responseType) throws SaslException {
        switch (responseType) {
            case WORD_RESPONSE:
                return 0;
            case INIT_WORD_RESPONSE:
                return 1;
            case HEX_RESPONSE:
                return 2;
            case INIT_HEX_RESPONSE:
                return 3;
            default:
                throw saslOTP.mechInvalidOTPResponseType().toSaslException();
        }
    }

    public static int getPasswordFormatTypeChoiceIndex(String passwordFormatType) throws SaslException {
        switch (passwordFormatType) {
            case PASS_PHRASE:
                return 0;
            case DIRECT_OTP:
                return 1;
            default:
                throw saslOTP.mechInvalidOTPPasswordFormatType().toSaslException();
        }
    }

    public static MessageDigest getMessageDigest(String algorithm) throws NoSuchAlgorithmException {
        switch (algorithm) {
            case ALGORITHM_OTP_MD5:
                return MessageDigest.getInstance("MD5");
            case ALGORITHM_OTP_SHA1:
                return MessageDigest.getInstance("SHA-1");
            case ALGORITHM_OTP_SHA_256:
                return MessageDigest.getInstance("SHA-256");
            case ALGORITHM_OTP_SHA_384:
                return MessageDigest.getInstance("SHA-384");
            case ALGORITHM_OTP_SHA_512:
                return MessageDigest.getInstance("SHA-512");
            default:
                throw new NoSuchAlgorithmException();
        }
    }

    public static String messageDigestAlgorithm(String algorithm) throws NoSuchAlgorithmException {
        switch (algorithm) {
            case ALGORITHM_OTP_MD5:
                return MD5;
            case ALGORITHM_OTP_SHA1:
                return SHA1;
            case ALGORITHM_OTP_SHA_256:
                return SHA256;
            case ALGORITHM_OTP_SHA_384:
                return SHA384;
            case ALGORITHM_OTP_SHA_512:
                return SHA512;
            default:
                throw new NoSuchAlgorithmException();
        }
    }

    public static void validateAlternateDictionary(String[] dictionary) throws SaslException {
        if ((dictionary == null) || (dictionary.length != DICTIONARY_SIZE)) {
            throw saslOTP.mechInvalidOTPAlternateDictionary().toSaslException();
        }
        for (int i = 0; i < dictionary.length; i++) {
            if (searchStandardDictionary(dictionary[i]) >= 0) {
                throw saslOTP.mechInvalidOTPAlternateDictionary().toSaslException();
            }
        }
    }

    public static void validateUserName(String userName) throws SaslException {
        if (userName == null) {
            throw saslOTP.mechNoLoginNameGiven().toSaslException();
        } else if (userName.length() > MAX_AUTHENTICATION_ID_LENGTH) {
            throw saslOTP.mechAuthenticationNameTooLong().toSaslException();
        } else if (userName.isEmpty()) {
            throw saslOTP.mechAuthenticationNameIsEmpty().toSaslException();
        }
    }

    public static void validateAuthorizationId(String authorizationId) throws SaslException {
        if ((authorizationId != null) && (authorizationId.length() > MAX_AUTHORIZATION_ID_LENGTH)) {
            throw saslOTP.mechAuthorizationIdTooLong().toSaslException();
        }
    }

    public static void validateAlgorithm(String algorithm) throws SaslException {
        switch (algorithm) {
            case ALGORITHM_OTP_MD5:
            case ALGORITHM_OTP_SHA1:
            case ALGORITHM_OTP_SHA_256:
            case ALGORITHM_OTP_SHA_384:
            case ALGORITHM_OTP_SHA_512:
                return;
            default:
                throw saslOTP.mechInvalidOTPAlgorithm(algorithm).toSaslException();
        }
    }

    public static void validateSequenceNumber(int sequenceNumber) throws SaslException {
        if (sequenceNumber < 1) {
            throw saslOTP.mechInvalidOTPSequenceNumber().toSaslException();
        }
    }

    public static void validateSeed(String seed) throws SaslException {
        if ((seed.length() < MIN_SEED_LENGTH) || (seed.length() > MAX_SEED_LENGTH)) {
            throw saslOTP.mechInvalidOTPSeed().toSaslException();
        }
        // Must only contain alphanumeric characters
        for (int i = 0; i < seed.length(); i++) {
            if (! Character.isLetterOrDigit(seed.charAt(i))) {
                throw saslOTP.mechInvalidCharacterInSeed().toSaslException();
            }
        }
    }

    public static void validatePassPhrase(String passPhrase) throws SaslException {
        if ((passPhrase == null) || (passPhrase.length() < MIN_PASS_PHRASE_LENGTH)
                || (passPhrase.length() > MAX_PASS_PHRASE_LENGTH)) {
            throw saslOTP.mechInvalidOTPPassPhrase().toSaslException();
        }
    }

    public static String generateRandomAlphanumericString(int length, Random random) {
        final byte[] chars = new byte[length];
        for (int i = 0; i < length; i ++) {
            chars[i] = randomCharDictionary[random.nextInt(36)];
        }
        return new String(chars, StandardCharsets.US_ASCII);
    }

    public static void skipDelims(CodePointIterator di, CodePointIterator cpi, int...delims) throws SaslException {
        while ((! di.hasNext()) && cpi.hasNext()) {
            if (! isDelim(cpi.next(), delims)) {
                throw saslOTP.mechInvalidMessageReceived().toSaslException();
            }
        }
    }

    public static void skipDelims(CodePointIterator di, CodePointIterator cpi) throws SaslException {
        skipDelims(di, cpi, DELIMS);
    }

    private static boolean isDelim(int c, int... delims) {
        for (int delim : delims) {
            if (delim == c) {
                return true;
            }
        }
        return false;
    }

    private static void reverse(byte[] bytes, int offset, int length) {
        byte tmp;
        int mid = (length / 2) + offset;
        for (int i = offset, j = offset + length - 1; i < mid; i++, j--) {
            tmp = bytes[i];
            bytes[i] = bytes[j];
            bytes[j] = tmp;
        }
    }

    private static long eightBytesToLong(final byte[] b) {
        long value = 0L;
        for (int i = 0; i < 8; i++) {
            value = (value << 8) | (b[i] & 0xff);
        }
        return value;
    }

    private static byte[] longToEightBytes(final long value) {
        byte[] b = new byte[8];
        int shift = 56;
        for (int i = 0; i < 8; i++) {
            b[i] = (byte) ((value >>> shift) & 0xff);
            shift -= 8;
        }
        return b;
    }

    // The standard OTP dictionary as defined in RFC 1760 (https://tools.ietf.org/html/rfc1760)
    private static final String[] STANDARD_DICTIONARY =
            new String[] {"A",     "ABE",   "ACE",   "ACT",   "AD",    "ADA",  "ADD",
        "AGO",   "AID",   "AIM",   "AIR",   "ALL",   "ALP",   "AM",    "AMY",
        "AN",    "ANA",   "AND",   "ANN",   "ANT",   "ANY",   "APE",   "APS",
        "APT",   "ARC",   "ARE",   "ARK",   "ARM",   "ART",   "AS",    "ASH",
        "ASK",   "AT",    "ATE",   "AUG",   "AUK",   "AVE",   "AWE",   "AWK",
        "AWL",   "AWN",   "AX",    "AYE",   "BAD",   "BAG",   "BAH",   "BAM",
        "BAN",   "BAR",   "BAT",   "BAY",   "BE",    "BED",   "BEE",   "BEG",
        "BEN",   "BET",   "BEY",   "BIB",   "BID",   "BIG",   "BIN",   "BIT",
        "BOB",   "BOG",   "BON",   "BOO",   "BOP",   "BOW",   "BOY",   "BUB",
        "BUD",   "BUG",   "BUM",   "BUN",   "BUS",   "BUT",   "BUY",   "BY",
        "BYE",   "CAB",   "CAL",   "CAM",   "CAN",   "CAP",   "CAR",   "CAT",
        "CAW",   "COD",   "COG",   "COL",   "CON",   "COO",   "COP",   "COT",
        "COW",   "COY",   "CRY",   "CUB",   "CUE",   "CUP",   "CUR",   "CUT",
        "DAB",   "DAD",   "DAM",   "DAN",   "DAR",   "DAY",   "DEE",   "DEL",
        "DEN",   "DES",   "DEW",   "DID",   "DIE",   "DIG",   "DIN",   "DIP",
        "DO",    "DOE",   "DOG",   "DON",   "DOT",   "DOW",   "DRY",   "DUB",
        "DUD",   "DUE",   "DUG",   "DUN",   "EAR",   "EAT",   "ED",    "EEL",
        "EGG",   "EGO",   "ELI",   "ELK",   "ELM",   "ELY",   "EM",    "END",
        "EST",   "ETC",   "EVA",   "EVE",   "EWE",   "EYE",   "FAD",   "FAN",
        "FAR",   "FAT",   "FAY",   "FED",   "FEE",   "FEW",   "FIB",   "FIG",
        "FIN",   "FIR",   "FIT",   "FLO",   "FLY",   "FOE",   "FOG",   "FOR",
        "FRY",   "FUM",   "FUN",   "FUR",   "GAB",   "GAD",   "GAG",   "GAL",
        "GAM",   "GAP",   "GAS",   "GAY",   "GEE",   "GEL",   "GEM",   "GET",
        "GIG",   "GIL",   "GIN",   "GO",    "GOT",   "GUM",   "GUN",   "GUS",
        "GUT",   "GUY",   "GYM",   "GYP",   "HA",    "HAD",   "HAL",   "HAM",
        "HAN",   "HAP",   "HAS",   "HAT",   "HAW",   "HAY",   "HE",    "HEM",
        "HEN",   "HER",   "HEW",   "HEY",   "HI",    "HID",   "HIM",   "HIP",
        "HIS",   "HIT",   "HO",    "HOB",   "HOC",   "HOE",   "HOG",   "HOP",
        "HOT",   "HOW",   "HUB",   "HUE",   "HUG",   "HUH",   "HUM",   "HUT",
        "I",     "ICY",   "IDA",   "IF",    "IKE",   "ILL",   "INK",   "INN",
        "IO",    "ION",   "IQ",    "IRA",   "IRE",   "IRK",   "IS",    "IT",
        "ITS",   "IVY",   "JAB",   "JAG",   "JAM",   "JAN",   "JAR",   "JAW",
        "JAY",   "JET",   "JIG",   "JIM",   "JO",    "JOB",   "JOE",   "JOG",
        "JOT",   "JOY",   "JUG",   "JUT",   "KAY",   "KEG",   "KEN",   "KEY",
        "KID",   "KIM",   "KIN",   "KIT",   "LA",    "LAB",   "LAC",   "LAD",
        "LAG",   "LAM",   "LAP",   "LAW",   "LAY",   "LEA",   "LED",   "LEE",
        "LEG",   "LEN",   "LEO",   "LET",   "LEW",   "LID",   "LIE",   "LIN",
        "LIP",   "LIT",   "LO",    "LOB",   "LOG",   "LOP",   "LOS",   "LOT",
        "LOU",   "LOW",   "LOY",   "LUG",   "LYE",   "MA",    "MAC",   "MAD",
        "MAE",   "MAN",   "MAO",   "MAP",   "MAT",   "MAW",   "MAY",   "ME",
        "MEG",   "MEL",   "MEN",   "MET",   "MEW",   "MID",   "MIN",   "MIT",
        "MOB",   "MOD",   "MOE",   "MOO",   "MOP",   "MOS",   "MOT",   "MOW",
        "MUD",   "MUG",   "MUM",   "MY",    "NAB",   "NAG",   "NAN",   "NAP",
        "NAT",   "NAY",   "NE",    "NED",   "NEE",   "NET",   "NEW",   "NIB",
        "NIL",   "NIP",   "NIT",   "NO",    "NOB",   "NOD",   "NON",   "NOR",
        "NOT",   "NOV",   "NOW",   "NU",    "NUN",   "NUT",   "O",     "OAF",
        "OAK",   "OAR",   "OAT",   "ODD",   "ODE",   "OF",    "OFF",   "OFT",
        "OH",    "OIL",   "OK",    "OLD",   "ON",    "ONE",   "OR",    "ORB",
        "ORE",   "ORR",   "OS",    "OTT",   "OUR",   "OUT",   "OVA",   "OW",
        "OWE",   "OWL",   "OWN",   "OX",    "PA",    "PAD",   "PAL",   "PAM",
        "PAN",   "PAP",   "PAR",   "PAT",   "PAW",   "PAY",   "PEA",   "PEG",
        "PEN",   "PEP",   "PER",   "PET",   "PEW",   "PHI",   "PI",    "PIE",
        "PIN",   "PIT",   "PLY",   "PO",    "POD",   "POE",   "POP",   "POT",
        "POW",   "PRO",   "PRY",   "PUB",   "PUG",   "PUN",   "PUP",   "PUT",
        "QUO",   "RAG",   "RAM",   "RAN",   "RAP",   "RAT",   "RAW",   "RAY",
        "REB",   "RED",   "REP",   "RET",   "RIB",   "RID",   "RIG",   "RIM",
        "RIO",   "RIP",   "ROB",   "ROD",   "ROE",   "RON",   "ROT",   "ROW",
        "ROY",   "RUB",   "RUE",   "RUG",   "RUM",   "RUN",   "RYE",   "SAC",
        "SAD",   "SAG",   "SAL",   "SAM",   "SAN",   "SAP",   "SAT",   "SAW",
        "SAY",   "SEA",   "SEC",   "SEE",   "SEN",   "SET",   "SEW",   "SHE",
        "SHY",   "SIN",   "SIP",   "SIR",   "SIS",   "SIT",   "SKI",   "SKY",
        "SLY",   "SO",    "SOB",   "SOD",   "SON",   "SOP",   "SOW",   "SOY",
        "SPA",   "SPY",   "SUB",   "SUD",   "SUE",   "SUM",   "SUN",   "SUP",
        "TAB",   "TAD",   "TAG",   "TAN",   "TAP",   "TAR",   "TEA",   "TED",
        "TEE",   "TEN",   "THE",   "THY",   "TIC",   "TIE",   "TIM",   "TIN",
        "TIP",   "TO",    "TOE",   "TOG",   "TOM",   "TON",   "TOO",   "TOP",
        "TOW",   "TOY",   "TRY",   "TUB",   "TUG",   "TUM",   "TUN",   "TWO",
        "UN",    "UP",    "US",    "USE",   "VAN",   "VAT",   "VET",   "VIE",
        "WAD",   "WAG",   "WAR",   "WAS",   "WAY",   "WE",    "WEB",   "WED",
        "WEE",   "WET",   "WHO",   "WHY",   "WIN",   "WIT",   "WOK",   "WON",
        "WOO",   "WOW",   "WRY",   "WU",    "YAM",   "YAP",   "YAW",   "YE",
        "YEA",   "YES",   "YET",   "YOU",   "ABED",  "ABEL",  "ABET",  "ABLE",
        "ABUT",  "ACHE",  "ACID",  "ACME",  "ACRE",  "ACTA",  "ACTS",  "ADAM",
        "ADDS",  "ADEN",  "AFAR",  "AFRO",  "AGEE",  "AHEM",  "AHOY",  "AIDA",
        "AIDE",  "AIDS",  "AIRY",  "AJAR",  "AKIN",  "ALAN",  "ALEC",  "ALGA",
        "ALIA",  "ALLY",  "ALMA",  "ALOE",  "ALSO",  "ALTO",  "ALUM",  "ALVA",
        "AMEN",  "AMES",  "AMID",  "AMMO",  "AMOK",  "AMOS",  "AMRA",  "ANDY",
        "ANEW",  "ANNA",  "ANNE",  "ANTE",  "ANTI",  "AQUA",  "ARAB",  "ARCH",
        "AREA",  "ARGO",  "ARID",  "ARMY",  "ARTS",  "ARTY",  "ASIA",  "ASKS",
        "ATOM",  "AUNT",  "AURA",  "AUTO",  "AVER",  "AVID",  "AVIS",  "AVON",
        "AVOW",  "AWAY",  "AWRY",  "BABE",  "BABY",  "BACH",  "BACK",  "BADE",
        "BAIL",  "BAIT",  "BAKE",  "BALD",  "BALE",  "BALI",  "BALK",  "BALL",
        "BALM",  "BAND",  "BANE",  "BANG",  "BANK",  "BARB",  "BARD",  "BARE",
        "BARK",  "BARN",  "BARR",  "BASE",  "BASH",  "BASK",  "BASS",  "BATE",
        "BATH",  "BAWD",  "BAWL",  "BEAD",  "BEAK",  "BEAM",  "BEAN",  "BEAR",
        "BEAT",  "BEAU",  "BECK",  "BEEF",  "BEEN",  "BEER",  "BEET",  "BELA",
        "BELL",  "BELT",  "BEND",  "BENT",  "BERG",  "BERN",  "BERT",  "BESS",
        "BEST",  "BETA",  "BETH",  "BHOY",  "BIAS",  "BIDE",  "BIEN",  "BILE",
        "BILK",  "BILL",  "BIND",  "BING",  "BIRD",  "BITE",  "BITS",  "BLAB",
        "BLAT",  "BLED",  "BLEW",  "BLOB",  "BLOC",  "BLOT",  "BLOW",  "BLUE",
        "BLUM",  "BLUR",  "BOAR",  "BOAT",  "BOCA",  "BOCK",  "BODE",  "BODY",
        "BOGY",  "BOHR",  "BOIL",  "BOLD",  "BOLO",  "BOLT",  "BOMB",  "BONA",
        "BOND",  "BONE",  "BONG",  "BONN",  "BONY",  "BOOK",  "BOOM",  "BOON",
        "BOOT",  "BORE",  "BORG",  "BORN",  "BOSE",  "BOSS",  "BOTH",  "BOUT",
        "BOWL",  "BOYD",  "BRAD",  "BRAE",  "BRAG",  "BRAN",  "BRAY",  "BRED",
        "BREW",  "BRIG",  "BRIM",  "BROW",  "BUCK",  "BUDD",  "BUFF",  "BULB",
        "BULK",  "BULL",  "BUNK",  "BUNT",  "BUOY",  "BURG",  "BURL",  "BURN",
        "BURR",  "BURT",  "BURY",  "BUSH",  "BUSS",  "BUST",  "BUSY",  "BYTE",
        "CADY",  "CAFE",  "CAGE",  "CAIN",  "CAKE",  "CALF",  "CALL",  "CALM",
        "CAME",  "CANE",  "CANT",  "CARD",  "CARE",  "CARL",  "CARR",  "CART",
        "CASE",  "CASH",  "CASK",  "CAST",  "CAVE",  "CEIL",  "CELL",  "CENT",
        "CERN",  "CHAD",  "CHAR",  "CHAT",  "CHAW",  "CHEF",  "CHEN",  "CHEW",
        "CHIC",  "CHIN",  "CHOU",  "CHOW",  "CHUB",  "CHUG",  "CHUM",  "CITE",
        "CITY",  "CLAD",  "CLAM",  "CLAN",  "CLAW",  "CLAY",  "CLOD",  "CLOG",
        "CLOT",  "CLUB",  "CLUE",  "COAL",  "COAT",  "COCA",  "COCK",  "COCO",
        "CODA",  "CODE",  "CODY",  "COED",  "COIL",  "COIN",  "COKE",  "COLA",
        "COLD",  "COLT",  "COMA",  "COMB",  "COME",  "COOK",  "COOL",  "COON",
        "COOT",  "CORD",  "CORE",  "CORK",  "CORN",  "COST",  "COVE",  "COWL",
        "CRAB",  "CRAG",  "CRAM",  "CRAY",  "CREW",  "CRIB",  "CROW",  "CRUD",
        "CUBA",  "CUBE",  "CUFF",  "CULL",  "CULT",  "CUNY",  "CURB",  "CURD",
        "CURE",  "CURL",  "CURT",  "CUTS",  "DADE",  "DALE",  "DAME",  "DANA",
        "DANE",  "DANG",  "DANK",  "DARE",  "DARK",  "DARN",  "DART",  "DASH",
        "DATA",  "DATE",  "DAVE",  "DAVY",  "DAWN",  "DAYS",  "DEAD",  "DEAF",
        "DEAL",  "DEAN",  "DEAR",  "DEBT",  "DECK",  "DEED",  "DEEM",  "DEER",
        "DEFT",  "DEFY",  "DELL",  "DENT",  "DENY",  "DESK",  "DIAL",  "DICE",
        "DIED",  "DIET",  "DIME",  "DINE",  "DING",  "DINT",  "DIRE",  "DIRT",
        "DISC",  "DISH",  "DISK",  "DIVE",  "DOCK",  "DOES",  "DOLE",  "DOLL",
        "DOLT",  "DOME",  "DONE",  "DOOM",  "DOOR",  "DORA",  "DOSE",  "DOTE",
        "DOUG",  "DOUR",  "DOVE",  "DOWN",  "DRAB",  "DRAG",  "DRAM",  "DRAW",
        "DREW",  "DRUB",  "DRUG",  "DRUM",  "DUAL",  "DUCK",  "DUCT",  "DUEL",
        "DUET",  "DUKE",  "DULL",  "DUMB",  "DUNE",  "DUNK",  "DUSK",  "DUST",
        "DUTY",  "EACH",  "EARL",  "EARN",  "EASE",  "EAST",  "EASY",  "EBEN",
        "ECHO",  "EDDY",  "EDEN",  "EDGE",  "EDGY",  "EDIT",  "EDNA",  "EGAN",
        "ELAN",  "ELBA",  "ELLA",  "ELSE",  "EMIL",  "EMIT",  "EMMA",  "ENDS",
        "ERIC",  "EROS",  "EVEN",  "EVER",  "EVIL",  "EYED",  "FACE",  "FACT",
        "FADE",  "FAIL",  "FAIN",  "FAIR",  "FAKE",  "FALL",  "FAME",  "FANG",
        "FARM",  "FAST",  "FATE",  "FAWN",  "FEAR",  "FEAT",  "FEED",  "FEEL",
        "FEET",  "FELL",  "FELT",  "FEND",  "FERN",  "FEST",  "FEUD",  "FIEF",
        "FIGS",  "FILE",  "FILL",  "FILM",  "FIND",  "FINE",  "FINK",  "FIRE",
        "FIRM",  "FISH",  "FISK",  "FIST",  "FITS",  "FIVE",  "FLAG",  "FLAK",
        "FLAM",  "FLAT",  "FLAW",  "FLEA",  "FLED",  "FLEW",  "FLIT",  "FLOC",
        "FLOG",  "FLOW",  "FLUB",  "FLUE",  "FOAL",  "FOAM",  "FOGY",  "FOIL",
        "FOLD",  "FOLK",  "FOND",  "FONT",  "FOOD",  "FOOL",  "FOOT",  "FORD",
        "FORE",  "FORK",  "FORM",  "FORT",  "FOSS",  "FOUL",  "FOUR",  "FOWL",
        "FRAU",  "FRAY",  "FRED",  "FREE",  "FRET",  "FREY",  "FROG",  "FROM",
        "FUEL",  "FULL",  "FUME",  "FUND",  "FUNK",  "FURY",  "FUSE",  "FUSS",
        "GAFF",  "GAGE",  "GAIL",  "GAIN",  "GAIT",  "GALA",  "GALE",  "GALL",
        "GALT",  "GAME",  "GANG",  "GARB",  "GARY",  "GASH",  "GATE",  "GAUL",
        "GAUR",  "GAVE",  "GAWK",  "GEAR",  "GELD",  "GENE",  "GENT",  "GERM",
        "GETS",  "GIBE",  "GIFT",  "GILD",  "GILL",  "GILT",  "GINA",  "GIRD",
        "GIRL",  "GIST",  "GIVE",  "GLAD",  "GLEE",  "GLEN",  "GLIB",  "GLOB",
        "GLOM",  "GLOW",  "GLUE",  "GLUM",  "GLUT",  "GOAD",  "GOAL",  "GOAT",
        "GOER",  "GOES",  "GOLD",  "GOLF",  "GONE",  "GONG",  "GOOD",  "GOOF",
        "GORE",  "GORY",  "GOSH",  "GOUT",  "GOWN",  "GRAB",  "GRAD",  "GRAY",
        "GREG",  "GREW",  "GREY",  "GRID",  "GRIM",  "GRIN",  "GRIT",  "GROW",
        "GRUB",  "GULF",  "GULL",  "GUNK",  "GURU",  "GUSH",  "GUST",  "GWEN",
        "GWYN",  "HAAG",  "HAAS",  "HACK",  "HAIL",  "HAIR",  "HALE",  "HALF",
        "HALL",  "HALO",  "HALT",  "HAND",  "HANG",  "HANK",  "HANS",  "HARD",
        "HARK",  "HARM",  "HART",  "HASH",  "HAST",  "HATE",  "HATH",  "HAUL",
        "HAVE",  "HAWK",  "HAYS",  "HEAD",  "HEAL",  "HEAR",  "HEAT",  "HEBE",
        "HECK",  "HEED",  "HEEL",  "HEFT",  "HELD",  "HELL",  "HELM",  "HERB",
        "HERD",  "HERE",  "HERO",  "HERS",  "HESS",  "HEWN",  "HICK",  "HIDE",
        "HIGH",  "HIKE",  "HILL",  "HILT",  "HIND",  "HINT",  "HIRE",  "HISS",
        "HIVE",  "HOBO",  "HOCK",  "HOFF",  "HOLD",  "HOLE",  "HOLM",  "HOLT",
        "HOME",  "HONE",  "HONK",  "HOOD",  "HOOF",  "HOOK",  "HOOT",  "HORN",
        "HOSE",  "HOST",  "HOUR",  "HOVE",  "HOWE",  "HOWL",  "HOYT",  "HUCK",
        "HUED",  "HUFF",  "HUGE",  "HUGH",  "HUGO",  "HULK",  "HULL",  "HUNK",
        "HUNT",  "HURD",  "HURL",  "HURT",  "HUSH",  "HYDE",  "HYMN",  "IBIS",
        "ICON",  "IDEA",  "IDLE",  "IFFY",  "INCA",  "INCH",  "INTO",  "IONS",
        "IOTA",  "IOWA",  "IRIS",  "IRMA",  "IRON",  "ISLE",  "ITCH",  "ITEM",
        "IVAN",  "JACK",  "JADE",  "JAIL",  "JAKE",  "JANE",  "JAVA",  "JEAN",
        "JEFF",  "JERK",  "JESS",  "JEST",  "JIBE",  "JILL",  "JILT",  "JIVE",
        "JOAN",  "JOBS",  "JOCK",  "JOEL",  "JOEY",  "JOHN",  "JOIN",  "JOKE",
        "JOLT",  "JOVE",  "JUDD",  "JUDE",  "JUDO",  "JUDY",  "JUJU",  "JUKE",
        "JULY",  "JUNE",  "JUNK",  "JUNO",  "JURY",  "JUST",  "JUTE",  "KAHN",
        "KALE",  "KANE",  "KANT",  "KARL",  "KATE",  "KEEL",  "KEEN",  "KENO",
        "KENT",  "KERN",  "KERR",  "KEYS",  "KICK",  "KILL",  "KIND",  "KING",
        "KIRK",  "KISS",  "KITE",  "KLAN",  "KNEE",  "KNEW",  "KNIT",  "KNOB",
        "KNOT",  "KNOW",  "KOCH",  "KONG",  "KUDO",  "KURD",  "KURT",  "KYLE",
        "LACE",  "LACK",  "LACY",  "LADY",  "LAID",  "LAIN",  "LAIR",  "LAKE",
        "LAMB",  "LAME",  "LAND",  "LANE",  "LANG",  "LARD",  "LARK",  "LASS",
        "LAST",  "LATE",  "LAUD",  "LAVA",  "LAWN",  "LAWS",  "LAYS",  "LEAD",
        "LEAF",  "LEAK",  "LEAN",  "LEAR",  "LEEK",  "LEER",  "LEFT",  "LEND",
        "LENS",  "LENT",  "LEON",  "LESK",  "LESS",  "LEST",  "LETS",  "LIAR",
        "LICE",  "LICK",  "LIED",  "LIEN",  "LIES",  "LIEU",  "LIFE",  "LIFT",
        "LIKE",  "LILA",  "LILT",  "LILY",  "LIMA",  "LIMB",  "LIME",  "LIND",
        "LINE",  "LINK",  "LINT",  "LION",  "LISA",  "LIST",  "LIVE",  "LOAD",
        "LOAF",  "LOAM",  "LOAN",  "LOCK",  "LOFT",  "LOGE",  "LOIS",  "LOLA",
        "LONE",  "LONG",  "LOOK",  "LOON",  "LOOT",  "LORD",  "LORE",  "LOSE",
        "LOSS",  "LOST",  "LOUD",  "LOVE",  "LOWE",  "LUCK",  "LUCY",  "LUGE",
        "LUKE",  "LULU",  "LUND",  "LUNG",  "LURA",  "LURE",  "LURK",  "LUSH",
        "LUST",  "LYLE",  "LYNN",  "LYON",  "LYRA",  "MACE",  "MADE",  "MAGI",
        "MAID",  "MAIL",  "MAIN",  "MAKE",  "MALE",  "MALI",  "MALL",  "MALT",
        "MANA",  "MANN",  "MANY",  "MARC",  "MARE",  "MARK",  "MARS",  "MART",
        "MARY",  "MASH",  "MASK",  "MASS",  "MAST",  "MATE",  "MATH",  "MAUL",
        "MAYO",  "MEAD",  "MEAL",  "MEAN",  "MEAT",  "MEEK",  "MEET",  "MELD",
        "MELT",  "MEMO",  "MEND",  "MENU",  "MERT",  "MESH",  "MESS",  "MICE",
        "MIKE",  "MILD",  "MILE",  "MILK",  "MILL",  "MILT",  "MIMI",  "MIND",
        "MINE",  "MINI",  "MINK",  "MINT",  "MIRE",  "MISS",  "MIST",  "MITE",
        "MITT",  "MOAN",  "MOAT",  "MOCK",  "MODE",  "MOLD",  "MOLE",  "MOLL",
        "MOLT",  "MONA",  "MONK",  "MONT",  "MOOD",  "MOON",  "MOOR",  "MOOT",
        "MORE",  "MORN",  "MORT",  "MOSS",  "MOST",  "MOTH",  "MOVE",  "MUCH",
        "MUCK",  "MUDD",  "MUFF",  "MULE",  "MULL",  "MURK",  "MUSH",  "MUST",
        "MUTE",  "MUTT",  "MYRA",  "MYTH",  "NAGY",  "NAIL",  "NAIR",  "NAME",
        "NARY",  "NASH",  "NAVE",  "NAVY",  "NEAL",  "NEAR",  "NEAT",  "NECK",
        "NEED",  "NEIL",  "NELL",  "NEON",  "NERO",  "NESS",  "NEST",  "NEWS",
        "NEWT",  "NIBS",  "NICE",  "NICK",  "NILE",  "NINA",  "NINE",  "NOAH",
        "NODE",  "NOEL",  "NOLL",  "NONE",  "NOOK",  "NOON",  "NORM",  "NOSE",
        "NOTE",  "NOUN",  "NOVA",  "NUDE",  "NULL",  "NUMB",  "OATH",  "OBEY",
        "OBOE",  "ODIN",  "OHIO",  "OILY",  "OINT",  "OKAY",  "OLAF",  "OLDY",
        "OLGA",  "OLIN",  "OMAN",  "OMEN",  "OMIT",  "ONCE",  "ONES",  "ONLY",
        "ONTO",  "ONUS",  "ORAL",  "ORGY",  "OSLO",  "OTIS",  "OTTO",  "OUCH",
        "OUST",  "OUTS",  "OVAL",  "OVEN",  "OVER",  "OWLY",  "OWNS",  "QUAD",
        "QUIT",  "QUOD",  "RACE",  "RACK",  "RACY",  "RAFT",  "RAGE",  "RAID",
        "RAIL",  "RAIN",  "RAKE",  "RANK",  "RANT",  "RARE",  "RASH",  "RATE",
        "RAVE",  "RAYS",  "READ",  "REAL",  "REAM",  "REAR",  "RECK",  "REED",
        "REEF",  "REEK",  "REEL",  "REID",  "REIN",  "RENA",  "REND",  "RENT",
        "REST",  "RICE",  "RICH",  "RICK",  "RIDE",  "RIFT",  "RILL",  "RIME",
        "RING",  "RINK",  "RISE",  "RISK",  "RITE",  "ROAD",  "ROAM",  "ROAR",
        "ROBE",  "ROCK",  "RODE",  "ROIL",  "ROLL",  "ROME",  "ROOD",  "ROOF",
        "ROOK",  "ROOM",  "ROOT",  "ROSA",  "ROSE",  "ROSS",  "ROSY",  "ROTH",
        "ROUT",  "ROVE",  "ROWE",  "ROWS",  "RUBE",  "RUBY",  "RUDE",  "RUDY",
        "RUIN",  "RULE",  "RUNG",  "RUNS",  "RUNT",  "RUSE",  "RUSH",  "RUSK",
        "RUSS",  "RUST",  "RUTH",  "SACK",  "SAFE",  "SAGE",  "SAID",  "SAIL",
        "SALE",  "SALK",  "SALT",  "SAME",  "SAND",  "SANE",  "SANG",  "SANK",
        "SARA",  "SAUL",  "SAVE",  "SAYS",  "SCAN",  "SCAR",  "SCAT",  "SCOT",
        "SEAL",  "SEAM",  "SEAR",  "SEAT",  "SEED",  "SEEK",  "SEEM",  "SEEN",
        "SEES",  "SELF",  "SELL",  "SEND",  "SENT",  "SETS",  "SEWN",  "SHAG",
        "SHAM",  "SHAW",  "SHAY",  "SHED",  "SHIM",  "SHIN",  "SHOD",  "SHOE",
        "SHOT",  "SHOW",  "SHUN",  "SHUT",  "SICK",  "SIDE",  "SIFT",  "SIGH",
        "SIGN",  "SILK",  "SILL",  "SILO",  "SILT",  "SINE",  "SING",  "SINK",
        "SIRE",  "SITE",  "SITS",  "SITU",  "SKAT",  "SKEW",  "SKID",  "SKIM",
        "SKIN",  "SKIT",  "SLAB",  "SLAM",  "SLAT",  "SLAY",  "SLED",  "SLEW",
        "SLID",  "SLIM",  "SLIT",  "SLOB",  "SLOG",  "SLOT",  "SLOW",  "SLUG",
        "SLUM",  "SLUR",  "SMOG",  "SMUG",  "SNAG",  "SNOB",  "SNOW",  "SNUB",
        "SNUG",  "SOAK",  "SOAR",  "SOCK",  "SODA",  "SOFA",  "SOFT",  "SOIL",
        "SOLD",  "SOME",  "SONG",  "SOON",  "SOOT",  "SORE",  "SORT",  "SOUL",
        "SOUR",  "SOWN",  "STAB",  "STAG",  "STAN",  "STAR",  "STAY",  "STEM",
        "STEW",  "STIR",  "STOW",  "STUB",  "STUN",  "SUCH",  "SUDS",  "SUIT",
        "SULK",  "SUMS",  "SUNG",  "SUNK",  "SURE",  "SURF",  "SWAB",  "SWAG",
        "SWAM",  "SWAN",  "SWAT",  "SWAY",  "SWIM",  "SWUM",  "TACK",  "TACT",
        "TAIL",  "TAKE",  "TALE",  "TALK",  "TALL",  "TANK",  "TASK",  "TATE",
        "TAUT",  "TEAL",  "TEAM",  "TEAR",  "TECH",  "TEEM",  "TEEN",  "TEET",
        "TELL",  "TEND",  "TENT",  "TERM",  "TERN",  "TESS",  "TEST",  "THAN",
        "THAT",  "THEE",  "THEM",  "THEN",  "THEY",  "THIN",  "THIS",  "THUD",
        "THUG",  "TICK",  "TIDE",  "TIDY",  "TIED",  "TIER",  "TILE",  "TILL",
        "TILT",  "TIME",  "TINA",  "TINE",  "TINT",  "TINY",  "TIRE",  "TOAD",
        "TOGO",  "TOIL",  "TOLD",  "TOLL",  "TONE",  "TONG",  "TONY",  "TOOK",
        "TOOL",  "TOOT",  "TORE",  "TORN",  "TOTE",  "TOUR",  "TOUT",  "TOWN",
        "TRAG",  "TRAM",  "TRAY",  "TREE",  "TREK",  "TRIG",  "TRIM",  "TRIO",
        "TROD",  "TROT",  "TROY",  "TRUE",  "TUBA",  "TUBE",  "TUCK",  "TUFT",
        "TUNA",  "TUNE",  "TUNG",  "TURF",  "TURN",  "TUSK",  "TWIG",  "TWIN",
        "TWIT",  "ULAN",  "UNIT",  "URGE",  "USED",  "USER",  "USES",  "UTAH",
        "VAIL",  "VAIN",  "VALE",  "VARY",  "VASE",  "VAST",  "VEAL",  "VEDA",
        "VEIL",  "VEIN",  "VEND",  "VENT",  "VERB",  "VERY",  "VETO",  "VICE",
        "VIEW",  "VINE",  "VISE",  "VOID",  "VOLT",  "VOTE",  "WACK",  "WADE",
        "WAGE",  "WAIL",  "WAIT",  "WAKE",  "WALE",  "WALK",  "WALL",  "WALT",
        "WAND",  "WANE",  "WANG",  "WANT",  "WARD",  "WARM",  "WARN",  "WART",
        "WASH",  "WAST",  "WATS",  "WATT",  "WAVE",  "WAVY",  "WAYS",  "WEAK",
        "WEAL",  "WEAN",  "WEAR",  "WEED",  "WEEK",  "WEIR",  "WELD",  "WELL",
        "WELT",  "WENT",  "WERE",  "WERT",  "WEST",  "WHAM",  "WHAT",  "WHEE",
        "WHEN",  "WHET",  "WHOA",  "WHOM",  "WICK",  "WIFE",  "WILD",  "WILL",
        "WIND",  "WINE",  "WING",  "WINK",  "WINO",  "WIRE",  "WISE",  "WISH",
        "WITH",  "WOLF",  "WONT",  "WOOD",  "WOOL",  "WORD",  "WORE",  "WORK",
        "WORM",  "WORN",  "WOVE",  "WRIT",  "WYNN",  "YALE",  "YANG",  "YANK",
        "YARD",  "YARN",  "YAWL",  "YAWN",  "YEAH",  "YEAR",  "YELL",  "YOGA",
        "YOKE" };
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy